aboutsummaryrefslogtreecommitdiff
path: root/src/util/plugin.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/util/plugin.c')
-rw-r--r--src/util/plugin.c361
1 files changed, 361 insertions, 0 deletions
diff --git a/src/util/plugin.c b/src/util/plugin.c
new file mode 100644
index 0000000..4e0385a
--- /dev/null
+++ b/src/util/plugin.c
@@ -0,0 +1,361 @@
+/*
+ This file is part of GNUnet
+ (C) 2002, 2003, 2004, 2005, 2006, 2009 Christian Grothoff (and other contributing authors)
+
+ GNUnet is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published
+ by the Free Software Foundation; either version 2, or (at your
+ option) any later version.
+
+ GNUnet 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
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with GNUnet; see the file COPYING. If not, write to the
+ Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ Boston, MA 02111-1307, USA.
+*/
+
+/**
+ * @file util/plugin.c
+ * @brief Methods to access plugins
+ * @author Christian Grothoff
+ */
+
+#include "platform.h"
+#include <ltdl.h>
+#include "gnunet_common.h"
+#include "gnunet_os_lib.h"
+#include "gnunet_plugin_lib.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
+
+/**
+ * Linked list of active plugins.
+ */
+struct PluginList
+{
+ /**
+ * This is a linked list.
+ */
+ struct PluginList *next;
+
+ /**
+ * Name of the library.
+ */
+ char *name;
+
+ /**
+ * System handle.
+ */
+ void *handle;
+};
+
+
+/**
+ * Have we been initialized?
+ */
+static int initialized;
+
+
+/**
+ * Libtool search path before we started.
+ */
+static char *old_dlsearchpath;
+
+
+/**
+ * List of plugins we have loaded.
+ */
+static struct PluginList *plugins;
+
+
+/**
+ * Setup libtool paths.
+ */
+static void
+plugin_init ()
+{
+ int err;
+ const char *opath;
+ char *path;
+ char *cpath;
+
+ err = lt_dlinit ();
+ if (err > 0)
+ {
+ FPRINTF (stderr, _("Initialization of plugin mechanism failed: %s!\n"),
+ lt_dlerror ());
+ return;
+ }
+ opath = lt_dlgetsearchpath ();
+ if (opath != NULL)
+ old_dlsearchpath = GNUNET_strdup (opath);
+ path = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
+ if (path != NULL)
+ {
+ if (opath != NULL)
+ {
+ GNUNET_asprintf (&cpath, "%s:%s", opath, path);
+ lt_dlsetsearchpath (cpath);
+ GNUNET_free (path);
+ GNUNET_free (cpath);
+ }
+ else
+ {
+ lt_dlsetsearchpath (path);
+ GNUNET_free (path);
+ }
+ }
+}
+
+
+/**
+ * Shutdown libtool.
+ */
+static void
+plugin_fini ()
+{
+ lt_dlsetsearchpath (old_dlsearchpath);
+ if (old_dlsearchpath != NULL)
+ {
+ GNUNET_free (old_dlsearchpath);
+ old_dlsearchpath = NULL;
+ }
+ lt_dlexit ();
+}
+
+
+/**
+ * Lookup a function in the plugin.
+ */
+static GNUNET_PLUGIN_Callback
+resolve_function (struct PluginList *plug, const char *name)
+{
+ char *initName;
+ void *mptr;
+
+ GNUNET_asprintf (&initName, "_%s_%s", plug->name, name);
+ mptr = lt_dlsym (plug->handle, &initName[1]);
+ if (mptr == NULL)
+ mptr = lt_dlsym (plug->handle, initName);
+ if (mptr == NULL)
+ LOG (GNUNET_ERROR_TYPE_ERROR,
+ _("`%s' failed to resolve method '%s' with error: %s\n"), "lt_dlsym",
+ &initName[1], lt_dlerror ());
+ GNUNET_free (initName);
+ return mptr;
+}
+
+/**
+ * Test if a plugin exists.
+ *
+ * Note that the library must export a symbol called
+ * "library_name_init" for the test to succeed.
+ *
+ * @param library_name name of the plugin to test if it is installed
+ * @return GNUNET_YES if the plugin exists, GNUNET_NO if not
+ */
+int
+GNUNET_PLUGIN_test (const char *library_name)
+{
+ void *libhandle;
+ GNUNET_PLUGIN_Callback init;
+ struct PluginList plug;
+
+ if (!initialized)
+ {
+ initialized = GNUNET_YES;
+ plugin_init ();
+ }
+ libhandle = lt_dlopenext (library_name);
+ if (libhandle == NULL)
+ return GNUNET_NO;
+ plug.handle = libhandle;
+ plug.name = (char *) library_name;
+ init = resolve_function (&plug, "init");
+ if (init == NULL)
+ {
+ GNUNET_break (0);
+ lt_dlclose (libhandle);
+ return GNUNET_NO;
+ }
+ lt_dlclose (libhandle);
+ return GNUNET_YES;
+}
+
+
+/**
+ * Setup plugin (runs the "init" callback and returns whatever "init"
+ * returned). If "init" returns NULL, the plugin is unloaded.
+ *
+ * Note that the library must export symbols called
+ * "library_name_init" and "library_name_done". These will be called
+ * when the library is loaded and unloaded respectively.
+ *
+ * @param library_name name of the plugin to load
+ * @param arg argument to the plugin initialization function
+ * @return whatever the initialization function returned
+ */
+void *
+GNUNET_PLUGIN_load (const char *library_name, void *arg)
+{
+ void *libhandle;
+ struct PluginList *plug;
+ GNUNET_PLUGIN_Callback init;
+ void *ret;
+
+ if (!initialized)
+ {
+ initialized = GNUNET_YES;
+ plugin_init ();
+ }
+ libhandle = lt_dlopenext (library_name);
+ if (libhandle == NULL)
+ {
+ LOG (GNUNET_ERROR_TYPE_ERROR,
+ _("`%s' failed for library `%s' with error: %s\n"), "lt_dlopenext",
+ library_name, lt_dlerror ());
+ return NULL;
+ }
+ plug = GNUNET_malloc (sizeof (struct PluginList));
+ plug->handle = libhandle;
+ plug->name = GNUNET_strdup (library_name);
+ plug->next = plugins;
+ plugins = plug;
+ init = resolve_function (plug, "init");
+ if ((init == NULL) || (NULL == (ret = init (arg))))
+ {
+ lt_dlclose (libhandle);
+ GNUNET_free (plug->name);
+ plugins = plug->next;
+ GNUNET_free (plug);
+ return NULL;
+ }
+ return ret;
+}
+
+
+/**
+ * Unload plugin (runs the "done" callback and returns whatever "done"
+ * returned). The plugin is then unloaded.
+ *
+ * @param library_name name of the plugin to unload
+ * @param arg argument to the plugin shutdown function
+ * @return whatever the shutdown function returned
+ */
+void *
+GNUNET_PLUGIN_unload (const char *library_name, void *arg)
+{
+ struct PluginList *pos;
+ struct PluginList *prev;
+ GNUNET_PLUGIN_Callback done;
+ void *ret;
+
+ prev = NULL;
+ pos = plugins;
+ while ((pos != NULL) && (0 != strcmp (pos->name, library_name)))
+ {
+ prev = pos;
+ pos = pos->next;
+ }
+ if (pos == NULL)
+ return NULL;
+
+ done = resolve_function (pos, "done");
+ ret = NULL;
+ if (done != NULL)
+ ret = done (arg);
+ if (prev == NULL)
+ plugins = pos->next;
+ else
+ prev->next = pos->next;
+ lt_dlclose (pos->handle);
+ GNUNET_free (pos->name);
+ GNUNET_free (pos);
+ if (plugins == NULL)
+ {
+ plugin_fini ();
+ initialized = GNUNET_NO;
+ }
+ return ret;
+}
+
+
+struct LoadAllContext
+{
+ const char *basename;
+ void *arg;
+ GNUNET_PLUGIN_LoaderCallback cb;
+ void *cb_cls;
+};
+
+
+static int
+find_libraries (void *cls, const char *filename)
+{
+ struct LoadAllContext *lac = cls;
+ const char *slashpos;
+ const char *libname;
+ char *basename;
+ char *dot;
+ void *lib_ret;
+ size_t n;
+
+ libname = filename;
+ while (NULL != (slashpos = strstr (libname, DIR_SEPARATOR_STR)))
+ libname = slashpos + 1;
+ n = strlen (libname);
+ if (0 != strncmp (lac->basename, libname, strlen (lac->basename)))
+ return GNUNET_OK; /* wrong name */
+ if ((n > 3) && (0 == strcmp (&libname[n - 3], ".la")))
+ return GNUNET_OK; /* .la file */
+ basename = GNUNET_strdup (libname);
+ if (NULL != (dot = strstr (basename, ".")))
+ *dot = '\0';
+ lib_ret = GNUNET_PLUGIN_load (basename, lac->arg);
+ if (NULL != lib_ret)
+ lac->cb (lac->cb_cls, basename, lib_ret);
+ GNUNET_free (basename);
+ return GNUNET_OK;
+}
+
+
+/**
+ * Load all compatible plugins with the given base name.
+ *
+ * Note that the library must export symbols called
+ * "basename_ANYTHING_init" and "basename_ANYTHING__done". These will
+ * be called when the library is loaded and unloaded respectively.
+ *
+ * @param basename basename of the plugins to load
+ * @param arg argument to the plugin initialization function
+ * @param cb function to call for each plugin found
+ * @param cb_cls closure for 'cb'
+ */
+void
+GNUNET_PLUGIN_load_all (const char *basename, void *arg,
+ GNUNET_PLUGIN_LoaderCallback cb, void *cb_cls)
+{
+ struct LoadAllContext lac;
+ char *path;
+
+ path = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
+ if (path == NULL)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ _("Could not determine plugin installation path.\n"));
+ return;
+ }
+ lac.basename = basename;
+ lac.arg = arg;
+ lac.cb = cb;
+ lac.cb_cls = cb_cls;
+ GNUNET_DISK_directory_scan (path, &find_libraries, &lac);
+ GNUNET_free (path);
+}
+
+
+/* end of plugin.c */