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