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