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;
42 if (name != NULL && strchr(name, '.') != NULL) {
43 snprintf(buf, size, "python.%s", name);
44 return;
45 }
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 }
58 n = PyObject_GetAttrString(callback, "__name__"); /* New reference. */
59 if (n != NULL)
60 name = PyString_AsString(n);
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;
138 }
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
157 }
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};
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;
190 }
192 static PyObject *cpy_register_config(PyObject *self, PyObject *args, PyObject *kwds) {
193 return cpy_register_generic(&cpy_config_callbacks, args, kwds);
194 }
196 static PyObject *cpy_register_init(PyObject *self, PyObject *args, PyObject *kwds) {
197 return cpy_register_generic(&cpy_init_callbacks, args, kwds);
198 }
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};
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);
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);
230 }
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};
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);
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);
263 }
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);
267 }
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);
271 }
273 static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args, PyObject *kwds) {
274 return cpy_register_generic(&cpy_shutdown_callbacks, args, kwds);
275 }
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;
284 }
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;
293 }
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;
302 }
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;
311 }
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;
320 }
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;
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;
354 }
356 static int cpy_init(void) {
357 cpy_callback_t *c;
358 PyObject *ret;
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;
371 }
373 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
374 int i;
375 PyObject *item, *values, *children, *tmp;
377 if (parent == NULL)
378 parent = Py_None;
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 }
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;
402 }
404 static int cpy_config(oconfig_item_t *ci) {
405 int i;
406 PyObject *sys;
407 PyObject *sys_path;
408 PyObject *module;
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();
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;
445 if (strcasecmp(item->key, "ModulePath") == 0) {
446 char *dir = NULL;
447 PyObject *dir_object;
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;
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;
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;
514 }
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);
521 }