summaryrefslogtreecommitdiff
path: root/src/plugins/pglist.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/pglist.c')
-rw-r--r--src/plugins/pglist.c435
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);