diff --git a/meson.build b/meson.build index e3a982e32..51599f52d 100644 --- a/meson.build +++ b/meson.build @@ -200,6 +200,9 @@ if get_option('plugin_uefi') if fwup.version().version_compare('>= 11') conf.set('HAVE_FWUP_GET_ESP_MOUNTPOINT', '1') endif + if fwup.version().version_compare('>= 12') + conf.set('HAVE_FWUP_VERSION', '1') + endif efivar = dependency('efivar') conf.set_quoted('EFIVAR_LIBRARY_VERSION', efivar.version()) conf.set_quoted('LIBFWUP_LIBRARY_VERSION', fwup.version()) diff --git a/plugins/uefi/fu-plugin-uefi.c b/plugins/uefi/fu-plugin-uefi.c index 284949d99..09d3102d6 100644 --- a/plugins/uefi/fu-plugin-uefi.c +++ b/plugins/uefi/fu-plugin-uefi.c @@ -53,11 +53,18 @@ void fu_plugin_init (FuPlugin *plugin) { FuPluginData *data = fu_plugin_alloc_data (plugin, sizeof (FuPluginData)); +#ifdef HAVE_FWUP_VERSION + g_autofree gchar *version_str = NULL; +#endif data->ux_capsule = FALSE; data->esp_path = NULL; fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_RUN_AFTER, "upower"); fu_plugin_add_report_metadata (plugin, "FwupdateVersion", LIBFWUP_LIBRARY_VERSION); fu_plugin_add_report_metadata (plugin, "EfivarVersion", EFIVAR_LIBRARY_VERSION); +#ifdef HAVE_FWUP_VERSION + version_str = g_strdup_printf ("%i", fwup_version ()); + fu_plugin_add_runtime_version (plugin, "com.redhat.fwupdate", version_str); +#endif } void diff --git a/src/fu-engine.c b/src/fu-engine.c index 9a489c5f4..7d4406a92 100644 --- a/src/fu-engine.c +++ b/src/fu-engine.c @@ -79,6 +79,7 @@ struct _FuEngine FuSmbios *smbios; FuHwids *hwids; FuQuirks *quirks; + GHashTable *runtime_versions; }; enum { @@ -95,11 +96,6 @@ static guint signals[SIGNAL_LAST] = { 0 }; G_DEFINE_TYPE (FuEngine, fu_engine, G_TYPE_OBJECT) -#define FU_ENGINE_REQUIREMENT_FIRMWARE_RUNTIME NULL /* yes, NULL */ -#define FU_ENGINE_REQUIREMENT_FIRMWARE_BOOTLOADER "bootloader" -#define FU_ENGINE_REQUIREMENT_FIRMWARE_VENDOR "vendor-id" -#define FU_ENGINE_REQUIREMENT_ID_FWUPD "org.freedesktop.fwupd" - static void fu_engine_emit_changed (FuEngine *self) { @@ -898,35 +894,15 @@ _as_store_get_apps_by_provide (AsStore *store, AsProvideKind kind, const gchar * } static gboolean -fu_engine_check_version_requirement (AsApp *app, - AsRequireKind kind, - const gchar *id, - const gchar *version, - GError **error) +fu_engine_check_requirement_firmware (FuEngine *self, AsRequire *req, + FuDevice *device, GError **error) { - AsRequire *req; g_autoptr(GError) error_local = NULL; - /* check args */ - if (version == NULL) { - g_debug ("no parameter 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_local)) { - - /* firmware */ - if (g_strcmp0 (id, FU_ENGINE_REQUIREMENT_FIRMWARE_RUNTIME) == 0) { + /* old firmware version */ + if (as_require_get_value (req) == NULL) { + const gchar *version = fu_device_get_version (device); + if (!as_require_version_compare (req, version, &error_local)) { if (as_require_get_compare (req) == AS_REQUIRE_COMPARE_GE) { g_set_error (error, FWUPD_ERROR, @@ -942,27 +918,13 @@ fu_engine_check_version_requirement (AsApp *app, } return FALSE; } + return TRUE; + } - /* fwupd */ - if (g_strcmp0 (id, FU_ENGINE_REQUIREMENT_ID_FWUPD) == 0) { - if (as_require_get_compare (req) == AS_REQUIRE_COMPARE_GE) { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "Not compatible with fwupd version %s, requires >= %s", - version, as_require_get_version (req)); - } else { - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "Not compatible with fwupd version: %s", - error_local->message); - } - return FALSE; - } - - /* bootloader */ - if (g_strcmp0 (id, FU_ENGINE_REQUIREMENT_FIRMWARE_BOOTLOADER) == 0) { + /* bootloader version */ + if (g_strcmp0 (as_require_get_value (req), "bootloader") == 0) { + const gchar *version = fu_device_get_version_bootloader (device); + if (!as_require_version_compare (req, version, &error_local)) { if (as_require_get_compare (req) == AS_REQUIRE_COMPARE_GE) { g_set_error (error, FWUPD_ERROR, @@ -978,9 +940,13 @@ fu_engine_check_version_requirement (AsApp *app, } return FALSE; } + return TRUE; + } - /* vendor */ - if (g_strcmp0 (id, FU_ENGINE_REQUIREMENT_FIRMWARE_VENDOR) == 0) { + /* vendor ID */ + if (g_strcmp0 (as_require_get_value (req), "vendor-id") == 0) { + const gchar *version = fu_device_get_vendor_id (device); + if (!as_require_version_compare (req, version, &error_local)) { if (as_require_get_compare (req) == AS_REQUIRE_COMPARE_GE) { g_set_error (error, FWUPD_ERROR, @@ -996,88 +962,105 @@ fu_engine_check_version_requirement (AsApp *app, } return FALSE; } - - /* anything else */ - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "Not compatible with %s: %s", - id, error_local->message); - return FALSE; + return TRUE; } - /* 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; + /* not supported */ + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "cannot handle firmware requirement %s", + as_require_get_value (req)); + return FALSE; } static gboolean -fu_engine_check_hardware_requirement (FuEngine *self, AsApp *app, GError **error) +fu_engine_check_requirement_id (FuEngine *self, AsRequire *req, GError **error) { - GPtrArray *requires = as_app_get_requires (app); - - /* check each HWID requirement */ - for (guint i = 0; i < requires->len; i++) { - AsRequire *req = g_ptr_array_index (requires, i); - if (as_require_get_kind (req) != AS_REQUIRE_KIND_HARDWARE) - continue; - if (!fu_hwids_has_guid (self->hwids, as_require_get_value (req))) { + g_autoptr(GError) error_local = NULL; + const gchar *version = g_hash_table_lookup (self->runtime_versions, + as_require_get_value (req)); + if (version == NULL) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no version available for %s", + as_require_get_value (req)); + return FALSE; + } + if (!as_require_version_compare (req, version, &error_local)) { + if (as_require_get_compare (req) == AS_REQUIRE_COMPARE_GE) { g_set_error (error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, - "no HWIDs matched %s", - as_require_get_value (req)); - return FALSE; + "Not compatible with %s version %s, requires >= %s", + as_require_get_value (req), version, + as_require_get_version (req)); + } else { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "Not compatible with %s version: %s", + as_require_get_value (req), error_local->message); } - g_debug ("HWID provided %s", as_require_get_value (req)); + 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, as_require_get_value (req)); return TRUE; } static gboolean +fu_engine_check_requirement_hardware (FuEngine *self, AsRequire *req, GError **error) +{ + if (!fu_hwids_has_guid (self->hwids, as_require_get_value (req))) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no HWIDs matched %s", + as_require_get_value (req)); + return FALSE; + } + g_debug ("HWID provided %s", as_require_get_value (req)); + return TRUE; +} + +static gboolean +fu_engine_check_requirement (FuEngine *self, AsRequire *req, FuDevice *device, GError **error) +{ + /* ensure component requirement */ + if (as_require_get_kind (req) == AS_REQUIRE_KIND_ID) + return fu_engine_check_requirement_id (self, req, error); + + /* ensure firmware requirement */ + if (device != NULL && as_require_get_kind (req) == AS_REQUIRE_KIND_FIRMWARE) + return fu_engine_check_requirement_firmware (self, req, device, error); + + /* ensure hardware requirement */ + if (as_require_get_kind (req) == AS_REQUIRE_KIND_HARDWARE) + return fu_engine_check_requirement_hardware (self, req, error); + + /* not supported */ + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "cannot handle requirement type %s", + as_require_kind_to_string (as_require_get_kind (req))); + return FALSE; +} + +gboolean fu_engine_check_requirements (FuEngine *self, AsApp *app, FuDevice *device, GError **error) { - /* make sure requirements are satisfied */ - if (!fu_engine_check_version_requirement (app, - AS_REQUIRE_KIND_ID, - FU_ENGINE_REQUIREMENT_ID_FWUPD, - VERSION, - error)) { - return FALSE; + GPtrArray *reqs = as_app_get_requires (app); + for (guint i = 0; i < reqs->len; i++) { + AsRequire *req = g_ptr_array_index (reqs, i); + if (!fu_engine_check_requirement (self, req, device, error)) + return FALSE; } - if (!fu_engine_check_hardware_requirement (self, app, error)) - return FALSE; - - if (device != NULL) { - if (!fu_engine_check_version_requirement (app, - AS_REQUIRE_KIND_FIRMWARE, - FU_ENGINE_REQUIREMENT_FIRMWARE_RUNTIME, - fu_device_get_version (device), - error)) { - return FALSE; - } - if (!fu_engine_check_version_requirement (app, - AS_REQUIRE_KIND_FIRMWARE, - FU_ENGINE_REQUIREMENT_FIRMWARE_BOOTLOADER, - fu_device_get_version_bootloader (device), - error)) { - return FALSE; - } - if (!fu_engine_check_version_requirement (app, - AS_REQUIRE_KIND_FIRMWARE, - FU_ENGINE_REQUIREMENT_FIRMWARE_VENDOR, - fu_device_get_vendor_id (device), - error)) { - return FALSE; - } - } - - /* success */ return TRUE; } @@ -3158,6 +3141,7 @@ fu_engine_load_plugins (FuEngine *self, GError **error) fu_plugin_set_smbios (plugin, self->smbios); fu_plugin_set_supported (plugin, self->supported_guids); fu_plugin_set_quirks (plugin, self->quirks); + fu_plugin_set_runtime_versions (plugin, self->runtime_versions); g_debug ("adding plugin %s", filename); if (!fu_plugin_open (plugin, filename, &error_local)) { g_warning ("failed to open plugin %s: %s", @@ -3570,6 +3554,16 @@ fu_engine_class_init (FuEngineClass *klass) G_TYPE_NONE, 1, G_TYPE_UINT); } +void +fu_engine_add_runtime_version (FuEngine *self, + const gchar *component_id, + const gchar *version) +{ + g_hash_table_insert (self->runtime_versions, + g_strdup (component_id), + g_strdup (version)); +} + static void fu_engine_init (FuEngine *self) { @@ -3585,6 +3579,16 @@ fu_engine_init (FuEngine *self) self->profile = as_profile_new (); self->store = as_store_new (); self->supported_guids = g_ptr_array_new_with_free_func (g_free); + self->runtime_versions = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); + + /* add some runtime versions of things the daemon depends on */ + fu_engine_add_runtime_version (self, "org.freedesktop.fwupd", VERSION); +#if AS_CHECK_VERSION(0,7,8) + fu_engine_add_runtime_version (self, "org.freedesktop.appstream-glib", as_version_string ()); +#endif +#if G_USB_CHECK_VERSION(0,3,1) + fu_engine_add_runtime_version (self, "org.freedesktop.gusb", g_usb_version_string ()); +#endif } static void @@ -3607,6 +3611,7 @@ fu_engine_finalize (GObject *obj) g_object_unref (self->store); g_object_unref (self->device_list); g_ptr_array_unref (self->supported_guids); + g_hash_table_unref (self->runtime_versions); G_OBJECT_CLASS (fu_engine_parent_class)->finalize (obj); } diff --git a/src/fu-engine.h b/src/fu-engine.h index 707bed965..0f418d054 100644 --- a/src/fu-engine.h +++ b/src/fu-engine.h @@ -111,6 +111,13 @@ void fu_engine_add_device (FuEngine *self, FuDevice *device); void fu_engine_add_plugin (FuEngine *self, FuPlugin *plugin); +void fu_engine_add_runtime_version (FuEngine *self, + const gchar *component_id, + const gchar *version); +gboolean fu_engine_check_requirements (FuEngine *self, + AsApp *app, + FuDevice *device, + GError **error); G_END_DECLS diff --git a/src/fu-plugin-private.h b/src/fu-plugin-private.h index a80ea5dd1..d89148f13 100644 --- a/src/fu-plugin-private.h +++ b/src/fu-plugin-private.h @@ -40,6 +40,8 @@ void fu_plugin_set_supported (FuPlugin *plugin, GPtrArray *supported_guids); void fu_plugin_set_quirks (FuPlugin *plugin, FuQuirks *quirks); +void fu_plugin_set_runtime_versions (FuPlugin *plugin, + GHashTable *runtime_versions); void fu_plugin_set_smbios (FuPlugin *plugin, FuSmbios *smbios); guint fu_plugin_get_order (FuPlugin *plugin); diff --git a/src/fu-plugin.c b/src/fu-plugin.c index f3cb3303a..af6222aba 100644 --- a/src/fu-plugin.c +++ b/src/fu-plugin.c @@ -57,6 +57,7 @@ typedef struct { gchar *name; FuHwids *hwids; FuQuirks *quirks; + GHashTable *runtime_versions; GPtrArray *supported_guids; FuSmbios *smbios; GHashTable *devices; /* platform_id:GObject */ @@ -660,6 +661,34 @@ fu_plugin_get_quirks (FuPlugin *plugin) return priv->quirks; } +void +fu_plugin_set_runtime_versions (FuPlugin *plugin, GHashTable *runtime_versions) +{ + FuPluginPrivate *priv = GET_PRIVATE (plugin); + priv->runtime_versions = g_hash_table_ref (runtime_versions); +} + +/** + * fu_plugin_add_runtime_version: + * @plugin: A #FuPlugin + * @component_id: An AppStream component id, e.g. "org.gnome.Software" + * @version: A version string, e.g. "1.2.3" + * + * Sets a runtime version of a specific dependancy. + * + * Since: 1.0.7 + **/ +void +fu_plugin_add_runtime_version (FuPlugin *plugin, + const gchar *component_id, + const gchar *version) +{ + FuPluginPrivate *priv = GET_PRIVATE (plugin); + g_hash_table_insert (priv->runtime_versions, + g_strdup (component_id), + g_strdup (version)); +} + /** * fu_plugin_lookup_quirk_by_id: * @plugin: A #FuPlugin diff --git a/src/fu-plugin.h b/src/fu-plugin.h index 700076fb3..3ceffaadb 100644 --- a/src/fu-plugin.h +++ b/src/fu-plugin.h @@ -149,6 +149,10 @@ void fu_plugin_add_report_metadata (FuPlugin *plugin, const gchar *value); gchar *fu_plugin_get_config_value (FuPlugin *plugin, const gchar *key); +void fu_plugin_add_runtime_version (FuPlugin *plugin, + const gchar *component_id, + const gchar *version); + G_END_DECLS #endif /* __FU_PLUGIN_H */ diff --git a/src/fu-self-test.c b/src/fu-self-test.c index d51cc1c7c..00e029642 100644 --- a/src/fu-self-test.c +++ b/src/fu-self-test.c @@ -52,6 +52,120 @@ #include "fu-keyring-pkcs7.h" #endif +static void +fu_engine_requirements_missing_func (void) +{ + gboolean ret; + g_autoptr(AsApp) app = as_app_new (); + g_autoptr(AsRequire) req = as_require_new (); + g_autoptr(FuEngine) engine = fu_engine_new (); + g_autoptr(GError) error = NULL; + + /* set up a dummy version */ + fu_engine_add_runtime_version (engine, "org.test.dummy", "1.2.3"); + + /* make the component require one thing */ + as_require_set_kind (req, AS_REQUIRE_KIND_ID); + as_require_set_compare (req, AS_REQUIRE_COMPARE_GE); + as_require_set_version (req, "1.2.3"); + as_require_set_value (req, "not.going.to.exist"); + as_app_add_require (app, req); + + /* check this fails */ + ret = fu_engine_check_requirements (engine, app, NULL, &error); + g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); + g_assert (!ret); +} + +static void +fu_engine_requirements_unsupported_func (void) +{ + gboolean ret; + g_autoptr(AsApp) app = as_app_new (); + g_autoptr(AsRequire) req = as_require_new (); + g_autoptr(FuEngine) engine = fu_engine_new (); + g_autoptr(GError) error = NULL; + + /* set up a dummy version */ + fu_engine_add_runtime_version (engine, "org.test.dummy", "1.2.3"); + + /* make the component require one thing that we don't support */ + as_require_set_kind (req, AS_REQUIRE_KIND_LAST); + as_require_set_compare (req, AS_REQUIRE_COMPARE_GE); + as_require_set_version (req, "2.6.0"); + as_app_add_require (app, req); + + /* check this fails */ + ret = fu_engine_check_requirements (engine, app, NULL, &error); + g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); + g_assert (!ret); +} + +static void +fu_engine_requirements_func (void) +{ + gboolean ret; + g_autoptr(AsApp) app = as_app_new (); + g_autoptr(AsRequire) req = as_require_new (); + g_autoptr(FuEngine) engine = fu_engine_new (); + g_autoptr(GError) error = NULL; + + /* set up some dummy versions */ + fu_engine_add_runtime_version (engine, "org.test.dummy", "1.2.3"); + fu_engine_add_runtime_version (engine, "com.hughski.colorhug", "7.8.9"); + + /* make the component require one thing */ + as_require_set_kind (req, AS_REQUIRE_KIND_ID); + as_require_set_compare (req, AS_REQUIRE_COMPARE_GE); + as_require_set_version (req, "1.2.3"); + as_require_set_value (req, "org.test.dummy"); + as_app_add_require (app, req); + + /* check this passes */ + ret = fu_engine_check_requirements (engine, app, NULL, &error); + g_assert_no_error (error); + g_assert (ret); +} + +static void +fu_engine_requirements_device_func (void) +{ + gboolean ret; + g_autoptr(AsApp) app = as_app_new (); + g_autoptr(AsRequire) req1 = as_require_new (); + g_autoptr(AsRequire) req2 = as_require_new (); + g_autoptr(AsRequire) req3 = as_require_new (); + g_autoptr(FuDevice) device = fu_device_new (); + g_autoptr(FuEngine) engine = fu_engine_new (); + g_autoptr(GError) error = NULL; + + /* set up a dummy device */ + fu_device_set_version (device, "1.2.3"); + fu_device_set_version_bootloader (device, "4.5.6"); + fu_device_set_vendor_id (device, "FFFF"); + + /* make the component require three things */ + as_require_set_kind (req1, AS_REQUIRE_KIND_FIRMWARE); + as_require_set_compare (req1, AS_REQUIRE_COMPARE_GE); + as_require_set_version (req1, "1.2.3"); + as_app_add_require (app, req1); + as_require_set_kind (req2, AS_REQUIRE_KIND_FIRMWARE); + as_require_set_compare (req2, AS_REQUIRE_COMPARE_EQ); + as_require_set_version (req2, "4.5.6"); + as_require_set_value (req2, "bootloader"); + as_app_add_require (app, req3); + as_require_set_kind (req3, AS_REQUIRE_KIND_FIRMWARE); + as_require_set_compare (req3, AS_REQUIRE_COMPARE_EQ); + as_require_set_version (req3, "FFFF"); + as_require_set_value (req3, "vendor-id"); + as_app_add_require (app, req3); + + /* check this passes */ + ret = fu_engine_check_requirements (engine, app, device, &error); + g_assert_no_error (error); + g_assert (ret); +} + static void fu_engine_partial_hash_func (void) { @@ -2004,6 +2118,10 @@ main (int argc, char **argv) g_test_add_func ("/fwupd/engine{require-hwid}", fu_engine_require_hwid_func); g_test_add_func ("/fwupd/engine{partial-hash}", fu_engine_partial_hash_func); g_test_add_func ("/fwupd/engine{downgrade}", fu_engine_downgrade_func); + g_test_add_func ("/fwupd/engine{requirements-success}", fu_engine_requirements_func); + g_test_add_func ("/fwupd/engine{requirements-missing}", fu_engine_requirements_missing_func); + g_test_add_func ("/fwupd/engine{requirements-unsupported}", fu_engine_requirements_unsupported_func); + g_test_add_func ("/fwupd/engine{requirements-device}", fu_engine_requirements_device_func); g_test_add_func ("/fwupd/hwids", fu_hwids_func); g_test_add_func ("/fwupd/smbios", fu_smbios_func); g_test_add_func ("/fwupd/smbios3", fu_smbios3_func);