diff --git a/src/fu-engine.c b/src/fu-engine.c new file mode 100644 index 000000000..96407b325 --- /dev/null +++ b/src/fu-engine.c @@ -0,0 +1,2551 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2015-2017 Richard Hughes + * + * Licensed under the GNU Lesser General Public License Version 2.1 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" + +#include +#include +#include +#include + +#include "fwupd-common-private.h" +#include "fwupd-enums-private.h" +#include "fwupd-error.h" +#include "fwupd-release-private.h" +#include "fwupd-remote-private.h" +#include "fwupd-resources.h" + +#include "fu-config.h" +#include "fu-debug.h" +#include "fu-device.h" +#include "fu-engine.h" +#include "fu-hwids.h" +#include "fu-keyring.h" +#include "fu-pending.h" +#include "fu-plugin.h" +#include "fu-plugin-private.h" +#include "fu-quirks.h" + +static void fu_engine_finalize (GObject *obj); + +struct _FuEngine +{ + GObject parent_instance; + GUsbContext *usb_ctx; + FuConfig *config; + GPtrArray *devices; /* of FuDeviceItem */ + FwupdStatus status; + guint percentage; + FuPending *pending; + AsProfile *profile; + AsStore *store; + gboolean coldplug_running; + guint coldplug_id; + guint coldplug_delay; + GPtrArray *plugins; /* of FuPlugin */ + GHashTable *plugins_hash; /* of name : FuPlugin */ + GHashTable *hwids; /* of hwid : 1 */ +}; + +enum { + SIGNAL_CHANGED, + SIGNAL_DEVICE_ADDED, + SIGNAL_DEVICE_REMOVED, + SIGNAL_DEVICE_CHANGED, + SIGNAL_STATUS_CHANGED, + SIGNAL_PERCENTAGE_CHANGED, + SIGNAL_LAST +}; + +static guint signals[SIGNAL_LAST] = { 0 }; + +G_DEFINE_TYPE (FuEngine, fu_engine, G_TYPE_OBJECT) + +typedef struct { + FuDevice *device; + FuPlugin *plugin; +} FuDeviceItem; + +static gboolean fu_engine_get_updates_item_update (FuEngine *self, FuDeviceItem *item); + +static void +fu_engine_emit_changed (FuEngine *self) +{ + g_signal_emit (self, signals[SIGNAL_CHANGED], 0); +} + +static void +fu_engine_emit_device_added (FuEngine *self, FuDevice *device) +{ + g_signal_emit (self, signals[SIGNAL_DEVICE_ADDED], 0, device); +} + +static void +fu_engine_emit_device_removed (FuEngine *self, FuDevice *device) +{ + g_signal_emit (self, signals[SIGNAL_DEVICE_REMOVED], 0, device); +} + +static void +fu_engine_emit_device_changed (FuEngine *self, FuDevice *device) +{ + g_signal_emit (self, signals[SIGNAL_DEVICE_CHANGED], 0, device); +} + +/** + * fu_engine_get_status: + * @self: A #FuEngine + * @device_id: A device ID + * @error: A #GError, or %NULL + * + * Gets the current engine status. + * + * Returns: a #FwupdStatus, e.g. %FWUPD_STATUS_DECOMPRESSING + **/ +FwupdStatus +fu_engine_get_status (FuEngine *self) +{ + g_return_val_if_fail (FU_IS_ENGINE (self), 0); + return self->status; +} + +/** + * fu_engine_profile_dump: + * @self: A #FuEngine + * + * Dumps the engine profiling state to the console. + **/ +void +fu_engine_profile_dump (FuEngine *self) +{ + g_return_if_fail (FU_IS_ENGINE (self)); + as_profile_dump (self->profile); +} + +static void +fu_engine_set_status (FuEngine *self, FwupdStatus status) +{ + if (self->status == status) + return; + self->status = status; + + /* emit changed */ + g_debug ("Emitting PropertyChanged('Status'='%s')", + fwupd_status_to_string (status)); + g_signal_emit (self, signals[SIGNAL_STATUS_CHANGED], 0, status); +} + +static void +fu_engine_set_percentage (FuEngine *self, guint percentage) +{ + if (self->percentage == percentage) + return; + self->percentage = percentage; + + /* emit changed */ + g_signal_emit (self, signals[SIGNAL_PERCENTAGE_CHANGED], 0, percentage); +} + +static void +fu_engine_item_free (FuDeviceItem *item) +{ + g_object_unref (item->device); + g_object_unref (item->plugin); + g_free (item); +} + +static FuDeviceItem * +fu_engine_get_item_by_id (FuEngine *self, const gchar *device_id, GError **error) +{ + for (guint i = 0; i < self->devices->len; i++) { + FuDeviceItem *item = g_ptr_array_index (self->devices, i); + if (g_strcmp0 (fu_device_get_id (item->device), device_id) == 0) + return item; + if (g_strcmp0 (fu_device_get_equivalent_id (item->device), device_id) == 0) + return item; + } + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "Device %s was not found", + device_id); + return NULL; +} + +static FuDeviceItem * +fu_engine_get_item_by_guid (FuEngine *self, const gchar *guid) +{ + for (guint i = 0; i < self->devices->len; i++) { + FuDeviceItem *item = g_ptr_array_index (self->devices, i); + if (fu_device_has_guid (item->device, guid)) + return item; + } + return NULL; +} + +static FuPlugin * +fu_engine_get_plugin_by_name (FuEngine *self, const gchar *name) +{ + for (guint i = 0; i < self->plugins->len; i++) { + FuPlugin *plugin = g_ptr_array_index (self->plugins, i); + if (g_strcmp0 (fu_plugin_get_name (plugin), name) == 0) + return plugin; + } + return NULL; +} + +static const gchar * +fu_engine_get_sysconfig_dir (void) +{ + if (g_file_test (SYSCONFDIR, G_FILE_TEST_EXISTS)) + return SYSCONFDIR; + return "/etc"; +} + +static void +fu_engine_set_release_from_item (FwupdRelease *rel, AsRelease *release) +{ + AsChecksum *csum; + const gchar *tmp; + + tmp = as_release_get_version (release); + if (tmp != NULL) + fwupd_release_set_version (rel, tmp); + tmp = as_release_get_description (release, NULL); + if (tmp != NULL) + fwupd_release_set_description (rel, tmp); + tmp = as_release_get_location_default (release); + if (tmp != NULL) + fwupd_release_set_uri (rel, tmp); + csum = as_release_get_checksum_by_target (release, AS_CHECKSUM_TARGET_CONTENT); + if (csum != NULL) { + tmp = as_checksum_get_filename (csum); + if (tmp != NULL) + fwupd_release_set_filename (rel, tmp); + } + csum = as_release_get_checksum_by_target (release, AS_CHECKSUM_TARGET_CONTAINER); + if (csum != NULL) { + tmp = as_checksum_get_value (csum); + if (tmp != NULL) + fwupd_release_add_checksum (rel, tmp); + } + fwupd_release_set_size (rel, as_release_get_size (release, AS_SIZE_KIND_INSTALLED)); +} + +static gboolean +fu_engine_get_release_trust_flags (AsRelease *release, + FwupdTrustFlags *trust_flags, + GError **error) +{ + AsChecksum *csum_tmp; + GBytes *blob_payload; + GBytes *blob_signature; + const gchar *fn; + g_autofree gchar *pki_dir = NULL; + g_autofree gchar *fn_signature = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(FuKeyring) kr = NULL; + + /* no filename? */ + csum_tmp = as_release_get_checksum_by_target (release, AS_CHECKSUM_TARGET_CONTENT); + fn = as_checksum_get_filename (csum_tmp); + if (fn == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no filename"); + return FALSE; + } + + /* no signature == no trust */ + fn_signature = g_strdup_printf ("%s.asc", fn); + blob_signature = as_release_get_blob (release, fn_signature); + if (blob_signature == NULL) { + g_debug ("firmware archive contained no GPG signature"); + return TRUE; + } + + /* get payload */ + blob_payload = as_release_get_blob (release, fn); + if (blob_payload == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no payload"); + return FALSE; + } + + /* check we were installed correctly */ + pki_dir = g_build_filename (fu_engine_get_sysconfig_dir (), "pki", "fwupd", NULL); + if (!g_file_test (pki_dir, G_FILE_TEST_EXISTS)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "PKI directory %s not found", pki_dir); + return FALSE; + } + + /* verify against the system trusted keys */ + kr = fu_keyring_new (); + if (!fu_keyring_add_public_keys (kr, pki_dir, error)) + return FALSE; + if (!fu_keyring_verify_data (kr, blob_payload, blob_signature, &error_local)) { + g_warning ("untrusted as failed to verify: %s", + error_local->message); + return TRUE; + } + + /* awesome! */ + g_debug ("marking payload as trusted"); + *trust_flags |= FWUPD_TRUST_FLAG_PAYLOAD; + return TRUE; +} + +/** + * fu_engine_unlock: + * @self: A #FuEngine + * @device_id: A device ID + * @error: A #GError, or %NULL + * + * Unlocks a device. + * + * Returns: %TRUE for success + **/ +gboolean +fu_engine_unlock (FuEngine *self, const gchar *device_id, GError **error) +{ + FuDeviceItem *item; + + g_return_val_if_fail (FU_IS_ENGINE (self), FALSE); + g_return_val_if_fail (device_id != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* check the device exists */ + item = fu_engine_get_item_by_id (self, device_id, error); + if (item == NULL) + return FALSE; + + /* run the correct plugin that added this */ + if (!fu_plugin_runner_unlock (item->plugin, + item->device, + error)) + return FALSE; + + /* make the UI update */ + fu_engine_emit_device_changed (self, item->device); + fu_engine_emit_changed (self); + return TRUE; +} + +static AsApp * +fu_engine_verify_update_device_to_app (FuDevice *device) +{ + AsApp *app = NULL; + GPtrArray *checksums; + g_autofree gchar *id = NULL; + g_autoptr(AsProvide) prov = NULL; + g_autoptr(AsRelease) rel = NULL; + + /* make a plausible ID */ + id = g_strdup_printf ("%s.firmware", fu_device_get_guid_default (device)); + + /* add app to store */ + app = as_app_new (); + as_app_set_id (app, id); + as_app_set_kind (app, AS_APP_KIND_FIRMWARE); + rel = as_release_new (); + as_release_set_version (rel, fu_device_get_version (device)); + checksums = fu_device_get_checksums (device); + for (guint j = 0; j < checksums->len; j++) { + const gchar *checksum = g_ptr_array_index (checksums, j); + g_autoptr(AsChecksum) csum = as_checksum_new (); + as_checksum_set_kind (csum, fwupd_checksum_guess_kind (checksum)); + as_checksum_set_value (csum, checksum); + as_checksum_set_target (csum, AS_CHECKSUM_TARGET_CONTENT); + as_release_add_checksum (rel, csum); + } + as_app_add_release (app, rel); + prov = as_provide_new (); + as_provide_set_kind (prov, AS_PROVIDE_KIND_FIRMWARE_FLASHED); + as_provide_set_value (prov, fu_device_get_guid_default (device)); + as_app_add_provide (app, prov); + return app; +} + +static AsStore * +fu_engine_load_verify_store (GError **error) +{ + const gchar *fn = "/var/lib/fwupd/verify.xml"; + g_autoptr(AsStore) store = NULL; + g_autoptr(GFile) file = NULL; + + /* load existing store */ + store = as_store_new (); + as_store_set_api_version (store, 0.9); + file = g_file_new_for_path (fn); + if (g_file_query_exists (file, NULL)) { + if (!as_store_from_file (store, file, NULL, NULL, error)) + return NULL; + } + return g_steal_pointer (&store); +} + +/** + * fu_engine_verify_update: + * @self: A #FuEngine + * @device_id: A device ID + * @error: A #GError, or %NULL + * + * Updates the verification store entry for a specific device. + * + * Returns: %TRUE for success + **/ +gboolean +fu_engine_verify_update (FuEngine *self, const gchar *device_id, GError **error) +{ + FuDeviceItem *item; + GPtrArray *checksums; + const gchar *fn = "/var/lib/fwupd/verify.xml"; + g_autoptr(AsApp) app = NULL; + g_autoptr(AsStore) store = NULL; + g_autoptr(GFile) file = NULL; + + g_return_val_if_fail (FU_IS_ENGINE (self), FALSE); + g_return_val_if_fail (device_id != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* check the devices still exists */ + item = fu_engine_get_item_by_id (self, device_id, error); + if (item == NULL) + return FALSE; + + /* unlock device if required */ + if (fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_LOCKED)) { + if (!fu_plugin_runner_unlock (item->plugin, + item->device, + error)) + return FALSE; + fu_engine_emit_device_changed (self, item->device); + } + + /* get the checksum */ + checksums = fu_device_get_checksums (item->device); + if (checksums->len == 0) { + if (!fu_plugin_runner_verify (item->plugin, + item->device, + FU_PLUGIN_VERIFY_FLAG_NONE, + error)) + return FALSE; + fu_engine_emit_device_changed (self, item->device); + } + + /* we got nothing */ + if (checksums->len == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "device verification not supported"); + return FALSE; + } + + /* load existing store */ + store = fu_engine_load_verify_store (error); + if (store == NULL) + return FALSE; + + /* add to store */ + app = fu_engine_verify_update_device_to_app (item->device); + as_store_add_app (store, app); + + /* write */ + g_debug ("writing %s", fn); + file = g_file_new_for_path (fn); + return as_store_to_file (store, file, + AS_NODE_TO_XML_FLAG_ADD_HEADER | + AS_NODE_TO_XML_FLAG_FORMAT_INDENT | + AS_NODE_TO_XML_FLAG_FORMAT_MULTILINE, + NULL, error); +} + +static AsApp * +fu_engine_store_get_app_by_guids (AsStore *store, FuDevice *device) +{ + GPtrArray *guids = fu_device_get_guids (device); + for (guint i = 0; i < guids->len; i++) { + AsApp *app = NULL; + app = as_store_get_app_by_provide (store, + AS_PROVIDE_KIND_FIRMWARE_FLASHED, + g_ptr_array_index (guids, i)); + if (app != NULL) + return app; + } + return NULL; +} + +/** + * fu_engine_verify: + * @self: A #FuEngine + * @device_id: A device ID + * @error: A #GError, or %NULL + * + * Verifies a device firmware checksum using the verification store entry. + * + * Returns: %TRUE for success + **/ +gboolean +fu_engine_verify (FuEngine *self, const gchar *device_id, GError **error) +{ + AsApp *app; + AsChecksum *csum; + AsRelease *release; + FuDeviceItem *item = NULL; + GPtrArray *checksums; + const gchar *hash = NULL; + const gchar *version = NULL; + g_autoptr(AsStore) store = NULL; + + g_return_val_if_fail (FU_IS_ENGINE (self), FALSE); + g_return_val_if_fail (device_id != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* check the id exists */ + item = fu_engine_get_item_by_id (self, device_id, error); + if (item == NULL) + return FALSE; + + /* set the device firmware hash */ + if (!fu_plugin_runner_verify (item->plugin, item->device, + FU_PLUGIN_VERIFY_FLAG_NONE, error)) + return FALSE; + + /* find component in metadata */ + store = fu_engine_load_verify_store (error); + if (store == NULL) + return FALSE; + app = fu_engine_store_get_app_by_guids (store, item->device); + if (app == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "No metadata"); + return FALSE; + } + + /* find version in metadata */ + version = fu_device_get_version (item->device); + release = as_app_get_release (app, version); + if (release == NULL) { + /* try again with the system metadata */ + app = fu_engine_store_get_app_by_guids (self->store, item->device); + if (app == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "No system metadata"); + return FALSE; + } + release = as_app_get_release (app, version); + } + if (release == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "No version %s", version); + return FALSE; + } + + /* find checksum */ + csum = as_release_get_checksum_by_target (release, AS_CHECKSUM_TARGET_CONTENT); + if (csum == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "No content checksum for %s", version); + return FALSE; + } + + /* get the matching checksum */ + checksums = fu_device_get_checksums (item->device); + if (checksums->len == 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "No device checksums for %s", version); + return FALSE; + } + for (guint j = 0; j < checksums->len; j++) { + const gchar *hash_tmp = g_ptr_array_index (checksums, j); + GChecksumType hash_kind = fwupd_checksum_guess_kind (hash_tmp); + if (as_checksum_get_kind (csum) == hash_kind) { + hash = hash_tmp; + break; + } + } + if (hash == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "No matching hash kind for %s", version); + return FALSE; + } + if (g_strcmp0 (as_checksum_get_value (csum), hash) != 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "For v%s expected %s, got %s", + version, + as_checksum_get_value (csum), + hash); + return FALSE; + } + + /* success */ + return TRUE; +} + +static AsScreenshot * +_as_app_get_screenshot_default (AsApp *app) +{ + GPtrArray *array = as_app_get_screenshots (app); + if (array->len == 0) + return NULL; + return g_ptr_array_index (array, 0); +} + +#if AS_CHECK_VERSION(0,6,7) +static gboolean +fu_engine_check_version_requirement (AsApp *app, + AsRequireKind kind, + const gchar *id, + const gchar *version, + GError **error) +{ + AsRequire *req; + + /* check args */ + if (version == NULL) { + g_debug ("no paramater given for %s{%s}", + as_require_kind_to_string (kind), id); + return TRUE; + } + + /* does requirement exist */ + req = as_app_get_require_by_value (app, kind, id); + if (req == NULL) { + g_debug ("no requirement on %s{%s}", + as_require_kind_to_string (kind), id); + return TRUE; + } + + /* check version */ + if (!as_require_version_compare (req, version, error)) { + g_prefix_error (error, "Value of %s incorrect: ", id); + return FALSE; + } + + /* success */ + g_debug ("requirement %s %s %s on %s passed", + as_require_get_version (req), + as_require_compare_to_string (as_require_get_compare (req)), + version, id); + return TRUE; +} +#endif + +static gboolean +fu_engine_check_requirements (AsApp *app, FuDevice *device, GError **error) +{ +#if AS_CHECK_VERSION(0,6,7) + /* make sure requirements are satisfied */ + if (!fu_engine_check_version_requirement (app, + AS_REQUIRE_KIND_ID, + "org.freedesktop.fwupd", + VERSION, + error)) { + return FALSE; + } + + if (device != NULL) { + if (!fu_engine_check_version_requirement (app, + AS_REQUIRE_KIND_FIRMWARE, + NULL, + fu_device_get_version (device), + error)) { + return FALSE; + } + if (!fu_engine_check_version_requirement (app, + AS_REQUIRE_KIND_FIRMWARE, + "bootloader", + fu_device_get_version_bootloader (device), + error)) { + return FALSE; + } + if (!fu_engine_check_version_requirement (app, + AS_REQUIRE_KIND_FIRMWARE, + "vendor-id", + fu_device_get_vendor_id (device), + error)) { + return FALSE; + } + } +#endif + + /* success */ + return TRUE; +} + +static void +fu_engine_vendor_fixup_provide_value (AsApp *app) +{ + GPtrArray *provides; + + /* no quirk required */ + if (as_app_get_kind (app) != AS_APP_KIND_FIRMWARE) + return; + + /* fix each provide to be a GUID */ + provides = as_app_get_provides (app); + for (guint i = 0; i < provides->len; i++) { + AsProvide *prov = g_ptr_array_index (provides, i); + const gchar *value = as_provide_get_value (prov); + g_autofree gchar *guid = NULL; + if (as_provide_get_kind (prov) != AS_PROVIDE_KIND_FIRMWARE_FLASHED) + continue; + if (as_utils_guid_is_valid (value)) + continue; + guid = as_utils_guid_from_string (value); + as_provide_set_value (prov, guid); + } +} + +static void +fu_engine_vendor_quirk_release_version (AsApp *app) +{ + AsVersionParseFlag flags = AS_VERSION_PARSE_FLAG_USE_TRIPLET; + GPtrArray *releases; + + /* no quirk required */ + if (as_app_get_kind (app) != AS_APP_KIND_FIRMWARE) + return; + + for (guint i = 0; quirk_table[i].identifier != NULL; i++) { + if (g_str_has_prefix (as_app_get_id(app), quirk_table[i].identifier)) + flags = quirk_table[i].flags; + } + + /* fix each release */ + releases = as_app_get_releases (app); + for (guint i = 0; i < releases->len; i++) { + AsRelease *rel; + const gchar *version; + guint64 ver_uint32; + g_autofree gchar *version_new = NULL; + + rel = g_ptr_array_index (releases, i); + version = as_release_get_version (rel); + if (version == NULL) + continue; + if (g_strstr_len (version, -1, ".") != NULL) + continue; + + /* metainfo files use hex and the LVFS uses decimal */ + if (g_str_has_prefix (version, "0x")) { + ver_uint32 = g_ascii_strtoull (version + 2, NULL, 16); + } else { + ver_uint32 = g_ascii_strtoull (version, NULL, 10); + } + if (ver_uint32 == 0) + continue; + + /* convert to dotted decimal */ + version_new = as_utils_version_from_uint32 ((guint32) ver_uint32, flags); + as_release_set_version (rel, version_new); + } +} + +static gchar * +fu_engine_get_guids_from_store (AsStore *store) +{ + AsProvide *prov; + GPtrArray *provides; + GPtrArray *apps; + GString *str = g_string_new (""); + + /* return a string with all the firmware apps in the store */ + apps = as_store_get_apps (store); + for (guint i = 0; i < apps->len; i++) { + AsApp *app = AS_APP (g_ptr_array_index (apps, i)); + provides = as_app_get_provides (app); + for (guint j = 0; j < provides->len; j++) { + prov = AS_PROVIDE (g_ptr_array_index (provides, j)); + if (as_provide_get_kind (prov) != AS_PROVIDE_KIND_FIRMWARE_FLASHED) + continue; + g_string_append_printf (str, "%s,", as_provide_get_value (prov)); + } + } + if (str->len == 0) + return NULL; + g_string_truncate (str, str->len - 1); + return g_string_free (str, FALSE); +} + +static FuDeviceItem * +fu_engine_get_item_by_wildcard (FuEngine *self, AsStore *store, GError **error) +{ + g_autofree gchar *guids = NULL; + for (guint i = 0; i < self->devices->len; i++) { + FuDeviceItem *item_tmp = g_ptr_array_index (self->devices, i); + if (fu_engine_store_get_app_by_guids (store, item_tmp->device) != NULL) + return item_tmp; + } + guids = fu_engine_get_guids_from_store (store); + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "Firmware is not for any attached hardware: got %s", + guids); + return NULL; +} + +/** + * fu_engine_install: + * @self: A #FuEngine + * @device_id: A device ID + * @store: The #AsStore with the firmware metadata + * @blob_cab: The #GBytes of the .cab file + * @flags: The #FwupdInstallFlags, e.g. %FWUPD_DEVICE_FLAG_ALLOW_ONLINE + * @error: A #GError, or %NULL + * + * Installs a specfic firmware file on a device. + * + * Returns: %TRUE for success + **/ +gboolean +fu_engine_install (FuEngine *self, + const gchar *device_id, + AsStore *store, + GBytes *blob_cab, + FwupdInstallFlags flags, + GError **error) +{ + AsApp *app; + AsChecksum *csum_tmp; + AsRelease *rel; + FuDeviceItem *item; + GBytes *blob_fw; + const gchar *tmp; + const gchar *version; + gboolean is_downgrade; + gint vercmp; + + g_return_val_if_fail (FU_IS_ENGINE (self), FALSE); + g_return_val_if_fail (device_id != NULL, FALSE); + g_return_val_if_fail (AS_IS_STORE (store), FALSE); + g_return_val_if_fail (blob_cab != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* wildcard */ + if (g_strcmp0 (device_id, FWUPD_DEVICE_ID_ANY) == 0) { + item = fu_engine_get_item_by_wildcard (self, store, error); + if (item == NULL) + return FALSE; + } else { + /* find the specific device */ + item = fu_engine_get_item_by_id (self, device_id, error); + if (item == NULL) + return FALSE; + } + + /* check the device is not locked */ + if (fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_LOCKED)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Device %s is locked", + device_id); + return FALSE; + } + + /* no update abilities */ + if (!fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_ALLOW_OFFLINE) && + !fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_ALLOW_ONLINE)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Device %s does not currently allow updates", + device_id); + return FALSE; + } + + /* Called with online update, test if device is supposed to allow this */ + if (!(flags & FWUPD_INSTALL_FLAG_OFFLINE) && + !fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_ALLOW_ONLINE)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Device %s does not allow online updates", + device_id); + return FALSE; + } + + /* Called with offline update, test if device is supposed to allow this */ + if (flags & FWUPD_INSTALL_FLAG_OFFLINE && + !fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_ALLOW_OFFLINE)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Device %s does not allow offline updates", + device_id); + return FALSE; + } + + /* find from guid */ + app = fu_engine_store_get_app_by_guids (store, item->device); + if (app == NULL) { + g_autofree gchar *guid = NULL; + guid = fu_engine_get_guids_from_store (store); + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "Firmware is not for this hw: required %s got %s", + fu_device_get_guid_default (item->device), guid); + return FALSE; + } + + /* not in bootloader mode */ + if (fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER)) { + const gchar *caption = NULL; + AsScreenshot *ss = _as_app_get_screenshot_default (app); + if (ss != NULL) + caption = as_screenshot_get_caption (ss, NULL); + if (caption != NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Device %s needs to manually be put in update mode: %s", + fu_device_get_name (item->device), caption); + } else { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Device %s needs to manually be put in update mode", + fu_device_get_name (item->device)); + } + return FALSE; + } + + /* possibly convert the version from 0x to dotted */ + fu_engine_vendor_quirk_release_version (app); + + /* possibly convert the flashed provide to a GUID */ + fu_engine_vendor_fixup_provide_value (app); + + /* check we can install it */ + if (!fu_engine_check_requirements (app, item->device, error)) + return FALSE; + + /* parse the DriverVer */ + rel = as_app_get_release_default (app); + if (rel == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "No releases in the firmware component"); + return FALSE; + } + + /* get the blob */ + csum_tmp = as_release_get_checksum_by_target (rel, AS_CHECKSUM_TARGET_CONTENT); + tmp = as_checksum_get_filename (csum_tmp); + if (tmp == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "No checksum filename"); + return FALSE; + } + + /* not all devices have to use the same blob */ + blob_fw = as_release_get_blob (rel, tmp); + if (blob_fw == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "Failed to get firmware blob"); + return FALSE; + } + + version = as_release_get_version (rel); + fu_device_set_update_version (item->device, version); + + /* compare to the lowest supported version, if it exists */ + tmp = fu_device_get_version_lowest (item->device); + if (tmp != NULL && as_utils_vercmp (tmp, version) > 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_VERSION_NEWER, + "Specified firmware is older than the minimum " + "required version '%s < %s'", tmp, version); + return FALSE; + } + + /* compare the versions of what we have installed */ + tmp = fu_device_get_version (item->device); + if (tmp == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Device %s does not yet have a current version", + device_id); + return FALSE; + } + vercmp = as_utils_vercmp (tmp, version); + if (vercmp == 0 && (flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) == 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_VERSION_SAME, + "Specified firmware is already installed '%s'", + tmp); + return FALSE; + } + is_downgrade = vercmp > 0; + if (is_downgrade && (flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) == 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_VERSION_NEWER, + "Specified firmware is older than installed '%s < %s'", + tmp, version); + return FALSE; + } + + /* signal to all the plugins the update is about to happen */ + for (guint j = 0; j < self->plugins->len; j++) { + FuPlugin *plugin = g_ptr_array_index (self->plugins, j); + if (!fu_plugin_runner_update_prepare (plugin, item->device, error)) + return FALSE; + } + + /* do the update */ + if (!fu_plugin_runner_update (item->plugin, + item->device, + blob_cab, + blob_fw, + flags, + error)) { + for (guint j = 0; j < self->plugins->len; j++) { + FuPlugin *plugin = g_ptr_array_index (self->plugins, j); + g_autoptr(GError) error_local = NULL; + if (!fu_plugin_runner_update_cleanup (plugin, + item->device, + &error_local)) { + g_warning ("failed to update-cleanup " + "after failed update: %s", + error_local->message); + } + } + return FALSE; + } + + /* signal to all the plugins the update has happened */ + for (guint j = 0; j < self->plugins->len; j++) { + FuPlugin *plugin = g_ptr_array_index (self->plugins, j); + g_autoptr(GError) error_local = NULL; + if (!fu_plugin_runner_update_cleanup (plugin, item->device, &error_local)) { + g_warning ("failed to update-cleanup: %s", + error_local->message); + } + } + + /* make the UI update */ + fu_device_set_modified (item->device, (guint64) g_get_real_time () / G_USEC_PER_SEC); + fu_engine_emit_device_changed (self, item->device); + fu_engine_emit_changed (self); + return TRUE; +} + +static FuDeviceItem * +fu_engine_get_item_by_id_fallback_pending (FuEngine *self, const gchar *id, GError **error) +{ + FuDevice *dev; + FuPlugin *plugin; + FuDeviceItem *item = NULL; + FwupdUpdateState update_state; + const gchar *tmp; + g_autoptr(GPtrArray) devices = NULL; + + /* not a wildcard */ + if (g_strcmp0 (id, FWUPD_DEVICE_ID_ANY) != 0) + return fu_engine_get_item_by_id (self, id, error); + + /* allow '*' for any */ + devices = fu_pending_get_devices (self->pending, error); + if (devices == NULL) + return NULL; + for (guint i = 0; i < devices->len; i++) { + dev = g_ptr_array_index (devices, i); + update_state = fu_device_get_update_state (dev); + if (update_state == FWUPD_UPDATE_STATE_UNKNOWN) + continue; + if (update_state == FWUPD_UPDATE_STATE_PENDING) + continue; + + /* if the device is not still connected, fake a FuDeviceItem */ + item = fu_engine_get_item_by_id (self, fu_device_get_id (dev), NULL); + if (item == NULL) { + tmp = fu_device_get_plugin (dev); + plugin = fu_engine_get_plugin_by_name (self, tmp); + if (plugin == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no plugin %s found", tmp); + return NULL; + } + item = g_new0 (FuDeviceItem, 1); + item->device = g_object_ref (dev); + item->plugin = g_object_ref (plugin); + g_ptr_array_add (self->devices, item); + + /* FIXME: just a boolean on FuDeviceItem? */ + fu_device_set_metadata (dev, "FakeDevice", "TRUE"); + } + break; + } + + /* no device found */ + if (item == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no suitable devices found"); + } + return item; +} + +/** + * fu_engine_get_action_id_for_device: + * @self: A #FuEngine + * @device_id: A device ID + * @store: The #AsStore with the firmware metadata + * @flags: The #FwupdInstallFlags, e.g. %FWUPD_DEVICE_FLAG_ALLOW_ONLINE + * @error: A #GError, or %NULL + * + * Gets the PolicyKit action ID to use for the install operation. + * + * Returns: string, e.g. "org.freedesktop.fwupd.update-internal-trusted" + **/ +const gchar * +fu_engine_get_action_id_for_device (FuEngine *self, + const gchar *device_id, + AsStore *store, + FwupdInstallFlags flags, + GError **error) +{ + AsApp *app; + AsRelease *release; + FuDeviceItem *item; + FwupdTrustFlags trust_flags = FWUPD_TRUST_FLAG_NONE; + const gchar *version; + const gchar *version_release; + gboolean is_downgrade; + gboolean is_trusted; + gint vercmp; + + g_return_val_if_fail (FU_IS_ENGINE (self), NULL); + g_return_val_if_fail (AS_IS_STORE (store), NULL); + g_return_val_if_fail (device_id != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* wildcard */ + if (g_strcmp0 (device_id, FWUPD_DEVICE_ID_ANY) == 0) { + item = fu_engine_get_item_by_wildcard (self, store, error); + if (item == NULL) + return NULL; + } else { + /* find the specific device */ + item = fu_engine_get_item_by_id (self, device_id, error); + if (item == NULL) + return NULL; + } + + /* get device */ + version = fu_device_get_version (item->device); + if (version == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Device with ID %s has no firmware version", + device_id); + return NULL; + } + + /* match the GUIDs in the XML */ + app = fu_engine_store_get_app_by_guids (store, item->device); + if (app == NULL) { + g_autofree gchar *guid = NULL; + guid = fu_engine_get_guids_from_store (store); + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware is not for this hw: required %s got %s", + fu_device_get_guid_default (item->device), guid); + return NULL; + } + + /* possibly convert the version from 0x to dotted */ + fu_engine_vendor_quirk_release_version (app); + + /* possibly convert the flashed provide to a GUID */ + fu_engine_vendor_fixup_provide_value (app); + + /* get latest release */ + release = as_app_get_release_default (app); + if (release == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "%s [%s] has no firmware update metadata", + fu_device_get_id (item->device), + fu_device_get_name (item->device)); + return NULL; + } + + /* is this a downgrade or re-install */ + version_release = as_release_get_version (release); + if (version_release == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Release has no firmware version"); + return NULL; + } + vercmp = as_utils_vercmp (version, version_release); + if (vercmp == 0 && (flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) == 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_VERSION_SAME, + "Specified firmware is already installed '%s'", + version_release); + return NULL; + } + is_downgrade = vercmp > 0; + if (is_downgrade && (flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) == 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_VERSION_NEWER, + "Specified firmware is older than installed '%s < %s'", + version_release, version); + return NULL; + } + + /* verify */ + if (!fu_engine_get_release_trust_flags (release, &trust_flags, error)) + return NULL; + is_trusted = (trust_flags & FWUPD_TRUST_FLAG_PAYLOAD) > 0; + + /* relax authentication checks for removable devices */ + if (!fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_INTERNAL)) { + if (is_downgrade) + return "org.freedesktop.fwupd.downgrade-hotplug"; + if (is_trusted) + return "org.freedesktop.fwupd.update-hotplug-trusted"; + return "org.freedesktop.fwupd.update-hotplug"; + } + + /* internal device */ + if (is_downgrade) + return "org.freedesktop.fwupd.downgrade-internal"; + if (is_trusted) + return "org.freedesktop.fwupd.update-internal-trusted"; + return "org.freedesktop.fwupd.update-internal"; +} + +static gboolean +fu_engine_load_metadata_from_file (FuEngine *self, + const gchar *path, + const gchar *remote_id, + GError **error) +{ + GPtrArray *apps; + g_autoptr(AsStore) store = NULL; + g_autoptr(GFile) file = NULL; + + /* load the store locally until we know it is valid */ + store = as_store_new (); + file = g_file_new_for_path (path); + if (!as_store_from_file (store, file, NULL, NULL, error)) + return FALSE; + + /* add the new application from the store */ + apps = as_store_get_apps (store); + for (guint i = 0; i < apps->len; i++) { + AsApp *app = g_ptr_array_index (apps, i); + + /* does this app already exist */ + if (as_store_get_app_by_id (self->store, as_app_get_id (app)) != NULL) { + g_debug ("%s exists in remote %s, skipping", + as_app_get_unique_id (app), + as_app_get_metadata_item (app, "fwupd::RemoteID")); + continue; + } + if (remote_id != NULL && remote_id[0] != '\0') + as_app_add_metadata (app, "fwupd::RemoteID", remote_id); + as_store_add_app (self->store, app); + } + return TRUE; +} + +static gboolean +fu_engine_load_metadata_store (FuEngine *self, GError **error) +{ + GPtrArray *apps; + GPtrArray *remotes; + + /* clear existing store */ + as_store_remove_all (self->store); + + /* load each enabled metadata file */ + remotes = fu_config_get_remotes (self->config); + for (guint i = 0; i < remotes->len; i++) { + const gchar *path = NULL; + FwupdRemote *remote = g_ptr_array_index (remotes, i); + if (!fwupd_remote_get_enabled (remote)) { + g_debug ("remote %s not enabled, so skipping", + fwupd_remote_get_id (remote)); + continue; + } + path = fwupd_remote_get_filename_cache (remote); + if (!g_file_test (path, G_FILE_TEST_EXISTS)) { + g_debug ("no %s, so skipping", path); + continue; + } + if (!fu_engine_load_metadata_from_file (self, path, + fwupd_remote_get_id (remote), + error)) + return FALSE; + } + + /* print what we've got */ + apps = as_store_get_apps (self->store); + if (apps->len == 0) { + g_debug ("no devices in store"); + } else { + g_debug ("devices now in store:"); + for (guint i = 0; i < apps->len; i++) { + AsApp *app = g_ptr_array_index (apps, i); + g_debug ("%u\t%s\t%s", i + 1, + as_app_get_id (app), + as_app_get_name (app, NULL)); + } + } + + /* are any devices now supported? */ + for (guint i = 0; i < self->devices->len; i++) { + FuDeviceItem *item = g_ptr_array_index (self->devices, i); + if (fu_engine_get_updates_item_update (self, item)) + fu_engine_emit_device_changed (self, item->device); + } + + return TRUE; +} + +static gboolean +fu_engine_set_contents (const gchar *filename, GBytes *blob, GError **error) +{ + const gchar *data; + gsize size; + g_autoptr(GFile) file = NULL; + g_autoptr(GFile) file_parent = NULL; + + file = g_file_new_for_path (filename); + file_parent = g_file_get_parent (file); + if (!g_file_query_exists (file_parent, NULL)) { + if (!g_file_make_directory_with_parents (file_parent, NULL, error)) + return FALSE; + } + data = g_bytes_get_data (blob, &size); + return g_file_set_contents (filename, data, size, error); +} + +/** + * fu_engine_update_metadata: + * @self: A #FuEngine + * @remote_id: A remote ID, e.g. "lvfs" + * @fd: file descriptor of the metadata + * @fd_sig: file descriptor of the metadata signature + * @error: A #GError, or %NULL + * + * Updates the metadata for a specific remote. + * + * Note: this will close the fds when done + * + * Returns: %TRUE for success + **/ +gboolean +fu_engine_update_metadata (FuEngine *self, const gchar *remote_id, + gint fd, gint fd_sig, GError **error) +{ + FwupdRemote *remote; + g_autoptr(GBytes) bytes_raw = NULL; + g_autoptr(GBytes) bytes_sig = NULL; + g_autoptr(FuKeyring) kr = NULL; + g_autoptr(GInputStream) stream_fd = NULL; + g_autoptr(GInputStream) stream = NULL; + g_autoptr(GInputStream) stream_sig = NULL; + + g_return_val_if_fail (FU_IS_ENGINE (self), FALSE); + g_return_val_if_fail (remote_id != NULL, FALSE); + g_return_val_if_fail (fd > 0, FALSE); + g_return_val_if_fail (fd_sig > 0, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* ensures the fd's are closed on error */ + stream_fd = g_unix_input_stream_new (fd, TRUE); + stream_sig = g_unix_input_stream_new (fd_sig, TRUE); + + /* check remote is valid */ + remote = fu_config_get_remote_by_id (self->config, remote_id); + if (remote == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "remote %s not found", remote_id); + return FALSE; + } + if (!fwupd_remote_get_enabled (remote)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "remote %s not enabled", remote_id); + return FALSE; + } + + /* read the entire file into memory */ + bytes_raw = g_input_stream_read_bytes (stream_fd, 0x100000, NULL, error); + if (bytes_raw == NULL) + return FALSE; + + /* read signature */ + bytes_sig = g_input_stream_read_bytes (stream_sig, 0x800, NULL, error); + if (bytes_sig == NULL) + return FALSE; + + /* verify file */ + kr = fu_keyring_new (); + if (!fu_keyring_add_public_keys (kr, "/etc/pki/fwupd-metadata", error)) + return FALSE; + if (!fu_keyring_verify_data (kr, bytes_raw, bytes_sig, error)) + return FALSE; + + /* save XML to remotes.d */ + if (!fu_engine_set_contents (fwupd_remote_get_filename_cache (remote), bytes_raw, error)) + return FALSE; + return fu_engine_load_metadata_store (self, error); +} + +static gboolean +fu_engine_get_updates_item_update (FuEngine *self, FuDeviceItem *item) +{ + AsApp *app; + AsRelease *release; + GPtrArray *releases; + const gchar *tmp; + const gchar *version; + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) updates_list = NULL; + + /* get device version */ + version = fu_device_get_version (item->device); + if (version == NULL) + return FALSE; + + /* match the GUIDs in the XML */ + app = fu_engine_store_get_app_by_guids (self->store, item->device); + if (app == NULL) + return FALSE; + + /* possibly convert the version from 0x to dotted */ + fu_engine_vendor_quirk_release_version (app); + + /* possibly convert the flashed provide to a GUID */ + fu_engine_vendor_fixup_provide_value (app); + + /* get latest release */ + release = as_app_get_release_default (app); + if (release == NULL) { + g_debug ("%s [%s] has no firmware update metadata", + fu_device_get_id (item->device), + fu_device_get_name (item->device)); + return FALSE; + } + + /* supported in metadata */ + fwupd_device_add_flag (fwupd_result_get_device (FWUPD_RESULT (item->device)), + FWUPD_DEVICE_FLAG_SUPPORTED); + + /* check if actually newer than what we have installed */ + if (as_utils_vercmp (as_release_get_version (release), version) <= 0) { + g_debug ("%s has no firmware updates", + fu_device_get_id (item->device)); + return FALSE; + } + + /* check we can install it */ + if (!fu_engine_check_requirements (app, item->device, &error)) { + g_debug ("can not be installed: %s", error->message); + return FALSE; + } + + /* only show devices that can be updated */ + if (!fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_ALLOW_OFFLINE) && + !fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_ALLOW_ONLINE)) { + g_debug ("ignoring %s [%s] as not updatable live or offline", + fu_device_get_id (item->device), + fu_device_get_name (item->device)); + return FALSE; + } + + /* add application metadata */ + fu_device_set_update_id (item->device, as_app_get_id (app)); + tmp = as_app_get_developer_name (app, NULL); + if (tmp != NULL) + fu_device_set_update_vendor (item->device, tmp); + tmp = as_app_get_name (app, NULL); + if (tmp != NULL) + fu_device_set_update_name (item->device, tmp); + tmp = as_app_get_comment (app, NULL); + if (tmp != NULL) + fu_device_set_update_summary (item->device, tmp); + tmp = as_app_get_description (app, NULL); + if (tmp != NULL) + fu_device_set_description (item->device, tmp); + tmp = as_app_get_url_item (app, AS_URL_KIND_HOMEPAGE); + if (tmp != NULL) + fu_device_set_update_homepage (item->device, tmp); + tmp = as_app_get_project_license (app); + if (tmp != NULL) + fu_device_set_update_license (item->device, tmp); + tmp = as_app_get_metadata_item (app, "fwupd::RemoteID"); + if (tmp != NULL) + fu_device_set_update_remote_id (item->device, tmp); +#if AS_CHECK_VERSION(0,6,1) + tmp = as_app_get_unique_id (app); + if (tmp != NULL) + fu_device_set_unique_id (item->device, tmp); +#else + fu_device_set_unique_id (item->device, as_app_get_id (app)); +#endif + + /* add release information */ + fu_engine_set_release_from_item (fwupd_result_get_release (FWUPD_RESULT (item->device)), release); + + /* get the list of releases newer than the one installed */ + updates_list = g_ptr_array_new (); + releases = as_app_get_releases (app); + for (guint i = 0; i < releases->len; i++) { + release = g_ptr_array_index (releases, i); + if (as_utils_vercmp (as_release_get_version (release), version) <= 0) + continue; + tmp = as_release_get_description (release, NULL); + if (tmp == NULL) + continue; + g_ptr_array_add (updates_list, release); + } + + /* no prefix on each release */ + if (updates_list->len == 1) { + release = g_ptr_array_index (updates_list, 0); + fu_device_set_update_description (item->device, + as_release_get_description (release, NULL)); + } else { + g_autoptr(GString) update_desc = NULL; + update_desc = g_string_new (""); + + /* get the descriptions with a version prefix */ + for (guint i = 0; i < updates_list->len; i++) { + release = g_ptr_array_index (updates_list, i); + g_string_append_printf (update_desc, + "

%s:

%s", + as_release_get_version (release), + as_release_get_description (release, NULL)); + } + if (update_desc->len > 0) + fu_device_set_update_description (item->device, update_desc->str); + } + + /* success */ + return TRUE; +} + +/** + * fu_engine_read_from_fd: + * @fd: A file descriptor + * @count: The maximum number of bytes to read + * @error: A #GError, or %NULL + * + * Reads a blob from a specific file descriptor. + * + * Note: this will close the fd when done + * + * Returns: (transfer container): a #GBytes, or %NULL + **/ +GBytes * +fu_engine_read_from_fd (gint fd, gsize count, GError **error) // FIXME: MOVE? +{ + g_autoptr(GBytes) blob = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(GInputStream) stream = NULL; + + g_return_val_if_fail (fd > 0, NULL); + g_return_val_if_fail (count > 0, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* read the entire fd to a data blob */ + stream = g_unix_input_stream_new (fd, TRUE); + blob = g_input_stream_read_bytes (stream, count, NULL, &error_local); + if (blob == NULL) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + error_local->message); + return NULL; + } + return g_steal_pointer (&blob); +} + +/** + * fu_engine_get_store_from_blob: + * @self: A #FuEngine + * @blob_cab: A #GBytes + * @error: A #GError, or %NULL + * + * Creates an AppStream store from a .cab file blob. + * + * Returns: (transfer container): a #AsStore, or %NULL + **/ +AsStore * +fu_engine_get_store_from_blob (FuEngine *self, GBytes *blob_cab, GError **error) +{ + g_autofree gchar *checksum = NULL; + g_autoptr(AsStore) store = NULL; + g_autoptr(GError) error_local = NULL; + + g_return_val_if_fail (FU_IS_ENGINE (self), NULL); + g_return_val_if_fail (blob_cab != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* load file */ + store = as_store_new (); + fu_engine_set_status (self, FWUPD_STATUS_DECOMPRESSING); + if (!as_store_from_bytes (store, blob_cab, NULL, &error_local)) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + error_local->message); + return NULL; + } + + /* get a checksum of the file and use it as the origin */ + checksum = g_compute_checksum_for_data (G_CHECKSUM_SHA256, + g_bytes_get_data (blob_cab, NULL), + g_bytes_get_size (blob_cab)); + as_store_set_origin (store, checksum); + + return g_steal_pointer (&store); +} + +static FwupdResult * +fu_engine_get_result_from_app (FuEngine *self, AsApp *app, GError **error) +{ + FwupdTrustFlags trust_flags = FWUPD_TRUST_FLAG_NONE; + AsRelease *release; + FwupdDevice *dev; + FwupdRelease *rel; + GPtrArray *provides; + g_autoptr(FwupdResult) res = NULL; + + res = fwupd_result_new (); + dev = fwupd_result_get_device (res); + provides = as_app_get_provides (app); + for (guint i = 0; i < provides->len; i++) { + AsProvide *prov = AS_PROVIDE (g_ptr_array_index (provides, i)); + FuDeviceItem *item; + const gchar *guid; + + /* not firmware */ + if (as_provide_get_kind (prov) != AS_PROVIDE_KIND_FIRMWARE_FLASHED) + continue; + + /* is a online or offline update appropriate */ + guid = as_provide_get_value (prov); + if (guid == NULL) + continue; + item = fu_engine_get_item_by_guid (self, guid); + if (item != NULL) { + fwupd_device_set_flags (dev, fu_device_get_flags (item->device)); + fwupd_device_set_id (dev, fu_device_get_id (item->device)); + } + + /* add GUID */ + fwupd_device_add_guid (dev, guid); + } + if (fwupd_device_get_guids(dev)->len == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "component has no GUIDs"); + return NULL; + } + + /* check we can install it */ + if (!fu_engine_check_requirements (app, NULL, error)) + return NULL; + + /* verify trust */ + release = as_app_get_release_default (app); + if (!fu_engine_get_release_trust_flags (release, &trust_flags, error)) + return NULL; + fwupd_result_set_update_trust_flags (res, trust_flags); + + /* possibly convert the version from 0x to dotted */ + fu_engine_vendor_quirk_release_version (app); + + /* possibly convert the flashed provide to a GUID */ + fu_engine_vendor_fixup_provide_value (app); + + /* create a result with all the metadata in */ + fwupd_device_set_description (dev, as_app_get_description (app, NULL)); + rel = fwupd_result_get_release (res); + fwupd_release_set_homepage (rel, as_app_get_url_item (app, AS_URL_KIND_HOMEPAGE)); + fwupd_release_set_license (rel, as_app_get_project_license (app)); + fwupd_release_set_name (rel, as_app_get_name (app, NULL)); + fwupd_release_set_summary (rel, as_app_get_comment (app, NULL)); + fwupd_release_set_vendor (rel, as_app_get_developer_name (app, NULL)); +#if AS_CHECK_VERSION(0,6,1) + fwupd_result_set_unique_id (res, as_app_get_unique_id (app)); +#else + fwupd_result_set_unique_id (res, as_app_get_id (app)); +#endif + fwupd_release_set_appstream_id (rel, as_app_get_id (app)); + fu_engine_set_release_from_item (rel, release); + return g_steal_pointer (&res); +} + +/** + * fu_engine_get_details_local: + * @self: A #FuEngine + * @fd: A file descriptor + * @error: A #GError, or %NULL + * + * Gets the details about a local file. + * + * Note: this will close the fd when done + * + * Returns: (transfer container) (element-type FwupdResult): results + **/ +GPtrArray * +fu_engine_get_details_local (FuEngine *self, gint fd, GError **error) +{ + GPtrArray *apps; + g_autoptr(AsStore) store = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GPtrArray) details = NULL; + + g_return_val_if_fail (FU_IS_ENGINE (self), NULL); + g_return_val_if_fail (fd > 0, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* get all apps */ + blob = fu_engine_read_from_fd (fd, FU_ENGINE_FIRMWARE_SIZE_MAX, error); + if (blob == NULL) + return NULL; + store = fu_engine_get_store_from_blob (self, blob, error); + if (store == NULL) + return NULL; + apps = as_store_get_apps (store); + if (apps->len == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no components"); + return NULL; + } + + /* create results with all the metadata in */ + details = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + for (guint i = 0; i < apps->len; i++) { + AsApp *app = g_ptr_array_index (apps, i); + FwupdResult *res = NULL; + + /* check we can install it */ + if (!fu_engine_check_requirements (app, NULL, error)) + return NULL; + + as_app_set_origin (app, as_store_get_origin (store)); + res = fu_engine_get_result_from_app (self, app, error); + if (res == NULL) + return NULL; + g_ptr_array_add (details, res); + } + return g_steal_pointer (&details); +} + +/** + * fu_engine_get_devices: + * @self: A #FuEngine + * @error: A #GError, or %NULL + * + * Gets the list of devices. + * + * Returns: (transfer container) (element-type FwupdDevice): results + **/ +GPtrArray * +fu_engine_get_devices (FuEngine *self, GError **error) +{ + GPtrArray *devices; + + g_return_val_if_fail (FU_IS_ENGINE (self), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + for (guint i = 0; i < self->devices->len; i++) { + FuDeviceItem *item; + item = g_ptr_array_index (self->devices, i); + g_ptr_array_add (devices, g_object_ref (item->device)); + } + if (devices->len == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "No detected devices"); + return NULL; + } + return devices; +} + +/** + * fu_engine_get_updates: + * @self: A #FuEngine + * @error: A #GError, or %NULL + * + * Gets the list of updates. + * + * Returns: (transfer container) (element-type FwupdDevice): results + **/ +GPtrArray * +fu_engine_get_updates (FuEngine *self, GError **error) +{ + GPtrArray *updates; + + g_return_val_if_fail (FU_IS_ENGINE (self), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + updates = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + for (guint i = 0; i < self->devices->len; i++) { + FuDeviceItem *item = g_ptr_array_index (self->devices, i); + if (fu_engine_get_updates_item_update (self, item)) + g_ptr_array_add (updates, g_object_ref (item->device)); + } + if (updates->len == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "No devices can be updated"); + return NULL; + } + return updates; +} + +/** + * fu_engine_get_remotes: + * @self: A #FuEngine + * @device_id: A device ID + * @error: A #GError, or %NULL + * + * Gets the list of remotes in use by the engine. + * + * Returns: (transfer container) (element-type FwupdRemote): results + **/ +GPtrArray * +fu_engine_get_remotes (FuEngine *self, GError **error) +{ + GPtrArray *remotes; + + g_return_val_if_fail (FU_IS_ENGINE (self), NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + remotes = fu_config_get_remotes (self->config); + if (remotes->len == 0) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "No remotes configured"); + return NULL; + } + return g_ptr_array_ref (remotes); +} + +/** + * fu_engine_get_releases: + * @self: A #FuEngine + * @device_id: A device ID + * @error: A #GError, or %NULL + * + * Gets the releases available for a specific device. + * + * Returns: (transfer container) (element-type FwupdResult): results + **/ +GPtrArray * +fu_engine_get_releases (FuEngine *self, const gchar *device_id, GError **error) +{ + FuDeviceItem *item; + GPtrArray *device_guids; + g_autoptr(GPtrArray) releases = NULL; + + g_return_val_if_fail (FU_IS_ENGINE (self), NULL); + g_return_val_if_fail (device_id != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* find the device */ + item = fu_engine_get_item_by_id (self, device_id, error); + if (item == NULL) + return NULL; + + /* get all the releases for the device */ + releases = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + device_guids = fu_device_get_guids (item->device); + for (guint i = 0; i < device_guids->len; i++) { + GPtrArray *releases_tmp; + const gchar *guid = g_ptr_array_index (device_guids, i); + AsApp *app = as_store_get_app_by_provide (self->store, + AS_PROVIDE_KIND_FIRMWARE_FLASHED, + guid); + if (app == NULL) + continue; + releases_tmp = as_app_get_releases (app); + for (guint j = 0; j < releases_tmp->len; j++) { + AsRelease *release = g_ptr_array_index (releases_tmp, j); + FwupdRelease *rel = fwupd_release_new (); + fu_engine_set_release_from_item (rel, release); + g_ptr_array_add (releases, g_object_ref (rel)); + } + } + + /* no devices */ + if (releases->len == 0) { + g_set_error_literal (error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "No releases for device"); + return NULL; + } + return g_steal_pointer (&releases); +} + +/** + * fu_engine_clear_results: + * @self: A #FuEngine + * @device_id: A device ID + * @error: A #GError, or %NULL + * + * Clear the historical state of a specific device operation. + * + * Returns: %TRUE for success + **/ +gboolean +fu_engine_clear_results (FuEngine *self, const gchar *device_id, GError **error) +{ + FuDeviceItem *item; + + g_return_val_if_fail (FU_IS_ENGINE (self), FALSE); + g_return_val_if_fail (device_id != NULL, FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* find the device */ + item = fu_engine_get_item_by_id_fallback_pending (self, device_id, error); + if (item == NULL) + return FALSE; + + /* call into the plugin */ + return fu_plugin_runner_clear_results (item->plugin, item->device, error); +} + +/** + * fu_engine_get_results: + * @self: A #FuEngine + * @device_id: A device ID + * @error: A #GError, or %NULL + * + * Gets the historical state of a specific device operation. + * + * Returns: (transfer container): a #FwupdResult, or %NULL + **/ +FwupdResult * +fu_engine_get_results (FuEngine *self, const gchar *device_id, GError **error) +{ + FuDeviceItem *item; + + g_return_val_if_fail (FU_IS_ENGINE (self), NULL); + g_return_val_if_fail (device_id != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + + /* find the device */ + item = fu_engine_get_item_by_id_fallback_pending (self, device_id, error); + if (item == NULL) + return NULL; + + /* call into the plugin */ + if (!fu_plugin_runner_get_results (item->plugin, item->device, error)) + return NULL; + + /* ensure the unique ID is set */ + if (fwupd_result_get_unique_id (FWUPD_RESULT (item->device)) == NULL) { + g_autofree gchar *id2 = NULL; + FwupdResult *res = FWUPD_RESULT (item->device); + FwupdDevice *dev = fwupd_result_get_device (res); +#if AS_CHECK_VERSION(0,6,1) + id2 = as_utils_unique_id_build (AS_APP_SCOPE_SYSTEM, + AS_BUNDLE_KIND_UNKNOWN, + NULL, + AS_APP_KIND_FIRMWARE, + fwupd_device_get_name (dev), + fwupd_device_get_version (dev)); +#else + id2 = g_strdup_printf ("system/*/*/firmware/%s/%s", + fwupd_device_get_name (dev), + fwupd_device_get_version (dev)); +#endif + fwupd_result_set_unique_id (res, id2); + } + return g_object_ref (item->device); +} + +static void +fu_engine_plugins_setup (FuEngine *self) +{ + g_autoptr(AsProfileTask) ptask = NULL; + + ptask = as_profile_start_literal (self->profile, "FuMain:setup"); + g_assert (ptask != NULL); + for (guint i = 0; i < self->plugins->len; i++) { + g_autoptr(GError) error = NULL; + g_autoptr(AsProfileTask) ptask2 = NULL; + FuPlugin *plugin = g_ptr_array_index (self->plugins, i); + ptask2 = as_profile_start (self->profile, + "FuMain:setup{%s}", + fu_plugin_get_name (plugin)); + g_assert (ptask2 != NULL); + if (!fu_plugin_runner_startup (plugin, &error)) { + fu_plugin_set_enabled (plugin, FALSE); + g_message ("disabling plugin because: %s", error->message); + } + } +} + +static void +fu_engine_plugins_coldplug (FuEngine *self) +{ + g_autoptr(AsProfileTask) ptask = NULL; + + /* don't allow coldplug to be scheduled when in coldplug */ + self->coldplug_running = TRUE; + + /* prepare */ + for (guint i = 0; i < self->plugins->len; i++) { + g_autoptr(GError) error = NULL; + FuPlugin *plugin = g_ptr_array_index (self->plugins, i); + if (!fu_plugin_runner_coldplug_prepare (plugin, &error)) + g_warning ("failed to prepare coldplug: %s", error->message); + } + + /* do this in one place */ + if (self->coldplug_delay > 0) { + g_debug ("sleeping for %ums", self->coldplug_delay); + g_usleep (self->coldplug_delay * 1000); + } + + /* exec */ + ptask = as_profile_start_literal (self->profile, "FuMain:coldplug"); + g_assert (ptask != NULL); + for (guint i = 0; i < self->plugins->len; i++) { + g_autoptr(GError) error = NULL; + g_autoptr(AsProfileTask) ptask2 = NULL; + FuPlugin *plugin = g_ptr_array_index (self->plugins, i); + ptask2 = as_profile_start (self->profile, + "FuMain:coldplug{%s}", + fu_plugin_get_name (plugin)); + g_assert (ptask2 != NULL); + if (!fu_plugin_runner_coldplug (plugin, &error)) { + fu_plugin_set_enabled (plugin, FALSE); + g_message ("disabling plugin because: %s", error->message); + } + } + + /* cleanup */ + for (guint i = 0; i < self->plugins->len; i++) { + g_autoptr(GError) error = NULL; + FuPlugin *plugin = g_ptr_array_index (self->plugins, i); + if (!fu_plugin_runner_coldplug_cleanup (plugin, &error)) + g_warning ("failed to cleanup coldplug: %s", error->message); + } + + /* we can recoldplug from this point on */ + self->coldplug_running = FALSE; +} + +static void +fu_engine_plugin_device_added_cb (FuPlugin *plugin, + FuDevice *device, + gpointer user_data) +{ + FuEngine *self = (FuEngine *) user_data; + FuDeviceItem *item; + GPtrArray *blacklisted_devices; + + /* device has no GUIDs set! */ + if (fu_device_get_guid_default (device) == NULL) { + g_warning ("no GUIDs for device %s [%s]", + fu_device_get_id (device), + fu_device_get_name (device)); + return; + } + + /* is this GUID blacklisted */ + blacklisted_devices = fu_config_get_blacklist_devices (self->config); + for (guint i = 0; i < blacklisted_devices->len; i++) { + const gchar *guid = g_ptr_array_index (blacklisted_devices, i); + if (g_strcmp0 (guid, fu_device_get_guid_default (device)) == 0) { + g_debug ("%s is blacklisted [%s], ignoring from %s", + fu_device_get_id (device), + fu_device_get_guid_default (device), + fu_plugin_get_name (plugin)); + return; + } + } + + /* remove any fake device */ + item = fu_engine_get_item_by_id (self, fu_device_get_id (device), NULL); + if (item != NULL) { + g_debug ("already added %s by %s, ignoring same device from %s", + fu_device_get_id (item->device), + fu_device_get_plugin (item->device), + fu_plugin_get_name (plugin)); + return; + } + + /* create new device */ + item = g_new0 (FuDeviceItem, 1); + item->device = g_object_ref (device); + item->plugin = g_object_ref (plugin); + g_ptr_array_add (self->devices, item); + + /* match the metadata at this point so clients can tell if the + * device is worthy */ + fu_engine_get_updates_item_update (self, item); + + /* notify clients */ + fu_engine_emit_device_added (self, item->device); + fu_engine_emit_changed (self); +} + +static void +fu_engine_plugin_device_removed_cb (FuPlugin *plugin, + FuDevice *device, + gpointer user_data) +{ + FuEngine *self = (FuEngine *) user_data; + FuDeviceItem *item; + g_autoptr(GError) error = NULL; + + item = fu_engine_get_item_by_id (self, fu_device_get_id (device), &error); + if (item == NULL) { + g_debug ("%s", error->message); + return; + } + + /* check this came from the same plugin */ + if (g_strcmp0 (fu_plugin_get_name (plugin), + fu_plugin_get_name (item->plugin)) != 0) { + g_debug ("ignoring duplicate removal from %s", + fu_plugin_get_name (plugin)); + return; + } + + /* make the UI update */ + fu_engine_emit_device_removed (self, device); + g_ptr_array_remove (self->devices, item); + fu_engine_emit_changed (self); +} + +static void +fu_engine_plugin_status_changed_cb (FuPlugin *plugin, + FwupdStatus status, + gpointer user_data) +{ + FuEngine *self = (FuEngine *) user_data; + fu_engine_set_status (self, status); +} + +static void +fu_engine_plugin_percentage_changed_cb (FuPlugin *plugin, + guint percentage, + gpointer user_data) +{ + FuEngine *self = (FuEngine *) user_data; + fu_engine_set_percentage (self, percentage); +} + +static gboolean +fu_engine_recoldplug_delay_cb (gpointer user_data) +{ + FuEngine *self = (FuEngine *) user_data; + g_debug ("performing a recoldplug"); + fu_engine_plugins_coldplug (self); + self->coldplug_id = 0; + return FALSE; +} + +static void +fu_engine_plugin_recoldplug_cb (FuPlugin *plugin, FuEngine *self) +{ + if (self->coldplug_running) { + g_warning ("coldplug already running, cannot recoldplug"); + return; + } + g_debug ("scheduling a recoldplug"); + if (self->coldplug_id != 0) + g_source_remove (self->coldplug_id); + self->coldplug_id = g_timeout_add (1500, fu_engine_recoldplug_delay_cb, self); +} + +static void +fu_engine_plugin_set_coldplug_delay_cb (FuPlugin *plugin, guint duration, FuEngine *self) +{ + self->coldplug_delay = MAX (self->coldplug_delay, duration); + g_debug ("got coldplug delay of %ums, global maximum is now %ums", + duration, self->coldplug_delay); +} + +static gboolean +fu_engine_load_hwids (FuEngine *self, GError **error) +{ + g_autoptr(FuHwids) hwids = fu_hwids_new (); + + /* read files in /sys */ + if (!fu_hwids_setup (hwids, NULL, error)) + return FALSE; + + /* add GUIDs */ + for (guint i = 0; i < 15; i++) { + g_autofree gchar *guid = NULL; + g_autofree gchar *key = NULL; + g_autofree gchar *values = NULL; + g_autoptr(GError) error_local = NULL; + + /* get the GUID and add to hash */ + key = g_strdup_printf ("HardwareID-%u", i); + guid = fu_hwids_get_guid (hwids, key, &error_local); + if (guid == NULL) { + g_debug ("%s is not available, %s", key, error_local->message); + continue; + } + g_hash_table_insert (self->hwids, + g_strdup (guid), + GUINT_TO_POINTER (1)); + + /* show what makes up the GUID */ + values = fu_hwids_get_replace_values (hwids, key, NULL); + g_debug ("{%s} <- %s", guid, values); + } + + return TRUE; +} + +static gboolean +fu_engine_load_plugins (FuEngine *self, GError **error) +{ + const gchar *fn; + g_autoptr(GDir) dir = NULL; + + /* search */ + dir = g_dir_open (PLUGINDIR, 0, error); + if (dir == NULL) + return FALSE; + while ((fn = g_dir_read_name (dir)) != NULL) { + GPtrArray *blacklist; + g_autofree gchar *filename = NULL; + g_autoptr(FuPlugin) plugin = NULL; + g_autoptr(GError) error_local = NULL; + + /* ignore non-plugins */ + if (!g_str_has_suffix (fn, ".so")) + continue; + + /* open module */ + filename = g_build_filename (PLUGINDIR, fn, NULL); + plugin = fu_plugin_new (); + fu_plugin_set_usb_context (plugin, self->usb_ctx); + fu_plugin_set_hwids (plugin, self->hwids); + g_debug ("adding plugin %s", filename); + if (!fu_plugin_open (plugin, filename, &error_local)) { + g_warning ("failed to open plugin %s: %s", + filename, error_local->message); + continue; + } + + /* is blacklisted */ + blacklist = fu_config_get_blacklist_plugins (self->config); + for (guint i = 0; i < blacklist->len; i++) { + const gchar *name = g_ptr_array_index (blacklist, i); + if (g_strcmp0 (name, fu_plugin_get_name (plugin)) == 0) { + fu_plugin_set_enabled (plugin, FALSE); + break; + } + } + if (!fu_plugin_get_enabled (plugin)) { + g_debug ("%s blacklisted by config", + fu_plugin_get_name (plugin)); + continue; + } + + /* watch for changes */ + g_signal_connect (plugin, "device-added", + G_CALLBACK (fu_engine_plugin_device_added_cb), + self); + g_signal_connect (plugin, "device-removed", + G_CALLBACK (fu_engine_plugin_device_removed_cb), + self); + g_signal_connect (plugin, "status-changed", + G_CALLBACK (fu_engine_plugin_status_changed_cb), + self); + g_signal_connect (plugin, "percentage-changed", + G_CALLBACK (fu_engine_plugin_percentage_changed_cb), + self); + g_signal_connect (plugin, "recoldplug", + G_CALLBACK (fu_engine_plugin_recoldplug_cb), + self); + g_signal_connect (plugin, "set-coldplug-delay", + G_CALLBACK (fu_engine_plugin_set_coldplug_delay_cb), + self); + + /* add */ + g_ptr_array_add (self->plugins, g_object_ref (plugin)); + g_hash_table_insert (self->plugins_hash, + g_strdup (fu_plugin_get_name (plugin)), + g_object_ref (plugin)); + } + + return TRUE; +} + +/** + * fu_engine_check_plugins_pending: + * @self: A #FuEngine + * @error: A #GError, or %NULL + * + * Checks if any plugins have pending devices to be added. + * + * Returns: %FALSE if any plugins have pending devices. + **/ +gboolean +fu_engine_check_plugins_pending (FuEngine *self, GError **error) +{ + g_return_val_if_fail (FU_IS_ENGINE (self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + for (guint i = 0; i < self->plugins->len; i++) { + FuPlugin *plugin = g_ptr_array_index (self->plugins, i); + if (fu_plugin_has_device_delay (plugin)) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "%s pending", + fu_plugin_get_name (plugin)); + return FALSE; + } + } + return TRUE; +} + +static gboolean +fu_engine_cleanup_state (GError **error) +{ + const gchar *filenames[] = { + "/var/cache/app-info/xmls/fwupd-verify.xml", + "/var/cache/app-info/xmls/fwupd.xml", + NULL }; + for (guint i = 0; filenames[i] != NULL; i++) { + g_autoptr(GFile) file = g_file_new_for_path (filenames[i]); + if (g_file_query_exists (file, NULL)) { + if (!g_file_delete (file, NULL, error)) + return FALSE; + } + } + return TRUE; +} + +/** + * fu_engine_load: + * @self: A #FuEngine + * @error: A #GError, or %NULL + * + * Load the firmware update engine so it is ready for use. + * + * Returns: %TRUE for success + **/ +gboolean +fu_engine_load (FuEngine *self, GError **error) +{ + g_return_val_if_fail (FU_IS_ENGINE (self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* read config file */ + if (!fu_config_load (self->config, error)) { + g_prefix_error (error, "Failed to load config: "); + return FALSE; + } + + /* load AppStream metadata */ + as_store_add_filter (self->store, AS_APP_KIND_FIRMWARE); + if (!fu_engine_load_metadata_store (self, error)) { + g_prefix_error (error, "Failed to load AppStream data: "); + return FALSE; + } + + /* set shared USB context */ + self->usb_ctx = g_usb_context_new (error); + if (self->usb_ctx == NULL) { + g_prefix_error (error, "Failed to get USB context: "); + return FALSE; + } + + /* load the hwids */ + if (!fu_engine_load_hwids (self, error)) { + g_prefix_error (error, "Failed to load hwids: "); + return FALSE; + } + + /* delete old data files */ + if (!fu_engine_cleanup_state (error)) { + g_prefix_error (error, "Failed to clean up: "); + return FALSE; + } + + /* load plugin */ + if (!fu_engine_load_plugins (self, error)) { + g_prefix_error (error, "Failed to load plugins: "); + return FALSE; + } + + /* disable udev? */ + if (!fu_config_get_enable_option_rom (self->config)) { + FuPlugin *plugin = g_hash_table_lookup (self->plugins_hash, "udev"); + if (plugin != NULL) + fu_plugin_set_enabled (plugin, FALSE); + } + + /* add devices */ + fu_engine_plugins_setup (self); + g_usb_context_enumerate (self->usb_ctx); + fu_engine_plugins_coldplug (self); + + /* success */ + return TRUE; +} + +static void +fu_engine_class_init (FuEngineClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = fu_engine_finalize; + + signals[SIGNAL_CHANGED] = + g_signal_new ("changed", + G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, + 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, + G_TYPE_NONE, 0); + signals[SIGNAL_DEVICE_ADDED] = + g_signal_new ("device-added", + G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, + 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, FU_TYPE_DEVICE); + signals[SIGNAL_DEVICE_REMOVED] = + g_signal_new ("device-removed", + G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, + 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT, + G_TYPE_NONE, 1, FU_TYPE_DEVICE); + signals[SIGNAL_STATUS_CHANGED] = + g_signal_new ("status-changed", + G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, + 0, NULL, NULL, g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, G_TYPE_UINT); + signals[SIGNAL_PERCENTAGE_CHANGED] = + g_signal_new ("percentage-changed", + G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, + 0, NULL, NULL, g_cclosure_marshal_VOID__UINT, + G_TYPE_NONE, 1, G_TYPE_UINT); +} + +static void +fu_engine_init (FuEngine *self) +{ + self->percentage = 0; + self->status = FWUPD_STATUS_IDLE; + self->config = fu_config_new (); + self->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_engine_item_free); + self->hwids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); + self->pending = fu_pending_new (); + self->profile = as_profile_new (); + self->store = as_store_new (); + self->plugins = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + self->plugins_hash = g_hash_table_new_full (g_str_hash, g_str_equal, + g_free, (GDestroyNotify) g_object_unref); +} + +static void +fu_engine_finalize (GObject *obj) +{ + FuEngine *self = FU_ENGINE (obj); + + if (self->usb_ctx != NULL) + g_object_unref (self->usb_ctx); + if (self->coldplug_id != 0) + g_source_remove (self->coldplug_id); + + g_hash_table_unref (self->hwids); + g_hash_table_unref (self->plugins_hash); + g_object_unref (self->config); + g_object_unref (self->pending); + g_object_unref (self->profile); + g_object_unref (self->store); + g_ptr_array_unref (self->devices); + g_ptr_array_unref (self->plugins); + + G_OBJECT_CLASS (fu_engine_parent_class)->finalize (obj); +} + +FuEngine * +fu_engine_new (void) +{ + FuEngine *self; + self = g_object_new (FU_TYPE_ENGINE, NULL); + return FU_ENGINE (self); +} diff --git a/src/fu-engine.h b/src/fu-engine.h new file mode 100644 index 000000000..0afa13d0e --- /dev/null +++ b/src/fu-engine.h @@ -0,0 +1,99 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- + * + * Copyright (C) 2017 Richard Hughes + * + * Licensed under the GNU Lesser General Public License Version 2.1 + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef __FU_ENGINE_H +#define __FU_ENGINE_H + +G_BEGIN_DECLS + +#include +#include + +#include "fwupd-result.h" +#include "fwupd-enums.h" + +#define FU_TYPE_ENGINE (fu_engine_get_type ()) +G_DECLARE_FINAL_TYPE (FuEngine, fu_engine, FU, ENGINE, GObject) + +#define FU_ENGINE_FIRMWARE_SIZE_MAX (32 * 1024 * 1024) /* bytes */ + +FuEngine *fu_engine_new (void); +gboolean fu_engine_load (FuEngine *self, + GError **error); +FwupdStatus fu_engine_get_status (FuEngine *self); +void fu_engine_profile_dump (FuEngine *self); +gboolean fu_engine_check_plugins_pending (FuEngine *self, + GError **error); +GBytes *fu_engine_read_from_fd (gint fd, + gsize count, + GError **error); +AsStore *fu_engine_get_store_from_blob (FuEngine *self, + GBytes *blob, + GError **error); +const gchar *fu_engine_get_action_id_for_device (FuEngine *self, + const gchar *device_id, + AsStore *store, + FwupdInstallFlags flags, + GError **error); + +GPtrArray *fu_engine_get_devices (FuEngine *self, + GError **error); +GPtrArray *fu_engine_get_updates (FuEngine *self, + GError **error); +GPtrArray *fu_engine_get_remotes (FuEngine *self, + GError **error); +GPtrArray *fu_engine_get_releases (FuEngine *self, + const gchar *device_id, + GError **error); +FwupdResult *fu_engine_get_results (FuEngine *self, + const gchar *device_id, + GError **error); +gboolean fu_engine_clear_results (FuEngine *self, + const gchar *device_id, + GError **error); +gboolean fu_engine_update_metadata (FuEngine *self, + const gchar *remote_id, + gint fd, + gint fd_sig, + GError **error); +gboolean fu_engine_unlock (FuEngine *self, + const gchar *device_id, + GError **error); +gboolean fu_engine_verify (FuEngine *self, + const gchar *device_id, + GError **error); +gboolean fu_engine_verify_update (FuEngine *self, + const gchar *device_id, + GError **error); +gboolean fu_engine_install (FuEngine *self, + const gchar *device_id, + AsStore *store, + GBytes *blob_cab, + FwupdInstallFlags flags, + GError **error); +GPtrArray *fu_engine_get_details_local (FuEngine *self, + gint fd, + GError **error); + +G_END_DECLS + +#endif /* __FU_ENGINE_H */ + diff --git a/src/fu-main.c b/src/fu-main.c index f823ccc45..48ff5db10 100644 --- a/src/fu-main.c +++ b/src/fu-main.c @@ -21,73 +21,39 @@ #include "config.h" -#include #include -#include -#include +#include #include -#include #include #include #include #include -#include -#include "fwupd-common-private.h" -#include "fwupd-enums-private.h" #include "fwupd-release-private.h" #include "fwupd-remote-private.h" #include "fwupd-resources.h" -#include "fu-config.h" #include "fu-debug.h" #include "fu-device.h" -#include "fu-hwids.h" -#include "fu-plugin-private.h" -#include "fu-keyring.h" -#include "fu-pending.h" -#include "fu-plugin.h" -#include "fu-quirks.h" +#include "fu-engine.h" #ifndef HAVE_POLKIT_0_114 G_DEFINE_AUTOPTR_CLEANUP_FUNC(PolkitAuthorizationResult, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC(PolkitSubject, g_object_unref) #endif -#define FU_MAIN_FIRMWARE_SIZE_MAX (32 * 1024 * 1024) /* bytes */ - typedef struct { GDBusConnection *connection; GDBusNodeInfo *introspection_daemon; GDBusProxy *proxy_uid; - GUsbContext *usb_ctx; - FuConfig *config; GMainLoop *loop; - GPtrArray *devices; /* of FuDeviceItem */ PolkitAuthority *authority; - FwupdStatus status; - guint percentage; - FuPending *pending; - AsProfile *profile; - AsStore *store; guint owner_id; - gboolean coldplug_running; - guint coldplug_id; - guint coldplug_delay; - GPtrArray *plugins; /* of FuPlugin */ - GHashTable *plugins_hash; /* of name : FuPlugin */ - GHashTable *hwids; /* of hwid : 1 */ + FuEngine *engine; } FuMainPrivate; -typedef struct { - FuDevice *device; - FuPlugin *plugin; -} FuDeviceItem; - -static gboolean fu_main_get_updates_item_update (FuMainPrivate *priv, FuDeviceItem *item); - static void -fu_main_emit_changed (FuMainPrivate *priv) +fu_main_engine_changed_cb (FuEngine *engine, FuMainPrivate *priv) { /* not yet connected */ if (priv->connection == NULL) @@ -101,7 +67,9 @@ fu_main_emit_changed (FuMainPrivate *priv) } static void -fu_main_emit_device_added (FuMainPrivate *priv, FuDevice *device) +fu_main_engine_device_added_cb (FuEngine *engine, + FuDevice *device, + FuMainPrivate *priv) { GVariant *val; @@ -118,7 +86,9 @@ fu_main_emit_device_added (FuMainPrivate *priv, FuDevice *device) } static void -fu_main_emit_device_removed (FuMainPrivate *priv, FuDevice *device) +fu_main_engine_device_removed_cb (FuEngine *engine, + FuDevice *device, + FuMainPrivate *priv) { GVariant *val; @@ -135,7 +105,9 @@ fu_main_emit_device_removed (FuMainPrivate *priv, FuDevice *device) } static void -fu_main_emit_device_changed (FuMainPrivate *priv, FuDevice *device) +fu_main_engine_device_changed_cb (FuEngine *engine, + FuDevice *device, + FuMainPrivate *priv) { GVariant *val; @@ -184,1712 +156,213 @@ fu_main_emit_property_changed (FuMainPrivate *priv, g_variant_builder_clear (&invalidated_builder); } -static void -fu_main_set_status (FuMainPrivate *priv, FwupdStatus status) -{ - if (priv->status == status) - return; - priv->status = status; - /* emit changed */ +static void +fu_main_engine_status_changed_cb (FuEngine *engine, + FwupdStatus status, + FuMainPrivate *priv) +{ g_debug ("Emitting PropertyChanged('Status'='%s')", fwupd_status_to_string (status)); - fu_main_emit_property_changed (priv, "Status", g_variant_new_uint32 (status)); + fu_main_emit_property_changed (priv, "Status", + g_variant_new_uint32 (status)); } static void -fu_main_set_percentage (FuMainPrivate *priv, guint percentage) +fu_main_engine_percentage_changed_cb (FuEngine *engine, + guint percentage, + FuMainPrivate *priv) { - if (priv->percentage == percentage) - return; - priv->percentage = percentage; - - /* emit changed */ g_debug ("Emitting PropertyChanged('Percentage'='%u%%')", percentage); fu_main_emit_property_changed (priv, "Percentage", g_variant_new_uint32 (percentage)); } static GVariant * -fu_main_device_array_to_variant (GPtrArray *devices, GError **error) +fu_main_device_array_to_variant (GPtrArray *devices) { GVariantBuilder builder; - - /* no devices */ - if (devices->len == 0) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOTHING_TO_DO, - "Nothing to do"); - return NULL; - } - + g_return_val_if_fail (devices->len > 0, NULL); g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY); for (guint i = 0; i < devices->len; i++) { - GVariant *tmp; - FuDeviceItem *item; - item = g_ptr_array_index (devices, i); - tmp = fwupd_result_to_data (FWUPD_RESULT (item->device), "{sa{sv}}"); + FuDevice *device = g_ptr_array_index (devices, i); + GVariant *tmp = fwupd_result_to_data (FWUPD_RESULT (device), "{sa{sv}}"); g_variant_builder_add_value (&builder, tmp); } return g_variant_new ("(a{sa{sv}})", &builder); } -static void -fu_main_invocation_return_value (FuMainPrivate *priv, - GDBusMethodInvocation *invocation, - GVariant *parameters) +static GVariant * +fu_main_release_array_to_variant (GPtrArray *results) { - fu_main_set_status (priv, FWUPD_STATUS_IDLE); - g_dbus_method_invocation_return_value (invocation, parameters); + GVariantBuilder builder; + g_return_val_if_fail (results->len > 0, NULL); + g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY); + for (guint i = 0; i < results->len; i++) { + FwupdRelease *rel = g_ptr_array_index (results, i); + GVariant *tmp = fwupd_release_to_data (rel, "a{sv}"); + g_variant_builder_add_value (&builder, tmp); + } + return g_variant_new ("(aa{sv})", &builder); } -static void -fu_main_invocation_return_error (FuMainPrivate *priv, - GDBusMethodInvocation *invocation, - const GError *error) +static GVariant * +fu_main_remote_array_to_variant (GPtrArray *remotes) { - fu_main_set_status (priv, FWUPD_STATUS_IDLE); - g_dbus_method_invocation_return_gerror (invocation, error); + GVariantBuilder builder; + g_return_val_if_fail (remotes->len > 0, NULL); + g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY); + for (guint i = 0; i < remotes->len; i++) { + FwupdRemote *remote = g_ptr_array_index (remotes, i); + GVariant *tmp = fwupd_remote_to_data (remote, "a{sv}"); + g_variant_builder_add_value (&builder, tmp); + } + return g_variant_new ("(aa{sv})", &builder); } -static void -fu_main_item_free (FuDeviceItem *item) +static GVariant * +fu_main_result_array_to_variant (GPtrArray *results) { - g_object_unref (item->device); - g_object_unref (item->plugin); - g_free (item); + GVariantBuilder builder; + g_return_val_if_fail (results->len > 0, NULL); + g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY); + for (guint i = 0; i < results->len; i++) { + FwupdResult *result = g_ptr_array_index (results, i); + GVariant *tmp = fwupd_result_to_data (result, "{sa{sv}}"); + g_variant_builder_add_value (&builder, tmp); + } + return g_variant_new ("(a{sa{sv}})", &builder); } -static FuDeviceItem * -fu_main_get_item_by_id (FuMainPrivate *priv, const gchar *id) -{ - for (guint i = 0; i < priv->devices->len; i++) { - FuDeviceItem *item = g_ptr_array_index (priv->devices, i); - if (g_strcmp0 (fu_device_get_id (item->device), id) == 0) - return item; - if (g_strcmp0 (fu_device_get_equivalent_id (item->device), id) == 0) - return item; - } - return NULL; -} - -static FuDeviceItem * -fu_main_get_item_by_guid (FuMainPrivate *priv, const gchar *guid) -{ - for (guint i = 0; i < priv->devices->len; i++) { - FuDeviceItem *item = g_ptr_array_index (priv->devices, i); - if (fu_device_has_guid (item->device, guid)) - return item; - } - return NULL; -} - -static FuPlugin * -fu_main_get_plugin_by_name (FuMainPrivate *priv, const gchar *name) -{ - for (guint i = 0; i < priv->plugins->len; i++) { - FuPlugin *plugin = g_ptr_array_index (priv->plugins, i); - if (g_strcmp0 (fu_plugin_get_name (plugin), name) == 0) - return plugin; - } - return NULL; -} - -static const gchar * -fu_main_get_sysconfig_dir (void) -{ - if (g_file_test (SYSCONFDIR, G_FILE_TEST_EXISTS)) - return SYSCONFDIR; - return "/etc"; -} - -static void -fu_main_set_release_from_item (FwupdRelease *rel, AsRelease *release) -{ - AsChecksum *csum; - const gchar *tmp; - - tmp = as_release_get_version (release); - if (tmp != NULL) - fwupd_release_set_version (rel, tmp); - tmp = as_release_get_description (release, NULL); - if (tmp != NULL) - fwupd_release_set_description (rel, tmp); - tmp = as_release_get_location_default (release); - if (tmp != NULL) - fwupd_release_set_uri (rel, tmp); - csum = as_release_get_checksum_by_target (release, AS_CHECKSUM_TARGET_CONTENT); - if (csum != NULL) { - tmp = as_checksum_get_filename (csum); - if (tmp != NULL) - fwupd_release_set_filename (rel, tmp); - } - csum = as_release_get_checksum_by_target (release, AS_CHECKSUM_TARGET_CONTAINER); - if (csum != NULL) { - tmp = as_checksum_get_value (csum); - if (tmp != NULL) - fwupd_release_add_checksum (rel, tmp); - } - fwupd_release_set_size (rel, as_release_get_size (release, AS_SIZE_KIND_INSTALLED)); -} - -static gboolean -fu_main_get_release_trust_flags (AsRelease *release, - FwupdTrustFlags *trust_flags, - GError **error) -{ - AsChecksum *csum_tmp; - GBytes *blob_payload; - GBytes *blob_signature; - const gchar *fn; - g_autofree gchar *pki_dir = NULL; - g_autofree gchar *fn_signature = NULL; - g_autoptr(GError) error_local = NULL; - g_autoptr(FuKeyring) kr = NULL; - - /* no filename? */ - csum_tmp = as_release_get_checksum_by_target (release, AS_CHECKSUM_TARGET_CONTENT); - fn = as_checksum_get_filename (csum_tmp); - if (fn == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "no filename"); - return FALSE; - } - - /* no signature == no trust */ - fn_signature = g_strdup_printf ("%s.asc", fn); - blob_signature = as_release_get_blob (release, fn_signature); - if (blob_signature == NULL) { - g_debug ("firmware archive contained no GPG signature"); - return TRUE; - } - - /* get payload */ - blob_payload = as_release_get_blob (release, fn); - if (blob_payload == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "no payload"); - return FALSE; - } - - /* check we were installed correctly */ - pki_dir = g_build_filename (fu_main_get_sysconfig_dir (), "pki", "fwupd", NULL); - if (!g_file_test (pki_dir, G_FILE_TEST_EXISTS)) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "PKI directory %s not found", pki_dir); - return FALSE; - } - - /* verify against the system trusted keys */ - kr = fu_keyring_new (); - if (!fu_keyring_add_public_keys (kr, pki_dir, error)) - return FALSE; - if (!fu_keyring_verify_data (kr, blob_payload, blob_signature, &error_local)) { - g_warning ("untrusted as failed to verify: %s", - error_local->message); - return TRUE; - } - - /* awesome! */ - g_debug ("marking payload as trusted"); - *trust_flags |= FWUPD_TRUST_FLAG_PAYLOAD; - return TRUE; -} - -typedef enum { - FU_MAIN_AUTH_KIND_UNKNOWN, - FU_MAIN_AUTH_KIND_INSTALL, - FU_MAIN_AUTH_KIND_UNLOCK, - FU_MAIN_AUTH_KIND_VERIFY_UPDATE, - FU_MAIN_AUTH_KIND_LAST -} FuMainAuthKind; - typedef struct { GDBusMethodInvocation *invocation; AsStore *store; - FwupdTrustFlags trust_flags; - GPtrArray *devices; /* of FuDevice */ - GPtrArray *blob_fws; /* of GBytes */ FwupdInstallFlags flags; GBytes *blob_cab; - gboolean is_downgrade; - FuMainAuthKind auth_kind; FuMainPrivate *priv; + gchar *device_id; } FuMainAuthHelper; static void -fu_main_helper_free (FuMainAuthHelper *helper) +fu_main_auth_helper_free (FuMainAuthHelper *helper) { - /* free */ - if (helper->devices != NULL) - g_ptr_array_unref (helper->devices); - if (helper->blob_fws != NULL) - g_ptr_array_unref (helper->blob_fws); if (helper->blob_cab != NULL) g_bytes_unref (helper->blob_cab); if (helper->store != NULL) g_object_unref (helper->store); + g_free (helper->device_id); g_object_unref (helper->invocation); g_free (helper); } +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuMainAuthHelper, fu_main_auth_helper_free) + +/* error may or may not already have been set */ static gboolean -fu_main_plugin_unlock_authenticated (FuMainAuthHelper *helper, GError **error) +fu_main_authorization_is_valid (PolkitAuthorizationResult *auth, GError **error) { - /* check the devices still exists */ - for (guint i = 0; i < helper->devices->len; i ++) { - FuDeviceItem *item; - FuDevice *device = g_ptr_array_index (helper->devices, i); - - item = fu_main_get_item_by_id (helper->priv, - fu_device_get_id (device)); - if (item == NULL) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "device %s was removed", - fu_device_get_id (device)); - return FALSE; - } - - /* run the correct plugin that added this */ - if (!fu_plugin_runner_unlock (item->plugin, - item->device, - error)) - return FALSE; - - /* make the UI update */ - fu_main_emit_device_changed (helper->priv, item->device); - } - - /* make the UI update */ - fu_main_emit_changed (helper->priv); - - return TRUE; -} - -static AsApp * -fu_main_verify_update_device_to_app (FuDevice *device) -{ - AsApp *app = NULL; - GPtrArray *checksums; - g_autofree gchar *id = NULL; - g_autoptr(AsProvide) prov = NULL; - g_autoptr(AsRelease) rel = NULL; - - /* make a plausible ID */ - id = g_strdup_printf ("%s.firmware", fu_device_get_guid_default (device)); - - /* add app to store */ - app = as_app_new (); - as_app_set_id (app, id); - as_app_set_kind (app, AS_APP_KIND_FIRMWARE); - rel = as_release_new (); - as_release_set_version (rel, fu_device_get_version (device)); - checksums = fu_device_get_checksums (device); - for (guint j = 0; j < checksums->len; j++) { - const gchar *checksum = g_ptr_array_index (checksums, j); - g_autoptr(AsChecksum) csum = as_checksum_new (); - as_checksum_set_kind (csum, fwupd_checksum_guess_kind (checksum)); - as_checksum_set_value (csum, checksum); - as_checksum_set_target (csum, AS_CHECKSUM_TARGET_CONTENT); - as_release_add_checksum (rel, csum); - } - as_app_add_release (app, rel); - prov = as_provide_new (); - as_provide_set_kind (prov, AS_PROVIDE_KIND_FIRMWARE_FLASHED); - as_provide_set_value (prov, fu_device_get_guid_default (device)); - as_app_add_provide (app, prov); - return app; -} - -static AsStore * -fu_main_load_verify_store (GError **error) -{ - const gchar *fn = "/var/lib/fwupd/verify.xml"; - g_autoptr(AsStore) store = NULL; - g_autoptr(GFile) file = NULL; - - /* load existing store */ - store = as_store_new (); - as_store_set_api_version (store, 0.9); - file = g_file_new_for_path (fn); - if (g_file_query_exists (file, NULL)) { - if (!as_store_from_file (store, file, NULL, NULL, error)) - return NULL; - } - return g_steal_pointer (&store); -} - -static gboolean -fu_main_plugin_verify_update_authenticated (FuMainAuthHelper *helper, GError **error) -{ - const gchar *fn = "/var/lib/fwupd/verify.xml"; - g_autoptr(AsStore) store = NULL; - g_autoptr(GFile) file = NULL; - - /* load existing store */ - store = fu_main_load_verify_store (error); - if (store == NULL) - return FALSE; - - /* check the devices still exists */ - for (guint i = 0; i < helper->devices->len; i ++) { - FuDevice *device = g_ptr_array_index (helper->devices, i); - FuDeviceItem *item; - GPtrArray *checksums; - g_autoptr(AsApp) app = NULL; - - item = fu_main_get_item_by_id (helper->priv, - fu_device_get_id (device)); - if (item == NULL) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "device %s was removed", - fu_device_get_id (device)); - return FALSE; - } - - /* unlock device if required */ - if (fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_LOCKED)) { - if (!fu_plugin_runner_unlock (item->plugin, - item->device, - error)) - return FALSE; - fu_main_emit_device_changed (helper->priv, item->device); - } - - /* get the checksum */ - checksums = fu_device_get_checksums (item->device); - if (checksums->len == 0) { - if (!fu_plugin_runner_verify (item->plugin, - item->device, - FU_PLUGIN_VERIFY_FLAG_NONE, - error)) - return FALSE; - fu_main_emit_device_changed (helper->priv, item->device); - } - - /* we got nothing */ - if (checksums->len == 0) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "device verification not supported"); - return FALSE; - } - - /* add to store */ - app = fu_main_verify_update_device_to_app (item->device); - as_store_add_app (store, app); - } - - /* write */ - g_debug ("writing %s", fn); - file = g_file_new_for_path (fn); - return as_store_to_file (store, file, - AS_NODE_TO_XML_FLAG_ADD_HEADER | - AS_NODE_TO_XML_FLAG_FORMAT_INDENT | - AS_NODE_TO_XML_FLAG_FORMAT_MULTILINE, - NULL, error); -} - -static gboolean -fu_main_plugin_update_authenticated (FuMainAuthHelper *helper, GError **error) -{ - FuMainPrivate *priv = helper->priv; - FuDeviceItem *item; - - /* check the devices still exists */ - for (guint i = 0; i < helper->devices->len; i ++) { - FuDevice *device = g_ptr_array_index (helper->devices, i); - item = fu_main_get_item_by_id (helper->priv, - fu_device_get_id (device)); - if (item == NULL) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "device %s was removed", - fu_device_get_id (device)); - return FALSE; - } - - /* Called with online update, test if device is supposed to allow this */ - if (!(helper->flags & FWUPD_INSTALL_FLAG_OFFLINE) && - !fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_ALLOW_ONLINE)) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Device %s does not allow online updates", - fu_device_get_id (device)); - return FALSE; - } - /* Called with offline update, test if device is supposed to allow this */ - if (helper->flags & FWUPD_INSTALL_FLAG_OFFLINE && - !fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_ALLOW_OFFLINE)) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Device %s does not allow offline updates", - fu_device_get_id (device)); - return FALSE; - } - } - - /* run the correct plugin for each device */ - for (guint i = 0; i < helper->devices->len; i ++) { - FuDevice *device = g_ptr_array_index (helper->devices, i); - GBytes *blob_fw = g_ptr_array_index (helper->blob_fws, i); - item = fu_main_get_item_by_id (helper->priv, - fu_device_get_id (device)); - - /* signal to all the plugins the update is about to happen */ - for (guint j = 0; j < priv->plugins->len; j++) { - FuPlugin *plugin = g_ptr_array_index (priv->plugins, j); - if (!fu_plugin_runner_update_prepare (plugin, device, error)) - return FALSE; - } - - /* do the update */ - if (!fu_plugin_runner_update (item->plugin, - item->device, - helper->blob_cab, - blob_fw, - helper->flags, - error)) { - for (guint j = 0; j < priv->plugins->len; j++) { - FuPlugin *plugin = g_ptr_array_index (priv->plugins, j); - g_autoptr(GError) error_local = NULL; - if (!fu_plugin_runner_update_cleanup (plugin, - device, - &error_local)) { - g_warning ("failed to update-cleanup " - "after failed update: %s", - error_local->message); - } - } - return FALSE; - } - - /* signal to all the plugins the update has happened */ - for (guint j = 0; j < priv->plugins->len; j++) { - FuPlugin *plugin = g_ptr_array_index (priv->plugins, j); - g_autoptr(GError) error_local = NULL; - if (!fu_plugin_runner_update_cleanup (plugin, device, &error_local)) { - g_warning ("failed to update-cleanup: %s", - error_local->message); - } - } - - /* make the UI update */ - fu_device_set_modified (item->device, (guint64) g_get_real_time () / G_USEC_PER_SEC); - fu_main_emit_device_changed (helper->priv, item->device); - } - - /* make the UI update */ - fu_main_emit_changed (helper->priv); - return TRUE; -} - -static void -fu_main_check_authorization_cb (GObject *source, GAsyncResult *res, gpointer user_data) -{ - FuMainAuthHelper *helper = (FuMainAuthHelper *) user_data; - g_autoptr(GError) error = NULL; - g_autoptr(GError) error_local = NULL; - g_autoptr(PolkitAuthorizationResult) auth = NULL; - - /* get result */ - auth = polkit_authority_check_authorization_finish (POLKIT_AUTHORITY (source), - res, &error_local); + /* failed */ if (auth == NULL) { - g_set_error (&error, + g_autofree gchar *message = g_strdup ((*error)->message); + g_clear_error (error); + g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_AUTH_FAILED, - "could not check for auth: %s", - error_local->message); - fu_main_invocation_return_error (helper->priv, helper->invocation, error); - fu_main_helper_free (helper); - return; + "Could not check for auth: %s", message); + return FALSE; } /* did not auth */ if (!polkit_authorization_result_get_is_authorized (auth)) { - g_set_error_literal (&error, + g_set_error_literal (error, FWUPD_ERROR, FWUPD_ERROR_AUTH_FAILED, - "failed to obtain auth"); - fu_main_invocation_return_error (helper->priv, helper->invocation, error); - fu_main_helper_free (helper); - return; - } - - /* we're good to go */ - if (helper->auth_kind == FU_MAIN_AUTH_KIND_INSTALL) { - if (!fu_main_plugin_update_authenticated (helper, &error)) { - fu_main_invocation_return_error (helper->priv, - helper->invocation, - error); - fu_main_helper_free (helper); - return; - } - } else if (helper->auth_kind == FU_MAIN_AUTH_KIND_UNLOCK) { - if (!fu_main_plugin_unlock_authenticated (helper, &error)) { - fu_main_invocation_return_error (helper->priv, - helper->invocation, - error); - fu_main_helper_free (helper); - return; - } - } else if (helper->auth_kind == FU_MAIN_AUTH_KIND_VERIFY_UPDATE) { - if (!fu_main_plugin_verify_update_authenticated (helper, &error)) { - fu_main_invocation_return_error (helper->priv, - helper->invocation, - error); - fu_main_helper_free (helper); - return; - } - } else { - g_assert_not_reached (); + "Failed to obtain auth"); + return FALSE; } /* success */ - fu_main_invocation_return_value (helper->priv, helper->invocation, NULL); - fu_main_helper_free (helper); -} - -static gchar * -fu_main_get_guids_from_store (AsStore *store) -{ - AsProvide *prov; - GPtrArray *provides; - GPtrArray *apps; - GString *str = g_string_new (""); - - /* return a string with all the firmware apps in the store */ - apps = as_store_get_apps (store); - for (guint i = 0; i < apps->len; i++) { - AsApp *app = AS_APP (g_ptr_array_index (apps, i)); - provides = as_app_get_provides (app); - for (guint j = 0; j < provides->len; j++) { - prov = AS_PROVIDE (g_ptr_array_index (provides, j)); - if (as_provide_get_kind (prov) != AS_PROVIDE_KIND_FIRMWARE_FLASHED) - continue; - g_string_append_printf (str, "%s,", as_provide_get_value (prov)); - } - } - if (str->len == 0) - return NULL; - g_string_truncate (str, str->len - 1); - return g_string_free (str, FALSE); + return TRUE; } static void -fu_main_vendor_fixup_provide_value (AsApp *app) +fu_main_authorize_unlock_cb (GObject *source, GAsyncResult *res, gpointer user_data) { - GPtrArray *provides; + g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *) user_data; + g_autoptr(GError) error = NULL; + g_autoptr(PolkitAuthorizationResult) auth = NULL; - /* no quirk required */ - if (as_app_get_kind (app) != AS_APP_KIND_FIRMWARE) + /* get result */ + auth = polkit_authority_check_authorization_finish (POLKIT_AUTHORITY (source), + res, &error); + if (!fu_main_authorization_is_valid (auth, &error)) { + g_dbus_method_invocation_return_gerror (helper->invocation, error); return; - - /* fix each provide to be a GUID */ - provides = as_app_get_provides (app); - for (guint i = 0; i < provides->len; i++) { - AsProvide *prov = g_ptr_array_index (provides, i); - const gchar *value = as_provide_get_value (prov); - g_autofree gchar *guid = NULL; - if (as_provide_get_kind (prov) != AS_PROVIDE_KIND_FIRMWARE_FLASHED) - continue; - if (as_utils_guid_is_valid (value)) - continue; - guid = as_utils_guid_from_string (value); - as_provide_set_value (prov, guid); } + + /* authenticated */ + if (!fu_engine_unlock (helper->priv->engine, helper->device_id, &error)) { + g_dbus_method_invocation_return_gerror (helper->invocation, error); + return; + } + + /* success */ + g_dbus_method_invocation_return_value (helper->invocation, NULL); } static void -fu_main_vendor_quirk_release_version (AsApp *app) +fu_main_authorize_verify_update_cb (GObject *source, GAsyncResult *res, gpointer user_data) { - AsVersionParseFlag flags = AS_VERSION_PARSE_FLAG_USE_TRIPLET; - GPtrArray *releases; + g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *) user_data; + g_autoptr(GError) error = NULL; + g_autoptr(PolkitAuthorizationResult) auth = NULL; - /* no quirk required */ - if (as_app_get_kind (app) != AS_APP_KIND_FIRMWARE) + /* get result */ + auth = polkit_authority_check_authorization_finish (POLKIT_AUTHORITY (source), + res, &error); + if (!fu_main_authorization_is_valid (auth, &error)) { + g_dbus_method_invocation_return_gerror (helper->invocation, error); return; - - for (guint i = 0; quirk_table[i].identifier != NULL; i++) { - if (g_str_has_prefix (as_app_get_id(app), quirk_table[i].identifier)) - flags = quirk_table[i].flags; } - /* fix each release */ - releases = as_app_get_releases (app); - for (guint i = 0; i < releases->len; i++) { - AsRelease *rel; - const gchar *version; - guint64 ver_uint32; - g_autofree gchar *version_new = NULL; - - rel = g_ptr_array_index (releases, i); - version = as_release_get_version (rel); - if (version == NULL) - continue; - if (g_strstr_len (version, -1, ".") != NULL) - continue; - - /* metainfo files use hex and the LVFS uses decimal */ - if (g_str_has_prefix (version, "0x")) { - ver_uint32 = g_ascii_strtoull (version + 2, NULL, 16); - } else { - ver_uint32 = g_ascii_strtoull (version, NULL, 10); - } - if (ver_uint32 == 0) - continue; - - /* convert to dotted decimal */ - version_new = as_utils_version_from_uint32 ((guint32) ver_uint32, flags); - as_release_set_version (rel, version_new); - } -} - -#if AS_CHECK_VERSION(0,6,7) -static gboolean -fu_main_check_version_requirement (AsApp *app, - AsRequireKind kind, - const gchar *id, - const gchar *version, - GError **error) -{ - AsRequire *req; - - /* check args */ - if (version == NULL) { - g_debug ("no paramater given for %s{%s}", - as_require_kind_to_string (kind), id); - return TRUE; - } - - /* does requirement exist */ - req = as_app_get_require_by_value (app, kind, id); - if (req == NULL) { - g_debug ("no requirement on %s{%s}", - as_require_kind_to_string (kind), id); - return TRUE; - } - - /* check version */ - if (!as_require_version_compare (req, version, error)) { - g_prefix_error (error, "Value of %s incorrect: ", id); - return FALSE; + /* authenticated */ + if (!fu_engine_verify_update (helper->priv->engine, helper->device_id, &error)) { + g_dbus_method_invocation_return_gerror (helper->invocation, error); + return; } /* success */ - g_debug ("requirement %s %s %s on %s passed", - as_require_get_version (req), - as_require_compare_to_string (as_require_get_compare (req)), - version, id); - return TRUE; -} -#endif - -static AsApp * -fu_main_store_get_app_by_guids (AsStore *store, FuDevice *device) -{ - GPtrArray *guids = fu_device_get_guids (device); - for (guint i = 0; i < guids->len; i++) { - AsApp *app = NULL; - app = as_store_get_app_by_provide (store, - AS_PROVIDE_KIND_FIRMWARE_FLASHED, - g_ptr_array_index (guids, i)); - if (app != NULL) - return app; - } - return NULL; + g_dbus_method_invocation_return_value (helper->invocation, NULL); } -static gboolean -fu_main_check_app_versions (AsApp *app, FuDevice *device, GError **error) +static void +fu_main_authorize_install_cb (GObject *source, GAsyncResult *res, gpointer user_data) { -#if AS_CHECK_VERSION(0,6,7) - /* make sure requirements are satisfied */ - if (!fu_main_check_version_requirement (app, - AS_REQUIRE_KIND_ID, - "org.freedesktop.fwupd", - VERSION, - error)) { - return FALSE; - } - - if (device != NULL) { - if (!fu_main_check_version_requirement (app, - AS_REQUIRE_KIND_FIRMWARE, - NULL, - fu_device_get_version (device), - error)) { - return FALSE; - } - if (!fu_main_check_version_requirement (app, - AS_REQUIRE_KIND_FIRMWARE, - "bootloader", - fu_device_get_version_bootloader (device), - error)) { - return FALSE; - } - if (!fu_main_check_version_requirement (app, - AS_REQUIRE_KIND_FIRMWARE, - "vendor-id", - fu_device_get_vendor_id (device), - error)) { - return FALSE; - } - } -#endif - - /* success */ - return TRUE; -} - -static AsScreenshot * -_as_app_get_screenshot_default (AsApp *app) -{ - GPtrArray *array = as_app_get_screenshots (app); - if (array->len == 0) - return NULL; - return g_ptr_array_index (array, 0); -} - -static gboolean -fu_main_update_helper_for_device (FuMainAuthHelper *helper, - FuDevice *device, - GError **error) -{ - AsApp *app; - AsChecksum *csum_tmp; - AsRelease *rel; - GBytes *blob_fw; - const gchar *tmp; - const gchar *version; - gboolean is_downgrade; - gint vercmp; - - /* find from guid */ - app = fu_main_store_get_app_by_guids (helper->store, device); - if (app == NULL) { - g_autofree gchar *guid = NULL; - guid = fu_main_get_guids_from_store (helper->store); - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "firmware is not for this hw: required %s got %s", - fu_device_get_guid_default (device), guid); - return FALSE; - } - - /* check we can install it */ - if (!fu_main_check_app_versions (app, device, error)) - return FALSE; - - /* parse the DriverVer */ - rel = as_app_get_release_default (app); - if (rel == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "no releases in the firmware component"); - return FALSE; - } - - /* no update abilities */ - if (!fu_device_has_flag (device, FWUPD_DEVICE_FLAG_ALLOW_OFFLINE) && - !fu_device_has_flag (device, FWUPD_DEVICE_FLAG_ALLOW_ONLINE)) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "Device %s does not currently allow updates", - fu_device_get_id (device)); - return FALSE; - } - - /* not in bootloader mode */ - if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_NEEDS_BOOTLOADER)) { - const gchar *caption = NULL; - AsScreenshot *ss = _as_app_get_screenshot_default (app); - if (ss != NULL) - caption = as_screenshot_get_caption (ss, NULL); - if (caption != NULL) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "Device %s needs to manually be put in update mode: %s", - fu_device_get_name (device), caption); - } else { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "Device %s needs to manually be put in update mode", - fu_device_get_name (device)); - } - return FALSE; - } - - /* get the blob */ - csum_tmp = as_release_get_checksum_by_target (rel, AS_CHECKSUM_TARGET_CONTENT); - tmp = as_checksum_get_filename (csum_tmp); - if (tmp == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "no checksum filename"); - return FALSE; - } - - /* not all devices have to use the same blob */ - blob_fw = as_release_get_blob (rel, tmp); - if (blob_fw == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_READ, - "failed to get firmware blob"); - return FALSE; - } - - /* possibly convert the version from 0x to dotted */ - fu_main_vendor_quirk_release_version (app); - - /* possibly convert the flashed provide to a GUID */ - fu_main_vendor_fixup_provide_value (app); - - version = as_release_get_version (rel); - fu_device_set_update_version (device, version); - - /* compare to the lowest supported version, if it exists */ - tmp = fu_device_get_version_lowest (device); - if (tmp != NULL && as_utils_vercmp (tmp, version) > 0) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_VERSION_NEWER, - "Specified firmware is older than the minimum " - "required version '%s < %s'", tmp, version); - return FALSE; - } - - /* check the device is locked */ - if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_LOCKED)) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "Device %s is locked", - fu_device_get_id (device)); - return FALSE; - } - - /* compare the versions of what we have installed */ - tmp = fu_device_get_version (device); - if (tmp == NULL) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "Device %s does not yet have a current version", - fu_device_get_id (device)); - return FALSE; - } - vercmp = as_utils_vercmp (tmp, version); - if (vercmp == 0 && (helper->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) == 0) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_VERSION_SAME, - "Specified firmware is already installed '%s'", - tmp); - return FALSE; - } - is_downgrade = vercmp > 0; - if (is_downgrade && (helper->flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) == 0) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_VERSION_NEWER, - "Specified firmware is older than installed '%s < %s'", - tmp, version); - return FALSE; - } - - /* if any downgrade, we want the global to be true */ - if (is_downgrade) - helper->is_downgrade = is_downgrade; - - /* verify */ - if (!fu_main_get_release_trust_flags (rel, &helper->trust_flags, error)) - return FALSE; - - /* success */ - g_ptr_array_add (helper->blob_fws, g_bytes_ref (blob_fw)); - return TRUE; -} - -static gboolean -fu_main_update_helper (FuMainAuthHelper *helper, GError **error) -{ - g_autoptr(GError) error_first = NULL; - - /* load store file which also decompresses firmware */ - fu_main_set_status (helper->priv, FWUPD_STATUS_DECOMPRESSING); - if (!as_store_from_bytes (helper->store, helper->blob_cab, NULL, error)) - return FALSE; - - /* we've specified a specific device; failure is critical */ - if (helper->devices->len > 0) { - for (guint i = 0; i < helper->devices->len; i ++) { - FuDevice *device = g_ptr_array_index (helper->devices, i); - if (!fu_main_update_helper_for_device (helper, device, error)) - return FALSE; - } - return TRUE; - } - - /* if we've not chosen a device, try and find anything in the - * cabinet 'store' that matches any installed device and is updatable */ - for (guint i = 0; i < helper->priv->devices->len; i++) { - AsApp *app; - FuDeviceItem *item; - g_autoptr(GError) error_local = NULL; - - /* guid found */ - item = g_ptr_array_index (helper->priv->devices, i); - app = fu_main_store_get_app_by_guids (helper->store, item->device); - if (app == NULL) - continue; - - /* check we can install it */ - if (!fu_main_check_app_versions (app, item->device, &error_local)) { - if (error_first == NULL) - error_first = g_error_copy (error_local); - continue; - } - - /* try this device, error not fatal */ - if (!fu_main_update_helper_for_device (helper, - item->device, - &error_local)) { - g_debug ("failed to add %s: %s", - fu_device_get_id (item->device), - error_local->message); - - /* save this for later */ - if (error_first == NULL) - error_first = g_error_copy (error_local); - continue; - } - - /* success */ - g_ptr_array_add (helper->devices, g_object_ref (item->device)); - } - if (helper->devices->len == 0) { - if (error_first != NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - error_first->message); - } else { - g_autofree gchar *guid = NULL; - guid = fu_main_get_guids_from_store (helper->store); - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "no attached hardware matched %s", - guid); - } - return FALSE; - } - - /* sanity check */ - if (helper->devices->len != helper->blob_fws->len) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "not enough firmware blobs (%u) for devices (%u)", - helper->blob_fws->len, - helper->devices->len); - return FALSE; - } - - return TRUE; -} - -static guint -fu_main_dbus_get_uid (FuMainPrivate *priv, const gchar *sender) -{ - guint uid; + g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *) user_data; g_autoptr(GError) error = NULL; - g_autoptr(GVariant) value = NULL; + g_autoptr(PolkitAuthorizationResult) auth = NULL; - if (priv->proxy_uid == NULL) - return G_MAXUINT; - value = g_dbus_proxy_call_sync (priv->proxy_uid, - "GetConnectionUnixUser", - g_variant_new ("(s)", sender), - G_DBUS_CALL_FLAGS_NONE, - -1, - NULL, - &error); - if (value == NULL) { - g_warning ("Failed to get uid for %s: %s", - sender, error->message); - return G_MAXUINT; - } - g_variant_get (value, "(u)", &uid); - return uid; -} - -static FuDeviceItem * -fu_main_get_item_by_id_fallback_pending (FuMainPrivate *priv, const gchar *id, GError **error) -{ - FuDevice *dev; - FuPlugin *plugin; - FuDeviceItem *item = NULL; - FwupdUpdateState update_state; - const gchar *tmp; - g_autoptr(GPtrArray) devices = NULL; - - /* not a wildcard */ - if (g_strcmp0 (id, FWUPD_DEVICE_ID_ANY) != 0) { - item = fu_main_get_item_by_id (priv, id); - if (item == NULL) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "no suitable device found for %s", id); - } - return item; + /* get result */ + auth = polkit_authority_check_authorization_finish (POLKIT_AUTHORITY (source), + res, &error); + if (!fu_main_authorization_is_valid (auth, &error)) { + g_dbus_method_invocation_return_gerror (helper->invocation, error); + return; } - /* allow '*' for any */ - devices = fu_pending_get_devices (priv->pending, error); - if (devices == NULL) - return NULL; - for (guint i = 0; i < devices->len; i++) { - dev = g_ptr_array_index (devices, i); - update_state = fu_device_get_update_state (dev); - if (update_state == FWUPD_UPDATE_STATE_UNKNOWN) - continue; - if (update_state == FWUPD_UPDATE_STATE_PENDING) - continue; - - /* if the device is not still connected, fake a FuDeviceItem */ - item = fu_main_get_item_by_id (priv, fu_device_get_id (dev)); - if (item == NULL) { - tmp = fu_device_get_plugin (dev); - plugin = fu_main_get_plugin_by_name (priv, tmp); - if (plugin == NULL) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "no plugin %s found", tmp); - return NULL; - } - item = g_new0 (FuDeviceItem, 1); - item->device = g_object_ref (dev); - item->plugin = g_object_ref (plugin); - g_ptr_array_add (priv->devices, item); - - /* FIXME: just a boolean on FuDeviceItem? */ - fu_device_set_metadata (dev, "FakeDevice", "TRUE"); - } - break; - } - - /* no device found */ - if (item == NULL) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "no suitable devices found"); - } - return item; -} - -static const gchar * -fu_main_get_action_id_for_device (FuMainAuthHelper *helper) -{ - gboolean all_removable = TRUE; - gboolean is_trusted; - - /* only test the payload */ - is_trusted = (helper->trust_flags & FWUPD_TRUST_FLAG_PAYLOAD) > 0; - - /* any non-removable means false */ - for (guint i = 0; i < helper->devices->len; i ++) { - FuDevice *device = g_ptr_array_index (helper->devices, i); - if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_INTERNAL)) { - all_removable = FALSE; - break; - } - } - - /* relax authentication checks for removable devices */ - if (all_removable) { - if (helper->is_downgrade) - return "org.freedesktop.fwupd.downgrade-hotplug"; - if (is_trusted) - return "org.freedesktop.fwupd.update-hotplug-trusted"; - return "org.freedesktop.fwupd.update-hotplug"; - } - - /* internal device */ - if (helper->is_downgrade) - return "org.freedesktop.fwupd.downgrade-internal"; - if (is_trusted) - return "org.freedesktop.fwupd.update-internal-trusted"; - return "org.freedesktop.fwupd.update-internal"; -} - -static gboolean -fu_main_load_metadata_from_file (FuMainPrivate *priv, - const gchar *path, - const gchar *remote_id, - GError **error) -{ - GPtrArray *apps; - g_autoptr(AsStore) store = NULL; - g_autoptr(GFile) file = NULL; - - /* load the store locally until we know it is valid */ - store = as_store_new (); - file = g_file_new_for_path (path); - if (!as_store_from_file (store, file, NULL, NULL, error)) - return FALSE; - - /* add the new application from the store */ - apps = as_store_get_apps (store); - for (guint i = 0; i < apps->len; i++) { - AsApp *app = g_ptr_array_index (apps, i); - - /* does this app already exist */ - if (as_store_get_app_by_id (priv->store, as_app_get_id (app)) != NULL) { - g_debug ("%s exists in remote %s, skipping", - as_app_get_unique_id (app), - as_app_get_metadata_item (app, "fwupd::RemoteID")); - continue; - } - if (remote_id != NULL && remote_id[0] != '\0') - as_app_add_metadata (app, "fwupd::RemoteID", remote_id); - as_store_add_app (priv->store, app); - } - return TRUE; -} - -static gboolean -fu_main_load_metadata_store (FuMainPrivate *priv, GError **error) -{ - GPtrArray *apps; - GPtrArray *remotes; - - /* clear existing store */ - as_store_remove_all (priv->store); - - /* load each enabled metadata file */ - remotes = fu_config_get_remotes (priv->config); - for (guint i = 0; i < remotes->len; i++) { - const gchar *path = NULL; - FwupdRemote *remote = g_ptr_array_index (remotes, i); - if (!fwupd_remote_get_enabled (remote)) { - g_debug ("remote %s not enabled, so skipping", - fwupd_remote_get_id (remote)); - continue; - } - path = fwupd_remote_get_filename_cache (remote); - if (!g_file_test (path, G_FILE_TEST_EXISTS)) { - g_debug ("no %s, so skipping", path); - continue; - } - if (!fu_main_load_metadata_from_file (priv, path, - fwupd_remote_get_id (remote), - error)) - return FALSE; - } - - /* print what we've got */ - apps = as_store_get_apps (priv->store); - if (apps->len == 0) { - g_debug ("no devices in store"); - } else { - g_debug ("devices now in store:"); - for (guint i = 0; i < apps->len; i++) { - AsApp *app = g_ptr_array_index (apps, i); - g_debug ("%u\t%s\t%s", i + 1, - as_app_get_id (app), - as_app_get_name (app, NULL)); - } - } - - /* are any devices now supported? */ - for (guint i = 0; i < priv->devices->len; i++) { - FuDeviceItem *item = g_ptr_array_index (priv->devices, i); - if (fu_main_get_updates_item_update (priv, item)) - fu_main_emit_device_changed (priv, item->device); - } - - return TRUE; -} - -static gboolean -fu_main_set_contents (const gchar *filename, GBytes *blob, GError **error) -{ - const gchar *data; - gsize size; - g_autoptr(GFile) file = NULL; - g_autoptr(GFile) file_parent = NULL; - - file = g_file_new_for_path (filename); - file_parent = g_file_get_parent (file); - if (!g_file_query_exists (file_parent, NULL)) { - if (!g_file_make_directory_with_parents (file_parent, NULL, error)) - return FALSE; - } - data = g_bytes_get_data (blob, &size); - return g_file_set_contents (filename, data, size, error); -} - -static gboolean -fu_main_daemon_update_metadata (FuMainPrivate *priv, const gchar *remote_id, - gint fd, gint fd_sig, GError **error) -{ - FwupdRemote *remote; - g_autoptr(GBytes) bytes_raw = NULL; - g_autoptr(GBytes) bytes_sig = NULL; - g_autoptr(FuKeyring) kr = NULL; - g_autoptr(GInputStream) stream_fd = NULL; - g_autoptr(GInputStream) stream = NULL; - g_autoptr(GInputStream) stream_sig = NULL; - - /* check remote is valid */ - remote = fu_config_get_remote_by_id (priv->config, remote_id); - if (remote == NULL) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "remote %s not found", remote_id); - return FALSE; - } - if (!fwupd_remote_get_enabled (remote)) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "remote %s not enabled", remote_id); - return FALSE; - } - - /* read the entire file into memory */ - stream_fd = g_unix_input_stream_new (fd, TRUE); - bytes_raw = g_input_stream_read_bytes (stream_fd, 0x100000, NULL, error); - if (bytes_raw == NULL) - return FALSE; - - /* read signature */ - stream_sig = g_unix_input_stream_new (fd_sig, TRUE); - bytes_sig = g_input_stream_read_bytes (stream_sig, 0x800, NULL, error); - if (bytes_sig == NULL) - return FALSE; - - /* verify file */ - kr = fu_keyring_new (); - if (!fu_keyring_add_public_keys (kr, "/etc/pki/fwupd-metadata", error)) - return FALSE; - if (!fu_keyring_verify_data (kr, bytes_raw, bytes_sig, error)) - return FALSE; - - /* save XML to remotes.d */ - if (!fu_main_set_contents (fwupd_remote_get_filename_cache (remote), bytes_raw, error)) - return FALSE; - return fu_main_load_metadata_store (priv, error); -} - -static gboolean -fu_main_get_updates_item_update (FuMainPrivate *priv, FuDeviceItem *item) -{ - AsApp *app; - AsRelease *release; - GPtrArray *releases; - const gchar *tmp; - const gchar *version; - g_autoptr(GError) error = NULL; - g_autoptr(GPtrArray) updates_list = NULL; - - /* get device version */ - version = fu_device_get_version (item->device); - if (version == NULL) - return FALSE; - - /* match the GUIDs in the XML */ - app = fu_main_store_get_app_by_guids (priv->store, item->device); - if (app == NULL) - return FALSE; - - /* possibly convert the version from 0x to dotted */ - fu_main_vendor_quirk_release_version (app); - - /* possibly convert the flashed provide to a GUID */ - fu_main_vendor_fixup_provide_value (app); - - /* get latest release */ - release = as_app_get_release_default (app); - if (release == NULL) { - g_debug ("%s [%s] has no firmware update metadata", - fu_device_get_id (item->device), - fu_device_get_name (item->device)); - return FALSE; - } - - /* supported in metadata */ - fwupd_device_add_flag (fwupd_result_get_device (FWUPD_RESULT (item->device)), - FWUPD_DEVICE_FLAG_SUPPORTED); - - /* check if actually newer than what we have installed */ - if (as_utils_vercmp (as_release_get_version (release), version) <= 0) { - g_debug ("%s has no firmware updates", - fu_device_get_id (item->device)); - return FALSE; - } - - /* check we can install it */ - if (!fu_main_check_app_versions (app, item->device, &error)) { - g_debug ("can not be installed: %s", error->message); - return FALSE; - } - - /* only show devices that can be updated */ - if (!fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_ALLOW_OFFLINE) && - !fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_ALLOW_ONLINE)) { - g_debug ("ignoring %s [%s] as not updatable live or offline", - fu_device_get_id (item->device), - fu_device_get_name (item->device)); - return FALSE; - } - - /* add application metadata */ - fu_device_set_update_id (item->device, as_app_get_id (app)); - tmp = as_app_get_developer_name (app, NULL); - if (tmp != NULL) - fu_device_set_update_vendor (item->device, tmp); - tmp = as_app_get_name (app, NULL); - if (tmp != NULL) - fu_device_set_update_name (item->device, tmp); - tmp = as_app_get_comment (app, NULL); - if (tmp != NULL) - fu_device_set_update_summary (item->device, tmp); - tmp = as_app_get_description (app, NULL); - if (tmp != NULL) - fu_device_set_description (item->device, tmp); - tmp = as_app_get_url_item (app, AS_URL_KIND_HOMEPAGE); - if (tmp != NULL) - fu_device_set_update_homepage (item->device, tmp); - tmp = as_app_get_project_license (app); - if (tmp != NULL) - fu_device_set_update_license (item->device, tmp); - tmp = as_app_get_metadata_item (app, "fwupd::RemoteID"); - if (tmp != NULL) - fu_device_set_update_remote_id (item->device, tmp); -#if AS_CHECK_VERSION(0,6,1) - tmp = as_app_get_unique_id (app); - if (tmp != NULL) - fu_device_set_unique_id (item->device, tmp); -#else - fu_device_set_unique_id (item->device, as_app_get_id (app)); -#endif - - /* add release information */ - fu_main_set_release_from_item (fwupd_result_get_release (FWUPD_RESULT (item->device)), release); - - /* get the list of releases newer than the one installed */ - updates_list = g_ptr_array_new (); - releases = as_app_get_releases (app); - for (guint i = 0; i < releases->len; i++) { - release = g_ptr_array_index (releases, i); - if (as_utils_vercmp (as_release_get_version (release), version) <= 0) - continue; - tmp = as_release_get_description (release, NULL); - if (tmp == NULL) - continue; - g_ptr_array_add (updates_list, release); - } - - /* no prefix on each release */ - if (updates_list->len == 1) { - release = g_ptr_array_index (updates_list, 0); - fu_device_set_update_description (item->device, - as_release_get_description (release, NULL)); - } else { - g_autoptr(GString) update_desc = NULL; - update_desc = g_string_new (""); - - /* get the descriptions with a version prefix */ - for (guint i = 0; i < updates_list->len; i++) { - release = g_ptr_array_index (updates_list, i); - g_string_append_printf (update_desc, - "

%s:

%s", - as_release_get_version (release), - as_release_get_description (release, NULL)); - } - if (update_desc->len > 0) - fu_device_set_update_description (item->device, update_desc->str); + /* authenticated */ + if (!fu_engine_install (helper->priv->engine, + helper->device_id, + helper->store, + helper->blob_cab, + helper->flags, + &error)) { + g_dbus_method_invocation_return_gerror (helper->invocation, error); + return; } /* success */ - return TRUE; -} - -/* find any updates using the AppStream metadata */ -static GPtrArray * -fu_main_get_updates (FuMainPrivate *priv, GError **error) -{ - GPtrArray *updates = g_ptr_array_new (); - for (guint i = 0; i < priv->devices->len; i++) { - FuDeviceItem *item = g_ptr_array_index (priv->devices, i); - if (fu_main_get_updates_item_update (priv, item)) - g_ptr_array_add (updates, item); - } - return updates; -} - -/* find releases for a device */ -static GVariant * -fu_main_get_releases_to_variant (FuMainPrivate *priv, FuDeviceItem *item, GError **error) -{ - GPtrArray *device_guids; - GVariantBuilder builder; - g_autoptr(GPtrArray) results = NULL; - - /* get all the results for the device */ - results = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); - device_guids = fu_device_get_guids (item->device); - for (guint i = 0; i < device_guids->len; i++) { - GPtrArray *releases; - const gchar *guid = g_ptr_array_index (device_guids, i); - AsApp *app = as_store_get_app_by_provide (priv->store, - AS_PROVIDE_KIND_FIRMWARE_FLASHED, - guid); - if (app == NULL) - continue; - releases = as_app_get_releases (app); - for (guint j = 0; j < releases->len; j++) { - AsRelease *release = g_ptr_array_index (releases, j); - FwupdRelease *rel = fwupd_release_new (); - fu_main_set_release_from_item (rel, release); - g_ptr_array_add (results, rel); - } - } - - /* no devices */ - if (results->len == 0) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_NOTHING_TO_DO, - "No releases for device"); - return NULL; - } - - /* convert the objects to a variant */ - g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY); - for (guint i = 0; i < results->len; i++) { - GVariant *tmp; - FwupdRelease *rel = g_ptr_array_index (results, i); - tmp = fwupd_release_to_data (rel, "a{sv}"); //FIXME? - g_variant_builder_add_value (&builder, tmp); - } - return g_variant_new ("(aa{sv})", &builder); -} - -static AsStore * -fu_main_get_store_from_fd (FuMainPrivate *priv, gint fd, GError **error) -{ - g_autofree gchar *checksum = NULL; - g_autoptr(AsStore) store = NULL; - g_autoptr(GBytes) blob_cab = NULL; - g_autoptr(GError) error_local = NULL; - g_autoptr(GInputStream) stream = NULL; - - /* read the entire fd to a data blob */ - stream = g_unix_input_stream_new (fd, TRUE); - blob_cab = g_input_stream_read_bytes (stream, - FU_MAIN_FIRMWARE_SIZE_MAX, - NULL, &error_local); - if (blob_cab == NULL){ - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - error_local->message); - return NULL; - } - - /* load file */ - store = as_store_new (); - if (!as_store_from_bytes (store, blob_cab, NULL, &error_local)) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - error_local->message); - return NULL; - } - - /* get a checksum of the file and use it as the origin */ - checksum = g_compute_checksum_for_data (G_CHECKSUM_SHA256, - g_bytes_get_data (blob_cab, NULL), - g_bytes_get_size (blob_cab)); - as_store_set_origin (store, checksum); - - return g_steal_pointer (&store); -} - -static FwupdResult * -fu_main_get_result_from_app (FuMainPrivate *priv, AsApp *app, GError **error) -{ - FwupdTrustFlags trust_flags = FWUPD_TRUST_FLAG_NONE; - AsRelease *release; - FwupdDevice *dev; - FwupdRelease *rel; - GPtrArray *provides; - g_autoptr(FwupdResult) res = NULL; - - res = fwupd_result_new (); - dev = fwupd_result_get_device (res); - provides = as_app_get_provides (app); - for (guint i = 0; i < provides->len; i++) { - AsProvide *prov = AS_PROVIDE (g_ptr_array_index (provides, i)); - FuDeviceItem *item; - const gchar *guid; - - /* not firmware */ - if (as_provide_get_kind (prov) != AS_PROVIDE_KIND_FIRMWARE_FLASHED) - continue; - - /* is a online or offline update appropriate */ - guid = as_provide_get_value (prov); - if (guid == NULL) - continue; - item = fu_main_get_item_by_guid (priv, guid); - if (item != NULL) { - fwupd_device_set_flags (dev, fu_device_get_flags (item->device)); - fwupd_device_set_id (dev, fu_device_get_id (item->device)); - } - - /* add GUID */ - fwupd_device_add_guid (dev, guid); - } - if (fwupd_device_get_guids(dev)->len == 0) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "component has no GUIDs"); - return NULL; - } - - /* check we can install it */ - if (!fu_main_check_app_versions (app, NULL, error)) - return NULL; - - /* verify trust */ - release = as_app_get_release_default (app); - if (!fu_main_get_release_trust_flags (release, &trust_flags, error)) - return NULL; - fwupd_result_set_update_trust_flags (res, trust_flags); - - /* possibly convert the version from 0x to dotted */ - fu_main_vendor_quirk_release_version (app); - - /* possibly convert the flashed provide to a GUID */ - fu_main_vendor_fixup_provide_value (app); - - /* create a result with all the metadata in */ - fwupd_device_set_description (dev, as_app_get_description (app, NULL)); - rel = fwupd_result_get_release (res); - fwupd_release_set_homepage (rel, as_app_get_url_item (app, AS_URL_KIND_HOMEPAGE)); - fwupd_release_set_license (rel, as_app_get_project_license (app)); - fwupd_release_set_name (rel, as_app_get_name (app, NULL)); - fwupd_release_set_summary (rel, as_app_get_comment (app, NULL)); - fwupd_release_set_vendor (rel, as_app_get_developer_name (app, NULL)); -#if AS_CHECK_VERSION(0,6,1) - fwupd_result_set_unique_id (res, as_app_get_unique_id (app)); -#else - fwupd_result_set_unique_id (res, as_app_get_id (app)); -#endif - fwupd_release_set_appstream_id (rel, as_app_get_id (app)); - fu_main_set_release_from_item (rel, release); - return g_steal_pointer (&res); -} - -static GVariant * -fu_main_get_details_local_from_fd (FuMainPrivate *priv, gint fd, GError **error) -{ - GPtrArray *apps; - GVariantBuilder builder; - g_autoptr(AsStore) store = NULL; - - store = fu_main_get_store_from_fd (priv, fd, error); - if (store == NULL) - return NULL; - - /* get all apps */ - apps = as_store_get_apps (store); - if (apps->len == 0) { - g_set_error_literal (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "no components"); - return NULL; - } - - /* create results with all the metadata in */ - g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY); - for (guint i = 0; i < apps->len; i++) { - g_autoptr(FwupdResult) res = NULL; - AsApp *app = g_ptr_array_index (apps, i); - GVariant *tmp; - - /* check we can install it */ - if (!fu_main_check_app_versions (app, NULL, error)) - return NULL; - - as_app_set_origin (app, as_store_get_origin (store)); - res = fu_main_get_result_from_app (priv, app, error); - if (res == NULL) - return NULL; - tmp = fwupd_result_to_data (res, "{sa{sv}}"); - g_variant_builder_add_value (&builder, tmp); - } - return g_variant_new ("(a{sa{sv}})", &builder); -} - -static GVariant * -fu_main_get_remotes_as_variant (FuMainPrivate *priv, GError **error) -{ - GVariantBuilder builder; - GPtrArray *remotes; - - /* get the list of remotes */ - remotes = fu_config_get_remotes (priv->config); - if (remotes->len == 0) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "no remotes configured"); - return NULL; - } - - g_variant_builder_init (&builder, G_VARIANT_TYPE_ARRAY); - for (guint i = 0; i < remotes->len; i++) { - GVariant *tmp; - FwupdRemote *remote = g_ptr_array_index (remotes, i); - tmp = fwupd_remote_to_data (remote, "a{sv}"); - g_variant_builder_add_value (&builder, tmp); - } - return g_variant_new ("(aa{sv})", &builder); + g_dbus_method_invocation_return_value (helper->invocation, NULL); } static void @@ -1899,178 +372,93 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, GDBusMethodInvocation *invocation, gpointer user_data) { FuMainPrivate *priv = (FuMainPrivate *) user_data; - GVariant *val; + GVariant *val = NULL; g_autoptr(GError) error = NULL; - /* return 'as' */ if (g_strcmp0 (method_name, "GetDevices") == 0) { + g_autoptr(GPtrArray) devices = NULL; g_debug ("Called %s()", method_name); - val = fu_main_device_array_to_variant (priv->devices, &error); - if (val == NULL) { - if (g_error_matches (error, - FWUPD_ERROR, - FWUPD_ERROR_NOTHING_TO_DO)) { - g_prefix_error (&error, "No detected devices: "); - } - fu_main_invocation_return_error (priv, invocation, error); + devices = fu_engine_get_devices (priv->engine, &error); + if (devices == NULL) { + g_dbus_method_invocation_return_gerror (invocation, error); return; } - fu_main_invocation_return_value (priv, invocation, val); + val = fu_main_device_array_to_variant (devices); + g_dbus_method_invocation_return_value (invocation, val); return; } - - /* return 'as' */ if (g_strcmp0 (method_name, "GetUpdates") == 0) { g_autoptr(GPtrArray) updates = NULL; g_debug ("Called %s()", method_name); - updates = fu_main_get_updates (priv, &error); + updates = fu_engine_get_updates (priv->engine, &error); if (updates == NULL) { - fu_main_invocation_return_error (priv, invocation, error); + g_dbus_method_invocation_return_gerror (invocation, error); return; } - val = fu_main_device_array_to_variant (updates, &error); - if (val == NULL) { - if (g_error_matches (error, - FWUPD_ERROR, - FWUPD_ERROR_NOTHING_TO_DO)) { - g_prefix_error (&error, "No devices can be updated: "); - } - fu_main_invocation_return_error (priv, invocation, error); - return; - } - fu_main_invocation_return_value (priv, invocation, val); + val = fu_main_device_array_to_variant (updates); + g_dbus_method_invocation_return_value (invocation, val); return; } - - /* return variant */ if (g_strcmp0 (method_name, "GetReleases") == 0) { - FuDeviceItem *item; - const gchar *device_id = NULL; + const gchar *device_id; g_autoptr(GPtrArray) releases = NULL; - g_variant_get (parameters, "(&s)", &device_id); g_debug ("Called %s(%s)", method_name, device_id); - - /* find the device */ - item = fu_main_get_item_by_id (priv, device_id); - if (item == NULL) { - g_set_error (&error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "no device with ID %s", - device_id); - fu_main_invocation_return_error (priv, invocation, error); + releases = fu_engine_get_releases (priv->engine, device_id, &error); + if (releases == NULL) { + g_dbus_method_invocation_return_gerror (invocation, error); return; } - val = fu_main_get_releases_to_variant (priv, item, &error); - if (val == NULL) { - if (g_error_matches (error, - FWUPD_ERROR, - FWUPD_ERROR_NOTHING_TO_DO)) { - g_prefix_error (&error, "No releases found: "); - } - fu_main_invocation_return_error (priv, invocation, error); - return; - } - fu_main_invocation_return_value (priv, invocation, val); + val = fu_main_release_array_to_variant (releases); + g_dbus_method_invocation_return_value (invocation, val); return; } - - /* return variant */ if (g_strcmp0 (method_name, "GetRemotes") == 0) { + g_autoptr(GPtrArray) remotes = NULL; g_debug ("Called %s()", method_name); - val = fu_main_get_remotes_as_variant (priv, &error); - if (val == NULL) { - fu_main_invocation_return_error (priv, invocation, error); + remotes = fu_engine_get_remotes (priv->engine, &error); + if (remotes == NULL) { + g_dbus_method_invocation_return_gerror (invocation, error); return; } - fu_main_invocation_return_value (priv, invocation, val); + val = fu_main_remote_array_to_variant (remotes); + g_dbus_method_invocation_return_value (invocation, val); return; } - - /* return '' */ if (g_strcmp0 (method_name, "ClearResults") == 0) { - FuDeviceItem *item = NULL; - const gchar *id = NULL; - - g_variant_get (parameters, "(&s)", &id); - g_debug ("Called %s(%s)", method_name, id); - - /* find device */ - item = fu_main_get_item_by_id_fallback_pending (priv, id, &error); - if (item == NULL) { - fu_main_invocation_return_error (priv, invocation, error); + const gchar *device_id; + g_variant_get (parameters, "(&s)", &device_id); + g_debug ("Called %s(%s)", method_name, device_id); + if (!fu_engine_clear_results (priv->engine, device_id, &error)) { + g_dbus_method_invocation_return_gerror (invocation, error); return; } - - /* call into the plugin */ - if (!fu_plugin_runner_clear_results (item->plugin, item->device, &error)) { - fu_main_invocation_return_error (priv, invocation, error); - return; - } - - /* success */ - fu_main_invocation_return_value (priv, invocation, NULL); + g_dbus_method_invocation_return_value (invocation, NULL); return; } - - /* return 'a{sv}' */ if (g_strcmp0 (method_name, "GetResults") == 0) { - FuDeviceItem *item = NULL; - const gchar *id = NULL; - - g_variant_get (parameters, "(&s)", &id); - g_debug ("Called %s(%s)", method_name, id); - - /* find device */ - item = fu_main_get_item_by_id_fallback_pending (priv, id, &error); - if (item == NULL) { - fu_main_invocation_return_error (priv, invocation, error); + const gchar *device_id = NULL; + g_autoptr(FwupdResult) result = NULL; + g_variant_get (parameters, "(&s)", &device_id); + g_debug ("Called %s(%s)", method_name, device_id); + result = fu_engine_get_results (priv->engine, device_id, &error); + if (result == NULL) { + g_dbus_method_invocation_return_gerror (invocation, error); return; } - - /* call into the plugin */ - if (!fu_plugin_runner_get_results (item->plugin, item->device, &error)) { - fu_main_invocation_return_error (priv, invocation, error); - return; - } - - /* ensure the unique ID is set */ - if (fwupd_result_get_unique_id (FWUPD_RESULT (item->device)) == NULL) { - g_autofree gchar *id2 = NULL; - FwupdResult *res = FWUPD_RESULT (item->device); - FwupdDevice *dev = fwupd_result_get_device (res); -#if AS_CHECK_VERSION(0,6,1) - id2 = as_utils_unique_id_build (AS_APP_SCOPE_SYSTEM, - AS_BUNDLE_KIND_UNKNOWN, - NULL, - AS_APP_KIND_FIRMWARE, - fwupd_device_get_name (dev), - fwupd_device_get_version (dev)); -#else - id2 = g_strdup_printf ("system/*/*/firmware/%s/%s", - fwupd_device_get_name (dev), - fwupd_device_get_version (dev)); -#endif - fwupd_result_set_unique_id (res, id2); - } - - /* success */ - val = fwupd_result_to_data (FWUPD_RESULT (item->device), "(a{sv})"); - fu_main_invocation_return_value (priv, invocation, val); + val = fwupd_result_to_data (result, "(a{sv})"); + g_dbus_method_invocation_return_value (invocation, val); return; } - - /* return '' */ if (g_strcmp0 (method_name, "UpdateMetadataWithId") == 0) { GDBusMessage *message; GUnixFDList *fd_list; - const gchar *id = NULL; + const gchar *remote_id = NULL; gint fd_data; gint fd_sig; - g_variant_get (parameters, "(&shh)", &id, &fd_data, &fd_sig); - g_debug ("Called %s(%s,%i,%i)", method_name, id, fd_data, fd_sig); + g_variant_get (parameters, "(&shh)", &remote_id, &fd_data, &fd_sig); + g_debug ("Called %s(%s,%i,%i)", method_name, remote_id, fd_data, fd_sig); /* update the metadata store */ message = g_dbus_method_invocation_get_message (invocation); @@ -2080,107 +468,67 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "invalid handle"); - fu_main_invocation_return_error (priv, invocation, error); + g_dbus_method_invocation_return_gerror (invocation, error); return; } fd_data = g_unix_fd_list_get (fd_list, 0, &error); if (fd_data < 0) { - fu_main_invocation_return_error (priv, invocation, error); + g_dbus_method_invocation_return_gerror (invocation, error); return; } fd_sig = g_unix_fd_list_get (fd_list, 1, &error); if (fd_sig < 0) { - fu_main_invocation_return_error (priv, invocation, error); + g_dbus_method_invocation_return_gerror (invocation, error); return; } - if (!fu_main_daemon_update_metadata (priv, id, fd_data, fd_sig, &error)) { - g_prefix_error (&error, "failed to update metadata: "); - fu_main_invocation_return_error (priv, invocation, error); + + /* store new metadata (will close the fds when done) */ + if (!fu_engine_update_metadata (priv->engine, remote_id, + fd_data, fd_sig, &error)) { + g_prefix_error (&error, "Failed to update metadata: "); + g_dbus_method_invocation_return_gerror (invocation, error); return; } - fu_main_invocation_return_value (priv, invocation, NULL); + g_dbus_method_invocation_return_value (invocation, NULL); return; } - - /* return 's' */ if (g_strcmp0 (method_name, "Unlock") == 0) { - FuDeviceItem *item = NULL; FuMainAuthHelper *helper; - const gchar *id = NULL; + const gchar *device_id = NULL; g_autoptr(PolkitSubject) subject = NULL; - /* check the id exists */ - g_variant_get (parameters, "(&s)", &id); - g_debug ("Called %s(%s)", method_name, id); - item = fu_main_get_item_by_id (priv, id); - if (item == NULL) { - g_set_error (&error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "No such device %s", id); - fu_main_invocation_return_error (priv, invocation, error); - return; - } - - /* check the device is locked */ - if (!fu_device_has_flag (item->device, FWUPD_DEVICE_FLAG_LOCKED)) { - g_set_error (&error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "Device %s is not locked", id); - fu_main_invocation_return_error (priv, invocation, error); - return; - } - - /* process the firmware */ - helper = g_new0 (FuMainAuthHelper, 1); - helper->auth_kind = FU_MAIN_AUTH_KIND_UNLOCK; - helper->invocation = g_object_ref (invocation); - helper->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); - helper->priv = priv; - - /* FIXME: do we want to support "*"? */ - g_ptr_array_add (helper->devices, g_object_ref (item->device)); + g_variant_get (parameters, "(&s)", &device_id); + g_debug ("Called %s(%s)", method_name, device_id); /* authenticate */ + helper = g_new0 (FuMainAuthHelper, 1); + helper->priv = priv; + helper->invocation = g_object_ref (invocation); + helper->device_id = g_strdup (device_id); subject = polkit_system_bus_name_new (sender); - polkit_authority_check_authorization (helper->priv->authority, subject, + polkit_authority_check_authorization (priv->authority, subject, "org.freedesktop.fwupd.device-unlock", NULL, POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, NULL, - fu_main_check_authorization_cb, + fu_main_authorize_unlock_cb, helper); return; } - - /* return 'b' */ if (g_strcmp0 (method_name, "VerifyUpdate") == 0) { - FuDeviceItem *item = NULL; FuMainAuthHelper *helper; - const gchar *id = NULL; + const gchar *device_id = NULL; g_autoptr(PolkitSubject) subject = NULL; /* check the id exists */ - g_variant_get (parameters, "(&s)", &id); - g_debug ("Called %s(%s)", method_name, id); - item = fu_main_get_item_by_id (priv, id); - if (item == NULL) { - g_set_error (&error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "No such device %s", id); - fu_main_invocation_return_error (priv, invocation, error); - return; - } + g_variant_get (parameters, "(&s)", &device_id); + g_debug ("Called %s(%s)", method_name, device_id); - /* process the firmware */ + /* create helper object */ helper = g_new0 (FuMainAuthHelper, 1); - helper->auth_kind = FU_MAIN_AUTH_KIND_VERIFY_UPDATE; helper->invocation = g_object_ref (invocation); - helper->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); + helper->device_id = g_strdup (device_id); helper->priv = priv; - g_ptr_array_add (helper->devices, g_object_ref (item->device)); /* authenticate */ subject = polkit_system_bus_name_new (sender); @@ -2189,192 +537,63 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, NULL, POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, NULL, - fu_main_check_authorization_cb, + fu_main_authorize_verify_update_cb, helper); return; } - - /* return 's' */ if (g_strcmp0 (method_name, "Verify") == 0) { - AsApp *app; - AsChecksum *csum; - AsRelease *release; - FuDeviceItem *item = NULL; - GPtrArray *checksums; - const gchar *hash = NULL; - const gchar *id = NULL; - const gchar *version = NULL; - g_autoptr(AsStore) store = NULL; - - /* check the id exists */ - g_variant_get (parameters, "(&s)", &id); - g_debug ("Called %s(%s)", method_name, id); - item = fu_main_get_item_by_id (priv, id); - if (item == NULL) { - g_set_error (&error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "No such device %s", id); - fu_main_invocation_return_error (priv, invocation, error); + const gchar *device_id = NULL; + g_variant_get (parameters, "(&s)", &device_id); + g_debug ("Called %s(%s)", method_name, device_id); + if (!fu_engine_verify (priv->engine, device_id, &error)) { + g_dbus_method_invocation_return_gerror (invocation, error); return; } - - /* set the device firmware hash */ - if (!fu_plugin_runner_verify (item->plugin, item->device, - FU_PLUGIN_VERIFY_FLAG_NONE, &error)) { - fu_main_invocation_return_error (priv, invocation, error); - return; - } - - /* find component in metadata */ - store = fu_main_load_verify_store (&error); - if (store == NULL) { - g_set_error_literal (&error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "No store could be loaded"); - fu_main_invocation_return_error (priv, invocation, error); - return; - } - app = fu_main_store_get_app_by_guids (store, item->device); - if (app == NULL) { - g_set_error_literal (&error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "No metadata"); - fu_main_invocation_return_error (priv, invocation, error); - return; - } - - /* find version in metadata */ - version = fu_device_get_version (item->device); - release = as_app_get_release (app, version); - if (release == NULL) { - /* try again with the system metadata */ - app = fu_main_store_get_app_by_guids (priv->store, item->device); - if (app == NULL) { - g_set_error_literal (&error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "No system metadata"); - fu_main_invocation_return_error (priv, invocation, error); - return; - } - release = as_app_get_release (app, version); - } - if (release == NULL) { - g_set_error (&error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "No version %s", version); - fu_main_invocation_return_error (priv, invocation, error); - return; - } - - /* find checksum */ - csum = as_release_get_checksum_by_target (release, AS_CHECKSUM_TARGET_CONTENT); - if (csum == NULL) { - g_set_error (&error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "No content checksum for %s", version); - fu_main_invocation_return_error (priv, invocation, error); - return; - } - - /* get the matching checksum */ - checksums = fu_device_get_checksums (item->device); - if (checksums->len == 0) { - g_set_error (&error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "No device checksums for %s", version); - fu_main_invocation_return_error (priv, invocation, error); - return; - } - for (guint j = 0; j < checksums->len; j++) { - const gchar *hash_tmp = g_ptr_array_index (checksums, j); - GChecksumType hash_kind = fwupd_checksum_guess_kind (hash_tmp); - if (as_checksum_get_kind (csum) == hash_kind) { - hash = hash_tmp; - break; - } - } - if (hash == NULL) { - g_set_error (&error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "No matching hash kind for %s", version); - fu_main_invocation_return_error (priv, invocation, error); - return; - } - if (g_strcmp0 (as_checksum_get_value (csum), hash) != 0) { - g_set_error (&error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "For v%s expected %s, got %s", - version, - as_checksum_get_value (csum), - hash); - fu_main_invocation_return_error (priv, invocation, error); - return; - } - fu_main_invocation_return_value (priv, invocation, NULL); + g_dbus_method_invocation_return_value (invocation, NULL); return; } - - /* return '' */ if (g_strcmp0 (method_name, "Install") == 0) { - FuDeviceItem *item = NULL; FuMainAuthHelper *helper; - FwupdInstallFlags flags = FWUPD_INSTALL_FLAG_NONE; - GDBusMessage *message; - GUnixFDList *fd_list; GVariant *prop_value; const gchar *action_id; - const gchar *id = NULL; + const gchar *device_id = NULL; gchar *prop_key; gint32 fd_handle = 0; gint fd; + GDBusMessage *message; + GUnixFDList *fd_list; g_autoptr(PolkitSubject) subject = NULL; g_autoptr(GVariantIter) iter = NULL; - g_autoptr(GBytes) blob_cab = NULL; - g_autoptr(GInputStream) stream = NULL; /* check the id exists */ - g_variant_get (parameters, "(&sha{sv})", &id, &fd_handle, &iter); - g_debug ("Called %s(%s,%i)", method_name, id, fd_handle); - if (g_strcmp0 (id, FWUPD_DEVICE_ID_ANY) != 0) { - item = fu_main_get_item_by_id (priv, id); - if (item == NULL) { - g_set_error (&error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "no such device %s", id); - fu_main_invocation_return_error (priv, invocation, error); - return; - } - } + g_variant_get (parameters, "(&sha{sv})", &device_id, &fd_handle, &iter); + g_debug ("Called %s(%s,%i)", method_name, device_id, fd_handle); - /* get options */ - while (g_variant_iter_next (iter, "{&sv}", - &prop_key, &prop_value)) { + /* create helper object */ + helper = g_new0 (FuMainAuthHelper, 1); + helper->invocation = g_object_ref (invocation); + helper->device_id = g_strdup (device_id); + helper->priv = priv; + + /* get flags */ + while (g_variant_iter_next (iter, "{&sv}", &prop_key, &prop_value)) { g_debug ("got option %s", prop_key); if (g_strcmp0 (prop_key, "offline") == 0 && g_variant_get_boolean (prop_value) == TRUE) - flags |= FWUPD_INSTALL_FLAG_OFFLINE; + helper->flags |= FWUPD_INSTALL_FLAG_OFFLINE; if (g_strcmp0 (prop_key, "allow-older") == 0 && g_variant_get_boolean (prop_value) == TRUE) - flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER; + helper->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER; if (g_strcmp0 (prop_key, "allow-reinstall") == 0 && g_variant_get_boolean (prop_value) == TRUE) - flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; + helper->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; if (g_strcmp0 (prop_key, "force") == 0 && g_variant_get_boolean (prop_value) == TRUE) - flags |= FWUPD_INSTALL_FLAG_FORCE; + helper->flags |= FWUPD_INSTALL_FLAG_FORCE; g_variant_unref (prop_value); } + /* get the fd */ message = g_dbus_method_invocation_get_message (invocation); fd_list = g_dbus_message_get_unix_fd_list (message); @@ -2383,74 +602,56 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "invalid handle"); - fu_main_invocation_return_error (priv, invocation, error); + g_dbus_method_invocation_return_gerror (invocation, error); return; } fd = g_unix_fd_list_get (fd_list, 0, &error); if (fd < 0) { - fu_main_invocation_return_error (priv, invocation, error); + g_dbus_method_invocation_return_gerror (invocation, error); return; } - /* read the entire fd to a data blob */ - stream = g_unix_input_stream_new (fd, TRUE); - blob_cab = g_input_stream_read_bytes (stream, - FU_MAIN_FIRMWARE_SIZE_MAX, - NULL, &error); - if (blob_cab == NULL){ - fu_main_invocation_return_error (priv, invocation, error); + /* parse the cab file before authenticating so we can work out + * what action ID to use, for instance, if this is trusted -- + * this will also close the fd when done */ + helper->blob_cab = fu_engine_read_from_fd (fd, FU_ENGINE_FIRMWARE_SIZE_MAX, &error); + if (helper->blob_cab == NULL) { + g_dbus_method_invocation_return_gerror (invocation, error); return; } - - /* process the firmware */ - helper = g_new0 (FuMainAuthHelper, 1); - helper->auth_kind = FU_MAIN_AUTH_KIND_INSTALL; - helper->invocation = g_object_ref (invocation); - helper->trust_flags = FWUPD_TRUST_FLAG_NONE; - helper->blob_cab = g_bytes_ref (blob_cab); - helper->flags = flags; - helper->priv = priv; - helper->store = as_store_new (); - helper->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); - helper->blob_fws = g_ptr_array_new_with_free_func ((GDestroyNotify) g_bytes_unref); - if (item != NULL) - g_ptr_array_add (helper->devices, g_object_ref (item->device)); - if (!fu_main_update_helper (helper, &error)) { - fu_main_invocation_return_error (helper->priv, helper->invocation, error); - fu_main_helper_free (helper); - return; - } - - /* is root */ - if (fu_main_dbus_get_uid (priv, sender) == 0) { - if (!fu_main_plugin_update_authenticated (helper, &error)) { - fu_main_invocation_return_error (priv, invocation, error); - } else { - fu_main_invocation_return_value (priv, invocation, NULL); - } - fu_main_helper_free (helper); + helper->store = fu_engine_get_store_from_blob (priv->engine, + helper->blob_cab, + &error); + if (helper->store == NULL) { + g_dbus_method_invocation_return_gerror (invocation, error); return; } /* authenticate */ - action_id = fu_main_get_action_id_for_device (helper); + action_id = fu_engine_get_action_id_for_device (priv->engine, + helper->device_id, + helper->store, + helper->flags, + &error); + if (action_id == NULL) { + g_dbus_method_invocation_return_gerror (invocation, error); + return; + } subject = polkit_system_bus_name_new (sender); - polkit_authority_check_authorization (helper->priv->authority, subject, - action_id, - NULL, + polkit_authority_check_authorization (priv->authority, subject, + action_id, NULL, POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION, NULL, - fu_main_check_authorization_cb, + fu_main_authorize_install_cb, helper); return; } - - /* get multiple result objects from a local file */ if (g_strcmp0 (method_name, "GetDetailsLocal") == 0) { GDBusMessage *message; GUnixFDList *fd_list; gint32 fd_handle = 0; gint fd; + g_autoptr(GPtrArray) results = NULL; /* get parameters */ g_variant_get (parameters, "(h)", &fd_handle); @@ -2464,31 +665,30 @@ fu_main_daemon_method_call (GDBusConnection *connection, const gchar *sender, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "invalid handle"); - fu_main_invocation_return_error (priv, invocation, error); + g_dbus_method_invocation_return_gerror (invocation, error); return; } fd = g_unix_fd_list_get (fd_list, 0, &error); if (fd < 0) { - fu_main_invocation_return_error (priv, invocation, error); + g_dbus_method_invocation_return_gerror (invocation, error); return; } - /* get details about the file */ - val = fu_main_get_details_local_from_fd (priv, fd, &error); - if (val == NULL) { - fu_main_invocation_return_error (priv, invocation, error); + /* get details about the file (will close the fd when done) */ + results = fu_engine_get_details_local (priv->engine, fd, &error); + if (results == NULL) { + g_dbus_method_invocation_return_gerror (invocation, error); return; } - fu_main_invocation_return_value (priv, invocation, val); + val = fu_main_result_array_to_variant (results); + g_dbus_method_invocation_return_value (invocation, val); return; } - - /* we suck */ g_set_error (&error, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD, "no such method %s", method_name); - fu_main_invocation_return_error (priv, invocation, error); + g_dbus_method_invocation_return_gerror (invocation, error); } static GVariant * @@ -2503,7 +703,7 @@ fu_main_daemon_get_property (GDBusConnection *connection_, const gchar *sender, return g_variant_new_string (VERSION); if (g_strcmp0 (property_name, "Status") == 0) - return g_variant_new_uint32 (priv->status); + return g_variant_new_uint32 (fu_engine_get_status (priv->engine)); /* return an error */ g_set_error (error, @@ -2514,79 +714,6 @@ fu_main_daemon_get_property (GDBusConnection *connection_, const gchar *sender, return NULL; } -static void -fu_main_plugins_setup (FuMainPrivate *priv) -{ - g_autoptr(AsProfileTask) ptask = NULL; - - ptask = as_profile_start_literal (priv->profile, "FuMain:setup"); - g_assert (ptask != NULL); - for (guint i = 0; i < priv->plugins->len; i++) { - g_autoptr(GError) error = NULL; - g_autoptr(AsProfileTask) ptask2 = NULL; - FuPlugin *plugin = g_ptr_array_index (priv->plugins, i); - ptask2 = as_profile_start (priv->profile, - "FuMain:setup{%s}", - fu_plugin_get_name (plugin)); - g_assert (ptask2 != NULL); - if (!fu_plugin_runner_startup (plugin, &error)) { - fu_plugin_set_enabled (plugin, FALSE); - g_message ("disabling plugin because: %s", error->message); - } - } -} - -static void -fu_main_plugins_coldplug (FuMainPrivate *priv) -{ - g_autoptr(AsProfileTask) ptask = NULL; - - /* don't allow coldplug to be scheduled when in coldplug */ - priv->coldplug_running = TRUE; - - /* prepare */ - for (guint i = 0; i < priv->plugins->len; i++) { - g_autoptr(GError) error = NULL; - FuPlugin *plugin = g_ptr_array_index (priv->plugins, i); - if (!fu_plugin_runner_coldplug_prepare (plugin, &error)) - g_warning ("failed to prepare coldplug: %s", error->message); - } - - /* do this in one place */ - if (priv->coldplug_delay > 0) { - g_debug ("sleeping for %ums", priv->coldplug_delay); - g_usleep (priv->coldplug_delay * 1000); - } - - /* exec */ - ptask = as_profile_start_literal (priv->profile, "FuMain:coldplug"); - g_assert (ptask != NULL); - for (guint i = 0; i < priv->plugins->len; i++) { - g_autoptr(GError) error = NULL; - g_autoptr(AsProfileTask) ptask2 = NULL; - FuPlugin *plugin = g_ptr_array_index (priv->plugins, i); - ptask2 = as_profile_start (priv->profile, - "FuMain:coldplug{%s}", - fu_plugin_get_name (plugin)); - g_assert (ptask2 != NULL); - if (!fu_plugin_runner_coldplug (plugin, &error)) { - fu_plugin_set_enabled (plugin, FALSE); - g_message ("disabling plugin because: %s", error->message); - } - } - - /* cleanup */ - for (guint i = 0; i < priv->plugins->len; i++) { - g_autoptr(GError) error = NULL; - FuPlugin *plugin = g_ptr_array_index (priv->plugins, i); - if (!fu_plugin_runner_coldplug_cleanup (plugin, &error)) - g_warning ("failed to cleanup coldplug: %s", error->message); - } - - /* we can recoldplug from this point on */ - priv->coldplug_running = FALSE; -} - static void fu_main_on_bus_acquired_cb (GDBusConnection *connection, const gchar *name, @@ -2629,7 +756,7 @@ fu_main_on_bus_acquired_cb (GDBusConnection *connection, /* dump startup profile data */ if (fu_debug_is_verbose ()) - as_profile_dump (priv->profile); + fu_engine_profile_dump (priv->engine); } static void @@ -2677,268 +804,6 @@ fu_main_load_introspection (const gchar *filename, GError **error) return g_dbus_node_info_new_for_xml (g_bytes_get_data (data, NULL), error); } -static void -fu_main_plugin_device_added_cb (FuPlugin *plugin, - FuDevice *device, - gpointer user_data) -{ - FuMainPrivate *priv = (FuMainPrivate *) user_data; - FuDeviceItem *item; - GPtrArray *blacklisted_devices; - - /* device has no GUIDs set! */ - if (fu_device_get_guid_default (device) == NULL) { - g_warning ("no GUIDs for device %s [%s]", - fu_device_get_id (device), - fu_device_get_name (device)); - return; - } - - /* is this GUID blacklisted */ - blacklisted_devices = fu_config_get_blacklist_devices (priv->config); - for (guint i = 0; i < blacklisted_devices->len; i++) { - const gchar *guid = g_ptr_array_index (blacklisted_devices, i); - if (g_strcmp0 (guid, fu_device_get_guid_default (device)) == 0) { - g_debug ("%s is blacklisted [%s], ignoring from %s", - fu_device_get_id (device), - fu_device_get_guid_default (device), - fu_plugin_get_name (plugin)); - return; - } - } - - /* remove any fake device */ - item = fu_main_get_item_by_id (priv, fu_device_get_id (device)); - if (item != NULL) { - g_debug ("already added %s by %s, ignoring same device from %s", - fu_device_get_id (item->device), - fu_device_get_plugin (item->device), - fu_plugin_get_name (plugin)); - return; - } - - /* create new device */ - item = g_new0 (FuDeviceItem, 1); - item->device = g_object_ref (device); - item->plugin = g_object_ref (plugin); - g_ptr_array_add (priv->devices, item); - - /* match the metadata at this point so clients can tell if the - * device is worthy */ - fu_main_get_updates_item_update (priv, item); - - /* notify clients */ - fu_main_emit_device_added (priv, item->device); - fu_main_emit_changed (priv); -} - -static void -fu_main_plugin_device_removed_cb (FuPlugin *plugin, - FuDevice *device, - gpointer user_data) -{ - FuMainPrivate *priv = (FuMainPrivate *) user_data; - FuDeviceItem *item; - - item = fu_main_get_item_by_id (priv, fu_device_get_id (device)); - if (item == NULL) { - g_debug ("no device to remove %s", fu_device_get_id (device)); - return; - } - - /* check this came from the same plugin */ - if (g_strcmp0 (fu_plugin_get_name (plugin), - fu_plugin_get_name (item->plugin)) != 0) { - g_debug ("ignoring duplicate removal from %s", - fu_plugin_get_name (plugin)); - return; - } - - /* make the UI update */ - fu_main_emit_device_removed (priv, device); - g_ptr_array_remove (priv->devices, item); - fu_main_emit_changed (priv); -} - -static void -fu_main_plugin_status_changed_cb (FuPlugin *plugin, - FwupdStatus status, - gpointer user_data) -{ - FuMainPrivate *priv = (FuMainPrivate *) user_data; - fu_main_set_status (priv, status); -} - -static void -fu_main_plugin_percentage_changed_cb (FuPlugin *plugin, - guint percentage, - gpointer user_data) -{ - FuMainPrivate *priv = (FuMainPrivate *) user_data; - fu_main_set_percentage (priv, percentage); -} - -static gboolean -fu_main_recoldplug_delay_cb (gpointer user_data) -{ - FuMainPrivate *priv = (FuMainPrivate *) user_data; - g_debug ("performing a recoldplug"); - fu_main_plugins_coldplug (priv); - priv->coldplug_id = 0; - return FALSE; -} - -static void -fu_main_plugin_recoldplug_cb (FuPlugin *plugin, FuMainPrivate *priv) -{ - if (priv->coldplug_running) { - g_warning ("coldplug already running, cannot recoldplug"); - return; - } - g_debug ("scheduling a recoldplug"); - if (priv->coldplug_id != 0) - g_source_remove (priv->coldplug_id); - priv->coldplug_id = g_timeout_add (1500, fu_main_recoldplug_delay_cb, priv); -} - -static void -fu_main_plugin_set_coldplug_delay_cb (FuPlugin *plugin, guint duration, FuMainPrivate *priv) -{ - priv->coldplug_delay = MAX (priv->coldplug_delay, duration); - g_debug ("got coldplug delay of %ums, global maximum is now %ums", - duration, priv->coldplug_delay); -} - -static gboolean -fu_main_load_hwids (FuMainPrivate *priv, GError **error) -{ - g_autoptr(FuHwids) hwids = fu_hwids_new (); - - /* read files in /sys */ - if (!fu_hwids_setup (hwids, NULL, error)) - return FALSE; - - /* add GUIDs */ - for (guint i = 0; i < 15; i++) { - g_autofree gchar *guid = NULL; - g_autofree gchar *key = NULL; - g_autofree gchar *values = NULL; - g_autoptr(GError) error_local = NULL; - - /* get the GUID and add to hash */ - key = g_strdup_printf ("HardwareID-%u", i); - guid = fu_hwids_get_guid (hwids, key, &error_local); - if (guid == NULL) { - g_debug ("%s is not available, %s", key, error_local->message); - continue; - } - g_hash_table_insert (priv->hwids, - g_strdup (guid), - GUINT_TO_POINTER (1)); - - /* show what makes up the GUID */ - values = fu_hwids_get_replace_values (hwids, key, NULL); - g_debug ("{%s} <- %s", guid, values); - } - - return TRUE; -} - -static gboolean -fu_main_load_plugins (FuMainPrivate *priv, GError **error) -{ - const gchar *fn; - g_autoptr(GDir) dir = NULL; - - /* search */ - dir = g_dir_open (PLUGINDIR, 0, error); - if (dir == NULL) - return FALSE; - while ((fn = g_dir_read_name (dir)) != NULL) { - GPtrArray *blacklist; - g_autofree gchar *filename = NULL; - g_autoptr(FuPlugin) plugin = NULL; - g_autoptr(GError) error_local = NULL; - - /* ignore non-plugins */ - if (!g_str_has_suffix (fn, ".so")) - continue; - - /* open module */ - filename = g_build_filename (PLUGINDIR, fn, NULL); - plugin = fu_plugin_new (); - fu_plugin_set_usb_context (plugin, priv->usb_ctx); - fu_plugin_set_hwids (plugin, priv->hwids); - g_debug ("adding plugin %s", filename); - if (!fu_plugin_open (plugin, filename, &error_local)) { - g_warning ("failed to open plugin %s: %s", - filename, error_local->message); - continue; - } - - /* is blacklisted */ - blacklist = fu_config_get_blacklist_plugins (priv->config); - for (guint i = 0; i < blacklist->len; i++) { - const gchar *name = g_ptr_array_index (blacklist, i); - if (g_strcmp0 (name, fu_plugin_get_name (plugin)) == 0) { - fu_plugin_set_enabled (plugin, FALSE); - break; - } - } - if (!fu_plugin_get_enabled (plugin)) { - g_debug ("%s blacklisted by config", - fu_plugin_get_name (plugin)); - continue; - } - - /* watch for changes */ - g_signal_connect (plugin, "device-added", - G_CALLBACK (fu_main_plugin_device_added_cb), - priv); - g_signal_connect (plugin, "device-removed", - G_CALLBACK (fu_main_plugin_device_removed_cb), - priv); - g_signal_connect (plugin, "status-changed", - G_CALLBACK (fu_main_plugin_status_changed_cb), - priv); - g_signal_connect (plugin, "percentage-changed", - G_CALLBACK (fu_main_plugin_percentage_changed_cb), - priv); - g_signal_connect (plugin, "recoldplug", - G_CALLBACK (fu_main_plugin_recoldplug_cb), - priv); - g_signal_connect (plugin, "set-coldplug-delay", - G_CALLBACK (fu_main_plugin_set_coldplug_delay_cb), - priv); - - /* add */ - g_ptr_array_add (priv->plugins, g_object_ref (plugin)); - g_hash_table_insert (priv->plugins_hash, - g_strdup (fu_plugin_get_name (plugin)), - g_object_ref (plugin)); - } - - return TRUE; -} - -/* returns FALSE if any plugins have pending devices to be added */ -static gboolean -fu_main_check_plugins_pending (FuMainPrivate *priv, GError **error) -{ - for (guint i = 0; i < priv->plugins->len; i++) { - FuPlugin *plugin = g_ptr_array_index (priv->plugins, i); - if (fu_plugin_has_device_delay (plugin)) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "%s pending", - fu_plugin_get_name (plugin)); - return FALSE; - } - } - return TRUE; -} - static gboolean fu_main_perhaps_own_name (gpointer user_data) { @@ -2946,7 +811,7 @@ fu_main_perhaps_own_name (gpointer user_data) g_autoptr(GError) error = NULL; /* are any plugins pending */ - if (!fu_main_check_plugins_pending (priv, &error)) { + if (!fu_engine_check_plugins_pending (priv->engine, &error)) { g_debug ("trying again: %s", error->message); return G_SOURCE_CONTINUE; } @@ -2973,53 +838,19 @@ fu_main_private_free (FuMainPrivate *priv) g_bus_unown_name (priv->owner_id); if (priv->proxy_uid != NULL) g_object_unref (priv->proxy_uid); - if (priv->usb_ctx != NULL) - g_object_unref (priv->usb_ctx); - if (priv->config != NULL) - g_object_unref (priv->config); + if (priv->engine != NULL) + g_object_unref (priv->engine); if (priv->connection != NULL) g_object_unref (priv->connection); if (priv->authority != NULL) g_object_unref (priv->authority); - if (priv->profile != NULL) - g_object_unref (priv->profile); - if (priv->store != NULL) - g_object_unref (priv->store); if (priv->introspection_daemon != NULL) g_dbus_node_info_unref (priv->introspection_daemon); - if (priv->pending != NULL) - g_object_unref (priv->pending); - if (priv->coldplug_id != 0) - g_source_remove (priv->coldplug_id); - if (priv->plugins != NULL) - g_ptr_array_unref (priv->plugins); - if (priv->hwids != NULL) - g_hash_table_unref (priv->hwids); - if (priv->plugins_hash != NULL) - g_hash_table_unref (priv->plugins_hash); - g_ptr_array_unref (priv->devices); g_free (priv); } G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuMainPrivate, fu_main_private_free) -static gboolean -fu_main_cleanup_state (GError **error) -{ - const gchar *filenames[] = { - "/var/cache/app-info/xmls/fwupd-verify.xml", - "/var/cache/app-info/xmls/fwupd.xml", - NULL }; - for (guint i = 0; filenames[i] != NULL; i++) { - g_autoptr(GFile) file = g_file_new_for_path (filenames[i]); - if (g_file_query_exists (file, NULL)) { - if (!g_file_delete (file, NULL, error)) - return FALSE; - } - } - return TRUE; -} - int main (int argc, char *argv[]) { @@ -3058,63 +889,35 @@ main (int argc, char *argv[]) /* create new objects */ priv = g_new0 (FuMainPrivate, 1); - priv->config = fu_config_new (); - priv->status = FWUPD_STATUS_IDLE; - priv->percentage = 0; - priv->devices = g_ptr_array_new_with_free_func ((GDestroyNotify) fu_main_item_free); priv->loop = g_main_loop_new (NULL, FALSE); - priv->pending = fu_pending_new (); - priv->profile = as_profile_new (); - /* read config file */ - if (!fu_config_load (priv->config, &error)) { - g_printerr ("Failed to load config: %s\n", error->message); + /* load engine */ + priv->engine = fu_engine_new (); + g_signal_connect (priv->engine, "changed", + G_CALLBACK (fu_main_engine_changed_cb), + priv); + g_signal_connect (priv->engine, "device-added", + G_CALLBACK (fu_main_engine_device_added_cb), + priv); + g_signal_connect (priv->engine, "device-removed", + G_CALLBACK (fu_main_engine_device_removed_cb), + priv); + g_signal_connect (priv->engine, "device-changed", + G_CALLBACK (fu_main_engine_device_changed_cb), + priv); + g_signal_connect (priv->engine, "status-changed", + G_CALLBACK (fu_main_engine_status_changed_cb), + priv); + g_signal_connect (priv->engine, "percentage-changed", + G_CALLBACK (fu_main_engine_percentage_changed_cb), + priv); + if (!fu_engine_load (priv->engine, &error)) { + g_printerr ("Failed to load engine: %s\n", error->message); return EXIT_FAILURE; } - /* load AppStream metadata */ - priv->store = as_store_new (); - as_store_add_filter (priv->store, AS_APP_KIND_FIRMWARE); - if (!fu_main_load_metadata_store (priv, &error)) { - g_printerr ("Failed to load AppStream data: %s\n", error->message); - return EXIT_FAILURE; - } - - /* set shared USB context */ - priv->usb_ctx = g_usb_context_new (&error); - if (priv->usb_ctx == NULL) { - g_printerr ("Failed to get USB context: %s\n", error->message); - return EXIT_FAILURE; - } - - /* load the hwids */ - priv->hwids = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); - if (!fu_main_load_hwids (priv, &error)) { - g_printerr ("Failed to load hwids: %s\n", error->message); - return EXIT_FAILURE; - } - - /* delete old data files */ - if (!fu_main_cleanup_state (&error)) { - g_printerr ("Failed to clean up: %s\n", error->message); - return EXIT_FAILURE; - } - - /* load plugin */ - priv->plugins = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); - priv->plugins_hash = g_hash_table_new_full (g_str_hash, g_str_equal, - g_free, (GDestroyNotify) g_object_unref); - if (!fu_main_load_plugins (priv, &error)) { - g_printerr ("Failed to load plugins: %s\n", error->message); - return EXIT_FAILURE; - } - - /* disable udev? */ - if (!fu_config_get_enable_option_rom (priv->config)) { - FuPlugin *plugin = g_hash_table_lookup (priv->plugins_hash, "udev"); - if (plugin != NULL) - fu_plugin_set_enabled (plugin, FALSE); - } + /* keep polling until all the plugins are ready */ + g_timeout_add (200, fu_main_perhaps_own_name, priv); /* load introspection from file */ priv->introspection_daemon = fu_main_load_introspection (FWUPD_DBUS_INTERFACE ".xml", @@ -3131,14 +934,6 @@ main (int argc, char *argv[]) return EXIT_FAILURE; } - /* add devices */ - fu_main_plugins_setup (priv); - g_usb_context_enumerate (priv->usb_ctx); - fu_main_plugins_coldplug (priv); - - /* keep polling until all the plugins are ready */ - g_timeout_add (200, fu_main_perhaps_own_name, priv); - /* Only timeout and close the mainloop if we have specified it * on the command line */ if (immediate_exit) @@ -3153,4 +948,3 @@ main (int argc, char *argv[]) /* success */ return EXIT_SUCCESS; } - diff --git a/src/meson.build b/src/meson.build index 47deb68ca..6e0378c25 100644 --- a/src/meson.build +++ b/src/meson.build @@ -47,6 +47,7 @@ executable( resources_src, sources : [ 'fu-config.c', + 'fu-engine.c', 'fu-main.c', 'fu-hwids.c', 'fu-debug.c',