service_new: ready_confirm_fd
[oweals/gnunet.git] / src / util / plugin.c
index 567b401c43e412db038461523b1b3c60e4e5c841..c7ac47a7c355fb96f0661e451abacb30f01a593a 100644 (file)
@@ -1,10 +1,10 @@
 /*
      This file is part of GNUnet
-     (C) 2002, 2003, 2004, 2005, 2006, 2009 Christian Grothoff (and other contributing authors)
+     Copyright (C) 2002-2013 GNUnet e.V.
 
      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
+     by the Free Software Foundation; either version 3, or (at your
      option) any later version.
 
      GNUnet is distributed in the hope that it will be useful, but
@@ -14,8 +14,8 @@
 
      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.
+     Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+     Boston, MA 02110-1301, USA.
 */
 
 /**
@@ -26,9 +26,9 @@
 
 #include "platform.h"
 #include <ltdl.h>
-#include "gnunet_common.h"
-#include "gnunet_os_lib.h"
-#include "gnunet_plugin_lib.h"
+#include "gnunet_util_lib.h"
+
+#define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
 
 /**
  * Linked list of active plugins.
@@ -57,13 +57,11 @@ struct PluginList
  */
 static int initialized;
 
-
 /**
  * Libtool search path before we started.
  */
 static char *old_dlsearchpath;
 
-
 /**
  * List of plugins we have loaded.
  */
@@ -73,7 +71,7 @@ static struct PluginList *plugins;
 /**
  * Setup libtool paths.
  */
-static void 
+static void
 plugin_init ()
 {
   int err;
@@ -83,34 +81,31 @@ plugin_init ()
 
   err = lt_dlinit ();
   if (err > 0)
-    {
-      fprintf (stderr,
-               _("Initialization of plugin mechanism failed: %s!\n"),
-               lt_dlerror ());
-      return;
-    }
+  {
+    FPRINTF (stderr,
+             _("Initialization of plugin mechanism failed: %s!\n"),
+             lt_dlerror ());
+    return;
+  }
   opath = lt_dlgetsearchpath ();
-  if (opath != NULL)
+  if (NULL != opath)
     old_dlsearchpath = GNUNET_strdup (opath);
   path = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
-  if (path != NULL)
+  if (NULL != path)
+  {
+    if (NULL != opath)
+    {
+      GNUNET_asprintf (&cpath, "%s:%s", opath, path);
+      lt_dlsetsearchpath (cpath);
+      GNUNET_free (path);
+      GNUNET_free (cpath);
+    }
+    else
     {
-      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);
-        }
+      lt_dlsetsearchpath (path);
+      GNUNET_free (path);
     }
+  }
 }
 
 
@@ -121,43 +116,90 @@ static void
 plugin_fini ()
 {
   lt_dlsetsearchpath (old_dlsearchpath);
-  if (old_dlsearchpath != NULL)
-    {
-      GNUNET_free (old_dlsearchpath);
-      old_dlsearchpath = NULL;
-    }
+  if (NULL != old_dlsearchpath)
+  {
+    GNUNET_free (old_dlsearchpath);
+    old_dlsearchpath = NULL;
+  }
   lt_dlexit ();
 }
 
 
 /**
  * Lookup a function in the plugin.
+ *
+ * @param plug the plugin to check
+ * @param name name of the symbol to look for
+ * @return NULL if the symbol was not found
  */
 static GNUNET_PLUGIN_Callback
