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