Commit | Line | Data |
---|---|---|
33b34c43 PP |
1 | /* |
2 | * plugin.c | |
3 | * | |
55bb57e0 | 4 | * Babeltrace Plugin (generic) |
33b34c43 PP |
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> | |
33b34c43 | 32 | #include <babeltrace/plugin/plugin-internal.h> |
55bb57e0 | 33 | #include <babeltrace/plugin/plugin-so-internal.h> |
33b34c43 | 34 | #include <glib.h> |
33b34c43 PP |
35 | #include <unistd.h> |
36 | #include <stdlib.h> | |
37 | #include <sys/stat.h> | |
38 | #include <dirent.h> | |
39 | ||
55bb57e0 PP |
40 | #ifdef WITH_PYTHON_PLUGINS |
41 | # include <babeltrace/plugin/plugin-python-enabled-internal.h> | |
42 | #else | |
43 | # include <babeltrace/plugin/plugin-python-disabled-internal.h> | |
44 | #endif | |
33b34c43 | 45 | |
33b34c43 | 46 | static |
55bb57e0 | 47 | void bt_plugin_destroy(struct bt_object *obj) |
33b34c43 | 48 | { |
55bb57e0 | 49 | struct bt_plugin *plugin; |
33b34c43 PP |
50 | |
51 | assert(obj); | |
55bb57e0 | 52 | plugin = container_of(obj, struct bt_plugin, base); |
6ba0b073 | 53 | |
55bb57e0 PP |
54 | if (plugin->type == BT_PLUGIN_TYPE_SO) { |
55 | bt_plugin_so_destroy_spec_data(plugin); | |
56 | } else if (plugin->type == BT_PLUGIN_TYPE_PYTHON) { | |
57 | bt_plugin_python_destroy_spec_data(plugin); | |
58 | } else { | |
59 | assert(false); | |
33b34c43 PP |
60 | } |
61 | ||
55bb57e0 PP |
62 | if (plugin->comp_classes) { |
63 | g_ptr_array_free(plugin->comp_classes, TRUE); | |
33b34c43 PP |
64 | } |
65 | ||
55bb57e0 PP |
66 | if (plugin->info.name) { |
67 | g_string_free(plugin->info.name, TRUE); | |
33b34c43 PP |
68 | } |
69 | ||
55bb57e0 PP |
70 | if (plugin->info.path) { |
71 | g_string_free(plugin->info.path, TRUE); | |
33b34c43 PP |
72 | } |
73 | ||
55bb57e0 PP |
74 | if (plugin->info.description) { |
75 | g_string_free(plugin->info.description, TRUE); | |
33b34c43 PP |
76 | } |
77 | ||
55bb57e0 PP |
78 | if (plugin->info.author) { |
79 | g_string_free(plugin->info.author, TRUE); | |
33b34c43 PP |
80 | } |
81 | ||
55bb57e0 PP |
82 | if (plugin->info.license) { |
83 | g_string_free(plugin->info.license, TRUE); | |
33b34c43 PP |
84 | } |
85 | ||
55bb57e0 PP |
86 | if (plugin->info.version.extra) { |
87 | g_string_free(plugin->info.version.extra, TRUE); | |
33b34c43 PP |
88 | } |
89 | ||
90 | g_free(plugin); | |
91 | } | |
92 | ||
55bb57e0 PP |
93 | BT_HIDDEN |
94 | struct bt_plugin *bt_plugin_create_empty(enum bt_plugin_type type) | |
6ba0b073 PP |
95 | { |
96 | struct bt_plugin *plugin = NULL; | |
97 | ||
98 | plugin = g_new0(struct bt_plugin, 1); | |
99 | if (!plugin) { | |
100 | goto error; | |
101 | } | |
102 | ||
103 | bt_object_init(plugin, bt_plugin_destroy); | |
55bb57e0 | 104 | plugin->type = type; |
6ba0b073 PP |
105 | |
106 | /* Create empty array of component classes */ | |
107 | plugin->comp_classes = | |
108 | g_ptr_array_new_with_free_func((GDestroyNotify) bt_put); | |
109 | if (!plugin->comp_classes) { | |
110 | goto error; | |
111 | } | |
112 | ||
55bb57e0 PP |
113 | /* Create empty info */ |
114 | plugin->info.name = g_string_new(NULL); | |
115 | if (!plugin->info.name) { | |
116 | goto error; | |
6ba0b073 PP |
117 | } |
118 | ||
55bb57e0 PP |
119 | plugin->info.path = g_string_new(NULL); |
120 | if (!plugin->info.path) { | |
121 | goto error; | |
6ba0b073 PP |
122 | } |
123 | ||
55bb57e0 PP |
124 | plugin->info.description = g_string_new(NULL); |
125 | if (!plugin->info.description) { | |
126 | goto error; | |
33b34c43 PP |
127 | } |
128 | ||
55bb57e0 PP |
129 | plugin->info.author = g_string_new(NULL); |
130 | if (!plugin->info.author) { | |
131 | goto error; | |
6ba0b073 PP |
132 | } |
133 | ||
55bb57e0 PP |
134 | plugin->info.license = g_string_new(NULL); |
135 | if (!plugin->info.license) { | |
6ba0b073 PP |
136 | goto error; |
137 | } | |
138 | ||
55bb57e0 PP |
139 | plugin->info.version.extra = g_string_new(NULL); |
140 | if (!plugin->info.version.extra) { | |
141 | goto error; | |
6ba0b073 PP |
142 | } |
143 | ||
144 | goto end; | |
145 | ||
146 | error: | |
55bb57e0 | 147 | BT_PUT(plugin); |
6ba0b073 PP |
148 | |
149 | end: | |
55bb57e0 | 150 | return plugin; |
6ba0b073 PP |
151 | } |
152 | ||
153 | struct bt_plugin **bt_plugin_create_all_from_static(void) | |
154 | { | |
55bb57e0 | 155 | return bt_plugin_so_create_all_from_static(); |
6ba0b073 PP |
156 | } |
157 | ||
158 | struct bt_plugin **bt_plugin_create_all_from_file(const char *path) | |
33b34c43 | 159 | { |
6ba0b073 | 160 | struct bt_plugin **plugins = NULL; |
33b34c43 PP |
161 | |
162 | if (!path) { | |
6ba0b073 | 163 | goto end; |
33b34c43 PP |
164 | } |
165 | ||
55bb57e0 | 166 | printf_verbose("Trying to load plugins from `%s`\n", path); |
6ba0b073 | 167 | |
55bb57e0 PP |
168 | /* Try shared object plugins */ |
169 | plugins = bt_plugin_so_create_all_from_file(path); | |
170 | if (plugins) { | |
6ba0b073 PP |
171 | goto end; |
172 | } | |
173 | ||
55bb57e0 PP |
174 | plugins = bt_plugin_python_create_all_from_file(path); |
175 | if (plugins) { | |
6ba0b073 PP |
176 | goto end; |
177 | } | |
178 | ||
33b34c43 | 179 | end: |
6ba0b073 | 180 | return plugins; |
33b34c43 PP |
181 | } |
182 | ||
183 | /* Allocate dirent as recommended by READDIR(3), NOTES on readdir_r */ | |
184 | static | |
185 | struct dirent *alloc_dirent(const char *path) | |
186 | { | |
187 | size_t len; | |
188 | long name_max; | |
189 | struct dirent *entry; | |
190 | ||
191 | name_max = pathconf(path, _PC_NAME_MAX); | |
192 | if (name_max == -1) { | |
193 | name_max = PATH_MAX; | |
194 | } | |
195 | len = offsetof(struct dirent, d_name) + name_max + 1; | |
196 | entry = zmalloc(len); | |
197 | return entry; | |
198 | } | |
199 | ||
200 | static | |
201 | enum bt_plugin_status bt_plugin_create_append_all_from_dir( | |
202 | GPtrArray *plugins, const char *path, bool recurse) | |
203 | { | |
204 | DIR *directory = NULL; | |
205 | struct dirent *entry = NULL, *result = NULL; | |
206 | char *file_path = NULL; | |
6ba0b073 | 207 | size_t path_len; |
33b34c43 PP |
208 | enum bt_plugin_status ret = BT_PLUGIN_STATUS_OK; |
209 | ||
6ba0b073 PP |
210 | if (!path) { |
211 | ret = BT_PLUGIN_STATUS_ERROR; | |
212 | goto end; | |
213 | } | |
214 | ||
215 | path_len = strlen(path); | |
216 | ||
33b34c43 PP |
217 | if (path_len >= PATH_MAX) { |
218 | ret = BT_PLUGIN_STATUS_ERROR; | |
219 | goto end; | |
220 | } | |
221 | ||
222 | entry = alloc_dirent(path); | |
223 | if (!entry) { | |
224 | ret = BT_PLUGIN_STATUS_ERROR; | |
225 | goto end; | |
226 | } | |
227 | ||
228 | file_path = zmalloc(PATH_MAX); | |
229 | if (!file_path) { | |
230 | ret = BT_PLUGIN_STATUS_NOMEM; | |
231 | goto end; | |
232 | } | |
233 | ||
234 | strncpy(file_path, path, path_len); | |
235 | /* Append a trailing '/' to the path */ | |
236 | if (file_path[path_len - 1] != '/') { | |
237 | file_path[path_len++] = '/'; | |
238 | } | |
239 | ||
240 | directory = opendir(file_path); | |
241 | if (!directory) { | |
242 | perror("Failed to open plug-in directory"); | |
243 | ret = BT_PLUGIN_STATUS_ERROR; | |
244 | goto end; | |
245 | } | |
246 | ||
247 | /* Recursively walk directory */ | |
248 | while (!readdir_r(directory, entry, &result) && result) { | |
249 | struct stat st; | |
250 | int stat_ret; | |
251 | size_t file_name_len; | |
252 | ||
253 | if (result->d_name[0] == '.') { | |
254 | /* Skip hidden files, . and .. */ | |
255 | continue; | |
256 | } | |
257 | ||
258 | file_name_len = strlen(result->d_name); | |
259 | ||
260 | if (path_len + file_name_len >= PATH_MAX) { | |
261 | continue; | |
262 | } | |
263 | ||
264 | strncpy(file_path + path_len, result->d_name, file_name_len); | |
265 | file_path[path_len + file_name_len] = '\0'; | |
33b34c43 PP |
266 | stat_ret = stat(file_path, &st); |
267 | if (stat_ret < 0) { | |
268 | /* Continue to next file / directory. */ | |
269 | printf_perror("Failed to stat() plugin file\n"); | |
270 | continue; | |
271 | } | |
272 | ||
273 | if (S_ISDIR(st.st_mode) && recurse) { | |
274 | ret = bt_plugin_create_append_all_from_dir(plugins, | |
275 | file_path, true); | |
276 | if (ret < 0) { | |
277 | goto end; | |
278 | } | |
279 | } else if (S_ISREG(st.st_mode)) { | |
6ba0b073 PP |
280 | struct bt_plugin **plugins_from_file = |
281 | bt_plugin_create_all_from_file(file_path); | |
33b34c43 | 282 | |
6ba0b073 PP |
283 | if (plugins_from_file) { |
284 | struct bt_plugin **plugin; | |
285 | ||
286 | for (plugin = plugins_from_file; *plugin; plugin++) { | |
287 | /* Transfer ownership to array */ | |
288 | g_ptr_array_add(plugins, *plugin); | |
289 | } | |
290 | ||
291 | free(plugins_from_file); | |
33b34c43 PP |
292 | } |
293 | } | |
294 | } | |
295 | end: | |
296 | if (directory) { | |
297 | if (closedir(directory)) { | |
298 | /* | |
299 | * We don't want to override the error since there is | |
300 | * nothing could do. | |
301 | */ | |
302 | perror("Failed to close plug-in directory"); | |
303 | } | |
304 | } | |
305 | free(entry); | |
306 | free(file_path); | |
307 | return ret; | |
308 | } | |
309 | ||
310 | struct bt_plugin **bt_plugin_create_all_from_dir(const char *path, | |
311 | bool recurse) | |
312 | { | |
6ba0b073 | 313 | GPtrArray *plugins_array = g_ptr_array_new(); |
33b34c43 PP |
314 | struct bt_plugin **plugins = NULL; |
315 | enum bt_plugin_status status; | |
316 | ||
33b34c43 PP |
317 | /* Append found plugins to array */ |
318 | status = bt_plugin_create_append_all_from_dir(plugins_array, path, | |
319 | recurse); | |
320 | if (status < 0) { | |
321 | goto error; | |
322 | } | |
323 | ||
324 | /* Add sentinel to array */ | |
325 | g_ptr_array_add(plugins_array, NULL); | |
326 | plugins = (struct bt_plugin **) plugins_array->pdata; | |
327 | goto end; | |
328 | ||
329 | error: | |
330 | if (plugins_array) { | |
331 | g_ptr_array_free(plugins_array, TRUE); | |
332 | plugins_array = NULL; | |
333 | } | |
334 | ||
335 | end: | |
336 | if (plugins_array) { | |
337 | g_ptr_array_free(plugins_array, FALSE); | |
338 | } | |
339 | ||
340 | return plugins; | |
341 | } | |
342 | ||
33b34c43 PP |
343 | const char *bt_plugin_get_name(struct bt_plugin *plugin) |
344 | { | |
55bb57e0 | 345 | return plugin && plugin->info.name_set ? plugin->info.name->str : NULL; |
33b34c43 PP |
346 | } |
347 | ||
348 | const char *bt_plugin_get_author(struct bt_plugin *plugin) | |
349 | { | |
55bb57e0 | 350 | return plugin && plugin->info.author_set ? plugin->info.author->str : NULL; |
33b34c43 PP |
351 | } |
352 | ||
353 | const char *bt_plugin_get_license(struct bt_plugin *plugin) | |
354 | { | |
55bb57e0 | 355 | return plugin && plugin->info.license_set ? plugin->info.license->str : NULL; |
33b34c43 PP |
356 | } |
357 | ||
358 | const char *bt_plugin_get_path(struct bt_plugin *plugin) | |
359 | { | |
55bb57e0 | 360 | return plugin && plugin->info.path_set ? plugin->info.path->str : NULL; |
33b34c43 PP |
361 | } |
362 | ||
363 | const char *bt_plugin_get_description(struct bt_plugin *plugin) | |
364 | { | |
55bb57e0 | 365 | return plugin && plugin->info.description_set ? plugin->info.description->str : NULL; |
33b34c43 PP |
366 | } |
367 | ||
b6de043b PP |
368 | enum bt_plugin_status bt_plugin_get_version(struct bt_plugin *plugin, |
369 | unsigned int *major, unsigned int *minor, unsigned int *patch, | |
370 | const char **extra) | |
371 | { | |
372 | enum bt_plugin_status status = BT_PLUGIN_STATUS_OK; | |
373 | ||
55bb57e0 | 374 | if (!plugin || !plugin->info.version_set) { |
b6de043b PP |
375 | status = BT_PLUGIN_STATUS_ERROR; |
376 | goto end; | |
377 | } | |
378 | ||
379 | if (major) { | |
55bb57e0 | 380 | *major = plugin->info.version.major; |
b6de043b PP |
381 | } |
382 | ||
383 | if (minor) { | |
55bb57e0 | 384 | *minor = plugin->info.version.minor; |
b6de043b PP |
385 | } |
386 | ||
387 | if (patch) { | |
55bb57e0 | 388 | *patch = plugin->info.version.patch; |
b6de043b PP |
389 | } |
390 | ||
391 | if (extra) { | |
55bb57e0 | 392 | *extra = plugin->info.version.extra->str; |
b6de043b PP |
393 | } |
394 | ||
395 | end: | |
396 | return status; | |
397 | } | |
398 | ||
33b34c43 PP |
399 | int bt_plugin_get_component_class_count(struct bt_plugin *plugin) |
400 | { | |
401 | return plugin ? plugin->comp_classes->len : -1; | |
402 | } | |
403 | ||
404 | struct bt_component_class *bt_plugin_get_component_class( | |
405 | struct bt_plugin *plugin, size_t index) | |
406 | { | |
407 | struct bt_component_class *comp_class = NULL; | |
408 | ||
409 | if (!plugin || index >= plugin->comp_classes->len) { | |
410 | goto error; | |
411 | } | |
412 | ||
413 | comp_class = g_ptr_array_index(plugin->comp_classes, index); | |
414 | bt_get(comp_class); | |
415 | goto end; | |
416 | ||
417 | error: | |
418 | BT_PUT(comp_class); | |
419 | ||
420 | end: | |
421 | return comp_class; | |
422 | } | |
423 | ||
424 | struct bt_component_class *bt_plugin_get_component_class_by_name_and_type( | |
425 | struct bt_plugin *plugin, const char *name, | |
d3e4dcd8 | 426 | enum bt_component_class_type type) |
33b34c43 PP |
427 | { |
428 | struct bt_component_class *comp_class = NULL; | |
429 | size_t i; | |
430 | ||
431 | if (!plugin || !name) { | |
432 | goto error; | |
433 | } | |
434 | ||
435 | for (i = 0; i < plugin->comp_classes->len; i++) { | |
436 | struct bt_component_class *comp_class_candidate = | |
437 | g_ptr_array_index(plugin->comp_classes, i); | |
438 | const char *comp_class_cand_name = | |
439 | bt_component_class_get_name(comp_class_candidate); | |
d3e4dcd8 | 440 | enum bt_component_class_type comp_class_cand_type = |
33b34c43 PP |
441 | bt_component_class_get_type(comp_class_candidate); |
442 | ||
443 | assert(comp_class_cand_name); | |
444 | assert(comp_class_cand_type >= 0); | |
445 | ||
446 | if (strcmp(name, comp_class_cand_name) == 0 && | |
447 | comp_class_cand_type == type) { | |
448 | comp_class = bt_get(comp_class_candidate); | |
449 | break; | |
450 | } | |
451 | } | |
452 | ||
453 | goto end; | |
454 | ||
455 | error: | |
456 | BT_PUT(comp_class); | |
457 | ||
458 | end: | |
459 | return comp_class; | |
460 | } | |
461 | ||
33b34c43 PP |
462 | enum bt_plugin_status bt_plugin_add_component_class( |
463 | struct bt_plugin *plugin, struct bt_component_class *comp_class) | |
464 | { | |
465 | enum bt_plugin_status status = BT_PLUGIN_STATUS_OK; | |
466 | struct bt_component_class *comp_class_dup = NULL; | |
467 | int ret; | |
468 | int comp_class_index = -1; | |
469 | ||
470 | if (!plugin || !comp_class || plugin->frozen) { | |
471 | goto error; | |
472 | } | |
473 | ||
474 | /* Check for duplicate */ | |
475 | comp_class_dup = bt_plugin_get_component_class_by_name_and_type(plugin, | |
476 | bt_component_class_get_name(comp_class), | |
477 | bt_component_class_get_type(comp_class)); | |
478 | if (comp_class_dup) { | |
479 | printf_verbose("Plugin `%s`: adding component class with existing name `%s` and type %d\n", | |
55bb57e0 | 480 | bt_plugin_get_name(plugin), |
33b34c43 PP |
481 | bt_component_class_get_name(comp_class), |
482 | bt_component_class_get_type(comp_class)); | |
483 | goto error; | |
484 | } | |
485 | ||
486 | /* Add new component class */ | |
487 | comp_class_index = plugin->comp_classes->len; | |
488 | g_ptr_array_add(plugin->comp_classes, bt_get(comp_class)); | |
489 | ||
55bb57e0 PP |
490 | /* Special case for a shared object plugin */ |
491 | if (plugin->type == BT_PLUGIN_TYPE_SO) { | |
492 | ret = bt_plugin_so_on_add_component_class(plugin, comp_class); | |
493 | if (ret) { | |
494 | goto error; | |
495 | } | |
33b34c43 PP |
496 | } |
497 | ||
498 | goto end; | |
499 | ||
500 | error: | |
33b34c43 PP |
501 | /* Remove entry from plugin's component classes (if added) */ |
502 | if (comp_class_index >= 0) { | |
503 | g_ptr_array_remove_index(plugin->comp_classes, | |
504 | comp_class_index); | |
505 | } | |
506 | ||
507 | status = BT_PLUGIN_STATUS_ERROR; | |
508 | ||
509 | end: | |
510 | bt_put(comp_class_dup); | |
511 | return status; | |
512 | } |