-resolve_function (struct PluginList *plug, const char *name)
+resolve_function (struct PluginList *plug,
+                  const char *name)
 {
   char *initName;
   void *mptr;
 
-  GNUNET_asprintf (&initName, "_%s_%s", plug->name, name);
+  GNUNET_asprintf (&initName,
+                   "_%s_%s",
+                   plug->name,
+                   name);
   mptr = lt_dlsym (plug->handle, &initName[1]);
-  if (mptr == NULL)
+  if (NULL == mptr)
     mptr = lt_dlsym (plug->handle, initName);
-  if (mptr == NULL)
-    GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
-                _("`%s' failed to resolve method '%s' with error: %s\n"),
-                "lt_dlsym", &initName[1], lt_dlerror ());
+  if (NULL == mptr)
+    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;
 }
 
 
 /**
- * Setup plugin (runs the "init" callback and returns whatever "init"
- * returned).  If "init" returns NULL, the plugin is unloaded.
+ * 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 (NULL == libhandle)
+    return GNUNET_NO;
+  plug.handle = libhandle;
+  plug.name = (char *) library_name;
+  init = resolve_function (&plug, "init");
+  if (NULL == init)
+  {
+    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
+ * `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
@@ -172,38 +214,40 @@ GNUNET_PLUGIN_load (const char *library_name, void *arg)
   GNUNET_PLUGIN_Callback init;
   void *ret;
 
-  if (! initialized)
-    {
-      initialized = GNUNET_YES;
-      plugin_init ();
-    }
+  if (!initialized)
+  {
+    initialized = GNUNET_YES;
+    plugin_init ();
+  }
   libhandle = lt_dlopenext (library_name);
   if (libhandle == NULL)
-    {
-      GNUNET_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));
+  {
+    LOG (GNUNET_ERROR_TYPE_ERROR,
+         _("`%s' failed for library `%s' with error: %s\n"),
+         "lt_dlopenext",
+         library_name, lt_dlerror ());
+    return NULL;
+  }
+  plug = GNUNET_new (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))))
-    {
-      GNUNET_free (plug->name);
-      plugins = plug->next;
-      GNUNET_free (plug);
-      return NULL;
-    }
+  {
+    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"
+ * 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
@@ -211,7 +255,8 @@ GNUNET_PLUGIN_load (const char *library_name, void *arg)
  * @return whatever the shutdown function returned
  */
 void *
-GNUNET_PLUGIN_unload (const char *library_name, void *arg)
+GNUNET_PLUGIN_unload (const char *library_name,
+                      void *arg)
 {
   struct PluginList *pos;
   struct PluginList *prev;
@@ -220,33 +265,133 @@ GNUNET_PLUGIN_unload (const char *library_name, void *arg)
 
   prev = NULL;
   pos = plugins;
-  while ((pos != NULL) && (0 != strcmp (pos->name, library_name)))
-    {
-      prev = pos;
-      pos = pos->next;
-    }
-  if (pos == NULL)
+  while ((NULL != pos) && (0 != strcmp (pos->name, library_name)))
+  {
+    prev = pos;
+    pos = pos->next;
+  }
+  if (NULL == pos)
     return NULL;
 
   done = resolve_function (pos, "done");
   ret = NULL;
-  if (done != NULL)
+  if (NULL != done)
     ret = done (arg);
-  if (prev == NULL)
+  if (NULL == prev)
     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;
-    }
+  if (NULL == plugins)
+  {
+    plugin_fini ();
+    initialized = GNUNET_NO;
+  }
   return ret;
 }
 
 
+/**
+ * Closure for #find_libraries().
+ */
+struct LoadAllContext
+{
+  /**
+   * Prefix the plugin names we find have to match.
+   */
+  const char *basename;
+
+  /**
+   * Argument to give to 'init' when loading the plugin.
+   */
+  void *arg;
+
+  /**
+   * Function to call for each plugin.
+   */
+  GNUNET_PLUGIN_LoaderCallback cb;
+
+  /**
+   * Closure for @e cb
+   */
+  void *cb_cls;
+};
+
+
+/**
+ * Function called on each plugin in the directory.  Loads
+ * the plugins that match the given basename.
+ *
+ * @param cls the `struct LoadAllContext` describing which
+ *            plugins to load and what to do with them
+ * @param filename name of a plugin library to check
+ * @return #GNUNET_OK (continue loading)
+ */
+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 @a 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 (NULL == path)
+  {
+    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 */