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