Returns now GNUNET_SYSERR
[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 /**
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,
88                _("Initialization of plugin mechanism failed: %s!\n"),
89                lt_dlerror ());
90       return;
91     }
92   opath = lt_dlgetsearchpath ();
93   if (opath != NULL)
94     old_dlsearchpath = GNUNET_strdup (opath);
95   path = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_LIBDIR);
96   if (path != NULL)
97     {
98       if (opath != NULL)
99         {
100           GNUNET_asprintf (&cpath,
101                            "%s:%s",
102                            opath,
103                            path);
104           lt_dlsetsearchpath (cpath);
105           GNUNET_free (path);
106           GNUNET_free (cpath);
107         }
108       else
109         {
110           lt_dlsetsearchpath (path);
111           GNUNET_free (path);
112         }
113     }
114 }
115
116
117 /**
118  * Shutdown libtool.
119  */
120 static void
121 plugin_fini ()
122 {
123   lt_dlsetsearchpath (old_dlsearchpath);
124   if (old_dlsearchpath != NULL)
125     {
126       GNUNET_free (old_dlsearchpath);
127       old_dlsearchpath = NULL;
128     }
129   lt_dlexit ();
130 }
131
132
133 /**
134  * Lookup a function in the plugin.
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 (mptr == NULL)
145     mptr = lt_dlsym (plug->handle, initName);
146   if (mptr == NULL)
147     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
148                 _("`%s' failed to resolve method '%s' with error: %s\n"),
149                 "lt_dlsym", &initName[1], lt_dlerror ());
150   GNUNET_free (initName);
151   return mptr;
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 (libhandle == NULL)
177     return GNUNET_NO;
178   plug.handle = libhandle;
179   plug.name = (char*) library_name;
180   init = resolve_function (&plug, "init");
181   if (init == NULL)
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       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
221                   _("`%s' failed for library `%s' with error: %s\n"),
222                   "lt_dlopenext", library_name, lt_dlerror ());
223       return NULL;
224     }
225   plug = GNUNET_malloc (sizeof (struct PluginList));
226   plug->handle = libhandle;
227   plug->name = GNUNET_strdup (library_name);
228   plug->next = plugins;
229   plugins = plug;
230   init = resolve_function (plug, "init");
231   if ((init == NULL) || (NULL == (ret = init (arg))))
232     {
233       lt_dlclose (libhandle);
234       GNUNET_free (plug->name);
235       plugins = plug->next;
236       GNUNET_free (plug);
237       return NULL;
238     }
239   return ret;
240 }
241
242
243 /**
244  * Unload plugin (runs the "done" callback and returns whatever "done"
245  * returned).  The plugin is then unloaded.
246  *
247  * @param library_name name of the plugin to unload
248  * @param arg argument to the plugin shutdown function
249  * @return whatever the shutdown function returned
250  */
251 void *
252 GNUNET_PLUGIN_unload (const char *library_name, void *arg)
253 {
254   struct PluginList *pos;
255   struct PluginList *prev;
256   GNUNET_PLUGIN_Callback done;
257   void *ret;
258
259   prev = NULL;
260   pos = plugins;
261   while ((pos != NULL) && (0 != strcmp (pos->name, library_name)))
262     {
263       prev = pos;
264       pos = pos->next;
265     }
266   if (pos == NULL)
267     return NULL;
268
269   done = resolve_function (pos, "done");
270   ret = NULL;
271   if (done != NULL)
272     ret = done (arg);
273   if (prev == NULL)
274     plugins = pos->next;
275   else
276     prev->next = pos->next;
277   lt_dlclose (pos->handle);
278   GNUNET_free (pos->name);
279   GNUNET_free (pos);
280   if (plugins == NULL)
281     {
282       plugin_fini();
283       initialized = GNUNET_NO;
284     }
285   return ret;
286 }
287
288
289
290 /* end of plugin.c */