aboutsummaryrefslogtreecommitdiff
path: root/lib/cpluff/libcpluff/pcontrol.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/cpluff/libcpluff/pcontrol.c')
-rw-r--r--lib/cpluff/libcpluff/pcontrol.c1243
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);
-}