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) {
929 printf("================================================================\n");
930 printf("collectd shutdown while running an interactive session. This will\n");
931 printf("probably leave your terminal in a mess.\n");
932 printf("Run the command \"reset\" to get it back into a usable state.\n");
933 printf("You can press Ctrl+D in the interactive session to\n");
934 printf("close collectd and avoid this problem in the future.\n");
935 printf("================================================================\n");
940 for (cpy_callback_t *c = cpy_shutdown_callbacks; c; c = c->next) {
941 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
943 cpy_log_exception("shutdown callback");
949 Py_BEGIN_ALLOW_THREADS
950 cpy_unregister_list(&cpy_config_callbacks);
951 cpy_unregister_list(&cpy_init_callbacks);
952 cpy_unregister_list(&cpy_shutdown_callbacks);
953 cpy_shutdown_triggered = 1;
956 if (!cpy_num_callbacks) {
965 static void *cpy_interactive(void *pipefd) {
966 PyOS_sighandler_t cur_sig;
968 /* Signal handler in a plugin? Bad stuff, but the best way to
969 * handle it I guess. In an interactive session people will
970 * press Ctrl+C at some time, which will generate a SIGINT.
971 * This will cause collectd to shutdown, thus killing the
972 * interactive interpreter, and leaving the terminal in a
973 * mess. Chances are, this isn't what the user wanted to do.
975 * So this is the plan:
976 * 1. Restore Python's own signal handler
977 * 2. Tell Python we just forked so it will accept this thread
978 * as the main one. No version of Python will ever handle
979 * interrupts anywhere but in the main thread.
980 * 3. After the interactive loop is done, restore collectd's
982 * 4. Raise SIGINT for a clean shutdown. The signal is sent to
983 * the main thread to ensure it wakes up the main interval
984 * sleep so that collectd shuts down immediately not in 10
987 * This will make sure that SIGINT won't kill collectd but
988 * still interrupt syscalls like sleep and pause. */
990 if (PyImport_ImportModule("readline") == NULL) {
991 /* This interactive session will suck. */
992 cpy_log_exception("interactive session init");
994 cur_sig = PyOS_setsig(SIGINT, python_sigint_handler);
996 PyEval_InitThreads();
997 close(*(int *) pipefd);
998 PyRun_InteractiveLoop(stdin, "<stdin>");
999 PyOS_setsig(SIGINT, cur_sig);
1001 state = PyEval_SaveThread();
1002 NOTICE("python: Interactive interpreter exited, stopping collectd ...");
1003 pthread_kill(main_thread, SIGINT);
1007 static int cpy_init(void) {
1011 static pthread_t thread;
1013 if (!Py_IsInitialized()) {
1014 WARNING("python: Plugin loaded but not configured.");
1015 plugin_unregister_shutdown("python");
1019 main_thread = pthread_self();
1020 if (do_interactive) {
1022 ERROR("python: Unable to create pipe.");
1025 if (plugin_thread_create(&thread, NULL, cpy_interactive, pipefd + 1)) {
1026 ERROR("python: Error creating thread for interactive interpreter.");
1028 if(read(pipefd[0], &buf, 1))
1030 (void)close(pipefd[0]);
1032 PyEval_InitThreads();
1033 state = PyEval_SaveThread();
1036 for (cpy_callback_t *c = cpy_init_callbacks; c; c = c->next) {
1037 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
1039 cpy_log_exception("init callback");
1048 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
1049 PyObject *item, *values, *children, *tmp;
1054 values = PyTuple_New(ci->values_num); /* New reference. */
1055 for (int i = 0; i < ci->values_num; ++i) {
1056 if (ci->values[i].type == OCONFIG_TYPE_STRING) {
1057 PyTuple_SET_ITEM(values, i, cpy_string_to_unicode_or_bytes(ci->values[i].value.string));
1058 } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
1059 PyTuple_SET_ITEM(values, i, PyFloat_FromDouble(ci->values[i].value.number));
1060 } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
1061 PyTuple_SET_ITEM(values, i, PyBool_FromLong(ci->values[i].value.boolean));
1065 tmp = cpy_string_to_unicode_or_bytes(ci->key);
1066 item = PyObject_CallFunction((void *) &ConfigType, "NONO", tmp, parent, values, Py_None);
1069 children = PyTuple_New(ci->children_num); /* New reference. */
1070 for (int i = 0; i < ci->children_num; ++i) {
1071 PyTuple_SET_ITEM(children, i, cpy_oconfig_to_pyconfig(ci->children + i, item));
1073 tmp = ((Config *) item)->children;
1074 ((Config *) item)->children = children;
1080 static struct PyModuleDef collectdmodule = {
1081 PyModuleDef_HEAD_INIT,
1082 "collectd", /* name of module */
1083 "The python interface to collectd", /* module documentation, may be NULL */
1088 PyMODINIT_FUNC PyInit_collectd(void) {
1089 return PyModule_Create(&collectdmodule);
1093 static int cpy_init_python(void) {
1094 PyOS_sighandler_t cur_sig;
1099 wchar_t *argv = L"";
1100 /* Add a builtin module, before Py_Initialize */
1101 PyImport_AppendInittab("collectd", PyInit_collectd);
1106 /* Chances are the current signal handler is already SIG_DFL, but let's make sure. */
1107 cur_sig = PyOS_setsig(SIGINT, SIG_DFL);
1109 python_sigint_handler = PyOS_setsig(SIGINT, cur_sig);
1111 PyType_Ready(&ConfigType);
1112 PyType_Ready(&PluginDataType);
1113 ValuesType.tp_base = &PluginDataType;
1114 PyType_Ready(&ValuesType);
1115 NotificationType.tp_base = &PluginDataType;
1116 PyType_Ready(&NotificationType);
1117 SignedType.tp_base = &PyLong_Type;
1118 PyType_Ready(&SignedType);
1119 UnsignedType.tp_base = &PyLong_Type;
1120 PyType_Ready(&UnsignedType);
1121 sys = PyImport_ImportModule("sys"); /* New reference. */
1123 cpy_log_exception("python initialization");
1126 sys_path = PyObject_GetAttrString(sys, "path"); /* New reference. */
1128 if (sys_path == NULL) {
1129 cpy_log_exception("python initialization");
1132 PySys_SetArgv(1, &argv);
1133 PyList_SetSlice(sys_path, 0, 1, NULL);
1136 module = PyImport_ImportModule("collectd");
1138 module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
1140 PyModule_AddObject(module, "Config", (void *) &ConfigType); /* Steals a reference. */
1141 PyModule_AddObject(module, "Values", (void *) &ValuesType); /* Steals a reference. */
1142 PyModule_AddObject(module, "Notification", (void *) &NotificationType); /* Steals a reference. */
1143 PyModule_AddObject(module, "Signed", (void *) &SignedType); /* Steals a reference. */
1144 PyModule_AddObject(module, "Unsigned", (void *) &UnsignedType); /* Steals a reference. */
1145 PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG);
1146 PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO);
1147 PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE);
1148 PyModule_AddIntConstant(module, "LOG_WARNING", LOG_WARNING);
1149 PyModule_AddIntConstant(module, "LOG_ERROR", LOG_ERR);
1150 PyModule_AddIntConstant(module, "NOTIF_FAILURE", NOTIF_FAILURE);
1151 PyModule_AddIntConstant(module, "NOTIF_WARNING", NOTIF_WARNING);
1152 PyModule_AddIntConstant(module, "NOTIF_OKAY", NOTIF_OKAY);
1153 PyModule_AddStringConstant(module, "DS_TYPE_COUNTER", DS_TYPE_TO_STRING(DS_TYPE_COUNTER));
1154 PyModule_AddStringConstant(module, "DS_TYPE_GAUGE", DS_TYPE_TO_STRING(DS_TYPE_GAUGE));
1155 PyModule_AddStringConstant(module, "DS_TYPE_DERIVE", DS_TYPE_TO_STRING(DS_TYPE_DERIVE));
1156 PyModule_AddStringConstant(module, "DS_TYPE_ABSOLUTE", DS_TYPE_TO_STRING(DS_TYPE_ABSOLUTE));
1160 static int cpy_config(oconfig_item_t *ci) {
1164 /* Ok in theory we shouldn't do initialization at this point
1165 * but we have to. In order to give python scripts a chance
1166 * to register a config callback we need to be able to execute
1167 * python code during the config callback so we have to start
1168 * the interpreter here. */
1169 /* Do *not* use the python "thread" module at this point! */
1171 if (!Py_IsInitialized() && cpy_init_python()) return 1;
1173 for (int i = 0; i < ci->children_num; ++i) {
1174 oconfig_item_t *item = ci->children + i;
1176 if (strcasecmp(item->key, "Interactive") == 0) {
1177 if (cf_util_get_boolean(item, &do_interactive) != 0) {
1181 } else if (strcasecmp(item->key, "Encoding") == 0) {
1182 char *encoding = NULL;
1183 if (cf_util_get_string(item, &encoding) != 0) {
1188 ERROR("python: \"Encoding\" was used in the config file but Python3 was used, which does not support changing encodings");
1193 /* Why is this even necessary? And undocumented? */
1194 if (PyUnicode_SetDefaultEncoding(encoding)) {
1195 cpy_log_exception("setting default encoding");
1200 } else if (strcasecmp(item->key, "LogTraces") == 0) {
1202 if (cf_util_get_boolean(item, &log_traces) != 0) {
1207 Py_XDECREF(cpy_format_exception);
1208 cpy_format_exception = NULL;
1211 if (cpy_format_exception)
1213 tb = PyImport_ImportModule("traceback"); /* New reference. */
1215 cpy_log_exception("python initialization");
1219 cpy_format_exception = PyObject_GetAttrString(tb, "format_exception"); /* New reference. */
1221 if (cpy_format_exception == NULL) {
1222 cpy_log_exception("python initialization");
1225 } else if (strcasecmp(item->key, "ModulePath") == 0) {
1227 PyObject *dir_object;
1229 if (cf_util_get_string(item, &dir) != 0) {
1233 dir_object = cpy_string_to_unicode_or_bytes(dir); /* New reference. */
1234 if (dir_object == NULL) {
1235 ERROR("python plugin: Unable to convert \"%s\" to "
1236 "a python object.", dir);
1238 cpy_log_exception("python initialization");
1242 if (PyList_Insert(sys_path, 0, dir_object) != 0) {
1243 ERROR("python plugin: Unable to prepend \"%s\" to "
1244 "python module path.", dir);
1245 cpy_log_exception("python initialization");
1248 Py_DECREF(dir_object);
1250 } else if (strcasecmp(item->key, "Import") == 0) {
1251 char *module_name = NULL;
1254 if (cf_util_get_string(item, &module_name) != 0) {
1258 module = PyImport_ImportModule(module_name); /* New reference. */
1259 if (module == NULL) {
1260 ERROR("python plugin: Error importing module \"%s\".", module_name);
1261 cpy_log_exception("importing module");
1266 } else if (strcasecmp(item->key, "Module") == 0) {
1271 if (cf_util_get_string(item, &name) != 0) {
1275 for (c = cpy_config_callbacks; c; c = c->next) {
1276 if (strcasecmp(c->name + 7, name) == 0)
1280 WARNING("python plugin: Found a configuration for the \"%s\" plugin, "
1281 "but the plugin isn't loaded or didn't register "
1282 "a configuration callback.", name);
1287 if (c->data == NULL)
1288 ret = PyObject_CallFunction(c->callback, "N",
1289 cpy_oconfig_to_pyconfig(item, NULL)); /* New reference. */
1291 ret = PyObject_CallFunction(c->callback, "NO",
1292 cpy_oconfig_to_pyconfig(item, NULL), c->data); /* New reference. */
1294 cpy_log_exception("loading module");
1299 ERROR("python plugin: Unknown config key \"%s\".", item->key);
1306 void module_register(void) {
1307 plugin_register_complex_config("python", cpy_config);
1308 plugin_register_init("python", cpy_init);
1309 plugin_register_shutdown("python", cpy_shutdown);