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