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>
40 typedef struct cpy_callback_s {
44 struct cpy_callback_s *next;
47 static char log_doc[] = "This function sends a string to all logging plugins.";
49 static char get_ds_doc[] = "get_dataset(name) -> definition\n"
51 "Returns the definition of a dataset specified by name.\n"
53 "'name' is a string specifying the dataset to query.\n"
54 "'definition' is a list of 4-tuples. Every tuple represents a \n"
55 " data source within the data set and its 4 values are the \n"
56 " name, type, min and max value.\n"
57 " 'name' is a string.\n"
58 " 'type' is a string that is equal to either DS_TYPE_COUNTER,\n"
59 " DS_TYPE_GAUGE, DS_TYPE_DERIVE or DS_TYPE_ABSOLUTE.\n"
60 " 'min' and 'max' are either a float or None.";
62 static char flush_doc[] = "flush([plugin][, timeout][, identifier]) -> None\n"
64 "Flushes the cache of another plugin.";
66 static char unregister_doc[] = "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[] = "register_log(callback[, data][, name]) -> identifier\n"
71 "Register a callback function for log messages.\n"
73 "'callback' is a callable object that will be called every time something\n"
75 "'data' is an optional object that will be passed back to the callback\n"
76 " function every time it is called.\n"
77 "'name' is an optional identifier for this callback. The default name\n"
78 " is 'python.<module>'.\n"
79 " Every callback needs a unique identifier, so if you want to\n"
80 " register this callback multiple time from the same module you need\n"
81 " to specify a name here.\n"
82 "'identifier' is the full identifier assigned to this callback.\n"
84 "The callback function will be called with two or three parameters:\n"
85 "severity: An integer that should be compared to the LOG_ constants.\n"
86 "message: The text to be logged.\n"
87 "data: The optional data parameter passed to the register function.\n"
88 " If the parameter was omitted it will be omitted here, too.";
90 static char reg_init_doc[] = "register_init(callback[, data][, name]) -> identifier\n"
92 "Register a callback function that will be executed once after the config.\n"
93 "file has been read, all plugins heve been loaded and the collectd has\n"
94 "forked into the background.\n"
96 "'callback' is a callable object that will be executed.\n"
97 "'data' is an optional object that will be passed back to the callback\n"
98 " function when it is called.\n"
99 "'name' is an optional identifier for this callback. The default name\n"
100 " is 'python.<module>'.\n"
101 " Every callback needs a unique identifier, so if you want to\n"
102 " register this callback multiple time from the same module you need\n"
103 " to specify a name here.\n"
104 "'identifier' is the full identifier assigned to this callback.\n"
106 "The callback function will be called without parameters, except for\n"
107 "data if it was supplied.";
109 static char reg_config_doc[] = "register_config(callback[, data][, name]) -> identifier\n"
111 "Register a callback function for config file entries.\n"
112 "'callback' is a callable object that will be called for every config block.\n"
113 "'data' is an optional object that will be passed back to the callback\n"
114 " function every time it is called.\n"
115 "'name' is an optional identifier for this callback. The default name\n"
116 " is 'python.<module>'.\n"
117 " Every callback needs a unique identifier, so if you want to\n"
118 " register this callback multiple time from the same module you need\n"
119 " to specify a name here.\n"
120 "'identifier' is the full identifier assigned to this callback.\n"
122 "The callback function will be called with one or two parameters:\n"
123 "config: A Config object.\n"
124 "data: The optional data parameter passed to the register function.\n"
125 " If the parameter was omitted it will be omitted here, too.";
127 static char reg_read_doc[] = "register_read(callback[, interval][, data][, name]) -> identifier\n"
129 "Register a callback function for reading data. It will just be called\n"
130 "in a fixed interval to signal that it's time to dispatch new values.\n"
131 "'callback' is a callable object that will be called every time something\n"
133 "'interval' is the number of seconds between between calls to the callback\n"
134 " function. Full float precision is supported here.\n"
135 "'data' is an optional object that will be passed back to the callback\n"
136 " function every time it is called.\n"
137 "'name' is an optional identifier for this callback. The default name\n"
138 " is 'python.<module>'.\n"
139 " Every callback needs a unique identifier, so if you want to\n"
140 " register this callback multiple time from the same module you need\n"
141 " to specify a name here.\n"
142 "'identifier' is the full identifier assigned to this callback.\n"
144 "The callback function will be called without parameters, except for\n"
145 "data if it was supplied.";
147 static char reg_write_doc[] = "register_write(callback[, data][, name]) -> identifier\n"
149 "Register a callback function to receive values dispatched by other plugins.\n"
150 "'callback' is a callable object that will be called every time a value\n"
152 "'data' is an optional object that will be passed back to the callback\n"
153 " function every time it is called.\n"
154 "'name' is an optional identifier for this callback. The default name\n"
155 " is 'python.<module>'.\n"
156 " Every callback needs a unique identifier, so if you want to\n"
157 " register this callback multiple time from the same module you need\n"
158 " to specify a name here.\n"
159 "'identifier' is the full identifier assigned to this callback.\n"
161 "The callback function will be called with one or two parameters:\n"
162 "values: A Values object which is a copy of the dispatched values.\n"
163 "data: The optional data parameter passed to the register function.\n"
164 " If the parameter was omitted it will be omitted here, too.";
166 static char reg_notification_doc[] = "register_notification(callback[, data][, name]) -> identifier\n"
168 "Register a callback function for notifications.\n"
169 "'callback' is a callable object that will be called every time a notification\n"
171 "'data' is an optional object that will be passed back to the callback\n"
172 " function every time it is called.\n"
173 "'name' is an optional identifier for this callback. The default name\n"
174 " is 'python.<module>'.\n"
175 " Every callback needs a unique identifier, so if you want to\n"
176 " register this callback multiple time from the same module you need\n"
177 " to specify a name here.\n"
178 "'identifier' is the full identifier assigned to this callback.\n"
180 "The callback function will be called with one or two parameters:\n"
181 "notification: A copy of the notification that was dispatched.\n"
182 "data: The optional data parameter passed to the register function.\n"
183 " If the parameter was omitted it will be omitted here, too.";
185 static char reg_flush_doc[] = "register_flush(callback[, data][, name]) -> identifier\n"
187 "Register a callback function for flush messages.\n"
188 "'callback' is a callable object that will be called every time a plugin\n"
189 " requests a flush for either this or all plugins.\n"
190 "'data' is an optional object that will be passed back to the callback\n"
191 " function every time it is called.\n"
192 "'name' is an optional identifier for this callback. The default name\n"
193 " is 'python.<module>'.\n"
194 " Every callback needs a unique identifier, so if you want to\n"
195 " register this callback multiple time from the same module you need\n"
196 " to specify a name here.\n"
197 "'identifier' is the full identifier assigned to this callback.\n"
199 "The callback function will be called with two or three parameters:\n"
200 "timeout: Indicates that only data older than 'timeout' seconds is to\n"
202 "id: Specifies which values are to be flushed.\n"
203 "data: The optional data parameter passed to the register function.\n"
204 " If the parameter was omitted it will be omitted here, too.";
206 static char reg_shutdown_doc[] = "register_shutdown(callback[, data][, name]) -> identifier\n"
208 "Register a callback function for collectd shutdown.\n"
209 "'callback' is a callable object that will be called once collectd is\n"
211 "'data' is an optional object that will be passed back to the callback\n"
212 " function if it is called.\n"
213 "'name' is an optional identifier for this callback. The default name\n"
214 " is 'python.<module>'.\n"
215 " Every callback needs a unique identifier, so if you want to\n"
216 " register this callback multiple time from the same module you need\n"
217 " to specify a name here.\n"
218 "'identifier' is the full identifier assigned to this callback.\n"
220 "The callback function will be called with no parameters except for\n"
221 " data if it was supplied.";
224 static int do_interactive = 0;
226 /* This is our global thread state. Python saves some stuff in thread-local
227 * storage. So if we allow the interpreter to run in the background
228 * (the scriptwriters might have created some threads from python), we have
229 * to save the state so we can resume it later after shutdown. */
231 static PyThreadState *state;
233 static PyObject *sys_path, *cpy_format_exception;
235 static cpy_callback_t *cpy_config_callbacks;
236 static cpy_callback_t *cpy_init_callbacks;
237 static cpy_callback_t *cpy_shutdown_callbacks;
239 static void cpy_destroy_user_data(void *data) {
240 cpy_callback_t *c = data;
242 Py_DECREF(c->callback);
247 /* You must hold the GIL to call this function!
248 * But if you managed to extract the callback parameter then you probably already do. */
250 static void cpy_build_name(char *buf, size_t size, PyObject *callback, const char *name) {
251 const char *module = NULL;
252 PyObject *mod = NULL;
255 snprintf(buf, size, "python.%s", name);
259 mod = PyObject_GetAttrString(callback, "__module__"); /* New reference. */
261 module = cpy_unicode_or_bytes_to_string(&mod);
263 if (module != NULL) {
264 snprintf(buf, size, "python.%s", module);
271 snprintf(buf, size, "python.%p", callback);
275 void cpy_log_exception(const char *context) {
277 const char *typename = NULL, *message = NULL;
278 PyObject *type, *value, *traceback, *tn, *m, *list;
280 PyErr_Fetch(&type, &value, &traceback);
281 PyErr_NormalizeException(&type, &value, &traceback);
282 if (type == NULL) return;
283 tn = PyObject_GetAttrString(type, "__name__"); /* New reference. */
284 m = PyObject_Str(value); /* New reference. */
286 typename = cpy_unicode_or_bytes_to_string(&tn);
288 message = cpy_unicode_or_bytes_to_string(&m);
289 if (typename == NULL)
290 typename = "NamelessException";
293 Py_BEGIN_ALLOW_THREADS
294 ERROR("Unhandled python exception in %s: %s: %s", context, typename, message);
298 if (!cpy_format_exception || !traceback) {
302 Py_XDECREF(traceback);
305 list = PyObject_CallFunction(cpy_format_exception, "NNN", type, value, traceback); /* New reference. Steals references from "type", "value" and "traceback". */
307 l = PyObject_Length(list);
309 for (i = 0; i < l; ++i) {
314 line = PyList_GET_ITEM(list, i); /* Borrowed reference. */
317 msg = cpy_unicode_or_bytes_to_string(&line);
326 if (cpy[strlen(cpy) - 1] == '\n')
327 cpy[strlen(cpy) - 1] = 0;
329 Py_BEGIN_ALLOW_THREADS
340 static int cpy_read_callback(user_data_t *data) {
341 cpy_callback_t *c = data->data;
345 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
347 cpy_log_exception("read callback");
357 static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_list, user_data_t *data) {
359 cpy_callback_t *c = data->data;
360 PyObject *ret, *list, *temp, *dict = NULL;
364 list = PyList_New(value_list->values_len); /* New reference. */
366 cpy_log_exception("write callback");
367 CPY_RETURN_FROM_THREADS 0;
369 for (i = 0; i < value_list->values_len; ++i) {
370 if (ds->ds[i].type == DS_TYPE_COUNTER) {
371 if ((long) value_list->values[i].counter == value_list->values[i].counter)
372 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].counter));
374 PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].counter));
375 } else if (ds->ds[i].type == DS_TYPE_GAUGE) {
376 PyList_SetItem(list, i, PyFloat_FromDouble(value_list->values[i].gauge));
377 } else if (ds->ds[i].type == DS_TYPE_DERIVE) {
378 if ((long) value_list->values[i].derive == value_list->values[i].derive)
379 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].derive));
381 PyList_SetItem(list, i, PyLong_FromLongLong(value_list->values[i].derive));
382 } else if (ds->ds[i].type == DS_TYPE_ABSOLUTE) {
383 if ((long) value_list->values[i].absolute == value_list->values[i].absolute)
384 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].absolute));
386 PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].absolute));
388 Py_BEGIN_ALLOW_THREADS
389 ERROR("cpy_write_callback: Unknown value type %d.", ds->ds[i].type);
392 CPY_RETURN_FROM_THREADS 0;
394 if (PyErr_Occurred() != NULL) {
395 cpy_log_exception("value building for write callback");
397 CPY_RETURN_FROM_THREADS 0;
400 dict = PyDict_New(); /* New reference. */
401 if (value_list->meta) {
404 meta_data_t *meta = value_list->meta;
406 num = meta_data_toc(meta, &table);
407 for (i = 0; i < num; ++i) {
415 type = meta_data_type(meta, table[i]);
416 if (type == MD_TYPE_STRING) {
417 if (meta_data_get_string(meta, table[i], &string))
419 temp = cpy_string_to_unicode_or_bytes(string); /* New reference. */
421 PyDict_SetItemString(dict, table[i], temp);
423 } else if (type == MD_TYPE_SIGNED_INT) {
424 if (meta_data_get_signed_int(meta, table[i], &si))
426 temp = PyObject_CallFunctionObjArgs((void *) &SignedType, PyLong_FromLongLong(si), (void *) 0); /* New reference. */
427 PyDict_SetItemString(dict, table[i], temp);
429 } else if (type == MD_TYPE_UNSIGNED_INT) {
430 if (meta_data_get_unsigned_int(meta, table[i], &ui))
432 temp = PyObject_CallFunctionObjArgs((void *) &UnsignedType, PyLong_FromUnsignedLongLong(ui), (void *) 0); /* New reference. */
433 PyDict_SetItemString(dict, table[i], temp);
435 } else if (type == MD_TYPE_DOUBLE) {
436 if (meta_data_get_double(meta, table[i], &d))
438 temp = PyFloat_FromDouble(d); /* New reference. */
439 PyDict_SetItemString(dict, table[i], temp);
441 } else if (type == MD_TYPE_BOOLEAN) {
442 if (meta_data_get_boolean(meta, table[i], &b))
445 PyDict_SetItemString(dict, table[i], Py_True);
447 PyDict_SetItemString(dict, table[i], Py_False);
453 v = (Values *) Values_New(); /* New reference. */
454 sstrncpy(v->data.host, value_list->host, sizeof(v->data.host));
455 sstrncpy(v->data.type, value_list->type, sizeof(v->data.type));
456 sstrncpy(v->data.type_instance, value_list->type_instance, sizeof(v->data.type_instance));
457 sstrncpy(v->data.plugin, value_list->plugin, sizeof(v->data.plugin));
458 sstrncpy(v->data.plugin_instance, value_list->plugin_instance, sizeof(v->data.plugin_instance));
459 v->data.time = CDTIME_T_TO_DOUBLE(value_list->time);
460 v->interval = CDTIME_T_TO_DOUBLE(value_list->interval);
464 v->meta = dict; /* Steals a reference. */
465 ret = PyObject_CallFunctionObjArgs(c->callback, v, c->data, (void *) 0); /* New reference. */
468 cpy_log_exception("write callback");
476 static int cpy_notification_callback(const notification_t *notification, user_data_t *data) {
477 cpy_callback_t *c = data->data;
478 PyObject *ret, *notify;
482 notify = Notification_New(); /* New reference. */
483 n = (Notification *) notify;
484 sstrncpy(n->data.host, notification->host, sizeof(n->data.host));
485 sstrncpy(n->data.type, notification->type, sizeof(n->data.type));
486 sstrncpy(n->data.type_instance, notification->type_instance, sizeof(n->data.type_instance));
487 sstrncpy(n->data.plugin, notification->plugin, sizeof(n->data.plugin));
488 sstrncpy(n->data.plugin_instance, notification->plugin_instance, sizeof(n->data.plugin_instance));
489 n->data.time = CDTIME_T_TO_DOUBLE(notification->time);
490 sstrncpy(n->message, notification->message, sizeof(n->message));
491 n->severity = notification->severity;
492 ret = PyObject_CallFunctionObjArgs(c->callback, n, c->data, (void *) 0); /* New reference. */
495 cpy_log_exception("notification callback");
503 static void cpy_log_callback(int severity, const char *message, user_data_t *data) {
504 cpy_callback_t * c = data->data;
505 PyObject *ret, *text;
508 text = cpy_string_to_unicode_or_bytes(message); /* New reference. */
510 ret = PyObject_CallFunction(c->callback, "iN", severity, text); /* New reference. Steals a reference from "text". */
512 ret = PyObject_CallFunction(c->callback, "iNO", severity, text, c->data); /* New reference. Steals a reference from "text". */
516 /* Do we really want to trigger a log callback because a log callback failed?
519 /* In case someone wanted to be clever, replaced stderr and failed at that. */
527 static void cpy_flush_callback(int timeout, const char *id, user_data_t *data) {
528 cpy_callback_t * c = data->data;
529 PyObject *ret, *text;
532 text = cpy_string_to_unicode_or_bytes(id);
534 ret = PyObject_CallFunction(c->callback, "iN", timeout, text); /* New reference. */
536 ret = PyObject_CallFunction(c->callback, "iNO", timeout, text, c->data); /* New reference. */
539 cpy_log_exception("flush callback");
546 static PyObject *cpy_register_generic(cpy_callback_t **list_head, PyObject *args, PyObject *kwds) {
550 PyObject *callback = NULL, *data = NULL, *mod = NULL;
551 static char *kwlist[] = {"callback", "data", "name", NULL};
553 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oet", kwlist, &callback, &data, NULL, &name) == 0) return NULL;
554 if (PyCallable_Check(callback) == 0) {
556 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
559 cpy_build_name(buf, sizeof(buf), callback, name);
564 c = malloc(sizeof(*c));
567 memset (c, 0, sizeof (*c));
569 c->name = strdup(buf);
570 c->callback = callback;
572 c->next = *list_head;
576 return cpy_string_to_unicode_or_bytes(buf);
579 static PyObject *float_or_none(float number) {
583 return PyFloat_FromDouble(number);
586 static PyObject *cpy_get_dataset(PyObject *self, PyObject *args) {
589 const data_set_t *ds;
590 PyObject *list, *tuple;
592 if (PyArg_ParseTuple(args, "et", NULL, &name) == 0) return NULL;
593 ds = plugin_get_ds(name);
596 PyErr_Format(PyExc_TypeError, "Dataset %s not found", name);
599 list = PyList_New(ds->ds_num); /* New reference. */
600 for (i = 0; i < ds->ds_num; ++i) {
601 tuple = PyTuple_New(4);
602 PyTuple_SET_ITEM(tuple, 0, cpy_string_to_unicode_or_bytes(ds->ds[i].name));
603 PyTuple_SET_ITEM(tuple, 1, cpy_string_to_unicode_or_bytes(DS_TYPE_TO_STRING(ds->ds[i].type)));
604 PyTuple_SET_ITEM(tuple, 2, float_or_none(ds->ds[i].min));
605 PyTuple_SET_ITEM(tuple, 3, float_or_none(ds->ds[i].max));
606 PyList_SET_ITEM(list, i, tuple);
611 static PyObject *cpy_flush(PyObject *self, PyObject *args, PyObject *kwds) {
613 char *plugin = NULL, *identifier = NULL;
614 static char *kwlist[] = {"plugin", "timeout", "identifier", NULL};
616 if (PyArg_ParseTupleAndKeywords(args, kwds, "|etiet", kwlist, NULL, &plugin, &timeout, NULL, &identifier) == 0) return NULL;
617 Py_BEGIN_ALLOW_THREADS
618 plugin_flush(plugin, timeout, identifier);
621 PyMem_Free(identifier);
625 static PyObject *cpy_register_config(PyObject *self, PyObject *args, PyObject *kwds) {
626 return cpy_register_generic(&cpy_config_callbacks, args, kwds);
629 static PyObject *cpy_register_init(PyObject *self, PyObject *args, PyObject *kwds) {
630 return cpy_register_generic(&cpy_init_callbacks, args, kwds);
633 typedef int reg_function_t(const char *name, void *callback, void *data);
635 static PyObject *cpy_register_generic_userdata(void *reg, void *handler, PyObject *args, PyObject *kwds) {
637 reg_function_t *register_function = (reg_function_t *) reg;
638 cpy_callback_t *c = NULL;
639 user_data_t user_data;
641 PyObject *callback = NULL, *data = NULL;
642 static char *kwlist[] = {"callback", "data", "name", NULL};
644 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oet", kwlist, &callback, &data, NULL, &name) == 0) return NULL;
645 if (PyCallable_Check(callback) == 0) {
647 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
650 cpy_build_name(buf, sizeof(buf), callback, name);
656 c = malloc(sizeof(*c));
659 memset (c, 0, sizeof (*c));
661 c->name = strdup(buf);
662 c->callback = callback;
666 memset (&user_data, 0, sizeof (user_data));
667 user_data.free_func = cpy_destroy_user_data;
670 register_function(buf, handler, &user_data);
671 return cpy_string_to_unicode_or_bytes(buf);
674 static PyObject *cpy_register_read(PyObject *self, PyObject *args, PyObject *kwds) {
676 cpy_callback_t *c = NULL;
677 user_data_t user_data;
680 PyObject *callback = NULL, *data = NULL;
682 static char *kwlist[] = {"callback", "interval", "data", "name", NULL};
684 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|dOet", kwlist, &callback, &interval, &data, NULL, &name) == 0) return NULL;
685 if (PyCallable_Check(callback) == 0) {
687 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
690 cpy_build_name(buf, sizeof(buf), callback, name);
696 c = malloc(sizeof(*c));
699 memset (c, 0, sizeof (*c));
701 c->name = strdup(buf);
702 c->callback = callback;
706 memset (&user_data, 0, sizeof (user_data));
707 user_data.free_func = cpy_destroy_user_data;
710 ts.tv_sec = interval;
711 ts.tv_nsec = (interval - ts.tv_sec) * 1000000000;
712 plugin_register_complex_read(/* group = */ "python", buf,
713 cpy_read_callback, &ts, &user_data);
715 return cpy_string_to_unicode_or_bytes(buf);
718 static PyObject *cpy_register_log(PyObject *self, PyObject *args, PyObject *kwds) {
719 return cpy_register_generic_userdata((void *) plugin_register_log,
720 (void *) cpy_log_callback, args, kwds);
723 static PyObject *cpy_register_write(PyObject *self, PyObject *args, PyObject *kwds) {
724 return cpy_register_generic_userdata((void *) plugin_register_write,
725 (void *) cpy_write_callback, args, kwds);
728 static PyObject *cpy_register_notification(PyObject *self, PyObject *args, PyObject *kwds) {
729 return cpy_register_generic_userdata((void *) plugin_register_notification,
730 (void *) cpy_notification_callback, args, kwds);
733 static PyObject *cpy_register_flush(PyObject *self, PyObject *args, PyObject *kwds) {
734 return cpy_register_generic_userdata((void *) plugin_register_flush,
735 (void *) cpy_flush_callback, args, kwds);
738 static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args, PyObject *kwds) {
739 return cpy_register_generic(&cpy_shutdown_callbacks, args, kwds);
742 static PyObject *cpy_error(PyObject *self, PyObject *args) {
744 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
745 Py_BEGIN_ALLOW_THREADS
746 plugin_log(LOG_ERR, "%s", text);
752 static PyObject *cpy_warning(PyObject *self, PyObject *args) {
754 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
755 Py_BEGIN_ALLOW_THREADS
756 plugin_log(LOG_WARNING, "%s", text);
762 static PyObject *cpy_notice(PyObject *self, PyObject *args) {
764 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
765 Py_BEGIN_ALLOW_THREADS
766 plugin_log(LOG_NOTICE, "%s", text);
772 static PyObject *cpy_info(PyObject *self, PyObject *args) {
774 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
775 Py_BEGIN_ALLOW_THREADS
776 plugin_log(LOG_INFO, "%s", text);
782 static PyObject *cpy_debug(PyObject *self, PyObject *args) {
785 if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
786 Py_BEGIN_ALLOW_THREADS
787 plugin_log(LOG_DEBUG, "%s", text);
794 static PyObject *cpy_unregister_generic(cpy_callback_t **list_head, PyObject *arg, const char *desc) {
797 cpy_callback_t *prev = NULL, *tmp;
800 name = cpy_unicode_or_bytes_to_string(&arg);
803 if (!PyCallable_Check(arg)) {
804 PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
808 cpy_build_name(buf, sizeof(buf), arg, NULL);
811 for (tmp = *list_head; tmp; prev = tmp, tmp = tmp->next)
812 if (strcmp(name, tmp->name) == 0)
817 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
820 /* Yes, this is actually save. To call this function the caller has to
821 * hold the GIL. Well, save as long as there is only one GIL anyway ... */
823 *list_head = tmp->next;
825 prev->next = tmp->next;
826 cpy_destroy_user_data(tmp);
830 typedef int cpy_unregister_function_t(const char *name);
832 static PyObject *cpy_unregister_generic_userdata(cpy_unregister_function_t *unreg, PyObject *arg, const char *desc) {
837 name = cpy_unicode_or_bytes_to_string(&arg);
840 if (!PyCallable_Check(arg)) {
841 PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
845 cpy_build_name(buf, sizeof(buf), arg, NULL);
848 if (unreg(name) == 0) {
852 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
857 static PyObject *cpy_unregister_log(PyObject *self, PyObject *arg) {
858 return cpy_unregister_generic_userdata(plugin_unregister_log, arg, "log");
861 static PyObject *cpy_unregister_init(PyObject *self, PyObject *arg) {
862 return cpy_unregister_generic(&cpy_init_callbacks, arg, "init");
865 static PyObject *cpy_unregister_config(PyObject *self, PyObject *arg) {
866 return cpy_unregister_generic(&cpy_config_callbacks, arg, "config");
869 static PyObject *cpy_unregister_read(PyObject *self, PyObject *arg) {
870 return cpy_unregister_generic_userdata(plugin_unregister_read, arg, "read");
873 static PyObject *cpy_unregister_write(PyObject *self, PyObject *arg) {
874 return cpy_unregister_generic_userdata(plugin_unregister_write, arg, "write");
877 static PyObject *cpy_unregister_notification(PyObject *self, PyObject *arg) {
878 return cpy_unregister_generic_userdata(plugin_unregister_notification, arg, "notification");
881 static PyObject *cpy_unregister_flush(PyObject *self, PyObject *arg) {
882 return cpy_unregister_generic_userdata(plugin_unregister_flush, arg, "flush");
885 static PyObject *cpy_unregister_shutdown(PyObject *self, PyObject *arg) {
886 return cpy_unregister_generic(&cpy_shutdown_callbacks, arg, "shutdown");
889 static PyMethodDef cpy_methods[] = {
890 {"debug", cpy_debug, METH_VARARGS, log_doc},
891 {"info", cpy_info, METH_VARARGS, log_doc},
892 {"notice", cpy_notice, METH_VARARGS, log_doc},
893 {"warning", cpy_warning, METH_VARARGS, log_doc},
894 {"error", cpy_error, METH_VARARGS, log_doc},
895 {"get_dataset", (PyCFunction) cpy_get_dataset, METH_VARARGS, get_ds_doc},
896 {"flush", (PyCFunction) cpy_flush, METH_VARARGS | METH_KEYWORDS, flush_doc},
897 {"register_log", (PyCFunction) cpy_register_log, METH_VARARGS | METH_KEYWORDS, reg_log_doc},
898 {"register_init", (PyCFunction) cpy_register_init, METH_VARARGS | METH_KEYWORDS, reg_init_doc},
899 {"register_config", (PyCFunction) cpy_register_config, METH_VARARGS | METH_KEYWORDS, reg_config_doc},
900 {"register_read", (PyCFunction) cpy_register_read, METH_VARARGS | METH_KEYWORDS, reg_read_doc},
901 {"register_write", (PyCFunction) cpy_register_write, METH_VARARGS | METH_KEYWORDS, reg_write_doc},
902 {"register_notification", (PyCFunction) cpy_register_notification, METH_VARARGS | METH_KEYWORDS, reg_notification_doc},
903 {"register_flush", (PyCFunction) cpy_register_flush, METH_VARARGS | METH_KEYWORDS, reg_flush_doc},
904 {"register_shutdown", (PyCFunction) cpy_register_shutdown, METH_VARARGS | METH_KEYWORDS, reg_shutdown_doc},
905 {"unregister_log", cpy_unregister_log, METH_O, unregister_doc},
906 {"unregister_init", cpy_unregister_init, METH_O, unregister_doc},
907 {"unregister_config", cpy_unregister_config, METH_O, unregister_doc},
908 {"unregister_read", cpy_unregister_read, METH_O, unregister_doc},
909 {"unregister_write", cpy_unregister_write, METH_O, unregister_doc},
910 {"unregister_notification", cpy_unregister_notification, METH_O, unregister_doc},
911 {"unregister_flush", cpy_unregister_flush, METH_O, unregister_doc},
912 {"unregister_shutdown", cpy_unregister_shutdown, METH_O, unregister_doc},
916 static int cpy_shutdown(void) {
920 /* This can happen if the module was loaded but not configured. */
922 PyEval_RestoreThread(state);
924 for (c = cpy_shutdown_callbacks; c; c = c->next) {
925 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
927 cpy_log_exception("shutdown callback");
936 static void cpy_int_handler(int sig) {
940 static void *cpy_interactive(void *data) {
942 struct sigaction sig_int_action, old;
944 /* Signal handler in a plugin? Bad stuff, but the best way to
945 * handle it I guess. In an interactive session people will
946 * press Ctrl+C at some time, which will generate a SIGINT.
947 * This will cause collectd to shutdown, thus killing the
948 * interactive interpreter, and leaving the terminal in a
949 * mess. Chances are, this isn't what the user wanted to do.
951 * So this is the plan:
952 * 1. Block SIGINT in the main thread.
953 * 2. Install our own signal handler that does nothing.
954 * 3. Unblock SIGINT in the interactive thread.
956 * This will make sure that SIGINT won't kill collectd but
957 * still interrupt syscalls like sleep and pause.
958 * It does not raise a KeyboardInterrupt exception because so
959 * far nobody managed to figure out how to do that. */
960 memset (&sig_int_action, '\0', sizeof (sig_int_action));
961 sig_int_action.sa_handler = cpy_int_handler;
962 sigaction (SIGINT, &sig_int_action, &old);
964 sigemptyset(&sigset);
965 sigaddset(&sigset, SIGINT);
966 pthread_sigmask(SIG_UNBLOCK, &sigset, NULL);
967 PyEval_AcquireThread(state);
968 if (PyImport_ImportModule("readline") == NULL) {
969 /* This interactive session will suck. */
970 cpy_log_exception("interactive session init");
972 PyRun_InteractiveLoop(stdin, "<stdin>");
974 PyEval_ReleaseThread(state);
975 NOTICE("python: Interactive interpreter exited, stopping collectd ...");
976 /* Restore the original collectd SIGINT handler and raise SIGINT.
977 * The main thread still has SIGINT blocked and there's nothing we
978 * can do about that so this thread will handle it. But that's not
979 * important, except that it won't interrupt the main loop and so
980 * it might take a few seconds before collectd really shuts down. */
981 sigaction (SIGINT, &old, NULL);
987 static int cpy_init(void) {
990 static pthread_t thread;
993 if (!Py_IsInitialized()) {
994 WARNING("python: Plugin loaded but not configured.");
995 plugin_unregister_shutdown("python");
998 PyEval_InitThreads();
999 /* Now it's finally OK to use python threads. */
1000 for (c = cpy_init_callbacks; c; c = c->next) {
1001 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
1003 cpy_log_exception("init callback");
1007 sigemptyset(&sigset);
1008 sigaddset(&sigset, SIGINT);
1009 pthread_sigmask(SIG_BLOCK, &sigset, NULL);
1010 state = PyEval_SaveThread();
1011 if (do_interactive) {
1012 if (plugin_thread_create(&thread, NULL, cpy_interactive, NULL)) {
1013 ERROR("python: Error creating thread for interactive interpreter.");
1020 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
1022 PyObject *item, *values, *children, *tmp;
1027 values = PyTuple_New(ci->values_num); /* New reference. */
1028 for (i = 0; i < ci->values_num; ++i) {
1029 if (ci->values[i].type == OCONFIG_TYPE_STRING) {
1030 PyTuple_SET_ITEM(values, i, cpy_string_to_unicode_or_bytes(ci->values[i].value.string));
1031 } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
1032 PyTuple_SET_ITEM(values, i, PyFloat_FromDouble(ci->values[i].value.number));
1033 } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
1034 PyTuple_SET_ITEM(values, i, PyBool_FromLong(ci->values[i].value.boolean));
1038 tmp = cpy_string_to_unicode_or_bytes(ci->key);
1039 item = PyObject_CallFunction((void *) &ConfigType, "NONO", tmp, parent, values, Py_None);
1042 children = PyTuple_New(ci->children_num); /* New reference. */
1043 for (i = 0; i < ci->children_num; ++i) {
1044 PyTuple_SET_ITEM(children, i, cpy_oconfig_to_pyconfig(ci->children + i, item));
1046 tmp = ((Config *) item)->children;
1047 ((Config *) item)->children = children;
1053 static struct PyModuleDef collectdmodule = {
1054 PyModuleDef_HEAD_INIT,
1055 "collectd", /* name of module */
1056 "The python interface to collectd", /* module documentation, may be NULL */
1061 PyMODINIT_FUNC PyInit_collectd(void) {
1062 return PyModule_Create(&collectdmodule);
1066 static int cpy_init_python() {
1071 wchar_t *argv = L"";
1072 /* Add a builtin module, before Py_Initialize */
1073 PyImport_AppendInittab("collectd", PyInit_collectd);
1080 PyType_Ready(&ConfigType);
1081 PyType_Ready(&PluginDataType);
1082 ValuesType.tp_base = &PluginDataType;
1083 PyType_Ready(&ValuesType);
1084 NotificationType.tp_base = &PluginDataType;
1085 PyType_Ready(&NotificationType);
1086 SignedType.tp_base = &PyLong_Type;
1087 PyType_Ready(&SignedType);
1088 UnsignedType.tp_base = &PyLong_Type;
1089 PyType_Ready(&UnsignedType);
1090 sys = PyImport_ImportModule("sys"); /* New reference. */
1092 cpy_log_exception("python initialization");
1095 sys_path = PyObject_GetAttrString(sys, "path"); /* New reference. */
1097 if (sys_path == NULL) {
1098 cpy_log_exception("python initialization");
1101 PySys_SetArgv(1, &argv);
1102 PyList_SetSlice(sys_path, 0, 1, NULL);
1105 module = PyImport_ImportModule("collectd");
1107 module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
1109 PyModule_AddObject(module, "Config", (void *) &ConfigType); /* Steals a reference. */
1110 PyModule_AddObject(module, "Values", (void *) &ValuesType); /* Steals a reference. */
1111 PyModule_AddObject(module, "Notification", (void *) &NotificationType); /* Steals a reference. */
1112 PyModule_AddObject(module, "Signed", (void *) &SignedType); /* Steals a reference. */
1113 PyModule_AddObject(module, "Unsigned", (void *) &UnsignedType); /* Steals a reference. */
1114 PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG);
1115 PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO);
1116 PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE);
1117 PyModule_AddIntConstant(module, "LOG_WARNING", LOG_WARNING);
1118 PyModule_AddIntConstant(module, "LOG_ERROR", LOG_ERR);
1119 PyModule_AddIntConstant(module, "NOTIF_FAILURE", NOTIF_FAILURE);
1120 PyModule_AddIntConstant(module, "NOTIF_WARNING", NOTIF_WARNING);
1121 PyModule_AddIntConstant(module, "NOTIF_OKAY", NOTIF_OKAY);
1122 PyModule_AddStringConstant(module, "DS_TYPE_COUNTER", DS_TYPE_TO_STRING(DS_TYPE_COUNTER));
1123 PyModule_AddStringConstant(module, "DS_TYPE_GAUGE", DS_TYPE_TO_STRING(DS_TYPE_GAUGE));
1124 PyModule_AddStringConstant(module, "DS_TYPE_DERIVE", DS_TYPE_TO_STRING(DS_TYPE_DERIVE));
1125 PyModule_AddStringConstant(module, "DS_TYPE_ABSOLUTE", DS_TYPE_TO_STRING(DS_TYPE_ABSOLUTE));
1129 static int cpy_config(oconfig_item_t *ci) {
1133 /* Ok in theory we shouldn't do initialization at this point
1134 * but we have to. In order to give python scripts a chance
1135 * to register a config callback we need to be able to execute
1136 * python code during the config callback so we have to start
1137 * the interpreter here. */
1138 /* Do *not* use the python "thread" module at this point! */
1140 if (!Py_IsInitialized() && cpy_init_python()) return 1;
1142 for (i = 0; i < ci->children_num; ++i) {
1143 oconfig_item_t *item = ci->children + i;
1145 if (strcasecmp(item->key, "Interactive") == 0) {
1146 if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_BOOLEAN)
1148 do_interactive = item->values[0].value.boolean;
1149 } else if (strcasecmp(item->key, "Encoding") == 0) {
1150 if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_STRING)
1153 NOTICE("python: \"Encoding\" was used in the config file but Python3 was used, which does not support changing encodings. Ignoring this.");
1155 /* Why is this even necessary? And undocumented? */
1156 if (PyUnicode_SetDefaultEncoding(item->values[0].value.string))
1157 cpy_log_exception("setting default encoding");
1159 } else if (strcasecmp(item->key, "LogTraces") == 0) {
1160 if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_BOOLEAN)
1162 if (!item->values[0].value.boolean) {
1163 Py_XDECREF(cpy_format_exception);
1164 cpy_format_exception = NULL;
1167 if (cpy_format_exception)
1169 tb = PyImport_ImportModule("traceback"); /* New reference. */
1171 cpy_log_exception("python initialization");
1174 cpy_format_exception = PyObject_GetAttrString(tb, "format_exception"); /* New reference. */
1176 if (cpy_format_exception == NULL)
1177 cpy_log_exception("python initialization");
1178 } else if (strcasecmp(item->key, "ModulePath") == 0) {
1180 PyObject *dir_object;
1182 if (cf_util_get_string(item, &dir) != 0)
1184 dir_object = cpy_string_to_unicode_or_bytes(dir); /* New reference. */
1185 if (dir_object == NULL) {
1186 ERROR("python plugin: Unable to convert \"%s\" to "
1187 "a python object.", dir);
1189 cpy_log_exception("python initialization");
1192 if (PyList_Insert(sys_path, 0, dir_object) != 0) {
1193 ERROR("python plugin: Unable to prepend \"%s\" to "
1194 "python module path.", dir);
1195 cpy_log_exception("python initialization");
1197 Py_DECREF(dir_object);
1199 } else if (strcasecmp(item->key, "Import") == 0) {
1200 char *module_name = NULL;
1203 if (cf_util_get_string(item, &module_name) != 0)
1205 module = PyImport_ImportModule(module_name); /* New reference. */
1206 if (module == NULL) {
1207 ERROR("python plugin: Error importing module \"%s\".", module_name);
1208 cpy_log_exception("importing module");
1212 } else if (strcasecmp(item->key, "Module") == 0) {
1217 if (cf_util_get_string(item, &name) != 0)
1219 for (c = cpy_config_callbacks; c; c = c->next) {
1220 if (strcasecmp(c->name + 7, name) == 0)
1224 WARNING("python plugin: Found a configuration for the \"%s\" plugin, "
1225 "but the plugin isn't loaded or didn't register "
1226 "a configuration callback.", name);
1231 if (c->data == NULL)
1232 ret = PyObject_CallFunction(c->callback, "N",
1233 cpy_oconfig_to_pyconfig(item, NULL)); /* New reference. */
1235 ret = PyObject_CallFunction(c->callback, "NO",
1236 cpy_oconfig_to_pyconfig(item, NULL), c->data); /* New reference. */
1238 cpy_log_exception("loading module");
1242 WARNING("python plugin: Ignoring unknown config key \"%s\".", item->key);
1248 void module_register(void) {
1249 plugin_register_complex_config("python", cpy_config);
1250 plugin_register_init("python", cpy_init);
1251 plugin_register_shutdown("python", cpy_shutdown);