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