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;
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;
261 static int cpy_num_callbacks;
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));
706 PyTuple_SET_ITEM(tuple, 1, cpy_string_to_unicode_or_bytes(
707 DS_TYPE_TO_STRING(ds->ds[i].type)));
708 PyTuple_SET_ITEM(tuple, 2, float_or_none(ds->ds[i].min));
709 PyTuple_SET_ITEM(tuple, 3, float_or_none(ds->ds[i].max));
710 PyList_SET_ITEM(list, i, tuple);
715 static PyObject *cpy_flush(PyObject *self, PyObject *args, PyObject *kwds) {
717 char *plugin = NULL, *identifier = NULL;
718 static char *kwlist[] = {"plugin", "timeout", "identifier", NULL};
720 if (PyArg_ParseTupleAndKeywords(args, kwds, "|etiet", kwlist, NULL, &plugin,
721 &timeout, NULL, &identifier) == 0)
723 Py_BEGIN_ALLOW_THREADS;
724 plugin_flush(plugin, timeout, identifier);
725 Py_END_ALLOW_THREADS;
727 PyMem_Free(identifier);
731 static PyObject *cpy_register_config(PyObject *self, PyObject *args,
733 return cpy_register_generic(&cpy_config_callbacks, args, kwds);
736 static PyObject *cpy_register_init(PyObject *self, PyObject *args,
738 return cpy_register_generic(&cpy_init_callbacks, args, kwds);
741 typedef int reg_function_t(const char *name, void *callback, void *data);
743 static PyObject *cpy_register_generic_userdata(void *reg, void *handler,
744 PyObject *args, PyObject *kwds) {
746 reg_function_t *register_function = (reg_function_t *)reg;
747 cpy_callback_t *c = NULL;
749 PyObject *callback = NULL, *data = NULL;
750 static char *kwlist[] = {"callback", "data", "name", NULL};
752 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oet", kwlist, &callback, &data,
755 if (PyCallable_Check(callback) == 0) {
757 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
760 cpy_build_name(buf, sizeof(buf), callback, name);
766 c = calloc(1, sizeof(*c));
770 c->name = strdup(buf);
771 c->callback = callback;
775 register_function(buf, handler,
777 .data = c, .free_func = cpy_destroy_user_data,
781 return cpy_string_to_unicode_or_bytes(buf);
784 static PyObject *cpy_register_read(PyObject *self, PyObject *args,
787 cpy_callback_t *c = NULL;
790 PyObject *callback = NULL, *data = NULL;
791 static char *kwlist[] = {"callback", "interval", "data", "name", NULL};
793 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|dOet", kwlist, &callback,
794 &interval, &data, NULL, &name) == 0)
796 if (PyCallable_Check(callback) == 0) {
798 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
801 cpy_build_name(buf, sizeof(buf), callback, name);
807 c = calloc(1, sizeof(*c));
811 c->name = strdup(buf);
812 c->callback = callback;
816 plugin_register_complex_read(
817 /* group = */ "python", buf, cpy_read_callback,
818 DOUBLE_TO_CDTIME_T(interval),
820 .data = c, .free_func = cpy_destroy_user_data,
823 return cpy_string_to_unicode_or_bytes(buf);
826 static PyObject *cpy_register_log(PyObject *self, PyObject *args,
828 return cpy_register_generic_userdata((void *)plugin_register_log,
829 (void *)cpy_log_callback, args, kwds);
832 static PyObject *cpy_register_write(PyObject *self, PyObject *args,
834 return cpy_register_generic_userdata((void *)plugin_register_write,
835 (void *)cpy_write_callback, args, kwds);
838 static PyObject *cpy_register_notification(PyObject *self, PyObject *args,
840 return cpy_register_generic_userdata((void *)plugin_register_notification,
841 (void *)cpy_notification_callback, args,
845 static PyObject *cpy_register_flush(PyObject *self, PyObject *args,
847 return cpy_register_generic_userdata((void *)plugin_register_flush,
848 (void *)cpy_flush_callback, args, kwds);
851 static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args,
853 return cpy_register_generic(&cpy_shutdown_callbacks, args, kwds);
856 static PyObject *cpy_error(PyObject *self, PyObject *args) {
858 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
860 Py_BEGIN_ALLOW_THREADS;
861 plugin_log(LOG_ERR, "%s", text);
862 Py_END_ALLOW_THREADS;
867 static PyObject *cpy_warning(PyObject *self, PyObject *args) {
869 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
871 Py_BEGIN_ALLOW_THREADS;
872 plugin_log(LOG_WARNING, "%s", text);
873 Py_END_ALLOW_THREADS;
878 static PyObject *cpy_notice(PyObject *self, PyObject *args) {
880 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
882 Py_BEGIN_ALLOW_THREADS;
883 plugin_log(LOG_NOTICE, "%s", text);
884 Py_END_ALLOW_THREADS;
889 static PyObject *cpy_info(PyObject *self, PyObject *args) {
891 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
893 Py_BEGIN_ALLOW_THREADS;
894 plugin_log(LOG_INFO, "%s", text);
895 Py_END_ALLOW_THREADS;
900 static PyObject *cpy_debug(PyObject *self, PyObject *args) {
903 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
905 Py_BEGIN_ALLOW_THREADS;
906 plugin_log(LOG_DEBUG, "%s", text);
907 Py_END_ALLOW_THREADS;
913 static PyObject *cpy_unregister_generic(cpy_callback_t **list_head,
914 PyObject *arg, const char *desc) {
917 cpy_callback_t *prev = NULL, *tmp;
920 name = cpy_unicode_or_bytes_to_string(&arg);
923 if (!PyCallable_Check(arg)) {
924 PyErr_SetString(PyExc_TypeError, "This function needs a string or a "
925 "callable object as its only "
930 cpy_build_name(buf, sizeof(buf), arg, NULL);
933 for (tmp = *list_head; tmp; prev = tmp, tmp = tmp->next)
934 if (strcmp(name, tmp->name) == 0)
939 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.",
943 /* Yes, this is actually safe. To call this function the caller has to
944 * hold the GIL. Well, safe as long as there is only one GIL anyway ... */
946 *list_head = tmp->next;
948 prev->next = tmp->next;
949 cpy_destroy_user_data(tmp);
953 static void cpy_unregister_list(cpy_callback_t **list_head) {
954 cpy_callback_t *cur, *next;
955 for (cur = *list_head; cur; cur = next) {
957 cpy_destroy_user_data(cur);
962 typedef int cpy_unregister_function_t(const char *name);
965 cpy_unregister_generic_userdata(cpy_unregister_function_t *unreg, PyObject *arg,
971 name = cpy_unicode_or_bytes_to_string(&arg);
974 if (!PyCallable_Check(arg)) {
975 PyErr_SetString(PyExc_TypeError, "This function needs a string or a "
976 "callable object as its only "
981 cpy_build_name(buf, sizeof(buf), arg, NULL);
984 if (unreg(name) == 0) {
988 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.",
994 static PyObject *cpy_unregister_log(PyObject *self, PyObject *arg) {
995 return cpy_unregister_generic_userdata(plugin_unregister_log, arg, "log");
998 static PyObject *cpy_unregister_init(PyObject *self, PyObject *arg) {
999 return cpy_unregister_generic(&cpy_init_callbacks, arg, "init");
1002 static PyObject *cpy_unregister_config(PyObject *self, PyObject *arg) {
1003 return cpy_unregister_generic(&cpy_config_callbacks, arg, "config");
1006 static PyObject *cpy_unregister_read(PyObject *self, PyObject *arg) {
1007 return cpy_unregister_generic_userdata(plugin_unregister_read, arg, "read");
1010 static PyObject *cpy_unregister_write(PyObject *self, PyObject *arg) {
1011 return cpy_unregister_generic_userdata(plugin_unregister_write, arg, "write");
1014 static PyObject *cpy_unregister_notification(PyObject *self, PyObject *arg) {
1015 return cpy_unregister_generic_userdata(plugin_unregister_notification, arg,
1019 static PyObject *cpy_unregister_flush(PyObject *self, PyObject *arg) {
1020 return cpy_unregister_generic_userdata(plugin_unregister_flush, arg, "flush");
1023 static PyObject *cpy_unregister_shutdown(PyObject *self, PyObject *arg) {
1024 return cpy_unregister_generic(&cpy_shutdown_callbacks, arg, "shutdown");
1027 static PyMethodDef cpy_methods[] = {
1028 {"debug", cpy_debug, METH_VARARGS, log_doc},
1029 {"info", cpy_info, METH_VARARGS, log_doc},
1030 {"notice", cpy_notice, METH_VARARGS, log_doc},
1031 {"warning", cpy_warning, METH_VARARGS, log_doc},
1032 {"error", cpy_error, METH_VARARGS, log_doc},
1033 {"get_dataset", (PyCFunction)cpy_get_dataset, METH_VARARGS, get_ds_doc},
1034 {"flush", (PyCFunction)cpy_flush, METH_VARARGS | METH_KEYWORDS, flush_doc},
1035 {"register_log", (PyCFunction)cpy_register_log,
1036 METH_VARARGS | METH_KEYWORDS, reg_log_doc},
1037 {"register_init", (PyCFunction)cpy_register_init,
1038 METH_VARARGS | METH_KEYWORDS, reg_init_doc},
1039 {"register_config", (PyCFunction)cpy_register_config,
1040 METH_VARARGS | METH_KEYWORDS, reg_config_doc},
1041 {"register_read", (PyCFunction)cpy_register_read,
1042 METH_VARARGS | METH_KEYWORDS, reg_read_doc},
1043 {"register_write", (PyCFunction)cpy_register_write,
1044 METH_VARARGS | METH_KEYWORDS, reg_write_doc},
1045 {"register_notification", (PyCFunction)cpy_register_notification,
1046 METH_VARARGS | METH_KEYWORDS, reg_notification_doc},
1047 {"register_flush", (PyCFunction)cpy_register_flush,
1048 METH_VARARGS | METH_KEYWORDS, reg_flush_doc},
1049 {"register_shutdown", (PyCFunction)cpy_register_shutdown,
1050 METH_VARARGS | METH_KEYWORDS, reg_shutdown_doc},
1051 {"unregister_log", cpy_unregister_log, METH_O, unregister_doc},
1052 {"unregister_init", cpy_unregister_init, METH_O, unregister_doc},
1053 {"unregister_config", cpy_unregister_config, METH_O, unregister_doc},
1054 {"unregister_read", cpy_unregister_read, METH_O, unregister_doc},
1055 {"unregister_write", cpy_unregister_write, METH_O, unregister_doc},
1056 {"unregister_notification", cpy_unregister_notification, METH_O,
1058 {"unregister_flush", cpy_unregister_flush, METH_O, unregister_doc},
1059 {"unregister_shutdown", cpy_unregister_shutdown, METH_O, unregister_doc},
1062 static int cpy_shutdown(void) {
1067 "================================================================\n");
1069 "collectd shutdown while running an interactive session. This will\n");
1070 printf("probably leave your terminal in a mess.\n");
1071 printf("Run the command \"reset\" to get it back into a usable state.\n");
1072 printf("You can press Ctrl+D in the interactive session to\n");
1073 printf("close collectd and avoid this problem in the future.\n");
1075 "================================================================\n");
1080 for (cpy_callback_t *c = cpy_shutdown_callbacks; c; c = c->next) {
1081 ret = PyObject_CallFunctionObjArgs(c->callback, c->data,
1082 (void *)0); /* New reference. */
1084 cpy_log_exception("shutdown callback");
1090 Py_BEGIN_ALLOW_THREADS;
1091 cpy_unregister_list(&cpy_config_callbacks);
1092 cpy_unregister_list(&cpy_init_callbacks);
1093 cpy_unregister_list(&cpy_shutdown_callbacks);
1094 cpy_shutdown_triggered = 1;
1095 Py_END_ALLOW_THREADS;
1097 if (!cpy_num_callbacks) {
1106 static void *cpy_interactive(void *pipefd) {
1107 PyOS_sighandler_t cur_sig;
1109 /* Signal handler in a plugin? Bad stuff, but the best way to
1110 * handle it I guess. In an interactive session people will
1111 * press Ctrl+C at some time, which will generate a SIGINT.
1112 * This will cause collectd to shutdown, thus killing the
1113 * interactive interpreter, and leaving the terminal in a
1114 * mess. Chances are, this isn't what the user wanted to do.
1116 * So this is the plan:
1117 * 1. Restore Python's own signal handler
1118 * 2. Tell Python we just forked so it will accept this thread
1119 * as the main one. No version of Python will ever handle
1120 * interrupts anywhere but in the main thread.
1121 * 3. After the interactive loop is done, restore collectd's
1123 * 4. Raise SIGINT for a clean shutdown. The signal is sent to
1124 * the main thread to ensure it wakes up the main interval
1125 * sleep so that collectd shuts down immediately not in 10
1128 * This will make sure that SIGINT won't kill collectd but
1129 * still interrupt syscalls like sleep and pause. */
1131 if (PyImport_ImportModule("readline") == NULL) {
1132 /* This interactive session will suck. */
1133 cpy_log_exception("interactive session init");
1135 cur_sig = PyOS_setsig(SIGINT, python_sigint_handler);
1137 PyEval_InitThreads();
1138 close(*(int *)pipefd);
1139 PyRun_InteractiveLoop(stdin, "<stdin>");
1140 PyOS_setsig(SIGINT, cur_sig);
1142 state = PyEval_SaveThread();
1143 NOTICE("python: Interactive interpreter exited, stopping collectd ...");
1144 pthread_kill(main_thread, SIGINT);
1148 static int cpy_init(void) {
1152 static pthread_t thread;
1154 if (!Py_IsInitialized()) {
1155 WARNING("python: Plugin loaded but not configured.");
1156 plugin_unregister_shutdown("python");
1160 main_thread = pthread_self();
1161 if (do_interactive) {
1163 ERROR("python: Unable to create pipe.");
1166 if (plugin_thread_create(&thread, NULL, cpy_interactive, pipefd + 1,
1167 "python interpreter")) {
1168 ERROR("python: Error creating thread for interactive interpreter.");
1170 if (read(pipefd[0], &buf, 1))
1172 (void)close(pipefd[0]);
1174 PyEval_InitThreads();
1175 state = PyEval_SaveThread();
1178 for (cpy_callback_t *c = cpy_init_callbacks; c; c = c->next) {
1179 ret = PyObject_CallFunctionObjArgs(c->callback, c->data,
1180 (void *)0); /* New reference. */
1182 cpy_log_exception("init callback");
1191 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
1192 PyObject *item, *values, *children, *tmp;
1197 values = PyTuple_New(ci->values_num); /* New reference. */
1198 for (int i = 0; i < ci->values_num; ++i) {
1199 if (ci->values[i].type == OCONFIG_TYPE_STRING) {
1200 PyTuple_SET_ITEM(values, i, cpy_string_to_unicode_or_bytes(
1201 ci->values[i].value.string));
1202 } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
1203 PyTuple_SET_ITEM(values, i,
1204 PyFloat_FromDouble(ci->values[i].value.number));
1205 } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
1206 PyTuple_SET_ITEM(values, i, PyBool_FromLong(ci->values[i].value.boolean));
1210 tmp = cpy_string_to_unicode_or_bytes(ci->key);
1211 item = PyObject_CallFunction((void *)&ConfigType, "NONO", tmp, parent, values,
1215 children = PyTuple_New(ci->children_num); /* New reference. */
1216 for (int i = 0; i < ci->children_num; ++i) {
1217 PyTuple_SET_ITEM(children, i,
1218 cpy_oconfig_to_pyconfig(ci->children + i, item));
1220 tmp = ((Config *)item)->children;
1221 ((Config *)item)->children = children;
1227 static struct PyModuleDef collectdmodule = {
1228 PyModuleDef_HEAD_INIT, "collectd", /* name of module */
1229 "The python interface to collectd", /* module documentation, may be NULL */
1232 PyMODINIT_FUNC PyInit_collectd(void) {
1233 return PyModule_Create(&collectdmodule);
1237 static int cpy_init_python(void) {
1238 PyOS_sighandler_t cur_sig;
1239 PyObject *sys, *errordict;
1243 wchar_t *argv = L"";
1244 /* Add a builtin module, before Py_Initialize */
1245 PyImport_AppendInittab("collectd", PyInit_collectd);
1250 /* Chances are the current signal handler is already SIG_DFL, but let's make
1252 cur_sig = PyOS_setsig(SIGINT, SIG_DFL);
1254 python_sigint_handler = PyOS_setsig(SIGINT, cur_sig);
1256 PyType_Ready(&ConfigType);
1257 PyType_Ready(&PluginDataType);
1258 ValuesType.tp_base = &PluginDataType;
1259 PyType_Ready(&ValuesType);
1260 NotificationType.tp_base = &PluginDataType;
1261 PyType_Ready(&NotificationType);
1262 SignedType.tp_base = &PyLong_Type;
1263 PyType_Ready(&SignedType);
1264 UnsignedType.tp_base = &PyLong_Type;
1265 PyType_Ready(&UnsignedType);
1266 errordict = PyDict_New();
1267 PyDict_SetItemString(
1268 errordict, "__doc__",
1269 cpy_string_to_unicode_or_bytes(CollectdError_doc)); /* New reference. */
1270 CollectdError = PyErr_NewException("collectd.CollectdError", NULL, errordict);
1271 sys = PyImport_ImportModule("sys"); /* New reference. */
1273 cpy_log_exception("python initialization");
1276 sys_path = PyObject_GetAttrString(sys, "path"); /* New reference. */
1278 if (sys_path == NULL) {
1279 cpy_log_exception("python initialization");
1282 PySys_SetArgv(1, &argv);
1283 PyList_SetSlice(sys_path, 0, 1, NULL);
1286 module = PyImport_ImportModule("collectd");
1288 module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
1290 PyModule_AddObject(module, "Config",
1291 (void *)&ConfigType); /* Steals a reference. */
1292 PyModule_AddObject(module, "Values",
1293 (void *)&ValuesType); /* Steals a reference. */
1294 PyModule_AddObject(module, "Notification",
1295 (void *)&NotificationType); /* Steals a reference. */
1296 PyModule_AddObject(module, "Signed",
1297 (void *)&SignedType); /* Steals a reference. */
1298 PyModule_AddObject(module, "Unsigned",
1299 (void *)&UnsignedType); /* Steals a reference. */
1300 Py_XINCREF(CollectdError);
1301 PyModule_AddObject(module, "CollectdError",
1302 CollectdError); /* Steals a reference. */
1303 PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG);
1304 PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO);
1305 PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE);
1306 PyModule_AddIntConstant(module, "LOG_WARNING", LOG_WARNING);
1307 PyModule_AddIntConstant(module, "LOG_ERROR", LOG_ERR);
1308 PyModule_AddIntConstant(module, "NOTIF_FAILURE", NOTIF_FAILURE);
1309 PyModule_AddIntConstant(module, "NOTIF_WARNING", NOTIF_WARNING);
1310 PyModule_AddIntConstant(module, "NOTIF_OKAY", NOTIF_OKAY);
1311 PyModule_AddStringConstant(module, "DS_TYPE_COUNTER",
1312 DS_TYPE_TO_STRING(DS_TYPE_COUNTER));
1313 PyModule_AddStringConstant(module, "DS_TYPE_GAUGE",
1314 DS_TYPE_TO_STRING(DS_TYPE_GAUGE));
1315 PyModule_AddStringConstant(module, "DS_TYPE_DERIVE",
1316 DS_TYPE_TO_STRING(DS_TYPE_DERIVE));
1317 PyModule_AddStringConstant(module, "DS_TYPE_ABSOLUTE",
1318 DS_TYPE_TO_STRING(DS_TYPE_ABSOLUTE));
1322 static int cpy_config(oconfig_item_t *ci) {
1326 /* Ok in theory we shouldn't do initialization at this point
1327 * but we have to. In order to give python scripts a chance
1328 * to register a config callback we need to be able to execute
1329 * python code during the config callback so we have to start
1330 * the interpreter here. */
1331 /* Do *not* use the python "thread" module at this point! */
1333 if (!Py_IsInitialized() && cpy_init_python())
1336 for (int i = 0; i < ci->children_num; ++i) {
1337 oconfig_item_t *item = ci->children + i;
1339 if (strcasecmp(item->key, "Interactive") == 0) {
1340 if (cf_util_get_boolean(item, &do_interactive) != 0) {
1344 } else if (strcasecmp(item->key, "Encoding") == 0) {
1345 char *encoding = NULL;
1346 if (cf_util_get_string(item, &encoding) != 0) {
1351 ERROR("python: \"Encoding\" was used in the config file but Python3 was "
1352 "used, which does not support changing encodings");
1357 /* Why is this even necessary? And undocumented? */
1358 if (PyUnicode_SetDefaultEncoding(encoding)) {
1359 cpy_log_exception("setting default encoding");
1364 } else if (strcasecmp(item->key, "LogTraces") == 0) {
1366 if (cf_util_get_boolean(item, &log_traces) != 0) {
1371 Py_XDECREF(cpy_format_exception);
1372 cpy_format_exception = NULL;
1375 if (cpy_format_exception)
1377 tb = PyImport_ImportModule("traceback"); /* New reference. */
1379 cpy_log_exception("python initialization");
1383 cpy_format_exception =
1384 PyObject_GetAttrString(tb, "format_exception"); /* New reference. */
1386 if (cpy_format_exception == NULL) {
1387 cpy_log_exception("python initialization");
1390 } else if (strcasecmp(item->key, "ModulePath") == 0) {
1392 PyObject *dir_object;
1394 if (cf_util_get_string(item, &dir) != 0) {
1398 dir_object = cpy_string_to_unicode_or_bytes(dir); /* New reference. */
1399 if (dir_object == NULL) {
1400 ERROR("python plugin: Unable to convert \"%s\" to "
1404 cpy_log_exception("python initialization");
1408 if (PyList_Insert(sys_path, 0, dir_object) != 0) {
1409 ERROR("python plugin: Unable to prepend \"%s\" to "
1410 "python module path.",
1412 cpy_log_exception("python initialization");
1415 Py_DECREF(dir_object);
1417 } else if (strcasecmp(item->key, "Import") == 0) {
1418 char *module_name = NULL;
1421 if (cf_util_get_string(item, &module_name) != 0) {
1425 module = PyImport_ImportModule(module_name); /* New reference. */
1426 if (module == NULL) {
1427 ERROR("python plugin: Error importing module \"%s\".", module_name);
1428 cpy_log_exception("importing module");
1433 } else if (strcasecmp(item->key, "Module") == 0) {
1438 if (cf_util_get_string(item, &name) != 0) {
1442 for (c = cpy_config_callbacks; c; c = c->next) {
1443 if (strcasecmp(c->name + 7, name) == 0)
1447 WARNING("python plugin: Found a configuration for the \"%s\" plugin, "
1448 "but the plugin isn't loaded or didn't register "
1449 "a configuration callback.",
1455 if (c->data == NULL)
1456 ret = PyObject_CallFunction(
1458 cpy_oconfig_to_pyconfig(item, NULL)); /* New reference. */
1460 ret = PyObject_CallFunction(c->callback, "NO",
1461 cpy_oconfig_to_pyconfig(item, NULL),
1462 c->data); /* New reference. */
1464 cpy_log_exception("loading module");
1469 ERROR("python plugin: Unknown config key \"%s\".", item->key);
1476 void module_register(void) {
1477 plugin_register_complex_config("python", cpy_config);
1478 plugin_register_init("python", cpy_init);
1479 plugin_register_shutdown("python", cpy_shutdown);