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