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 register_function(buf, handler, &(user_data_t) {
670 .free_func = cpy_destroy_user_data,
674 return cpy_string_to_unicode_or_bytes(buf);
677 static PyObject *cpy_register_read(PyObject *self, PyObject *args, PyObject *kwds) {
679 cpy_callback_t *c = NULL;
682 PyObject *callback = NULL, *data = NULL;
683 static char *kwlist[] = {"callback", "interval", "data", "name", NULL};
685 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|dOet", kwlist, &callback, &interval, &data, NULL, &name) == 0) return NULL;
686 if (PyCallable_Check(callback) == 0) {
688 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
691 cpy_build_name(buf, sizeof(buf), callback, name);
697 c = calloc(1, sizeof(*c));
701 c->name = strdup(buf);
702 c->callback = callback;
706 plugin_register_complex_read(/* group = */ "python", buf,
707 cpy_read_callback, DOUBLE_TO_CDTIME_T (interval),
710 .free_func = cpy_destroy_user_data,
713 return cpy_string_to_unicode_or_bytes(buf);
716 static PyObject *cpy_register_log(PyObject *self, PyObject *args, PyObject *kwds) {
717 return cpy_register_generic_userdata((void *) plugin_register_log,
718 (void *) cpy_log_callback, args, kwds);
721 static PyObject *cpy_register_write(PyObject *self, PyObject *args, PyObject *kwds) {
722 return cpy_register_generic_userdata((void *) plugin_register_write,
723 (void *) cpy_write_callback, args, kwds);
726 static PyObject *cpy_register_notification(PyObject *self, PyObject *args, PyObject *kwds) {
727 return cpy_register_generic_userdata((void *) plugin_register_notification,
728 (void *) cpy_notification_callback, args, kwds);
731 static PyObject *cpy_register_flush(PyObject *self, PyObject *args, PyObject *kwds) {
732 return cpy_register_generic_userdata((void *) plugin_register_flush,
733 (void *) cpy_flush_callback, args, kwds);
736 static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args, PyObject *kwds) {
737 return cpy_register_generic(&cpy_shutdown_callbacks, args, kwds);
740 static PyObject *cpy_error(PyObject *self, PyObject *args) {
742 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
743 Py_BEGIN_ALLOW_THREADS
744 plugin_log(LOG_ERR, "%s", text);
750 static PyObject *cpy_warning(PyObject *self, PyObject *args) {
752 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
753 Py_BEGIN_ALLOW_THREADS
754 plugin_log(LOG_WARNING, "%s", text);
760 static PyObject *cpy_notice(PyObject *self, PyObject *args) {
762 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
763 Py_BEGIN_ALLOW_THREADS
764 plugin_log(LOG_NOTICE, "%s", text);
770 static PyObject *cpy_info(PyObject *self, PyObject *args) {
772 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
773 Py_BEGIN_ALLOW_THREADS
774 plugin_log(LOG_INFO, "%s", text);
780 static PyObject *cpy_debug(PyObject *self, PyObject *args) {
783 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
784 Py_BEGIN_ALLOW_THREADS
785 plugin_log(LOG_DEBUG, "%s", text);
792 static PyObject *cpy_unregister_generic(cpy_callback_t **list_head, PyObject *arg, const char *desc) {
795 cpy_callback_t *prev = NULL, *tmp;
798 name = cpy_unicode_or_bytes_to_string(&arg);
801 if (!PyCallable_Check(arg)) {
802 PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
806 cpy_build_name(buf, sizeof(buf), arg, NULL);
809 for (tmp = *list_head; tmp; prev = tmp, tmp = tmp->next)
810 if (strcmp(name, tmp->name) == 0)
815 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
818 /* Yes, this is actually safe. To call this function the caller has to
819 * hold the GIL. Well, safe as long as there is only one GIL anyway ... */
821 *list_head = tmp->next;
823 prev->next = tmp->next;
824 cpy_destroy_user_data(tmp);
828 static void cpy_unregister_list(cpy_callback_t **list_head) {
829 cpy_callback_t *cur, *next;
830 for (cur = *list_head; cur; cur = next) {
832 cpy_destroy_user_data(cur);
837 typedef int cpy_unregister_function_t(const char *name);
839 static PyObject *cpy_unregister_generic_userdata(cpy_unregister_function_t *unreg, PyObject *arg, const char *desc) {
844 name = cpy_unicode_or_bytes_to_string(&arg);
847 if (!PyCallable_Check(arg)) {
848 PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
852 cpy_build_name(buf, sizeof(buf), arg, NULL);
855 if (unreg(name) == 0) {
859 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
864 static PyObject *cpy_unregister_log(PyObject *self, PyObject *arg) {
865 return cpy_unregister_generic_userdata(plugin_unregister_log, arg, "log");
868 static PyObject *cpy_unregister_init(PyObject *self, PyObject *arg) {
869 return cpy_unregister_generic(&cpy_init_callbacks, arg, "init");
872 static PyObject *cpy_unregister_config(PyObject *self, PyObject *arg) {
873 return cpy_unregister_generic(&cpy_config_callbacks, arg, "config");
876 static PyObject *cpy_unregister_read(PyObject *self, PyObject *arg) {
877 return cpy_unregister_generic_userdata(plugin_unregister_read, arg, "read");
880 static PyObject *cpy_unregister_write(PyObject *self, PyObject *arg) {
881 return cpy_unregister_generic_userdata(plugin_unregister_write, arg, "write");
884 static PyObject *cpy_unregister_notification(PyObject *self, PyObject *arg) {
885 return cpy_unregister_generic_userdata(plugin_unregister_notification, arg, "notification");
888 static PyObject *cpy_unregister_flush(PyObject *self, PyObject *arg) {
889 return cpy_unregister_generic_userdata(plugin_unregister_flush, arg, "flush");
892 static PyObject *cpy_unregister_shutdown(PyObject *self, PyObject *arg) {
893 return cpy_unregister_generic(&cpy_shutdown_callbacks, arg, "shutdown");
896 static PyMethodDef cpy_methods[] = {
897 {"debug", cpy_debug, METH_VARARGS, log_doc},
898 {"info", cpy_info, METH_VARARGS, log_doc},
899 {"notice", cpy_notice, METH_VARARGS, log_doc},
900 {"warning", cpy_warning, METH_VARARGS, log_doc},
901 {"error", cpy_error, METH_VARARGS, log_doc},
902 {"get_dataset", (PyCFunction) cpy_get_dataset, METH_VARARGS, get_ds_doc},
903 {"flush", (PyCFunction) cpy_flush, METH_VARARGS | METH_KEYWORDS, flush_doc},
904 {"register_log", (PyCFunction) cpy_register_log, METH_VARARGS | METH_KEYWORDS, reg_log_doc},
905 {"register_init", (PyCFunction) cpy_register_init, METH_VARARGS | METH_KEYWORDS, reg_init_doc},
906 {"register_config", (PyCFunction) cpy_register_config, METH_VARARGS | METH_KEYWORDS, reg_config_doc},
907 {"register_read", (PyCFunction) cpy_register_read, METH_VARARGS | METH_KEYWORDS, reg_read_doc},
908 {"register_write", (PyCFunction) cpy_register_write, METH_VARARGS | METH_KEYWORDS, reg_write_doc},
909 {"register_notification", (PyCFunction) cpy_register_notification, METH_VARARGS | METH_KEYWORDS, reg_notification_doc},
910 {"register_flush", (PyCFunction) cpy_register_flush, METH_VARARGS | METH_KEYWORDS, reg_flush_doc},
911 {"register_shutdown", (PyCFunction) cpy_register_shutdown, METH_VARARGS | METH_KEYWORDS, reg_shutdown_doc},
912 {"unregister_log", cpy_unregister_log, METH_O, unregister_doc},
913 {"unregister_init", cpy_unregister_init, METH_O, unregister_doc},
914 {"unregister_config", cpy_unregister_config, METH_O, unregister_doc},
915 {"unregister_read", cpy_unregister_read, METH_O, unregister_doc},
916 {"unregister_write", cpy_unregister_write, METH_O, unregister_doc},
917 {"unregister_notification", cpy_unregister_notification, METH_O, unregister_doc},
918 {"unregister_flush", cpy_unregister_flush, METH_O, unregister_doc},
919 {"unregister_shutdown", cpy_unregister_shutdown, METH_O, unregister_doc},
923 static int cpy_shutdown(void) {
927 printf("================================================================\n");
928 printf("collectd shutdown while running an interactive session. This will\n");
929 printf("probably leave your terminal in a mess.\n");
930 printf("Run the command \"reset\" to get it back into a usable state.\n");
931 printf("You can press Ctrl+D in the interactive session to\n");
932 printf("close collectd and avoid this problem in the future.\n");
933 printf("================================================================\n");
938 for (cpy_callback_t *c = cpy_shutdown_callbacks; c; c = c->next) {
939 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
941 cpy_log_exception("shutdown callback");
947 Py_BEGIN_ALLOW_THREADS
948 cpy_unregister_list(&cpy_config_callbacks);
949 cpy_unregister_list(&cpy_init_callbacks);
950 cpy_unregister_list(&cpy_shutdown_callbacks);
951 cpy_shutdown_triggered = 1;
954 if (!cpy_num_callbacks) {
963 static void *cpy_interactive(void *pipefd) {
964 PyOS_sighandler_t cur_sig;
966 /* Signal handler in a plugin? Bad stuff, but the best way to
967 * handle it I guess. In an interactive session people will
968 * press Ctrl+C at some time, which will generate a SIGINT.
969 * This will cause collectd to shutdown, thus killing the
970 * interactive interpreter, and leaving the terminal in a
971 * mess. Chances are, this isn't what the user wanted to do.
973 * So this is the plan:
974 * 1. Restore Python's own signal handler
975 * 2. Tell Python we just forked so it will accept this thread
976 * as the main one. No version of Python will ever handle
977 * interrupts anywhere but in the main thread.
978 * 3. After the interactive loop is done, restore collectd's
980 * 4. Raise SIGINT for a clean shutdown. The signal is sent to
981 * the main thread to ensure it wakes up the main interval
982 * sleep so that collectd shuts down immediately not in 10
985 * This will make sure that SIGINT won't kill collectd but
986 * still interrupt syscalls like sleep and pause. */
988 if (PyImport_ImportModule("readline") == NULL) {
989 /* This interactive session will suck. */
990 cpy_log_exception("interactive session init");
992 cur_sig = PyOS_setsig(SIGINT, python_sigint_handler);
994 PyEval_InitThreads();
995 close(*(int *) pipefd);
996 PyRun_InteractiveLoop(stdin, "<stdin>");
997 PyOS_setsig(SIGINT, cur_sig);
999 state = PyEval_SaveThread();
1000 NOTICE("python: Interactive interpreter exited, stopping collectd ...");
1001 pthread_kill(main_thread, SIGINT);
1005 static int cpy_init(void) {
1009 static pthread_t thread;
1011 if (!Py_IsInitialized()) {
1012 WARNING("python: Plugin loaded but not configured.");
1013 plugin_unregister_shutdown("python");
1017 main_thread = pthread_self();
1018 if (do_interactive) {
1020 ERROR("python: Unable to create pipe.");
1023 if (plugin_thread_create(&thread, NULL, cpy_interactive, pipefd + 1)) {
1024 ERROR("python: Error creating thread for interactive interpreter.");
1026 if(read(pipefd[0], &buf, 1))
1028 (void)close(pipefd[0]);
1030 PyEval_InitThreads();
1031 state = PyEval_SaveThread();
1034 for (cpy_callback_t *c = cpy_init_callbacks; c; c = c->next) {
1035 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
1037 cpy_log_exception("init callback");
1046 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
1047 PyObject *item, *values, *children, *tmp;
1052 values = PyTuple_New(ci->values_num); /* New reference. */
1053 for (int i = 0; i < ci->values_num; ++i) {
1054 if (ci->values[i].type == OCONFIG_TYPE_STRING) {
1055 PyTuple_SET_ITEM(values, i, cpy_string_to_unicode_or_bytes(ci->values[i].value.string));
1056 } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
1057 PyTuple_SET_ITEM(values, i, PyFloat_FromDouble(ci->values[i].value.number));
1058 } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
1059 PyTuple_SET_ITEM(values, i, PyBool_FromLong(ci->values[i].value.boolean));
1063 tmp = cpy_string_to_unicode_or_bytes(ci->key);
1064 item = PyObject_CallFunction((void *) &ConfigType, "NONO", tmp, parent, values, Py_None);
1067 children = PyTuple_New(ci->children_num); /* New reference. */
1068 for (int i = 0; i < ci->children_num; ++i) {
1069 PyTuple_SET_ITEM(children, i, cpy_oconfig_to_pyconfig(ci->children + i, item));
1071 tmp = ((Config *) item)->children;
1072 ((Config *) item)->children = children;
1078 static struct PyModuleDef collectdmodule = {
1079 PyModuleDef_HEAD_INIT,
1080 "collectd", /* name of module */
1081 "The python interface to collectd", /* module documentation, may be NULL */
1086 PyMODINIT_FUNC PyInit_collectd(void) {
1087 return PyModule_Create(&collectdmodule);
1091 static int cpy_init_python(void) {
1092 PyOS_sighandler_t cur_sig;
1097 wchar_t *argv = L"";
1098 /* Add a builtin module, before Py_Initialize */
1099 PyImport_AppendInittab("collectd", PyInit_collectd);
1104 /* Chances are the current signal handler is already SIG_DFL, but let's make sure. */
1105 cur_sig = PyOS_setsig(SIGINT, SIG_DFL);
1107 python_sigint_handler = PyOS_setsig(SIGINT, cur_sig);
1109 PyType_Ready(&ConfigType);
1110 PyType_Ready(&PluginDataType);
1111 ValuesType.tp_base = &PluginDataType;
1112 PyType_Ready(&ValuesType);
1113 NotificationType.tp_base = &PluginDataType;
1114 PyType_Ready(&NotificationType);
1115 SignedType.tp_base = &PyLong_Type;
1116 PyType_Ready(&SignedType);
1117 UnsignedType.tp_base = &PyLong_Type;
1118 PyType_Ready(&UnsignedType);
1119 sys = PyImport_ImportModule("sys"); /* New reference. */
1121 cpy_log_exception("python initialization");
1124 sys_path = PyObject_GetAttrString(sys, "path"); /* New reference. */
1126 if (sys_path == NULL) {
1127 cpy_log_exception("python initialization");
1130 PySys_SetArgv(1, &argv);
1131 PyList_SetSlice(sys_path, 0, 1, NULL);
1134 module = PyImport_ImportModule("collectd");
1136 module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
1138 PyModule_AddObject(module, "Config", (void *) &ConfigType); /* Steals a reference. */
1139 PyModule_AddObject(module, "Values", (void *) &ValuesType); /* Steals a reference. */
1140 PyModule_AddObject(module, "Notification", (void *) &NotificationType); /* Steals a reference. */
1141 PyModule_AddObject(module, "Signed", (void *) &SignedType); /* Steals a reference. */
1142 PyModule_AddObject(module, "Unsigned", (void *) &UnsignedType); /* Steals a reference. */
1143 PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG);
1144 PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO);
1145 PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE);
1146 PyModule_AddIntConstant(module, "LOG_WARNING", LOG_WARNING);
1147 PyModule_AddIntConstant(module, "LOG_ERROR", LOG_ERR);
1148 PyModule_AddIntConstant(module, "NOTIF_FAILURE", NOTIF_FAILURE);
1149 PyModule_AddIntConstant(module, "NOTIF_WARNING", NOTIF_WARNING);
1150 PyModule_AddIntConstant(module, "NOTIF_OKAY", NOTIF_OKAY);
1151 PyModule_AddStringConstant(module, "DS_TYPE_COUNTER", DS_TYPE_TO_STRING(DS_TYPE_COUNTER));
1152 PyModule_AddStringConstant(module, "DS_TYPE_GAUGE", DS_TYPE_TO_STRING(DS_TYPE_GAUGE));
1153 PyModule_AddStringConstant(module, "DS_TYPE_DERIVE", DS_TYPE_TO_STRING(DS_TYPE_DERIVE));
1154 PyModule_AddStringConstant(module, "DS_TYPE_ABSOLUTE", DS_TYPE_TO_STRING(DS_TYPE_ABSOLUTE));
1158 static int cpy_config(oconfig_item_t *ci) {
1162 /* Ok in theory we shouldn't do initialization at this point
1163 * but we have to. In order to give python scripts a chance
1164 * to register a config callback we need to be able to execute
1165 * python code during the config callback so we have to start
1166 * the interpreter here. */
1167 /* Do *not* use the python "thread" module at this point! */
1169 if (!Py_IsInitialized() && cpy_init_python()) return 1;
1171 for (int i = 0; i < ci->children_num; ++i) {
1172 oconfig_item_t *item = ci->children + i;
1174 if (strcasecmp(item->key, "Interactive") == 0) {
1175 if (cf_util_get_boolean(item, &do_interactive) != 0) {
1179 } else if (strcasecmp(item->key, "Encoding") == 0) {
1180 char *encoding = NULL;
1181 if (cf_util_get_string(item, &encoding) != 0) {
1186 ERROR("python: \"Encoding\" was used in the config file but Python3 was used, which does not support changing encodings");
1191 /* Why is this even necessary? And undocumented? */
1192 if (PyUnicode_SetDefaultEncoding(encoding)) {
1193 cpy_log_exception("setting default encoding");
1198 } else if (strcasecmp(item->key, "LogTraces") == 0) {
1200 if (cf_util_get_boolean(item, &log_traces) != 0) {
1205 Py_XDECREF(cpy_format_exception);
1206 cpy_format_exception = NULL;
1209 if (cpy_format_exception)
1211 tb = PyImport_ImportModule("traceback"); /* New reference. */
1213 cpy_log_exception("python initialization");
1217 cpy_format_exception = PyObject_GetAttrString(tb, "format_exception"); /* New reference. */
1219 if (cpy_format_exception == NULL) {
1220 cpy_log_exception("python initialization");
1223 } else if (strcasecmp(item->key, "ModulePath") == 0) {
1225 PyObject *dir_object;
1227 if (cf_util_get_string(item, &dir) != 0) {
1231 dir_object = cpy_string_to_unicode_or_bytes(dir); /* New reference. */
1232 if (dir_object == NULL) {
1233 ERROR("python plugin: Unable to convert \"%s\" to "
1234 "a python object.", dir);
1236 cpy_log_exception("python initialization");
1240 if (PyList_Insert(sys_path, 0, dir_object) != 0) {
1241 ERROR("python plugin: Unable to prepend \"%s\" to "
1242 "python module path.", dir);
1243 cpy_log_exception("python initialization");
1246 Py_DECREF(dir_object);
1248 } else if (strcasecmp(item->key, "Import") == 0) {
1249 char *module_name = NULL;
1252 if (cf_util_get_string(item, &module_name) != 0) {
1256 module = PyImport_ImportModule(module_name); /* New reference. */
1257 if (module == NULL) {
1258 ERROR("python plugin: Error importing module \"%s\".", module_name);
1259 cpy_log_exception("importing module");
1264 } else if (strcasecmp(item->key, "Module") == 0) {
1269 if (cf_util_get_string(item, &name) != 0) {
1273 for (c = cpy_config_callbacks; c; c = c->next) {
1274 if (strcasecmp(c->name + 7, name) == 0)
1278 WARNING("python plugin: Found a configuration for the \"%s\" plugin, "
1279 "but the plugin isn't loaded or didn't register "
1280 "a configuration callback.", name);
1285 if (c->data == NULL)
1286 ret = PyObject_CallFunction(c->callback, "N",
1287 cpy_oconfig_to_pyconfig(item, NULL)); /* New reference. */
1289 ret = PyObject_CallFunction(c->callback, "NO",
1290 cpy_oconfig_to_pyconfig(item, NULL), c->data); /* New reference. */
1292 cpy_log_exception("loading module");
1297 ERROR("python plugin: Unknown config key \"%s\".", item->key);
1304 void module_register(void) {
1305 plugin_register_complex_config("python", cpy_config);
1306 plugin_register_init("python", cpy_init);
1307 plugin_register_shutdown("python", cpy_shutdown);