iotivity 0.9.0
[platform/upstream/iotivity.git] / service / protocol-plugin / lib / cpluff / libcpluff / context.c
1 /*-------------------------------------------------------------------------
2  * C-Pluff, a plug-in framework for C
3  * Copyright 2007 Johannes Lehtinen
4  * 
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:
11  *
12  * The above copyright notice and this permission notice shall be included
13  * in all copies or substantial portions of the Software.
14  *
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  *-----------------------------------------------------------------------*/
23
24 /** @file
25  * Plug-in context implementation
26  */
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <assert.h>
31 #include <stdarg.h>
32 #include <string.h>
33 #include "../kazlib/list.h"
34 #include "cpluff.h"
35 #include "util.h"
36 #ifdef CP_THREADS
37 #include "thread.h"
38 #endif
39 #include "internal.h"
40
41
42 /* ------------------------------------------------------------------------
43  * Variables
44  * ----------------------------------------------------------------------*/
45
46 /// Existing contexts
47 static list_t *contexts = NULL;
48
49
50 /* ------------------------------------------------------------------------
51  * Function definitions
52  * ----------------------------------------------------------------------*/
53
54 // Generic 
55
56 static void free_plugin_env(cp_plugin_env_t *env) {
57         assert(env != NULL);
58         
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;
64         }
65         if (env->loggers != NULL) {
66                 cpi_unregister_loggers(env->loggers, NULL);
67                 list_destroy(env->loggers);
68                 env->loggers = NULL;
69         }
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;
74         }
75         if (env->infos != NULL) {
76                 assert(hash_isempty(env->infos));
77                 hash_destroy(env->infos);
78                 env->infos = NULL;
79         }
80         if (env->plugins != NULL) {
81                 assert(hash_isempty(env->plugins));
82                 hash_destroy(env->plugins);
83                 env->plugins = NULL;
84         }
85         if (env->started_plugins != NULL) {
86                 assert(list_isempty(env->started_plugins));
87                 list_destroy(env->started_plugins);
88                 env->started_plugins = NULL;
89         }
90         if (env->ext_points != NULL) {
91                 assert(hash_isempty(env->ext_points));
92                 hash_destroy(env->ext_points);
93         }
94         if (env->extensions != NULL) {
95                 assert(hash_isempty(env->extensions));
96                 hash_destroy(env->extensions);
97         }
98         if (env->run_funcs != NULL) {
99                 assert(list_isempty(env->run_funcs));
100                 list_destroy(env->run_funcs);
101         }
102         
103         // Destroy mutex 
104 #ifdef CP_THREADS
105         if (env->mutex != NULL) {
106                 cpi_destroy_mutex(env->mutex);
107         }
108 #endif
109
110         // Free environment
111         free(env);
112
113 }
114
115 CP_HIDDEN void cpi_free_context(cp_context_t *context) {
116         assert(context != NULL);
117         
118         // Free environment if this is the client program context
119         if (context->plugin == NULL && context->env != NULL) {
120                 free_plugin_env(context->env);
121         }
122         
123         // Destroy symbol lists
124         if (context->resolved_symbols != NULL) {
125                 assert(hash_isempty(context->resolved_symbols));
126                 hash_destroy(context->resolved_symbols);
127         }
128         if (context->symbol_providers != NULL) {
129                 assert(hash_isempty(context->symbol_providers));
130                 hash_destroy(context->symbol_providers);
131         }
132
133         // Free context
134         free(context);  
135 }
136
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;
140         
141         assert(env != NULL);
142         assert(error != NULL);
143         
144         do {
145                 
146                 // Allocate memory for the context
147                 if ((context = malloc(sizeof(cp_context_t))) == NULL) {
148                         status = CP_ERR_RESOURCE;
149                         break;
150                 }
151                 
152                 // Initialize context
153                 context->plugin = plugin;
154                 context->env = env;
155                 context->resolved_symbols = NULL;
156                 context->symbol_providers = NULL;
157                 
158         } while (0);
159         
160         // Free context on error
161         if (status != CP_OK && context != NULL) {
162                 free(context);
163                 context = NULL;
164         }
165         
166         *error = status;
167         return context;
168 }
169
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;
174
175         // Initialize internal state
176         do {
177         
178                 // Allocate memory for the plug-in environment
179                 if ((env = malloc(sizeof(cp_plugin_env_t))) == NULL) {
180                         status = CP_ERR_RESOURCE;
181                         break;
182                 }
183         
184                 // Initialize plug-in environment
185                 memset(env, 0, sizeof(cp_plugin_env_t));
186 #ifdef CP_THREADS
187                 env->mutex = cpi_create_mutex();
188 #endif
189                 env->argc = 0;
190                 env->argv = NULL;
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
207 #ifdef CP_THREADS
208                         || env->mutex == NULL
209 #endif
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;
218                         break;
219                 }
220                 
221                 // Create the plug-in context
222                 if ((context = cpi_new_context(NULL, env, &status)) == NULL) {
223                         break;
224                 }
225                 env = NULL;
226
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;
232                         }
233                 }
234                 if (status == CP_OK) {
235                         lnode_t *node;
236                         
237                         if ((node = lnode_create(context)) == NULL) {
238                                 status = CP_ERR_RESOURCE;
239                         } else {
240                                 list_append(contexts, node);
241                         }
242                 }
243                 cpi_unlock_framework();
244                 
245         } while (0);
246         
247         // Release resources on failure 
248         if (status != CP_OK) {
249                 if (env != NULL) {
250                         free_plugin_env(env);
251                 }
252                 if (context != NULL) {
253                         cpi_free_context(context);
254                 }
255                 context = NULL;
256         }
257
258         // Return the final status 
259         if (error != NULL) {
260                 *error = status;
261         }
262         
263         // Return the context (or NULL on failure) 
264         return context;
265 }
266
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."));
271         }
272
273         // Check invocation
274         cpi_lock_context(context);
275         cpi_check_invocation(context, CPI_CF_ANY, __func__);
276         cpi_unlock_context(context);
277
278 #ifdef CP_THREADS
279         assert(context->env->mutex == NULL || !cpi_is_mutex_locked(context->env->mutex));
280 #else
281         assert(!context->env->locked);
282 #endif
283
284         // Remove context from the context list
285         cpi_lock_framework();
286         if (contexts != NULL) {
287                 lnode_t *node;
288                 
289                 if ((node = list_find(contexts, context, cpi_comp_ptr)) != NULL) {
290                         list_delete(contexts, node);
291                         lnode_destroy(node);
292                 }
293         }
294         cpi_unlock_framework();
295
296         // Unload all plug-ins 
297         cp_uninstall_plugins(context);
298
299         // Release remaining information objects
300         cpi_release_infos(context);
301         
302         // Free context
303         cpi_free_context(context);
304 }
305
306 CP_HIDDEN void cpi_destroy_all_contexts(void) {
307         cpi_lock_framework();
308         if (contexts != NULL) {
309                 lnode_t *node;
310                 
311                 while ((node = list_last(contexts)) != NULL) {
312                         cpi_unlock_framework();
313                         cp_destroy_context(lnode_get(node));
314                         cpi_lock_framework();
315                 }
316                 list_destroy(contexts);
317                 contexts = NULL;
318         }
319         cpi_unlock_framework();
320 }
321
322
323 // Plug-in directories 
324
325 CP_C_API cp_status_t cp_register_pcollection(cp_context_t *context, const char *dir) {
326         char *d = NULL;
327         lnode_t *node = NULL;
328         cp_status_t status = CP_OK;
329         
330         CHECK_NOT_NULL(context);
331         CHECK_NOT_NULL(dir);
332         
333         cpi_lock_context(context);
334         cpi_check_invocation(context, CPI_CF_ANY, __func__);
335         do {
336         
337                 // Check if directory has already been registered 
338                 if (list_find(context->env->plugin_dirs, dir, (int (*)(const void *, const void *)) strcmp) != NULL) {
339                         break;
340                 }
341         
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;
347                         break;
348                 }
349         
350                 // Register directory 
351                 strcpy(d, dir);
352                 list_append(context->env->plugin_dirs, node);
353                 
354         } while (0);
355
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);
359         } else {
360                 cpi_debugf(context, N_("The plug-in collection in path %s was registered."), dir);
361         }
362         cpi_unlock_context(context);
363
364         // Release resources on failure 
365         if (status != CP_OK) {  
366                 if (d != NULL) {
367                         free(d);
368                 }
369                 if (node != NULL) {
370                         lnode_destroy(node);
371                 }
372         }
373         
374         return status;
375 }
376
377 CP_C_API void cp_unregister_pcollection(cp_context_t *context, const char *dir) {
378         char *d;
379         lnode_t *node;
380         
381         CHECK_NOT_NULL(context);
382         CHECK_NOT_NULL(dir);
383         
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);
387         if (node != NULL) {
388                 d = lnode_get(node);
389                 list_delete(context->env->plugin_dirs, node);
390                 lnode_destroy(node);
391                 free(d);
392         }
393         cpi_debugf(context, N_("The plug-in collection in path %s was unregistered."), dir);
394         cpi_unlock_context(context);
395 }
396
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);
404 }
405
406
407 // Startup arguments
408
409 CP_C_API void cp_set_context_args(cp_context_t *ctx, char **argv) {
410         int argc;
411         
412         CHECK_NOT_NULL(ctx);
413         CHECK_NOT_NULL(argv);
414         for (argc = 0; argv[argc] != NULL; argc++);
415         if (argc < 1) {
416                 cpi_fatalf(_("At least one startup argument must be given in call to function %s."), __func__);
417         }
418         cpi_lock_context(ctx);
419         ctx->env->argc = argc;
420         ctx->env->argv = argv;
421         cpi_unlock_context(ctx);
422 }
423
424 CP_C_API char **cp_get_context_args(cp_context_t *ctx, int *argc) {
425         char **argv;
426         
427         CHECK_NOT_NULL(ctx);
428         cpi_lock_context(ctx);
429         if (argc != NULL) {
430                 *argc = ctx->env->argc;
431         }
432         argv = ctx->env->argv;
433         cpi_unlock_context(ctx);
434         return argv;
435 }
436
437
438 // Checking API call invocation
439
440 CP_HIDDEN void cpi_check_invocation(cp_context_t *ctx, int funcmask, const char *func) {
441         assert(ctx != NULL);
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);
448         }
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);
452         }
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);
456         }
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);
460         }
461         if (ctx->env->in_create_func_invocation) {
462                 cpi_fatalf(_("Function %s was called from within a plug-in create function invocation."), func);
463         }
464         if (ctx->env->in_destroy_func_invocation) {
465                 cpi_fatalf(_("Function %s was called from within a plug-in destroy function invocation."), func);
466         }
467 }
468
469
470 // Locking 
471
472 #if defined(CP_THREADS) || !defined(NDEBUG)
473
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++;
479 #endif
480 }
481
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--;
488 #endif
489 }
490
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);
496         assert(0);
497 #endif
498 }
499
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);
505 #endif
506 }
507
508
509 // Debug helpers
510
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);
516         } else {
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);
520         }
521         assert(size >= 4);
522         strcpy(name + size - 4, "...");
523         return name;
524 }
525
526 #endif