Added init callbacks with full threading support.
[collectd.git] / src / python.c
1 #include <Python.h>
2 #include <structmember.h>
3
4 #include "collectd.h"
5 #include "common.h"
6
7 typedef struct cpy_callback_s {
8         char *name;
9         PyObject *callback;
10         PyObject *data;
11         struct cpy_callback_s *next;
12 } cpy_callback_t;
13
14 /* This is our global thread state. Python saves some stuff in thread-local
15  * storage. So if we allow the interpreter to run in the background
16  * (the scriptwriters might have created some threads from python), we have
17  * to save the state so we can resume it later from a different thread.
18
19  * Technically the Global Interpreter Lock (GIL) and thread states can be
20  * manipulated independently. But to keep stuff from getting too complex
21  * we'll just use PyEval_SaveTread and PyEval_RestoreThreas which takes
22  * care of the thread states as well as the GIL. */
23
24 static PyThreadState *state;
25
26 static cpy_callback_t *cpy_config_callbacks;
27 static cpy_callback_t *cpy_init_callbacks;
28
29 typedef struct {
30         PyObject_HEAD      /* No semicolon! */
31         PyObject *parent;
32         PyObject *key;
33         PyObject *values;
34         PyObject *children;
35 } Config;
36
37 static void Config_dealloc(PyObject *s) {
38         Config *self = (Config *) s;
39         
40         Py_XDECREF(self->parent);
41         Py_XDECREF(self->key);
42         Py_XDECREF(self->values);
43         Py_XDECREF(self->children);
44         self->ob_type->tp_free(s);
45 }
46
47 static PyObject *Config_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
48         Config *self;
49         
50         self = (Config *) type->tp_alloc(type, 0);
51         if (self == NULL)
52                 return NULL;
53         
54         self->parent = NULL;
55         self->key = NULL;
56         self->values = NULL;
57         self->children = NULL;
58         return (PyObject *) self;
59 }
60
61 static int Config_init(PyObject *s, PyObject *args, PyObject *kwds) {
62         PyObject *key = NULL, *parent = NULL, *values = NULL, *children = NULL, *tmp;
63         Config *self = (Config *) s;
64         static char *kwlist[] = {"key", "parent", "values", "children", NULL};
65         
66         if (!PyArg_ParseTupleAndKeywords(args, kwds, "S|OOO", kwlist,
67                         &key, &parent, &values, &children))
68                 return -1;
69         
70         if (values == NULL) {
71                 values = PyTuple_New(0);
72                 PyErr_Clear();
73         }
74         if (children == NULL) {
75                 children = PyTuple_New(0);
76                 PyErr_Clear();
77         }
78         tmp = self->key;
79         Py_INCREF(key);
80         self->key = key;
81         Py_XDECREF(tmp);
82         if (parent != NULL) {
83                 tmp = self->parent;
84                 Py_INCREF(parent);
85                 self->parent = parent;
86                 Py_XDECREF(tmp);
87         }
88         if (values != NULL) {
89                 tmp = self->values;
90                 Py_INCREF(values);
91                 self->values = values;
92                 Py_XDECREF(tmp);
93         }
94         if (children != NULL) {
95                 tmp = self->children;
96                 Py_INCREF(children);
97                 self->children = children;
98                 Py_XDECREF(tmp);
99         }
100         return 0;
101 }
102
103 static PyMemberDef Config_members[] = {
104     {"Parent", T_OBJECT, offsetof(Config, parent), 0, "Parent node"},
105     {"Key", T_OBJECT_EX, offsetof(Config, key), 0, "Keyword of this node"},
106     {"Values", T_OBJECT_EX, offsetof(Config, values), 0, "Values after the key"},
107     {"Children", T_OBJECT_EX, offsetof(Config, children), 0, "Childnodes of this node"},
108     {NULL}
109 };
110
111 static PyTypeObject ConfigType = {
112     PyObject_HEAD_INIT(NULL)
113     0,                         /* Always 0 */
114     "collectd.Config",         /* tp_name */
115     sizeof(Config),            /* tp_basicsize */
116     0,                         /* Will be filled in later */
117     Config_dealloc,            /* tp_dealloc */
118     0,                         /* tp_print */
119     0,                         /* tp_getattr */
120     0,                         /* tp_setattr */
121     0,                         /* tp_compare */
122     0,                         /* tp_repr */
123     0,                         /* tp_as_number */
124     0,                         /* tp_as_sequence */
125     0,                         /* tp_as_mapping */
126     0,                         /* tp_hash */
127     0,                         /* tp_call */
128     0,                         /* tp_str */
129     0,                         /* tp_getattro */
130     0,                         /* tp_setattro */
131     0,                         /* tp_as_buffer */
132     Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
133     "Cool help text later",    /* tp_doc */
134     0,                         /* tp_traverse */
135     0,                         /* tp_clear */
136     0,                         /* tp_richcompare */
137     0,                         /* tp_weaklistoffset */
138     0,                         /* tp_iter */
139     0,                         /* tp_iternext */
140     0,                         /* tp_methods */
141     Config_members,            /* tp_members */
142     0,                         /* tp_getset */
143     0,                         /* tp_base */
144     0,                         /* tp_dict */
145     0,                         /* tp_descr_get */
146     0,                         /* tp_descr_set */
147     0,                         /* tp_dictoffset */
148     Config_init,               /* tp_init */
149     0,                         /* tp_alloc */
150     Config_new                 /* tp_new */
151 };
152
153 static PyObject *cpy_register_generic(cpy_callback_t **list_head, PyObject *args, PyObject *kwds) {
154         cpy_callback_t *c;
155         const char *name = NULL;
156         PyObject *callback = NULL, *data = NULL;
157         static char *kwlist[] = {"callback", "data", "name", NULL};
158         
159         if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
160         if (PyCallable_Check(callback) == 0) {
161                 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
162                 return NULL;
163         }
164         if (name == NULL) {
165                 PyObject *mod;
166                 
167                 mod = PyObject_GetAttrString(callback, "__module__");
168                 if (mod != NULL) name = PyString_AsString(mod);
169                 if (name == NULL) {
170                         PyErr_SetString(PyExc_ValueError, "No module name specified and "
171                                 "callback function does not have a \"__module__\" attribute.");
172                         return NULL;
173                 }
174         }
175         c = malloc(sizeof(*c));
176         c->name = strdup(name);
177         c->callback = callback;
178         c->data = data;
179         c->next = *list_head;
180         *list_head = c;
181         Py_RETURN_NONE;
182 }
183
184 static PyObject *cpy_register_config(PyObject *self, PyObject *args, PyObject *kwds) {
185         return cpy_register_generic(&cpy_config_callbacks, args, kwds);
186 }
187
188 static PyObject *cpy_register_init(PyObject *self, PyObject *args, PyObject *kwds) {
189         return cpy_register_generic(&cpy_init_callbacks, args, kwds);
190 }
191
192 static PyMethodDef cpy_methods[] = {
193         {"register_init", (PyCFunction) cpy_register_init, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
194         {"register_config", (PyCFunction) cpy_register_config, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
195         {0, 0, 0, 0}
196 };
197
198 static int cpy_shutdown(void) {
199         /* This can happen if the module was loaded but not configured. */
200         if (state != NULL)
201                 PyEval_RestoreThread(state);
202         Py_Finalize();
203         return 0;
204 }
205
206 static int cpy_init(void) {
207         cpy_callback_t *c;
208         PyObject *ret;
209         
210         PyEval_InitThreads();
211         /* Now it's finally OK to use python threads. */
212         for (c = cpy_init_callbacks; c; c = c->next) {
213                 if (c->data == NULL)
214                         ret = PyObject_CallObject(c->callback, NULL); /* New reference. */
215                 else
216                         ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
217                 if (ret == NULL)
218                         PyErr_Print(); /* FIXME */
219                 else
220                         Py_DECREF(ret);
221         }
222         state = PyEval_SaveThread();
223         return 0;
224 }
225
226 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
227         int i;
228         PyObject *item, *values, *children, *tmp;
229         
230         if (parent == NULL)
231                 parent = Py_None;
232         
233         values = PyTuple_New(ci->values_num); /* New reference. */
234         for (i = 0; i < ci->values_num; ++i) {
235                 if (ci->values[i].type == OCONFIG_TYPE_STRING) {
236                         PyTuple_SET_ITEM(values, i, PyString_FromString(ci->values[i].value.string));
237                 } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
238                         PyTuple_SET_ITEM(values, i, PyFloat_FromDouble(ci->values[i].value.number));
239                 } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
240                         PyTuple_SET_ITEM(values, i, PyBool_FromLong(ci->values[i].value.boolean));
241                 }
242         }
243         
244         item = PyObject_CallFunction((PyObject *) &ConfigType, "sONO", ci->key, parent, values, Py_None);
245         if (item == NULL)
246                 return NULL;
247         children = PyTuple_New(ci->children_num); /* New reference. */
248         for (i = 0; i < ci->children_num; ++i) {
249                         PyTuple_SET_ITEM(children, i, cpy_oconfig_to_pyconfig(ci->children + i, item));
250         }
251         tmp = ((Config *) item)->children;
252         ((Config *) item)->children = children;
253         Py_XDECREF(tmp);
254         return item;
255 }
256
257 static int cpy_config(oconfig_item_t *ci) {
258         int i;
259         PyObject *sys;
260         PyObject *sys_path;
261         PyObject *module;
262         
263         /* Ok in theory we shouldn't do initialization at this point
264          * but we have to. In order to give python scripts a chance
265          * to register a config callback we need to be able to execute
266          * python code during the config callback so we have to start
267          * the interpreter here. */
268         /* Do *not* use the python "thread" module at this point! */
269         Py_Initialize();
270         
271         PyType_Ready(&ConfigType);
272         sys = PyImport_ImportModule("sys"); /* New reference. */
273         if (sys == NULL) {
274                 ERROR("python module: Unable to import \"sys\" module.");
275                 /* Just print the default python exception text to stderr. */
276                 PyErr_Print();
277                 return 1;
278         }
279         sys_path = PyObject_GetAttrString(sys, "path"); /* New reference. */
280         Py_DECREF(sys);
281         if (sys_path == NULL) {
282                 ERROR("python module: Unable to read \"sys.path\".");
283                 PyErr_Print();
284                 return 1;
285         }
286         module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
287         PyModule_AddObject(module, "Config", (PyObject *) &ConfigType); /* Steals a reference. */
288         for (i = 0; i < ci->children_num; ++i) {
289                 oconfig_item_t *item = ci->children + i;
290                 
291                 if (strcasecmp(item->key, "ModulePath") == 0) {
292                         char *dir = NULL;
293                         PyObject *dir_object;
294                         
295                         if (cf_util_get_string(item, &dir) != 0) 
296                                 continue;
297                         dir_object = PyString_FromString(dir); /* New reference. */
298                         if (dir_object == NULL) {
299                                 ERROR("python plugin: Unable to convert \"%s\" to "
300                                       "a python object.", dir);
301                                 free(dir);
302                                 PyErr_Print();
303                                 continue;
304                         }
305                         if (PyList_Append(sys_path, dir_object) != 0) {
306                                 ERROR("python plugin: Unable to append \"%s\" to "
307                                       "python module path.", dir);
308                                 PyErr_Print();
309                         }
310                         Py_DECREF(dir_object);
311                         free(dir);
312                 } else if (strcasecmp(item->key, "Import") == 0) {
313                         char *module_name = NULL;
314                         PyObject *module;
315                         
316                         if (cf_util_get_string(item, &module_name) != 0) 
317                                 continue;
318                         module = PyImport_ImportModule(module_name); /* New reference. */
319                         if (module == NULL) {
320                                 ERROR("python plugin: Error importing module \"%s\".", module_name);
321                                 PyErr_Print();
322                         }
323                         free(module_name);
324                         Py_XDECREF(module);
325                 } else if (strcasecmp(item->key, "Module") == 0) {
326                         char *name = NULL;
327                         cpy_callback_t *c;
328                         PyObject *ret;
329                         
330                         if (cf_util_get_string(item, &name) != 0)
331                                 continue;
332                         for (c = cpy_config_callbacks; c; c = c->next) {
333                                 if (strcasecmp(c->name, name) == 0)
334                                         break;
335                         }
336                         if (c == NULL) {
337                                 WARNING("python plugin: Found a configuration for the \"%s\" plugin, "
338                                         "but the plugin isn't loaded or didn't register "
339                                         "a configuration callback.", name);
340                                 free(name);
341                                 continue;
342                         }
343                         free(name);
344                         if (c->data == NULL)
345                                 ret = PyObject_CallFunction(c->callback, "N",
346                                         cpy_oconfig_to_pyconfig(item, NULL)); /* New reference. */
347                         else
348                                 ret = PyObject_CallFunction(c->callback, "NO",
349                                         cpy_oconfig_to_pyconfig(item, NULL), c->data); /* New reference. */
350                         if (ret == NULL)
351                                 PyErr_Print();
352                         else
353                                 Py_DECREF(ret);
354                 } else {
355                         WARNING("python plugin: Ignoring unknown config key \"%s\".", item->key);
356                 }
357         }
358         Py_DECREF(sys_path);
359         return 0;
360 }
361
362 void module_register(void) {
363         plugin_register_complex_config("python", cpy_config);
364         plugin_register_init("python", cpy_init);
365 //      plugin_register_read("python", cna_read);
366         plugin_register_shutdown("python", cpy_shutdown);
367 }