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 char CollectdError_doc[] =
237 "Basic exception for collectd Python scripts.\n"
238 "\n"
239 "Throwing this exception will not cause a stacktrace to be logged, \n"
240 "even if LogTraces is enabled in the config.";
242 static pthread_t main_thread;
243 static PyOS_sighandler_t python_sigint_handler;
244 static _Bool do_interactive = 0;
246 /* This is our global thread state. Python saves some stuff in thread-local
247 * storage. So if we allow the interpreter to run in the background
248 * (the scriptwriters might have created some threads from python), we have
249 * to save the state so we can resume it later after shutdown. */
251 static PyThreadState *state;
253 static PyObject *sys_path, *cpy_format_exception, *CollectdError;
255 static cpy_callback_t *cpy_config_callbacks;
256 static cpy_callback_t *cpy_init_callbacks;
257 static cpy_callback_t *cpy_shutdown_callbacks;
259 /* Make sure to hold the GIL while modifying these. */
260 static int cpy_shutdown_triggered = 0;
261 static int cpy_num_callbacks = 0;
263 static void cpy_destroy_user_data(void *data) {
264 cpy_callback_t *c = data;
265 free(c->name);
266 CPY_LOCK_THREADS
267 Py_DECREF(c->callback);
268 Py_XDECREF(c->data);
269 free(c);
270 --cpy_num_callbacks;
271 if (!cpy_num_callbacks && cpy_shutdown_triggered) {
272 Py_Finalize();
273 return;
274 }
275 CPY_RELEASE_THREADS
276 }
278 /* You must hold the GIL to call this function!
279 * But if you managed to extract the callback parameter then you probably
280 * already do. */
282 static void cpy_build_name(char *buf, size_t size, PyObject *callback,
283 const char *name) {
284 const char *module = NULL;
285 PyObject *mod = NULL;
287 if (name != NULL) {
288 snprintf(buf, size, "python.%s", name);
289 return;
290 }
292 mod = PyObject_GetAttrString(callback, "__module__"); /* New reference. */
293 if (mod != NULL)
294 module = cpy_unicode_or_bytes_to_string(&mod);
296 if (module != NULL) {
297 snprintf(buf, size, "python.%s", module);
298 Py_XDECREF(mod);
299 PyErr_Clear();
300 return;
301 }
302 Py_XDECREF(mod);
304 snprintf(buf, size, "python.%p", callback);
305 PyErr_Clear();
306 }
308 void cpy_log_exception(const char *context) {
309 int l = 0, collectd_error;
310 const char *typename = NULL, *message = NULL;
311 PyObject *type, *value, *traceback, *tn, *m, *list;
313 PyErr_Fetch(&type, &value, &traceback);
314 PyErr_NormalizeException(&type, &value, &traceback);
315 if (type == NULL)
316 return;
317 collectd_error = PyErr_GivenExceptionMatches(value, CollectdError);
318 tn = PyObject_GetAttrString(type, "__name__"); /* New reference. */
319 m = PyObject_Str(value); /* New reference. */
320 if (tn != NULL)
321 typename = cpy_unicode_or_bytes_to_string(&tn);
322 if (m != NULL)
323 message = cpy_unicode_or_bytes_to_string(&m);
324 if (typename == NULL)
325 typename = "NamelessException";
326 if (message == NULL)
327 message = "N/A";
328 Py_BEGIN_ALLOW_THREADS;
329 if (collectd_error) {
330 WARNING("%s in %s: %s", typename, context, message);
331 } else {
332 ERROR("Unhandled python exception in %s: %s: %s", context, typename,
333 message);
334 }
335 Py_END_ALLOW_THREADS;
336 Py_XDECREF(tn);
337 Py_XDECREF(m);
338 if (!cpy_format_exception || !traceback || collectd_error) {
339 PyErr_Clear();
340 Py_DECREF(type);
341 Py_XDECREF(value);
342 Py_XDECREF(traceback);
343 return;
344 }
345 list = PyObject_CallFunction(cpy_format_exception, "NNN", type, value,
346 traceback); /* New reference. Steals references
347 from "type", "value" and
348 "traceback". */
349 if (list)
350 l = PyObject_Length(list);
352 for (int i = 0; i < l; ++i) {
353 PyObject *line;
354 char const *msg;
355 char *cpy;
357 line = PyList_GET_ITEM(list, i); /* Borrowed reference. */
358 Py_INCREF(line);
360 msg = cpy_unicode_or_bytes_to_string(&line);
361 Py_DECREF(line);
362 if (msg == NULL)
363 continue;
365 cpy = strdup(msg);
366 if (cpy == NULL)
367 continue;
369 if (cpy[strlen(cpy) - 1] == '\n')
370 cpy[strlen(cpy) - 1] = 0;
372 Py_BEGIN_ALLOW_THREADS;
373 ERROR("%s", cpy);
374 Py_END_ALLOW_THREADS;
376 free(cpy);
377 }
379 Py_XDECREF(list);
380 PyErr_Clear();
381 }
383 static int cpy_read_callback(user_data_t *data) {
384 cpy_callback_t *c = data->data;
385 PyObject *ret;
387 CPY_LOCK_THREADS
388 ret = PyObject_CallFunctionObjArgs(c->callback, c->data,
389 (void *)0); /* New reference. */
390 if (ret == NULL) {
391 cpy_log_exception("read callback");
392 } else {
393 Py_DECREF(ret);
394 }
395 CPY_RELEASE_THREADS
396 if (ret == NULL)
397 return 1;
398 return 0;
399 }
401 static int cpy_write_callback(const data_set_t *ds,
402 const value_list_t *value_list,
403 user_data_t *data) {
404 cpy_callback_t *c = data->data;
405 PyObject *ret, *list, *temp, *dict = NULL;
406 Values *v;
408 CPY_LOCK_THREADS
409 list = PyList_New(value_list->values_len); /* New reference. */
410 if (list == NULL) {
411 cpy_log_exception("write callback");
412 CPY_RETURN_FROM_THREADS 0;
413 }
414 for (size_t i = 0; i < value_list->values_len; ++i) {
415 if (ds->ds[i].type == DS_TYPE_COUNTER) {
416 PyList_SetItem(
417 list, i, PyLong_FromUnsignedLongLong(value_list->values[i].counter));
418 } else if (ds->ds[i].type == DS_TYPE_GAUGE) {
419 PyList_SetItem(list, i, PyFloat_FromDouble(value_list->values[i].gauge));
420 } else if (ds->ds[i].type == DS_TYPE_DERIVE) {
421 PyList_SetItem(list, i,
422 PyLong_FromLongLong(value_list->values[i].derive));
423 } else if (ds->ds[i].type == DS_TYPE_ABSOLUTE) {
424 PyList_SetItem(
425 list, i, PyLong_FromUnsignedLongLong(value_list->values[i].absolute));
426 } else {
427 Py_BEGIN_ALLOW_THREADS;
428 ERROR("cpy_write_callback: Unknown value type %d.", ds->ds[i].type);
429 Py_END_ALLOW_THREADS;
430 Py_DECREF(list);
431 CPY_RETURN_FROM_THREADS 0;
432 }
433 if (PyErr_Occurred() != NULL) {
434 cpy_log_exception("value building for write callback");
435 Py_DECREF(list);
436 CPY_RETURN_FROM_THREADS 0;
437 }
438 }
439 dict = PyDict_New(); /* New reference. */
440 if (value_list->meta) {
441 char **table = NULL;
442 meta_data_t *meta = value_list->meta;
444 int num = meta_data_toc(meta, &table);
445 for (int i = 0; i < num; ++i) {
446 int type;
447 char *string;
448 int64_t si;
449 uint64_t ui;
450 double d;
451 _Bool b;
453 type = meta_data_type(meta, table[i]);
454 if (type == MD_TYPE_STRING) {
455 if (meta_data_get_string(meta, table[i], &string))
456 continue;
457 temp = cpy_string_to_unicode_or_bytes(string); /* New reference. */
458 free(string);
459 PyDict_SetItemString(dict, table[i], temp);
460 Py_XDECREF(temp);
461 } else if (type == MD_TYPE_SIGNED_INT) {
462 if (meta_data_get_signed_int(meta, table[i], &si))
463 continue;
464 PyObject *sival = PyLong_FromLongLong(si); /* New reference */
465 temp = PyObject_CallFunctionObjArgs((void *)&SignedType, sival,
466 (void *)0); /* New reference. */
467 PyDict_SetItemString(dict, table[i], temp);
468 Py_XDECREF(temp);
469 Py_XDECREF(sival);
470 } else if (type == MD_TYPE_UNSIGNED_INT) {
471 if (meta_data_get_unsigned_int(meta, table[i], &ui))
472 continue;
473 PyObject *uval = PyLong_FromUnsignedLongLong(ui); /* New reference */
474 temp = PyObject_CallFunctionObjArgs((void *)&UnsignedType, uval,
475 (void *)0); /* New reference. */
476 PyDict_SetItemString(dict, table[i], temp);
477 Py_XDECREF(temp);
478 Py_XDECREF(uval);
479 } else if (type == MD_TYPE_DOUBLE) {
480 if (meta_data_get_double(meta, table[i], &d))
481 continue;
482 temp = PyFloat_FromDouble(d); /* New reference. */
483 PyDict_SetItemString(dict, table[i], temp);
484 Py_XDECREF(temp);
485 } else if (type == MD_TYPE_BOOLEAN) {
486 if (meta_data_get_boolean(meta, table[i], &b))
487 continue;
488 if (b)
489 PyDict_SetItemString(dict, table[i], Py_True);
490 else
491 PyDict_SetItemString(dict, table[i], Py_False);
492 }
493 free(table[i]);
494 }
495 free(table);
496 }
497 v = (Values *)Values_New(); /* New reference. */
498 sstrncpy(v->data.host, value_list->host, sizeof(v->data.host));
499 sstrncpy(v->data.type, value_list->type, sizeof(v->data.type));
500 sstrncpy(v->data.type_instance, value_list->type_instance,
501 sizeof(v->data.type_instance));
502 sstrncpy(v->data.plugin, value_list->plugin, sizeof(v->data.plugin));
503 sstrncpy(v->data.plugin_instance, value_list->plugin_instance,
504 sizeof(v->data.plugin_instance));
505 v->data.time = CDTIME_T_TO_DOUBLE(value_list->time);
506 v->interval = CDTIME_T_TO_DOUBLE(value_list->interval);
507 Py_CLEAR(v->values);
508 v->values = list;
509 Py_CLEAR(v->meta);
510 v->meta = dict; /* Steals a reference. */
511 ret = PyObject_CallFunctionObjArgs(c->callback, v, c->data,
512 (void *)0); /* New reference. */
513 Py_XDECREF(v);
514 if (ret == NULL) {
515 cpy_log_exception("write callback");
516 } else {
517 Py_DECREF(ret);
518 }
519 CPY_RELEASE_THREADS
520 return 0;
521 }
523 static int cpy_notification_callback(const notification_t *notification,
524 user_data_t *data) {
525 cpy_callback_t *c = data->data;
526 PyObject *ret, *notify;
527 Notification *n;
529 CPY_LOCK_THREADS
530 PyObject *dict = PyDict_New(); /* New reference. */
531 for (notification_meta_t *meta = notification->meta; meta != NULL;
532 meta = meta->next) {
533 PyObject *temp = NULL;
534 if (meta->type == NM_TYPE_STRING) {
535 temp = cpy_string_to_unicode_or_bytes(
536 meta->nm_value.nm_string); /* New reference. */
537 PyDict_SetItemString(dict, meta->name, temp);
538 Py_XDECREF(temp);
539 } else if (meta->type == NM_TYPE_SIGNED_INT) {
540 PyObject *sival = PyLong_FromLongLong(meta->nm_value.nm_signed_int);
541 temp = PyObject_CallFunctionObjArgs((void *)&SignedType, sival,
542 (void *)0); /* New reference. */
543 PyDict_SetItemString(dict, meta->name, temp);
544 Py_XDECREF(temp);
545 Py_XDECREF(sival);
546 } else if (meta->type == NM_TYPE_UNSIGNED_INT) {
547 PyObject *uval =
548 PyLong_FromUnsignedLongLong(meta->nm_value.nm_unsigned_int);
549 temp = PyObject_CallFunctionObjArgs((void *)&UnsignedType, uval,
550 (void *)0); /* New reference. */
551 PyDict_SetItemString(dict, meta->name, temp);
552 Py_XDECREF(temp);
553 Py_XDECREF(uval);
554 } else if (meta->type == NM_TYPE_DOUBLE) {
555 temp = PyFloat_FromDouble(meta->nm_value.nm_double); /* New reference. */
556 PyDict_SetItemString(dict, meta->name, temp);
557 Py_XDECREF(temp);
558 } else if (meta->type == NM_TYPE_BOOLEAN) {
559 PyDict_SetItemString(dict, meta->name,
560 meta->nm_value.nm_boolean ? Py_True : Py_False);
561 }
562 }
563 notify = Notification_New(); /* New reference. */
564 n = (Notification *)notify;
565 sstrncpy(n->data.host, notification->host, sizeof(n->data.host));
566 sstrncpy(n->data.type, notification->type, sizeof(n->data.type));
567 sstrncpy(n->data.type_instance, notification->type_instance,
568 sizeof(n->data.type_instance));
569 sstrncpy(n->data.plugin, notification->plugin, sizeof(n->data.plugin));
570 sstrncpy(n->data.plugin_instance, notification->plugin_instance,
571 sizeof(n->data.plugin_instance));
572 n->data.time = CDTIME_T_TO_DOUBLE(notification->time);
573 sstrncpy(n->message, notification->message, sizeof(n->message));
574 n->severity = notification->severity;
575 Py_CLEAR(n->meta);
576 n->meta = dict; /* Steals a reference. */
577 ret = PyObject_CallFunctionObjArgs(c->callback, n, c->data,
578 (void *)0); /* New reference. */
579 Py_XDECREF(notify);
580 if (ret == NULL) {
581 cpy_log_exception("notification callback");
582 } else {
583 Py_DECREF(ret);
584 }
585 CPY_RELEASE_THREADS
586 return 0;
587 }
589 static void cpy_log_callback(int severity, const char *message,
590 user_data_t *data) {
591 cpy_callback_t *c = data->data;
592 PyObject *ret, *text;
594 CPY_LOCK_THREADS
595 text = cpy_string_to_unicode_or_bytes(message); /* New reference. */
596 if (c->data == NULL)
597 ret = PyObject_CallFunction(
598 c->callback, "iN", severity,
599 text); /* New reference. Steals a reference from "text". */
600 else
601 ret = PyObject_CallFunction(
602 c->callback, "iNO", severity, text,
603 c->data); /* New reference. Steals a reference from "text". */
605 if (ret == NULL) {
606 /* FIXME */
607 /* Do we really want to trigger a log callback because a log callback
608 * failed?
609 * Probably not. */
610 PyErr_Print();
611 /* In case someone wanted to be clever, replaced stderr and failed at that.
612 */
613 PyErr_Clear();
614 } else {
615 Py_DECREF(ret);
616 }
617 CPY_RELEASE_THREADS
618 }
620 static void cpy_flush_callback(int timeout, const char *id, user_data_t *data) {
621 cpy_callback_t *c = data->data;
622 PyObject *ret, *text;
624 CPY_LOCK_THREADS
625 if (id) {
626 text = cpy_string_to_unicode_or_bytes(id);
627 } else {
628 text = Py_None;
629 Py_INCREF(text);
630 }
631 if (c->data == NULL)
632 ret = PyObject_CallFunction(c->callback, "iN", timeout,
633 text); /* New reference. */
634 else
635 ret = PyObject_CallFunction(c->callback, "iNO", timeout, text,
636 c->data); /* New reference. */
638 if (ret == NULL) {
639 cpy_log_exception("flush callback");
640 } else {
641 Py_DECREF(ret);
642 }
643 CPY_RELEASE_THREADS
644 }
646 static PyObject *cpy_register_generic(cpy_callback_t **list_head,
647 PyObject *args, PyObject *kwds) {
648 char buf[512];
649 cpy_callback_t *c;
650 char *name = NULL;
651 PyObject *callback = NULL, *data = NULL, *mod = NULL;
652 static char *kwlist[] = {"callback", "data", "name", NULL};
654 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oet", kwlist, &callback, &data,
655 NULL, &name) == 0)
656 return NULL;
657 if (PyCallable_Check(callback) == 0) {
658 PyMem_Free(name);
659 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
660 return NULL;
661 }
662 cpy_build_name(buf, sizeof(buf), callback, name);
664 Py_INCREF(callback);
665 Py_XINCREF(data);
667 c = calloc(1, sizeof(*c));
668 if (c == NULL)
669 return NULL;
671 c->name = strdup(buf);
672 c->callback = callback;
673 c->data = data;
674 c->next = *list_head;
675 ++cpy_num_callbacks;
676 *list_head = c;
677 Py_XDECREF(mod);
678 PyMem_Free(name);
679 return cpy_string_to_unicode_or_bytes(buf);
680 }
682 static PyObject *float_or_none(float number) {
683 if (isnan(number)) {
684 Py_RETURN_NONE;
685 }
686 return PyFloat_FromDouble(number);
687 }
689 static PyObject *cpy_get_dataset(PyObject *self, PyObject *args) {
690 char *name;
691 const data_set_t *ds;
692 PyObject *list, *tuple;
694 if (PyArg_ParseTuple(args, "et", NULL, &name) == 0)
695 return NULL;
696 ds = plugin_get_ds(name);
697 PyMem_Free(name);
698 if (ds == NULL) {
699 PyErr_Format(PyExc_TypeError, "Dataset %s not found", name);
700 return NULL;
701 }
702 list = PyList_New(ds->ds_num); /* New reference. */
703 for (size_t i = 0; i < ds->ds_num; ++i) {
704 tuple = PyTuple_New(4);
705 PyTuple_SET_ITEM(tuple, 0, cpy_string_to_unicode_or_bytes(ds->ds[i].name));
706 PyTuple_SET_ITEM(
707 tuple, 1,
708 cpy_string_to_unicode_or_bytes(DS_TYPE_TO_STRING(ds->ds[i].type)));
709 PyTuple_SET_ITEM(tuple, 2, float_or_none(ds->ds[i].min));
710 PyTuple_SET_ITEM(tuple, 3, float_or_none(ds->ds[i].max));
711 PyList_SET_ITEM(list, i, tuple);
712 }
713 return list;
714 }
716 static PyObject *cpy_flush(PyObject *self, PyObject *args, PyObject *kwds) {
717 int timeout = -1;
718 char *plugin = NULL, *identifier = NULL;
719 static char *kwlist[] = {"plugin", "timeout", "identifier", NULL};
721 if (PyArg_ParseTupleAndKeywords(args, kwds, "|etiet", kwlist, NULL, &plugin,
722 &timeout, NULL, &identifier) == 0)
723 return NULL;
724 Py_BEGIN_ALLOW_THREADS;
725 plugin_flush(plugin, timeout, identifier);
726 Py_END_ALLOW_THREADS;
727 PyMem_Free(plugin);
728 PyMem_Free(identifier);
729 Py_RETURN_NONE;
730 }
732 static PyObject *cpy_register_config(PyObject *self, PyObject *args,
733 PyObject *kwds) {
734 return cpy_register_generic(&cpy_config_callbacks, args, kwds);
735 }
737 static PyObject *cpy_register_init(PyObject *self, PyObject *args,
738 PyObject *kwds) {
739 return cpy_register_generic(&cpy_init_callbacks, args, kwds);
740 }
742 typedef int reg_function_t(const char *name, void *callback, void *data);
744 static PyObject *cpy_register_generic_userdata(void *reg, void *handler,
745 PyObject *args, PyObject *kwds) {
746 char buf[512];
747 reg_function_t *register_function = (reg_function_t *)reg;
748 cpy_callback_t *c = NULL;
749 char *name = NULL;
750 PyObject *callback = NULL, *data = NULL;
751 static char *kwlist[] = {"callback", "data", "name", NULL};
753 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oet", kwlist, &callback, &data,
754 NULL, &name) == 0)
755 return NULL;
756 if (PyCallable_Check(callback) == 0) {
757 PyMem_Free(name);
758 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
759 return NULL;
760 }
761 cpy_build_name(buf, sizeof(buf), callback, name);
762 PyMem_Free(name);
764 Py_INCREF(callback);
765 Py_XINCREF(data);
767 c = calloc(1, sizeof(*c));
768 if (c == NULL)
769 return NULL;
771 c->name = strdup(buf);
772 c->callback = callback;
773 c->data = data;
774 c->next = NULL;
776 register_function(buf, handler,
777 &(user_data_t){
778 .data = c,
779 .free_func = cpy_destroy_user_data,
780 });
782 ++cpy_num_callbacks;
783 return cpy_string_to_unicode_or_bytes(buf);
784 }
786 static PyObject *cpy_register_read(PyObject *self, PyObject *args,
787 PyObject *kwds) {
788 char buf[512];
789 cpy_callback_t *c = NULL;
790 double interval = 0;
791 char *name = NULL;
792 PyObject *callback = NULL, *data = NULL;
793 static char *kwlist[] = {"callback", "interval", "data", "name", NULL};
795 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|dOet", kwlist, &callback,
796 &interval, &data, NULL, &name) == 0)
797 return NULL;
798 if (PyCallable_Check(callback) == 0) {
799 PyMem_Free(name);
800 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
801 return NULL;
802 }
803 cpy_build_name(buf, sizeof(buf), callback, name);
804 PyMem_Free(name);
806 Py_INCREF(callback);
807 Py_XINCREF(data);
809 c = calloc(1, sizeof(*c));
810 if (c == NULL)
811 return NULL;
813 c->name = strdup(buf);
814 c->callback = callback;
815 c->data = data;
816 c->next = NULL;
818 plugin_register_complex_read(
819 /* group = */ "python", buf, cpy_read_callback,
820 DOUBLE_TO_CDTIME_T(interval),
821 &(user_data_t){
822 .data = c,
823 .free_func = cpy_destroy_user_data,
824 });
825 ++cpy_num_callbacks;
826 return cpy_string_to_unicode_or_bytes(buf);
827 }
829 static PyObject *cpy_register_log(PyObject *self, PyObject *args,
830 PyObject *kwds) {
831 return cpy_register_generic_userdata((void *)plugin_register_log,
832 (void *)cpy_log_callback, args, kwds);
833 }
835 static PyObject *cpy_register_write(PyObject *self, PyObject *args,
836 PyObject *kwds) {
837 return cpy_register_generic_userdata((void *)plugin_register_write,
838 (void *)cpy_write_callback, args, kwds);
839 }
841 static PyObject *cpy_register_notification(PyObject *self, PyObject *args,
842 PyObject *kwds) {
843 return cpy_register_generic_userdata((void *)plugin_register_notification,
844 (void *)cpy_notification_callback, args,
845 kwds);
846 }
848 static PyObject *cpy_register_flush(PyObject *self, PyObject *args,
849 PyObject *kwds) {
850 return cpy_register_generic_userdata((void *)plugin_register_flush,
851 (void *)cpy_flush_callback, args, kwds);
852 }
854 static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args,
855 PyObject *kwds) {
856 return cpy_register_generic(&cpy_shutdown_callbacks, args, kwds);
857 }
859 static PyObject *cpy_error(PyObject *self, PyObject *args) {
860 char *text;
861 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
862 return NULL;
863 Py_BEGIN_ALLOW_THREADS;
864 plugin_log(LOG_ERR, "%s", text);
865 Py_END_ALLOW_THREADS;
866 PyMem_Free(text);
867 Py_RETURN_NONE;
868 }
870 static PyObject *cpy_warning(PyObject *self, PyObject *args) {
871 char *text;
872 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
873 return NULL;
874 Py_BEGIN_ALLOW_THREADS;
875 plugin_log(LOG_WARNING, "%s", text);
876 Py_END_ALLOW_THREADS;
877 PyMem_Free(text);
878 Py_RETURN_NONE;
879 }
881 static PyObject *cpy_notice(PyObject *self, PyObject *args) {
882 char *text;
883 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
884 return NULL;
885 Py_BEGIN_ALLOW_THREADS;
886 plugin_log(LOG_NOTICE, "%s", text);
887 Py_END_ALLOW_THREADS;
888 PyMem_Free(text);
889 Py_RETURN_NONE;
890 }
892 static PyObject *cpy_info(PyObject *self, PyObject *args) {
893 char *text;
894 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
895 return NULL;
896 Py_BEGIN_ALLOW_THREADS;
897 plugin_log(LOG_INFO, "%s", text);
898 Py_END_ALLOW_THREADS;
899 PyMem_Free(text);
900 Py_RETURN_NONE;
901 }
903 static PyObject *cpy_debug(PyObject *self, PyObject *args) {
904 #ifdef COLLECT_DEBUG
905 char *text;
906 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
907 return NULL;
908 Py_BEGIN_ALLOW_THREADS;
909 plugin_log(LOG_DEBUG, "%s", text);
910 Py_END_ALLOW_THREADS;
911 PyMem_Free(text);
912 #endif
913 Py_RETURN_NONE;
914 }
916 static PyObject *cpy_unregister_generic(cpy_callback_t **list_head,
917 PyObject *arg, const char *desc) {
918 char buf[512];
919 const char *name;
920 cpy_callback_t *prev = NULL, *tmp;
922 Py_INCREF(arg);
923 name = cpy_unicode_or_bytes_to_string(&arg);
924 if (name == NULL) {
925 PyErr_Clear();
926 if (!PyCallable_Check(arg)) {
927 PyErr_SetString(PyExc_TypeError, "This function needs a string or a "
928 "callable object as its only "
929 "parameter.");
930 Py_DECREF(arg);
931 return NULL;
932 }
933 cpy_build_name(buf, sizeof(buf), arg, NULL);
934 name = buf;
935 }
936 for (tmp = *list_head; tmp; prev = tmp, tmp = tmp->next)
937 if (strcmp(name, tmp->name) == 0)
938 break;
940 Py_DECREF(arg);
941 if (tmp == NULL) {
942 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.",
943 desc, name);
944 return NULL;
945 }
946 /* Yes, this is actually safe. To call this function the caller has to
947 * hold the GIL. Well, safe as long as there is only one GIL anyway ... */
948 if (prev == NULL)
949 *list_head = tmp->next;
950 else
951 prev->next = tmp->next;
952 cpy_destroy_user_data(tmp);
953 Py_RETURN_NONE;
954 }
956 static void cpy_unregister_list(cpy_callback_t **list_head) {
957 cpy_callback_t *cur, *next;
958 for (cur = *list_head; cur; cur = next) {
959 next = cur->next;
960 cpy_destroy_user_data(cur);
961 }
962 *list_head = NULL;
963 }
965 typedef int cpy_unregister_function_t(const char *name);
967 static PyObject *
968 cpy_unregister_generic_userdata(cpy_unregister_function_t *unreg, PyObject *arg,
969 const char *desc) {
970 char buf[512];
971 const char *name;
973 Py_INCREF(arg);
974 name = cpy_unicode_or_bytes_to_string(&arg);
975 if (name == NULL) {
976 PyErr_Clear();
977 if (!PyCallable_Check(arg)) {
978 PyErr_SetString(PyExc_TypeError, "This function needs a string or a "
979 "callable object as its only "
980 "parameter.");
981 Py_DECREF(arg);
982 return NULL;
983 }
984 cpy_build_name(buf, sizeof(buf), arg, NULL);
985 name = buf;
986 }
987 if (unreg(name) == 0) {
988 Py_DECREF(arg);
989 Py_RETURN_NONE;
990 }
991 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.",
992 desc, name);
993 Py_DECREF(arg);
994 return NULL;
995 }
997 static PyObject *cpy_unregister_log(PyObject *self, PyObject *arg) {
998 return cpy_unregister_generic_userdata(plugin_unregister_log, arg, "log");
999 }
1001 static PyObject *cpy_unregister_init(PyObject *self, PyObject *arg) {
1002 return cpy_unregister_generic(&cpy_init_callbacks, arg, "init");
1003 }
1005 static PyObject *cpy_unregister_config(PyObject *self, PyObject *arg) {
1006 return cpy_unregister_generic(&cpy_config_callbacks, arg, "config");
1007 }
1009 static PyObject *cpy_unregister_read(PyObject *self, PyObject *arg) {
1010 return cpy_unregister_generic_userdata(plugin_unregister_read, arg, "read");
1011 }
1013 static PyObject *cpy_unregister_write(PyObject *self, PyObject *arg) {
1014 return cpy_unregister_generic_userdata(plugin_unregister_write, arg, "write");
1015 }
1017 static PyObject *cpy_unregister_notification(PyObject *self, PyObject *arg) {
1018 return cpy_unregister_generic_userdata(plugin_unregister_notification, arg,
1019 "notification");
1020 }
1022 static PyObject *cpy_unregister_flush(PyObject *self, PyObject *arg) {
1023 return cpy_unregister_generic_userdata(plugin_unregister_flush, arg, "flush");
1024 }
1026 static PyObject *cpy_unregister_shutdown(PyObject *self, PyObject *arg) {
1027 return cpy_unregister_generic(&cpy_shutdown_callbacks, arg, "shutdown");
1028 }
1030 static PyMethodDef cpy_methods[] = {
1031 {"debug", cpy_debug, METH_VARARGS, log_doc},
1032 {"info", cpy_info, METH_VARARGS, log_doc},
1033 {"notice", cpy_notice, METH_VARARGS, log_doc},
1034 {"warning", cpy_warning, METH_VARARGS, log_doc},
1035 {"error", cpy_error, METH_VARARGS, log_doc},
1036 {"get_dataset", (PyCFunction)cpy_get_dataset, METH_VARARGS, get_ds_doc},
1037 {"flush", (PyCFunction)cpy_flush, METH_VARARGS | METH_KEYWORDS, flush_doc},
1038 {"register_log", (PyCFunction)cpy_register_log,
1039 METH_VARARGS | METH_KEYWORDS, reg_log_doc},
1040 {"register_init", (PyCFunction)cpy_register_init,
1041 METH_VARARGS | METH_KEYWORDS, reg_init_doc},
1042 {"register_config", (PyCFunction)cpy_register_config,
1043 METH_VARARGS | METH_KEYWORDS, reg_config_doc},
1044 {"register_read", (PyCFunction)cpy_register_read,
1045 METH_VARARGS | METH_KEYWORDS, reg_read_doc},
1046 {"register_write", (PyCFunction)cpy_register_write,
1047 METH_VARARGS | METH_KEYWORDS, reg_write_doc},
1048 {"register_notification", (PyCFunction)cpy_register_notification,
1049 METH_VARARGS | METH_KEYWORDS, reg_notification_doc},
1050 {"register_flush", (PyCFunction)cpy_register_flush,
1051 METH_VARARGS | METH_KEYWORDS, reg_flush_doc},
1052 {"register_shutdown", (PyCFunction)cpy_register_shutdown,
1053 METH_VARARGS | METH_KEYWORDS, reg_shutdown_doc},
1054 {"unregister_log", cpy_unregister_log, METH_O, unregister_doc},
1055 {"unregister_init", cpy_unregister_init, METH_O, unregister_doc},
1056 {"unregister_config", cpy_unregister_config, METH_O, unregister_doc},
1057 {"unregister_read", cpy_unregister_read, METH_O, unregister_doc},
1058 {"unregister_write", cpy_unregister_write, METH_O, unregister_doc},
1059 {"unregister_notification", cpy_unregister_notification, METH_O,
1060 unregister_doc},
1061 {"unregister_flush", cpy_unregister_flush, METH_O, unregister_doc},
1062 {"unregister_shutdown", cpy_unregister_shutdown, METH_O, unregister_doc},
1063 {0, 0, 0, 0}};
1065 static int cpy_shutdown(void) {
1066 PyObject *ret;
1068 if (!state) {
1069 printf(
1070 "================================================================\n");
1071 printf(
1072 "collectd shutdown while running an interactive session. This will\n");
1073 printf("probably leave your terminal in a mess.\n");
1074 printf("Run the command \"reset\" to get it back into a usable state.\n");
1075 printf("You can press Ctrl+D in the interactive session to\n");
1076 printf("close collectd and avoid this problem in the future.\n");
1077 printf(
1078 "================================================================\n");
1079 }
1081 CPY_LOCK_THREADS
1083 for (cpy_callback_t *c = cpy_shutdown_callbacks; c; c = c->next) {
1084 ret = PyObject_CallFunctionObjArgs(c->callback, c->data,
1085 (void *)0); /* New reference. */
1086 if (ret == NULL)
1087 cpy_log_exception("shutdown callback");
1088 else
1089 Py_DECREF(ret);
1090 }
1091 PyErr_Print();
1093 Py_BEGIN_ALLOW_THREADS;
1094 cpy_unregister_list(&cpy_config_callbacks);
1095 cpy_unregister_list(&cpy_init_callbacks);
1096 cpy_unregister_list(&cpy_shutdown_callbacks);
1097 cpy_shutdown_triggered = 1;
1098 Py_END_ALLOW_THREADS;
1100 if (!cpy_num_callbacks) {
1101 Py_Finalize();
1102 return 0;
1103 }
1105 CPY_RELEASE_THREADS
1106 return 0;
1107 }
1109 static void *cpy_interactive(void *pipefd) {
1110 PyOS_sighandler_t cur_sig;
1112 /* Signal handler in a plugin? Bad stuff, but the best way to
1113 * handle it I guess. In an interactive session people will
1114 * press Ctrl+C at some time, which will generate a SIGINT.
1115 * This will cause collectd to shutdown, thus killing the
1116 * interactive interpreter, and leaving the terminal in a
1117 * mess. Chances are, this isn't what the user wanted to do.
1118 *
1119 * So this is the plan:
1120 * 1. Restore Python's own signal handler
1121 * 2. Tell Python we just forked so it will accept this thread
1122 * as the main one. No version of Python will ever handle
1123 * interrupts anywhere but in the main thread.
1124 * 3. After the interactive loop is done, restore collectd's
1125 * SIGINT handler.
1126 * 4. Raise SIGINT for a clean shutdown. The signal is sent to
1127 * the main thread to ensure it wakes up the main interval
1128 * sleep so that collectd shuts down immediately not in 10
1129 * seconds.
1130 *
1131 * This will make sure that SIGINT won't kill collectd but
1132 * still interrupt syscalls like sleep and pause. */
1134 if (PyImport_ImportModule("readline") == NULL) {
1135 /* This interactive session will suck. */
1136 cpy_log_exception("interactive session init");
1137 }
1138 cur_sig = PyOS_setsig(SIGINT, python_sigint_handler);
1139 PyOS_AfterFork();
1140 PyEval_InitThreads();
1141 close(*(int *)pipefd);
1142 PyRun_InteractiveLoop(stdin, "<stdin>");
1143 PyOS_setsig(SIGINT, cur_sig);
1144 PyErr_Print();
1145 state = PyEval_SaveThread();
1146 NOTICE("python: Interactive interpreter exited, stopping collectd ...");
1147 pthread_kill(main_thread, SIGINT);
1148 return NULL;
1149 }
1151 static int cpy_init(void) {
1152 PyObject *ret;
1153 int pipefd[2];
1154 char buf;
1155 static pthread_t thread;
1157 if (!Py_IsInitialized()) {
1158 WARNING("python: Plugin loaded but not configured.");
1159 plugin_unregister_shutdown("python");
1160 Py_Finalize();
1161 return 0;
1162 }
1163 main_thread = pthread_self();
1164 if (do_interactive) {
1165 if (pipe(pipefd)) {
1166 ERROR("python: Unable to create pipe.");
1167 return 1;
1168 }
1169 if (plugin_thread_create(&thread, NULL, cpy_interactive, pipefd + 1,
1170 "python interpreter")) {
1171 ERROR("python: Error creating thread for interactive interpreter.");
1172 }
1173 if (read(pipefd[0], &buf, 1))
1174 ;
1175 (void)close(pipefd[0]);
1176 } else {
1177 PyEval_InitThreads();
1178 state = PyEval_SaveThread();
1179 }
1180 CPY_LOCK_THREADS
1181 for (cpy_callback_t *c = cpy_init_callbacks; c; c = c->next) {
1182 ret = PyObject_CallFunctionObjArgs(c->callback, c->data,
1183 (void *)0); /* New reference. */
1184 if (ret == NULL)
1185 cpy_log_exception("init callback");
1186 else
1187 Py_DECREF(ret);
1188 }
1189 CPY_RELEASE_THREADS
1191 return 0;
1192 }
1194 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
1195 PyObject *item, *values, *children, *tmp;
1197 if (parent == NULL)
1198 parent = Py_None;
1200 values = PyTuple_New(ci->values_num); /* New reference. */
1201 for (int i = 0; i < ci->values_num; ++i) {
1202 if (ci->values[i].type == OCONFIG_TYPE_STRING) {
1203 PyTuple_SET_ITEM(
1204 values, i,
1205 cpy_string_to_unicode_or_bytes(ci->values[i].value.string));
1206 } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
1207 PyTuple_SET_ITEM(values, i,
1208 PyFloat_FromDouble(ci->values[i].value.number));
1209 } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
1210 PyTuple_SET_ITEM(values, i, PyBool_FromLong(ci->values[i].value.boolean));
1211 }
1212 }
1214 tmp = cpy_string_to_unicode_or_bytes(ci->key);
1215 item = PyObject_CallFunction((void *)&ConfigType, "NONO", tmp, parent, values,
1216 Py_None);
1217 if (item == NULL)
1218 return NULL;
1219 children = PyTuple_New(ci->children_num); /* New reference. */
1220 for (int i = 0; i < ci->children_num; ++i) {
1221 PyTuple_SET_ITEM(children, i,
1222 cpy_oconfig_to_pyconfig(ci->children + i, item));
1223 }
1224 tmp = ((Config *)item)->children;
1225 ((Config *)item)->children = children;
1226 Py_XDECREF(tmp);
1227 return item;
1228 }
1230 #ifdef IS_PY3K
1231 static struct PyModuleDef collectdmodule = {
1232 PyModuleDef_HEAD_INIT, "collectd", /* name of module */
1233 "The python interface to collectd", /* module documentation, may be NULL */
1234 -1, cpy_methods};
1236 PyMODINIT_FUNC PyInit_collectd(void) {
1237 return PyModule_Create(&collectdmodule);
1238 }
1239 #endif
1241 static int cpy_init_python(void) {
1242 PyOS_sighandler_t cur_sig;
1243 PyObject *sys, *errordict;
1244 PyObject *module;
1246 #ifdef IS_PY3K
1247 wchar_t *argv = L"";
1248 /* Add a builtin module, before Py_Initialize */
1249 PyImport_AppendInittab("collectd", PyInit_collectd);
1250 #else
1251 char *argv = "";
1252 #endif
1254 /* Chances are the current signal handler is already SIG_DFL, but let's make
1255 * sure. */
1256 cur_sig = PyOS_setsig(SIGINT, SIG_DFL);
1257 Py_Initialize();
1258 python_sigint_handler = PyOS_setsig(SIGINT, cur_sig);
1260 PyType_Ready(&ConfigType);
1261 PyType_Ready(&PluginDataType);
1262 ValuesType.tp_base = &PluginDataType;
1263 PyType_Ready(&ValuesType);
1264 NotificationType.tp_base = &PluginDataType;
1265 PyType_Ready(&NotificationType);
1266 SignedType.tp_base = &PyLong_Type;
1267 PyType_Ready(&SignedType);
1268 UnsignedType.tp_base = &PyLong_Type;
1269 PyType_Ready(&UnsignedType);
1270 errordict = PyDict_New();
1271 PyDict_SetItemString(
1272 errordict, "__doc__",
1273 cpy_string_to_unicode_or_bytes(CollectdError_doc)); /* New reference. */
1274 CollectdError = PyErr_NewException("collectd.CollectdError", NULL, errordict);
1275 sys = PyImport_ImportModule("sys"); /* New reference. */
1276 if (sys == NULL) {
1277 cpy_log_exception("python initialization");
1278 return 1;
1279 }
1280 sys_path = PyObject_GetAttrString(sys, "path"); /* New reference. */
1281 Py_DECREF(sys);
1282 if (sys_path == NULL) {
1283 cpy_log_exception("python initialization");
1284 return 1;
1285 }
1286 PySys_SetArgv(1, &argv);
1287 PyList_SetSlice(sys_path, 0, 1, NULL);
1289 #ifdef IS_PY3K
1290 module = PyImport_ImportModule("collectd");
1291 #else
1292 module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
1293 #endif
1294 PyModule_AddObject(module, "Config",
1295 (void *)&ConfigType); /* Steals a reference. */
1296 PyModule_AddObject(module, "Values",
1297 (void *)&ValuesType); /* Steals a reference. */
1298 PyModule_AddObject(module, "Notification",
1299 (void *)&NotificationType); /* Steals a reference. */
1300 PyModule_AddObject(module, "Signed",
1301 (void *)&SignedType); /* Steals a reference. */
1302 PyModule_AddObject(module, "Unsigned",
1303 (void *)&UnsignedType); /* Steals a reference. */
1304 Py_XINCREF(CollectdError);
1305 PyModule_AddObject(module, "CollectdError",
1306 CollectdError); /* Steals a reference. */
1307 PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG);
1308 PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO);
1309 PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE);
1310 PyModule_AddIntConstant(module, "LOG_WARNING", LOG_WARNING);
1311 PyModule_AddIntConstant(module, "LOG_ERROR", LOG_ERR);
1312 PyModule_AddIntConstant(module, "NOTIF_FAILURE", NOTIF_FAILURE);
1313 PyModule_AddIntConstant(module, "NOTIF_WARNING", NOTIF_WARNING);
1314 PyModule_AddIntConstant(module, "NOTIF_OKAY", NOTIF_OKAY);
1315 PyModule_AddStringConstant(module, "DS_TYPE_COUNTER",
1316 DS_TYPE_TO_STRING(DS_TYPE_COUNTER));
1317 PyModule_AddStringConstant(module, "DS_TYPE_GAUGE",
1318 DS_TYPE_TO_STRING(DS_TYPE_GAUGE));
1319 PyModule_AddStringConstant(module, "DS_TYPE_DERIVE",
1320 DS_TYPE_TO_STRING(DS_TYPE_DERIVE));
1321 PyModule_AddStringConstant(module, "DS_TYPE_ABSOLUTE",
1322 DS_TYPE_TO_STRING(DS_TYPE_ABSOLUTE));
1323 return 0;
1324 }
1326 static int cpy_config(oconfig_item_t *ci) {
1327 PyObject *tb;
1328 int status = 0;
1330 /* Ok in theory we shouldn't do initialization at this point
1331 * but we have to. In order to give python scripts a chance
1332 * to register a config callback we need to be able to execute
1333 * python code during the config callback so we have to start
1334 * the interpreter here. */
1335 /* Do *not* use the python "thread" module at this point! */
1337 if (!Py_IsInitialized() && cpy_init_python())
1338 return 1;
1340 for (int i = 0; i < ci->children_num; ++i) {
1341 oconfig_item_t *item = ci->children + i;
1343 if (strcasecmp(item->key, "Interactive") == 0) {
1344 if (cf_util_get_boolean(item, &do_interactive) != 0) {
1345 status = 1;
1346 continue;
1347 }
1348 } else if (strcasecmp(item->key, "Encoding") == 0) {
1349 char *encoding = NULL;
1350 if (cf_util_get_string(item, &encoding) != 0) {
1351 status = 1;
1352 continue;
1353 }
1354 #ifdef IS_PY3K
1355 ERROR("python: \"Encoding\" was used in the config file but Python3 was "
1356 "used, which does not support changing encodings");
1357 status = 1;
1358 sfree(encoding);
1359 continue;
1360 #else
1361 /* Why is this even necessary? And undocumented? */
1362 if (PyUnicode_SetDefaultEncoding(encoding)) {
1363 cpy_log_exception("setting default encoding");
1364 status = 1;
1365 }
1366 #endif
1367 sfree(encoding);
1368 } else if (strcasecmp(item->key, "LogTraces") == 0) {
1369 _Bool log_traces;
1370 if (cf_util_get_boolean(item, &log_traces) != 0) {
1371 status = 1;
1372 continue;
1373 }
1374 if (!log_traces) {
1375 Py_XDECREF(cpy_format_exception);
1376 cpy_format_exception = NULL;
1377 continue;
1378 }
1379 if (cpy_format_exception)
1380 continue;
1381 tb = PyImport_ImportModule("traceback"); /* New reference. */
1382 if (tb == NULL) {
1383 cpy_log_exception("python initialization");
1384 status = 1;
1385 continue;
1386 }
1387 cpy_format_exception =
1388 PyObject_GetAttrString(tb, "format_exception"); /* New reference. */
1389 Py_DECREF(tb);
1390 if (cpy_format_exception == NULL) {
1391 cpy_log_exception("python initialization");
1392 status = 1;
1393 }
1394 } else if (strcasecmp(item->key, "ModulePath") == 0) {
1395 char *dir = NULL;
1396 PyObject *dir_object;
1398 if (cf_util_get_string(item, &dir) != 0) {
1399 status = 1;
1400 continue;
1401 }
1402 dir_object = cpy_string_to_unicode_or_bytes(dir); /* New reference. */
1403 if (dir_object == NULL) {
1404 ERROR("python plugin: Unable to convert \"%s\" to "
1405 "a python object.",
1406 dir);
1407 free(dir);
1408 cpy_log_exception("python initialization");
1409 status = 1;
1410 continue;
1411 }
1412 if (PyList_Insert(sys_path, 0, dir_object) != 0) {
1413 ERROR("python plugin: Unable to prepend \"%s\" to "
1414 "python module path.",
1415 dir);
1416 cpy_log_exception("python initialization");
1417 status = 1;
1418 }
1419 Py_DECREF(dir_object);
1420 free(dir);
1421 } else if (strcasecmp(item->key, "Import") == 0) {
1422 char *module_name = NULL;
1423 PyObject *module;
1425 if (cf_util_get_string(item, &module_name) != 0) {
1426 status = 1;
1427 continue;
1428 }
1429 module = PyImport_ImportModule(module_name); /* New reference. */
1430 if (module == NULL) {
1431 ERROR("python plugin: Error importing module \"%s\".", module_name);
1432 cpy_log_exception("importing module");
1433 status = 1;
1434 }
1435 free(module_name);
1436 Py_XDECREF(module);
1437 } else if (strcasecmp(item->key, "Module") == 0) {
1438 char *name = NULL;
1439 cpy_callback_t *c;
1440 PyObject *ret;
1442 if (cf_util_get_string(item, &name) != 0) {
1443 status = 1;
1444 continue;
1445 }
1446 for (c = cpy_config_callbacks; c; c = c->next) {
1447 if (strcasecmp(c->name + 7, name) == 0)
1448 break;
1449 }
1450 if (c == NULL) {
1451 WARNING("python plugin: Found a configuration for the \"%s\" plugin, "
1452 "but the plugin isn't loaded or didn't register "
1453 "a configuration callback.",
1454 name);
1455 free(name);
1456 continue;
1457 }
1458 free(name);
1459 if (c->data == NULL)
1460 ret = PyObject_CallFunction(
1461 c->callback, "N",
1462 cpy_oconfig_to_pyconfig(item, NULL)); /* New reference. */
1463 else
1464 ret = PyObject_CallFunction(c->callback, "NO",
1465 cpy_oconfig_to_pyconfig(item, NULL),
1466 c->data); /* New reference. */
1467 if (ret == NULL) {
1468 cpy_log_exception("loading module");
1469 status = 1;
1470 } else
1471 Py_DECREF(ret);
1472 } else {
1473 ERROR("python plugin: Unknown config key \"%s\".", item->key);
1474 status = 1;
1475 }
1476 }
1477 return status;
1478 }
1480 void module_register(void) {
1481 plugin_register_complex_config("python", cpy_config);
1482 plugin_register_init("python", cpy_init);
1483 plugin_register_shutdown("python", cpy_shutdown);
1484 }