diff options
Diffstat (limited to 'lib/cpluff/libcpluff/pcontrol.c')
-rw-r--r-- | lib/cpluff/libcpluff/pcontrol.c | 1243 |
1 files changed, 0 insertions, 1243 deletions
diff --git a/lib/cpluff/libcpluff/pcontrol.c b/lib/cpluff/libcpluff/pcontrol.c deleted file mode 100644 index 2f475910ff..0000000000 --- a/lib/cpluff/libcpluff/pcontrol.c +++ /dev/null @@ -1,1243 +0,0 @@ -/*------------------------------------------------------------------------- - * C-Pluff, a plug-in framework for C - * Copyright 2007 Johannes Lehtinen - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY - * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, - * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE - * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - *-----------------------------------------------------------------------*/ - -/** @file - * Core plug-in management functions - */ - -#ifdef HAVE_CONFIG_H -#include <config.h> -#endif - -#include <stdio.h> -#include <stdlib.h> -#include <assert.h> -#include <string.h> -#include <stddef.h> -#include "../kazlib/list.h" -#include "../kazlib/hash.h" -#include "cpluff.h" -#include "defines.h" -#include "util.h" -#include "internal.h" -#ifdef _WIN32 -#include <windows.h> -#endif - - -/* ------------------------------------------------------------------------ - * Function definitions - * ----------------------------------------------------------------------*/ - -// Plug-in control - -#ifndef NDEBUG -static void assert_processed_zero(cp_context_t *context) { - hscan_t scan; - hnode_t *node; - - hash_scan_begin(&scan, context->env->plugins); - while ((node = hash_scan_next(&scan)) != NULL) { - cp_plugin_t *plugin = hnode_get(node); - assert(plugin->processed == 0); - } -} -#else -#define assert_processed_zero(c) assert(1) -#endif - -static void unregister_extensions(cp_context_t *context, cp_plugin_info_t *plugin) { - unsigned int i; - - for (i = 0; i < plugin->num_ext_points; i++) { - cp_ext_point_t *ep = plugin->ext_points + i; - hnode_t *hnode; - - if ((hnode = hash_lookup(context->env->ext_points, ep->identifier)) != NULL - && hnode_get(hnode) == ep) { - hash_delete_free(context->env->ext_points, hnode); - } - } - for (i = 0; i < plugin->num_extensions; i++) { - cp_extension_t *e = plugin->extensions + i; - hnode_t *hnode; - - if ((hnode = hash_lookup(context->env->extensions, e->ext_point_id)) != NULL) { - list_t *el = hnode_get(hnode); - lnode_t *lnode = list_first(el); - - while (lnode != NULL) { - lnode_t *nn = list_next(el, lnode); - if (lnode_get(lnode) == e) { - list_delete(el, lnode); - lnode_destroy(lnode); - break; - } - lnode = nn; - } - if (list_isempty(el)) { - char *epid = (char *) hnode_getkey(hnode); - hash_delete_free(context->env->extensions, hnode); - free(epid); - list_destroy(el); - } - } - } -} - -CP_C_API cp_status_t cp_install_plugin(cp_context_t *context, cp_plugin_info_t *plugin) { - cp_plugin_t *rp = NULL; - cp_status_t status = CP_OK; - cpi_plugin_event_t event; - unsigned int i; - - CHECK_NOT_NULL(context); - CHECK_NOT_NULL(plugin); - - cpi_lock_context(context); - cpi_check_invocation(context, CPI_CF_ANY, __func__); - do { - - // Check that there is no conflicting plug-in already loaded - if (hash_lookup(context->env->plugins, plugin->identifier) != NULL) { - cpi_errorf(context, - N_("Plug-in %s could not be installed because a plug-in with the same identifier is already installed."), - plugin->identifier); - status = CP_ERR_CONFLICT; - break; - } - - // Increase usage count for the plug-in descriptor - cpi_use_info(context, plugin); - - // Allocate space for the plug-in state - if ((rp = malloc(sizeof(cp_plugin_t))) == NULL) { - status = CP_ERR_RESOURCE; - break; - } - - // Initialize plug-in state - memset(rp, 0, sizeof(cp_plugin_t)); - rp->context = NULL; - rp->plugin = plugin; - rp->state = CP_PLUGIN_INSTALLED; - rp->imported = NULL; - rp->runtime_lib = NULL; - rp->runtime_funcs = NULL; - rp->plugin_data = NULL; - rp->importing = list_create(LISTCOUNT_T_MAX); - if (rp->importing == NULL) { - status = CP_ERR_RESOURCE; - break; - } - if (!hash_alloc_insert(context->env->plugins, plugin->identifier, rp)) { - status = CP_ERR_RESOURCE; - break; - } - - // Register extension points - for (i = 0; status == CP_OK && i < plugin->num_ext_points; i++) { - cp_ext_point_t *ep = plugin->ext_points + i; - hnode_t *hnode; - - if ((hnode = hash_lookup(context->env->ext_points, ep->identifier)) != NULL) { - cpi_errorf(context, N_("Plug-in %s could not be installed because extension point %s conflicts with an already installed extension point."), plugin->identifier, ep->identifier); - status = CP_ERR_CONFLICT; - } else if (!hash_alloc_insert(context->env->ext_points, ep->identifier, ep)) { - status = CP_ERR_RESOURCE; - } - } - - // Register extensions - for (i = 0; status == CP_OK && i < plugin->num_extensions; i++) { - cp_extension_t *e = plugin->extensions + i; - hnode_t *hnode; - lnode_t *lnode; - list_t *el; - - if ((hnode = hash_lookup(context->env->extensions, e->ext_point_id)) == NULL) { - char *epid; - if ((el = list_create(LISTCOUNT_T_MAX)) != NULL - && (epid = strdup(e->ext_point_id)) != NULL) { - if (!hash_alloc_insert(context->env->extensions, epid, el)) { - list_destroy(el); - status = CP_ERR_RESOURCE; - break; - } - } else { - if (el != NULL) { - list_destroy(el); - } - status = CP_ERR_RESOURCE; - break; - } - } else { - el = hnode_get(hnode); - } - if ((lnode = lnode_create(e)) != NULL) { - list_append(el, lnode); - } else { - status = CP_ERR_RESOURCE; - break; - } - } - - // Break if previous loops failed - if (status != CP_OK) { - break; - } - - // Plug-in installed - event.plugin_id = plugin->identifier; - event.old_state = CP_PLUGIN_UNINSTALLED; - event.new_state = rp->state; - cpi_deliver_event(context, &event); - - } while (0); - - // Release resources on failure - if (status != CP_OK) { - if (rp != NULL) { - if (rp->importing != NULL) { - list_destroy(rp->importing); - } - free(rp); - } - unregister_extensions(context, plugin); - } - - // Report possible resource error - if (status == CP_ERR_RESOURCE) { - cpi_errorf(context, - N_("Plug-in %s could not be installed due to insufficient system resources."), plugin->identifier); - } - cpi_unlock_context(context); - - return status; -} - -/** - * Unresolves the plug-in runtime information. - * - * @param plugin the plug-in to unresolve - */ -static void unresolve_plugin_runtime(cp_plugin_t *plugin) { - - // Destroy the plug-in instance, if necessary - if (plugin->context != NULL) { - plugin->context->env->in_destroy_func_invocation++; - plugin->runtime_funcs->destroy(plugin->plugin_data); - plugin->context->env->in_destroy_func_invocation--; - plugin->plugin_data = NULL; - cpi_free_context(plugin->context); - plugin->context = NULL; - } - - // Close plug-in runtime library - plugin->runtime_funcs = NULL; - if (plugin->runtime_lib != NULL) { - DLCLOSE(plugin->runtime_lib); - plugin->runtime_lib = NULL; - } -} - -/** - * Loads and resolves the plug-in runtime library and initialization functions. - * - * @param context the plug-in context - * @param plugin the plugin - * @return CP_OK (zero) on success or error code on failure - */ -static int resolve_plugin_runtime(cp_context_t *context, cp_plugin_t *plugin) { - char *rlpath = NULL; - int rlpath_len; - cp_status_t status = CP_OK; - - assert(plugin->runtime_lib == NULL); - if (plugin->plugin->runtime_lib_name == NULL) { - return CP_OK; - } - - do { - int ppath_len, lname_len; - int cpluff_compatibility = 1; - - // Check C-Pluff compatibility - if (plugin->plugin->req_cpluff_version != NULL) { -#ifdef CP_ABI_COMPATIBILITY - cpluff_compatibility = ( - cpi_vercmp(plugin->plugin->req_cpluff_version, CP_VERSION) <= 0 - && cpi_vercmp(plugin->plugin->req_cpluff_version, CP_ABI_COMPATIBILITY) >= 0); -#else - cpluff_compatibility = (cpi_vercmp(plugin->plugin->req_cpluff_version, CP_VERSION) == 0); -#endif - } - if (!cpluff_compatibility) { - cpi_errorf(context, N_("Plug-in %s could not be resolved due to version incompatibility with C-Pluff."), plugin->plugin->identifier); - status = CP_ERR_DEPENDENCY; - break; - } - - // Construct a path to plug-in runtime library. - /// @todo Add platform specific prefix (for example, "lib") - ppath_len = strlen(plugin->plugin->plugin_path); - lname_len = strlen(plugin->plugin->runtime_lib_name); - rlpath_len = ppath_len + lname_len + strlen(CP_SHREXT) + 2; - if ((rlpath = malloc(rlpath_len * sizeof(char))) == NULL) { - cpi_errorf(context, N_("Plug-in %s runtime library could not be loaded due to insufficient memory."), plugin->plugin->identifier); - status = CP_ERR_RESOURCE; - break; - } - memset(rlpath, 0, rlpath_len * sizeof(char)); - strcpy(rlpath, plugin->plugin->plugin_path); - rlpath[ppath_len] = CP_FNAMESEP_CHAR; - strcpy(rlpath + ppath_len + 1, plugin->plugin->runtime_lib_name); - strcpy(rlpath + ppath_len + 1 + lname_len, CP_SHREXT); - - // Open the plug-in runtime library - plugin->runtime_lib = DLOPEN(rlpath); - if (plugin->runtime_lib == NULL) { - const char *error = DLERROR(); - if (error == NULL) { - error = _("Unspecified error."); - } - cpi_errorf(context, N_("Plug-in %s runtime library %s could not be opened: %s"), plugin->plugin->identifier, rlpath, error); - status = CP_ERR_RUNTIME; - break; - } - - // Resolve plug-in functions - if (plugin->plugin->runtime_funcs_symbol != NULL) { - plugin->runtime_funcs = (cp_plugin_runtime_t *) DLSYM(plugin->runtime_lib, plugin->plugin->runtime_funcs_symbol); - if (plugin->runtime_funcs == NULL) { - const char *error = DLERROR(); - if (error == NULL) { - error = _("Unspecified error."); - } - cpi_errorf(context, N_("Plug-in %s symbol %s containing plug-in runtime information could not be resolved: %s"), plugin->plugin->identifier, plugin->plugin->runtime_funcs_symbol, error); - status = CP_ERR_RUNTIME; - break; - } - if (plugin->runtime_funcs->create == NULL - || plugin->runtime_funcs->destroy == NULL) { - cpi_errorf(context, N_("Plug-in %s is missing a constructor or destructor function."), plugin->plugin->identifier); - status = CP_ERR_RUNTIME; - break; - } - } - - } while (0); - - // Release resources - free(rlpath); - if (status != CP_OK) { - unresolve_plugin_runtime(plugin); - } - - return status; -} - -/** - * Resolves the specified plug-in import into a plug-in pointer. Does not - * try to resolve the imported plug-in. - * - * @param context the plug-in context - * @param plugin the plug-in being resolved - * @param import the plug-in import to resolve - * @param ipptr filled with pointer to the resolved plug-in or NULL - * @return CP_OK on success or error code on failure - */ -static int resolve_plugin_import(cp_context_t *context, cp_plugin_t *plugin, cp_plugin_import_t *import, cp_plugin_t **ipptr) { - cp_plugin_t *ip = NULL; - hnode_t *node; - - // Lookup the plug-in - node = hash_lookup(context->env->plugins, import->plugin_id); - if (node != NULL) { - ip = hnode_get(node); - } - - // Check plug-in version - if (ip != NULL - && import->version != NULL - && (ip->plugin->version == NULL - || (ip->plugin->abi_bw_compatibility == NULL - && cpi_vercmp(import->version, ip->plugin->version) != 0) - || (ip->plugin->abi_bw_compatibility != NULL - && (cpi_vercmp(import->version, ip->plugin->version) > 0 - || cpi_vercmp(import->version, ip->plugin->abi_bw_compatibility) < 0)))) { - cpi_errorf(context, - N_("Plug-in %s could not be resolved due to version incompatibility with plug-in %s."), - plugin->plugin->identifier, - import->plugin_id); - *ipptr = NULL; - return CP_ERR_DEPENDENCY; - } - - // Check if missing mandatory plug-in - if (ip == NULL && !import->optional) { - cpi_errorf(context, - N_("Plug-in %s could not be resolved because it depends on plug-in %s which is not installed."), - plugin->plugin->identifier, - import->plugin_id); - *ipptr = NULL; - return CP_ERR_DEPENDENCY; - } - - // Return imported plug-in - *ipptr = ip; - return CP_OK; -} - -/** - * Resolves the specified plug-in and its dependencies while leaving plug-ins - * with circular dependencies in a preliminarily resolved state. - * - * @param context the plug-in context - * @param plugin the plug-in - * @return CP_OK (zero) or CP_OK_PRELIMINARY or an error code - */ -static int resolve_plugin_prel_rec(cp_context_t *context, cp_plugin_t *plugin) { - cp_status_t status = CP_OK; - int error_reported = 0; - lnode_t *node = NULL; - unsigned int i; - - // Check if already resolved - if (plugin->state >= CP_PLUGIN_RESOLVED) { - return CP_OK; - } - - // Check for dependency loops - if (plugin->processed) { - return CP_OK_PRELIMINARY; - } - plugin->processed = 1; - - do { - - // Recursively resolve the imported plug-ins - assert(plugin->imported == NULL); - if ((plugin->imported = list_create(LISTCOUNT_T_MAX)) == NULL) { - status = CP_ERR_RESOURCE; - break; - } - for (i = 0; i < plugin->plugin->num_imports; i++) { - cp_plugin_t *ip; - int s; - - if ((node = lnode_create(NULL)) == NULL) { - status = CP_ERR_RESOURCE; - break; - } - if ((s = resolve_plugin_import(context, plugin, plugin->plugin->imports + i, &ip)) != CP_OK) { - error_reported = 1; - status = s; - break; - } - if (ip != NULL) { - lnode_put(node, ip); - list_append(plugin->imported, node); - node = NULL; - if (!cpi_ptrset_add(ip->importing, plugin)) { - status = CP_ERR_RESOURCE; - break; - } else if ((s = resolve_plugin_prel_rec(context, ip)) != CP_OK && s != CP_OK_PRELIMINARY) { - cpi_errorf(context, N_("Plug-in %s could not be resolved because it depends on plug-in %s which could not be resolved."), plugin->plugin->identifier, ip->plugin->identifier); - error_reported = 1; - status = s; - break; - } - } else { - lnode_destroy(node); - node = NULL; - } - } - if (status != CP_OK) { - break; - } - - // Resolve this plug-in - assert(plugin->state == CP_PLUGIN_INSTALLED); - if ((i = resolve_plugin_runtime(context, plugin)) != CP_OK) { - status = i; - error_reported = 1; - break; - } - - // Notify event listeners and update state if completely resolved - if (status == CP_OK) { - cpi_plugin_event_t event; - - plugin->processed = 0; - event.plugin_id = plugin->plugin->identifier; - event.old_state = plugin->state; - event.new_state = plugin->state = CP_PLUGIN_RESOLVED; - cpi_deliver_event(context, &event); - } - - } while (0); - - // Clean up - if (node != NULL) { - lnode_destroy(node); - } - - // Handle errors - if (status == CP_ERR_RESOURCE && !error_reported) { - cpi_errorf(context, N_("Plug-in %s could not be resolved because of insufficient memory."), plugin->plugin->identifier); - } - - return status; -} - -/** - * Recursively commits the resolving process for the specified plug-in and - * its dependencies. - * - * @param context the plug-in context - * @param plugin the plug-in - */ -static void resolve_plugin_commit_rec(cp_context_t *context, cp_plugin_t *plugin) { - - // Check if already committed - if (!plugin->processed) { - return; - } - plugin->processed = 0; - - // Commit if only preliminarily resolved - if (plugin->state < CP_PLUGIN_RESOLVED) { - cpi_plugin_event_t event; - lnode_t *node; - - // Recursively commit dependencies - node = list_first(plugin->imported); - while (node != NULL) { - resolve_plugin_commit_rec(context, (cp_plugin_t *) lnode_get(node)); - node = list_next(plugin->imported, node); - } - - // Notify event listeners and update state - event.plugin_id = plugin->plugin->identifier; - event.old_state = plugin->state; - event.new_state = plugin->state = CP_PLUGIN_RESOLVED; - cpi_deliver_event(context, &event); - } -} - -/** - * Recursively cleans up the specified plug-in and its dependencies after - * a failed resolving attempt. - * - * @param plugin the plug-in - */ -static void resolve_plugin_failed_rec(cp_plugin_t *plugin) { - - // Check if already cleaned up - if (!plugin->processed) { - return; - } - plugin->processed = 0; - - // Clean up if only preliminarily resolved - if (plugin->state < CP_PLUGIN_RESOLVED) { - lnode_t *node; - - // Recursively clean up depedencies - while ((node = list_first(plugin->imported)) != NULL) { - cp_plugin_t *ip = lnode_get(node); - - resolve_plugin_failed_rec(ip); - cpi_ptrset_remove(ip->importing, plugin); - list_delete(plugin->imported, node); - lnode_destroy(node); - } - list_destroy(plugin->imported); - plugin->imported = NULL; - } -} - -/** - * Resolves the specified plug-in and its dependencies. - * - * @param context the plug-in context - * @param plugin the plug-in to be resolved - * @return CP_OK (zero) on success or an error code on failure - */ -static int resolve_plugin(cp_context_t *context, cp_plugin_t *plugin) { - cp_status_t status; - - if ((status = resolve_plugin_prel_rec(context, plugin)) == CP_OK || status == CP_OK_PRELIMINARY) { - status = CP_OK; - resolve_plugin_commit_rec(context, plugin); - } else { - resolve_plugin_failed_rec(plugin); - } - assert_processed_zero(context); - return status; -} - -/** - * Starts the plug-in runtime of the specified plug-in. This function does - * not consider dependencies and assumes that the plug-in is resolved but - * not yet started. - * - * @param context the plug-in context - * @param plugin the plug-in - * @return CP_OK (zero) on success or an error code on failure - */ -static int start_plugin_runtime(cp_context_t *context, cp_plugin_t *plugin) { - cp_status_t status = CP_OK; - cpi_plugin_event_t event; - lnode_t *node = NULL; - - event.plugin_id = plugin->plugin->identifier; - do { - - // Allocate space for the list node - node = lnode_create(plugin); - if (node == NULL) { - status = CP_ERR_RESOURCE; - break; - } - - // Set up plug-in instance - if (plugin->runtime_funcs != NULL) { - - // Create plug-in instance if necessary - if (plugin->context == NULL) { - if ((plugin->context = cpi_new_context(plugin, context->env, &status)) == NULL) { - break; - } - context->env->in_create_func_invocation++; - plugin->plugin_data = plugin->runtime_funcs->create(plugin->context); - context->env->in_create_func_invocation--; - if (plugin->plugin_data == NULL) { - status = CP_ERR_RUNTIME; - break; - } - } - - // Start plug-in - if (plugin->runtime_funcs->start != NULL) { - int s; - - // About to start the plug-in - event.old_state = plugin->state; - event.new_state = plugin->state = CP_PLUGIN_STARTING; - cpi_deliver_event(context, &event); - - // Start the plug-in - context->env->in_start_func_invocation++; - s = plugin->runtime_funcs->start(plugin->plugin_data); - context->env->in_start_func_invocation--; - - if (s != CP_OK) { - - // Roll back plug-in state - if (plugin->runtime_funcs->stop != NULL) { - - // Update state - event.old_state = plugin->state; - event.new_state = plugin->state = CP_PLUGIN_STOPPING; - cpi_deliver_event(context, &event); - - // Call stop function - context->env->in_stop_func_invocation++; - plugin->runtime_funcs->stop(plugin->plugin_data); - context->env->in_stop_func_invocation--; - } - - // Destroy plug-in object - context->env->in_destroy_func_invocation++; - plugin->runtime_funcs->destroy(plugin->plugin_data); - context->env->in_destroy_func_invocation--; - - status = CP_ERR_RUNTIME; - break; - } - } - } - - // Plug-in active - list_append(context->env->started_plugins, node); - event.old_state = plugin->state; - event.new_state = plugin->state = CP_PLUGIN_ACTIVE; - cpi_deliver_event(context, &event); - - } while (0); - - // Release resources and roll back plug-in state on failure - if (status != CP_OK) { - if (node != NULL) { - lnode_destroy(node); - } - if (plugin->context != NULL) { - cpi_free_context(plugin->context); - plugin->context = NULL; - } - if (plugin->state != CP_PLUGIN_RESOLVED) { - event.old_state = plugin->state; - event.new_state = plugin->state = CP_PLUGIN_RESOLVED; - cpi_deliver_event(context, &event); - } - plugin->plugin_data = NULL; - } - - // Report error on failure - switch (status) { - case CP_ERR_RESOURCE: - cpi_errorf(context, - N_("Plug-in %s could not be started due to insufficient memory."), - plugin->plugin->identifier); - break; - case CP_ERR_RUNTIME: - cpi_errorf(context, - N_("Plug-in %s failed to start due to plug-in runtime error."), - plugin->plugin->identifier); - break; - default: - break; - } - - return status; -} - -static void warn_dependency_loop(cp_context_t *context, cp_plugin_t *plugin, list_t *importing, int dynamic) { - char *msgbase; - char *msg; - int msgsize; - lnode_t *node; - - // Take the message base - if (dynamic) { - msgbase = N_("Detected a runtime plug-in dependency loop: %s"); - } else { - msgbase = N_("Detected a static plug-in dependency loop: %s"); - } - - // Calculate the required message space - msgsize = 0; - msgsize += strlen(plugin->plugin->identifier); - msgsize += 2; - node = list_last(importing); - while (node != NULL) { - cp_plugin_t *p = lnode_get(node); - if (p == plugin) { - break; - } - msgsize += strlen(p->plugin->identifier); - msgsize += 2; - node = list_prev(importing, node); - } - msg = malloc(sizeof(char) * msgsize); - if (msg != NULL) { - strcpy(msg, plugin->plugin->identifier); - node = list_last(importing); - while (node != NULL) { - cp_plugin_t *p = lnode_get(node); - if (p == plugin) { - break; - } - strcat(msg, ", "); - strcat(msg, p->plugin->identifier); - node = list_prev(importing, node); - } - strcat(msg, "."); - cpi_infof(context, msgbase, msg); - free(msg); - } else { - cpi_infof(context, msgbase, plugin->plugin->identifier); - } -} - -/** - * Starts the specified plug-in and its dependencies. - * - * @param context the plug-in context - * @param plugin the plug-in - * @param importing stack of importing plug-ins - * @return CP_OK (zero) on success or an error code on failure - */ -static int start_plugin_rec(cp_context_t *context, cp_plugin_t *plugin, list_t *importing) { - cp_status_t status = CP_OK; - lnode_t *node; - - // Check if already started or starting - if (plugin->state == CP_PLUGIN_ACTIVE) { - return CP_OK; - } else if (plugin->state == CP_PLUGIN_STARTING) { - warn_dependency_loop(context, plugin, importing, 1); - return CP_OK; - } - assert(plugin->state == CP_PLUGIN_RESOLVED); - - // Check for dependency loops - if (cpi_ptrset_contains(importing, plugin)) { - warn_dependency_loop(context, plugin, importing, 0); - return CP_OK; - } - if (!cpi_ptrset_add(importing, plugin)) { - cpi_errorf(context, - N_("Plug-in %s could not be started due to insufficient memory."), - plugin->plugin->identifier); - return CP_ERR_RESOURCE; - } - - // Start up dependencies - node = list_first(plugin->imported); - while (node != NULL) { - cp_plugin_t *ip = lnode_get(node); - - if ((status = start_plugin_rec(context, ip, importing)) != CP_OK) { - break; - } - node = list_next(plugin->imported, node); - } - cpi_ptrset_remove(importing, plugin); - - // Start up this plug-in - if (status == CP_OK) { - status = start_plugin_runtime(context, plugin); - } - - return status; -} - -CP_HIDDEN cp_status_t cpi_start_plugin(cp_context_t *context, cp_plugin_t *plugin) { - cp_status_t status; - - if ((status = resolve_plugin(context, plugin)) == CP_OK) { - list_t *importing = list_create(LISTCOUNT_T_MAX); - if (importing != NULL) { - status = start_plugin_rec(context, plugin, importing); - assert(list_isempty(importing)); - list_destroy(importing); - } else { - cpi_errorf(context, - N_("Plug-in %s could not be started due to insufficient memory."), - plugin->plugin->identifier); - status = CP_ERR_RESOURCE; - } - } - return status; -} - -CP_C_API cp_status_t cp_start_plugin(cp_context_t *context, const char *id) { - hnode_t *node; - cp_status_t status = CP_OK; - - CHECK_NOT_NULL(context); - CHECK_NOT_NULL(id); - - // Look up and start the plug-in - cpi_lock_context(context); - cpi_check_invocation(context, CPI_CF_ANY, __func__); - node = hash_lookup(context->env->plugins, id); - if (node != NULL) { - status = cpi_start_plugin(context, hnode_get(node)); - } else { - cpi_warnf(context, N_("Unknown plug-in %s could not be started."), id); - status = CP_ERR_UNKNOWN; - } - cpi_unlock_context(context); - - return status; -} - -/** - * Stops the plug-in runtime of the specified plug-in. This function does - * not consider dependencies and assumes that the plug-in is active. - * - * @param context the plug-in context - * @param plugin the plug-in - */ -static void stop_plugin_runtime(cp_context_t *context, cp_plugin_t *plugin) { - cpi_plugin_event_t event; - - // Destroy plug-in instance - event.plugin_id = plugin->plugin->identifier; - if (plugin->context != NULL) { - - // Wait until possible run functions have stopped - cpi_stop_plugin_run(plugin); - - // Stop the plug-in - if (plugin->runtime_funcs->stop != NULL) { - - // About to stop the plug-in - event.old_state = plugin->state; - event.new_state = plugin->state = CP_PLUGIN_STOPPING; - cpi_deliver_event(context, &event); - - // Invoke stop function - context->env->in_stop_func_invocation++; - plugin->runtime_funcs->stop(plugin->plugin_data); - context->env->in_stop_func_invocation--; - - } - - // Unregister all logger functions - cpi_unregister_loggers(plugin->context->env->loggers, plugin); - - // Unregister all plug-in listeners - cpi_unregister_plisteners(plugin->context->env->plugin_listeners, plugin); - - // Release resolved symbols - if (plugin->context->resolved_symbols != NULL) { - while (!hash_isempty(plugin->context->resolved_symbols)) { - hscan_t scan; - hnode_t *node; - const void *ptr; - - hash_scan_begin(&scan, plugin->context->resolved_symbols); - node = hash_scan_next(&scan); - ptr = hnode_getkey(node); - cp_release_symbol(context, ptr); - } - assert(hash_isempty(plugin->context->resolved_symbols)); - } - if (plugin->context->symbol_providers != NULL) { - assert(hash_isempty(plugin->context->symbol_providers)); - } - - // Release defined symbols - if (plugin->defined_symbols != NULL) { - hscan_t scan; - hnode_t *node; - - hash_scan_begin(&scan, plugin->defined_symbols); - while ((node = hash_scan_next(&scan)) != NULL) { - char *n = (char *) hnode_getkey(node); - hash_scan_delfree(plugin->defined_symbols, node); - free(n); - } - hash_destroy(plugin->defined_symbols); - plugin->defined_symbols = NULL; - } - - } - - // Plug-in stopped - cpi_ptrset_remove(context->env->started_plugins, plugin); - event.old_state = plugin->state; - event.new_state = plugin->state = CP_PLUGIN_RESOLVED; - cpi_deliver_event(context, &event); -} - -/** - * Stops the plug-in and all plug-ins depending on it. - * - * @param context the plug-in context - * @param plugin the plug-in - */ -static void stop_plugin_rec(cp_context_t *context, cp_plugin_t *plugin) { - lnode_t *node; - - // Check if already stopped - if (plugin->state < CP_PLUGIN_ACTIVE) { - return; - } - - // Check for dependency loops - if (plugin->processed) { - return; - } - plugin->processed = 1; - - // Stop the depending plug-ins - node = list_first(plugin->importing); - while (node != NULL) { - stop_plugin_rec(context, lnode_get(node)); - node = list_next(plugin->importing, node); - } - - // Stop this plug-in - assert(plugin->state == CP_PLUGIN_ACTIVE); - stop_plugin_runtime(context, plugin); - assert(plugin->state < CP_PLUGIN_ACTIVE); - - // Clear processed flag - plugin->processed = 0; -} - -static void stop_plugin(cp_context_t *context, cp_plugin_t *plugin) { - stop_plugin_rec(context, plugin); - assert_processed_zero(context); -} - -CP_C_API cp_status_t cp_stop_plugin(cp_context_t *context, const char *id) { - hnode_t *node; - cp_plugin_t *plugin; - cp_status_t status = CP_OK; - - CHECK_NOT_NULL(context); - CHECK_NOT_NULL(id); - - // Look up and stop the plug-in - cpi_lock_context(context); - cpi_check_invocation(context, CPI_CF_ANY, __func__); - node = hash_lookup(context->env->plugins, id); - if (node != NULL) { - plugin = hnode_get(node); - stop_plugin(context, plugin); - } else { - cpi_warnf(context, N_("Unknown plug-in %s could not be stopped."), id); - status = CP_ERR_UNKNOWN; - } - cpi_unlock_context(context); - - return status; -} - -CP_C_API void cp_stop_plugins(cp_context_t *context) { - lnode_t *node; - - CHECK_NOT_NULL(context); - - // Stop the active plug-ins in the reverse order they were started - cpi_lock_context(context); - cpi_check_invocation(context, CPI_CF_ANY, __func__); - while ((node = list_last(context->env->started_plugins)) != NULL) { - stop_plugin(context, lnode_get(node)); - } - cpi_unlock_context(context); -} - -static void unresolve_plugin_rec(cp_context_t *context, cp_plugin_t *plugin) { - lnode_t *node; - cpi_plugin_event_t event; - - // Check if already unresolved - if (plugin->state < CP_PLUGIN_RESOLVED) { - return; - } - assert(plugin->state == CP_PLUGIN_RESOLVED); - - // Clear the list of imported plug-ins (also breaks dependency loops) - while ((node = list_first(plugin->imported)) != NULL) { - cp_plugin_t *ip = lnode_get(node); - - cpi_ptrset_remove(ip->importing, plugin); - list_delete(plugin->imported, node); - lnode_destroy(node); - } - assert(list_isempty(plugin->imported)); - list_destroy(plugin->imported); - plugin->imported = NULL; - - // Unresolve depending plugins - while ((node = list_first(plugin->importing)) != NULL) { - unresolve_plugin_rec(context, lnode_get(node)); - } - - // Unresolve this plug-in - unresolve_plugin_runtime(plugin); - event.plugin_id = plugin->plugin->identifier; - event.old_state = plugin->state; - event.new_state = plugin->state = CP_PLUGIN_INSTALLED; - cpi_deliver_event(context, &event); -} - -/** - * Unresolves a plug-in. - * - * @param context the plug-in context - * @param plug-in the plug-in to be unresolved - */ -static void unresolve_plugin(cp_context_t *context, cp_plugin_t *plugin) { - stop_plugin(context, plugin); - unresolve_plugin_rec(context, plugin); -} - -static void free_plugin_import_content(cp_plugin_import_t *import) { - assert(import != NULL); - free(import->plugin_id); - free(import->version); -} - -static void free_ext_point_content(cp_ext_point_t *ext_point) { - free(ext_point->name); - free(ext_point->local_id); - free(ext_point->identifier); - free(ext_point->schema_path); -} - -static void free_extension_content(cp_extension_t *extension) { - free(extension->name); - free(extension->local_id); - free(extension->identifier); - free(extension->ext_point_id); -} - -static void free_cfg_element_content(cp_cfg_element_t *ce) { - unsigned int i; - - assert(ce != NULL); - free(ce->name); - if (ce->atts != NULL) { - free(ce->atts[0]); - free(ce->atts); - } - free(ce->value); - for (i = 0; i < ce->num_children; i++) { - free_cfg_element_content(ce->children + i); - } - free(ce->children); -} - -CP_HIDDEN void cpi_free_plugin(cp_plugin_info_t *plugin) { - unsigned int i; - - assert(plugin != NULL); - free(plugin->name); - free(plugin->identifier); - free(plugin->version); - free(plugin->provider_name); - free(plugin->plugin_path); - free(plugin->abi_bw_compatibility); - free(plugin->api_bw_compatibility); - free(plugin->req_cpluff_version); - for (i = 0; i < plugin->num_imports; i++) { - free_plugin_import_content(plugin->imports + i); - } - free(plugin->imports); - free(plugin->runtime_lib_name); - free(plugin->runtime_funcs_symbol); - for (i = 0; i < plugin->num_ext_points; i++) { - free_ext_point_content(plugin->ext_points + i); - } - free(plugin->ext_points); - for (i = 0; i < plugin->num_extensions; i++) { - free_extension_content(plugin->extensions + i); - if (plugin->extensions[i].configuration != NULL) { - free_cfg_element_content(plugin->extensions[i].configuration); - free(plugin->extensions[i].configuration); - } - } - free(plugin->extensions); - free(plugin); -} - -/** - * Frees any memory allocated for a registered plug-in. - * - * @param context the plug-in context - * @param plugin the plug-in to be freed - */ -static void free_registered_plugin(cp_context_t *context, cp_plugin_t *plugin) { - assert(context != NULL); - assert(plugin != NULL); - - // Release plug-in information - cpi_release_info(context, plugin->plugin); - - // Release data structures - if (plugin->importing != NULL) { - assert(list_isempty(plugin->importing)); - list_destroy(plugin->importing); - } - assert(plugin->imported == NULL); - - free(plugin); -} - -/** - * Uninstalls a plug-in associated with the specified hash node. - * - * @param context the plug-in context - * @param node the hash node of the plug-in to be uninstalled - */ -static void uninstall_plugin(cp_context_t *context, hnode_t *node) { - cp_plugin_t *plugin; - cpi_plugin_event_t event; - - // Check if already uninstalled - plugin = (cp_plugin_t *) hnode_get(node); - if (plugin->state <= CP_PLUGIN_UNINSTALLED) { - // TODO: Is this possible state? - return; - } - - // Make sure the plug-in is not in resolved state - unresolve_plugin(context, plugin); - assert(plugin->state == CP_PLUGIN_INSTALLED); - - // Plug-in uninstalled - event.plugin_id = plugin->plugin->identifier; - event.old_state = plugin->state; - event.new_state = plugin->state = CP_PLUGIN_UNINSTALLED; - cpi_deliver_event(context, &event); - - // Unregister extension objects - unregister_extensions(context, plugin->plugin); - - // Unregister the plug-in - hash_delete_free(context->env->plugins, node); - - // Free the plug-in data structures - free_registered_plugin(context, plugin); -} - -CP_C_API cp_status_t cp_uninstall_plugin(cp_context_t *context, const char *id) { - hnode_t *node; - cp_status_t status = CP_OK; - - CHECK_NOT_NULL(context); - CHECK_NOT_NULL(id); - - // Look up and unload the plug-in - cpi_lock_context(context); - cpi_check_invocation(context, CPI_CF_ANY, __func__); - node = hash_lookup(context->env->plugins, id); - if (node != NULL) { - uninstall_plugin(context, node); - } else { - cpi_warnf(context, N_("Unknown plug-in %s could not be uninstalled."), id); - status = CP_ERR_UNKNOWN; - } - cpi_unlock_context(context); - - return status; -} - -CP_C_API void cp_uninstall_plugins(cp_context_t *context) { - hscan_t scan; - hnode_t *node; - - CHECK_NOT_NULL(context); - - cpi_lock_context(context); - cpi_check_invocation(context, CPI_CF_ANY, __func__); - cp_stop_plugins(context); - while (1) { - hash_scan_begin(&scan, context->env->plugins); - if ((node = hash_scan_next(&scan)) != NULL) { - uninstall_plugin(context, node); - } else { - break; - } - } - cpi_unlock_context(context); -} |