Some signal dodging stuff.
[collectd.git] / src / python.c
1 #include <Python.h>
2 #include <structmember.h>
3
4 #include <signal.h>
5 #if HAVE_PTHREAD_H
6 # include <pthread.h>
7 #endif
8
9 #include "collectd.h"
10 #include "common.h"
11
12 #include "cpython.h"
13
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;
20
21 static char log_doc[] = "This function sends a string to all logging plugins.";
22
23 static char flush_doc[] = "flush([plugin][, timeout][, identifier]) -> None\n"
24                 "\n"
25                 "Flushes the cache of another plugin.";
26
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.";
29
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.";
51
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.";
71
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.";
88
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.";
109
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.";
129
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.";
149
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.";
168
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.";
186
187
188 static int do_interactive = 0;
189
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. */
194
195 static PyThreadState *state;
196
197 static PyObject *cpy_format_exception;
198
199 static cpy_callback_t *cpy_config_callbacks;
200 static cpy_callback_t *cpy_init_callbacks;
201 static cpy_callback_t *cpy_shutdown_callbacks;
202
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 }
210
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. */
213
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;
217         
218         if (name != NULL && (strchr(name, '.') != NULL || short_name)) {
219                 snprintf(buf, size, "python.%s", name);
220                 return;
221         }
222         
223         mod = PyObject_GetAttrString(callback, "__module__"); /* New reference. */
224         if (mod != NULL)
225                 module = PyString_AsString(mod);
226         else
227                 module = "collectd";
228         
229         if (short_name) {
230                 snprintf(buf, size, "python.%s", module);
231                 Py_XDECREF(mod);
232                 return;
233         }
234         
235         if (name != NULL) {
236                 snprintf(buf, size, "python.%s.%s", module, name);
237                 Py_XDECREF(mod);
238                 return;
239         }
240         
241         n = PyObject_GetAttrString(callback, "__name__"); /* New reference. */
242         if (n != NULL)
243                 name = PyString_AsString(n);
244         
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 }
252
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;
257         
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;
291                 
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 }
303
304 static int cpy_read_callback(user_data_t *data) {
305         cpy_callback_t *c = data->data;
306         PyObject *ret;
307
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 }
318
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;
323
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 }
371
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;
375
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 }
389
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;
393
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. */
399
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 }
410
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;
414
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. */
420
421         if (ret == NULL) {
422                 cpy_log_exception("flush callback");
423         } else {
424                 Py_DECREF(ret);
425         }
426         CPY_RELEASE_THREADS
427 }
428
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};
435         
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);
442
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 }
454
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};
459         
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 }
466
467 static PyObject *cpy_register_config(PyObject *self, PyObject *args, PyObject *kwds) {
468         return cpy_register_generic(&cpy_config_callbacks, args, kwds, 1);
469 }
470
471 static PyObject *cpy_register_init(PyObject *self, PyObject *args, PyObject *kwds) {
472         return cpy_register_generic(&cpy_init_callbacks, args, kwds, 0);
473 }
474
475 typedef int reg_function_t(const char *name, void *callback, void *data);
476
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};
485         
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);
492         
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 }
506
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};
516         
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);
523         
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 }
539
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 }
543
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 }
547
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 }
551
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 }
555
556 static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args, PyObject *kwds) {
557         return cpy_register_generic(&cpy_shutdown_callbacks, args, kwds, 0);
558 }
559
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 }
568
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 }
577
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 }
586
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 }
595
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 }
604
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;
609
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;
623         
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 }
637
638 typedef int cpy_unregister_function_t(const char *name);
639
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;
643
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 }
659
660 static PyObject *cpy_unregister_log(PyObject *self, PyObject *arg) {
661         return cpy_unregister_generic_userdata(plugin_unregister_log, arg, "log", 0);
662 }
663
664 static PyObject *cpy_unregister_init(PyObject *self, PyObject *arg) {
665         return cpy_unregister_generic(&cpy_init_callbacks, arg, "init", 0);
666 }
667
668 static PyObject *cpy_unregister_config(PyObject *self, PyObject *arg) {
669         return cpy_unregister_generic(&cpy_config_callbacks, arg, "config", 1);
670 }
671
672 static PyObject *cpy_unregister_read(PyObject *self, PyObject *arg) {
673         return cpy_unregister_generic_userdata(plugin_unregister_read, arg, "read", 0);
674 }
675
676 static PyObject *cpy_unregister_write(PyObject *self, PyObject *arg) {
677         return cpy_unregister_generic_userdata(plugin_unregister_write, arg, "write", 0);
678 }
679
680 static PyObject *cpy_unregister_notification(PyObject *self, PyObject *arg) {
681         return cpy_unregister_generic_userdata(plugin_unregister_notification, arg, "notification", 0);
682 }
683
684 static PyObject *cpy_unregister_flush(PyObject *self, PyObject *arg) {
685         return cpy_unregister_generic_userdata(plugin_unregister_flush, arg, "flush", 1);
686 }
687
688 static PyObject *cpy_unregister_shutdown(PyObject *self, PyObject *arg) {
689         return cpy_unregister_generic(&cpy_shutdown_callbacks, arg, "shutdown", 0);
690 }
691
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 };
717
718 static int cpy_shutdown(void) {
719         cpy_callback_t *c;
720         PyObject *ret;
721         
722         /* This can happen if the module was loaded but not configured. */
723         if (state != NULL)
724                 PyEval_RestoreThread(state);
725
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 }
737
738 static void cpy_int_handler(int sig) {
739         return;
740 }
741
742 static void *cpy_interactive(void *data) {
743         sigset_t sigset;
744         struct sigaction sig_int_action, old;
745         
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);
765         
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 }
788
789 static int cpy_init(void) {
790         cpy_callback_t *c;
791         PyObject *ret;
792         static pthread_t thread;
793         sigset_t sigset;
794         
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         }
813
814         return 0;
815 }
816
817 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
818         int i;
819         PyObject *item, *values, *children, *tmp;
820         
821         if (parent == NULL)
822                 parent = Py_None;
823         
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         }
834         
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 }
847
848 static int cpy_config(oconfig_item_t *ci) {
849         int i;
850         PyObject *sys, *tb;
851         PyObject *sys_path;
852         PyObject *module;
853         
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();
861         
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;
893                 
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;
920                         
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;
941                         
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;
956                         
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 }
988
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 }