Remove memory allocation in olsr_timer_add()
[oonf.git] / src / core / olsr_plugins.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon(olsrd)
4  * Copyright (c) 2004-2011, the olsr.org team - see HISTORY file
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * * Redistributions of source code must retain the above copyright
12  *   notice, this list of conditions and the following disclaimer.
13  * * Redistributions in binary form must reproduce the above copyright
14  *   notice, this list of conditions and the following disclaimer in
15  *   the documentation and/or other materials provided with the
16  *   distribution.
17  * * Neither the name of olsr.org, olsrd nor the names of its
18  *   contributors may be used to endorse or promote products derived
19  *   from this software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32  * POSSIBILITY OF SUCH DAMAGE.
33  *
34  * Visit http://www.olsr.org for more information.
35  *
36  * If you find this software useful feel free to make a donation
37  * to the project. For more information see the website or contact
38  * the copyright holders.
39  *
40  */
41
42 #include <assert.h>
43 #include <dlfcn.h>
44 #include <errno.h>
45 #include <stdio.h>
46 #include <stdlib.h>
47
48 #include "common/autobuf.h"
49 #include "common/list.h"
50 #include "common/avl.h"
51 #include "common/avl_comp.h"
52 #include "common/template.h"
53
54 #include "builddata/data.h"
55 #include "builddata/plugin_static.h"
56
57 #include "olsr_logging.h"
58 #include "olsr_plugins.h"
59 #include "olsr.h"
60
61 /* Local functions */
62 struct avl_tree plugin_tree;
63 static bool plugin_tree_initialized = false;
64
65 /* library loading patterns */
66 static const char *dlopen_values[5];
67 static const char *dlopen_keys[5] = {
68   "LIB",
69   "PATH",
70   "PRE",
71   "POST",
72   "VER",
73 };
74
75 static const char *dlopen_patterns[] = {
76   "%PATH%/%PRE%%LIB%%POST%.%VER%",
77   "%PATH%/%PRE%%LIB%%POST%",
78   "%PATH%/%LIB%",
79   "%PRE%%LIB%%POST%.%VER%",
80   "%PRE%%LIB%%POST%",
81   "%LIB%",
82 };
83
84 static void _init_plugin_tree(void);
85 static int _unload_plugin(struct olsr_plugin *plugin, bool cleanup);
86 static void *_open_plugin(const char *filename);
87
88 /* remember if initialized or not */
89 OLSR_SUBSYSTEM_STATE(_plugins_state);
90
91 /**
92  * Initialize the plugin loader system
93  */
94 void
95 olsr_plugins_init(void) {
96   if (olsr_subsystem_init(&_plugins_state))
97     return;
98
99   _init_plugin_tree();
100
101   /* load predefined values for dlopen templates */
102   dlopen_values[1] = ".";
103   dlopen_values[2] = olsr_log_get_builddata()->sharedlibrary_prefix;
104   dlopen_values[3] = olsr_log_get_builddata()->sharedlibrary_postfix;
105   dlopen_values[4] = olsr_log_get_builddata()->version;
106 }
107
108 /**
109  * Disable and unload all plugins
110  */
111 void
112 olsr_plugins_cleanup(void) {
113   struct olsr_plugin *plugin, *iterator;
114
115   if (olsr_subsystem_cleanup(&_plugins_state))
116     return;
117
118   OLSR_FOR_ALL_PLUGIN_ENTRIES(plugin, iterator) {
119     olsr_plugins_disable(plugin);
120     _unload_plugin(plugin, true);
121   }
122 }
123
124 /**
125  * This function is called by the constructor of a plugin to
126  * insert the plugin into the global list. It will be called before
127  * any subsystem was initialized!
128  * @param pl_def pointer to plugin definition
129  */
130 void
131 olsr_plugins_hook(struct olsr_plugin *pl_def) {
132   assert (pl_def->name);
133
134   /* make sure plugin tree is initialized */
135   _init_plugin_tree();
136
137   /* check if plugin is already in tree */
138   if (olsr_plugins_get(pl_def->name)) {
139     return;
140   }
141
142   /* hook static plugin into avl tree */
143   pl_def->p_node.key = pl_def->name;
144   avl_insert(&plugin_tree, &pl_def->p_node);
145 }
146
147 /**
148  * Initialize all static plugins
149  * @return -1 if a static plugin could not be loaded, 0 otherwise
150  */
151 int
152 olsr_plugins_init_static(void) {
153   struct olsr_plugin *p, *it;
154   int error = 0;
155
156   assert(!avl_is_empty(&plugin_tree));
157
158   OLSR_FOR_ALL_PLUGIN_ENTRIES(p, it) {
159     if (olsr_plugins_load(p->name) == NULL) {
160       OLSR_WARN(LOG_PLUGINLOADER, "Cannot load plugin '%s'", p->name);
161       error = -1;
162     }
163   }
164   return error;
165 }
166
167 /**
168  * Query for a certain plugin name
169  * @param libname name of plugin
170  * @return pointer to plugin db entry, NULL if not found
171  */
172 struct olsr_plugin *
173 olsr_plugins_get(const char *libname) {
174   struct olsr_plugin *plugin;
175   char *ptr, memorize = 0;
176
177   /* extract only the filename, without path, prefix or suffix */
178   if ((ptr = strrchr(libname, '/')) != NULL) {
179     libname = ptr + 1;
180   }
181
182   if ((ptr = strstr(libname, "olsrd_")) != NULL) {
183     libname = ptr + strlen("olsrd_");
184   }
185
186   if ((ptr = strrchr(libname, '.')) != NULL) {
187     memorize = *ptr;
188     *ptr = 0;
189   }
190
191   plugin = avl_find_element(&plugin_tree, libname, plugin, p_node);
192
193   if (ptr) {
194     /* restore path */
195     *ptr = memorize;
196   }
197   return plugin;
198 }
199
200 /**
201  * Load a plugin and call its initialize callback
202  * @param libname the name of the library(file)
203  * @return plugin db object
204  */
205 struct olsr_plugin *
206 olsr_plugins_load(const char *libname)
207 {
208   void *dlhandle;
209   struct olsr_plugin *plugin;
210
211   /* see if the plugin is there */
212   if ((plugin = olsr_plugins_get(libname)) == NULL) {
213     /* attempt to load the plugin */
214     dlhandle = _open_plugin(libname);
215
216     if (dlhandle == NULL) {
217       /* Logging output has already been done by _open_plugin() */
218       return NULL;
219     }
220
221     /* plugin should be in the tree now */
222     if ((plugin = olsr_plugins_get(libname)) == NULL) {
223       OLSR_WARN(LOG_PLUGINLOADER, "dynamic library loading failed: \"%s\"!\n", dlerror());
224       return NULL;
225     }
226
227     plugin->int_dlhandle = dlhandle;
228   }
229
230   if (!plugin->int_loaded && plugin->load != NULL) {
231     if (plugin->load()) {
232       OLSR_WARN(LOG_PLUGINLOADER, "Load callback failed for plugin %s\n", plugin->name);
233       return NULL;
234     }
235     OLSR_DEBUG(LOG_PLUGINLOADER, "Load callback of plugin %s successful\n", plugin->name);
236   }
237   plugin->int_loaded = true;
238   return plugin;
239 }
240
241 /**
242  * Enable a loaded plugin.
243  * @param plugin pointer to plugin db object
244  * @return 0 if plugin was enabled, -1 otherwise
245  */
246 int
247 olsr_plugins_enable(struct olsr_plugin *plugin) {
248   if (plugin->int_enabled) {
249     OLSR_DEBUG(LOG_PLUGINLOADER, "Plugin %s is already active.\n", plugin->name);
250     return 0;
251   }
252
253   if (!plugin->int_loaded && plugin->load != NULL) {
254     if (plugin->load()) {
255       OLSR_WARN(LOG_PLUGINLOADER, "Error, pre init failed for plugin %s\n", plugin->name);
256       return -1;
257     }
258     OLSR_DEBUG(LOG_PLUGINLOADER, "Pre initialization of plugin %s successful\n", plugin->name);
259   }
260
261   plugin->int_loaded = true;
262
263   if (plugin->enable != NULL) {
264     if (plugin->enable()) {
265       OLSR_WARN(LOG_PLUGINLOADER, "Error, post init failed for plugin %s\n", plugin->name);
266       return -1;
267     }
268     OLSR_DEBUG(LOG_PLUGINLOADER, "Post initialization of plugin %s successful\n", plugin->name);
269   }
270   plugin->int_enabled = true;
271
272   if (plugin->author != NULL && plugin->descr != NULL) {
273     OLSR_INFO(LOG_PLUGINLOADER, "Plugin '%s' (%s) by %s activated successfully\n",
274         plugin->descr, plugin->name, plugin->author);
275   }
276   else {
277     OLSR_INFO(LOG_PLUGINLOADER, "Plugin '%s' activated successfully\n", plugin->name);
278   }
279
280   return 0;
281 }
282
283 /**
284  * Disable (but not unload) an active plugin
285  * @param plugin pointer to plugin db object
286  * @return 0 if plugin was disabled, 1 otherwise
287  */
288 int
289 olsr_plugins_disable(struct olsr_plugin *plugin) {
290   if (!plugin->int_enabled) {
291     OLSR_DEBUG(LOG_PLUGINLOADER, "Plugin %s is not active.\n", plugin->name);
292     return 0;
293   }
294
295   if (!plugin->deactivate) {
296     OLSR_DEBUG(LOG_PLUGINLOADER, "Plugin %s does not support disabling\n", plugin->name);
297     return 1;
298   }
299
300   OLSR_INFO(LOG_PLUGINLOADER, "Deactivating plugin %s\n", plugin->name);
301
302   if (plugin->disable != NULL) {
303     if (plugin->disable()) {
304       OLSR_DEBUG(LOG_PLUGINLOADER, "Plugin %s cannot be deactivated, error in pre cleanup\n", plugin->name);
305       return 1;
306     }
307     OLSR_DEBUG(LOG_PLUGINLOADER, "Pre cleanup of plugin %s successful\n", plugin->name);
308   }
309
310   plugin->int_enabled = false;
311   return 0;
312 }
313
314 /**
315  * Unloads an active plugin. Static plugins cannot be removed until
316  * final cleanup.
317  * @param plugin pointer to plugin db object
318  * @return 0 if plugin was removed, 1 otherwise
319  */
320 int
321 olsr_plugins_unload(struct olsr_plugin *plugin) {
322   return _unload_plugin(plugin, false);
323 }
324
325 /**
326  * Initialize plugin tree for early loading of static plugins
327  */
328 static void
329 _init_plugin_tree(void) {
330   if (plugin_tree_initialized) {
331     return;
332   }
333   avl_init(&plugin_tree, avl_comp_strcasecmp, false, NULL);
334   plugin_tree_initialized = true;
335 }
336
337 /**
338  * Internal helper function to unload a plugin using the old API
339  * @param plugin pointer to plugin db object
340  * @param cleanup true if this is the final cleanup
341  *   before OLSR shuts down, false otherwise
342  * @return 0 if the plugin was removed, -1 otherwise
343  */
344 static int
345 _unload_plugin(struct olsr_plugin *plugin, bool cleanup) {
346   if (plugin->int_enabled) {
347     /* deactivate first if necessary */
348     olsr_plugins_disable(plugin);
349   }
350
351   if (plugin->int_dlhandle == NULL && !cleanup) {
352     /*
353      * this is a static plugin and OLSR is not shutting down,
354      * so it cannot be unloaded
355      */
356     return -1;
357   }
358
359   OLSR_INFO(LOG_PLUGINLOADER, "Unloading plugin %s\n", plugin->name);
360
361   if (plugin->unload != NULL) {
362     plugin->unload();
363   }
364
365   /* remove first from tree */
366   avl_delete(&plugin_tree, &plugin->p_node);
367
368   /* cleanup */
369   if (plugin->int_dlhandle) {
370     dlclose(plugin->int_dlhandle);
371   }
372
373   return false;
374 }
375
376 /**
377  * Internal helper to load plugin with different variants of the
378  * filename.
379  * @param filename pointer to filename
380  */
381 static void *
382 _open_plugin(const char *filename) {
383   struct abuf_template_storage table[5];
384   struct autobuf abuf;
385   void *result;
386   size_t i;
387   int indexCount;
388
389   if (abuf_init(&abuf)) {
390     OLSR_WARN_OOM(LOG_PLUGINLOADER);
391     return NULL;
392   }
393
394   result = NULL;
395   dlopen_values[0] = filename;
396
397   for (i=0; result == NULL && i<ARRAYSIZE(dlopen_patterns); i++) {
398     if ((indexCount = abuf_template_init(dlopen_keys, ARRAYSIZE(dlopen_keys),
399         dlopen_patterns[i], table, ARRAYSIZE(table))) == -1) {
400       OLSR_WARN(LOG_PLUGINLOADER, "Could not parse pattern %s for dlopen",
401           dlopen_patterns[i]);
402       continue;
403     }
404
405     abuf_clear(&abuf);
406     abuf_templatef(&abuf, dlopen_patterns[i], dlopen_values, table, indexCount);
407
408     OLSR_DEBUG(LOG_PLUGINLOADER, "Trying to load library: %s", abuf_getptr(&abuf));
409     result = dlopen(abuf_getptr(&abuf), RTLD_NOW);
410   }
411   if (result == NULL) {
412     OLSR_WARN(LOG_PLUGINLOADER, "Loading of plugin %s failed.\n", filename);
413   }
414   else {
415     OLSR_INFO(LOG_PLUGINLOADER, "Loading plugin %s from %s\n", filename, abuf_getptr(&abuf));
416   }
417
418   abuf_free(&abuf);
419   return result;
420 }