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