From 08a37992f99b97dc082edc717efc7219586df5dc Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Tue, 12 Sep 2017 12:57:43 +0100 Subject: [PATCH] Allow plugins to depend on each other The only things that plugins can declare is that they should be run before, after or never with regard to another plugin. --- plugins/uefi/fu-plugin-uefi.c | 6 ++ src/fu-engine.c | 108 ++++++++++++++++++++++++++++++++-- src/fu-plugin-private.h | 5 ++ src/fu-plugin.c | 74 +++++++++++++++++++++++ src/fu-plugin.h | 19 ++++++ 5 files changed, 207 insertions(+), 5 deletions(-) diff --git a/plugins/uefi/fu-plugin-uefi.c b/plugins/uefi/fu-plugin-uefi.c index 731fd29d8..b073e4458 100644 --- a/plugins/uefi/fu-plugin-uefi.c +++ b/plugins/uefi/fu-plugin-uefi.c @@ -30,6 +30,12 @@ #include "fu-plugin.h" #include "fu-plugin-vfuncs.h" +void +fu_plugin_init (FuPlugin *plugin) +{ + fu_plugin_add_rule (plugin, FU_PLUGIN_RULE_RUN_AFTER, "upower"); +} + static fwup_resource * fu_plugin_uefi_find (fwup_resource_iter *iter, const gchar *guid_str, GError **error) { diff --git a/src/fu-engine.c b/src/fu-engine.c index 425180f0f..a91a76aea 100644 --- a/src/fu-engine.c +++ b/src/fu-engine.c @@ -2661,16 +2661,23 @@ fu_engine_plugin_set_coldplug_delay_cb (FuPlugin *plugin, guint duration, FuEngi static gint fu_engine_plugin_sort_cb (gconstpointer a, gconstpointer b) { - FuPlugin *plugin1 = *((FuPlugin **) a); - FuPlugin *plugin2 = *((FuPlugin **) b); - return g_strcmp0 (fu_plugin_get_name (plugin1), - fu_plugin_get_name (plugin2)); + FuPlugin **pa = (FuPlugin **) a; + FuPlugin **pb = (FuPlugin **) b; + if (fu_plugin_get_order (*pa) < fu_plugin_get_order (*pb)) + return -1; + if (fu_plugin_get_order (*pa) > fu_plugin_get_order (*pb)) + return 1; + return 0; } static gboolean fu_engine_load_plugins (FuEngine *self, GError **error) { + FuPlugin *dep; + GPtrArray *deps; const gchar *fn; + gboolean changes; + guint dep_loop_check = 0; g_autoptr(GDir) dir = NULL; /* search */ @@ -2745,8 +2752,99 @@ fu_engine_load_plugins (FuEngine *self, GError **error) g_strdup (fu_plugin_get_name (plugin)), g_object_ref (plugin)); } - g_ptr_array_sort (self->plugins, fu_engine_plugin_sort_cb); + /* order by deps */ + do { + changes = FALSE; + for (guint i = 0; i < self->plugins->len; i++) { + FuPlugin *plugin = g_ptr_array_index (self->plugins, i); + deps = fu_plugin_get_rules (plugin, FU_PLUGIN_RULE_RUN_AFTER); + for (guint j = 0; j < deps->len && !changes; j++) { + const gchar *plugin_name = g_ptr_array_index (deps, j); + dep = fu_engine_get_plugin_by_name (self, plugin_name); + if (dep == NULL) { + g_debug ("cannot find plugin '%s' " + "requested by '%s'", + plugin_name, + fu_plugin_get_name (plugin)); + continue; + } + if (!fu_plugin_get_enabled (dep)) + continue; + if (fu_plugin_get_order (plugin) <= fu_plugin_get_order (dep)) { + g_debug ("%s [%u] to be ordered after %s [%u] " + "so promoting to [%u]", + fu_plugin_get_name (plugin), + fu_plugin_get_order (plugin), + fu_plugin_get_name (dep), + fu_plugin_get_order (dep), + fu_plugin_get_order (dep) + 1); + fu_plugin_set_order (plugin, fu_plugin_get_order (dep) + 1); + changes = TRUE; + } + } + } + for (guint i = 0; i < self->plugins->len; i++) { + FuPlugin *plugin = g_ptr_array_index (self->plugins, i); + deps = fu_plugin_get_rules (plugin, FU_PLUGIN_RULE_RUN_BEFORE); + for (guint j = 0; j < deps->len && !changes; j++) { + const gchar *plugin_name = g_ptr_array_index (deps, j); + dep = fu_engine_get_plugin_by_name (self, plugin_name); + if (dep == NULL) { + g_debug ("cannot find plugin '%s' " + "requested by '%s'", + plugin_name, + fu_plugin_get_name (plugin)); + continue; + } + if (!fu_plugin_get_enabled (dep)) + continue; + if (fu_plugin_get_order (plugin) >= fu_plugin_get_order (dep)) { + g_debug ("%s [%u] to be ordered before %s [%u] " + "so promoting to [%u]", + fu_plugin_get_name (plugin), + fu_plugin_get_order (plugin), + fu_plugin_get_name (dep), + fu_plugin_get_order (dep), + fu_plugin_get_order (dep) + 1); + fu_plugin_set_order (dep, fu_plugin_get_order (plugin) + 1); + changes = TRUE; + } + } + } + + /* check we're not stuck */ + if (dep_loop_check++ > 100) { + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "got stuck in dep loop"); + return FALSE; + } + } while (changes); + + /* check for conflicts */ + for (guint i = 0; i < self->plugins->len; i++) { + FuPlugin *plugin = g_ptr_array_index (self->plugins, i); + if (!fu_plugin_get_enabled (plugin)) + continue; + deps = fu_plugin_get_rules (plugin, FU_PLUGIN_RULE_CONFLICTS); + for (guint j = 0; j < deps->len && !changes; j++) { + const gchar *plugin_name = g_ptr_array_index (deps, j); + dep = fu_engine_get_plugin_by_name (self, plugin_name); + if (dep == NULL) + continue; + if (!fu_plugin_get_enabled (dep)) + continue; + g_debug ("disabling %s as conflicts with %s", + fu_plugin_get_name (dep), + fu_plugin_get_name (plugin)); + fu_plugin_set_enabled (dep, FALSE); + } + } + + /* sort by order */ + g_ptr_array_sort (self->plugins, fu_engine_plugin_sort_cb); return TRUE; } diff --git a/src/fu-plugin-private.h b/src/fu-plugin-private.h index a59332ea4..b1a36dbd8 100644 --- a/src/fu-plugin-private.h +++ b/src/fu-plugin-private.h @@ -39,6 +39,11 @@ void fu_plugin_set_supported (FuPlugin *plugin, GPtrArray *supported_guids); void fu_plugin_set_smbios (FuPlugin *plugin, FuSmbios *smbios); +guint fu_plugin_get_order (FuPlugin *plugin); +void fu_plugin_set_order (FuPlugin *plugin, + guint order); +GPtrArray *fu_plugin_get_rules (FuPlugin *plugin, + FuPluginRule rule); gboolean fu_plugin_open (FuPlugin *plugin, const gchar *filename, GError **error); diff --git a/src/fu-plugin.c b/src/fu-plugin.c index 7525514fa..22fecacf8 100644 --- a/src/fu-plugin.c +++ b/src/fu-plugin.c @@ -43,6 +43,8 @@ typedef struct { GModule *module; GUsbContext *usb_ctx; gboolean enabled; + guint order; + GPtrArray *rules[FU_PLUGIN_RULE_LAST]; gchar *name; FuHwids *hwids; GPtrArray *supported_guids; @@ -1210,6 +1212,73 @@ fu_plugin_runner_get_results (FuPlugin *plugin, FuDevice *device, GError **error return TRUE; } +/** + * fu_plugin_get_order: + * @plugin: a #FuPlugin + * + * Gets the plugin order, where higher numbers are run after lower + * numbers. + * + * Returns: the integer value + **/ +guint +fu_plugin_get_order (FuPlugin *plugin) +{ + FuPluginPrivate *priv = fu_plugin_get_instance_private (plugin); + return priv->order; +} + +/** + * fu_plugin_set_order: + * @plugin: a #FuPlugin + * @order: a integer value + * + * Sets the plugin order, where higher numbers are run after lower + * numbers. + **/ +void +fu_plugin_set_order (FuPlugin *plugin, guint order) +{ + FuPluginPrivate *priv = fu_plugin_get_instance_private (plugin); + priv->order = order; +} + +/** + * fu_plugin_add_rule: + * @plugin: a #FuPlugin + * @rule: a #FuPluginRule, e.g. %FU_PLUGIN_RULE_CONFLICTS + * @name: a plugin name, e.g. "upower" + * + * If the plugin name is found, the rule will be used to sort the plugin list, + * for example the plugin specified by @name will be ordered after this plugin + * when %FU_PLUGIN_RULE_RUN_AFTER is used. + * + * NOTE: The depsolver is iterative and may not solve overly-complicated rules; + * If depsolving fails then fwupd will not start. + **/ +void +fu_plugin_add_rule (FuPlugin *plugin, FuPluginRule rule, const gchar *name) +{ + FuPluginPrivate *priv = fu_plugin_get_instance_private (plugin); + g_ptr_array_add (priv->rules[rule], g_strdup (name)); +} + +/** + * fu_plugin_get_rules: + * @plugin: a #FuPlugin + * @rule: a #FuPluginRule, e.g. %FU_PLUGIN_RULE_CONFLICTS + * + * Gets the plugin IDs that should be run after this plugin. + * + * Returns: (element-type utf8) (transfer none): the list of plugin names, e.g. ['appstream'] + **/ +GPtrArray * +fu_plugin_get_rules (FuPlugin *plugin, FuPluginRule rule) +{ + FuPluginPrivate *priv = fu_plugin_get_instance_private (plugin); + return priv->rules[rule]; +} + static void fu_plugin_class_init (FuPluginClass *klass) { @@ -1267,6 +1336,8 @@ fu_plugin_init (FuPlugin *plugin) priv->devices = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_object_unref); priv->devices_delay = g_hash_table_new (g_direct_hash, g_direct_equal); + for (guint i = 0; i < FU_PLUGIN_RULE_LAST; i++) + priv->rules[i] = g_ptr_array_new_with_free_func (g_free); } static void @@ -1285,6 +1356,9 @@ fu_plugin_finalize (GObject *object) } } + for (guint i = 0; i < FU_PLUGIN_RULE_LAST; i++) + g_ptr_array_unref (priv->rules[i]); + if (priv->usb_ctx != NULL) g_object_unref (priv->usb_ctx); if (priv->hwids != NULL) diff --git a/src/fu-plugin.h b/src/fu-plugin.h index edbc931fc..4852f698f 100644 --- a/src/fu-plugin.h +++ b/src/fu-plugin.h @@ -63,6 +63,22 @@ typedef enum { FU_PLUGIN_VERIFY_FLAG_LAST } FuPluginVerifyFlags; +/** + * FuPluginRule: + * @FU_PLUGIN_RULE_CONFLICTS: The plugin conflicts with another + * @FU_PLUGIN_RULE_RUN_AFTER: Order the plugin after another + * @FU_PLUGIN_RULE_RUN_BEFORE: Order the plugin before another + * + * The rules used for ordering plugins. + * Plugins are expected to add rules in fu_plugin_initialize(). + **/ +typedef enum { + FU_PLUGIN_RULE_CONFLICTS, + FU_PLUGIN_RULE_RUN_AFTER, + FU_PLUGIN_RULE_RUN_BEFORE, + FU_PLUGIN_RULE_LAST +} FuPluginRule; + typedef struct FuPluginData FuPluginData; /* for plugins to use */ @@ -108,6 +124,9 @@ const gchar *fu_plugin_get_smbios_string (FuPlugin *plugin, guint8 offset); GBytes *fu_plugin_get_smbios_data (FuPlugin *plugin, guint8 structure_type); +void fu_plugin_add_rule (FuPlugin *plugin, + FuPluginRule rule, + const gchar *name); G_END_DECLS