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 pthread_t main_thread;
237 static PyOS_sighandler_t python_sigint_handler;
238 static _Bool do_interactive = 0;
240 /* This is our global thread state. Python saves some stuff in thread-local
241 * storage. So if we allow the interpreter to run in the background
242 * (the scriptwriters might have created some threads from python), we have
243 * to save the state so we can resume it later after shutdown. */
245 static PyThreadState *state;
247 static PyObject *sys_path, *cpy_format_exception;
249 static cpy_callback_t *cpy_config_callbacks;
250 static cpy_callback_t *cpy_init_callbacks;
251 static cpy_callback_t *cpy_shutdown_callbacks;
253 /* Make sure to hold the GIL while modifying these. */
254 static int cpy_shutdown_triggered = 0;
255 static int cpy_num_callbacks = 0;
257 static void cpy_destroy_user_data(void *data) {
258 cpy_callback_t *c = data;
261 Py_DECREF(c->callback);
265 if (!cpy_num_callbacks && cpy_shutdown_triggered) {
272 /* You must hold the GIL to call this function!
273 * But if you managed to extract the callback parameter then you probably
276 static void cpy_build_name(char *buf, size_t size, PyObject *callback,
278 const char *module = NULL;
279 PyObject *mod = NULL;
282 snprintf(buf, size, "python.%s", name);
286 mod = PyObject_GetAttrString(callback, "__module__"); /* New reference. */
288 module = cpy_unicode_or_bytes_to_string(&mod);
290 if (module != NULL) {
291 snprintf(buf, size, "python.%s", module);
298 snprintf(buf, size, "python.%p", callback);
302 void cpy_log_exception(const char *context) {
304 const char *typename = NULL, *message = NULL;
305 PyObject *type, *value, *traceback, *tn, *m, *list;
307 PyErr_Fetch(&type, &value, &traceback);
308 PyErr_NormalizeException(&type, &value, &traceback);
311 tn = PyObject_GetAttrString(type, "__name__"); /* New reference. */
312 m = PyObject_Str(value); /* New reference. */
314 typename = cpy_unicode_or_bytes_to_string(&tn);
316 message = cpy_unicode_or_bytes_to_string(&m);
317 if (typename == NULL)
318 typename = "NamelessException";
321 Py_BEGIN_ALLOW_THREADS ERROR("Unhandled python exception in %s: %s: %s",
322 context, typename, message);
323 Py_END_ALLOW_THREADS Py_XDECREF(tn);
325 if (!cpy_format_exception || !traceback) {
329 Py_XDECREF(traceback);
332 list = PyObject_CallFunction(cpy_format_exception, "NNN", type, value,
333 traceback); /* New reference. Steals references
334 from "type", "value" and
337 l = PyObject_Length(list);
339 for (int i = 0; i < l; ++i) {
344 line = PyList_GET_ITEM(list, i); /* Borrowed reference. */
347 msg = cpy_unicode_or_bytes_to_string(&line);
356 if (cpy[strlen(cpy) - 1] == '\n')
357 cpy[strlen(cpy) - 1] = 0;
359 Py_BEGIN_ALLOW_THREADS ERROR("%s", cpy);
369 static int cpy_read_callback(user_data_t *data) {
370 cpy_callback_t *c = data->data;
374 ret = PyObject_CallFunctionObjArgs(c->callback, c->data,
375 (void *)0); /* New reference. */
377 cpy_log_exception("read callback");
387 static int cpy_write_callback(const data_set_t *ds,
388 const value_list_t *value_list,
390 cpy_callback_t *c = data->data;
391 PyObject *ret, *list, *temp, *dict = NULL;
395 list = PyList_New(value_list->values_len); /* New reference. */
397 cpy_log_exception("write callback");
398 CPY_RETURN_FROM_THREADS 0;
400 for (size_t i = 0; i < value_list->values_len; ++i) {
401 if (ds->ds[i].type == DS_TYPE_COUNTER) {
403 list, i, PyLong_FromUnsignedLongLong(value_list->values[i].counter));
404 } else if (ds->ds[i].type == DS_TYPE_GAUGE) {
405 PyList_SetItem(list, i, PyFloat_FromDouble(value_list->values[i].gauge));
406 } else if (ds->ds[i].type == DS_TYPE_DERIVE) {
407 PyList_SetItem(list, i,
408 PyLong_FromLongLong(value_list->values[i].derive));
409 } else if (ds->ds[i].type == DS_TYPE_ABSOLUTE) {
411 list, i, PyLong_FromUnsignedLongLong(value_list->values[i].absolute));
413 Py_BEGIN_ALLOW_THREADS ERROR("cpy_write_callback: Unknown value type %d.",
415 Py_END_ALLOW_THREADS Py_DECREF(list);
416 CPY_RETURN_FROM_THREADS 0;
418 if (PyErr_Occurred() != NULL) {
419 cpy_log_exception("value building for write callback");
421 CPY_RETURN_FROM_THREADS 0;
424 dict = PyDict_New(); /* New reference. */
425 if (value_list->meta) {
427 meta_data_t *meta = value_list->meta;
429 int num = meta_data_toc(meta, &table);
430 for (int i = 0; i < num; ++i) {
438 type = meta_data_type(meta, table[i]);
439 if (type == MD_TYPE_STRING) {
440 if (meta_data_get_string(meta, table[i], &string))
442 temp = cpy_string_to_unicode_or_bytes(string); /* New reference. */
444 PyDict_SetItemString(dict, table[i], temp);
446 } else if (type == MD_TYPE_SIGNED_INT) {
447 if (meta_data_get_signed_int(meta, table[i], &si))
449 temp = PyObject_CallFunctionObjArgs((void *)&SignedType,
450 PyLong_FromLongLong(si),
451 (void *)0); /* New reference. */
452 PyDict_SetItemString(dict, table[i], temp);
454 } else if (type == MD_TYPE_UNSIGNED_INT) {
455 if (meta_data_get_unsigned_int(meta, table[i], &ui))
457 temp = PyObject_CallFunctionObjArgs((void *)&UnsignedType,
458 PyLong_FromUnsignedLongLong(ui),
459 (void *)0); /* New reference. */
460 PyDict_SetItemString(dict, table[i], temp);
462 } else if (type == MD_TYPE_DOUBLE) {
463 if (meta_data_get_double(meta, table[i], &d))
465 temp = PyFloat_FromDouble(d); /* New reference. */
466 PyDict_SetItemString(dict, table[i], temp);
468 } else if (type == MD_TYPE_BOOLEAN) {
469 if (meta_data_get_boolean(meta, table[i], &b))
472 PyDict_SetItemString(dict, table[i], Py_True);
474 PyDict_SetItemString(dict, table[i], Py_False);
480 v = (Values *)Values_New(); /* New reference. */
481 sstrncpy(v->data.host, value_list->host, sizeof(v->data.host));
482 sstrncpy(v->data.type, value_list->type, sizeof(v->data.type));
483 sstrncpy(v->data.type_instance, value_list->type_instance,
484 sizeof(v->data.type_instance));
485 sstrncpy(v->data.plugin, value_list->plugin, sizeof(v->data.plugin));
486 sstrncpy(v->data.plugin_instance, value_list->plugin_instance,
487 sizeof(v->data.plugin_instance));
488 v->data.time = CDTIME_T_TO_DOUBLE(value_list->time);
489 v->interval = CDTIME_T_TO_DOUBLE(value_list->interval);
493 v->meta = dict; /* Steals a reference. */
494 ret = PyObject_CallFunctionObjArgs(c->callback, v, c->data,
495 (void *)0); /* New reference. */
498 cpy_log_exception("write callback");
506 static int cpy_notification_callback(const notification_t *notification,
508 cpy_callback_t *c = data->data;
509 PyObject *ret, *notify;
513 notify = Notification_New(); /* New reference. */
514 n = (Notification *)notify;
515 sstrncpy(n->data.host, notification->host, sizeof(n->data.host));
516 sstrncpy(n->data.type, notification->type, sizeof(n->data.type));
517 sstrncpy(n->data.type_instance, notification->type_instance,
518 sizeof(n->data.type_instance));
519 sstrncpy(n->data.plugin, notification->plugin, sizeof(n->data.plugin));
520 sstrncpy(n->data.plugin_instance, notification->plugin_instance,
521 sizeof(n->data.plugin_instance));
522 n->data.time = CDTIME_T_TO_DOUBLE(notification->time);
523 sstrncpy(n->message, notification->message, sizeof(n->message));
524 n->severity = notification->severity;
525 ret = PyObject_CallFunctionObjArgs(c->callback, n, c->data,
526 (void *)0); /* New reference. */
529 cpy_log_exception("notification callback");
537 static void cpy_log_callback(int severity, const char *message,
539 cpy_callback_t *c = data->data;
540 PyObject *ret, *text;
543 text = cpy_string_to_unicode_or_bytes(message); /* New reference. */
545 ret = PyObject_CallFunction(
546 c->callback, "iN", severity,
547 text); /* New reference. Steals a reference from "text". */
549 ret = PyObject_CallFunction(
550 c->callback, "iNO", severity, text,
551 c->data); /* New reference. Steals a reference from "text". */
555 /* Do we really want to trigger a log callback because a log callback
559 /* In case someone wanted to be clever, replaced stderr and failed at that.
568 static void cpy_flush_callback(int timeout, const char *id, user_data_t *data) {
569 cpy_callback_t *c = data->data;
570 PyObject *ret, *text;
574 text = cpy_string_to_unicode_or_bytes(id);
580 ret = PyObject_CallFunction(c->callback, "iN", timeout,
581 text); /* New reference. */
583 ret = PyObject_CallFunction(c->callback, "iNO", timeout, text,
584 c->data); /* New reference. */
587 cpy_log_exception("flush callback");
594 static PyObject *cpy_register_generic(cpy_callback_t **list_head,
595 PyObject *args, PyObject *kwds) {
599 PyObject *callback = NULL, *data = NULL, *mod = NULL;
600 static char *kwlist[] = {"callback", "data", "name", NULL};
602 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oet", kwlist, &callback, &data,
605 if (PyCallable_Check(callback) == 0) {
607 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
610 cpy_build_name(buf, sizeof(buf), callback, name);
615 c = calloc(1, sizeof(*c));
619 c->name = strdup(buf);
620 c->callback = callback;
622 c->next = *list_head;
627 return cpy_string_to_unicode_or_bytes(buf);
630 static PyObject *float_or_none(float number) {
634 return PyFloat_FromDouble(number);
637 static PyObject *cpy_get_dataset(PyObject *self, PyObject *args) {
639 const data_set_t *ds;
640 PyObject *list, *tuple;
642 if (PyArg_ParseTuple(args, "et", NULL, &name) == 0)
644 ds = plugin_get_ds(name);
647 PyErr_Format(PyExc_TypeError, "Dataset %s not found", name);
650 list = PyList_New(ds->ds_num); /* New reference. */
651 for (size_t i = 0; i < ds->ds_num; ++i) {
652 tuple = PyTuple_New(4);
653 PyTuple_SET_ITEM(tuple, 0, cpy_string_to_unicode_or_bytes(ds->ds[i].name));
654 PyTuple_SET_ITEM(tuple, 1, cpy_string_to_unicode_or_bytes(
655 DS_TYPE_TO_STRING(ds->ds[i].type)));
656 PyTuple_SET_ITEM(tuple, 2, float_or_none(ds->ds[i].min));
657 PyTuple_SET_ITEM(tuple, 3, float_or_none(ds->ds[i].max));
658 PyList_SET_ITEM(list, i, tuple);
663 static PyObject *cpy_flush(PyObject *self, PyObject *args, PyObject *kwds) {
665 char *plugin = NULL, *identifier = NULL;
666 static char *kwlist[] = {"plugin", "timeout", "identifier", NULL};
668 if (PyArg_ParseTupleAndKeywords(args, kwds, "|etiet", kwlist, NULL, &plugin,
669 &timeout, NULL, &identifier) == 0)
671 Py_BEGIN_ALLOW_THREADS plugin_flush(plugin, timeout, identifier);
672 Py_END_ALLOW_THREADS PyMem_Free(plugin);
673 PyMem_Free(identifier);
677 static PyObject *cpy_register_config(PyObject *self, PyObject *args,
679 return cpy_register_generic(&cpy_config_callbacks, args, kwds);
682 static PyObject *cpy_register_init(PyObject *self, PyObject *args,
684 return cpy_register_generic(&cpy_init_callbacks, args, kwds);
687 typedef int reg_function_t(const char *name, void *callback, void *data);
689 static PyObject *cpy_register_generic_userdata(void *reg, void *handler,
690 PyObject *args, PyObject *kwds) {
692 reg_function_t *register_function = (reg_function_t *)reg;
693 cpy_callback_t *c = NULL;
695 PyObject *callback = NULL, *data = NULL;
696 static char *kwlist[] = {"callback", "data", "name", NULL};
698 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oet", kwlist, &callback, &data,
701 if (PyCallable_Check(callback) == 0) {
703 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
706 cpy_build_name(buf, sizeof(buf), callback, name);
712 c = calloc(1, sizeof(*c));
716 c->name = strdup(buf);
717 c->callback = callback;
721 register_function(buf, handler,
723 .data = c, .free_func = cpy_destroy_user_data,
727 return cpy_string_to_unicode_or_bytes(buf);
730 static PyObject *cpy_register_read(PyObject *self, PyObject *args,
733 cpy_callback_t *c = NULL;
736 PyObject *callback = NULL, *data = NULL;
737 static char *kwlist[] = {"callback", "interval", "data", "name", NULL};
739 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|dOet", kwlist, &callback,
740 &interval, &data, NULL, &name) == 0)
742 if (PyCallable_Check(callback) == 0) {
744 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
747 cpy_build_name(buf, sizeof(buf), callback, name);
753 c = calloc(1, sizeof(*c));
757 c->name = strdup(buf);
758 c->callback = callback;
762 plugin_register_complex_read(
763 /* group = */ "python", buf, cpy_read_callback,
764 DOUBLE_TO_CDTIME_T(interval),
766 .data = c, .free_func = cpy_destroy_user_data,
769 return cpy_string_to_unicode_or_bytes(buf);
772 static PyObject *cpy_register_log(PyObject *self, PyObject *args,
774 return cpy_register_generic_userdata((void *)plugin_register_log,
775 (void *)cpy_log_callback, args, kwds);
778 static PyObject *cpy_register_write(PyObject *self, PyObject *args,
780 return cpy_register_generic_userdata((void *)plugin_register_write,
781 (void *)cpy_write_callback, args, kwds);
784 static PyObject *cpy_register_notification(PyObject *self, PyObject *args,
786 return cpy_register_generic_userdata((void *)plugin_register_notification,
787 (void *)cpy_notification_callback, args,
791 static PyObject *cpy_register_flush(PyObject *self, PyObject *args,
793 return cpy_register_generic_userdata((void *)plugin_register_flush,
794 (void *)cpy_flush_callback, args, kwds);
797 static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args,
799 return cpy_register_generic(&cpy_shutdown_callbacks, args, kwds);
802 static PyObject *cpy_error(PyObject *self, PyObject *args) {
804 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
806 Py_BEGIN_ALLOW_THREADS plugin_log(LOG_ERR, "%s", text);
807 Py_END_ALLOW_THREADS PyMem_Free(text);
811 static PyObject *cpy_warning(PyObject *self, PyObject *args) {
813 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
815 Py_BEGIN_ALLOW_THREADS plugin_log(LOG_WARNING, "%s", text);
816 Py_END_ALLOW_THREADS PyMem_Free(text);
820 static PyObject *cpy_notice(PyObject *self, PyObject *args) {
822 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
824 Py_BEGIN_ALLOW_THREADS plugin_log(LOG_NOTICE, "%s", text);
825 Py_END_ALLOW_THREADS PyMem_Free(text);
829 static PyObject *cpy_info(PyObject *self, PyObject *args) {
831 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
833 Py_BEGIN_ALLOW_THREADS plugin_log(LOG_INFO, "%s", text);
834 Py_END_ALLOW_THREADS PyMem_Free(text);
838 static PyObject *cpy_debug(PyObject *self, PyObject *args) {
841 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0)
843 Py_BEGIN_ALLOW_THREADS plugin_log(LOG_DEBUG, "%s", text);
844 Py_END_ALLOW_THREADS PyMem_Free(text);
849 static PyObject *cpy_unregister_generic(cpy_callback_t **list_head,
850 PyObject *arg, const char *desc) {
853 cpy_callback_t *prev = NULL, *tmp;
856 name = cpy_unicode_or_bytes_to_string(&arg);
859 if (!PyCallable_Check(arg)) {
860 PyErr_SetString(PyExc_TypeError, "This function needs a string or a "
861 "callable object as its only "
866 cpy_build_name(buf, sizeof(buf), arg, NULL);
869 for (tmp = *list_head; tmp; prev = tmp, tmp = tmp->next)
870 if (strcmp(name, tmp->name) == 0)
875 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.",
879 /* Yes, this is actually safe. To call this function the caller has to
880 * hold the GIL. Well, safe as long as there is only one GIL anyway ... */
882 *list_head = tmp->next;
884 prev->next = tmp->next;
885 cpy_destroy_user_data(tmp);
889 static void cpy_unregister_list(cpy_callback_t **list_head) {
890 cpy_callback_t *cur, *next;
891 for (cur = *list_head; cur; cur = next) {
893 cpy_destroy_user_data(cur);
898 typedef int cpy_unregister_function_t(const char *name);
901 cpy_unregister_generic_userdata(cpy_unregister_function_t *unreg, PyObject *arg,
907 name = cpy_unicode_or_bytes_to_string(&arg);
910 if (!PyCallable_Check(arg)) {
911 PyErr_SetString(PyExc_TypeError, "This function needs a string or a "
912 "callable object as its only "
917 cpy_build_name(buf, sizeof(buf), arg, NULL);
920 if (unreg(name) == 0) {
924 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.",
930 static PyObject *cpy_unregister_log(PyObject *self, PyObject *arg) {
931 return cpy_unregister_generic_userdata(plugin_unregister_log, arg, "log");
934 static PyObject *cpy_unregister_init(PyObject *self, PyObject *arg) {
935 return cpy_unregister_generic(&cpy_init_callbacks, arg, "init");
938 static PyObject *cpy_unregister_config(PyObject *self, PyObject *arg) {
939 return cpy_unregister_generic(&cpy_config_callbacks, arg, "config");
942 static PyObject *cpy_unregister_read(PyObject *self, PyObject *arg) {
943 return cpy_unregister_generic_userdata(plugin_unregister_read, arg, "read");
946 static PyObject *cpy_unregister_write(PyObject *self, PyObject *arg) {
947 return cpy_unregister_generic_userdata(plugin_unregister_write, arg, "write");
950 static PyObject *cpy_unregister_notification(PyObject *self, PyObject *arg) {
951 return cpy_unregister_generic_userdata(plugin_unregister_notification, arg,
955 static PyObject *cpy_unregister_flush(PyObject *self, PyObject *arg) {
956 return cpy_unregister_generic_userdata(plugin_unregister_flush, arg, "flush");
959 static PyObject *cpy_unregister_shutdown(PyObject *self, PyObject *arg) {
960 return cpy_unregister_generic(&cpy_shutdown_callbacks, arg, "shutdown");
963 static PyMethodDef cpy_methods[] = {
964 {"debug", cpy_debug, METH_VARARGS, log_doc},
965 {"info", cpy_info, METH_VARARGS, log_doc},
966 {"notice", cpy_notice, METH_VARARGS, log_doc},
967 {"warning", cpy_warning, METH_VARARGS, log_doc},
968 {"error", cpy_error, METH_VARARGS, log_doc},
969 {"get_dataset", (PyCFunction)cpy_get_dataset, METH_VARARGS, get_ds_doc},
970 {"flush", (PyCFunction)cpy_flush, METH_VARARGS | METH_KEYWORDS, flush_doc},
971 {"register_log", (PyCFunction)cpy_register_log,
972 METH_VARARGS | METH_KEYWORDS, reg_log_doc},
973 {"register_init", (PyCFunction)cpy_register_init,
974 METH_VARARGS | METH_KEYWORDS, reg_init_doc},
975 {"register_config", (PyCFunction)cpy_register_config,
976 METH_VARARGS | METH_KEYWORDS, reg_config_doc},
977 {"register_read", (PyCFunction)cpy_register_read,
978 METH_VARARGS | METH_KEYWORDS, reg_read_doc},
979 {"register_write", (PyCFunction)cpy_register_write,
980 METH_VARARGS | METH_KEYWORDS, reg_write_doc},
981 {"register_notification", (PyCFunction)cpy_register_notification,
982 METH_VARARGS | METH_KEYWORDS, reg_notification_doc},
983 {"register_flush", (PyCFunction)cpy_register_flush,
984 METH_VARARGS | METH_KEYWORDS, reg_flush_doc},
985 {"register_shutdown", (PyCFunction)cpy_register_shutdown,
986 METH_VARARGS | METH_KEYWORDS, reg_shutdown_doc},
987 {"unregister_log", cpy_unregister_log, METH_O, unregister_doc},
988 {"unregister_init", cpy_unregister_init, METH_O, unregister_doc},
989 {"unregister_config", cpy_unregister_config, METH_O, unregister_doc},
990 {"unregister_read", cpy_unregister_read, METH_O, unregister_doc},
991 {"unregister_write", cpy_unregister_write, METH_O, unregister_doc},
992 {"unregister_notification", cpy_unregister_notification, METH_O,
994 {"unregister_flush", cpy_unregister_flush, METH_O, unregister_doc},
995 {"unregister_shutdown", cpy_unregister_shutdown, METH_O, unregister_doc},
998 static int cpy_shutdown(void) {
1003 "================================================================\n");
1005 "collectd shutdown while running an interactive session. This will\n");
1006 printf("probably leave your terminal in a mess.\n");
1007 printf("Run the command \"reset\" to get it back into a usable state.\n");
1008 printf("You can press Ctrl+D in the interactive session to\n");
1009 printf("close collectd and avoid this problem in the future.\n");
1011 "================================================================\n");
1016 for (cpy_callback_t *c = cpy_shutdown_callbacks; c; c = c->next) {
1017 ret = PyObject_CallFunctionObjArgs(c->callback, c->data,
1018 (void *)0); /* New reference. */
1020 cpy_log_exception("shutdown callback");
1026 Py_BEGIN_ALLOW_THREADS cpy_unregister_list(&cpy_config_callbacks);
1027 cpy_unregister_list(&cpy_init_callbacks);
1028 cpy_unregister_list(&cpy_shutdown_callbacks);
1029 cpy_shutdown_triggered = 1;
1030 Py_END_ALLOW_THREADS
1032 if (!cpy_num_callbacks) {
1041 static void *cpy_interactive(void *pipefd) {
1042 PyOS_sighandler_t cur_sig;
1044 /* Signal handler in a plugin? Bad stuff, but the best way to
1045 * handle it I guess. In an interactive session people will
1046 * press Ctrl+C at some time, which will generate a SIGINT.
1047 * This will cause collectd to shutdown, thus killing the
1048 * interactive interpreter, and leaving the terminal in a
1049 * mess. Chances are, this isn't what the user wanted to do.
1051 * So this is the plan:
1052 * 1. Restore Python's own signal handler
1053 * 2. Tell Python we just forked so it will accept this thread
1054 * as the main one. No version of Python will ever handle
1055 * interrupts anywhere but in the main thread.
1056 * 3. After the interactive loop is done, restore collectd's
1058 * 4. Raise SIGINT for a clean shutdown. The signal is sent to
1059 * the main thread to ensure it wakes up the main interval
1060 * sleep so that collectd shuts down immediately not in 10
1063 * This will make sure that SIGINT won't kill collectd but
1064 * still interrupt syscalls like sleep and pause. */
1066 if (PyImport_ImportModule("readline") == NULL) {
1067 /* This interactive session will suck. */
1068 cpy_log_exception("interactive session init");
1070 cur_sig = PyOS_setsig(SIGINT, python_sigint_handler);
1072 PyEval_InitThreads();
1073 close(*(int *)pipefd);
1074 PyRun_InteractiveLoop(stdin, "<stdin>");
1075 PyOS_setsig(SIGINT, cur_sig);
1077 state = PyEval_SaveThread();
1078 NOTICE("python: Interactive interpreter exited, stopping collectd ...");
1079 pthread_kill(main_thread, SIGINT);
1083 static int cpy_init(void) {
1087 static pthread_t thread;
1089 if (!Py_IsInitialized()) {
1090 WARNING("python: Plugin loaded but not configured.");
1091 plugin_unregister_shutdown("python");
1095 main_thread = pthread_self();
1096 if (do_interactive) {
1098 ERROR("python: Unable to create pipe.");
1101 if (plugin_thread_create(&thread, NULL, cpy_interactive, pipefd + 1,
1102 "python interpreter")) {
1103 ERROR("python: Error creating thread for interactive interpreter.");
1105 if (read(pipefd[0], &buf, 1))
1107 (void)close(pipefd[0]);
1109 PyEval_InitThreads();
1110 state = PyEval_SaveThread();
1113 for (cpy_callback_t *c = cpy_init_callbacks; c; c = c->next) {
1114 ret = PyObject_CallFunctionObjArgs(c->callback, c->data,
1115 (void *)0); /* New reference. */
1117 cpy_log_exception("init callback");
1126 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
1127 PyObject *item, *values, *children, *tmp;
1132 values = PyTuple_New(ci->values_num); /* New reference. */
1133 for (int i = 0; i < ci->values_num; ++i) {
1134 if (ci->values[i].type == OCONFIG_TYPE_STRING) {
1135 PyTuple_SET_ITEM(values, i, cpy_string_to_unicode_or_bytes(
1136 ci->values[i].value.string));
1137 } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
1138 PyTuple_SET_ITEM(values, i,
1139 PyFloat_FromDouble(ci->values[i].value.number));
1140 } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
1141 PyTuple_SET_ITEM(values, i, PyBool_FromLong(ci->values[i].value.boolean));
1145 tmp = cpy_string_to_unicode_or_bytes(ci->key);
1146 item = PyObject_CallFunction((void *)&ConfigType, "NONO", tmp, parent, values,
1150 children = PyTuple_New(ci->children_num); /* New reference. */
1151 for (int i = 0; i < ci->children_num; ++i) {
1152 PyTuple_SET_ITEM(children, i,
1153 cpy_oconfig_to_pyconfig(ci->children + i, item));
1155 tmp = ((Config *)item)->children;
1156 ((Config *)item)->children = children;
1162 static struct PyModuleDef collectdmodule = {
1163 PyModuleDef_HEAD_INIT, "collectd", /* name of module */
1164 "The python interface to collectd", /* module documentation, may be NULL */
1167 PyMODINIT_FUNC PyInit_collectd(void) {
1168 return PyModule_Create(&collectdmodule);
1172 static int cpy_init_python(void) {
1173 PyOS_sighandler_t cur_sig;
1178 wchar_t *argv = L"";
1179 /* Add a builtin module, before Py_Initialize */
1180 PyImport_AppendInittab("collectd", PyInit_collectd);
1185 /* Chances are the current signal handler is already SIG_DFL, but let's make
1187 cur_sig = PyOS_setsig(SIGINT, SIG_DFL);
1189 python_sigint_handler = PyOS_setsig(SIGINT, cur_sig);
1191 PyType_Ready(&ConfigType);
1192 PyType_Ready(&PluginDataType);
1193 ValuesType.tp_base = &PluginDataType;
1194 PyType_Ready(&ValuesType);
1195 NotificationType.tp_base = &PluginDataType;
1196 PyType_Ready(&NotificationType);
1197 SignedType.tp_base = &PyLong_Type;
1198 PyType_Ready(&SignedType);
1199 UnsignedType.tp_base = &PyLong_Type;
1200 PyType_Ready(&UnsignedType);
1201 sys = PyImport_ImportModule("sys"); /* New reference. */
1203 cpy_log_exception("python initialization");
1206 sys_path = PyObject_GetAttrString(sys, "path"); /* New reference. */
1208 if (sys_path == NULL) {
1209 cpy_log_exception("python initialization");
1212 PySys_SetArgv(1, &argv);
1213 PyList_SetSlice(sys_path, 0, 1, NULL);
1216 module = PyImport_ImportModule("collectd");
1218 module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
1220 PyModule_AddObject(module, "Config",
1221 (void *)&ConfigType); /* Steals a reference. */
1222 PyModule_AddObject(module, "Values",
1223 (void *)&ValuesType); /* Steals a reference. */
1224 PyModule_AddObject(module, "Notification",
1225 (void *)&NotificationType); /* Steals a reference. */
1226 PyModule_AddObject(module, "Signed",
1227 (void *)&SignedType); /* Steals a reference. */
1228 PyModule_AddObject(module, "Unsigned",
1229 (void *)&UnsignedType); /* Steals a reference. */
1230 PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG);
1231 PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO);
1232 PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE);
1233 PyModule_AddIntConstant(module, "LOG_WARNING", LOG_WARNING);
1234 PyModule_AddIntConstant(module, "LOG_ERROR", LOG_ERR);
1235 PyModule_AddIntConstant(module, "NOTIF_FAILURE", NOTIF_FAILURE);
1236 PyModule_AddIntConstant(module, "NOTIF_WARNING", NOTIF_WARNING);
1237 PyModule_AddIntConstant(module, "NOTIF_OKAY", NOTIF_OKAY);
1238 PyModule_AddStringConstant(module, "DS_TYPE_COUNTER",
1239 DS_TYPE_TO_STRING(DS_TYPE_COUNTER));
1240 PyModule_AddStringConstant(module, "DS_TYPE_GAUGE",
1241 DS_TYPE_TO_STRING(DS_TYPE_GAUGE));
1242 PyModule_AddStringConstant(module, "DS_TYPE_DERIVE",
1243 DS_TYPE_TO_STRING(DS_TYPE_DERIVE));
1244 PyModule_AddStringConstant(module, "DS_TYPE_ABSOLUTE",
1245 DS_TYPE_TO_STRING(DS_TYPE_ABSOLUTE));
1249 static int cpy_config(oconfig_item_t *ci) {
1253 /* Ok in theory we shouldn't do initialization at this point
1254 * but we have to. In order to give python scripts a chance
1255 * to register a config callback we need to be able to execute
1256 * python code during the config callback so we have to start
1257 * the interpreter here. */
1258 /* Do *not* use the python "thread" module at this point! */
1260 if (!Py_IsInitialized() && cpy_init_python())
1263 for (int i = 0; i < ci->children_num; ++i) {
1264 oconfig_item_t *item = ci->children + i;
1266 if (strcasecmp(item->key, "Interactive") == 0) {
1267 if (cf_util_get_boolean(item, &do_interactive) != 0) {
1271 } else if (strcasecmp(item->key, "Encoding") == 0) {
1272 char *encoding = NULL;
1273 if (cf_util_get_string(item, &encoding) != 0) {
1278 ERROR("python: \"Encoding\" was used in the config file but Python3 was "
1279 "used, which does not support changing encodings");
1284 /* Why is this even necessary? And undocumented? */
1285 if (PyUnicode_SetDefaultEncoding(encoding)) {
1286 cpy_log_exception("setting default encoding");
1291 } else if (strcasecmp(item->key, "LogTraces") == 0) {
1293 if (cf_util_get_boolean(item, &log_traces) != 0) {
1298 Py_XDECREF(cpy_format_exception);
1299 cpy_format_exception = NULL;
1302 if (cpy_format_exception)
1304 tb = PyImport_ImportModule("traceback"); /* New reference. */
1306 cpy_log_exception("python initialization");
1310 cpy_format_exception =
1311 PyObject_GetAttrString(tb, "format_exception"); /* New reference. */
1313 if (cpy_format_exception == NULL) {
1314 cpy_log_exception("python initialization");
1317 } else if (strcasecmp(item->key, "ModulePath") == 0) {
1319 PyObject *dir_object;
1321 if (cf_util_get_string(item, &dir) != 0) {
1325 dir_object = cpy_string_to_unicode_or_bytes(dir); /* New reference. */
1326 if (dir_object == NULL) {
1327 ERROR("python plugin: Unable to convert \"%s\" to "
1331 cpy_log_exception("python initialization");
1335 if (PyList_Insert(sys_path, 0, dir_object) != 0) {
1336 ERROR("python plugin: Unable to prepend \"%s\" to "
1337 "python module path.",
1339 cpy_log_exception("python initialization");
1342 Py_DECREF(dir_object);
1344 } else if (strcasecmp(item->key, "Import") == 0) {
1345 char *module_name = NULL;
1348 if (cf_util_get_string(item, &module_name) != 0) {
1352 module = PyImport_ImportModule(module_name); /* New reference. */
1353 if (module == NULL) {
1354 ERROR("python plugin: Error importing module \"%s\".", module_name);
1355 cpy_log_exception("importing module");
1360 } else if (strcasecmp(item->key, "Module") == 0) {
1365 if (cf_util_get_string(item, &name) != 0) {
1369 for (c = cpy_config_callbacks; c; c = c->next) {
1370 if (strcasecmp(c->name + 7, name) == 0)
1374 WARNING("python plugin: Found a configuration for the \"%s\" plugin, "
1375 "but the plugin isn't loaded or didn't register "
1376 "a configuration callback.",
1382 if (c->data == NULL)
1383 ret = PyObject_CallFunction(
1385 cpy_oconfig_to_pyconfig(item, NULL)); /* New reference. */
1387 ret = PyObject_CallFunction(c->callback, "NO",
1388 cpy_oconfig_to_pyconfig(item, NULL),
1389 c->data); /* New reference. */
1391 cpy_log_exception("loading module");
1396 ERROR("python plugin: Unknown config key \"%s\".", item->key);
1403 void module_register(void) {
1404 plugin_register_complex_config("python", cpy_config);
1405 plugin_register_init("python", cpy_init);
1406 plugin_register_shutdown("python", cpy_shutdown);