Code

No need to check for NULL if you're gonna pass NULL anyway.
[collectd.git] / src / python.c
1 #include <Python.h>
2 #include <structmember.h>
4 #include "collectd.h"
5 #include "common.h"
7 #include "cpython.h"
9 typedef struct cpy_callback_s {
10         char *name;
11         PyObject *callback;
12         PyObject *data;
13         struct cpy_callback_s *next;
14 } cpy_callback_t;
16 /* This is our global thread state. Python saves some stuff in thread-local
17  * storage. So if we allow the interpreter to run in the background
18  * (the scriptwriters might have created some threads from python), we have
19  * to save the state so we can resume it later after shutdown. */
21 static PyThreadState *state;
23 static cpy_callback_t *cpy_config_callbacks;
24 static cpy_callback_t *cpy_init_callbacks;
25 static cpy_callback_t *cpy_shutdown_callbacks;
27 static void cpy_destroy_user_data(void *data) {
28         cpy_callback_t *c = data;
29         free(c->name);
30         Py_DECREF(c->callback);
31         Py_XDECREF(c->data);
32         free(c);
33 }
35 /* You must hold the GIL to call this function!
36  * But if you managed to extract the callback parameter then you probably already do. */
38 static void cpy_build_name(char *buf, size_t size, PyObject *callback, const char *name) {
39         const char *module;
40         PyObject *mod = NULL, *n = NULL;
41         
42         if (name != NULL && strchr(name, '.') != NULL) {
43                 snprintf(buf, size, "python.%s", name);
44                 return;
45         }
46         
47         mod = PyObject_GetAttrString(callback, "__module__"); /* New reference. */
48         if (mod != NULL)
49                 module = PyString_AsString(mod);
50         else
51                 module = "collectd";
52         if (name != NULL) {
53                 snprintf(buf, size, "python.%s.%s", module, name);
54                 Py_XDECREF(mod);
55                 return;
56         }
57         
58         n = PyObject_GetAttrString(callback, "__name__"); /* New reference. */
59         if (n != NULL)
60                 name = PyString_AsString(n);
61         
62         if (name != NULL)
63                 snprintf(buf, size, "python.%s.%s", module, name);
64         else
65                 snprintf(buf, size, "python.%s.%p", module, callback);
66         Py_XDECREF(mod);
67         Py_XDECREF(n);
68 }
70 static int cpy_read_callback(user_data_t *data) {
71         cpy_callback_t *c = data->data;
72         PyObject *ret;
74         CPY_LOCK_THREADS
75                 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
76                 if (ret == NULL) {
77                         /* FIXME */
78                         PyErr_Print();
79                 } else {
80                         Py_DECREF(ret);
81                 }
82         CPY_RELEASE_THREADS
83         return 0;
84 }
86 static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_list, user_data_t *data) {
87         int i;
88         cpy_callback_t *c = data->data;
89         PyObject *ret, *v, *list;
91         CPY_LOCK_THREADS
92                 list = PyList_New(value_list->values_len); /* New reference. */
93                 if (list == NULL) {
94                         PyErr_Print();
95                         CPY_RETURN_FROM_THREADS 0;
96                 }
97                 for (i = 0; i < value_list->values_len; ++i) {
98                         if (ds->ds->type == DS_TYPE_COUNTER) {
99                                 if ((long) value_list->values[i].counter == value_list->values[i].counter)
100                                         PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].counter));
101                                 else
102                                         PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].counter));
103                         } else if (ds->ds->type == DS_TYPE_GAUGE) {
104                                 PyList_SetItem(list, i, PyFloat_FromDouble(value_list->values[i].gauge));
105                         } else if (ds->ds->type == DS_TYPE_DERIVE) {
106                                 if ((long) value_list->values[i].derive == value_list->values[i].derive)
107                                         PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].derive));
108                                 else
109                                         PyList_SetItem(list, i, PyLong_FromLongLong(value_list->values[i].derive));
110                         } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
111                                 if ((long) value_list->values[i].absolute == value_list->values[i].absolute)
112                                         PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].absolute));
113                                 else
114                                         PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].absolute));
115                         } else {
116                                 ERROR("cpy_write_callback: Unknown value type %d.", ds->ds->type);
117                                 Py_DECREF(list);
118                                 CPY_RETURN_FROM_THREADS 0;
119                         }
120                         if (PyErr_Occurred() != NULL) {
121                                 PyErr_Print();
122                                 CPY_RETURN_FROM_THREADS 0;
123                         }
124                 }
125                 v = PyObject_CallFunction((PyObject *) &ValuesType, "sOssssdi", value_list->type, list,
126                                 value_list->plugin_instance, value_list->type_instance, value_list->plugin,
127                                 value_list->host, (double) value_list->time, value_list->interval);
128                 Py_DECREF(list);
129                 ret = PyObject_CallFunctionObjArgs(c->callback, v, c->data, (void *) 0); /* New reference. */
130                 if (ret == NULL) {
131                         /* FIXME */
132                         PyErr_Print();
133                 } else {
134                         Py_DECREF(ret);
135                 }
136         CPY_RELEASE_THREADS
137         return 0;
140 static void cpy_log_callback(int severity, const char *message, user_data_t *data) {
141         cpy_callback_t * c = data->data;
142         PyObject *ret;
144         CPY_LOCK_THREADS
145         if (c->data == NULL)
146                 ret = PyObject_CallFunction(c->callback, "is", severity, message); /* New reference. */
147         else
148                 ret = PyObject_CallFunction(c->callback, "isO", severity, message, c->data); /* New reference. */
150         if (ret == NULL) {
151                 /* FIXME */
152                 PyErr_Print();
153         } else {
154                 Py_DECREF(ret);
155         }
156         CPY_RELEASE_THREADS
159 static PyObject *cpy_register_generic(cpy_callback_t **list_head, PyObject *args, PyObject *kwds) {
160         cpy_callback_t *c;
161         const char *name = NULL;
162         PyObject *callback = NULL, *data = NULL, *mod = NULL;
163         static char *kwlist[] = {"callback", "data", "name", NULL};
164         
165         if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
166         if (PyCallable_Check(callback) == 0) {
167                 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
168                 return NULL;
169         }
170         if (name == NULL) {
171                 mod = PyObject_GetAttrString(callback, "__module__"); /* New reference. */
172                 if (mod != NULL) name = PyString_AsString(mod);
173                 if (name == NULL) {
174                         Py_XDECREF(mod);
175                         PyErr_SetString(PyExc_ValueError, "No module name specified and "
176                                 "callback function does not have a \"__module__\" attribute.");
177                         return NULL;
178                 }
179         }
180         Py_INCREF(callback);
181         Py_XINCREF(data);
182         c = malloc(sizeof(*c));
183         c->name = strdup(name);
184         c->callback = callback;
185         c->data = data;
186         c->next = *list_head;
187         *list_head = c;
188         Py_XDECREF(mod);
189         Py_RETURN_NONE;
192 static PyObject *cpy_register_config(PyObject *self, PyObject *args, PyObject *kwds) {
193         return cpy_register_generic(&cpy_config_callbacks, args, kwds);
196 static PyObject *cpy_register_init(PyObject *self, PyObject *args, PyObject *kwds) {
197         return cpy_register_generic(&cpy_init_callbacks, args, kwds);
200 typedef int reg_function_t(const char *name, void *callback, void *data);
202 static PyObject *cpy_register_generic_userdata(void *reg, void *handler, PyObject *args, PyObject *kwds) {
203         char buf[512];
204         reg_function_t *register_function = (reg_function_t *) reg;
205         cpy_callback_t *c = NULL;
206         user_data_t *user_data = NULL;
207         const char *name = NULL;
208         PyObject *callback = NULL, *data = NULL;
209         static char *kwlist[] = {"callback", "data", "name", NULL};
210         
211         if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
212         if (PyCallable_Check(callback) == 0) {
213                 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
214                 return NULL;
215         }
216         cpy_build_name(buf, sizeof(buf), callback, name);
217         
218         Py_INCREF(callback);
219         Py_XINCREF(data);
220         c = malloc(sizeof(*c));
221         c->name = strdup(buf);
222         c->callback = callback;
223         c->data = data;
224         c->next = NULL;
225         user_data = malloc(sizeof(*user_data));
226         user_data->free_func = cpy_destroy_user_data;
227         user_data->data = c;
228         register_function(buf, handler, user_data);
229         return PyString_FromString(buf);
232 static PyObject *cpy_register_read(PyObject *self, PyObject *args, PyObject *kwds) {
233         char buf[512];
234         cpy_callback_t *c = NULL;
235         user_data_t *user_data = NULL;
236         double interval = 0;
237         const char *name = NULL;
238         PyObject *callback = NULL, *data = NULL;
239         struct timespec ts;
240         static char *kwlist[] = {"callback", "interval", "data", "name", NULL};
241         
242         if (PyArg_ParseTupleAndKeywords(args, kwds, "O|dOz", kwlist, &callback, &interval, &data, &name) == 0) return NULL;
243         if (PyCallable_Check(callback) == 0) {
244                 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
245                 return NULL;
246         }
247         cpy_build_name(buf, sizeof(buf), callback, name);
248         
249         Py_INCREF(callback);
250         Py_XINCREF(data);
251         c = malloc(sizeof(*c));
252         c->name = strdup(buf);
253         c->callback = callback;
254         c->data = data;
255         c->next = NULL;
256         user_data = malloc(sizeof(*user_data));
257         user_data->free_func = cpy_destroy_user_data;
258         user_data->data = c;
259         ts.tv_sec = interval;
260         ts.tv_nsec = (interval - ts.tv_sec) * 1000000000;
261         plugin_register_complex_read(buf, cpy_read_callback, &ts, user_data);
262         return PyString_FromString(buf);
265 static PyObject *cpy_register_log(PyObject *self, PyObject *args, PyObject *kwds) {
266         return cpy_register_generic_userdata(plugin_register_log, cpy_log_callback, args, kwds);
269 static PyObject *cpy_register_write(PyObject *self, PyObject *args, PyObject *kwds) {
270         return cpy_register_generic_userdata(plugin_register_write, cpy_write_callback, args, kwds);
273 static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args, PyObject *kwds) {
274         return cpy_register_generic(&cpy_shutdown_callbacks, args, kwds);
277 static PyObject *cpy_Error(PyObject *self, PyObject *args) {
278         const char *text;
279         if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
280         Py_BEGIN_ALLOW_THREADS
281         plugin_log(LOG_ERR, "%s", text);
282         Py_END_ALLOW_THREADS
283         Py_RETURN_NONE;
286 static PyObject *cpy_Warning(PyObject *self, PyObject *args) {
287         const char *text;
288         if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
289         Py_BEGIN_ALLOW_THREADS
290         plugin_log(LOG_WARNING, "%s", text);
291         Py_END_ALLOW_THREADS
292         Py_RETURN_NONE;
295 static PyObject *cpy_Notice(PyObject *self, PyObject *args) {
296         const char *text;
297         if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
298         Py_BEGIN_ALLOW_THREADS
299         plugin_log(LOG_NOTICE, "%s", text);
300         Py_END_ALLOW_THREADS
301         Py_RETURN_NONE;
304 static PyObject *cpy_Info(PyObject *self, PyObject *args) {
305         const char *text;
306         if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
307         Py_BEGIN_ALLOW_THREADS
308         plugin_log(LOG_INFO, "%s", text);
309         Py_END_ALLOW_THREADS
310         Py_RETURN_NONE;
313 static PyObject *cpy_Debug(PyObject *self, PyObject *args) {
314 #ifdef COLLECT_DEBUG
315         const char *text;
316         if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
317         plugin_log(LOG_DEBUG, "%s", text);
318 #endif
319         Py_RETURN_NONE;
322 static PyMethodDef cpy_methods[] = {
323         {"Debug", cpy_Debug, METH_VARARGS, "This is an unhelpful text."},
324         {"Info", cpy_Info, METH_VARARGS, "This is an unhelpful text."},
325         {"Notice", cpy_Notice, METH_VARARGS, "This is an unhelpful text."},
326         {"Warning", cpy_Warning, METH_VARARGS, "This is an unhelpful text."},
327         {"Error", cpy_Error, METH_VARARGS, "This is an unhelpful text."},
328         {"register_log", (PyCFunction) cpy_register_log, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
329         {"register_init", (PyCFunction) cpy_register_init, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
330         {"register_config", (PyCFunction) cpy_register_config, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
331         {"register_read", (PyCFunction) cpy_register_read, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
332         {"register_write", (PyCFunction) cpy_register_write, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
333         {"register_shutdown", (PyCFunction) cpy_register_shutdown, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
334         {0, 0, 0, 0}
335 };
337 static int cpy_shutdown(void) {
338         cpy_callback_t *c;
339         PyObject *ret;
340         
341         /* This can happen if the module was loaded but not configured. */
342         if (state != NULL)
343                 PyEval_RestoreThread(state);
345         for (c = cpy_shutdown_callbacks; c; c = c->next) {
346                 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
347                 if (ret == NULL)
348                         PyErr_Print(); /* FIXME */
349                 else
350                         Py_DECREF(ret);
351         }
352         Py_Finalize();
353         return 0;
356 static int cpy_init(void) {
357         cpy_callback_t *c;
358         PyObject *ret;
359         
360         PyEval_InitThreads();
361         /* Now it's finally OK to use python threads. */
362         for (c = cpy_init_callbacks; c; c = c->next) {
363                 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
364                 if (ret == NULL)
365                         PyErr_Print(); /* FIXME */
366                 else
367                         Py_DECREF(ret);
368         }
369         state = PyEval_SaveThread();
370         return 0;
373 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
374         int i;
375         PyObject *item, *values, *children, *tmp;
376         
377         if (parent == NULL)
378                 parent = Py_None;
379         
380         values = PyTuple_New(ci->values_num); /* New reference. */
381         for (i = 0; i < ci->values_num; ++i) {
382                 if (ci->values[i].type == OCONFIG_TYPE_STRING) {
383                         PyTuple_SET_ITEM(values, i, PyString_FromString(ci->values[i].value.string));
384                 } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
385                         PyTuple_SET_ITEM(values, i, PyFloat_FromDouble(ci->values[i].value.number));
386                 } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
387                         PyTuple_SET_ITEM(values, i, PyBool_FromLong(ci->values[i].value.boolean));
388                 }
389         }
390         
391         item = PyObject_CallFunction((PyObject *) &ConfigType, "sONO", ci->key, parent, values, Py_None);
392         if (item == NULL)
393                 return NULL;
394         children = PyTuple_New(ci->children_num); /* New reference. */
395         for (i = 0; i < ci->children_num; ++i) {
396                         PyTuple_SET_ITEM(children, i, cpy_oconfig_to_pyconfig(ci->children + i, item));
397         }
398         tmp = ((Config *) item)->children;
399         ((Config *) item)->children = children;
400         Py_XDECREF(tmp);
401         return item;
404 static int cpy_config(oconfig_item_t *ci) {
405         int i;
406         PyObject *sys;
407         PyObject *sys_path;
408         PyObject *module;
409         
410         /* Ok in theory we shouldn't do initialization at this point
411          * but we have to. In order to give python scripts a chance
412          * to register a config callback we need to be able to execute
413          * python code during the config callback so we have to start
414          * the interpreter here. */
415         /* Do *not* use the python "thread" module at this point! */
416         Py_Initialize();
417         
418         PyType_Ready(&ConfigType);
419         PyType_Ready(&ValuesType);
420         sys = PyImport_ImportModule("sys"); /* New reference. */
421         if (sys == NULL) {
422                 ERROR("python module: Unable to import \"sys\" module.");
423                 /* Just print the default python exception text to stderr. */
424                 PyErr_Print();
425                 return 1;
426         }
427         sys_path = PyObject_GetAttrString(sys, "path"); /* New reference. */
428         Py_DECREF(sys);
429         if (sys_path == NULL) {
430                 ERROR("python module: Unable to read \"sys.path\".");
431                 PyErr_Print();
432                 return 1;
433         }
434         module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
435         PyModule_AddObject(module, "Config", (PyObject *) &ConfigType); /* Steals a reference. */
436         PyModule_AddObject(module, "Values", (PyObject *) &ValuesType); /* Steals a reference. */
437         PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG);
438         PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO);
439         PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE);
440         PyModule_AddIntConstant(module, "LOG_WARNING", LOG_WARNING);
441         PyModule_AddIntConstant(module, "LOG_ERROR", LOG_ERR);
442         for (i = 0; i < ci->children_num; ++i) {
443                 oconfig_item_t *item = ci->children + i;
444                 
445                 if (strcasecmp(item->key, "ModulePath") == 0) {
446                         char *dir = NULL;
447                         PyObject *dir_object;
448                         
449                         if (cf_util_get_string(item, &dir) != 0) 
450                                 continue;
451                         dir_object = PyString_FromString(dir); /* New reference. */
452                         if (dir_object == NULL) {
453                                 ERROR("python plugin: Unable to convert \"%s\" to "
454                                       "a python object.", dir);
455                                 free(dir);
456                                 PyErr_Print();
457                                 continue;
458                         }
459                         if (PyList_Append(sys_path, dir_object) != 0) {
460                                 ERROR("python plugin: Unable to append \"%s\" to "
461                                       "python module path.", dir);
462                                 PyErr_Print();
463                         }
464                         Py_DECREF(dir_object);
465                         free(dir);
466                 } else if (strcasecmp(item->key, "Import") == 0) {
467                         char *module_name = NULL;
468                         PyObject *module;
469                         
470                         if (cf_util_get_string(item, &module_name) != 0) 
471                                 continue;
472                         module = PyImport_ImportModule(module_name); /* New reference. */
473                         if (module == NULL) {
474                                 ERROR("python plugin: Error importing module \"%s\".", module_name);
475                                 PyErr_Print();
476                         }
477                         free(module_name);
478                         Py_XDECREF(module);
479                 } else if (strcasecmp(item->key, "Module") == 0) {
480                         char *name = NULL;
481                         cpy_callback_t *c;
482                         PyObject *ret;
483                         
484                         if (cf_util_get_string(item, &name) != 0)
485                                 continue;
486                         for (c = cpy_config_callbacks; c; c = c->next) {
487                                 if (strcasecmp(c->name, name) == 0)
488                                         break;
489                         }
490                         if (c == NULL) {
491                                 WARNING("python plugin: Found a configuration for the \"%s\" plugin, "
492                                         "but the plugin isn't loaded or didn't register "
493                                         "a configuration callback.", name);
494                                 free(name);
495                                 continue;
496                         }
497                         free(name);
498                         if (c->data == NULL)
499                                 ret = PyObject_CallFunction(c->callback, "N",
500                                         cpy_oconfig_to_pyconfig(item, NULL)); /* New reference. */
501                         else
502                                 ret = PyObject_CallFunction(c->callback, "NO",
503                                         cpy_oconfig_to_pyconfig(item, NULL), c->data); /* New reference. */
504                         if (ret == NULL)
505                                 PyErr_Print();
506                         else
507                                 Py_DECREF(ret);
508                 } else {
509                         WARNING("python plugin: Ignoring unknown config key \"%s\".", item->key);
510                 }
511         }
512         Py_DECREF(sys_path);
513         return 0;
516 void module_register(void) {
517         plugin_register_complex_config("python", cpy_config);
518         plugin_register_init("python", cpy_init);
519 //      plugin_register_read("python", cna_read);
520         plugin_register_shutdown("python", cpy_shutdown);