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[] =
48 "get_dataset(name) -> definition\n"
50 "Returns the definition of a dataset specified by name.\n"
52 "'name' is a string specifying the dataset to query.\n"
53 "'definition' is a list of 4-tuples. Every tuple represents a \n"
54 " data source within the data set and its 4 values are the \n"
55 " name, type, min and max value.\n"
56 " 'name' is a string.\n"
57 " 'type' is a string that is equal to either DS_TYPE_COUNTER,\n"
58 " DS_TYPE_GAUGE, DS_TYPE_DERIVE or DS_TYPE_ABSOLUTE.\n"
59 " 'min' and 'max' are either a float or None.";
61 static char flush_doc[] = "flush([plugin][, timeout][, identifier]) -> None\n"
63 "Flushes the cache of another plugin.";
65 static char unregister_doc[] =
66 "Unregisters a callback. This function needs exactly one parameter either\n"
67 "the function to unregister or the callback identifier to unregister.";
69 static char reg_log_doc[] =
70 "register_log(callback[, data][, name]) -> identifier\n"
72 "Register a callback function for log messages.\n"
74 "'callback' is a callable object that will be called every time something\n"
76 "'data' is an optional object that will be passed back to the callback\n"
77 " function every time it is called.\n"
78 "'name' is an optional identifier for this callback. The default name\n"
79 " is 'python.<module>'.\n"
80 " Every callback needs a unique identifier, so if you want to\n"
81 " register this callback multiple time from the same module you need\n"
82 " to specify a name here.\n"
83 "'identifier' is the full identifier assigned to this callback.\n"
85 "The callback function will be called with two or three parameters:\n"
86 "severity: An integer that should be compared to the LOG_ constants.\n"
87 "message: The text to be logged.\n"
88 "data: The optional data parameter passed to the register function.\n"
89 " If the parameter was omitted it will be omitted here, too.";
91 static char reg_init_doc[] =
92 "register_init(callback[, data][, name]) -> identifier\n"
94 "Register a callback function that will be executed once after the "
96 "file has been read, all plugins heve been loaded and the collectd has\n"
97 "forked into the background.\n"
99 "'callback' is a callable object that will be executed.\n"
100 "'data' is an optional object that will be passed back to the callback\n"
101 " function when it is called.\n"
102 "'name' is an optional identifier for this callback. The default name\n"
103 " is 'python.<module>'.\n"
104 " Every callback needs a unique identifier, so if you want to\n"
105 " register this callback multiple time from the same module you need\n"
106 " to specify a name here.\n"
107 "'identifier' is the full identifier assigned to this callback.\n"
109 "The callback function will be called without parameters, except for\n"
110 "data if it was supplied.";
112 static char reg_config_doc[] =
113 "register_config(callback[, data][, name]) -> identifier\n"
115 "Register a callback function for config file entries.\n"
116 "'callback' is a callable object that will be called for every config "
118 "'data' is an optional object that will be passed back to the callback\n"
119 " function every time it is called.\n"
120 "'name' is an optional identifier for this callback. The default name\n"
121 " is 'python.<module>'.\n"
122 " Every callback needs a unique identifier, so if you want to\n"
123 " register this callback multiple time from the same module you need\n"
124 " to specify a name here.\n"
125 "'identifier' is the full identifier assigned to this callback.\n"
127 "The callback function will be called with one or two parameters:\n"
128 "config: A Config object.\n"
129 "data: The optional data parameter passed to the register function.\n"
130 " If the parameter was omitted it will be omitted here, too.";
132 static char reg_read_doc[] =
133 "register_read(callback[, interval][, data][, name]) -> identifier\n"
135 "Register a callback function for reading data. It will just be called\n"
136 "in a fixed interval to signal that it's time to dispatch new values.\n"
137 "'callback' is a callable object that will be called every time something\n"
139 "'interval' is the number of seconds between between calls to the "
141 " function. Full float precision is supported here.\n"
142 "'data' is an optional object that will be passed back to the callback\n"
143 " function every time it is called.\n"
144 "'name' is an optional identifier for this callback. The default name\n"
145 " is 'python.<module>'.\n"
146 " Every callback needs a unique identifier, so if you want to\n"
147 " register this callback multiple time from the same module you need\n"
148 " to specify a name here.\n"
149 "'identifier' is the full identifier assigned to this callback.\n"
151 "The callback function will be called without parameters, except for\n"
152 "data if it was supplied.";
154 static char reg_write_doc[] =
155 "register_write(callback[, data][, name]) -> identifier\n"
157 "Register a callback function to receive values dispatched by other "
159 "'callback' is a callable object that will be called every time a value\n"
161 "'data' is an optional object that will be passed back to the callback\n"
162 " function every time it is called.\n"
163 "'name' is an optional identifier for this callback. The default name\n"
164 " is 'python.<module>'.\n"
165 " Every callback needs a unique identifier, so if you want to\n"
166 " register this callback multiple time from the same module you need\n"
167 " to specify a name here.\n"
168 "'identifier' is the full identifier assigned to this callback.\n"
170 "The callback function will be called with one or two parameters:\n"
171 "values: A Values object which is a copy of the dispatched values.\n"
172 "data: The optional data parameter passed to the register function.\n"
173 " If the parameter was omitted it will be omitted here, too.";
175 static char reg_notification_doc[] =
176 "register_notification(callback[, data][, name]) -> identifier\n"
178 "Register a callback function for notifications.\n"
179 "'callback' is a callable object that will be called every time a "
182 "'data' is an optional object that will be passed back to the callback\n"
183 " function every time it is called.\n"
184 "'name' is an optional identifier for this callback. The default name\n"
185 " is 'python.<module>'.\n"
186 " Every callback needs a unique identifier, so if you want to\n"
187 " register this callback multiple time from the same module you need\n"
188 " to specify a name here.\n"
189 "'identifier' is the full identifier assigned to this callback.\n"
191 "The callback function will be called with one or two parameters:\n"
192 "notification: A copy of the notification that was dispatched.\n"
193 "data: The optional data parameter passed to the register function.\n"
194 " If the parameter was omitted it will be omitted here, too.";
196 static char reg_flush_doc[] =
197 "register_flush(callback[, data][, name]) -> identifier\n"
199 "Register a callback function for flush messages.\n"
200 "'callback' is a callable object that will be called every time a plugin\n"
201 " requests a flush for either this or all plugins.\n"
202 "'data' is an optional object that will be passed back to the callback\n"
203 " function every time it is called.\n"
204 "'name' is an optional identifier for this callback. The default name\n"
205 " is 'python.<module>'.\n"
206 " Every callback needs a unique identifier, so if you want to\n"
207 " register this callback multiple time from the same module you need\n"
208 " to specify a name here.\n"
209 "'identifier' is the full identifier assigned to this callback.\n"
211 "The callback function will be called with two or three parameters:\n"
212 "timeout: Indicates that only data older than 'timeout' seconds is to\n"
214 "id: Specifies which values are to be flushed. Might be None.\n"
215 "data: The optional data parameter passed to the register function.\n"
216 " If the parameter was omitted it will be omitted here, too.";
218 static char reg_shutdown_doc[] =
219 "register_shutdown(callback[, data][, name]) -> identifier\n"
221 "Register a callback function for collectd shutdown.\n"
222 "'callback' is a callable object that will be called once collectd is\n"
224 "'data' is an optional object that will be passed back to the callback\n"
225 " function if it is called.\n"
226 "'name' is an optional identifier for this callback. The default name\n"
227 " is 'python.<module>'.\n"
228 " Every callback needs a unique identifier, so if you want to\n"
229 " register this callback multiple time from the same module you need\n"
230 " to specify a name here.\n"
231 "'identifier' is the full identifier assigned to this callback.\n"
233 "The callback function will be called with no parameters except for\n"
234 " data if it was supplied.";
236 static char CollectdError_doc[] =
237 "Basic exception for collectd Python scripts.\n"
239 "Throwing this exception will not cause a stacktrace to be logged, \n"
240 "even if LogTraces is enabled in the config.";
242 static pthread_t main_thread;
243 static PyOS_sighandler_t python_sigint_handler;
244 static _Bool do_interactive = 0;
246 /* This is our global thread state. Python saves some stuff in thread-local
247 * storage. So if we allow the interpreter to run in the background
248 * (the scriptwriters might have created some threads from python), we have
249 * to save the state so we can resume it later after shutdown. */
251 static PyThreadState *state;
253 static PyObject *sys_path, *cpy_format_exception, *CollectdError;
255 static cpy_callback_t *cpy_config_callbacks;
256 static cpy_callback_t *cpy_init_callbacks;
257 static cpy_callback_t *cpy_shutdown_callbacks;
259 /* Make sure to hold the GIL while modifying these. */
260 static int cpy_shutdown_triggered = 0;
261 static int cpy_num_callbacks = 0;
263 static void cpy_destroy_user_data(void *data) {
264 cpy_callback_t *c = data;
267 Py_DECREF(c->callback);
271 if (!cpy_num_callbacks && cpy_shutdown_triggered) {
278 /* You must hold the GIL to call this function!
279 * But if you managed to extract the callback parameter then you probably
282 static void cpy_build_name(char *buf, size_t size, PyObject *callback,
284 const char *module = NULL;
285 PyObject *mod = NULL;
288 snprintf(buf, size, "python.%s", name);
292 mod = PyObject_GetAttrString(callback, "__module__"); /* New reference. */
294 module = cpy_unicode_or_bytes_to_string(&mod);
296 if (module != NULL) {
297 snprintf(buf, size, "python.%s", module);
304 snprintf(buf, size, "python.%p", callback);
308 void cpy_log_exception(const char *context) {
309 int l = 0, collectd_error;
310 const char *typename = NULL, *message = NULL;
311 PyObject *type, *value, *traceback, *tn, *m, *list;
313 PyErr_Fetch(&type, &value, &traceback);
314 PyErr_NormalizeException(&type, &value, &traceback);
317 collectd_error = PyErr_GivenExceptionMatches(value, CollectdError);
318 tn = PyObject_GetAttrString(type, "__name__"); /* New reference. */
319 m = PyObject_Str(value); /* New reference. */
321 typename = cpy_unicode_or_bytes_to_string(&tn);
323 message = cpy_unicode_or_bytes_to_string(&m);
324 if (typename == NULL)
325 typename = "NamelessException";
328 Py_BEGIN_ALLOW_THREADS;
329 if (collectd_error) {
330 WARNING("%s in %s: %s", typename, context, message);
332 ERROR("Unhandled python exception in %s: %s: %s", context, typename,
335 Py_END_ALLOW_THREADS;
338 if (!cpy_format_exception || !traceback || collectd_error) {
342 Py_XDECREF(traceback);
345 list = PyObject_CallFunction(cpy_format_exception, "NNN", type, value,
346 traceback); /* New reference. Steals references
347 from "type", "value" and
350 l = PyObject_Length(list);
352 for (int i = 0; i < l; ++i) {
357 line = PyList_GET_ITEM(list, i); /* Borrowed reference. */
360 msg = cpy_unicode_or_bytes_to_string(&line);
369 if (cpy[strlen(cpy) - 1] == '\n')
370 cpy[strlen(cpy) - 1] = 0;
372 Py_BEGIN_ALLOW_THREADS;
374 Py_END_ALLOW_THREADS;
383 static int cpy_read_callback(user_data_t *data) {
384 cpy_callback_t *c = data->data;
388 ret = PyObject_CallFunctionObjArgs(c->callback, c->data,
389 (void *)0); /* New reference. */
391 cpy_log_exception("read callback");
401 static int cpy_write_callback(const data_set_t *ds,
402 const value_list_t *value_list,
404 cpy_callback_t *c = data->data;
405 PyObject *ret, *list, *temp, *dict = NULL;
409 list = PyList_New(value_list->values_len); /* New reference. */
411 cpy_log_exception("write callback");
412 CPY_RETURN_FROM_THREADS 0;
414 for (size_t i = 0; i < value_list->values_len; ++i) {
415 if (ds->ds[i].type == DS_TYPE_COUNTER) {
417 list, i, PyLong_FromUnsignedLongLong(value_list->values[i].counter));
418 } else if (ds->ds[i].type == DS_TYPE_GAUGE) {
419 PyList_SetItem(list, i, PyFloat_FromDouble(value_list->values[i].gauge));
420 } else if (ds->ds[i].type == DS_TYPE_DERIVE) {
421 PyList_SetItem(list, i,
422 PyLong_FromLongLong(value_list->values[i].derive));
423 } else if (ds->ds[i].type == DS_TYPE_ABSOLUTE) {
425 list, i, PyLong_FromUnsignedLongLong(value_list->values[i].absolute));
427 Py_BEGIN_ALLOW_THREADS;
428 ERROR("cpy_write_callback: Unknown value type %d.", ds->ds[i].type);
429 Py_END_ALLOW_THREADS;
431 CPY_RETURN_FROM_THREADS 0;
433 if (PyErr_Occurred() != NULL) {
434 cpy_log_exception("value building for write callback");
436 CPY_RETURN_FROM_THREADS 0;
439 dict = PyDict_New(); /* New reference. */
440 if (value_list->meta) {
442 meta_data_t *meta = value_list->meta;
444 int num = meta_data_toc(meta, &table);
445 for (int i = 0; i < num; ++i) {
453 type = meta_data_type(meta, table[i]);
454 if (type == MD_TYPE_STRING) {
455 if (meta_data_get_string(meta, table[i], &string))
457 temp = cpy_string_to_unicode_or_bytes(string); /* New reference. */
459 PyDict_SetItemString(dict, table[i], temp);
461 } else if (type == MD_TYPE_SIGNED_INT) {
462 if (meta_data_get_signed_int(meta, table[i], &si))
464 PyObject *sival = PyLong_FromLongLong(si); /* New reference */
465 temp = PyObject_CallFunctionObjArgs((void *)&SignedType, sival,
466 (void *)0); /* New reference. */
467 PyDict_SetItemString(dict, table[i], temp);
470 } else if (type == MD_TYPE_UNSIGNED_INT) {
471 if (meta_data_get_unsigned_int(meta, table[i], &ui))
473 PyObject *uval = PyLong_FromUnsignedLongLong(ui); /* New reference */
474 temp = PyObject_CallFunctionObjArgs((void *)&UnsignedType, uval,
475 (void *)0); /* New reference. */
476 PyDict_SetItemString(dict, table[i], temp);
479 } else if (type == MD_TYPE_DOUBLE) {
480 if (meta_data_get_double(meta, table[i], &d))
482 temp = PyFloat_FromDouble(d); /* New reference. */
483 PyDict_SetItemString(dict, table[i], temp);
485 } else if (type == MD_TYPE_BOOLEAN) {
486 if (meta_data_get_boolean(meta, table[i], &b))
489 PyDict_SetItemString(dict, table[i], Py_True);
491 PyDict_SetItemString(dict, table[i], Py_False);
497 v = (Values *)Values_New(); /* New reference. */
498 sstrncpy(v->data.host, value_list->host, sizeof(v->data.host));
499 sstrncpy(v->data.type, value_list->type, sizeof(v->data.type));
500 sstrncpy(v->data.type_instance, value_list->type_instance,
501 sizeof(v->data.type_instance));
502 sstrncpy(v->data.plugin, value_list->plugin, sizeof(v->data.plugin));
503 sstrncpy(v->data.plugin_instance, value_list->plugin_instance,
504 sizeof(v->data.plugin_instance));
505 v->data.time = CDTIME_T_TO_DOUBLE(value_list->time);
506 v->interval = CDTIME_T_TO_DOUBLE(value_list->interval);
510 v->meta = dict; /* Steals a reference. */
511 ret = PyObject_CallFunctionObjArgs(c->callback, v, c->data,
512 (void *)0); /* New reference. */
515 cpy_log_exception("write callback");
523 static int cpy_notification_callback(const notification_t *notification,
525 cpy_callback_t *c = data->data;
526 PyObject *ret, *notify;
530 PyObject *dict = PyDict_New(); /* New reference. */
531 for (notification_meta_t *meta = notification->meta; meta != NULL;
533 PyObject *temp = NULL;
534 if (meta->type == NM_TYPE_STRING) {
535 temp = cpy_string_to_unicode_or_bytes(
536 meta->nm_value.nm_string); /* New reference. */
537 PyDict_SetItemString(dict, meta->name, temp);
539 } else if (meta->type == NM_TYPE_SIGNED_INT) {
540 PyObject *sival = PyLong_FromLongLong(meta->nm_value.nm_signed_int);
541 temp = PyObject_CallFunctionObjArgs((void *)&SignedType, sival,
542 (void *)0); /* New reference. */
543 PyDict_SetItemString(dict, meta->name, temp);
546 } else if (meta->type == NM_TYPE_UNSIGNED_INT) {
548 PyLong_FromUnsignedLongLong(meta->nm_value.nm_unsigned_int);
549 temp = PyObject_CallFunctionObjArgs((void *)&UnsignedType, uval,
550 (void *)0); /* New reference. */
551 PyDict_SetItemString(dict, meta->name, temp);
554 } else if (meta->type == NM_TYPE_DOUBLE) {
555 temp = PyFloat_FromDouble(meta->nm_value.nm_double); /* New reference. */
556 PyDict_SetItemString(dict, meta->name, temp);
558 } else if (meta->type == NM_TYPE_BOOLEAN) {
559 PyDict_SetItemString(dict, meta->name,
560 meta->nm_value.nm_boolean ? Py_True : Py_False);
563 notify = Notification_New(); /* New reference. */
564 n = (Notification *)notify;
565 sstrncpy(n->data.host, notification->host, sizeof(n->data.host));
566 sstrncpy(n->data.type, notification->type, sizeof(n->data.type));
567 sstrncpy(n->data.type_instance, notification->type_instance,
568 sizeof(n->data.type_instance));
569 sstrncpy(n->data.plugin, notification->plugin, sizeof(n->data.plugin));
570 sstrncpy(n->data.plugin_instance, notification->plugin_instance,
571 sizeof(n->data.plugin_instance));
572 n->data.time = CDTIME_T_TO_DOUBLE(notification->time);
573 sstrncpy(n->message, notification->message, sizeof(n->message));
574 n->severity = notification->severity;
576 n->meta = dict; /* Steals a reference. */
577 ret = PyObject_CallFunctionObjArgs(c->callback, n, c->data,
578 (void *)0); /* New reference. */
581 cpy_log_exception("notification callback");
589 static void cpy_log_callback(int severity, const char *message,
591 cpy_callback_t *c = data->data;
592 PyObject *ret, *text;
595 text = cpy_string_to_unicode_or_bytes(message); /* New reference. */
597 ret = PyObject_CallFunction(
598 c->callback, "iN", severity,
599 text); /* New reference. Steals a reference from "text". */
601 ret = PyObject_CallFunction(
602 c->callback, "iNO", severity, text,
603 c->data); /* New reference. Steals a reference from "text". */
607 /* Do we really want to trigger a log callback because a log callback
611 /* In case someone wanted to be clever, replaced stderr and failed at that.
620 static void cpy_flush_callback(int timeout, const char *id, user_data_t *data) {
621 cpy_callback_t *c = data->data;
622 PyObject *ret, *text;
626 text = cpy_string_to_unicode_or_bytes(id);
632 ret = PyObject_CallFunction(c->callback, "iN", timeout,
633 text); /* New reference. */
635 ret = PyObject_CallFunction(c->callback, "iNO", timeout, text,
636 c->data); /* New reference. */
639 cpy_log_exception("flush callback");
646 static PyObject *cpy_register_generic(cpy_callback_t **list_head,
647 PyObject *args, PyObject *kwds) {
651 PyObject *callback = NULL, *data = NULL, *mod = NULL;
652 static char *kwlist[] = {"callback", "data", "name", NULL};
654 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oet", kwlist, &callback, &data,
657 if (PyCallable_Check(callback) == 0) {
659 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
662 cpy_build_name(buf, sizeof(buf), callback, name);
667 c = calloc(1, sizeof(*c));
671 c->name = strdup(buf);
672 c->callback = callback;
674 c->next = *list_head;
679 return cpy_string_to_unicode_or_bytes(buf);
682 static PyObject *float_or_none(float number) {
686 return PyFloat_FromDouble(number);
689 static PyObject *cpy_get_dataset(PyObject *self, PyObject *args) {
691 const data_set_t *ds;
692 PyObject *list, *tuple;
694 if (PyArg_ParseTuple(args, "et", NULL, &name) == 0)
696 ds = plugin_get_ds(name);
699 PyErr_Format(PyExc_TypeError, "Dataset %s not found", name);
702 list = PyList_New(ds->ds_num); /* New reference. */
703 for (size_t i = 0; i < ds->ds_num; ++i) {
704 tuple = PyTuple_New(4);
705 PyTuple_SET_ITEM(tuple, 0, cpy_string_to_unicode_or_bytes(ds->ds[i].name));
708 cpy_string_to_unicode_or_bytes(DS_TYPE_TO_STRING(ds->ds[i].type)));
709 PyTuple_SET_ITEM(tuple, 2, float_or_none(ds->ds[i].min));
710 PyTuple_SET_ITEM(tuple, 3, float_or_none(ds->ds[i].max));
711 PyList_SET_ITEM(list, i, tuple);
716 static PyObject *cpy_flush(PyObject *self, PyObject *args, PyObject *kwds) {
718 char *plugin = NULL, *identifier = NULL;
719 static char *kwlist[] = {"plugin", "timeout", "identifier", NULL};
721 if (PyArg_ParseTupleAndKeywords(args, kwds, "|etiet", kwlist, NULL, &plugin,
722 &timeout, NULL, &identifier) == 0)
724 Py_BEGIN_ALLOW_THREADS;
725 plugin_flush(plugin, timeout, identifier);
726 Py_END_ALLOW_THREADS;
728 PyMem_Free(identifier);
732 static PyObject *cpy_register_config(PyObject *self, PyObject *args,
734 return cpy_register_generic(&cpy_config_callbacks, args, kwds);
737 static PyObject *cpy_register_init(PyObject *self, PyObject *args,
739 return cpy_register_generic(&cpy_init_callbacks, args, kwds);
742 typedef int reg_function_t(const char *name, void *callback, void *data);
744 static PyObject *cpy_register_generic_userdata(void *reg, void *handler,
745 PyObject *args, PyObject *kwds) {
747 reg_function_t *register_function = (reg_function_t *)reg;
748 cpy_callback_t *c = NULL;
750 PyObject *callback = NULL, *data = NULL;
751 static char *kwlist[] = {"callback", "data", "name", NULL};
753 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oet", kwlist, &callback, &data,
756 if (PyCallable_Check(callback) == 0) {
758 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
761 cpy_build_name(buf, sizeof(buf), callback, name);
767 c = calloc(1, sizeof(*c));
771 c->name = strdup(buf);
772 c->callback = callback;
776 register_function(buf, handler,
779 .free_func = cpy_destroy_user_data,
783 return cpy_string_to_unicode_or_bytes(buf);
786 static PyObject *cpy_register_read(PyObject *self, PyObject *args,
789 cpy_callback_t *c = NULL;
792 PyObject *callback = NULL, *data = NULL;
793 static char *kwlist[] = {"callback", "interval", "data", "name", NULL};
795 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|dOet", kwlist, &callback,
796 &interval, &data, NULL, &name) == 0)
798 if (PyCallable_Check(callback) == 0) {
800 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
803 cpy_build_name(buf, sizeof(buf), callback, name);
809 c = calloc(1, sizeof(*c));
813 c->name = strdup(buf);
814 c->callback = callback;
818 plugin_register_complex_read(
819 /* group = */ "python", buf, cpy_read_callback,
820 DOUBLE_TO_CDTIME_T(interval),
823 .free_func = cpy_destroy_user_data,
826 return cpy_string_to_unicode_or_bytes(buf);
829 static PyObject *cpy_register_log(PyObject *self, PyObject *args,
831 return cpy_register_generic_userdata((void *)plugin_register_log,
832 (void *)cpy_log_callback, args, kwds);
835 static PyObject *cpy_register_write(PyObject *self, PyObject *args,
837 return cpy_register_generic_userdata((void *)plugin_register_write,
838 (void *)cpy_write_callback, args, kwds);
841 static PyObject *cpy_register_notification(PyObject *self, PyObject *args,
843 return cpy_register_generic_userdata((void *)plugin_register_notification,
844 (void *)cpy_notification_callback, args,
848 static PyObject *cpy_register_flush(PyObject *self, PyObject *args,
850 return cpy_register_generic_userdata((void *)plugin_register_flush,
851 (void *)cpy_flush_callback, args, kwds);
854 static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args,
856 return cpy_register_generic(&cpy_shutdown_callbacks, args, kwds);
859 static PyObject *cpy_error(PyObject *self, PyObject *args) {
861 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
863 Py_BEGIN_ALLOW_THREADS;
864 plugin_log(LOG_ERR, "%s", text);
865 Py_END_ALLOW_THREADS;
870 static PyObject *cpy_warning(PyObject *self, PyObject *args) {
872 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
874 Py_BEGIN_ALLOW_THREADS;
875 plugin_log(LOG_WARNING, "%s", text);
876 Py_END_ALLOW_THREADS;
881 static PyObject *cpy_notice(PyObject *self, PyObject *args) {
883 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
885 Py_BEGIN_ALLOW_THREADS;
886 plugin_log(LOG_NOTICE, "%s", text);
887 Py_END_ALLOW_THREADS;
892 static PyObject *cpy_info(PyObject *self, PyObject *args) {
894 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
896 Py_BEGIN_ALLOW_THREADS;
897 plugin_log(LOG_INFO, "%s", text);
898 Py_END_ALLOW_THREADS;
903 static PyObject *cpy_debug(PyObject *self, PyObject *args) {
906 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
908 Py_BEGIN_ALLOW_THREADS;
909 plugin_log(LOG_DEBUG, "%s", text);
910 Py_END_ALLOW_THREADS;
916 static PyObject *cpy_unregister_generic(cpy_callback_t **list_head,
917 PyObject *arg, const char *desc) {
920 cpy_callback_t *prev = NULL, *tmp;
923 name = cpy_unicode_or_bytes_to_string(&arg);
926 if (!PyCallable_Check(arg)) {
927 PyErr_SetString(PyExc_TypeError, "This function needs a string or a "
928 "callable object as its only "
933 cpy_build_name(buf, sizeof(buf), arg, NULL);
936 for (tmp = *list_head; tmp; prev = tmp, tmp = tmp->next)
937 if (strcmp(name, tmp->name) == 0)
942 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.",
946 /* Yes, this is actually safe. To call this function the caller has to
947 * hold the GIL. Well, safe as long as there is only one GIL anyway ... */
949 *list_head = tmp->next;
951 prev->next = tmp->next;
952 cpy_destroy_user_data(tmp);
956 static void cpy_unregister_list(cpy_callback_t **list_head) {
957 cpy_callback_t *cur, *next;
958 for (cur = *list_head; cur; cur = next) {
960 cpy_destroy_user_data(cur);
965 typedef int cpy_unregister_function_t(const char *name);
968 cpy_unregister_generic_userdata(cpy_unregister_function_t *unreg, PyObject *arg,
974 name = cpy_unicode_or_bytes_to_string(&arg);
977 if (!PyCallable_Check(arg)) {
978 PyErr_SetString(PyExc_TypeError, "This function needs a string or a "
979 "callable object as its only "
984 cpy_build_name(buf, sizeof(buf), arg, NULL);
987 if (unreg(name) == 0) {
991 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.",
997 static PyObject *cpy_unregister_log(PyObject *self, PyObject *arg) {
998 return cpy_unregister_generic_userdata(plugin_unregister_log, arg, "log");
1001 static PyObject *cpy_unregister_init(PyObject *self, PyObject *arg) {
1002 return cpy_unregister_generic(&cpy_init_callbacks, arg, "init");
1005 static PyObject *cpy_unregister_config(PyObject *self, PyObject *arg) {
1006 return cpy_unregister_generic(&cpy_config_callbacks, arg, "config");
1009 static PyObject *cpy_unregister_read(PyObject *self, PyObject *arg) {
1010 return cpy_unregister_generic_userdata(plugin_unregister_read, arg, "read");
1013 static PyObject *cpy_unregister_write(PyObject *self, PyObject *arg) {
1014 return cpy_unregister_generic_userdata(plugin_unregister_write, arg, "write");
1017 static PyObject *cpy_unregister_notification(PyObject *self, PyObject *arg) {
1018 return cpy_unregister_generic_userdata(plugin_unregister_notification, arg,
1022 static PyObject *cpy_unregister_flush(PyObject *self, PyObject *arg) {
1023 return cpy_unregister_generic_userdata(plugin_unregister_flush, arg, "flush");
1026 static PyObject *cpy_unregister_shutdown(PyObject *self, PyObject *arg) {
1027 return cpy_unregister_generic(&cpy_shutdown_callbacks, arg, "shutdown");
1030 static PyMethodDef cpy_methods[] = {
1031 {"debug", cpy_debug, METH_VARARGS, log_doc},
1032 {"info", cpy_info, METH_VARARGS, log_doc},
1033 {"notice", cpy_notice, METH_VARARGS, log_doc},
1034 {"warning", cpy_warning, METH_VARARGS, log_doc},
1035 {"error", cpy_error, METH_VARARGS, log_doc},
1036 {"get_dataset", (PyCFunction)cpy_get_dataset, METH_VARARGS, get_ds_doc},
1037 {"flush", (PyCFunction)cpy_flush, METH_VARARGS | METH_KEYWORDS, flush_doc},
1038 {"register_log", (PyCFunction)cpy_register_log,
1039 METH_VARARGS | METH_KEYWORDS, reg_log_doc},
1040 {"register_init", (PyCFunction)cpy_register_init,
1041 METH_VARARGS | METH_KEYWORDS, reg_init_doc},
1042 {"register_config", (PyCFunction)cpy_register_config,
1043 METH_VARARGS | METH_KEYWORDS, reg_config_doc},
1044 {"register_read", (PyCFunction)cpy_register_read,
1045 METH_VARARGS | METH_KEYWORDS, reg_read_doc},
1046 {"register_write", (PyCFunction)cpy_register_write,
1047 METH_VARARGS | METH_KEYWORDS, reg_write_doc},
1048 {"register_notification", (PyCFunction)cpy_register_notification,
1049 METH_VARARGS | METH_KEYWORDS, reg_notification_doc},
1050 {"register_flush", (PyCFunction)cpy_register_flush,
1051 METH_VARARGS | METH_KEYWORDS, reg_flush_doc},
1052 {"register_shutdown", (PyCFunction)cpy_register_shutdown,
1053 METH_VARARGS | METH_KEYWORDS, reg_shutdown_doc},
1054 {"unregister_log", cpy_unregister_log, METH_O, unregister_doc},
1055 {"unregister_init", cpy_unregister_init, METH_O, unregister_doc},
1056 {"unregister_config", cpy_unregister_config, METH_O, unregister_doc},
1057 {"unregister_read", cpy_unregister_read, METH_O, unregister_doc},
1058 {"unregister_write", cpy_unregister_write, METH_O, unregister_doc},
1059 {"unregister_notification", cpy_unregister_notification, METH_O,
1061 {"unregister_flush", cpy_unregister_flush, METH_O, unregister_doc},
1062 {"unregister_shutdown", cpy_unregister_shutdown, METH_O, unregister_doc},
1065 static int cpy_shutdown(void) {
1070 "================================================================\n");
1072 "collectd shutdown while running an interactive session. This will\n");
1073 printf("probably leave your terminal in a mess.\n");
1074 printf("Run the command \"reset\" to get it back into a usable state.\n");
1075 printf("You can press Ctrl+D in the interactive session to\n");
1076 printf("close collectd and avoid this problem in the future.\n");
1078 "================================================================\n");
1083 for (cpy_callback_t *c = cpy_shutdown_callbacks; c; c = c->next) {
1084 ret = PyObject_CallFunctionObjArgs(c->callback, c->data,
1085 (void *)0); /* New reference. */
1087 cpy_log_exception("shutdown callback");
1093 Py_BEGIN_ALLOW_THREADS;
1094 cpy_unregister_list(&cpy_config_callbacks);
1095 cpy_unregister_list(&cpy_init_callbacks);
1096 cpy_unregister_list(&cpy_shutdown_callbacks);
1097 cpy_shutdown_triggered = 1;
1098 Py_END_ALLOW_THREADS;
1100 if (!cpy_num_callbacks) {
1109 static void *cpy_interactive(void *pipefd) {
1110 PyOS_sighandler_t cur_sig;
1112 /* Signal handler in a plugin? Bad stuff, but the best way to
1113 * handle it I guess. In an interactive session people will
1114 * press Ctrl+C at some time, which will generate a SIGINT.
1115 * This will cause collectd to shutdown, thus killing the
1116 * interactive interpreter, and leaving the terminal in a
1117 * mess. Chances are, this isn't what the user wanted to do.
1119 * So this is the plan:
1120 * 1. Restore Python's own signal handler
1121 * 2. Tell Python we just forked so it will accept this thread
1122 * as the main one. No version of Python will ever handle
1123 * interrupts anywhere but in the main thread.
1124 * 3. After the interactive loop is done, restore collectd's
1126 * 4. Raise SIGINT for a clean shutdown. The signal is sent to
1127 * the main thread to ensure it wakes up the main interval
1128 * sleep so that collectd shuts down immediately not in 10
1131 * This will make sure that SIGINT won't kill collectd but
1132 * still interrupt syscalls like sleep and pause. */
1134 if (PyImport_ImportModule("readline") == NULL) {
1135 /* This interactive session will suck. */
1136 cpy_log_exception("interactive session init");
1138 cur_sig = PyOS_setsig(SIGINT, python_sigint_handler);
1140 PyEval_InitThreads();
1141 close(*(int *)pipefd);
1142 PyRun_InteractiveLoop(stdin, "<stdin>");
1143 PyOS_setsig(SIGINT, cur_sig);
1145 state = PyEval_SaveThread();
1146 NOTICE("python: Interactive interpreter exited, stopping collectd ...");
1147 pthread_kill(main_thread, SIGINT);
1151 static int cpy_init(void) {
1155 static pthread_t thread;
1157 if (!Py_IsInitialized()) {
1158 WARNING("python: Plugin loaded but not configured.");
1159 plugin_unregister_shutdown("python");
1163 main_thread = pthread_self();
1164 if (do_interactive) {
1166 ERROR("python: Unable to create pipe.");
1169 if (plugin_thread_create(&thread, NULL, cpy_interactive, pipefd + 1,
1170 "python interpreter")) {
1171 ERROR("python: Error creating thread for interactive interpreter.");
1173 if (read(pipefd[0], &buf, 1))
1175 (void)close(pipefd[0]);
1177 PyEval_InitThreads();
1178 state = PyEval_SaveThread();
1181 for (cpy_callback_t *c = cpy_init_callbacks; c; c = c->next) {
1182 ret = PyObject_CallFunctionObjArgs(c->callback, c->data,
1183 (void *)0); /* New reference. */
1185 cpy_log_exception("init callback");
1194 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
1195 PyObject *item, *values, *children, *tmp;
1200 values = PyTuple_New(ci->values_num); /* New reference. */
1201 for (int i = 0; i < ci->values_num; ++i) {
1202 if (ci->values[i].type == OCONFIG_TYPE_STRING) {
1205 cpy_string_to_unicode_or_bytes(ci->values[i].value.string));
1206 } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
1207 PyTuple_SET_ITEM(values, i,
1208 PyFloat_FromDouble(ci->values[i].value.number));
1209 } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
1210 PyTuple_SET_ITEM(values, i, PyBool_FromLong(ci->values[i].value.boolean));
1214 tmp = cpy_string_to_unicode_or_bytes(ci->key);
1215 item = PyObject_CallFunction((void *)&ConfigType, "NONO", tmp, parent, values,
1219 children = PyTuple_New(ci->children_num); /* New reference. */
1220 for (int i = 0; i < ci->children_num; ++i) {
1221 PyTuple_SET_ITEM(children, i,
1222 cpy_oconfig_to_pyconfig(ci->children + i, item));
1224 tmp = ((Config *)item)->children;
1225 ((Config *)item)->children = children;
1231 static struct PyModuleDef collectdmodule = {
1232 PyModuleDef_HEAD_INIT, "collectd", /* name of module */
1233 "The python interface to collectd", /* module documentation, may be NULL */
1236 PyMODINIT_FUNC PyInit_collectd(void) {
1237 return PyModule_Create(&collectdmodule);
1241 static int cpy_init_python(void) {
1242 PyOS_sighandler_t cur_sig;
1243 PyObject *sys, *errordict;
1247 wchar_t *argv = L"";
1248 /* Add a builtin module, before Py_Initialize */
1249 PyImport_AppendInittab("collectd", PyInit_collectd);
1254 /* Chances are the current signal handler is already SIG_DFL, but let's make
1256 cur_sig = PyOS_setsig(SIGINT, SIG_DFL);
1258 python_sigint_handler = PyOS_setsig(SIGINT, cur_sig);
1260 PyType_Ready(&ConfigType);
1261 PyType_Ready(&PluginDataType);
1262 ValuesType.tp_base = &PluginDataType;
1263 PyType_Ready(&ValuesType);
1264 NotificationType.tp_base = &PluginDataType;
1265 PyType_Ready(&NotificationType);
1266 SignedType.tp_base = &PyLong_Type;
1267 PyType_Ready(&SignedType);
1268 UnsignedType.tp_base = &PyLong_Type;
1269 PyType_Ready(&UnsignedType);
1270 errordict = PyDict_New();
1271 PyDict_SetItemString(
1272 errordict, "__doc__",
1273 cpy_string_to_unicode_or_bytes(CollectdError_doc)); /* New reference. */
1274 CollectdError = PyErr_NewException("collectd.CollectdError", NULL, errordict);
1275 sys = PyImport_ImportModule("sys"); /* New reference. */
1277 cpy_log_exception("python initialization");
1280 sys_path = PyObject_GetAttrString(sys, "path"); /* New reference. */
1282 if (sys_path == NULL) {
1283 cpy_log_exception("python initialization");
1286 PySys_SetArgv(1, &argv);
1287 PyList_SetSlice(sys_path, 0, 1, NULL);
1290 module = PyImport_ImportModule("collectd");
1292 module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
1294 PyModule_AddObject(module, "Config",
1295 (void *)&ConfigType); /* Steals a reference. */
1296 PyModule_AddObject(module, "Values",
1297 (void *)&ValuesType); /* Steals a reference. */
1298 PyModule_AddObject(module, "Notification",
1299 (void *)&NotificationType); /* Steals a reference. */
1300 PyModule_AddObject(module, "Signed",
1301 (void *)&SignedType); /* Steals a reference. */
1302 PyModule_AddObject(module, "Unsigned",
1303 (void *)&UnsignedType); /* Steals a reference. */
1304 Py_XINCREF(CollectdError);
1305 PyModule_AddObject(module, "CollectdError",
1306 CollectdError); /* Steals a reference. */
1307 PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG);
1308 PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO);
1309 PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE);
1310 PyModule_AddIntConstant(module, "LOG_WARNING", LOG_WARNING);
1311 PyModule_AddIntConstant(module, "LOG_ERROR", LOG_ERR);
1312 PyModule_AddIntConstant(module, "NOTIF_FAILURE", NOTIF_FAILURE);
1313 PyModule_AddIntConstant(module, "NOTIF_WARNING", NOTIF_WARNING);
1314 PyModule_AddIntConstant(module, "NOTIF_OKAY", NOTIF_OKAY);
1315 PyModule_AddStringConstant(module, "DS_TYPE_COUNTER",
1316 DS_TYPE_TO_STRING(DS_TYPE_COUNTER));
1317 PyModule_AddStringConstant(module, "DS_TYPE_GAUGE",
1318 DS_TYPE_TO_STRING(DS_TYPE_GAUGE));
1319 PyModule_AddStringConstant(module, "DS_TYPE_DERIVE",
1320 DS_TYPE_TO_STRING(DS_TYPE_DERIVE));
1321 PyModule_AddStringConstant(module, "DS_TYPE_ABSOLUTE",
1322 DS_TYPE_TO_STRING(DS_TYPE_ABSOLUTE));
1326 static int cpy_config(oconfig_item_t *ci) {
1330 /* Ok in theory we shouldn't do initialization at this point
1331 * but we have to. In order to give python scripts a chance
1332 * to register a config callback we need to be able to execute
1333 * python code during the config callback so we have to start
1334 * the interpreter here. */
1335 /* Do *not* use the python "thread" module at this point! */
1337 if (!Py_IsInitialized() && cpy_init_python())
1340 for (int i = 0; i < ci->children_num; ++i) {
1341 oconfig_item_t *item = ci->children + i;
1343 if (strcasecmp(item->key, "Interactive") == 0) {
1344 if (cf_util_get_boolean(item, &do_interactive) != 0) {
1348 } else if (strcasecmp(item->key, "Encoding") == 0) {
1349 char *encoding = NULL;
1350 if (cf_util_get_string(item, &encoding) != 0) {
1355 ERROR("python: \"Encoding\" was used in the config file but Python3 was "
1356 "used, which does not support changing encodings");
1361 /* Why is this even necessary? And undocumented? */
1362 if (PyUnicode_SetDefaultEncoding(encoding)) {
1363 cpy_log_exception("setting default encoding");
1368 } else if (strcasecmp(item->key, "LogTraces") == 0) {
1370 if (cf_util_get_boolean(item, &log_traces) != 0) {
1375 Py_XDECREF(cpy_format_exception);
1376 cpy_format_exception = NULL;
1379 if (cpy_format_exception)
1381 tb = PyImport_ImportModule("traceback"); /* New reference. */
1383 cpy_log_exception("python initialization");
1387 cpy_format_exception =
1388 PyObject_GetAttrString(tb, "format_exception"); /* New reference. */
1390 if (cpy_format_exception == NULL) {
1391 cpy_log_exception("python initialization");
1394 } else if (strcasecmp(item->key, "ModulePath") == 0) {
1396 PyObject *dir_object;
1398 if (cf_util_get_string(item, &dir) != 0) {
1402 dir_object = cpy_string_to_unicode_or_bytes(dir); /* New reference. */
1403 if (dir_object == NULL) {
1404 ERROR("python plugin: Unable to convert \"%s\" to "
1408 cpy_log_exception("python initialization");
1412 if (PyList_Insert(sys_path, 0, dir_object) != 0) {
1413 ERROR("python plugin: Unable to prepend \"%s\" to "
1414 "python module path.",
1416 cpy_log_exception("python initialization");
1419 Py_DECREF(dir_object);
1421 } else if (strcasecmp(item->key, "Import") == 0) {
1422 char *module_name = NULL;
1425 if (cf_util_get_string(item, &module_name) != 0) {
1429 module = PyImport_ImportModule(module_name); /* New reference. */
1430 if (module == NULL) {
1431 ERROR("python plugin: Error importing module \"%s\".", module_name);
1432 cpy_log_exception("importing module");
1437 } else if (strcasecmp(item->key, "Module") == 0) {
1442 if (cf_util_get_string(item, &name) != 0) {
1446 for (c = cpy_config_callbacks; c; c = c->next) {
1447 if (strcasecmp(c->name + 7, name) == 0)
1451 WARNING("python plugin: Found a configuration for the \"%s\" plugin, "
1452 "but the plugin isn't loaded or didn't register "
1453 "a configuration callback.",
1459 if (c->data == NULL)
1460 ret = PyObject_CallFunction(
1462 cpy_oconfig_to_pyconfig(item, NULL)); /* New reference. */
1464 ret = PyObject_CallFunction(c->callback, "NO",
1465 cpy_oconfig_to_pyconfig(item, NULL),
1466 c->data); /* New reference. */
1468 cpy_log_exception("loading module");
1473 ERROR("python plugin: Unknown config key \"%s\".", item->key);
1480 void module_register(void) {
1481 plugin_register_complex_config("python", cpy_config);
1482 plugin_register_init("python", cpy_init);
1483 plugin_register_shutdown("python", cpy_shutdown);