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>
38 typedef struct cpy_callback_s {
42 struct cpy_callback_s *next;
45 static char log_doc[] = "This function sends a string to all logging plugins.";
47 static char get_ds_doc[] = "get_dataset(name) -> definition\n"
49 "Returns the definition of a dataset specified by name.\n"
51 "'name' is a string specifying the dataset to query.\n"
52 "'definition' is a list of 4-tuples. Every tuple represents a \n"
53 " data source within the data set and its 4 values are the \n"
54 " name, type, min and max value.\n"
55 " 'name' is a string.\n"
56 " 'type' is a string that is equal to either DS_TYPE_COUNTER,\n"
57 " DS_TYPE_GAUGE, DS_TYPE_DERIVE or DS_TYPE_ABSOLUTE.\n"
58 " 'min' and 'max' are either a float or None.";
60 static char flush_doc[] = "flush([plugin][, timeout][, identifier]) -> None\n"
62 "Flushes the cache of another plugin.";
64 static char unregister_doc[] = "Unregisters a callback. This function needs exactly one parameter either\n"
65 "the function to unregister or the callback identifier to unregister.";
67 static char reg_log_doc[] = "register_log(callback[, data][, name]) -> identifier\n"
69 "Register a callback function for log messages.\n"
71 "'callback' is a callable object that will be called every time something\n"
73 "'data' is an optional object that will be passed back to the callback\n"
74 " function every time it is called.\n"
75 "'name' is an optional identifier for this callback. The default name\n"
76 " is 'python.<module>'.\n"
77 " Every callback needs a unique identifier, so if you want to\n"
78 " register this callback multiple time from the same module you need\n"
79 " to specify a name here.\n"
80 "'identifier' is the full identifier assigned to this callback.\n"
82 "The callback function will be called with two or three parameters:\n"
83 "severity: An integer that should be compared to the LOG_ constants.\n"
84 "message: The text to be logged.\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_init_doc[] = "register_init(callback[, data][, name]) -> identifier\n"
90 "Register a callback function that will be executed once after the config.\n"
91 "file has been read, all plugins heve been loaded and the collectd has\n"
92 "forked into the background.\n"
94 "'callback' is a callable object that will be executed.\n"
95 "'data' is an optional object that will be passed back to the callback\n"
96 " function when it is called.\n"
97 "'name' is an optional identifier for this callback. The default name\n"
98 " is 'python.<module>'.\n"
99 " Every callback needs a unique identifier, so if you want to\n"
100 " register this callback multiple time from the same module you need\n"
101 " to specify a name here.\n"
102 "'identifier' is the full identifier assigned to this callback.\n"
104 "The callback function will be called without parameters, except for\n"
105 "data if it was supplied.";
107 static char reg_config_doc[] = "register_config(callback[, data][, name]) -> identifier\n"
109 "Register a callback function for config file entries.\n"
110 "'callback' is a callable object that will be called for every config block.\n"
111 "'data' is an optional object that will be passed back to the callback\n"
112 " function every time it is called.\n"
113 "'name' is an optional identifier for this callback. The default name\n"
114 " is 'python.<module>'.\n"
115 " Every callback needs a unique identifier, so if you want to\n"
116 " register this callback multiple time from the same module you need\n"
117 " to specify a name here.\n"
118 "'identifier' is the full identifier assigned to this callback.\n"
120 "The callback function will be called with one or two parameters:\n"
121 "config: A Config object.\n"
122 "data: The optional data parameter passed to the register function.\n"
123 " If the parameter was omitted it will be omitted here, too.";
125 static char reg_read_doc[] = "register_read(callback[, interval][, data][, name]) -> identifier\n"
127 "Register a callback function for reading data. It will just be called\n"
128 "in a fixed interval to signal that it's time to dispatch new values.\n"
129 "'callback' is a callable object that will be called every time something\n"
131 "'interval' is the number of seconds between between calls to the callback\n"
132 " function. Full float precision is supported here.\n"
133 "'data' is an optional object that will be passed back to the callback\n"
134 " function every time it is called.\n"
135 "'name' is an optional identifier for this callback. The default name\n"
136 " is 'python.<module>'.\n"
137 " Every callback needs a unique identifier, so if you want to\n"
138 " register this callback multiple time from the same module you need\n"
139 " to specify a name here.\n"
140 "'identifier' is the full identifier assigned to this callback.\n"
142 "The callback function will be called without parameters, except for\n"
143 "data if it was supplied.";
145 static char reg_write_doc[] = "register_write(callback[, data][, name]) -> identifier\n"
147 "Register a callback function to receive values dispatched by other plugins.\n"
148 "'callback' is a callable object that will be called every time a value\n"
150 "'data' is an optional object that will be passed back to the callback\n"
151 " function every time it is called.\n"
152 "'name' is an optional identifier for this callback. The default name\n"
153 " is 'python.<module>'.\n"
154 " Every callback needs a unique identifier, so if you want to\n"
155 " register this callback multiple time from the same module you need\n"
156 " to specify a name here.\n"
157 "'identifier' is the full identifier assigned to this callback.\n"
159 "The callback function will be called with one or two parameters:\n"
160 "values: A Values object which is a copy of the dispatched values.\n"
161 "data: The optional data parameter passed to the register function.\n"
162 " If the parameter was omitted it will be omitted here, too.";
164 static char reg_notification_doc[] = "register_notification(callback[, data][, name]) -> identifier\n"
166 "Register a callback function for notifications.\n"
167 "'callback' is a callable object that will be called every time a notification\n"
169 "'data' is an optional object that will be passed back to the callback\n"
170 " function every time it is called.\n"
171 "'name' is an optional identifier for this callback. The default name\n"
172 " is 'python.<module>'.\n"
173 " Every callback needs a unique identifier, so if you want to\n"
174 " register this callback multiple time from the same module you need\n"
175 " to specify a name here.\n"
176 "'identifier' is the full identifier assigned to this callback.\n"
178 "The callback function will be called with one or two parameters:\n"
179 "notification: A copy of the notification that was dispatched.\n"
180 "data: The optional data parameter passed to the register function.\n"
181 " If the parameter was omitted it will be omitted here, too.";
183 static char reg_flush_doc[] = "register_flush(callback[, data][, name]) -> identifier\n"
185 "Register a callback function for flush messages.\n"
186 "'callback' is a callable object that will be called every time a plugin\n"
187 " requests a flush for either this or all plugins.\n"
188 "'data' is an optional object that will be passed back to the callback\n"
189 " function every time it is called.\n"
190 "'name' is an optional identifier for this callback. The default name\n"
191 " is 'python.<module>'.\n"
192 " Every callback needs a unique identifier, so if you want to\n"
193 " register this callback multiple time from the same module you need\n"
194 " to specify a name here.\n"
195 "'identifier' is the full identifier assigned to this callback.\n"
197 "The callback function will be called with two or three parameters:\n"
198 "timeout: Indicates that only data older than 'timeout' seconds is to\n"
200 "id: Specifies which values are to be flushed. Might be None.\n"
201 "data: The optional data parameter passed to the register function.\n"
202 " If the parameter was omitted it will be omitted here, too.";
204 static char reg_shutdown_doc[] = "register_shutdown(callback[, data][, name]) -> identifier\n"
206 "Register a callback function for collectd shutdown.\n"
207 "'callback' is a callable object that will be called once collectd is\n"
209 "'data' is an optional object that will be passed back to the callback\n"
210 " function if it is called.\n"
211 "'name' is an optional identifier for this callback. The default name\n"
212 " is 'python.<module>'.\n"
213 " Every callback needs a unique identifier, so if you want to\n"
214 " register this callback multiple time from the same module you need\n"
215 " to specify a name here.\n"
216 "'identifier' is the full identifier assigned to this callback.\n"
218 "The callback function will be called with no parameters except for\n"
219 " data if it was supplied.";
222 static pthread_t main_thread;
223 static PyOS_sighandler_t python_sigint_handler;
224 static _Bool 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 /* Make sure to hold the GIL while modifying these. */
240 static int cpy_shutdown_triggered = 0;
241 static int cpy_num_callbacks = 0;
243 static void cpy_destroy_user_data(void *data) {
244 cpy_callback_t *c = data;
247 Py_DECREF(c->callback);
251 if (!cpy_num_callbacks && cpy_shutdown_triggered) {
258 /* You must hold the GIL to call this function!
259 * But if you managed to extract the callback parameter then you probably already do. */
261 static void cpy_build_name(char *buf, size_t size, PyObject *callback, const char *name) {
262 const char *module = NULL;
263 PyObject *mod = NULL;
266 snprintf(buf, size, "python.%s", name);
270 mod = PyObject_GetAttrString(callback, "__module__"); /* New reference. */
272 module = cpy_unicode_or_bytes_to_string(&mod);
274 if (module != NULL) {
275 snprintf(buf, size, "python.%s", module);
282 snprintf(buf, size, "python.%p", callback);
286 void cpy_log_exception(const char *context) {
288 const char *typename = NULL, *message = NULL;
289 PyObject *type, *value, *traceback, *tn, *m, *list;
291 PyErr_Fetch(&type, &value, &traceback);
292 PyErr_NormalizeException(&type, &value, &traceback);
293 if (type == NULL) return;
294 tn = PyObject_GetAttrString(type, "__name__"); /* New reference. */
295 m = PyObject_Str(value); /* New reference. */
297 typename = cpy_unicode_or_bytes_to_string(&tn);
299 message = cpy_unicode_or_bytes_to_string(&m);
300 if (typename == NULL)
301 typename = "NamelessException";
304 Py_BEGIN_ALLOW_THREADS
305 ERROR("Unhandled python exception in %s: %s: %s", context, typename, message);
309 if (!cpy_format_exception || !traceback) {
313 Py_XDECREF(traceback);
316 list = PyObject_CallFunction(cpy_format_exception, "NNN", type, value, traceback); /* New reference. Steals references from "type", "value" and "traceback". */
318 l = PyObject_Length(list);
320 for (int i = 0; i < l; ++i) {
325 line = PyList_GET_ITEM(list, i); /* Borrowed reference. */
328 msg = cpy_unicode_or_bytes_to_string(&line);
337 if (cpy[strlen(cpy) - 1] == '\n')
338 cpy[strlen(cpy) - 1] = 0;
340 Py_BEGIN_ALLOW_THREADS
351 static int cpy_read_callback(user_data_t *data) {
352 cpy_callback_t *c = data->data;
356 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
358 cpy_log_exception("read callback");
368 static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_list, user_data_t *data) {
369 cpy_callback_t *c = data->data;
370 PyObject *ret, *list, *temp, *dict = NULL;
374 list = PyList_New(value_list->values_len); /* New reference. */
376 cpy_log_exception("write callback");
377 CPY_RETURN_FROM_THREADS 0;
379 for (size_t i = 0; i < value_list->values_len; ++i) {
380 if (ds->ds[i].type == DS_TYPE_COUNTER) {
381 PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].counter));
382 } else if (ds->ds[i].type == DS_TYPE_GAUGE) {
383 PyList_SetItem(list, i, PyFloat_FromDouble(value_list->values[i].gauge));
384 } else if (ds->ds[i].type == DS_TYPE_DERIVE) {
385 PyList_SetItem(list, i, PyLong_FromLongLong(value_list->values[i].derive));
386 } else if (ds->ds[i].type == DS_TYPE_ABSOLUTE) {
387 PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].absolute));
389 Py_BEGIN_ALLOW_THREADS
390 ERROR("cpy_write_callback: Unknown value type %d.", ds->ds[i].type);
393 CPY_RETURN_FROM_THREADS 0;
395 if (PyErr_Occurred() != NULL) {
396 cpy_log_exception("value building for write callback");
398 CPY_RETURN_FROM_THREADS 0;
401 dict = PyDict_New(); /* New reference. */
402 if (value_list->meta) {
404 meta_data_t *meta = value_list->meta;
406 int num = meta_data_toc(meta, &table);
407 for (int i = 0; i < num; ++i) {
415 type = meta_data_type(meta, table[i]);
416 if (type == MD_TYPE_STRING) {
417 if (meta_data_get_string(meta, table[i], &string))
419 temp = cpy_string_to_unicode_or_bytes(string); /* New reference. */
421 PyDict_SetItemString(dict, table[i], temp);
423 } else if (type == MD_TYPE_SIGNED_INT) {
424 if (meta_data_get_signed_int(meta, table[i], &si))
426 temp = PyObject_CallFunctionObjArgs((void *) &SignedType, PyLong_FromLongLong(si), (void *) 0); /* New reference. */
427 PyDict_SetItemString(dict, table[i], temp);
429 } else if (type == MD_TYPE_UNSIGNED_INT) {
430 if (meta_data_get_unsigned_int(meta, table[i], &ui))
432 temp = PyObject_CallFunctionObjArgs((void *) &UnsignedType, PyLong_FromUnsignedLongLong(ui), (void *) 0); /* New reference. */
433 PyDict_SetItemString(dict, table[i], temp);
435 } else if (type == MD_TYPE_DOUBLE) {
436 if (meta_data_get_double(meta, table[i], &d))
438 temp = PyFloat_FromDouble(d); /* New reference. */
439 PyDict_SetItemString(dict, table[i], temp);
441 } else if (type == MD_TYPE_BOOLEAN) {
442 if (meta_data_get_boolean(meta, table[i], &b))
445 PyDict_SetItemString(dict, table[i], Py_True);
447 PyDict_SetItemString(dict, table[i], Py_False);
453 v = (Values *) Values_New(); /* New reference. */
454 sstrncpy(v->data.host, value_list->host, sizeof(v->data.host));
455 sstrncpy(v->data.type, value_list->type, sizeof(v->data.type));
456 sstrncpy(v->data.type_instance, value_list->type_instance, sizeof(v->data.type_instance));
457 sstrncpy(v->data.plugin, value_list->plugin, sizeof(v->data.plugin));
458 sstrncpy(v->data.plugin_instance, value_list->plugin_instance, sizeof(v->data.plugin_instance));
459 v->data.time = CDTIME_T_TO_DOUBLE(value_list->time);
460 v->interval = CDTIME_T_TO_DOUBLE(value_list->interval);
464 v->meta = dict; /* Steals a reference. */
465 ret = PyObject_CallFunctionObjArgs(c->callback, v, c->data, (void *) 0); /* New reference. */
468 cpy_log_exception("write callback");
476 static int cpy_notification_callback(const notification_t *notification, user_data_t *data) {
477 cpy_callback_t *c = data->data;
478 PyObject *ret, *notify;
482 notify = Notification_New(); /* New reference. */
483 n = (Notification *) notify;
484 sstrncpy(n->data.host, notification->host, sizeof(n->data.host));
485 sstrncpy(n->data.type, notification->type, sizeof(n->data.type));
486 sstrncpy(n->data.type_instance, notification->type_instance, sizeof(n->data.type_instance));
487 sstrncpy(n->data.plugin, notification->plugin, sizeof(n->data.plugin));
488 sstrncpy(n->data.plugin_instance, notification->plugin_instance, sizeof(n->data.plugin_instance));
489 n->data.time = CDTIME_T_TO_DOUBLE(notification->time);
490 sstrncpy(n->message, notification->message, sizeof(n->message));
491 n->severity = notification->severity;
492 ret = PyObject_CallFunctionObjArgs(c->callback, n, c->data, (void *) 0); /* New reference. */
495 cpy_log_exception("notification callback");
503 static void cpy_log_callback(int severity, const char *message, user_data_t *data) {
504 cpy_callback_t * c = data->data;
505 PyObject *ret, *text;
508 text = cpy_string_to_unicode_or_bytes(message); /* New reference. */
510 ret = PyObject_CallFunction(c->callback, "iN", severity, text); /* New reference. Steals a reference from "text". */
512 ret = PyObject_CallFunction(c->callback, "iNO", severity, text, c->data); /* New reference. Steals a reference from "text". */
516 /* Do we really want to trigger a log callback because a log callback failed?
519 /* In case someone wanted to be clever, replaced stderr and failed at that. */
527 static void cpy_flush_callback(int timeout, const char *id, user_data_t *data) {
528 cpy_callback_t * c = data->data;
529 PyObject *ret, *text;
533 text = cpy_string_to_unicode_or_bytes(id);
539 ret = PyObject_CallFunction(c->callback, "iN", timeout, text); /* New reference. */
541 ret = PyObject_CallFunction(c->callback, "iNO", timeout, text, c->data); /* New reference. */
544 cpy_log_exception("flush callback");
551 static PyObject *cpy_register_generic(cpy_callback_t **list_head, PyObject *args, PyObject *kwds) {
555 PyObject *callback = NULL, *data = NULL, *mod = NULL;
556 static char *kwlist[] = {"callback", "data", "name", NULL};
558 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oet", kwlist, &callback, &data, NULL, &name) == 0) return NULL;
559 if (PyCallable_Check(callback) == 0) {
561 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
564 cpy_build_name(buf, sizeof(buf), callback, name);
569 c = calloc(1, sizeof(*c));
573 c->name = strdup(buf);
574 c->callback = callback;
576 c->next = *list_head;
581 return cpy_string_to_unicode_or_bytes(buf);
584 static PyObject *float_or_none(float number) {
588 return PyFloat_FromDouble(number);
591 static PyObject *cpy_get_dataset(PyObject *self, PyObject *args) {
593 const data_set_t *ds;
594 PyObject *list, *tuple;
596 if (PyArg_ParseTuple(args, "et", NULL, &name) == 0) return NULL;
597 ds = plugin_get_ds(name);
600 PyErr_Format(PyExc_TypeError, "Dataset %s not found", name);
603 list = PyList_New(ds->ds_num); /* New reference. */
604 for (size_t i = 0; i < ds->ds_num; ++i) {
605 tuple = PyTuple_New(4);
606 PyTuple_SET_ITEM(tuple, 0, cpy_string_to_unicode_or_bytes(ds->ds[i].name));
607 PyTuple_SET_ITEM(tuple, 1, cpy_string_to_unicode_or_bytes(DS_TYPE_TO_STRING(ds->ds[i].type)));
608 PyTuple_SET_ITEM(tuple, 2, float_or_none(ds->ds[i].min));
609 PyTuple_SET_ITEM(tuple, 3, float_or_none(ds->ds[i].max));
610 PyList_SET_ITEM(list, i, tuple);
615 static PyObject *cpy_flush(PyObject *self, PyObject *args, PyObject *kwds) {
617 char *plugin = NULL, *identifier = NULL;
618 static char *kwlist[] = {"plugin", "timeout", "identifier", NULL};
620 if (PyArg_ParseTupleAndKeywords(args, kwds, "|etiet", kwlist, NULL, &plugin, &timeout, NULL, &identifier) == 0) return NULL;
621 Py_BEGIN_ALLOW_THREADS
622 plugin_flush(plugin, timeout, identifier);
625 PyMem_Free(identifier);
629 static PyObject *cpy_register_config(PyObject *self, PyObject *args, PyObject *kwds) {
630 return cpy_register_generic(&cpy_config_callbacks, args, kwds);
633 static PyObject *cpy_register_init(PyObject *self, PyObject *args, PyObject *kwds) {
634 return cpy_register_generic(&cpy_init_callbacks, args, kwds);
637 typedef int reg_function_t(const char *name, void *callback, void *data);
639 static PyObject *cpy_register_generic_userdata(void *reg, void *handler, PyObject *args, PyObject *kwds) {
641 reg_function_t *register_function = (reg_function_t *) reg;
642 cpy_callback_t *c = NULL;
644 PyObject *callback = NULL, *data = NULL;
645 static char *kwlist[] = {"callback", "data", "name", NULL};
647 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oet", kwlist, &callback, &data, NULL, &name) == 0) return NULL;
648 if (PyCallable_Check(callback) == 0) {
650 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
653 cpy_build_name(buf, sizeof(buf), callback, name);
659 c = calloc(1, sizeof(*c));
663 c->name = strdup(buf);
664 c->callback = callback;
668 user_data_t user_data = {
670 .free_func = cpy_destroy_user_data
673 register_function(buf, handler, &user_data);
675 return cpy_string_to_unicode_or_bytes(buf);
678 static PyObject *cpy_register_read(PyObject *self, PyObject *args, PyObject *kwds) {
680 cpy_callback_t *c = NULL;
683 PyObject *callback = NULL, *data = NULL;
684 static char *kwlist[] = {"callback", "interval", "data", "name", NULL};
686 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|dOet", kwlist, &callback, &interval, &data, NULL, &name) == 0) return NULL;
687 if (PyCallable_Check(callback) == 0) {
689 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
692 cpy_build_name(buf, sizeof(buf), callback, name);
698 c = calloc(1, sizeof(*c));
702 c->name = strdup(buf);
703 c->callback = callback;
707 user_data_t user_data = {
709 .free_func = cpy_destroy_user_data
712 plugin_register_complex_read(/* group = */ "python", buf,
713 cpy_read_callback, DOUBLE_TO_CDTIME_T (interval), &user_data);
715 return cpy_string_to_unicode_or_bytes(buf);
718 static PyObject *cpy_register_log(PyObject *self, PyObject *args, PyObject *kwds) {
719 return cpy_register_generic_userdata((void *) plugin_register_log,
720 (void *) cpy_log_callback, args, kwds);
723 static PyObject *cpy_register_write(PyObject *self, PyObject *args, PyObject *kwds) {
724 return cpy_register_generic_userdata((void *) plugin_register_write,
725 (void *) cpy_write_callback, args, kwds);
728 static PyObject *cpy_register_notification(PyObject *self, PyObject *args, PyObject *kwds) {
729 return cpy_register_generic_userdata((void *) plugin_register_notification,
730 (void *) cpy_notification_callback, args, kwds);
733 static PyObject *cpy_register_flush(PyObject *self, PyObject *args, PyObject *kwds) {
734 return cpy_register_generic_userdata((void *) plugin_register_flush,
735 (void *) cpy_flush_callback, args, kwds);
738 static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args, PyObject *kwds) {
739 return cpy_register_generic(&cpy_shutdown_callbacks, args, kwds);
742 static PyObject *cpy_error(PyObject *self, PyObject *args) {
744 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
745 Py_BEGIN_ALLOW_THREADS
746 plugin_log(LOG_ERR, "%s", text);
752 static PyObject *cpy_warning(PyObject *self, PyObject *args) {
754 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
755 Py_BEGIN_ALLOW_THREADS
756 plugin_log(LOG_WARNING, "%s", text);
762 static PyObject *cpy_notice(PyObject *self, PyObject *args) {
764 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
765 Py_BEGIN_ALLOW_THREADS
766 plugin_log(LOG_NOTICE, "%s", text);
772 static PyObject *cpy_info(PyObject *self, PyObject *args) {
774 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
775 Py_BEGIN_ALLOW_THREADS
776 plugin_log(LOG_INFO, "%s", text);
782 static PyObject *cpy_debug(PyObject *self, PyObject *args) {
785 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
786 Py_BEGIN_ALLOW_THREADS
787 plugin_log(LOG_DEBUG, "%s", text);
794 static PyObject *cpy_unregister_generic(cpy_callback_t **list_head, PyObject *arg, const char *desc) {
797 cpy_callback_t *prev = NULL, *tmp;
800 name = cpy_unicode_or_bytes_to_string(&arg);
803 if (!PyCallable_Check(arg)) {
804 PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
808 cpy_build_name(buf, sizeof(buf), arg, NULL);
811 for (tmp = *list_head; tmp; prev = tmp, tmp = tmp->next)
812 if (strcmp(name, tmp->name) == 0)
817 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
820 /* Yes, this is actually safe. To call this function the caller has to
821 * hold the GIL. Well, safe as long as there is only one GIL anyway ... */
823 *list_head = tmp->next;
825 prev->next = tmp->next;
826 cpy_destroy_user_data(tmp);
830 static void cpy_unregister_list(cpy_callback_t **list_head) {
831 cpy_callback_t *cur, *next;
832 for (cur = *list_head; cur; cur = next) {
834 cpy_destroy_user_data(cur);
839 typedef int cpy_unregister_function_t(const char *name);
841 static PyObject *cpy_unregister_generic_userdata(cpy_unregister_function_t *unreg, PyObject *arg, const char *desc) {
846 name = cpy_unicode_or_bytes_to_string(&arg);
849 if (!PyCallable_Check(arg)) {
850 PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
854 cpy_build_name(buf, sizeof(buf), arg, NULL);
857 if (unreg(name) == 0) {
861 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
866 static PyObject *cpy_unregister_log(PyObject *self, PyObject *arg) {
867 return cpy_unregister_generic_userdata(plugin_unregister_log, arg, "log");
870 static PyObject *cpy_unregister_init(PyObject *self, PyObject *arg) {
871 return cpy_unregister_generic(&cpy_init_callbacks, arg, "init");
874 static PyObject *cpy_unregister_config(PyObject *self, PyObject *arg) {
875 return cpy_unregister_generic(&cpy_config_callbacks, arg, "config");
878 static PyObject *cpy_unregister_read(PyObject *self, PyObject *arg) {
879 return cpy_unregister_generic_userdata(plugin_unregister_read, arg, "read");
882 static PyObject *cpy_unregister_write(PyObject *self, PyObject *arg) {
883 return cpy_unregister_generic_userdata(plugin_unregister_write, arg, "write");
886 static PyObject *cpy_unregister_notification(PyObject *self, PyObject *arg) {
887 return cpy_unregister_generic_userdata(plugin_unregister_notification, arg, "notification");
890 static PyObject *cpy_unregister_flush(PyObject *self, PyObject *arg) {
891 return cpy_unregister_generic_userdata(plugin_unregister_flush, arg, "flush");
894 static PyObject *cpy_unregister_shutdown(PyObject *self, PyObject *arg) {
895 return cpy_unregister_generic(&cpy_shutdown_callbacks, arg, "shutdown");
898 static PyMethodDef cpy_methods[] = {
899 {"debug", cpy_debug, METH_VARARGS, log_doc},
900 {"info", cpy_info, METH_VARARGS, log_doc},
901 {"notice", cpy_notice, METH_VARARGS, log_doc},
902 {"warning", cpy_warning, METH_VARARGS, log_doc},
903 {"error", cpy_error, METH_VARARGS, log_doc},
904 {"get_dataset", (PyCFunction) cpy_get_dataset, METH_VARARGS, get_ds_doc},
905 {"flush", (PyCFunction) cpy_flush, METH_VARARGS | METH_KEYWORDS, flush_doc},
906 {"register_log", (PyCFunction) cpy_register_log, METH_VARARGS | METH_KEYWORDS, reg_log_doc},
907 {"register_init", (PyCFunction) cpy_register_init, METH_VARARGS | METH_KEYWORDS, reg_init_doc},
908 {"register_config", (PyCFunction) cpy_register_config, METH_VARARGS | METH_KEYWORDS, reg_config_doc},
909 {"register_read", (PyCFunction) cpy_register_read, METH_VARARGS | METH_KEYWORDS, reg_read_doc},
910 {"register_write", (PyCFunction) cpy_register_write, METH_VARARGS | METH_KEYWORDS, reg_write_doc},
911 {"register_notification", (PyCFunction) cpy_register_notification, METH_VARARGS | METH_KEYWORDS, reg_notification_doc},
912 {"register_flush", (PyCFunction) cpy_register_flush, METH_VARARGS | METH_KEYWORDS, reg_flush_doc},
913 {"register_shutdown", (PyCFunction) cpy_register_shutdown, METH_VARARGS | METH_KEYWORDS, reg_shutdown_doc},
914 {"unregister_log", cpy_unregister_log, METH_O, unregister_doc},
915 {"unregister_init", cpy_unregister_init, METH_O, unregister_doc},
916 {"unregister_config", cpy_unregister_config, METH_O, unregister_doc},
917 {"unregister_read", cpy_unregister_read, METH_O, unregister_doc},
918 {"unregister_write", cpy_unregister_write, METH_O, unregister_doc},
919 {"unregister_notification", cpy_unregister_notification, METH_O, unregister_doc},
920 {"unregister_flush", cpy_unregister_flush, METH_O, unregister_doc},
921 {"unregister_shutdown", cpy_unregister_shutdown, METH_O, unregister_doc},
925 static int cpy_shutdown(void) {
928 PyEval_RestoreThread(state);
930 for (cpy_callback_t *c = cpy_shutdown_callbacks; c; c = c->next) {
931 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
933 cpy_log_exception("shutdown callback");
939 cpy_unregister_list(&cpy_config_callbacks);
940 cpy_unregister_list(&cpy_init_callbacks);
941 cpy_unregister_list(&cpy_shutdown_callbacks);
942 cpy_shutdown_triggered = 1;
944 if (!cpy_num_callbacks)
950 static void *cpy_interactive(void *data) {
951 PyOS_sighandler_t cur_sig;
953 /* Signal handler in a plugin? Bad stuff, but the best way to
954 * handle it I guess. In an interactive session people will
955 * press Ctrl+C at some time, which will generate a SIGINT.
956 * This will cause collectd to shutdown, thus killing the
957 * interactive interpreter, and leaving the terminal in a
958 * mess. Chances are, this isn't what the user wanted to do.
960 * So this is the plan:
961 * 1. Restore Python's own signal handler
962 * 2. Tell Python we just forked so it will accept this thread
963 * as the main one. No version of Python will ever handle
964 * interrupts anywhere but in the main thread.
965 * 3. After the interactive loop is done, restore collectd's
967 * 4. Raise SIGINT for a clean shutdown. The signal is sent to
968 * the main thread to ensure it wakes up the main interval
969 * sleep so that collectd shuts down immediately not in 10
972 * This will make sure that SIGINT won't kill collectd but
973 * still interrupt syscalls like sleep and pause. */
975 PyEval_AcquireThread(state);
976 if (PyImport_ImportModule("readline") == NULL) {
977 /* This interactive session will suck. */
978 cpy_log_exception("interactive session init");
980 cur_sig = PyOS_setsig(SIGINT, python_sigint_handler);
981 /* We totally forked just now. Everyone saw that, right? */
983 PyRun_InteractiveLoop(stdin, "<stdin>");
984 PyOS_setsig(SIGINT, cur_sig);
986 PyEval_ReleaseThread(state);
987 NOTICE("python: Interactive interpreter exited, stopping collectd ...");
988 pthread_kill(main_thread, SIGINT);
992 static int cpy_init(void) {
994 static pthread_t thread;
996 if (!Py_IsInitialized()) {
997 WARNING("python: Plugin loaded but not configured.");
998 plugin_unregister_shutdown("python");
1002 PyEval_InitThreads();
1003 /* Now it's finally OK to use python threads. */
1004 for (cpy_callback_t *c = cpy_init_callbacks; c; c = c->next) {
1005 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
1007 cpy_log_exception("init callback");
1011 state = PyEval_SaveThread();
1012 main_thread = pthread_self();
1013 if (do_interactive) {
1014 if (plugin_thread_create(&thread, NULL, cpy_interactive, NULL)) {
1015 ERROR("python: Error creating thread for interactive interpreter.");
1022 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
1023 PyObject *item, *values, *children, *tmp;
1028 values = PyTuple_New(ci->values_num); /* New reference. */
1029 for (int i = 0; i < ci->values_num; ++i) {
1030 if (ci->values[i].type == OCONFIG_TYPE_STRING) {
1031 PyTuple_SET_ITEM(values, i, cpy_string_to_unicode_or_bytes(ci->values[i].value.string));
1032 } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
1033 PyTuple_SET_ITEM(values, i, PyFloat_FromDouble(ci->values[i].value.number));
1034 } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
1035 PyTuple_SET_ITEM(values, i, PyBool_FromLong(ci->values[i].value.boolean));
1039 tmp = cpy_string_to_unicode_or_bytes(ci->key);
1040 item = PyObject_CallFunction((void *) &ConfigType, "NONO", tmp, parent, values, Py_None);
1043 children = PyTuple_New(ci->children_num); /* New reference. */
1044 for (int i = 0; i < ci->children_num; ++i) {
1045 PyTuple_SET_ITEM(children, i, cpy_oconfig_to_pyconfig(ci->children + i, item));
1047 tmp = ((Config *) item)->children;
1048 ((Config *) item)->children = children;
1054 static struct PyModuleDef collectdmodule = {
1055 PyModuleDef_HEAD_INIT,
1056 "collectd", /* name of module */
1057 "The python interface to collectd", /* module documentation, may be NULL */
1062 PyMODINIT_FUNC PyInit_collectd(void) {
1063 return PyModule_Create(&collectdmodule);
1067 static int cpy_init_python(void) {
1068 PyOS_sighandler_t cur_sig;
1073 wchar_t *argv = L"";
1074 /* Add a builtin module, before Py_Initialize */
1075 PyImport_AppendInittab("collectd", PyInit_collectd);
1080 /* Chances are the current signal handler is already SIG_DFL, but let's make sure. */
1081 cur_sig = PyOS_setsig(SIGINT, SIG_DFL);
1083 python_sigint_handler = PyOS_setsig(SIGINT, cur_sig);
1085 PyType_Ready(&ConfigType);
1086 PyType_Ready(&PluginDataType);
1087 ValuesType.tp_base = &PluginDataType;
1088 PyType_Ready(&ValuesType);
1089 NotificationType.tp_base = &PluginDataType;
1090 PyType_Ready(&NotificationType);
1091 SignedType.tp_base = &PyLong_Type;
1092 PyType_Ready(&SignedType);
1093 UnsignedType.tp_base = &PyLong_Type;
1094 PyType_Ready(&UnsignedType);
1095 sys = PyImport_ImportModule("sys"); /* New reference. */
1097 cpy_log_exception("python initialization");
1100 sys_path = PyObject_GetAttrString(sys, "path"); /* New reference. */
1102 if (sys_path == NULL) {
1103 cpy_log_exception("python initialization");
1106 PySys_SetArgv(1, &argv);
1107 PyList_SetSlice(sys_path, 0, 1, NULL);
1110 module = PyImport_ImportModule("collectd");
1112 module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
1114 PyModule_AddObject(module, "Config", (void *) &ConfigType); /* Steals a reference. */
1115 PyModule_AddObject(module, "Values", (void *) &ValuesType); /* Steals a reference. */
1116 PyModule_AddObject(module, "Notification", (void *) &NotificationType); /* Steals a reference. */
1117 PyModule_AddObject(module, "Signed", (void *) &SignedType); /* Steals a reference. */
1118 PyModule_AddObject(module, "Unsigned", (void *) &UnsignedType); /* Steals a reference. */
1119 PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG);
1120 PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO);
1121 PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE);
1122 PyModule_AddIntConstant(module, "LOG_WARNING", LOG_WARNING);
1123 PyModule_AddIntConstant(module, "LOG_ERROR", LOG_ERR);
1124 PyModule_AddIntConstant(module, "NOTIF_FAILURE", NOTIF_FAILURE);
1125 PyModule_AddIntConstant(module, "NOTIF_WARNING", NOTIF_WARNING);
1126 PyModule_AddIntConstant(module, "NOTIF_OKAY", NOTIF_OKAY);
1127 PyModule_AddStringConstant(module, "DS_TYPE_COUNTER", DS_TYPE_TO_STRING(DS_TYPE_COUNTER));
1128 PyModule_AddStringConstant(module, "DS_TYPE_GAUGE", DS_TYPE_TO_STRING(DS_TYPE_GAUGE));
1129 PyModule_AddStringConstant(module, "DS_TYPE_DERIVE", DS_TYPE_TO_STRING(DS_TYPE_DERIVE));
1130 PyModule_AddStringConstant(module, "DS_TYPE_ABSOLUTE", DS_TYPE_TO_STRING(DS_TYPE_ABSOLUTE));
1134 static int cpy_config(oconfig_item_t *ci) {
1138 /* Ok in theory we shouldn't do initialization at this point
1139 * but we have to. In order to give python scripts a chance
1140 * to register a config callback we need to be able to execute
1141 * python code during the config callback so we have to start
1142 * the interpreter here. */
1143 /* Do *not* use the python "thread" module at this point! */
1145 if (!Py_IsInitialized() && cpy_init_python()) return 1;
1147 for (int i = 0; i < ci->children_num; ++i) {
1148 oconfig_item_t *item = ci->children + i;
1150 if (strcasecmp(item->key, "Interactive") == 0) {
1151 if (cf_util_get_boolean(item, &do_interactive) != 0) {
1155 } else if (strcasecmp(item->key, "Encoding") == 0) {
1156 char *encoding = NULL;
1157 if (cf_util_get_string(item, &encoding) != 0) {
1162 ERROR("python: \"Encoding\" was used in the config file but Python3 was used, which does not support changing encodings");
1167 /* Why is this even necessary? And undocumented? */
1168 if (PyUnicode_SetDefaultEncoding(encoding)) {
1169 cpy_log_exception("setting default encoding");
1174 } else if (strcasecmp(item->key, "LogTraces") == 0) {
1176 if (cf_util_get_boolean(item, &log_traces) != 0) {
1181 Py_XDECREF(cpy_format_exception);
1182 cpy_format_exception = NULL;
1185 if (cpy_format_exception)
1187 tb = PyImport_ImportModule("traceback"); /* New reference. */
1189 cpy_log_exception("python initialization");
1193 cpy_format_exception = PyObject_GetAttrString(tb, "format_exception"); /* New reference. */
1195 if (cpy_format_exception == NULL) {
1196 cpy_log_exception("python initialization");
1199 } else if (strcasecmp(item->key, "ModulePath") == 0) {
1201 PyObject *dir_object;
1203 if (cf_util_get_string(item, &dir) != 0) {
1207 dir_object = cpy_string_to_unicode_or_bytes(dir); /* New reference. */
1208 if (dir_object == NULL) {
1209 ERROR("python plugin: Unable to convert \"%s\" to "
1210 "a python object.", dir);
1212 cpy_log_exception("python initialization");
1216 if (PyList_Insert(sys_path, 0, dir_object) != 0) {
1217 ERROR("python plugin: Unable to prepend \"%s\" to "
1218 "python module path.", dir);
1219 cpy_log_exception("python initialization");
1222 Py_DECREF(dir_object);
1224 } else if (strcasecmp(item->key, "Import") == 0) {
1225 char *module_name = NULL;
1228 if (cf_util_get_string(item, &module_name) != 0) {
1232 module = PyImport_ImportModule(module_name); /* New reference. */
1233 if (module == NULL) {
1234 ERROR("python plugin: Error importing module \"%s\".", module_name);
1235 cpy_log_exception("importing module");
1240 } else if (strcasecmp(item->key, "Module") == 0) {
1245 if (cf_util_get_string(item, &name) != 0) {
1249 for (c = cpy_config_callbacks; c; c = c->next) {
1250 if (strcasecmp(c->name + 7, name) == 0)
1254 WARNING("python plugin: Found a configuration for the \"%s\" plugin, "
1255 "but the plugin isn't loaded or didn't register "
1256 "a configuration callback.", name);
1261 if (c->data == NULL)
1262 ret = PyObject_CallFunction(c->callback, "N",
1263 cpy_oconfig_to_pyconfig(item, NULL)); /* New reference. */
1265 ret = PyObject_CallFunction(c->callback, "NO",
1266 cpy_oconfig_to_pyconfig(item, NULL), c->data); /* New reference. */
1268 cpy_log_exception("loading module");
1273 ERROR("python plugin: Unknown config key \"%s\".", item->key);
1280 void module_register(void) {
1281 plugin_register_complex_config("python", cpy_config);
1282 plugin_register_init("python", cpy_init);
1283 plugin_register_shutdown("python", cpy_shutdown);