diff options
Diffstat (limited to 'src/libstrongswan/plugins/plugin_loader.c')
-rw-r--r-- | src/libstrongswan/plugins/plugin_loader.c | 485 |
1 files changed, 434 insertions, 51 deletions
diff --git a/src/libstrongswan/plugins/plugin_loader.c b/src/libstrongswan/plugins/plugin_loader.c index b4d7bf7c7..f97cbb31f 100644 --- a/src/libstrongswan/plugins/plugin_loader.c +++ b/src/libstrongswan/plugins/plugin_loader.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2010 Tobias Brunner + * Copyright (C) 2010-2012 Tobias Brunner * Copyright (C) 2007 Martin Willi * Hochschule fuer Technik Rapperswil * @@ -23,11 +23,13 @@ #include <stdio.h> #include <debug.h> +#include <library.h> #include <integrity_checker.h> #include <utils/linked_list.h> #include <plugins/plugin.h> typedef struct private_plugin_loader_t private_plugin_loader_t; +typedef struct plugin_entry_t plugin_entry_t; /** * private data of plugin_loader @@ -40,20 +42,67 @@ struct private_plugin_loader_t { plugin_loader_t public; /** - * list of loaded plugins + * List of plugins, as plugin_entry_t */ linked_list_t *plugins; + + /** + * List of names of loaded plugins + */ + char *loaded_plugins; +}; + +/** + * Entry for a plugin + */ +struct plugin_entry_t { + + /** + * Plugin instance + */ + plugin_t *plugin; + + /** + * dlopen handle, if in separate lib + */ + void *handle; + + /** + * List of loaded features + */ + linked_list_t *loaded; + + /** + * List features failed to load + */ + linked_list_t *failed; }; /** + * Destroy a plugin entry + */ +static void plugin_entry_destroy(plugin_entry_t *entry) +{ + DESTROY_IF(entry->plugin); + if (entry->handle) + { + dlclose(entry->handle); + } + entry->loaded->destroy(entry->loaded); + entry->failed->destroy(entry->failed); + free(entry); +} + +/** * create a plugin * returns: NOT_FOUND, if the constructor was not found * FAILED, if the plugin could not be constructed */ static status_t create_plugin(private_plugin_loader_t *this, void *handle, - char *name, bool integrity, plugin_t **plugin) + char *name, bool integrity, plugin_entry_t **entry) { char create[128]; + plugin_t *plugin; plugin_constructor_t constructor; if (snprintf(create, sizeof(create), "%s_plugin_create", @@ -77,13 +126,18 @@ static status_t create_plugin(private_plugin_loader_t *this, void *handle, DBG1(DBG_LIB, "plugin '%s': passed file and segment integrity tests", name); } - *plugin = constructor(); - if (*plugin == NULL) + plugin = constructor(); + if (plugin == NULL) { DBG1(DBG_LIB, "plugin '%s': failed to load - %s returned NULL", name, create); return FAILED; } + INIT(*entry, + .plugin = plugin, + .loaded = linked_list_create(), + .failed = linked_list_create(), + ); DBG2(DBG_LIB, "plugin '%s': loaded successfully", name); return SUCCESS; } @@ -91,28 +145,21 @@ static status_t create_plugin(private_plugin_loader_t *this, void *handle, /** * load a single plugin */ -static plugin_t* load_plugin(private_plugin_loader_t *this, - char *path, char *name) +static bool load_plugin(private_plugin_loader_t *this, char *name, char *file) { - char file[PATH_MAX]; + plugin_entry_t *entry; void *handle; - plugin_t *plugin; - switch (create_plugin(this, RTLD_DEFAULT, name, FALSE, &plugin)) + switch (create_plugin(this, RTLD_DEFAULT, name, FALSE, &entry)) { case SUCCESS: - return plugin; + this->plugins->insert_last(this->plugins, entry); + return TRUE; case NOT_FOUND: /* try to load the plugin from a file */ break; default: - return NULL; - } - - if (snprintf(file, sizeof(file), "%s/libstrongswan-%s.so", path, - name) >= sizeof(file)) - { - return NULL; + return FALSE; } if (lib->integrity) { @@ -120,26 +167,80 @@ static plugin_t* load_plugin(private_plugin_loader_t *this, { DBG1(DBG_LIB, "plugin '%s': failed file integrity test of '%s'", name, file); - return NULL; + return FALSE; } } handle = dlopen(file, RTLD_LAZY); if (handle == NULL) { DBG1(DBG_LIB, "plugin '%s' failed to load: %s", name, dlerror()); - return NULL; + return FALSE; } - if (create_plugin(this, handle, name, TRUE, &plugin) != SUCCESS) + if (create_plugin(this, handle, name, TRUE, &entry) != SUCCESS) { dlclose(handle); - return NULL; + return FALSE; } - /* we do not store or free dlopen() handles, leak_detective requires - * the modules to keep loaded until leak report */ - return plugin; + entry->handle = handle; + this->plugins->insert_last(this->plugins, entry); + return TRUE; } /** + * Convert enumerated entries to plugin_t + */ +static bool plugin_filter(void *null, plugin_entry_t **entry, plugin_t **plugin, + void *in, linked_list_t **list) +{ + *plugin = (*entry)->plugin; + if (list) + { + *list = (*entry)->loaded; + } + return TRUE; +} + +METHOD(plugin_loader_t, create_plugin_enumerator, enumerator_t*, + private_plugin_loader_t *this) +{ + return enumerator_create_filter( + this->plugins->create_enumerator(this->plugins), + (void*)plugin_filter, NULL, NULL); +} + +/** + * Create a list of the names of all loaded plugins + */ +static char* loaded_plugins_list(private_plugin_loader_t *this) +{ + int buf_len = 128, len = 0; + char *buf, *name; + enumerator_t *enumerator; + plugin_t *plugin; + + buf = malloc(buf_len); + buf[0] = '\0'; + enumerator = create_plugin_enumerator(this); + while (enumerator->enumerate(enumerator, &plugin, NULL)) + { + name = plugin->get_name(plugin); + if (len + (strlen(name) + 1) >= buf_len) + { + buf_len <<= 1; + buf = realloc(buf, buf_len); + } + len += snprintf(&buf[len], buf_len - len, "%s ", name); + } + enumerator->destroy(enumerator); + if (len > 0 && buf[len - 1] == ' ') + { + buf[len - 1] = '\0'; + } + return buf; +} + + +/** * Check if a plugin is already loaded */ static bool plugin_loaded(private_plugin_loader_t *this, char *name) @@ -148,8 +249,8 @@ static bool plugin_loaded(private_plugin_loader_t *this, char *name) bool found = FALSE; plugin_t *plugin; - enumerator = this->plugins->create_enumerator(this->plugins); - while (enumerator->enumerate(enumerator, &plugin)) + enumerator = create_plugin_enumerator(this); + while (enumerator->enumerate(enumerator, &plugin, NULL)) { if (streq(plugin->get_name(plugin), name)) { @@ -161,6 +262,235 @@ static bool plugin_loaded(private_plugin_loader_t *this, char *name) return found; } +/** + * Check if a feature of a plugin is already loaded + */ +static bool feature_loaded(private_plugin_loader_t *this, plugin_entry_t *entry, + plugin_feature_t *feature) +{ + return entry->loaded->find_first(entry->loaded, NULL, + (void**)&feature) == SUCCESS; +} + +/** + * Check if loading a feature of a plugin failed + */ +static bool feature_failed(private_plugin_loader_t *this, plugin_entry_t *entry, + plugin_feature_t *feature) +{ + return entry->failed->find_first(entry->failed, NULL, + (void**)&feature) == SUCCESS; +} + +/** + * Check if dependencies are satisfied + */ +static bool dependencies_satisfied(private_plugin_loader_t *this, + plugin_entry_t *entry, bool soft, bool report, + plugin_feature_t *features, int count) +{ + int i; + + /* first entry is provided feature, followed by dependencies */ + for (i = 1; i < count; i++) + { + enumerator_t *entries, *loaded; + plugin_feature_t *feature; + plugin_entry_t *current; + bool found = FALSE; + + if (features[i].kind != FEATURE_DEPENDS && + features[i].kind != FEATURE_SDEPEND) + { /* end of dependencies */ + break; + } + entries = this->plugins->create_enumerator(this->plugins); + while (entries->enumerate(entries, ¤t)) + { + loaded = current->loaded->create_enumerator(current->loaded); + while (loaded->enumerate(loaded, &feature)) + { + if (plugin_feature_matches(&features[i], feature)) + { + found = TRUE; + break; + } + } + loaded->destroy(loaded); + } + entries->destroy(entries); + + if (!found && (features[i].kind != FEATURE_SDEPEND || soft)) + { + if (report) + { + char *provide, *depend, *name; + + name = entry->plugin->get_name(entry->plugin); + provide = plugin_feature_get_string(&features[0]); + depend = plugin_feature_get_string(&features[i]); + DBG2(DBG_LIB, "feature %s in '%s' plugin has unsatisfied " + "dependency: %s", provide, name, depend); + free(provide); + free(depend); + } + return FALSE; + } + } + return TRUE; +} + +/** + * Check if a given feature is still required as dependency + */ +static bool dependency_required(private_plugin_loader_t *this, + plugin_feature_t *dep) +{ + enumerator_t *enumerator; + plugin_feature_t *features; + plugin_entry_t *entry; + int count, i; + + enumerator = this->plugins->create_enumerator(this->plugins); + while (enumerator->enumerate(enumerator, &entry)) + { + if (!entry->plugin->get_features) + { /* features not supported */ + continue; + } + count = entry->plugin->get_features(entry->plugin, &features); + for (i = 0; i < count; i++) + { + if (feature_loaded(this, entry, &features[i])) + { + while (++i < count && (features[i].kind == FEATURE_DEPENDS || + features[i].kind == FEATURE_SDEPEND)) + { + if (plugin_feature_matches(&features[i], dep)) + { + enumerator->destroy(enumerator); + return TRUE; + } + } + } + } + } + enumerator->destroy(enumerator); + return FALSE; +} + +/** + * Load plugin features in correct order + */ +static int load_features(private_plugin_loader_t *this, bool soft, bool report) +{ + enumerator_t *enumerator; + plugin_feature_t *feature, *reg; + plugin_entry_t *entry; + int count, i, loaded = 0; + + enumerator = this->plugins->create_enumerator(this->plugins); + while (enumerator->enumerate(enumerator, &entry)) + { + if (!entry->plugin->get_features) + { /* feature interface not supported */ + continue; + } + reg = NULL; + count = entry->plugin->get_features(entry->plugin, &feature); + for (i = 0; i < count; i++) + { + switch (feature->kind) + { + case FEATURE_PROVIDE: + if (!feature_loaded(this, entry, feature) && + !feature_failed(this, entry, feature) && + dependencies_satisfied(this, entry, soft, report, + feature, count - i)) + { + if (plugin_feature_load(entry->plugin, feature, reg)) + { + entry->loaded->insert_last(entry->loaded, feature); + loaded++; + } + else + { + entry->failed->insert_last(entry->failed, feature); + } + } + break; + case FEATURE_REGISTER: + case FEATURE_CALLBACK: + reg = feature; + break; + default: + break; + } + feature++; + } + } + enumerator->destroy(enumerator); + return loaded; +} + +/** + * Try to unload plugin features on which is not depended anymore + */ +static int unload_features(private_plugin_loader_t *this, plugin_entry_t *entry) +{ + plugin_feature_t *feature, *reg = NULL; + int count, i, unloaded = 0; + + count = entry->plugin->get_features(entry->plugin, &feature); + for (i = 0; i < count; i++) + { + switch (feature->kind) + { + case FEATURE_PROVIDE: + if (feature_loaded(this, entry, feature) && + !dependency_required(this, feature) && + plugin_feature_unload(entry->plugin, feature, reg)) + { + entry->loaded->remove(entry->loaded, feature, NULL); + unloaded++; + } + break; + case FEATURE_REGISTER: + case FEATURE_CALLBACK: + reg = feature; + break; + default: + break; + } + feature++; + } + return unloaded; +} + +/** + * Remove plugins that we were not able to load any features from. + */ +static void purge_plugins(private_plugin_loader_t *this) +{ + enumerator_t *enumerator; + plugin_entry_t *entry; + + enumerator = this->plugins->create_enumerator(this->plugins); + while (enumerator->enumerate(enumerator, &entry)) + { + if (!entry->plugin->get_features) + { /* feature interface not supported */ + continue; + } + if (!entry->loaded->get_count(entry->loaded)) + { + this->plugins->remove_at(this->plugins, enumerator); + plugin_entry_destroy(entry); + } + } + enumerator->destroy(enumerator); +} + METHOD(plugin_loader_t, load_plugins, bool, private_plugin_loader_t *this, char *path, char *list) { @@ -176,8 +506,8 @@ METHOD(plugin_loader_t, load_plugins, bool, enumerator = enumerator_create_token(list, " ", " "); while (!critical_failed && enumerator->enumerate(enumerator, &token)) { - plugin_t *plugin; bool critical = FALSE; + char file[PATH_MAX]; int len; token = strdup(token); @@ -192,42 +522,86 @@ METHOD(plugin_loader_t, load_plugins, bool, free(token); continue; } - plugin = load_plugin(this, path, token); - if (plugin) + if (snprintf(file, sizeof(file), "%s/libstrongswan-%s.so", + path, token) >= sizeof(file)) { - this->plugins->insert_last(this->plugins, plugin); + return FALSE; } - else + if (!load_plugin(this, token, file) && critical) { - if (critical) - { - critical_failed = TRUE; - DBG1(DBG_LIB, "loading critical plugin '%s' failed", token); - } + critical_failed = TRUE; + DBG1(DBG_LIB, "loading critical plugin '%s' failed", token); } free(token); + /* TODO: we currently load features after each plugin is loaded. This + * will not be necessary once we have features support in all plugins. + */ + while (load_features(this, TRUE, FALSE)) + { + /* try load new features until we don't get new ones */ + } } enumerator->destroy(enumerator); + if (!critical_failed) + { + while (load_features(this, FALSE, FALSE)) + { + /* enforce loading features, ignoring soft dependencies */ + } + /* report missing dependencies */ + load_features(this, FALSE, TRUE); + /* unload plugins that we were not able to load any features for */ + purge_plugins(this); + } + if (!critical_failed) + { + free(this->loaded_plugins); + this->loaded_plugins = loaded_plugins_list(this); + } return !critical_failed; } METHOD(plugin_loader_t, unload, void, private_plugin_loader_t *this) { - plugin_t *plugin; + enumerator_t *enumerator; + plugin_entry_t *entry; + linked_list_t *list; - /* unload plugins in reverse order */ - while (this->plugins->remove_last(this->plugins, - (void**)&plugin) == SUCCESS) + /* unload plugins in reverse order, for those not supporting features */ + list = linked_list_create(); + while (this->plugins->remove_last(this->plugins, (void**)&entry) == SUCCESS) { - plugin->destroy(plugin); + list->insert_last(list, entry); } -} - -METHOD(plugin_loader_t, create_plugin_enumerator, enumerator_t*, - private_plugin_loader_t *this) -{ - return this->plugins->create_enumerator(this->plugins); + while (list->remove_last(list, (void**)&entry) == SUCCESS) + { + this->plugins->insert_first(this->plugins, entry); + } + list->destroy(list); + while (this->plugins->get_count(this->plugins)) + { + enumerator = this->plugins->create_enumerator(this->plugins); + while (enumerator->enumerate(enumerator, &entry)) + { + if (entry->plugin->get_features) + { /* supports features */ + while (unload_features(this, entry)); + } + if (entry->loaded->get_count(entry->loaded) == 0) + { + if (lib->leak_detective) + { /* keep handle to report leaks properly */ + entry->handle = NULL; + } + this->plugins->remove_at(this->plugins, enumerator); + plugin_entry_destroy(entry); + } + } + enumerator->destroy(enumerator); + } + free(this->loaded_plugins); + this->loaded_plugins = NULL; } /** @@ -240,11 +614,11 @@ static u_int reload_by_name(private_plugin_loader_t *this, char *name) plugin_t *plugin; enumerator = create_plugin_enumerator(this); - while (enumerator->enumerate(enumerator, &plugin)) + while (enumerator->enumerate(enumerator, &plugin, NULL)) { if (name == NULL || streq(name, plugin->get_name(plugin))) { - if (plugin->reload(plugin)) + if (plugin->reload && plugin->reload(plugin)) { DBG2(DBG_LIB, "reloaded configuration of '%s' plugin", plugin->get_name(plugin)); @@ -276,10 +650,18 @@ METHOD(plugin_loader_t, reload, u_int, return reloaded; } +METHOD(plugin_loader_t, loaded_plugins, char*, + private_plugin_loader_t *this) +{ + return this->loaded_plugins ?: ""; +} + METHOD(plugin_loader_t, destroy, void, private_plugin_loader_t *this) { - this->plugins->destroy_offset(this->plugins, offsetof(plugin_t, destroy)); + unload(this); + this->plugins->destroy(this->plugins); + free(this->loaded_plugins); free(this); } @@ -296,6 +678,7 @@ plugin_loader_t *plugin_loader_create() .reload = _reload, .unload = _unload, .create_plugin_enumerator = _create_plugin_enumerator, + .loaded_plugins = _loaded_plugins, .destroy = _destroy, }, .plugins = linked_list_create(), |