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.
This commit is contained in:
Richard Hughes 2017-09-12 12:57:43 +01:00
parent feb038099b
commit 08a37992f9
5 changed files with 207 additions and 5 deletions

View File

@ -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)
{

View File

@ -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;
}

View File

@ -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);

View File

@ -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)

View File

@ -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