Commit | Line | Data |
---|---|---|
33b34c43 PP |
1 | /* |
2 | * plugin.c | |
3 | * | |
4 | * Babeltrace Plugin | |
5 | * | |
6 | * Copyright 2016 Jérémie Galarneau <jeremie.galarneau@efficios.com> | |
7 | * Copyright 2017 Philippe Proulx <pproulx@efficios.com> | |
8 | * | |
9 | * Author: Jérémie Galarneau <jeremie.galarneau@efficios.com> | |
10 | * | |
11 | * Permission is hereby granted, free of charge, to any person obtaining a copy | |
12 | * of this software and associated documentation files (the "Software"), to deal | |
13 | * in the Software without restriction, including without limitation the rights | |
14 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
15 | * copies of the Software, and to permit persons to whom the Software is | |
16 | * furnished to do so, subject to the following conditions: | |
17 | * | |
18 | * The above copyright notice and this permission notice shall be included in | |
19 | * all copies or substantial portions of the Software. | |
20 | * | |
21 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
22 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
23 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
24 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
25 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
26 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
27 | * SOFTWARE. | |
28 | */ | |
29 | ||
30 | #include <babeltrace/compiler.h> | |
31 | #include <babeltrace/ref.h> | |
32 | #include <babeltrace/plugin/plugin-dev.h> | |
33 | #include <babeltrace/plugin/plugin-internal.h> | |
34 | #include <babeltrace/component/component-class-internal.h> | |
35 | #include <string.h> | |
36 | #include <stdbool.h> | |
37 | #include <glib.h> | |
38 | #include <gmodule.h> | |
39 | #include <unistd.h> | |
40 | #include <stdlib.h> | |
41 | #include <sys/stat.h> | |
42 | #include <dirent.h> | |
43 | ||
44 | #define PLUGIN_SYMBOL_NAME "__bt_plugin_name" | |
45 | #define PLUGIN_SYMBOL_AUTHOR "__bt_plugin_author" | |
46 | #define PLUGIN_SYMBOL_LICENSE "__bt_plugin_license" | |
47 | #define PLUGIN_SYMBOL_INIT "__bt_plugin_init" | |
48 | #define PLUGIN_SYMBOL_EXIT "__bt_plugin_exit" | |
49 | #define PLUGIN_SYMBOL_DESCRIPTION "__bt_plugin_description" | |
50 | #define NATIVE_PLUGIN_SUFFIX ".so" | |
51 | #define NATIVE_PLUGIN_SUFFIX_LEN sizeof(NATIVE_PLUGIN_SUFFIX) | |
52 | #define LIBTOOL_PLUGIN_SUFFIX ".la" | |
53 | #define LIBTOOL_PLUGIN_SUFFIX_LEN sizeof(LIBTOOL_PLUGIN_SUFFIX) | |
54 | ||
55 | #define PLUGIN_SUFFIX_LEN max_t(size_t, sizeof(NATIVE_PLUGIN_SUFFIX), \ | |
56 | sizeof(LIBTOOL_PLUGIN_SUFFIX)) | |
57 | ||
58 | #define SECTION_BEGIN(_name) &__start_##_name | |
59 | #define SECTION_END(_name) &__stop_##_name | |
60 | #define SECTION_ELEMENT_COUNT(_name) (SECTION_END(_name) - SECTION_BEGIN(_name)) | |
61 | ||
62 | #define DECLARE_SECTION(_type, _name) \ | |
63 | extern _type const __start_##_name __attribute((weak)); \ | |
64 | extern _type const __stop_##_name __attribute((weak)) | |
65 | ||
66 | DECLARE_SECTION(bt_plugin_init_func, __bt_plugin_init_funcs); | |
67 | DECLARE_SECTION(bt_plugin_exit_func, __bt_plugin_exit_funcs); | |
68 | DECLARE_SECTION(const char *, __bt_plugin_names); | |
69 | DECLARE_SECTION(const char *, __bt_plugin_authors); | |
70 | DECLARE_SECTION(const char *, __bt_plugin_licenses); | |
71 | DECLARE_SECTION(const char *, __bt_plugin_descriptions); | |
72 | ||
73 | #define PRINT_SECTION(_printer, _name) \ | |
74 | do { \ | |
75 | _printer("Section " #_name " [%p - %p], (%zu elements)\n", \ | |
76 | SECTION_BEGIN(_name), SECTION_END(_name), \ | |
77 | SECTION_ELEMENT_COUNT(_name)); \ | |
78 | } while (0) | |
79 | ||
80 | #define PRINT_PLUG_IN_SECTIONS(_printer) \ | |
81 | do { \ | |
82 | PRINT_SECTION(_printer, __bt_plugin_init_funcs); \ | |
83 | PRINT_SECTION(_printer, __bt_plugin_exit_funcs); \ | |
84 | PRINT_SECTION(_printer, __bt_plugin_names); \ | |
85 | PRINT_SECTION(_printer, __bt_plugin_authors); \ | |
86 | PRINT_SECTION(_printer, __bt_plugin_licenses); \ | |
87 | PRINT_SECTION(_printer, __bt_plugin_descriptions); \ | |
88 | } while (0) | |
89 | ||
90 | /* | |
91 | * This hash table, global to the library, maps component class pointers | |
92 | * to shared library handles. | |
93 | * | |
94 | * The keys (component classes) are NOT owned by this hash table, whereas | |
95 | * the values (shared library handles) are owned by this hash table. | |
96 | * | |
97 | * The keys are the component classes created with | |
98 | * bt_plugin_add_component_class(). They keep the shared library handle | |
99 | * object created by their plugin alive so that the plugin's code is | |
100 | * not discarded when it could still be in use by living components | |
101 | * created from those component classes: | |
102 | * | |
103 | * [component] --ref-> [component class] --through this HT-> [shlib handle] | |
104 | * | |
105 | * This hash table exists for two reasons: | |
106 | * | |
107 | * 1. To allow this application: | |
108 | * | |
109 | * my_plugin = bt_plugin_create_from_file("/path/to/my-plugin.so"); | |
110 | * // instantiate components from the plugin's component classes | |
111 | * BT_PUT(my_plugin); | |
112 | * // user code of instantiated components still exists | |
113 | * | |
114 | * 2. To decouple the plugin subsystem from the component subsystem: | |
115 | * while plugins objects need to know component class objects, the | |
116 | * opposite is not necessary, thus it makes no sense for a component | |
117 | * class to keep a reference to the plugin object from which it was | |
118 | * created. | |
119 | * | |
120 | * An entry is removed from this HT when a component class is destroyed | |
121 | * thanks to a custom destroy listener. When the entry is removed, the | |
122 | * GLib function calls the value destroy notifier of the HT, which is | |
123 | * bt_put(). This decreases the reference count of the mapped shared | |
124 | * library handle. Assuming the original plugin object which contained | |
125 | * some component classes is put first, when the last component class is | |
126 | * removed from this HT, the shared library handle object's reference | |
127 | * count falls to zero and the shared library is finally closed. | |
128 | */ | |
129 | static | |
130 | GHashTable *comp_classes_to_shlib_handles; | |
131 | ||
132 | __attribute__((constructor)) static | |
133 | void init_comp_classes_to_shlib_handles(void) { | |
134 | comp_classes_to_shlib_handles = g_hash_table_new_full(g_direct_hash, | |
135 | g_direct_equal, NULL, bt_put); | |
136 | assert(comp_classes_to_shlib_handles); | |
137 | } | |
138 | ||
139 | __attribute__((destructor)) static | |
140 | void fini_comp_classes_to_shlib_handles(void) { | |
141 | if (comp_classes_to_shlib_handles) { | |
142 | g_hash_table_destroy(comp_classes_to_shlib_handles); | |
143 | } | |
144 | } | |
145 | ||
146 | static | |
147 | void bt_plugin_shared_lib_handle_destroy(struct bt_object *obj) | |
148 | { | |
149 | struct bt_plugin_shared_lib_handle *shared_lib_handle; | |
150 | ||
151 | assert(obj); | |
152 | shared_lib_handle = container_of(obj, | |
153 | struct bt_plugin_shared_lib_handle, base); | |
154 | ||
155 | if (shared_lib_handle->init_called && shared_lib_handle->exit) { | |
156 | enum bt_plugin_status status = shared_lib_handle->exit(); | |
157 | ||
158 | if (status < 0) { | |
159 | printf_verbose("Plugin `%s` exited with error %d\n", | |
160 | shared_lib_handle->name, status); | |
161 | } | |
162 | } | |
163 | ||
164 | if (shared_lib_handle->module) { | |
165 | if (!g_module_close(shared_lib_handle->module)) { | |
166 | printf_error("Module close error: %s\n", | |
167 | g_module_error()); | |
168 | } | |
169 | } | |
170 | ||
171 | if (shared_lib_handle->path) { | |
172 | g_string_free(shared_lib_handle->path, TRUE); | |
173 | } | |
174 | ||
175 | g_free(shared_lib_handle); | |
176 | } | |
177 | ||
178 | static | |
179 | struct bt_plugin_shared_lib_handle *bt_plugin_shared_lib_handle_create( | |
180 | const char *path) | |
181 | { | |
182 | struct bt_plugin_shared_lib_handle *shared_lib_handle = NULL; | |
183 | gpointer symbol = NULL; | |
184 | ||
185 | shared_lib_handle = g_new0(struct bt_plugin_shared_lib_handle, 1); | |
186 | if (!shared_lib_handle) { | |
187 | goto error; | |
188 | } | |
189 | ||
190 | bt_object_init(shared_lib_handle, bt_plugin_shared_lib_handle_destroy); | |
191 | ||
192 | if (!path) { | |
193 | goto end; | |
194 | } | |
195 | ||
196 | shared_lib_handle->path = g_string_new(path); | |
197 | if (!shared_lib_handle->path) { | |
198 | goto error; | |
199 | } | |
200 | ||
201 | shared_lib_handle->module = g_module_open(path, 0); | |
202 | if (!shared_lib_handle->module) { | |
203 | printf_verbose("Module open error: %s\n", g_module_error()); | |
204 | goto error; | |
205 | } | |
206 | ||
207 | if (!g_module_symbol(shared_lib_handle->module, PLUGIN_SYMBOL_NAME, | |
208 | (gpointer *) &shared_lib_handle->name)) { | |
209 | printf_verbose("Unable to resolve plugin symbol %s from %s\n", | |
210 | PLUGIN_SYMBOL_NAME, | |
211 | g_module_name(shared_lib_handle->module)); | |
212 | goto error; | |
213 | } | |
214 | ||
215 | if (!g_module_symbol(shared_lib_handle->module, PLUGIN_SYMBOL_LICENSE, | |
216 | (gpointer *) &shared_lib_handle->license)) { | |
217 | printf_verbose("Unable to resolve plugin symbol %s from %s\n", | |
218 | PLUGIN_SYMBOL_LICENSE, | |
219 | g_module_name(shared_lib_handle->module)); | |
220 | goto error; | |
221 | } | |
222 | ||
223 | if (!g_module_symbol(shared_lib_handle->module, PLUGIN_SYMBOL_AUTHOR, | |
224 | (gpointer *) &shared_lib_handle->author)) { | |
225 | printf_verbose("Unable to resolve plugin symbol %s from %s\n", | |
226 | PLUGIN_SYMBOL_AUTHOR, | |
227 | g_module_name(shared_lib_handle->module)); | |
228 | goto error; | |
229 | } | |
230 | ||
231 | if (!g_module_symbol(shared_lib_handle->module, PLUGIN_SYMBOL_DESCRIPTION, | |
232 | (gpointer *) &shared_lib_handle->description)) { | |
233 | printf_verbose("Unable to resolve plugin symbol %s from %s\n", | |
234 | PLUGIN_SYMBOL_DESCRIPTION, | |
235 | g_module_name(shared_lib_handle->module)); | |
236 | goto error; | |
237 | } | |
238 | ||
239 | if (!g_module_symbol(shared_lib_handle->module, PLUGIN_SYMBOL_INIT, | |
240 | &symbol)) { | |
241 | printf_verbose("Unable to resolve plugin symbol %s from %s\n", | |
242 | PLUGIN_SYMBOL_INIT, | |
243 | g_module_name(shared_lib_handle->module)); | |
244 | goto error; | |
245 | } else { | |
246 | shared_lib_handle->init = *((bt_plugin_init_func *) symbol); | |
247 | if (!shared_lib_handle->init) { | |
248 | printf_verbose("NULL %s symbol target\n", | |
249 | PLUGIN_SYMBOL_INIT); | |
250 | goto error; | |
251 | } | |
252 | } | |
253 | ||
254 | if (!g_module_symbol(shared_lib_handle->module, PLUGIN_SYMBOL_EXIT, | |
255 | &symbol)) { | |
256 | printf_verbose("Unable to resolve plugin symbol %s from %s\n", | |
257 | PLUGIN_SYMBOL_EXIT, | |
258 | g_module_name(shared_lib_handle->module)); | |
259 | goto error; | |
260 | } else { | |
261 | shared_lib_handle->exit = *((bt_plugin_exit_func *) symbol); | |
262 | if (!shared_lib_handle->exit) { | |
263 | printf_verbose("NULL %s symbol target\n", | |
264 | PLUGIN_SYMBOL_EXIT); | |
265 | goto error; | |
266 | } | |
267 | } | |
268 | ||
269 | goto end; | |
270 | ||
271 | error: | |
272 | BT_PUT(shared_lib_handle); | |
273 | ||
274 | end: | |
275 | return shared_lib_handle; | |
276 | } | |
277 | ||
278 | static | |
279 | void bt_plugin_destroy(struct bt_object *obj) | |
280 | { | |
281 | struct bt_plugin *plugin; | |
282 | ||
283 | assert(obj); | |
284 | plugin = container_of(obj, struct bt_plugin, base); | |
285 | ||
286 | BT_PUT(plugin->shared_lib_handle); | |
287 | ||
288 | if (plugin->comp_classes) { | |
289 | g_ptr_array_free(plugin->comp_classes, TRUE); | |
290 | } | |
291 | ||
292 | g_free(plugin); | |
293 | } | |
294 | ||
295 | static | |
296 | enum bt_plugin_status init_plugin(struct bt_plugin *plugin) | |
297 | { | |
298 | enum bt_plugin_status status = BT_PLUGIN_STATUS_OK; | |
299 | ||
300 | if (plugin->shared_lib_handle->init) { | |
301 | status = plugin->shared_lib_handle->init(plugin); | |
302 | ||
303 | if (status < 0) { | |
304 | printf_verbose("Plugin `%s` initialization error: %d\n", | |
305 | plugin->shared_lib_handle->name, status); | |
306 | goto end; | |
307 | } | |
308 | } | |
309 | ||
310 | plugin->shared_lib_handle->init_called = true; | |
311 | ||
312 | /* | |
313 | * The initialization function should have added the component | |
314 | * classes at this point. We freeze the plugin so that it's not | |
315 | * possible to add component classes to this plugin object after | |
316 | * this stage (plugin object becomes immutable). | |
317 | */ | |
318 | plugin->frozen = true; | |
319 | ||
320 | end: | |
321 | return status; | |
322 | } | |
323 | ||
324 | struct bt_plugin *bt_plugin_create_from_file(const char *path) | |
325 | { | |
326 | size_t path_len; | |
327 | struct bt_plugin *plugin = NULL; | |
328 | bool is_libtool_wrapper = false, is_shared_object = false; | |
329 | ||
330 | if (!path) { | |
331 | goto error; | |
332 | } | |
333 | ||
334 | path_len = strlen(path); | |
335 | if (path_len <= PLUGIN_SUFFIX_LEN) { | |
336 | goto error; | |
337 | } | |
338 | ||
339 | path_len++; | |
340 | /* | |
341 | * Check if the file ends with a known plugin file type suffix (i.e. .so | |
342 | * or .la on Linux). | |
343 | */ | |
344 | is_libtool_wrapper = !strncmp(LIBTOOL_PLUGIN_SUFFIX, | |
345 | path + path_len - LIBTOOL_PLUGIN_SUFFIX_LEN, | |
346 | LIBTOOL_PLUGIN_SUFFIX_LEN); | |
347 | is_shared_object = !strncmp(NATIVE_PLUGIN_SUFFIX, | |
348 | path + path_len - NATIVE_PLUGIN_SUFFIX_LEN, | |
349 | NATIVE_PLUGIN_SUFFIX_LEN); | |
350 | if (!is_shared_object && !is_libtool_wrapper) { | |
351 | /* Name indicates that this is not a plugin file. */ | |
352 | goto error; | |
353 | } | |
354 | ||
355 | plugin = g_new0(struct bt_plugin, 1); | |
356 | if (!plugin) { | |
357 | goto error; | |
358 | } | |
359 | ||
360 | bt_object_init(plugin, bt_plugin_destroy); | |
361 | ||
362 | /* Create shared lib handle */ | |
363 | plugin->shared_lib_handle = bt_plugin_shared_lib_handle_create(path); | |
364 | if (!plugin->shared_lib_handle) { | |
365 | printf_verbose("Failed to create a shared library handle (path `%s`)\n", | |
366 | path); | |
367 | goto error; | |
368 | } | |
369 | ||
370 | /* Create empty array of component classes */ | |
371 | plugin->comp_classes = | |
372 | g_ptr_array_new_with_free_func((GDestroyNotify) bt_put); | |
373 | if (!plugin->comp_classes) { | |
374 | goto error; | |
375 | } | |
376 | ||
377 | /* Initialize plugin */ | |
378 | if (init_plugin(plugin) < 0) { | |
379 | goto error; | |
380 | } | |
381 | ||
382 | goto end; | |
383 | ||
384 | error: | |
385 | BT_PUT(plugin); | |
386 | ||
387 | end: | |
388 | return plugin; | |
389 | } | |
390 | ||
391 | /* Allocate dirent as recommended by READDIR(3), NOTES on readdir_r */ | |
392 | static | |
393 | struct dirent *alloc_dirent(const char *path) | |
394 | { | |
395 | size_t len; | |
396 | long name_max; | |
397 | struct dirent *entry; | |
398 | ||
399 | name_max = pathconf(path, _PC_NAME_MAX); | |
400 | if (name_max == -1) { | |
401 | name_max = PATH_MAX; | |
402 | } | |
403 | len = offsetof(struct dirent, d_name) + name_max + 1; | |
404 | entry = zmalloc(len); | |
405 | return entry; | |
406 | } | |
407 | ||
408 | static | |
409 | enum bt_plugin_status bt_plugin_create_append_all_from_dir( | |
410 | GPtrArray *plugins, const char *path, bool recurse) | |
411 | { | |
412 | DIR *directory = NULL; | |
413 | struct dirent *entry = NULL, *result = NULL; | |
414 | char *file_path = NULL; | |
415 | size_t path_len = strlen(path); | |
416 | enum bt_plugin_status ret = BT_PLUGIN_STATUS_OK; | |
417 | ||
418 | if (path_len >= PATH_MAX) { | |
419 | ret = BT_PLUGIN_STATUS_ERROR; | |
420 | goto end; | |
421 | } | |
422 | ||
423 | entry = alloc_dirent(path); | |
424 | if (!entry) { | |
425 | ret = BT_PLUGIN_STATUS_ERROR; | |
426 | goto end; | |
427 | } | |
428 | ||
429 | file_path = zmalloc(PATH_MAX); | |
430 | if (!file_path) { | |
431 | ret = BT_PLUGIN_STATUS_NOMEM; | |
432 | goto end; | |
433 | } | |
434 | ||
435 | strncpy(file_path, path, path_len); | |
436 | /* Append a trailing '/' to the path */ | |
437 | if (file_path[path_len - 1] != '/') { | |
438 | file_path[path_len++] = '/'; | |
439 | } | |
440 | ||
441 | directory = opendir(file_path); | |
442 | if (!directory) { | |
443 | perror("Failed to open plug-in directory"); | |
444 | ret = BT_PLUGIN_STATUS_ERROR; | |
445 | goto end; | |
446 | } | |
447 | ||
448 | /* Recursively walk directory */ | |
449 | while (!readdir_r(directory, entry, &result) && result) { | |
450 | struct stat st; | |
451 | int stat_ret; | |
452 | size_t file_name_len; | |
453 | ||
454 | if (result->d_name[0] == '.') { | |
455 | /* Skip hidden files, . and .. */ | |
456 | continue; | |
457 | } | |
458 | ||
459 | file_name_len = strlen(result->d_name); | |
460 | ||
461 | if (path_len + file_name_len >= PATH_MAX) { | |
462 | continue; | |
463 | } | |
464 | ||
465 | strncpy(file_path + path_len, result->d_name, file_name_len); | |
466 | file_path[path_len + file_name_len] = '\0'; | |
467 | ||
468 | stat_ret = stat(file_path, &st); | |
469 | if (stat_ret < 0) { | |
470 | /* Continue to next file / directory. */ | |
471 | printf_perror("Failed to stat() plugin file\n"); | |
472 | continue; | |
473 | } | |
474 | ||
475 | if (S_ISDIR(st.st_mode) && recurse) { | |
476 | ret = bt_plugin_create_append_all_from_dir(plugins, | |
477 | file_path, true); | |
478 | if (ret < 0) { | |
479 | goto end; | |
480 | } | |
481 | } else if (S_ISREG(st.st_mode)) { | |
482 | struct bt_plugin *plugin = bt_plugin_create_from_file(file_path); | |
483 | ||
484 | if (plugin) { | |
485 | /* Transfer ownership to array */ | |
486 | g_ptr_array_add(plugins, plugin); | |
487 | } | |
488 | } | |
489 | } | |
490 | end: | |
491 | if (directory) { | |
492 | if (closedir(directory)) { | |
493 | /* | |
494 | * We don't want to override the error since there is | |
495 | * nothing could do. | |
496 | */ | |
497 | perror("Failed to close plug-in directory"); | |
498 | } | |
499 | } | |
500 | free(entry); | |
501 | free(file_path); | |
502 | return ret; | |
503 | } | |
504 | ||
505 | struct bt_plugin **bt_plugin_create_all_from_dir(const char *path, | |
506 | bool recurse) | |
507 | { | |
508 | GPtrArray *plugins_array = NULL; | |
509 | struct bt_plugin **plugins = NULL; | |
510 | enum bt_plugin_status status; | |
511 | ||
512 | if (!path) { | |
513 | goto error; | |
514 | } | |
515 | ||
516 | plugins_array = g_ptr_array_new(); | |
517 | if (!plugins_array) { | |
518 | goto error; | |
519 | } | |
520 | ||
521 | /* Append found plugins to array */ | |
522 | status = bt_plugin_create_append_all_from_dir(plugins_array, path, | |
523 | recurse); | |
524 | if (status < 0) { | |
525 | goto error; | |
526 | } | |
527 | ||
528 | /* Add sentinel to array */ | |
529 | g_ptr_array_add(plugins_array, NULL); | |
530 | plugins = (struct bt_plugin **) plugins_array->pdata; | |
531 | goto end; | |
532 | ||
533 | error: | |
534 | if (plugins_array) { | |
535 | g_ptr_array_free(plugins_array, TRUE); | |
536 | plugins_array = NULL; | |
537 | } | |
538 | ||
539 | end: | |
540 | if (plugins_array) { | |
541 | g_ptr_array_free(plugins_array, FALSE); | |
542 | } | |
543 | ||
544 | return plugins; | |
545 | } | |
546 | ||
547 | static | |
548 | struct bt_plugin *bt_plugin_create_from_static_at_index(size_t i) | |
549 | { | |
550 | struct bt_plugin *plugin = NULL; | |
551 | ||
552 | plugin = g_new0(struct bt_plugin, 1); | |
553 | if (!plugin) { | |
554 | goto error; | |
555 | } | |
556 | ||
557 | bt_object_init(plugin, bt_plugin_destroy); | |
558 | ||
559 | /* Create shared lib handle */ | |
560 | plugin->shared_lib_handle = bt_plugin_shared_lib_handle_create(NULL); | |
561 | if (!plugin->shared_lib_handle) { | |
562 | goto error; | |
563 | } | |
564 | ||
565 | /* Fill shared lib handle */ | |
566 | plugin->shared_lib_handle->init = | |
567 | (SECTION_BEGIN(__bt_plugin_init_funcs))[i]; | |
568 | if (!plugin->shared_lib_handle->init) { | |
569 | goto error; | |
570 | } | |
571 | ||
572 | plugin->shared_lib_handle->exit = | |
573 | (SECTION_BEGIN(__bt_plugin_exit_funcs))[i]; | |
574 | if (!plugin->shared_lib_handle->exit) { | |
575 | goto error; | |
576 | } | |
577 | ||
578 | plugin->shared_lib_handle->name = (SECTION_BEGIN(__bt_plugin_names))[i]; | |
579 | plugin->shared_lib_handle->author = | |
580 | (SECTION_BEGIN(__bt_plugin_authors))[i]; | |
581 | plugin->shared_lib_handle->license = | |
582 | (SECTION_BEGIN(__bt_plugin_licenses))[i]; | |
583 | plugin->shared_lib_handle->description = | |
584 | (SECTION_BEGIN(__bt_plugin_descriptions))[i]; | |
585 | ||
586 | /* Create empty array of component classes */ | |
587 | plugin->comp_classes = | |
588 | g_ptr_array_new_with_free_func((GDestroyNotify) bt_put); | |
589 | if (!plugin->comp_classes) { | |
590 | goto error; | |
591 | } | |
592 | ||
593 | /* Initialize plugin */ | |
594 | if (init_plugin(plugin) < 0) { | |
595 | goto error; | |
596 | } | |
597 | ||
598 | goto end; | |
599 | ||
600 | error: | |
601 | BT_PUT(plugin); | |
602 | ||
603 | end: | |
604 | return plugin; | |
605 | } | |
606 | ||
607 | struct bt_plugin **bt_plugin_create_all_from_static(void) | |
608 | { | |
609 | size_t count, i; | |
610 | struct bt_plugin **plugins = NULL; | |
611 | ||
612 | PRINT_PLUG_IN_SECTIONS(printf_verbose); | |
613 | count = SECTION_ELEMENT_COUNT(__bt_plugin_init_funcs); | |
614 | if (SECTION_ELEMENT_COUNT(__bt_plugin_exit_funcs) != count || | |
615 | SECTION_ELEMENT_COUNT(__bt_plugin_names) != count || | |
616 | SECTION_ELEMENT_COUNT(__bt_plugin_authors) != count || | |
617 | SECTION_ELEMENT_COUNT(__bt_plugin_licenses) != count || | |
618 | SECTION_ELEMENT_COUNT(__bt_plugin_descriptions) != count) { | |
619 | printf_error("Some statically-linked plug-ins do not define all the mandatory symbols\n"); | |
620 | goto error; | |
621 | } | |
622 | ||
623 | printf_verbose("Detected %zu statically-linked plug-ins\n", count); | |
624 | plugins = g_new0(struct bt_plugin *, count + 1); | |
625 | if (!plugins) { | |
626 | goto error; | |
627 | } | |
628 | ||
629 | for (i = 0; i < count; i++) { | |
630 | struct bt_plugin *plugin = | |
631 | bt_plugin_create_from_static_at_index(i); | |
632 | ||
633 | if (!plugin) { | |
634 | printf_error("Cannot create statically-linked plug-in at index %zu\n", | |
635 | i); | |
636 | goto error; | |
637 | } | |
638 | ||
639 | /* Transfer ownership to the array */ | |
640 | plugins[i] = plugin; | |
641 | } | |
642 | ||
643 | goto end; | |
644 | ||
645 | error: | |
646 | g_free(plugins); | |
647 | ||
648 | end: | |
649 | return plugins; | |
650 | } | |
651 | ||
652 | const char *bt_plugin_get_name(struct bt_plugin *plugin) | |
653 | { | |
654 | return plugin ? plugin->shared_lib_handle->name : NULL; | |
655 | } | |
656 | ||
657 | const char *bt_plugin_get_author(struct bt_plugin *plugin) | |
658 | { | |
659 | return plugin ? plugin->shared_lib_handle->author : NULL; | |
660 | } | |
661 | ||
662 | const char *bt_plugin_get_license(struct bt_plugin *plugin) | |
663 | { | |
664 | return plugin ? plugin->shared_lib_handle->license : NULL; | |
665 | } | |
666 | ||
667 | const char *bt_plugin_get_path(struct bt_plugin *plugin) | |
668 | { | |
669 | return (plugin && plugin->shared_lib_handle->path) ? | |
670 | plugin->shared_lib_handle->path->str : NULL; | |
671 | } | |
672 | ||
673 | const char *bt_plugin_get_description(struct bt_plugin *plugin) | |
674 | { | |
675 | return plugin ? plugin->shared_lib_handle->description : NULL; | |
676 | } | |
677 | ||
678 | int bt_plugin_get_component_class_count(struct bt_plugin *plugin) | |
679 | { | |
680 | return plugin ? plugin->comp_classes->len : -1; | |
681 | } | |
682 | ||
683 | struct bt_component_class *bt_plugin_get_component_class( | |
684 | struct bt_plugin *plugin, size_t index) | |
685 | { | |
686 | struct bt_component_class *comp_class = NULL; | |
687 | ||
688 | if (!plugin || index >= plugin->comp_classes->len) { | |
689 | goto error; | |
690 | } | |
691 | ||
692 | comp_class = g_ptr_array_index(plugin->comp_classes, index); | |
693 | bt_get(comp_class); | |
694 | goto end; | |
695 | ||
696 | error: | |
697 | BT_PUT(comp_class); | |
698 | ||
699 | end: | |
700 | return comp_class; | |
701 | } | |
702 | ||
703 | struct bt_component_class *bt_plugin_get_component_class_by_name_and_type( | |
704 | struct bt_plugin *plugin, const char *name, | |
705 | enum bt_component_type type) | |
706 | { | |
707 | struct bt_component_class *comp_class = NULL; | |
708 | size_t i; | |
709 | ||
710 | if (!plugin || !name) { | |
711 | goto error; | |
712 | } | |
713 | ||
714 | for (i = 0; i < plugin->comp_classes->len; i++) { | |
715 | struct bt_component_class *comp_class_candidate = | |
716 | g_ptr_array_index(plugin->comp_classes, i); | |
717 | const char *comp_class_cand_name = | |
718 | bt_component_class_get_name(comp_class_candidate); | |
719 | enum bt_component_type comp_class_cand_type = | |
720 | bt_component_class_get_type(comp_class_candidate); | |
721 | ||
722 | assert(comp_class_cand_name); | |
723 | assert(comp_class_cand_type >= 0); | |
724 | ||
725 | if (strcmp(name, comp_class_cand_name) == 0 && | |
726 | comp_class_cand_type == type) { | |
727 | comp_class = bt_get(comp_class_candidate); | |
728 | break; | |
729 | } | |
730 | } | |
731 | ||
732 | goto end; | |
733 | ||
734 | error: | |
735 | BT_PUT(comp_class); | |
736 | ||
737 | end: | |
738 | return comp_class; | |
739 | } | |
740 | ||
741 | static | |
742 | void plugin_comp_class_destroy_listener(struct bt_component_class *comp_class, | |
743 | void *data) | |
744 | { | |
745 | gboolean exists = g_hash_table_remove(comp_classes_to_shlib_handles, | |
746 | comp_class); | |
747 | assert(exists); | |
748 | } | |
749 | ||
750 | enum bt_plugin_status bt_plugin_add_component_class( | |
751 | struct bt_plugin *plugin, struct bt_component_class *comp_class) | |
752 | { | |
753 | enum bt_plugin_status status = BT_PLUGIN_STATUS_OK; | |
754 | struct bt_component_class *comp_class_dup = NULL; | |
755 | int ret; | |
756 | int comp_class_index = -1; | |
757 | ||
758 | if (!plugin || !comp_class || plugin->frozen) { | |
759 | goto error; | |
760 | } | |
761 | ||
762 | /* Check for duplicate */ | |
763 | comp_class_dup = bt_plugin_get_component_class_by_name_and_type(plugin, | |
764 | bt_component_class_get_name(comp_class), | |
765 | bt_component_class_get_type(comp_class)); | |
766 | if (comp_class_dup) { | |
767 | printf_verbose("Plugin `%s`: adding component class with existing name `%s` and type %d\n", | |
768 | plugin->shared_lib_handle->name, | |
769 | bt_component_class_get_name(comp_class), | |
770 | bt_component_class_get_type(comp_class)); | |
771 | goto error; | |
772 | } | |
773 | ||
774 | /* Add new component class */ | |
775 | comp_class_index = plugin->comp_classes->len; | |
776 | g_ptr_array_add(plugin->comp_classes, bt_get(comp_class)); | |
777 | ||
778 | /* Map component class pointer to shared lib handle in global HT */ | |
779 | g_hash_table_insert(comp_classes_to_shlib_handles, comp_class, | |
780 | bt_get(plugin->shared_lib_handle)); | |
781 | ||
782 | /* Add our custom destroy listener */ | |
783 | ret = bt_component_class_add_destroy_listener(comp_class, | |
784 | plugin_comp_class_destroy_listener, NULL); | |
785 | if (ret) { | |
786 | goto error; | |
787 | } | |
788 | ||
789 | goto end; | |
790 | ||
791 | error: | |
792 | /* Remove entry from global hash table (if exists) */ | |
793 | g_hash_table_remove(comp_classes_to_shlib_handles, | |
794 | comp_class); | |
795 | ||
796 | /* Remove entry from plugin's component classes (if added) */ | |
797 | if (comp_class_index >= 0) { | |
798 | g_ptr_array_remove_index(plugin->comp_classes, | |
799 | comp_class_index); | |
800 | } | |
801 | ||
802 | status = BT_PLUGIN_STATUS_ERROR; | |
803 | ||
804 | end: | |
805 | bt_put(comp_class_dup); | |
806 | return status; | |
807 | } |