From e7e95452fde6a82bb2e2330f4bcc502e92b78a85 Mon Sep 17 00:00:00 2001 From: Richard Hughes Date: Wed, 22 Nov 2017 09:05:53 +0000 Subject: [PATCH] trivial: Move the plugin list to a new object FuEngine is getting somewhat large and complicated, so split out as much plugin list-specific functionality as possible --- src/fu-engine.c | 226 ++++++++++----------------------- src/fu-plugin-list.c | 295 +++++++++++++++++++++++++++++++++++++++++++ src/fu-plugin-list.h | 47 +++++++ src/fu-self-test.c | 77 +++++++++++ src/meson.build | 2 + 5 files changed, 485 insertions(+), 162 deletions(-) create mode 100644 src/fu-plugin-list.c create mode 100644 src/fu-plugin-list.h diff --git a/src/fu-engine.c b/src/fu-engine.c index 58e49b985..3fab25fe3 100644 --- a/src/fu-engine.c +++ b/src/fu-engine.c @@ -43,6 +43,7 @@ #include "fu-keyring.h" #include "fu-pending.h" #include "fu-plugin.h" +#include "fu-plugin-list.h" #include "fu-plugin-private.h" #include "fu-quirks.h" #include "fu-smbios.h" @@ -70,8 +71,7 @@ struct _FuEngine gboolean coldplug_running; guint coldplug_id; guint coldplug_delay; - GPtrArray *plugins; /* of FuPlugin */ - GHashTable *plugins_hash; /* of name : FuPlugin */ + FuPluginList *plugin_list; GPtrArray *supported_guids; FuSmbios *smbios; FuHwids *hwids; @@ -242,21 +242,6 @@ fu_engine_get_item_by_guid (FuEngine *self, const gchar *guid) return NULL; } -static FuPlugin * -fu_engine_get_plugin_by_name (FuEngine *self, const gchar *name, GError **error) -{ - 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; - } - g_set_error (error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "no plugin %s found", name); - return NULL; -} - static void fu_engine_set_release_from_appstream (FuEngine *self, FwupdRelease *rel, @@ -467,7 +452,9 @@ fu_engine_unlock (FuEngine *self, const gchar *device_id, GError **error) return FALSE; /* get the plugin */ - plugin = fu_engine_get_plugin_by_name (self, fu_device_get_plugin (item->device), error); + plugin = fu_plugin_list_find_by_name (self->plugin_list, + fu_device_get_plugin (item->device), + error); if (plugin == NULL) return FALSE; @@ -626,7 +613,9 @@ fu_engine_verify_update (FuEngine *self, const gchar *device_id, GError **error) return FALSE; /* get the plugin */ - plugin = fu_engine_get_plugin_by_name (self, fu_device_get_plugin (item->device), error); + plugin = fu_plugin_list_find_by_name (self->plugin_list, + fu_device_get_plugin (item->device), + error); if (plugin == NULL) return FALSE; @@ -718,7 +707,9 @@ fu_engine_verify (FuEngine *self, const gchar *device_id, GError **error) return FALSE; /* get the plugin */ - plugin = fu_engine_get_plugin_by_name (self, fu_device_get_plugin (item->device), error); + plugin = fu_plugin_list_find_by_name (self->plugin_list, + fu_device_get_plugin (item->device), + error); if (plugin == NULL) return FALSE; @@ -1077,6 +1068,7 @@ fu_engine_install (FuEngine *self, FuDeviceItem *item; FuPlugin *plugin; GBytes *blob_fw; + GPtrArray *plugins; const gchar *tmp; const gchar *version; gboolean is_downgrade; @@ -1247,7 +1239,9 @@ fu_engine_install (FuEngine *self, } /* get the plugin */ - plugin = fu_engine_get_plugin_by_name (self, fu_device_get_plugin (item->device), error); + plugin = fu_plugin_list_find_by_name (self->plugin_list, + fu_device_get_plugin (item->device), + error); if (plugin == NULL) return FALSE; @@ -1281,8 +1275,9 @@ fu_engine_install (FuEngine *self, } /* signal to all the plugins the update is about to happen */ - for (guint j = 0; j < self->plugins->len; j++) { - FuPlugin *plugin_tmp = g_ptr_array_index (self->plugins, j); + plugins = fu_plugin_list_get_all (self->plugin_list); + for (guint j = 0; j < plugins->len; j++) { + FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j); if (!fu_plugin_runner_update_prepare (plugin_tmp, item->device, error)) return FALSE; } @@ -1309,8 +1304,8 @@ fu_engine_install (FuEngine *self, g_warning ("failed to attach device after failed update: %s", error_attach->message); } - for (guint j = 0; j < self->plugins->len; j++) { - FuPlugin *plugin_tmp = g_ptr_array_index (self->plugins, j); + for (guint j = 0; j < plugins->len; j++) { + FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j); g_autoptr(GError) error_local = NULL; if (!fu_plugin_runner_update_cleanup (plugin_tmp, item->device, @@ -1336,8 +1331,8 @@ fu_engine_install (FuEngine *self, return FALSE; /* signal to all the plugins the update has happened */ - for (guint j = 0; j < self->plugins->len; j++) { - FuPlugin *plugin_tmp = g_ptr_array_index (self->plugins, j); + for (guint j = 0; j < plugins->len; j++) { + FuPlugin *plugin_tmp = g_ptr_array_index (plugins, j); g_autoptr(GError) error_local = NULL; if (!fu_plugin_runner_update_cleanup (plugin_tmp, item->device, &error_local)) { g_warning ("failed to update-cleanup: %s", @@ -1396,7 +1391,7 @@ fu_engine_get_item_by_id_fallback_pending (FuEngine *self, const gchar *id, GErr 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, error); + plugin = fu_plugin_list_find_by_name (self->plugin_list, tmp, error); if (plugin == NULL) return NULL; item = fu_engine_add_item (self, dev, plugin); @@ -2446,7 +2441,9 @@ fu_engine_clear_results (FuEngine *self, const gchar *device_id, GError **error) return FALSE; /* get the plugin */ - plugin = fu_engine_get_plugin_by_name (self, fu_device_get_plugin (item->device), error); + plugin = fu_plugin_list_find_by_name (self->plugin_list, + fu_device_get_plugin (item->device), + error); if (plugin == NULL) return FALSE; @@ -2480,7 +2477,9 @@ fu_engine_get_results (FuEngine *self, const gchar *device_id, GError **error) return NULL; /* get the plugin */ - plugin = fu_engine_get_plugin_by_name (self, fu_device_get_plugin (item->device), error); + plugin = fu_plugin_list_find_by_name (self->plugin_list, + fu_device_get_plugin (item->device), + error); if (plugin == NULL) return FALSE; @@ -2494,14 +2493,16 @@ fu_engine_get_results (FuEngine *self, const gchar *device_id, GError **error) static void fu_engine_plugins_setup (FuEngine *self) { + GPtrArray *plugins; 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++) { + plugins = fu_plugin_list_get_all (self->plugin_list); + for (guint i = 0; i < plugins->len; i++) { g_autoptr(GError) error = NULL; g_autoptr(AsProfileTask) ptask2 = NULL; - FuPlugin *plugin = g_ptr_array_index (self->plugins, i); + FuPlugin *plugin = g_ptr_array_index (plugins, i); ptask2 = as_profile_start (self->profile, "FuMain:setup{%s}", fu_plugin_get_name (plugin)); @@ -2516,6 +2517,7 @@ fu_engine_plugins_setup (FuEngine *self) static void fu_engine_plugins_coldplug (FuEngine *self) { + GPtrArray *plugins; g_autoptr(AsProfileTask) ptask = NULL; g_autoptr(GString) str = g_string_new (NULL); @@ -2523,9 +2525,10 @@ fu_engine_plugins_coldplug (FuEngine *self) self->coldplug_running = TRUE; /* prepare */ - for (guint i = 0; i < self->plugins->len; i++) { + plugins = fu_plugin_list_get_all (self->plugin_list); + for (guint i = 0; i < plugins->len; i++) { g_autoptr(GError) error = NULL; - FuPlugin *plugin = g_ptr_array_index (self->plugins, i); + FuPlugin *plugin = g_ptr_array_index (plugins, i); if (!fu_plugin_runner_coldplug_prepare (plugin, &error)) g_warning ("failed to prepare coldplug: %s", error->message); } @@ -2539,10 +2542,10 @@ fu_engine_plugins_coldplug (FuEngine *self) /* exec */ ptask = as_profile_start_literal (self->profile, "FuMain:coldplug"); g_assert (ptask != NULL); - for (guint i = 0; i < self->plugins->len; i++) { + for (guint i = 0; i < plugins->len; i++) { g_autoptr(GError) error = NULL; g_autoptr(AsProfileTask) ptask2 = NULL; - FuPlugin *plugin = g_ptr_array_index (self->plugins, i); + FuPlugin *plugin = g_ptr_array_index (plugins, i); ptask2 = as_profile_start (self->profile, "FuMain:coldplug{%s}", fu_plugin_get_name (plugin)); @@ -2554,16 +2557,16 @@ fu_engine_plugins_coldplug (FuEngine *self) } /* cleanup */ - for (guint i = 0; i < self->plugins->len; i++) { + for (guint i = 0; i < plugins->len; i++) { g_autoptr(GError) error = NULL; - FuPlugin *plugin = g_ptr_array_index (self->plugins, i); + FuPlugin *plugin = g_ptr_array_index (plugins, i); if (!fu_plugin_runner_coldplug_cleanup (plugin, &error)) g_warning ("failed to cleanup coldplug: %s", error->message); } /* print what we do have */ - for (guint i = 0; i < self->plugins->len; i++) { - FuPlugin *plugin = g_ptr_array_index (self->plugins, i); + for (guint i = 0; i < plugins->len; i++) { + FuPlugin *plugin = g_ptr_array_index (plugins, i); if (!fu_plugin_get_enabled (plugin)) continue; g_string_append_printf (str, "%s, ", fu_plugin_get_name (plugin)); @@ -2580,13 +2583,15 @@ fu_engine_plugins_coldplug (FuEngine *self) static void fu_engine_plugin_device_register (FuEngine *self, FuDevice *device) { + GPtrArray *plugins; if (fu_device_has_flag (device, FWUPD_DEVICE_FLAG_REGISTERED)) { g_warning ("already registered %s, ignoring", fu_device_get_id (device)); return; } - for (guint i = 0; i < self->plugins->len; i++) { - FuPlugin *plugin = g_ptr_array_index (self->plugins, i); + plugins = fu_plugin_list_get_all (self->plugin_list); + for (guint i = 0; i < plugins->len; i++) { + FuPlugin *plugin = g_ptr_array_index (plugins, i); fu_plugin_runner_device_register (plugin, device); } fu_device_add_flag (device, FWUPD_DEVICE_FLAG_REGISTERED); @@ -2693,7 +2698,9 @@ fu_engine_plugin_device_removed_cb (FuPlugin *plugin, } /* get the plugin */ - plugin_old = fu_engine_get_plugin_by_name (self, fu_device_get_plugin (item->device), &error); + plugin_old = fu_plugin_list_find_by_name (self->plugin_list, + fu_device_get_plugin (item->device), + &error); if (plugin_old == NULL) { g_debug ("%s", error->message); return; @@ -2762,35 +2769,17 @@ fu_engine_plugin_set_coldplug_delay_cb (FuPlugin *plugin, guint duration, FuEngi duration, self->coldplug_delay); } -static gint -fu_engine_plugin_sort_cb (gconstpointer a, gconstpointer b) -{ - 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; -} - +/* for the self tests to use */ void fu_engine_add_plugin (FuEngine *self, FuPlugin *plugin) { - 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)); + fu_plugin_list_add (self->plugin_list, plugin); } 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 */ @@ -2861,101 +2850,14 @@ fu_engine_load_plugins (FuEngine *self, GError **error) self); /* add */ - fu_engine_add_plugin (self, plugin); + fu_plugin_list_add (self->plugin_list, plugin); } - /* 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, NULL); - 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, NULL); - 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; - } - } - } + /* depsolve into the correct order */ + if (!fu_plugin_list_depsolve (self->plugin_list, error)) + return FALSE; - /* 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, NULL); - 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); + /* success */ return TRUE; } @@ -2971,11 +2873,14 @@ fu_engine_load_plugins (FuEngine *self, GError **error) gboolean fu_engine_check_plugins_pending (FuEngine *self, GError **error) { + GPtrArray *plugins; + 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); + plugins = fu_plugin_list_get_all (self->plugin_list); + for (guint i = 0; i < plugins->len; i++) { + FuPlugin *plugin = g_ptr_array_index (plugins, i); if (fu_plugin_has_device_delay (plugin)) { g_set_error (error, FWUPD_ERROR, @@ -3124,12 +3029,10 @@ fu_engine_init (FuEngine *self) self->hwids = fu_hwids_new (); self->quirks = fu_quirks_new (); self->pending = fu_pending_new (); + self->plugin_list = fu_plugin_list_new (); self->profile = as_profile_new (); self->store = as_store_new (); self->supported_guids = g_ptr_array_new_with_free_func (g_free); - 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 @@ -3142,17 +3045,16 @@ fu_engine_finalize (GObject *obj) if (self->coldplug_id != 0) g_source_remove (self->coldplug_id); - g_hash_table_unref (self->plugins_hash); g_object_unref (self->config); g_object_unref (self->smbios); g_object_unref (self->quirks); g_object_unref (self->hwids); g_object_unref (self->pending); + g_object_unref (self->plugin_list); g_object_unref (self->profile); g_object_unref (self->store); g_ptr_array_unref (self->devices); g_ptr_array_unref (self->supported_guids); - g_ptr_array_unref (self->plugins); G_OBJECT_CLASS (fu_engine_parent_class)->finalize (obj); } diff --git a/src/fu-plugin-list.c b/src/fu-plugin-list.c new file mode 100644 index 000000000..5c0702cb7 --- /dev/null +++ b/src/fu-plugin-list.c @@ -0,0 +1,295 @@ +/* -*- 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 + */ + +#include "config.h" + +#include + +#include "fu-plugin-list.h" +#include "fu-plugin-private.h" + +#include "fwupd-error.h" + +/** + * SECTION:fu-plugin-list + * @short_description: a list of plugins + * + * This list of plugins provides a way to get the specific plugin quickly using + * a hash table and also any plugin-list specific functionality such as + * sorting by dependancy order. + * + * See also: #FuPlugin + */ + +static void fu_plugin_list_finalize (GObject *obj); + +struct _FuPluginList +{ + GObject parent_instance; + GPtrArray *plugins; /* of FuPlugin */ + GHashTable *plugins_hash; /* of name : FuPlugin */ +}; + +G_DEFINE_TYPE (FuPluginList, fu_plugin_list, G_TYPE_OBJECT) + +/** + * fu_plugin_list_get_all: + * @self: A #FuPluginList + * + * Gets all the plugins that have been added. + * + * Returns: (transfer none) (element-type FuPlugin): the plugins + * + * Since: 1.0.2 + **/ +GPtrArray * +fu_plugin_list_get_all (FuPluginList *self) +{ + g_return_val_if_fail (FU_IS_PLUGIN_LIST (self), NULL); + return self->plugins; +} + +/** + * fu_plugin_list_add: + * @self: A #FuPluginList + * @plugin: A #FuPlugin + * + * Adds a plugin to the list. The plugin name is used for a hash key and must + * be set before calling this function. + * + * Since: 1.0.2 + **/ +void +fu_plugin_list_add (FuPluginList *self, FuPlugin *plugin) +{ + g_return_if_fail (FU_IS_PLUGIN_LIST (self)); + g_return_if_fail (FU_IS_PLUGIN (plugin)); + g_return_if_fail (fu_plugin_get_name (plugin) != NULL); + 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)); +} + +/** + * fu_plugin_list_find_by_name: + * @self: A #FuPluginList + * @name: A #FuPlugin name, e.g. "dfu" + * @error: A #GError, or %NULL + * + * Finds a specific plugin using the plugin name. + * + * Returns: (transfer none): a plugin, or %NULL + * + * Since: 1.0.2 + **/ +FuPlugin * +fu_plugin_list_find_by_name (FuPluginList *self, const gchar *name, GError **error) +{ + g_return_val_if_fail (FU_IS_PLUGIN_LIST (self), NULL); + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (error == NULL || *error == NULL, NULL); + 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; + } + g_set_error (error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no plugin %s found", name); + return NULL; +} + +static gint +fu_plugin_list_sort_cb (gconstpointer a, gconstpointer b) +{ + 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; +} + +/** + * fu_plugin_list_depsolve: + * @self: A #FuPluginList + * @error: A #GError, or %NULL + * + * Depsolves the list of plugins into the correct order. Some plugin methods + * are called on all plugins and for some situations the order they are called + * may be important. Use fu_plugin_add_rule() to affect the depsolved order + * if required. + * + * Returns: %TRUE for success, or %FALSE if the set could not be depsolved + * + * Since: 1.0.2 + **/ +gboolean +fu_plugin_list_depsolve (FuPluginList *self, GError **error) +{ + FuPlugin *dep; + GPtrArray *deps; + gboolean changes; + guint dep_loop_check = 0; + + g_return_val_if_fail (FU_IS_PLUGIN_LIST (self), FALSE); + g_return_val_if_fail (error == NULL || *error == NULL, FALSE); + + /* 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_plugin_list_find_by_name (self, plugin_name, NULL); + 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_plugin_list_find_by_name (self, plugin_name, NULL); + 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_literal (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_plugin_list_find_by_name (self, plugin_name, NULL); + 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_plugin_list_sort_cb); + return TRUE; +} + +static void +fu_plugin_list_class_init (FuPluginListClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + object_class->finalize = fu_plugin_list_finalize; +} + +static void +fu_plugin_list_init (FuPluginList *self) +{ + 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_plugin_list_finalize (GObject *obj) +{ + FuPluginList *self = FU_PLUGIN_LIST (obj); + + g_ptr_array_unref (self->plugins); + g_hash_table_unref (self->plugins_hash); + + G_OBJECT_CLASS (fu_plugin_list_parent_class)->finalize (obj); +} + +/** + * fu_plugin_list_new: + * + * Creates a new plugin list. + * + * Returns: (transfer full): a #FuPluginList + * + * Since: 1.0.2 + **/ +FuPluginList * +fu_plugin_list_new (void) +{ + FuPluginList *self; + self = g_object_new (FU_TYPE_PLUGIN_LIST, NULL); + return FU_PLUGIN_LIST (self); +} diff --git a/src/fu-plugin-list.h b/src/fu-plugin-list.h new file mode 100644 index 000000000..cda10a9f0 --- /dev/null +++ b/src/fu-plugin-list.h @@ -0,0 +1,47 @@ +/* -*- 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_PLUGIN_LIST_H +#define __FU_PLUGIN_LIST_H + +G_BEGIN_DECLS + +#include + +#include "fu-plugin.h" + +#define FU_TYPE_PLUGIN_LIST (fu_plugin_list_get_type ()) +G_DECLARE_FINAL_TYPE (FuPluginList, fu_plugin_list, FU, PLUGIN_LIST, GObject) + +FuPluginList *fu_plugin_list_new (void); +void fu_plugin_list_add (FuPluginList *self, + FuPlugin *plugin); +GPtrArray *fu_plugin_list_get_all (FuPluginList *self); +FuPlugin *fu_plugin_list_find_by_name (FuPluginList *self, + const gchar *name, + GError **error); +gboolean fu_plugin_list_depsolve (FuPluginList *self, + GError **error); + +G_END_DECLS + +#endif /* __FU_PLUGIN_LIST_H */ + diff --git a/src/fu-self-test.c b/src/fu-self-test.c index 05735e332..48a00b8a4 100644 --- a/src/fu-self-test.c +++ b/src/fu-self-test.c @@ -36,6 +36,7 @@ #include "fu-keyring.h" #include "fu-pending.h" #include "fu-plugin-private.h" +#include "fu-plugin-list.h" #include "fu-progressbar.h" #include "fu-hwids.h" #include "fu-smbios.h" @@ -694,6 +695,80 @@ fu_plugin_module_func (void) g_unlink (pending_cap); } +static void +fu_plugin_list_func (void) +{ + GPtrArray *plugins; + FuPlugin *plugin; + g_autoptr(FuPluginList) plugin_list = fu_plugin_list_new (); + g_autoptr(FuPlugin) plugin1 = fu_plugin_new (); + g_autoptr(FuPlugin) plugin2 = fu_plugin_new (); + g_autoptr(GError) error = NULL; + + fu_plugin_set_name (plugin1, "plugin1"); + fu_plugin_set_name (plugin2, "plugin2"); + + /* get all the plugins */ + fu_plugin_list_add (plugin_list, plugin1); + fu_plugin_list_add (plugin_list, plugin2); + plugins = fu_plugin_list_get_all (plugin_list); + g_assert_cmpint (plugins->len, ==, 2); + + /* get a single plugin */ + plugin = fu_plugin_list_find_by_name (plugin_list, "plugin1", &error); + g_assert_no_error (error); + g_assert (plugin != NULL); + g_assert_cmpstr (fu_plugin_get_name (plugin), ==, "plugin1"); + + /* does not exist */ + plugin = fu_plugin_list_find_by_name (plugin_list, "nope", &error); + g_assert_error (error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); + g_assert (plugin == NULL); +} + +static void +fu_plugin_list_depsolve_func (void) +{ + GPtrArray *plugins; + FuPlugin *plugin; + gboolean ret; + g_autoptr(FuPluginList) plugin_list = fu_plugin_list_new (); + g_autoptr(FuPlugin) plugin1 = fu_plugin_new (); + g_autoptr(FuPlugin) plugin2 = fu_plugin_new (); + g_autoptr(GError) error = NULL; + + fu_plugin_set_name (plugin1, "plugin1"); + fu_plugin_set_name (plugin2, "plugin2"); + + /* add rule then depsolve */ + fu_plugin_list_add (plugin_list, plugin1); + fu_plugin_list_add (plugin_list, plugin2); + fu_plugin_add_rule (plugin1, FU_PLUGIN_RULE_RUN_AFTER, "plugin2"); + ret = fu_plugin_list_depsolve (plugin_list, &error); + g_assert_no_error (error); + g_assert (ret); + plugins = fu_plugin_list_get_all (plugin_list); + g_assert_cmpint (plugins->len, ==, 2); + plugin = g_ptr_array_index (plugins, 0); + g_assert_cmpstr (fu_plugin_get_name (plugin), ==, "plugin2"); + g_assert_cmpint (fu_plugin_get_order (plugin), ==, 0); + g_assert (fu_plugin_get_enabled (plugin)); + + /* add another rule, then re-depsolve */ + fu_plugin_add_rule (plugin1, FU_PLUGIN_RULE_CONFLICTS, "plugin2"); + ret = fu_plugin_list_depsolve (plugin_list, &error); + g_assert_no_error (error); + g_assert (ret); + plugin = fu_plugin_list_find_by_name (plugin_list, "plugin1", &error); + g_assert_no_error (error); + g_assert (plugin != NULL); + g_assert (fu_plugin_get_enabled (plugin)); + plugin = fu_plugin_list_find_by_name (plugin_list, "plugin2", &error); + g_assert_no_error (error); + g_assert (plugin != NULL); + g_assert (!fu_plugin_get_enabled (plugin)); +} + static void fu_pending_func (void) { @@ -1088,6 +1163,8 @@ main (int argc, char **argv) g_test_add_func ("/fwupd/smbios", fu_smbios_func); g_test_add_func ("/fwupd/smbios3", fu_smbios3_func); g_test_add_func ("/fwupd/pending", fu_pending_func); + g_test_add_func ("/fwupd/plugin-list", fu_plugin_list_func); + g_test_add_func ("/fwupd/plugin-list{depsolve}", fu_plugin_list_depsolve_func); g_test_add_func ("/fwupd/plugin{delay}", fu_plugin_delay_func); g_test_add_func ("/fwupd/plugin{module}", fu_plugin_module_func); g_test_add_func ("/fwupd/plugin{quirks}", fu_plugin_quirks_func); diff --git a/src/meson.build b/src/meson.build index 8d0383be7..f8bdb54bb 100644 --- a/src/meson.build +++ b/src/meson.build @@ -132,6 +132,7 @@ executable( 'fu-keyring.c', 'fu-pending.c', 'fu-plugin.c', + 'fu-plugin-list.c', 'fu-quirks.c', 'fu-smbios.c', 'fu-usb-device.c', @@ -193,6 +194,7 @@ if get_option('enable-tests') 'fu-keyring.c', 'fu-keyring-result.c', 'fu-plugin.c', + 'fu-plugin-list.c', 'fu-progressbar.c', 'fu-quirks.c', 'fu-smbios.c',