4c3162c61838e96d6509dd33f006ba3f5832e1b3
[olsrd.git] / src / plugin_loader.c
1
2 /*
3  * The olsr.org Optimized Link-State Routing daemon(olsrd)
4  * Copyright (c) 2004-2009, 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 "plugin_loader.h"
43 #include "plugin.h"
44 #include "plugin_util.h"
45 #include "defs.h"
46 #include "olsr.h"
47 #include "olsr_logging.h"
48 #include "common/avl.h"
49 #include "common/list.h"
50 #include "olsr_cookie.h"
51
52 #include <dlfcn.h>
53 #include <errno.h>
54 #include <stdlib.h>
55
56 /* Local functions */
57 static struct olsr_plugin *olsr_load_legacy_plugin(const char *, void *);
58
59 struct avl_tree plugin_tree;
60 static bool plugin_tree_initialized = false;
61
62 static struct olsr_cookie_info *plugin_mem_cookie = NULL;
63
64 static bool olsr_internal_unload_plugin(struct olsr_plugin *plugin, bool cleanup);
65
66 /**
67  * This function is called by the constructor of a plugin.
68  * because of this the first call has to initialize the list
69  * head.
70  *
71  * @param pl_def pointer to plugin definition
72  */
73 void
74 olsr_hookup_plugin(struct olsr_plugin *pl_def) {
75   fprintf(stdout, "hookup %s\n", pl_def->p_name);
76   if (!plugin_tree_initialized) {
77     avl_init(&plugin_tree, avl_comp_strcasecmp);
78     plugin_tree_initialized = true;
79   }
80   pl_def->p_node.key = strdup(pl_def->p_name);
81   avl_insert(&plugin_tree, &pl_def->p_node, AVL_DUP_NO);
82 }
83
84 struct olsr_plugin *olsr_get_plugin(const char *libname) {
85   struct avl_node *node;
86   /* SOT: Hacked away the funny plugin check which fails if pathname is included */
87   if (strrchr(libname, '/')) libname = strrchr(libname, '/') + 1;
88   if ((node = avl_find(&plugin_tree, libname)) != NULL) {
89     return plugin_node2tree(node);
90   }
91   return NULL;
92 }
93
94 void
95 olsr_init_pluginsystem(bool fail_fast) {
96   struct plugin_entry *entry;
97   struct olsr_plugin *plugin;
98
99   plugin_mem_cookie = olsr_alloc_cookie("Plugin handle", OLSR_COOKIE_TYPE_MEMORY);
100   olsr_cookie_set_memory_size(plugin_mem_cookie, sizeof(struct olsr_plugin));
101
102   /* could already be initialized */
103   if (!plugin_tree_initialized) {
104     avl_init(&plugin_tree, avl_comp_strcasecmp);
105     plugin_tree_initialized = true;
106   }
107
108   OLSR_INFO(LOG_PLUGINS, "Activating configured plugins...\n");
109   /* first load anything requested but not already loaded */
110   for (entry = olsr_cnf->plugins; entry != NULL; entry = entry->next) {
111     if (olsr_load_plugin(entry->name) == NULL) {
112       if (fail_fast) {
113         OLSR_ERROR(LOG_PLUGINS, "Cannot load plugin %s.\n", entry->name);
114         olsr_exit(1);
115       }
116       OLSR_WARN(LOG_PLUGINS, "Cannot load plugin %s.\n", entry->name);
117     }
118   }
119
120   /* now hookup parameters to plugins */
121   for (entry = olsr_cnf->plugins; entry != NULL; entry = entry->next) {
122     plugin = olsr_get_plugin(entry->name);
123     if (plugin == NULL) {
124       if (fail_fast) {
125         OLSR_ERROR(LOG_PLUGINS, "Internal error in plugin storage tree, cannot find plugin %s\n", entry->name);
126         olsr_exit(1);
127       }
128       OLSR_WARN(LOG_PLUGINS, "Internal error in plugin storage tree, cannot find plugin %s\n", entry->name);
129     }
130
131     plugin->params = entry->params;
132   }
133
134   /* activate all plugins (configured and static linked ones) */
135   OLSR_FOR_ALL_PLUGIN_ENTRIES(plugin) {
136     if (olsr_activate_plugin(plugin)) {
137       if (fail_fast) {
138         OLSR_ERROR(LOG_PLUGINS, "Error, cannot activate plugin %s.\n", entry->name);
139         olsr_exit(1);
140       }
141       OLSR_WARN(LOG_PLUGINS, "Error, cannot activate plugin %s.\n", entry->name);
142     }
143   } OLSR_FOR_ALL_PLUGIN_ENTRIES_END(plugin)
144   OLSR_INFO(LOG_PLUGINS, "All preconfigured plugins loaded.\n");
145 }
146
147 void
148 olsr_destroy_pluginsystem(void) {
149   struct olsr_plugin *plugin;
150
151   OLSR_FOR_ALL_PLUGIN_ENTRIES(plugin) {
152     olsr_deactivate_plugin(plugin);
153     olsr_internal_unload_plugin(plugin, true);
154   } OLSR_FOR_ALL_PLUGIN_ENTRIES_END(plugin)
155 }
156
157 static struct olsr_plugin *
158 olsr_load_legacy_plugin(const char *libname, void *dlhandle) {
159   get_interface_version_func get_interface_version;
160   get_plugin_parameters_func get_plugin_parameters;
161   plugin_init_func init_plugin;
162
163   int plugin_interface_version = -1;
164   struct olsr_plugin *plugin = NULL;
165
166   /* Fetch the interface version function, 3 different ways */
167   get_interface_version = dlsym(dlhandle, "olsrd_plugin_interface_version");
168   if (get_interface_version == NULL) {
169     OLSR_WARN(LOG_PLUGINS, "Warning, cannot determine plugin version of '%s'\n", libname);
170     return NULL;
171   }
172
173   plugin_interface_version = get_interface_version();
174
175   if (plugin_interface_version != 5) {
176     /* old plugin interface */
177     OLSR_ERROR(LOG_PLUGINS, "Failed to load plugin, version %d is too old\n",
178                plugin_interface_version);
179     return NULL;
180   }
181
182   /* Fetch the init function */
183   init_plugin = dlsym(dlhandle, "olsrd_plugin_init");
184   if (init_plugin == NULL) {
185     OLSR_WARN(LOG_PLUGINS, "Failed to fetch plugin init function: %s\n", dlerror());
186     return NULL;
187   }
188
189   get_plugin_parameters = dlsym(dlhandle, "olsrd_get_plugin_parameters");
190   if (get_plugin_parameters == NULL) {
191     OLSR_WARN(LOG_PLUGINS, "Failed to fetch plugin parameters: %s\n", dlerror());
192     return NULL;
193   }
194
195   OLSR_DEBUG(LOG_PLUGINS, "Got plugin %s, version: %d - OK\n", libname, plugin_interface_version);
196
197   /* initialize plugin structure */
198   plugin = (struct olsr_plugin *)olsr_cookie_malloc(plugin_mem_cookie);
199   plugin->p_name = libname;
200   plugin->p_version = plugin_interface_version;
201   plugin->p_legacy_init = init_plugin;
202
203   plugin->p_node.key = strdup(plugin->p_name);
204   plugin->dlhandle = dlhandle;
205
206   /* get parameters */
207   get_plugin_parameters(&plugin->p_param, &plugin->p_param_cnt);
208
209   avl_insert(&plugin_tree, &plugin->p_node, AVL_DUP_NO);
210   return plugin;
211 }
212
213 /**
214  *Try to load a shared library
215  *
216  *@param libname the name of the library(file)
217  *
218  *@return dlhandle
219  */
220 struct olsr_plugin *
221 olsr_load_plugin(const char *libname)
222 {
223   void *dlhandle;
224   struct olsr_plugin *plugin;
225
226   /* see if the plugin is there */
227   if ((plugin = olsr_get_plugin(libname)) != NULL) {
228     return plugin;
229   }
230
231   /* attempt to load the plugin */
232   if (olsr_cnf->dlPath) {
233     char *path = olsr_malloc(strlen(olsr_cnf->dlPath) + strlen(libname) + 1, "Memory for absolute library path");
234     strcpy(path, olsr_cnf->dlPath);
235     strcat(path, libname);
236     OLSR_INFO(LOG_PLUGINS, "Loading plugin %s from %s\n", libname, path);
237     dlhandle = dlopen(path, RTLD_NOW);
238     free(path);
239   } else {
240     OLSR_INFO(LOG_PLUGINS, "Loading plugin %s\n", libname);
241     dlhandle = dlopen(libname, RTLD_NOW);
242   }
243   if (dlhandle == NULL) {
244     OLSR_ERROR(LOG_PLUGINS, "DL loading failed: \"%s\"!\n", dlerror());
245     return NULL;
246   }
247
248   /* version 6 plugins should be in the tree now*/
249   if ((plugin = olsr_get_plugin(libname)) != NULL) {
250     plugin->dlhandle = dlhandle;
251     return plugin;
252   }
253
254   /* try to load a legacy plugin */
255   return olsr_load_legacy_plugin(libname, dlhandle);
256 }
257
258 static bool
259 olsr_internal_unload_plugin(struct olsr_plugin *plugin, bool cleanup) {
260   bool legacy = false;
261
262   if (plugin->active) {
263     olsr_deactivate_plugin(plugin);
264   }
265
266   if (plugin->dlhandle == NULL && !cleanup) {
267     /* this is a static plugin, it cannot be unloaded */
268     return true;
269   }
270
271   OLSR_INFO(LOG_PLUGINS, "Unloading plugin %s\n", plugin->p_name);
272
273   /* remove first from tree */
274   avl_delete(&plugin_tree, &plugin->p_node);
275   free(plugin->p_node.key);
276
277   legacy = plugin->p_version == 5;
278
279   /* cleanup */
280   if (plugin->dlhandle) {
281     dlclose(plugin->dlhandle);
282   }
283
284   /*
285    * legacy must be cached because it plugin memory will be gone after dlclose() for
286    * modern plugins
287    */
288   if (legacy) {
289     olsr_cookie_free(plugin_mem_cookie, plugin);
290   }
291   return false;
292 }
293
294 bool
295 olsr_unload_plugin(struct olsr_plugin *plugin) {
296   return olsr_internal_unload_plugin(plugin, false);
297 }
298
299 bool olsr_activate_plugin(struct olsr_plugin *plugin) {
300   struct plugin_param *params;
301   unsigned int i;
302
303   if (plugin->active) {
304     OLSR_DEBUG(LOG_PLUGINS, "Plugin %s is already active.\n", plugin->p_name);
305     return false;
306   }
307
308   if (plugin->p_pre_init != NULL) {
309     if (plugin->p_pre_init()) {
310       OLSR_WARN(LOG_PLUGINS, "Error, pre init failed for plugin %s\n", plugin->p_name);
311       return true;
312     }
313     OLSR_DEBUG(LOG_PLUGINS, "Pre initialization of plugin %s successful\n", plugin->p_name);
314   }
315
316   /* initialize parameters */
317   OLSR_INFO(LOG_PLUGINS, "Activating plugin %s\n", plugin->p_name);
318   for (params = plugin->params; params != NULL; params = params->next) {
319     OLSR_INFO_NH(LOG_PLUGINS, "    \"%s\" = \"%s\"... ", params->key, params->value);
320
321     for (i = 0; i < plugin->p_param_cnt; i++) {
322       if (0 == plugin->p_param[i].name[0] || 0 == strcasecmp(plugin->p_param[i].name, params->key)) {
323         /* we have found it! */
324         if (plugin->p_param[i].set_plugin_parameter(params->value, plugin->p_param[i].data,
325             0 == plugin->p_param[i].name[0] ? (set_plugin_parameter_addon) params->key : plugin->p_param[i].addon)) {
326           OLSR_DEBUG(LOG_PLUGINS, "Bad plugin parameter \"%s\" = \"%s\"... ", params->key, params->value);
327           return true;
328         }
329         break;
330       }
331     }
332
333     if (i == plugin->p_param_cnt) {
334       OLSR_INFO_NH(LOG_PLUGINS, "    Ignored parameter \"%s\"\n", params->key);
335     }
336   }
337
338   if (plugin->p_post_init != NULL) {
339     if (plugin->p_post_init()) {
340       OLSR_WARN(LOG_PLUGINS, "Error, post init failed for plugin %s\n", plugin->p_name);
341       return true;
342     }
343     OLSR_DEBUG(LOG_PLUGINS, "Post initialization of plugin %s successful\n", plugin->p_name);
344   }
345   if (plugin->p_legacy_init != NULL) {
346     if (plugin->p_legacy_init() != 1) {
347       OLSR_WARN(LOG_PLUGINS, "Error, legacy init failed for plugin %s\n", plugin->p_name);
348       return true;
349     }
350     OLSR_DEBUG(LOG_PLUGINS, "Post initialization of plugin %s successful\n", plugin->p_name);
351   }
352   plugin->active = true;
353
354   if (plugin->p_author != NULL && plugin->p_descr != NULL) {
355     OLSR_INFO(LOG_PLUGINS, "Plugin '%s' (%s) by %s activated sucessfully\n",
356         plugin->p_descr, plugin->p_name, plugin->p_author);
357   }
358   else {
359     OLSR_INFO(LOG_PLUGINS, "%sPlugin '%s' activated sucessfully\n",
360         plugin->p_version != 6 ? "Legacy " : "", plugin->p_name);
361   }
362
363   return false;
364 }
365
366 bool olsr_deactivate_plugin(struct olsr_plugin *plugin) {
367   if (!plugin->active) {
368     OLSR_DEBUG(LOG_PLUGINS, "Plugin %s is not active.\n", plugin->p_name);
369     return false;
370   }
371
372   OLSR_INFO(LOG_PLUGINS, "Deactivating plugin %s\n", plugin->p_name);
373   if (plugin->p_pre_cleanup != NULL) {
374     if (plugin->p_pre_cleanup()) {
375       OLSR_DEBUG(LOG_PLUGINS, "Plugin %s cannot be deactivated, error in pre cleanup\n", plugin->p_name);
376       return true;
377     }
378     OLSR_DEBUG(LOG_PLUGINS, "Pre cleanup of plugin %s successful\n", plugin->p_name);
379   }
380
381   plugin->active = false;
382
383   if (plugin->p_post_cleanup != NULL) {
384     plugin->p_post_cleanup();
385   }
386
387   return false;
388 }
389
390 /*
391  * Local Variables:
392  * mode: c
393  * style: linux
394  * c-basic-offset: 4
395  * indent-tabs-mode: nil
396  * End:
397  */