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[] =
48 "get_dataset(name) -> definition\n"
49 "\n"
50 "Returns the definition of a dataset specified by name.\n"
51 "\n"
52 "'name' is a string specifying the dataset to query.\n"
53 "'definition' is a list of 4-tuples. Every tuple represents a \n"
54 " data source within the data set and its 4 values are the \n"
55 " name, type, min and max value.\n"
56 " 'name' is a string.\n"
57 " 'type' is a string that is equal to either DS_TYPE_COUNTER,\n"
58 " DS_TYPE_GAUGE, DS_TYPE_DERIVE or DS_TYPE_ABSOLUTE.\n"
59 " 'min' and 'max' are either a float or None.";
61 static char flush_doc[] = "flush([plugin][, timeout][, identifier]) -> None\n"
62 "\n"
63 "Flushes the cache of another plugin.";
65 static char unregister_doc[] =
66 "Unregisters a callback. This function needs exactly one parameter either\n"
67 "the function to unregister or the callback identifier to unregister.";
69 static char reg_log_doc[] =
70 "register_log(callback[, data][, name]) -> identifier\n"
71 "\n"
72 "Register a callback function for log messages.\n"
73 "\n"
74 "'callback' is a callable object that will be called every time something\n"
75 " is logged.\n"
76 "'data' is an optional object that will be passed back to the callback\n"
77 " function every time it is called.\n"
78 "'name' is an optional identifier for this callback. The default name\n"
79 " is 'python.<module>'.\n"
80 " Every callback needs a unique identifier, so if you want to\n"
81 " register this callback multiple time from the same module you need\n"
82 " to specify a name here.\n"
83 "'identifier' is the full identifier assigned to this callback.\n"
84 "\n"
85 "The callback function will be called with two or three parameters:\n"
86 "severity: An integer that should be compared to the LOG_ constants.\n"
87 "message: The text to be logged.\n"
88 "data: The optional data parameter passed to the register function.\n"
89 " If the parameter was omitted it will be omitted here, too.";
91 static char reg_init_doc[] =
92 "register_init(callback[, data][, name]) -> identifier\n"
93 "\n"
94 "Register a callback function that will be executed once after the "
95 "config.\n"
96 "file has been read, all plugins heve been loaded and the collectd has\n"
97 "forked into the background.\n"
98 "\n"
99 "'callback' is a callable object that will be executed.\n"
100 "'data' is an optional object that will be passed back to the callback\n"
101 " function when it is called.\n"
102 "'name' is an optional identifier for this callback. The default name\n"
103 " is 'python.<module>'.\n"
104 " Every callback needs a unique identifier, so if you want to\n"
105 " register this callback multiple time from the same module you need\n"
106 " to specify a name here.\n"
107 "'identifier' is the full identifier assigned to this callback.\n"
108 "\n"
109 "The callback function will be called without parameters, except for\n"
110 "data if it was supplied.";
112 static char reg_config_doc[] =
113 "register_config(callback[, data][, name]) -> identifier\n"
114 "\n"
115 "Register a callback function for config file entries.\n"
116 "'callback' is a callable object that will be called for every config "
117 "block.\n"
118 "'data' is an optional object that will be passed back to the callback\n"
119 " function every time it is called.\n"
120 "'name' is an optional identifier for this callback. The default name\n"
121 " is 'python.<module>'.\n"
122 " Every callback needs a unique identifier, so if you want to\n"
123 " register this callback multiple time from the same module you need\n"
124 " to specify a name here.\n"
125 "'identifier' is the full identifier assigned to this callback.\n"
126 "\n"
127 "The callback function will be called with one or two parameters:\n"
128 "config: A Config object.\n"
129 "data: The optional data parameter passed to the register function.\n"
130 " If the parameter was omitted it will be omitted here, too.";
132 static char reg_read_doc[] =
133 "register_read(callback[, interval][, data][, name]) -> identifier\n"
134 "\n"
135 "Register a callback function for reading data. It will just be called\n"
136 "in a fixed interval to signal that it's time to dispatch new values.\n"
137 "'callback' is a callable object that will be called every time something\n"
138 " is logged.\n"
139 "'interval' is the number of seconds between between calls to the "
140 "callback\n"
141 " function. Full float precision is supported here.\n"
142 "'data' is an optional object that will be passed back to the callback\n"
143 " function every time it is called.\n"
144 "'name' is an optional identifier for this callback. The default name\n"
145 " is 'python.<module>'.\n"
146 " Every callback needs a unique identifier, so if you want to\n"
147 " register this callback multiple time from the same module you need\n"
148 " to specify a name here.\n"
149 "'identifier' is the full identifier assigned to this callback.\n"
150 "\n"
151 "The callback function will be called without parameters, except for\n"
152 "data if it was supplied.";
154 static char reg_write_doc[] =
155 "register_write(callback[, data][, name]) -> identifier\n"
156 "\n"
157 "Register a callback function to receive values dispatched by other "
158 "plugins.\n"
159 "'callback' is a callable object that will be called every time a value\n"
160 " is dispatched.\n"
161 "'data' is an optional object that will be passed back to the callback\n"
162 " function every time it is called.\n"
163 "'name' is an optional identifier for this callback. The default name\n"
164 " is 'python.<module>'.\n"
165 " Every callback needs a unique identifier, so if you want to\n"
166 " register this callback multiple time from the same module you need\n"
167 " to specify a name here.\n"
168 "'identifier' is the full identifier assigned to this callback.\n"
169 "\n"
170 "The callback function will be called with one or two parameters:\n"
171 "values: A Values object which is a copy of the dispatched values.\n"
172 "data: The optional data parameter passed to the register function.\n"
173 " If the parameter was omitted it will be omitted here, too.";
175 static char reg_notification_doc[] =
176 "register_notification(callback[, data][, name]) -> identifier\n"
177 "\n"
178 "Register a callback function for notifications.\n"
179 "'callback' is a callable object that will be called every time a "
180 "notification\n"
181 " is dispatched.\n"
182 "'data' is an optional object that will be passed back to the callback\n"
183 " function every time it is called.\n"
184 "'name' is an optional identifier for this callback. The default name\n"
185 " is 'python.<module>'.\n"
186 " Every callback needs a unique identifier, so if you want to\n"
187 " register this callback multiple time from the same module you need\n"
188 " to specify a name here.\n"
189 "'identifier' is the full identifier assigned to this callback.\n"
190 "\n"
191 "The callback function will be called with one or two parameters:\n"
192 "notification: A copy of the notification that was dispatched.\n"
193 "data: The optional data parameter passed to the register function.\n"
194 " If the parameter was omitted it will be omitted here, too.";
196 static char reg_flush_doc[] =
197 "register_flush(callback[, data][, name]) -> identifier\n"
198 "\n"
199 "Register a callback function for flush messages.\n"
200 "'callback' is a callable object that will be called every time a plugin\n"
201 " requests a flush for either this or all plugins.\n"
202 "'data' is an optional object that will be passed back to the callback\n"
203 " function every time it is called.\n"
204 "'name' is an optional identifier for this callback. The default name\n"
205 " is 'python.<module>'.\n"
206 " Every callback needs a unique identifier, so if you want to\n"
207 " register this callback multiple time from the same module you need\n"
208 " to specify a name here.\n"
209 "'identifier' is the full identifier assigned to this callback.\n"
210 "\n"
211 "The callback function will be called with two or three parameters:\n"
212 "timeout: Indicates that only data older than 'timeout' seconds is to\n"
213 " be flushed.\n"
214 "id: Specifies which values are to be flushed. Might be None.\n"
215 "data: The optional data parameter passed to the register function.\n"
216 " If the parameter was omitted it will be omitted here, too.";
218 static char reg_shutdown_doc[] =
219 "register_shutdown(callback[, data][, name]) -> identifier\n"
220 "\n"
221 "Register a callback function for collectd shutdown.\n"
222 "'callback' is a callable object that will be called once collectd is\n"
223 " shutting down.\n"
224 "'data' is an optional object that will be passed back to the callback\n"
225 " function if it is called.\n"
226 "'name' is an optional identifier for this callback. The default name\n"
227 " is 'python.<module>'.\n"
228 " Every callback needs a unique identifier, so if you want to\n"
229 " register this callback multiple time from the same module you need\n"
230 " to specify a name here.\n"
231 "'identifier' is the full identifier assigned to this callback.\n"
232 "\n"
233 "The callback function will be called with no parameters except for\n"
234 " data if it was supplied.";
236 static pthread_t main_thread;
237 static PyOS_sighandler_t python_sigint_handler;
238 static _Bool do_interactive = 0;
240 /* This is our global thread state. Python saves some stuff in thread-local
241 * storage. So if we allow the interpreter to run in the background
242 * (the scriptwriters might have created some threads from python), we have
243 * to save the state so we can resume it later after shutdown. */
245 static PyThreadState *state;
247 static PyObject *sys_path, *cpy_format_exception;
249 static cpy_callback_t *cpy_config_callbacks;
250 static cpy_callback_t *cpy_init_callbacks;
251 static cpy_callback_t *cpy_shutdown_callbacks;
253 /* Make sure to hold the GIL while modifying these. */
254 static int cpy_shutdown_triggered = 0;
255 static int cpy_num_callbacks = 0;
257 static void cpy_destroy_user_data(void *data) {
258 cpy_callback_t *c = data;
259 free(c->name);
260 CPY_LOCK_THREADS
261 Py_DECREF(c->callback);
262 Py_XDECREF(c->data);
263 free(c);
264 --cpy_num_callbacks;
265 if (!cpy_num_callbacks && cpy_shutdown_triggered) {
266 Py_Finalize();
267 return;
268 }
269 CPY_RELEASE_THREADS
270 }
272 /* You must hold the GIL to call this function!
273 * But if you managed to extract the callback parameter then you probably
274 * already do. */
276 static void cpy_build_name(char *buf, size_t size, PyObject *callback,
277 const char *name) {
278 const char *module = NULL;
279 PyObject *mod = NULL;
281 if (name != NULL) {
282 snprintf(buf, size, "python.%s", name);
283 return;
284 }
286 mod = PyObject_GetAttrString(callback, "__module__"); /* New reference. */
287 if (mod != NULL)
288 module = cpy_unicode_or_bytes_to_string(&mod);
290 if (module != NULL) {
291 snprintf(buf, size, "python.%s", module);
292 Py_XDECREF(mod);
293 PyErr_Clear();
294 return;
295 }
296 Py_XDECREF(mod);
298 snprintf(buf, size, "python.%p", callback);
299 PyErr_Clear();
300 }
302 void cpy_log_exception(const char *context) {
303 int l = 0;
304 const char *typename = NULL, *message = NULL;
305 PyObject *type, *value, *traceback, *tn, *m, *list;
307 PyErr_Fetch(&type, &value, &traceback);
308 PyErr_NormalizeException(&type, &value, &traceback);
309 if (type == NULL)
310 return;
311 tn = PyObject_GetAttrString(type, "__name__"); /* New reference. */
312 m = PyObject_Str(value); /* New reference. */
313 if (tn != NULL)
314 typename = cpy_unicode_or_bytes_to_string(&tn);
315 if (m != NULL)
316 message = cpy_unicode_or_bytes_to_string(&m);
317 if (typename == NULL)
318 typename = "NamelessException";
319 if (message == NULL)
320 message = "N/A";
321 Py_BEGIN_ALLOW_THREADS ERROR("Unhandled python exception in %s: %s: %s",
322 context, typename, message);
323 Py_END_ALLOW_THREADS Py_XDECREF(tn);
324 Py_XDECREF(m);
325 if (!cpy_format_exception || !traceback) {
326 PyErr_Clear();
327 Py_DECREF(type);
328 Py_XDECREF(value);
329 Py_XDECREF(traceback);
330 return;
331 }
332 list = PyObject_CallFunction(cpy_format_exception, "NNN", type, value,
333 traceback); /* New reference. Steals references
334 from "type", "value" and
335 "traceback". */
336 if (list)
337 l = PyObject_Length(list);
339 for (int i = 0; i < l; ++i) {
340 PyObject *line;
341 char const *msg;
342 char *cpy;
344 line = PyList_GET_ITEM(list, i); /* Borrowed reference. */
345 Py_INCREF(line);
347 msg = cpy_unicode_or_bytes_to_string(&line);
348 Py_DECREF(line);
349 if (msg == NULL)
350 continue;
352 cpy = strdup(msg);
353 if (cpy == NULL)
354 continue;
356 if (cpy[strlen(cpy) - 1] == '\n')
357 cpy[strlen(cpy) - 1] = 0;
359 Py_BEGIN_ALLOW_THREADS ERROR("%s", cpy);
360 Py_END_ALLOW_THREADS
362 free(cpy);
363 }
365 Py_XDECREF(list);
366 PyErr_Clear();
367 }
369 static int cpy_read_callback(user_data_t *data) {
370 cpy_callback_t *c = data->data;
371 PyObject *ret;
373 CPY_LOCK_THREADS
374 ret = PyObject_CallFunctionObjArgs(c->callback, c->data,
375 (void *)0); /* New reference. */
376 if (ret == NULL) {
377 cpy_log_exception("read callback");
378 } else {
379 Py_DECREF(ret);
380 }
381 CPY_RELEASE_THREADS
382 if (ret == NULL)
383 return 1;
384 return 0;
385 }
387 static int cpy_write_callback(const data_set_t *ds,
388 const value_list_t *value_list,
389 user_data_t *data) {
390 cpy_callback_t *c = data->data;
391 PyObject *ret, *list, *temp, *dict = NULL;
392 Values *v;
394 CPY_LOCK_THREADS
395 list = PyList_New(value_list->values_len); /* New reference. */
396 if (list == NULL) {
397 cpy_log_exception("write callback");
398 CPY_RETURN_FROM_THREADS 0;
399 }
400 for (size_t i = 0; i < value_list->values_len; ++i) {
401 if (ds->ds[i].type == DS_TYPE_COUNTER) {
402 PyList_SetItem(
403 list, i, PyLong_FromUnsignedLongLong(value_list->values[i].counter));
404 } else if (ds->ds[i].type == DS_TYPE_GAUGE) {
405 PyList_SetItem(list, i, PyFloat_FromDouble(value_list->values[i].gauge));
406 } else if (ds->ds[i].type == DS_TYPE_DERIVE) {
407 PyList_SetItem(list, i,
408 PyLong_FromLongLong(value_list->values[i].derive));
409 } else if (ds->ds[i].type == DS_TYPE_ABSOLUTE) {
410 PyList_SetItem(
411 list, i, PyLong_FromUnsignedLongLong(value_list->values[i].absolute));
412 } else {
413 Py_BEGIN_ALLOW_THREADS ERROR("cpy_write_callback: Unknown value type %d.",
414 ds->ds[i].type);
415 Py_END_ALLOW_THREADS Py_DECREF(list);
416 CPY_RETURN_FROM_THREADS 0;
417 }
418 if (PyErr_Occurred() != NULL) {
419 cpy_log_exception("value building for write callback");
420 Py_DECREF(list);
421 CPY_RETURN_FROM_THREADS 0;
422 }
423 }
424 dict = PyDict_New(); /* New reference. */
425 if (value_list->meta) {
426 char **table;
427 meta_data_t *meta = value_list->meta;
429 int num = meta_data_toc(meta, &table);
430 for (int i = 0; i < num; ++i) {
431 int type;
432 char *string;
433 int64_t si;
434 uint64_t ui;
435 double d;
436 _Bool b;
438 type = meta_data_type(meta, table[i]);
439 if (type == MD_TYPE_STRING) {
440 if (meta_data_get_string(meta, table[i], &string))
441 continue;
442 temp = cpy_string_to_unicode_or_bytes(string); /* New reference. */
443 free(string);
444 PyDict_SetItemString(dict, table[i], temp);
445 Py_XDECREF(temp);
446 } else if (type == MD_TYPE_SIGNED_INT) {
447 if (meta_data_get_signed_int(meta, table[i], &si))
448 continue;
449 temp = PyObject_CallFunctionObjArgs((void *)&SignedType,
450 PyLong_FromLongLong(si),
451 (void *)0); /* New reference. */
452 PyDict_SetItemString(dict, table[i], temp);
453 Py_XDECREF(temp);
454 } else if (type == MD_TYPE_UNSIGNED_INT) {
455 if (meta_data_get_unsigned_int(meta, table[i], &ui))
456 continue;
457 temp = PyObject_CallFunctionObjArgs((void *)&UnsignedType,
458 PyLong_FromUnsignedLongLong(ui),
459 (void *)0); /* New reference. */
460 PyDict_SetItemString(dict, table[i], temp);
461 Py_XDECREF(temp);
462 } else if (type == MD_TYPE_DOUBLE) {
463 if (meta_data_get_double(meta, table[i], &d))
464 continue;
465 temp = PyFloat_FromDouble(d); /* New reference. */
466 PyDict_SetItemString(dict, table[i], temp);
467 Py_XDECREF(temp);
468 } else if (type == MD_TYPE_BOOLEAN) {
469 if (meta_data_get_boolean(meta, table[i], &b))
470 continue;
471 if (b)
472 PyDict_SetItemString(dict, table[i], Py_True);
473 else
474 PyDict_SetItemString(dict, table[i], Py_False);
475 }
476 free(table[i]);
477 }
478 free(table);
479 }
480 v = (Values *)Values_New(); /* New reference. */
481 sstrncpy(v->data.host, value_list->host, sizeof(v->data.host));
482 sstrncpy(v->data.type, value_list->type, sizeof(v->data.type));
483 sstrncpy(v->data.type_instance, value_list->type_instance,
484 sizeof(v->data.type_instance));
485 sstrncpy(v->data.plugin, value_list->plugin, sizeof(v->data.plugin));
486 sstrncpy(v->data.plugin_instance, value_list->plugin_instance,
487 sizeof(v->data.plugin_instance));
488 v->data.time = CDTIME_T_TO_DOUBLE(value_list->time);
489 v->interval = CDTIME_T_TO_DOUBLE(value_list->interval);
490 Py_CLEAR(v->values);
491 v->values = list;
492 Py_CLEAR(v->meta);
493 v->meta = dict; /* Steals a reference. */
494 ret = PyObject_CallFunctionObjArgs(c->callback, v, c->data,
495 (void *)0); /* New reference. */
496 Py_XDECREF(v);
497 if (ret == NULL) {
498 cpy_log_exception("write callback");
499 } else {
500 Py_DECREF(ret);
501 }
502 CPY_RELEASE_THREADS
503 return 0;
504 }
506 static int cpy_notification_callback(const notification_t *notification,
507 user_data_t *data) {
508 cpy_callback_t *c = data->data;
509 PyObject *ret, *notify;
510 Notification *n;
512 CPY_LOCK_THREADS
513 notify = Notification_New(); /* New reference. */
514 n = (Notification *)notify;
515 sstrncpy(n->data.host, notification->host, sizeof(n->data.host));
516 sstrncpy(n->data.type, notification->type, sizeof(n->data.type));
517 sstrncpy(n->data.type_instance, notification->type_instance,
518 sizeof(n->data.type_instance));
519 sstrncpy(n->data.plugin, notification->plugin, sizeof(n->data.plugin));
520 sstrncpy(n->data.plugin_instance, notification->plugin_instance,
521 sizeof(n->data.plugin_instance));
522 n->data.time = CDTIME_T_TO_DOUBLE(notification->time);
523 sstrncpy(n->message, notification->message, sizeof(n->message));
524 n->severity = notification->severity;
525 ret = PyObject_CallFunctionObjArgs(c->callback, n, c->data,
526 (void *)0); /* New reference. */
527 Py_XDECREF(notify);
528 if (ret == NULL) {
529 cpy_log_exception("notification callback");
530 } else {
531 Py_DECREF(ret);
532 }
533 CPY_RELEASE_THREADS
534 return 0;
535 }
537 static void cpy_log_callback(int severity, const char *message,
538 user_data_t *data) {
539 cpy_callback_t *c = data->data;
540 PyObject *ret, *text;
542 CPY_LOCK_THREADS
543 text = cpy_string_to_unicode_or_bytes(message); /* New reference. */
544 if (c->data == NULL)
545 ret = PyObject_CallFunction(
546 c->callback, "iN", severity,
547 text); /* New reference. Steals a reference from "text". */
548 else
549 ret = PyObject_CallFunction(
550 c->callback, "iNO", severity, text,
551 c->data); /* New reference. Steals a reference from "text". */
553 if (ret == NULL) {
554 /* FIXME */
555 /* Do we really want to trigger a log callback because a log callback
556 * failed?
557 * Probably not. */
558 PyErr_Print();
559 /* In case someone wanted to be clever, replaced stderr and failed at that.
560 */
561 PyErr_Clear();
562 } else {
563 Py_DECREF(ret);
564 }
565 CPY_RELEASE_THREADS
566 }
568 static void cpy_flush_callback(int timeout, const char *id, user_data_t *data) {
569 cpy_callback_t *c = data->data;
570 PyObject *ret, *text;
572 CPY_LOCK_THREADS
573 if (id) {
574 text = cpy_string_to_unicode_or_bytes(id);
575 } else {
576 text = Py_None;
577 Py_INCREF(text);
578 }
579 if (c->data == NULL)
580 ret = PyObject_CallFunction(c->callback, "iN", timeout,
581 text); /* New reference. */
582 else
583 ret = PyObject_CallFunction(c->callback, "iNO", timeout, text,
584 c->data); /* New reference. */
586 if (ret == NULL) {
587 cpy_log_exception("flush callback");
588 } else {
589 Py_DECREF(ret);
590 }
591 CPY_RELEASE_THREADS
592 }
594 static PyObject *cpy_register_generic(cpy_callback_t **list_head,
595 PyObject *args, PyObject *kwds) {
596 char buf[512];
597 cpy_callback_t *c;
598 char *name = NULL;
599 PyObject *callback = NULL, *data = NULL, *mod = NULL;
600 static char *kwlist[] = {"callback", "data", "name", NULL};
602 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oet", kwlist, &callback, &data,
603 NULL, &name) == 0)
604 return NULL;
605 if (PyCallable_Check(callback) == 0) {
606 PyMem_Free(name);
607 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
608 return NULL;
609 }
610 cpy_build_name(buf, sizeof(buf), callback, name);
612 Py_INCREF(callback);
613 Py_XINCREF(data);
615 c = calloc(1, sizeof(*c));
616 if (c == NULL)
617 return NULL;
619 c->name = strdup(buf);
620 c->callback = callback;
621 c->data = data;
622 c->next = *list_head;
623 ++cpy_num_callbacks;
624 *list_head = c;
625 Py_XDECREF(mod);
626 PyMem_Free(name);
627 return cpy_string_to_unicode_or_bytes(buf);
628 }
630 static PyObject *float_or_none(float number) {
631 if (isnan(number)) {
632 Py_RETURN_NONE;
633 }
634 return PyFloat_FromDouble(number);
635 }
637 static PyObject *cpy_get_dataset(PyObject *self, PyObject *args) {
638 char *name;
639 const data_set_t *ds;
640 PyObject *list, *tuple;
642 if (PyArg_ParseTuple(args, "et", NULL, &name) == 0)
643 return NULL;
644 ds = plugin_get_ds(name);
645 PyMem_Free(name);
646 if (ds == NULL) {
647 PyErr_Format(PyExc_TypeError, "Dataset %s not found", name);
648 return NULL;
649 }
650 list = PyList_New(ds->ds_num); /* New reference. */
651 for (size_t i = 0; i < ds->ds_num; ++i) {
652 tuple = PyTuple_New(4);
653 PyTuple_SET_ITEM(tuple, 0, cpy_string_to_unicode_or_bytes(ds->ds[i].name));
654 PyTuple_SET_ITEM(tuple, 1, cpy_string_to_unicode_or_bytes(
655 DS_TYPE_TO_STRING(ds->ds[i].type)));
656 PyTuple_SET_ITEM(tuple, 2, float_or_none(ds->ds[i].min));
657 PyTuple_SET_ITEM(tuple, 3, float_or_none(ds->ds[i].max));
658 PyList_SET_ITEM(list, i, tuple);
659 }
660 return list;
661 }
663 static PyObject *cpy_flush(PyObject *self, PyObject *args, PyObject *kwds) {
664 int timeout = -1;
665 char *plugin = NULL, *identifier = NULL;
666 static char *kwlist[] = {"plugin", "timeout", "identifier", NULL};
668 if (PyArg_ParseTupleAndKeywords(args, kwds, "|etiet", kwlist, NULL, &plugin,
669 &timeout, NULL, &identifier) == 0)
670 return NULL;
671 Py_BEGIN_ALLOW_THREADS plugin_flush(plugin, timeout, identifier);
672 Py_END_ALLOW_THREADS PyMem_Free(plugin);
673 PyMem_Free(identifier);
674 Py_RETURN_NONE;
675 }
677 static PyObject *cpy_register_config(PyObject *self, PyObject *args,
678 PyObject *kwds) {
679 return cpy_register_generic(&cpy_config_callbacks, args, kwds);
680 }
682 static PyObject *cpy_register_init(PyObject *self, PyObject *args,
683 PyObject *kwds) {
684 return cpy_register_generic(&cpy_init_callbacks, args, kwds);
685 }
687 typedef int reg_function_t(const char *name, void *callback, void *data);
689 static PyObject *cpy_register_generic_userdata(void *reg, void *handler,
690 PyObject *args, PyObject *kwds) {
691 char buf[512];
692 reg_function_t *register_function = (reg_function_t *)reg;
693 cpy_callback_t *c = NULL;
694 char *name = NULL;
695 PyObject *callback = NULL, *data = NULL;
696 static char *kwlist[] = {"callback", "data", "name", NULL};
698 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oet", kwlist, &callback, &data,
699 NULL, &name) == 0)
700 return NULL;
701 if (PyCallable_Check(callback) == 0) {
702 PyMem_Free(name);
703 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
704 return NULL;
705 }
706 cpy_build_name(buf, sizeof(buf), callback, name);
707 PyMem_Free(name);
709 Py_INCREF(callback);
710 Py_XINCREF(data);
712 c = calloc(1, sizeof(*c));
713 if (c == NULL)
714 return NULL;
716 c->name = strdup(buf);
717 c->callback = callback;
718 c->data = data;
719 c->next = NULL;
721 user_data_t user_data = {.data = c, .free_func = cpy_destroy_user_data};
723 register_function(buf, handler, &user_data);
724 ++cpy_num_callbacks;
725 return cpy_string_to_unicode_or_bytes(buf);
726 }
728 static PyObject *cpy_register_read(PyObject *self, PyObject *args,
729 PyObject *kwds) {
730 char buf[512];
731 cpy_callback_t *c = NULL;
732 double interval = 0;
733 char *name = NULL;
734 PyObject *callback = NULL, *data = NULL;
735 static char *kwlist[] = {"callback", "interval", "data", "name", NULL};
737 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|dOet", kwlist, &callback,
738 &interval, &data, NULL, &name) == 0)
739 return NULL;
740 if (PyCallable_Check(callback) == 0) {
741 PyMem_Free(name);
742 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
743 return NULL;
744 }
745 cpy_build_name(buf, sizeof(buf), callback, name);
746 PyMem_Free(name);
748 Py_INCREF(callback);
749 Py_XINCREF(data);
751 c = calloc(1, sizeof(*c));
752 if (c == NULL)
753 return NULL;
755 c->name = strdup(buf);
756 c->callback = callback;
757 c->data = data;
758 c->next = NULL;
760 user_data_t user_data = {.data = c, .free_func = cpy_destroy_user_data};
762 plugin_register_complex_read(/* group = */ "python", buf, cpy_read_callback,
763 DOUBLE_TO_CDTIME_T(interval), &user_data);
764 ++cpy_num_callbacks;
765 return cpy_string_to_unicode_or_bytes(buf);
766 }
768 static PyObject *cpy_register_log(PyObject *self, PyObject *args,
769 PyObject *kwds) {
770 return cpy_register_generic_userdata((void *)plugin_register_log,
771 (void *)cpy_log_callback, args, kwds);
772 }
774 static PyObject *cpy_register_write(PyObject *self, PyObject *args,
775 PyObject *kwds) {
776 return cpy_register_generic_userdata((void *)plugin_register_write,
777 (void *)cpy_write_callback, args, kwds);
778 }
780 static PyObject *cpy_register_notification(PyObject *self, PyObject *args,
781 PyObject *kwds) {
782 return cpy_register_generic_userdata((void *)plugin_register_notification,
783 (void *)cpy_notification_callback, args,
784 kwds);
785 }
787 static PyObject *cpy_register_flush(PyObject *self, PyObject *args,
788 PyObject *kwds) {
789 return cpy_register_generic_userdata((void *)plugin_register_flush,
790 (void *)cpy_flush_callback, args, kwds);
791 }
793 static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args,
794 PyObject *kwds) {
795 return cpy_register_generic(&cpy_shutdown_callbacks, args, kwds);
796 }
798 static PyObject *cpy_error(PyObject *self, PyObject *args) {
799 char *text;
800 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
801 return NULL;
802 Py_BEGIN_ALLOW_THREADS plugin_log(LOG_ERR, "%s", text);
803 Py_END_ALLOW_THREADS PyMem_Free(text);
804 Py_RETURN_NONE;
805 }
807 static PyObject *cpy_warning(PyObject *self, PyObject *args) {
808 char *text;
809 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
810 return NULL;
811 Py_BEGIN_ALLOW_THREADS plugin_log(LOG_WARNING, "%s", text);
812 Py_END_ALLOW_THREADS PyMem_Free(text);
813 Py_RETURN_NONE;
814 }
816 static PyObject *cpy_notice(PyObject *self, PyObject *args) {
817 char *text;
818 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
819 return NULL;
820 Py_BEGIN_ALLOW_THREADS plugin_log(LOG_NOTICE, "%s", text);
821 Py_END_ALLOW_THREADS PyMem_Free(text);
822 Py_RETURN_NONE;
823 }
825 static PyObject *cpy_info(PyObject *self, PyObject *args) {
826 char *text;
827 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
828 return NULL;
829 Py_BEGIN_ALLOW_THREADS plugin_log(LOG_INFO, "%s", text);
830 Py_END_ALLOW_THREADS PyMem_Free(text);
831 Py_RETURN_NONE;
832 }
834 static PyObject *cpy_debug(PyObject *self, PyObject *args) {
835 #ifdef COLLECT_DEBUG
836 char *text;
837 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
838 return NULL;
839 Py_BEGIN_ALLOW_THREADS plugin_log(LOG_DEBUG, "%s", text);
840 Py_END_ALLOW_THREADS PyMem_Free(text);
841 #endif
842 Py_RETURN_NONE;
843 }
845 static PyObject *cpy_unregister_generic(cpy_callback_t **list_head,
846 PyObject *arg, const char *desc) {
847 char buf[512];
848 const char *name;
849 cpy_callback_t *prev = NULL, *tmp;
851 Py_INCREF(arg);
852 name = cpy_unicode_or_bytes_to_string(&arg);
853 if (name == NULL) {
854 PyErr_Clear();
855 if (!PyCallable_Check(arg)) {
856 PyErr_SetString(PyExc_TypeError, "This function needs a string or a "
857 "callable object as its only "
858 "parameter.");
859 Py_DECREF(arg);
860 return NULL;
861 }
862 cpy_build_name(buf, sizeof(buf), arg, NULL);
863 name = buf;
864 }
865 for (tmp = *list_head; tmp; prev = tmp, tmp = tmp->next)
866 if (strcmp(name, tmp->name) == 0)
867 break;
869 Py_DECREF(arg);
870 if (tmp == NULL) {
871 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.",
872 desc, name);
873 return NULL;
874 }
875 /* Yes, this is actually safe. To call this function the caller has to
876 * hold the GIL. Well, safe as long as there is only one GIL anyway ... */
877 if (prev == NULL)
878 *list_head = tmp->next;
879 else
880 prev->next = tmp->next;
881 cpy_destroy_user_data(tmp);
882 Py_RETURN_NONE;
883 }
885 static void cpy_unregister_list(cpy_callback_t **list_head) {
886 cpy_callback_t *cur, *next;
887 for (cur = *list_head; cur; cur = next) {
888 next = cur->next;
889 cpy_destroy_user_data(cur);
890 }
891 *list_head = NULL;
892 }
894 typedef int cpy_unregister_function_t(const char *name);
896 static PyObject *
897 cpy_unregister_generic_userdata(cpy_unregister_function_t *unreg, PyObject *arg,
898 const char *desc) {
899 char buf[512];
900 const char *name;
902 Py_INCREF(arg);
903 name = cpy_unicode_or_bytes_to_string(&arg);
904 if (name == NULL) {
905 PyErr_Clear();
906 if (!PyCallable_Check(arg)) {
907 PyErr_SetString(PyExc_TypeError, "This function needs a string or a "
908 "callable object as its only "
909 "parameter.");
910 Py_DECREF(arg);
911 return NULL;
912 }
913 cpy_build_name(buf, sizeof(buf), arg, NULL);
914 name = buf;
915 }
916 if (unreg(name) == 0) {
917 Py_DECREF(arg);
918 Py_RETURN_NONE;
919 }
920 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.",
921 desc, name);
922 Py_DECREF(arg);
923 return NULL;
924 }
926 static PyObject *cpy_unregister_log(PyObject *self, PyObject *arg) {
927 return cpy_unregister_generic_userdata(plugin_unregister_log, arg, "log");
928 }
930 static PyObject *cpy_unregister_init(PyObject *self, PyObject *arg) {
931 return cpy_unregister_generic(&cpy_init_callbacks, arg, "init");
932 }
934 static PyObject *cpy_unregister_config(PyObject *self, PyObject *arg) {
935 return cpy_unregister_generic(&cpy_config_callbacks, arg, "config");
936 }
938 static PyObject *cpy_unregister_read(PyObject *self, PyObject *arg) {
939 return cpy_unregister_generic_userdata(plugin_unregister_read, arg, "read");
940 }
942 static PyObject *cpy_unregister_write(PyObject *self, PyObject *arg) {
943 return cpy_unregister_generic_userdata(plugin_unregister_write, arg, "write");
944 }
946 static PyObject *cpy_unregister_notification(PyObject *self, PyObject *arg) {
947 return cpy_unregister_generic_userdata(plugin_unregister_notification, arg,
948 "notification");
949 }
951 static PyObject *cpy_unregister_flush(PyObject *self, PyObject *arg) {
952 return cpy_unregister_generic_userdata(plugin_unregister_flush, arg, "flush");
953 }
955 static PyObject *cpy_unregister_shutdown(PyObject *self, PyObject *arg) {
956 return cpy_unregister_generic(&cpy_shutdown_callbacks, arg, "shutdown");
957 }
959 static PyMethodDef cpy_methods[] = {
960 {"debug", cpy_debug, METH_VARARGS, log_doc},
961 {"info", cpy_info, METH_VARARGS, log_doc},
962 {"notice", cpy_notice, METH_VARARGS, log_doc},
963 {"warning", cpy_warning, METH_VARARGS, log_doc},
964 {"error", cpy_error, METH_VARARGS, log_doc},
965 {"get_dataset", (PyCFunction)cpy_get_dataset, METH_VARARGS, get_ds_doc},
966 {"flush", (PyCFunction)cpy_flush, METH_VARARGS | METH_KEYWORDS, flush_doc},
967 {"register_log", (PyCFunction)cpy_register_log,
968 METH_VARARGS | METH_KEYWORDS, reg_log_doc},
969 {"register_init", (PyCFunction)cpy_register_init,
970 METH_VARARGS | METH_KEYWORDS, reg_init_doc},
971 {"register_config", (PyCFunction)cpy_register_config,
972 METH_VARARGS | METH_KEYWORDS, reg_config_doc},
973 {"register_read", (PyCFunction)cpy_register_read,
974 METH_VARARGS | METH_KEYWORDS, reg_read_doc},
975 {"register_write", (PyCFunction)cpy_register_write,
976 METH_VARARGS | METH_KEYWORDS, reg_write_doc},
977 {"register_notification", (PyCFunction)cpy_register_notification,
978 METH_VARARGS | METH_KEYWORDS, reg_notification_doc},
979 {"register_flush", (PyCFunction)cpy_register_flush,
980 METH_VARARGS | METH_KEYWORDS, reg_flush_doc},
981 {"register_shutdown", (PyCFunction)cpy_register_shutdown,
982 METH_VARARGS | METH_KEYWORDS, reg_shutdown_doc},
983 {"unregister_log", cpy_unregister_log, METH_O, unregister_doc},
984 {"unregister_init", cpy_unregister_init, METH_O, unregister_doc},
985 {"unregister_config", cpy_unregister_config, METH_O, unregister_doc},
986 {"unregister_read", cpy_unregister_read, METH_O, unregister_doc},
987 {"unregister_write", cpy_unregister_write, METH_O, unregister_doc},
988 {"unregister_notification", cpy_unregister_notification, METH_O,
989 unregister_doc},
990 {"unregister_flush", cpy_unregister_flush, METH_O, unregister_doc},
991 {"unregister_shutdown", cpy_unregister_shutdown, METH_O, unregister_doc},
992 {0, 0, 0, 0}};
994 static int cpy_shutdown(void) {
995 PyObject *ret;
997 if (!state) {
998 printf(
999 "================================================================\n");
1000 printf(
1001 "collectd shutdown while running an interactive session. This will\n");
1002 printf("probably leave your terminal in a mess.\n");
1003 printf("Run the command \"reset\" to get it back into a usable state.\n");
1004 printf("You can press Ctrl+D in the interactive session to\n");
1005 printf("close collectd and avoid this problem in the future.\n");
1006 printf(
1007 "================================================================\n");
1008 }
1010 CPY_LOCK_THREADS
1012 for (cpy_callback_t *c = cpy_shutdown_callbacks; c; c = c->next) {
1013 ret = PyObject_CallFunctionObjArgs(c->callback, c->data,
1014 (void *)0); /* New reference. */
1015 if (ret == NULL)
1016 cpy_log_exception("shutdown callback");
1017 else
1018 Py_DECREF(ret);
1019 }
1020 PyErr_Print();
1022 Py_BEGIN_ALLOW_THREADS cpy_unregister_list(&cpy_config_callbacks);
1023 cpy_unregister_list(&cpy_init_callbacks);
1024 cpy_unregister_list(&cpy_shutdown_callbacks);
1025 cpy_shutdown_triggered = 1;
1026 Py_END_ALLOW_THREADS
1028 if (!cpy_num_callbacks) {
1029 Py_Finalize();
1030 return 0;
1031 }
1033 CPY_RELEASE_THREADS
1034 return 0;
1035 }
1037 static void *cpy_interactive(void *pipefd) {
1038 PyOS_sighandler_t cur_sig;
1040 /* Signal handler in a plugin? Bad stuff, but the best way to
1041 * handle it I guess. In an interactive session people will
1042 * press Ctrl+C at some time, which will generate a SIGINT.
1043 * This will cause collectd to shutdown, thus killing the
1044 * interactive interpreter, and leaving the terminal in a
1045 * mess. Chances are, this isn't what the user wanted to do.
1046 *
1047 * So this is the plan:
1048 * 1. Restore Python's own signal handler
1049 * 2. Tell Python we just forked so it will accept this thread
1050 * as the main one. No version of Python will ever handle
1051 * interrupts anywhere but in the main thread.
1052 * 3. After the interactive loop is done, restore collectd's
1053 * SIGINT handler.
1054 * 4. Raise SIGINT for a clean shutdown. The signal is sent to
1055 * the main thread to ensure it wakes up the main interval
1056 * sleep so that collectd shuts down immediately not in 10
1057 * seconds.
1058 *
1059 * This will make sure that SIGINT won't kill collectd but
1060 * still interrupt syscalls like sleep and pause. */
1062 if (PyImport_ImportModule("readline") == NULL) {
1063 /* This interactive session will suck. */
1064 cpy_log_exception("interactive session init");
1065 }
1066 cur_sig = PyOS_setsig(SIGINT, python_sigint_handler);
1067 PyOS_AfterFork();
1068 PyEval_InitThreads();
1069 close(*(int *)pipefd);
1070 PyRun_InteractiveLoop(stdin, "<stdin>");
1071 PyOS_setsig(SIGINT, cur_sig);
1072 PyErr_Print();
1073 state = PyEval_SaveThread();
1074 NOTICE("python: Interactive interpreter exited, stopping collectd ...");
1075 pthread_kill(main_thread, SIGINT);
1076 return NULL;
1077 }
1079 static int cpy_init(void) {
1080 PyObject *ret;
1081 int pipefd[2];
1082 char buf;
1083 static pthread_t thread;
1085 if (!Py_IsInitialized()) {
1086 WARNING("python: Plugin loaded but not configured.");
1087 plugin_unregister_shutdown("python");
1088 Py_Finalize();
1089 return 0;
1090 }
1091 main_thread = pthread_self();
1092 if (do_interactive) {
1093 if (pipe(pipefd)) {
1094 ERROR("python: Unable to create pipe.");
1095 return 1;
1096 }
1097 if (plugin_thread_create(&thread, NULL, cpy_interactive, pipefd + 1)) {
1098 ERROR("python: Error creating thread for interactive interpreter.");
1099 }
1100 if (read(pipefd[0], &buf, 1))
1101 ;
1102 (void)close(pipefd[0]);
1103 } else {
1104 PyEval_InitThreads();
1105 state = PyEval_SaveThread();
1106 }
1107 CPY_LOCK_THREADS
1108 for (cpy_callback_t *c = cpy_init_callbacks; c; c = c->next) {
1109 ret = PyObject_CallFunctionObjArgs(c->callback, c->data,
1110 (void *)0); /* New reference. */
1111 if (ret == NULL)
1112 cpy_log_exception("init callback");
1113 else
1114 Py_DECREF(ret);
1115 }
1116 CPY_RELEASE_THREADS
1118 return 0;
1119 }
1121 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
1122 PyObject *item, *values, *children, *tmp;
1124 if (parent == NULL)
1125 parent = Py_None;
1127 values = PyTuple_New(ci->values_num); /* New reference. */
1128 for (int i = 0; i < ci->values_num; ++i) {
1129 if (ci->values[i].type == OCONFIG_TYPE_STRING) {
1130 PyTuple_SET_ITEM(values, i, cpy_string_to_unicode_or_bytes(
1131 ci->values[i].value.string));
1132 } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
1133 PyTuple_SET_ITEM(values, i,
1134 PyFloat_FromDouble(ci->values[i].value.number));
1135 } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
1136 PyTuple_SET_ITEM(values, i, PyBool_FromLong(ci->values[i].value.boolean));
1137 }
1138 }
1140 tmp = cpy_string_to_unicode_or_bytes(ci->key);
1141 item = PyObject_CallFunction((void *)&ConfigType, "NONO", tmp, parent, values,
1142 Py_None);
1143 if (item == NULL)
1144 return NULL;
1145 children = PyTuple_New(ci->children_num); /* New reference. */
1146 for (int i = 0; i < ci->children_num; ++i) {
1147 PyTuple_SET_ITEM(children, i,
1148 cpy_oconfig_to_pyconfig(ci->children + i, item));
1149 }
1150 tmp = ((Config *)item)->children;
1151 ((Config *)item)->children = children;
1152 Py_XDECREF(tmp);
1153 return item;
1154 }
1156 #ifdef IS_PY3K
1157 static struct PyModuleDef collectdmodule = {
1158 PyModuleDef_HEAD_INIT, "collectd", /* name of module */
1159 "The python interface to collectd", /* module documentation, may be NULL */
1160 -1, cpy_methods};
1162 PyMODINIT_FUNC PyInit_collectd(void) {
1163 return PyModule_Create(&collectdmodule);
1164 }
1165 #endif
1167 static int cpy_init_python(void) {
1168 PyOS_sighandler_t cur_sig;
1169 PyObject *sys;
1170 PyObject *module;
1172 #ifdef IS_PY3K
1173 wchar_t *argv = L"";
1174 /* Add a builtin module, before Py_Initialize */
1175 PyImport_AppendInittab("collectd", PyInit_collectd);
1176 #else
1177 char *argv = "";
1178 #endif
1180 /* Chances are the current signal handler is already SIG_DFL, but let's make
1181 * sure. */
1182 cur_sig = PyOS_setsig(SIGINT, SIG_DFL);
1183 Py_Initialize();
1184 python_sigint_handler = PyOS_setsig(SIGINT, cur_sig);
1186 PyType_Ready(&ConfigType);
1187 PyType_Ready(&PluginDataType);
1188 ValuesType.tp_base = &PluginDataType;
1189 PyType_Ready(&ValuesType);
1190 NotificationType.tp_base = &PluginDataType;
1191 PyType_Ready(&NotificationType);
1192 SignedType.tp_base = &PyLong_Type;
1193 PyType_Ready(&SignedType);
1194 UnsignedType.tp_base = &PyLong_Type;
1195 PyType_Ready(&UnsignedType);
1196 sys = PyImport_ImportModule("sys"); /* New reference. */
1197 if (sys == NULL) {
1198 cpy_log_exception("python initialization");
1199 return 1;
1200 }
1201 sys_path = PyObject_GetAttrString(sys, "path"); /* New reference. */
1202 Py_DECREF(sys);
1203 if (sys_path == NULL) {
1204 cpy_log_exception("python initialization");
1205 return 1;
1206 }
1207 PySys_SetArgv(1, &argv);
1208 PyList_SetSlice(sys_path, 0, 1, NULL);
1210 #ifdef IS_PY3K
1211 module = PyImport_ImportModule("collectd");
1212 #else
1213 module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
1214 #endif
1215 PyModule_AddObject(module, "Config",
1216 (void *)&ConfigType); /* Steals a reference. */
1217 PyModule_AddObject(module, "Values",
1218 (void *)&ValuesType); /* Steals a reference. */
1219 PyModule_AddObject(module, "Notification",
1220 (void *)&NotificationType); /* Steals a reference. */
1221 PyModule_AddObject(module, "Signed",
1222 (void *)&SignedType); /* Steals a reference. */
1223 PyModule_AddObject(module, "Unsigned",
1224 (void *)&UnsignedType); /* Steals a reference. */
1225 PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG);
1226 PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO);
1227 PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE);
1228 PyModule_AddIntConstant(module, "LOG_WARNING", LOG_WARNING);
1229 PyModule_AddIntConstant(module, "LOG_ERROR", LOG_ERR);
1230 PyModule_AddIntConstant(module, "NOTIF_FAILURE", NOTIF_FAILURE);
1231 PyModule_AddIntConstant(module, "NOTIF_WARNING", NOTIF_WARNING);
1232 PyModule_AddIntConstant(module, "NOTIF_OKAY", NOTIF_OKAY);
1233 PyModule_AddStringConstant(module, "DS_TYPE_COUNTER",
1234 DS_TYPE_TO_STRING(DS_TYPE_COUNTER));
1235 PyModule_AddStringConstant(module, "DS_TYPE_GAUGE",
1236 DS_TYPE_TO_STRING(DS_TYPE_GAUGE));
1237 PyModule_AddStringConstant(module, "DS_TYPE_DERIVE",
1238 DS_TYPE_TO_STRING(DS_TYPE_DERIVE));
1239 PyModule_AddStringConstant(module, "DS_TYPE_ABSOLUTE",
1240 DS_TYPE_TO_STRING(DS_TYPE_ABSOLUTE));
1241 return 0;
1242 }
1244 static int cpy_config(oconfig_item_t *ci) {
1245 PyObject *tb;
1246 int status = 0;
1248 /* Ok in theory we shouldn't do initialization at this point
1249 * but we have to. In order to give python scripts a chance
1250 * to register a config callback we need to be able to execute
1251 * python code during the config callback so we have to start
1252 * the interpreter here. */
1253 /* Do *not* use the python "thread" module at this point! */
1255 if (!Py_IsInitialized() && cpy_init_python())
1256 return 1;
1258 for (int i = 0; i < ci->children_num; ++i) {
1259 oconfig_item_t *item = ci->children + i;
1261 if (strcasecmp(item->key, "Interactive") == 0) {
1262 if (cf_util_get_boolean(item, &do_interactive) != 0) {
1263 status = 1;
1264 continue;
1265 }
1266 } else if (strcasecmp(item->key, "Encoding") == 0) {
1267 char *encoding = NULL;
1268 if (cf_util_get_string(item, &encoding) != 0) {
1269 status = 1;
1270 continue;
1271 }
1272 #ifdef IS_PY3K
1273 ERROR("python: \"Encoding\" was used in the config file but Python3 was "
1274 "used, which does not support changing encodings");
1275 status = 1;
1276 sfree(encoding);
1277 continue;
1278 #else
1279 /* Why is this even necessary? And undocumented? */
1280 if (PyUnicode_SetDefaultEncoding(encoding)) {
1281 cpy_log_exception("setting default encoding");
1282 status = 1;
1283 }
1284 #endif
1285 sfree(encoding);
1286 } else if (strcasecmp(item->key, "LogTraces") == 0) {
1287 _Bool log_traces;
1288 if (cf_util_get_boolean(item, &log_traces) != 0) {
1289 status = 1;
1290 continue;
1291 }
1292 if (!log_traces) {
1293 Py_XDECREF(cpy_format_exception);
1294 cpy_format_exception = NULL;
1295 continue;
1296 }
1297 if (cpy_format_exception)
1298 continue;
1299 tb = PyImport_ImportModule("traceback"); /* New reference. */
1300 if (tb == NULL) {
1301 cpy_log_exception("python initialization");
1302 status = 1;
1303 continue;
1304 }
1305 cpy_format_exception =
1306 PyObject_GetAttrString(tb, "format_exception"); /* New reference. */
1307 Py_DECREF(tb);
1308 if (cpy_format_exception == NULL) {
1309 cpy_log_exception("python initialization");
1310 status = 1;
1311 }
1312 } else if (strcasecmp(item->key, "ModulePath") == 0) {
1313 char *dir = NULL;
1314 PyObject *dir_object;
1316 if (cf_util_get_string(item, &dir) != 0) {
1317 status = 1;
1318 continue;
1319 }
1320 dir_object = cpy_string_to_unicode_or_bytes(dir); /* New reference. */
1321 if (dir_object == NULL) {
1322 ERROR("python plugin: Unable to convert \"%s\" to "
1323 "a python object.",
1324 dir);
1325 free(dir);
1326 cpy_log_exception("python initialization");
1327 status = 1;
1328 continue;
1329 }
1330 if (PyList_Insert(sys_path, 0, dir_object) != 0) {
1331 ERROR("python plugin: Unable to prepend \"%s\" to "
1332 "python module path.",
1333 dir);
1334 cpy_log_exception("python initialization");
1335 status = 1;
1336 }
1337 Py_DECREF(dir_object);
1338 free(dir);
1339 } else if (strcasecmp(item->key, "Import") == 0) {
1340 char *module_name = NULL;
1341 PyObject *module;
1343 if (cf_util_get_string(item, &module_name) != 0) {
1344 status = 1;
1345 continue;
1346 }
1347 module = PyImport_ImportModule(module_name); /* New reference. */
1348 if (module == NULL) {
1349 ERROR("python plugin: Error importing module \"%s\".", module_name);
1350 cpy_log_exception("importing module");
1351 status = 1;
1352 }
1353 free(module_name);
1354 Py_XDECREF(module);
1355 } else if (strcasecmp(item->key, "Module") == 0) {
1356 char *name = NULL;
1357 cpy_callback_t *c;
1358 PyObject *ret;
1360 if (cf_util_get_string(item, &name) != 0) {
1361 status = 1;
1362 continue;
1363 }
1364 for (c = cpy_config_callbacks; c; c = c->next) {
1365 if (strcasecmp(c->name + 7, name) == 0)
1366 break;
1367 }
1368 if (c == NULL) {
1369 WARNING("python plugin: Found a configuration for the \"%s\" plugin, "
1370 "but the plugin isn't loaded or didn't register "
1371 "a configuration callback.",
1372 name);
1373 free(name);
1374 continue;
1375 }
1376 free(name);
1377 if (c->data == NULL)
1378 ret = PyObject_CallFunction(
1379 c->callback, "N",
1380 cpy_oconfig_to_pyconfig(item, NULL)); /* New reference. */
1381 else
1382 ret = PyObject_CallFunction(c->callback, "NO",
1383 cpy_oconfig_to_pyconfig(item, NULL),
1384 c->data); /* New reference. */
1385 if (ret == NULL) {
1386 cpy_log_exception("loading module");
1387 status = 1;
1388 } else
1389 Py_DECREF(ret);
1390 } else {
1391 ERROR("python plugin: Unknown config key \"%s\".", item->key);
1392 status = 1;
1393 }
1394 }
1395 return (status);
1396 }
1398 void module_register(void) {
1399 plugin_register_complex_config("python", cpy_config);
1400 plugin_register_init("python", cpy_init);
1401 plugin_register_shutdown("python", cpy_shutdown);
1402 }