* Copyright (C) 2011-2021 Gary Kramlich <grim@reaperworld.com> * 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 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, see <https://www.gnu.org/licenses/>. #include <glib/gi18n-lib.h> #include <gplugin/gplugin-core.h> #include <gplugin/gplugin-file-tree.h> #include <gplugin/gplugin-manager.h> #include <gplugin/gplugin-native-loader.h> #include <gplugin/gplugin-private.h> * SECTION:gplugin-manager * @Short_description: API for managing plugins * The manager is used to manager all plugins in GPlugin. This includes * loading, unloading, querying, checking for new plugins, and so on. * GPluginManagerForeachFunc: * @id: The id of the plugin. * @plugins: A #GSList of each plugin that has the id @id. * @data: User data passed to gplugin_manager_foreach(). * A callback function for gplugin_manager_foreach(). * @loading_plugin: Signal emitted before a plugin is loaded. * @loaded_plugin: Signal emitted after a plugin is loaded. * @load_failed: Signal emitted when a plugin fails to load. * @unloading_plugin: Signal emitted before a plugin is unloaded. * @unloaded_plugin: Signal emitted after a plugin is unloaded. * @unload_plugin_failed: Signal emitted when a plugin fails to unload. * Virtual function table for #GPluginManager. /****************************************************************************** *****************************************************************************/ /****************************************************************************** *****************************************************************************/ GHashTable *plugins_filename_view; GHashTable *loaders_by_extension; G_DEFINE_TYPE_WITH_PRIVATE(GPluginManager, gplugin_manager, G_TYPE_OBJECT); /****************************************************************************** *****************************************************************************/ GPluginManager *default_manager = NULL; static guint signals[N_SIGNALS] = { const gchar *dependency_pattern = "^(?P<id>.+?)((?P<op>\\<=|\\<|==|=|\\>=|\\>)(?P<version>.+))?$"; GRegex *dependency_regex = NULL; /****************************************************************************** *****************************************************************************/ gplugin_manager_str_hash(gconstpointer v) gplugin_manager_remove_list_value( G_GNUC_UNUSED gpointer k, G_GNUC_UNUSED gpointer d) for(l = (GSList *)v; l; l = l->next) { if(l->data && G_IS_OBJECT(l->data)) g_object_unref(G_OBJECT(l->data)); g_slist_free((GSList *)v); * gplugin_manager_find_loader_by_type: * @manager: The #GPluginManager instance. * @type: The #GType of the loader to find. * Looks up a #GPluginLoader instance by its type. * Returns: (transfer none): The #GPluginLoader instance or %NULL. gplugin_manager_find_loader_by_type(GPluginManager *manager, GType type) GPluginManagerPrivate *priv = NULL; g_return_val_if_fail(GPLUGIN_IS_MANAGER(manager), NULL); g_return_val_if_fail(g_type_is_a(type, GPLUGIN_TYPE_LOADER), NULL); priv = gplugin_manager_get_instance_private(manager); for(l = priv->loaders; l; l = l->next) { if(G_OBJECT_TYPE(l->data) == type) { return GPLUGIN_LOADER(l->data); gplugin_manager_foreach_unload_plugin( G_GNUC_UNUSED gpointer data) gchar *id = (gchar *)key; for(l = (GList *)value; l; l = l->next) { GPluginPlugin *plugin = GPLUGIN_PLUGIN(l->data); GPluginLoader *loader = NULL; if(gplugin_plugin_get_state(plugin) != GPLUGIN_PLUGIN_STATE_LOADED) { loader = gplugin_plugin_get_loader(plugin); if(!gplugin_loader_unload_plugin(loader, plugin, &error)) { "failed to unload plugin with id %s: %s", error ? error->message : "unknown"); g_object_unref(G_OBJECT(loader)); gplugin_manager_normalize_path(const gchar *path) if(g_str_has_suffix(path, G_DIR_SEPARATOR_S)) { return g_strdup_printf("%s%s", path, G_DIR_SEPARATOR_S); gplugin_manager_compare_paths(gconstpointer a, gconstpointer b) gchar *keya = NULL, *keyb = NULL; keya = g_utf8_collate_key_for_filename((const gchar *)a, -1); keyb = g_utf8_collate_key_for_filename((const gchar *)b, -1); gplugin_manager_load_dependencies( GSList *dependencies = NULL, *l = NULL; gboolean all_loaded = TRUE; gplugin_manager_get_plugin_dependencies(manager, plugin, &ourerror); g_propagate_error(error, ourerror); for(l = dependencies; l != NULL; l = l->next) { GPluginPlugin *dependency = GPLUGIN_PLUGIN(l->data); loaded = gplugin_manager_load_plugin(manager, dependency, &ourerror); if(!loaded || ourerror != NULL) { g_propagate_error(error, ourerror); g_slist_free_full(dependencies, g_object_unref); /****************************************************************************** *****************************************************************************/ gplugin_manager_loading_cb( G_GNUC_UNUSED GPluginManager *manager, G_GNUC_UNUSED GPluginPlugin *plugin, G_GNUC_UNUSED GError **error) gplugin_manager_unloading_cb( G_GNUC_UNUSED GPluginManager *manager, G_GNUC_UNUSED GPluginPlugin *plugin, G_GNUC_UNUSED GError **error) /****************************************************************************** *****************************************************************************/ gplugin_manager_finalize(GObject *obj) GPluginManager *manager = GPLUGIN_MANAGER(obj); GPluginManagerPrivate *priv = gplugin_manager_get_instance_private(manager); g_queue_free_full(priv->paths, g_free); /* unload all of the loaded plugins */ gplugin_manager_foreach_unload_plugin, /* free all the data in the plugins hash table and destroy it */ g_hash_table_foreach_remove( gplugin_manager_remove_list_value, g_clear_pointer(&priv->plugins, g_hash_table_destroy); /* destroy the filename view */ g_clear_pointer(&priv->plugins_filename_view, g_hash_table_destroy); /* clean up our list of loaders */ g_slist_free_full(priv->loaders, g_object_unref); /* free all the data in the loaders hash table and destroy it */ g_hash_table_foreach_remove( priv->loaders_by_extension, gplugin_manager_remove_list_value, g_clear_pointer(&priv->loaders_by_extension, g_hash_table_destroy); /* call the base class's destructor */ G_OBJECT_CLASS(gplugin_manager_parent_class)->finalize(obj); gplugin_manager_class_init(GPluginManagerClass *klass) GObjectClass *obj_class = G_OBJECT_CLASS(klass); GPluginManagerClass *manager_class = GPLUGIN_MANAGER_CLASS(klass); obj_class->finalize = gplugin_manager_finalize; manager_class->loading_plugin = gplugin_manager_loading_cb; manager_class->unloading_plugin = gplugin_manager_unloading_cb; * GPluginManager::loading-plugin: * @manager: The #GPluginManager instance. * @plugin: The #GPluginPlugin that's about to be loaded. * @error: Return address for a #GError. * Emitted before @plugin is loaded. * Return FALSE to stop loading signals[SIG_LOADING] = g_signal_new( G_OBJECT_CLASS_TYPE(manager_class), G_STRUCT_OFFSET(GPluginManagerClass, loading_plugin), gplugin_boolean_accumulator, * GPluginManager::loaded-plugin: * @manager: the #gpluginpluginmanager instance. * @plugin: the #gpluginplugin that's about to be loaded. * emitted after a plugin is loaded. signals[SIG_LOADED] = g_signal_new( G_OBJECT_CLASS_TYPE(manager_class), G_STRUCT_OFFSET(GPluginManagerClass, loaded_plugin), * GPluginManager::load-plugin-failed: * @manager: The #GPluginManager instance. * @plugin: The #GPluginPlugin that failed to load. * emitted after a plugin fails to load. signals[SIG_LOAD_FAILED] = g_signal_new( G_OBJECT_CLASS_TYPE(manager_class), G_STRUCT_OFFSET(GPluginManagerClass, load_failed), * GPluginManager::unloading-plugin * @manager: the #GPluginManager instance. * @plugin: the #GPluginPlugin that's about to be unloaded. * @error: Return address for a #GError. * emitted before a plugin is unloaded. * Return FALSE to stop unloading signals[SIG_UNLOADING] = g_signal_new( G_OBJECT_CLASS_TYPE(manager_class), G_STRUCT_OFFSET(GPluginManagerClass, unloading_plugin), gplugin_boolean_accumulator, * GPluginManager::unloaded-plugin: * @manager: the #gpluginpluginmanager instance. * @plugin: the #gpluginplugin that's about to be loaded. * emitted after a plugin is successfully unloaded. signals[SIG_UNLOADED] = g_signal_new( G_OBJECT_CLASS_TYPE(manager_class), G_STRUCT_OFFSET(GPluginManagerClass, unloaded_plugin), * GPluginManager::unload-plugin-failed: * @manager: The #GPluginManager instance. * @plugin: The #GPluginPlugin instance that failed to unload. * @error: A #GError instance. * Emitted when @manager was asked to unload @plugin, but @plugin returned * %FALSE when its unload function was called. signals[SIG_UNLOAD_FAILED] = g_signal_new( G_OBJECT_CLASS_TYPE(manager_class), G_STRUCT_OFFSET(GPluginManagerClass, unload_plugin_failed), gplugin_manager_init(GPluginManager *manager) GPluginManagerPrivate *priv = gplugin_manager_get_instance_private(manager); priv->paths = g_queue_new(); /* the plugins hashtable is keyed on a plugin id and holds a GSList of all * plugins that share that id. g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); /* the filename view is hash table keyed on the filename of the plugin with * a value of the plugin itself. priv->plugins_filename_view = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_object_unref); /* The loaders_by_extension hash table is keyed on the supported extensions * of the loader. Which means that a loader that supports multiple * extensions will be in the table multiple times. * We deal with collisions by using a GSList for the value which will hold * references to instances of the actual loaders. * Storing this in this method allows us to quickly figure out which loader * to use by the filename and helps us to avoid iterating the loaders table priv->loaders_by_extension = g_hash_table_new_full( gplugin_manager_str_hash, /****************************************************************************** *****************************************************************************/ gplugin_manager_private_init(gboolean register_native_loader) if(GPLUGIN_IS_MANAGER(default_manager)) { default_manager = g_object_new(GPLUGIN_TYPE_MANAGER, NULL); if(register_native_loader) { if(!gplugin_manager_register_loader( GPLUGIN_TYPE_NATIVE_LOADER, g_error("failed to register loader: %s", error->message); g_error("failed to register loader: unknown failure"); dependency_regex = g_regex_new(dependency_pattern, 0, 0, NULL); gplugin_manager_private_uninit(void) g_regex_unref(dependency_regex); g_clear_object(&default_manager); /****************************************************************************** *****************************************************************************/ * gplugin_manager_append_path: * @manager: The #GPluginManager instance. * @path: A path to add to the end of the plugin search paths. * Adds @path to the end of the list of paths to search for plugins. gplugin_manager_append_path(GPluginManager *manager, const gchar *path) GPluginManagerPrivate *priv = NULL; gchar *normalized = NULL; g_return_if_fail(GPLUGIN_IS_MANAGER(manager)); g_return_if_fail(path != NULL); normalized = gplugin_manager_normalize_path(path); priv = gplugin_manager_get_instance_private(manager); gplugin_manager_compare_paths); g_queue_push_tail(priv->paths, normalized); * gplugin_manager_prepend_path: * @manager: The #GPluginManager instance. * @path: A path to add to the beginning of the plugin search paths. * Adds @path to the beginning of the list of paths to search for plugins. gplugin_manager_prepend_path(GPluginManager *manager, const gchar *path) GPluginManagerPrivate *priv = NULL; gchar *normalized = NULL; g_return_if_fail(GPLUGIN_IS_MANAGER(manager)); g_return_if_fail(path != NULL); normalized = gplugin_manager_normalize_path(path); priv = gplugin_manager_get_instance_private(manager); gplugin_manager_compare_paths); g_queue_push_head(priv->paths, normalized); * gplugin_manager_remove_path: * @manager: The #GPluginManager instance. * @path: A path to remove from the plugin search paths. * Removes @path from the list of paths to search for plugins. gplugin_manager_remove_path(GPluginManager *manager, const gchar *path) GPluginManagerPrivate *priv = NULL; gchar *normalized = NULL; g_return_if_fail(GPLUGIN_IS_MANAGER(manager)); g_return_if_fail(path != NULL); normalized = gplugin_manager_normalize_path(path); priv = gplugin_manager_get_instance_private(manager); gplugin_manager_compare_paths); g_queue_delete_link(priv->paths, l); * gplugin_manager_remove_paths: * @manager: The #GPluginManager instance. * Clears all paths that are set to search for plugins. gplugin_manager_remove_paths(GPluginManager *manager) GPluginManagerPrivate *priv = NULL; g_return_if_fail(GPLUGIN_IS_MANAGER(manager)); priv = gplugin_manager_get_instance_private(manager); /* g_queue_clear_full was added in 2.60 but we require 2.40 */ g_queue_foreach(priv->paths, (GFunc)g_free, NULL); g_queue_clear(priv->paths); * gplugin_manager_add_default_paths: * @manager: The #GPluginManager instance. * Adds the path that GPlugin was installed to to the plugin search path, as * well as `${XDG_CONFIG_HOME}/gplugin` so users can install additional loaders gplugin_manager_add_default_paths(GPluginManager *manager) g_return_if_fail(GPLUGIN_IS_MANAGER(manager)); path = g_build_filename(PREFIX, LIBDIR, "gplugin", NULL); gplugin_manager_prepend_path(manager, path); path = g_build_filename(g_get_user_config_dir(), "gplugin", NULL); gplugin_manager_prepend_path(manager, path); * gplugin_manager_add_app_paths: * @manager: The #GPluginManager instance. * @prefix: The installation prefix for the application. * @appname: The name of the application whose paths to add. * Adds the application installation path for @appname. This will add * `@prefix/@appname/plugins` to the list as well as * `${XDG_CONFIG_HOME}/@appname/plugins`. gplugin_manager_add_app_paths( g_return_if_fail(GPLUGIN_IS_MANAGER(manager)); g_return_if_fail(appname != NULL); path = g_build_filename(prefix, LIBDIR, appname, NULL); gplugin_manager_prepend_path(manager, path); path = g_build_filename(g_get_user_config_dir(), appname, "plugins", NULL); gplugin_manager_prepend_path(manager, path); * gplugin_manager_get_paths: * @manager: The #GPluginManager instance. * Gets the list of paths which will be searched for plugins. * Returns: (element-type utf8) (transfer none): The list of paths which will * be searched for plugins. gplugin_manager_get_paths(GPluginManager *manager) GPluginManagerPrivate *priv = NULL; g_return_val_if_fail(GPLUGIN_IS_MANAGER(manager), NULL); priv = gplugin_manager_get_instance_private(manager); return priv->paths->head; * gplugin_manager_register_loader: * @manager: The #GPluginManager instance. * @type: #GType of a #GPluginLoader. * @error: (out) (nullable): The return address for a #GError. * Registers @type as an available loader. * Returns: %TRUE if the loader was successfully register, %FALSE otherwise gplugin_manager_register_loader( GPluginManagerPrivate *priv = NULL; GPluginLoader *loader = NULL; GSList *l = NULL, *exts = NULL; g_return_val_if_fail(GPLUGIN_IS_MANAGER(manager), FALSE); g_return_val_if_fail(g_type_is_a(type, GPLUGIN_TYPE_LOADER), FALSE); loader = gplugin_manager_find_loader_by_type(manager, type); if(GPLUGIN_IS_LOADER(loader)) { _("loader %s was already registered"), /* Create the loader instance first. If we can't create it, we bail */ loader = g_object_new(type, NULL); if(!GPLUGIN_IS_LOADER(loader)) { _("failed to create loader instance for %s"), priv = gplugin_manager_get_instance_private(manager); g_slist_prepend(priv->loaders, g_object_ref(G_OBJECT(loader))); exts = gplugin_loader_get_supported_extensions(loader); for(l = exts; l; l = l->next) { GSList *existing = NULL, *ll = NULL; const gchar *ext = (const gchar *)l->data; /* grab any existing loaders that are registered for this type so that * we can prepend our loader. But before we add ours, we remove any * old copies we might have of ours. existing = g_hash_table_lookup(priv->loaders_by_extension, ext); for(ll = existing; ll; ll = ll->next) { if(G_OBJECT_TYPE(ll->data) == type) { GPluginLoader *old = GPLUGIN_LOADER(ll->data); existing = g_slist_remove(existing, old); g_object_unref(G_OBJECT(old)); existing = g_slist_prepend(existing, g_object_ref(G_OBJECT(loader))); /* Now insert the updated slist back into the hash table */ priv->loaders_by_extension, /* make a note that we need to refresh */ priv->refresh_needed = TRUE; /* we remove our initial reference from the loader now to avoid a leak */ g_object_unref(G_OBJECT(loader)); * gplugin_manager_unregister_loader: * @manager: The #GPluginManager instance. * @type: #GType of a #GPluginLoader. * @error: (out) (nullable): The return address for a #GError. * Unregisters @type as an available loader. * Returns: %TRUE if the loader was successfully unregistered, %FALSE * otherwise with @error set. gplugin_manager_unregister_loader( GPluginManagerPrivate *priv = NULL; GPluginLoader *loader = NULL; GSList *l = NULL, *exts = NULL; g_return_val_if_fail(GPLUGIN_IS_MANAGER(manager), FALSE); g_return_val_if_fail(g_type_is_a(type, GPLUGIN_TYPE_LOADER), FALSE); loader = gplugin_manager_find_loader_by_type(manager, type); if(!GPLUGIN_IS_LOADER(loader)) { _("loader %s is not registered"), priv = gplugin_manager_get_instance_private(manager); exts = gplugin_loader_get_supported_extensions(loader); for(l = exts; l; l = l->next) { ext = (const gchar *)exts->data; los = g_hash_table_lookup(priv->loaders_by_extension, ext); for(ll = los; ll; ll = ll->next) { GPluginLoader *lo = GPLUGIN_LOADER(ll->data); /* check if this is not the loader we're looking for */ if(G_OBJECT_TYPE(lo) != type) /* at this point, the loader we're at is of the type we're * removing. So we'll remove it from the los SList. Then if the * SList is empty, we remove it from the hash table, otherwise we los = g_slist_remove(los, lo); priv->loaders_by_extension, g_hash_table_remove(priv->loaders_by_extension, ext); /* kill our ref to the loader */ g_object_unref(G_OBJECT(lo)); /* now move to the next extension to check */ priv->loaders = g_slist_remove(priv->loaders, loader); g_object_unref(G_OBJECT(loader)); * gplugin_manager_get_loaders: * @manager: The #GPluginManager instance. * Returns a list of all registered #GPluginLoader's. * Returns: (element-type GPlugin.Loader) (transfer full): Returns a list of all gplugin_manager_get_loaders(GPluginManager *manager) GPluginManagerPrivate *priv = NULL; g_return_val_if_fail(GPLUGIN_IS_MANAGER(manager), FALSE); priv = gplugin_manager_get_instance_private(manager); return g_slist_copy_deep(priv->loaders, (GCopyFunc)g_object_ref, NULL); * gplugin_manager_refresh: * @manager: The #GPluginManager instance. * Forces a refresh of all plugins found in the search paths. gplugin_manager_refresh(GPluginManager *manager) GPluginManagerPrivate *priv = NULL; GList *error_messages = NULL, *l = NULL; gchar *error_message = NULL; g_return_if_fail(GPLUGIN_IS_MANAGER(manager)); priv = gplugin_manager_get_instance_private(manager); /* build a tree of all possible plugins */ root = gplugin_file_tree_new(priv->paths->head); priv->refresh_needed = TRUE; while(priv->refresh_needed) { for(l = error_messages; l; l = l->next) g_list_free(error_messages); priv->refresh_needed = FALSE; for(dir = root->children; dir; dir = dir->next) { GPluginFileTreeEntry *e = dir->data; const gchar *path = e->filename; for(file = dir->children; file; file = file->next) { GPluginPlugin *plugin = NULL; GPluginLoader *loader = NULL; e = (GPluginFileTreeEntry *)file->data; /* Build the path and see if we need to probe it! */ filename = g_build_filename(path, e->filename, NULL); g_hash_table_lookup(priv->plugins_filename_view, filename); if(plugin && GPLUGIN_IS_PLUGIN(plugin)) { GPluginPluginState state = gplugin_plugin_get_state(plugin); /* The plugin is in our "view", check its state. If it's * queried or loaded, move on to the next one. if(state == GPLUGIN_PLUGIN_STATE_QUERIED || state == GPLUGIN_PLUGIN_STATE_LOADED) { /* grab the list of loaders for this extension */ priv->loaders_by_extension, if(!GPLUGIN_IS_LOADER(l->data)) { loader = GPLUGIN_LOADER(l->data); /* Try to probe the plugin with the current loader */ gplugin_loader_query_plugin(loader, filename, &error); /* Check the GError, if it's set, output its message and error_message = g_strdup_printf( _("failed to query '%s' with " G_OBJECT_TYPE_NAME(loader), g_list_prepend(error_messages, error_message); /* if the plugin instance is good, then break out of this if(GPLUGIN_IS_PLUGIN(plugin)) { g_object_unref(G_OBJECT(plugin)); /* check if our plugin instance is good. If it's not good we * don't need to do anything but free the filename which we'll if(GPLUGIN_IS_PLUGIN(plugin)) { /* we have a good plugin, huzzah! We need to add it to our * "view" as well as the main plugin hash table. /* we want the internal filename from the plugin to avoid * duplicate memory, so we need to grab it for the "view". gchar *real_filename = gplugin_plugin_get_filename(plugin); /* we also need the GPluginPluginInfo to get the plugin's * ID for the key in our main hash table. GPluginPluginInfo *info = gplugin_plugin_get_info(plugin); const gchar *id = gplugin_plugin_info_get_id(info); GSList *l = NULL, *ll = NULL; /* throw a warning if the info->id is NULL */ error_message = g_strdup_printf( _("Plugin %s has a NULL id."), g_object_unref(G_OBJECT(info)); g_list_prepend(error_messages, error_message); /* now insert into our view */ priv->plugins_filename_view, g_object_ref(G_OBJECT(plugin))); /* Grab the list of plugins with our id and prepend the new * plugin to it before updating it. l = g_hash_table_lookup(priv->plugins, id); for(ll = l; ll; ll = ll->next) { GPluginPlugin *splugin = GPLUGIN_PLUGIN(ll->data); gchar *sfilename = gplugin_plugin_get_filename(splugin); if(!g_strcmp0(real_filename, sfilename)) l = g_slist_prepend(l, g_object_ref(plugin)); g_hash_table_insert(priv->plugins, g_strdup(id), l); /* check if the plugin is supposed to be loaded on query, if(gplugin_plugin_info_get_load_on_query(info)) { gplugin_loader_load_plugin(loader, plugin, &error); error_message = g_strdup_printf( _("failed to load %s during query: %s"), (error) ? error->message : _("Unknown")); g_list_prepend(error_messages, error_message); /* if errors is greater than 0 set * manager->refresh_needed to TRUE. priv->refresh_needed = TRUE; g_object_unref(G_OBJECT(info)); /* since the plugin is now stored in our hash tables we * need to remove this function's reference to it. g_object_unref(G_OBJECT(plugin)); error_messages = g_list_reverse(error_messages); for(l = error_messages; l; l = l->next) { g_warning("%s", (gchar *)l->data); g_list_free(error_messages); gplugin_file_tree_free(root); * gplugin_manager_foreach: * @manager: The #GPluginManager instance. * @func: (scope call): The #GPluginManagerForeachFunc to call. * @data: User data to pass to func. * Calls @func for each plugin that is known. GPluginManagerForeachFunc func, GPluginManagerPrivate *priv = NULL; gpointer id = NULL, plugins = NULL; g_return_if_fail(GPLUGIN_IS_MANAGER(manager)); g_return_if_fail(func != NULL); priv = gplugin_manager_get_instance_private(manager); g_hash_table_iter_init(&iter, priv->plugins); while(g_hash_table_iter_next(&iter, &id, &plugins)) { func((gchar *)id, (GSList *)plugins, data); * gplugin_manager_find_plugins: * @manager: The #GPluginManager instance. * @id: id string of the plugin to find. * Finds all plugins matching @id. * Returns: (element-type GPlugin.Plugin) (transfer full): A #GSList of * referenced #GPluginPlugin's matching @id. Call * g_slist_free_full() with a `DestroyNotify` of g_object_unref() on * the returned value when you're done with it. gplugin_manager_find_plugins(GPluginManager *manager, const gchar *id) GPluginManagerPrivate *priv = NULL; GSList *plugins_list = NULL, *l; g_return_val_if_fail(GPLUGIN_IS_MANAGER(manager), NULL); g_return_val_if_fail(id != NULL, NULL); priv = gplugin_manager_get_instance_private(manager); l = g_hash_table_lookup(priv->plugins, id); plugins_list = g_slist_copy_deep(l, (GCopyFunc)g_object_ref, NULL); * gplugin_manager_find_plugins_with_version: * @manager: The #GPluginManager instance. * @id: The ID of the plugin to find. * @op: one of <, <=, =, ==, >=, >. * @version: The version to compare against. * Similar to gplugin_manager_find_plugins() but only returns plugins whose * versions match @op and @version. This is primarily used for dependency * loading where a plugin may depend on a specific range of versions of another * Returns: (element-type GPlugin.Plugin) (transfer full): A #GSList of * referenced #GPluginPlugin's matching @id. Call * g_slist_free_full() with a `DestroyNotify` of g_object_unref() on * the returned value when you're done with it. gplugin_manager_find_plugins_with_version( GSList *plugins = NULL, *filtered = NULL, *l = NULL; g_return_val_if_fail(GPLUGIN_IS_MANAGER(manager), NULL); plugins = gplugin_manager_find_plugins(manager, id); if((op == NULL || *op == '\0') && (version == NULL || *version == '\0')) { /* we weren't actually passed an operator and a version so just return * the list we have based on the id. for(l = plugins; l; l = l->next) { GPluginPlugin *plugin = GPLUGIN_PLUGIN(l->data); GPluginPluginInfo *info = NULL; const gchar *found_version = NULL; /* get the plugin's version from it's info */ info = gplugin_plugin_get_info(plugin); found_version = gplugin_plugin_info_get_version(info); /* now compare the version of the plugin to passed in version. This * should be done in this order so it's easier to track the operators. * IE: we want to keep the inequality the same. result = gplugin_version_compare(found_version, version); /* we need to keep info around until we're done using found_version */ g_object_unref(G_OBJECT(info)); keep = (g_strcmp0(op, "<") == 0 || g_strcmp0(op, "<=") == 0); (g_strcmp0(op, "=") == 0 || g_strcmp0(op, "==") == 0 || g_strcmp0(op, "<=") == 0 || g_strcmp0(op, ">=") == 0); keep = (g_strcmp0(op, ">") == 0 || g_strcmp0(op, ">=") == 0); g_slist_prepend(filtered, g_object_ref(G_OBJECT(plugin))); g_slist_free_full(plugins, g_object_unref); return g_slist_reverse(filtered); * gplugin_manager_find_plugins_with_state: * @manager: The #GPluginManager instance. * @state: The #GPluginPluginState to look for. * Finds all plugins that currently have a state of @state. * Returns: (element-type GPlugin.Plugin) (transfer full): A #GSList of * referenced #GPluginPlugin's whose state is @state. Call * g_slist_free_full() with a `DestroyNotify` of g_object_unref() on * the returned value when you're done with it. gplugin_manager_find_plugins_with_state( GPluginPluginState state) GPluginManagerPrivate *priv = NULL; g_return_val_if_fail(GPLUGIN_IS_MANAGER(manager), NULL); priv = gplugin_manager_get_instance_private(manager); g_hash_table_iter_init(&iter, priv->plugins); while(g_hash_table_iter_next(&iter, NULL, &value)) { for(l = (GSList *)value; l != NULL; l = l->next) { GPluginPlugin *plugin = GPLUGIN_PLUGIN(l->data); if(gplugin_plugin_get_state(plugin) == state) { g_slist_prepend(plugins, g_object_ref(G_OBJECT(plugin))); * gplugin_manager_find_plugin: * @manager: The #GPluginManager instance. * @id: The id of the plugin to find. * Finds the first plugin matching @id. This function uses * #gplugin_manager_find_plugins and returns the first plugin in the * Return value: (transfer full): A referenced #GPluginPlugin instance or NULL * if no plugin matching @id was found. gplugin_manager_find_plugin(GPluginManager *manager, const gchar *id) GSList *plugins_list = NULL; GPluginPlugin *plugin = NULL; g_return_val_if_fail(id != NULL, NULL); plugins_list = gplugin_manager_find_plugins(manager, id); if(plugins_list == NULL) { plugin = GPLUGIN_PLUGIN(g_object_ref(G_OBJECT(plugins_list->data))); g_slist_free_full(plugins_list, g_object_unref); * gplugin_manager_find_plugin_with_newest_version: * @manager: The #GPluginManager instance. * @id: The id of the plugin to find. * Calls gplugin_manager_find_plugins() with @id, and then returns the plugins * with the highest version number or %NULL if no plugins with @id are found. * Returns: (transfer full): The #GPluginPlugin with an id of @id that has the * highest version number, or %NULL if no plugins were found with @id. gplugin_manager_find_plugin_with_newest_version( GPluginPlugin *plugin_a = NULL; GPluginPluginInfo *info_a = NULL; const gchar *version_a = NULL; g_return_val_if_fail(id != NULL, NULL); l = gplugin_manager_find_plugins(manager, id); for(; l != NULL; l = g_slist_delete_link(l, l)) { GPluginPlugin *plugin_b = NULL; GPluginPluginInfo *info_b = NULL; const gchar *version_b = NULL; if(!GPLUGIN_IS_PLUGIN(l->data)) { plugin_b = GPLUGIN_PLUGIN(l->data); info_b = gplugin_plugin_get_info(plugin_b); /* If this is the first plugin we've found, set the plugin_a values and if(!GPLUGIN_IS_PLUGIN(plugin_a)) { version_a = gplugin_plugin_info_get_version(info_a); /* At this point, we've seen another plugin, so we need to compare version_b = gplugin_plugin_info_get_version(info_b); cmp = gplugin_version_compare(version_a, version_b); /* plugin_b has a newer version, so set the plugin_a pointers to * the plugin_b pointers as well as the version pointers. g_set_object(&plugin_a, plugin_b); g_set_object(&info_a, info_b); /* Clean up the plugin_b pointers. */ g_clear_object(&plugin_b); * gplugin_manager_get_plugin_dependencies: * @manager: The #GPluginManager instance. * @plugin: The #GPluginPlugin whose dependencies to get. * @error: (out) (nullable): Return address for a #GError. * Returns a list of all the #GPluginPlugin's that @plugin depends on. * Return value: (element-type GPlugin.Plugin) (transfer full): A #GSList of * #GPluginPlugin's that @plugin depends on, or %NULL on error * with @error set. Call g_slist_free_full() with a * `DestroyNotify` of g_object_unref() on the returned value when gplugin_manager_get_plugin_dependencies( GPluginPluginInfo *info = NULL; const gchar *const *dependencies = NULL; g_return_val_if_fail(GPLUGIN_IS_MANAGER(manager), NULL); g_return_val_if_fail(GPLUGIN_IS_PLUGIN(plugin), NULL); info = gplugin_plugin_get_info(plugin); dependencies = gplugin_plugin_info_get_dependencies(info); g_object_unref(G_OBJECT(info)); if(dependencies == NULL) { for(i = 0; dependencies[i] != NULL; i++) { ors = g_strsplit(dependencies[i], "|", 0); for(o = 0; ors[o]; o++) { GMatchInfo *match = NULL; gchar *oid = NULL, *oop = NULL, *over = NULL; if(!g_regex_match(dependency_regex, ors[o], 0, &match)) { /* grab the or'd id, op, and version */ oid = g_match_info_fetch_named(match, "id"); oop = g_match_info_fetch_named(match, "op"); over = g_match_info_fetch_named(match, "version"); /* free the match info */ g_match_info_free(match); /* now look for a plugin matching the id */ matches = gplugin_manager_find_plugins_with_version( /* prepend the first found match to our return value */ ret = g_slist_prepend(ret, g_object_ref(matches->data)); g_slist_free_full(matches, g_object_unref); _("failed to find dependency %s for %s"), gplugin_plugin_info_get_id(info)); g_slist_free_full(ret, g_object_unref); * gplugin_manager_load_plugin: * @manager: The #GPluginManager instance. * @plugin: #GPluginPlugin instance. * @error: (out) (nullable): return location for a #GError or %NULL. * Loads @plugin and all of its dependencies. If a dependency can not be * loaded, @plugin will not be loaded either. However, any other plugins that * @plugin depends on that were loaded from this call, will not be unloaded. * Return value: %TRUE if @plugin was loaded successfully or already loaded, gplugin_manager_load_plugin( GPluginPluginInfo *info = NULL; GPluginLoader *loader = NULL; GError *real_error = NULL; g_return_val_if_fail(GPLUGIN_IS_MANAGER(manager), FALSE); g_return_val_if_fail(GPLUGIN_IS_PLUGIN(plugin), FALSE); /* if the plugin is already loaded there's nothing for us to do */ if(gplugin_plugin_get_state(plugin) == GPLUGIN_PLUGIN_STATE_LOADED) { /* now try to get the plugin info from the plugin */ info = gplugin_plugin_get_info(plugin); gchar *filename = gplugin_plugin_get_filename(plugin); _("Plugin %s did not return value plugin info"), gplugin_plugin_set_state(plugin, GPLUGIN_PLUGIN_STATE_LOAD_FAILED); if(!gplugin_manager_load_dependencies(manager, plugin, info, &real_error)) { g_object_unref(G_OBJECT(info)); g_propagate_error(error, real_error); g_object_unref(G_OBJECT(info)); /* now load the actual plugin */ loader = gplugin_plugin_get_loader(plugin); if(!GPLUGIN_IS_LOADER(loader)) { gchar *filename = gplugin_plugin_get_filename(plugin); _("The loader for %s is not a loader. This " gplugin_plugin_set_state(plugin, GPLUGIN_PLUGIN_STATE_LOAD_FAILED); g_object_unref(G_OBJECT(loader)); g_signal_emit(manager, signals[SIG_LOADING], 0, plugin, &real_error, &ret); /* Set the plugin's error. */ g_object_set(G_OBJECT(plugin), "error", real_error, NULL); g_propagate_error(error, real_error); gplugin_plugin_set_state(plugin, GPLUGIN_PLUGIN_STATE_LOAD_FAILED); g_object_unref(G_OBJECT(loader)); ret = gplugin_loader_load_plugin(loader, plugin, &real_error); g_clear_error(&real_error); g_signal_emit(manager, signals[SIG_LOADED], 0, plugin); g_signal_emit(manager, signals[SIG_LOAD_FAILED], 0, plugin); g_propagate_error(error, real_error); g_object_unref(G_OBJECT(loader)); * gplugin_manager_unload_plugin: * @manager: The #GPluginManager instance. * @plugin: #GPluginPlugin instance. * @error: (out) (nullable): Return location for a #GError or %NULL. * Unloads @plugin. If @plugin has dependencies, they are not unloaded. * Returns: %TRUE if @plugin was unloaded successfully or not loaded, %FALSE gplugin_manager_unload_plugin( GPluginLoader *loader = NULL; GError *real_error = NULL; g_return_val_if_fail(GPLUGIN_IS_MANAGER(manager), FALSE); g_return_val_if_fail(GPLUGIN_IS_PLUGIN(plugin), FALSE); if(gplugin_plugin_get_state(plugin) != GPLUGIN_PLUGIN_STATE_LOADED) { loader = gplugin_plugin_get_loader(plugin); if(!GPLUGIN_IS_LOADER(loader)) { _("Plugin loader is not a loader")); g_object_unref(G_OBJECT(loader)); /* Set the plugin's error. */ g_object_set(G_OBJECT(plugin), "error", real_error, NULL); g_propagate_error(error, real_error); gplugin_plugin_set_state(plugin, GPLUGIN_PLUGIN_STATE_LOAD_FAILED); g_object_unref(G_OBJECT(loader)); ret = gplugin_loader_unload_plugin(loader, plugin, &real_error); g_clear_error(&real_error); g_signal_emit(manager, signals[SIG_UNLOADED], 0, plugin); g_signal_emit(manager, signals[SIG_UNLOAD_FAILED], 0, plugin); g_propagate_error(error, real_error); g_object_unref(G_OBJECT(loader)); * gplugin_manager_list_plugins: * @manager: The #GPluginManager instance. * Returns a #GList of all plugin id's. Each id should be queried directly * Return value: (element-type utf8) (transfer container): A #GList of each gplugin_manager_list_plugins(GPluginManager *manager) GPluginManagerPrivate *priv = NULL; g_return_val_if_fail(GPLUGIN_IS_MANAGER(manager), NULL); priv = gplugin_manager_get_instance_private(manager); g_hash_table_iter_init(&iter, priv->plugins); while(g_hash_table_iter_next(&iter, &key, NULL)) { g_queue_push_tail(queue, (gchar *)key); ret = g_list_copy(queue->head); * gplugin_manager_get_default: * Gets the default #GPluginManager in GPlugin. * Returns: (transfer none): The default GPluginManager instance. gplugin_manager_get_default(void)