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 * Plug-in context implementation
33 #include "../kazlib/list.h"
42 /* ------------------------------------------------------------------------
44 * ----------------------------------------------------------------------*/
47 static list_t *contexts = NULL;
50 /* ------------------------------------------------------------------------
51 * Function definitions
52 * ----------------------------------------------------------------------*/
56 static void free_plugin_env(cp_plugin_env_t *env) {
59 // Free environment data
60 if (env->plugin_listeners != NULL) {
61 cpi_unregister_plisteners(env->plugin_listeners, NULL);
62 list_destroy(env->plugin_listeners);
63 env->plugin_listeners = NULL;
65 if (env->loggers != NULL) {
66 cpi_unregister_loggers(env->loggers, NULL);
67 list_destroy(env->loggers);
70 if (env->plugin_dirs != NULL) {
71 list_process(env->plugin_dirs, NULL, cpi_process_free_ptr);
72 list_destroy(env->plugin_dirs);
73 env->plugin_dirs = NULL;
75 if (env->infos != NULL) {
76 assert(hash_isempty(env->infos));
77 hash_destroy(env->infos);
80 if (env->plugins != NULL) {
81 assert(hash_isempty(env->plugins));
82 hash_destroy(env->plugins);
85 if (env->started_plugins != NULL) {
86 assert(list_isempty(env->started_plugins));
87 list_destroy(env->started_plugins);
88 env->started_plugins = NULL;
90 if (env->ext_points != NULL) {
91 assert(hash_isempty(env->ext_points));
92 hash_destroy(env->ext_points);
94 if (env->extensions != NULL) {
95 assert(hash_isempty(env->extensions));
96 hash_destroy(env->extensions);
98 if (env->run_funcs != NULL) {
99 assert(list_isempty(env->run_funcs));
100 list_destroy(env->run_funcs);
105 if (env->mutex != NULL) {
106 cpi_destroy_mutex(env->mutex);
115 CP_HIDDEN void cpi_free_context(cp_context_t *context) {
116 assert(context != NULL);
118 // Free environment if this is the client program context
119 if (context->plugin == NULL && context->env != NULL) {
120 free_plugin_env(context->env);
123 // Destroy symbol lists
124 if (context->resolved_symbols != NULL) {
125 assert(hash_isempty(context->resolved_symbols));
126 hash_destroy(context->resolved_symbols);
128 if (context->symbol_providers != NULL) {
129 assert(hash_isempty(context->symbol_providers));
130 hash_destroy(context->symbol_providers);
137 CP_HIDDEN cp_context_t * cpi_new_context(cp_plugin_t *plugin, cp_plugin_env_t *env, cp_status_t *error) {
138 cp_context_t *context = NULL;
139 cp_status_t status = CP_OK;
142 assert(error != NULL);
146 // Allocate memory for the context
147 if ((context = malloc(sizeof(cp_context_t))) == NULL) {
148 status = CP_ERR_RESOURCE;
152 // Initialize context
153 context->plugin = plugin;
155 context->resolved_symbols = NULL;
156 context->symbol_providers = NULL;
160 // Free context on error
161 if (status != CP_OK && context != NULL) {
170 CP_C_API cp_context_t * cp_create_context(cp_status_t *error) {
171 cp_plugin_env_t *env = NULL;
172 cp_context_t *context = NULL;
173 cp_status_t status = CP_OK;
175 // Initialize internal state
178 // Allocate memory for the plug-in environment
179 if ((env = malloc(sizeof(cp_plugin_env_t))) == NULL) {
180 status = CP_ERR_RESOURCE;
184 // Initialize plug-in environment
185 memset(env, 0, sizeof(cp_plugin_env_t));
187 env->mutex = cpi_create_mutex();
191 env->plugin_listeners = list_create(LISTCOUNT_T_MAX);
192 env->loggers = list_create(LISTCOUNT_T_MAX);
193 env->log_min_severity = CP_LOG_NONE;
194 env->plugin_dirs = list_create(LISTCOUNT_T_MAX);
195 env->infos = hash_create(HASHCOUNT_T_MAX, cpi_comp_ptr, cpi_hashfunc_ptr);
196 env->plugins = hash_create(HASHCOUNT_T_MAX,
197 (int (*)(const void *, const void *)) strcmp, NULL);
198 env->started_plugins = list_create(LISTCOUNT_T_MAX);
199 env->ext_points = hash_create(HASHCOUNT_T_MAX,
200 (int (*)(const void *, const void *)) strcmp, NULL);
201 env->extensions = hash_create(HASHCOUNT_T_MAX,
202 (int (*)(const void *, const void *)) strcmp, NULL);
203 env->run_funcs = list_create(LISTCOUNT_T_MAX);
204 env->run_wait = NULL;
205 if (env->plugin_listeners == NULL
206 || env->loggers == NULL
208 || env->mutex == NULL
210 || env->plugin_dirs == NULL
211 || env->infos == NULL
212 || env->plugins == NULL
213 || env->started_plugins == NULL
214 || env->ext_points == NULL
215 || env->extensions == NULL
216 || env->run_funcs == NULL) {
217 status = CP_ERR_RESOURCE;
221 // Create the plug-in context
222 if ((context = cpi_new_context(NULL, env, &status)) == NULL) {
227 // Create a context list, if necessary, and add context to the list
228 cpi_lock_framework();
229 if (contexts == NULL) {
230 if ((contexts = list_create(LISTCOUNT_T_MAX)) == NULL) {
231 status = CP_ERR_RESOURCE;
234 if (status == CP_OK) {
237 if ((node = lnode_create(context)) == NULL) {
238 status = CP_ERR_RESOURCE;
240 list_append(contexts, node);
243 cpi_unlock_framework();
247 // Release resources on failure
248 if (status != CP_OK) {
250 free_plugin_env(env);
252 if (context != NULL) {
253 cpi_free_context(context);
258 // Return the final status
263 // Return the context (or NULL on failure)
267 CP_C_API void cp_destroy_context(cp_context_t *context) {
268 CHECK_NOT_NULL(context);
269 if (context->plugin != NULL) {
270 cpi_fatalf(_("Only the main program can destroy a plug-in context."));
274 cpi_lock_context(context);
275 cpi_check_invocation(context, CPI_CF_ANY, __func__);
276 cpi_unlock_context(context);
279 assert(context->env->mutex == NULL || !cpi_is_mutex_locked(context->env->mutex));
281 assert(!context->env->locked);
284 // Remove context from the context list
285 cpi_lock_framework();
286 if (contexts != NULL) {
289 if ((node = list_find(contexts, context, cpi_comp_ptr)) != NULL) {
290 list_delete(contexts, node);
294 cpi_unlock_framework();
296 // Unload all plug-ins
297 cp_uninstall_plugins(context);
299 // Release remaining information objects
300 cpi_release_infos(context);
303 cpi_free_context(context);
306 CP_HIDDEN void cpi_destroy_all_contexts(void) {
307 cpi_lock_framework();
308 if (contexts != NULL) {
311 while ((node = list_last(contexts)) != NULL) {
312 cpi_unlock_framework();
313 cp_destroy_context(lnode_get(node));
314 cpi_lock_framework();
316 list_destroy(contexts);
319 cpi_unlock_framework();
323 // Plug-in directories
325 CP_C_API cp_status_t cp_register_pcollection(cp_context_t *context, const char *dir) {
327 lnode_t *node = NULL;
328 cp_status_t status = CP_OK;
330 CHECK_NOT_NULL(context);
333 cpi_lock_context(context);
334 cpi_check_invocation(context, CPI_CF_ANY, __func__);
337 // Check if directory has already been registered
338 if (list_find(context->env->plugin_dirs, dir, (int (*)(const void *, const void *)) strcmp) != NULL) {
342 // Allocate resources
343 d = malloc(sizeof(char) * (strlen(dir) + 1));
344 node = lnode_create(d);
345 if (d == NULL || node == NULL) {
346 status = CP_ERR_RESOURCE;
350 // Register directory
352 list_append(context->env->plugin_dirs, node);
356 // Report error or success
357 if (status != CP_OK) {
358 cpi_errorf(context, N_("The plug-in collection in path %s could not be registered due to insufficient memory."), dir);
360 cpi_debugf(context, N_("The plug-in collection in path %s was registered."), dir);
362 cpi_unlock_context(context);
364 // Release resources on failure
365 if (status != CP_OK) {
377 CP_C_API void cp_unregister_pcollection(cp_context_t *context, const char *dir) {
381 CHECK_NOT_NULL(context);
384 cpi_lock_context(context);
385 cpi_check_invocation(context, CPI_CF_ANY, __func__);
386 node = list_find(context->env->plugin_dirs, dir, (int (*)(const void *, const void *)) strcmp);
389 list_delete(context->env->plugin_dirs, node);
393 cpi_debugf(context, N_("The plug-in collection in path %s was unregistered."), dir);
394 cpi_unlock_context(context);
397 CP_C_API void cp_unregister_pcollections(cp_context_t *context) {
398 CHECK_NOT_NULL(context);
399 cpi_lock_context(context);
400 cpi_check_invocation(context, CPI_CF_ANY, __func__);
401 list_process(context->env->plugin_dirs, NULL, cpi_process_free_ptr);
402 cpi_debug(context, N_("All plug-in collections were unregistered."));
403 cpi_unlock_context(context);
409 CP_C_API void cp_set_context_args(cp_context_t *ctx, char **argv) {
413 CHECK_NOT_NULL(argv);
414 for (argc = 0; argv[argc] != NULL; argc++);
416 cpi_fatalf(_("At least one startup argument must be given in call to function %s."), __func__);
418 cpi_lock_context(ctx);
419 ctx->env->argc = argc;
420 ctx->env->argv = argv;
421 cpi_unlock_context(ctx);
424 CP_C_API char **cp_get_context_args(cp_context_t *ctx, int *argc) {
428 cpi_lock_context(ctx);
430 *argc = ctx->env->argc;
432 argv = ctx->env->argv;
433 cpi_unlock_context(ctx);
438 // Checking API call invocation
440 CP_HIDDEN void cpi_check_invocation(cp_context_t *ctx, int funcmask, const char *func) {
442 assert(funcmask != 0);
443 assert(func != NULL);
444 assert(cpi_is_context_locked(ctx));
445 if ((funcmask & CPI_CF_LOGGER)
446 &&ctx->env->in_logger_invocation) {
447 cpi_fatalf(_("Function %s was called from within a logger invocation."), func);
449 if ((funcmask & CPI_CF_LISTENER)
450 && ctx->env->in_event_listener_invocation) {
451 cpi_fatalf(_("Function %s was called from within an event listener invocation."), func);
453 if ((funcmask & CPI_CF_START)
454 && ctx->env->in_start_func_invocation) {
455 cpi_fatalf(_("Function %s was called from within a plug-in start function invocation."), func);
457 if ((funcmask & CPI_CF_STOP)
458 && ctx->env->in_stop_func_invocation) {
459 cpi_fatalf(_("Function %s was called from within a plug-in stop function invocation."), func);
461 if (ctx->env->in_create_func_invocation) {
462 cpi_fatalf(_("Function %s was called from within a plug-in create function invocation."), func);
464 if (ctx->env->in_destroy_func_invocation) {
465 cpi_fatalf(_("Function %s was called from within a plug-in destroy function invocation."), func);
472 #if defined(CP_THREADS) || !defined(NDEBUG)
474 CP_HIDDEN void cpi_lock_context(cp_context_t *context) {
475 #if defined(CP_THREADS)
476 cpi_lock_mutex(context->env->mutex);
477 #elif !defined(NDEBUG)
478 context->env->locked++;
482 CP_HIDDEN void cpi_unlock_context(cp_context_t *context) {
483 #if defined(CP_THREADS)
484 cpi_unlock_mutex(context->env->mutex);
485 #elif !defined(NDEBUG)
486 assert(context->env->locked > 0);
487 context->env->locked--;
491 CP_HIDDEN void cpi_wait_context(cp_context_t *context) {
492 #if defined(CP_THREADS)
493 cpi_wait_mutex(context->env->mutex);
494 #elif !defined(NDEBUG)
495 assert(context->env->locked > 0);
500 CP_HIDDEN void cpi_signal_context(cp_context_t *context) {
501 #if defined(CP_THREADS)
502 cpi_signal_mutex(context->env->mutex);
503 #elif !defined(NDEBUG)
504 assert(context->env->locked > 0);
511 CP_HIDDEN char *cpi_context_owner(cp_context_t *ctx, char *name, size_t size) {
512 if (ctx->plugin != NULL) {
513 /* TRANSLATORS: The context owner (when it is a plug-in) used in some strings.
514 Search for "context owner" to find these strings. */
515 snprintf(name, size, _("Plug-in %s"), ctx->plugin->plugin->identifier);
517 /* TRANSLATORS: The context owner (when it is the main program) used in some strings.
518 Search for "context owner" to find these strings. */
519 strncpy(name, _("The main program"), size);
522 strcpy(name + size - 4, "...");