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 PyObject *cpy_format_exception;
31 static cpy_callback_t *cpy_config_callbacks;
32 static cpy_callback_t *cpy_init_callbacks;
33 static cpy_callback_t *cpy_shutdown_callbacks;
35 static void cpy_destroy_user_data(void *data) {
36 cpy_callback_t *c = data;
38 Py_DECREF(c->callback);
43 /* You must hold the GIL to call this function!
44 * But if you managed to extract the callback parameter then you probably already do. */
46 static void cpy_build_name(char *buf, size_t size, PyObject *callback, const char *name) {
48 PyObject *mod = NULL, *n = NULL;
50 if (name != NULL && strchr(name, '.') != NULL) {
51 snprintf(buf, size, "python.%s", name);
55 mod = PyObject_GetAttrString(callback, "__module__"); /* New reference. */
57 module = PyString_AsString(mod);
61 snprintf(buf, size, "python.%s.%s", module, name);
66 n = PyObject_GetAttrString(callback, "__name__"); /* New reference. */
68 name = PyString_AsString(n);
71 snprintf(buf, size, "python.%s.%s", module, name);
73 snprintf(buf, size, "python.%s.%p", module, callback);
78 static void cpy_log_exception(const char *context) {
80 const char *typename = NULL, *message = NULL;
81 PyObject *type, *value, *traceback, *tn, *m, *list;
83 PyErr_Fetch(&type, &value, &traceback);
84 PyErr_NormalizeException(&type, &value, &traceback);
85 if (type == NULL) return;
86 tn = PyObject_GetAttrString(type, "__name__"); /* New reference. */
87 m = PyObject_GetAttrString(value, "message"); /* New reference. */
89 typename = PyString_AsString(tn);
91 message = PyString_AsString(m);
93 typename = "NamelessException";
96 ERROR("Unhandled python exception in %s: %s: %s", context, typename, message);
99 if (!cpy_format_exception) {
103 Py_XDECREF(traceback);
110 list = PyObject_CallFunction(cpy_format_exception, "NNN", type, value, traceback);
112 l = PyObject_Length(list);
113 for (i = 0; i < l; ++i) {
117 line = PyList_GET_ITEM(list, i);
118 s = strdup(PyString_AsString(line));
120 if (s[strlen(s) - 1] == '\n')
121 s[strlen(s) - 1] = 0;
128 static int cpy_read_callback(user_data_t *data) {
129 cpy_callback_t *c = data->data;
133 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
135 cpy_log_exception("read callback");
143 static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_list, user_data_t *data) {
145 cpy_callback_t *c = data->data;
146 PyObject *ret, *v, *list;
149 list = PyList_New(value_list->values_len); /* New reference. */
151 cpy_log_exception("write callback");
152 CPY_RETURN_FROM_THREADS 0;
154 for (i = 0; i < value_list->values_len; ++i) {
155 if (ds->ds->type == DS_TYPE_COUNTER) {
156 if ((long) value_list->values[i].counter == value_list->values[i].counter)
157 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].counter));
159 PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].counter));
160 } else if (ds->ds->type == DS_TYPE_GAUGE) {
161 PyList_SetItem(list, i, PyFloat_FromDouble(value_list->values[i].gauge));
162 } else if (ds->ds->type == DS_TYPE_DERIVE) {
163 if ((long) value_list->values[i].derive == value_list->values[i].derive)
164 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].derive));
166 PyList_SetItem(list, i, PyLong_FromLongLong(value_list->values[i].derive));
167 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
168 if ((long) value_list->values[i].absolute == value_list->values[i].absolute)
169 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].absolute));
171 PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].absolute));
173 ERROR("cpy_write_callback: Unknown value type %d.", ds->ds->type);
175 CPY_RETURN_FROM_THREADS 0;
177 if (PyErr_Occurred() != NULL) {
178 cpy_log_exception("value building for write callback");
179 CPY_RETURN_FROM_THREADS 0;
182 v = PyObject_CallFunction((PyObject *) &ValuesType, "sOssssdi", value_list->type, list,
183 value_list->plugin_instance, value_list->type_instance, value_list->plugin,
184 value_list->host, (double) value_list->time, value_list->interval);
186 ret = PyObject_CallFunctionObjArgs(c->callback, v, c->data, (void *) 0); /* New reference. */
188 cpy_log_exception("write callback");
196 static void cpy_log_callback(int severity, const char *message, user_data_t *data) {
197 cpy_callback_t * c = data->data;
202 ret = PyObject_CallFunction(c->callback, "is", severity, message); /* New reference. */
204 ret = PyObject_CallFunction(c->callback, "isO", severity, message, c->data); /* New reference. */
208 /* Do we really want to trigger a log callback because a log callback failed?
217 static PyObject *cpy_register_generic(cpy_callback_t **list_head, PyObject *args, PyObject *kwds) {
219 const char *name = NULL;
220 PyObject *callback = NULL, *data = NULL, *mod = NULL;
221 static char *kwlist[] = {"callback", "data", "name", NULL};
223 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
224 if (PyCallable_Check(callback) == 0) {
225 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
229 mod = PyObject_GetAttrString(callback, "__module__"); /* New reference. */
230 if (mod != NULL) name = PyString_AsString(mod);
233 PyErr_SetString(PyExc_ValueError, "No module name specified and "
234 "callback function does not have a \"__module__\" attribute.");
240 c = malloc(sizeof(*c));
241 c->name = strdup(name);
242 c->callback = callback;
244 c->next = *list_head;
250 static PyObject *cpy_register_config(PyObject *self, PyObject *args, PyObject *kwds) {
251 return cpy_register_generic(&cpy_config_callbacks, args, kwds);
254 static PyObject *cpy_register_init(PyObject *self, PyObject *args, PyObject *kwds) {
255 return cpy_register_generic(&cpy_init_callbacks, args, kwds);
258 typedef int reg_function_t(const char *name, void *callback, void *data);
260 static PyObject *cpy_register_generic_userdata(void *reg, void *handler, PyObject *args, PyObject *kwds) {
262 reg_function_t *register_function = (reg_function_t *) reg;
263 cpy_callback_t *c = NULL;
264 user_data_t *user_data = NULL;
265 const char *name = NULL;
266 PyObject *callback = NULL, *data = NULL;
267 static char *kwlist[] = {"callback", "data", "name", NULL};
269 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
270 if (PyCallable_Check(callback) == 0) {
271 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
274 cpy_build_name(buf, sizeof(buf), callback, name);
278 c = malloc(sizeof(*c));
279 c->name = strdup(buf);
280 c->callback = callback;
283 user_data = malloc(sizeof(*user_data));
284 user_data->free_func = cpy_destroy_user_data;
286 register_function(buf, handler, user_data);
287 return PyString_FromString(buf);
290 static PyObject *cpy_register_read(PyObject *self, PyObject *args, PyObject *kwds) {
292 cpy_callback_t *c = NULL;
293 user_data_t *user_data = NULL;
295 const char *name = NULL;
296 PyObject *callback = NULL, *data = NULL;
298 static char *kwlist[] = {"callback", "interval", "data", "name", NULL};
300 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|dOz", kwlist, &callback, &interval, &data, &name) == 0) return NULL;
301 if (PyCallable_Check(callback) == 0) {
302 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
305 cpy_build_name(buf, sizeof(buf), callback, name);
309 c = malloc(sizeof(*c));
310 c->name = strdup(buf);
311 c->callback = callback;
314 user_data = malloc(sizeof(*user_data));
315 user_data->free_func = cpy_destroy_user_data;
317 ts.tv_sec = interval;
318 ts.tv_nsec = (interval - ts.tv_sec) * 1000000000;
319 plugin_register_complex_read(buf, cpy_read_callback, &ts, user_data);
320 return PyString_FromString(buf);
323 static PyObject *cpy_register_log(PyObject *self, PyObject *args, PyObject *kwds) {
324 return cpy_register_generic_userdata(plugin_register_log, cpy_log_callback, args, kwds);
327 static PyObject *cpy_register_write(PyObject *self, PyObject *args, PyObject *kwds) {
328 return cpy_register_generic_userdata(plugin_register_write, cpy_write_callback, args, kwds);
331 static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args, PyObject *kwds) {
332 return cpy_register_generic(&cpy_shutdown_callbacks, args, kwds);
335 static PyObject *cpy_Error(PyObject *self, PyObject *args) {
337 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
338 Py_BEGIN_ALLOW_THREADS
339 plugin_log(LOG_ERR, "%s", text);
344 static PyObject *cpy_Warning(PyObject *self, PyObject *args) {
346 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
347 Py_BEGIN_ALLOW_THREADS
348 plugin_log(LOG_WARNING, "%s", text);
353 static PyObject *cpy_Notice(PyObject *self, PyObject *args) {
355 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
356 Py_BEGIN_ALLOW_THREADS
357 plugin_log(LOG_NOTICE, "%s", text);
362 static PyObject *cpy_Info(PyObject *self, PyObject *args) {
364 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
365 Py_BEGIN_ALLOW_THREADS
366 plugin_log(LOG_INFO, "%s", text);
371 static PyObject *cpy_Debug(PyObject *self, PyObject *args) {
374 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
375 plugin_log(LOG_DEBUG, "%s", text);
380 static PyMethodDef cpy_methods[] = {
381 {"debug", cpy_Debug, METH_VARARGS, "This is an unhelpful text."},
382 {"info", cpy_Info, METH_VARARGS, "This is an unhelpful text."},
383 {"notice", cpy_Notice, METH_VARARGS, "This is an unhelpful text."},
384 {"warning", cpy_Warning, METH_VARARGS, "This is an unhelpful text."},
385 {"error", cpy_Error, METH_VARARGS, "This is an unhelpful text."},
386 {"register_log", (PyCFunction) cpy_register_log, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
387 {"register_init", (PyCFunction) cpy_register_init, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
388 {"register_config", (PyCFunction) cpy_register_config, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
389 {"register_read", (PyCFunction) cpy_register_read, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
390 {"register_write", (PyCFunction) cpy_register_write, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
391 {"register_shutdown", (PyCFunction) cpy_register_shutdown, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
395 static int cpy_shutdown(void) {
399 /* This can happen if the module was loaded but not configured. */
401 PyEval_RestoreThread(state);
403 for (c = cpy_shutdown_callbacks; c; c = c->next) {
404 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
406 cpy_log_exception("shutdown callback");
414 static void *cpy_interactive(void *data) {
416 if (PyImport_ImportModule("readline") == NULL) {
417 /* This interactive session will suck. */
418 cpy_log_exception("interactive session init");
420 PyRun_InteractiveLoop(stdin, "<stdin>");
422 NOTICE("python: Interactive interpreter exited, stopping collectd ...");
427 static int cpy_init(void) {
430 static pthread_t thread;
432 PyEval_InitThreads();
433 /* Now it's finally OK to use python threads. */
434 for (c = cpy_init_callbacks; c; c = c->next) {
435 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
437 cpy_log_exception("init callback");
441 state = PyEval_SaveThread();
442 if (do_interactive) {
443 if (pthread_create(&thread, NULL, cpy_interactive, NULL)) {
444 ERROR("python: Error creating thread for interactive interpreter.");
451 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
453 PyObject *item, *values, *children, *tmp;
458 values = PyTuple_New(ci->values_num); /* New reference. */
459 for (i = 0; i < ci->values_num; ++i) {
460 if (ci->values[i].type == OCONFIG_TYPE_STRING) {
461 PyTuple_SET_ITEM(values, i, PyString_FromString(ci->values[i].value.string));
462 } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
463 PyTuple_SET_ITEM(values, i, PyFloat_FromDouble(ci->values[i].value.number));
464 } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
465 PyTuple_SET_ITEM(values, i, PyBool_FromLong(ci->values[i].value.boolean));
469 item = PyObject_CallFunction((PyObject *) &ConfigType, "sONO", ci->key, parent, values, Py_None);
472 children = PyTuple_New(ci->children_num); /* New reference. */
473 for (i = 0; i < ci->children_num; ++i) {
474 PyTuple_SET_ITEM(children, i, cpy_oconfig_to_pyconfig(ci->children + i, item));
476 tmp = ((Config *) item)->children;
477 ((Config *) item)->children = children;
482 static int cpy_config(oconfig_item_t *ci) {
488 /* Ok in theory we shouldn't do initialization at this point
489 * but we have to. In order to give python scripts a chance
490 * to register a config callback we need to be able to execute
491 * python code during the config callback so we have to start
492 * the interpreter here. */
493 /* Do *not* use the python "thread" module at this point! */
496 PyType_Ready(&ConfigType);
497 PyType_Ready(&ValuesType);
498 sys = PyImport_ImportModule("sys"); /* New reference. */
500 cpy_log_exception("python initialization");
503 sys_path = PyObject_GetAttrString(sys, "path"); /* New reference. */
505 if (sys_path == NULL) {
506 cpy_log_exception("python initialization");
509 module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
510 PyModule_AddObject(module, "Config", (PyObject *) &ConfigType); /* Steals a reference. */
511 PyModule_AddObject(module, "Values", (PyObject *) &ValuesType); /* Steals a reference. */
512 PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG);
513 PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO);
514 PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE);
515 PyModule_AddIntConstant(module, "LOG_WARNING", LOG_WARNING);
516 PyModule_AddIntConstant(module, "LOG_ERROR", LOG_ERR);
517 for (i = 0; i < ci->children_num; ++i) {
518 oconfig_item_t *item = ci->children + i;
520 if (strcasecmp(item->key, "Interactive") == 0) {
521 if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_BOOLEAN)
523 do_interactive = item->values[0].value.boolean;
524 } else if (strcasecmp(item->key, "LogTraces") == 0) {
525 if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_BOOLEAN)
527 if (!item->values[0].value.boolean) {
528 Py_XDECREF(cpy_format_exception);
529 cpy_format_exception = NULL;
532 if (cpy_format_exception)
534 tb = PyImport_ImportModule("traceback"); /* New reference. */
536 cpy_log_exception("python initialization");
539 cpy_format_exception = PyObject_GetAttrString(tb, "format_exception"); /* New reference. */
541 if (cpy_format_exception == NULL)
542 cpy_log_exception("python initialization");
543 } else if (strcasecmp(item->key, "ModulePath") == 0) {
545 PyObject *dir_object;
547 if (cf_util_get_string(item, &dir) != 0)
549 dir_object = PyString_FromString(dir); /* New reference. */
550 if (dir_object == NULL) {
551 ERROR("python plugin: Unable to convert \"%s\" to "
552 "a python object.", dir);
554 cpy_log_exception("python initialization");
557 if (PyList_Append(sys_path, dir_object) != 0) {
558 ERROR("python plugin: Unable to append \"%s\" to "
559 "python module path.", dir);
560 cpy_log_exception("python initialization");
562 Py_DECREF(dir_object);
564 } else if (strcasecmp(item->key, "Import") == 0) {
565 char *module_name = NULL;
568 if (cf_util_get_string(item, &module_name) != 0)
570 module = PyImport_ImportModule(module_name); /* New reference. */
571 if (module == NULL) {
572 ERROR("python plugin: Error importing module \"%s\".", module_name);
573 cpy_log_exception("python initialization");
578 } else if (strcasecmp(item->key, "Module") == 0) {
583 if (cf_util_get_string(item, &name) != 0)
585 for (c = cpy_config_callbacks; c; c = c->next) {
586 if (strcasecmp(c->name, name) == 0)
590 WARNING("python plugin: Found a configuration for the \"%s\" plugin, "
591 "but the plugin isn't loaded or didn't register "
592 "a configuration callback.", name);
598 ret = PyObject_CallFunction(c->callback, "N",
599 cpy_oconfig_to_pyconfig(item, NULL)); /* New reference. */
601 ret = PyObject_CallFunction(c->callback, "NO",
602 cpy_oconfig_to_pyconfig(item, NULL), c->data); /* New reference. */
604 cpy_log_exception("loading module");
608 WARNING("python plugin: Ignoring unknown config key \"%s\".", item->key);
615 void module_register(void) {
616 plugin_register_complex_config("python", cpy_config);
617 plugin_register_init("python", cpy_init);
618 // plugin_register_read("python", cna_read);
619 plugin_register_shutdown("python", cpy_shutdown);