Proper exception logging.
[collectd.git] / src / python.c
1 #include <Python.h>
2 #include <structmember.h>
3
4 #if HAVE_PTHREAD_H
5 # include <pthread.h>
6 #endif
7
8 #include "collectd.h"
9 #include "common.h"
10
11 #include "cpython.h"
12
13 typedef struct cpy_callback_s {
14         char *name;
15         PyObject *callback;
16         PyObject *data;
17         struct cpy_callback_s *next;
18 } cpy_callback_t;
19
20 static int do_interactive = 0;
21
22 /* This is our global thread state. Python saves some stuff in thread-local
23  * storage. So if we allow the interpreter to run in the background
24  * (the scriptwriters might have created some threads from python), we have
25  * to save the state so we can resume it later after shutdown. */
26
27 static PyThreadState *state;
28
29 static PyObject *cpy_format_exception;
30
31 static cpy_callback_t *cpy_config_callbacks;
32 static cpy_callback_t *cpy_init_callbacks;
33 static cpy_callback_t *cpy_shutdown_callbacks;
34
35 static void cpy_destroy_user_data(void *data) {
36         cpy_callback_t *c = data;
37         free(c->name);
38         Py_DECREF(c->callback);
39         Py_XDECREF(c->data);
40         free(c);
41 }
42
43 /* You must hold the GIL to call this function!
44  * But if you managed to extract the callback parameter then you probably already do. */
45
46 static void cpy_build_name(char *buf, size_t size, PyObject *callback, const char *name) {
47         const char *module;
48         PyObject *mod = NULL, *n = NULL;
49         
50         if (name != NULL && strchr(name, '.') != NULL) {
51                 snprintf(buf, size, "python.%s", name);
52                 return;
53         }
54         
55         mod = PyObject_GetAttrString(callback, "__module__"); /* New reference. */
56         if (mod != NULL)
57                 module = PyString_AsString(mod);
58         else
59                 module = "collectd";
60         if (name != NULL) {
61                 snprintf(buf, size, "python.%s.%s", module, name);
62                 Py_XDECREF(mod);
63                 return;
64         }
65         
66         n = PyObject_GetAttrString(callback, "__name__"); /* New reference. */
67         if (n != NULL)
68                 name = PyString_AsString(n);
69         
70         if (name != NULL)
71                 snprintf(buf, size, "python.%s.%s", module, name);
72         else
73                 snprintf(buf, size, "python.%s.%p", module, callback);
74         Py_XDECREF(mod);
75         Py_XDECREF(n);
76 }
77
78 static void cpy_log_exception(const char *context) {
79         int l = 0, i;
80         const char *typename = NULL, *message = NULL;
81         PyObject *type, *value, *traceback, *tn, *m, *list;
82         
83         PyErr_Fetch(&type, &value, &traceback);
84         PyErr_NormalizeException(&type, &value, &traceback);
85         if (type == NULL) return;
86         tn = PyObject_GetAttrString(type, "__name__"); /* New reference. */
87         m = PyObject_GetAttrString(value, "message"); /* New reference. */
88         if (tn != NULL)
89                 typename = PyString_AsString(tn);
90         if (m != NULL)
91                 message = PyString_AsString(m);
92         if (typename == NULL)
93                 typename = "NamelessException";
94         if (message == NULL)
95                 message = "N/A";
96         ERROR("Unhandled python exception in %s: %s: %s", context, typename, message);
97         Py_XDECREF(tn);
98         Py_XDECREF(m);
99         if (!cpy_format_exception) {
100                 PyErr_Clear();
101                 Py_XDECREF(type);
102                 Py_XDECREF(value);
103                 Py_XDECREF(traceback);
104                 return;
105         }
106         if (!traceback) {
107                 PyErr_Clear();
108                 return;
109         }
110         list = PyObject_CallFunction(cpy_format_exception, "NNN", type, value, traceback);
111         if (list)
112                 l = PyObject_Length(list);
113         for (i = 0; i < l; ++i) {
114                 char *s;
115                 PyObject *line;
116                 
117                 line = PyList_GET_ITEM(list, i);
118                 s = strdup(PyString_AsString(line));
119                 Py_DECREF(line);
120                 if (s[strlen(s) - 1] == '\n')
121                         s[strlen(s) - 1] = 0;
122                 ERROR("%s", s);
123                 free(s);
124         }
125         PyErr_Clear();
126 }
127
128 static int cpy_read_callback(user_data_t *data) {
129         cpy_callback_t *c = data->data;
130         PyObject *ret;
131
132         CPY_LOCK_THREADS
133                 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
134                 if (ret == NULL) {
135                         cpy_log_exception("read callback");
136                 } else {
137                         Py_DECREF(ret);
138                 }
139         CPY_RELEASE_THREADS
140         return 0;
141 }
142
143 static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_list, user_data_t *data) {
144         int i;
145         cpy_callback_t *c = data->data;
146         PyObject *ret, *v, *list;
147
148         CPY_LOCK_THREADS
149                 list = PyList_New(value_list->values_len); /* New reference. */
150                 if (list == NULL) {
151                         cpy_log_exception("write callback");
152                         CPY_RETURN_FROM_THREADS 0;
153                 }
154                 for (i = 0; i < value_list->values_len; ++i) {
155                         if (ds->ds->type == DS_TYPE_COUNTER) {
156                                 if ((long) value_list->values[i].counter == value_list->values[i].counter)
157                                         PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].counter));
158                                 else
159                                         PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].counter));
160                         } else if (ds->ds->type == DS_TYPE_GAUGE) {
161                                 PyList_SetItem(list, i, PyFloat_FromDouble(value_list->values[i].gauge));
162                         } else if (ds->ds->type == DS_TYPE_DERIVE) {
163                                 if ((long) value_list->values[i].derive == value_list->values[i].derive)
164                                         PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].derive));
165                                 else
166                                         PyList_SetItem(list, i, PyLong_FromLongLong(value_list->values[i].derive));
167                         } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
168                                 if ((long) value_list->values[i].absolute == value_list->values[i].absolute)
169                                         PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].absolute));
170                                 else
171                                         PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].absolute));
172                         } else {
173                                 ERROR("cpy_write_callback: Unknown value type %d.", ds->ds->type);
174                                 Py_DECREF(list);
175                                 CPY_RETURN_FROM_THREADS 0;
176                         }
177                         if (PyErr_Occurred() != NULL) {
178                                 cpy_log_exception("value building for write callback");
179                                 CPY_RETURN_FROM_THREADS 0;
180                         }
181                 }
182                 v = PyObject_CallFunction((PyObject *) &ValuesType, "sOssssdi", value_list->type, list,
183                                 value_list->plugin_instance, value_list->type_instance, value_list->plugin,
184                                 value_list->host, (double) value_list->time, value_list->interval);
185                 Py_DECREF(list);
186                 ret = PyObject_CallFunctionObjArgs(c->callback, v, c->data, (void *) 0); /* New reference. */
187                 if (ret == NULL) {
188                         cpy_log_exception("write callback");
189                 } else {
190                         Py_DECREF(ret);
191                 }
192         CPY_RELEASE_THREADS
193         return 0;
194 }
195
196 static void cpy_log_callback(int severity, const char *message, user_data_t *data) {
197         cpy_callback_t * c = data->data;
198         PyObject *ret;
199
200         CPY_LOCK_THREADS
201         if (c->data == NULL)
202                 ret = PyObject_CallFunction(c->callback, "is", severity, message); /* New reference. */
203         else
204                 ret = PyObject_CallFunction(c->callback, "isO", severity, message, c->data); /* New reference. */
205
206         if (ret == NULL) {
207                 /* FIXME */
208                 /* Do we really want to trigger a log callback because a log callback failed?
209                  * Probably not. */
210                 PyErr_Print();
211         } else {
212                 Py_DECREF(ret);
213         }
214         CPY_RELEASE_THREADS
215 }
216
217 static PyObject *cpy_register_generic(cpy_callback_t **list_head, PyObject *args, PyObject *kwds) {
218         cpy_callback_t *c;
219         const char *name = NULL;
220         PyObject *callback = NULL, *data = NULL, *mod = NULL;
221         static char *kwlist[] = {"callback", "data", "name", NULL};
222         
223         if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
224         if (PyCallable_Check(callback) == 0) {
225                 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
226                 return NULL;
227         }
228         if (name == NULL) {
229                 mod = PyObject_GetAttrString(callback, "__module__"); /* New reference. */
230                 if (mod != NULL) name = PyString_AsString(mod);
231                 if (name == NULL) {
232                         Py_XDECREF(mod);
233                         PyErr_SetString(PyExc_ValueError, "No module name specified and "
234                                 "callback function does not have a \"__module__\" attribute.");
235                         return NULL;
236                 }
237         }
238         Py_INCREF(callback);
239         Py_XINCREF(data);
240         c = malloc(sizeof(*c));
241         c->name = strdup(name);
242         c->callback = callback;
243         c->data = data;
244         c->next = *list_head;
245         *list_head = c;
246         Py_XDECREF(mod);
247         Py_RETURN_NONE;
248 }
249
250 static PyObject *cpy_register_config(PyObject *self, PyObject *args, PyObject *kwds) {
251         return cpy_register_generic(&cpy_config_callbacks, args, kwds);
252 }
253
254 static PyObject *cpy_register_init(PyObject *self, PyObject *args, PyObject *kwds) {
255         return cpy_register_generic(&cpy_init_callbacks, args, kwds);
256 }
257
258 typedef int reg_function_t(const char *name, void *callback, void *data);
259
260 static PyObject *cpy_register_generic_userdata(void *reg, void *handler, PyObject *args, PyObject *kwds) {
261         char buf[512];
262         reg_function_t *register_function = (reg_function_t *) reg;
263         cpy_callback_t *c = NULL;
264         user_data_t *user_data = NULL;
265         const char *name = NULL;
266         PyObject *callback = NULL, *data = NULL;
267         static char *kwlist[] = {"callback", "data", "name", NULL};
268         
269         if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
270         if (PyCallable_Check(callback) == 0) {
271                 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
272                 return NULL;
273         }
274         cpy_build_name(buf, sizeof(buf), callback, name);
275         
276         Py_INCREF(callback);
277         Py_XINCREF(data);
278         c = malloc(sizeof(*c));
279         c->name = strdup(buf);
280         c->callback = callback;
281         c->data = data;
282         c->next = NULL;
283         user_data = malloc(sizeof(*user_data));
284         user_data->free_func = cpy_destroy_user_data;
285         user_data->data = c;
286         register_function(buf, handler, user_data);
287         return PyString_FromString(buf);
288 }
289
290 static PyObject *cpy_register_read(PyObject *self, PyObject *args, PyObject *kwds) {
291         char buf[512];
292         cpy_callback_t *c = NULL;
293         user_data_t *user_data = NULL;
294         double interval = 0;
295         const char *name = NULL;
296         PyObject *callback = NULL, *data = NULL;
297         struct timespec ts;
298         static char *kwlist[] = {"callback", "interval", "data", "name", NULL};
299         
300         if (PyArg_ParseTupleAndKeywords(args, kwds, "O|dOz", kwlist, &callback, &interval, &data, &name) == 0) return NULL;
301         if (PyCallable_Check(callback) == 0) {
302                 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
303                 return NULL;
304         }
305         cpy_build_name(buf, sizeof(buf), callback, name);
306         
307         Py_INCREF(callback);
308         Py_XINCREF(data);
309         c = malloc(sizeof(*c));
310         c->name = strdup(buf);
311         c->callback = callback;
312         c->data = data;
313         c->next = NULL;
314         user_data = malloc(sizeof(*user_data));
315         user_data->free_func = cpy_destroy_user_data;
316         user_data->data = c;
317         ts.tv_sec = interval;
318         ts.tv_nsec = (interval - ts.tv_sec) * 1000000000;
319         plugin_register_complex_read(buf, cpy_read_callback, &ts, user_data);
320         return PyString_FromString(buf);
321 }
322
323 static PyObject *cpy_register_log(PyObject *self, PyObject *args, PyObject *kwds) {
324         return cpy_register_generic_userdata(plugin_register_log, cpy_log_callback, args, kwds);
325 }
326
327 static PyObject *cpy_register_write(PyObject *self, PyObject *args, PyObject *kwds) {
328         return cpy_register_generic_userdata(plugin_register_write, cpy_write_callback, args, kwds);
329 }
330
331 static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args, PyObject *kwds) {
332         return cpy_register_generic(&cpy_shutdown_callbacks, args, kwds);
333 }
334
335 static PyObject *cpy_Error(PyObject *self, PyObject *args) {
336         const char *text;
337         if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
338         Py_BEGIN_ALLOW_THREADS
339         plugin_log(LOG_ERR, "%s", text);
340         Py_END_ALLOW_THREADS
341         Py_RETURN_NONE;
342 }
343
344 static PyObject *cpy_Warning(PyObject *self, PyObject *args) {
345         const char *text;
346         if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
347         Py_BEGIN_ALLOW_THREADS
348         plugin_log(LOG_WARNING, "%s", text);
349         Py_END_ALLOW_THREADS
350         Py_RETURN_NONE;
351 }
352
353 static PyObject *cpy_Notice(PyObject *self, PyObject *args) {
354         const char *text;
355         if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
356         Py_BEGIN_ALLOW_THREADS
357         plugin_log(LOG_NOTICE, "%s", text);
358         Py_END_ALLOW_THREADS
359         Py_RETURN_NONE;
360 }
361
362 static PyObject *cpy_Info(PyObject *self, PyObject *args) {
363         const char *text;
364         if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
365         Py_BEGIN_ALLOW_THREADS
366         plugin_log(LOG_INFO, "%s", text);
367         Py_END_ALLOW_THREADS
368         Py_RETURN_NONE;
369 }
370
371 static PyObject *cpy_Debug(PyObject *self, PyObject *args) {
372 #ifdef COLLECT_DEBUG
373         const char *text;
374         if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
375         plugin_log(LOG_DEBUG, "%s", text);
376 #endif
377         Py_RETURN_NONE;
378 }
379
380 static PyMethodDef cpy_methods[] = {
381         {"debug", cpy_Debug, METH_VARARGS, "This is an unhelpful text."},
382         {"info", cpy_Info, METH_VARARGS, "This is an unhelpful text."},
383         {"notice", cpy_Notice, METH_VARARGS, "This is an unhelpful text."},
384         {"warning", cpy_Warning, METH_VARARGS, "This is an unhelpful text."},
385         {"error", cpy_Error, METH_VARARGS, "This is an unhelpful text."},
386         {"register_log", (PyCFunction) cpy_register_log, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
387         {"register_init", (PyCFunction) cpy_register_init, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
388         {"register_config", (PyCFunction) cpy_register_config, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
389         {"register_read", (PyCFunction) cpy_register_read, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
390         {"register_write", (PyCFunction) cpy_register_write, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
391         {"register_shutdown", (PyCFunction) cpy_register_shutdown, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
392         {0, 0, 0, 0}
393 };
394
395 static int cpy_shutdown(void) {
396         cpy_callback_t *c;
397         PyObject *ret;
398         
399         /* This can happen if the module was loaded but not configured. */
400         if (state != NULL)
401                 PyEval_RestoreThread(state);
402
403         for (c = cpy_shutdown_callbacks; c; c = c->next) {
404                 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
405                 if (ret == NULL)
406                         cpy_log_exception("shutdown callback");
407                 else
408                         Py_DECREF(ret);
409         }
410         Py_Finalize();
411         return 0;
412 }
413
414 static void *cpy_interactive(void *data) {
415         CPY_LOCK_THREADS
416                 if (PyImport_ImportModule("readline") == NULL) {
417                         /* This interactive session will suck. */
418                         cpy_log_exception("interactive session init");
419                 }
420                 PyRun_InteractiveLoop(stdin, "<stdin>");
421         CPY_RELEASE_THREADS
422         NOTICE("python: Interactive interpreter exited, stopping collectd ...");
423         raise(SIGINT);
424         return NULL;
425 }
426
427 static int cpy_init(void) {
428         cpy_callback_t *c;
429         PyObject *ret;
430         static pthread_t thread;
431         
432         PyEval_InitThreads();
433         /* Now it's finally OK to use python threads. */
434         for (c = cpy_init_callbacks; c; c = c->next) {
435                 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
436                 if (ret == NULL)
437                         cpy_log_exception("init callback");
438                 else
439                         Py_DECREF(ret);
440         }
441         state = PyEval_SaveThread();
442         if (do_interactive) {
443                 if (pthread_create(&thread, NULL, cpy_interactive, NULL)) {
444                         ERROR("python: Error creating thread for interactive interpreter.");
445                 }
446         }
447
448         return 0;
449 }
450
451 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
452         int i;
453         PyObject *item, *values, *children, *tmp;
454         
455         if (parent == NULL)
456                 parent = Py_None;
457         
458         values = PyTuple_New(ci->values_num); /* New reference. */
459         for (i = 0; i < ci->values_num; ++i) {
460                 if (ci->values[i].type == OCONFIG_TYPE_STRING) {
461                         PyTuple_SET_ITEM(values, i, PyString_FromString(ci->values[i].value.string));
462                 } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
463                         PyTuple_SET_ITEM(values, i, PyFloat_FromDouble(ci->values[i].value.number));
464                 } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
465                         PyTuple_SET_ITEM(values, i, PyBool_FromLong(ci->values[i].value.boolean));
466                 }
467         }
468         
469         item = PyObject_CallFunction((PyObject *) &ConfigType, "sONO", ci->key, parent, values, Py_None);
470         if (item == NULL)
471                 return NULL;
472         children = PyTuple_New(ci->children_num); /* New reference. */
473         for (i = 0; i < ci->children_num; ++i) {
474                         PyTuple_SET_ITEM(children, i, cpy_oconfig_to_pyconfig(ci->children + i, item));
475         }
476         tmp = ((Config *) item)->children;
477         ((Config *) item)->children = children;
478         Py_XDECREF(tmp);
479         return item;
480 }
481
482 static int cpy_config(oconfig_item_t *ci) {
483         int i;
484         PyObject *sys, *tb;
485         PyObject *sys_path;
486         PyObject *module;
487         
488         /* Ok in theory we shouldn't do initialization at this point
489          * but we have to. In order to give python scripts a chance
490          * to register a config callback we need to be able to execute
491          * python code during the config callback so we have to start
492          * the interpreter here. */
493         /* Do *not* use the python "thread" module at this point! */
494         Py_Initialize();
495         
496         PyType_Ready(&ConfigType);
497         PyType_Ready(&ValuesType);
498         sys = PyImport_ImportModule("sys"); /* New reference. */
499         if (sys == NULL) {
500                 cpy_log_exception("python initialization");
501                 return 1;
502         }
503         sys_path = PyObject_GetAttrString(sys, "path"); /* New reference. */
504         Py_DECREF(sys);
505         if (sys_path == NULL) {
506                 cpy_log_exception("python initialization");
507                 return 1;
508         }
509         module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
510         PyModule_AddObject(module, "Config", (PyObject *) &ConfigType); /* Steals a reference. */
511         PyModule_AddObject(module, "Values", (PyObject *) &ValuesType); /* Steals a reference. */
512         PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG);
513         PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO);
514         PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE);
515         PyModule_AddIntConstant(module, "LOG_WARNING", LOG_WARNING);
516         PyModule_AddIntConstant(module, "LOG_ERROR", LOG_ERR);
517         for (i = 0; i < ci->children_num; ++i) {
518                 oconfig_item_t *item = ci->children + i;
519                 
520                 if (strcasecmp(item->key, "Interactive") == 0) {
521                         if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_BOOLEAN)
522                                 continue;
523                         do_interactive = item->values[0].value.boolean;
524                 } else if (strcasecmp(item->key, "LogTraces") == 0) {
525                         if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_BOOLEAN)
526                                 continue;
527                         if (!item->values[0].value.boolean) {
528                                 Py_XDECREF(cpy_format_exception);
529                                 cpy_format_exception = NULL;
530                                 continue;
531                         }
532                         if (cpy_format_exception)
533                                 continue;
534                         tb = PyImport_ImportModule("traceback"); /* New reference. */
535                         if (tb == NULL) {
536                                 cpy_log_exception("python initialization");
537                                 continue;
538                         }
539                         cpy_format_exception = PyObject_GetAttrString(tb, "format_exception"); /* New reference. */
540                         Py_DECREF(tb);
541                         if (cpy_format_exception == NULL)
542                                 cpy_log_exception("python initialization");
543                 } else if (strcasecmp(item->key, "ModulePath") == 0) {
544                         char *dir = NULL;
545                         PyObject *dir_object;
546                         
547                         if (cf_util_get_string(item, &dir) != 0) 
548                                 continue;
549                         dir_object = PyString_FromString(dir); /* New reference. */
550                         if (dir_object == NULL) {
551                                 ERROR("python plugin: Unable to convert \"%s\" to "
552                                       "a python object.", dir);
553                                 free(dir);
554                                 cpy_log_exception("python initialization");
555                                 continue;
556                         }
557                         if (PyList_Append(sys_path, dir_object) != 0) {
558                                 ERROR("python plugin: Unable to append \"%s\" to "
559                                       "python module path.", dir);
560                                 cpy_log_exception("python initialization");
561                         }
562                         Py_DECREF(dir_object);
563                         free(dir);
564                 } else if (strcasecmp(item->key, "Import") == 0) {
565                         char *module_name = NULL;
566                         PyObject *module;
567                         
568                         if (cf_util_get_string(item, &module_name) != 0) 
569                                 continue;
570                         module = PyImport_ImportModule(module_name); /* New reference. */
571                         if (module == NULL) {
572                                 ERROR("python plugin: Error importing module \"%s\".", module_name);
573                                 cpy_log_exception("python initialization");
574                                 PyErr_Print();
575                         }
576                         free(module_name);
577                         Py_XDECREF(module);
578                 } else if (strcasecmp(item->key, "Module") == 0) {
579                         char *name = NULL;
580                         cpy_callback_t *c;
581                         PyObject *ret;
582                         
583                         if (cf_util_get_string(item, &name) != 0)
584                                 continue;
585                         for (c = cpy_config_callbacks; c; c = c->next) {
586                                 if (strcasecmp(c->name, name) == 0)
587                                         break;
588                         }
589                         if (c == NULL) {
590                                 WARNING("python plugin: Found a configuration for the \"%s\" plugin, "
591                                         "but the plugin isn't loaded or didn't register "
592                                         "a configuration callback.", name);
593                                 free(name);
594                                 continue;
595                         }
596                         free(name);
597                         if (c->data == NULL)
598                                 ret = PyObject_CallFunction(c->callback, "N",
599                                         cpy_oconfig_to_pyconfig(item, NULL)); /* New reference. */
600                         else
601                                 ret = PyObject_CallFunction(c->callback, "NO",
602                                         cpy_oconfig_to_pyconfig(item, NULL), c->data); /* New reference. */
603                         if (ret == NULL)
604                                 cpy_log_exception("loading module");
605                         else
606                                 Py_DECREF(ret);
607                 } else {
608                         WARNING("python plugin: Ignoring unknown config key \"%s\".", item->key);
609                 }
610         }
611         Py_DECREF(sys_path);
612         return 0;
613 }
614
615 void module_register(void) {
616         plugin_register_complex_config("python", cpy_config);
617         plugin_register_init("python", cpy_init);
618 //      plugin_register_read("python", cna_read);
619         plugin_register_shutdown("python", cpy_shutdown);
620 }