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