2 #include <structmember.h>
13 typedef struct cpy_callback_s {
17 struct cpy_callback_s *next;
20 static int do_interactive = 0;
22 /* This is our global thread state. Python saves some stuff in thread-local
23 * storage. So if we allow the interpreter to run in the background
24 * (the scriptwriters might have created some threads from python), we have
25 * to save the state so we can resume it later after shutdown. */
27 static PyThreadState *state;
29 static cpy_callback_t *cpy_config_callbacks;
30 static cpy_callback_t *cpy_init_callbacks;
31 static cpy_callback_t *cpy_shutdown_callbacks;
33 static void cpy_destroy_user_data(void *data) {
34 cpy_callback_t *c = data;
36 Py_DECREF(c->callback);
41 /* You must hold the GIL to call this function!
42 * But if you managed to extract the callback parameter then you probably already do. */
44 static void cpy_build_name(char *buf, size_t size, PyObject *callback, const char *name) {
46 PyObject *mod = NULL, *n = NULL;
48 if (name != NULL && strchr(name, '.') != NULL) {
49 snprintf(buf, size, "python.%s", name);
53 mod = PyObject_GetAttrString(callback, "__module__"); /* New reference. */
55 module = PyString_AsString(mod);
59 snprintf(buf, size, "python.%s.%s", module, name);
64 n = PyObject_GetAttrString(callback, "__name__"); /* New reference. */
66 name = PyString_AsString(n);
69 snprintf(buf, size, "python.%s.%s", module, name);
71 snprintf(buf, size, "python.%s.%p", module, callback);
76 static int cpy_read_callback(user_data_t *data) {
77 cpy_callback_t *c = data->data;
81 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
92 static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_list, user_data_t *data) {
94 cpy_callback_t *c = data->data;
95 PyObject *ret, *v, *list;
98 list = PyList_New(value_list->values_len); /* New reference. */
101 CPY_RETURN_FROM_THREADS 0;
103 for (i = 0; i < value_list->values_len; ++i) {
104 if (ds->ds->type == DS_TYPE_COUNTER) {
105 if ((long) value_list->values[i].counter == value_list->values[i].counter)
106 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].counter));
108 PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].counter));
109 } else if (ds->ds->type == DS_TYPE_GAUGE) {
110 PyList_SetItem(list, i, PyFloat_FromDouble(value_list->values[i].gauge));
111 } else if (ds->ds->type == DS_TYPE_DERIVE) {
112 if ((long) value_list->values[i].derive == value_list->values[i].derive)
113 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].derive));
115 PyList_SetItem(list, i, PyLong_FromLongLong(value_list->values[i].derive));
116 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
117 if ((long) value_list->values[i].absolute == value_list->values[i].absolute)
118 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].absolute));
120 PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].absolute));
122 ERROR("cpy_write_callback: Unknown value type %d.", ds->ds->type);
124 CPY_RETURN_FROM_THREADS 0;
126 if (PyErr_Occurred() != NULL) {
128 CPY_RETURN_FROM_THREADS 0;
131 v = PyObject_CallFunction((PyObject *) &ValuesType, "sOssssdi", value_list->type, list,
132 value_list->plugin_instance, value_list->type_instance, value_list->plugin,
133 value_list->host, (double) value_list->time, value_list->interval);
135 ret = PyObject_CallFunctionObjArgs(c->callback, v, c->data, (void *) 0); /* New reference. */
146 static void cpy_log_callback(int severity, const char *message, user_data_t *data) {
147 cpy_callback_t * c = data->data;
152 ret = PyObject_CallFunction(c->callback, "is", severity, message); /* New reference. */
154 ret = PyObject_CallFunction(c->callback, "isO", severity, message, c->data); /* New reference. */
165 static PyObject *cpy_register_generic(cpy_callback_t **list_head, PyObject *args, PyObject *kwds) {
167 const char *name = NULL;
168 PyObject *callback = NULL, *data = NULL, *mod = NULL;
169 static char *kwlist[] = {"callback", "data", "name", NULL};
171 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
172 if (PyCallable_Check(callback) == 0) {
173 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
177 mod = PyObject_GetAttrString(callback, "__module__"); /* New reference. */
178 if (mod != NULL) name = PyString_AsString(mod);
181 PyErr_SetString(PyExc_ValueError, "No module name specified and "
182 "callback function does not have a \"__module__\" attribute.");
188 c = malloc(sizeof(*c));
189 c->name = strdup(name);
190 c->callback = callback;
192 c->next = *list_head;
198 static PyObject *cpy_register_config(PyObject *self, PyObject *args, PyObject *kwds) {
199 return cpy_register_generic(&cpy_config_callbacks, args, kwds);
202 static PyObject *cpy_register_init(PyObject *self, PyObject *args, PyObject *kwds) {
203 return cpy_register_generic(&cpy_init_callbacks, args, kwds);
206 typedef int reg_function_t(const char *name, void *callback, void *data);
208 static PyObject *cpy_register_generic_userdata(void *reg, void *handler, PyObject *args, PyObject *kwds) {
210 reg_function_t *register_function = (reg_function_t *) reg;
211 cpy_callback_t *c = NULL;
212 user_data_t *user_data = NULL;
213 const char *name = NULL;
214 PyObject *callback = NULL, *data = NULL;
215 static char *kwlist[] = {"callback", "data", "name", NULL};
217 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
218 if (PyCallable_Check(callback) == 0) {
219 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
222 cpy_build_name(buf, sizeof(buf), callback, name);
226 c = malloc(sizeof(*c));
227 c->name = strdup(buf);
228 c->callback = callback;
231 user_data = malloc(sizeof(*user_data));
232 user_data->free_func = cpy_destroy_user_data;
234 register_function(buf, handler, user_data);
235 return PyString_FromString(buf);
238 static PyObject *cpy_register_read(PyObject *self, PyObject *args, PyObject *kwds) {
240 cpy_callback_t *c = NULL;
241 user_data_t *user_data = NULL;
243 const char *name = NULL;
244 PyObject *callback = NULL, *data = NULL;
246 static char *kwlist[] = {"callback", "interval", "data", "name", NULL};
248 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|dOz", kwlist, &callback, &interval, &data, &name) == 0) return NULL;
249 if (PyCallable_Check(callback) == 0) {
250 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
253 cpy_build_name(buf, sizeof(buf), callback, name);
257 c = malloc(sizeof(*c));
258 c->name = strdup(buf);
259 c->callback = callback;
262 user_data = malloc(sizeof(*user_data));
263 user_data->free_func = cpy_destroy_user_data;
265 ts.tv_sec = interval;
266 ts.tv_nsec = (interval - ts.tv_sec) * 1000000000;
267 plugin_register_complex_read(buf, cpy_read_callback, &ts, user_data);
268 return PyString_FromString(buf);
271 static PyObject *cpy_register_log(PyObject *self, PyObject *args, PyObject *kwds) {
272 return cpy_register_generic_userdata(plugin_register_log, cpy_log_callback, args, kwds);
275 static PyObject *cpy_register_write(PyObject *self, PyObject *args, PyObject *kwds) {
276 return cpy_register_generic_userdata(plugin_register_write, cpy_write_callback, args, kwds);
279 static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args, PyObject *kwds) {
280 return cpy_register_generic(&cpy_shutdown_callbacks, args, kwds);
283 static PyObject *cpy_Error(PyObject *self, PyObject *args) {
285 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
286 Py_BEGIN_ALLOW_THREADS
287 plugin_log(LOG_ERR, "%s", text);
292 static PyObject *cpy_Warning(PyObject *self, PyObject *args) {
294 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
295 Py_BEGIN_ALLOW_THREADS
296 plugin_log(LOG_WARNING, "%s", text);
301 static PyObject *cpy_Notice(PyObject *self, PyObject *args) {
303 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
304 Py_BEGIN_ALLOW_THREADS
305 plugin_log(LOG_NOTICE, "%s", text);
310 static PyObject *cpy_Info(PyObject *self, PyObject *args) {
312 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
313 Py_BEGIN_ALLOW_THREADS
314 plugin_log(LOG_INFO, "%s", text);
319 static PyObject *cpy_Debug(PyObject *self, PyObject *args) {
322 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
323 plugin_log(LOG_DEBUG, "%s", text);
328 static PyMethodDef cpy_methods[] = {
329 {"debug", cpy_Debug, METH_VARARGS, "This is an unhelpful text."},
330 {"info", cpy_Info, METH_VARARGS, "This is an unhelpful text."},
331 {"notice", cpy_Notice, METH_VARARGS, "This is an unhelpful text."},
332 {"warning", cpy_Warning, METH_VARARGS, "This is an unhelpful text."},
333 {"error", cpy_Error, METH_VARARGS, "This is an unhelpful text."},
334 {"register_log", (PyCFunction) cpy_register_log, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
335 {"register_init", (PyCFunction) cpy_register_init, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
336 {"register_config", (PyCFunction) cpy_register_config, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
337 {"register_read", (PyCFunction) cpy_register_read, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
338 {"register_write", (PyCFunction) cpy_register_write, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
339 {"register_shutdown", (PyCFunction) cpy_register_shutdown, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
343 static int cpy_shutdown(void) {
347 /* This can happen if the module was loaded but not configured. */
349 PyEval_RestoreThread(state);
351 for (c = cpy_shutdown_callbacks; c; c = c->next) {
352 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
354 PyErr_Print(); /* FIXME */
362 static void *cpy_interactive(void *data) {
364 if (PyImport_ImportModule("readline") == NULL) {
365 /* This interactive session will suck. */
366 PyErr_Print(); /* FIXME */
368 PyRun_InteractiveLoop(stdin, "<stdin>");
370 NOTICE("python: Interactive interpreter exited, stopping collectd ...");
375 static int cpy_init(void) {
378 static pthread_t thread;
380 PyEval_InitThreads();
381 /* Now it's finally OK to use python threads. */
382 for (c = cpy_init_callbacks; c; c = c->next) {
383 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
385 PyErr_Print(); /* FIXME */
389 state = PyEval_SaveThread();
390 if (do_interactive) {
391 if (pthread_create(&thread, NULL, cpy_interactive, NULL)) {
392 ERROR("python: Error creating thread for interactive interpreter.");
399 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
401 PyObject *item, *values, *children, *tmp;
406 values = PyTuple_New(ci->values_num); /* New reference. */
407 for (i = 0; i < ci->values_num; ++i) {
408 if (ci->values[i].type == OCONFIG_TYPE_STRING) {
409 PyTuple_SET_ITEM(values, i, PyString_FromString(ci->values[i].value.string));
410 } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
411 PyTuple_SET_ITEM(values, i, PyFloat_FromDouble(ci->values[i].value.number));
412 } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
413 PyTuple_SET_ITEM(values, i, PyBool_FromLong(ci->values[i].value.boolean));
417 item = PyObject_CallFunction((PyObject *) &ConfigType, "sONO", ci->key, parent, values, Py_None);
420 children = PyTuple_New(ci->children_num); /* New reference. */
421 for (i = 0; i < ci->children_num; ++i) {
422 PyTuple_SET_ITEM(children, i, cpy_oconfig_to_pyconfig(ci->children + i, item));
424 tmp = ((Config *) item)->children;
425 ((Config *) item)->children = children;
430 static int cpy_config(oconfig_item_t *ci) {
436 /* Ok in theory we shouldn't do initialization at this point
437 * but we have to. In order to give python scripts a chance
438 * to register a config callback we need to be able to execute
439 * python code during the config callback so we have to start
440 * the interpreter here. */
441 /* Do *not* use the python "thread" module at this point! */
444 PyType_Ready(&ConfigType);
445 PyType_Ready(&ValuesType);
446 sys = PyImport_ImportModule("sys"); /* New reference. */
448 ERROR("python module: Unable to import \"sys\" module.");
449 /* Just print the default python exception text to stderr. */
453 sys_path = PyObject_GetAttrString(sys, "path"); /* New reference. */
455 if (sys_path == NULL) {
456 ERROR("python module: Unable to read \"sys.path\".");
460 module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
461 PyModule_AddObject(module, "Config", (PyObject *) &ConfigType); /* Steals a reference. */
462 PyModule_AddObject(module, "Values", (PyObject *) &ValuesType); /* Steals a reference. */
463 PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG);
464 PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO);
465 PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE);
466 PyModule_AddIntConstant(module, "LOG_WARNING", LOG_WARNING);
467 PyModule_AddIntConstant(module, "LOG_ERROR", LOG_ERR);
468 for (i = 0; i < ci->children_num; ++i) {
469 oconfig_item_t *item = ci->children + i;
471 if (strcasecmp(item->key, "Interactive") == 0) {
472 if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_BOOLEAN ||
473 !item->values[0].value.boolean)
476 } else if (strcasecmp(item->key, "ModulePath") == 0) {
478 PyObject *dir_object;
480 if (cf_util_get_string(item, &dir) != 0)
482 dir_object = PyString_FromString(dir); /* New reference. */
483 if (dir_object == NULL) {
484 ERROR("python plugin: Unable to convert \"%s\" to "
485 "a python object.", dir);
490 if (PyList_Append(sys_path, dir_object) != 0) {
491 ERROR("python plugin: Unable to append \"%s\" to "
492 "python module path.", dir);
495 Py_DECREF(dir_object);
497 } else if (strcasecmp(item->key, "Import") == 0) {
498 char *module_name = NULL;
501 if (cf_util_get_string(item, &module_name) != 0)
503 module = PyImport_ImportModule(module_name); /* New reference. */
504 if (module == NULL) {
505 ERROR("python plugin: Error importing module \"%s\".", module_name);
510 } else if (strcasecmp(item->key, "Module") == 0) {
515 if (cf_util_get_string(item, &name) != 0)
517 for (c = cpy_config_callbacks; c; c = c->next) {
518 if (strcasecmp(c->name, name) == 0)
522 WARNING("python plugin: Found a configuration for the \"%s\" plugin, "
523 "but the plugin isn't loaded or didn't register "
524 "a configuration callback.", name);
530 ret = PyObject_CallFunction(c->callback, "N",
531 cpy_oconfig_to_pyconfig(item, NULL)); /* New reference. */
533 ret = PyObject_CallFunction(c->callback, "NO",
534 cpy_oconfig_to_pyconfig(item, NULL), c->data); /* New reference. */
540 WARNING("python plugin: Ignoring unknown config key \"%s\".", item->key);
547 void module_register(void) {
548 plugin_register_complex_config("python", cpy_config);
549 plugin_register_init("python", cpy_init);
550 // plugin_register_read("python", cna_read);
551 plugin_register_shutdown("python", cpy_shutdown);