-fix
[oweals/gnunet.git] / src / util / plugin.c
1 /*
2      This file is part of GNUnet
3      (C) 2002-2013 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  * List of plugins we have loaded.
68  */
69 static struct PluginList *plugins;
70
71
72 /**
73  * Setup libtool paths.
74  */
75 static void
76 plugin_init ()
77 {
78   int err;
79   const char *opath;
80   char *path;
81   char *cpath;
82
83   err = lt_dlinit ();
84   if (err > 0)
85   {
86     FPRINTF (stderr,
87              _("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  * @param plug the plugin to check
133  * @param name name of the symbol to look for
134  * @return NULL if the symbol was not found
135  */
136 static GNUNET_PLUGIN_Callback
137 resolve_function (struct PluginList *plug, const char *name)
138 {
139   char *initName;
140   void *mptr;
141
142   GNUNET_asprintf (&initName, "_%s_%s", plug->name, name);
143   mptr = lt_dlsym (plug->handle, &initName[1]);
144   if (NULL == mptr)
145     mptr = lt_dlsym (plug->handle, initName);
146   if (NULL == mptr)
147     LOG (GNUNET_ERROR_TYPE_ERROR,
148          _("`%s' failed to resolve method '%s' with error: %s\n"),
149          "lt_dlsym",
150          &initName[1], lt_dlerror ());
151   GNUNET_free (initName);
152   return mptr;
153 }
154
155
156 /**
157  * Test if a plugin exists.
158  *
159  * Note that the library must export a symbol called
160  * `library_name_init` for the test to succeed.
161  *
162  * @param library_name name of the plugin to test if it is installed
163  * @return #GNUNET_YES if the plugin exists, #GNUNET_NO if not
164  */
165 int
166 GNUNET_PLUGIN_test (const char *library_name)
167 {
168   void *libhandle;
169   GNUNET_PLUGIN_Callback init;
170   struct PluginList plug;
171
172   if (! initialized)
173   {
174     initialized = GNUNET_YES;
175     plugin_init ();
176   }
177   libhandle = lt_dlopenext (library_name);
178   if (NULL == libhandle)
179     return GNUNET_NO;
180   plug.handle = libhandle;
181   plug.name = (char *) library_name;
182   init = resolve_function (&plug, "init");
183   if (NULL == init)
184   {
185     GNUNET_break (0);
186     lt_dlclose (libhandle);
187     return GNUNET_NO;
188   }
189   lt_dlclose (libhandle);
190   return GNUNET_YES;
191 }
192
193
194 /**
195  * Setup plugin (runs the `init` callback and returns whatever `init`
196  * returned).  If `init` returns NULL, the plugin is unloaded.
197  *
198  * Note that the library must export symbols called
199  * `library_name_init` and `library_name_done`.  These will be called
200  * when the library is loaded and unloaded respectively.
201  *
202  * @param library_name name of the plugin to load
203  * @param arg argument to the plugin initialization function
204  * @return whatever the initialization function returned
205  */
206 void *
207 GNUNET_PLUGIN_load (const char *library_name, void *arg)
208 {
209   void *libhandle;
210   struct PluginList *plug;
211   GNUNET_PLUGIN_Callback init;
212   void *ret;
213
214   if (!initialized)
215   {
216     initialized = GNUNET_YES;
217     plugin_init ();
218   }
219   libhandle = lt_dlopenext (library_name);
220   if (libhandle == NULL)
221   {
222     LOG (GNUNET_ERROR_TYPE_ERROR,
223          _("`%s' failed for library `%s' with error: %s\n"),
224          "lt_dlopenext",
225          library_name, lt_dlerror ());
226     return NULL;
227   }
228   plug = GNUNET_new (struct PluginList);
229   plug->handle = libhandle;
230   plug->name = GNUNET_strdup (library_name);
231   plug->next = plugins;
232   plugins = plug;
233   init = resolve_function (plug, "init");
234   if ((init == NULL) || (NULL == (ret = init (arg))))
235   {
236     lt_dlclose (libhandle);
237     GNUNET_free (plug->name);
238     plugins = plug->next;
239     GNUNET_free (plug);
240     return NULL;
241   }
242   return ret;
243 }
244
245
246 /**
247  * Unload plugin (runs the `done` callback and returns whatever `done`
248  * returned).  The plugin is then unloaded.
249  *
250  * @param library_name name of the plugin to unload
251  * @param arg argument to the plugin shutdown function
252  * @return whatever the shutdown function returned
253  */
254 void *
255 GNUNET_PLUGIN_unload (const char *library_name,
256                       void *arg)
257 {
258   struct PluginList *pos;
259   struct PluginList *prev;
260   GNUNET_PLUGIN_Callback done;
261   void *ret;
262
263   prev = NULL;
264   pos = plugins;
265   while ((NULL != pos) && (0 != strcmp (pos->name, library_name)))
266   {
267     prev = pos;
268     pos = pos->next;
269   }
270   if (NULL == pos)
271     return NULL;
272
273   done = resolve_function (pos, "done");
274   ret = NULL;
275   if (NULL != done)
276     ret = done (arg);
277   if (NULL == prev)
278     plugins = pos->next;
279   else
280     prev->next = pos->next;
281   lt_dlclose (pos->handle);
282   GNUNET_free (pos->name);
283   GNUNET_free (pos);
284   if (NULL == plugins)
285   {
286     plugin_fini ();
287     initialized = GNUNET_NO;
288   }
289   return ret;
290 }
291
292
293 /**
294  * Closure for #find_libraries().
295  */
296 struct LoadAllContext
297 {
298   /**
299    * Prefix the plugin names we find have to match.
300    */
301   const char *basename;
302
303   /**
304    * Argument to give to 'init' when loading the plugin.
305    */
306   void *arg;
307
308   /**
309    * Function to call for each plugin.
310    */
311   GNUNET_PLUGIN_LoaderCallback cb;
312
313   /**
314    * Closure for @e cb
315    */
316   void *cb_cls;
317 };
318
319
320 /**
321  * Function called on each plugin in the directory.  Loads
322  * the plugins that match the given basename.
323  *
324  * @param cls the `struct LoadAllContext` describing which
325  *            plugins to load and what to do with them
326  * @param filename name of a plugin library to check
327  * @return #GNUNET_OK (continue loading)
328  */
329 static int
330 find_libraries (void *cls, const char *filename)
331 {
332   struct LoadAllContext *lac = cls;
333   const char *slashpos;
334   const char *libname;
335   char *basename;
336   char *dot;
337   void *lib_ret;
338   size_t n;
339
340   libname = filename;
341   while (NULL != (slashpos = strstr (libname, DIR_SEPARATOR_STR)))
342     libname = slashpos + 1;
343   n = strlen (libname);
344   if (0 != strncmp (lac->basename, libname, strlen (lac->basename)))
345     return GNUNET_OK;           /* wrong name */
346   if ((n > 3) && (0 == strcmp (&libname[n - 3], ".la")))
347     return GNUNET_OK;           /* .la file */
348   basename = GNUNET_strdup (libname);
349   if (NULL != (dot = strstr (basename, ".")))
350     *dot = '\0';
351   lib_ret = GNUNET_PLUGIN_load (basename, lac->arg);
352   if (NULL != lib_ret)
353     lac->cb (lac->cb_cls, basename, lib_ret);
354   GNUNET_free (basename);
355   return GNUNET_OK;
356 }
357
358
359 /**
360  * Load all compatible plugins with the given base name.
361  *
362  * Note that the library must export symbols called
363  * `basename_ANYTHING_init` and `basename_ANYTHING__done`.  These will
364  * be called when the library is loaded and unloaded respectively.
365  *
366  * @param basename basename of the plugins to load
367  * @param arg argument to the plugin initialization function
368  * @param cb function to call for each plugin found
369  * @param cb_cls closure for @a cb
370  */
371 void
372 GNUNET_PLUGIN_load_all (const char *basename, void *arg,
373                         GNUNET_PLUGIN_LoaderCallback cb, void *cb_cls)
374 {
375   struct LoadAllContext lac;
376   char *path;
377
378   path = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
379   if (NULL == path)
380   {
381     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
382                 _("Could not determine plugin installation path.\n"));
383     return;
384   }
385   lac.basename = basename;
386   lac.arg = arg;
387   lac.cb = cb;
388   lac.cb_cls = cb_cls;
389   GNUNET_DISK_directory_scan (path, &find_libraries, &lac);
390   GNUNET_free (path);
391 }
392
393
394 /* end of plugin.c */