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