2 #include <structmember.h>
13 typedef struct cpy_callback_s {
17 struct cpy_callback_s *next;
20 static char log_doc[] = "This function sends a string to all logging plugins.";
22 static char flush_doc[] = "flush([plugin][, timeout][, identifier]) -> None\n"
24 "Flushes the cache of another plugin.";
26 static char unregister_doc[] = "Unregisters a callback. This function needs exactly one parameter either\n"
27 "the function to unregister or the callback identifier to unregister.";
29 static char reg_log_doc[] = "register_log(callback[, data][, name]) -> identifier\n"
31 "Register a callback function for log messages.\n"
33 "'callback' is a callable object that will be called every time something\n"
35 "'data' is an optional object that will be passed back to the callback\n"
36 " function every time it is called.\n"
37 "'name' is an optional identifier for this callback. The default name\n"
38 " is 'python.<module>.<name>'. If 'name' contains a '.' it\n"
39 " replaces both module and name, otherwise it replaces only name.\n"
40 " Every callback needs a unique identifier, so if you want to\n"
41 " register one function multiple time you need to specify a name\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>.<name>'. If 'name' contains a '.' it\n"
62 " replaces both module and name, otherwise it replaces only name.\n"
63 " Every callback needs a unique identifier, so if you want to\n"
64 " register one function multiple time you need to specify a name\n"
66 "'identifier' is the full identifier assigned to this callback.\n"
68 "The callback function will be called without parameters, except for\n"
69 "data if it was supplied.";
71 static char reg_config_doc[] = "register_config(callback[, data][, name]) -> identifier\n"
73 "Register a callback function for config file entries.\n"
74 "'callback' is a callable object that will be called for every config block.\n"
75 "'data' is an optional object that will be passed back to the callback\n"
76 " function every time it is called.\n"
77 "'name' is an optional identifier for this callback. The default name\n"
78 " is 'python.<module>'. Every callback needs a unique identifier,\n"
79 " so if you want to register one function multiple time you need to\n"
80 " 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>.<name>'. If 'name' contains a '.' it\n"
100 " replaces both module and name, otherwise it replaces only name.\n"
101 " Every callback needs a unique identifier, so if you want to\n"
102 " register one function multiple time you need to specify a name\n"
104 "'identifier' is the full identifier assigned to this callback.\n"
106 "The callback function will be called without parameters, except for\n"
107 "data if it was supplied.";
109 static char reg_write_doc[] = "register_write(callback[, data][, name]) -> identifier\n"
111 "Register a callback function to receive values dispatched by other plugins.\n"
112 "'callback' is a callable object that will be called every time a value\n"
114 "'data' is an optional object that will be passed back to the callback\n"
115 " function every time it is called.\n"
116 "'name' is an optional identifier for this callback. The default name\n"
117 " is 'python.<module>.<name>'. If 'name' contains a '.' it\n"
118 " replaces both module and name, otherwise it replaces only name.\n"
119 " Every callback needs a unique identifier, so if you want to\n"
120 " register one function multiple time you need to specify a name\n"
122 "'identifier' is the full identifier assigned to this callback.\n"
124 "The callback function will be called with one or two parameters:\n"
125 "values: A Values object which is a copy of the dispatched values.\n"
126 "data: The optional data parameter passed to the register function.\n"
127 " If the parameter was obmitted it will be obmitted here, too.";
129 static char reg_notification_doc[] = "register_notification(callback[, data][, name]) -> identifier\n"
131 "Register a callback function for notifications.\n"
132 "'callback' is a callable object that will be called every time a notification\n"
134 "'data' is an optional object that will be passed back to the callback\n"
135 " function every time it is called.\n"
136 "'name' is an optional identifier for this callback. The default name\n"
137 " is 'python.<module>.<name>'. If 'name' contains a '.' it\n"
138 " replaces both module and name, otherwise it replaces only name.\n"
139 " Every callback needs a unique identifier, so if you want to\n"
140 " register one function multiple time you need to specify a name\n"
142 "'identifier' is the full identifier assigned to this callback.\n"
144 "The callback function will be called with one or two parameters:\n"
145 "notification: A copy of the notification that was dispatched.\n"
146 "data: The optional data parameter passed to the register function.\n"
147 " If the parameter was obmitted it will be obmitted here, too.";
149 static char reg_flush_doc[] = "register_flush(callback[, data][, name]) -> identifier\n"
151 "Register a callback function for flush messages.\n"
152 "'callback' is a callable object that will be called every time a plugin\n"
153 " requests a flush for either this or all plugins.\n"
154 "'data' is an optional object that will be passed back to the callback\n"
155 " function every time it is called.\n"
156 "'name' is an optional identifier for this callback. The default name\n"
157 " is 'python.<module>'. Every callback needs a unique identifier,\n"
158 " so if you want to register one function multiple time you need to\n"
159 " specify a name here.\n"
160 "'identifier' is the full identifier assigned to this callback.\n"
162 "The callback function will be called with two or three parameters:\n"
165 "data: The optional data parameter passed to the register function.\n"
166 " If the parameter was obmitted it will be obmitted here, too.";
168 static char reg_shutdown_doc[] = "register_shutdown(callback[, data][, name]) -> identifier\n"
170 "Register a callback function for collectd shutdown.\n"
171 "'callback' is a callable object that will be called once collectd is\n"
173 "'data' is an optional object that will be passed back to the callback\n"
174 " function if it is called.\n"
175 "'name' is an optional identifier for this callback. The default name\n"
176 " is 'python.<module>.<name>'. If 'name' contains a '.' it\n"
177 " replaces both module and name, otherwise it replaces only name.\n"
178 " Every callback needs a unique identifier, so if you want to\n"
179 " register one function multiple time you need to specify a name\n"
181 "'identifier' is the full identifier assigned to this callback.\n"
183 "The callback function will be called with no parameters except for\n"
184 " data if it was supplied.";
187 static int do_interactive = 0;
189 /* This is our global thread state. Python saves some stuff in thread-local
190 * storage. So if we allow the interpreter to run in the background
191 * (the scriptwriters might have created some threads from python), we have
192 * to save the state so we can resume it later after shutdown. */
194 static PyThreadState *state;
196 static PyObject *cpy_format_exception;
198 static cpy_callback_t *cpy_config_callbacks;
199 static cpy_callback_t *cpy_init_callbacks;
200 static cpy_callback_t *cpy_shutdown_callbacks;
202 static void cpy_destroy_user_data(void *data) {
203 cpy_callback_t *c = data;
205 Py_DECREF(c->callback);
210 /* You must hold the GIL to call this function!
211 * But if you managed to extract the callback parameter then you probably already do. */
213 static void cpy_build_name(char *buf, size_t size, PyObject *callback, const char *name, int short_name) {
215 PyObject *mod = NULL, *n = NULL;
217 if (name != NULL && (strchr(name, '.') != NULL || short_name)) {
218 snprintf(buf, size, "python.%s", name);
222 mod = PyObject_GetAttrString(callback, "__module__"); /* New reference. */
224 module = PyString_AsString(mod);
229 snprintf(buf, size, "python.%s", module);
235 snprintf(buf, size, "python.%s.%s", module, name);
240 n = PyObject_GetAttrString(callback, "__name__"); /* New reference. */
242 name = PyString_AsString(n);
245 snprintf(buf, size, "python.%s.%s", module, name);
247 snprintf(buf, size, "python.%s.%p", module, callback);
252 static void cpy_log_exception(const char *context) {
254 const char *typename = NULL, *message = NULL;
255 PyObject *type, *value, *traceback, *tn, *m, *list;
257 PyErr_Fetch(&type, &value, &traceback);
258 PyErr_NormalizeException(&type, &value, &traceback);
259 if (type == NULL) return;
260 tn = PyObject_GetAttrString(type, "__name__"); /* New reference. */
261 m = PyObject_GetAttrString(value, "message"); /* New reference. */
263 typename = PyString_AsString(tn);
265 message = PyString_AsString(m);
266 if (typename == NULL)
267 typename = "NamelessException";
270 ERROR("Unhandled python exception in %s: %s: %s", context, typename, message);
273 if (!cpy_format_exception) {
277 Py_XDECREF(traceback);
284 list = PyObject_CallFunction(cpy_format_exception, "NNN", type, value, traceback); /* New reference. */
286 l = PyObject_Length(list);
287 for (i = 0; i < l; ++i) {
291 line = PyList_GET_ITEM(list, i); /* Borrowed reference. */
292 s = strdup(PyString_AsString(line));
294 if (s[strlen(s) - 1] == '\n')
295 s[strlen(s) - 1] = 0;
303 static int cpy_read_callback(user_data_t *data) {
304 cpy_callback_t *c = data->data;
308 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
310 cpy_log_exception("read callback");
318 static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_list, user_data_t *data) {
320 cpy_callback_t *c = data->data;
321 PyObject *ret, *v, *list;
324 list = PyList_New(value_list->values_len); /* New reference. */
326 cpy_log_exception("write callback");
327 CPY_RETURN_FROM_THREADS 0;
329 for (i = 0; i < value_list->values_len; ++i) {
330 if (ds->ds->type == DS_TYPE_COUNTER) {
331 if ((long) value_list->values[i].counter == value_list->values[i].counter)
332 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].counter));
334 PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].counter));
335 } else if (ds->ds->type == DS_TYPE_GAUGE) {
336 PyList_SetItem(list, i, PyFloat_FromDouble(value_list->values[i].gauge));
337 } else if (ds->ds->type == DS_TYPE_DERIVE) {
338 if ((long) value_list->values[i].derive == value_list->values[i].derive)
339 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].derive));
341 PyList_SetItem(list, i, PyLong_FromLongLong(value_list->values[i].derive));
342 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
343 if ((long) value_list->values[i].absolute == value_list->values[i].absolute)
344 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].absolute));
346 PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].absolute));
348 ERROR("cpy_write_callback: Unknown value type %d.", ds->ds->type);
350 CPY_RETURN_FROM_THREADS 0;
352 if (PyErr_Occurred() != NULL) {
353 cpy_log_exception("value building for write callback");
354 CPY_RETURN_FROM_THREADS 0;
357 v = PyObject_CallFunction((PyObject *) &ValuesType, "sOssssdi", value_list->type, list,
358 value_list->plugin_instance, value_list->type_instance, value_list->plugin,
359 value_list->host, (double) value_list->time, value_list->interval);
361 ret = PyObject_CallFunctionObjArgs(c->callback, v, c->data, (void *) 0); /* New reference. */
363 cpy_log_exception("write callback");
371 static int cpy_notification_callback(const notification_t *notification, user_data_t *data) {
372 cpy_callback_t *c = data->data;
376 n = PyObject_CallFunction((PyObject *) &NotificationType, "ssssssdi", notification->type, notification->message,
377 notification->plugin_instance, notification->type_instance, notification->plugin,
378 notification->host, (double) notification->time, notification->severity);
379 ret = PyObject_CallFunctionObjArgs(c->callback, n, c->data, (void *) 0); /* New reference. */
381 cpy_log_exception("notification callback");
389 static void cpy_log_callback(int severity, const char *message, user_data_t *data) {
390 cpy_callback_t * c = data->data;
395 ret = PyObject_CallFunction(c->callback, "is", severity, message); /* New reference. */
397 ret = PyObject_CallFunction(c->callback, "isO", severity, message, c->data); /* New reference. */
401 /* Do we really want to trigger a log callback because a log callback failed?
410 static void cpy_flush_callback(int timeout, const char *id, user_data_t *data) {
411 cpy_callback_t * c = data->data;
416 ret = PyObject_CallFunction(c->callback, "is", timeout, id); /* New reference. */
418 ret = PyObject_CallFunction(c->callback, "isO", timeout, id, c->data); /* New reference. */
421 cpy_log_exception("flush callback");
428 static PyObject *cpy_register_generic(cpy_callback_t **list_head, PyObject *args, PyObject *kwds, int short_name) {
431 const char *name = NULL;
432 PyObject *callback = NULL, *data = NULL, *mod = NULL;
433 static char *kwlist[] = {"callback", "data", "name", NULL};
435 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
436 if (PyCallable_Check(callback) == 0) {
437 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
440 cpy_build_name(buf, sizeof(buf), callback, name, short_name);
444 c = malloc(sizeof(*c));
445 c->name = strdup(buf);
446 c->callback = callback;
448 c->next = *list_head;
451 return PyString_FromString(buf);
454 static PyObject *cpy_flush(cpy_callback_t **list_head, PyObject *args, PyObject *kwds) {
456 const char *plugin = NULL, *identifier = NULL;
457 static char *kwlist[] = {"plugin", "timeout", "identifier", NULL};
459 if (PyArg_ParseTupleAndKeywords(args, kwds, "|ziz", kwlist, &plugin, &timeout, &identifier) == 0) return NULL;
460 Py_BEGIN_ALLOW_THREADS
461 plugin_flush(plugin, timeout, identifier);
466 static PyObject *cpy_register_config(PyObject *self, PyObject *args, PyObject *kwds) {
467 return cpy_register_generic(&cpy_config_callbacks, args, kwds, 1);
470 static PyObject *cpy_register_init(PyObject *self, PyObject *args, PyObject *kwds) {
471 return cpy_register_generic(&cpy_init_callbacks, args, kwds, 0);
474 typedef int reg_function_t(const char *name, void *callback, void *data);
476 static PyObject *cpy_register_generic_userdata(void *reg, void *handler, PyObject *args, PyObject *kwds, int short_name) {
478 reg_function_t *register_function = (reg_function_t *) reg;
479 cpy_callback_t *c = NULL;
480 user_data_t *user_data = NULL;
481 const char *name = NULL;
482 PyObject *callback = NULL, *data = NULL;
483 static char *kwlist[] = {"callback", "data", "name", NULL};
485 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
486 if (PyCallable_Check(callback) == 0) {
487 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
490 cpy_build_name(buf, sizeof(buf), callback, name, short_name);
494 c = malloc(sizeof(*c));
495 c->name = strdup(buf);
496 c->callback = callback;
499 user_data = malloc(sizeof(*user_data));
500 user_data->free_func = cpy_destroy_user_data;
502 register_function(buf, handler, user_data);
503 return PyString_FromString(buf);
506 static PyObject *cpy_register_read(PyObject *self, PyObject *args, PyObject *kwds) {
508 cpy_callback_t *c = NULL;
509 user_data_t *user_data = NULL;
511 const char *name = NULL;
512 PyObject *callback = NULL, *data = NULL;
514 static char *kwlist[] = {"callback", "interval", "data", "name", NULL};
516 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|dOz", kwlist, &callback, &interval, &data, &name) == 0) return NULL;
517 if (PyCallable_Check(callback) == 0) {
518 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
521 cpy_build_name(buf, sizeof(buf), callback, name, 0);
525 c = malloc(sizeof(*c));
526 c->name = strdup(buf);
527 c->callback = callback;
530 user_data = malloc(sizeof(*user_data));
531 user_data->free_func = cpy_destroy_user_data;
533 ts.tv_sec = interval;
534 ts.tv_nsec = (interval - ts.tv_sec) * 1000000000;
535 plugin_register_complex_read(buf, cpy_read_callback, &ts, user_data);
536 return PyString_FromString(buf);
539 static PyObject *cpy_register_log(PyObject *self, PyObject *args, PyObject *kwds) {
540 return cpy_register_generic_userdata(plugin_register_log, cpy_log_callback, args, kwds, 0);
543 static PyObject *cpy_register_write(PyObject *self, PyObject *args, PyObject *kwds) {
544 return cpy_register_generic_userdata(plugin_register_write, cpy_write_callback, args, kwds, 0);
547 static PyObject *cpy_register_notification(PyObject *self, PyObject *args, PyObject *kwds) {
548 return cpy_register_generic_userdata(plugin_register_notification, cpy_notification_callback, args, kwds, 0);
551 static PyObject *cpy_register_flush(PyObject *self, PyObject *args, PyObject *kwds) {
552 return cpy_register_generic_userdata(plugin_register_flush, cpy_flush_callback, args, kwds, 1);
555 static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args, PyObject *kwds) {
556 return cpy_register_generic(&cpy_shutdown_callbacks, args, kwds, 0);
559 static PyObject *cpy_error(PyObject *self, PyObject *args) {
561 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
562 Py_BEGIN_ALLOW_THREADS
563 plugin_log(LOG_ERR, "%s", text);
568 static PyObject *cpy_warning(PyObject *self, PyObject *args) {
570 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
571 Py_BEGIN_ALLOW_THREADS
572 plugin_log(LOG_WARNING, "%s", text);
577 static PyObject *cpy_notice(PyObject *self, PyObject *args) {
579 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
580 Py_BEGIN_ALLOW_THREADS
581 plugin_log(LOG_NOTICE, "%s", text);
586 static PyObject *cpy_info(PyObject *self, PyObject *args) {
588 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
589 Py_BEGIN_ALLOW_THREADS
590 plugin_log(LOG_INFO, "%s", text);
595 static PyObject *cpy_debug(PyObject *self, PyObject *args) {
598 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
599 plugin_log(LOG_DEBUG, "%s", text);
604 static PyObject *cpy_unregister_generic(cpy_callback_t **list_head, PyObject *arg, const char *desc, int short_name) {
607 cpy_callback_t *prev = NULL, *tmp;
609 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, short_name);
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, int short_name) {
643 if (PyString_Check(arg)) {
644 name = PyString_AsString(arg);
646 if (!PyCallable_Check(arg)) {
647 PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
650 cpy_build_name(buf, sizeof(buf), arg, NULL, short_name);
653 if (unreg(name) == 0)
655 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
659 static PyObject *cpy_unregister_log(PyObject *self, PyObject *arg) {
660 return cpy_unregister_generic_userdata(plugin_unregister_log, arg, "log", 0);
663 static PyObject *cpy_unregister_init(PyObject *self, PyObject *arg) {
664 return cpy_unregister_generic(&cpy_init_callbacks, arg, "init", 0);
667 static PyObject *cpy_unregister_config(PyObject *self, PyObject *arg) {
668 return cpy_unregister_generic(&cpy_config_callbacks, arg, "config", 1);
671 static PyObject *cpy_unregister_read(PyObject *self, PyObject *arg) {
672 return cpy_unregister_generic_userdata(plugin_unregister_read, arg, "read", 0);
675 static PyObject *cpy_unregister_write(PyObject *self, PyObject *arg) {
676 return cpy_unregister_generic_userdata(plugin_unregister_write, arg, "write", 0);
679 static PyObject *cpy_unregister_notification(PyObject *self, PyObject *arg) {
680 return cpy_unregister_generic_userdata(plugin_unregister_notification, arg, "notification", 0);
683 static PyObject *cpy_unregister_flush(PyObject *self, PyObject *arg) {
684 return cpy_unregister_generic_userdata(plugin_unregister_flush, arg, "flush", 1);
687 static PyObject *cpy_unregister_shutdown(PyObject *self, PyObject *arg) {
688 return cpy_unregister_generic(&cpy_shutdown_callbacks, arg, "shutdown", 0);
691 static PyMethodDef cpy_methods[] = {
692 {"debug", cpy_debug, METH_VARARGS, log_doc},
693 {"info", cpy_info, METH_VARARGS, log_doc},
694 {"notice", cpy_notice, METH_VARARGS, log_doc},
695 {"warning", cpy_warning, METH_VARARGS, log_doc},
696 {"error", cpy_error, METH_VARARGS, log_doc},
697 {"flush", (PyCFunction) cpy_flush, METH_VARARGS | METH_KEYWORDS, flush_doc},
698 {"register_log", (PyCFunction) cpy_register_log, METH_VARARGS | METH_KEYWORDS, reg_log_doc},
699 {"register_init", (PyCFunction) cpy_register_init, METH_VARARGS | METH_KEYWORDS, reg_init_doc},
700 {"register_config", (PyCFunction) cpy_register_config, METH_VARARGS | METH_KEYWORDS, reg_config_doc},
701 {"register_read", (PyCFunction) cpy_register_read, METH_VARARGS | METH_KEYWORDS, reg_read_doc},
702 {"register_write", (PyCFunction) cpy_register_write, METH_VARARGS | METH_KEYWORDS, reg_write_doc},
703 {"register_notification", (PyCFunction) cpy_register_notification, METH_VARARGS | METH_KEYWORDS, reg_notification_doc},
704 {"register_flush", (PyCFunction) cpy_register_flush, METH_VARARGS | METH_KEYWORDS, reg_flush_doc},
705 {"register_shutdown", (PyCFunction) cpy_register_shutdown, METH_VARARGS | METH_KEYWORDS, reg_shutdown_doc},
706 {"unregister_log", cpy_unregister_log, METH_O, unregister_doc},
707 {"unregister_init", cpy_unregister_init, METH_O, unregister_doc},
708 {"unregister_config", cpy_unregister_config, METH_O, unregister_doc},
709 {"unregister_read", cpy_unregister_read, METH_O, unregister_doc},
710 {"unregister_write", cpy_unregister_write, METH_O, unregister_doc},
711 {"unregister_notification", cpy_unregister_notification, METH_O, unregister_doc},
712 {"unregister_flush", cpy_unregister_flush, METH_O, unregister_doc},
713 {"unregister_shutdown", cpy_unregister_shutdown, METH_O, unregister_doc},
717 static int cpy_shutdown(void) {
721 /* This can happen if the module was loaded but not configured. */
723 PyEval_RestoreThread(state);
725 for (c = cpy_shutdown_callbacks; c; c = c->next) {
726 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
728 cpy_log_exception("shutdown callback");
736 static void *cpy_interactive(void *data) {
738 if (PyImport_ImportModule("readline") == NULL) {
739 /* This interactive session will suck. */
740 cpy_log_exception("interactive session init");
742 PyRun_InteractiveLoop(stdin, "<stdin>");
744 NOTICE("python: Interactive interpreter exited, stopping collectd ...");
749 static int cpy_init(void) {
752 static pthread_t thread;
754 PyEval_InitThreads();
755 /* Now it's finally OK to use python threads. */
756 for (c = cpy_init_callbacks; c; c = c->next) {
757 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
759 cpy_log_exception("init callback");
763 state = PyEval_SaveThread();
764 if (do_interactive) {
765 if (pthread_create(&thread, NULL, cpy_interactive, NULL)) {
766 ERROR("python: Error creating thread for interactive interpreter.");
773 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
775 PyObject *item, *values, *children, *tmp;
780 values = PyTuple_New(ci->values_num); /* New reference. */
781 for (i = 0; i < ci->values_num; ++i) {
782 if (ci->values[i].type == OCONFIG_TYPE_STRING) {
783 PyTuple_SET_ITEM(values, i, PyString_FromString(ci->values[i].value.string));
784 } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
785 PyTuple_SET_ITEM(values, i, PyFloat_FromDouble(ci->values[i].value.number));
786 } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
787 PyTuple_SET_ITEM(values, i, PyBool_FromLong(ci->values[i].value.boolean));
791 item = PyObject_CallFunction((PyObject *) &ConfigType, "sONO", ci->key, parent, values, Py_None);
794 children = PyTuple_New(ci->children_num); /* New reference. */
795 for (i = 0; i < ci->children_num; ++i) {
796 PyTuple_SET_ITEM(children, i, cpy_oconfig_to_pyconfig(ci->children + i, item));
798 tmp = ((Config *) item)->children;
799 ((Config *) item)->children = children;
804 static int cpy_config(oconfig_item_t *ci) {
810 /* Ok in theory we shouldn't do initialization at this point
811 * but we have to. In order to give python scripts a chance
812 * to register a config callback we need to be able to execute
813 * python code during the config callback so we have to start
814 * the interpreter here. */
815 /* Do *not* use the python "thread" module at this point! */
818 PyType_Ready(&ConfigType);
819 PyType_Ready(&PluginDataType);
820 ValuesType.tp_base = &PluginDataType;
821 PyType_Ready(&ValuesType);
822 NotificationType.tp_base = &PluginDataType;
823 PyType_Ready(&NotificationType);
824 sys = PyImport_ImportModule("sys"); /* New reference. */
826 cpy_log_exception("python initialization");
829 sys_path = PyObject_GetAttrString(sys, "path"); /* New reference. */
831 if (sys_path == NULL) {
832 cpy_log_exception("python initialization");
835 module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
836 PyModule_AddObject(module, "Config", (PyObject *) &ConfigType); /* Steals a reference. */
837 PyModule_AddObject(module, "Values", (PyObject *) &ValuesType); /* Steals a reference. */
838 PyModule_AddObject(module, "Notification", (PyObject *) &NotificationType); /* Steals a reference. */
839 PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG);
840 PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO);
841 PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE);
842 PyModule_AddIntConstant(module, "LOG_WARNING", LOG_WARNING);
843 PyModule_AddIntConstant(module, "LOG_ERROR", LOG_ERR);
844 PyModule_AddIntConstant(module, "NOTIF_FAILURE", NOTIF_FAILURE);
845 PyModule_AddIntConstant(module, "NOTIF_WARNING", NOTIF_WARNING);
846 PyModule_AddIntConstant(module, "NOTIF_OKAY", NOTIF_OKAY);
847 for (i = 0; i < ci->children_num; ++i) {
848 oconfig_item_t *item = ci->children + i;
850 if (strcasecmp(item->key, "Interactive") == 0) {
851 if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_BOOLEAN)
853 do_interactive = item->values[0].value.boolean;
854 } else if (strcasecmp(item->key, "LogTraces") == 0) {
855 if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_BOOLEAN)
857 if (!item->values[0].value.boolean) {
858 Py_XDECREF(cpy_format_exception);
859 cpy_format_exception = NULL;
862 if (cpy_format_exception)
864 tb = PyImport_ImportModule("traceback"); /* New reference. */
866 cpy_log_exception("python initialization");
869 cpy_format_exception = PyObject_GetAttrString(tb, "format_exception"); /* New reference. */
871 if (cpy_format_exception == NULL)
872 cpy_log_exception("python initialization");
873 } else if (strcasecmp(item->key, "ModulePath") == 0) {
875 PyObject *dir_object;
877 if (cf_util_get_string(item, &dir) != 0)
879 dir_object = PyString_FromString(dir); /* New reference. */
880 if (dir_object == NULL) {
881 ERROR("python plugin: Unable to convert \"%s\" to "
882 "a python object.", dir);
884 cpy_log_exception("python initialization");
887 if (PyList_Append(sys_path, dir_object) != 0) {
888 ERROR("python plugin: Unable to append \"%s\" to "
889 "python module path.", dir);
890 cpy_log_exception("python initialization");
892 Py_DECREF(dir_object);
894 } else if (strcasecmp(item->key, "Import") == 0) {
895 char *module_name = NULL;
898 if (cf_util_get_string(item, &module_name) != 0)
900 module = PyImport_ImportModule(module_name); /* New reference. */
901 if (module == NULL) {
902 ERROR("python plugin: Error importing module \"%s\".", module_name);
903 cpy_log_exception("importing module");
908 } else if (strcasecmp(item->key, "Module") == 0) {
913 if (cf_util_get_string(item, &name) != 0)
915 for (c = cpy_config_callbacks; c; c = c->next) {
916 if (strcasecmp(c->name + 7, name) == 0)
920 WARNING("python plugin: Found a configuration for the \"%s\" plugin, "
921 "but the plugin isn't loaded or didn't register "
922 "a configuration callback.", name);
928 ret = PyObject_CallFunction(c->callback, "N",
929 cpy_oconfig_to_pyconfig(item, NULL)); /* New reference. */
931 ret = PyObject_CallFunction(c->callback, "NO",
932 cpy_oconfig_to_pyconfig(item, NULL), c->data); /* New reference. */
934 cpy_log_exception("loading module");
938 WARNING("python plugin: Ignoring unknown config key \"%s\".", item->key);
945 void module_register(void) {
946 plugin_register_complex_config("python", cpy_config);
947 plugin_register_init("python", cpy_init);
948 plugin_register_shutdown("python", cpy_shutdown);