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 static void cpy_build_name(char *buf, size_t size, PyObject *callback, const char *name) {
36 const char *module;
37 PyObject *mod = NULL, *n = NULL;
39 if (name != NULL && strchr(name, '.') != NULL) {
40 snprintf(buf, size, "python.%s", name);
41 return;
42 }
44 mod = PyObject_GetAttrString(callback, "__module__"); /* New reference. */
45 if (mod != NULL)
46 module = PyString_AsString(mod);
47 else
48 module = "collectd";
49 if (name != NULL) {
50 snprintf(buf, size, "python.%s.%s", module, name);
51 Py_XDECREF(mod);
52 return;
53 }
55 n = PyObject_GetAttrString(callback, "__name__"); /* New reference. */
56 if (n != NULL)
57 name = PyString_AsString(n);
59 if (name != NULL)
60 snprintf(buf, size, "python.%s.%s", module, name);
61 else
62 snprintf(buf, size, "python.%s.%p", module, callback);
63 Py_XDECREF(mod);
64 Py_XDECREF(n);
65 }
67 static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_list, user_data_t *data) {
68 int i;
69 cpy_callback_t * c = data->data;
70 PyObject *ret, *v, *list;
72 CPY_LOCK_THREADS
73 list = PyList_New(value_list->values_len); /* New reference. */
74 if (list == NULL) {
75 PyErr_Print();
76 CPY_RETURN_FROM_THREADS 0;
77 }
78 for (i = 0; i < value_list->values_len; ++i) {
79 if (ds->ds->type == DS_TYPE_COUNTER) {
80 if ((long) value_list->values[i].counter == value_list->values[i].counter)
81 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].counter));
82 else
83 PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].counter));
84 } else if (ds->ds->type == DS_TYPE_GAUGE) {
85 PyList_SetItem(list, i, PyFloat_FromDouble(value_list->values[i].gauge));
86 } else if (ds->ds->type == DS_TYPE_DERIVE) {
87 if ((long) value_list->values[i].derive == value_list->values[i].derive)
88 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].derive));
89 else
90 PyList_SetItem(list, i, PyLong_FromLongLong(value_list->values[i].derive));
91 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
92 if ((long) value_list->values[i].absolute == value_list->values[i].absolute)
93 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].absolute));
94 else
95 PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].absolute));
96 } else {
97 ERROR("cpy_write_callback: Unknown value type %d.", ds->ds->type);
98 Py_DECREF(list);
99 CPY_RETURN_FROM_THREADS 0;
100 }
101 if (PyErr_Occurred() != NULL) {
102 PyErr_Print();
103 CPY_RETURN_FROM_THREADS 0;
104 }
105 }
106 v = PyObject_CallFunction((PyObject *) &ValuesType, "sOssssdi", value_list->type, list,
107 value_list->plugin_instance, value_list->type_instance, value_list->plugin,
108 value_list->host, (double) value_list->time, value_list->interval);
109 Py_DECREF(list);
110 ret = PyObject_CallFunctionObjArgs(c->callback, v, (void *) 0);
111 if (ret == NULL) {
112 /* FIXME */
113 PyErr_Print();
114 } else {
115 Py_DECREF(ret);
116 }
117 CPY_RELEASE_THREADS
118 return 0;
119 }
121 static void cpy_log_callback(int severity, const char *message, user_data_t *data) {
122 cpy_callback_t * c = data->data;
123 PyObject *ret;
125 CPY_LOCK_THREADS
126 if (c->data == NULL)
127 ret = PyObject_CallFunction(c->callback, "is", severity, message); /* New reference. */
128 else
129 ret = PyObject_CallFunction(c->callback, "isO", severity, message, c->data); /* New reference. */
131 if (ret == NULL) {
132 /* FIXME */
133 PyErr_Print();
134 } else {
135 Py_DECREF(ret);
136 }
137 CPY_RELEASE_THREADS
138 }
140 static PyObject *cpy_register_generic(cpy_callback_t **list_head, PyObject *args, PyObject *kwds) {
141 cpy_callback_t *c;
142 const char *name = NULL;
143 PyObject *callback = NULL, *data = NULL, *mod = NULL;
144 static char *kwlist[] = {"callback", "data", "name", NULL};
146 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
147 if (PyCallable_Check(callback) == 0) {
148 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
149 return NULL;
150 }
151 if (name == NULL) {
152 mod = PyObject_GetAttrString(callback, "__module__"); /* New reference. */
153 if (mod != NULL) name = PyString_AsString(mod);
154 if (name == NULL) {
155 Py_XDECREF(mod);
156 PyErr_SetString(PyExc_ValueError, "No module name specified and "
157 "callback function does not have a \"__module__\" attribute.");
158 return NULL;
159 }
160 }
161 Py_INCREF(callback);
162 Py_XINCREF(data);
163 c = malloc(sizeof(*c));
164 c->name = strdup(name);
165 c->callback = callback;
166 c->data = data;
167 c->next = *list_head;
168 *list_head = c;
169 Py_XDECREF(mod);
170 Py_RETURN_NONE;
171 }
173 static PyObject *cpy_register_config(PyObject *self, PyObject *args, PyObject *kwds) {
174 return cpy_register_generic(&cpy_config_callbacks, args, kwds);
175 }
177 static PyObject *cpy_register_init(PyObject *self, PyObject *args, PyObject *kwds) {
178 return cpy_register_generic(&cpy_init_callbacks, args, kwds);
179 }
181 typedef int reg_function_t(const char *name, void *callback, void *data);
183 static PyObject *cpy_register_generic_userdata(void *reg, void *handler, PyObject *args, PyObject *kwds) {
184 char buf[512];
185 reg_function_t *register_function = (reg_function_t *) reg;
186 cpy_callback_t *c = NULL;
187 user_data_t *user_data = NULL;
188 const char *name = NULL;
189 PyObject *callback = NULL, *data = NULL;
190 static char *kwlist[] = {"callback", "data", "name", NULL};
192 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
193 if (PyCallable_Check(callback) == 0) {
194 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
195 return NULL;
196 }
197 cpy_build_name(buf, sizeof(buf), callback, name);
199 Py_INCREF(callback);
200 Py_XINCREF(data);
201 c = malloc(sizeof(*c));
202 c->name = strdup(buf);
203 c->callback = callback;
204 c->data = data;
205 c->next = NULL;
206 user_data = malloc(sizeof(*user_data));
207 user_data->free_func = cpy_destroy_user_data;
208 user_data->data = c;
209 register_function(buf, handler, user_data);
210 return PyString_FromString(buf);
211 }
213 static PyObject *cpy_register_log(PyObject *self, PyObject *args, PyObject *kwds) {
214 return cpy_register_generic_userdata(plugin_register_log, cpy_log_callback, args, kwds);
215 }
217 static PyObject *cpy_register_write(PyObject *self, PyObject *args, PyObject *kwds) {
218 return cpy_register_generic_userdata(plugin_register_write, cpy_write_callback, args, kwds);
219 }
221 static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args, PyObject *kwds) {
222 return cpy_register_generic(&cpy_shutdown_callbacks, args, kwds);
223 }
225 static PyObject *cpy_Error(PyObject *self, PyObject *args) {
226 const char *text;
227 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
228 Py_BEGIN_ALLOW_THREADS
229 plugin_log(LOG_ERR, "%s", text);
230 Py_END_ALLOW_THREADS
231 Py_RETURN_NONE;
232 }
234 static PyObject *cpy_Warning(PyObject *self, PyObject *args) {
235 const char *text;
236 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
237 Py_BEGIN_ALLOW_THREADS
238 plugin_log(LOG_WARNING, "%s", text);
239 Py_END_ALLOW_THREADS
240 Py_RETURN_NONE;
241 }
243 static PyObject *cpy_Notice(PyObject *self, PyObject *args) {
244 const char *text;
245 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
246 Py_BEGIN_ALLOW_THREADS
247 plugin_log(LOG_NOTICE, "%s", text);
248 Py_END_ALLOW_THREADS
249 Py_RETURN_NONE;
250 }
252 static PyObject *cpy_Info(PyObject *self, PyObject *args) {
253 const char *text;
254 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
255 Py_BEGIN_ALLOW_THREADS
256 plugin_log(LOG_INFO, "%s", text);
257 Py_END_ALLOW_THREADS
258 Py_RETURN_NONE;
259 }
261 static PyObject *cpy_Debug(PyObject *self, PyObject *args) {
262 #ifdef COLLECT_DEBUG
263 const char *text;
264 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
265 plugin_log(LOG_DEBUG, "%s", text);
266 #endif
267 Py_RETURN_NONE;
268 }
270 static PyMethodDef cpy_methods[] = {
271 {"Debug", cpy_Debug, METH_VARARGS, "This is an unhelpful text."},
272 {"Info", cpy_Info, METH_VARARGS, "This is an unhelpful text."},
273 {"Notice", cpy_Notice, METH_VARARGS, "This is an unhelpful text."},
274 {"Warning", cpy_Warning, METH_VARARGS, "This is an unhelpful text."},
275 {"Error", cpy_Error, METH_VARARGS, "This is an unhelpful text."},
276 {"register_log", (PyCFunction) cpy_register_log, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
277 {"register_init", (PyCFunction) cpy_register_init, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
278 {"register_config", (PyCFunction) cpy_register_config, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
279 {"register_write", (PyCFunction) cpy_register_write, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
280 {"register_shutdown", (PyCFunction) cpy_register_shutdown, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
281 {0, 0, 0, 0}
282 };
284 static int cpy_shutdown(void) {
285 cpy_callback_t *c;
286 PyObject *ret;
288 /* This can happen if the module was loaded but not configured. */
289 if (state != NULL)
290 PyEval_RestoreThread(state);
292 for (c = cpy_shutdown_callbacks; c; c = c->next) {
293 if (c->data == NULL)
294 ret = PyObject_CallObject(c->callback, NULL); /* New reference. */
295 else
296 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
297 if (ret == NULL)
298 PyErr_Print(); /* FIXME */
299 else
300 Py_DECREF(ret);
301 }
302 Py_Finalize();
303 return 0;
304 }
306 static int cpy_init(void) {
307 cpy_callback_t *c;
308 PyObject *ret;
310 PyEval_InitThreads();
311 /* Now it's finally OK to use python threads. */
312 for (c = cpy_init_callbacks; c; c = c->next) {
313 if (c->data == NULL)
314 ret = PyObject_CallObject(c->callback, NULL); /* New reference. */
315 else
316 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
317 if (ret == NULL)
318 PyErr_Print(); /* FIXME */
319 else
320 Py_DECREF(ret);
321 }
322 state = PyEval_SaveThread();
323 return 0;
324 }
326 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
327 int i;
328 PyObject *item, *values, *children, *tmp;
330 if (parent == NULL)
331 parent = Py_None;
333 values = PyTuple_New(ci->values_num); /* New reference. */
334 for (i = 0; i < ci->values_num; ++i) {
335 if (ci->values[i].type == OCONFIG_TYPE_STRING) {
336 PyTuple_SET_ITEM(values, i, PyString_FromString(ci->values[i].value.string));
337 } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
338 PyTuple_SET_ITEM(values, i, PyFloat_FromDouble(ci->values[i].value.number));
339 } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
340 PyTuple_SET_ITEM(values, i, PyBool_FromLong(ci->values[i].value.boolean));
341 }
342 }
344 item = PyObject_CallFunction((PyObject *) &ConfigType, "sONO", ci->key, parent, values, Py_None);
345 if (item == NULL)
346 return NULL;
347 children = PyTuple_New(ci->children_num); /* New reference. */
348 for (i = 0; i < ci->children_num; ++i) {
349 PyTuple_SET_ITEM(children, i, cpy_oconfig_to_pyconfig(ci->children + i, item));
350 }
351 tmp = ((Config *) item)->children;
352 ((Config *) item)->children = children;
353 Py_XDECREF(tmp);
354 return item;
355 }
357 static int cpy_config(oconfig_item_t *ci) {
358 int i;
359 PyObject *sys;
360 PyObject *sys_path;
361 PyObject *module;
363 /* Ok in theory we shouldn't do initialization at this point
364 * but we have to. In order to give python scripts a chance
365 * to register a config callback we need to be able to execute
366 * python code during the config callback so we have to start
367 * the interpreter here. */
368 /* Do *not* use the python "thread" module at this point! */
369 Py_Initialize();
371 PyType_Ready(&ConfigType);
372 PyType_Ready(&ValuesType);
373 sys = PyImport_ImportModule("sys"); /* New reference. */
374 if (sys == NULL) {
375 ERROR("python module: Unable to import \"sys\" module.");
376 /* Just print the default python exception text to stderr. */
377 PyErr_Print();
378 return 1;
379 }
380 sys_path = PyObject_GetAttrString(sys, "path"); /* New reference. */
381 Py_DECREF(sys);
382 if (sys_path == NULL) {
383 ERROR("python module: Unable to read \"sys.path\".");
384 PyErr_Print();
385 return 1;
386 }
387 module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
388 PyModule_AddObject(module, "Config", (PyObject *) &ConfigType); /* Steals a reference. */
389 PyModule_AddObject(module, "Values", (PyObject *) &ValuesType); /* Steals a reference. */
390 PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG);
391 PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO);
392 PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE);
393 PyModule_AddIntConstant(module, "LOG_WARNING", LOG_WARNING);
394 PyModule_AddIntConstant(module, "LOG_ERROR", LOG_ERR);
395 for (i = 0; i < ci->children_num; ++i) {
396 oconfig_item_t *item = ci->children + i;
398 if (strcasecmp(item->key, "ModulePath") == 0) {
399 char *dir = NULL;
400 PyObject *dir_object;
402 if (cf_util_get_string(item, &dir) != 0)
403 continue;
404 dir_object = PyString_FromString(dir); /* New reference. */
405 if (dir_object == NULL) {
406 ERROR("python plugin: Unable to convert \"%s\" to "
407 "a python object.", dir);
408 free(dir);
409 PyErr_Print();
410 continue;
411 }
412 if (PyList_Append(sys_path, dir_object) != 0) {
413 ERROR("python plugin: Unable to append \"%s\" to "
414 "python module path.", dir);
415 PyErr_Print();
416 }
417 Py_DECREF(dir_object);
418 free(dir);
419 } else if (strcasecmp(item->key, "Import") == 0) {
420 char *module_name = NULL;
421 PyObject *module;
423 if (cf_util_get_string(item, &module_name) != 0)
424 continue;
425 module = PyImport_ImportModule(module_name); /* New reference. */
426 if (module == NULL) {
427 ERROR("python plugin: Error importing module \"%s\".", module_name);
428 PyErr_Print();
429 }
430 free(module_name);
431 Py_XDECREF(module);
432 } else if (strcasecmp(item->key, "Module") == 0) {
433 char *name = NULL;
434 cpy_callback_t *c;
435 PyObject *ret;
437 if (cf_util_get_string(item, &name) != 0)
438 continue;
439 for (c = cpy_config_callbacks; c; c = c->next) {
440 if (strcasecmp(c->name, name) == 0)
441 break;
442 }
443 if (c == NULL) {
444 WARNING("python plugin: Found a configuration for the \"%s\" plugin, "
445 "but the plugin isn't loaded or didn't register "
446 "a configuration callback.", name);
447 free(name);
448 continue;
449 }
450 free(name);
451 if (c->data == NULL)
452 ret = PyObject_CallFunction(c->callback, "N",
453 cpy_oconfig_to_pyconfig(item, NULL)); /* New reference. */
454 else
455 ret = PyObject_CallFunction(c->callback, "NO",
456 cpy_oconfig_to_pyconfig(item, NULL), c->data); /* New reference. */
457 if (ret == NULL)
458 PyErr_Print();
459 else
460 Py_DECREF(ret);
461 } else {
462 WARNING("python plugin: Ignoring unknown config key \"%s\".", item->key);
463 }
464 }
465 Py_DECREF(sys_path);
466 return 0;
467 }
469 void module_register(void) {
470 plugin_register_complex_config("python", cpy_config);
471 plugin_register_init("python", cpy_init);
472 // plugin_register_read("python", cna_read);
473 plugin_register_shutdown("python", cpy_shutdown);
474 }