Imported Upstream version 0.9.1
[platform/upstream/iotivity.git] / service / protocol-plugin / lib / cpluff / libcpluff / psymbol.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  * Dynamic plug-in symbols
26  */
27
28 #ifdef __linux__
29 #ifndef _POSIX_C_SOURCE
30 // Defining _POSIX_C_SOURCE macro with 200809L (or greater) as value
31 // causes header files to expose definitions
32 // corresponding to the POSIX.1-2008 base
33 // specification (excluding the XSI extension).
34 // For POSIX.1-2008 base specification,
35 // Refer http://pubs.opengroup.org/stage7tc1/
36 //
37 // For this specific file, see use of strndup,
38 // Refer http://man7.org/linux/man-pages/man3/strdup.3.html
39 #define _POSIX_C_SOURCE 200809L
40 #endif
41 #endif
42
43 #include <stdlib.h>
44 #include <string.h>
45 #include <assert.h>
46 #include "../kazlib/hash.h"
47 #include "cpluff.h"
48 #include "defines.h"
49 #include "internal.h"
50 #include "util.h"
51
52
53 /* ------------------------------------------------------------------------
54  * Data structures
55  * ----------------------------------------------------------------------*/
56
57 /// Information about symbol providing plug-in
58 typedef struct symbol_provider_info_t {
59         
60         // The providing plug-in
61         cp_plugin_t *plugin;
62         
63         // Whether there is also an import dependency for the plug-in
64         int imported;
65         
66         // Total symbol usage count
67         int usage_count;
68         
69 } symbol_provider_info_t;
70
71 /// Information about used symbol
72 typedef struct symbol_info_t {
73
74         // Symbol usage count
75         int usage_count;
76         
77         // Information about providing plug-in
78         symbol_provider_info_t *provider_info;
79         
80 } symbol_info_t;
81
82
83 /* ------------------------------------------------------------------------
84  * Function definitions
85  * ----------------------------------------------------------------------*/
86
87 CP_C_API cp_status_t cp_define_symbol(cp_context_t *context, const char *name, void *ptr) {
88         cp_status_t status = CP_OK;
89         
90         CHECK_NOT_NULL(context);
91         CHECK_NOT_NULL(name);
92 //      CHECK_NOT_NULL(ptr);
93         if (context->plugin == NULL) {
94                 cpi_fatalf(_("Only plug-ins can define context specific symbols."));
95         }
96         
97         cpi_lock_context(context);
98         cpi_check_invocation(context, CPI_CF_LOGGER | CPI_CF_LISTENER, __func__);
99         do {
100                 char *n;
101                 
102                 // Create a symbol hash if necessary
103                 if (context->plugin->defined_symbols == NULL) {
104                         if ((context->plugin->defined_symbols = hash_create(HASHCOUNT_T_MAX, (int (*)(const void *, const void *)) strcmp, NULL)) == NULL) {
105                                 status = CP_ERR_RESOURCE;
106                                 break;
107                         }
108                 }
109                 
110                 // Check for a previously defined symbol
111                 if (hash_lookup(context->plugin->defined_symbols, name) != NULL) {
112                         status = CP_ERR_CONFLICT;
113                         break;
114                 }
115
116                 // Insert the symbol into the symbol hash
117                 n = strdup(name);
118                 if (n == NULL || !hash_alloc_insert(context->plugin->defined_symbols, n, ptr)) {
119                         free(n);
120                         status = CP_ERR_RESOURCE;
121                         break;
122                 } 
123
124         } while (0);
125         
126         // Report error
127         if (status != CP_OK) {
128                 switch (status) {
129                         case CP_ERR_RESOURCE:
130                                 cpi_errorf(context, N_("Plug-in %s could not define symbol %s due to insufficient memory."), context->plugin->plugin->identifier, name);
131                                 break;
132                         case CP_ERR_CONFLICT:
133                                 cpi_errorf(context, N_("Plug-in %s tried to redefine symbol %s."), context->plugin->plugin->identifier, name);
134                                 break;
135                         default:
136                                 break;
137                 }
138         }
139         cpi_unlock_context(context);
140         
141         return status;
142 }
143
144 CP_C_API void * cp_resolve_symbol(cp_context_t *context, const char *id, const char *name, cp_status_t *error) {
145         cp_status_t status = CP_OK;
146         int error_reported = 1;
147         hnode_t *node;
148         void *symbol = NULL;
149         symbol_info_t *symbol_info = NULL;
150         symbol_provider_info_t *provider_info = NULL;
151         cp_plugin_t *pp = NULL;
152
153         CHECK_NOT_NULL(context);
154         CHECK_NOT_NULL(id);
155         CHECK_NOT_NULL(name);
156         
157         // Resolve the symbol
158         cpi_lock_context(context);
159         cpi_check_invocation(context, CPI_CF_LOGGER | CPI_CF_LISTENER | CPI_CF_STOP, __func__);
160         do {
161
162                 // Allocate space for symbol hashes, if necessary
163                 if (context->resolved_symbols == NULL) {
164                         context->resolved_symbols = hash_create(HASHCOUNT_T_MAX, cpi_comp_ptr, cpi_hashfunc_ptr);
165                 }
166                 if (context->symbol_providers == NULL) {
167                         context->symbol_providers = hash_create(HASHCOUNT_T_MAX, cpi_comp_ptr, cpi_hashfunc_ptr);
168                 }
169                 if (context->resolved_symbols == NULL
170                         || context->symbol_providers == NULL) {
171                         status = CP_ERR_RESOURCE;
172                         break;
173                 }
174
175                 // Look up the symbol defining plug-in
176                 node = hash_lookup(context->env->plugins, id);
177                 if (node == NULL) {
178                         cpi_warnf(context, N_("Symbol %s in unknown plug-in %s could not be resolved."), name, id);
179                         status = CP_ERR_UNKNOWN;
180                         break;
181                 }
182                 pp = hnode_get(node);
183
184                 // Make sure the plug-in has been started
185                 if ((status = cpi_start_plugin(context, pp)) != CP_OK) {
186                         cpi_errorf(context, N_("Symbol %s in plug-in %s could not be resolved because the plug-in could not be started."), name, id);
187                         error_reported = 1;
188                         break;
189                 }
190
191                 // Check for a context specific symbol
192                 if (pp->defined_symbols != NULL && (node = hash_lookup(pp->defined_symbols, name)) != NULL) {
193                         symbol = hnode_get(node);
194                 }
195
196                 // Fall back to global symbols, if necessary
197                 if (symbol == NULL && pp->runtime_lib != NULL) {
198                         symbol = DLSYM(pp->runtime_lib, name);
199                 }
200                 if (symbol == NULL) {
201                         const char *error = DLERROR();
202                         if (error == NULL) {
203                                 error = _("Unspecified error.");
204                         }
205                         cpi_warnf(context, N_("Symbol %s in plug-in %s could not be resolved: %s"), name, id, error);
206                         status = CP_ERR_UNKNOWN;
207                         break;
208                 }
209
210                 // Lookup or initialize symbol provider information
211                 if ((node = hash_lookup(context->symbol_providers, pp)) != NULL) {
212                         provider_info = hnode_get(node);
213                 } else {
214                         if ((provider_info = malloc(sizeof(symbol_provider_info_t))) == NULL) {
215                                 status = CP_ERR_RESOURCE;
216                                 break;
217                         }
218                         memset(provider_info, 0, sizeof(symbol_provider_info_t));
219                         provider_info->plugin = pp;
220                         provider_info->imported = (context->plugin == NULL || cpi_ptrset_contains(context->plugin->imported, pp));
221                         if (!hash_alloc_insert(context->symbol_providers, pp, provider_info)) {
222                                 status = CP_ERR_RESOURCE;
223                                 break;
224                         }
225                 }
226                 
227                 // Lookup or initialize symbol information
228                 if ((node = hash_lookup(context->resolved_symbols, symbol)) != NULL) {
229                         symbol_info = hnode_get(node);
230                 } else {
231                         if ((symbol_info = malloc(sizeof(symbol_info_t))) == NULL) {
232                                 status = CP_ERR_RESOURCE;
233                                 break;
234                         }
235                         memset(symbol_info, 0, sizeof(symbol_info_t));
236                         symbol_info->provider_info = provider_info;
237                         if (!hash_alloc_insert(context->resolved_symbols, symbol, symbol_info)) {
238                                 status = CP_ERR_RESOURCE;
239                                 break;
240                         }
241                 }
242                 
243                 // Add dependencies (for plug-in)
244                 if (provider_info != NULL
245                         && !provider_info->imported
246                         && provider_info->usage_count == 0) {
247                         if (!cpi_ptrset_add(context->plugin->imported, pp)) {
248                                 status = CP_ERR_RESOURCE;
249                                 break;
250                         }
251                         if (!cpi_ptrset_add(pp->importing, context->plugin)) {
252                                 cpi_ptrset_remove(context->plugin->imported, pp);
253                                 status = CP_ERR_RESOURCE;
254                                 break;
255                         }
256                         cpi_debugf(context, "A dynamic dependency was created from plug-in %s to plug-in %s.", context->plugin->plugin->identifier, pp->plugin->identifier);
257                 }
258                 
259                 // Increase usage counts
260                 symbol_info->usage_count++;
261                 provider_info->usage_count++;
262
263                 if (cpi_is_logged(context, CP_LOG_DEBUG)) {
264                         char owner[64];
265                         /* TRANSLATORS: First %s is the context owner */
266                         cpi_debugf(context, "%s resolved symbol %s defined by plug-in %s.", cpi_context_owner(context, owner, sizeof(owner)), name, id);
267                 }
268         } while (0);
269
270         // Clean up
271         if (symbol_info != NULL && symbol_info->usage_count == 0) {
272                 if ((node = hash_lookup(context->resolved_symbols, symbol)) != NULL) {
273                         hash_delete_free(context->resolved_symbols, node);
274                 }
275                 free(symbol_info);
276         }
277         if (provider_info != NULL && provider_info->usage_count == 0) {
278                 if ((node = hash_lookup(context->symbol_providers, pp)) != NULL) {
279                         hash_delete_free(context->symbol_providers, node);
280                 }
281                 free(provider_info);
282         }
283
284         // Report insufficient memory error
285         if (status == CP_ERR_RESOURCE && !error_reported) {
286                 cpi_errorf(context, N_("Symbol %s in plug-in %s could not be resolved due to insufficient memory."), name, id);
287         }
288         cpi_unlock_context(context);
289
290         // Return error code
291         if (error != NULL) {
292                 *error = status;
293         }
294         
295         // Return symbol
296         return symbol;
297 }
298
299 CP_C_API void cp_release_symbol(cp_context_t *context, const void *ptr) {
300         hnode_t *node;
301         symbol_info_t *symbol_info;
302         symbol_provider_info_t *provider_info;
303         
304         CHECK_NOT_NULL(context);
305         CHECK_NOT_NULL(ptr);
306
307         cpi_lock_context(context);
308         cpi_check_invocation(context, CPI_CF_LOGGER | CPI_CF_LISTENER, __func__);
309         do {
310
311                 // Look up the symbol
312                 if ((node = hash_lookup(context->resolved_symbols, ptr)) == NULL) {
313                         cpi_errorf(context, N_("Could not release unknown symbol at address %p."), ptr);
314                         break;
315                 }
316                 symbol_info = hnode_get(node);
317                 provider_info = symbol_info->provider_info;
318         
319                 // Decrease usage count
320                 assert(symbol_info->usage_count > 0);
321                 symbol_info->usage_count--;
322                 assert(provider_info->usage_count > 0);
323                 provider_info->usage_count--;
324         
325                 // Check if the symbol is not being used anymore
326                 if (symbol_info->usage_count == 0) {
327                         hash_delete_free(context->resolved_symbols, node);
328                         free(symbol_info);
329                         if (cpi_is_logged(context, CP_LOG_DEBUG)) {
330                                 char owner[64];
331                                 /* TRANSLATORS: First %s is the context owner */
332                                 cpi_debugf(context, _("%s released the symbol at address %p defined by plug-in %s."), cpi_context_owner(context, owner, sizeof(owner)), ptr, provider_info->plugin->plugin->identifier);
333                         }
334                 }
335         
336                 // Check if the symbol providing plug-in is not being used anymore
337                 if (provider_info->usage_count == 0) {
338                         node = hash_lookup(context->symbol_providers, provider_info->plugin);
339                         assert(node != NULL);
340                         hash_delete_free(context->symbol_providers, node);
341                         if (!provider_info->imported) {
342                                 cpi_ptrset_remove(context->plugin->imported, provider_info->plugin);
343                                 cpi_ptrset_remove(provider_info->plugin->importing, context->plugin);
344                                 cpi_debugf(context, _("A dynamic dependency from plug-in %s to plug-in %s was removed."), context->plugin->plugin->identifier, provider_info->plugin->plugin->identifier);
345                         }
346                         free(provider_info);
347                 }
348                 
349         } while (0);
350         cpi_unlock_context(context);
351 }