uncrustify as demanded.
[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      SPDX-License-Identifier: AGPL3.0-or-later
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-plugin", __VA_ARGS__)
32
33 /**
34  * Linked list of active plugins.
35  */
36 struct PluginList {
37   /**
38    * This is a linked list.
39    */
40   struct PluginList *next;
41
42   /**
43    * Name of the library.
44    */
45   char *name;
46
47   /**
48    * System handle.
49    */
50   void *handle;
51 };
52
53
54 /**
55  * Have we been initialized?
56  */
57 static int initialized;
58
59 /**
60  * Libtool search path before we started.
61  */
62 static char *old_dlsearchpath;
63
64 /**
65  * List of plugins we have loaded.
66  */
67 static struct PluginList *plugins;
68
69
70 /**
71  * Setup libtool paths.
72  */
73 static void
74 plugin_init()
75 {
76   int err;
77   const char *opath;
78   char *path;
79   char *cpath;
80
81   err = lt_dlinit();
82   if (err > 0)
83     {
84       fprintf(stderr,
85               _("Initialization of plugin mechanism failed: %s!\n"),
86               lt_dlerror());
87       return;
88     }
89   opath = lt_dlgetsearchpath();
90   if (NULL != opath)
91     old_dlsearchpath = GNUNET_strdup(opath);
92   path = GNUNET_OS_installation_get_path(GNUNET_OS_IPK_LIBDIR);
93   if (NULL != path)
94     {
95       if (NULL != opath)
96         {
97           GNUNET_asprintf(&cpath, "%s:%s", opath, path);
98           lt_dlsetsearchpath(cpath);
99           GNUNET_free(path);
100           GNUNET_free(cpath);
101         }
102       else
103         {
104           lt_dlsetsearchpath(path);
105           GNUNET_free(path);
106         }
107     }
108 }
109
110
111 /**
112  * Shutdown libtool.
113  */
114 static void
115 plugin_fini()
116 {
117   lt_dlsetsearchpath(old_dlsearchpath);
118   if (NULL != old_dlsearchpath)
119     {
120       GNUNET_free(old_dlsearchpath);
121       old_dlsearchpath = NULL;
122     }
123   lt_dlexit();
124 }
125
126
127 /**
128  * Lookup a function in the plugin.
129  *
130  * @param plug the plugin to check
131  * @param name name of the symbol to look for
132  * @return NULL if the symbol was not found
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 (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],
149         lt_dlerror());
150   GNUNET_free(initName);
151   return mptr;
152 }
153
154
155 /**
156  * Test if a plugin exists.
157  *
158  * Note that the library must export a symbol called
159  * `library_name_init` for the test to succeed.
160  *
161  * @param library_name name of the plugin to test if it is installed
162  * @return #GNUNET_YES if the plugin exists, #GNUNET_NO if not
163  */
164 int
165 GNUNET_PLUGIN_test(const char *library_name)
166 {
167   void *libhandle;
168   GNUNET_PLUGIN_Callback init;
169   struct PluginList plug;
170
171   if (!initialized)
172     {
173       initialized = GNUNET_YES;
174       plugin_init();
175     }
176   libhandle = lt_dlopenext(library_name);
177   if (NULL == libhandle)
178     return GNUNET_NO;
179   plug.handle = libhandle;
180   plug.name = (char *)library_name;
181   init = resolve_function(&plug, "init");
182   if (NULL == init)
183     {
184       GNUNET_break(0);
185       lt_dlclose(libhandle);
186       return GNUNET_NO;
187     }
188   lt_dlclose(libhandle);
189   return GNUNET_YES;
190 }
191
192
193 /**
194  * Setup plugin (runs the `init` callback and returns whatever `init`
195  * returned).  If `init` returns NULL, the plugin is unloaded.
196  *
197  * Note that the library must export symbols called
198  * `library_name_init` and `library_name_done`.  These will be called
199  * when the library is loaded and unloaded respectively.
200  *
201  * @param library_name name of the plugin to load
202  * @param arg argument to the plugin initialization function
203  * @return whatever the initialization function returned
204  */
205 void *
206 GNUNET_PLUGIN_load(const char *library_name, void *arg)
207 {
208   void *libhandle;
209   struct PluginList *plug;
210   GNUNET_PLUGIN_Callback init;
211   void *ret;
212
213   if (!initialized)
214     {
215       initialized = GNUNET_YES;
216       plugin_init();
217     }
218   libhandle = lt_dlopenext(library_name);
219   if (libhandle == NULL)
220     {
221       LOG(GNUNET_ERROR_TYPE_ERROR,
222           _("`%s' failed for library `%s' with error: %s\n"),
223           "lt_dlopenext",
224           library_name,
225           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, void *arg)
256 {
257   struct PluginList *pos;
258   struct PluginList *prev;
259   GNUNET_PLUGIN_Callback done;
260   void *ret;
261
262   prev = NULL;
263   pos = plugins;
264   while ((NULL != pos) && (0 != strcmp(pos->name, library_name)))
265     {
266       prev = pos;
267       pos = pos->next;
268     }
269   if (NULL == pos)
270     return NULL;
271
272   done = resolve_function(pos, "done");
273   ret = NULL;
274   if (NULL != done)
275     ret = done(arg);
276   if (NULL == prev)
277     plugins = pos->next;
278   else
279     prev->next = pos->next;
280   lt_dlclose(pos->handle);
281   GNUNET_free(pos->name);
282   GNUNET_free(pos);
283   if (NULL == plugins)
284     {
285       plugin_fini();
286       initialized = GNUNET_NO;
287     }
288   return ret;
289 }
290
291
292 /**
293  * Closure for #find_libraries().
294  */
295 struct LoadAllContext {
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,
371                        void *arg,
372                        GNUNET_PLUGIN_LoaderCallback cb,
373                        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 */