2 #include <structmember.h>
14 typedef struct cpy_callback_s {
18 struct cpy_callback_s *next;
21 static char log_doc[] = "This function sends a string to all logging plugins.";
23 static char flush_doc[] = "flush([plugin][, timeout][, identifier]) -> None\n"
25 "Flushes the cache of another plugin.";
27 static char unregister_doc[] = "Unregisters a callback. This function needs exactly one parameter either\n"
28 "the function to unregister or the callback identifier to unregister.";
30 static char reg_log_doc[] = "register_log(callback[, data][, name]) -> identifier\n"
32 "Register a callback function for log messages.\n"
34 "'callback' is a callable object that will be called every time something\n"
36 "'data' is an optional object that will be passed back to the callback\n"
37 " function every time it is called.\n"
38 "'name' is an optional identifier for this callback. The default name\n"
39 " is 'python.<module>'.\n"
40 " Every callback needs a unique identifier, so if you want to\n"
41 " register this callback multiple time from the same module you need\n"
42 " to specify a name here.\n"
43 "'identifier' is the full identifier assigned to this callback.\n"
45 "The callback function will be called with two or three parameters:\n"
46 "severity: An integer that should be compared to the LOG_ constants.\n"
47 "message: The text to be logged.\n"
48 "data: The optional data parameter passed to the register function.\n"
49 " If the parameter was obmitted it will be obmitted here, too.";
51 static char reg_init_doc[] = "register_init(callback[, data][, name]) -> identifier\n"
53 "Register a callback function that will be executed once after the config.\n"
54 "file has been read, all plugins heve been loaded and the collectd has\n"
55 "forked into the backgroud.\n"
57 "'callback' is a callable object that will be executed.\n"
58 "'data' is an optional object that will be passed back to the callback\n"
59 " function when it is called.\n"
60 "'name' is an optional identifier for this callback. The default name\n"
61 " is 'python.<module>'.\n"
62 " Every callback needs a unique identifier, so if you want to\n"
63 " register this callback multiple time from the same module you need\n"
64 " to specify a name here.\n"
65 "'identifier' is the full identifier assigned to this callback.\n"
67 "The callback function will be called without parameters, except for\n"
68 "data if it was supplied.";
70 static char reg_config_doc[] = "register_config(callback[, data][, name]) -> identifier\n"
72 "Register a callback function for config file entries.\n"
73 "'callback' is a callable object that will be called for every config block.\n"
74 "'data' is an optional object that will be passed back to the callback\n"
75 " function every time it is called.\n"
76 "'name' is an optional identifier for this callback. The default name\n"
77 " is 'python.<module>'.\n"
78 " Every callback needs a unique identifier, so if you want to\n"
79 " register this callback multiple time from the same module you need\n"
80 " to specify a name here.\n"
81 "'identifier' is the full identifier assigned to this callback.\n"
83 "The callback function will be called with one or two parameters:\n"
84 "config: A Config object.\n"
85 "data: The optional data parameter passed to the register function.\n"
86 " If the parameter was obmitted it will be obmitted here, too.";
88 static char reg_read_doc[] = "register_read(callback[, interval][, data][, name]) -> identifier\n"
90 "Register a callback function for reading data. It will just be called\n"
91 "in a fixed interval to signal that it's time to dispatch new values.\n"
92 "'callback' is a callable object that will be called every time something\n"
94 "'interval' is the number of seconds between between calls to the callback\n"
95 " function. Full float precision is supported here.\n"
96 "'data' is an optional object that will be passed back to the callback\n"
97 " function every time it is called.\n"
98 "'name' is an optional identifier for this callback. The default name\n"
99 " is 'python.<module>'.\n"
100 " Every callback needs a unique identifier, so if you want to\n"
101 " register this callback multiple time from the same module you need\n"
102 " to specify a name here.\n"
103 "'identifier' is the full identifier assigned to this callback.\n"
105 "The callback function will be called without parameters, except for\n"
106 "data if it was supplied.";
108 static char reg_write_doc[] = "register_write(callback[, data][, name]) -> identifier\n"
110 "Register a callback function to receive values dispatched by other plugins.\n"
111 "'callback' is a callable object that will be called every time a value\n"
113 "'data' is an optional object that will be passed back to the callback\n"
114 " function every time it is called.\n"
115 "'name' is an optional identifier for this callback. The default name\n"
116 " is 'python.<module>'.\n"
117 " Every callback needs a unique identifier, so if you want to\n"
118 " register this callback multiple time from the same module you need\n"
119 " to specify a name here.\n"
120 "'identifier' is the full identifier assigned to this callback.\n"
122 "The callback function will be called with one or two parameters:\n"
123 "values: A Values object which is a copy of the dispatched values.\n"
124 "data: The optional data parameter passed to the register function.\n"
125 " If the parameter was obmitted it will be obmitted here, too.";
127 static char reg_notification_doc[] = "register_notification(callback[, data][, name]) -> identifier\n"
129 "Register a callback function for notifications.\n"
130 "'callback' is a callable object that will be called every time a notification\n"
132 "'data' is an optional object that will be passed back to the callback\n"
133 " function every time it is called.\n"
134 "'name' is an optional identifier for this callback. The default name\n"
135 " is 'python.<module>'.\n"
136 " Every callback needs a unique identifier, so if you want to\n"
137 " register this callback multiple time from the same module you need\n"
138 " to specify a name here.\n"
139 "'identifier' is the full identifier assigned to this callback.\n"
141 "The callback function will be called with one or two parameters:\n"
142 "notification: A copy of the notification that was dispatched.\n"
143 "data: The optional data parameter passed to the register function.\n"
144 " If the parameter was obmitted it will be obmitted here, too.";
146 static char reg_flush_doc[] = "register_flush(callback[, data][, name]) -> identifier\n"
148 "Register a callback function for flush messages.\n"
149 "'callback' is a callable object that will be called every time a plugin\n"
150 " requests a flush for either this or all plugins.\n"
151 "'data' is an optional object that will be passed back to the callback\n"
152 " function every time it is called.\n"
153 "'name' is an optional identifier for this callback. The default name\n"
154 " is 'python.<module>'.\n"
155 " Every callback needs a unique identifier, so if you want to\n"
156 " register this callback multiple time from the same module you need\n"
157 " to specify a name here.\n"
158 "'identifier' is the full identifier assigned to this callback.\n"
160 "The callback function will be called with two or three parameters:\n"
161 "timeout: Indicates that only data older than 'timeout' seconds is to\n"
163 "id: Specifies which values are to be flushed.\n"
164 "data: The optional data parameter passed to the register function.\n"
165 " If the parameter was obmitted it will be obmitted here, too.";
167 static char reg_shutdown_doc[] = "register_shutdown(callback[, data][, name]) -> identifier\n"
169 "Register a callback function for collectd shutdown.\n"
170 "'callback' is a callable object that will be called once collectd is\n"
172 "'data' is an optional object that will be passed back to the callback\n"
173 " function if it is called.\n"
174 "'name' is an optional identifier for this callback. The default name\n"
175 " is 'python.<module>'.\n"
176 " Every callback needs a unique identifier, so if you want to\n"
177 " register this callback multiple time from the same module you need\n"
178 " to specify a name here.\n"
179 "'identifier' is the full identifier assigned to this callback.\n"
181 "The callback function will be called with no parameters except for\n"
182 " data if it was supplied.";
185 static int do_interactive = 0;
187 /* This is our global thread state. Python saves some stuff in thread-local
188 * storage. So if we allow the interpreter to run in the background
189 * (the scriptwriters might have created some threads from python), we have
190 * to save the state so we can resume it later after shutdown. */
192 static PyThreadState *state;
194 static PyObject *cpy_format_exception;
196 static cpy_callback_t *cpy_config_callbacks;
197 static cpy_callback_t *cpy_init_callbacks;
198 static cpy_callback_t *cpy_shutdown_callbacks;
200 static void cpy_destroy_user_data(void *data) {
201 cpy_callback_t *c = data;
203 Py_DECREF(c->callback);
208 /* You must hold the GIL to call this function!
209 * But if you managed to extract the callback parameter then you probably already do. */
211 static void cpy_build_name(char *buf, size_t size, PyObject *callback, const char *name) {
212 const char *module = NULL;
213 PyObject *mod = NULL;
216 snprintf(buf, size, "python.%s", name);
220 mod = PyObject_GetAttrString(callback, "__module__"); /* New reference. */
222 module = PyString_AsString(mod);
224 if (module != NULL) {
225 snprintf(buf, size, "python.%s", module);
231 snprintf(buf, size, "python.%p", callback);
234 static void cpy_log_exception(const char *context) {
236 const char *typename = NULL, *message = NULL;
237 PyObject *type, *value, *traceback, *tn, *m, *list;
239 PyErr_Fetch(&type, &value, &traceback);
240 PyErr_NormalizeException(&type, &value, &traceback);
241 if (type == NULL) return;
242 tn = PyObject_GetAttrString(type, "__name__"); /* New reference. */
243 m = PyObject_GetAttrString(value, "message"); /* New reference. */
245 typename = PyString_AsString(tn);
247 message = PyString_AsString(m);
248 if (typename == NULL)
249 typename = "NamelessException";
252 Py_BEGIN_ALLOW_THREADS
253 ERROR("Unhandled python exception in %s: %s: %s", context, typename, message);
257 if (!cpy_format_exception) {
261 Py_XDECREF(traceback);
268 list = PyObject_CallFunction(cpy_format_exception, "NNN", type, value, traceback); /* New reference. */
270 l = PyObject_Length(list);
271 for (i = 0; i < l; ++i) {
275 line = PyList_GET_ITEM(list, i); /* Borrowed reference. */
276 s = strdup(PyString_AsString(line));
278 if (s[strlen(s) - 1] == '\n')
279 s[strlen(s) - 1] = 0;
280 Py_BEGIN_ALLOW_THREADS
289 static int cpy_read_callback(user_data_t *data) {
290 cpy_callback_t *c = data->data;
294 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
296 cpy_log_exception("read callback");
306 static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_list, user_data_t *data) {
308 cpy_callback_t *c = data->data;
309 PyObject *ret, *v, *list;
312 list = PyList_New(value_list->values_len); /* New reference. */
314 cpy_log_exception("write callback");
315 CPY_RETURN_FROM_THREADS 0;
317 for (i = 0; i < value_list->values_len; ++i) {
318 if (ds->ds->type == DS_TYPE_COUNTER) {
319 if ((long) value_list->values[i].counter == value_list->values[i].counter)
320 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].counter));
322 PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].counter));
323 } else if (ds->ds->type == DS_TYPE_GAUGE) {
324 PyList_SetItem(list, i, PyFloat_FromDouble(value_list->values[i].gauge));
325 } else if (ds->ds->type == DS_TYPE_DERIVE) {
326 if ((long) value_list->values[i].derive == value_list->values[i].derive)
327 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].derive));
329 PyList_SetItem(list, i, PyLong_FromLongLong(value_list->values[i].derive));
330 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
331 if ((long) value_list->values[i].absolute == value_list->values[i].absolute)
332 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].absolute));
334 PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].absolute));
336 Py_BEGIN_ALLOW_THREADS
337 ERROR("cpy_write_callback: Unknown value type %d.", ds->ds->type);
340 CPY_RETURN_FROM_THREADS 0;
342 if (PyErr_Occurred() != NULL) {
343 cpy_log_exception("value building for write callback");
344 CPY_RETURN_FROM_THREADS 0;
347 v = PyObject_CallFunction((PyObject *) &ValuesType, "sOssssdi", value_list->type, list,
348 value_list->plugin_instance, value_list->type_instance, value_list->plugin,
349 value_list->host, (double) value_list->time, value_list->interval);
351 ret = PyObject_CallFunctionObjArgs(c->callback, v, c->data, (void *) 0); /* New reference. */
353 cpy_log_exception("write callback");
361 static int cpy_notification_callback(const notification_t *notification, user_data_t *data) {
362 cpy_callback_t *c = data->data;
366 n = PyObject_CallFunction((PyObject *) &NotificationType, "ssssssdi", notification->type, notification->message,
367 notification->plugin_instance, notification->type_instance, notification->plugin,
368 notification->host, (double) notification->time, notification->severity);
369 ret = PyObject_CallFunctionObjArgs(c->callback, n, c->data, (void *) 0); /* New reference. */
371 cpy_log_exception("notification callback");
379 static void cpy_log_callback(int severity, const char *message, user_data_t *data) {
380 cpy_callback_t * c = data->data;
385 ret = PyObject_CallFunction(c->callback, "is", severity, message); /* New reference. */
387 ret = PyObject_CallFunction(c->callback, "isO", severity, message, c->data); /* New reference. */
391 /* Do we really want to trigger a log callback because a log callback failed?
394 /* In case someone wanted to be clever, replaced stderr and failed at that. */
402 static void cpy_flush_callback(int timeout, const char *id, user_data_t *data) {
403 cpy_callback_t * c = data->data;
408 ret = PyObject_CallFunction(c->callback, "is", timeout, id); /* New reference. */
410 ret = PyObject_CallFunction(c->callback, "isO", timeout, id, c->data); /* New reference. */
413 cpy_log_exception("flush callback");
420 static PyObject *cpy_register_generic(cpy_callback_t **list_head, PyObject *args, PyObject *kwds) {
423 const char *name = NULL;
424 PyObject *callback = NULL, *data = NULL, *mod = NULL;
425 static char *kwlist[] = {"callback", "data", "name", NULL};
427 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
428 if (PyCallable_Check(callback) == 0) {
429 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
432 cpy_build_name(buf, sizeof(buf), callback, name);
436 c = malloc(sizeof(*c));
437 c->name = strdup(buf);
438 c->callback = callback;
440 c->next = *list_head;
443 return PyString_FromString(buf);
446 static PyObject *cpy_flush(cpy_callback_t **list_head, PyObject *args, PyObject *kwds) {
448 const char *plugin = NULL, *identifier = NULL;
449 static char *kwlist[] = {"plugin", "timeout", "identifier", NULL};
451 if (PyArg_ParseTupleAndKeywords(args, kwds, "|ziz", kwlist, &plugin, &timeout, &identifier) == 0) return NULL;
452 Py_BEGIN_ALLOW_THREADS
453 plugin_flush(plugin, timeout, identifier);
458 static PyObject *cpy_register_config(PyObject *self, PyObject *args, PyObject *kwds) {
459 return cpy_register_generic(&cpy_config_callbacks, args, kwds);
462 static PyObject *cpy_register_init(PyObject *self, PyObject *args, PyObject *kwds) {
463 return cpy_register_generic(&cpy_init_callbacks, args, kwds);
466 typedef int reg_function_t(const char *name, void *callback, void *data);
468 static PyObject *cpy_register_generic_userdata(void *reg, void *handler, PyObject *args, PyObject *kwds) {
470 reg_function_t *register_function = (reg_function_t *) reg;
471 cpy_callback_t *c = NULL;
472 user_data_t *user_data = NULL;
473 const char *name = NULL;
474 PyObject *callback = NULL, *data = NULL;
475 static char *kwlist[] = {"callback", "data", "name", NULL};
477 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
478 if (PyCallable_Check(callback) == 0) {
479 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
482 cpy_build_name(buf, sizeof(buf), callback, name);
486 c = malloc(sizeof(*c));
487 c->name = strdup(buf);
488 c->callback = callback;
491 user_data = malloc(sizeof(*user_data));
492 user_data->free_func = cpy_destroy_user_data;
494 register_function(buf, handler, user_data);
495 return PyString_FromString(buf);
498 static PyObject *cpy_register_read(PyObject *self, PyObject *args, PyObject *kwds) {
500 cpy_callback_t *c = NULL;
501 user_data_t *user_data = NULL;
503 const char *name = NULL;
504 PyObject *callback = NULL, *data = NULL;
506 static char *kwlist[] = {"callback", "interval", "data", "name", NULL};
508 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|dOz", kwlist, &callback, &interval, &data, &name) == 0) return NULL;
509 if (PyCallable_Check(callback) == 0) {
510 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
513 cpy_build_name(buf, sizeof(buf), callback, name);
517 c = malloc(sizeof(*c));
518 c->name = strdup(buf);
519 c->callback = callback;
522 user_data = malloc(sizeof(*user_data));
523 user_data->free_func = cpy_destroy_user_data;
525 ts.tv_sec = interval;
526 ts.tv_nsec = (interval - ts.tv_sec) * 1000000000;
527 plugin_register_complex_read(buf, cpy_read_callback, &ts, user_data);
528 return PyString_FromString(buf);
531 static PyObject *cpy_register_log(PyObject *self, PyObject *args, PyObject *kwds) {
532 return cpy_register_generic_userdata(plugin_register_log, cpy_log_callback, args, kwds);
535 static PyObject *cpy_register_write(PyObject *self, PyObject *args, PyObject *kwds) {
536 return cpy_register_generic_userdata(plugin_register_write, cpy_write_callback, args, kwds);
539 static PyObject *cpy_register_notification(PyObject *self, PyObject *args, PyObject *kwds) {
540 return cpy_register_generic_userdata(plugin_register_notification, cpy_notification_callback, args, kwds);
543 static PyObject *cpy_register_flush(PyObject *self, PyObject *args, PyObject *kwds) {
544 return cpy_register_generic_userdata(plugin_register_flush, cpy_flush_callback, args, kwds);
547 static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args, PyObject *kwds) {
548 return cpy_register_generic(&cpy_shutdown_callbacks, args, kwds);
551 static PyObject *cpy_error(PyObject *self, PyObject *args) {
553 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
554 Py_BEGIN_ALLOW_THREADS
555 plugin_log(LOG_ERR, "%s", text);
560 static PyObject *cpy_warning(PyObject *self, PyObject *args) {
562 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
563 Py_BEGIN_ALLOW_THREADS
564 plugin_log(LOG_WARNING, "%s", text);
569 static PyObject *cpy_notice(PyObject *self, PyObject *args) {
571 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
572 Py_BEGIN_ALLOW_THREADS
573 plugin_log(LOG_NOTICE, "%s", text);
578 static PyObject *cpy_info(PyObject *self, PyObject *args) {
580 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
581 Py_BEGIN_ALLOW_THREADS
582 plugin_log(LOG_INFO, "%s", text);
587 static PyObject *cpy_debug(PyObject *self, PyObject *args) {
590 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
591 Py_BEGIN_ALLOW_THREADS
592 plugin_log(LOG_DEBUG, "%s", text);
598 static PyObject *cpy_unregister_generic(cpy_callback_t **list_head, PyObject *arg, const char *desc) {
601 cpy_callback_t *prev = NULL, *tmp;
603 if (PyUnicode_Check(arg)) {
604 arg = PyUnicode_AsEncodedString(arg, NULL, NULL);
607 name = PyString_AsString(arg);
609 } else if (PyString_Check(arg)) {
610 name = PyString_AsString(arg);
612 if (!PyCallable_Check(arg)) {
613 PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
616 cpy_build_name(buf, sizeof(buf), arg, NULL);
619 for (tmp = *list_head; tmp; prev = tmp, tmp = tmp->next)
620 if (strcmp(name, tmp->name) == 0)
624 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
627 /* Yes, this is actually save. To call this function the calles has to
628 * hold the GIL. Well, save as long as there is only one GIL anyway ... */
630 *list_head = tmp->next;
632 prev->next = tmp->next;
633 cpy_destroy_user_data(tmp);
637 typedef int cpy_unregister_function_t(const char *name);
639 static PyObject *cpy_unregister_generic_userdata(cpy_unregister_function_t *unreg, PyObject *arg, const char *desc) {
643 if (PyUnicode_Check(arg)) {
644 arg = PyUnicode_AsEncodedString(arg, NULL, NULL);
647 name = PyString_AsString(arg);
649 } else if (PyString_Check(arg)) {
650 name = PyString_AsString(arg);
652 if (!PyCallable_Check(arg)) {
653 PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
656 cpy_build_name(buf, sizeof(buf), arg, NULL);
659 if (unreg(name) == 0)
661 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
665 static PyObject *cpy_unregister_log(PyObject *self, PyObject *arg) {
666 return cpy_unregister_generic_userdata(plugin_unregister_log, arg, "log");
669 static PyObject *cpy_unregister_init(PyObject *self, PyObject *arg) {
670 return cpy_unregister_generic(&cpy_init_callbacks, arg, "init");
673 static PyObject *cpy_unregister_config(PyObject *self, PyObject *arg) {
674 return cpy_unregister_generic(&cpy_config_callbacks, arg, "config");
677 static PyObject *cpy_unregister_read(PyObject *self, PyObject *arg) {
678 return cpy_unregister_generic_userdata(plugin_unregister_read, arg, "read");
681 static PyObject *cpy_unregister_write(PyObject *self, PyObject *arg) {
682 return cpy_unregister_generic_userdata(plugin_unregister_write, arg, "write");
685 static PyObject *cpy_unregister_notification(PyObject *self, PyObject *arg) {
686 return cpy_unregister_generic_userdata(plugin_unregister_notification, arg, "notification");
689 static PyObject *cpy_unregister_flush(PyObject *self, PyObject *arg) {
690 return cpy_unregister_generic_userdata(plugin_unregister_flush, arg, "flush");
693 static PyObject *cpy_unregister_shutdown(PyObject *self, PyObject *arg) {
694 return cpy_unregister_generic(&cpy_shutdown_callbacks, arg, "shutdown");
697 static PyMethodDef cpy_methods[] = {
698 {"debug", cpy_debug, METH_VARARGS, log_doc},
699 {"info", cpy_info, METH_VARARGS, log_doc},
700 {"notice", cpy_notice, METH_VARARGS, log_doc},
701 {"warning", cpy_warning, METH_VARARGS, log_doc},
702 {"error", cpy_error, METH_VARARGS, log_doc},
703 {"flush", (PyCFunction) cpy_flush, METH_VARARGS | METH_KEYWORDS, flush_doc},
704 {"register_log", (PyCFunction) cpy_register_log, METH_VARARGS | METH_KEYWORDS, reg_log_doc},
705 {"register_init", (PyCFunction) cpy_register_init, METH_VARARGS | METH_KEYWORDS, reg_init_doc},
706 {"register_config", (PyCFunction) cpy_register_config, METH_VARARGS | METH_KEYWORDS, reg_config_doc},
707 {"register_read", (PyCFunction) cpy_register_read, METH_VARARGS | METH_KEYWORDS, reg_read_doc},
708 {"register_write", (PyCFunction) cpy_register_write, METH_VARARGS | METH_KEYWORDS, reg_write_doc},
709 {"register_notification", (PyCFunction) cpy_register_notification, METH_VARARGS | METH_KEYWORDS, reg_notification_doc},
710 {"register_flush", (PyCFunction) cpy_register_flush, METH_VARARGS | METH_KEYWORDS, reg_flush_doc},
711 {"register_shutdown", (PyCFunction) cpy_register_shutdown, METH_VARARGS | METH_KEYWORDS, reg_shutdown_doc},
712 {"unregister_log", cpy_unregister_log, METH_O, unregister_doc},
713 {"unregister_init", cpy_unregister_init, METH_O, unregister_doc},
714 {"unregister_config", cpy_unregister_config, METH_O, unregister_doc},
715 {"unregister_read", cpy_unregister_read, METH_O, unregister_doc},
716 {"unregister_write", cpy_unregister_write, METH_O, unregister_doc},
717 {"unregister_notification", cpy_unregister_notification, METH_O, unregister_doc},
718 {"unregister_flush", cpy_unregister_flush, METH_O, unregister_doc},
719 {"unregister_shutdown", cpy_unregister_shutdown, METH_O, unregister_doc},
723 static int cpy_shutdown(void) {
727 /* This can happen if the module was loaded but not configured. */
729 PyEval_RestoreThread(state);
731 for (c = cpy_shutdown_callbacks; c; c = c->next) {
732 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
734 cpy_log_exception("shutdown callback");
743 static void cpy_int_handler(int sig) {
747 static void *cpy_interactive(void *data) {
749 struct sigaction sig_int_action, old;
751 /* Signal handler in a plugin? Bad stuff, but the best way to
752 * handle it I guess. In an interactive session people will
753 * press Ctrl+C at some time, which will generate a SIGINT.
754 * This will cause collectd to shutdown, thus killing the
755 * interactive interpreter, and leaving the terminal in a
756 * mess. Chances are, this isn't what the user wanted to do.
758 * So this is the plan:
759 * 1. Block SIGINT in the main thread.
760 * 2. Install our own signal handler that does nothing.
761 * 3. Unblock SIGINT in the interactive thread.
763 * This will make sure that SIGINT won't kill collectd but
764 * still interrupt syscalls like sleep and pause.
765 * It does not raise a KeyboardInterrupt exception because so
766 * far nobody managed to figure out how to do that. */
767 memset (&sig_int_action, '\0', sizeof (sig_int_action));
768 sig_int_action.sa_handler = cpy_int_handler;
769 sigaction (SIGINT, &sig_int_action, &old);
771 sigemptyset(&sigset);
772 sigaddset(&sigset, SIGINT);
773 pthread_sigmask(SIG_UNBLOCK, &sigset, NULL);
774 PyEval_AcquireThread(state);
775 if (PyImport_ImportModule("readline") == NULL) {
776 /* This interactive session will suck. */
777 cpy_log_exception("interactive session init");
779 PyRun_InteractiveLoop(stdin, "<stdin>");
781 PyEval_ReleaseThread(state);
782 NOTICE("python: Interactive interpreter exited, stopping collectd ...");
783 /* Restore the original collectd SIGINT handler and raise SIGINT.
784 * The main thread still has SIGINT blocked and there's nothing we
785 * can do about that so this thread will handle it. But that's not
786 * important, except that it won't interrupt the main loop and so
787 * it might take a few seconds before collectd really shuts down. */
788 sigaction (SIGINT, &old, NULL);
794 static int cpy_init(void) {
797 static pthread_t thread;
800 PyEval_InitThreads();
801 /* Now it's finally OK to use python threads. */
802 for (c = cpy_init_callbacks; c; c = c->next) {
803 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
805 cpy_log_exception("init callback");
809 sigemptyset(&sigset);
810 sigaddset(&sigset, SIGINT);
811 pthread_sigmask(SIG_BLOCK, &sigset, NULL);
812 state = PyEval_SaveThread();
813 if (do_interactive) {
814 if (pthread_create(&thread, NULL, cpy_interactive, NULL)) {
815 ERROR("python: Error creating thread for interactive interpreter.");
822 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
824 PyObject *item, *values, *children, *tmp;
829 values = PyTuple_New(ci->values_num); /* New reference. */
830 for (i = 0; i < ci->values_num; ++i) {
831 if (ci->values[i].type == OCONFIG_TYPE_STRING) {
832 PyTuple_SET_ITEM(values, i, PyString_FromString(ci->values[i].value.string));
833 } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
834 PyTuple_SET_ITEM(values, i, PyFloat_FromDouble(ci->values[i].value.number));
835 } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
836 PyTuple_SET_ITEM(values, i, PyBool_FromLong(ci->values[i].value.boolean));
840 item = PyObject_CallFunction((PyObject *) &ConfigType, "sONO", ci->key, parent, values, Py_None);
843 children = PyTuple_New(ci->children_num); /* New reference. */
844 for (i = 0; i < ci->children_num; ++i) {
845 PyTuple_SET_ITEM(children, i, cpy_oconfig_to_pyconfig(ci->children + i, item));
847 tmp = ((Config *) item)->children;
848 ((Config *) item)->children = children;
853 static int cpy_config(oconfig_item_t *ci) {
859 /* Ok in theory we shouldn't do initialization at this point
860 * but we have to. In order to give python scripts a chance
861 * to register a config callback we need to be able to execute
862 * python code during the config callback so we have to start
863 * the interpreter here. */
864 /* Do *not* use the python "thread" module at this point! */
867 PyType_Ready(&ConfigType);
868 PyType_Ready(&PluginDataType);
869 ValuesType.tp_base = &PluginDataType;
870 PyType_Ready(&ValuesType);
871 NotificationType.tp_base = &PluginDataType;
872 PyType_Ready(&NotificationType);
873 sys = PyImport_ImportModule("sys"); /* New reference. */
875 cpy_log_exception("python initialization");
878 sys_path = PyObject_GetAttrString(sys, "path"); /* New reference. */
880 if (sys_path == NULL) {
881 cpy_log_exception("python initialization");
884 module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
885 PyModule_AddObject(module, "Config", (PyObject *) &ConfigType); /* Steals a reference. */
886 PyModule_AddObject(module, "Values", (PyObject *) &ValuesType); /* Steals a reference. */
887 PyModule_AddObject(module, "Notification", (PyObject *) &NotificationType); /* Steals a reference. */
888 PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG);
889 PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO);
890 PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE);
891 PyModule_AddIntConstant(module, "LOG_WARNING", LOG_WARNING);
892 PyModule_AddIntConstant(module, "LOG_ERROR", LOG_ERR);
893 PyModule_AddIntConstant(module, "NOTIF_FAILURE", NOTIF_FAILURE);
894 PyModule_AddIntConstant(module, "NOTIF_WARNING", NOTIF_WARNING);
895 PyModule_AddIntConstant(module, "NOTIF_OKAY", NOTIF_OKAY);
896 for (i = 0; i < ci->children_num; ++i) {
897 oconfig_item_t *item = ci->children + i;
899 if (strcasecmp(item->key, "Interactive") == 0) {
900 if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_BOOLEAN)
902 do_interactive = item->values[0].value.boolean;
903 } else if (strcasecmp(item->key, "Encoding") == 0) {
904 if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_STRING)
906 /* Why is this even necessary? And undocumented? */
907 if (PyUnicode_SetDefaultEncoding(item->values[0].value.string))
908 cpy_log_exception("setting default encoding");
909 } else if (strcasecmp(item->key, "LogTraces") == 0) {
910 if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_BOOLEAN)
912 if (!item->values[0].value.boolean) {
913 Py_XDECREF(cpy_format_exception);
914 cpy_format_exception = NULL;
917 if (cpy_format_exception)
919 tb = PyImport_ImportModule("traceback"); /* New reference. */
921 cpy_log_exception("python initialization");
924 cpy_format_exception = PyObject_GetAttrString(tb, "format_exception"); /* New reference. */
926 if (cpy_format_exception == NULL)
927 cpy_log_exception("python initialization");
928 } else if (strcasecmp(item->key, "ModulePath") == 0) {
930 PyObject *dir_object;
932 if (cf_util_get_string(item, &dir) != 0)
934 dir_object = PyString_FromString(dir); /* New reference. */
935 if (dir_object == NULL) {
936 ERROR("python plugin: Unable to convert \"%s\" to "
937 "a python object.", dir);
939 cpy_log_exception("python initialization");
942 if (PyList_Append(sys_path, dir_object) != 0) {
943 ERROR("python plugin: Unable to append \"%s\" to "
944 "python module path.", dir);
945 cpy_log_exception("python initialization");
947 Py_DECREF(dir_object);
949 } else if (strcasecmp(item->key, "Import") == 0) {
950 char *module_name = NULL;
953 if (cf_util_get_string(item, &module_name) != 0)
955 module = PyImport_ImportModule(module_name); /* New reference. */
956 if (module == NULL) {
957 ERROR("python plugin: Error importing module \"%s\".", module_name);
958 cpy_log_exception("importing module");
963 } else if (strcasecmp(item->key, "Module") == 0) {
968 if (cf_util_get_string(item, &name) != 0)
970 for (c = cpy_config_callbacks; c; c = c->next) {
971 if (strcasecmp(c->name + 7, name) == 0)
975 WARNING("python plugin: Found a configuration for the \"%s\" plugin, "
976 "but the plugin isn't loaded or didn't register "
977 "a configuration callback.", name);
983 ret = PyObject_CallFunction(c->callback, "N",
984 cpy_oconfig_to_pyconfig(item, NULL)); /* New reference. */
986 ret = PyObject_CallFunction(c->callback, "NO",
987 cpy_oconfig_to_pyconfig(item, NULL), c->data); /* New reference. */
989 cpy_log_exception("loading module");
993 WARNING("python plugin: Ignoring unknown config key \"%s\".", item->key);
1000 void module_register(void) {
1001 plugin_register_complex_config("python", cpy_config);
1002 plugin_register_init("python", cpy_init);
1003 plugin_register_shutdown("python", cpy_shutdown);