2 * collectd - src/python.c
3 * Copyright (C) 2009 Sven Trenkel
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
12 * The above copyright notice and this permission notice shall be included in
13 * all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
24 * Sven Trenkel <collectd at semidefinite.de>
28 #include <structmember.h>
40 typedef struct cpy_callback_s {
44 struct cpy_callback_s *next;
47 static char log_doc[] = "This function sends a string to all logging plugins.";
49 static char get_ds_doc[] = "get_dataset(name) -> definition\n"
51 "Returns the definition of a dataset specified by name.\n"
53 "'name' is a string specifying the dataset to query.\n"
54 "'definition' is a list of 4-tuples. Every tuple represents a \n"
55 " data source within the data set and its 4 values are the \n"
56 " name, type, min and max value.\n"
57 " 'name' is a string.\n"
58 " 'type' is a string that is equal to either DS_TYPE_COUNTER,\n"
59 " DS_TYPE_GAUGE, DS_TYPE_DERIVE or DS_TYPE_ABSOLUTE.\n"
60 " 'min' and 'max' are either a float or None.";
62 static char flush_doc[] = "flush([plugin][, timeout][, identifier]) -> None\n"
64 "Flushes the cache of another plugin.";
66 static char unregister_doc[] = "Unregisters a callback. This function needs exactly one parameter either\n"
67 "the function to unregister or the callback identifier to unregister.";
69 static char reg_log_doc[] = "register_log(callback[, data][, name]) -> identifier\n"
71 "Register a callback function for log messages.\n"
73 "'callback' is a callable object that will be called every time something\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>'.\n"
79 " Every callback needs a unique identifier, so if you want to\n"
80 " register this callback multiple time from the same module you need\n"
81 " to specify a name here.\n"
82 "'identifier' is the full identifier assigned to this callback.\n"
84 "The callback function will be called with two or three parameters:\n"
85 "severity: An integer that should be compared to the LOG_ constants.\n"
86 "message: The text to be logged.\n"
87 "data: The optional data parameter passed to the register function.\n"
88 " If the parameter was omitted it will be omitted here, too.";
90 static char reg_init_doc[] = "register_init(callback[, data][, name]) -> identifier\n"
92 "Register a callback function that will be executed once after the config.\n"
93 "file has been read, all plugins heve been loaded and the collectd has\n"
94 "forked into the background.\n"
96 "'callback' is a callable object that will be executed.\n"
97 "'data' is an optional object that will be passed back to the callback\n"
98 " function when it is called.\n"
99 "'name' is an optional identifier for this callback. The default name\n"
100 " is 'python.<module>'.\n"
101 " Every callback needs a unique identifier, so if you want to\n"
102 " register this callback multiple time from the same module you need\n"
103 " to specify a name here.\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_config_doc[] = "register_config(callback[, data][, name]) -> identifier\n"
111 "Register a callback function for config file entries.\n"
112 "'callback' is a callable object that will be called for every config block.\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 "config: A Config object.\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_read_doc[] = "register_read(callback[, interval][, data][, name]) -> identifier\n"
129 "Register a callback function for reading data. It will just be called\n"
130 "in a fixed interval to signal that it's time to dispatch new values.\n"
131 "'callback' is a callable object that will be called every time something\n"
133 "'interval' is the number of seconds between between calls to the callback\n"
134 " function. Full float precision is supported here.\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>'.\n"
139 " Every callback needs a unique identifier, so if you want to\n"
140 " register this callback multiple time from the same module you need\n"
141 " to specify a name here.\n"
142 "'identifier' is the full identifier assigned to this callback.\n"
144 "The callback function will be called without parameters, except for\n"
145 "data if it was supplied.";
147 static char reg_write_doc[] = "register_write(callback[, data][, name]) -> identifier\n"
149 "Register a callback function to receive values dispatched by other plugins.\n"
150 "'callback' is a callable object that will be called every time a value\n"
152 "'data' is an optional object that will be passed back to the callback\n"
153 " function every time it is called.\n"
154 "'name' is an optional identifier for this callback. The default name\n"
155 " is 'python.<module>'.\n"
156 " Every callback needs a unique identifier, so if you want to\n"
157 " register this callback multiple time from the same module you need\n"
158 " to specify a name here.\n"
159 "'identifier' is the full identifier assigned to this callback.\n"
161 "The callback function will be called with one or two parameters:\n"
162 "values: A Values object which is a copy of the dispatched values.\n"
163 "data: The optional data parameter passed to the register function.\n"
164 " If the parameter was omitted it will be omitted here, too.";
166 static char reg_notification_doc[] = "register_notification(callback[, data][, name]) -> identifier\n"
168 "Register a callback function for notifications.\n"
169 "'callback' is a callable object that will be called every time a notification\n"
171 "'data' is an optional object that will be passed back to the callback\n"
172 " function every time it is called.\n"
173 "'name' is an optional identifier for this callback. The default name\n"
174 " is 'python.<module>'.\n"
175 " Every callback needs a unique identifier, so if you want to\n"
176 " register this callback multiple time from the same module you need\n"
177 " to specify a name here.\n"
178 "'identifier' is the full identifier assigned to this callback.\n"
180 "The callback function will be called with one or two parameters:\n"
181 "notification: A copy of the notification that was dispatched.\n"
182 "data: The optional data parameter passed to the register function.\n"
183 " If the parameter was omitted it will be omitted here, too.";
185 static char reg_flush_doc[] = "register_flush(callback[, data][, name]) -> identifier\n"
187 "Register a callback function for flush messages.\n"
188 "'callback' is a callable object that will be called every time a plugin\n"
189 " requests a flush for either this or all plugins.\n"
190 "'data' is an optional object that will be passed back to the callback\n"
191 " function every time it is called.\n"
192 "'name' is an optional identifier for this callback. The default name\n"
193 " is 'python.<module>'.\n"
194 " Every callback needs a unique identifier, so if you want to\n"
195 " register this callback multiple time from the same module you need\n"
196 " to specify a name here.\n"
197 "'identifier' is the full identifier assigned to this callback.\n"
199 "The callback function will be called with two or three parameters:\n"
200 "timeout: Indicates that only data older than 'timeout' seconds is to\n"
202 "id: Specifies which values are to be flushed.\n"
203 "data: The optional data parameter passed to the register function.\n"
204 " If the parameter was omitted it will be omitted here, too.";
206 static char reg_shutdown_doc[] = "register_shutdown(callback[, data][, name]) -> identifier\n"
208 "Register a callback function for collectd shutdown.\n"
209 "'callback' is a callable object that will be called once collectd is\n"
211 "'data' is an optional object that will be passed back to the callback\n"
212 " function if it is called.\n"
213 "'name' is an optional identifier for this callback. The default name\n"
214 " is 'python.<module>'.\n"
215 " Every callback needs a unique identifier, so if you want to\n"
216 " register this callback multiple time from the same module you need\n"
217 " to specify a name here.\n"
218 "'identifier' is the full identifier assigned to this callback.\n"
220 "The callback function will be called with no parameters except for\n"
221 " data if it was supplied.";
224 static int do_interactive = 0;
226 /* This is our global thread state. Python saves some stuff in thread-local
227 * storage. So if we allow the interpreter to run in the background
228 * (the scriptwriters might have created some threads from python), we have
229 * to save the state so we can resume it later after shutdown. */
231 static PyThreadState *state;
233 static PyObject *sys_path, *cpy_format_exception;
235 static cpy_callback_t *cpy_config_callbacks;
236 static cpy_callback_t *cpy_init_callbacks;
237 static cpy_callback_t *cpy_shutdown_callbacks;
239 static void cpy_destroy_user_data(void *data) {
240 cpy_callback_t *c = data;
242 Py_DECREF(c->callback);
247 /* You must hold the GIL to call this function!
248 * But if you managed to extract the callback parameter then you probably already do. */
250 static void cpy_build_name(char *buf, size_t size, PyObject *callback, const char *name) {
251 const char *module = NULL;
252 PyObject *mod = NULL;
255 snprintf(buf, size, "python.%s", name);
259 mod = PyObject_GetAttrString(callback, "__module__"); /* New reference. */
261 module = cpy_unicode_or_bytes_to_string(&mod);
263 if (module != NULL) {
264 snprintf(buf, size, "python.%s", module);
271 snprintf(buf, size, "python.%p", callback);
275 void cpy_log_exception(const char *context) {
277 const char *typename = NULL, *message = NULL;
278 PyObject *type, *value, *traceback, *tn, *m, *list;
280 PyErr_Fetch(&type, &value, &traceback);
281 PyErr_NormalizeException(&type, &value, &traceback);
282 if (type == NULL) return;
283 tn = PyObject_GetAttrString(type, "__name__"); /* New reference. */
284 m = PyObject_Str(value); /* New reference. */
286 typename = cpy_unicode_or_bytes_to_string(&tn);
288 message = cpy_unicode_or_bytes_to_string(&m);
289 if (typename == NULL)
290 typename = "NamelessException";
293 Py_BEGIN_ALLOW_THREADS
294 ERROR("Unhandled python exception in %s: %s: %s", context, typename, message);
298 if (!cpy_format_exception || !traceback) {
302 Py_XDECREF(traceback);
305 list = PyObject_CallFunction(cpy_format_exception, "NNN", type, value, traceback); /* New reference. */
307 l = PyObject_Length(list);
308 for (i = 0; i < l; ++i) {
312 line = PyList_GET_ITEM(list, i); /* Borrowed reference. */
314 s = strdup(cpy_unicode_or_bytes_to_string(&line));
316 if (s[strlen(s) - 1] == '\n')
317 s[strlen(s) - 1] = 0;
318 Py_BEGIN_ALLOW_THREADS
327 Py_XDECREF(traceback);
330 static int cpy_read_callback(user_data_t *data) {
331 cpy_callback_t *c = data->data;
335 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
337 cpy_log_exception("read callback");
347 static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_list, user_data_t *data) {
349 cpy_callback_t *c = data->data;
350 PyObject *ret, *list, *temp, *dict = NULL;
354 list = PyList_New(value_list->values_len); /* New reference. */
356 cpy_log_exception("write callback");
357 CPY_RETURN_FROM_THREADS 0;
359 for (i = 0; i < value_list->values_len; ++i) {
360 if (ds->ds[i].type == DS_TYPE_COUNTER) {
361 PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].counter));
362 } else if (ds->ds[i].type == DS_TYPE_GAUGE) {
363 PyList_SetItem(list, i, PyFloat_FromDouble(value_list->values[i].gauge));
364 } else if (ds->ds[i].type == DS_TYPE_DERIVE) {
365 PyList_SetItem(list, i, PyLong_FromLongLong(value_list->values[i].derive));
366 } else if (ds->ds[i].type == DS_TYPE_ABSOLUTE) {
367 PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].absolute));
369 Py_BEGIN_ALLOW_THREADS
370 ERROR("cpy_write_callback: Unknown value type %d.", ds->ds[i].type);
373 CPY_RETURN_FROM_THREADS 0;
375 if (PyErr_Occurred() != NULL) {
376 cpy_log_exception("value building for write callback");
378 CPY_RETURN_FROM_THREADS 0;
381 dict = PyDict_New(); /* New reference. */
382 if (value_list->meta) {
385 meta_data_t *meta = value_list->meta;
387 num = meta_data_toc(meta, &table);
388 for (i = 0; i < num; ++i) {
396 type = meta_data_type(meta, table[i]);
397 if (type == MD_TYPE_STRING) {
398 if (meta_data_get_string(meta, table[i], &string))
400 temp = cpy_string_to_unicode_or_bytes(string); /* New reference. */
402 PyDict_SetItemString(dict, table[i], temp);
404 } else if (type == MD_TYPE_SIGNED_INT) {
405 if (meta_data_get_signed_int(meta, table[i], &si))
407 temp = PyObject_CallFunctionObjArgs((void *) &SignedType, PyLong_FromLongLong(si), (void *) 0); /* New reference. */
408 PyDict_SetItemString(dict, table[i], temp);
410 } else if (type == MD_TYPE_UNSIGNED_INT) {
411 if (meta_data_get_unsigned_int(meta, table[i], &ui))
413 temp = PyObject_CallFunctionObjArgs((void *) &UnsignedType, PyLong_FromUnsignedLongLong(ui), (void *) 0); /* New reference. */
414 PyDict_SetItemString(dict, table[i], temp);
416 } else if (type == MD_TYPE_DOUBLE) {
417 if (meta_data_get_double(meta, table[i], &d))
419 temp = PyFloat_FromDouble(d); /* New reference. */
420 PyDict_SetItemString(dict, table[i], temp);
422 } else if (type == MD_TYPE_BOOLEAN) {
423 if (meta_data_get_boolean(meta, table[i], &b))
426 PyDict_SetItemString(dict, table[i], Py_True);
428 PyDict_SetItemString(dict, table[i], Py_False);
434 v = (Values *) Values_New(); /* New reference. */
435 sstrncpy(v->data.host, value_list->host, sizeof(v->data.host));
436 sstrncpy(v->data.type, value_list->type, sizeof(v->data.type));
437 sstrncpy(v->data.type_instance, value_list->type_instance, sizeof(v->data.type_instance));
438 sstrncpy(v->data.plugin, value_list->plugin, sizeof(v->data.plugin));
439 sstrncpy(v->data.plugin_instance, value_list->plugin_instance, sizeof(v->data.plugin_instance));
440 v->data.time = CDTIME_T_TO_DOUBLE(value_list->time);
441 v->interval = CDTIME_T_TO_DOUBLE(value_list->interval);
445 v->meta = dict; /* Steals a reference. */
446 ret = PyObject_CallFunctionObjArgs(c->callback, v, c->data, (void *) 0); /* New reference. */
449 cpy_log_exception("write callback");
457 static int cpy_notification_callback(const notification_t *notification, user_data_t *data) {
458 cpy_callback_t *c = data->data;
459 PyObject *ret, *notify;
463 notify = Notification_New(); /* New reference. */
464 n = (Notification *) notify;
465 sstrncpy(n->data.host, notification->host, sizeof(n->data.host));
466 sstrncpy(n->data.type, notification->type, sizeof(n->data.type));
467 sstrncpy(n->data.type_instance, notification->type_instance, sizeof(n->data.type_instance));
468 sstrncpy(n->data.plugin, notification->plugin, sizeof(n->data.plugin));
469 sstrncpy(n->data.plugin_instance, notification->plugin_instance, sizeof(n->data.plugin_instance));
470 n->data.time = CDTIME_T_TO_DOUBLE(notification->time);
471 sstrncpy(n->message, notification->message, sizeof(n->message));
472 n->severity = notification->severity;
473 ret = PyObject_CallFunctionObjArgs(c->callback, n, c->data, (void *) 0); /* New reference. */
476 cpy_log_exception("notification callback");
484 static void cpy_log_callback(int severity, const char *message, user_data_t *data) {
485 cpy_callback_t * c = data->data;
486 PyObject *ret, *text;
489 text = cpy_string_to_unicode_or_bytes(message); /* New reference. */
491 ret = PyObject_CallFunction(c->callback, "iN", severity, text); /* New reference. Steals a reference from "text". */
493 ret = PyObject_CallFunction(c->callback, "iNO", severity, text, c->data); /* New reference. Steals a reference from "text". */
497 /* Do we really want to trigger a log callback because a log callback failed?
500 /* In case someone wanted to be clever, replaced stderr and failed at that. */
508 static void cpy_flush_callback(int timeout, const char *id, user_data_t *data) {
509 cpy_callback_t * c = data->data;
510 PyObject *ret, *text;
513 text = cpy_string_to_unicode_or_bytes(id);
515 ret = PyObject_CallFunction(c->callback, "iN", timeout, text); /* New reference. */
517 ret = PyObject_CallFunction(c->callback, "iNO", timeout, text, c->data); /* New reference. */
520 cpy_log_exception("flush callback");
527 static PyObject *cpy_register_generic(cpy_callback_t **list_head, PyObject *args, PyObject *kwds) {
531 PyObject *callback = NULL, *data = NULL, *mod = NULL;
532 static char *kwlist[] = {"callback", "data", "name", NULL};
534 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oet", kwlist, &callback, &data, NULL, &name) == 0) return NULL;
535 if (PyCallable_Check(callback) == 0) {
537 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
540 cpy_build_name(buf, sizeof(buf), callback, name);
544 c = malloc(sizeof(*c));
545 c->name = strdup(buf);
546 c->callback = callback;
548 c->next = *list_head;
552 return cpy_string_to_unicode_or_bytes(buf);
555 static PyObject *float_or_none(float number) {
559 return PyFloat_FromDouble(number);
562 static PyObject *cpy_get_dataset(PyObject *self, PyObject *args) {
565 const data_set_t *ds;
566 PyObject *list, *tuple;
568 if (PyArg_ParseTuple(args, "et", NULL, &name) == 0) return NULL;
569 ds = plugin_get_ds(name);
572 PyErr_Format(PyExc_TypeError, "Dataset %s not found", name);
575 list = PyList_New(ds->ds_num); /* New reference. */
576 for (i = 0; i < ds->ds_num; ++i) {
577 tuple = PyTuple_New(4);
578 PyTuple_SET_ITEM(tuple, 0, cpy_string_to_unicode_or_bytes(ds->ds[i].name));
579 PyTuple_SET_ITEM(tuple, 1, cpy_string_to_unicode_or_bytes(DS_TYPE_TO_STRING(ds->ds[i].type)));
580 PyTuple_SET_ITEM(tuple, 2, float_or_none(ds->ds[i].min));
581 PyTuple_SET_ITEM(tuple, 3, float_or_none(ds->ds[i].max));
582 PyList_SET_ITEM(list, i, tuple);
587 static PyObject *cpy_flush(PyObject *self, PyObject *args, PyObject *kwds) {
589 char *plugin = NULL, *identifier = NULL;
590 static char *kwlist[] = {"plugin", "timeout", "identifier", NULL};
592 if (PyArg_ParseTupleAndKeywords(args, kwds, "|etiet", kwlist, NULL, &plugin, &timeout, NULL, &identifier) == 0) return NULL;
593 Py_BEGIN_ALLOW_THREADS
594 plugin_flush(plugin, timeout, identifier);
597 PyMem_Free(identifier);
601 static PyObject *cpy_register_config(PyObject *self, PyObject *args, PyObject *kwds) {
602 return cpy_register_generic(&cpy_config_callbacks, args, kwds);
605 static PyObject *cpy_register_init(PyObject *self, PyObject *args, PyObject *kwds) {
606 return cpy_register_generic(&cpy_init_callbacks, args, kwds);
609 typedef int reg_function_t(const char *name, void *callback, void *data);
611 static PyObject *cpy_register_generic_userdata(void *reg, void *handler, PyObject *args, PyObject *kwds) {
613 reg_function_t *register_function = (reg_function_t *) reg;
614 cpy_callback_t *c = NULL;
615 user_data_t *user_data = NULL;
617 PyObject *callback = NULL, *data = NULL;
618 static char *kwlist[] = {"callback", "data", "name", NULL};
620 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oet", kwlist, &callback, &data, NULL, &name) == 0) return NULL;
621 if (PyCallable_Check(callback) == 0) {
623 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
626 cpy_build_name(buf, sizeof(buf), callback, name);
631 c = malloc(sizeof(*c));
632 c->name = strdup(buf);
633 c->callback = callback;
636 user_data = malloc(sizeof(*user_data));
637 user_data->free_func = cpy_destroy_user_data;
639 register_function(buf, handler, user_data);
640 return cpy_string_to_unicode_or_bytes(buf);
643 static PyObject *cpy_register_read(PyObject *self, PyObject *args, PyObject *kwds) {
645 cpy_callback_t *c = NULL;
646 user_data_t *user_data = NULL;
649 PyObject *callback = NULL, *data = NULL;
650 static char *kwlist[] = {"callback", "interval", "data", "name", NULL};
652 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|dOet", kwlist, &callback, &interval, &data, NULL, &name) == 0) return NULL;
653 if (PyCallable_Check(callback) == 0) {
655 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
658 cpy_build_name(buf, sizeof(buf), callback, name);
663 c = malloc(sizeof(*c));
664 c->name = strdup(buf);
665 c->callback = callback;
668 user_data = malloc(sizeof(*user_data));
669 user_data->free_func = cpy_destroy_user_data;
671 plugin_register_complex_read(/* group = */ NULL, buf,
672 cpy_read_callback, DOUBLE_TO_CDTIME_T (interval), user_data);
673 return cpy_string_to_unicode_or_bytes(buf);
676 static PyObject *cpy_register_log(PyObject *self, PyObject *args, PyObject *kwds) {
677 return cpy_register_generic_userdata((void *) plugin_register_log,
678 (void *) cpy_log_callback, args, kwds);
681 static PyObject *cpy_register_write(PyObject *self, PyObject *args, PyObject *kwds) {
682 return cpy_register_generic_userdata((void *) plugin_register_write,
683 (void *) cpy_write_callback, args, kwds);
686 static PyObject *cpy_register_notification(PyObject *self, PyObject *args, PyObject *kwds) {
687 return cpy_register_generic_userdata((void *) plugin_register_notification,
688 (void *) cpy_notification_callback, args, kwds);
691 static PyObject *cpy_register_flush(PyObject *self, PyObject *args, PyObject *kwds) {
692 return cpy_register_generic_userdata((void *) plugin_register_flush,
693 (void *) cpy_flush_callback, args, kwds);
696 static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args, PyObject *kwds) {
697 return cpy_register_generic(&cpy_shutdown_callbacks, args, kwds);
700 static PyObject *cpy_error(PyObject *self, PyObject *args) {
702 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
703 Py_BEGIN_ALLOW_THREADS
704 plugin_log(LOG_ERR, "%s", text);
710 static PyObject *cpy_warning(PyObject *self, PyObject *args) {
712 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
713 Py_BEGIN_ALLOW_THREADS
714 plugin_log(LOG_WARNING, "%s", text);
720 static PyObject *cpy_notice(PyObject *self, PyObject *args) {
722 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
723 Py_BEGIN_ALLOW_THREADS
724 plugin_log(LOG_NOTICE, "%s", text);
730 static PyObject *cpy_info(PyObject *self, PyObject *args) {
732 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
733 Py_BEGIN_ALLOW_THREADS
734 plugin_log(LOG_INFO, "%s", text);
740 static PyObject *cpy_debug(PyObject *self, PyObject *args) {
743 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
744 Py_BEGIN_ALLOW_THREADS
745 plugin_log(LOG_DEBUG, "%s", text);
752 static PyObject *cpy_unregister_generic(cpy_callback_t **list_head, PyObject *arg, const char *desc) {
755 cpy_callback_t *prev = NULL, *tmp;
758 name = cpy_unicode_or_bytes_to_string(&arg);
761 if (!PyCallable_Check(arg)) {
762 PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
766 cpy_build_name(buf, sizeof(buf), arg, NULL);
769 for (tmp = *list_head; tmp; prev = tmp, tmp = tmp->next)
770 if (strcmp(name, tmp->name) == 0)
775 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
778 /* Yes, this is actually save. To call this function the caller has to
779 * hold the GIL. Well, save as long as there is only one GIL anyway ... */
781 *list_head = tmp->next;
783 prev->next = tmp->next;
784 cpy_destroy_user_data(tmp);
788 typedef int cpy_unregister_function_t(const char *name);
790 static PyObject *cpy_unregister_generic_userdata(cpy_unregister_function_t *unreg, PyObject *arg, const char *desc) {
795 name = cpy_unicode_or_bytes_to_string(&arg);
798 if (!PyCallable_Check(arg)) {
799 PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
803 cpy_build_name(buf, sizeof(buf), arg, NULL);
806 if (unreg(name) == 0) {
810 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
815 static PyObject *cpy_unregister_log(PyObject *self, PyObject *arg) {
816 return cpy_unregister_generic_userdata(plugin_unregister_log, arg, "log");
819 static PyObject *cpy_unregister_init(PyObject *self, PyObject *arg) {
820 return cpy_unregister_generic(&cpy_init_callbacks, arg, "init");
823 static PyObject *cpy_unregister_config(PyObject *self, PyObject *arg) {
824 return cpy_unregister_generic(&cpy_config_callbacks, arg, "config");
827 static PyObject *cpy_unregister_read(PyObject *self, PyObject *arg) {
828 return cpy_unregister_generic_userdata(plugin_unregister_read, arg, "read");
831 static PyObject *cpy_unregister_write(PyObject *self, PyObject *arg) {
832 return cpy_unregister_generic_userdata(plugin_unregister_write, arg, "write");
835 static PyObject *cpy_unregister_notification(PyObject *self, PyObject *arg) {
836 return cpy_unregister_generic_userdata(plugin_unregister_notification, arg, "notification");
839 static PyObject *cpy_unregister_flush(PyObject *self, PyObject *arg) {
840 return cpy_unregister_generic_userdata(plugin_unregister_flush, arg, "flush");
843 static PyObject *cpy_unregister_shutdown(PyObject *self, PyObject *arg) {
844 return cpy_unregister_generic(&cpy_shutdown_callbacks, arg, "shutdown");
847 static PyMethodDef cpy_methods[] = {
848 {"debug", cpy_debug, METH_VARARGS, log_doc},
849 {"info", cpy_info, METH_VARARGS, log_doc},
850 {"notice", cpy_notice, METH_VARARGS, log_doc},
851 {"warning", cpy_warning, METH_VARARGS, log_doc},
852 {"error", cpy_error, METH_VARARGS, log_doc},
853 {"get_dataset", (PyCFunction) cpy_get_dataset, METH_VARARGS, get_ds_doc},
854 {"flush", (PyCFunction) cpy_flush, METH_VARARGS | METH_KEYWORDS, flush_doc},
855 {"register_log", (PyCFunction) cpy_register_log, METH_VARARGS | METH_KEYWORDS, reg_log_doc},
856 {"register_init", (PyCFunction) cpy_register_init, METH_VARARGS | METH_KEYWORDS, reg_init_doc},
857 {"register_config", (PyCFunction) cpy_register_config, METH_VARARGS | METH_KEYWORDS, reg_config_doc},
858 {"register_read", (PyCFunction) cpy_register_read, METH_VARARGS | METH_KEYWORDS, reg_read_doc},
859 {"register_write", (PyCFunction) cpy_register_write, METH_VARARGS | METH_KEYWORDS, reg_write_doc},
860 {"register_notification", (PyCFunction) cpy_register_notification, METH_VARARGS | METH_KEYWORDS, reg_notification_doc},
861 {"register_flush", (PyCFunction) cpy_register_flush, METH_VARARGS | METH_KEYWORDS, reg_flush_doc},
862 {"register_shutdown", (PyCFunction) cpy_register_shutdown, METH_VARARGS | METH_KEYWORDS, reg_shutdown_doc},
863 {"unregister_log", cpy_unregister_log, METH_O, unregister_doc},
864 {"unregister_init", cpy_unregister_init, METH_O, unregister_doc},
865 {"unregister_config", cpy_unregister_config, METH_O, unregister_doc},
866 {"unregister_read", cpy_unregister_read, METH_O, unregister_doc},
867 {"unregister_write", cpy_unregister_write, METH_O, unregister_doc},
868 {"unregister_notification", cpy_unregister_notification, METH_O, unregister_doc},
869 {"unregister_flush", cpy_unregister_flush, METH_O, unregister_doc},
870 {"unregister_shutdown", cpy_unregister_shutdown, METH_O, unregister_doc},
874 static int cpy_shutdown(void) {
878 /* This can happen if the module was loaded but not configured. */
880 PyEval_RestoreThread(state);
882 for (c = cpy_shutdown_callbacks; c; c = c->next) {
883 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
885 cpy_log_exception("shutdown callback");
894 static void cpy_int_handler(int sig) {
898 static void *cpy_interactive(void *data) {
900 struct sigaction sig_int_action, old;
902 /* Signal handler in a plugin? Bad stuff, but the best way to
903 * handle it I guess. In an interactive session people will
904 * press Ctrl+C at some time, which will generate a SIGINT.
905 * This will cause collectd to shutdown, thus killing the
906 * interactive interpreter, and leaving the terminal in a
907 * mess. Chances are, this isn't what the user wanted to do.
909 * So this is the plan:
910 * 1. Block SIGINT in the main thread.
911 * 2. Install our own signal handler that does nothing.
912 * 3. Unblock SIGINT in the interactive thread.
914 * This will make sure that SIGINT won't kill collectd but
915 * still interrupt syscalls like sleep and pause.
916 * It does not raise a KeyboardInterrupt exception because so
917 * far nobody managed to figure out how to do that. */
918 memset (&sig_int_action, '\0', sizeof (sig_int_action));
919 sig_int_action.sa_handler = cpy_int_handler;
920 sigaction (SIGINT, &sig_int_action, &old);
922 sigemptyset(&sigset);
923 sigaddset(&sigset, SIGINT);
924 pthread_sigmask(SIG_UNBLOCK, &sigset, NULL);
925 PyEval_AcquireThread(state);
926 if (PyImport_ImportModule("readline") == NULL) {
927 /* This interactive session will suck. */
928 cpy_log_exception("interactive session init");
930 PyRun_InteractiveLoop(stdin, "<stdin>");
932 PyEval_ReleaseThread(state);
933 NOTICE("python: Interactive interpreter exited, stopping collectd ...");
934 /* Restore the original collectd SIGINT handler and raise SIGINT.
935 * The main thread still has SIGINT blocked and there's nothing we
936 * can do about that so this thread will handle it. But that's not
937 * important, except that it won't interrupt the main loop and so
938 * it might take a few seconds before collectd really shuts down. */
939 sigaction (SIGINT, &old, NULL);
945 static int cpy_init(void) {
948 static pthread_t thread;
951 if (!Py_IsInitialized()) {
952 WARNING("python: Plugin loaded but not configured.");
953 plugin_unregister_shutdown("python");
956 PyEval_InitThreads();
957 /* Now it's finally OK to use python threads. */
958 for (c = cpy_init_callbacks; c; c = c->next) {
959 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
961 cpy_log_exception("init callback");
965 sigemptyset(&sigset);
966 sigaddset(&sigset, SIGINT);
967 pthread_sigmask(SIG_BLOCK, &sigset, NULL);
968 state = PyEval_SaveThread();
969 if (do_interactive) {
970 if (plugin_thread_create(&thread, NULL, cpy_interactive, NULL)) {
971 ERROR("python: Error creating thread for interactive interpreter.");
978 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
980 PyObject *item, *values, *children, *tmp;
985 values = PyTuple_New(ci->values_num); /* New reference. */
986 for (i = 0; i < ci->values_num; ++i) {
987 if (ci->values[i].type == OCONFIG_TYPE_STRING) {
988 PyTuple_SET_ITEM(values, i, cpy_string_to_unicode_or_bytes(ci->values[i].value.string));
989 } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
990 PyTuple_SET_ITEM(values, i, PyFloat_FromDouble(ci->values[i].value.number));
991 } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
992 PyTuple_SET_ITEM(values, i, PyBool_FromLong(ci->values[i].value.boolean));
996 tmp = cpy_string_to_unicode_or_bytes(ci->key);
997 item = PyObject_CallFunction((void *) &ConfigType, "NONO", tmp, parent, values, Py_None);
1000 children = PyTuple_New(ci->children_num); /* New reference. */
1001 for (i = 0; i < ci->children_num; ++i) {
1002 PyTuple_SET_ITEM(children, i, cpy_oconfig_to_pyconfig(ci->children + i, item));
1004 tmp = ((Config *) item)->children;
1005 ((Config *) item)->children = children;
1011 static struct PyModuleDef collectdmodule = {
1012 PyModuleDef_HEAD_INIT,
1013 "collectd", /* name of module */
1014 "The python interface to collectd", /* module documentation, may be NULL */
1019 PyMODINIT_FUNC PyInit_collectd(void) {
1020 return PyModule_Create(&collectdmodule);
1024 static int cpy_init_python() {
1029 wchar_t *argv = L"";
1030 /* Add a builtin module, before Py_Initialize */
1031 PyImport_AppendInittab("collectd", PyInit_collectd);
1038 PyType_Ready(&ConfigType);
1039 PyType_Ready(&PluginDataType);
1040 ValuesType.tp_base = &PluginDataType;
1041 PyType_Ready(&ValuesType);
1042 NotificationType.tp_base = &PluginDataType;
1043 PyType_Ready(&NotificationType);
1044 SignedType.tp_base = &PyLong_Type;
1045 PyType_Ready(&SignedType);
1046 UnsignedType.tp_base = &PyLong_Type;
1047 PyType_Ready(&UnsignedType);
1048 sys = PyImport_ImportModule("sys"); /* New reference. */
1050 cpy_log_exception("python initialization");
1053 sys_path = PyObject_GetAttrString(sys, "path"); /* New reference. */
1055 if (sys_path == NULL) {
1056 cpy_log_exception("python initialization");
1059 PySys_SetArgv(1, &argv);
1060 PyList_SetSlice(sys_path, 0, 1, NULL);
1063 module = PyImport_ImportModule("collectd");
1065 module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
1067 PyModule_AddObject(module, "Config", (void *) &ConfigType); /* Steals a reference. */
1068 PyModule_AddObject(module, "Values", (void *) &ValuesType); /* Steals a reference. */
1069 PyModule_AddObject(module, "Notification", (void *) &NotificationType); /* Steals a reference. */
1070 PyModule_AddObject(module, "Signed", (void *) &SignedType); /* Steals a reference. */
1071 PyModule_AddObject(module, "Unsigned", (void *) &UnsignedType); /* Steals a reference. */
1072 PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG);
1073 PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO);
1074 PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE);
1075 PyModule_AddIntConstant(module, "LOG_WARNING", LOG_WARNING);
1076 PyModule_AddIntConstant(module, "LOG_ERROR", LOG_ERR);
1077 PyModule_AddIntConstant(module, "NOTIF_FAILURE", NOTIF_FAILURE);
1078 PyModule_AddIntConstant(module, "NOTIF_WARNING", NOTIF_WARNING);
1079 PyModule_AddIntConstant(module, "NOTIF_OKAY", NOTIF_OKAY);
1080 PyModule_AddStringConstant(module, "DS_TYPE_COUNTER", DS_TYPE_TO_STRING(DS_TYPE_COUNTER));
1081 PyModule_AddStringConstant(module, "DS_TYPE_GAUGE", DS_TYPE_TO_STRING(DS_TYPE_GAUGE));
1082 PyModule_AddStringConstant(module, "DS_TYPE_DERIVE", DS_TYPE_TO_STRING(DS_TYPE_DERIVE));
1083 PyModule_AddStringConstant(module, "DS_TYPE_ABSOLUTE", DS_TYPE_TO_STRING(DS_TYPE_ABSOLUTE));
1087 static int cpy_config(oconfig_item_t *ci) {
1091 /* Ok in theory we shouldn't do initialization at this point
1092 * but we have to. In order to give python scripts a chance
1093 * to register a config callback we need to be able to execute
1094 * python code during the config callback so we have to start
1095 * the interpreter here. */
1096 /* Do *not* use the python "thread" module at this point! */
1098 if (!Py_IsInitialized() && cpy_init_python()) return 1;
1100 for (i = 0; i < ci->children_num; ++i) {
1101 oconfig_item_t *item = ci->children + i;
1103 if (strcasecmp(item->key, "Interactive") == 0) {
1104 if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_BOOLEAN)
1106 do_interactive = item->values[0].value.boolean;
1107 } else if (strcasecmp(item->key, "Encoding") == 0) {
1108 if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_STRING)
1111 NOTICE("python: \"Encoding\" was used in the config file but Python3 was used, which does not support changing encodings. Ignoring this.");
1113 /* Why is this even necessary? And undocumented? */
1114 if (PyUnicode_SetDefaultEncoding(item->values[0].value.string))
1115 cpy_log_exception("setting default encoding");
1117 } else if (strcasecmp(item->key, "LogTraces") == 0) {
1118 if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_BOOLEAN)
1120 if (!item->values[0].value.boolean) {
1121 Py_XDECREF(cpy_format_exception);
1122 cpy_format_exception = NULL;
1125 if (cpy_format_exception)
1127 tb = PyImport_ImportModule("traceback"); /* New reference. */
1129 cpy_log_exception("python initialization");
1132 cpy_format_exception = PyObject_GetAttrString(tb, "format_exception"); /* New reference. */
1134 if (cpy_format_exception == NULL)
1135 cpy_log_exception("python initialization");
1136 } else if (strcasecmp(item->key, "ModulePath") == 0) {
1138 PyObject *dir_object;
1140 if (cf_util_get_string(item, &dir) != 0)
1142 dir_object = cpy_string_to_unicode_or_bytes(dir); /* New reference. */
1143 if (dir_object == NULL) {
1144 ERROR("python plugin: Unable to convert \"%s\" to "
1145 "a python object.", dir);
1147 cpy_log_exception("python initialization");
1150 if (PyList_Insert(sys_path, 0, dir_object) != 0) {
1151 ERROR("python plugin: Unable to prepend \"%s\" to "
1152 "python module path.", dir);
1153 cpy_log_exception("python initialization");
1155 Py_DECREF(dir_object);
1157 } else if (strcasecmp(item->key, "Import") == 0) {
1158 char *module_name = NULL;
1161 if (cf_util_get_string(item, &module_name) != 0)
1163 module = PyImport_ImportModule(module_name); /* New reference. */
1164 if (module == NULL) {
1165 ERROR("python plugin: Error importing module \"%s\".", module_name);
1166 cpy_log_exception("importing module");
1170 } else if (strcasecmp(item->key, "Module") == 0) {
1175 if (cf_util_get_string(item, &name) != 0)
1177 for (c = cpy_config_callbacks; c; c = c->next) {
1178 if (strcasecmp(c->name + 7, name) == 0)
1182 WARNING("python plugin: Found a configuration for the \"%s\" plugin, "
1183 "but the plugin isn't loaded or didn't register "
1184 "a configuration callback.", name);
1189 if (c->data == NULL)
1190 ret = PyObject_CallFunction(c->callback, "N",
1191 cpy_oconfig_to_pyconfig(item, NULL)); /* New reference. */
1193 ret = PyObject_CallFunction(c->callback, "NO",
1194 cpy_oconfig_to_pyconfig(item, NULL), c->data); /* New reference. */
1196 cpy_log_exception("loading module");
1200 WARNING("python plugin: Ignoring unknown config key \"%s\".", item->key);
1206 void module_register(void) {
1207 plugin_register_complex_config("python", cpy_config);
1208 plugin_register_init("python", cpy_init);
1209 plugin_register_shutdown("python", cpy_shutdown);