-doxygen and style fixes
[oweals/gnunet.git] / src / util / plugin.c
1 /*
2      This file is part of GNUnet
3      (C) 2002, 2003, 2004, 2005, 2006, 2009 Christian Grothoff (and other contributing authors)
4
5      GNUnet is free software; you can redistribute it and/or modify
6      it under the terms of the GNU General Public License as published
7      by the Free Software Foundation; either version 3, or (at your
8      option) any later version.
9
10      GNUnet is distributed in the hope that it will be useful, but
11      WITHOUT ANY WARRANTY; without even the implied warranty of
12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13      General Public License for more details.
14
15      You should have received a copy of the GNU General Public License
16      along with GNUnet; see the file COPYING.  If not, write to the
17      Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18      Boston, MA 02111-1307, USA.
19 */
20
21 /**
22  * @file util/plugin.c
23  * @brief Methods to access plugins
24  * @author Christian Grothoff
25  */
26
27 #include "platform.h"
28 #include <ltdl.h>
29 #include "gnunet_util_lib.h"
30
31 #define LOG(kind,...) GNUNET_log_from (kind, "util", __VA_ARGS__)
32
33 /**
34  * Linked list of active plugins.
35  */
36 struct PluginList
37 {
38   /**
39    * This is a linked list.
40    */
41   struct PluginList *next;
42
43   /**
44    * Name of the library.
45    */
46   char *name;
47
48   /**
49    * System handle.
50    */
51   void *handle;
52 };
53
54
55 /**
56  * Have we been initialized?
57  */
58 static int initialized;
59
60
61 /**
62  * Libtool search path before we started.
63  */
64 static char *old_dlsearchpath;
65
66
67 /**
68  * List of plugins we have loaded.
69  */
70 static struct PluginList *plugins;
71
72
73 /**
74  * Setup libtool paths.
75  */
76 static void
77 plugin_init ()
78 {
79   int err;
80   const char *opath;
81   char *path;
82   char *cpath;
83
84   err = lt_dlinit ();
85   if (err > 0)
86   {
87     FPRINTF (stderr, _("Initialization of plugin mechanism failed: %s!\n"),
88              lt_dlerror ());
89     return;
90   }
91   opath = lt_dlgetsearchpath ();
92   if (opath != NULL)
93     old_dlsearchpath = GNUNET_strdup (opath);
94   path = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
95   if (path != NULL)
96   {
97     if (opath != NULL)
98     {
99       GNUNET_asprintf (&cpath, "%s:%s", opath, path);
100       lt_dlsetsearchpath (cpath);
101       GNUNET_free (path);
102       GNUNET_free (cpath);
103     }
104     else
105     {
106       lt_dlsetsearchpath (path);
107       GNUNET_free (path);
108     }
109   }
110 }
111
112
113 /**
114  * Shutdown libtool.
115  */
116 static void
117 plugin_fini ()
118 {
119   lt_dlsetsearchpath (old_dlsearchpath);
120   if (old_dlsearchpath != NULL)
121   {
122     GNUNET_free (old_dlsearchpath);
123     old_dlsearchpath = NULL;
124   }
125   lt_dlexit ();
126 }
127
128
129 /**
130  * Lookup a function in the plugin.
131  */
132 static GNUNET_PLUGIN_Callback
133 resolve_function (struct PluginList *plug, const char *name)
134 {
135   char *initName;
136   void *mptr;
137
138   GNUNET_asprintf (&initName, "_%s_%s", plug->name, name);
139   mptr = lt_dlsym (plug->handle, &initName[1]);
140   if (mptr == NULL)
141     mptr = lt_dlsym (plug->handle, initName);
142   if (mptr == NULL)
143     LOG (GNUNET_ERROR_TYPE_ERROR,
144          _("`%s' failed to resolve method '%s' with error: %s\n"), "lt_dlsym",
145          &initName[1], lt_dlerror ());
146   GNUNET_free (initName);
147   return mptr;
148 }
149
150 /**
151  * Test if a plugin exists.
152  *
153  * Note that the library must export a symbol called
154  * "library_name_init" for the test to succeed.
155  *
156  * @param library_name name of the plugin to test if it is installed
157  * @return GNUNET_YES if the plugin exists, GNUNET_NO if not
158  */
159 int
160 GNUNET_PLUGIN_test (const char *library_name)
161 {
162   void *libhandle;
163   GNUNET_PLUGIN_Callback init;
164   struct PluginList plug;
165
166   if (!initialized)
167   {
168     initialized = GNUNET_YES;
169     plugin_init ();
170   }
171   libhandle = lt_dlopenext (library_name);
172   if (libhandle == NULL)
173     return GNUNET_NO;
174   plug.handle = libhandle;
175   plug.name = (char *) library_name;
176   init = resolve_function (&plug, "init");
177   if (init == NULL)
178   {
179     GNUNET_break (0);
180     lt_dlclose (libhandle);
181     return GNUNET_NO;
182   }
183   lt_dlclose (libhandle);
184   return GNUNET_YES;
185 }
186
187
188 /**
189  * Setup plugin (runs the "init" callback and returns whatever "init"
190  * returned).  If "init" returns NULL, the plugin is unloaded.
191  *
192  * Note that the library must export symbols called
193  * "library_name_init" and "library_name_done".  These will be called
194  * when the library is loaded and unloaded respectively.
195  *
196  * @param library_name name of the plugin to load
197  * @param arg argument to the plugin initialization function
198  * @return whatever the initialization function returned
199  */
200 void *
201 GNUNET_PLUGIN_load (const char *library_name, void *arg)
202 {
203   void *libhandle;
204   struct PluginList *plug;
205   GNUNET_PLUGIN_Callback init;
206   void *ret;
207
208   if (!initialized)
209   {
210     initialized = GNUNET_YES;
211     plugin_init ();
212   }
213   libhandle = lt_dlopenext (library_name);
214   if (libhandle == NULL)
215   {
216     LOG (GNUNET_ERROR_TYPE_ERROR,
217          _("`%s' failed for library `%s' with error: %s\n"), "lt_dlopenext",
218          library_name, lt_dlerror ());
219     return NULL;
220   }
221   plug = GNUNET_malloc (sizeof (struct PluginList));
222   plug->handle = libhandle;
223   plug->name = GNUNET_strdup (library_name);
224   plug->next = plugins;
225   plugins = plug;
226   init = resolve_function (plug, "init");
227   if ((init == NULL) || (NULL == (ret = init (arg))))
228   {
229     lt_dlclose (libhandle);
230     GNUNET_free (plug->name);
231     plugins = plug->next;
232     GNUNET_free (plug);
233     return NULL;
234   }
235   return ret;
236 }
237
238
239 /**
240  * Unload plugin (runs the "done" callback and returns whatever "done"
241  * returned).  The plugin is then unloaded.
242  *
243  * @param library_name name of the plugin to unload
244  * @param arg argument to the plugin shutdown function
245  * @return whatever the shutdown function returned
246  */
247 void *
248 GNUNET_PLUGIN_unload (const char *library_name, void *arg)
249 {
250   struct PluginList *pos;
251   struct PluginList *prev;
252   GNUNET_PLUGIN_Callback done;
253   void *ret;
254
255   prev = NULL;
256   pos = plugins;
257   while ((pos != NULL) && (0 != strcmp (pos->name, library_name)))
258   {
259     prev = pos;
260     pos = pos->next;
261   }
262   if (pos == NULL)
263     return NULL;
264
265   done = resolve_function (pos, "done");
266   ret = NULL;
267   if (done != NULL)
268     ret = done (arg);
269   if (prev == NULL)
270     plugins = pos->next;
271   else
272     prev->next = pos->next;
273   lt_dlclose (pos->handle);
274   GNUNET_free (pos->name);
275   GNUNET_free (pos);
276   if (plugins == NULL)
277   {
278     plugin_fini ();
279     initialized = GNUNET_NO;
280   }
281   return ret;
282 }
283
284
285 struct LoadAllContext
286 {
287   const char *basename;
288   void *arg;
289   GNUNET_PLUGIN_LoaderCallback cb;
290   void *cb_cls;
291 };
292
293
294 static int
295 find_libraries (void *cls, const char *filename)
296 {
297   struct LoadAllContext *lac = cls;
298   const char *slashpos;
299   const char *libname;
300   char *basename;
301   char *dot;
302   void *lib_ret;
303   size_t n;
304
305   libname = filename;
306   while (NULL != (slashpos = strstr (libname, DIR_SEPARATOR_STR)))
307     libname = slashpos + 1;
308   n = strlen (libname);
309   if (0 != strncmp (lac->basename, libname, strlen (lac->basename)))
310     return GNUNET_OK;           /* wrong name */
311   if ((n > 3) && (0 == strcmp (&libname[n - 3], ".la")))
312     return GNUNET_OK;           /* .la file */
313   basename = GNUNET_strdup (libname);
314   if (NULL != (dot = strstr (basename, ".")))
315     *dot = '\0';
316   lib_ret = GNUNET_PLUGIN_load (basename, lac->arg);
317   if (NULL != lib_ret)
318     lac->cb (lac->cb_cls, basename, lib_ret);
319   GNUNET_free (basename);
320   return GNUNET_OK;
321 }
322
323
324 /**
325  * Load all compatible plugins with the given base name.
326  *
327  * Note that the library must export symbols called
328  * "basename_ANYTHING_init" and "basename_ANYTHING__done".  These will
329  * be called when the library is loaded and unloaded respectively.
330  *
331  * @param basename basename of the plugins to load
332  * @param arg argument to the plugin initialization function
333  * @param cb function to call for each plugin found
334  * @param cb_cls closure for 'cb'
335  */
336 void
337 GNUNET_PLUGIN_load_all (const char *basename, void *arg,
338                         GNUNET_PLUGIN_LoaderCallback cb, void *cb_cls)
339 {
340   struct LoadAllContext lac;
341   char *path;
342
343   path = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
344   if (path == NULL)
345   {
346     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
347                 _("Could not determine plugin installation path.\n"));
348     return;
349   }
350   lac.basename = basename;
351   lac.arg = arg;
352   lac.cb = cb;
353   lac.cb_cls = cb_cls;
354   GNUNET_DISK_directory_scan (path, &find_libraries, &lac);
355   GNUNET_free (path);
356 }
357
358
359 /* end of plugin.c */