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>.<name>'. If 'name' contains a '.' it\n"
40 " replaces both module and name, otherwise it replaces only name.\n"
41 " Every callback needs a unique identifier, so if you want to\n"
42 " register one function multiple time you need to specify a name\n"
44 "'identifier' is the full identifier assigned to this callback.\n"
46 "The callback function will be called with two or three parameters:\n"
47 "severity: An integer that should be compared to the LOG_ constants.\n"
48 "message: The text to be logged.\n"
49 "data: The optional data parameter passed to the register function.\n"
50 " If the parameter was obmitted it will be obmitted here, too.";
52 static char reg_init_doc[] = "register_init(callback[, data][, name]) -> identifier\n"
54 "Register a callback function that will be executed once after the config.\n"
55 "file has been read, all plugins heve been loaded and the collectd has\n"
56 "forked into the backgroud.\n"
58 "'callback' is a callable object that will be executed.\n"
59 "'data' is an optional object that will be passed back to the callback\n"
60 " function when it is called.\n"
61 "'name' is an optional identifier for this callback. The default name\n"
62 " is 'python.<module>.<name>'. If 'name' contains a '.' it\n"
63 " replaces both module and name, otherwise it replaces only name.\n"
64 " Every callback needs a unique identifier, so if you want to\n"
65 " register one function multiple time you need to specify a name\n"
67 "'identifier' is the full identifier assigned to this callback.\n"
69 "The callback function will be called without parameters, except for\n"
70 "data if it was supplied.";
72 static char reg_config_doc[] = "register_config(callback[, data][, name]) -> identifier\n"
74 "Register a callback function for config file entries.\n"
75 "'callback' is a callable object that will be called for every config block.\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>'. Every callback needs a unique identifier,\n"
80 " so if you want to register one function multiple time you need to\n"
81 " specify a name here.\n"
82 "'identifier' is the full identifier assigned to this callback.\n"
84 "The callback function will be called with one or two parameters:\n"
85 "config: A Config object.\n"
86 "data: The optional data parameter passed to the register function.\n"
87 " If the parameter was obmitted it will be obmitted here, too.";
89 static char reg_read_doc[] = "register_read(callback[, interval][, data][, name]) -> identifier\n"
91 "Register a callback function for reading data. It will just be called\n"
92 "in a fixed interval to signal that it's time to dispatch new values.\n"
93 "'callback' is a callable object that will be called every time something\n"
95 "'interval' is the number of seconds between between calls to the callback\n"
96 " function. Full float precision is supported here.\n"
97 "'data' is an optional object that will be passed back to the callback\n"
98 " function every time it is called.\n"
99 "'name' is an optional identifier for this callback. The default name\n"
100 " is 'python.<module>.<name>'. If 'name' contains a '.' it\n"
101 " replaces both module and name, otherwise it replaces only name.\n"
102 " Every callback needs a unique identifier, so if you want to\n"
103 " register one function multiple time you need to specify a name\n"
105 "'identifier' is the full identifier assigned to this callback.\n"
107 "The callback function will be called without parameters, except for\n"
108 "data if it was supplied.";
110 static char reg_write_doc[] = "register_write(callback[, data][, name]) -> identifier\n"
112 "Register a callback function to receive values dispatched by other plugins.\n"
113 "'callback' is a callable object that will be called every time a value\n"
115 "'data' is an optional object that will be passed back to the callback\n"
116 " function every time it is called.\n"
117 "'name' is an optional identifier for this callback. The default name\n"
118 " is 'python.<module>.<name>'. If 'name' contains a '.' it\n"
119 " replaces both module and name, otherwise it replaces only name.\n"
120 " Every callback needs a unique identifier, so if you want to\n"
121 " register one function multiple time you need to specify a name\n"
123 "'identifier' is the full identifier assigned to this callback.\n"
125 "The callback function will be called with one or two parameters:\n"
126 "values: A Values object which is a copy of the dispatched values.\n"
127 "data: The optional data parameter passed to the register function.\n"
128 " If the parameter was obmitted it will be obmitted here, too.";
130 static char reg_notification_doc[] = "register_notification(callback[, data][, name]) -> identifier\n"
132 "Register a callback function for notifications.\n"
133 "'callback' is a callable object that will be called every time a notification\n"
135 "'data' is an optional object that will be passed back to the callback\n"
136 " function every time it is called.\n"
137 "'name' is an optional identifier for this callback. The default name\n"
138 " is 'python.<module>.<name>'. If 'name' contains a '.' it\n"
139 " replaces both module and name, otherwise it replaces only name.\n"
140 " Every callback needs a unique identifier, so if you want to\n"
141 " register one function multiple time you need to specify a name\n"
143 "'identifier' is the full identifier assigned to this callback.\n"
145 "The callback function will be called with one or two parameters:\n"
146 "notification: A copy of the notification that was dispatched.\n"
147 "data: The optional data parameter passed to the register function.\n"
148 " If the parameter was obmitted it will be obmitted here, too.";
150 static char reg_flush_doc[] = "register_flush(callback[, data][, name]) -> identifier\n"
152 "Register a callback function for flush messages.\n"
153 "'callback' is a callable object that will be called every time a plugin\n"
154 " requests a flush for either this or all plugins.\n"
155 "'data' is an optional object that will be passed back to the callback\n"
156 " function every time it is called.\n"
157 "'name' is an optional identifier for this callback. The default name\n"
158 " is 'python.<module>'. Every callback needs a unique identifier,\n"
159 " so if you want to register one function multiple time you need to\n"
160 " specify a name here.\n"
161 "'identifier' is the full identifier assigned to this callback.\n"
163 "The callback function will be called with two or three parameters:\n"
164 "timeout: Indicates that only data older than 'timeout' seconds is to\n"
166 "id: Specifies which values are to be flushed.\n"
167 "data: The optional data parameter passed to the register function.\n"
168 " If the parameter was obmitted it will be obmitted here, too.";
170 static char reg_shutdown_doc[] = "register_shutdown(callback[, data][, name]) -> identifier\n"
172 "Register a callback function for collectd shutdown.\n"
173 "'callback' is a callable object that will be called once collectd is\n"
175 "'data' is an optional object that will be passed back to the callback\n"
176 " function if it is called.\n"
177 "'name' is an optional identifier for this callback. The default name\n"
178 " is 'python.<module>.<name>'. If 'name' contains a '.' it\n"
179 " replaces both module and name, otherwise it replaces only name.\n"
180 " Every callback needs a unique identifier, so if you want to\n"
181 " register one function multiple time you need to specify a name\n"
183 "'identifier' is the full identifier assigned to this callback.\n"
185 "The callback function will be called with no parameters except for\n"
186 " data if it was supplied.";
189 static int do_interactive = 0;
191 /* This is our global thread state. Python saves some stuff in thread-local
192 * storage. So if we allow the interpreter to run in the background
193 * (the scriptwriters might have created some threads from python), we have
194 * to save the state so we can resume it later after shutdown. */
196 static PyThreadState *state;
198 static PyObject *cpy_format_exception;
200 static cpy_callback_t *cpy_config_callbacks;
201 static cpy_callback_t *cpy_init_callbacks;
202 static cpy_callback_t *cpy_shutdown_callbacks;
204 static void cpy_destroy_user_data(void *data) {
205 cpy_callback_t *c = data;
207 Py_DECREF(c->callback);
212 /* You must hold the GIL to call this function!
213 * But if you managed to extract the callback parameter then you probably already do. */
215 static void cpy_build_name(char *buf, size_t size, PyObject *callback, const char *name, int short_name) {
217 PyObject *mod = NULL, *n = NULL;
219 if (name != NULL && (strchr(name, '.') != NULL || short_name)) {
220 snprintf(buf, size, "python.%s", name);
224 mod = PyObject_GetAttrString(callback, "__module__"); /* New reference. */
226 module = PyString_AsString(mod);
231 snprintf(buf, size, "python.%s", module);
237 snprintf(buf, size, "python.%s.%s", module, name);
242 n = PyObject_GetAttrString(callback, "__name__"); /* New reference. */
244 name = PyString_AsString(n);
247 snprintf(buf, size, "python.%s.%s", module, name);
249 snprintf(buf, size, "python.%s.%p", module, callback);
254 static void cpy_log_exception(const char *context) {
256 const char *typename = NULL, *message = NULL;
257 PyObject *type, *value, *traceback, *tn, *m, *list;
259 PyErr_Fetch(&type, &value, &traceback);
260 PyErr_NormalizeException(&type, &value, &traceback);
261 if (type == NULL) return;
262 tn = PyObject_GetAttrString(type, "__name__"); /* New reference. */
263 m = PyObject_GetAttrString(value, "message"); /* New reference. */
265 typename = PyString_AsString(tn);
267 message = PyString_AsString(m);
268 if (typename == NULL)
269 typename = "NamelessException";
272 Py_BEGIN_ALLOW_THREADS
273 ERROR("Unhandled python exception in %s: %s: %s", context, typename, message);
277 if (!cpy_format_exception) {
281 Py_XDECREF(traceback);
288 list = PyObject_CallFunction(cpy_format_exception, "NNN", type, value, traceback); /* New reference. */
290 l = PyObject_Length(list);
291 for (i = 0; i < l; ++i) {
295 line = PyList_GET_ITEM(list, i); /* Borrowed reference. */
296 s = strdup(PyString_AsString(line));
298 if (s[strlen(s) - 1] == '\n')
299 s[strlen(s) - 1] = 0;
300 Py_BEGIN_ALLOW_THREADS
309 static int cpy_read_callback(user_data_t *data) {
310 cpy_callback_t *c = data->data;
314 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
316 cpy_log_exception("read callback");
326 static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_list, user_data_t *data) {
328 cpy_callback_t *c = data->data;
329 PyObject *ret, *v, *list;
332 list = PyList_New(value_list->values_len); /* New reference. */
334 cpy_log_exception("write callback");
335 CPY_RETURN_FROM_THREADS 0;
337 for (i = 0; i < value_list->values_len; ++i) {
338 if (ds->ds->type == DS_TYPE_COUNTER) {
339 if ((long) value_list->values[i].counter == value_list->values[i].counter)
340 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].counter));
342 PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].counter));
343 } else if (ds->ds->type == DS_TYPE_GAUGE) {
344 PyList_SetItem(list, i, PyFloat_FromDouble(value_list->values[i].gauge));
345 } else if (ds->ds->type == DS_TYPE_DERIVE) {
346 if ((long) value_list->values[i].derive == value_list->values[i].derive)
347 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].derive));
349 PyList_SetItem(list, i, PyLong_FromLongLong(value_list->values[i].derive));
350 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
351 if ((long) value_list->values[i].absolute == value_list->values[i].absolute)
352 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].absolute));
354 PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].absolute));
356 Py_BEGIN_ALLOW_THREADS
357 ERROR("cpy_write_callback: Unknown value type %d.", ds->ds->type);
360 CPY_RETURN_FROM_THREADS 0;
362 if (PyErr_Occurred() != NULL) {
363 cpy_log_exception("value building for write callback");
364 CPY_RETURN_FROM_THREADS 0;
367 v = PyObject_CallFunction((PyObject *) &ValuesType, "sOssssdi", value_list->type, list,
368 value_list->plugin_instance, value_list->type_instance, value_list->plugin,
369 value_list->host, (double) value_list->time, value_list->interval);
371 ret = PyObject_CallFunctionObjArgs(c->callback, v, c->data, (void *) 0); /* New reference. */
373 cpy_log_exception("write callback");
381 static int cpy_notification_callback(const notification_t *notification, user_data_t *data) {
382 cpy_callback_t *c = data->data;
386 n = PyObject_CallFunction((PyObject *) &NotificationType, "ssssssdi", notification->type, notification->message,
387 notification->plugin_instance, notification->type_instance, notification->plugin,
388 notification->host, (double) notification->time, notification->severity);
389 ret = PyObject_CallFunctionObjArgs(c->callback, n, c->data, (void *) 0); /* New reference. */
391 cpy_log_exception("notification callback");
399 static void cpy_log_callback(int severity, const char *message, user_data_t *data) {
400 cpy_callback_t * c = data->data;
405 ret = PyObject_CallFunction(c->callback, "is", severity, message); /* New reference. */
407 ret = PyObject_CallFunction(c->callback, "isO", severity, message, c->data); /* New reference. */
411 /* Do we really want to trigger a log callback because a log callback failed?
414 /* In case someone wanted to be clever, replaced stderr and failed at that. */
422 static void cpy_flush_callback(int timeout, const char *id, user_data_t *data) {
423 cpy_callback_t * c = data->data;
428 ret = PyObject_CallFunction(c->callback, "is", timeout, id); /* New reference. */
430 ret = PyObject_CallFunction(c->callback, "isO", timeout, id, c->data); /* New reference. */
433 cpy_log_exception("flush callback");
440 static PyObject *cpy_register_generic(cpy_callback_t **list_head, PyObject *args, PyObject *kwds, int short_name) {
443 const char *name = NULL;
444 PyObject *callback = NULL, *data = NULL, *mod = NULL;
445 static char *kwlist[] = {"callback", "data", "name", NULL};
447 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
448 if (PyCallable_Check(callback) == 0) {
449 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
452 cpy_build_name(buf, sizeof(buf), callback, name, short_name);
456 c = malloc(sizeof(*c));
457 c->name = strdup(buf);
458 c->callback = callback;
460 c->next = *list_head;
463 return PyString_FromString(buf);
466 static PyObject *cpy_flush(cpy_callback_t **list_head, PyObject *args, PyObject *kwds) {
468 const char *plugin = NULL, *identifier = NULL;
469 static char *kwlist[] = {"plugin", "timeout", "identifier", NULL};
471 if (PyArg_ParseTupleAndKeywords(args, kwds, "|ziz", kwlist, &plugin, &timeout, &identifier) == 0) return NULL;
472 Py_BEGIN_ALLOW_THREADS
473 plugin_flush(plugin, timeout, identifier);
478 static PyObject *cpy_register_config(PyObject *self, PyObject *args, PyObject *kwds) {
479 return cpy_register_generic(&cpy_config_callbacks, args, kwds, 1);
482 static PyObject *cpy_register_init(PyObject *self, PyObject *args, PyObject *kwds) {
483 return cpy_register_generic(&cpy_init_callbacks, args, kwds, 0);
486 typedef int reg_function_t(const char *name, void *callback, void *data);
488 static PyObject *cpy_register_generic_userdata(void *reg, void *handler, PyObject *args, PyObject *kwds, int short_name) {
490 reg_function_t *register_function = (reg_function_t *) reg;
491 cpy_callback_t *c = NULL;
492 user_data_t *user_data = NULL;
493 const char *name = NULL;
494 PyObject *callback = NULL, *data = NULL;
495 static char *kwlist[] = {"callback", "data", "name", NULL};
497 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
498 if (PyCallable_Check(callback) == 0) {
499 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
502 cpy_build_name(buf, sizeof(buf), callback, name, short_name);
506 c = malloc(sizeof(*c));
507 c->name = strdup(buf);
508 c->callback = callback;
511 user_data = malloc(sizeof(*user_data));
512 user_data->free_func = cpy_destroy_user_data;
514 register_function(buf, handler, user_data);
515 return PyString_FromString(buf);
518 static PyObject *cpy_register_read(PyObject *self, PyObject *args, PyObject *kwds) {
520 cpy_callback_t *c = NULL;
521 user_data_t *user_data = NULL;
523 const char *name = NULL;
524 PyObject *callback = NULL, *data = NULL;
526 static char *kwlist[] = {"callback", "interval", "data", "name", NULL};
528 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|dOz", kwlist, &callback, &interval, &data, &name) == 0) return NULL;
529 if (PyCallable_Check(callback) == 0) {
530 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
533 cpy_build_name(buf, sizeof(buf), callback, name, 0);
537 c = malloc(sizeof(*c));
538 c->name = strdup(buf);
539 c->callback = callback;
542 user_data = malloc(sizeof(*user_data));
543 user_data->free_func = cpy_destroy_user_data;
545 ts.tv_sec = interval;
546 ts.tv_nsec = (interval - ts.tv_sec) * 1000000000;
547 plugin_register_complex_read(buf, cpy_read_callback, &ts, user_data);
548 return PyString_FromString(buf);
551 static PyObject *cpy_register_log(PyObject *self, PyObject *args, PyObject *kwds) {
552 return cpy_register_generic_userdata(plugin_register_log, cpy_log_callback, args, kwds, 0);
555 static PyObject *cpy_register_write(PyObject *self, PyObject *args, PyObject *kwds) {
556 return cpy_register_generic_userdata(plugin_register_write, cpy_write_callback, args, kwds, 0);
559 static PyObject *cpy_register_notification(PyObject *self, PyObject *args, PyObject *kwds) {
560 return cpy_register_generic_userdata(plugin_register_notification, cpy_notification_callback, args, kwds, 0);
563 static PyObject *cpy_register_flush(PyObject *self, PyObject *args, PyObject *kwds) {
564 return cpy_register_generic_userdata(plugin_register_flush, cpy_flush_callback, args, kwds, 1);
567 static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args, PyObject *kwds) {
568 return cpy_register_generic(&cpy_shutdown_callbacks, args, kwds, 0);
571 static PyObject *cpy_error(PyObject *self, PyObject *args) {
573 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
574 Py_BEGIN_ALLOW_THREADS
575 plugin_log(LOG_ERR, "%s", text);
580 static PyObject *cpy_warning(PyObject *self, PyObject *args) {
582 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
583 Py_BEGIN_ALLOW_THREADS
584 plugin_log(LOG_WARNING, "%s", text);
589 static PyObject *cpy_notice(PyObject *self, PyObject *args) {
591 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
592 Py_BEGIN_ALLOW_THREADS
593 plugin_log(LOG_NOTICE, "%s", text);
598 static PyObject *cpy_info(PyObject *self, PyObject *args) {
600 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
601 Py_BEGIN_ALLOW_THREADS
602 plugin_log(LOG_INFO, "%s", text);
607 static PyObject *cpy_debug(PyObject *self, PyObject *args) {
610 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
611 Py_BEGIN_ALLOW_THREADS
612 plugin_log(LOG_DEBUG, "%s", text);
618 static PyObject *cpy_unregister_generic(cpy_callback_t **list_head, PyObject *arg, const char *desc, int short_name) {
621 cpy_callback_t *prev = NULL, *tmp;
623 if (PyString_Check(arg)) {
624 name = PyString_AsString(arg);
626 if (!PyCallable_Check(arg)) {
627 PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
630 cpy_build_name(buf, sizeof(buf), arg, NULL, short_name);
633 for (tmp = *list_head; tmp; prev = tmp, tmp = tmp->next)
634 if (strcmp(name, tmp->name) == 0)
638 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
641 /* Yes, this is actually save. To call this function the calles has to
642 * hold the GIL. Well, save as long as there is only one GIL anyway ... */
644 *list_head = tmp->next;
646 prev->next = tmp->next;
647 cpy_destroy_user_data(tmp);
651 typedef int cpy_unregister_function_t(const char *name);
653 static PyObject *cpy_unregister_generic_userdata(cpy_unregister_function_t *unreg, PyObject *arg, const char *desc, int short_name) {
657 if (PyString_Check(arg)) {
658 name = PyString_AsString(arg);
660 if (!PyCallable_Check(arg)) {
661 PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
664 cpy_build_name(buf, sizeof(buf), arg, NULL, short_name);
667 if (unreg(name) == 0)
669 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
673 static PyObject *cpy_unregister_log(PyObject *self, PyObject *arg) {
674 return cpy_unregister_generic_userdata(plugin_unregister_log, arg, "log", 0);
677 static PyObject *cpy_unregister_init(PyObject *self, PyObject *arg) {
678 return cpy_unregister_generic(&cpy_init_callbacks, arg, "init", 0);
681 static PyObject *cpy_unregister_config(PyObject *self, PyObject *arg) {
682 return cpy_unregister_generic(&cpy_config_callbacks, arg, "config", 1);
685 static PyObject *cpy_unregister_read(PyObject *self, PyObject *arg) {
686 return cpy_unregister_generic_userdata(plugin_unregister_read, arg, "read", 0);
689 static PyObject *cpy_unregister_write(PyObject *self, PyObject *arg) {
690 return cpy_unregister_generic_userdata(plugin_unregister_write, arg, "write", 0);
693 static PyObject *cpy_unregister_notification(PyObject *self, PyObject *arg) {
694 return cpy_unregister_generic_userdata(plugin_unregister_notification, arg, "notification", 0);
697 static PyObject *cpy_unregister_flush(PyObject *self, PyObject *arg) {
698 return cpy_unregister_generic_userdata(plugin_unregister_flush, arg, "flush", 1);
701 static PyObject *cpy_unregister_shutdown(PyObject *self, PyObject *arg) {
702 return cpy_unregister_generic(&cpy_shutdown_callbacks, arg, "shutdown", 0);
705 static PyMethodDef cpy_methods[] = {
706 {"debug", cpy_debug, METH_VARARGS, log_doc},
707 {"info", cpy_info, METH_VARARGS, log_doc},
708 {"notice", cpy_notice, METH_VARARGS, log_doc},
709 {"warning", cpy_warning, METH_VARARGS, log_doc},
710 {"error", cpy_error, METH_VARARGS, log_doc},
711 {"flush", (PyCFunction) cpy_flush, METH_VARARGS | METH_KEYWORDS, flush_doc},
712 {"register_log", (PyCFunction) cpy_register_log, METH_VARARGS | METH_KEYWORDS, reg_log_doc},
713 {"register_init", (PyCFunction) cpy_register_init, METH_VARARGS | METH_KEYWORDS, reg_init_doc},
714 {"register_config", (PyCFunction) cpy_register_config, METH_VARARGS | METH_KEYWORDS, reg_config_doc},
715 {"register_read", (PyCFunction) cpy_register_read, METH_VARARGS | METH_KEYWORDS, reg_read_doc},
716 {"register_write", (PyCFunction) cpy_register_write, METH_VARARGS | METH_KEYWORDS, reg_write_doc},
717 {"register_notification", (PyCFunction) cpy_register_notification, METH_VARARGS | METH_KEYWORDS, reg_notification_doc},
718 {"register_flush", (PyCFunction) cpy_register_flush, METH_VARARGS | METH_KEYWORDS, reg_flush_doc},
719 {"register_shutdown", (PyCFunction) cpy_register_shutdown, METH_VARARGS | METH_KEYWORDS, reg_shutdown_doc},
720 {"unregister_log", cpy_unregister_log, METH_O, unregister_doc},
721 {"unregister_init", cpy_unregister_init, METH_O, unregister_doc},
722 {"unregister_config", cpy_unregister_config, METH_O, unregister_doc},
723 {"unregister_read", cpy_unregister_read, METH_O, unregister_doc},
724 {"unregister_write", cpy_unregister_write, METH_O, unregister_doc},
725 {"unregister_notification", cpy_unregister_notification, METH_O, unregister_doc},
726 {"unregister_flush", cpy_unregister_flush, METH_O, unregister_doc},
727 {"unregister_shutdown", cpy_unregister_shutdown, METH_O, unregister_doc},
731 static int cpy_shutdown(void) {
735 /* This can happen if the module was loaded but not configured. */
737 PyEval_RestoreThread(state);
739 for (c = cpy_shutdown_callbacks; c; c = c->next) {
740 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
742 cpy_log_exception("shutdown callback");
751 static void cpy_int_handler(int sig) {
755 static void *cpy_interactive(void *data) {
757 struct sigaction sig_int_action, old;
759 /* Signal handler in a plugin? Bad stuff, but the best way to
760 * handle it I guess. In an interactive session people will
761 * press Ctrl+C at some time, which will generate a SIGINT.
762 * This will cause collectd to shutdown, thus killing the
763 * interactive interpreter, and leaving the terminal in a
764 * mess. Chances are, this isn't what the user wanted to do.
766 * So this is the plan:
767 * 1. Block SIGINT in the main thread.
768 * 2. Install our own signal handler that does nothing.
769 * 3. Unblock SIGINT in the interactive thread.
771 * This will make sure that SIGINT won't kill collectd but
772 * still interrupt syscalls like sleep and pause.
773 * It does not raise a KeyboardInterrupt exception because so
774 * far nobody managed to figure out how to do that. */
775 memset (&sig_int_action, '\0', sizeof (sig_int_action));
776 sig_int_action.sa_handler = cpy_int_handler;
777 sigaction (SIGINT, &sig_int_action, &old);
779 sigemptyset(&sigset);
780 sigaddset(&sigset, SIGINT);
781 pthread_sigmask(SIG_UNBLOCK, &sigset, NULL);
782 PyEval_AcquireThread(state);
783 if (PyImport_ImportModule("readline") == NULL) {
784 /* This interactive session will suck. */
785 cpy_log_exception("interactive session init");
787 PyRun_InteractiveLoop(stdin, "<stdin>");
789 PyEval_ReleaseThread(state);
790 NOTICE("python: Interactive interpreter exited, stopping collectd ...");
791 /* Restore the original collectd SIGINT handler and raise SIGINT.
792 * The main thread still has SIGINT blocked and there's nothing we
793 * can do about that so this thread will handle it. But that's not
794 * important, except that it won't interrupt the main loop and so
795 * it might take a few seconds before collectd really shuts down. */
796 sigaction (SIGINT, &old, NULL);
802 static int cpy_init(void) {
805 static pthread_t thread;
808 PyEval_InitThreads();
809 /* Now it's finally OK to use python threads. */
810 for (c = cpy_init_callbacks; c; c = c->next) {
811 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
813 cpy_log_exception("init callback");
817 sigemptyset(&sigset);
818 sigaddset(&sigset, SIGINT);
819 pthread_sigmask(SIG_BLOCK, &sigset, NULL);
820 state = PyEval_SaveThread();
821 if (do_interactive) {
822 if (pthread_create(&thread, NULL, cpy_interactive, NULL)) {
823 ERROR("python: Error creating thread for interactive interpreter.");
830 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
832 PyObject *item, *values, *children, *tmp;
837 values = PyTuple_New(ci->values_num); /* New reference. */
838 for (i = 0; i < ci->values_num; ++i) {
839 if (ci->values[i].type == OCONFIG_TYPE_STRING) {
840 PyTuple_SET_ITEM(values, i, PyString_FromString(ci->values[i].value.string));
841 } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
842 PyTuple_SET_ITEM(values, i, PyFloat_FromDouble(ci->values[i].value.number));
843 } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
844 PyTuple_SET_ITEM(values, i, PyBool_FromLong(ci->values[i].value.boolean));
848 item = PyObject_CallFunction((PyObject *) &ConfigType, "sONO", ci->key, parent, values, Py_None);
851 children = PyTuple_New(ci->children_num); /* New reference. */
852 for (i = 0; i < ci->children_num; ++i) {
853 PyTuple_SET_ITEM(children, i, cpy_oconfig_to_pyconfig(ci->children + i, item));
855 tmp = ((Config *) item)->children;
856 ((Config *) item)->children = children;
861 static int cpy_config(oconfig_item_t *ci) {
867 /* Ok in theory we shouldn't do initialization at this point
868 * but we have to. In order to give python scripts a chance
869 * to register a config callback we need to be able to execute
870 * python code during the config callback so we have to start
871 * the interpreter here. */
872 /* Do *not* use the python "thread" module at this point! */
875 PyType_Ready(&ConfigType);
876 PyType_Ready(&PluginDataType);
877 ValuesType.tp_base = &PluginDataType;
878 PyType_Ready(&ValuesType);
879 NotificationType.tp_base = &PluginDataType;
880 PyType_Ready(&NotificationType);
881 sys = PyImport_ImportModule("sys"); /* New reference. */
883 cpy_log_exception("python initialization");
886 sys_path = PyObject_GetAttrString(sys, "path"); /* New reference. */
888 if (sys_path == NULL) {
889 cpy_log_exception("python initialization");
892 module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
893 PyModule_AddObject(module, "Config", (PyObject *) &ConfigType); /* Steals a reference. */
894 PyModule_AddObject(module, "Values", (PyObject *) &ValuesType); /* Steals a reference. */
895 PyModule_AddObject(module, "Notification", (PyObject *) &NotificationType); /* Steals a reference. */
896 PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG);
897 PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO);
898 PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE);
899 PyModule_AddIntConstant(module, "LOG_WARNING", LOG_WARNING);
900 PyModule_AddIntConstant(module, "LOG_ERROR", LOG_ERR);
901 PyModule_AddIntConstant(module, "NOTIF_FAILURE", NOTIF_FAILURE);
902 PyModule_AddIntConstant(module, "NOTIF_WARNING", NOTIF_WARNING);
903 PyModule_AddIntConstant(module, "NOTIF_OKAY", NOTIF_OKAY);
904 for (i = 0; i < ci->children_num; ++i) {
905 oconfig_item_t *item = ci->children + i;
907 if (strcasecmp(item->key, "Interactive") == 0) {
908 if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_BOOLEAN)
910 do_interactive = item->values[0].value.boolean;
911 } else if (strcasecmp(item->key, "LogTraces") == 0) {
912 if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_BOOLEAN)
914 if (!item->values[0].value.boolean) {
915 Py_XDECREF(cpy_format_exception);
916 cpy_format_exception = NULL;
919 if (cpy_format_exception)
921 tb = PyImport_ImportModule("traceback"); /* New reference. */
923 cpy_log_exception("python initialization");
926 cpy_format_exception = PyObject_GetAttrString(tb, "format_exception"); /* New reference. */
928 if (cpy_format_exception == NULL)
929 cpy_log_exception("python initialization");
930 } else if (strcasecmp(item->key, "ModulePath") == 0) {
932 PyObject *dir_object;
934 if (cf_util_get_string(item, &dir) != 0)
936 dir_object = PyString_FromString(dir); /* New reference. */
937 if (dir_object == NULL) {
938 ERROR("python plugin: Unable to convert \"%s\" to "
939 "a python object.", dir);
941 cpy_log_exception("python initialization");
944 if (PyList_Append(sys_path, dir_object) != 0) {
945 ERROR("python plugin: Unable to append \"%s\" to "
946 "python module path.", dir);
947 cpy_log_exception("python initialization");
949 Py_DECREF(dir_object);
951 } else if (strcasecmp(item->key, "Import") == 0) {
952 char *module_name = NULL;
955 if (cf_util_get_string(item, &module_name) != 0)
957 module = PyImport_ImportModule(module_name); /* New reference. */
958 if (module == NULL) {
959 ERROR("python plugin: Error importing module \"%s\".", module_name);
960 cpy_log_exception("importing module");
965 } else if (strcasecmp(item->key, "Module") == 0) {
970 if (cf_util_get_string(item, &name) != 0)
972 for (c = cpy_config_callbacks; c; c = c->next) {
973 if (strcasecmp(c->name + 7, name) == 0)
977 WARNING("python plugin: Found a configuration for the \"%s\" plugin, "
978 "but the plugin isn't loaded or didn't register "
979 "a configuration callback.", name);
985 ret = PyObject_CallFunction(c->callback, "N",
986 cpy_oconfig_to_pyconfig(item, NULL)); /* New reference. */
988 ret = PyObject_CallFunction(c->callback, "NO",
989 cpy_oconfig_to_pyconfig(item, NULL), c->data); /* New reference. */
991 cpy_log_exception("loading module");
995 WARNING("python plugin: Ignoring unknown config key \"%s\".", item->key);
1002 void module_register(void) {
1003 plugin_register_complex_config("python", cpy_config);
1004 plugin_register_init("python", cpy_init);
1005 plugin_register_shutdown("python", cpy_shutdown);