2 * Copyright (C) 2010-2012 Tobias Brunner
3 * Copyright (C) 2007 Martin Willi
4 * Hochschule fuer Technik Rapperswil
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>.
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
18 #include "plugin_loader.h"
25 #include <utils/debug.h>
27 #include <collections/hashtable.h>
28 #include <collections/linked_list.h>
29 #include <plugins/plugin.h>
30 #include <utils/integrity_checker.h>
32 typedef struct private_plugin_loader_t private_plugin_loader_t
;
33 typedef struct plugin_entry_t plugin_entry_t
;
36 * private data of plugin_loader
38 struct private_plugin_loader_t
{
43 plugin_loader_t
public;
46 * List of plugins, as plugin_entry_t
48 linked_list_t
*plugins
;
51 * Hashtable for loaded features, as plugin_feature_t
53 hashtable_t
*loaded_features
;
56 * List of names of loaded plugins
64 struct plugin_entry_t
{
72 * TRUE, if the plugin is marked as critical
77 * dlopen handle, if in separate lib
82 * List of loaded features
84 linked_list_t
*loaded
;
87 * List features failed to load
89 linked_list_t
*failed
;
93 * Destroy a plugin entry
95 static void plugin_entry_destroy(plugin_entry_t
*entry
)
97 DESTROY_IF(entry
->plugin
);
100 dlclose(entry
->handle
);
102 entry
->loaded
->destroy(entry
->loaded
);
103 entry
->failed
->destroy(entry
->failed
);
108 * Wrapper for static plugin features
113 * Implements plugin_t interface
118 * Name of the module registering these features
123 * Static plugin features
125 plugin_feature_t
*features
;
128 * Number of plugin features
134 METHOD(plugin_t
, get_static_name
, char*,
135 static_features_t
*this)
140 METHOD(plugin_t
, get_static_features
, int,
141 static_features_t
*this, plugin_feature_t
*features
[])
143 *features
= this->features
;
147 METHOD(plugin_t
, static_destroy
, void,
148 static_features_t
*this)
150 free(this->features
);
156 * Create a wrapper around static plugin features.
158 static plugin_t
*static_features_create(const char *name
,
159 plugin_feature_t features
[], int count
)
161 static_features_t
*this;
165 .get_name
= _get_static_name
,
166 .get_features
= _get_static_features
,
167 .destroy
= _static_destroy
,
169 .name
= strdup(name
),
170 .features
= calloc(count
, sizeof(plugin_feature_t
)),
174 memcpy(this->features
, features
, sizeof(plugin_feature_t
) * count
);
176 return &this->public;
180 * Compare function for hashtable of loaded features.
182 static bool plugin_feature_equals_ptr(plugin_feature_t
*a
, plugin_feature_t
*b
)
189 * returns: NOT_FOUND, if the constructor was not found
190 * FAILED, if the plugin could not be constructed
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
)
198 plugin_constructor_t constructor
;
200 if (snprintf(create
, sizeof(create
), "%s_plugin_create",
201 name
) >= sizeof(create
))
205 translate(create
, "-", "_");
206 constructor
= dlsym(handle
, create
);
207 if (constructor
== NULL
)
211 if (integrity
&& lib
->integrity
)
213 if (!lib
->integrity
->check_segment(lib
->integrity
, name
, constructor
))
215 DBG1(DBG_LIB
, "plugin '%s': failed segment integrity test", name
);
218 DBG1(DBG_LIB
, "plugin '%s': passed file and segment integrity tests",
221 plugin
= constructor();
224 DBG1(DBG_LIB
, "plugin '%s': failed to load - %s returned NULL", name
,
230 .critical
= critical
,
231 .loaded
= linked_list_create(),
232 .failed
= linked_list_create(),
234 DBG2(DBG_LIB
, "plugin '%s': loaded successfully", name
);
239 * load a single plugin
241 static bool load_plugin(private_plugin_loader_t
*this, char *name
, char *file
,
244 plugin_entry_t
*entry
;
247 switch (create_plugin(this, RTLD_DEFAULT
, name
, FALSE
, critical
, &entry
))
250 this->plugins
->insert_last(this->plugins
, entry
);
254 { /* try to load the plugin from a file */
263 if (!lib
->integrity
->check_file(lib
->integrity
, name
, file
))
265 DBG1(DBG_LIB
, "plugin '%s': failed file integrity test of '%s'",
270 handle
= dlopen(file
, RTLD_LAZY
);
273 DBG1(DBG_LIB
, "plugin '%s' failed to load: %s", name
, dlerror());
276 if (create_plugin(this, handle
, name
, TRUE
, critical
, &entry
) != SUCCESS
)
281 entry
->handle
= handle
;
282 this->plugins
->insert_last(this->plugins
, entry
);
287 * Convert enumerated entries to plugin_t
289 static bool plugin_filter(void *null
, plugin_entry_t
**entry
, plugin_t
**plugin
,
290 void *in
, linked_list_t
**list
)
292 *plugin
= (*entry
)->plugin
;
295 *list
= (*entry
)->loaded
;
300 METHOD(plugin_loader_t
, create_plugin_enumerator
, enumerator_t
*,
301 private_plugin_loader_t
*this)
303 return enumerator_create_filter(
304 this->plugins
->create_enumerator(this->plugins
),
305 (void*)plugin_filter
, NULL
, NULL
);
309 * Create a list of the names of all loaded plugins
311 static char* loaded_plugins_list(private_plugin_loader_t
*this)
313 int buf_len
= 128, len
= 0;
315 enumerator_t
*enumerator
;
318 buf
= malloc(buf_len
);
320 enumerator
= create_plugin_enumerator(this);
321 while (enumerator
->enumerate(enumerator
, &plugin
, NULL
))
323 name
= plugin
->get_name(plugin
);
324 if (len
+ (strlen(name
) + 1) >= buf_len
)
327 buf
= realloc(buf
, buf_len
);
329 len
+= snprintf(&buf
[len
], buf_len
- len
, "%s ", name
);
331 enumerator
->destroy(enumerator
);
332 if (len
> 0 && buf
[len
- 1] == ' ')
341 * Check if a plugin is already loaded
343 static bool plugin_loaded(private_plugin_loader_t
*this, char *name
)
345 enumerator_t
*enumerator
;
349 enumerator
= create_plugin_enumerator(this);
350 while (enumerator
->enumerate(enumerator
, &plugin
, NULL
))
352 if (streq(plugin
->get_name(plugin
), name
))
358 enumerator
->destroy(enumerator
);
363 * Check if a feature of a plugin is already loaded
365 static bool feature_loaded(private_plugin_loader_t
*this, plugin_entry_t
*entry
,
366 plugin_feature_t
*feature
)
368 return entry
->loaded
->find_first(entry
->loaded
, NULL
,
369 (void**)&feature
) == SUCCESS
;
373 * Check if loading a feature of a plugin failed
375 static bool feature_failed(private_plugin_loader_t
*this, plugin_entry_t
*entry
,
376 plugin_feature_t
*feature
)
378 return entry
->failed
->find_first(entry
->failed
, NULL
,
379 (void**)&feature
) == SUCCESS
;
383 * Check if dependencies are satisfied
385 static bool dependencies_satisfied(private_plugin_loader_t
*this,
386 plugin_entry_t
*entry
, bool soft
, bool report
,
387 plugin_feature_t
*features
, int count
)
391 /* first entry is provided feature, followed by dependencies */
392 for (i
= 1; i
< count
; i
++)
394 plugin_feature_t
*found
;
396 if (features
[i
].kind
!= FEATURE_DEPENDS
&&
397 features
[i
].kind
!= FEATURE_SDEPEND
)
398 { /* end of dependencies */
401 found
= this->loaded_features
->get_match(this->loaded_features
,
402 &features
[i
], (hashtable_equals_t
)plugin_feature_matches
);
403 if (!found
&& (features
[i
].kind
!= FEATURE_SDEPEND
|| soft
))
407 char *provide
, *depend
, *name
;
409 name
= entry
->plugin
->get_name(entry
->plugin
);
410 provide
= plugin_feature_get_string(&features
[0]);
411 depend
= plugin_feature_get_string(&features
[i
]);
412 DBG2(DBG_LIB
, "feature %s in '%s' plugin has unsatisfied "
413 "dependency: %s", provide
, name
, depend
);
424 * Check if a given feature is still required as dependency
426 static bool dependency_required(private_plugin_loader_t
*this,
427 plugin_feature_t
*dep
)
429 enumerator_t
*enumerator
;
430 plugin_feature_t
*features
;
431 plugin_entry_t
*entry
;
434 enumerator
= this->plugins
->create_enumerator(this->plugins
);
435 while (enumerator
->enumerate(enumerator
, &entry
))
437 if (!entry
->plugin
->get_features
)
438 { /* features not supported */
441 count
= entry
->plugin
->get_features(entry
->plugin
, &features
);
442 for (i
= 0; i
< count
; i
++)
444 if (&features
[i
] != dep
&&
445 feature_loaded(this, entry
, &features
[i
]))
447 while (++i
< count
&& (features
[i
].kind
== FEATURE_DEPENDS
||
448 features
[i
].kind
== FEATURE_SDEPEND
))
450 if (plugin_feature_matches(&features
[i
], dep
))
452 enumerator
->destroy(enumerator
);
459 enumerator
->destroy(enumerator
);
464 * Load plugin features in correct order
466 static int load_features(private_plugin_loader_t
*this, bool soft
, bool report
)
468 enumerator_t
*enumerator
;
469 plugin_feature_t
*feature
, *reg
;
470 plugin_entry_t
*entry
;
471 int count
, i
, loaded
= 0;
473 enumerator
= this->plugins
->create_enumerator(this->plugins
);
474 while (enumerator
->enumerate(enumerator
, &entry
))
476 if (!entry
->plugin
->get_features
)
477 { /* feature interface not supported */
481 count
= entry
->plugin
->get_features(entry
->plugin
, &feature
);
482 for (i
= 0; i
< count
; i
++)
484 switch (feature
->kind
)
486 case FEATURE_PROVIDE
:
487 if (!feature_loaded(this, entry
, feature
) &&
488 !feature_failed(this, entry
, feature
) &&
489 dependencies_satisfied(this, entry
, soft
, report
,
492 if (plugin_feature_load(entry
->plugin
, feature
, reg
))
494 this->loaded_features
->put(this->loaded_features
,
496 entry
->loaded
->insert_last(entry
->loaded
, feature
);
501 entry
->failed
->insert_last(entry
->failed
, feature
);
505 case FEATURE_REGISTER
:
506 case FEATURE_CALLBACK
:
514 if (loaded
&& !report
)
515 { /* got new feature, restart from beginning of list */
519 enumerator
->destroy(enumerator
);
524 * Try to unload plugin features on which is not depended anymore
526 static int unload_features(private_plugin_loader_t
*this, plugin_entry_t
*entry
)
528 plugin_feature_t
*feature
, *reg
= NULL
;
529 int count
, i
, unloaded
= 0;
531 count
= entry
->plugin
->get_features(entry
->plugin
, &feature
);
532 for (i
= 0; i
< count
; i
++)
534 switch (feature
->kind
)
536 case FEATURE_PROVIDE
:
537 if (feature_loaded(this, entry
, feature
) &&
538 !dependency_required(this, feature
) &&
539 plugin_feature_unload(entry
->plugin
, feature
, reg
))
541 this->loaded_features
->remove(this->loaded_features
,
543 entry
->loaded
->remove(entry
->loaded
, feature
, NULL
);
547 case FEATURE_REGISTER
:
548 case FEATURE_CALLBACK
:
560 * Check that we have all features loaded for critical plugins
562 static bool missing_critical_features(private_plugin_loader_t
*this)
564 enumerator_t
*enumerator
;
565 plugin_entry_t
*entry
;
566 bool critical_failed
= FALSE
;
568 enumerator
= this->plugins
->create_enumerator(this->plugins
);
569 while (enumerator
->enumerate(enumerator
, &entry
))
571 if (!entry
->plugin
->get_features
)
572 { /* feature interface not supported */
577 plugin_feature_t
*feature
;
578 char *name
, *provide
;
579 int count
, i
, failed
= 0;
581 name
= entry
->plugin
->get_name(entry
->plugin
);
582 count
= entry
->plugin
->get_features(entry
->plugin
, &feature
);
583 for (i
= 0; i
< count
; i
++, feature
++)
585 if (feature
->kind
== FEATURE_PROVIDE
&&
586 !feature_loaded(this, entry
, feature
))
588 provide
= plugin_feature_get_string(feature
);
589 DBG2(DBG_LIB
, " failed to load %s in critical plugin '%s'",
597 DBG1(DBG_LIB
, "failed to load %d feature%s in critical plugin "
598 "'%s'", failed
, failed
> 1 ?
"s" : "", name
);
599 critical_failed
= TRUE
;
603 enumerator
->destroy(enumerator
);
605 return critical_failed
;
609 * Remove plugins that we were not able to load any features from.
611 static void purge_plugins(private_plugin_loader_t
*this)
613 enumerator_t
*enumerator
;
614 plugin_entry_t
*entry
;
616 enumerator
= this->plugins
->create_enumerator(this->plugins
);
617 while (enumerator
->enumerate(enumerator
, &entry
))
619 if (!entry
->plugin
->get_features
)
620 { /* feature interface not supported */
623 if (!entry
->loaded
->get_count(entry
->loaded
))
625 this->plugins
->remove_at(this->plugins
, enumerator
);
626 plugin_entry_destroy(entry
);
629 enumerator
->destroy(enumerator
);
632 METHOD(plugin_loader_t
, add_static_features
, void,
633 private_plugin_loader_t
*this, const char *name
,
634 plugin_feature_t features
[], int count
, bool critical
)
636 plugin_entry_t
*entry
;
639 plugin
= static_features_create(name
, features
, count
);
643 .critical
= critical
,
644 .loaded
= linked_list_create(),
645 .failed
= linked_list_create(),
647 this->plugins
->insert_last(this->plugins
, entry
);
650 METHOD(plugin_loader_t
, load_plugins
, bool,
651 private_plugin_loader_t
*this, char *path
, char *list
)
653 enumerator_t
*enumerator
;
655 bool critical_failed
= FALSE
;
662 #endif /* PLUGINDIR */
664 enumerator
= enumerator_create_token(list
, " ", " ");
665 while (!critical_failed
&& enumerator
->enumerate(enumerator
, &token
))
667 bool critical
= FALSE
;
668 char buf
[PATH_MAX
], *file
= NULL
;
671 token
= strdup(token
);
673 if (token
[len
-1] == '!')
678 if (plugin_loaded(this, token
))
685 if (snprintf(buf
, sizeof(buf
), "%s/libstrongswan-%s.so",
686 path
, token
) >= sizeof(buf
))
692 if (!load_plugin(this, token
, file
, critical
) && critical
)
694 critical_failed
= TRUE
;
695 DBG1(DBG_LIB
, "loading critical plugin '%s' failed", token
);
698 /* TODO: we currently load features after each plugin is loaded. This
699 * will not be necessary once we have features support in all plugins.
701 while (load_features(this, TRUE
, FALSE
))
703 /* try load new features until we don't get new ones */
706 enumerator
->destroy(enumerator
);
707 if (!critical_failed
)
709 while (load_features(this, FALSE
, FALSE
))
711 /* enforce loading features, ignoring soft dependencies */
713 /* report missing dependencies */
714 load_features(this, FALSE
, TRUE
);
715 /* check for unloaded features provided by critical plugins */
716 critical_failed
= missing_critical_features(this);
717 /* unload plugins that we were not able to load any features for */
720 if (!critical_failed
)
722 free(this->loaded_plugins
);
723 this->loaded_plugins
= loaded_plugins_list(this);
725 return !critical_failed
;
728 METHOD(plugin_loader_t
, unload
, void,
729 private_plugin_loader_t
*this)
731 enumerator_t
*enumerator
;
732 plugin_entry_t
*entry
;
735 /* unload plugins in reverse order, for those not supporting features */
736 list
= linked_list_create();
737 while (this->plugins
->remove_last(this->plugins
, (void**)&entry
) == SUCCESS
)
739 list
->insert_last(list
, entry
);
741 while (list
->remove_last(list
, (void**)&entry
) == SUCCESS
)
743 this->plugins
->insert_first(this->plugins
, entry
);
746 while (this->plugins
->get_count(this->plugins
))
748 enumerator
= this->plugins
->create_enumerator(this->plugins
);
749 while (enumerator
->enumerate(enumerator
, &entry
))
751 if (entry
->plugin
->get_features
)
752 { /* supports features */
753 while (unload_features(this, entry
));
755 if (entry
->loaded
->get_count(entry
->loaded
) == 0)
757 if (lib
->leak_detective
)
758 { /* keep handle to report leaks properly */
759 entry
->handle
= NULL
;
761 this->plugins
->remove_at(this->plugins
, enumerator
);
762 plugin_entry_destroy(entry
);
765 enumerator
->destroy(enumerator
);
767 free(this->loaded_plugins
);
768 this->loaded_plugins
= NULL
;
772 * Reload a plugin by name, NULL for all
774 static u_int
reload_by_name(private_plugin_loader_t
*this, char *name
)
777 enumerator_t
*enumerator
;
780 enumerator
= create_plugin_enumerator(this);
781 while (enumerator
->enumerate(enumerator
, &plugin
, NULL
))
783 if (name
== NULL
|| streq(name
, plugin
->get_name(plugin
)))
785 if (plugin
->reload
&& plugin
->reload(plugin
))
787 DBG2(DBG_LIB
, "reloaded configuration of '%s' plugin",
788 plugin
->get_name(plugin
));
793 enumerator
->destroy(enumerator
);
797 METHOD(plugin_loader_t
, reload
, u_int
,
798 private_plugin_loader_t
*this, char *list
)
801 enumerator_t
*enumerator
;
806 return reload_by_name(this, NULL
);
808 enumerator
= enumerator_create_token(list
, " ", "");
809 while (enumerator
->enumerate(enumerator
, &name
))
811 reloaded
+= reload_by_name(this, name
);
813 enumerator
->destroy(enumerator
);
817 METHOD(plugin_loader_t
, loaded_plugins
, char*,
818 private_plugin_loader_t
*this)
820 return this->loaded_plugins ?
: "";
823 METHOD(plugin_loader_t
, destroy
, void,
824 private_plugin_loader_t
*this)
827 this->loaded_features
->destroy(this->loaded_features
);
828 this->plugins
->destroy(this->plugins
);
829 free(this->loaded_plugins
);
836 plugin_loader_t
*plugin_loader_create()
838 private_plugin_loader_t
*this;
842 .add_static_features
= _add_static_features
,
843 .load
= _load_plugins
,
846 .create_plugin_enumerator
= _create_plugin_enumerator
,
847 .loaded_plugins
= _loaded_plugins
,
850 .plugins
= linked_list_create(),
851 .loaded_features
= hashtable_create(
852 (hashtable_hash_t
)plugin_feature_hash
,
853 (hashtable_equals_t
)plugin_feature_equals_ptr
, 64),
856 return &this->public;