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 ERROR("Unhandled python exception in %s: %s: %s", context, typename, message);
272 Py_XDECREF(tn);
273 Py_XDECREF(m);
274 if (!cpy_format_exception) {
275 PyErr_Clear();
276 Py_XDECREF(type);
277 Py_XDECREF(value);
278 Py_XDECREF(traceback);
279 return;
280 }
281 if (!traceback) {
282 PyErr_Clear();
283 return;
284 }
285 list = PyObject_CallFunction(cpy_format_exception, "NNN", type, value, traceback); /* New reference. */
286 if (list)
287 l = PyObject_Length(list);
288 for (i = 0; i < l; ++i) {
289 char *s;
290 PyObject *line;
292 line = PyList_GET_ITEM(list, i); /* Borrowed reference. */
293 s = strdup(PyString_AsString(line));
294 Py_DECREF(line);
295 if (s[strlen(s) - 1] == '\n')
296 s[strlen(s) - 1] = 0;
297 ERROR("%s", s);
298 free(s);
299 }
300 Py_XDECREF(list);
301 PyErr_Clear();
302 }
304 static int cpy_read_callback(user_data_t *data) {
305 cpy_callback_t *c = data->data;
306 PyObject *ret;
308 CPY_LOCK_THREADS
309 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
310 if (ret == NULL) {
311 cpy_log_exception("read callback");
312 } else {
313 Py_DECREF(ret);
314 }
315 CPY_RELEASE_THREADS
316 return 0;
317 }
319 static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_list, user_data_t *data) {
320 int i;
321 cpy_callback_t *c = data->data;
322 PyObject *ret, *v, *list;
324 CPY_LOCK_THREADS
325 list = PyList_New(value_list->values_len); /* New reference. */
326 if (list == NULL) {
327 cpy_log_exception("write callback");
328 CPY_RETURN_FROM_THREADS 0;
329 }
330 for (i = 0; i < value_list->values_len; ++i) {
331 if (ds->ds->type == DS_TYPE_COUNTER) {
332 if ((long) value_list->values[i].counter == value_list->values[i].counter)
333 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].counter));
334 else
335 PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].counter));
336 } else if (ds->ds->type == DS_TYPE_GAUGE) {
337 PyList_SetItem(list, i, PyFloat_FromDouble(value_list->values[i].gauge));
338 } else if (ds->ds->type == DS_TYPE_DERIVE) {
339 if ((long) value_list->values[i].derive == value_list->values[i].derive)
340 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].derive));
341 else
342 PyList_SetItem(list, i, PyLong_FromLongLong(value_list->values[i].derive));
343 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
344 if ((long) value_list->values[i].absolute == value_list->values[i].absolute)
345 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].absolute));
346 else
347 PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].absolute));
348 } else {
349 ERROR("cpy_write_callback: Unknown value type %d.", ds->ds->type);
350 Py_DECREF(list);
351 CPY_RETURN_FROM_THREADS 0;
352 }
353 if (PyErr_Occurred() != NULL) {
354 cpy_log_exception("value building for write callback");
355 CPY_RETURN_FROM_THREADS 0;
356 }
357 }
358 v = PyObject_CallFunction((PyObject *) &ValuesType, "sOssssdi", value_list->type, list,
359 value_list->plugin_instance, value_list->type_instance, value_list->plugin,
360 value_list->host, (double) value_list->time, value_list->interval);
361 Py_DECREF(list);
362 ret = PyObject_CallFunctionObjArgs(c->callback, v, c->data, (void *) 0); /* New reference. */
363 if (ret == NULL) {
364 cpy_log_exception("write callback");
365 } else {
366 Py_DECREF(ret);
367 }
368 CPY_RELEASE_THREADS
369 return 0;
370 }
372 static int cpy_notification_callback(const notification_t *notification, user_data_t *data) {
373 cpy_callback_t *c = data->data;
374 PyObject *ret, *n;
376 CPY_LOCK_THREADS
377 n = PyObject_CallFunction((PyObject *) &NotificationType, "ssssssdi", notification->type, notification->message,
378 notification->plugin_instance, notification->type_instance, notification->plugin,
379 notification->host, (double) notification->time, notification->severity);
380 ret = PyObject_CallFunctionObjArgs(c->callback, n, c->data, (void *) 0); /* New reference. */
381 if (ret == NULL) {
382 cpy_log_exception("notification callback");
383 } else {
384 Py_DECREF(ret);
385 }
386 CPY_RELEASE_THREADS
387 return 0;
388 }
390 static void cpy_log_callback(int severity, const char *message, user_data_t *data) {
391 cpy_callback_t * c = data->data;
392 PyObject *ret;
394 CPY_LOCK_THREADS
395 if (c->data == NULL)
396 ret = PyObject_CallFunction(c->callback, "is", severity, message); /* New reference. */
397 else
398 ret = PyObject_CallFunction(c->callback, "isO", severity, message, c->data); /* New reference. */
400 if (ret == NULL) {
401 /* FIXME */
402 /* Do we really want to trigger a log callback because a log callback failed?
403 * Probably not. */
404 PyErr_Print();
405 } else {
406 Py_DECREF(ret);
407 }
408 CPY_RELEASE_THREADS
409 }
411 static void cpy_flush_callback(int timeout, const char *id, user_data_t *data) {
412 cpy_callback_t * c = data->data;
413 PyObject *ret;
415 CPY_LOCK_THREADS
416 if (c->data == NULL)
417 ret = PyObject_CallFunction(c->callback, "is", timeout, id); /* New reference. */
418 else
419 ret = PyObject_CallFunction(c->callback, "isO", timeout, id, c->data); /* New reference. */
421 if (ret == NULL) {
422 cpy_log_exception("flush callback");
423 } else {
424 Py_DECREF(ret);
425 }
426 CPY_RELEASE_THREADS
427 }
429 static PyObject *cpy_register_generic(cpy_callback_t **list_head, PyObject *args, PyObject *kwds, int short_name) {
430 char buf[512];
431 cpy_callback_t *c;
432 const char *name = NULL;
433 PyObject *callback = NULL, *data = NULL, *mod = NULL;
434 static char *kwlist[] = {"callback", "data", "name", NULL};
436 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
437 if (PyCallable_Check(callback) == 0) {
438 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
439 return NULL;
440 }
441 cpy_build_name(buf, sizeof(buf), callback, name, short_name);
443 Py_INCREF(callback);
444 Py_XINCREF(data);
445 c = malloc(sizeof(*c));
446 c->name = strdup(buf);
447 c->callback = callback;
448 c->data = data;
449 c->next = *list_head;
450 *list_head = c;
451 Py_XDECREF(mod);
452 return PyString_FromString(buf);
453 }
455 static PyObject *cpy_flush(cpy_callback_t **list_head, PyObject *args, PyObject *kwds) {
456 int timeout = -1;
457 const char *plugin = NULL, *identifier = NULL;
458 static char *kwlist[] = {"plugin", "timeout", "identifier", NULL};
460 if (PyArg_ParseTupleAndKeywords(args, kwds, "|ziz", kwlist, &plugin, &timeout, &identifier) == 0) return NULL;
461 Py_BEGIN_ALLOW_THREADS
462 plugin_flush(plugin, timeout, identifier);
463 Py_END_ALLOW_THREADS
464 Py_RETURN_NONE;
465 }
467 static PyObject *cpy_register_config(PyObject *self, PyObject *args, PyObject *kwds) {
468 return cpy_register_generic(&cpy_config_callbacks, args, kwds, 1);
469 }
471 static PyObject *cpy_register_init(PyObject *self, PyObject *args, PyObject *kwds) {
472 return cpy_register_generic(&cpy_init_callbacks, args, kwds, 0);
473 }
475 typedef int reg_function_t(const char *name, void *callback, void *data);
477 static PyObject *cpy_register_generic_userdata(void *reg, void *handler, PyObject *args, PyObject *kwds, int short_name) {
478 char buf[512];
479 reg_function_t *register_function = (reg_function_t *) reg;
480 cpy_callback_t *c = NULL;
481 user_data_t *user_data = NULL;
482 const char *name = NULL;
483 PyObject *callback = NULL, *data = NULL;
484 static char *kwlist[] = {"callback", "data", "name", NULL};
486 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
487 if (PyCallable_Check(callback) == 0) {
488 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
489 return NULL;
490 }
491 cpy_build_name(buf, sizeof(buf), callback, name, short_name);
493 Py_INCREF(callback);
494 Py_XINCREF(data);
495 c = malloc(sizeof(*c));
496 c->name = strdup(buf);
497 c->callback = callback;
498 c->data = data;
499 c->next = NULL;
500 user_data = malloc(sizeof(*user_data));
501 user_data->free_func = cpy_destroy_user_data;
502 user_data->data = c;
503 register_function(buf, handler, user_data);
504 return PyString_FromString(buf);
505 }
507 static PyObject *cpy_register_read(PyObject *self, PyObject *args, PyObject *kwds) {
508 char buf[512];
509 cpy_callback_t *c = NULL;
510 user_data_t *user_data = NULL;
511 double interval = 0;
512 const char *name = NULL;
513 PyObject *callback = NULL, *data = NULL;
514 struct timespec ts;
515 static char *kwlist[] = {"callback", "interval", "data", "name", NULL};
517 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|dOz", kwlist, &callback, &interval, &data, &name) == 0) return NULL;
518 if (PyCallable_Check(callback) == 0) {
519 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
520 return NULL;
521 }
522 cpy_build_name(buf, sizeof(buf), callback, name, 0);
524 Py_INCREF(callback);
525 Py_XINCREF(data);
526 c = malloc(sizeof(*c));
527 c->name = strdup(buf);
528 c->callback = callback;
529 c->data = data;
530 c->next = NULL;
531 user_data = malloc(sizeof(*user_data));
532 user_data->free_func = cpy_destroy_user_data;
533 user_data->data = c;
534 ts.tv_sec = interval;
535 ts.tv_nsec = (interval - ts.tv_sec) * 1000000000;
536 plugin_register_complex_read(buf, cpy_read_callback, &ts, user_data);
537 return PyString_FromString(buf);
538 }
540 static PyObject *cpy_register_log(PyObject *self, PyObject *args, PyObject *kwds) {
541 return cpy_register_generic_userdata(plugin_register_log, cpy_log_callback, args, kwds, 0);
542 }
544 static PyObject *cpy_register_write(PyObject *self, PyObject *args, PyObject *kwds) {
545 return cpy_register_generic_userdata(plugin_register_write, cpy_write_callback, args, kwds, 0);
546 }
548 static PyObject *cpy_register_notification(PyObject *self, PyObject *args, PyObject *kwds) {
549 return cpy_register_generic_userdata(plugin_register_notification, cpy_notification_callback, args, kwds, 0);
550 }
552 static PyObject *cpy_register_flush(PyObject *self, PyObject *args, PyObject *kwds) {
553 return cpy_register_generic_userdata(plugin_register_flush, cpy_flush_callback, args, kwds, 1);
554 }
556 static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args, PyObject *kwds) {
557 return cpy_register_generic(&cpy_shutdown_callbacks, args, kwds, 0);
558 }
560 static PyObject *cpy_error(PyObject *self, PyObject *args) {
561 const char *text;
562 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
563 Py_BEGIN_ALLOW_THREADS
564 plugin_log(LOG_ERR, "%s", text);
565 Py_END_ALLOW_THREADS
566 Py_RETURN_NONE;
567 }
569 static PyObject *cpy_warning(PyObject *self, PyObject *args) {
570 const char *text;
571 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
572 Py_BEGIN_ALLOW_THREADS
573 plugin_log(LOG_WARNING, "%s", text);
574 Py_END_ALLOW_THREADS
575 Py_RETURN_NONE;
576 }
578 static PyObject *cpy_notice(PyObject *self, PyObject *args) {
579 const char *text;
580 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
581 Py_BEGIN_ALLOW_THREADS
582 plugin_log(LOG_NOTICE, "%s", text);
583 Py_END_ALLOW_THREADS
584 Py_RETURN_NONE;
585 }
587 static PyObject *cpy_info(PyObject *self, PyObject *args) {
588 const char *text;
589 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
590 Py_BEGIN_ALLOW_THREADS
591 plugin_log(LOG_INFO, "%s", text);
592 Py_END_ALLOW_THREADS
593 Py_RETURN_NONE;
594 }
596 static PyObject *cpy_debug(PyObject *self, PyObject *args) {
597 #ifdef COLLECT_DEBUG
598 const char *text;
599 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
600 plugin_log(LOG_DEBUG, "%s", text);
601 #endif
602 Py_RETURN_NONE;
603 }
605 static PyObject *cpy_unregister_generic(cpy_callback_t **list_head, PyObject *arg, const char *desc, int short_name) {
606 char buf[512];
607 const char *name;
608 cpy_callback_t *prev = NULL, *tmp;
610 if (PyString_Check(arg)) {
611 name = PyString_AsString(arg);
612 } else {
613 if (!PyCallable_Check(arg)) {
614 PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
615 return NULL;
616 }
617 cpy_build_name(buf, sizeof(buf), arg, NULL, short_name);
618 name = buf;
619 }
620 for (tmp = *list_head; tmp; prev = tmp, tmp = tmp->next)
621 if (strcmp(name, tmp->name) == 0)
622 break;
624 if (tmp == NULL) {
625 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
626 return NULL;
627 }
628 /* Yes, this is actually save. To call this function the calles has to
629 * hold the GIL. Well, save as long as there is only one GIL anyway ... */
630 if (prev == NULL)
631 *list_head = tmp->next;
632 else
633 prev->next = tmp->next;
634 cpy_destroy_user_data(tmp);
635 Py_RETURN_NONE;
636 }
638 typedef int cpy_unregister_function_t(const char *name);
640 static PyObject *cpy_unregister_generic_userdata(cpy_unregister_function_t *unreg, PyObject *arg, const char *desc, int short_name) {
641 char buf[512];
642 const char *name;
644 if (PyString_Check(arg)) {
645 name = PyString_AsString(arg);
646 } else {
647 if (!PyCallable_Check(arg)) {
648 PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
649 return NULL;
650 }
651 cpy_build_name(buf, sizeof(buf), arg, NULL, short_name);
652 name = buf;
653 }
654 if (unreg(name) == 0)
655 Py_RETURN_NONE;
656 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
657 return NULL;
658 }
660 static PyObject *cpy_unregister_log(PyObject *self, PyObject *arg) {
661 return cpy_unregister_generic_userdata(plugin_unregister_log, arg, "log", 0);
662 }
664 static PyObject *cpy_unregister_init(PyObject *self, PyObject *arg) {
665 return cpy_unregister_generic(&cpy_init_callbacks, arg, "init", 0);
666 }
668 static PyObject *cpy_unregister_config(PyObject *self, PyObject *arg) {
669 return cpy_unregister_generic(&cpy_config_callbacks, arg, "config", 1);
670 }
672 static PyObject *cpy_unregister_read(PyObject *self, PyObject *arg) {
673 return cpy_unregister_generic_userdata(plugin_unregister_read, arg, "read", 0);
674 }
676 static PyObject *cpy_unregister_write(PyObject *self, PyObject *arg) {
677 return cpy_unregister_generic_userdata(plugin_unregister_write, arg, "write", 0);
678 }
680 static PyObject *cpy_unregister_notification(PyObject *self, PyObject *arg) {
681 return cpy_unregister_generic_userdata(plugin_unregister_notification, arg, "notification", 0);
682 }
684 static PyObject *cpy_unregister_flush(PyObject *self, PyObject *arg) {
685 return cpy_unregister_generic_userdata(plugin_unregister_flush, arg, "flush", 1);
686 }
688 static PyObject *cpy_unregister_shutdown(PyObject *self, PyObject *arg) {
689 return cpy_unregister_generic(&cpy_shutdown_callbacks, arg, "shutdown", 0);
690 }
692 static PyMethodDef cpy_methods[] = {
693 {"debug", cpy_debug, METH_VARARGS, log_doc},
694 {"info", cpy_info, METH_VARARGS, log_doc},
695 {"notice", cpy_notice, METH_VARARGS, log_doc},
696 {"warning", cpy_warning, METH_VARARGS, log_doc},
697 {"error", cpy_error, METH_VARARGS, log_doc},
698 {"flush", (PyCFunction) cpy_flush, METH_VARARGS | METH_KEYWORDS, flush_doc},
699 {"register_log", (PyCFunction) cpy_register_log, METH_VARARGS | METH_KEYWORDS, reg_log_doc},
700 {"register_init", (PyCFunction) cpy_register_init, METH_VARARGS | METH_KEYWORDS, reg_init_doc},
701 {"register_config", (PyCFunction) cpy_register_config, METH_VARARGS | METH_KEYWORDS, reg_config_doc},
702 {"register_read", (PyCFunction) cpy_register_read, METH_VARARGS | METH_KEYWORDS, reg_read_doc},
703 {"register_write", (PyCFunction) cpy_register_write, METH_VARARGS | METH_KEYWORDS, reg_write_doc},
704 {"register_notification", (PyCFunction) cpy_register_notification, METH_VARARGS | METH_KEYWORDS, reg_notification_doc},
705 {"register_flush", (PyCFunction) cpy_register_flush, METH_VARARGS | METH_KEYWORDS, reg_flush_doc},
706 {"register_shutdown", (PyCFunction) cpy_register_shutdown, METH_VARARGS | METH_KEYWORDS, reg_shutdown_doc},
707 {"unregister_log", cpy_unregister_log, METH_O, unregister_doc},
708 {"unregister_init", cpy_unregister_init, METH_O, unregister_doc},
709 {"unregister_config", cpy_unregister_config, METH_O, unregister_doc},
710 {"unregister_read", cpy_unregister_read, METH_O, unregister_doc},
711 {"unregister_write", cpy_unregister_write, METH_O, unregister_doc},
712 {"unregister_notification", cpy_unregister_notification, METH_O, unregister_doc},
713 {"unregister_flush", cpy_unregister_flush, METH_O, unregister_doc},
714 {"unregister_shutdown", cpy_unregister_shutdown, METH_O, unregister_doc},
715 {0, 0, 0, 0}
716 };
718 static int cpy_shutdown(void) {
719 cpy_callback_t *c;
720 PyObject *ret;
722 /* This can happen if the module was loaded but not configured. */
723 if (state != NULL)
724 PyEval_RestoreThread(state);
726 for (c = cpy_shutdown_callbacks; c; c = c->next) {
727 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
728 if (ret == NULL)
729 cpy_log_exception("shutdown callback");
730 else
731 Py_DECREF(ret);
732 }
733 PyErr_Print();
734 Py_Finalize();
735 return 0;
736 }
738 static void cpy_int_handler(int sig) {
739 return;
740 }
742 static void *cpy_interactive(void *data) {
743 sigset_t sigset;
744 struct sigaction sig_int_action, old;
746 /* Signal handler in a plugin? Bad stuff, but the best way to
747 * handle it I guess. In an interactive session people will
748 * press Ctrl+C at some time, which will generate a SIGINT.
749 * This will cause collectd to shutdown, thus killing the
750 * interactive interpreter, and leaving the terminal in a
751 * mess. Chances are, this isn't what the user wanted to do.
752 *
753 * So this is the plan:
754 * 1. Block SIGINT in the main thread.
755 * 2. Install our own signal handler that does nothing.
756 * 3. Unblock SIGINT in the interactive thread.
757 *
758 * This will make sure that SIGINT won't kill collectd but
759 * still interrupt syscalls like sleep and pause.
760 * It does not raise a KeyboardInterrupt exception because so
761 * far nobody managed to figure out how to do that. */
762 memset (&sig_int_action, '\0', sizeof (sig_int_action));
763 sig_int_action.sa_handler = cpy_int_handler;
764 sigaction (SIGINT, &sig_int_action, &old);
766 sigemptyset(&sigset);
767 sigaddset(&sigset, SIGINT);
768 pthread_sigmask(SIG_UNBLOCK, &sigset, NULL);
769 PyEval_AcquireThread(state);
770 if (PyImport_ImportModule("readline") == NULL) {
771 /* This interactive session will suck. */
772 cpy_log_exception("interactive session init");
773 }
774 PyRun_InteractiveLoop(stdin, "<stdin>");
775 PyErr_Print();
776 PyEval_ReleaseThread(state);
777 NOTICE("python: Interactive interpreter exited, stopping collectd ...");
778 /* Restore the original collectd SIGINT handler and raise SIGINT.
779 * The main thread still has SIGINT blocked and there's nothing we
780 * can do about that so this thread will handle it. But that's not
781 * important, except that it won't interrupt the main loop and so
782 * it might take a few seconds before collectd really shuts down. */
783 sigaction (SIGINT, &old, NULL);
784 raise(SIGINT);
785 pause();
786 return NULL;
787 }
789 static int cpy_init(void) {
790 cpy_callback_t *c;
791 PyObject *ret;
792 static pthread_t thread;
793 sigset_t sigset;
795 PyEval_InitThreads();
796 /* Now it's finally OK to use python threads. */
797 for (c = cpy_init_callbacks; c; c = c->next) {
798 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
799 if (ret == NULL)
800 cpy_log_exception("init callback");
801 else
802 Py_DECREF(ret);
803 }
804 sigemptyset(&sigset);
805 sigaddset(&sigset, SIGINT);
806 pthread_sigmask(SIG_BLOCK, &sigset, NULL);
807 state = PyEval_SaveThread();
808 if (do_interactive) {
809 if (pthread_create(&thread, NULL, cpy_interactive, NULL)) {
810 ERROR("python: Error creating thread for interactive interpreter.");
811 }
812 }
814 return 0;
815 }
817 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
818 int i;
819 PyObject *item, *values, *children, *tmp;
821 if (parent == NULL)
822 parent = Py_None;
824 values = PyTuple_New(ci->values_num); /* New reference. */
825 for (i = 0; i < ci->values_num; ++i) {
826 if (ci->values[i].type == OCONFIG_TYPE_STRING) {
827 PyTuple_SET_ITEM(values, i, PyString_FromString(ci->values[i].value.string));
828 } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
829 PyTuple_SET_ITEM(values, i, PyFloat_FromDouble(ci->values[i].value.number));
830 } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
831 PyTuple_SET_ITEM(values, i, PyBool_FromLong(ci->values[i].value.boolean));
832 }
833 }
835 item = PyObject_CallFunction((PyObject *) &ConfigType, "sONO", ci->key, parent, values, Py_None);
836 if (item == NULL)
837 return NULL;
838 children = PyTuple_New(ci->children_num); /* New reference. */
839 for (i = 0; i < ci->children_num; ++i) {
840 PyTuple_SET_ITEM(children, i, cpy_oconfig_to_pyconfig(ci->children + i, item));
841 }
842 tmp = ((Config *) item)->children;
843 ((Config *) item)->children = children;
844 Py_XDECREF(tmp);
845 return item;
846 }
848 static int cpy_config(oconfig_item_t *ci) {
849 int i;
850 PyObject *sys, *tb;
851 PyObject *sys_path;
852 PyObject *module;
854 /* Ok in theory we shouldn't do initialization at this point
855 * but we have to. In order to give python scripts a chance
856 * to register a config callback we need to be able to execute
857 * python code during the config callback so we have to start
858 * the interpreter here. */
859 /* Do *not* use the python "thread" module at this point! */
860 Py_Initialize();
862 PyType_Ready(&ConfigType);
863 PyType_Ready(&PluginDataType);
864 ValuesType.tp_base = &PluginDataType;
865 PyType_Ready(&ValuesType);
866 NotificationType.tp_base = &PluginDataType;
867 PyType_Ready(&NotificationType);
868 sys = PyImport_ImportModule("sys"); /* New reference. */
869 if (sys == NULL) {
870 cpy_log_exception("python initialization");
871 return 1;
872 }
873 sys_path = PyObject_GetAttrString(sys, "path"); /* New reference. */
874 Py_DECREF(sys);
875 if (sys_path == NULL) {
876 cpy_log_exception("python initialization");
877 return 1;
878 }
879 module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
880 PyModule_AddObject(module, "Config", (PyObject *) &ConfigType); /* Steals a reference. */
881 PyModule_AddObject(module, "Values", (PyObject *) &ValuesType); /* Steals a reference. */
882 PyModule_AddObject(module, "Notification", (PyObject *) &NotificationType); /* Steals a reference. */
883 PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG);
884 PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO);
885 PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE);
886 PyModule_AddIntConstant(module, "LOG_WARNING", LOG_WARNING);
887 PyModule_AddIntConstant(module, "LOG_ERROR", LOG_ERR);
888 PyModule_AddIntConstant(module, "NOTIF_FAILURE", NOTIF_FAILURE);
889 PyModule_AddIntConstant(module, "NOTIF_WARNING", NOTIF_WARNING);
890 PyModule_AddIntConstant(module, "NOTIF_OKAY", NOTIF_OKAY);
891 for (i = 0; i < ci->children_num; ++i) {
892 oconfig_item_t *item = ci->children + i;
894 if (strcasecmp(item->key, "Interactive") == 0) {
895 if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_BOOLEAN)
896 continue;
897 do_interactive = item->values[0].value.boolean;
898 } else if (strcasecmp(item->key, "LogTraces") == 0) {
899 if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_BOOLEAN)
900 continue;
901 if (!item->values[0].value.boolean) {
902 Py_XDECREF(cpy_format_exception);
903 cpy_format_exception = NULL;
904 continue;
905 }
906 if (cpy_format_exception)
907 continue;
908 tb = PyImport_ImportModule("traceback"); /* New reference. */
909 if (tb == NULL) {
910 cpy_log_exception("python initialization");
911 continue;
912 }
913 cpy_format_exception = PyObject_GetAttrString(tb, "format_exception"); /* New reference. */
914 Py_DECREF(tb);
915 if (cpy_format_exception == NULL)
916 cpy_log_exception("python initialization");
917 } else if (strcasecmp(item->key, "ModulePath") == 0) {
918 char *dir = NULL;
919 PyObject *dir_object;
921 if (cf_util_get_string(item, &dir) != 0)
922 continue;
923 dir_object = PyString_FromString(dir); /* New reference. */
924 if (dir_object == NULL) {
925 ERROR("python plugin: Unable to convert \"%s\" to "
926 "a python object.", dir);
927 free(dir);
928 cpy_log_exception("python initialization");
929 continue;
930 }
931 if (PyList_Append(sys_path, dir_object) != 0) {
932 ERROR("python plugin: Unable to append \"%s\" to "
933 "python module path.", dir);
934 cpy_log_exception("python initialization");
935 }
936 Py_DECREF(dir_object);
937 free(dir);
938 } else if (strcasecmp(item->key, "Import") == 0) {
939 char *module_name = NULL;
940 PyObject *module;
942 if (cf_util_get_string(item, &module_name) != 0)
943 continue;
944 module = PyImport_ImportModule(module_name); /* New reference. */
945 if (module == NULL) {
946 ERROR("python plugin: Error importing module \"%s\".", module_name);
947 cpy_log_exception("importing module");
948 PyErr_Print();
949 }
950 free(module_name);
951 Py_XDECREF(module);
952 } else if (strcasecmp(item->key, "Module") == 0) {
953 char *name = NULL;
954 cpy_callback_t *c;
955 PyObject *ret;
957 if (cf_util_get_string(item, &name) != 0)
958 continue;
959 for (c = cpy_config_callbacks; c; c = c->next) {
960 if (strcasecmp(c->name + 7, name) == 0)
961 break;
962 }
963 if (c == NULL) {
964 WARNING("python plugin: Found a configuration for the \"%s\" plugin, "
965 "but the plugin isn't loaded or didn't register "
966 "a configuration callback.", name);
967 free(name);
968 continue;
969 }
970 free(name);
971 if (c->data == NULL)
972 ret = PyObject_CallFunction(c->callback, "N",
973 cpy_oconfig_to_pyconfig(item, NULL)); /* New reference. */
974 else
975 ret = PyObject_CallFunction(c->callback, "NO",
976 cpy_oconfig_to_pyconfig(item, NULL), c->data); /* New reference. */
977 if (ret == NULL)
978 cpy_log_exception("loading module");
979 else
980 Py_DECREF(ret);
981 } else {
982 WARNING("python plugin: Ignoring unknown config key \"%s\".", item->key);
983 }
984 }
985 Py_DECREF(sys_path);
986 return 0;
987 }
989 void module_register(void) {
990 plugin_register_complex_config("python", cpy_config);
991 plugin_register_init("python", cpy_init);
992 plugin_register_shutdown("python", cpy_shutdown);
993 }