be50953c969be411598f2a49716642f40b1fe420
1 #include <Python.h>
2 #include <structmember.h>
4 #include "collectd.h"
5 #include "common.h"
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;
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.
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. */
24 static PyThreadState *state;
26 static cpy_callback_t *cpy_config_callbacks;
27 static cpy_callback_t *cpy_init_callbacks;
29 typedef struct {
30 PyObject_HEAD /* No semicolon! */
31 PyObject *parent;
32 PyObject *key;
33 PyObject *values;
34 PyObject *children;
35 } Config;
37 static void Config_dealloc(PyObject *s) {
38 Config *self = (Config *) s;
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 }
47 static PyObject *Config_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
48 Config *self;
50 self = (Config *) type->tp_alloc(type, 0);
51 if (self == NULL)
52 return NULL;
54 self->parent = NULL;
55 self->key = NULL;
56 self->values = NULL;
57 self->children = NULL;
58 return (PyObject *) self;
59 }
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};
66 if (!PyArg_ParseTupleAndKeywords(args, kwds, "S|OOO", kwlist,
67 &key, &parent, &values, &children))
68 return -1;
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 }
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 };
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 };
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};
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;
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 }
184 static PyObject *cpy_register_config(PyObject *self, PyObject *args, PyObject *kwds) {
185 return cpy_register_generic(&cpy_config_callbacks, args, kwds);
186 }
188 static PyObject *cpy_register_init(PyObject *self, PyObject *args, PyObject *kwds) {
189 return cpy_register_generic(&cpy_init_callbacks, args, kwds);
190 }
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 };
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 }
206 static int cpy_init(void) {
207 cpy_callback_t *c;
208 PyObject *ret;
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 }
226 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
227 int i;
228 PyObject *item, *values, *children, *tmp;
230 if (parent == NULL)
231 parent = Py_None;
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 }
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 }
257 static int cpy_config(oconfig_item_t *ci) {
258 int i;
259 PyObject *sys;
260 PyObject *sys_path;
261 PyObject *module;
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();
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;
291 if (strcasecmp(item->key, "ModulePath") == 0) {
292 char *dir = NULL;
293 PyObject *dir_object;
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;
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;
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 }
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 }