1 /**
2 * collectd - src/python.c
3 * Copyright (C) 2009 Sven Trenkel
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 * Authors:
24 * Sven Trenkel <collectd at semidefinite.de>
25 **/
27 #include <Python.h>
28 #include <structmember.h>
30 #include <signal.h>
32 #include "collectd.h"
34 #include "common.h"
36 #include "cpython.h"
38 typedef struct cpy_callback_s {
39 char *name;
40 PyObject *callback;
41 PyObject *data;
42 struct cpy_callback_s *next;
43 } cpy_callback_t;
45 static char log_doc[] = "This function sends a string to all logging plugins.";
47 static char get_ds_doc[] = "get_dataset(name) -> definition\n"
48 "\n"
49 "Returns the definition of a dataset specified by name.\n"
50 "\n"
51 "'name' is a string specifying the dataset to query.\n"
52 "'definition' is a list of 4-tuples. Every tuple represents a \n"
53 " data source within the data set and its 4 values are the \n"
54 " name, type, min and max value.\n"
55 " 'name' is a string.\n"
56 " 'type' is a string that is equal to either DS_TYPE_COUNTER,\n"
57 " DS_TYPE_GAUGE, DS_TYPE_DERIVE or DS_TYPE_ABSOLUTE.\n"
58 " 'min' and 'max' are either a float or None.";
60 static char flush_doc[] = "flush([plugin][, timeout][, identifier]) -> None\n"
61 "\n"
62 "Flushes the cache of another plugin.";
64 static char unregister_doc[] = "Unregisters a callback. This function needs exactly one parameter either\n"
65 "the function to unregister or the callback identifier to unregister.";
67 static char reg_log_doc[] = "register_log(callback[, data][, name]) -> identifier\n"
68 "\n"
69 "Register a callback function for log messages.\n"
70 "\n"
71 "'callback' is a callable object that will be called every time something\n"
72 " is logged.\n"
73 "'data' is an optional object that will be passed back to the callback\n"
74 " function every time it is called.\n"
75 "'name' is an optional identifier for this callback. The default name\n"
76 " is 'python.<module>'.\n"
77 " Every callback needs a unique identifier, so if you want to\n"
78 " register this callback multiple time from the same module you need\n"
79 " to specify a name here.\n"
80 "'identifier' is the full identifier assigned to this callback.\n"
81 "\n"
82 "The callback function will be called with two or three parameters:\n"
83 "severity: An integer that should be compared to the LOG_ constants.\n"
84 "message: The text to be logged.\n"
85 "data: The optional data parameter passed to the register function.\n"
86 " If the parameter was omitted it will be omitted here, too.";
88 static char reg_init_doc[] = "register_init(callback[, data][, name]) -> identifier\n"
89 "\n"
90 "Register a callback function that will be executed once after the config.\n"
91 "file has been read, all plugins heve been loaded and the collectd has\n"
92 "forked into the background.\n"
93 "\n"
94 "'callback' is a callable object that will be executed.\n"
95 "'data' is an optional object that will be passed back to the callback\n"
96 " function when it is called.\n"
97 "'name' is an optional identifier for this callback. The default name\n"
98 " is 'python.<module>'.\n"
99 " Every callback needs a unique identifier, so if you want to\n"
100 " register this callback multiple time from the same module you need\n"
101 " to specify a name here.\n"
102 "'identifier' is the full identifier assigned to this callback.\n"
103 "\n"
104 "The callback function will be called without parameters, except for\n"
105 "data if it was supplied.";
107 static char reg_config_doc[] = "register_config(callback[, data][, name]) -> identifier\n"
108 "\n"
109 "Register a callback function for config file entries.\n"
110 "'callback' is a callable object that will be called for every config block.\n"
111 "'data' is an optional object that will be passed back to the callback\n"
112 " function every time it is called.\n"
113 "'name' is an optional identifier for this callback. The default name\n"
114 " is 'python.<module>'.\n"
115 " Every callback needs a unique identifier, so if you want to\n"
116 " register this callback multiple time from the same module you need\n"
117 " to specify a name here.\n"
118 "'identifier' is the full identifier assigned to this callback.\n"
119 "\n"
120 "The callback function will be called with one or two parameters:\n"
121 "config: A Config object.\n"
122 "data: The optional data parameter passed to the register function.\n"
123 " If the parameter was omitted it will be omitted here, too.";
125 static char reg_read_doc[] = "register_read(callback[, interval][, data][, name]) -> identifier\n"
126 "\n"
127 "Register a callback function for reading data. It will just be called\n"
128 "in a fixed interval to signal that it's time to dispatch new values.\n"
129 "'callback' is a callable object that will be called every time something\n"
130 " is logged.\n"
131 "'interval' is the number of seconds between between calls to the callback\n"
132 " function. Full float precision is supported here.\n"
133 "'data' is an optional object that will be passed back to the callback\n"
134 " function every time it is called.\n"
135 "'name' is an optional identifier for this callback. The default name\n"
136 " is 'python.<module>'.\n"
137 " Every callback needs a unique identifier, so if you want to\n"
138 " register this callback multiple time from the same module you need\n"
139 " to specify a name here.\n"
140 "'identifier' is the full identifier assigned to this callback.\n"
141 "\n"
142 "The callback function will be called without parameters, except for\n"
143 "data if it was supplied.";
145 static char reg_write_doc[] = "register_write(callback[, data][, name]) -> identifier\n"
146 "\n"
147 "Register a callback function to receive values dispatched by other plugins.\n"
148 "'callback' is a callable object that will be called every time a value\n"
149 " is dispatched.\n"
150 "'data' is an optional object that will be passed back to the callback\n"
151 " function every time it is called.\n"
152 "'name' is an optional identifier for this callback. The default name\n"
153 " is 'python.<module>'.\n"
154 " Every callback needs a unique identifier, so if you want to\n"
155 " register this callback multiple time from the same module you need\n"
156 " to specify a name here.\n"
157 "'identifier' is the full identifier assigned to this callback.\n"
158 "\n"
159 "The callback function will be called with one or two parameters:\n"
160 "values: A Values object which is a copy of the dispatched values.\n"
161 "data: The optional data parameter passed to the register function.\n"
162 " If the parameter was omitted it will be omitted here, too.";
164 static char reg_notification_doc[] = "register_notification(callback[, data][, name]) -> identifier\n"
165 "\n"
166 "Register a callback function for notifications.\n"
167 "'callback' is a callable object that will be called every time a notification\n"
168 " is dispatched.\n"
169 "'data' is an optional object that will be passed back to the callback\n"
170 " function every time it is called.\n"
171 "'name' is an optional identifier for this callback. The default name\n"
172 " is 'python.<module>'.\n"
173 " Every callback needs a unique identifier, so if you want to\n"
174 " register this callback multiple time from the same module you need\n"
175 " to specify a name here.\n"
176 "'identifier' is the full identifier assigned to this callback.\n"
177 "\n"
178 "The callback function will be called with one or two parameters:\n"
179 "notification: A copy of the notification that was dispatched.\n"
180 "data: The optional data parameter passed to the register function.\n"
181 " If the parameter was omitted it will be omitted here, too.";
183 static char reg_flush_doc[] = "register_flush(callback[, data][, name]) -> identifier\n"
184 "\n"
185 "Register a callback function for flush messages.\n"
186 "'callback' is a callable object that will be called every time a plugin\n"
187 " requests a flush for either this or all plugins.\n"
188 "'data' is an optional object that will be passed back to the callback\n"
189 " function every time it is called.\n"
190 "'name' is an optional identifier for this callback. The default name\n"
191 " is 'python.<module>'.\n"
192 " Every callback needs a unique identifier, so if you want to\n"
193 " register this callback multiple time from the same module you need\n"
194 " to specify a name here.\n"
195 "'identifier' is the full identifier assigned to this callback.\n"
196 "\n"
197 "The callback function will be called with two or three parameters:\n"
198 "timeout: Indicates that only data older than 'timeout' seconds is to\n"
199 " be flushed.\n"
200 "id: Specifies which values are to be flushed. Might be None.\n"
201 "data: The optional data parameter passed to the register function.\n"
202 " If the parameter was omitted it will be omitted here, too.";
204 static char reg_shutdown_doc[] = "register_shutdown(callback[, data][, name]) -> identifier\n"
205 "\n"
206 "Register a callback function for collectd shutdown.\n"
207 "'callback' is a callable object that will be called once collectd is\n"
208 " shutting down.\n"
209 "'data' is an optional object that will be passed back to the callback\n"
210 " function if it is called.\n"
211 "'name' is an optional identifier for this callback. The default name\n"
212 " is 'python.<module>'.\n"
213 " Every callback needs a unique identifier, so if you want to\n"
214 " register this callback multiple time from the same module you need\n"
215 " to specify a name here.\n"
216 "'identifier' is the full identifier assigned to this callback.\n"
217 "\n"
218 "The callback function will be called with no parameters except for\n"
219 " data if it was supplied.";
222 static pthread_t main_thread;
223 static PyOS_sighandler_t python_sigint_handler;
224 static _Bool do_interactive = 0;
226 /* This is our global thread state. Python saves some stuff in thread-local
227 * storage. So if we allow the interpreter to run in the background
228 * (the scriptwriters might have created some threads from python), we have
229 * to save the state so we can resume it later after shutdown. */
231 static PyThreadState *state;
233 static PyObject *sys_path, *cpy_format_exception;
235 static cpy_callback_t *cpy_config_callbacks;
236 static cpy_callback_t *cpy_init_callbacks;
237 static cpy_callback_t *cpy_shutdown_callbacks;
239 /* Make sure to hold the GIL while modifying these. */
240 static int cpy_shutdown_triggered = 0;
241 static int cpy_num_callbacks = 0;
243 static void cpy_destroy_user_data(void *data) {
244 cpy_callback_t *c = data;
245 free(c->name);
246 CPY_LOCK_THREADS
247 Py_DECREF(c->callback);
248 Py_XDECREF(c->data);
249 free(c);
250 --cpy_num_callbacks;
251 if (!cpy_num_callbacks && cpy_shutdown_triggered) {
252 Py_Finalize();
253 return;
254 }
255 CPY_RELEASE_THREADS
256 }
258 /* You must hold the GIL to call this function!
259 * But if you managed to extract the callback parameter then you probably already do. */
261 static void cpy_build_name(char *buf, size_t size, PyObject *callback, const char *name) {
262 const char *module = NULL;
263 PyObject *mod = NULL;
265 if (name != NULL) {
266 snprintf(buf, size, "python.%s", name);
267 return;
268 }
270 mod = PyObject_GetAttrString(callback, "__module__"); /* New reference. */
271 if (mod != NULL)
272 module = cpy_unicode_or_bytes_to_string(&mod);
274 if (module != NULL) {
275 snprintf(buf, size, "python.%s", module);
276 Py_XDECREF(mod);
277 PyErr_Clear();
278 return;
279 }
280 Py_XDECREF(mod);
282 snprintf(buf, size, "python.%p", callback);
283 PyErr_Clear();
284 }
286 void cpy_log_exception(const char *context) {
287 int l = 0;
288 const char *typename = NULL, *message = NULL;
289 PyObject *type, *value, *traceback, *tn, *m, *list;
291 PyErr_Fetch(&type, &value, &traceback);
292 PyErr_NormalizeException(&type, &value, &traceback);
293 if (type == NULL) return;
294 tn = PyObject_GetAttrString(type, "__name__"); /* New reference. */
295 m = PyObject_Str(value); /* New reference. */
296 if (tn != NULL)
297 typename = cpy_unicode_or_bytes_to_string(&tn);
298 if (m != NULL)
299 message = cpy_unicode_or_bytes_to_string(&m);
300 if (typename == NULL)
301 typename = "NamelessException";
302 if (message == NULL)
303 message = "N/A";
304 Py_BEGIN_ALLOW_THREADS
305 ERROR("Unhandled python exception in %s: %s: %s", context, typename, message);
306 Py_END_ALLOW_THREADS
307 Py_XDECREF(tn);
308 Py_XDECREF(m);
309 if (!cpy_format_exception || !traceback) {
310 PyErr_Clear();
311 Py_DECREF(type);
312 Py_XDECREF(value);
313 Py_XDECREF(traceback);
314 return;
315 }
316 list = PyObject_CallFunction(cpy_format_exception, "NNN", type, value, traceback); /* New reference. Steals references from "type", "value" and "traceback". */
317 if (list)
318 l = PyObject_Length(list);
320 for (int i = 0; i < l; ++i) {
321 PyObject *line;
322 char const *msg;
323 char *cpy;
325 line = PyList_GET_ITEM(list, i); /* Borrowed reference. */
326 Py_INCREF(line);
328 msg = cpy_unicode_or_bytes_to_string(&line);
329 Py_DECREF(line);
330 if (msg == NULL)
331 continue;
333 cpy = strdup(msg);
334 if (cpy == NULL)
335 continue;
337 if (cpy[strlen(cpy) - 1] == '\n')
338 cpy[strlen(cpy) - 1] = 0;
340 Py_BEGIN_ALLOW_THREADS
341 ERROR("%s", cpy);
342 Py_END_ALLOW_THREADS
344 free(cpy);
345 }
347 Py_XDECREF(list);
348 PyErr_Clear();
349 }
351 static int cpy_read_callback(user_data_t *data) {
352 cpy_callback_t *c = data->data;
353 PyObject *ret;
355 CPY_LOCK_THREADS
356 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
357 if (ret == NULL) {
358 cpy_log_exception("read callback");
359 } else {
360 Py_DECREF(ret);
361 }
362 CPY_RELEASE_THREADS
363 if (ret == NULL)
364 return 1;
365 return 0;
366 }
368 static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_list, user_data_t *data) {
369 cpy_callback_t *c = data->data;
370 PyObject *ret, *list, *temp, *dict = NULL;
371 Values *v;
373 CPY_LOCK_THREADS
374 list = PyList_New(value_list->values_len); /* New reference. */
375 if (list == NULL) {
376 cpy_log_exception("write callback");
377 CPY_RETURN_FROM_THREADS 0;
378 }
379 for (size_t i = 0; i < value_list->values_len; ++i) {
380 if (ds->ds[i].type == DS_TYPE_COUNTER) {
381 PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].counter));
382 } else if (ds->ds[i].type == DS_TYPE_GAUGE) {
383 PyList_SetItem(list, i, PyFloat_FromDouble(value_list->values[i].gauge));
384 } else if (ds->ds[i].type == DS_TYPE_DERIVE) {
385 PyList_SetItem(list, i, PyLong_FromLongLong(value_list->values[i].derive));
386 } else if (ds->ds[i].type == DS_TYPE_ABSOLUTE) {
387 PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].absolute));
388 } else {
389 Py_BEGIN_ALLOW_THREADS
390 ERROR("cpy_write_callback: Unknown value type %d.", ds->ds[i].type);
391 Py_END_ALLOW_THREADS
392 Py_DECREF(list);
393 CPY_RETURN_FROM_THREADS 0;
394 }
395 if (PyErr_Occurred() != NULL) {
396 cpy_log_exception("value building for write callback");
397 Py_DECREF(list);
398 CPY_RETURN_FROM_THREADS 0;
399 }
400 }
401 dict = PyDict_New(); /* New reference. */
402 if (value_list->meta) {
403 char **table;
404 meta_data_t *meta = value_list->meta;
406 int num = meta_data_toc(meta, &table);
407 for (int i = 0; i < num; ++i) {
408 int type;
409 char *string;
410 int64_t si;
411 uint64_t ui;
412 double d;
413 _Bool b;
415 type = meta_data_type(meta, table[i]);
416 if (type == MD_TYPE_STRING) {
417 if (meta_data_get_string(meta, table[i], &string))
418 continue;
419 temp = cpy_string_to_unicode_or_bytes(string); /* New reference. */
420 free(string);
421 PyDict_SetItemString(dict, table[i], temp);
422 Py_XDECREF(temp);
423 } else if (type == MD_TYPE_SIGNED_INT) {
424 if (meta_data_get_signed_int(meta, table[i], &si))
425 continue;
426 temp = PyObject_CallFunctionObjArgs((void *) &SignedType, PyLong_FromLongLong(si), (void *) 0); /* New reference. */
427 PyDict_SetItemString(dict, table[i], temp);
428 Py_XDECREF(temp);
429 } else if (type == MD_TYPE_UNSIGNED_INT) {
430 if (meta_data_get_unsigned_int(meta, table[i], &ui))
431 continue;
432 temp = PyObject_CallFunctionObjArgs((void *) &UnsignedType, PyLong_FromUnsignedLongLong(ui), (void *) 0); /* New reference. */
433 PyDict_SetItemString(dict, table[i], temp);
434 Py_XDECREF(temp);
435 } else if (type == MD_TYPE_DOUBLE) {
436 if (meta_data_get_double(meta, table[i], &d))
437 continue;
438 temp = PyFloat_FromDouble(d); /* New reference. */
439 PyDict_SetItemString(dict, table[i], temp);
440 Py_XDECREF(temp);
441 } else if (type == MD_TYPE_BOOLEAN) {
442 if (meta_data_get_boolean(meta, table[i], &b))
443 continue;
444 if (b)
445 PyDict_SetItemString(dict, table[i], Py_True);
446 else
447 PyDict_SetItemString(dict, table[i], Py_False);
448 }
449 free(table[i]);
450 }
451 free(table);
452 }
453 v = (Values *) Values_New(); /* New reference. */
454 sstrncpy(v->data.host, value_list->host, sizeof(v->data.host));
455 sstrncpy(v->data.type, value_list->type, sizeof(v->data.type));
456 sstrncpy(v->data.type_instance, value_list->type_instance, sizeof(v->data.type_instance));
457 sstrncpy(v->data.plugin, value_list->plugin, sizeof(v->data.plugin));
458 sstrncpy(v->data.plugin_instance, value_list->plugin_instance, sizeof(v->data.plugin_instance));
459 v->data.time = CDTIME_T_TO_DOUBLE(value_list->time);
460 v->interval = CDTIME_T_TO_DOUBLE(value_list->interval);
461 Py_CLEAR(v->values);
462 v->values = list;
463 Py_CLEAR(v->meta);
464 v->meta = dict; /* Steals a reference. */
465 ret = PyObject_CallFunctionObjArgs(c->callback, v, c->data, (void *) 0); /* New reference. */
466 Py_XDECREF(v);
467 if (ret == NULL) {
468 cpy_log_exception("write callback");
469 } else {
470 Py_DECREF(ret);
471 }
472 CPY_RELEASE_THREADS
473 return 0;
474 }
476 static int cpy_notification_callback(const notification_t *notification, user_data_t *data) {
477 cpy_callback_t *c = data->data;
478 PyObject *ret, *notify;
479 Notification *n;
481 CPY_LOCK_THREADS
482 notify = Notification_New(); /* New reference. */
483 n = (Notification *) notify;
484 sstrncpy(n->data.host, notification->host, sizeof(n->data.host));
485 sstrncpy(n->data.type, notification->type, sizeof(n->data.type));
486 sstrncpy(n->data.type_instance, notification->type_instance, sizeof(n->data.type_instance));
487 sstrncpy(n->data.plugin, notification->plugin, sizeof(n->data.plugin));
488 sstrncpy(n->data.plugin_instance, notification->plugin_instance, sizeof(n->data.plugin_instance));
489 n->data.time = CDTIME_T_TO_DOUBLE(notification->time);
490 sstrncpy(n->message, notification->message, sizeof(n->message));
491 n->severity = notification->severity;
492 ret = PyObject_CallFunctionObjArgs(c->callback, n, c->data, (void *) 0); /* New reference. */
493 Py_XDECREF(notify);
494 if (ret == NULL) {
495 cpy_log_exception("notification callback");
496 } else {
497 Py_DECREF(ret);
498 }
499 CPY_RELEASE_THREADS
500 return 0;
501 }
503 static void cpy_log_callback(int severity, const char *message, user_data_t *data) {
504 cpy_callback_t * c = data->data;
505 PyObject *ret, *text;
507 CPY_LOCK_THREADS
508 text = cpy_string_to_unicode_or_bytes(message); /* New reference. */
509 if (c->data == NULL)
510 ret = PyObject_CallFunction(c->callback, "iN", severity, text); /* New reference. Steals a reference from "text". */
511 else
512 ret = PyObject_CallFunction(c->callback, "iNO", severity, text, c->data); /* New reference. Steals a reference from "text". */
514 if (ret == NULL) {
515 /* FIXME */
516 /* Do we really want to trigger a log callback because a log callback failed?
517 * Probably not. */
518 PyErr_Print();
519 /* In case someone wanted to be clever, replaced stderr and failed at that. */
520 PyErr_Clear();
521 } else {
522 Py_DECREF(ret);
523 }
524 CPY_RELEASE_THREADS
525 }
527 static void cpy_flush_callback(int timeout, const char *id, user_data_t *data) {
528 cpy_callback_t * c = data->data;
529 PyObject *ret, *text;
531 CPY_LOCK_THREADS
532 if (id) {
533 text = cpy_string_to_unicode_or_bytes(id);
534 } else {
535 text = Py_None;
536 Py_INCREF(text);
537 }
538 if (c->data == NULL)
539 ret = PyObject_CallFunction(c->callback, "iN", timeout, text); /* New reference. */
540 else
541 ret = PyObject_CallFunction(c->callback, "iNO", timeout, text, c->data); /* New reference. */
543 if (ret == NULL) {
544 cpy_log_exception("flush callback");
545 } else {
546 Py_DECREF(ret);
547 }
548 CPY_RELEASE_THREADS
549 }
551 static PyObject *cpy_register_generic(cpy_callback_t **list_head, PyObject *args, PyObject *kwds) {
552 char buf[512];
553 cpy_callback_t *c;
554 char *name = NULL;
555 PyObject *callback = NULL, *data = NULL, *mod = NULL;
556 static char *kwlist[] = {"callback", "data", "name", NULL};
558 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oet", kwlist, &callback, &data, NULL, &name) == 0) return NULL;
559 if (PyCallable_Check(callback) == 0) {
560 PyMem_Free(name);
561 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
562 return NULL;
563 }
564 cpy_build_name(buf, sizeof(buf), callback, name);
566 Py_INCREF(callback);
567 Py_XINCREF(data);
569 c = calloc(1, sizeof(*c));
570 if (c == NULL)
571 return NULL;
573 c->name = strdup(buf);
574 c->callback = callback;
575 c->data = data;
576 c->next = *list_head;
577 ++cpy_num_callbacks;
578 *list_head = c;
579 Py_XDECREF(mod);
580 PyMem_Free(name);
581 return cpy_string_to_unicode_or_bytes(buf);
582 }
584 static PyObject *float_or_none(float number) {
585 if (isnan(number)) {
586 Py_RETURN_NONE;
587 }
588 return PyFloat_FromDouble(number);
589 }
591 static PyObject *cpy_get_dataset(PyObject *self, PyObject *args) {
592 char *name;
593 const data_set_t *ds;
594 PyObject *list, *tuple;
596 if (PyArg_ParseTuple(args, "et", NULL, &name) == 0) return NULL;
597 ds = plugin_get_ds(name);
598 PyMem_Free(name);
599 if (ds == NULL) {
600 PyErr_Format(PyExc_TypeError, "Dataset %s not found", name);
601 return NULL;
602 }
603 list = PyList_New(ds->ds_num); /* New reference. */
604 for (size_t i = 0; i < ds->ds_num; ++i) {
605 tuple = PyTuple_New(4);
606 PyTuple_SET_ITEM(tuple, 0, cpy_string_to_unicode_or_bytes(ds->ds[i].name));
607 PyTuple_SET_ITEM(tuple, 1, cpy_string_to_unicode_or_bytes(DS_TYPE_TO_STRING(ds->ds[i].type)));
608 PyTuple_SET_ITEM(tuple, 2, float_or_none(ds->ds[i].min));
609 PyTuple_SET_ITEM(tuple, 3, float_or_none(ds->ds[i].max));
610 PyList_SET_ITEM(list, i, tuple);
611 }
612 return list;
613 }
615 static PyObject *cpy_flush(PyObject *self, PyObject *args, PyObject *kwds) {
616 int timeout = -1;
617 char *plugin = NULL, *identifier = NULL;
618 static char *kwlist[] = {"plugin", "timeout", "identifier", NULL};
620 if (PyArg_ParseTupleAndKeywords(args, kwds, "|etiet", kwlist, NULL, &plugin, &timeout, NULL, &identifier) == 0) return NULL;
621 Py_BEGIN_ALLOW_THREADS
622 plugin_flush(plugin, timeout, identifier);
623 Py_END_ALLOW_THREADS
624 PyMem_Free(plugin);
625 PyMem_Free(identifier);
626 Py_RETURN_NONE;
627 }
629 static PyObject *cpy_register_config(PyObject *self, PyObject *args, PyObject *kwds) {
630 return cpy_register_generic(&cpy_config_callbacks, args, kwds);
631 }
633 static PyObject *cpy_register_init(PyObject *self, PyObject *args, PyObject *kwds) {
634 return cpy_register_generic(&cpy_init_callbacks, args, kwds);
635 }
637 typedef int reg_function_t(const char *name, void *callback, void *data);
639 static PyObject *cpy_register_generic_userdata(void *reg, void *handler, PyObject *args, PyObject *kwds) {
640 char buf[512];
641 reg_function_t *register_function = (reg_function_t *) reg;
642 cpy_callback_t *c = NULL;
643 char *name = NULL;
644 PyObject *callback = NULL, *data = NULL;
645 static char *kwlist[] = {"callback", "data", "name", NULL};
647 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oet", kwlist, &callback, &data, NULL, &name) == 0) return NULL;
648 if (PyCallable_Check(callback) == 0) {
649 PyMem_Free(name);
650 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
651 return NULL;
652 }
653 cpy_build_name(buf, sizeof(buf), callback, name);
654 PyMem_Free(name);
656 Py_INCREF(callback);
657 Py_XINCREF(data);
659 c = calloc(1, sizeof(*c));
660 if (c == NULL)
661 return NULL;
663 c->name = strdup(buf);
664 c->callback = callback;
665 c->data = data;
666 c->next = NULL;
668 register_function(buf, handler, &(user_data_t) {
669 .data = c,
670 .free_func = cpy_destroy_user_data,
671 });
673 ++cpy_num_callbacks;
674 return cpy_string_to_unicode_or_bytes(buf);
675 }
677 static PyObject *cpy_register_read(PyObject *self, PyObject *args, PyObject *kwds) {
678 char buf[512];
679 cpy_callback_t *c = NULL;
680 double interval = 0;
681 char *name = NULL;
682 PyObject *callback = NULL, *data = NULL;
683 static char *kwlist[] = {"callback", "interval", "data", "name", NULL};
685 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|dOet", kwlist, &callback, &interval, &data, NULL, &name) == 0) return NULL;
686 if (PyCallable_Check(callback) == 0) {
687 PyMem_Free(name);
688 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
689 return NULL;
690 }
691 cpy_build_name(buf, sizeof(buf), callback, name);
692 PyMem_Free(name);
694 Py_INCREF(callback);
695 Py_XINCREF(data);
697 c = calloc(1, sizeof(*c));
698 if (c == NULL)
699 return NULL;
701 c->name = strdup(buf);
702 c->callback = callback;
703 c->data = data;
704 c->next = NULL;
706 plugin_register_complex_read(/* group = */ "python", buf,
707 cpy_read_callback, DOUBLE_TO_CDTIME_T (interval),
708 &(user_data_t) {
709 .data = c,
710 .free_func = cpy_destroy_user_data,
711 });
712 ++cpy_num_callbacks;
713 return cpy_string_to_unicode_or_bytes(buf);
714 }
716 static PyObject *cpy_register_log(PyObject *self, PyObject *args, PyObject *kwds) {
717 return cpy_register_generic_userdata((void *) plugin_register_log,
718 (void *) cpy_log_callback, args, kwds);
719 }
721 static PyObject *cpy_register_write(PyObject *self, PyObject *args, PyObject *kwds) {
722 return cpy_register_generic_userdata((void *) plugin_register_write,
723 (void *) cpy_write_callback, args, kwds);
724 }
726 static PyObject *cpy_register_notification(PyObject *self, PyObject *args, PyObject *kwds) {
727 return cpy_register_generic_userdata((void *) plugin_register_notification,
728 (void *) cpy_notification_callback, args, kwds);
729 }
731 static PyObject *cpy_register_flush(PyObject *self, PyObject *args, PyObject *kwds) {
732 return cpy_register_generic_userdata((void *) plugin_register_flush,
733 (void *) cpy_flush_callback, args, kwds);
734 }
736 static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args, PyObject *kwds) {
737 return cpy_register_generic(&cpy_shutdown_callbacks, args, kwds);
738 }
740 static PyObject *cpy_error(PyObject *self, PyObject *args) {
741 char *text;
742 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
743 Py_BEGIN_ALLOW_THREADS
744 plugin_log(LOG_ERR, "%s", text);
745 Py_END_ALLOW_THREADS
746 PyMem_Free(text);
747 Py_RETURN_NONE;
748 }
750 static PyObject *cpy_warning(PyObject *self, PyObject *args) {
751 char *text;
752 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
753 Py_BEGIN_ALLOW_THREADS
754 plugin_log(LOG_WARNING, "%s", text);
755 Py_END_ALLOW_THREADS
756 PyMem_Free(text);
757 Py_RETURN_NONE;
758 }
760 static PyObject *cpy_notice(PyObject *self, PyObject *args) {
761 char *text;
762 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
763 Py_BEGIN_ALLOW_THREADS
764 plugin_log(LOG_NOTICE, "%s", text);
765 Py_END_ALLOW_THREADS
766 PyMem_Free(text);
767 Py_RETURN_NONE;
768 }
770 static PyObject *cpy_info(PyObject *self, PyObject *args) {
771 char *text;
772 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
773 Py_BEGIN_ALLOW_THREADS
774 plugin_log(LOG_INFO, "%s", text);
775 Py_END_ALLOW_THREADS
776 PyMem_Free(text);
777 Py_RETURN_NONE;
778 }
780 static PyObject *cpy_debug(PyObject *self, PyObject *args) {
781 #ifdef COLLECT_DEBUG
782 char *text;
783 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
784 Py_BEGIN_ALLOW_THREADS
785 plugin_log(LOG_DEBUG, "%s", text);
786 Py_END_ALLOW_THREADS
787 PyMem_Free(text);
788 #endif
789 Py_RETURN_NONE;
790 }
792 static PyObject *cpy_unregister_generic(cpy_callback_t **list_head, PyObject *arg, const char *desc) {
793 char buf[512];
794 const char *name;
795 cpy_callback_t *prev = NULL, *tmp;
797 Py_INCREF(arg);
798 name = cpy_unicode_or_bytes_to_string(&arg);
799 if (name == NULL) {
800 PyErr_Clear();
801 if (!PyCallable_Check(arg)) {
802 PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
803 Py_DECREF(arg);
804 return NULL;
805 }
806 cpy_build_name(buf, sizeof(buf), arg, NULL);
807 name = buf;
808 }
809 for (tmp = *list_head; tmp; prev = tmp, tmp = tmp->next)
810 if (strcmp(name, tmp->name) == 0)
811 break;
813 Py_DECREF(arg);
814 if (tmp == NULL) {
815 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
816 return NULL;
817 }
818 /* Yes, this is actually safe. To call this function the caller has to
819 * hold the GIL. Well, safe as long as there is only one GIL anyway ... */
820 if (prev == NULL)
821 *list_head = tmp->next;
822 else
823 prev->next = tmp->next;
824 cpy_destroy_user_data(tmp);
825 Py_RETURN_NONE;
826 }
828 static void cpy_unregister_list(cpy_callback_t **list_head) {
829 cpy_callback_t *cur, *next;
830 for (cur = *list_head; cur; cur = next) {
831 next = cur->next;
832 cpy_destroy_user_data(cur);
833 }
834 *list_head = NULL;
835 }
837 typedef int cpy_unregister_function_t(const char *name);
839 static PyObject *cpy_unregister_generic_userdata(cpy_unregister_function_t *unreg, PyObject *arg, const char *desc) {
840 char buf[512];
841 const char *name;
843 Py_INCREF(arg);
844 name = cpy_unicode_or_bytes_to_string(&arg);
845 if (name == NULL) {
846 PyErr_Clear();
847 if (!PyCallable_Check(arg)) {
848 PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
849 Py_DECREF(arg);
850 return NULL;
851 }
852 cpy_build_name(buf, sizeof(buf), arg, NULL);
853 name = buf;
854 }
855 if (unreg(name) == 0) {
856 Py_DECREF(arg);
857 Py_RETURN_NONE;
858 }
859 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
860 Py_DECREF(arg);
861 return NULL;
862 }
864 static PyObject *cpy_unregister_log(PyObject *self, PyObject *arg) {
865 return cpy_unregister_generic_userdata(plugin_unregister_log, arg, "log");
866 }
868 static PyObject *cpy_unregister_init(PyObject *self, PyObject *arg) {
869 return cpy_unregister_generic(&cpy_init_callbacks, arg, "init");
870 }
872 static PyObject *cpy_unregister_config(PyObject *self, PyObject *arg) {
873 return cpy_unregister_generic(&cpy_config_callbacks, arg, "config");
874 }
876 static PyObject *cpy_unregister_read(PyObject *self, PyObject *arg) {
877 return cpy_unregister_generic_userdata(plugin_unregister_read, arg, "read");
878 }
880 static PyObject *cpy_unregister_write(PyObject *self, PyObject *arg) {
881 return cpy_unregister_generic_userdata(plugin_unregister_write, arg, "write");
882 }
884 static PyObject *cpy_unregister_notification(PyObject *self, PyObject *arg) {
885 return cpy_unregister_generic_userdata(plugin_unregister_notification, arg, "notification");
886 }
888 static PyObject *cpy_unregister_flush(PyObject *self, PyObject *arg) {
889 return cpy_unregister_generic_userdata(plugin_unregister_flush, arg, "flush");
890 }
892 static PyObject *cpy_unregister_shutdown(PyObject *self, PyObject *arg) {
893 return cpy_unregister_generic(&cpy_shutdown_callbacks, arg, "shutdown");
894 }
896 static PyMethodDef cpy_methods[] = {
897 {"debug", cpy_debug, METH_VARARGS, log_doc},
898 {"info", cpy_info, METH_VARARGS, log_doc},
899 {"notice", cpy_notice, METH_VARARGS, log_doc},
900 {"warning", cpy_warning, METH_VARARGS, log_doc},
901 {"error", cpy_error, METH_VARARGS, log_doc},
902 {"get_dataset", (PyCFunction) cpy_get_dataset, METH_VARARGS, get_ds_doc},
903 {"flush", (PyCFunction) cpy_flush, METH_VARARGS | METH_KEYWORDS, flush_doc},
904 {"register_log", (PyCFunction) cpy_register_log, METH_VARARGS | METH_KEYWORDS, reg_log_doc},
905 {"register_init", (PyCFunction) cpy_register_init, METH_VARARGS | METH_KEYWORDS, reg_init_doc},
906 {"register_config", (PyCFunction) cpy_register_config, METH_VARARGS | METH_KEYWORDS, reg_config_doc},
907 {"register_read", (PyCFunction) cpy_register_read, METH_VARARGS | METH_KEYWORDS, reg_read_doc},
908 {"register_write", (PyCFunction) cpy_register_write, METH_VARARGS | METH_KEYWORDS, reg_write_doc},
909 {"register_notification", (PyCFunction) cpy_register_notification, METH_VARARGS | METH_KEYWORDS, reg_notification_doc},
910 {"register_flush", (PyCFunction) cpy_register_flush, METH_VARARGS | METH_KEYWORDS, reg_flush_doc},
911 {"register_shutdown", (PyCFunction) cpy_register_shutdown, METH_VARARGS | METH_KEYWORDS, reg_shutdown_doc},
912 {"unregister_log", cpy_unregister_log, METH_O, unregister_doc},
913 {"unregister_init", cpy_unregister_init, METH_O, unregister_doc},
914 {"unregister_config", cpy_unregister_config, METH_O, unregister_doc},
915 {"unregister_read", cpy_unregister_read, METH_O, unregister_doc},
916 {"unregister_write", cpy_unregister_write, METH_O, unregister_doc},
917 {"unregister_notification", cpy_unregister_notification, METH_O, unregister_doc},
918 {"unregister_flush", cpy_unregister_flush, METH_O, unregister_doc},
919 {"unregister_shutdown", cpy_unregister_shutdown, METH_O, unregister_doc},
920 {0, 0, 0, 0}
921 };
923 static int cpy_shutdown(void) {
924 PyObject *ret;
926 if (!state) {
927 printf("================================================================\n");
928 printf("collectd shutdown while running an interactive session. This will\n");
929 printf("probably leave your terminal in a mess.\n");
930 printf("Run the command \"reset\" to get it back into a usable state.\n");
931 printf("You can press Ctrl+D in the interactive session to\n");
932 printf("close collectd and avoid this problem in the future.\n");
933 printf("================================================================\n");
934 }
936 CPY_LOCK_THREADS
938 for (cpy_callback_t *c = cpy_shutdown_callbacks; c; c = c->next) {
939 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
940 if (ret == NULL)
941 cpy_log_exception("shutdown callback");
942 else
943 Py_DECREF(ret);
944 }
945 PyErr_Print();
947 Py_BEGIN_ALLOW_THREADS
948 cpy_unregister_list(&cpy_config_callbacks);
949 cpy_unregister_list(&cpy_init_callbacks);
950 cpy_unregister_list(&cpy_shutdown_callbacks);
951 cpy_shutdown_triggered = 1;
952 Py_END_ALLOW_THREADS
954 if (!cpy_num_callbacks) {
955 Py_Finalize();
956 return 0;
957 }
959 CPY_RELEASE_THREADS
960 return 0;
961 }
963 static void *cpy_interactive(void *pipefd) {
964 PyOS_sighandler_t cur_sig;
966 /* Signal handler in a plugin? Bad stuff, but the best way to
967 * handle it I guess. In an interactive session people will
968 * press Ctrl+C at some time, which will generate a SIGINT.
969 * This will cause collectd to shutdown, thus killing the
970 * interactive interpreter, and leaving the terminal in a
971 * mess. Chances are, this isn't what the user wanted to do.
972 *
973 * So this is the plan:
974 * 1. Restore Python's own signal handler
975 * 2. Tell Python we just forked so it will accept this thread
976 * as the main one. No version of Python will ever handle
977 * interrupts anywhere but in the main thread.
978 * 3. After the interactive loop is done, restore collectd's
979 * SIGINT handler.
980 * 4. Raise SIGINT for a clean shutdown. The signal is sent to
981 * the main thread to ensure it wakes up the main interval
982 * sleep so that collectd shuts down immediately not in 10
983 * seconds.
984 *
985 * This will make sure that SIGINT won't kill collectd but
986 * still interrupt syscalls like sleep and pause. */
988 if (PyImport_ImportModule("readline") == NULL) {
989 /* This interactive session will suck. */
990 cpy_log_exception("interactive session init");
991 }
992 cur_sig = PyOS_setsig(SIGINT, python_sigint_handler);
993 PyOS_AfterFork();
994 PyEval_InitThreads();
995 close(*(int *) pipefd);
996 PyRun_InteractiveLoop(stdin, "<stdin>");
997 PyOS_setsig(SIGINT, cur_sig);
998 PyErr_Print();
999 state = PyEval_SaveThread();
1000 NOTICE("python: Interactive interpreter exited, stopping collectd ...");
1001 pthread_kill(main_thread, SIGINT);
1002 return NULL;
1003 }
1005 static int cpy_init(void) {
1006 PyObject *ret;
1007 int pipefd[2];
1008 char buf;
1009 static pthread_t thread;
1011 if (!Py_IsInitialized()) {
1012 WARNING("python: Plugin loaded but not configured.");
1013 plugin_unregister_shutdown("python");
1014 Py_Finalize();
1015 return 0;
1016 }
1017 main_thread = pthread_self();
1018 if (do_interactive) {
1019 if (pipe(pipefd)) {
1020 ERROR("python: Unable to create pipe.");
1021 return 1;
1022 }
1023 if (plugin_thread_create(&thread, NULL, cpy_interactive, pipefd + 1,
1024 "python interpreter")) {
1025 ERROR("python: Error creating thread for interactive interpreter.");
1026 }
1027 if(read(pipefd[0], &buf, 1))
1028 ;
1029 (void)close(pipefd[0]);
1030 } else {
1031 PyEval_InitThreads();
1032 state = PyEval_SaveThread();
1033 }
1034 CPY_LOCK_THREADS
1035 for (cpy_callback_t *c = cpy_init_callbacks; c; c = c->next) {
1036 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
1037 if (ret == NULL)
1038 cpy_log_exception("init callback");
1039 else
1040 Py_DECREF(ret);
1041 }
1042 CPY_RELEASE_THREADS
1044 return 0;
1045 }
1047 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
1048 PyObject *item, *values, *children, *tmp;
1050 if (parent == NULL)
1051 parent = Py_None;
1053 values = PyTuple_New(ci->values_num); /* New reference. */
1054 for (int i = 0; i < ci->values_num; ++i) {
1055 if (ci->values[i].type == OCONFIG_TYPE_STRING) {
1056 PyTuple_SET_ITEM(values, i, cpy_string_to_unicode_or_bytes(ci->values[i].value.string));
1057 } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
1058 PyTuple_SET_ITEM(values, i, PyFloat_FromDouble(ci->values[i].value.number));
1059 } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
1060 PyTuple_SET_ITEM(values, i, PyBool_FromLong(ci->values[i].value.boolean));
1061 }
1062 }
1064 tmp = cpy_string_to_unicode_or_bytes(ci->key);
1065 item = PyObject_CallFunction((void *) &ConfigType, "NONO", tmp, parent, values, Py_None);
1066 if (item == NULL)
1067 return NULL;
1068 children = PyTuple_New(ci->children_num); /* New reference. */
1069 for (int i = 0; i < ci->children_num; ++i) {
1070 PyTuple_SET_ITEM(children, i, cpy_oconfig_to_pyconfig(ci->children + i, item));
1071 }
1072 tmp = ((Config *) item)->children;
1073 ((Config *) item)->children = children;
1074 Py_XDECREF(tmp);
1075 return item;
1076 }
1078 #ifdef IS_PY3K
1079 static struct PyModuleDef collectdmodule = {
1080 PyModuleDef_HEAD_INIT,
1081 "collectd", /* name of module */
1082 "The python interface to collectd", /* module documentation, may be NULL */
1083 -1,
1084 cpy_methods
1085 };
1087 PyMODINIT_FUNC PyInit_collectd(void) {
1088 return PyModule_Create(&collectdmodule);
1089 }
1090 #endif
1092 static int cpy_init_python(void) {
1093 PyOS_sighandler_t cur_sig;
1094 PyObject *sys;
1095 PyObject *module;
1097 #ifdef IS_PY3K
1098 wchar_t *argv = L"";
1099 /* Add a builtin module, before Py_Initialize */
1100 PyImport_AppendInittab("collectd", PyInit_collectd);
1101 #else
1102 char *argv = "";
1103 #endif
1105 /* Chances are the current signal handler is already SIG_DFL, but let's make sure. */
1106 cur_sig = PyOS_setsig(SIGINT, SIG_DFL);
1107 Py_Initialize();
1108 python_sigint_handler = PyOS_setsig(SIGINT, cur_sig);
1110 PyType_Ready(&ConfigType);
1111 PyType_Ready(&PluginDataType);
1112 ValuesType.tp_base = &PluginDataType;
1113 PyType_Ready(&ValuesType);
1114 NotificationType.tp_base = &PluginDataType;
1115 PyType_Ready(&NotificationType);
1116 SignedType.tp_base = &PyLong_Type;
1117 PyType_Ready(&SignedType);
1118 UnsignedType.tp_base = &PyLong_Type;
1119 PyType_Ready(&UnsignedType);
1120 sys = PyImport_ImportModule("sys"); /* New reference. */
1121 if (sys == NULL) {
1122 cpy_log_exception("python initialization");
1123 return 1;
1124 }
1125 sys_path = PyObject_GetAttrString(sys, "path"); /* New reference. */
1126 Py_DECREF(sys);
1127 if (sys_path == NULL) {
1128 cpy_log_exception("python initialization");
1129 return 1;
1130 }
1131 PySys_SetArgv(1, &argv);
1132 PyList_SetSlice(sys_path, 0, 1, NULL);
1134 #ifdef IS_PY3K
1135 module = PyImport_ImportModule("collectd");
1136 #else
1137 module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
1138 #endif
1139 PyModule_AddObject(module, "Config", (void *) &ConfigType); /* Steals a reference. */
1140 PyModule_AddObject(module, "Values", (void *) &ValuesType); /* Steals a reference. */
1141 PyModule_AddObject(module, "Notification", (void *) &NotificationType); /* Steals a reference. */
1142 PyModule_AddObject(module, "Signed", (void *) &SignedType); /* Steals a reference. */
1143 PyModule_AddObject(module, "Unsigned", (void *) &UnsignedType); /* Steals a reference. */
1144 PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG);
1145 PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO);
1146 PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE);
1147 PyModule_AddIntConstant(module, "LOG_WARNING", LOG_WARNING);
1148 PyModule_AddIntConstant(module, "LOG_ERROR", LOG_ERR);
1149 PyModule_AddIntConstant(module, "NOTIF_FAILURE", NOTIF_FAILURE);
1150 PyModule_AddIntConstant(module, "NOTIF_WARNING", NOTIF_WARNING);
1151 PyModule_AddIntConstant(module, "NOTIF_OKAY", NOTIF_OKAY);
1152 PyModule_AddStringConstant(module, "DS_TYPE_COUNTER", DS_TYPE_TO_STRING(DS_TYPE_COUNTER));
1153 PyModule_AddStringConstant(module, "DS_TYPE_GAUGE", DS_TYPE_TO_STRING(DS_TYPE_GAUGE));
1154 PyModule_AddStringConstant(module, "DS_TYPE_DERIVE", DS_TYPE_TO_STRING(DS_TYPE_DERIVE));
1155 PyModule_AddStringConstant(module, "DS_TYPE_ABSOLUTE", DS_TYPE_TO_STRING(DS_TYPE_ABSOLUTE));
1156 return 0;
1157 }
1159 static int cpy_config(oconfig_item_t *ci) {
1160 PyObject *tb;
1161 int status = 0;
1163 /* Ok in theory we shouldn't do initialization at this point
1164 * but we have to. In order to give python scripts a chance
1165 * to register a config callback we need to be able to execute
1166 * python code during the config callback so we have to start
1167 * the interpreter here. */
1168 /* Do *not* use the python "thread" module at this point! */
1170 if (!Py_IsInitialized() && cpy_init_python()) return 1;
1172 for (int i = 0; i < ci->children_num; ++i) {
1173 oconfig_item_t *item = ci->children + i;
1175 if (strcasecmp(item->key, "Interactive") == 0) {
1176 if (cf_util_get_boolean(item, &do_interactive) != 0) {
1177 status = 1;
1178 continue;
1179 }
1180 } else if (strcasecmp(item->key, "Encoding") == 0) {
1181 char *encoding = NULL;
1182 if (cf_util_get_string(item, &encoding) != 0) {
1183 status = 1;
1184 continue;
1185 }
1186 #ifdef IS_PY3K
1187 ERROR("python: \"Encoding\" was used in the config file but Python3 was used, which does not support changing encodings");
1188 status = 1;
1189 sfree(encoding);
1190 continue;
1191 #else
1192 /* Why is this even necessary? And undocumented? */
1193 if (PyUnicode_SetDefaultEncoding(encoding)) {
1194 cpy_log_exception("setting default encoding");
1195 status = 1;
1196 }
1197 #endif
1198 sfree(encoding);
1199 } else if (strcasecmp(item->key, "LogTraces") == 0) {
1200 _Bool log_traces;
1201 if (cf_util_get_boolean(item, &log_traces) != 0) {
1202 status = 1;
1203 continue;
1204 }
1205 if (!log_traces) {
1206 Py_XDECREF(cpy_format_exception);
1207 cpy_format_exception = NULL;
1208 continue;
1209 }
1210 if (cpy_format_exception)
1211 continue;
1212 tb = PyImport_ImportModule("traceback"); /* New reference. */
1213 if (tb == NULL) {
1214 cpy_log_exception("python initialization");
1215 status = 1;
1216 continue;
1217 }
1218 cpy_format_exception = PyObject_GetAttrString(tb, "format_exception"); /* New reference. */
1219 Py_DECREF(tb);
1220 if (cpy_format_exception == NULL) {
1221 cpy_log_exception("python initialization");
1222 status = 1;
1223 }
1224 } else if (strcasecmp(item->key, "ModulePath") == 0) {
1225 char *dir = NULL;
1226 PyObject *dir_object;
1228 if (cf_util_get_string(item, &dir) != 0) {
1229 status = 1;
1230 continue;
1231 }
1232 dir_object = cpy_string_to_unicode_or_bytes(dir); /* New reference. */
1233 if (dir_object == NULL) {
1234 ERROR("python plugin: Unable to convert \"%s\" to "
1235 "a python object.", dir);
1236 free(dir);
1237 cpy_log_exception("python initialization");
1238 status = 1;
1239 continue;
1240 }
1241 if (PyList_Insert(sys_path, 0, dir_object) != 0) {
1242 ERROR("python plugin: Unable to prepend \"%s\" to "
1243 "python module path.", dir);
1244 cpy_log_exception("python initialization");
1245 status = 1;
1246 }
1247 Py_DECREF(dir_object);
1248 free(dir);
1249 } else if (strcasecmp(item->key, "Import") == 0) {
1250 char *module_name = NULL;
1251 PyObject *module;
1253 if (cf_util_get_string(item, &module_name) != 0) {
1254 status = 1;
1255 continue;
1256 }
1257 module = PyImport_ImportModule(module_name); /* New reference. */
1258 if (module == NULL) {
1259 ERROR("python plugin: Error importing module \"%s\".", module_name);
1260 cpy_log_exception("importing module");
1261 status = 1;
1262 }
1263 free(module_name);
1264 Py_XDECREF(module);
1265 } else if (strcasecmp(item->key, "Module") == 0) {
1266 char *name = NULL;
1267 cpy_callback_t *c;
1268 PyObject *ret;
1270 if (cf_util_get_string(item, &name) != 0) {
1271 status = 1;
1272 continue;
1273 }
1274 for (c = cpy_config_callbacks; c; c = c->next) {
1275 if (strcasecmp(c->name + 7, name) == 0)
1276 break;
1277 }
1278 if (c == NULL) {
1279 WARNING("python plugin: Found a configuration for the \"%s\" plugin, "
1280 "but the plugin isn't loaded or didn't register "
1281 "a configuration callback.", name);
1282 free(name);
1283 continue;
1284 }
1285 free(name);
1286 if (c->data == NULL)
1287 ret = PyObject_CallFunction(c->callback, "N",
1288 cpy_oconfig_to_pyconfig(item, NULL)); /* New reference. */
1289 else
1290 ret = PyObject_CallFunction(c->callback, "NO",
1291 cpy_oconfig_to_pyconfig(item, NULL), c->data); /* New reference. */
1292 if (ret == NULL) {
1293 cpy_log_exception("loading module");
1294 status = 1;
1295 } else
1296 Py_DECREF(ret);
1297 } else {
1298 ERROR("python plugin: Unknown config key \"%s\".", item->key);
1299 status = 1;
1300 }
1301 }
1302 return (status);
1303 }
1305 void module_register(void) {
1306 plugin_register_complex_config("python", cpy_config);
1307 plugin_register_init("python", cpy_init);
1308 plugin_register_shutdown("python", cpy_shutdown);
1309 }