1 #include <Python.h>
2 #include <structmember.h>
4 #include <signal.h>
5 #if HAVE_PTHREAD_H
6 # include <pthread.h>
7 #endif
9 #include "collectd.h"
10 #include "common.h"
12 #include "cpython.h"
14 typedef struct cpy_callback_s {
15 char *name;
16 PyObject *callback;
17 PyObject *data;
18 struct cpy_callback_s *next;
19 } cpy_callback_t;
21 static char log_doc[] = "This function sends a string to all logging plugins.";
23 static char flush_doc[] = "flush([plugin][, timeout][, identifier]) -> None\n"
24 "\n"
25 "Flushes the cache of another plugin.";
27 static char unregister_doc[] = "Unregisters a callback. This function needs exactly one parameter either\n"
28 "the function to unregister or the callback identifier to unregister.";
30 static char reg_log_doc[] = "register_log(callback[, data][, name]) -> identifier\n"
31 "\n"
32 "Register a callback function for log messages.\n"
33 "\n"
34 "'callback' is a callable object that will be called every time something\n"
35 " is logged.\n"
36 "'data' is an optional object that will be passed back to the callback\n"
37 " function every time it is called.\n"
38 "'name' is an optional identifier for this callback. The default name\n"
39 " is 'python.<module>.<name>'. If 'name' contains a '.' it\n"
40 " replaces both module and name, otherwise it replaces only name.\n"
41 " Every callback needs a unique identifier, so if you want to\n"
42 " register one function multiple time you need to specify a name\n"
43 " here.\n"
44 "'identifier' is the full identifier assigned to this callback.\n"
45 "\n"
46 "The callback function will be called with two or three parameters:\n"
47 "severity: An integer that should be compared to the LOG_ constants.\n"
48 "message: The text to be logged.\n"
49 "data: The optional data parameter passed to the register function.\n"
50 " If the parameter was obmitted it will be obmitted here, too.";
52 static char reg_init_doc[] = "register_init(callback[, data][, name]) -> identifier\n"
53 "\n"
54 "Register a callback function that will be executed once after the config.\n"
55 "file has been read, all plugins heve been loaded and the collectd has\n"
56 "forked into the backgroud.\n"
57 "\n"
58 "'callback' is a callable object that will be executed.\n"
59 "'data' is an optional object that will be passed back to the callback\n"
60 " function when it is called.\n"
61 "'name' is an optional identifier for this callback. The default name\n"
62 " is 'python.<module>.<name>'. If 'name' contains a '.' it\n"
63 " replaces both module and name, otherwise it replaces only name.\n"
64 " Every callback needs a unique identifier, so if you want to\n"
65 " register one function multiple time you need to specify a name\n"
66 " here.\n"
67 "'identifier' is the full identifier assigned to this callback.\n"
68 "\n"
69 "The callback function will be called without parameters, except for\n"
70 "data if it was supplied.";
72 static char reg_config_doc[] = "register_config(callback[, data][, name]) -> identifier\n"
73 "\n"
74 "Register a callback function for config file entries.\n"
75 "'callback' is a callable object that will be called for every config block.\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>'. Every callback needs a unique identifier,\n"
80 " so if you want to register one function multiple time you need to\n"
81 " specify a name here.\n"
82 "'identifier' is the full identifier assigned to this callback.\n"
83 "\n"
84 "The callback function will be called with one or two parameters:\n"
85 "config: A Config object.\n"
86 "data: The optional data parameter passed to the register function.\n"
87 " If the parameter was obmitted it will be obmitted here, too.";
89 static char reg_read_doc[] = "register_read(callback[, interval][, data][, name]) -> identifier\n"
90 "\n"
91 "Register a callback function for reading data. It will just be called\n"
92 "in a fixed interval to signal that it's time to dispatch new values.\n"
93 "'callback' is a callable object that will be called every time something\n"
94 " is logged.\n"
95 "'interval' is the number of seconds between between calls to the callback\n"
96 " function. Full float precision is supported here.\n"
97 "'data' is an optional object that will be passed back to the callback\n"
98 " function every time it is called.\n"
99 "'name' is an optional identifier for this callback. The default name\n"
100 " is 'python.<module>.<name>'. If 'name' contains a '.' it\n"
101 " replaces both module and name, otherwise it replaces only name.\n"
102 " Every callback needs a unique identifier, so if you want to\n"
103 " register one function multiple time you need to specify a name\n"
104 " here.\n"
105 "'identifier' is the full identifier assigned to this callback.\n"
106 "\n"
107 "The callback function will be called without parameters, except for\n"
108 "data if it was supplied.";
110 static char reg_write_doc[] = "register_write(callback[, data][, name]) -> identifier\n"
111 "\n"
112 "Register a callback function to receive values dispatched by other plugins.\n"
113 "'callback' is a callable object that will be called every time a value\n"
114 " is dispatched.\n"
115 "'data' is an optional object that will be passed back to the callback\n"
116 " function every time it is called.\n"
117 "'name' is an optional identifier for this callback. The default name\n"
118 " is 'python.<module>.<name>'. If 'name' contains a '.' it\n"
119 " replaces both module and name, otherwise it replaces only name.\n"
120 " Every callback needs a unique identifier, so if you want to\n"
121 " register one function multiple time you need to specify a name\n"
122 " here.\n"
123 "'identifier' is the full identifier assigned to this callback.\n"
124 "\n"
125 "The callback function will be called with one or two parameters:\n"
126 "values: A Values object which is a copy of the dispatched values.\n"
127 "data: The optional data parameter passed to the register function.\n"
128 " If the parameter was obmitted it will be obmitted here, too.";
130 static char reg_notification_doc[] = "register_notification(callback[, data][, name]) -> identifier\n"
131 "\n"
132 "Register a callback function for notifications.\n"
133 "'callback' is a callable object that will be called every time a notification\n"
134 " is dispatched.\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>.<name>'. If 'name' contains a '.' it\n"
139 " replaces both module and name, otherwise it replaces only name.\n"
140 " Every callback needs a unique identifier, so if you want to\n"
141 " register one function multiple time you need to specify a name\n"
142 " here.\n"
143 "'identifier' is the full identifier assigned to this callback.\n"
144 "\n"
145 "The callback function will be called with one or two parameters:\n"
146 "notification: A copy of the notification that was dispatched.\n"
147 "data: The optional data parameter passed to the register function.\n"
148 " If the parameter was obmitted it will be obmitted here, too.";
150 static char reg_flush_doc[] = "register_flush(callback[, data][, name]) -> identifier\n"
151 "\n"
152 "Register a callback function for flush messages.\n"
153 "'callback' is a callable object that will be called every time a plugin\n"
154 " requests a flush for either this or all plugins.\n"
155 "'data' is an optional object that will be passed back to the callback\n"
156 " function every time it is called.\n"
157 "'name' is an optional identifier for this callback. The default name\n"
158 " is 'python.<module>'. Every callback needs a unique identifier,\n"
159 " so if you want to register one function multiple time you need to\n"
160 " specify a name here.\n"
161 "'identifier' is the full identifier assigned to this callback.\n"
162 "\n"
163 "The callback function will be called with two or three parameters:\n"
164 "timeout: ???.\n"
165 "id: ???.\n"
166 "data: The optional data parameter passed to the register function.\n"
167 " If the parameter was obmitted it will be obmitted here, too.";
169 static char reg_shutdown_doc[] = "register_shutdown(callback[, data][, name]) -> identifier\n"
170 "\n"
171 "Register a callback function for collectd shutdown.\n"
172 "'callback' is a callable object that will be called once collectd is\n"
173 " shutting down.\n"
174 "'data' is an optional object that will be passed back to the callback\n"
175 " function if it is called.\n"
176 "'name' is an optional identifier for this callback. The default name\n"
177 " is 'python.<module>.<name>'. If 'name' contains a '.' it\n"
178 " replaces both module and name, otherwise it replaces only name.\n"
179 " Every callback needs a unique identifier, so if you want to\n"
180 " register one function multiple time you need to specify a name\n"
181 " here.\n"
182 "'identifier' is the full identifier assigned to this callback.\n"
183 "\n"
184 "The callback function will be called with no parameters except for\n"
185 " data if it was supplied.";
188 static int do_interactive = 0;
190 /* This is our global thread state. Python saves some stuff in thread-local
191 * storage. So if we allow the interpreter to run in the background
192 * (the scriptwriters might have created some threads from python), we have
193 * to save the state so we can resume it later after shutdown. */
195 static PyThreadState *state;
197 static PyObject *cpy_format_exception;
199 static cpy_callback_t *cpy_config_callbacks;
200 static cpy_callback_t *cpy_init_callbacks;
201 static cpy_callback_t *cpy_shutdown_callbacks;
203 static void cpy_destroy_user_data(void *data) {
204 cpy_callback_t *c = data;
205 free(c->name);
206 Py_DECREF(c->callback);
207 Py_XDECREF(c->data);
208 free(c);
209 }
211 /* You must hold the GIL to call this function!
212 * But if you managed to extract the callback parameter then you probably already do. */
214 static void cpy_build_name(char *buf, size_t size, PyObject *callback, const char *name, int short_name) {
215 const char *module;
216 PyObject *mod = NULL, *n = NULL;
218 if (name != NULL && (strchr(name, '.') != NULL || short_name)) {
219 snprintf(buf, size, "python.%s", name);
220 return;
221 }
223 mod = PyObject_GetAttrString(callback, "__module__"); /* New reference. */
224 if (mod != NULL)
225 module = PyString_AsString(mod);
226 else
227 module = "collectd";
229 if (short_name) {
230 snprintf(buf, size, "python.%s", module);
231 Py_XDECREF(mod);
232 return;
233 }
235 if (name != NULL) {
236 snprintf(buf, size, "python.%s.%s", module, name);
237 Py_XDECREF(mod);
238 return;
239 }
241 n = PyObject_GetAttrString(callback, "__name__"); /* New reference. */
242 if (n != NULL)
243 name = PyString_AsString(n);
245 if (name != NULL)
246 snprintf(buf, size, "python.%s.%s", module, name);
247 else
248 snprintf(buf, size, "python.%s.%p", module, callback);
249 Py_XDECREF(mod);
250 Py_XDECREF(n);
251 }
253 static void cpy_log_exception(const char *context) {
254 int l = 0, i;
255 const char *typename = NULL, *message = NULL;
256 PyObject *type, *value, *traceback, *tn, *m, *list;
258 PyErr_Fetch(&type, &value, &traceback);
259 PyErr_NormalizeException(&type, &value, &traceback);
260 if (type == NULL) return;
261 tn = PyObject_GetAttrString(type, "__name__"); /* New reference. */
262 m = PyObject_GetAttrString(value, "message"); /* New reference. */
263 if (tn != NULL)
264 typename = PyString_AsString(tn);
265 if (m != NULL)
266 message = PyString_AsString(m);
267 if (typename == NULL)
268 typename = "NamelessException";
269 if (message == NULL)
270 message = "N/A";
271 Py_BEGIN_ALLOW_THREADS
272 ERROR("Unhandled python exception in %s: %s: %s", context, typename, message);
273 Py_END_ALLOW_THREADS
274 Py_XDECREF(tn);
275 Py_XDECREF(m);
276 if (!cpy_format_exception) {
277 PyErr_Clear();
278 Py_XDECREF(type);
279 Py_XDECREF(value);
280 Py_XDECREF(traceback);
281 return;
282 }
283 if (!traceback) {
284 PyErr_Clear();
285 return;
286 }
287 list = PyObject_CallFunction(cpy_format_exception, "NNN", type, value, traceback); /* New reference. */
288 if (list)
289 l = PyObject_Length(list);
290 for (i = 0; i < l; ++i) {
291 char *s;
292 PyObject *line;
294 line = PyList_GET_ITEM(list, i); /* Borrowed reference. */
295 s = strdup(PyString_AsString(line));
296 Py_DECREF(line);
297 if (s[strlen(s) - 1] == '\n')
298 s[strlen(s) - 1] = 0;
299 Py_BEGIN_ALLOW_THREADS
300 ERROR("%s", s);
301 Py_END_ALLOW_THREADS
302 free(s);
303 }
304 Py_XDECREF(list);
305 PyErr_Clear();
306 }
308 static int cpy_read_callback(user_data_t *data) {
309 cpy_callback_t *c = data->data;
310 PyObject *ret;
312 CPY_LOCK_THREADS
313 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
314 if (ret == NULL) {
315 cpy_log_exception("read callback");
316 } else {
317 Py_DECREF(ret);
318 }
319 CPY_RELEASE_THREADS
320 if (ret == NULL)
321 return 1;
322 return 0;
323 }
325 static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_list, user_data_t *data) {
326 int i;
327 cpy_callback_t *c = data->data;
328 PyObject *ret, *v, *list;
330 CPY_LOCK_THREADS
331 list = PyList_New(value_list->values_len); /* New reference. */
332 if (list == NULL) {
333 cpy_log_exception("write callback");
334 CPY_RETURN_FROM_THREADS 0;
335 }
336 for (i = 0; i < value_list->values_len; ++i) {
337 if (ds->ds->type == DS_TYPE_COUNTER) {
338 if ((long) value_list->values[i].counter == value_list->values[i].counter)
339 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].counter));
340 else
341 PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].counter));
342 } else if (ds->ds->type == DS_TYPE_GAUGE) {
343 PyList_SetItem(list, i, PyFloat_FromDouble(value_list->values[i].gauge));
344 } else if (ds->ds->type == DS_TYPE_DERIVE) {
345 if ((long) value_list->values[i].derive == value_list->values[i].derive)
346 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].derive));
347 else
348 PyList_SetItem(list, i, PyLong_FromLongLong(value_list->values[i].derive));
349 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
350 if ((long) value_list->values[i].absolute == value_list->values[i].absolute)
351 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].absolute));
352 else
353 PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].absolute));
354 } else {
355 Py_BEGIN_ALLOW_THREADS
356 ERROR("cpy_write_callback: Unknown value type %d.", ds->ds->type);
357 Py_END_ALLOW_THREADS
358 Py_DECREF(list);
359 CPY_RETURN_FROM_THREADS 0;
360 }
361 if (PyErr_Occurred() != NULL) {
362 cpy_log_exception("value building for write callback");
363 CPY_RETURN_FROM_THREADS 0;
364 }
365 }
366 v = PyObject_CallFunction((PyObject *) &ValuesType, "sOssssdi", value_list->type, list,
367 value_list->plugin_instance, value_list->type_instance, value_list->plugin,
368 value_list->host, (double) value_list->time, value_list->interval);
369 Py_DECREF(list);
370 ret = PyObject_CallFunctionObjArgs(c->callback, v, c->data, (void *) 0); /* New reference. */
371 if (ret == NULL) {
372 cpy_log_exception("write callback");
373 } else {
374 Py_DECREF(ret);
375 }
376 CPY_RELEASE_THREADS
377 return 0;
378 }
380 static int cpy_notification_callback(const notification_t *notification, user_data_t *data) {
381 cpy_callback_t *c = data->data;
382 PyObject *ret, *n;
384 CPY_LOCK_THREADS
385 n = PyObject_CallFunction((PyObject *) &NotificationType, "ssssssdi", notification->type, notification->message,
386 notification->plugin_instance, notification->type_instance, notification->plugin,
387 notification->host, (double) notification->time, notification->severity);
388 ret = PyObject_CallFunctionObjArgs(c->callback, n, c->data, (void *) 0); /* New reference. */
389 if (ret == NULL) {
390 cpy_log_exception("notification callback");
391 } else {
392 Py_DECREF(ret);
393 }
394 CPY_RELEASE_THREADS
395 return 0;
396 }
398 static void cpy_log_callback(int severity, const char *message, user_data_t *data) {
399 cpy_callback_t * c = data->data;
400 PyObject *ret;
402 CPY_LOCK_THREADS
403 if (c->data == NULL)
404 ret = PyObject_CallFunction(c->callback, "is", severity, message); /* New reference. */
405 else
406 ret = PyObject_CallFunction(c->callback, "isO", severity, message, c->data); /* New reference. */
408 if (ret == NULL) {
409 /* FIXME */
410 /* Do we really want to trigger a log callback because a log callback failed?
411 * Probably not. */
412 PyErr_Print();
413 } else {
414 Py_DECREF(ret);
415 }
416 CPY_RELEASE_THREADS
417 }
419 static void cpy_flush_callback(int timeout, const char *id, user_data_t *data) {
420 cpy_callback_t * c = data->data;
421 PyObject *ret;
423 CPY_LOCK_THREADS
424 if (c->data == NULL)
425 ret = PyObject_CallFunction(c->callback, "is", timeout, id); /* New reference. */
426 else
427 ret = PyObject_CallFunction(c->callback, "isO", timeout, id, c->data); /* New reference. */
429 if (ret == NULL) {
430 cpy_log_exception("flush callback");
431 } else {
432 Py_DECREF(ret);
433 }
434 CPY_RELEASE_THREADS
435 }
437 static PyObject *cpy_register_generic(cpy_callback_t **list_head, PyObject *args, PyObject *kwds, int short_name) {
438 char buf[512];
439 cpy_callback_t *c;
440 const char *name = NULL;
441 PyObject *callback = NULL, *data = NULL, *mod = NULL;
442 static char *kwlist[] = {"callback", "data", "name", NULL};
444 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
445 if (PyCallable_Check(callback) == 0) {
446 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
447 return NULL;
448 }
449 cpy_build_name(buf, sizeof(buf), callback, name, short_name);
451 Py_INCREF(callback);
452 Py_XINCREF(data);
453 c = malloc(sizeof(*c));
454 c->name = strdup(buf);
455 c->callback = callback;
456 c->data = data;
457 c->next = *list_head;
458 *list_head = c;
459 Py_XDECREF(mod);
460 return PyString_FromString(buf);
461 }
463 static PyObject *cpy_flush(cpy_callback_t **list_head, PyObject *args, PyObject *kwds) {
464 int timeout = -1;
465 const char *plugin = NULL, *identifier = NULL;
466 static char *kwlist[] = {"plugin", "timeout", "identifier", NULL};
468 if (PyArg_ParseTupleAndKeywords(args, kwds, "|ziz", kwlist, &plugin, &timeout, &identifier) == 0) return NULL;
469 Py_BEGIN_ALLOW_THREADS
470 plugin_flush(plugin, timeout, identifier);
471 Py_END_ALLOW_THREADS
472 Py_RETURN_NONE;
473 }
475 static PyObject *cpy_register_config(PyObject *self, PyObject *args, PyObject *kwds) {
476 return cpy_register_generic(&cpy_config_callbacks, args, kwds, 1);
477 }
479 static PyObject *cpy_register_init(PyObject *self, PyObject *args, PyObject *kwds) {
480 return cpy_register_generic(&cpy_init_callbacks, args, kwds, 0);
481 }
483 typedef int reg_function_t(const char *name, void *callback, void *data);
485 static PyObject *cpy_register_generic_userdata(void *reg, void *handler, PyObject *args, PyObject *kwds, int short_name) {
486 char buf[512];
487 reg_function_t *register_function = (reg_function_t *) reg;
488 cpy_callback_t *c = NULL;
489 user_data_t *user_data = NULL;
490 const char *name = NULL;
491 PyObject *callback = NULL, *data = NULL;
492 static char *kwlist[] = {"callback", "data", "name", NULL};
494 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
495 if (PyCallable_Check(callback) == 0) {
496 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
497 return NULL;
498 }
499 cpy_build_name(buf, sizeof(buf), callback, name, short_name);
501 Py_INCREF(callback);
502 Py_XINCREF(data);
503 c = malloc(sizeof(*c));
504 c->name = strdup(buf);
505 c->callback = callback;
506 c->data = data;
507 c->next = NULL;
508 user_data = malloc(sizeof(*user_data));
509 user_data->free_func = cpy_destroy_user_data;
510 user_data->data = c;
511 register_function(buf, handler, user_data);
512 return PyString_FromString(buf);
513 }
515 static PyObject *cpy_register_read(PyObject *self, PyObject *args, PyObject *kwds) {
516 char buf[512];
517 cpy_callback_t *c = NULL;
518 user_data_t *user_data = NULL;
519 double interval = 0;
520 const char *name = NULL;
521 PyObject *callback = NULL, *data = NULL;
522 struct timespec ts;
523 static char *kwlist[] = {"callback", "interval", "data", "name", NULL};
525 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|dOz", kwlist, &callback, &interval, &data, &name) == 0) return NULL;
526 if (PyCallable_Check(callback) == 0) {
527 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
528 return NULL;
529 }
530 cpy_build_name(buf, sizeof(buf), callback, name, 0);
532 Py_INCREF(callback);
533 Py_XINCREF(data);
534 c = malloc(sizeof(*c));
535 c->name = strdup(buf);
536 c->callback = callback;
537 c->data = data;
538 c->next = NULL;
539 user_data = malloc(sizeof(*user_data));
540 user_data->free_func = cpy_destroy_user_data;
541 user_data->data = c;
542 ts.tv_sec = interval;
543 ts.tv_nsec = (interval - ts.tv_sec) * 1000000000;
544 plugin_register_complex_read(buf, cpy_read_callback, &ts, user_data);
545 return PyString_FromString(buf);
546 }
548 static PyObject *cpy_register_log(PyObject *self, PyObject *args, PyObject *kwds) {
549 return cpy_register_generic_userdata(plugin_register_log, cpy_log_callback, args, kwds, 0);
550 }
552 static PyObject *cpy_register_write(PyObject *self, PyObject *args, PyObject *kwds) {
553 return cpy_register_generic_userdata(plugin_register_write, cpy_write_callback, args, kwds, 0);
554 }
556 static PyObject *cpy_register_notification(PyObject *self, PyObject *args, PyObject *kwds) {
557 return cpy_register_generic_userdata(plugin_register_notification, cpy_notification_callback, args, kwds, 0);
558 }
560 static PyObject *cpy_register_flush(PyObject *self, PyObject *args, PyObject *kwds) {
561 return cpy_register_generic_userdata(plugin_register_flush, cpy_flush_callback, args, kwds, 1);
562 }
564 static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args, PyObject *kwds) {
565 return cpy_register_generic(&cpy_shutdown_callbacks, args, kwds, 0);
566 }
568 static PyObject *cpy_error(PyObject *self, PyObject *args) {
569 const char *text;
570 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
571 Py_BEGIN_ALLOW_THREADS
572 plugin_log(LOG_ERR, "%s", text);
573 Py_END_ALLOW_THREADS
574 Py_RETURN_NONE;
575 }
577 static PyObject *cpy_warning(PyObject *self, PyObject *args) {
578 const char *text;
579 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
580 Py_BEGIN_ALLOW_THREADS
581 plugin_log(LOG_WARNING, "%s", text);
582 Py_END_ALLOW_THREADS
583 Py_RETURN_NONE;
584 }
586 static PyObject *cpy_notice(PyObject *self, PyObject *args) {
587 const char *text;
588 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
589 Py_BEGIN_ALLOW_THREADS
590 plugin_log(LOG_NOTICE, "%s", text);
591 Py_END_ALLOW_THREADS
592 Py_RETURN_NONE;
593 }
595 static PyObject *cpy_info(PyObject *self, PyObject *args) {
596 const char *text;
597 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
598 Py_BEGIN_ALLOW_THREADS
599 plugin_log(LOG_INFO, "%s", text);
600 Py_END_ALLOW_THREADS
601 Py_RETURN_NONE;
602 }
604 static PyObject *cpy_debug(PyObject *self, PyObject *args) {
605 #ifdef COLLECT_DEBUG
606 const char *text;
607 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
608 Py_BEGIN_ALLOW_THREADS
609 plugin_log(LOG_DEBUG, "%s", text);
610 Py_END_ALLOW_THREADS
611 #endif
612 Py_RETURN_NONE;
613 }
615 static PyObject *cpy_unregister_generic(cpy_callback_t **list_head, PyObject *arg, const char *desc, int short_name) {
616 char buf[512];
617 const char *name;
618 cpy_callback_t *prev = NULL, *tmp;
620 if (PyString_Check(arg)) {
621 name = PyString_AsString(arg);
622 } else {
623 if (!PyCallable_Check(arg)) {
624 PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
625 return NULL;
626 }
627 cpy_build_name(buf, sizeof(buf), arg, NULL, short_name);
628 name = buf;
629 }
630 for (tmp = *list_head; tmp; prev = tmp, tmp = tmp->next)
631 if (strcmp(name, tmp->name) == 0)
632 break;
634 if (tmp == NULL) {
635 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
636 return NULL;
637 }
638 /* Yes, this is actually save. To call this function the calles has to
639 * hold the GIL. Well, save as long as there is only one GIL anyway ... */
640 if (prev == NULL)
641 *list_head = tmp->next;
642 else
643 prev->next = tmp->next;
644 cpy_destroy_user_data(tmp);
645 Py_RETURN_NONE;
646 }
648 typedef int cpy_unregister_function_t(const char *name);
650 static PyObject *cpy_unregister_generic_userdata(cpy_unregister_function_t *unreg, PyObject *arg, const char *desc, int short_name) {
651 char buf[512];
652 const char *name;
654 if (PyString_Check(arg)) {
655 name = PyString_AsString(arg);
656 } else {
657 if (!PyCallable_Check(arg)) {
658 PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
659 return NULL;
660 }
661 cpy_build_name(buf, sizeof(buf), arg, NULL, short_name);
662 name = buf;
663 }
664 if (unreg(name) == 0)
665 Py_RETURN_NONE;
666 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
667 return NULL;
668 }
670 static PyObject *cpy_unregister_log(PyObject *self, PyObject *arg) {
671 return cpy_unregister_generic_userdata(plugin_unregister_log, arg, "log", 0);
672 }
674 static PyObject *cpy_unregister_init(PyObject *self, PyObject *arg) {
675 return cpy_unregister_generic(&cpy_init_callbacks, arg, "init", 0);
676 }
678 static PyObject *cpy_unregister_config(PyObject *self, PyObject *arg) {
679 return cpy_unregister_generic(&cpy_config_callbacks, arg, "config", 1);
680 }
682 static PyObject *cpy_unregister_read(PyObject *self, PyObject *arg) {
683 return cpy_unregister_generic_userdata(plugin_unregister_read, arg, "read", 0);
684 }
686 static PyObject *cpy_unregister_write(PyObject *self, PyObject *arg) {
687 return cpy_unregister_generic_userdata(plugin_unregister_write, arg, "write", 0);
688 }
690 static PyObject *cpy_unregister_notification(PyObject *self, PyObject *arg) {
691 return cpy_unregister_generic_userdata(plugin_unregister_notification, arg, "notification", 0);
692 }
694 static PyObject *cpy_unregister_flush(PyObject *self, PyObject *arg) {
695 return cpy_unregister_generic_userdata(plugin_unregister_flush, arg, "flush", 1);
696 }
698 static PyObject *cpy_unregister_shutdown(PyObject *self, PyObject *arg) {
699 return cpy_unregister_generic(&cpy_shutdown_callbacks, arg, "shutdown", 0);
700 }
702 static PyMethodDef cpy_methods[] = {
703 {"debug", cpy_debug, METH_VARARGS, log_doc},
704 {"info", cpy_info, METH_VARARGS, log_doc},
705 {"notice", cpy_notice, METH_VARARGS, log_doc},
706 {"warning", cpy_warning, METH_VARARGS, log_doc},
707 {"error", cpy_error, METH_VARARGS, log_doc},
708 {"flush", (PyCFunction) cpy_flush, METH_VARARGS | METH_KEYWORDS, flush_doc},
709 {"register_log", (PyCFunction) cpy_register_log, METH_VARARGS | METH_KEYWORDS, reg_log_doc},
710 {"register_init", (PyCFunction) cpy_register_init, METH_VARARGS | METH_KEYWORDS, reg_init_doc},
711 {"register_config", (PyCFunction) cpy_register_config, METH_VARARGS | METH_KEYWORDS, reg_config_doc},
712 {"register_read", (PyCFunction) cpy_register_read, METH_VARARGS | METH_KEYWORDS, reg_read_doc},
713 {"register_write", (PyCFunction) cpy_register_write, METH_VARARGS | METH_KEYWORDS, reg_write_doc},
714 {"register_notification", (PyCFunction) cpy_register_notification, METH_VARARGS | METH_KEYWORDS, reg_notification_doc},
715 {"register_flush", (PyCFunction) cpy_register_flush, METH_VARARGS | METH_KEYWORDS, reg_flush_doc},
716 {"register_shutdown", (PyCFunction) cpy_register_shutdown, METH_VARARGS | METH_KEYWORDS, reg_shutdown_doc},
717 {"unregister_log", cpy_unregister_log, METH_O, unregister_doc},
718 {"unregister_init", cpy_unregister_init, METH_O, unregister_doc},
719 {"unregister_config", cpy_unregister_config, METH_O, unregister_doc},
720 {"unregister_read", cpy_unregister_read, METH_O, unregister_doc},
721 {"unregister_write", cpy_unregister_write, METH_O, unregister_doc},
722 {"unregister_notification", cpy_unregister_notification, METH_O, unregister_doc},
723 {"unregister_flush", cpy_unregister_flush, METH_O, unregister_doc},
724 {"unregister_shutdown", cpy_unregister_shutdown, METH_O, unregister_doc},
725 {0, 0, 0, 0}
726 };
728 static int cpy_shutdown(void) {
729 cpy_callback_t *c;
730 PyObject *ret;
732 /* This can happen if the module was loaded but not configured. */
733 if (state != NULL)
734 PyEval_RestoreThread(state);
736 for (c = cpy_shutdown_callbacks; c; c = c->next) {
737 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
738 if (ret == NULL)
739 cpy_log_exception("shutdown callback");
740 else
741 Py_DECREF(ret);
742 }
743 PyErr_Print();
744 Py_Finalize();
745 return 0;
746 }
748 static void cpy_int_handler(int sig) {
749 return;
750 }
752 static void *cpy_interactive(void *data) {
753 sigset_t sigset;
754 struct sigaction sig_int_action, old;
756 /* Signal handler in a plugin? Bad stuff, but the best way to
757 * handle it I guess. In an interactive session people will
758 * press Ctrl+C at some time, which will generate a SIGINT.
759 * This will cause collectd to shutdown, thus killing the
760 * interactive interpreter, and leaving the terminal in a
761 * mess. Chances are, this isn't what the user wanted to do.
762 *
763 * So this is the plan:
764 * 1. Block SIGINT in the main thread.
765 * 2. Install our own signal handler that does nothing.
766 * 3. Unblock SIGINT in the interactive thread.
767 *
768 * This will make sure that SIGINT won't kill collectd but
769 * still interrupt syscalls like sleep and pause.
770 * It does not raise a KeyboardInterrupt exception because so
771 * far nobody managed to figure out how to do that. */
772 memset (&sig_int_action, '\0', sizeof (sig_int_action));
773 sig_int_action.sa_handler = cpy_int_handler;
774 sigaction (SIGINT, &sig_int_action, &old);
776 sigemptyset(&sigset);
777 sigaddset(&sigset, SIGINT);
778 pthread_sigmask(SIG_UNBLOCK, &sigset, NULL);
779 PyEval_AcquireThread(state);
780 if (PyImport_ImportModule("readline") == NULL) {
781 /* This interactive session will suck. */
782 cpy_log_exception("interactive session init");
783 }
784 PyRun_InteractiveLoop(stdin, "<stdin>");
785 PyErr_Print();
786 PyEval_ReleaseThread(state);
787 NOTICE("python: Interactive interpreter exited, stopping collectd ...");
788 /* Restore the original collectd SIGINT handler and raise SIGINT.
789 * The main thread still has SIGINT blocked and there's nothing we
790 * can do about that so this thread will handle it. But that's not
791 * important, except that it won't interrupt the main loop and so
792 * it might take a few seconds before collectd really shuts down. */
793 sigaction (SIGINT, &old, NULL);
794 raise(SIGINT);
795 pause();
796 return NULL;
797 }
799 static int cpy_init(void) {
800 cpy_callback_t *c;
801 PyObject *ret;
802 static pthread_t thread;
803 sigset_t sigset;
805 PyEval_InitThreads();
806 /* Now it's finally OK to use python threads. */
807 for (c = cpy_init_callbacks; c; c = c->next) {
808 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
809 if (ret == NULL)
810 cpy_log_exception("init callback");
811 else
812 Py_DECREF(ret);
813 }
814 sigemptyset(&sigset);
815 sigaddset(&sigset, SIGINT);
816 pthread_sigmask(SIG_BLOCK, &sigset, NULL);
817 state = PyEval_SaveThread();
818 if (do_interactive) {
819 if (pthread_create(&thread, NULL, cpy_interactive, NULL)) {
820 ERROR("python: Error creating thread for interactive interpreter.");
821 }
822 }
824 return 0;
825 }
827 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
828 int i;
829 PyObject *item, *values, *children, *tmp;
831 if (parent == NULL)
832 parent = Py_None;
834 values = PyTuple_New(ci->values_num); /* New reference. */
835 for (i = 0; i < ci->values_num; ++i) {
836 if (ci->values[i].type == OCONFIG_TYPE_STRING) {
837 PyTuple_SET_ITEM(values, i, PyString_FromString(ci->values[i].value.string));
838 } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
839 PyTuple_SET_ITEM(values, i, PyFloat_FromDouble(ci->values[i].value.number));
840 } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
841 PyTuple_SET_ITEM(values, i, PyBool_FromLong(ci->values[i].value.boolean));
842 }
843 }
845 item = PyObject_CallFunction((PyObject *) &ConfigType, "sONO", ci->key, parent, values, Py_None);
846 if (item == NULL)
847 return NULL;
848 children = PyTuple_New(ci->children_num); /* New reference. */
849 for (i = 0; i < ci->children_num; ++i) {
850 PyTuple_SET_ITEM(children, i, cpy_oconfig_to_pyconfig(ci->children + i, item));
851 }
852 tmp = ((Config *) item)->children;
853 ((Config *) item)->children = children;
854 Py_XDECREF(tmp);
855 return item;
856 }
858 static int cpy_config(oconfig_item_t *ci) {
859 int i;
860 PyObject *sys, *tb;
861 PyObject *sys_path;
862 PyObject *module;
864 /* Ok in theory we shouldn't do initialization at this point
865 * but we have to. In order to give python scripts a chance
866 * to register a config callback we need to be able to execute
867 * python code during the config callback so we have to start
868 * the interpreter here. */
869 /* Do *not* use the python "thread" module at this point! */
870 Py_Initialize();
872 PyType_Ready(&ConfigType);
873 PyType_Ready(&PluginDataType);
874 ValuesType.tp_base = &PluginDataType;
875 PyType_Ready(&ValuesType);
876 NotificationType.tp_base = &PluginDataType;
877 PyType_Ready(&NotificationType);
878 sys = PyImport_ImportModule("sys"); /* New reference. */
879 if (sys == NULL) {
880 cpy_log_exception("python initialization");
881 return 1;
882 }
883 sys_path = PyObject_GetAttrString(sys, "path"); /* New reference. */
884 Py_DECREF(sys);
885 if (sys_path == NULL) {
886 cpy_log_exception("python initialization");
887 return 1;
888 }
889 module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
890 PyModule_AddObject(module, "Config", (PyObject *) &ConfigType); /* Steals a reference. */
891 PyModule_AddObject(module, "Values", (PyObject *) &ValuesType); /* Steals a reference. */
892 PyModule_AddObject(module, "Notification", (PyObject *) &NotificationType); /* Steals a reference. */
893 PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG);
894 PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO);
895 PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE);
896 PyModule_AddIntConstant(module, "LOG_WARNING", LOG_WARNING);
897 PyModule_AddIntConstant(module, "LOG_ERROR", LOG_ERR);
898 PyModule_AddIntConstant(module, "NOTIF_FAILURE", NOTIF_FAILURE);
899 PyModule_AddIntConstant(module, "NOTIF_WARNING", NOTIF_WARNING);
900 PyModule_AddIntConstant(module, "NOTIF_OKAY", NOTIF_OKAY);
901 for (i = 0; i < ci->children_num; ++i) {
902 oconfig_item_t *item = ci->children + i;
904 if (strcasecmp(item->key, "Interactive") == 0) {
905 if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_BOOLEAN)
906 continue;
907 do_interactive = item->values[0].value.boolean;
908 } else if (strcasecmp(item->key, "LogTraces") == 0) {
909 if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_BOOLEAN)
910 continue;
911 if (!item->values[0].value.boolean) {
912 Py_XDECREF(cpy_format_exception);
913 cpy_format_exception = NULL;
914 continue;
915 }
916 if (cpy_format_exception)
917 continue;
918 tb = PyImport_ImportModule("traceback"); /* New reference. */
919 if (tb == NULL) {
920 cpy_log_exception("python initialization");
921 continue;
922 }
923 cpy_format_exception = PyObject_GetAttrString(tb, "format_exception"); /* New reference. */
924 Py_DECREF(tb);
925 if (cpy_format_exception == NULL)
926 cpy_log_exception("python initialization");
927 } else if (strcasecmp(item->key, "ModulePath") == 0) {
928 char *dir = NULL;
929 PyObject *dir_object;
931 if (cf_util_get_string(item, &dir) != 0)
932 continue;
933 dir_object = PyString_FromString(dir); /* New reference. */
934 if (dir_object == NULL) {
935 ERROR("python plugin: Unable to convert \"%s\" to "
936 "a python object.", dir);
937 free(dir);
938 cpy_log_exception("python initialization");
939 continue;
940 }
941 if (PyList_Append(sys_path, dir_object) != 0) {
942 ERROR("python plugin: Unable to append \"%s\" to "
943 "python module path.", dir);
944 cpy_log_exception("python initialization");
945 }
946 Py_DECREF(dir_object);
947 free(dir);
948 } else if (strcasecmp(item->key, "Import") == 0) {
949 char *module_name = NULL;
950 PyObject *module;
952 if (cf_util_get_string(item, &module_name) != 0)
953 continue;
954 module = PyImport_ImportModule(module_name); /* New reference. */
955 if (module == NULL) {
956 ERROR("python plugin: Error importing module \"%s\".", module_name);
957 cpy_log_exception("importing module");
958 PyErr_Print();
959 }
960 free(module_name);
961 Py_XDECREF(module);
962 } else if (strcasecmp(item->key, "Module") == 0) {
963 char *name = NULL;
964 cpy_callback_t *c;
965 PyObject *ret;
967 if (cf_util_get_string(item, &name) != 0)
968 continue;
969 for (c = cpy_config_callbacks; c; c = c->next) {
970 if (strcasecmp(c->name + 7, name) == 0)
971 break;
972 }
973 if (c == NULL) {
974 WARNING("python plugin: Found a configuration for the \"%s\" plugin, "
975 "but the plugin isn't loaded or didn't register "
976 "a configuration callback.", name);
977 free(name);
978 continue;
979 }
980 free(name);
981 if (c->data == NULL)
982 ret = PyObject_CallFunction(c->callback, "N",
983 cpy_oconfig_to_pyconfig(item, NULL)); /* New reference. */
984 else
985 ret = PyObject_CallFunction(c->callback, "NO",
986 cpy_oconfig_to_pyconfig(item, NULL), c->data); /* New reference. */
987 if (ret == NULL)
988 cpy_log_exception("loading module");
989 else
990 Py_DECREF(ret);
991 } else {
992 WARNING("python plugin: Ignoring unknown config key \"%s\".", item->key);
993 }
994 }
995 Py_DECREF(sys_path);
996 return 0;
997 }
999 void module_register(void) {
1000 plugin_register_complex_config("python", cpy_config);
1001 plugin_register_init("python", cpy_init);
1002 plugin_register_shutdown("python", cpy_shutdown);
1003 }