lib: rename `BABELTRACE_DISABLE_PYTHON_PLUGINS` -> `LIBBABELTRACE2_...`
[babeltrace.git] / src / python-plugin-provider / python-plugin-provider.c
1 /*
2 * python-plugin-provider.c
3 *
4 * Babeltrace Python plugin provider
5 *
6 * Copyright 2017 Philippe Proulx <pproulx@efficios.com>
7 *
8 * Permission is hereby granted, free of charge, to any person obtaining a copy
9 * of this software and associated documentation files (the "Software"), to deal
10 * in the Software without restriction, including without limitation the rights
11 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 * copies of the Software, and to permit persons to whom the Software is
13 * furnished to do so, subject to the following conditions:
14 *
15 * The above copyright notice and this permission notice shall be included in
16 * all copies or substantial portions of the Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 * SOFTWARE.
25 */
26
27 #define BT_LOG_TAG "LIB/PLUGIN-PY"
28
29 #include "lib/logging.h"
30 #include "common/macros.h"
31 #include "compat/compiler.h"
32 #include <babeltrace2/plugin/plugin-const.h>
33 #include "lib/plugin/plugin.h"
34 #include <babeltrace2/graph/component-class.h>
35 #include <babeltrace2/current-thread.h>
36 #include "lib/graph/component-class.h"
37 #include "py-common/py-common.h"
38 #include <stdlib.h>
39 #include <signal.h>
40 #include <Python.h>
41 #include <glib.h>
42 #include <gmodule.h>
43
44 #define PYTHON_PLUGIN_FILE_PREFIX "bt_plugin_"
45 #define PYTHON_PLUGIN_FILE_PREFIX_LEN (sizeof(PYTHON_PLUGIN_FILE_PREFIX) - 1)
46 #define PYTHON_PLUGIN_FILE_EXT ".py"
47 #define PYTHON_PLUGIN_FILE_EXT_LEN (sizeof(PYTHON_PLUGIN_FILE_EXT) - 1)
48
49 enum python_state {
50 /* init_python() not called yet */
51 PYTHON_STATE_NOT_INITED,
52
53 /* init_python() called once with success */
54 PYTHON_STATE_FULLY_INITIALIZED,
55
56 /* init_python() called once without success */
57 PYTHON_STATE_CANNOT_INITIALIZE,
58
59 /*
60 * init_python() called, but environment variable asks the
61 * Python interpreter not to be loaded.
62 */
63 PYTHON_STATE_WONT_INITIALIZE,
64 } python_state = PYTHON_STATE_NOT_INITED;
65
66 static PyObject *py_try_load_plugin_module_func = NULL;
67 static bool python_was_initialized_by_us;
68
69 static
70 void append_python_traceback_error_cause(void)
71 {
72 GString *exc = NULL;
73
74 if (Py_IsInitialized() && PyErr_Occurred()) {
75 exc = bt_py_common_format_current_exception(BT_LOG_OUTPUT_LEVEL);
76 if (!exc) {
77 BT_LOGE_STR("Failed to format Python exception.");
78 goto end;
79 }
80
81 (void) BT_CURRENT_THREAD_ERROR_APPEND_CAUSE_FROM_UNKNOWN(
82 "Babeltrace library", "%s", exc->str);
83 }
84
85 end:
86 if (exc) {
87 g_string_free(exc, TRUE);
88 }
89 }
90
91 static
92 void log_python_traceback(int log_level)
93 {
94 GString *exc = NULL;
95
96 if (Py_IsInitialized() && PyErr_Occurred()) {
97 exc = bt_py_common_format_current_exception(BT_LOG_OUTPUT_LEVEL);
98 if (!exc) {
99 BT_LOGE_STR("Failed to format Python exception.");
100 goto end;
101 }
102
103 BT_LOG_WRITE(log_level, BT_LOG_TAG,
104 "Exception occured: Python traceback:\n%s", exc->str);
105 }
106
107 end:
108 if (exc) {
109 g_string_free(exc, TRUE);
110 }
111 }
112
113 static
114 void pyerr_clear(void)
115 {
116 if (Py_IsInitialized()) {
117 PyErr_Clear();
118 }
119 }
120
121 static
122 int init_python(void)
123 {
124 int ret = BT_FUNC_STATUS_OK;
125 PyObject *py_bt2_py_plugin_mod = NULL;
126 const char *dis_python_env;
127 #ifndef __MINGW32__
128 sig_t old_sigint = signal(SIGINT, SIG_DFL);
129 #endif
130
131 switch (python_state) {
132 case PYTHON_STATE_NOT_INITED:
133 break;
134 case PYTHON_STATE_FULLY_INITIALIZED:
135 goto end;
136 case PYTHON_STATE_WONT_INITIALIZE:
137 ret = BT_FUNC_STATUS_NOT_FOUND;
138 goto end;
139 case PYTHON_STATE_CANNOT_INITIALIZE:
140 ret = BT_FUNC_STATUS_ERROR;
141 goto end;
142 default:
143 abort();
144 }
145
146 /*
147 * User can disable Python plugin support with the
148 * `LIBBABELTRACE2_DISABLE_PYTHON_PLUGINS` environment variable
149 * set to 1.
150 */
151 dis_python_env = getenv("LIBBABELTRACE2_DISABLE_PYTHON_PLUGINS");
152 if (dis_python_env && strcmp(dis_python_env, "1") == 0) {
153 BT_LOGI_STR("Python plugin support is disabled because the "
154 "`LIBBABELTRACE2_DISABLE_PYTHON_PLUGINS` environment "
155 "variable is set to `1`.");
156 python_state = PYTHON_STATE_WONT_INITIALIZE;
157 ret = BT_FUNC_STATUS_NOT_FOUND;
158 goto end;
159 }
160
161 if (!Py_IsInitialized()) {
162 BT_LOGI_STR("Python interpreter is not initialized: initializing Python interpreter.");
163 Py_InitializeEx(0);
164 python_was_initialized_by_us = true;
165 BT_LOGI("Initialized Python interpreter: version=\"%s\"",
166 Py_GetVersion());
167 } else {
168 BT_LOGI("Python interpreter is already initialized: version=\"%s\"",
169 Py_GetVersion());
170 }
171
172 py_bt2_py_plugin_mod = PyImport_ImportModule("bt2.py_plugin");
173 if (!py_bt2_py_plugin_mod) {
174 append_python_traceback_error_cause();
175 BT_LIB_LOGW_APPEND_CAUSE(
176 "Cannot import `bt2.py_plugin` Python module: "
177 "Python plugin support is disabled.");
178 python_state = PYTHON_STATE_CANNOT_INITIALIZE;
179 ret = BT_FUNC_STATUS_ERROR;
180 goto end;
181 }
182
183 py_try_load_plugin_module_func =
184 PyObject_GetAttrString(py_bt2_py_plugin_mod, "_try_load_plugin_module");
185 if (!py_try_load_plugin_module_func) {
186 append_python_traceback_error_cause();
187 BT_LIB_LOGW_APPEND_CAUSE(
188 "Cannot get `_try_load_plugin_module` attribute from `bt2.py_plugin` Python module: "
189 "Python plugin support is disabled.");
190 python_state = PYTHON_STATE_CANNOT_INITIALIZE;
191 ret = BT_FUNC_STATUS_ERROR;
192 goto end;
193 }
194
195 python_state = PYTHON_STATE_FULLY_INITIALIZED;
196
197 end:
198 #ifndef __MINGW32__
199 if (old_sigint != SIG_ERR) {
200 (void) signal(SIGINT, old_sigint);
201 }
202 #endif
203
204 log_python_traceback(ret == BT_FUNC_STATUS_ERROR ?
205 BT_LOG_WARNING : BT_LOG_INFO);
206 pyerr_clear();
207 Py_XDECREF(py_bt2_py_plugin_mod);
208 return ret;
209 }
210
211 __attribute__((destructor)) static
212 void fini_python(void) {
213 if (Py_IsInitialized() && python_was_initialized_by_us) {
214 if (py_try_load_plugin_module_func) {
215 Py_DECREF(py_try_load_plugin_module_func);
216 py_try_load_plugin_module_func = NULL;
217 }
218
219 Py_Finalize();
220 BT_LOGI_STR("Finalized Python interpreter.");
221 }
222
223 python_state = PYTHON_STATE_NOT_INITED;
224 }
225
226 static
227 int bt_plugin_from_python_plugin_info(PyObject *plugin_info,
228 bool fail_on_load_error, bt_plugin **plugin_out)
229 {
230 int status = BT_FUNC_STATUS_OK;
231 PyObject *py_name = NULL;
232 PyObject *py_author = NULL;
233 PyObject *py_description = NULL;
234 PyObject *py_license = NULL;
235 PyObject *py_version = NULL;
236 PyObject *py_comp_class_addrs = NULL;
237 const char *name = NULL;
238 const char *author = NULL;
239 const char *description = NULL;
240 const char *license = NULL;
241 unsigned int major = 0, minor = 0, patch = 0;
242 const char *version_extra = NULL;
243
244 BT_ASSERT(plugin_out);
245 *plugin_out = NULL;
246 BT_ASSERT(plugin_info);
247 BT_ASSERT(python_state == PYTHON_STATE_FULLY_INITIALIZED);
248 py_name = PyObject_GetAttrString(plugin_info, "name");
249 if (!py_name) {
250 if (fail_on_load_error) {
251 append_python_traceback_error_cause();
252 BT_LIB_LOGW_APPEND_CAUSE(
253 "Cannot find `name` attribute in Python plugin info object: "
254 "py-plugin-info-addr=%p", plugin_info);
255 status = BT_FUNC_STATUS_ERROR;
256 } else {
257 BT_LIB_LOGW(
258 "Cannot find `name` attribute in Python plugin info object: "
259 "py-plugin-info-addr=%p", plugin_info);
260 status = BT_FUNC_STATUS_NOT_FOUND;
261 }
262
263 goto error;
264 }
265
266 py_author = PyObject_GetAttrString(plugin_info, "author");
267 if (!py_author) {
268 if (fail_on_load_error) {
269 append_python_traceback_error_cause();
270 BT_LIB_LOGW_APPEND_CAUSE(
271 "Cannot find `author` attribute in Python plugin info object: "
272 "py-plugin-info-addr=%p", plugin_info);
273 status = BT_FUNC_STATUS_ERROR;
274 } else {
275 BT_LIB_LOGW(
276 "Cannot find `author` attribute in Python plugin info object: "
277 "py-plugin-info-addr=%p", plugin_info);
278 status = BT_FUNC_STATUS_NOT_FOUND;
279 }
280
281 goto error;
282 }
283
284 py_description = PyObject_GetAttrString(plugin_info, "description");
285 if (!py_description) {
286 if (fail_on_load_error) {
287 append_python_traceback_error_cause();
288 BT_LIB_LOGW_APPEND_CAUSE(
289 "Cannot find `description` attribute in Python plugin info object: "
290 "py-plugin-info-addr=%p", plugin_info);
291 status = BT_FUNC_STATUS_ERROR;
292 } else {
293 BT_LIB_LOGW(
294 "Cannot find `description` attribute in Python plugin info object: "
295 "py-plugin-info-addr=%p", plugin_info);
296 status = BT_FUNC_STATUS_NOT_FOUND;
297 }
298
299 goto error;
300 }
301
302 py_license = PyObject_GetAttrString(plugin_info, "license");
303 if (!py_license) {
304 if (fail_on_load_error) {
305 append_python_traceback_error_cause();
306 BT_LIB_LOGW_APPEND_CAUSE(
307 "Cannot find `license` attribute in Python plugin info object: "
308 "py-plugin-info-addr=%p", plugin_info);
309 status = BT_FUNC_STATUS_ERROR;
310 } else {
311 BT_LIB_LOGW(
312 "Cannot find `license` attribute in Python plugin info object: "
313 "py-plugin-info-addr=%p", plugin_info);
314 status = BT_FUNC_STATUS_NOT_FOUND;
315 }
316
317 goto error;
318 }
319
320 py_version = PyObject_GetAttrString(plugin_info, "version");
321 if (!py_version) {
322 if (fail_on_load_error) {
323 append_python_traceback_error_cause();
324 BT_LIB_LOGW_APPEND_CAUSE(
325 "Cannot find `version` attribute in Python plugin info object: "
326 "py-plugin-info-addr=%p", plugin_info);
327 status = BT_FUNC_STATUS_ERROR;
328 } else {
329 BT_LIB_LOGW(
330 "Cannot find `version` attribute in Python plugin info object: "
331 "py-plugin-info-addr=%p", plugin_info);
332 status = BT_FUNC_STATUS_NOT_FOUND;
333 }
334
335 goto error;
336 }
337
338 py_comp_class_addrs = PyObject_GetAttrString(plugin_info,
339 "comp_class_addrs");
340 if (!py_comp_class_addrs) {
341 if (fail_on_load_error) {
342 append_python_traceback_error_cause();
343 BT_LIB_LOGW_APPEND_CAUSE(
344 "Cannot find `comp_class_addrs` attribute in Python plugin info object: "
345 "py-plugin-info-addr=%p", plugin_info);
346 status = BT_FUNC_STATUS_ERROR;
347 } else {
348 BT_LIB_LOGW(
349 "Cannot find `comp_class_addrs` attribute in Python plugin info object: "
350 "py-plugin-info-addr=%p", plugin_info);
351 status = BT_FUNC_STATUS_NOT_FOUND;
352 }
353
354 goto error;
355 }
356
357 if (PyUnicode_Check(py_name)) {
358 name = PyUnicode_AsUTF8(py_name);
359 if (!name) {
360 if (fail_on_load_error) {
361 append_python_traceback_error_cause();
362 BT_LIB_LOGW_APPEND_CAUSE(
363 "Cannot decode Python plugin name string: "
364 "py-plugin-info-addr=%p", plugin_info);
365 status = BT_FUNC_STATUS_ERROR;
366 } else {
367 BT_LIB_LOGW(
368 "Cannot decode Python plugin name string: "
369 "py-plugin-info-addr=%p", plugin_info);
370 status = BT_FUNC_STATUS_NOT_FOUND;
371 }
372
373 goto error;
374 }
375 } else {
376 /* Plugin name is mandatory */
377 if (fail_on_load_error) {
378 append_python_traceback_error_cause();
379 BT_LIB_LOGW_APPEND_CAUSE(
380 "Plugin name is not a string: "
381 "py-plugin-info-addr=%p", plugin_info);
382 status = BT_FUNC_STATUS_ERROR;
383 } else {
384 BT_LIB_LOGW(
385 "Plugin name is not a string: "
386 "py-plugin-info-addr=%p", plugin_info);
387 status = BT_FUNC_STATUS_NOT_FOUND;
388 }
389
390 goto error;
391 }
392
393 if (PyUnicode_Check(py_author)) {
394 author = PyUnicode_AsUTF8(py_author);
395 if (!author) {
396 if (fail_on_load_error) {
397 append_python_traceback_error_cause();
398 BT_LIB_LOGW_APPEND_CAUSE(
399 "Cannot decode Python plugin author string: "
400 "py-plugin-info-addr=%p", plugin_info);
401 status = BT_FUNC_STATUS_ERROR;
402 } else {
403 BT_LIB_LOGW(
404 "Cannot decode Python plugin author string: "
405 "py-plugin-info-addr=%p", plugin_info);
406 status = BT_FUNC_STATUS_NOT_FOUND;
407 }
408
409 goto error;
410 }
411 }
412
413 if (PyUnicode_Check(py_description)) {
414 description = PyUnicode_AsUTF8(py_description);
415 if (!description) {
416 if (fail_on_load_error) {
417 append_python_traceback_error_cause();
418 BT_LIB_LOGW_APPEND_CAUSE(
419 "Cannot decode Python plugin description string: "
420 "py-plugin-info-addr=%p", plugin_info);
421 status = BT_FUNC_STATUS_ERROR;
422 } else {
423 BT_LIB_LOGW(
424 "Cannot decode Python plugin description string: "
425 "py-plugin-info-addr=%p", plugin_info);
426 status = BT_FUNC_STATUS_NOT_FOUND;
427 }
428
429 goto error;
430 }
431 }
432
433 if (PyUnicode_Check(py_license)) {
434 license = PyUnicode_AsUTF8(py_license);
435 if (!license) {
436 if (fail_on_load_error) {
437 append_python_traceback_error_cause();
438 BT_LIB_LOGW_APPEND_CAUSE(
439 "Cannot decode Python plugin license string: "
440 "py-plugin-info-addr=%p", plugin_info);
441 status = BT_FUNC_STATUS_ERROR;
442 } else {
443 BT_LIB_LOGW(
444 "Cannot decode Python plugin license string: "
445 "py-plugin-info-addr=%p", plugin_info);
446 status = BT_FUNC_STATUS_NOT_FOUND;
447 }
448
449 goto error;
450 }
451 }
452
453 if (PyTuple_Check(py_version)) {
454 if (PyTuple_Size(py_version) >= 3) {
455 PyObject *py_major = PyTuple_GetItem(py_version, 0);
456 PyObject *py_minor = PyTuple_GetItem(py_version, 1);
457 PyObject *py_patch = PyTuple_GetItem(py_version, 2);
458
459 BT_ASSERT(py_major);
460 BT_ASSERT(py_minor);
461 BT_ASSERT(py_patch);
462
463 if (PyLong_Check(py_major)) {
464 major = PyLong_AsUnsignedLong(py_major);
465 }
466
467 if (PyLong_Check(py_minor)) {
468 minor = PyLong_AsUnsignedLong(py_minor);
469 }
470
471 if (PyLong_Check(py_patch)) {
472 patch = PyLong_AsUnsignedLong(py_patch);
473 }
474
475 if (PyErr_Occurred()) {
476 /* Overflow error, most probably */
477 if (fail_on_load_error) {
478 append_python_traceback_error_cause();
479 BT_LIB_LOGW_APPEND_CAUSE(
480 "Invalid Python plugin version format: "
481 "py-plugin-info-addr=%p", plugin_info);
482 status = BT_FUNC_STATUS_ERROR;
483 } else {
484 BT_LIB_LOGW(
485 "Invalid Python plugin version format: "
486 "py-plugin-info-addr=%p", plugin_info);
487 status = BT_FUNC_STATUS_NOT_FOUND;
488 }
489
490 goto error;
491 }
492 }
493
494 if (PyTuple_Size(py_version) >= 4) {
495 PyObject *py_extra = PyTuple_GetItem(py_version, 3);
496
497 BT_ASSERT(py_extra);
498
499 if (PyUnicode_Check(py_extra)) {
500 version_extra = PyUnicode_AsUTF8(py_extra);
501 if (!version_extra) {
502 if (fail_on_load_error) {
503 append_python_traceback_error_cause();
504 BT_LIB_LOGW_APPEND_CAUSE(
505 "Cannot decode Python plugin version's extra string: "
506 "py-plugin-info-addr=%p", plugin_info);
507 status = BT_FUNC_STATUS_ERROR;
508 } else {
509 BT_LIB_LOGW(
510 "Cannot decode Python plugin version's extra string: "
511 "py-plugin-info-addr=%p", plugin_info);
512 status = BT_FUNC_STATUS_NOT_FOUND;
513 }
514
515 goto error;
516 }
517 }
518 }
519 }
520
521 *plugin_out = bt_plugin_create_empty(BT_PLUGIN_TYPE_PYTHON);
522 if (!*plugin_out) {
523 BT_LIB_LOGE_APPEND_CAUSE("Cannot create empty plugin object.");
524 status = BT_FUNC_STATUS_MEMORY_ERROR;
525 goto error;
526 }
527
528 bt_plugin_set_name(*plugin_out, name);
529
530 if (description) {
531 bt_plugin_set_description(*plugin_out, description);
532 }
533
534 if (author) {
535 bt_plugin_set_author(*plugin_out, author);
536 }
537
538 if (license) {
539 bt_plugin_set_license(*plugin_out, license);
540 }
541
542 bt_plugin_set_version(*plugin_out, major, minor, patch, version_extra);
543
544 if (PyList_Check(py_comp_class_addrs)) {
545 size_t i;
546
547 for (i = 0; i < PyList_Size(py_comp_class_addrs); i++) {
548 bt_component_class *comp_class;
549 PyObject *py_comp_class_addr;
550
551 py_comp_class_addr =
552 PyList_GetItem(py_comp_class_addrs, i);
553 BT_ASSERT(py_comp_class_addr);
554 if (PyLong_Check(py_comp_class_addr)) {
555 comp_class = PyLong_AsVoidPtr(py_comp_class_addr);
556 } else {
557 if (fail_on_load_error) {
558 append_python_traceback_error_cause();
559 BT_LIB_LOGW_APPEND_CAUSE(
560 "Component class address is not an integer in Python plugin info object: "
561 "py-plugin-info-addr=%p, index=%zu",
562 plugin_info, i);
563 status = BT_FUNC_STATUS_ERROR;
564 } else {
565 BT_LIB_LOGW(
566 "Component class address is not an integer in Python plugin info object: "
567 "py-plugin-info-addr=%p, index=%zu",
568 plugin_info, i);
569 status = BT_FUNC_STATUS_NOT_FOUND;
570 }
571
572 continue;
573 }
574
575 status = bt_plugin_add_component_class(*plugin_out,
576 comp_class);
577 if (status < 0) {
578 BT_LIB_LOGE_APPEND_CAUSE(
579 "Cannot add component class to plugin: "
580 "py-plugin-info-addr=%p, "
581 "plugin-addr=%p, plugin-name=\"%s\", "
582 "comp-class-addr=%p, "
583 "comp-class-name=\"%s\", "
584 "comp-class-type=%s",
585 plugin_info, *plugin_out,
586 bt_plugin_get_name(*plugin_out),
587 comp_class,
588 bt_component_class_get_name(comp_class),
589 bt_component_class_type_string(
590 bt_component_class_get_type(comp_class)));
591 goto error;
592 }
593 }
594 }
595
596 goto end;
597
598 error:
599 BT_ASSERT(status != BT_FUNC_STATUS_OK);
600 log_python_traceback(fail_on_load_error ? BT_LOG_WARNING : BT_LOG_INFO);
601 pyerr_clear();
602 BT_OBJECT_PUT_REF_AND_RESET(*plugin_out);
603
604 end:
605 Py_XDECREF(py_name);
606 Py_XDECREF(py_author);
607 Py_XDECREF(py_description);
608 Py_XDECREF(py_license);
609 Py_XDECREF(py_version);
610 Py_XDECREF(py_comp_class_addrs);
611 return status;
612 }
613
614 G_MODULE_EXPORT
615 int bt_plugin_python_create_all_from_file(const char *path,
616 bool fail_on_load_error, struct bt_plugin_set **plugin_set_out)
617 {
618 bt_plugin *plugin = NULL;
619 PyObject *py_plugin_info = NULL;
620 gchar *basename = NULL;
621 size_t path_len;
622 int status = BT_FUNC_STATUS_OK;
623
624 BT_ASSERT(path);
625
626 if (python_state == PYTHON_STATE_CANNOT_INITIALIZE) {
627 /*
628 * We do not even care about the rest of the function
629 * here because we already know Python cannot be fully
630 * initialized.
631 */
632 BT_LIB_LOGE_APPEND_CAUSE(
633 "Python interpreter could not be initialized previously.");
634 status = BT_FUNC_STATUS_ERROR;
635 goto error;
636 } else if (python_state == PYTHON_STATE_WONT_INITIALIZE) {
637 /*
638 * This is not an error: the environment requires that
639 * Python plugins are disabled, so it's simply not
640 * found.
641 */
642 BT_LOGI_STR("Python plugin support was disabled previously "
643 "because the `LIBBABELTRACE2_DISABLE_PYTHON_PLUGINS` "
644 "environment variable is set to `1`.");
645 status = BT_FUNC_STATUS_NOT_FOUND;
646 goto error;
647 }
648
649 BT_LOGI("Trying to create all Python plugins from file: path=\"%s\"",
650 path);
651 path_len = strlen(path);
652
653 /* File name ends with `.py` */
654 if (strncmp(path + path_len - PYTHON_PLUGIN_FILE_EXT_LEN,
655 PYTHON_PLUGIN_FILE_EXT,
656 PYTHON_PLUGIN_FILE_EXT_LEN) != 0) {
657 BT_LOGI("Skipping non-Python file: path=\"%s\"", path);
658 status = BT_FUNC_STATUS_NOT_FOUND;
659 goto error;
660 }
661
662 /* File name starts with `bt_plugin_` */
663 basename = g_path_get_basename(path);
664 if (!basename) {
665 BT_LIB_LOGE_APPEND_CAUSE(
666 "Cannot get path's basename: path=\"%s\"", path);
667 status = BT_FUNC_STATUS_ERROR;
668 goto error;
669 }
670
671 if (strncmp(basename, PYTHON_PLUGIN_FILE_PREFIX,
672 PYTHON_PLUGIN_FILE_PREFIX_LEN) != 0) {
673 BT_LOGI("Skipping Python file not starting with `%s`: "
674 "path=\"%s\"", PYTHON_PLUGIN_FILE_PREFIX, path);
675 status = BT_FUNC_STATUS_NOT_FOUND;
676 goto error;
677 }
678
679 /*
680 * Initialize Python now.
681 *
682 * This is not done in the library contructor because the
683 * interpreter is somewhat slow to initialize. If you don't
684 * have any potential Python plugins, you don't need to endure
685 * this waiting time everytime you load the library.
686 */
687 status = init_python();
688 if (status != BT_FUNC_STATUS_OK) {
689 /* init_python() logs and append errors */
690 goto error;
691 }
692
693 /*
694 * Call bt2.py_plugin._try_load_plugin_module() with this path
695 * to get plugin info if the plugin is loadable and complete.
696 * This function returns None when there's an error, but just in
697 * case we also manually clear the last Python error state.
698 */
699 BT_LOGD_STR("Getting Python plugin info object from Python module.");
700 py_plugin_info = PyObject_CallFunction(py_try_load_plugin_module_func,
701 "(s)", path);
702 if (!py_plugin_info || py_plugin_info == Py_None) {
703 if (fail_on_load_error) {
704 append_python_traceback_error_cause();
705 BT_LIB_LOGW_APPEND_CAUSE(
706 "Cannot load Python plugin: path=\"%s\"", path);
707 status = BT_FUNC_STATUS_ERROR;
708 } else {
709 BT_LIB_LOGW(
710 "Cannot load Python plugin: path=\"%s\"", path);
711 status = BT_FUNC_STATUS_NOT_FOUND;
712 }
713
714 goto error;
715 }
716
717 /*
718 * Get bt_plugin from plugin info object.
719 */
720 plugin = NULL;
721 status = bt_plugin_from_python_plugin_info(py_plugin_info,
722 fail_on_load_error, &plugin);
723 if (status < 0) {
724 /*
725 * bt_plugin_from_python_plugin_info() handles
726 * `fail_on_load_error`, so this is a "real" error.
727 */
728 BT_LIB_LOGW_APPEND_CAUSE(
729 "Cannot create plugin object from Python plugin info object: "
730 "path=\"%s\", py-plugin-info-addr=%p",
731 path, py_plugin_info);
732 BT_ASSERT(!plugin);
733 goto error;
734 } else if (status == BT_FUNC_STATUS_NOT_FOUND) {
735 BT_ASSERT(!plugin);
736 goto error;
737 }
738
739 BT_ASSERT(status == BT_FUNC_STATUS_OK);
740 BT_ASSERT(plugin);
741 bt_plugin_set_path(plugin, path);
742 *plugin_set_out = bt_plugin_set_create();
743 if (!*plugin_set_out) {
744 BT_LIB_LOGE_APPEND_CAUSE("Cannot create empty plugin set.");
745 status = BT_FUNC_STATUS_MEMORY_ERROR;
746 goto error;
747 }
748
749 bt_plugin_set_add_plugin(*plugin_set_out, plugin);
750 BT_LOGD("Created all Python plugins from file: path=\"%s\", "
751 "plugin-addr=%p, plugin-name=\"%s\"",
752 path, plugin, bt_plugin_get_name(plugin));
753 goto end;
754
755 error:
756 BT_ASSERT(status != BT_FUNC_STATUS_OK);
757 log_python_traceback(BT_LOG_WARNING);
758 pyerr_clear();
759 BT_OBJECT_PUT_REF_AND_RESET(*plugin_set_out);
760
761 end:
762 bt_plugin_put_ref(plugin);
763 Py_XDECREF(py_plugin_info);
764
765 g_free(basename);
766
767 return status;
768 }
This page took 0.044923 seconds and 5 git commands to generate.