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;
28 typedef struct {
29 PyObject_HEAD /* No semicolon! */
30 PyObject *parent;
31 PyObject *key;
32 PyObject *values;
33 PyObject *children;
34 } Config;
36 static void Config_dealloc(PyObject *s) {
37 Config *self = (Config *) s;
39 Py_XDECREF(self->parent);
40 Py_XDECREF(self->key);
41 Py_XDECREF(self->values);
42 Py_XDECREF(self->children);
43 self->ob_type->tp_free(s);
44 }
46 static PyObject *Config_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
47 Config *self;
49 self = (Config *) type->tp_alloc(type, 0);
50 if (self == NULL)
51 return NULL;
53 self->parent = NULL;
54 self->key = NULL;
55 self->values = NULL;
56 self->children = NULL;
57 return (PyObject *) self;
58 }
60 static int Config_init(PyObject *s, PyObject *args, PyObject *kwds) {
61 PyObject *key = NULL, *parent = NULL, *values = NULL, *children = NULL, *tmp;
62 Config *self = (Config *) s;
63 static char *kwlist[] = {"key", "parent", "values", "children", NULL};
65 if (!PyArg_ParseTupleAndKeywords(args, kwds, "S|OOO", kwlist,
66 &key, &parent, &values, &children))
67 return -1;
69 if (values == NULL) {
70 values = PyTuple_New(0);
71 PyErr_Clear();
72 }
73 if (children == NULL) {
74 children = PyTuple_New(0);
75 PyErr_Clear();
76 }
77 tmp = self->key;
78 Py_INCREF(key);
79 self->key = key;
80 Py_XDECREF(tmp);
81 if (parent != NULL) {
82 tmp = self->parent;
83 Py_INCREF(parent);
84 self->parent = parent;
85 Py_XDECREF(tmp);
86 }
87 if (values != NULL) {
88 tmp = self->values;
89 Py_INCREF(values);
90 self->values = values;
91 Py_XDECREF(tmp);
92 }
93 if (children != NULL) {
94 tmp = self->children;
95 Py_INCREF(children);
96 self->children = children;
97 Py_XDECREF(tmp);
98 }
99 return 0;
100 }
102 static PyMemberDef Config_members[] = {
103 {"Parent", T_OBJECT, offsetof(Config, parent), 0, "Parent node"},
104 {"Key", T_OBJECT_EX, offsetof(Config, key), 0, "Keyword of this node"},
105 {"Values", T_OBJECT_EX, offsetof(Config, values), 0, "Values after the key"},
106 {"Children", T_OBJECT_EX, offsetof(Config, children), 0, "Childnodes of this node"},
107 {NULL}
108 };
110 static PyTypeObject ConfigType = {
111 PyObject_HEAD_INIT(NULL)
112 0, /* Always 0 */
113 "collectd.Config", /* tp_name */
114 sizeof(Config), /* tp_basicsize */
115 0, /* Will be filled in later */
116 Config_dealloc, /* tp_dealloc */
117 0, /* tp_print */
118 0, /* tp_getattr */
119 0, /* tp_setattr */
120 0, /* tp_compare */
121 0, /* tp_repr */
122 0, /* tp_as_number */
123 0, /* tp_as_sequence */
124 0, /* tp_as_mapping */
125 0, /* tp_hash */
126 0, /* tp_call */
127 0, /* tp_str */
128 0, /* tp_getattro */
129 0, /* tp_setattro */
130 0, /* tp_as_buffer */
131 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
132 "Cool help text later", /* tp_doc */
133 0, /* tp_traverse */
134 0, /* tp_clear */
135 0, /* tp_richcompare */
136 0, /* tp_weaklistoffset */
137 0, /* tp_iter */
138 0, /* tp_iternext */
139 0, /* tp_methods */
140 Config_members, /* tp_members */
141 0, /* tp_getset */
142 0, /* tp_base */
143 0, /* tp_dict */
144 0, /* tp_descr_get */
145 0, /* tp_descr_set */
146 0, /* tp_dictoffset */
147 Config_init, /* tp_init */
148 0, /* tp_alloc */
149 Config_new /* tp_new */
150 };
152 static PyObject *cpy_register_config(PyObject *self, PyObject *args, PyObject *kwds) {
153 cpy_callback_t *c;
154 const char *name = NULL;
155 PyObject *callback = NULL, *data = NULL;
156 static char *kwlist[] = {"callback", "data", "name", NULL};
158 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
159 if (PyCallable_Check(callback) == 0) {
160 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
161 return 0;
162 }
163 if (name == NULL) {
164 PyObject *mod;
166 mod = PyObject_GetAttrString(callback, "__module__");
167 if (mod != NULL) name = PyString_AsString(mod);
168 if (name == NULL) {
169 PyErr_SetString(PyExc_ValueError, "No module name specified and "
170 "callback function does not have a \"__module__\" attribute.");
171 return 0;
172 }
173 }
174 c = malloc(sizeof(*c));
175 c->name = strdup(name);
176 c->callback = callback;
177 c->data = data;
178 c->next = cpy_config_callbacks;
179 cpy_config_callbacks = c;
180 return Py_None;
181 }
183 static PyMethodDef cpy_methods[] = {
184 {"register_config", (PyCFunction) cpy_register_config, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
185 {0, 0, 0, 0}
186 };
188 static int cpy_shutdown(void) {
189 /* This can happen if the module was loaded but not configured. */
190 if (state != NULL)
191 PyEval_RestoreThread(state);
192 Py_Finalize();
193 return 0;
194 }
196 static int cpy_init(void) {
197 PyEval_InitThreads();
198 /* Now it's finally OK to use python threads. */
199 return 0;
200 }
202 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
203 int i;
204 PyObject *item, *values, *children, *tmp;
206 if (parent == NULL)
207 parent = Py_None;
209 values = PyTuple_New(ci->values_num); /* New reference. */
210 for (i = 0; i < ci->values_num; ++i) {
211 if (ci->values[i].type == OCONFIG_TYPE_STRING) {
212 PyTuple_SET_ITEM(values, i, PyString_FromString(ci->values[i].value.string));
213 } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
214 PyTuple_SET_ITEM(values, i, PyFloat_FromDouble(ci->values[i].value.number));
215 } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
216 PyTuple_SET_ITEM(values, i, PyBool_FromLong(ci->values[i].value.boolean));
217 }
218 }
220 item = PyObject_CallFunction((PyObject *) &ConfigType, "sONO", ci->key, parent, values, Py_None);
221 if (item == NULL)
222 return NULL;
223 children = PyTuple_New(ci->children_num); /* New reference. */
224 for (i = 0; i < ci->children_num; ++i) {
225 PyTuple_SET_ITEM(children, i, cpy_oconfig_to_pyconfig(ci->children + i, item));
226 }
227 tmp = ((Config *) item)->children;
228 ((Config *) item)->children = children;
229 Py_XDECREF(tmp);
230 return item;
231 }
233 static int cpy_config(oconfig_item_t *ci) {
234 int i;
235 PyObject *sys;
236 PyObject *sys_path;
237 PyObject *module;
239 /* Ok in theory we shouldn't do initialization at this point
240 * but we have to. In order to give python scripts a chance
241 * to register a config callback we need to be able to execute
242 * python code during the config callback so we have to start
243 * the interpreter here. */
244 /* Do *not* use the python "thread" module at this point! */
245 Py_Initialize();
247 PyType_Ready(&ConfigType);
248 sys = PyImport_ImportModule("sys"); /* New reference. */
249 if (sys == NULL) {
250 ERROR("python module: Unable to import \"sys\" module.");
251 /* Just print the default python exception text to stderr. */
252 PyErr_Print();
253 return 1;
254 }
255 sys_path = PyObject_GetAttrString(sys, "path"); /* New reference. */
256 Py_DECREF(sys);
257 if (sys_path == NULL) {
258 ERROR("python module: Unable to read \"sys.path\".");
259 PyErr_Print();
260 return 1;
261 }
262 module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
263 PyModule_AddObject(module, "Config", (PyObject *) &ConfigType); /* Steals a reference. */
264 for (i = 0; i < ci->children_num; ++i) {
265 oconfig_item_t *item = ci->children + i;
267 if (strcasecmp(item->key, "ModulePath") == 0) {
268 char *dir = NULL;
269 PyObject *dir_object;
271 if (cf_util_get_string(item, &dir) != 0)
272 continue;
273 dir_object = PyString_FromString(dir); /* New reference. */
274 if (dir_object == NULL) {
275 ERROR("python plugin: Unable to convert \"%s\" to "
276 "a python object.", dir);
277 free(dir);
278 PyErr_Print();
279 continue;
280 }
281 if (PyList_Append(sys_path, dir_object) != 0) {
282 ERROR("python plugin: Unable to append \"%s\" to "
283 "python module path.", dir);
284 PyErr_Print();
285 }
286 Py_DECREF(dir_object);
287 free(dir);
288 } else if (strcasecmp(item->key, "Import") == 0) {
289 char *module_name = NULL;
290 PyObject *module;
292 if (cf_util_get_string(item, &module_name) != 0)
293 continue;
294 module = PyImport_ImportModule(module_name); /* New reference. */
295 if (module == NULL) {
296 ERROR("python plugin: Error importing module \"%s\".", module_name);
297 PyErr_Print();
298 }
299 free(module_name);
300 Py_XDECREF(module);
301 } else if (strcasecmp(item->key, "Module") == 0) {
302 char *name = NULL;
303 cpy_callback_t *c;
304 PyObject *ret;
306 if (cf_util_get_string(item, &name) != 0)
307 continue;
308 for (c = cpy_config_callbacks; c; c = c->next) {
309 if (strcasecmp(c->name, name) == 0)
310 break;
311 }
312 if (c == NULL) {
313 WARNING("python plugin: Found a configuration for the \"%s\" plugin, "
314 "but the plugin isn't loaded or didn't register "
315 "a configuration callback.", name);
316 free(name);
317 continue;
318 }
319 free(name);
320 ret = PyObject_CallFunction(c->callback, "N",
321 cpy_oconfig_to_pyconfig(item, NULL)); /* New reference. */
322 if (ret == NULL)
323 PyErr_Print();
324 else
325 Py_DECREF(ret);
326 } else {
327 WARNING("python plugin: Ignoring unknown config key \"%s\".", item->key);
328 }
329 }
330 Py_DECREF(sys_path);
331 return 0;
332 }
334 void module_register(void) {
335 plugin_register_complex_config("python", cpy_config);
336 plugin_register_init("python", cpy_init);
337 // plugin_register_read("python", cna_read);
338 plugin_register_shutdown("python", cpy_shutdown);
339 }