1 /*-------------------------------------------------------------------------
2 * C-Pluff, a plug-in framework for C
3 * Copyright 2007 Johannes Lehtinen
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
19 * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
20 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
21 * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 *-----------------------------------------------------------------------*/
25 * Core plug-in management functions
37 #include "../kazlib/list.h"
38 #include "../kazlib/hash.h"
45 /* ------------------------------------------------------------------------
46 * Function definitions
47 * ----------------------------------------------------------------------*/
52 static void assert_processed_zero(cp_context_t *context) {
56 hash_scan_begin(&scan, context->env->plugins);
57 while ((node = hash_scan_next(&scan)) != NULL) {
58 cp_plugin_t *plugin = hnode_get(node);
59 assert(plugin->processed == 0);
63 #define assert_processed_zero(c) assert(1)
66 static void unregister_extensions(cp_context_t *context, cp_plugin_info_t *plugin) {
69 for (i = 0; i < plugin->num_ext_points; i++) {
70 cp_ext_point_t *ep = plugin->ext_points + i;
73 if ((hnode = hash_lookup(context->env->ext_points, ep->identifier)) != NULL
74 && hnode_get(hnode) == ep) {
75 hash_delete_free(context->env->ext_points, hnode);
78 for (i = 0; i < plugin->num_extensions; i++) {
79 cp_extension_t *e = plugin->extensions + i;
82 if ((hnode = hash_lookup(context->env->extensions, e->ext_point_id)) != NULL) {
83 list_t *el = hnode_get(hnode);
84 lnode_t *lnode = list_first(el);
86 while (lnode != NULL) {
87 lnode_t *nn = list_next(el, lnode);
88 if (lnode_get(lnode) == e) {
89 list_delete(el, lnode);
95 if (list_isempty(el)) {
96 char *epid = (char *) hnode_getkey(hnode);
97 hash_delete_free(context->env->extensions, hnode);
105 CP_C_API cp_status_t cp_install_plugin(cp_context_t *context, cp_plugin_info_t *plugin) {
106 cp_plugin_t *rp = NULL;
107 cp_status_t status = CP_OK;
108 cpi_plugin_event_t event;
111 CHECK_NOT_NULL(context);
112 CHECK_NOT_NULL(plugin);
114 cpi_lock_context(context);
115 cpi_check_invocation(context, CPI_CF_ANY, __func__);
118 // Check that there is no conflicting plug-in already loaded
119 if (hash_lookup(context->env->plugins, plugin->identifier) != NULL) {
121 N_("Plug-in %s could not be installed because a plug-in with the same identifier is already installed."),
123 status = CP_ERR_CONFLICT;
127 // Increase usage count for the plug-in descriptor
128 cpi_use_info(context, plugin);
130 // Allocate space for the plug-in state
131 if ((rp = malloc(sizeof(cp_plugin_t))) == NULL) {
132 status = CP_ERR_RESOURCE;
136 // Initialize plug-in state
137 memset(rp, 0, sizeof(cp_plugin_t));
140 rp->state = CP_PLUGIN_INSTALLED;
142 rp->runtime_lib = NULL;
143 rp->runtime_funcs = NULL;
144 rp->plugin_data = NULL;
145 rp->importing = list_create(LISTCOUNT_T_MAX);
146 if (rp->importing == NULL) {
147 status = CP_ERR_RESOURCE;
150 if (!hash_alloc_insert(context->env->plugins, plugin->identifier, rp)) {
151 status = CP_ERR_RESOURCE;
155 // Register extension points
156 for (i = 0; status == CP_OK && i < plugin->num_ext_points; i++) {
157 cp_ext_point_t *ep = plugin->ext_points + i;
160 if ((hnode = hash_lookup(context->env->ext_points, ep->identifier)) != NULL) {
161 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);
162 status = CP_ERR_CONFLICT;
163 } else if (!hash_alloc_insert(context->env->ext_points, ep->identifier, ep)) {
164 status = CP_ERR_RESOURCE;
168 // Register extensions
169 for (i = 0; status == CP_OK && i < plugin->num_extensions; i++) {
170 cp_extension_t *e = plugin->extensions + i;
175 if ((hnode = hash_lookup(context->env->extensions, e->ext_point_id)) == NULL) {
177 if ((el = list_create(LISTCOUNT_T_MAX)) != NULL
178 && (epid = strdup(e->ext_point_id)) != NULL) {
179 if (!hash_alloc_insert(context->env->extensions, epid, el)) {
181 status = CP_ERR_RESOURCE;
188 status = CP_ERR_RESOURCE;
192 el = hnode_get(hnode);
194 if ((lnode = lnode_create(e)) != NULL) {
195 list_append(el, lnode);
197 status = CP_ERR_RESOURCE;
202 // Break if previous loops failed
203 if (status != CP_OK) {
208 event.plugin_id = plugin->identifier;
209 event.old_state = CP_PLUGIN_UNINSTALLED;
210 event.new_state = rp->state;
211 cpi_deliver_event(context, &event);
215 // Release resources on failure
216 if (status != CP_OK) {
218 if (rp->importing != NULL) {
219 list_destroy(rp->importing);
223 unregister_extensions(context, plugin);
226 // Report possible resource error
227 if (status == CP_ERR_RESOURCE) {
229 N_("Plug-in %s could not be installed due to insufficient system resources."), plugin->identifier);
231 cpi_unlock_context(context);
237 * Unresolves the plug-in runtime information.
239 * @param plugin the plug-in to unresolve
241 static void unresolve_plugin_runtime(cp_plugin_t *plugin) {
243 // Destroy the plug-in instance, if necessary
244 if (plugin->context != NULL) {
245 plugin->context->env->in_destroy_func_invocation++;
246 plugin->runtime_funcs->destroy(plugin->plugin_data);
247 plugin->context->env->in_destroy_func_invocation--;
248 plugin->plugin_data = NULL;
249 cpi_free_context(plugin->context);
250 plugin->context = NULL;
253 // Close plug-in runtime library
254 plugin->runtime_funcs = NULL;
255 if (plugin->runtime_lib != NULL) {
256 DLCLOSE(plugin->runtime_lib);
257 plugin->runtime_lib = NULL;
262 * Loads and resolves the plug-in runtime library and initialization functions.
264 * @param context the plug-in context
265 * @param plugin the plugin
266 * @return CP_OK (zero) on success or error code on failure
268 static int resolve_plugin_runtime(cp_context_t *context, cp_plugin_t *plugin) {
271 cp_status_t status = CP_OK;
273 assert(plugin->runtime_lib == NULL);
274 if (plugin->plugin->runtime_lib_name == NULL) {
279 int ppath_len, lname_len;
280 int cpluff_compatibility = 1;
282 // Check C-Pluff compatibility
283 if (plugin->plugin->req_cpluff_version != NULL) {
284 #ifdef CP_ABI_COMPATIBILITY
285 cpluff_compatibility = (
286 cpi_vercmp(plugin->plugin->req_cpluff_version, CP_VERSION) <= 0
287 && cpi_vercmp(plugin->plugin->req_cpluff_version, CP_ABI_COMPATIBILITY) >= 0);
289 cpluff_compatibility = (cpi_vercmp(plugin->plugin->req_cpluff_version, CP_VERSION) == 0);
292 if (!cpluff_compatibility) {
293 cpi_errorf(context, N_("Plug-in %s could not be resolved due to version incompatibility with C-Pluff."), plugin->plugin->identifier);
294 status = CP_ERR_DEPENDENCY;
298 // Construct a path to plug-in runtime library.
299 /// @todo Add platform specific prefix (for example, "lib")
300 ppath_len = strlen(plugin->plugin->plugin_path);
301 lname_len = strlen(plugin->plugin->runtime_lib_name);
302 rlpath_len = ppath_len + lname_len + strlen(CP_SHREXT) + 2;
303 if ((rlpath = malloc(rlpath_len * sizeof(char))) == NULL) {
304 cpi_errorf(context, N_("Plug-in %s runtime library could not be loaded due to insufficient memory."), plugin->plugin->identifier);
305 status = CP_ERR_RESOURCE;
308 memset(rlpath, 0, rlpath_len * sizeof(char));
309 strcpy(rlpath, plugin->plugin->plugin_path);
310 rlpath[ppath_len] = CP_FNAMESEP_CHAR;
311 strcpy(rlpath + ppath_len + 1, plugin->plugin->runtime_lib_name);
312 strcpy(rlpath + ppath_len + 1 + lname_len, CP_SHREXT);
314 // Open the plug-in runtime library
315 plugin->runtime_lib = DLOPEN(rlpath);
316 if (plugin->runtime_lib == NULL) {
317 const char *error = DLERROR();
319 error = _("Unspecified error.");
321 cpi_errorf(context, N_("Plug-in %s runtime library %s could not be opened: %s"), plugin->plugin->identifier, rlpath, error);
322 status = CP_ERR_RUNTIME;
326 // Resolve plug-in functions
327 if (plugin->plugin->runtime_funcs_symbol != NULL) {
328 plugin->runtime_funcs = (cp_plugin_runtime_t *) DLSYM(plugin->runtime_lib, plugin->plugin->runtime_funcs_symbol);
329 if (plugin->runtime_funcs == NULL) {
330 const char *error = DLERROR();
332 error = _("Unspecified error.");
334 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);
335 status = CP_ERR_RUNTIME;
338 if (plugin->runtime_funcs->create == NULL
339 || plugin->runtime_funcs->destroy == NULL) {
340 cpi_errorf(context, N_("Plug-in %s is missing a constructor or destructor function."), plugin->plugin->identifier);
341 status = CP_ERR_RUNTIME;
350 if (status != CP_OK) {
351 unresolve_plugin_runtime(plugin);
358 * Resolves the specified plug-in import into a plug-in pointer. Does not
359 * try to resolve the imported plug-in.
361 * @param context the plug-in context
362 * @param plugin the plug-in being resolved
363 * @param import the plug-in import to resolve
364 * @param ipptr filled with pointer to the resolved plug-in or NULL
365 * @return CP_OK on success or error code on failure
367 static int resolve_plugin_import(cp_context_t *context, cp_plugin_t *plugin, cp_plugin_import_t *import, cp_plugin_t **ipptr) {
368 cp_plugin_t *ip = NULL;
371 // Lookup the plug-in
372 node = hash_lookup(context->env->plugins, import->plugin_id);
374 ip = hnode_get(node);
377 // Check plug-in version
379 && import->version != NULL
380 && (ip->plugin->version == NULL
381 || (ip->plugin->abi_bw_compatibility == NULL
382 && cpi_vercmp(import->version, ip->plugin->version) != 0)
383 || (ip->plugin->abi_bw_compatibility != NULL
384 && (cpi_vercmp(import->version, ip->plugin->version) > 0
385 || cpi_vercmp(import->version, ip->plugin->abi_bw_compatibility) < 0)))) {
387 N_("Plug-in %s could not be resolved due to version incompatibility with plug-in %s."),
388 plugin->plugin->identifier,
391 return CP_ERR_DEPENDENCY;
394 // Check if missing mandatory plug-in
395 if (ip == NULL && !import->optional) {
397 N_("Plug-in %s could not be resolved because it depends on plug-in %s which is not installed."),
398 plugin->plugin->identifier,
401 return CP_ERR_DEPENDENCY;
404 // Return imported plug-in
410 * Resolves the specified plug-in and its dependencies while leaving plug-ins
411 * with circular dependencies in a preliminarily resolved state.
413 * @param context the plug-in context
414 * @param plugin the plug-in
415 * @return CP_OK (zero) or CP_OK_PRELIMINARY or an error code
417 static int resolve_plugin_prel_rec(cp_context_t *context, cp_plugin_t *plugin) {
418 cp_status_t status = CP_OK;
419 int error_reported = 0;
420 lnode_t *node = NULL;
423 // Check if already resolved
424 if (plugin->state >= CP_PLUGIN_RESOLVED) {
428 // Check for dependency loops
429 if (plugin->processed) {
430 return CP_OK_PRELIMINARY;
432 plugin->processed = 1;
436 // Recursively resolve the imported plug-ins
437 assert(plugin->imported == NULL);
438 if ((plugin->imported = list_create(LISTCOUNT_T_MAX)) == NULL) {
439 status = CP_ERR_RESOURCE;
442 for (i = 0; i < plugin->plugin->num_imports; i++) {
446 if ((node = lnode_create(NULL)) == NULL) {
447 status = CP_ERR_RESOURCE;
450 if ((s = resolve_plugin_import(context, plugin, plugin->plugin->imports + i, &ip)) != CP_OK) {
457 list_append(plugin->imported, node);
459 if (!cpi_ptrset_add(ip->importing, plugin)) {
460 status = CP_ERR_RESOURCE;
462 } else if ((s = resolve_plugin_prel_rec(context, ip)) != CP_OK && s != CP_OK_PRELIMINARY) {
463 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);
473 if (status != CP_OK) {
477 // Resolve this plug-in
478 assert(plugin->state == CP_PLUGIN_INSTALLED);
479 if ((i = resolve_plugin_runtime(context, plugin)) != CP_OK) {
485 // Notify event listeners and update state if completely resolved
486 if (status == CP_OK) {
487 cpi_plugin_event_t event;
489 plugin->processed = 0;
490 event.plugin_id = plugin->plugin->identifier;
491 event.old_state = plugin->state;
492 event.new_state = plugin->state = CP_PLUGIN_RESOLVED;
493 cpi_deliver_event(context, &event);
504 if (status == CP_ERR_RESOURCE && !error_reported) {
505 cpi_errorf(context, N_("Plug-in %s could not be resolved because of insufficient memory."), plugin->plugin->identifier);
512 * Recursively commits the resolving process for the specified plug-in and
515 * @param context the plug-in context
516 * @param plugin the plug-in
518 static void resolve_plugin_commit_rec(cp_context_t *context, cp_plugin_t *plugin) {
520 // Check if already committed
521 if (!plugin->processed) {
524 plugin->processed = 0;
526 // Commit if only preliminarily resolved
527 if (plugin->state < CP_PLUGIN_RESOLVED) {
528 cpi_plugin_event_t event;
531 // Recursively commit dependencies
532 node = list_first(plugin->imported);
533 while (node != NULL) {
534 resolve_plugin_commit_rec(context, (cp_plugin_t *) lnode_get(node));
535 node = list_next(plugin->imported, node);
538 // Notify event listeners and update state
539 event.plugin_id = plugin->plugin->identifier;
540 event.old_state = plugin->state;
541 event.new_state = plugin->state = CP_PLUGIN_RESOLVED;
542 cpi_deliver_event(context, &event);
547 * Recursively cleans up the specified plug-in and its dependencies after
548 * a failed resolving attempt.
550 * @param plugin the plug-in
552 static void resolve_plugin_failed_rec(cp_plugin_t *plugin) {
554 // Check if already cleaned up
555 if (!plugin->processed) {
558 plugin->processed = 0;
560 // Clean up if only preliminarily resolved
561 if (plugin->state < CP_PLUGIN_RESOLVED) {
564 // Recursively clean up depedencies
565 while ((node = list_first(plugin->imported)) != NULL) {
566 cp_plugin_t *ip = lnode_get(node);
568 resolve_plugin_failed_rec(ip);
569 cpi_ptrset_remove(ip->importing, plugin);
570 list_delete(plugin->imported, node);
573 list_destroy(plugin->imported);
574 plugin->imported = NULL;
579 * Resolves the specified plug-in and its dependencies.
581 * @param context the plug-in context
582 * @param plugin the plug-in to be resolved
583 * @return CP_OK (zero) on success or an error code on failure
585 static int resolve_plugin(cp_context_t *context, cp_plugin_t *plugin) {
588 if ((status = resolve_plugin_prel_rec(context, plugin)) == CP_OK || status == CP_OK_PRELIMINARY) {
590 resolve_plugin_commit_rec(context, plugin);
592 resolve_plugin_failed_rec(plugin);
594 assert_processed_zero(context);
599 * Starts the plug-in runtime of the specified plug-in. This function does
600 * not consider dependencies and assumes that the plug-in is resolved but
603 * @param context the plug-in context
604 * @param plugin the plug-in
605 * @return CP_OK (zero) on success or an error code on failure
607 static int start_plugin_runtime(cp_context_t *context, cp_plugin_t *plugin) {
608 cp_status_t status = CP_OK;
609 cpi_plugin_event_t event;
610 lnode_t *node = NULL;
612 event.plugin_id = plugin->plugin->identifier;
615 // Allocate space for the list node
616 node = lnode_create(plugin);
618 status = CP_ERR_RESOURCE;
622 // Set up plug-in instance
623 if (plugin->runtime_funcs != NULL) {
625 // Create plug-in instance if necessary
626 if (plugin->context == NULL) {
627 if ((plugin->context = cpi_new_context(plugin, context->env, &status)) == NULL) {
630 context->env->in_create_func_invocation++;
631 plugin->plugin_data = plugin->runtime_funcs->create(plugin->context);
632 context->env->in_create_func_invocation--;
633 if (plugin->plugin_data == NULL) {
634 status = CP_ERR_RUNTIME;
640 if (plugin->runtime_funcs->start != NULL) {
643 // About to start the plug-in
644 event.old_state = plugin->state;
645 event.new_state = plugin->state = CP_PLUGIN_STARTING;
646 cpi_deliver_event(context, &event);
649 context->env->in_start_func_invocation++;
650 s = plugin->runtime_funcs->start(plugin->plugin_data);
651 context->env->in_start_func_invocation--;
655 // Roll back plug-in state
656 if (plugin->runtime_funcs->stop != NULL) {
659 event.old_state = plugin->state;
660 event.new_state = plugin->state = CP_PLUGIN_STOPPING;
661 cpi_deliver_event(context, &event);
663 // Call stop function
664 context->env->in_stop_func_invocation++;
665 plugin->runtime_funcs->stop(plugin->plugin_data);
666 context->env->in_stop_func_invocation--;
669 // Destroy plug-in object
670 context->env->in_destroy_func_invocation++;
671 plugin->runtime_funcs->destroy(plugin->plugin_data);
672 context->env->in_destroy_func_invocation--;
674 status = CP_ERR_RUNTIME;
681 list_append(context->env->started_plugins, node);
682 event.old_state = plugin->state;
683 event.new_state = plugin->state = CP_PLUGIN_ACTIVE;
684 cpi_deliver_event(context, &event);
688 // Release resources and roll back plug-in state on failure
689 if (status != CP_OK) {
693 if (plugin->context != NULL) {
694 cpi_free_context(plugin->context);
695 plugin->context = NULL;
697 if (plugin->state != CP_PLUGIN_RESOLVED) {
698 event.old_state = plugin->state;
699 event.new_state = plugin->state = CP_PLUGIN_RESOLVED;
700 cpi_deliver_event(context, &event);
702 plugin->plugin_data = NULL;
705 // Report error on failure
707 case CP_ERR_RESOURCE:
709 N_("Plug-in %s could not be started due to insufficient memory."),
710 plugin->plugin->identifier);
714 N_("Plug-in %s failed to start due to plug-in runtime error."),
715 plugin->plugin->identifier);
724 static void warn_dependency_loop(cp_context_t *context, cp_plugin_t *plugin, list_t *importing, int dynamic) {
730 // Take the message base
732 msgbase = N_("Detected a runtime plug-in dependency loop: %s");
734 msgbase = N_("Detected a static plug-in dependency loop: %s");
737 // Calculate the required message space
739 msgsize += strlen(plugin->plugin->identifier);
741 node = list_last(importing);
742 while (node != NULL) {
743 cp_plugin_t *p = lnode_get(node);
747 msgsize += strlen(p->plugin->identifier);
749 node = list_prev(importing, node);
751 msg = malloc(sizeof(char) * msgsize);
753 strcpy(msg, plugin->plugin->identifier);
754 node = list_last(importing);
755 while (node != NULL) {
756 cp_plugin_t *p = lnode_get(node);
761 strcat(msg, p->plugin->identifier);
762 node = list_prev(importing, node);
765 cpi_infof(context, msgbase, msg);
768 cpi_infof(context, msgbase, plugin->plugin->identifier);
773 * Starts the specified plug-in and its dependencies.
775 * @param context the plug-in context
776 * @param plugin the plug-in
777 * @param importing stack of importing plug-ins
778 * @return CP_OK (zero) on success or an error code on failure
780 static int start_plugin_rec(cp_context_t *context, cp_plugin_t *plugin, list_t *importing) {
781 cp_status_t status = CP_OK;
784 // Check if already started or starting
785 if (plugin->state == CP_PLUGIN_ACTIVE) {
787 } else if (plugin->state == CP_PLUGIN_STARTING) {
788 warn_dependency_loop(context, plugin, importing, 1);
791 assert(plugin->state == CP_PLUGIN_RESOLVED);
793 // Check for dependency loops
794 if (cpi_ptrset_contains(importing, plugin)) {
795 warn_dependency_loop(context, plugin, importing, 0);
798 if (!cpi_ptrset_add(importing, plugin)) {
800 N_("Plug-in %s could not be started due to insufficient memory."),
801 plugin->plugin->identifier);
802 return CP_ERR_RESOURCE;
805 // Start up dependencies
806 node = list_first(plugin->imported);
807 while (node != NULL) {
808 cp_plugin_t *ip = lnode_get(node);
810 if ((status = start_plugin_rec(context, ip, importing)) != CP_OK) {
813 node = list_next(plugin->imported, node);
815 cpi_ptrset_remove(importing, plugin);
817 // Start up this plug-in
818 if (status == CP_OK) {
819 status = start_plugin_runtime(context, plugin);
825 CP_HIDDEN cp_status_t cpi_start_plugin(cp_context_t *context, cp_plugin_t *plugin) {
828 if ((status = resolve_plugin(context, plugin)) == CP_OK) {
829 list_t *importing = list_create(LISTCOUNT_T_MAX);
830 if (importing != NULL) {
831 status = start_plugin_rec(context, plugin, importing);
832 assert(list_isempty(importing));
833 list_destroy(importing);
836 N_("Plug-in %s could not be started due to insufficient memory."),
837 plugin->plugin->identifier);
838 status = CP_ERR_RESOURCE;
844 CP_C_API cp_status_t cp_start_plugin(cp_context_t *context, const char *id) {
846 cp_status_t status = CP_OK;
848 CHECK_NOT_NULL(context);
851 // Look up and start the plug-in
852 cpi_lock_context(context);
853 cpi_check_invocation(context, CPI_CF_ANY, __func__);
854 node = hash_lookup(context->env->plugins, id);
856 status = cpi_start_plugin(context, hnode_get(node));
858 cpi_warnf(context, N_("Unknown plug-in %s could not be started."), id);
859 status = CP_ERR_UNKNOWN;
861 cpi_unlock_context(context);
867 * Stops the plug-in runtime of the specified plug-in. This function does
868 * not consider dependencies and assumes that the plug-in is active.
870 * @param context the plug-in context
871 * @param plugin the plug-in
873 static void stop_plugin_runtime(cp_context_t *context, cp_plugin_t *plugin) {
874 cpi_plugin_event_t event;
876 // Destroy plug-in instance
877 event.plugin_id = plugin->plugin->identifier;
878 if (plugin->context != NULL) {
880 // Wait until possible run functions have stopped
881 cpi_stop_plugin_run(plugin);
884 if (plugin->runtime_funcs->stop != NULL) {
886 // About to stop the plug-in
887 event.old_state = plugin->state;
888 event.new_state = plugin->state = CP_PLUGIN_STOPPING;
889 cpi_deliver_event(context, &event);
891 // Invoke stop function
892 context->env->in_stop_func_invocation++;
893 plugin->runtime_funcs->stop(plugin->plugin_data);
894 context->env->in_stop_func_invocation--;
898 // Unregister all logger functions
899 cpi_unregister_loggers(plugin->context->env->loggers, plugin);
901 // Unregister all plug-in listeners
902 cpi_unregister_plisteners(plugin->context->env->plugin_listeners, plugin);
904 // Release resolved symbols
905 if (plugin->context->resolved_symbols != NULL) {
906 while (!hash_isempty(plugin->context->resolved_symbols)) {
911 hash_scan_begin(&scan, plugin->context->resolved_symbols);
912 node = hash_scan_next(&scan);
913 ptr = hnode_getkey(node);
914 cp_release_symbol(context, ptr);
916 assert(hash_isempty(plugin->context->resolved_symbols));
918 if (plugin->context->symbol_providers != NULL) {
919 assert(hash_isempty(plugin->context->symbol_providers));
922 // Release defined symbols
923 if (plugin->defined_symbols != NULL) {
927 hash_scan_begin(&scan, plugin->defined_symbols);
928 while ((node = hash_scan_next(&scan)) != NULL) {
929 char *n = (char *) hnode_getkey(node);
930 hash_scan_delfree(plugin->defined_symbols, node);
933 hash_destroy(plugin->defined_symbols);
934 plugin->defined_symbols = NULL;
940 cpi_ptrset_remove(context->env->started_plugins, plugin);
941 event.old_state = plugin->state;
942 event.new_state = plugin->state = CP_PLUGIN_RESOLVED;
943 cpi_deliver_event(context, &event);
947 * Stops the plug-in and all plug-ins depending on it.
949 * @param context the plug-in context
950 * @param plugin the plug-in
952 static void stop_plugin_rec(cp_context_t *context, cp_plugin_t *plugin) {
955 // Check if already stopped
956 if (plugin->state < CP_PLUGIN_ACTIVE) {
960 // Check for dependency loops
961 if (plugin->processed) {
964 plugin->processed = 1;
966 // Stop the depending plug-ins
967 node = list_first(plugin->importing);
968 while (node != NULL) {
969 stop_plugin_rec(context, lnode_get(node));
970 node = list_next(plugin->importing, node);
974 assert(plugin->state == CP_PLUGIN_ACTIVE);
975 stop_plugin_runtime(context, plugin);
976 assert(plugin->state < CP_PLUGIN_ACTIVE);
978 // Clear processed flag
979 plugin->processed = 0;
982 static void stop_plugin(cp_context_t *context, cp_plugin_t *plugin) {
983 stop_plugin_rec(context, plugin);
984 assert_processed_zero(context);
987 CP_C_API cp_status_t cp_stop_plugin(cp_context_t *context, const char *id) {
990 cp_status_t status = CP_OK;
992 CHECK_NOT_NULL(context);
995 // Look up and stop the plug-in
996 cpi_lock_context(context);
997 cpi_check_invocation(context, CPI_CF_ANY, __func__);
998 node = hash_lookup(context->env->plugins, id);
1000 plugin = hnode_get(node);
1001 stop_plugin(context, plugin);
1003 cpi_warnf(context, N_("Unknown plug-in %s could not be stopped."), id);
1004 status = CP_ERR_UNKNOWN;
1006 cpi_unlock_context(context);
1011 CP_C_API void cp_stop_plugins(cp_context_t *context) {
1014 CHECK_NOT_NULL(context);
1016 // Stop the active plug-ins in the reverse order they were started
1017 cpi_lock_context(context);
1018 cpi_check_invocation(context, CPI_CF_ANY, __func__);
1019 while ((node = list_last(context->env->started_plugins)) != NULL) {
1020 stop_plugin(context, lnode_get(node));
1022 cpi_unlock_context(context);
1025 static void unresolve_plugin_rec(cp_context_t *context, cp_plugin_t *plugin) {
1027 cpi_plugin_event_t event;
1029 // Check if already unresolved
1030 if (plugin->state < CP_PLUGIN_RESOLVED) {
1033 assert(plugin->state == CP_PLUGIN_RESOLVED);
1035 // Clear the list of imported plug-ins (also breaks dependency loops)
1036 while ((node = list_first(plugin->imported)) != NULL) {
1037 cp_plugin_t *ip = lnode_get(node);
1039 cpi_ptrset_remove(ip->importing, plugin);
1040 list_delete(plugin->imported, node);
1041 lnode_destroy(node);
1043 assert(list_isempty(plugin->imported));
1044 list_destroy(plugin->imported);
1045 plugin->imported = NULL;
1047 // Unresolve depending plugins
1048 while ((node = list_first(plugin->importing)) != NULL) {
1049 unresolve_plugin_rec(context, lnode_get(node));
1052 // Unresolve this plug-in
1053 unresolve_plugin_runtime(plugin);
1054 event.plugin_id = plugin->plugin->identifier;
1055 event.old_state = plugin->state;
1056 event.new_state = plugin->state = CP_PLUGIN_INSTALLED;
1057 cpi_deliver_event(context, &event);
1061 * Unresolves a plug-in.
1063 * @param context the plug-in context
1064 * @param plug-in the plug-in to be unresolved
1066 static void unresolve_plugin(cp_context_t *context, cp_plugin_t *plugin) {
1067 stop_plugin(context, plugin);
1068 unresolve_plugin_rec(context, plugin);
1071 static void free_plugin_import_content(cp_plugin_import_t *import) {
1072 assert(import != NULL);
1073 free(import->plugin_id);
1074 free(import->version);
1077 static void free_ext_point_content(cp_ext_point_t *ext_point) {
1078 free(ext_point->name);
1079 free(ext_point->local_id);
1080 free(ext_point->identifier);
1081 free(ext_point->schema_path);
1084 static void free_extension_content(cp_extension_t *extension) {
1085 free(extension->name);
1086 free(extension->local_id);
1087 free(extension->identifier);
1088 free(extension->ext_point_id);
1091 static void free_cfg_element_content(cp_cfg_element_t *ce) {
1096 if (ce->atts != NULL) {
1101 for (i = 0; i < ce->num_children; i++) {
1102 free_cfg_element_content(ce->children + i);
1107 CP_HIDDEN void cpi_free_plugin(cp_plugin_info_t *plugin) {
1110 assert(plugin != NULL);
1112 free(plugin->identifier);
1113 free(plugin->version);
1114 free(plugin->provider_name);
1115 free(plugin->plugin_path);
1116 free(plugin->abi_bw_compatibility);
1117 free(plugin->api_bw_compatibility);
1118 free(plugin->req_cpluff_version);
1119 for (i = 0; i < plugin->num_imports; i++) {
1120 free_plugin_import_content(plugin->imports + i);
1122 free(plugin->imports);
1123 free(plugin->runtime_lib_name);
1124 free(plugin->runtime_funcs_symbol);
1125 for (i = 0; i < plugin->num_ext_points; i++) {
1126 free_ext_point_content(plugin->ext_points + i);
1128 free(plugin->ext_points);
1129 for (i = 0; i < plugin->num_extensions; i++) {
1130 free_extension_content(plugin->extensions + i);
1131 if (plugin->extensions[i].configuration != NULL) {
1132 free_cfg_element_content(plugin->extensions[i].configuration);
1133 free(plugin->extensions[i].configuration);
1136 free(plugin->extensions);
1141 * Frees any memory allocated for a registered plug-in.
1143 * @param context the plug-in context
1144 * @param plugin the plug-in to be freed
1146 static void free_registered_plugin(cp_context_t *context, cp_plugin_t *plugin) {
1147 assert(context != NULL);
1148 assert(plugin != NULL);
1150 // Release plug-in information
1151 cpi_release_info(context, plugin->plugin);
1153 // Release data structures
1154 if (plugin->importing != NULL) {
1155 assert(list_isempty(plugin->importing));
1156 list_destroy(plugin->importing);
1158 assert(plugin->imported == NULL);
1164 * Uninstalls a plug-in associated with the specified hash node.
1166 * @param context the plug-in context
1167 * @param node the hash node of the plug-in to be uninstalled
1169 static void uninstall_plugin(cp_context_t *context, hnode_t *node) {
1170 cp_plugin_t *plugin;
1171 cpi_plugin_event_t event;
1173 // Check if already uninstalled
1174 plugin = (cp_plugin_t *) hnode_get(node);
1175 if (plugin->state <= CP_PLUGIN_UNINSTALLED) {
1176 // TODO: Is this possible state?
1180 // Make sure the plug-in is not in resolved state
1181 unresolve_plugin(context, plugin);
1182 assert(plugin->state == CP_PLUGIN_INSTALLED);
1184 // Plug-in uninstalled
1185 event.plugin_id = plugin->plugin->identifier;
1186 event.old_state = plugin->state;
1187 event.new_state = plugin->state = CP_PLUGIN_UNINSTALLED;
1188 cpi_deliver_event(context, &event);
1190 // Unregister extension objects
1191 unregister_extensions(context, plugin->plugin);
1193 // Unregister the plug-in
1194 hash_delete_free(context->env->plugins, node);
1196 // Free the plug-in data structures
1197 free_registered_plugin(context, plugin);
1200 CP_C_API cp_status_t cp_uninstall_plugin(cp_context_t *context, const char *id) {
1202 cp_status_t status = CP_OK;
1204 CHECK_NOT_NULL(context);
1207 // Look up and unload the plug-in
1208 cpi_lock_context(context);
1209 cpi_check_invocation(context, CPI_CF_ANY, __func__);
1210 node = hash_lookup(context->env->plugins, id);
1212 uninstall_plugin(context, node);
1214 cpi_warnf(context, N_("Unknown plug-in %s could not be uninstalled."), id);
1215 status = CP_ERR_UNKNOWN;
1217 cpi_unlock_context(context);
1222 CP_C_API void cp_uninstall_plugins(cp_context_t *context) {
1226 CHECK_NOT_NULL(context);
1228 cpi_lock_context(context);
1229 cpi_check_invocation(context, CPI_CF_ANY, __func__);
1230 cp_stop_plugins(context);
1232 hash_scan_begin(&scan, context->env->plugins);
1233 if ((node = hash_scan_next(&scan)) != NULL) {
1234 uninstall_plugin(context, node);
1239 cpi_unlock_context(context);