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 omitted it will be omitted 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 background.\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 omitted it will be omitted 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 omitted it will be omitted 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 omitted it will be omitted 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 omitted it will be omitted 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);
232 snprintf(buf, size, "python.%p", callback);
236 static void cpy_log_exception(const char *context) {
238 const char *typename = NULL, *message = NULL;
239 PyObject *type, *value, *traceback, *tn, *m, *list;
241 PyErr_Fetch(&type, &value, &traceback);
242 PyErr_NormalizeException(&type, &value, &traceback);
243 if (type == NULL) return;
244 tn = PyObject_GetAttrString(type, "__name__"); /* New reference. */
245 m = PyObject_GetAttrString(value, "message"); /* New reference. */
247 typename = PyString_AsString(tn);
249 message = PyString_AsString(m);
250 if (typename == NULL)
251 typename = "NamelessException";
254 Py_BEGIN_ALLOW_THREADS
255 ERROR("Unhandled python exception in %s: %s: %s", context, typename, message);
259 if (!cpy_format_exception) {
263 Py_XDECREF(traceback);
270 list = PyObject_CallFunction(cpy_format_exception, "NNN", type, value, traceback); /* New reference. */
272 l = PyObject_Length(list);
273 for (i = 0; i < l; ++i) {
277 line = PyList_GET_ITEM(list, i); /* Borrowed reference. */
278 s = strdup(PyString_AsString(line));
280 if (s[strlen(s) - 1] == '\n')
281 s[strlen(s) - 1] = 0;
282 Py_BEGIN_ALLOW_THREADS
291 static int cpy_read_callback(user_data_t *data) {
292 cpy_callback_t *c = data->data;
296 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
298 cpy_log_exception("read callback");
308 static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_list, user_data_t *data) {
310 cpy_callback_t *c = data->data;
311 PyObject *ret, *v, *list;
314 list = PyList_New(value_list->values_len); /* New reference. */
316 cpy_log_exception("write callback");
317 CPY_RETURN_FROM_THREADS 0;
319 for (i = 0; i < value_list->values_len; ++i) {
320 if (ds->ds->type == DS_TYPE_COUNTER) {
321 if ((long) value_list->values[i].counter == value_list->values[i].counter)
322 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].counter));
324 PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].counter));
325 } else if (ds->ds->type == DS_TYPE_GAUGE) {
326 PyList_SetItem(list, i, PyFloat_FromDouble(value_list->values[i].gauge));
327 } else if (ds->ds->type == DS_TYPE_DERIVE) {
328 if ((long) value_list->values[i].derive == value_list->values[i].derive)
329 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].derive));
331 PyList_SetItem(list, i, PyLong_FromLongLong(value_list->values[i].derive));
332 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
333 if ((long) value_list->values[i].absolute == value_list->values[i].absolute)
334 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].absolute));
336 PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].absolute));
338 Py_BEGIN_ALLOW_THREADS
339 ERROR("cpy_write_callback: Unknown value type %d.", ds->ds->type);
342 CPY_RETURN_FROM_THREADS 0;
344 if (PyErr_Occurred() != NULL) {
345 cpy_log_exception("value building for write callback");
346 CPY_RETURN_FROM_THREADS 0;
349 v = PyObject_CallFunction((void *) &ValuesType, "sOssssdi", value_list->type, list,
350 value_list->plugin_instance, value_list->type_instance, value_list->plugin,
351 value_list->host, (double) value_list->time, value_list->interval);
353 ret = PyObject_CallFunctionObjArgs(c->callback, v, c->data, (void *) 0); /* New reference. */
355 cpy_log_exception("write callback");
363 static int cpy_notification_callback(const notification_t *notification, user_data_t *data) {
364 cpy_callback_t *c = data->data;
368 n = PyObject_CallFunction((void *) &NotificationType, "ssssssdi", notification->type, notification->message,
369 notification->plugin_instance, notification->type_instance, notification->plugin,
370 notification->host, (double) notification->time, notification->severity);
371 ret = PyObject_CallFunctionObjArgs(c->callback, n, c->data, (void *) 0); /* New reference. */
373 cpy_log_exception("notification callback");
381 static void cpy_log_callback(int severity, const char *message, user_data_t *data) {
382 cpy_callback_t * c = data->data;
387 ret = PyObject_CallFunction(c->callback, "is", severity, message); /* New reference. */
389 ret = PyObject_CallFunction(c->callback, "isO", severity, message, c->data); /* New reference. */
393 /* Do we really want to trigger a log callback because a log callback failed?
396 /* In case someone wanted to be clever, replaced stderr and failed at that. */
404 static void cpy_flush_callback(int timeout, const char *id, user_data_t *data) {
405 cpy_callback_t * c = data->data;
410 ret = PyObject_CallFunction(c->callback, "is", timeout, id); /* New reference. */
412 ret = PyObject_CallFunction(c->callback, "isO", timeout, id, c->data); /* New reference. */
415 cpy_log_exception("flush callback");
422 static PyObject *cpy_register_generic(cpy_callback_t **list_head, PyObject *args, PyObject *kwds) {
425 const char *name = NULL;
426 PyObject *callback = NULL, *data = NULL, *mod = NULL;
427 static char *kwlist[] = {"callback", "data", "name", NULL};
429 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
430 if (PyCallable_Check(callback) == 0) {
431 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
434 cpy_build_name(buf, sizeof(buf), callback, name);
438 c = malloc(sizeof(*c));
439 c->name = strdup(buf);
440 c->callback = callback;
442 c->next = *list_head;
445 return PyString_FromString(buf);
448 static PyObject *cpy_flush(cpy_callback_t **list_head, PyObject *args, PyObject *kwds) {
450 const char *plugin = NULL, *identifier = NULL;
451 static char *kwlist[] = {"plugin", "timeout", "identifier", NULL};
453 if (PyArg_ParseTupleAndKeywords(args, kwds, "|ziz", kwlist, &plugin, &timeout, &identifier) == 0) return NULL;
454 Py_BEGIN_ALLOW_THREADS
455 plugin_flush(plugin, timeout, identifier);
460 static PyObject *cpy_register_config(PyObject *self, PyObject *args, PyObject *kwds) {
461 return cpy_register_generic(&cpy_config_callbacks, args, kwds);
464 static PyObject *cpy_register_init(PyObject *self, PyObject *args, PyObject *kwds) {
465 return cpy_register_generic(&cpy_init_callbacks, args, kwds);
468 typedef int reg_function_t(const char *name, void *callback, void *data);
470 static PyObject *cpy_register_generic_userdata(void *reg, void *handler, PyObject *args, PyObject *kwds) {
472 reg_function_t *register_function = (reg_function_t *) reg;
473 cpy_callback_t *c = NULL;
474 user_data_t *user_data = NULL;
475 const char *name = NULL;
476 PyObject *callback = NULL, *data = NULL;
477 static char *kwlist[] = {"callback", "data", "name", NULL};
479 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
480 if (PyCallable_Check(callback) == 0) {
481 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
484 cpy_build_name(buf, sizeof(buf), callback, name);
488 c = malloc(sizeof(*c));
489 c->name = strdup(buf);
490 c->callback = callback;
493 user_data = malloc(sizeof(*user_data));
494 user_data->free_func = cpy_destroy_user_data;
496 register_function(buf, handler, user_data);
497 return PyString_FromString(buf);
500 static PyObject *cpy_register_read(PyObject *self, PyObject *args, PyObject *kwds) {
502 cpy_callback_t *c = NULL;
503 user_data_t *user_data = NULL;
505 const char *name = NULL;
506 PyObject *callback = NULL, *data = NULL;
508 static char *kwlist[] = {"callback", "interval", "data", "name", NULL};
510 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|dOz", kwlist, &callback, &interval, &data, &name) == 0) return NULL;
511 if (PyCallable_Check(callback) == 0) {
512 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
515 cpy_build_name(buf, sizeof(buf), callback, name);
519 c = malloc(sizeof(*c));
520 c->name = strdup(buf);
521 c->callback = callback;
524 user_data = malloc(sizeof(*user_data));
525 user_data->free_func = cpy_destroy_user_data;
527 ts.tv_sec = interval;
528 ts.tv_nsec = (interval - ts.tv_sec) * 1000000000;
529 plugin_register_complex_read(buf, cpy_read_callback, &ts, user_data);
530 return PyString_FromString(buf);
533 static PyObject *cpy_register_log(PyObject *self, PyObject *args, PyObject *kwds) {
534 return cpy_register_generic_userdata((void *) plugin_register_log,
535 (void *) cpy_log_callback, args, kwds);
538 static PyObject *cpy_register_write(PyObject *self, PyObject *args, PyObject *kwds) {
539 return cpy_register_generic_userdata((void *) plugin_register_write,
540 (void *) cpy_write_callback, args, kwds);
543 static PyObject *cpy_register_notification(PyObject *self, PyObject *args, PyObject *kwds) {
544 return cpy_register_generic_userdata((void *) plugin_register_notification,
545 (void *) cpy_notification_callback, args, kwds);
548 static PyObject *cpy_register_flush(PyObject *self, PyObject *args, PyObject *kwds) {
549 return cpy_register_generic_userdata((void *) plugin_register_flush,
550 (void *) cpy_flush_callback, args, kwds);
553 static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args, PyObject *kwds) {
554 return cpy_register_generic(&cpy_shutdown_callbacks, args, kwds);
557 static PyObject *cpy_error(PyObject *self, PyObject *args) {
559 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
560 Py_BEGIN_ALLOW_THREADS
561 plugin_log(LOG_ERR, "%s", text);
566 static PyObject *cpy_warning(PyObject *self, PyObject *args) {
568 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
569 Py_BEGIN_ALLOW_THREADS
570 plugin_log(LOG_WARNING, "%s", text);
575 static PyObject *cpy_notice(PyObject *self, PyObject *args) {
577 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
578 Py_BEGIN_ALLOW_THREADS
579 plugin_log(LOG_NOTICE, "%s", text);
584 static PyObject *cpy_info(PyObject *self, PyObject *args) {
586 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
587 Py_BEGIN_ALLOW_THREADS
588 plugin_log(LOG_INFO, "%s", text);
593 static PyObject *cpy_debug(PyObject *self, PyObject *args) {
596 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
597 Py_BEGIN_ALLOW_THREADS
598 plugin_log(LOG_DEBUG, "%s", text);
604 static PyObject *cpy_unregister_generic(cpy_callback_t **list_head, PyObject *arg, const char *desc) {
607 cpy_callback_t *prev = NULL, *tmp;
609 if (PyUnicode_Check(arg)) {
610 arg = PyUnicode_AsEncodedString(arg, NULL, NULL);
613 name = PyString_AsString(arg);
615 } else if (PyString_Check(arg)) {
616 name = PyString_AsString(arg);
618 if (!PyCallable_Check(arg)) {
619 PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
622 cpy_build_name(buf, sizeof(buf), arg, NULL);
625 for (tmp = *list_head; tmp; prev = tmp, tmp = tmp->next)
626 if (strcmp(name, tmp->name) == 0)
630 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
633 /* Yes, this is actually save. To call this function the caller has to
634 * hold the GIL. Well, save as long as there is only one GIL anyway ... */
636 *list_head = tmp->next;
638 prev->next = tmp->next;
639 cpy_destroy_user_data(tmp);
643 typedef int cpy_unregister_function_t(const char *name);
645 static PyObject *cpy_unregister_generic_userdata(cpy_unregister_function_t *unreg, PyObject *arg, const char *desc) {
649 if (PyUnicode_Check(arg)) {
650 arg = PyUnicode_AsEncodedString(arg, NULL, NULL);
653 name = PyString_AsString(arg);
655 } else if (PyString_Check(arg)) {
656 name = PyString_AsString(arg);
658 if (!PyCallable_Check(arg)) {
659 PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
662 cpy_build_name(buf, sizeof(buf), arg, NULL);
665 if (unreg(name) == 0)
667 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
671 static PyObject *cpy_unregister_log(PyObject *self, PyObject *arg) {
672 return cpy_unregister_generic_userdata(plugin_unregister_log, arg, "log");
675 static PyObject *cpy_unregister_init(PyObject *self, PyObject *arg) {
676 return cpy_unregister_generic(&cpy_init_callbacks, arg, "init");
679 static PyObject *cpy_unregister_config(PyObject *self, PyObject *arg) {
680 return cpy_unregister_generic(&cpy_config_callbacks, arg, "config");
683 static PyObject *cpy_unregister_read(PyObject *self, PyObject *arg) {
684 return cpy_unregister_generic_userdata(plugin_unregister_read, arg, "read");
687 static PyObject *cpy_unregister_write(PyObject *self, PyObject *arg) {
688 return cpy_unregister_generic_userdata(plugin_unregister_write, arg, "write");
691 static PyObject *cpy_unregister_notification(PyObject *self, PyObject *arg) {
692 return cpy_unregister_generic_userdata(plugin_unregister_notification, arg, "notification");
695 static PyObject *cpy_unregister_flush(PyObject *self, PyObject *arg) {
696 return cpy_unregister_generic_userdata(plugin_unregister_flush, arg, "flush");
699 static PyObject *cpy_unregister_shutdown(PyObject *self, PyObject *arg) {
700 return cpy_unregister_generic(&cpy_shutdown_callbacks, arg, "shutdown");
703 static PyMethodDef cpy_methods[] = {
704 {"debug", cpy_debug, METH_VARARGS, log_doc},
705 {"info", cpy_info, METH_VARARGS, log_doc},
706 {"notice", cpy_notice, METH_VARARGS, log_doc},
707 {"warning", cpy_warning, METH_VARARGS, log_doc},
708 {"error", cpy_error, METH_VARARGS, log_doc},
709 {"flush", (PyCFunction) cpy_flush, METH_VARARGS | METH_KEYWORDS, flush_doc},
710 {"register_log", (PyCFunction) cpy_register_log, METH_VARARGS | METH_KEYWORDS, reg_log_doc},
711 {"register_init", (PyCFunction) cpy_register_init, METH_VARARGS | METH_KEYWORDS, reg_init_doc},
712 {"register_config", (PyCFunction) cpy_register_config, METH_VARARGS | METH_KEYWORDS, reg_config_doc},
713 {"register_read", (PyCFunction) cpy_register_read, METH_VARARGS | METH_KEYWORDS, reg_read_doc},
714 {"register_write", (PyCFunction) cpy_register_write, METH_VARARGS | METH_KEYWORDS, reg_write_doc},
715 {"register_notification", (PyCFunction) cpy_register_notification, METH_VARARGS | METH_KEYWORDS, reg_notification_doc},
716 {"register_flush", (PyCFunction) cpy_register_flush, METH_VARARGS | METH_KEYWORDS, reg_flush_doc},
717 {"register_shutdown", (PyCFunction) cpy_register_shutdown, METH_VARARGS | METH_KEYWORDS, reg_shutdown_doc},
718 {"unregister_log", cpy_unregister_log, METH_O, unregister_doc},
719 {"unregister_init", cpy_unregister_init, METH_O, unregister_doc},
720 {"unregister_config", cpy_unregister_config, METH_O, unregister_doc},
721 {"unregister_read", cpy_unregister_read, METH_O, unregister_doc},
722 {"unregister_write", cpy_unregister_write, METH_O, unregister_doc},
723 {"unregister_notification", cpy_unregister_notification, METH_O, unregister_doc},
724 {"unregister_flush", cpy_unregister_flush, METH_O, unregister_doc},
725 {"unregister_shutdown", cpy_unregister_shutdown, METH_O, unregister_doc},
729 static int cpy_shutdown(void) {
733 /* This can happen if the module was loaded but not configured. */
735 PyEval_RestoreThread(state);
737 for (c = cpy_shutdown_callbacks; c; c = c->next) {
738 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
740 cpy_log_exception("shutdown callback");
749 static void cpy_int_handler(int sig) {
753 static void *cpy_interactive(void *data) {
755 struct sigaction sig_int_action, old;
757 /* Signal handler in a plugin? Bad stuff, but the best way to
758 * handle it I guess. In an interactive session people will
759 * press Ctrl+C at some time, which will generate a SIGINT.
760 * This will cause collectd to shutdown, thus killing the
761 * interactive interpreter, and leaving the terminal in a
762 * mess. Chances are, this isn't what the user wanted to do.
764 * So this is the plan:
765 * 1. Block SIGINT in the main thread.
766 * 2. Install our own signal handler that does nothing.
767 * 3. Unblock SIGINT in the interactive thread.
769 * This will make sure that SIGINT won't kill collectd but
770 * still interrupt syscalls like sleep and pause.
771 * It does not raise a KeyboardInterrupt exception because so
772 * far nobody managed to figure out how to do that. */
773 memset (&sig_int_action, '\0', sizeof (sig_int_action));
774 sig_int_action.sa_handler = cpy_int_handler;
775 sigaction (SIGINT, &sig_int_action, &old);
777 sigemptyset(&sigset);
778 sigaddset(&sigset, SIGINT);
779 pthread_sigmask(SIG_UNBLOCK, &sigset, NULL);
780 PyEval_AcquireThread(state);
781 if (PyImport_ImportModule("readline") == NULL) {
782 /* This interactive session will suck. */
783 cpy_log_exception("interactive session init");
785 PyRun_InteractiveLoop(stdin, "<stdin>");
787 PyEval_ReleaseThread(state);
788 NOTICE("python: Interactive interpreter exited, stopping collectd ...");
789 /* Restore the original collectd SIGINT handler and raise SIGINT.
790 * The main thread still has SIGINT blocked and there's nothing we
791 * can do about that so this thread will handle it. But that's not
792 * important, except that it won't interrupt the main loop and so
793 * it might take a few seconds before collectd really shuts down. */
794 sigaction (SIGINT, &old, NULL);
800 static int cpy_init(void) {
803 static pthread_t thread;
806 PyEval_InitThreads();
807 /* Now it's finally OK to use python threads. */
808 for (c = cpy_init_callbacks; c; c = c->next) {
809 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
811 cpy_log_exception("init callback");
815 sigemptyset(&sigset);
816 sigaddset(&sigset, SIGINT);
817 pthread_sigmask(SIG_BLOCK, &sigset, NULL);
818 state = PyEval_SaveThread();
819 if (do_interactive) {
820 if (pthread_create(&thread, NULL, cpy_interactive, NULL)) {
821 ERROR("python: Error creating thread for interactive interpreter.");
828 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
830 PyObject *item, *values, *children, *tmp;
835 values = PyTuple_New(ci->values_num); /* New reference. */
836 for (i = 0; i < ci->values_num; ++i) {
837 if (ci->values[i].type == OCONFIG_TYPE_STRING) {
838 PyTuple_SET_ITEM(values, i, PyString_FromString(ci->values[i].value.string));
839 } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
840 PyTuple_SET_ITEM(values, i, PyFloat_FromDouble(ci->values[i].value.number));
841 } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
842 PyTuple_SET_ITEM(values, i, PyBool_FromLong(ci->values[i].value.boolean));
846 item = PyObject_CallFunction((void *) &ConfigType, "sONO", ci->key, parent, values, Py_None);
849 children = PyTuple_New(ci->children_num); /* New reference. */
850 for (i = 0; i < ci->children_num; ++i) {
851 PyTuple_SET_ITEM(children, i, cpy_oconfig_to_pyconfig(ci->children + i, item));
853 tmp = ((Config *) item)->children;
854 ((Config *) item)->children = children;
859 static int cpy_config(oconfig_item_t *ci) {
865 /* Ok in theory we shouldn't do initialization at this point
866 * but we have to. In order to give python scripts a chance
867 * to register a config callback we need to be able to execute
868 * python code during the config callback so we have to start
869 * the interpreter here. */
870 /* Do *not* use the python "thread" module at this point! */
873 PyType_Ready(&ConfigType);
874 PyType_Ready(&PluginDataType);
875 ValuesType.tp_base = &PluginDataType;
876 PyType_Ready(&ValuesType);
877 NotificationType.tp_base = &PluginDataType;
878 PyType_Ready(&NotificationType);
879 sys = PyImport_ImportModule("sys"); /* New reference. */
881 cpy_log_exception("python initialization");
884 sys_path = PyObject_GetAttrString(sys, "path"); /* New reference. */
886 if (sys_path == NULL) {
887 cpy_log_exception("python initialization");
890 module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
891 PyModule_AddObject(module, "Config", (void *) &ConfigType); /* Steals a reference. */
892 PyModule_AddObject(module, "Values", (void *) &ValuesType); /* Steals a reference. */
893 PyModule_AddObject(module, "Notification", (void *) &NotificationType); /* Steals a reference. */
894 PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG);
895 PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO);
896 PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE);
897 PyModule_AddIntConstant(module, "LOG_WARNING", LOG_WARNING);
898 PyModule_AddIntConstant(module, "LOG_ERROR", LOG_ERR);
899 PyModule_AddIntConstant(module, "NOTIF_FAILURE", NOTIF_FAILURE);
900 PyModule_AddIntConstant(module, "NOTIF_WARNING", NOTIF_WARNING);
901 PyModule_AddIntConstant(module, "NOTIF_OKAY", NOTIF_OKAY);
902 for (i = 0; i < ci->children_num; ++i) {
903 oconfig_item_t *item = ci->children + i;
905 if (strcasecmp(item->key, "Interactive") == 0) {
906 if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_BOOLEAN)
908 do_interactive = item->values[0].value.boolean;
909 } else if (strcasecmp(item->key, "Encoding") == 0) {
910 if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_STRING)
912 /* Why is this even necessary? And undocumented? */
913 if (PyUnicode_SetDefaultEncoding(item->values[0].value.string))
914 cpy_log_exception("setting default encoding");
915 } else if (strcasecmp(item->key, "LogTraces") == 0) {
916 if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_BOOLEAN)
918 if (!item->values[0].value.boolean) {
919 Py_XDECREF(cpy_format_exception);
920 cpy_format_exception = NULL;
923 if (cpy_format_exception)
925 tb = PyImport_ImportModule("traceback"); /* New reference. */
927 cpy_log_exception("python initialization");
930 cpy_format_exception = PyObject_GetAttrString(tb, "format_exception"); /* New reference. */
932 if (cpy_format_exception == NULL)
933 cpy_log_exception("python initialization");
934 } else if (strcasecmp(item->key, "ModulePath") == 0) {
936 PyObject *dir_object;
938 if (cf_util_get_string(item, &dir) != 0)
940 dir_object = PyString_FromString(dir); /* New reference. */
941 if (dir_object == NULL) {
942 ERROR("python plugin: Unable to convert \"%s\" to "
943 "a python object.", dir);
945 cpy_log_exception("python initialization");
948 if (PyList_Append(sys_path, dir_object) != 0) {
949 ERROR("python plugin: Unable to append \"%s\" to "
950 "python module path.", dir);
951 cpy_log_exception("python initialization");
953 Py_DECREF(dir_object);
955 } else if (strcasecmp(item->key, "Import") == 0) {
956 char *module_name = NULL;
959 if (cf_util_get_string(item, &module_name) != 0)
961 module = PyImport_ImportModule(module_name); /* New reference. */
962 if (module == NULL) {
963 ERROR("python plugin: Error importing module \"%s\".", module_name);
964 cpy_log_exception("importing module");
969 } else if (strcasecmp(item->key, "Module") == 0) {
974 if (cf_util_get_string(item, &name) != 0)
976 for (c = cpy_config_callbacks; c; c = c->next) {
977 if (strcasecmp(c->name + 7, name) == 0)
981 WARNING("python plugin: Found a configuration for the \"%s\" plugin, "
982 "but the plugin isn't loaded or didn't register "
983 "a configuration callback.", name);
989 ret = PyObject_CallFunction(c->callback, "N",
990 cpy_oconfig_to_pyconfig(item, NULL)); /* New reference. */
992 ret = PyObject_CallFunction(c->callback, "NO",
993 cpy_oconfig_to_pyconfig(item, NULL), c->data); /* New reference. */
995 cpy_log_exception("loading module");
999 WARNING("python plugin: Ignoring unknown config key \"%s\".", item->key);
1002 Py_DECREF(sys_path);
1006 void module_register(void) {
1007 plugin_register_complex_config("python", cpy_config);
1008 plugin_register_init("python", cpy_init);
1009 plugin_register_shutdown("python", cpy_shutdown);