Missed another plugin-pathname dep. - needs rework anyhow
[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   /* SOT: Hacked away the funny plugin check which fails if pathname is included */
200   if (strrchr(libname, '/')) libname = strrchr(libname, '/') + 1;
201   plugin->p_name = libname;
202   plugin->p_version = plugin_interface_version;
203   plugin->p_legacy_init = init_plugin;
204
205   plugin->p_node.key = strdup(plugin->p_name);
206   plugin->dlhandle = dlhandle;
207
208   /* get parameters */
209   get_plugin_parameters(&plugin->p_param, &plugin->p_param_cnt);
210
211   avl_insert(&plugin_tree, &plugin->p_node, AVL_DUP_NO);
212   return plugin;
213 }
214
215 /**
216  *Try to load a shared library
217  *
218  *@param libname the name of the library(file)
219  *
220  *@return dlhandle
221  */
222 struct olsr_plugin *
223 olsr_load_plugin(const char *libname)
224 {
225   void *dlhandle;
226   struct olsr_plugin *plugin;
227
228   /* see if the plugin is there */
229   if ((plugin = olsr_get_plugin(libname)) != NULL) {
230     return plugin;
231   }
232
233   /* attempt to load the plugin */
234   if (olsr_cnf->dlPath) {
235     char *path = olsr_malloc(strlen(olsr_cnf->dlPath) + strlen(libname) + 1, "Memory for absolute library path");
236     strcpy(path, olsr_cnf->dlPath);
237     strcat(path, libname);
238     OLSR_INFO(LOG_PLUGINS, "Loading plugin %s from %s\n", libname, path);
239     dlhandle = dlopen(path, RTLD_NOW);
240     free(path);
241   } else {
242     OLSR_INFO(LOG_PLUGINS, "Loading plugin %s\n", libname);
243     dlhandle = dlopen(libname, RTLD_NOW);
244   }
245   if (dlhandle == NULL) {
246     OLSR_ERROR(LOG_PLUGINS, "DL loading failed: \"%s\"!\n", dlerror());
247     return NULL;
248   }
249
250   /* version 6 plugins should be in the tree now*/
251   if ((plugin = olsr_get_plugin(libname)) != NULL) {
252     plugin->dlhandle = dlhandle;
253     return plugin;
254   }
255
256   /* try to load a legacy plugin */
257   return olsr_load_legacy_plugin(libname, dlhandle);
258 }
259
260 static bool
261 olsr_internal_unload_plugin(struct olsr_plugin *plugin, bool cleanup) {
262   bool legacy = false;
263
264   if (plugin->active) {
265     olsr_deactivate_plugin(plugin);
266   }
267
268   if (plugin->dlhandle == NULL && !cleanup) {
269     /* this is a static plugin, it cannot be unloaded */
270     return true;
271   }
272
273   OLSR_INFO(LOG_PLUGINS, "Unloading plugin %s\n", plugin->p_name);
274
275   /* remove first from tree */
276   avl_delete(&plugin_tree, &plugin->p_node);
277   free(plugin->p_node.key);
278
279   legacy = plugin->p_version == 5;
280
281   /* cleanup */
282   if (plugin->dlhandle) {
283     dlclose(plugin->dlhandle);
284   }
285
286   /*
287    * legacy must be cached because it plugin memory will be gone after dlclose() for
288    * modern plugins
289    */
290   if (legacy) {
291     olsr_cookie_free(plugin_mem_cookie, plugin);
292   }
293   return false;
294 }
295
296 bool
297 olsr_unload_plugin(struct olsr_plugin *plugin) {
298   return olsr_internal_unload_plugin(plugin, false);
299 }
300
301 bool olsr_activate_plugin(struct olsr_plugin *plugin) {
302   struct plugin_param *params;
303   unsigned int i;
304
305   if (plugin->active) {
306     OLSR_DEBUG(LOG_PLUGINS, "Plugin %s is already active.\n", plugin->p_name);
307     return false;
308   }
309
310   if (plugin->p_pre_init != NULL) {
311     if (plugin->p_pre_init()) {
312       OLSR_WARN(LOG_PLUGINS, "Error, pre init failed for plugin %s\n", plugin->p_name);
313       return true;
314     }
315     OLSR_DEBUG(LOG_PLUGINS, "Pre initialization of plugin %s successful\n", plugin->p_name);
316   }
317
318   /* initialize parameters */
319   OLSR_INFO(LOG_PLUGINS, "Activating plugin %s\n", plugin->p_name);
320   for (params = plugin->params; params != NULL; params = params->next) {
321     OLSR_INFO_NH(LOG_PLUGINS, "    \"%s\" = \"%s\"... ", params->key, params->value);
322
323     for (i = 0; i < plugin->p_param_cnt; i++) {
324       if (0 == plugin->p_param[i].name[0] || 0 == strcasecmp(plugin->p_param[i].name, params->key)) {
325         /* we have found it! */
326         if (plugin->p_param[i].set_plugin_parameter(params->value, plugin->p_param[i].data,
327             0 == plugin->p_param[i].name[0] ? (set_plugin_parameter_addon) params->key : plugin->p_param[i].addon)) {
328           OLSR_DEBUG(LOG_PLUGINS, "Bad plugin parameter \"%s\" = \"%s\"... ", params->key, params->value);
329           return true;
330         }
331         break;
332       }
333     }
334
335     if (i == plugin->p_param_cnt) {
336       OLSR_INFO_NH(LOG_PLUGINS, "    Ignored parameter \"%s\"\n", params->key);
337     }
338   }
339
340   if (plugin->p_post_init != NULL) {
341     if (plugin->p_post_init()) {
342       OLSR_WARN(LOG_PLUGINS, "Error, post init failed for plugin %s\n", plugin->p_name);
343       return true;
344     }
345     OLSR_DEBUG(LOG_PLUGINS, "Post initialization of plugin %s successful\n", plugin->p_name);
346   }
347   if (plugin->p_legacy_init != NULL) {
348     if (plugin->p_legacy_init() != 1) {
349       OLSR_WARN(LOG_PLUGINS, "Error, legacy init failed for plugin %s\n", plugin->p_name);
350       return true;
351     }
352     OLSR_DEBUG(LOG_PLUGINS, "Post initialization of plugin %s successful\n", plugin->p_name);
353   }
354   plugin->active = true;
355
356   if (plugin->p_author != NULL && plugin->p_descr != NULL) {
357     OLSR_INFO(LOG_PLUGINS, "Plugin '%s' (%s) by %s activated sucessfully\n",
358         plugin->p_descr, plugin->p_name, plugin->p_author);
359   }
360   else {
361     OLSR_INFO(LOG_PLUGINS, "%sPlugin '%s' activated sucessfully\n",
362         plugin->p_version != 6 ? "Legacy " : "", plugin->p_name);
363   }
364
365   return false;
366 }
367
368 bool olsr_deactivate_plugin(struct olsr_plugin *plugin) {
369   if (!plugin->active) {
370     OLSR_DEBUG(LOG_PLUGINS, "Plugin %s is not active.\n", plugin->p_name);
371     return false;
372   }
373
374   OLSR_INFO(LOG_PLUGINS, "Deactivating plugin %s\n", plugin->p_name);
375   if (plugin->p_pre_cleanup != NULL) {
376     if (plugin->p_pre_cleanup()) {
377       OLSR_DEBUG(LOG_PLUGINS, "Plugin %s cannot be deactivated, error in pre cleanup\n", plugin->p_name);
378       return true;
379     }
380     OLSR_DEBUG(LOG_PLUGINS, "Pre cleanup of plugin %s successful\n", plugin->p_name);
381   }
382
383   plugin->active = false;
384
385   if (plugin->p_post_cleanup != NULL) {
386     plugin->p_post_cleanup();
387   }
388
389   return false;
390 }
391
392 /*
393  * Local Variables:
394  * mode: c
395  * style: linux
396  * c-basic-offset: 4
397  * indent-tabs-mode: nil
398  * End:
399  */