Reset registration function for each plugin during feature loading
[strongswan.git] / src / libstrongswan / plugins / plugin_loader.c
1 /*
2 * Copyright (C) 2010 Tobias Brunner
3 * Copyright (C) 2007 Martin Willi
4 * Hochschule fuer Technik Rapperswil
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by the
8 * Free Software Foundation; either version 2 of the License, or (at your
9 * option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
13 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * for more details.
15 */
16
17 #define _GNU_SOURCE
18 #include "plugin_loader.h"
19
20 #include <string.h>
21 #include <dlfcn.h>
22 #include <limits.h>
23 #include <stdio.h>
24
25 #include <debug.h>
26 #include <library.h>
27 #include <integrity_checker.h>
28 #include <utils/linked_list.h>
29 #include <plugins/plugin.h>
30
31 typedef struct private_plugin_loader_t private_plugin_loader_t;
32 typedef struct plugin_entry_t plugin_entry_t;
33
34 /**
35 * private data of plugin_loader
36 */
37 struct private_plugin_loader_t {
38
39 /**
40 * public functions
41 */
42 plugin_loader_t public;
43
44 /**
45 * List of plugins, as plugin_entry_t
46 */
47 linked_list_t *plugins;
48 };
49
50 /**
51 * Entry for a plugin
52 */
53 struct plugin_entry_t {
54
55 /**
56 * Plugin instance
57 */
58 plugin_t *plugin;
59
60 /**
61 * dlopen handle, if in separate lib
62 */
63 void *handle;
64
65 /**
66 * List of loaded features
67 */
68 linked_list_t *loaded;
69 };
70
71 /**
72 * Destroy a plugin entry
73 */
74 static void plugin_entry_destroy(plugin_entry_t *entry)
75 {
76 DESTROY_IF(entry->plugin);
77 if (entry->handle)
78 {
79 dlclose(entry->handle);
80 }
81 entry->loaded->destroy(entry->loaded);
82 free(entry);
83 }
84
85 /**
86 * create a plugin
87 * returns: NOT_FOUND, if the constructor was not found
88 * FAILED, if the plugin could not be constructed
89 */
90 static status_t create_plugin(private_plugin_loader_t *this, void *handle,
91 char *name, bool integrity, plugin_entry_t **entry)
92 {
93 char create[128];
94 plugin_t *plugin;
95 plugin_constructor_t constructor;
96
97 if (snprintf(create, sizeof(create), "%s_plugin_create",
98 name) >= sizeof(create))
99 {
100 return FAILED;
101 }
102 translate(create, "-", "_");
103 constructor = dlsym(handle, create);
104 if (constructor == NULL)
105 {
106 return NOT_FOUND;
107 }
108 if (integrity && lib->integrity)
109 {
110 if (!lib->integrity->check_segment(lib->integrity, name, constructor))
111 {
112 DBG1(DBG_LIB, "plugin '%s': failed segment integrity test", name);
113 return FAILED;
114 }
115 DBG1(DBG_LIB, "plugin '%s': passed file and segment integrity tests",
116 name);
117 }
118 plugin = constructor();
119 if (plugin == NULL)
120 {
121 DBG1(DBG_LIB, "plugin '%s': failed to load - %s returned NULL", name,
122 create);
123 return FAILED;
124 }
125 INIT(*entry,
126 .plugin = plugin,
127 .loaded = linked_list_create(),
128 );
129 DBG2(DBG_LIB, "plugin '%s': loaded successfully", name);
130 return SUCCESS;
131 }
132
133 /**
134 * load a single plugin
135 */
136 static bool load_plugin(private_plugin_loader_t *this, char *name, char *file)
137 {
138 plugin_entry_t *entry;
139 void *handle;
140
141 switch (create_plugin(this, RTLD_DEFAULT, name, FALSE, &entry))
142 {
143 case SUCCESS:
144 this->plugins->insert_last(this->plugins, entry);
145 return TRUE;
146 case NOT_FOUND:
147 /* try to load the plugin from a file */
148 break;
149 default:
150 return FALSE;
151 }
152 if (lib->integrity)
153 {
154 if (!lib->integrity->check_file(lib->integrity, name, file))
155 {
156 DBG1(DBG_LIB, "plugin '%s': failed file integrity test of '%s'",
157 name, file);
158 return FALSE;
159 }
160 }
161 handle = dlopen(file, RTLD_LAZY);
162 if (handle == NULL)
163 {
164 DBG1(DBG_LIB, "plugin '%s' failed to load: %s", name, dlerror());
165 return FALSE;
166 }
167 if (create_plugin(this, handle, name, TRUE, &entry) != SUCCESS)
168 {
169 dlclose(handle);
170 return FALSE;
171 }
172 entry->handle = handle;
173 this->plugins->insert_last(this->plugins, entry);
174 return TRUE;
175 }
176
177 /**
178 * Convert enumerated entries to plugin_t
179 */
180 static bool plugin_filter(void *null, plugin_entry_t **entry, plugin_t **plugin,
181 void *in, linked_list_t **list)
182 {
183 *plugin = (*entry)->plugin;
184 if (list)
185 {
186 *list = (*entry)->loaded;
187 }
188 return TRUE;
189 }
190
191 METHOD(plugin_loader_t, create_plugin_enumerator, enumerator_t*,
192 private_plugin_loader_t *this)
193 {
194 return enumerator_create_filter(
195 this->plugins->create_enumerator(this->plugins),
196 (void*)plugin_filter, NULL, NULL);
197 }
198
199 /**
200 * Check if a plugin is already loaded
201 */
202 static bool plugin_loaded(private_plugin_loader_t *this, char *name)
203 {
204 enumerator_t *enumerator;
205 bool found = FALSE;
206 plugin_t *plugin;
207
208 enumerator = create_plugin_enumerator(this);
209 while (enumerator->enumerate(enumerator, &plugin, NULL))
210 {
211 if (streq(plugin->get_name(plugin), name))
212 {
213 found = TRUE;
214 break;
215 }
216 }
217 enumerator->destroy(enumerator);
218 return found;
219 }
220
221 /**
222 * Check if a feature of a plugin is already loaded
223 */
224 static bool feature_loaded(private_plugin_loader_t *this, plugin_entry_t *entry,
225 plugin_feature_t *feature)
226 {
227 return entry->loaded->find_first(entry->loaded, NULL,
228 (void**)&feature) == SUCCESS;
229 }
230
231 /**
232 * Check if dependencies are satisfied
233 */
234 static bool dependencies_satisfied(private_plugin_loader_t *this,
235 plugin_entry_t *entry, bool soft, bool report,
236 plugin_feature_t *features, int count)
237 {
238 int i;
239
240 /* first entry is provided feature, followed by dependencies */
241 for (i = 1; i < count; i++)
242 {
243 enumerator_t *entries, *loaded;
244 plugin_feature_t *feature;
245 plugin_entry_t *current;
246 bool found = FALSE;
247
248 if (features[i].kind != FEATURE_DEPENDS &&
249 features[i].kind != FEATURE_SDEPEND)
250 { /* end of dependencies */
251 break;
252 }
253 entries = this->plugins->create_enumerator(this->plugins);
254 while (entries->enumerate(entries, &current))
255 {
256 loaded = current->loaded->create_enumerator(current->loaded);
257 while (loaded->enumerate(loaded, &feature))
258 {
259 if (plugin_feature_matches(&features[i], feature))
260 {
261 found = TRUE;
262 break;
263 }
264 }
265 loaded->destroy(loaded);
266 }
267 entries->destroy(entries);
268
269 if (!found && (features[i].kind != FEATURE_SDEPEND || soft))
270 {
271 if (report)
272 {
273 char *provide, *depend, *name;
274
275 name = entry->plugin->get_name(entry->plugin);
276 provide = plugin_feature_get_string(&features[0]);
277 depend = plugin_feature_get_string(&features[i]);
278 DBG1(DBG_LIB, "feature %s in '%s' plugin has unsatisfied "
279 "dependency: %s", provide, name, depend);
280 free(provide);
281 free(depend);
282 }
283 return FALSE;
284 }
285 }
286 return TRUE;
287 }
288
289 /**
290 * Check if a given feature is still required as dependency
291 */
292 static bool dependency_required(private_plugin_loader_t *this,
293 plugin_feature_t *dep)
294 {
295 enumerator_t *enumerator;
296 plugin_feature_t *features;
297 plugin_entry_t *entry;
298 int count, i;
299
300 enumerator = this->plugins->create_enumerator(this->plugins);
301 while (enumerator->enumerate(enumerator, &entry))
302 {
303 if (!entry->plugin->get_features)
304 { /* features not supported */
305 continue;
306 }
307 count = entry->plugin->get_features(entry->plugin, &features);
308 for (i = 0; i < count; i++)
309 {
310 if (feature_loaded(this, entry, &features[i]))
311 {
312 while (++i < count && (features[i].kind == FEATURE_DEPENDS ||
313 features[i].kind == FEATURE_SDEPEND))
314 {
315 if (plugin_feature_matches(&features[i], dep))
316 {
317 enumerator->destroy(enumerator);
318 return TRUE;
319 }
320 }
321 }
322 }
323 }
324 enumerator->destroy(enumerator);
325 return FALSE;
326 }
327
328 /**
329 * Load plugin features in correct order
330 */
331 static int load_features(private_plugin_loader_t *this, bool soft, bool report)
332 {
333 enumerator_t *enumerator;
334 plugin_feature_t *feature, *reg;
335 plugin_entry_t *entry;
336 int count, i, loaded = 0;
337
338 enumerator = this->plugins->create_enumerator(this->plugins);
339 while (enumerator->enumerate(enumerator, &entry))
340 {
341 if (!entry->plugin->get_features)
342 { /* feature interface not supported */
343 continue;
344 }
345 reg = NULL;
346 count = entry->plugin->get_features(entry->plugin, &feature);
347 for (i = 0; i < count; i++)
348 {
349 switch (feature->kind)
350 {
351 case FEATURE_PROVIDE:
352 if (!feature_loaded(this, entry, feature) &&
353 dependencies_satisfied(this, entry, soft, report,
354 feature, count - i) &&
355 plugin_feature_load(entry->plugin, feature, reg))
356 {
357 entry->loaded->insert_last(entry->loaded, feature);
358 loaded++;
359 }
360 break;
361 case FEATURE_REGISTER:
362 case FEATURE_CALLBACK:
363 reg = feature;
364 break;
365 default:
366 break;
367 }
368 feature++;
369 }
370 }
371 enumerator->destroy(enumerator);
372 return loaded;
373 }
374
375 /**
376 * Try to unload plugin features on which is not depended anymore
377 */
378 static int unload_features(private_plugin_loader_t *this, plugin_entry_t *entry)
379 {
380 plugin_feature_t *feature, *reg = NULL;
381 int count, i, unloaded = 0;
382
383 count = entry->plugin->get_features(entry->plugin, &feature);
384 for (i = 0; i < count; i++)
385 {
386 switch (feature->kind)
387 {
388 case FEATURE_PROVIDE:
389 if (feature_loaded(this, entry, feature) &&
390 !dependency_required(this, feature) &&
391 plugin_feature_unload(entry->plugin, feature, reg))
392 {
393 entry->loaded->remove(entry->loaded, feature, NULL);
394 unloaded++;
395 }
396 break;
397 case FEATURE_REGISTER:
398 case FEATURE_CALLBACK:
399 reg = feature;
400 break;
401 default:
402 break;
403 }
404 feature++;
405 }
406 return unloaded;
407 }
408
409 /**
410 * Remove plugins that we were not able to load any features from.
411 */
412 static void purge_plugins(private_plugin_loader_t *this)
413 {
414 enumerator_t *enumerator;
415 plugin_entry_t *entry;
416
417 enumerator = this->plugins->create_enumerator(this->plugins);
418 while (enumerator->enumerate(enumerator, &entry))
419 {
420 if (!entry->plugin->get_features)
421 { /* feature interface not supported */
422 continue;
423 }
424 if (!entry->loaded->get_count(entry->loaded))
425 {
426 this->plugins->remove_at(this->plugins, enumerator);
427 plugin_entry_destroy(entry);
428 }
429 }
430 enumerator->destroy(enumerator);
431 }
432
433 METHOD(plugin_loader_t, load_plugins, bool,
434 private_plugin_loader_t *this, char *path, char *list)
435 {
436 enumerator_t *enumerator;
437 char *token;
438 bool critical_failed = FALSE;
439
440 if (path == NULL)
441 {
442 path = PLUGINDIR;
443 }
444
445 enumerator = enumerator_create_token(list, " ", " ");
446 while (!critical_failed && enumerator->enumerate(enumerator, &token))
447 {
448 bool critical = FALSE;
449 char file[PATH_MAX];
450 int len;
451
452 token = strdup(token);
453 len = strlen(token);
454 if (token[len-1] == '!')
455 {
456 critical = TRUE;
457 token[len-1] = '\0';
458 }
459 if (plugin_loaded(this, token))
460 {
461 free(token);
462 continue;
463 }
464 if (snprintf(file, sizeof(file), "%s/libstrongswan-%s.so",
465 path, token) >= sizeof(file))
466 {
467 return NULL;
468 }
469 if (!load_plugin(this, token, file) && critical)
470 {
471 critical_failed = TRUE;
472 DBG1(DBG_LIB, "loading critical plugin '%s' failed", token);
473 }
474 free(token);
475 /* TODO: we currently load features after each plugin is loaded. This
476 * will not be necessary once we have features support in all plugins.
477 */
478 while (load_features(this, TRUE, FALSE))
479 {
480 /* try load new features until we don't get new ones */
481 }
482 while (load_features(this, FALSE, FALSE))
483 {
484 /* second round, ignoring soft dependencies */
485 }
486 }
487 enumerator->destroy(enumerator);
488 if (!critical_failed)
489 {
490 /* report missing dependencies */
491 load_features(this, FALSE, TRUE);
492 /* unload plugins that we were not able to load any features for */
493 purge_plugins(this);
494 }
495 return !critical_failed;
496 }
497
498 METHOD(plugin_loader_t, unload, void,
499 private_plugin_loader_t *this)
500 {
501 enumerator_t *enumerator;
502 plugin_entry_t *entry;
503 linked_list_t *list;
504
505 /* unload plugins in reverse order, for those not supporting features */
506 list = linked_list_create();
507 while (this->plugins->remove_last(this->plugins, (void**)&entry) == SUCCESS)
508 {
509 list->insert_last(list, entry);
510 }
511 while (list->remove_last(list, (void**)&entry) == SUCCESS)
512 {
513 this->plugins->insert_first(this->plugins, entry);
514 }
515 list->destroy(list);
516 while (this->plugins->get_count(this->plugins))
517 {
518 enumerator = this->plugins->create_enumerator(this->plugins);
519 while (enumerator->enumerate(enumerator, &entry))
520 {
521 if (entry->plugin->get_features)
522 { /* supports features */
523 while (unload_features(this, entry));
524 }
525 if (entry->loaded->get_count(entry->loaded) == 0)
526 {
527 if (lib->leak_detective)
528 { /* keep handle to report leaks properly */
529 entry->handle = NULL;
530 }
531 this->plugins->remove_at(this->plugins, enumerator);
532 plugin_entry_destroy(entry);
533 }
534 }
535 enumerator->destroy(enumerator);
536 }
537 }
538
539 /**
540 * Reload a plugin by name, NULL for all
541 */
542 static u_int reload_by_name(private_plugin_loader_t *this, char *name)
543 {
544 u_int reloaded = 0;
545 enumerator_t *enumerator;
546 plugin_t *plugin;
547
548 enumerator = create_plugin_enumerator(this);
549 while (enumerator->enumerate(enumerator, &plugin, NULL))
550 {
551 if (name == NULL || streq(name, plugin->get_name(plugin)))
552 {
553 if (plugin->reload && plugin->reload(plugin))
554 {
555 DBG2(DBG_LIB, "reloaded configuration of '%s' plugin",
556 plugin->get_name(plugin));
557 reloaded++;
558 }
559 }
560 }
561 enumerator->destroy(enumerator);
562 return reloaded;
563 }
564
565 METHOD(plugin_loader_t, reload, u_int,
566 private_plugin_loader_t *this, char *list)
567 {
568 u_int reloaded = 0;
569 enumerator_t *enumerator;
570 char *name;
571
572 if (list == NULL)
573 {
574 return reload_by_name(this, NULL);
575 }
576 enumerator = enumerator_create_token(list, " ", "");
577 while (enumerator->enumerate(enumerator, &name))
578 {
579 reloaded += reload_by_name(this, name);
580 }
581 enumerator->destroy(enumerator);
582 return reloaded;
583 }
584
585 METHOD(plugin_loader_t, destroy, void,
586 private_plugin_loader_t *this)
587 {
588 unload(this);
589 this->plugins->destroy(this->plugins);
590 free(this);
591 }
592
593 /*
594 * see header file
595 */
596 plugin_loader_t *plugin_loader_create()
597 {
598 private_plugin_loader_t *this;
599
600 INIT(this,
601 .public = {
602 .load = _load_plugins,
603 .reload = _reload,
604 .unload = _unload,
605 .create_plugin_enumerator = _create_plugin_enumerator,
606 .destroy = _destroy,
607 },
608 .plugins = linked_list_create(),
609 );
610
611 return &this->public;
612 }
613