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