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