Refactored plugin-loader with improved dependency resolution
[strongswan.git] / src / libstrongswan / plugins / plugin_loader.c
1 /*
2 * Copyright (C) 2010-2013 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 <utils/debug.h>
26 #include <library.h>
27 #include <collections/hashtable.h>
28 #include <collections/linked_list.h>
29 #include <plugins/plugin.h>
30 #include <utils/integrity_checker.h>
31
32 typedef struct private_plugin_loader_t private_plugin_loader_t;
33 typedef struct registered_feature_t registered_feature_t;
34 typedef struct provided_feature_t provided_feature_t;
35 typedef struct plugin_entry_t plugin_entry_t;
36
37 /**
38 * private data of plugin_loader
39 */
40 struct private_plugin_loader_t {
41
42 /**
43 * public functions
44 */
45 plugin_loader_t public;
46
47 /**
48 * List of plugins, as plugin_entry_t
49 */
50 linked_list_t *plugins;
51
52 /**
53 * Hashtable for registered features, as registered_feature_t
54 */
55 hashtable_t *features;
56
57 /**
58 * Loaded features (stored in reverse order), as provided_feature_t
59 */
60 linked_list_t *loaded;
61
62 /**
63 * List of names of loaded plugins
64 */
65 char *loaded_plugins;
66 };
67
68 /**
69 * Registered plugin feature
70 */
71 struct registered_feature_t {
72
73 /**
74 * The registered feature
75 */
76 plugin_feature_t *feature;
77
78 /**
79 * List of plugins providing this feature, as provided_feature_t
80 */
81 linked_list_t *plugins;
82 };
83
84 /**
85 * Hash a registered feature
86 */
87 static bool registered_feature_hash(registered_feature_t *this)
88 {
89 return plugin_feature_hash(this->feature);
90 }
91
92 /**
93 * Compare two registered features
94 */
95 static bool registered_feature_equals(registered_feature_t *a,
96 registered_feature_t *b)
97 {
98 return plugin_feature_equals(a->feature, b->feature);
99 }
100
101 /**
102 * Feature as provided by a plugin
103 */
104 struct provided_feature_t {
105
106 /**
107 * Plugin providing the feature
108 */
109 plugin_entry_t *entry;
110
111 /**
112 * FEATURE_REGISTER or FEATURE_CALLBACK entry
113 */
114 plugin_feature_t *reg;
115
116 /**
117 * The provided feature (followed by dependencies)
118 */
119 plugin_feature_t *feature;
120
121 /**
122 * Maximum number of dependencies (following feature)
123 */
124 int dependencies;
125
126 /**
127 * TRUE if currently loading this feature (to prevent loops)
128 */
129 bool loading;
130
131 /**
132 * TRUE if feature loaded
133 */
134 bool loaded;
135
136 /**
137 * TRUE if feature failed to load
138 */
139 bool failed;
140 };
141
142 /**
143 * Entry for a plugin
144 */
145 struct plugin_entry_t {
146
147 /**
148 * Plugin instance
149 */
150 plugin_t *plugin;
151
152 /**
153 * TRUE, if the plugin is marked as critical
154 */
155 bool critical;
156
157 /**
158 * dlopen handle, if in separate lib
159 */
160 void *handle;
161
162 /**
163 * List of features, as provided_feature_t
164 */
165 linked_list_t *features;
166 };
167
168 /**
169 * Destroy a plugin entry
170 */
171 static void plugin_entry_destroy(plugin_entry_t *entry)
172 {
173 DESTROY_IF(entry->plugin);
174 if (entry->handle)
175 {
176 dlclose(entry->handle);
177 }
178 entry->features->destroy(entry->features);
179 free(entry);
180 }
181
182 /**
183 * Wrapper for static plugin features
184 */
185 typedef struct {
186
187 /**
188 * Implements plugin_t interface
189 */
190 plugin_t public;
191
192 /**
193 * Name of the module registering these features
194 */
195 char *name;
196
197 /**
198 * Static plugin features
199 */
200 plugin_feature_t *features;
201
202 /**
203 * Number of plugin features
204 */
205 int count;
206
207 } static_features_t;
208
209 METHOD(plugin_t, get_static_name, char*,
210 static_features_t *this)
211 {
212 return this->name;
213 }
214
215 METHOD(plugin_t, get_static_features, int,
216 static_features_t *this, plugin_feature_t *features[])
217 {
218 *features = this->features;
219 return this->count;
220 }
221
222 METHOD(plugin_t, static_destroy, void,
223 static_features_t *this)
224 {
225 free(this->features);
226 free(this->name);
227 free(this);
228 }
229
230 /**
231 * Create a wrapper around static plugin features.
232 */
233 static plugin_t *static_features_create(const char *name,
234 plugin_feature_t features[], int count)
235 {
236 static_features_t *this;
237
238 INIT(this,
239 .public = {
240 .get_name = _get_static_name,
241 .get_features = _get_static_features,
242 .destroy = _static_destroy,
243 },
244 .name = strdup(name),
245 .features = calloc(count, sizeof(plugin_feature_t)),
246 .count = count,
247 );
248
249 memcpy(this->features, features, sizeof(plugin_feature_t) * count);
250
251 return &this->public;
252 }
253
254 /**
255 * create a plugin
256 * returns: NOT_FOUND, if the constructor was not found
257 * FAILED, if the plugin could not be constructed
258 */
259 static status_t create_plugin(private_plugin_loader_t *this, void *handle,
260 char *name, bool integrity, bool critical,
261 plugin_entry_t **entry)
262 {
263 char create[128];
264 plugin_t *plugin;
265 plugin_constructor_t constructor;
266
267 if (snprintf(create, sizeof(create), "%s_plugin_create",
268 name) >= sizeof(create))
269 {
270 return FAILED;
271 }
272 translate(create, "-", "_");
273 constructor = dlsym(handle, create);
274 if (constructor == NULL)
275 {
276 return NOT_FOUND;
277 }
278 if (integrity && lib->integrity)
279 {
280 if (!lib->integrity->check_segment(lib->integrity, name, constructor))
281 {
282 DBG1(DBG_LIB, "plugin '%s': failed segment integrity test", name);
283 return FAILED;
284 }
285 DBG1(DBG_LIB, "plugin '%s': passed file and segment integrity tests",
286 name);
287 }
288 plugin = constructor();
289 if (plugin == NULL)
290 {
291 DBG1(DBG_LIB, "plugin '%s': failed to load - %s returned NULL", name,
292 create);
293 return FAILED;
294 }
295 INIT(*entry,
296 .plugin = plugin,
297 .critical = critical,
298 .features = linked_list_create(),
299 );
300 DBG2(DBG_LIB, "plugin '%s': loaded successfully", name);
301 return SUCCESS;
302 }
303
304 /**
305 * load a single plugin
306 */
307 static plugin_entry_t *load_plugin(private_plugin_loader_t *this, char *name,
308 char *file, bool critical)
309 {
310 plugin_entry_t *entry;
311 void *handle;
312
313 switch (create_plugin(this, RTLD_DEFAULT, name, FALSE, critical, &entry))
314 {
315 case SUCCESS:
316 this->plugins->insert_last(this->plugins, entry);
317 return entry;
318 case NOT_FOUND:
319 if (file)
320 { /* try to load the plugin from a file */
321 break;
322 }
323 /* fall-through */
324 default:
325 return NULL;
326 }
327 if (lib->integrity)
328 {
329 if (!lib->integrity->check_file(lib->integrity, name, file))
330 {
331 DBG1(DBG_LIB, "plugin '%s': failed file integrity test of '%s'",
332 name, file);
333 return NULL;
334 }
335 }
336 handle = dlopen(file, RTLD_LAZY);
337 if (handle == NULL)
338 {
339 DBG1(DBG_LIB, "plugin '%s' failed to load: %s", name, dlerror());
340 return NULL;
341 }
342 if (create_plugin(this, handle, name, TRUE, critical, &entry) != SUCCESS)
343 {
344 dlclose(handle);
345 return NULL;
346 }
347 entry->handle = handle;
348 this->plugins->insert_last(this->plugins, entry);
349 return entry;
350 }
351
352 /**
353 * Convert enumerated provided_feature_t to plugin_feature_t
354 */
355 static bool feature_filter(void *null, provided_feature_t **provided,
356 plugin_feature_t **feature)
357 {
358 *feature = (*provided)->feature;
359 return (*provided)->loaded;
360 }
361
362 /**
363 * Convert enumerated entries to plugin_t
364 */
365 static bool plugin_filter(void *null, plugin_entry_t **entry, plugin_t **plugin,
366 void *in, linked_list_t **list)
367 {
368 plugin_entry_t *this = *entry;
369
370 *plugin = this->plugin;
371 if (list)
372 {
373 enumerator_t *features;
374 features = enumerator_create_filter(
375 this->features->create_enumerator(this->features),
376 (void*)feature_filter, NULL, NULL);
377 *list = linked_list_create_from_enumerator(features);
378 }
379 return TRUE;
380 }
381
382 METHOD(plugin_loader_t, create_plugin_enumerator, enumerator_t*,
383 private_plugin_loader_t *this)
384 {
385 return enumerator_create_filter(
386 this->plugins->create_enumerator(this->plugins),
387 (void*)plugin_filter, NULL, NULL);
388 }
389
390 /**
391 * Create a list of the names of all loaded plugins
392 */
393 static char* loaded_plugins_list(private_plugin_loader_t *this)
394 {
395 int buf_len = 128, len = 0;
396 char *buf, *name;
397 enumerator_t *enumerator;
398 plugin_t *plugin;
399
400 buf = malloc(buf_len);
401 buf[0] = '\0';
402 enumerator = create_plugin_enumerator(this);
403 while (enumerator->enumerate(enumerator, &plugin, NULL))
404 {
405 name = plugin->get_name(plugin);
406 if (len + (strlen(name) + 1) >= buf_len)
407 {
408 buf_len <<= 1;
409 buf = realloc(buf, buf_len);
410 }
411 len += snprintf(&buf[len], buf_len - len, "%s ", name);
412 }
413 enumerator->destroy(enumerator);
414 if (len > 0 && buf[len - 1] == ' ')
415 {
416 buf[len - 1] = '\0';
417 }
418 return buf;
419 }
420
421 /**
422 * Check if a plugin is already loaded
423 */
424 static bool plugin_loaded(private_plugin_loader_t *this, char *name)
425 {
426 enumerator_t *enumerator;
427 bool found = FALSE;
428 plugin_t *plugin;
429
430 enumerator = create_plugin_enumerator(this);
431 while (enumerator->enumerate(enumerator, &plugin, NULL))
432 {
433 if (streq(plugin->get_name(plugin), name))
434 {
435 found = TRUE;
436 break;
437 }
438 }
439 enumerator->destroy(enumerator);
440 return found;
441 }
442
443 /**
444 * Forward declaration
445 */
446 static void load_provided(private_plugin_loader_t *this,
447 provided_feature_t *provided,
448 int level);
449
450 /**
451 * Used to find a loaded feature
452 */
453 static bool is_feature_loaded(provided_feature_t *item)
454 {
455 return item->loaded;
456 }
457
458 /**
459 * Used to find a loadable feature
460 */
461 static bool is_feature_loadable(provided_feature_t *item)
462 {
463 return !item->loading && !item->loaded && !item->failed;
464 }
465
466 /**
467 * Find a loaded and matching feature
468 */
469 static bool loaded_feature_matches(registered_feature_t *a,
470 registered_feature_t *b)
471 {
472 if (plugin_feature_matches(a->feature, b->feature))
473 {
474 return b->plugins->find_first(b->plugins, (void*)is_feature_loaded,
475 NULL) == SUCCESS;
476 }
477 return FALSE;
478 }
479
480 /**
481 * Find a loadable module that equals the requested feature
482 */
483 static bool loadable_feature_equals(registered_feature_t *a,
484 registered_feature_t *b)
485 {
486 if (plugin_feature_equals(a->feature, b->feature))
487 {
488 return b->plugins->find_first(b->plugins, (void*)is_feature_loadable,
489 NULL) == SUCCESS;
490 }
491 return FALSE;
492 }
493
494 /**
495 * Find a loadable module that matches the requested feature
496 */
497 static bool loadable_feature_matches(registered_feature_t *a,
498 registered_feature_t *b)
499 {
500 if (plugin_feature_matches(a->feature, b->feature))
501 {
502 return b->plugins->find_first(b->plugins, (void*)is_feature_loadable,
503 NULL) == SUCCESS;
504 }
505 return FALSE;
506 }
507
508 /**
509 * Returns a compatible plugin feature for the given depencency
510 */
511 static bool find_compatible_feature(private_plugin_loader_t *this,
512 plugin_feature_t *dependency)
513 {
514 registered_feature_t *feature, lookup = {
515 .feature = dependency,
516 };
517
518 feature = this->features->get_match(this->features, &lookup,
519 (void*)loaded_feature_matches);
520 return feature != NULL;
521 }
522
523 /**
524 * Load a registered plugin feature
525 */
526 static void load_registered(private_plugin_loader_t *this,
527 registered_feature_t *registered,
528 int level)
529 {
530 enumerator_t *enumerator;
531 provided_feature_t *provided;
532
533 enumerator = registered->plugins->create_enumerator(registered->plugins);
534 while (enumerator->enumerate(enumerator, &provided))
535 {
536 load_provided(this, provided, level);
537 }
538 enumerator->destroy(enumerator);
539 }
540
541 /**
542 * Try to load dependencies of the given feature
543 */
544 static bool load_dependencies(private_plugin_loader_t *this,
545 provided_feature_t *provided,
546 int level)
547 {
548 registered_feature_t *registered, lookup;
549 int indent = level * 2;
550 int i;
551
552 /* first entry is provided feature, followed by dependencies */
553 for (i = 1; i < provided->dependencies; i++)
554 {
555 if (provided->feature[i].kind != FEATURE_DEPENDS &&
556 provided->feature[i].kind != FEATURE_SDEPEND)
557 { /* end of dependencies */
558 break;
559 }
560
561 /* we load the feature even if a compatible one is already loaded,
562 * otherwise e.g. a specific database implementation loaded before
563 * another might cause a plugin feature loaded in-between to fail */
564 lookup.feature = &provided->feature[i];
565 do
566 { /* prefer an exactly matching feature, could be omitted but
567 * results in a more predictable behavior */
568 registered = this->features->get_match(this->features,
569 &lookup,
570 (void*)loadable_feature_equals);
571 if (!registered)
572 { /* try fuzzy matching */
573 registered = this->features->get_match(this->features,
574 &lookup,
575 (void*)loadable_feature_matches);
576 }
577 if (registered)
578 {
579 load_registered(this, registered, level);
580 }
581 /* we could stop after finding one but for dependencies like
582 * DB_ANY it might be needed to load all matching features */
583 }
584 while (registered);
585
586 if (!find_compatible_feature(this, &provided->feature[i]))
587 {
588 char *name, *provide, *depend;
589 bool soft = provided->feature[i].kind == FEATURE_SDEPEND;
590
591 name = provided->entry->plugin->get_name(provided->entry->plugin);
592 provide = plugin_feature_get_string(&provided->feature[0]);
593 depend = plugin_feature_get_string(&provided->feature[i]);
594 if (soft)
595 {
596 DBG2(DBG_LIB, "%*sfeature %s in plugin '%s' has unmet soft "
597 "dependency: %s", indent, "", provide, name, depend);
598 }
599 else
600 {
601 DBG1(DBG_LIB, "feature %s in plugin '%s' has unmet dependency: "
602 "%s", provide, name, depend);
603 }
604 free(provide);
605 free(depend);
606 if (soft)
607 { /* it's ok if we can't resolve soft dependencies */
608 continue;
609 }
610 return FALSE;
611 }
612 }
613 return TRUE;
614 }
615
616 /**
617 * Load registered plugin features
618 */
619 static void load_feature(private_plugin_loader_t *this,
620 provided_feature_t *provided,
621 int level)
622 {
623 if (load_dependencies(this, provided, level))
624 {
625 if (plugin_feature_load(provided->entry->plugin, provided->feature,
626 provided->reg))
627 {
628 provided->loaded = TRUE;
629 /* insert first so we can unload the features in reverse order */
630 this->loaded->insert_first(this->loaded, provided);
631 }
632 else
633 {
634 provided->failed = TRUE;
635 }
636 }
637 else
638 { /* TODO: we could check the current level and set a different flag when
639 * being loaded as dependency. If there are loops there is a chance the
640 * feature can be loaded later when loading the feature directly. */
641 provided->failed = TRUE;
642 }
643 }
644
645 /**
646 * Load a provided feature
647 */
648 static void load_provided(private_plugin_loader_t *this,
649 provided_feature_t *provided,
650 int level)
651 {
652 char *name, *provide;
653 int indent = level * 2;
654
655 if (provided->loaded || provided->failed)
656 {
657 return;
658 }
659 name = provided->entry->plugin->get_name(provided->entry->plugin);
660 provide = plugin_feature_get_string(provided->feature);
661 if (provided->loading)
662 { /* prevent loop */
663 DBG2(DBG_LIB, "%*sloop detected while loading %s in plugin '%s'",
664 indent, "", provide, name);
665 free(provide);
666 return;
667 }
668 DBG2(DBG_LIB, "%*sloading feature %s in plugin '%s'",
669 indent, "", provide, name);
670 free(provide);
671
672 provided->loading = TRUE;
673 load_feature(this, provided, level + 1);
674 provided->loading = FALSE;
675 }
676
677 /**
678 * Load registered plugin features
679 */
680 static void load_features(private_plugin_loader_t *this)
681 {
682 enumerator_t *enumerator, *inner;
683 plugin_entry_t *plugin;
684 provided_feature_t *provided;
685
686 /* we do this in plugin order to allow implicit dependencies to be resolved
687 * by reordering plugins */
688 enumerator = this->plugins->create_enumerator(this->plugins);
689 while (enumerator->enumerate(enumerator, &plugin))
690 {
691 inner = plugin->features->create_enumerator(plugin->features);
692 while (inner->enumerate(inner, &provided))
693 {
694 load_provided(this, provided, 0);
695 }
696 inner->destroy(inner);
697 }
698 enumerator->destroy(enumerator);
699 }
700
701 /**
702 * Register plugin features provided by the given plugin
703 */
704 static void register_features(private_plugin_loader_t *this,
705 plugin_entry_t *entry)
706 {
707 plugin_feature_t *feature, *reg;
708 registered_feature_t *registered, lookup;
709 provided_feature_t *provided;
710 int count, i;
711
712 if (!entry->plugin->get_features)
713 { /* feature interface not supported */
714 DBG1(DBG_LIB, "plugin '%s' does not provide features, deprecated",
715 entry->plugin->get_name(entry->plugin));
716 return;
717 }
718 reg = NULL;
719 count = entry->plugin->get_features(entry->plugin, &feature);
720 for (i = 0; i < count; i++)
721 {
722 switch (feature->kind)
723 {
724 case FEATURE_PROVIDE:
725 lookup.feature = feature;
726 registered = this->features->get(this->features, &lookup);
727 if (!registered)
728 {
729 INIT(registered,
730 .feature = feature,
731 .plugins = linked_list_create(),
732 );
733 this->features->put(this->features, registered, registered);
734 }
735 INIT(provided,
736 .entry = entry,
737 .feature = feature,
738 .reg = reg,
739 .dependencies = count - i,
740 );
741 registered->plugins->insert_last(registered->plugins,
742 provided);
743 entry->features->insert_last(entry->features, provided);
744 break;
745 case FEATURE_REGISTER:
746 case FEATURE_CALLBACK:
747 reg = feature;
748 break;
749 default:
750 break;
751 }
752 feature++;
753 }
754 }
755
756 /**
757 * Unregister a plugin feature
758 */
759 static void unregister_feature(private_plugin_loader_t *this,
760 provided_feature_t *provided)
761 {
762 registered_feature_t *registered, lookup;
763
764 lookup.feature = provided->feature;
765 registered = this->features->get(this->features, &lookup);
766 if (registered)
767 {
768 registered->plugins->remove(registered->plugins, provided, NULL);
769 if (registered->plugins->get_count(registered->plugins) == 0)
770 {
771 this->features->remove(this->features, &lookup);
772 registered->plugins->destroy(registered->plugins);
773 free(registered);
774 }
775 else if (registered->feature == provided->feature)
776 { /* update feature in case the providing plugin gets unloaded */
777 provided_feature_t *first;
778
779 registered->plugins->get_first(registered->plugins, (void**)&first);
780 registered->feature = first->feature;
781 }
782 }
783 free(provided);
784 }
785
786 /**
787 * Unregister plugin features
788 */
789 static void unregister_features(private_plugin_loader_t *this,
790 plugin_entry_t *entry)
791 {
792 provided_feature_t *provided;
793 enumerator_t *enumerator;
794
795 enumerator = entry->features->create_enumerator(entry->features);
796 while (enumerator->enumerate(enumerator, &provided))
797 {
798 entry->features->remove_at(entry->features, enumerator);
799 unregister_feature(this, provided);
800 }
801 enumerator->destroy(enumerator);
802 }
803
804 /**
805 * Check that we have all features loaded for critical plugins
806 */
807 static bool missing_critical_features(private_plugin_loader_t *this)
808 {
809 enumerator_t *enumerator, *features;
810 plugin_entry_t *entry;
811 bool critical_failed = FALSE;
812
813 enumerator = this->plugins->create_enumerator(this->plugins);
814 while (enumerator->enumerate(enumerator, &entry))
815 {
816 provided_feature_t *provided;
817 char *name, *provide;
818 int failed = 0;
819
820 if (!entry->plugin->get_features ||
821 !entry->critical)
822 { /* feature interface not supported, or not critical */
823 continue;
824 }
825
826 name = entry->plugin->get_name(entry->plugin);
827 features = entry->features->create_enumerator(entry->features);
828 while (features->enumerate(features, &provided))
829 {
830 if (!provided->loaded)
831 {
832 provide = plugin_feature_get_string(provided->feature);
833 DBG2(DBG_LIB, " failed to load %s in critical plugin '%s'",
834 provide, name);
835 free(provide);
836 failed++;
837 }
838 }
839 features->destroy(features);
840 if (failed)
841 {
842 DBG1(DBG_LIB, "failed to load %d feature%s in critical plugin '%s'",
843 failed, failed > 1 ? "s" : "", name);
844 critical_failed = TRUE;
845 }
846 }
847 enumerator->destroy(enumerator);
848
849 return critical_failed;
850 }
851
852 /**
853 * Remove plugins we were not able to load any plugin features from.
854 */
855 static void purge_plugins(private_plugin_loader_t *this)
856 {
857 enumerator_t *enumerator;
858 plugin_entry_t *entry;
859
860 enumerator = this->plugins->create_enumerator(this->plugins);
861 while (enumerator->enumerate(enumerator, &entry))
862 {
863 if (!entry->plugin->get_features)
864 { /* feature interface not supported */
865 continue;
866 }
867 if (entry->features->find_first(entry->features,
868 (void*)is_feature_loaded, NULL) != SUCCESS)
869 {
870 DBG2(DBG_LIB, "unloading plugin '%s' without loaded features",
871 entry->plugin->get_name(entry->plugin));
872 this->plugins->remove_at(this->plugins, enumerator);
873 unregister_features(this, entry);
874 plugin_entry_destroy(entry);
875 }
876 }
877 enumerator->destroy(enumerator);
878 }
879
880 METHOD(plugin_loader_t, add_static_features, void,
881 private_plugin_loader_t *this, const char *name,
882 plugin_feature_t features[], int count, bool critical)
883 {
884 plugin_entry_t *entry;
885 plugin_t *plugin;
886
887 plugin = static_features_create(name, features, count);
888
889 INIT(entry,
890 .plugin = plugin,
891 .critical = critical,
892 .features = linked_list_create(),
893 );
894 this->plugins->insert_last(this->plugins, entry);
895 register_features(this, entry);
896 }
897
898 METHOD(plugin_loader_t, load_plugins, bool,
899 private_plugin_loader_t *this, char *path, char *list)
900 {
901 enumerator_t *enumerator;
902 char *token;
903 bool critical_failed = FALSE;
904
905 #ifdef PLUGINDIR
906 if (path == NULL)
907 {
908 path = PLUGINDIR;
909 }
910 #endif /* PLUGINDIR */
911
912 enumerator = enumerator_create_token(list, " ", " ");
913 while (!critical_failed && enumerator->enumerate(enumerator, &token))
914 {
915 plugin_entry_t *entry;
916 bool critical = FALSE;
917 char buf[PATH_MAX], *file = NULL;
918 int len;
919
920 token = strdup(token);
921 len = strlen(token);
922 if (token[len-1] == '!')
923 {
924 critical = TRUE;
925 token[len-1] = '\0';
926 }
927 if (plugin_loaded(this, token))
928 {
929 free(token);
930 continue;
931 }
932 if (path)
933 {
934 if (snprintf(buf, sizeof(buf), "%s/libstrongswan-%s.so",
935 path, token) >= sizeof(buf))
936 {
937 return FALSE;
938 }
939 file = buf;
940 }
941 entry = load_plugin(this, token, file, critical);
942 if (entry)
943 {
944 register_features(this, entry);
945 }
946 else if (critical)
947 {
948 critical_failed = TRUE;
949 DBG1(DBG_LIB, "loading critical plugin '%s' failed", token);
950 }
951 free(token);
952 }
953 enumerator->destroy(enumerator);
954 if (!critical_failed)
955 {
956 load_features(this);
957 /* check for unloaded features provided by critical plugins */
958 critical_failed = missing_critical_features(this);
959 /* unload plugins that we were not able to load any features for */
960 purge_plugins(this);
961 }
962 if (!critical_failed)
963 {
964 free(this->loaded_plugins);
965 this->loaded_plugins = loaded_plugins_list(this);
966 }
967 return !critical_failed;
968 }
969
970 /**
971 * Unload plugin features, they are registered in reverse order
972 */
973 static void unload_features(private_plugin_loader_t *this)
974 {
975 enumerator_t *enumerator;
976 provided_feature_t *provided;
977 plugin_entry_t *entry;
978
979 enumerator = this->loaded->create_enumerator(this->loaded);
980 while (enumerator->enumerate(enumerator, &provided))
981 {
982 entry = provided->entry;
983 plugin_feature_unload(entry->plugin, provided->feature, provided->reg);
984 this->loaded->remove_at(this->loaded, enumerator);
985 entry->features->remove(entry->features, provided, NULL);
986 unregister_feature(this, provided);
987 }
988 enumerator->destroy(enumerator);
989 }
990
991 METHOD(plugin_loader_t, unload, void,
992 private_plugin_loader_t *this)
993 {
994 plugin_entry_t *entry;
995
996 /* unload features followed by plugins, in reverse order */
997 unload_features(this);
998 while (this->plugins->remove_last(this->plugins, (void**)&entry) == SUCCESS)
999 {
1000 if (lib->leak_detective)
1001 { /* keep handle to report leaks properly */
1002 entry->handle = NULL;
1003 }
1004 unregister_features(this, entry);
1005 plugin_entry_destroy(entry);
1006 }
1007 free(this->loaded_plugins);
1008 this->loaded_plugins = NULL;
1009 }
1010
1011 /**
1012 * Reload a plugin by name, NULL for all
1013 */
1014 static u_int reload_by_name(private_plugin_loader_t *this, char *name)
1015 {
1016 u_int reloaded = 0;
1017 enumerator_t *enumerator;
1018 plugin_t *plugin;
1019
1020 enumerator = create_plugin_enumerator(this);
1021 while (enumerator->enumerate(enumerator, &plugin, NULL))
1022 {
1023 if (name == NULL || streq(name, plugin->get_name(plugin)))
1024 {
1025 if (plugin->reload && plugin->reload(plugin))
1026 {
1027 DBG2(DBG_LIB, "reloaded configuration of '%s' plugin",
1028 plugin->get_name(plugin));
1029 reloaded++;
1030 }
1031 }
1032 }
1033 enumerator->destroy(enumerator);
1034 return reloaded;
1035 }
1036
1037 METHOD(plugin_loader_t, reload, u_int,
1038 private_plugin_loader_t *this, char *list)
1039 {
1040 u_int reloaded = 0;
1041 enumerator_t *enumerator;
1042 char *name;
1043
1044 if (list == NULL)
1045 {
1046 return reload_by_name(this, NULL);
1047 }
1048 enumerator = enumerator_create_token(list, " ", "");
1049 while (enumerator->enumerate(enumerator, &name))
1050 {
1051 reloaded += reload_by_name(this, name);
1052 }
1053 enumerator->destroy(enumerator);
1054 return reloaded;
1055 }
1056
1057 METHOD(plugin_loader_t, loaded_plugins, char*,
1058 private_plugin_loader_t *this)
1059 {
1060 return this->loaded_plugins ?: "";
1061 }
1062
1063 METHOD(plugin_loader_t, destroy, void,
1064 private_plugin_loader_t *this)
1065 {
1066 unload(this);
1067 this->features->destroy(this->features);
1068 this->loaded->destroy(this->loaded);
1069 this->plugins->destroy(this->plugins);
1070 free(this->loaded_plugins);
1071 free(this);
1072 }
1073
1074 /*
1075 * see header file
1076 */
1077 plugin_loader_t *plugin_loader_create()
1078 {
1079 private_plugin_loader_t *this;
1080
1081 INIT(this,
1082 .public = {
1083 .add_static_features = _add_static_features,
1084 .load = _load_plugins,
1085 .reload = _reload,
1086 .unload = _unload,
1087 .create_plugin_enumerator = _create_plugin_enumerator,
1088 .loaded_plugins = _loaded_plugins,
1089 .destroy = _destroy,
1090 },
1091 .plugins = linked_list_create(),
1092 .loaded = linked_list_create(),
1093 .features = hashtable_create(
1094 (hashtable_hash_t)registered_feature_hash,
1095 (hashtable_equals_t)registered_feature_equals, 64),
1096 );
1097
1098 return &this->public;
1099 }