mirror of
https://git.proxmox.com/git/fwupd
synced 2025-05-29 18:43:18 +00:00

FuEngine is getting somewhat large and complicated, so split out as much plugin list-specific functionality as possible
296 lines
8.3 KiB
C
296 lines
8.3 KiB
C
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
|
|
*
|
|
* Copyright (C) 2017 Richard Hughes <richard@hughsie.com>
|
|
*
|
|
* 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 <glib-object.h>
|
|
|
|
#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);
|
|
}
|