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 register_function(buf, handler,
722 &(user_data_t){
723 .data = c, .free_func = cpy_destroy_user_data,
724 });
726 ++cpy_num_callbacks;
727 return cpy_string_to_unicode_or_bytes(buf);
728 }
730 static PyObject *cpy_register_read(PyObject *self, PyObject *args,
731 PyObject *kwds) {
732 char buf[512];
733 cpy_callback_t *c = NULL;
734 double interval = 0;
735 char *name = NULL;
736 PyObject *callback = NULL, *data = NULL;
737 static char *kwlist[] = {"callback", "interval", "data", "name", NULL};
739 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|dOet", kwlist, &callback,
740 &interval, &data, NULL, &name) == 0)
741 return NULL;
742 if (PyCallable_Check(callback) == 0) {
743 PyMem_Free(name);
744 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
745 return NULL;
746 }
747 cpy_build_name(buf, sizeof(buf), callback, name);
748 PyMem_Free(name);
750 Py_INCREF(callback);
751 Py_XINCREF(data);
753 c = calloc(1, sizeof(*c));
754 if (c == NULL)
755 return NULL;
757 c->name = strdup(buf);
758 c->callback = callback;
759 c->data = data;
760 c->next = NULL;
762 plugin_register_complex_read(
763 /* group = */ "python", buf, cpy_read_callback,
764 DOUBLE_TO_CDTIME_T(interval),
765 &(user_data_t){
766 .data = c, .free_func = cpy_destroy_user_data,
767 });
768 ++cpy_num_callbacks;
769 return cpy_string_to_unicode_or_bytes(buf);
770 }
772 static PyObject *cpy_register_log(PyObject *self, PyObject *args,
773 PyObject *kwds) {
774 return cpy_register_generic_userdata((void *)plugin_register_log,
775 (void *)cpy_log_callback, args, kwds);
776 }
778 static PyObject *cpy_register_write(PyObject *self, PyObject *args,
779 PyObject *kwds) {
780 return cpy_register_generic_userdata((void *)plugin_register_write,
781 (void *)cpy_write_callback, args, kwds);
782 }
784 static PyObject *cpy_register_notification(PyObject *self, PyObject *args,
785 PyObject *kwds) {
786 return cpy_register_generic_userdata((void *)plugin_register_notification,
787 (void *)cpy_notification_callback, args,
788 kwds);
789 }
791 static PyObject *cpy_register_flush(PyObject *self, PyObject *args,
792 PyObject *kwds) {
793 return cpy_register_generic_userdata((void *)plugin_register_flush,
794 (void *)cpy_flush_callback, args, kwds);
795 }
797 static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args,
798 PyObject *kwds) {
799 return cpy_register_generic(&cpy_shutdown_callbacks, args, kwds);
800 }
802 static PyObject *cpy_error(PyObject *self, PyObject *args) {
803 char *text;
804 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
805 return NULL;
806 Py_BEGIN_ALLOW_THREADS plugin_log(LOG_ERR, "%s", text);
807 Py_END_ALLOW_THREADS PyMem_Free(text);
808 Py_RETURN_NONE;
809 }
811 static PyObject *cpy_warning(PyObject *self, PyObject *args) {
812 char *text;
813 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
814 return NULL;
815 Py_BEGIN_ALLOW_THREADS plugin_log(LOG_WARNING, "%s", text);
816 Py_END_ALLOW_THREADS PyMem_Free(text);
817 Py_RETURN_NONE;
818 }
820 static PyObject *cpy_notice(PyObject *self, PyObject *args) {
821 char *text;
822 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
823 return NULL;
824 Py_BEGIN_ALLOW_THREADS plugin_log(LOG_NOTICE, "%s", text);
825 Py_END_ALLOW_THREADS PyMem_Free(text);
826 Py_RETURN_NONE;
827 }
829 static PyObject *cpy_info(PyObject *self, PyObject *args) {
830 char *text;
831 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
832 return NULL;
833 Py_BEGIN_ALLOW_THREADS plugin_log(LOG_INFO, "%s", text);
834 Py_END_ALLOW_THREADS PyMem_Free(text);
835 Py_RETURN_NONE;
836 }
838 static PyObject *cpy_debug(PyObject *self, PyObject *args) {
839 #ifdef COLLECT_DEBUG
840 char *text;
841 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
842 return NULL;
843 Py_BEGIN_ALLOW_THREADS plugin_log(LOG_DEBUG, "%s", text);
844 Py_END_ALLOW_THREADS PyMem_Free(text);
845 #endif
846 Py_RETURN_NONE;
847 }
849 static PyObject *cpy_unregister_generic(cpy_callback_t **list_head,
850 PyObject *arg, const char *desc) {
851 char buf[512];
852 const char *name;
853 cpy_callback_t *prev = NULL, *tmp;
855 Py_INCREF(arg);
856 name = cpy_unicode_or_bytes_to_string(&arg);
857 if (name == NULL) {
858 PyErr_Clear();
859 if (!PyCallable_Check(arg)) {
860 PyErr_SetString(PyExc_TypeError, "This function needs a string or a "
861 "callable object as its only "
862 "parameter.");
863 Py_DECREF(arg);
864 return NULL;
865 }
866 cpy_build_name(buf, sizeof(buf), arg, NULL);
867 name = buf;
868 }
869 for (tmp = *list_head; tmp; prev = tmp, tmp = tmp->next)
870 if (strcmp(name, tmp->name) == 0)
871 break;
873 Py_DECREF(arg);
874 if (tmp == NULL) {
875 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.",
876 desc, name);
877 return NULL;
878 }
879 /* Yes, this is actually safe. To call this function the caller has to
880 * hold the GIL. Well, safe as long as there is only one GIL anyway ... */
881 if (prev == NULL)
882 *list_head = tmp->next;
883 else
884 prev->next = tmp->next;
885 cpy_destroy_user_data(tmp);
886 Py_RETURN_NONE;
887 }
889 static void cpy_unregister_list(cpy_callback_t **list_head) {
890 cpy_callback_t *cur, *next;
891 for (cur = *list_head; cur; cur = next) {
892 next = cur->next;
893 cpy_destroy_user_data(cur);
894 }
895 *list_head = NULL;
896 }
898 typedef int cpy_unregister_function_t(const char *name);
900 static PyObject *
901 cpy_unregister_generic_userdata(cpy_unregister_function_t *unreg, PyObject *arg,
902 const char *desc) {
903 char buf[512];
904 const char *name;
906 Py_INCREF(arg);
907 name = cpy_unicode_or_bytes_to_string(&arg);
908 if (name == NULL) {
909 PyErr_Clear();
910 if (!PyCallable_Check(arg)) {
911 PyErr_SetString(PyExc_TypeError, "This function needs a string or a "
912 "callable object as its only "
913 "parameter.");
914 Py_DECREF(arg);
915 return NULL;
916 }
917 cpy_build_name(buf, sizeof(buf), arg, NULL);
918 name = buf;
919 }
920 if (unreg(name) == 0) {
921 Py_DECREF(arg);
922 Py_RETURN_NONE;
923 }
924 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.",
925 desc, name);
926 Py_DECREF(arg);
927 return NULL;
928 }
930 static PyObject *cpy_unregister_log(PyObject *self, PyObject *arg) {
931 return cpy_unregister_generic_userdata(plugin_unregister_log, arg, "log");
932 }
934 static PyObject *cpy_unregister_init(PyObject *self, PyObject *arg) {
935 return cpy_unregister_generic(&cpy_init_callbacks, arg, "init");
936 }
938 static PyObject *cpy_unregister_config(PyObject *self, PyObject *arg) {
939 return cpy_unregister_generic(&cpy_config_callbacks, arg, "config");
940 }
942 static PyObject *cpy_unregister_read(PyObject *self, PyObject *arg) {
943 return cpy_unregister_generic_userdata(plugin_unregister_read, arg, "read");
944 }
946 static PyObject *cpy_unregister_write(PyObject *self, PyObject *arg) {
947 return cpy_unregister_generic_userdata(plugin_unregister_write, arg, "write");
948 }
950 static PyObject *cpy_unregister_notification(PyObject *self, PyObject *arg) {
951 return cpy_unregister_generic_userdata(plugin_unregister_notification, arg,
952 "notification");
953 }
955 static PyObject *cpy_unregister_flush(PyObject *self, PyObject *arg) {
956 return cpy_unregister_generic_userdata(plugin_unregister_flush, arg, "flush");
957 }
959 static PyObject *cpy_unregister_shutdown(PyObject *self, PyObject *arg) {
960 return cpy_unregister_generic(&cpy_shutdown_callbacks, arg, "shutdown");
961 }
963 static PyMethodDef cpy_methods[] = {
964 {"debug", cpy_debug, METH_VARARGS, log_doc},
965 {"info", cpy_info, METH_VARARGS, log_doc},
966 {"notice", cpy_notice, METH_VARARGS, log_doc},
967 {"warning", cpy_warning, METH_VARARGS, log_doc},
968 {"error", cpy_error, METH_VARARGS, log_doc},
969 {"get_dataset", (PyCFunction)cpy_get_dataset, METH_VARARGS, get_ds_doc},
970 {"flush", (PyCFunction)cpy_flush, METH_VARARGS | METH_KEYWORDS, flush_doc},
971 {"register_log", (PyCFunction)cpy_register_log,
972 METH_VARARGS | METH_KEYWORDS, reg_log_doc},
973 {"register_init", (PyCFunction)cpy_register_init,
974 METH_VARARGS | METH_KEYWORDS, reg_init_doc},
975 {"register_config", (PyCFunction)cpy_register_config,
976 METH_VARARGS | METH_KEYWORDS, reg_config_doc},
977 {"register_read", (PyCFunction)cpy_register_read,
978 METH_VARARGS | METH_KEYWORDS, reg_read_doc},
979 {"register_write", (PyCFunction)cpy_register_write,
980 METH_VARARGS | METH_KEYWORDS, reg_write_doc},
981 {"register_notification", (PyCFunction)cpy_register_notification,
982 METH_VARARGS | METH_KEYWORDS, reg_notification_doc},
983 {"register_flush", (PyCFunction)cpy_register_flush,
984 METH_VARARGS | METH_KEYWORDS, reg_flush_doc},
985 {"register_shutdown", (PyCFunction)cpy_register_shutdown,
986 METH_VARARGS | METH_KEYWORDS, reg_shutdown_doc},
987 {"unregister_log", cpy_unregister_log, METH_O, unregister_doc},
988 {"unregister_init", cpy_unregister_init, METH_O, unregister_doc},
989 {"unregister_config", cpy_unregister_config, METH_O, unregister_doc},
990 {"unregister_read", cpy_unregister_read, METH_O, unregister_doc},
991 {"unregister_write", cpy_unregister_write, METH_O, unregister_doc},
992 {"unregister_notification", cpy_unregister_notification, METH_O,
993 unregister_doc},
994 {"unregister_flush", cpy_unregister_flush, METH_O, unregister_doc},
995 {"unregister_shutdown", cpy_unregister_shutdown, METH_O, unregister_doc},
996 {0, 0, 0, 0}};
998 static int cpy_shutdown(void) {
999 PyObject *ret;
1001 if (!state) {
1002 printf(
1003 "================================================================\n");
1004 printf(
1005 "collectd shutdown while running an interactive session. This will\n");
1006 printf("probably leave your terminal in a mess.\n");
1007 printf("Run the command \"reset\" to get it back into a usable state.\n");
1008 printf("You can press Ctrl+D in the interactive session to\n");
1009 printf("close collectd and avoid this problem in the future.\n");
1010 printf(
1011 "================================================================\n");
1012 }
1014 CPY_LOCK_THREADS
1016 for (cpy_callback_t *c = cpy_shutdown_callbacks; c; c = c->next) {
1017 ret = PyObject_CallFunctionObjArgs(c->callback, c->data,
1018 (void *)0); /* New reference. */
1019 if (ret == NULL)
1020 cpy_log_exception("shutdown callback");
1021 else
1022 Py_DECREF(ret);
1023 }
1024 PyErr_Print();
1026 Py_BEGIN_ALLOW_THREADS cpy_unregister_list(&cpy_config_callbacks);
1027 cpy_unregister_list(&cpy_init_callbacks);
1028 cpy_unregister_list(&cpy_shutdown_callbacks);
1029 cpy_shutdown_triggered = 1;
1030 Py_END_ALLOW_THREADS
1032 if (!cpy_num_callbacks) {
1033 Py_Finalize();
1034 return 0;
1035 }
1037 CPY_RELEASE_THREADS
1038 return 0;
1039 }
1041 static void *cpy_interactive(void *pipefd) {
1042 PyOS_sighandler_t cur_sig;
1044 /* Signal handler in a plugin? Bad stuff, but the best way to
1045 * handle it I guess. In an interactive session people will
1046 * press Ctrl+C at some time, which will generate a SIGINT.
1047 * This will cause collectd to shutdown, thus killing the
1048 * interactive interpreter, and leaving the terminal in a
1049 * mess. Chances are, this isn't what the user wanted to do.
1050 *
1051 * So this is the plan:
1052 * 1. Restore Python's own signal handler
1053 * 2. Tell Python we just forked so it will accept this thread
1054 * as the main one. No version of Python will ever handle
1055 * interrupts anywhere but in the main thread.
1056 * 3. After the interactive loop is done, restore collectd's
1057 * SIGINT handler.
1058 * 4. Raise SIGINT for a clean shutdown. The signal is sent to
1059 * the main thread to ensure it wakes up the main interval
1060 * sleep so that collectd shuts down immediately not in 10
1061 * seconds.
1062 *
1063 * This will make sure that SIGINT won't kill collectd but
1064 * still interrupt syscalls like sleep and pause. */
1066 if (PyImport_ImportModule("readline") == NULL) {
1067 /* This interactive session will suck. */
1068 cpy_log_exception("interactive session init");
1069 }
1070 cur_sig = PyOS_setsig(SIGINT, python_sigint_handler);
1071 PyOS_AfterFork();
1072 PyEval_InitThreads();
1073 close(*(int *)pipefd);
1074 PyRun_InteractiveLoop(stdin, "<stdin>");
1075 PyOS_setsig(SIGINT, cur_sig);
1076 PyErr_Print();
1077 state = PyEval_SaveThread();
1078 NOTICE("python: Interactive interpreter exited, stopping collectd ...");
1079 pthread_kill(main_thread, SIGINT);
1080 return NULL;
1081 }
1083 static int cpy_init(void) {
1084 PyObject *ret;
1085 int pipefd[2];
1086 char buf;
1087 static pthread_t thread;
1089 if (!Py_IsInitialized()) {
1090 WARNING("python: Plugin loaded but not configured.");
1091 plugin_unregister_shutdown("python");
1092 Py_Finalize();
1093 return 0;
1094 }
1095 main_thread = pthread_self();
1096 if (do_interactive) {
1097 if (pipe(pipefd)) {
1098 ERROR("python: Unable to create pipe.");
1099 return 1;
1100 }
1101 if (plugin_thread_create(&thread, NULL, cpy_interactive, pipefd + 1,
1102 "python interpreter")) {
1103 ERROR("python: Error creating thread for interactive interpreter.");
1104 }
1105 if (read(pipefd[0], &buf, 1))
1106 ;
1107 (void)close(pipefd[0]);
1108 } else {
1109 PyEval_InitThreads();
1110 state = PyEval_SaveThread();
1111 }
1112 CPY_LOCK_THREADS
1113 for (cpy_callback_t *c = cpy_init_callbacks; c; c = c->next) {
1114 ret = PyObject_CallFunctionObjArgs(c->callback, c->data,
1115 (void *)0); /* New reference. */
1116 if (ret == NULL)
1117 cpy_log_exception("init callback");
1118 else
1119 Py_DECREF(ret);
1120 }
1121 CPY_RELEASE_THREADS
1123 return 0;
1124 }
1126 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
1127 PyObject *item, *values, *children, *tmp;
1129 if (parent == NULL)
1130 parent = Py_None;
1132 values = PyTuple_New(ci->values_num); /* New reference. */
1133 for (int i = 0; i < ci->values_num; ++i) {
1134 if (ci->values[i].type == OCONFIG_TYPE_STRING) {
1135 PyTuple_SET_ITEM(values, i, cpy_string_to_unicode_or_bytes(
1136 ci->values[i].value.string));
1137 } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
1138 PyTuple_SET_ITEM(values, i,
1139 PyFloat_FromDouble(ci->values[i].value.number));
1140 } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
1141 PyTuple_SET_ITEM(values, i, PyBool_FromLong(ci->values[i].value.boolean));
1142 }
1143 }
1145 tmp = cpy_string_to_unicode_or_bytes(ci->key);
1146 item = PyObject_CallFunction((void *)&ConfigType, "NONO", tmp, parent, values,
1147 Py_None);
1148 if (item == NULL)
1149 return NULL;
1150 children = PyTuple_New(ci->children_num); /* New reference. */
1151 for (int i = 0; i < ci->children_num; ++i) {
1152 PyTuple_SET_ITEM(children, i,
1153 cpy_oconfig_to_pyconfig(ci->children + i, item));
1154 }
1155 tmp = ((Config *)item)->children;
1156 ((Config *)item)->children = children;
1157 Py_XDECREF(tmp);
1158 return item;
1159 }
1161 #ifdef IS_PY3K
1162 static struct PyModuleDef collectdmodule = {
1163 PyModuleDef_HEAD_INIT, "collectd", /* name of module */
1164 "The python interface to collectd", /* module documentation, may be NULL */
1165 -1, cpy_methods};
1167 PyMODINIT_FUNC PyInit_collectd(void) {
1168 return PyModule_Create(&collectdmodule);
1169 }
1170 #endif
1172 static int cpy_init_python(void) {
1173 PyOS_sighandler_t cur_sig;
1174 PyObject *sys;
1175 PyObject *module;
1177 #ifdef IS_PY3K
1178 wchar_t *argv = L"";
1179 /* Add a builtin module, before Py_Initialize */
1180 PyImport_AppendInittab("collectd", PyInit_collectd);
1181 #else
1182 char *argv = "";
1183 #endif
1185 /* Chances are the current signal handler is already SIG_DFL, but let's make
1186 * sure. */
1187 cur_sig = PyOS_setsig(SIGINT, SIG_DFL);
1188 Py_Initialize();
1189 python_sigint_handler = PyOS_setsig(SIGINT, cur_sig);
1191 PyType_Ready(&ConfigType);
1192 PyType_Ready(&PluginDataType);
1193 ValuesType.tp_base = &PluginDataType;
1194 PyType_Ready(&ValuesType);
1195 NotificationType.tp_base = &PluginDataType;
1196 PyType_Ready(&NotificationType);
1197 SignedType.tp_base = &PyLong_Type;
1198 PyType_Ready(&SignedType);
1199 UnsignedType.tp_base = &PyLong_Type;
1200 PyType_Ready(&UnsignedType);
1201 sys = PyImport_ImportModule("sys"); /* New reference. */
1202 if (sys == NULL) {
1203 cpy_log_exception("python initialization");
1204 return 1;
1205 }
1206 sys_path = PyObject_GetAttrString(sys, "path"); /* New reference. */
1207 Py_DECREF(sys);
1208 if (sys_path == NULL) {
1209 cpy_log_exception("python initialization");
1210 return 1;
1211 }
1212 PySys_SetArgv(1, &argv);
1213 PyList_SetSlice(sys_path, 0, 1, NULL);
1215 #ifdef IS_PY3K
1216 module = PyImport_ImportModule("collectd");
1217 #else
1218 module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
1219 #endif
1220 PyModule_AddObject(module, "Config",
1221 (void *)&ConfigType); /* Steals a reference. */
1222 PyModule_AddObject(module, "Values",
1223 (void *)&ValuesType); /* Steals a reference. */
1224 PyModule_AddObject(module, "Notification",
1225 (void *)&NotificationType); /* Steals a reference. */
1226 PyModule_AddObject(module, "Signed",
1227 (void *)&SignedType); /* Steals a reference. */
1228 PyModule_AddObject(module, "Unsigned",
1229 (void *)&UnsignedType); /* Steals a reference. */
1230 PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG);
1231 PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO);
1232 PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE);
1233 PyModule_AddIntConstant(module, "LOG_WARNING", LOG_WARNING);
1234 PyModule_AddIntConstant(module, "LOG_ERROR", LOG_ERR);
1235 PyModule_AddIntConstant(module, "NOTIF_FAILURE", NOTIF_FAILURE);
1236 PyModule_AddIntConstant(module, "NOTIF_WARNING", NOTIF_WARNING);
1237 PyModule_AddIntConstant(module, "NOTIF_OKAY", NOTIF_OKAY);
1238 PyModule_AddStringConstant(module, "DS_TYPE_COUNTER",
1239 DS_TYPE_TO_STRING(DS_TYPE_COUNTER));
1240 PyModule_AddStringConstant(module, "DS_TYPE_GAUGE",
1241 DS_TYPE_TO_STRING(DS_TYPE_GAUGE));
1242 PyModule_AddStringConstant(module, "DS_TYPE_DERIVE",
1243 DS_TYPE_TO_STRING(DS_TYPE_DERIVE));
1244 PyModule_AddStringConstant(module, "DS_TYPE_ABSOLUTE",
1245 DS_TYPE_TO_STRING(DS_TYPE_ABSOLUTE));
1246 return 0;
1247 }
1249 static int cpy_config(oconfig_item_t *ci) {
1250 PyObject *tb;
1251 int status = 0;
1253 /* Ok in theory we shouldn't do initialization at this point
1254 * but we have to. In order to give python scripts a chance
1255 * to register a config callback we need to be able to execute
1256 * python code during the config callback so we have to start
1257 * the interpreter here. */
1258 /* Do *not* use the python "thread" module at this point! */
1260 if (!Py_IsInitialized() && cpy_init_python())
1261 return 1;
1263 for (int i = 0; i < ci->children_num; ++i) {
1264 oconfig_item_t *item = ci->children + i;
1266 if (strcasecmp(item->key, "Interactive") == 0) {
1267 if (cf_util_get_boolean(item, &do_interactive) != 0) {
1268 status = 1;
1269 continue;
1270 }
1271 } else if (strcasecmp(item->key, "Encoding") == 0) {
1272 char *encoding = NULL;
1273 if (cf_util_get_string(item, &encoding) != 0) {
1274 status = 1;
1275 continue;
1276 }
1277 #ifdef IS_PY3K
1278 ERROR("python: \"Encoding\" was used in the config file but Python3 was "
1279 "used, which does not support changing encodings");
1280 status = 1;
1281 sfree(encoding);
1282 continue;
1283 #else
1284 /* Why is this even necessary? And undocumented? */
1285 if (PyUnicode_SetDefaultEncoding(encoding)) {
1286 cpy_log_exception("setting default encoding");
1287 status = 1;
1288 }
1289 #endif
1290 sfree(encoding);
1291 } else if (strcasecmp(item->key, "LogTraces") == 0) {
1292 _Bool log_traces;
1293 if (cf_util_get_boolean(item, &log_traces) != 0) {
1294 status = 1;
1295 continue;
1296 }
1297 if (!log_traces) {
1298 Py_XDECREF(cpy_format_exception);
1299 cpy_format_exception = NULL;
1300 continue;
1301 }
1302 if (cpy_format_exception)
1303 continue;
1304 tb = PyImport_ImportModule("traceback"); /* New reference. */
1305 if (tb == NULL) {
1306 cpy_log_exception("python initialization");
1307 status = 1;
1308 continue;
1309 }
1310 cpy_format_exception =
1311 PyObject_GetAttrString(tb, "format_exception"); /* New reference. */
1312 Py_DECREF(tb);
1313 if (cpy_format_exception == NULL) {
1314 cpy_log_exception("python initialization");
1315 status = 1;
1316 }
1317 } else if (strcasecmp(item->key, "ModulePath") == 0) {
1318 char *dir = NULL;
1319 PyObject *dir_object;
1321 if (cf_util_get_string(item, &dir) != 0) {
1322 status = 1;
1323 continue;
1324 }
1325 dir_object = cpy_string_to_unicode_or_bytes(dir); /* New reference. */
1326 if (dir_object == NULL) {
1327 ERROR("python plugin: Unable to convert \"%s\" to "
1328 "a python object.",
1329 dir);
1330 free(dir);
1331 cpy_log_exception("python initialization");
1332 status = 1;
1333 continue;
1334 }
1335 if (PyList_Insert(sys_path, 0, dir_object) != 0) {
1336 ERROR("python plugin: Unable to prepend \"%s\" to "
1337 "python module path.",
1338 dir);
1339 cpy_log_exception("python initialization");
1340 status = 1;
1341 }
1342 Py_DECREF(dir_object);
1343 free(dir);
1344 } else if (strcasecmp(item->key, "Import") == 0) {
1345 char *module_name = NULL;
1346 PyObject *module;
1348 if (cf_util_get_string(item, &module_name) != 0) {
1349 status = 1;
1350 continue;
1351 }
1352 module = PyImport_ImportModule(module_name); /* New reference. */
1353 if (module == NULL) {
1354 ERROR("python plugin: Error importing module \"%s\".", module_name);
1355 cpy_log_exception("importing module");
1356 status = 1;
1357 }
1358 free(module_name);
1359 Py_XDECREF(module);
1360 } else if (strcasecmp(item->key, "Module") == 0) {
1361 char *name = NULL;
1362 cpy_callback_t *c;
1363 PyObject *ret;
1365 if (cf_util_get_string(item, &name) != 0) {
1366 status = 1;
1367 continue;
1368 }
1369 for (c = cpy_config_callbacks; c; c = c->next) {
1370 if (strcasecmp(c->name + 7, name) == 0)
1371 break;
1372 }
1373 if (c == NULL) {
1374 WARNING("python plugin: Found a configuration for the \"%s\" plugin, "
1375 "but the plugin isn't loaded or didn't register "
1376 "a configuration callback.",
1377 name);
1378 free(name);
1379 continue;
1380 }
1381 free(name);
1382 if (c->data == NULL)
1383 ret = PyObject_CallFunction(
1384 c->callback, "N",
1385 cpy_oconfig_to_pyconfig(item, NULL)); /* New reference. */
1386 else
1387 ret = PyObject_CallFunction(c->callback, "NO",
1388 cpy_oconfig_to_pyconfig(item, NULL),
1389 c->data); /* New reference. */
1390 if (ret == NULL) {
1391 cpy_log_exception("loading module");
1392 status = 1;
1393 } else
1394 Py_DECREF(ret);
1395 } else {
1396 ERROR("python plugin: Unknown config key \"%s\".", item->key);
1397 status = 1;
1398 }
1399 }
1400 return (status);
1401 }
1403 void module_register(void) {
1404 plugin_register_complex_config("python", cpy_config);
1405 plugin_register_init("python", cpy_init);
1406 plugin_register_shutdown("python", cpy_shutdown);
1407 }