Don't depend on a feature that has a dependency to the same feauture during unload
[strongswan.git] / src / libstrongswan / plugins / plugin_loader.c
1 /*
2 * Copyright (C) 2010-2012 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 * List of names of loaded plugins
51 */
52 char *loaded_plugins;
53 };
54
55 /**
56 * Entry for a plugin
57 */
58 struct plugin_entry_t {
59
60 /**
61 * Plugin instance
62 */
63 plugin_t *plugin;
64
65 /**
66 * dlopen handle, if in separate lib
67 */
68 void *handle;
69
70 /**
71 * List of loaded features
72 */
73 linked_list_t *loaded;
74
75 /**
76 * List features failed to load
77 */
78 linked_list_t *failed;
79 };
80
81 /**
82 * Destroy a plugin entry
83 */
84 static void plugin_entry_destroy(plugin_entry_t *entry)
85 {
86 DESTROY_IF(entry->plugin);
87 if (entry->handle)
88 {
89 dlclose(entry->handle);
90 }
91 entry->loaded->destroy(entry->loaded);
92 entry->failed->destroy(entry->failed);
93 free(entry);
94 }
95
96 /**
97 * create a plugin
98 * returns: NOT_FOUND, if the constructor was not found
99 * FAILED, if the plugin could not be constructed
100 */
101 static status_t create_plugin(private_plugin_loader_t *this, void *handle,
102 char *name, bool integrity, plugin_entry_t **entry)
103 {
104 char create[128];
105 plugin_t *plugin;
106 plugin_constructor_t constructor;
107
108 if (snprintf(create, sizeof(create), "%s_plugin_create",
109 name) >= sizeof(create))
110 {
111 return FAILED;
112 }
113 translate(create, "-", "_");
114 constructor = dlsym(handle, create);
115 if (constructor == NULL)
116 {
117 return NOT_FOUND;
118 }
119 if (integrity && lib->integrity)
120 {
121 if (!lib->integrity->check_segment(lib->integrity, name, constructor))
122 {
123 DBG1(DBG_LIB, "plugin '%s': failed segment integrity test", name);
124 return FAILED;
125 }
126 DBG1(DBG_LIB, "plugin '%s': passed file and segment integrity tests",
127 name);
128 }
129 plugin = constructor();
130 if (plugin == NULL)
131 {
132 DBG1(DBG_LIB, "plugin '%s': failed to load - %s returned NULL", name,
133 create);
134 return FAILED;
135 }
136 INIT(*entry,
137 .plugin = plugin,
138 .loaded = linked_list_create(),
139 .failed = linked_list_create(),
140 );
141 DBG2(DBG_LIB, "plugin '%s': loaded successfully", name);
142 return SUCCESS;
143 }
144
145 /**
146 * load a single plugin
147 */
148 static bool load_plugin(private_plugin_loader_t *this, char *name, char *file)
149 {
150 plugin_entry_t *entry;
151 void *handle;
152
153 switch (create_plugin(this, RTLD_DEFAULT, name, FALSE, &entry))
154 {
155 case SUCCESS:
156 this->plugins->insert_last(this->plugins, entry);
157 return TRUE;
158 case NOT_FOUND:
159 /* try to load the plugin from a file */
160 break;
161 default:
162 return FALSE;
163 }
164 if (lib->integrity)
165 {
166 if (!lib->integrity->check_file(lib->integrity, name, file))
167 {
168 DBG1(DBG_LIB, "plugin '%s': failed file integrity test of '%s'",
169 name, file);
170 return FALSE;
171 }
172 }
173 handle = dlopen(file, RTLD_LAZY);
174 if (handle == NULL)
175 {
176 DBG1(DBG_LIB, "plugin '%s' failed to load: %s", name, dlerror());
177 return FALSE;
178 }
179 if (create_plugin(this, handle, name, TRUE, &entry) != SUCCESS)
180 {
181 dlclose(handle);
182 return FALSE;
183 }
184 entry->handle = handle;
185 this->plugins->insert_last(this->plugins, entry);
186 return TRUE;
187 }
188
189 /**
190 * Convert enumerated entries to plugin_t
191 */
192 static bool plugin_filter(void *null, plugin_entry_t **entry, plugin_t **plugin,
193 void *in, linked_list_t **list)
194 {
195 *plugin = (*entry)->plugin;
196 if (list)
197 {
198 *list = (*entry)->loaded;
199 }
200 return TRUE;
201 }
202
203 METHOD(plugin_loader_t, create_plugin_enumerator, enumerator_t*,
204 private_plugin_loader_t *this)
205 {
206 return enumerator_create_filter(
207 this->plugins->create_enumerator(this->plugins),
208 (void*)plugin_filter, NULL, NULL);
209 }
210
211 /**
212 * Create a list of the names of all loaded plugins
213 */
214 static char* loaded_plugins_list(private_plugin_loader_t *this)
215 {
216 int buf_len = 128, len = 0;
217 char *buf, *name;
218 enumerator_t *enumerator;
219 plugin_t *plugin;
220
221 buf = malloc(buf_len);
222 buf[0] = '\0';
223 enumerator = create_plugin_enumerator(this);
224 while (enumerator->enumerate(enumerator, &plugin, NULL))
225 {
226 name = plugin->get_name(plugin);
227 if (len + (strlen(name) + 1) >= buf_len)
228 {
229 buf_len <<= 1;
230 buf = realloc(buf, buf_len);
231 }
232 len += snprintf(&buf[len], buf_len - len, "%s ", name);
233 }
234 enumerator->destroy(enumerator);
235 if (len > 0 && buf[len - 1] == ' ')
236 {
237 buf[len - 1] = '\0';
238 }
239 return buf;
240 }
241
242
243 /**
244 * Check if a plugin is already loaded
245 */
246 static bool plugin_loaded(private_plugin_loader_t *this, char *name)
247 {
248 enumerator_t *enumerator;
249 bool found = FALSE;
250 plugin_t *plugin;
251
252 enumerator = create_plugin_enumerator(this);
253 while (enumerator->enumerate(enumerator, &plugin, NULL))
254 {
255 if (streq(plugin->get_name(plugin), name))
256 {
257 found = TRUE;
258 break;
259 }
260 }
261 enumerator->destroy(enumerator);
262 return found;
263 }
264
265 /**
266 * Check if a feature of a plugin is already loaded
267 */
268 static bool feature_loaded(private_plugin_loader_t *this, plugin_entry_t *entry,
269 plugin_feature_t *feature)
270 {
271 return entry->loaded->find_first(entry->loaded, NULL,
272 (void**)&feature) == SUCCESS;
273 }
274
275 /**
276 * Check if loading a feature of a plugin failed
277 */
278 static bool feature_failed(private_plugin_loader_t *this, plugin_entry_t *entry,
279 plugin_feature_t *feature)
280 {
281 return entry->failed->find_first(entry->failed, NULL,
282 (void**)&feature) == SUCCESS;
283 }
284
285 /**
286 * Check if dependencies are satisfied
287 */
288 static bool dependencies_satisfied(private_plugin_loader_t *this,
289 plugin_entry_t *entry, bool soft, bool report,
290 plugin_feature_t *features, int count)
291 {
292 int i;
293
294 /* first entry is provided feature, followed by dependencies */
295 for (i = 1; i < count; i++)
296 {
297 enumerator_t *entries, *loaded;
298 plugin_feature_t *feature;
299 plugin_entry_t *current;
300 bool found = FALSE;
301
302 if (features[i].kind != FEATURE_DEPENDS &&
303 features[i].kind != FEATURE_SDEPEND)
304 { /* end of dependencies */
305 break;
306 }
307 entries = this->plugins->create_enumerator(this->plugins);
308 while (entries->enumerate(entries, &current))
309 {
310 loaded = current->loaded->create_enumerator(current->loaded);
311 while (loaded->enumerate(loaded, &feature))
312 {
313 if (plugin_feature_matches(&features[i], feature))
314 {
315 found = TRUE;
316 break;
317 }
318 }
319 loaded->destroy(loaded);
320 }
321 entries->destroy(entries);
322
323 if (!found && (features[i].kind != FEATURE_SDEPEND || soft))
324 {
325 if (report)
326 {
327 char *provide, *depend, *name;
328
329 name = entry->plugin->get_name(entry->plugin);
330 provide = plugin_feature_get_string(&features[0]);
331 depend = plugin_feature_get_string(&features[i]);
332 DBG2(DBG_LIB, "feature %s in '%s' plugin has unsatisfied "
333 "dependency: %s", provide, name, depend);
334 free(provide);
335 free(depend);
336 }
337 return FALSE;
338 }
339 }
340 return TRUE;
341 }
342
343 /**
344 * Check if a given feature is still required as dependency
345 */
346 static bool dependency_required(private_plugin_loader_t *this,
347 plugin_feature_t *dep)
348 {
349 enumerator_t *enumerator;
350 plugin_feature_t *features;
351 plugin_entry_t *entry;
352 int count, i;
353
354 enumerator = this->plugins->create_enumerator(this->plugins);
355 while (enumerator->enumerate(enumerator, &entry))
356 {
357 if (!entry->plugin->get_features)
358 { /* features not supported */
359 continue;
360 }
361 count = entry->plugin->get_features(entry->plugin, &features);
362 for (i = 0; i < count; i++)
363 {
364 if (&features[i] != dep &&
365 feature_loaded(this, entry, &features[i]))
366 {
367 while (++i < count && (features[i].kind == FEATURE_DEPENDS ||
368 features[i].kind == FEATURE_SDEPEND))
369 {
370 if (plugin_feature_matches(&features[i], dep))
371 {
372 enumerator->destroy(enumerator);
373 return TRUE;
374 }
375 }
376 }
377 }
378 }
379 enumerator->destroy(enumerator);
380 return FALSE;
381 }
382
383 /**
384 * Load plugin features in correct order
385 */
386 static int load_features(private_plugin_loader_t *this, bool soft, bool report)
387 {
388 enumerator_t *enumerator;
389 plugin_feature_t *feature, *reg;
390 plugin_entry_t *entry;
391 int count, i, loaded = 0;
392
393 enumerator = this->plugins->create_enumerator(this->plugins);
394 while (enumerator->enumerate(enumerator, &entry))
395 {
396 if (!entry->plugin->get_features)
397 { /* feature interface not supported */
398 continue;
399 }
400 reg = NULL;
401 count = entry->plugin->get_features(entry->plugin, &feature);
402 for (i = 0; i < count; i++)
403 {
404 switch (feature->kind)
405 {
406 case FEATURE_PROVIDE:
407 if (!feature_loaded(this, entry, feature) &&
408 !feature_failed(this, entry, feature) &&
409 dependencies_satisfied(this, entry, soft, report,
410 feature, count - i))
411 {
412 if (plugin_feature_load(entry->plugin, feature, reg))
413 {
414 entry->loaded->insert_last(entry->loaded, feature);
415 loaded++;
416 }
417 else
418 {
419 entry->failed->insert_last(entry->failed, feature);
420 }
421 }
422 break;
423 case FEATURE_REGISTER:
424 case FEATURE_CALLBACK:
425 reg = feature;
426 break;
427 default:
428 break;
429 }
430 feature++;
431 }
432 }
433 enumerator->destroy(enumerator);
434 return loaded;
435 }
436
437 /**
438 * Try to unload plugin features on which is not depended anymore
439 */
440 static int unload_features(private_plugin_loader_t *this, plugin_entry_t *entry)
441 {
442 plugin_feature_t *feature, *reg = NULL;
443 int count, i, unloaded = 0;
444
445 count = entry->plugin->get_features(entry->plugin, &feature);
446 for (i = 0; i < count; i++)
447 {
448 switch (feature->kind)
449 {
450 case FEATURE_PROVIDE:
451 if (feature_loaded(this, entry, feature) &&
452 !dependency_required(this, feature) &&
453 plugin_feature_unload(entry->plugin, feature, reg))
454 {
455 entry->loaded->remove(entry->loaded, feature, NULL);
456 unloaded++;
457 }
458 break;
459 case FEATURE_REGISTER:
460 case FEATURE_CALLBACK:
461 reg = feature;
462 break;
463 default:
464 break;
465 }
466 feature++;
467 }
468 return unloaded;
469 }
470
471 /**
472 * Remove plugins that we were not able to load any features from.
473 */
474 static void purge_plugins(private_plugin_loader_t *this)
475 {
476 enumerator_t *enumerator;
477 plugin_entry_t *entry;
478
479 enumerator = this->plugins->create_enumerator(this->plugins);
480 while (enumerator->enumerate(enumerator, &entry))
481 {
482 if (!entry->plugin->get_features)
483 { /* feature interface not supported */
484 continue;
485 }
486 if (!entry->loaded->get_count(entry->loaded))
487 {
488 this->plugins->remove_at(this->plugins, enumerator);
489 plugin_entry_destroy(entry);
490 }
491 }
492 enumerator->destroy(enumerator);
493 }
494
495 METHOD(plugin_loader_t, load_plugins, bool,
496 private_plugin_loader_t *this, char *path, char *list)
497 {
498 enumerator_t *enumerator;
499 char *token;
500 bool critical_failed = FALSE;
501
502 if (path == NULL)
503 {
504 path = PLUGINDIR;
505 }
506
507 enumerator = enumerator_create_token(list, " ", " ");
508 while (!critical_failed && enumerator->enumerate(enumerator, &token))
509 {
510 bool critical = FALSE;
511 char file[PATH_MAX];
512 int len;
513
514 token = strdup(token);
515 len = strlen(token);
516 if (token[len-1] == '!')
517 {
518 critical = TRUE;
519 token[len-1] = '\0';
520 }
521 if (plugin_loaded(this, token))
522 {
523 free(token);
524 continue;
525 }
526 if (snprintf(file, sizeof(file), "%s/libstrongswan-%s.so",
527 path, token) >= sizeof(file))
528 {
529 return NULL;
530 }
531 if (!load_plugin(this, token, file) && critical)
532 {
533 critical_failed = TRUE;
534 DBG1(DBG_LIB, "loading critical plugin '%s' failed", token);
535 }
536 free(token);
537 /* TODO: we currently load features after each plugin is loaded. This
538 * will not be necessary once we have features support in all plugins.
539 */
540 while (load_features(this, TRUE, FALSE))
541 {
542 /* try load new features until we don't get new ones */
543 }
544 }
545 enumerator->destroy(enumerator);
546 if (!critical_failed)
547 {
548 while (load_features(this, FALSE, FALSE))
549 {
550 /* enforce loading features, ignoring soft dependencies */
551 }
552 /* report missing dependencies */
553 load_features(this, FALSE, TRUE);
554 /* unload plugins that we were not able to load any features for */
555 purge_plugins(this);
556 }
557 if (!critical_failed)
558 {
559 free(this->loaded_plugins);
560 this->loaded_plugins = loaded_plugins_list(this);
561 }
562 return !critical_failed;
563 }
564
565 METHOD(plugin_loader_t, unload, void,
566 private_plugin_loader_t *this)
567 {
568 enumerator_t *enumerator;
569 plugin_entry_t *entry;
570 linked_list_t *list;
571
572 /* unload plugins in reverse order, for those not supporting features */
573 list = linked_list_create();
574 while (this->plugins->remove_last(this->plugins, (void**)&entry) == SUCCESS)
575 {
576 list->insert_last(list, entry);
577 }
578 while (list->remove_last(list, (void**)&entry) == SUCCESS)
579 {
580 this->plugins->insert_first(this->plugins, entry);
581 }
582 list->destroy(list);
583 while (this->plugins->get_count(this->plugins))
584 {
585 enumerator = this->plugins->create_enumerator(this->plugins);
586 while (enumerator->enumerate(enumerator, &entry))
587 {
588 if (entry->plugin->get_features)
589 { /* supports features */
590 while (unload_features(this, entry));
591 }
592 if (entry->loaded->get_count(entry->loaded) == 0)
593 {
594 if (lib->leak_detective)
595 { /* keep handle to report leaks properly */
596 entry->handle = NULL;
597 }
598 this->plugins->remove_at(this->plugins, enumerator);
599 plugin_entry_destroy(entry);
600 }
601 }
602 enumerator->destroy(enumerator);
603 }
604 free(this->loaded_plugins);
605 this->loaded_plugins = NULL;
606 }
607
608 /**
609 * Reload a plugin by name, NULL for all
610 */
611 static u_int reload_by_name(private_plugin_loader_t *this, char *name)
612 {
613 u_int reloaded = 0;
614 enumerator_t *enumerator;
615 plugin_t *plugin;
616
617 enumerator = create_plugin_enumerator(this);
618 while (enumerator->enumerate(enumerator, &plugin, NULL))
619 {
620 if (name == NULL || streq(name, plugin->get_name(plugin)))
621 {
622 if (plugin->reload && plugin->reload(plugin))
623 {
624 DBG2(DBG_LIB, "reloaded configuration of '%s' plugin",
625 plugin->get_name(plugin));
626 reloaded++;
627 }
628 }
629 }
630 enumerator->destroy(enumerator);
631 return reloaded;
632 }
633
634 METHOD(plugin_loader_t, reload, u_int,
635 private_plugin_loader_t *this, char *list)
636 {
637 u_int reloaded = 0;
638 enumerator_t *enumerator;
639 char *name;
640
641 if (list == NULL)
642 {
643 return reload_by_name(this, NULL);
644 }
645 enumerator = enumerator_create_token(list, " ", "");
646 while (enumerator->enumerate(enumerator, &name))
647 {
648 reloaded += reload_by_name(this, name);
649 }
650 enumerator->destroy(enumerator);
651 return reloaded;
652 }
653
654 METHOD(plugin_loader_t, loaded_plugins, char*,
655 private_plugin_loader_t *this)
656 {
657 return this->loaded_plugins ?: "";
658 }
659
660 METHOD(plugin_loader_t, destroy, void,
661 private_plugin_loader_t *this)
662 {
663 unload(this);
664 this->plugins->destroy(this->plugins);
665 free(this->loaded_plugins);
666 free(this);
667 }
668
669 /*
670 * see header file
671 */
672 plugin_loader_t *plugin_loader_create()
673 {
674 private_plugin_loader_t *this;
675
676 INIT(this,
677 .public = {
678 .load = _load_plugins,
679 .reload = _reload,
680 .unload = _unload,
681 .create_plugin_enumerator = _create_plugin_enumerator,
682 .loaded_plugins = _loaded_plugins,
683 .destroy = _destroy,
684 },
685 .plugins = linked_list_create(),
686 );
687
688 return &this->public;
689 }
690