2 #include <structmember.h>
9 typedef struct cpy_callback_s {
13 struct cpy_callback_s *next;
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;
30 Py_DECREF(c->callback);
35 static void cpy_log_callback(int severity, const char *message, user_data_t *data) {
36 cpy_callback_t * c = data->data;
41 ret = PyObject_CallFunction(c->callback, "is", severity, message); /* New reference. */
43 ret = PyObject_CallFunction(c->callback, "isO", severity, message, c->data); /* New reference. */
54 static PyObject *cpy_register_generic(cpy_callback_t **list_head, PyObject *args, PyObject *kwds) {
56 const char *name = NULL;
57 PyObject *callback = NULL, *data = NULL;
58 static char *kwlist[] = {"callback", "data", "name", NULL};
60 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
61 if (PyCallable_Check(callback) == 0) {
62 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
68 mod = PyObject_GetAttrString(callback, "__module__");
69 if (mod != NULL) name = PyString_AsString(mod);
71 PyErr_SetString(PyExc_ValueError, "No module name specified and "
72 "callback function does not have a \"__module__\" attribute.");
78 c = malloc(sizeof(*c));
79 c->name = strdup(name);
80 c->callback = callback;
87 static PyObject *cpy_register_config(PyObject *self, PyObject *args, PyObject *kwds) {
88 return cpy_register_generic(&cpy_config_callbacks, args, kwds);
91 static PyObject *cpy_register_init(PyObject *self, PyObject *args, PyObject *kwds) {
92 return cpy_register_generic(&cpy_init_callbacks, args, kwds);
95 static PyObject *cpy_register_log(PyObject *self, PyObject *args, PyObject *kwds) {
96 cpy_callback_t *c = NULL;
97 user_data_t *user_data = NULL;
98 const char *name = NULL;
99 PyObject *callback = NULL, *data = NULL;
100 static char *kwlist[] = {"callback", "data", "name", NULL};
102 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
103 if (PyCallable_Check(callback) == 0) {
104 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
110 mod = PyObject_GetAttrString(callback, "__module__");
111 if (mod != NULL) name = PyString_AsString(mod);
113 PyErr_SetString(PyExc_ValueError, "No module name specified and "
114 "callback function does not have a \"__module__\" attribute.");
120 c = malloc(sizeof(*c));
121 c->name = strdup(name);
122 c->callback = callback;
125 user_data = malloc(sizeof(*user_data));
126 user_data->free_func = cpy_destroy_user_data;
128 plugin_register_log(name, cpy_log_callback, user_data);
132 static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args, PyObject *kwds) {
133 return cpy_register_generic(&cpy_shutdown_callbacks, args, kwds);
136 static PyObject *cpy_Error(PyObject *self, PyObject *args) {
138 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
139 Py_BEGIN_ALLOW_THREADS
140 plugin_log(LOG_ERR, "%s", text);
145 static PyObject *cpy_Warning(PyObject *self, PyObject *args) {
147 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
148 Py_BEGIN_ALLOW_THREADS
149 plugin_log(LOG_WARNING, "%s", text);
154 static PyObject *cpy_Notice(PyObject *self, PyObject *args) {
156 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
157 Py_BEGIN_ALLOW_THREADS
158 plugin_log(LOG_NOTICE, "%s", text);
163 static PyObject *cpy_Info(PyObject *self, PyObject *args) {
165 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
166 Py_BEGIN_ALLOW_THREADS
167 plugin_log(LOG_INFO, "%s", text);
172 static PyObject *cpy_Debug(PyObject *self, PyObject *args) {
175 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
176 plugin_log(LOG_DEBUG, "%s", text);
181 static PyMethodDef cpy_methods[] = {
182 {"Debug", cpy_Debug, METH_VARARGS, "This is an unhelpful text."},
183 {"Info", cpy_Info, METH_VARARGS, "This is an unhelpful text."},
184 {"Notice", cpy_Notice, METH_VARARGS, "This is an unhelpful text."},
185 {"Warning", cpy_Warning, METH_VARARGS, "This is an unhelpful text."},
186 {"Error", cpy_Error, METH_VARARGS, "This is an unhelpful text."},
187 {"register_log", (PyCFunction) cpy_register_log, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
188 {"register_init", (PyCFunction) cpy_register_init, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
189 {"register_config", (PyCFunction) cpy_register_config, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
190 {"register_shutdown", (PyCFunction) cpy_register_shutdown, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
194 static int cpy_shutdown(void) {
198 /* This can happen if the module was loaded but not configured. */
200 PyEval_RestoreThread(state);
202 for (c = cpy_shutdown_callbacks; c; c = c->next) {
204 ret = PyObject_CallObject(c->callback, NULL); /* New reference. */
206 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
208 PyErr_Print(); /* FIXME */
216 static int cpy_init(void) {
220 PyEval_InitThreads();
221 /* Now it's finally OK to use python threads. */
222 for (c = cpy_init_callbacks; c; c = c->next) {
224 ret = PyObject_CallObject(c->callback, NULL); /* New reference. */
226 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
228 PyErr_Print(); /* FIXME */
232 state = PyEval_SaveThread();
236 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
238 PyObject *item, *values, *children, *tmp;
243 values = PyTuple_New(ci->values_num); /* New reference. */
244 for (i = 0; i < ci->values_num; ++i) {
245 if (ci->values[i].type == OCONFIG_TYPE_STRING) {
246 PyTuple_SET_ITEM(values, i, PyString_FromString(ci->values[i].value.string));
247 } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
248 PyTuple_SET_ITEM(values, i, PyFloat_FromDouble(ci->values[i].value.number));
249 } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
250 PyTuple_SET_ITEM(values, i, PyBool_FromLong(ci->values[i].value.boolean));
254 item = PyObject_CallFunction((PyObject *) &ConfigType, "sONO", ci->key, parent, values, Py_None);
257 children = PyTuple_New(ci->children_num); /* New reference. */
258 for (i = 0; i < ci->children_num; ++i) {
259 PyTuple_SET_ITEM(children, i, cpy_oconfig_to_pyconfig(ci->children + i, item));
261 tmp = ((Config *) item)->children;
262 ((Config *) item)->children = children;
267 static int cpy_config(oconfig_item_t *ci) {
273 /* Ok in theory we shouldn't do initialization at this point
274 * but we have to. In order to give python scripts a chance
275 * to register a config callback we need to be able to execute
276 * python code during the config callback so we have to start
277 * the interpreter here. */
278 /* Do *not* use the python "thread" module at this point! */
281 PyType_Ready(&ConfigType);
282 PyType_Ready(&ValuesType);
283 sys = PyImport_ImportModule("sys"); /* New reference. */
285 ERROR("python module: Unable to import \"sys\" module.");
286 /* Just print the default python exception text to stderr. */
290 sys_path = PyObject_GetAttrString(sys, "path"); /* New reference. */
292 if (sys_path == NULL) {
293 ERROR("python module: Unable to read \"sys.path\".");
297 module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
298 PyModule_AddObject(module, "Config", (PyObject *) &ConfigType); /* Steals a reference. */
299 PyModule_AddObject(module, "Values", (PyObject *) &ValuesType); /* Steals a reference. */
300 PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG);
301 PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO);
302 PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE);
303 PyModule_AddIntConstant(module, "LOG_WARNING", LOG_WARNING);
304 PyModule_AddIntConstant(module, "LOG_ERROR", LOG_ERR);
305 for (i = 0; i < ci->children_num; ++i) {
306 oconfig_item_t *item = ci->children + i;
308 if (strcasecmp(item->key, "ModulePath") == 0) {
310 PyObject *dir_object;
312 if (cf_util_get_string(item, &dir) != 0)
314 dir_object = PyString_FromString(dir); /* New reference. */
315 if (dir_object == NULL) {
316 ERROR("python plugin: Unable to convert \"%s\" to "
317 "a python object.", dir);
322 if (PyList_Append(sys_path, dir_object) != 0) {
323 ERROR("python plugin: Unable to append \"%s\" to "
324 "python module path.", dir);
327 Py_DECREF(dir_object);
329 } else if (strcasecmp(item->key, "Import") == 0) {
330 char *module_name = NULL;
333 if (cf_util_get_string(item, &module_name) != 0)
335 module = PyImport_ImportModule(module_name); /* New reference. */
336 if (module == NULL) {
337 ERROR("python plugin: Error importing module \"%s\".", module_name);
342 } else if (strcasecmp(item->key, "Module") == 0) {
347 if (cf_util_get_string(item, &name) != 0)
349 for (c = cpy_config_callbacks; c; c = c->next) {
350 if (strcasecmp(c->name, name) == 0)
354 WARNING("python plugin: Found a configuration for the \"%s\" plugin, "
355 "but the plugin isn't loaded or didn't register "
356 "a configuration callback.", name);
362 ret = PyObject_CallFunction(c->callback, "N",
363 cpy_oconfig_to_pyconfig(item, NULL)); /* New reference. */
365 ret = PyObject_CallFunction(c->callback, "NO",
366 cpy_oconfig_to_pyconfig(item, NULL), c->data); /* New reference. */
372 WARNING("python plugin: Ignoring unknown config key \"%s\".", item->key);
379 void module_register(void) {
380 plugin_register_complex_config("python", cpy_config);
381 plugin_register_init("python", cpy_init);
382 // plugin_register_read("python", cna_read);
383 plugin_register_shutdown("python", cpy_shutdown);