diff options
Diffstat (limited to 'src/plugins/pglist.c')
-rw-r--r-- | src/plugins/pglist.c | 435 |
1 files changed, 246 insertions, 189 deletions
diff --git a/src/plugins/pglist.c b/src/plugins/pglist.c index e4cb825..3e107b8 100644 --- a/src/plugins/pglist.c +++ b/src/plugins/pglist.c @@ -30,13 +30,16 @@ #include <malloc.h> #include <stdlib.h> #include <string.h> +#include <unistd.h> #include <i18n.h> -#include "dt.h" +#include "manager.h" +#include "native.h" #include "plugin-int.h" +#include "../common/cpp.h" #include "../common/extstr.h" #include "../core/logs.h" #include "../core/nox.h" @@ -44,6 +47,12 @@ +/** + * Prototype de la fonction de création, à garder synchronisé avec + * NATIVE_PLUGIN_ENTRYPOINT() (cf. native-int.h). + */ +typedef GPluginModule * (* get_plugin_instance_cb) (GModule *); + /* Liste de l'ensemble des greffons */ static GPluginModule **_pg_list = NULL; static size_t _pg_count = 0; @@ -61,6 +70,9 @@ static void browse_directory_for_plugins(const char *); /* Suit les variations du compteur de références d'un greffon. */ static void on_plugin_ref_toggle(gpointer, GPluginModule *, gboolean); +/* Fournit le greffon répondant à un nom donné. */ +static GPluginModule *_find_plugin_by_name(const char *, size_t *); + /****************************************************************************** @@ -83,8 +95,7 @@ bool init_all_plugins(bool load) char *saveptr; /* Sauvegarde pour parcours */ char *udir; /* Répertoire supplémentaire ? */ - result = init_chrysalide_dynamic_types(); - if (!result) goto exit; + result = true; g_rw_lock_init(&_pg_lock); @@ -108,8 +119,6 @@ bool init_all_plugins(bool load) if (load) load_remaning_plugins(); - exit: - return result; } @@ -130,64 +139,22 @@ bool init_all_plugins(bool load) void exit_all_plugins(void) { size_t i; /* Boucle de parcours */ - const plugin_interface *pg_iface; /* Définition du greffon */ lock_plugin_list_for_reading(); - if (_pg_list != NULL) + for (i = 0; i < _pg_count; i++) { - for (i = 0; i < _pg_count; i++) - { - assert(_pg_list[i] != NULL); - - /** - * Si le greffon a conduit à la mise en place d'autres greffons, le - * système de dépendances ne suffit pas pour le décompte des références : - * le greffon voit à un instant T son compteur décroître ici ; à un - * instant T+1, un greffon fils décrémente à son tour le compteur vers - * le greffon principal. - * - * Le compteur du conteneur tombe alors à 0, et le code correspondant - * est retiré. Lorsque que le flot d'exécution revient à la procédure - * de sortie du second greffon, son code n'est plus en mémoire. - * - * On s'assure donc que les greffons qui génèrent d'autres greffons - * sont bien traités en dernier. - */ - - pg_iface = g_plugin_module_get_interface(_pg_list[i]); - - if (pg_iface != NULL && pg_iface->container) - g_object_ref(_pg_list[i]); - - g_object_unref(_pg_list[i]); - - } - - for (i = 0; i < _pg_count; i++) - { - if (_pg_list[i] == NULL) - continue; - - pg_iface = g_plugin_module_get_interface(_pg_list[i]); - - if (pg_iface == NULL || !pg_iface->container) - continue; - - g_object_unref(_pg_list[i]); - - } + assert(_pg_list[i] != NULL); + unref_object(_pg_list[i]); + } + if (_pg_list != NULL) free(_pg_list); - } - unlock_plugin_list_for_reading(); g_rw_lock_clear(&_pg_lock); - exit_chrysalide_dynamic_types(); - } @@ -244,83 +211,95 @@ static int filter_dirs_or_mods(const struct dirent *entry) * * * Paramètres : dir = répertoire à parcourir en quête de greffons (sans /). * * * -* Description : Part à la recherche de greffons sous forme de modules. * +* Description : Indique la version (NOX/UI) associée à un nom de fichier. * * * -* Retour : - * +* Retour : true si la version complémentaire existe ou false. * * * * Remarques : - * * * ******************************************************************************/ -static void browse_directory_for_plugins(const char *dir) +static bool check_for_plugin_versions(const char *dir, const char *filename, bool *is_nox, bool *is_ui) { - struct dirent **namelist; /* Eléments trouvés */ - int ret; /* Bilan du parcours */ - bool nox; /* Absence de support graphique*/ - char *filename; /* Elément à ausculter */ - GPluginModule *plugin; /* Greffon à intégrer ou pas */ - - ret = scandir(dir, &namelist, filter_dirs_or_mods, alphasort); - if (ret < 0) - { - LOG_ERROR_N("scandir"); - return; - } - - nox = run_in_nox_mode(); - - while (ret--) - { + bool result; /* Bilan à renvoyer */ + size_t length; /* Taille du nom de fichier */ + char *alt_path; /* Autre chemin complet testé */ + int ret; /* Bilan d'une impression */ - if (nox) - { #ifdef _WIN32 -# define UI_SHARED_SUFFIX "-ui.dll" +# define SHARED_SUFFIX ".dll" #else -# define UI_SHARED_SUFFIX "-ui.so" +# define SHARED_SUFFIX ".so" #endif +#define UI_SHARED_SUFFIX "ui" SHARED_SUFFIX - if (strstr(namelist[ret]->d_name, UI_SHARED_SUFFIX) != NULL) - { - log_variadic_message(LMT_ERROR, _("Skipping unsuitable file: %s"), namelist[ret]->d_name); - continue; - } + result = false; - } + /* Propriétés du fichier courant */ - filename = (char *)calloc(strlen(dir) + 1 + strlen(namelist[ret]->d_name) + 1, sizeof(char)); + length = strlen(filename); - strcpy(filename, dir); - strcat(filename, G_DIR_SEPARATOR_S); - strcat(filename, namelist[ret]->d_name); + if (length < STATIC_STR_SIZE(UI_SHARED_SUFFIX)) + *is_ui = false; - if (namelist[ret]->d_type == DT_DIR) - browse_directory_for_plugins(filename); + else + *is_ui = (strcmp(filename + length - STATIC_STR_SIZE(UI_SHARED_SUFFIX), UI_SHARED_SUFFIX) == 0); + + if (*is_ui) + *is_nox = false; + + else + { + if (length < STATIC_STR_SIZE(SHARED_SUFFIX)) + *is_nox = false; else - { - plugin = g_plugin_module_new(filename); + *is_nox = (strcmp(filename + length - STATIC_STR_SIZE(SHARED_SUFFIX), SHARED_SUFFIX) == 0); + + } + + /* Recherche d'une version alternative */ - if (plugin != NULL) - register_plugin(plugin); + if (*is_nox || *is_ui) + { + + if (*is_nox) + ret = asprintf(&alt_path, "%s%s%.*s%s", + dir, G_DIR_SEPARATOR_S, + (int)(length - STATIC_STR_SIZE(SHARED_SUFFIX)), filename, + UI_SHARED_SUFFIX); + else + ret = asprintf(&alt_path, "%s%s%.*s%s", + dir, G_DIR_SEPARATOR_S, + (int)(length - STATIC_STR_SIZE(SHARED_SUFFIX)), filename, + SHARED_SUFFIX); + if (ret <= 0) + { + LOG_ERROR_N("asprintf"); + goto exit; } - free(filename); - free(namelist[ret]); + ret = access(alt_path, R_OK | X_OK); + + result = (ret == 0); + + free(alt_path); } - free(namelist); + exit: + + return result; } /****************************************************************************** * * -* Paramètres : plugin = greffon à ajouter aux autres disponibles. * +* Paramètres : dir = répertoire à parcourir en quête de greffons (sans /). * * * -* Description : Ajoute un greffon à la liste principale de greffons. * +* Description : Part à la recherche de greffons sous forme de modules. * * * * Retour : - * * * @@ -328,52 +307,91 @@ static void browse_directory_for_plugins(const char *dir) * * ******************************************************************************/ -void _register_plugin(GPluginModule *plugin) +static void browse_directory_for_plugins(const char *dir) { - size_t i; /* Boucle de parcours */ - const plugin_interface *pg_iface; /* Informations à consulter */ - const char *name; /* Désignation du greffon */ - - /** - * L'appel sans verrou n'est fourni que pour les greffons - * mettant en place des greffons en interne ! - */ - - /* Recherche d'un éventuel doublon */ + struct dirent **namelist; /* Eléments trouvés */ + int ret; /* Bilan d'un appel */ + int k; /* Boucle de parcours */ + bool nox_mode; /* Absence de support graphique*/ + char *filename; /* Elément à ausculter */ + bool is_nox; /* Chemin de version basique ? */ + bool is_ui; /* Chemin de version graphique */ + bool has_alt; /* Existence d'une alternative */ + GModule *module; /* Abstration de manipulation */ + get_plugin_instance_cb get_instance; /* Point d'entrée exporté */ + GPluginModule *plugin; /* Greffon à intégrer ou pas */ - pg_iface = g_plugin_module_get_interface(plugin); + ret = scandir(dir, &namelist, filter_dirs_or_mods, alphasort); + if (ret < 0) + { + LOG_ERROR_N("scandir"); + return; + } - name = pg_iface->name; + nox_mode = run_in_nox_mode(); - for (i = 0; i < _pg_count; i++) + for (k = ret; k--; ) { - pg_iface = g_plugin_module_get_interface(_pg_list[i]); - - if (strcmp(name, pg_iface->name) == 0) + ret = asprintf(&filename, "%s%s%s", dir, G_DIR_SEPARATOR_S, namelist[k]->d_name); + if (ret <= 0) { - log_variadic_message(LMT_ERROR, - _("Plugin '%s' already registered!"), name); + LOG_ERROR_N("asprintf"); + continue; + } - break; + if (namelist[k]->d_type == DT_DIR) + browse_directory_for_plugins(filename); - } + else + { + has_alt = check_for_plugin_versions(dir, namelist[k]->d_name, &is_nox, &is_ui); - } + if ((nox_mode && is_nox) || (!nox_mode && ((is_nox && !has_alt) || is_ui))) + { + module = g_module_open(filename, G_MODULE_BIND_LAZY); + if (module == NULL) + { + log_variadic_message(LMT_ERROR, + _("Error while loading the plugin candidate '%s' : %s"), + filename, g_module_error()); + goto next_file; + } + + get_instance = NULL; + + if (!g_module_symbol(module, "get_chrysalide_plugin_instance", (gpointer *)&get_instance)) + log_variadic_message(LMT_ERROR, + _("No '%s' entry in plugin candidate '%s'"), + "<sym>", filename); + + if (get_instance == NULL) + plugin = NULL; + else + plugin = get_instance(module); + + if (plugin != NULL) + { + register_plugin(plugin); + unref_object(plugin); + } + + else + g_module_close(module); - /* Ajout du greffon à la liste */ + } + else + log_variadic_message(LMT_INFO, _("Skipping unsuitable file for plugin: %s"), filename); - if (i == _pg_count) - { - _pg_list = (GPluginModule **)realloc(_pg_list, ++_pg_count * sizeof(GPluginModule)); + } - _pg_list[_pg_count - 1] = plugin; + next_file: - g_object_add_toggle_ref(G_OBJECT(plugin), (GToggleNotify)on_plugin_ref_toggle, NULL); + free(filename); + free(namelist[k]); } - else - /* FIXME : leak(plugin); */; + free(namelist); } @@ -394,24 +412,55 @@ void _register_plugin(GPluginModule *plugin) static void on_plugin_ref_toggle(gpointer unused, GPluginModule *plugin, gboolean last) { - const plugin_interface *pg_iface; /* Vitrine d'un greffon */ + const char *name; /* Désignation du greffon */ size_t index; /* Indice du greffon */ GPluginModule *same; /* Juste pour la récupération */ + GModule *module; /* Structure de chargement GLib*/ if (last) { assert(g_rw_lock_writer_trylock(&_pg_lock) == FALSE); - pg_iface = g_plugin_module_get_interface(plugin); + name = g_plugin_module_get_name(plugin); + + /** + * Les mécanismes de g_object_unref() prennent en compte la bascule d'un + * compteur de références initialement à 2 avant appel pour déclencher + * cet appel à on_plugin_ref_toggle() mis en place par g_object_add_toggle_ref(). + * + * Incrémenter ce compteur à nouveau, via get_plugin_by_name(), puis le + * décrémenter ensuite via unref_object() va conduire à une nouvelle + * bascule des statuts de suivi dans g_object_unref(). + * + * Il est ainsi impératif de rechercher une instance du greffon dans + * la liste des extensions sans toucher au compteur de références. + */ + + same = _find_plugin_by_name(name, &index); - same = get_plugin_by_name(pg_iface->name, &index); assert(same != NULL); + assert(same == plugin); _pg_list[index] = NULL; - g_object_remove_toggle_ref(G_OBJECT(plugin), (GToggleNotify)on_plugin_ref_toggle, NULL); + /** + * Suppression de la dernière référence. + */ + + if (G_IS_NATIVE_PLUGIN(plugin)) + module = g_native_plugin_get_module(G_NATIVE_PLUGIN(plugin)); + else + module = NULL; + + g_object_remove_toggle_ref(G_OBJECT(same), (GToggleNotify)on_plugin_ref_toggle, NULL); + + /** + * Plus aucun code issu du greffon n'est désormais utile. Le module associé peut + * être libéré de la mémoire. + */ - g_object_unref(G_OBJECT(same)); + if (module != NULL) + g_module_close(module); } @@ -432,9 +481,40 @@ static void on_plugin_ref_toggle(gpointer unused, GPluginModule *plugin, gboolea void register_plugin(GPluginModule *plugin) { + size_t i; /* Boucle de parcours */ + const char *name; /* Désignation du greffon */ + const char *existing; /* Nom d'un greffon en place */ + g_rw_lock_writer_lock(&_pg_lock); - _register_plugin(plugin); + /* Recherche d'un éventuel doublon */ + + name = g_plugin_module_get_name(plugin); + + for (i = 0; i < _pg_count; i++) + { + existing = g_plugin_module_get_name(_pg_list[i]); + + if (strcmp(name, existing) == 0) + { + log_variadic_message(LMT_ERROR, _("Plugin '%s' already registered!"), name); + break; + } + + } + + /* Ajout du greffon à la liste */ + + if (i == _pg_count) + { + _pg_list = realloc(_pg_list, ++_pg_count * sizeof(GPluginModule)); + + _pg_list[_pg_count - 1] = plugin; + ref_object(plugin); + + g_object_add_toggle_ref(G_OBJECT(plugin), (GToggleNotify)on_plugin_ref_toggle, NULL); + + } g_rw_lock_writer_unlock(&_pg_lock); @@ -502,13 +582,18 @@ void load_remaning_plugins(void) /* Supprime les greffons non chargés */ - for (i = 0; i < _pg_count; i++) + for (i = 0; i < _pg_count;) { flags = g_plugin_module_get_flags(_pg_list[i]); - if ((flags & PSF_LOADED) == 0) + if (flags & PSF_LOADED) + i++; + + else { - g_object_unref(G_OBJECT(_pg_list[i])); + unref_object(_pg_list[i]); + + assert(_pg_list[i] == NULL); memmove(&_pg_list[i], &_pg_list[i + 1], (_pg_count - i - 1) * sizeof(GPluginModule *)); _pg_count--; @@ -535,15 +620,16 @@ void load_remaning_plugins(void) * * * Retour : Instance du greffon trouvé ou NULL si aucun. * * * -* Remarques : - * +* Remarques : Le compteur de référence d'un greffon trouvé n'est pas * +* modifié. * * * ******************************************************************************/ -GPluginModule *get_plugin_by_name(const char *name, size_t *index) +static GPluginModule *_find_plugin_by_name(const char *name, size_t *index) { GPluginModule *result; /* Greffon trouvé à renvoyer */ size_t i; /* Boucle de parcours */ - const plugin_interface *pg_iface; /* Vitrine d'un greffon */ + const char *current; /* Nom du greffon courant */ result = NULL; @@ -557,9 +643,9 @@ GPluginModule *get_plugin_by_name(const char *name, size_t *index) /* Si on est en train de procéder à un nettoyage... */ if (_pg_list[i] == NULL) continue; - pg_iface = g_plugin_module_get_interface(_pg_list[i]); + current = g_plugin_module_get_name(_pg_list[i]); - if (strcmp(pg_iface->name, name) == 0) + if (strcmp(current, name) == 0) { result = _pg_list[i]; @@ -570,9 +656,6 @@ GPluginModule *get_plugin_by_name(const char *name, size_t *index) } - if (result != NULL) - g_object_ref(G_OBJECT(result)); - return result; } @@ -580,33 +663,25 @@ GPluginModule *get_plugin_by_name(const char *name, size_t *index) /****************************************************************************** * * -* Paramètres : count = nombre de greffons trouvés. [OUT] * +* Paramètres : name = désignation du greffon recherché. * +* index = indice du greffon trouvé. [OUT] * * * -* Description : Fournit la liste de l'ensemble des greffons. * +* Description : Fournit le greffon répondant à un nom donné. * * * -* Retour : Liste de tous les greffons chargés. * +* Retour : Instance du greffon trouvé ou NULL si aucun. * * * * Remarques : - * * * ******************************************************************************/ -GPluginModule **get_all_plugins(size_t *count) +GPluginModule *get_plugin_by_name(const char *name, size_t *index) { - GPluginModule **result; /* Liste à retourner */ - size_t i; /* Boucle de parcours */ - - g_rw_lock_reader_lock(&_pg_lock); - - result = malloc(_pg_count * sizeof(GPluginModule *)); - *count = _pg_count; + GPluginModule *result; /* Greffon trouvé à renvoyer */ - for (i = 0; i < _pg_count; i++) - { - result[i] = _pg_list[i]; - g_object_ref(G_OBJECT(_pg_list[i])); - } + result = _find_plugin_by_name(name, index); - g_rw_lock_reader_unlock(&_pg_lock); + if (result != NULL) + ref_object(result); return result; @@ -615,48 +690,30 @@ GPluginModule **get_all_plugins(size_t *count) /****************************************************************************** * * -* Paramètres : action = fonctionnalité recherchée. * -* count = nombre de greffons trouvés. [OUT] * +* Paramètres : count = nombre de greffons trouvés. [OUT] * * * -* Description : Fournit les greffons offrant le service demandé. * +* Description : Fournit la liste de l'ensemble des greffons. * * * -* Retour : Liste de greffons correspondants issue d'un tri interne. * +* Retour : Liste de tous les greffons chargés. * * * * Remarques : - * * * ******************************************************************************/ -GPluginModule **get_all_plugins_for_action(PluginAction action, size_t *count) +GPluginModule **get_all_plugins(size_t *count) { GPluginModule **result; /* Liste à retourner */ - size_t i; /* Boucle de parcours #1 */ - const plugin_interface *pg_iface; /* Informations à consulter */ - size_t j; /* Boucle de parcours #2 */ - - result = NULL; - *count = 0; + size_t i; /* Boucle de parcours */ g_rw_lock_reader_lock(&_pg_lock); + result = malloc(_pg_count * sizeof(GPluginModule *)); + *count = _pg_count; + for (i = 0; i < _pg_count; i++) { - pg_iface = g_plugin_module_get_interface(_pg_list[i]); - - for (j = 0; j < pg_iface->actions_count; j++) - { - if (pg_iface->actions[j] == action) - { - result = realloc(result, ++(*count) * sizeof(GPluginModule *)); - - result[*count - 1] = _pg_list[i]; - g_object_ref(G_OBJECT(_pg_list[i])); - - break; - - } - - } - + result[i] = _pg_list[i]; + ref_object(result[i]); } g_rw_lock_reader_unlock(&_pg_lock); |