2 #include <structmember.h>
7 typedef struct cpy_callback_s {
11 struct cpy_callback_s *next;
14 /* This is our global thread state. Python saves some stuff in thread-local
15 * storage. So if we allow the interpreter to run in the background
16 * (the scriptwriters might have created some threads from python), we have
17 * to save the state so we can resume it later after shutdown. */
19 static PyThreadState *state;
21 /* Serves the same purpose as PyEval_ThreadsInitialized but doesn't require
24 static int cpy_have_threads;
26 /* These two macros are basicly Py_BEGIN_ALLOW_THREADS and Py_BEGIN_ALLOW_THREADS
27 * from the other direction. If a Python thread calls a C function
28 * Py_BEGIN_ALLOW_THREADS is used to allow other python threads to run because
29 * we don't intend to call any Python functions.
31 * These two macros are used whenever a C thread intends to call some Python
32 * function, usually because some registered callback was triggered.
33 * Just like Py_BEGIN_ALLOW_THREADS it opens a block so these macros have to be
34 * used in pairs. They aquire the GIL, create a new Python thread state and swap
35 * the current thread state with the new one. This means this thread is now allowed
36 * to execute Python code. */
38 #define CPY_LOCK_THREADS {\
39 PyGILState_STATE gil_state;\
40 if (cpy_have_threads)\
41 gil_state = PyGILState_Ensure();
43 #define CPY_RELEASE_THREADS \
44 if (cpy_have_threads)\
45 PyGILState_Release(gil_state);\
49 static cpy_callback_t *cpy_config_callbacks;
50 static cpy_callback_t *cpy_init_callbacks;
51 static cpy_callback_t *cpy_shutdown_callbacks;
53 static void cpy_destroy_user_data(void *data) {
54 cpy_callback_t *c = data;
56 Py_DECREF(c->callback);
61 static void cpy_log_callback(int severity, const char *message, user_data_t *data) {
62 cpy_callback_t * c = data->data;
67 ret = PyObject_CallFunction(c->callback, "is", severity, message); /* New reference. */
69 ret = PyObject_CallFunction(c->callback, "isO", severity, message, c->data); /* New reference. */
81 PyObject_HEAD /* No semicolon! */
88 static void Config_dealloc(PyObject *s) {
89 Config *self = (Config *) s;
91 Py_XDECREF(self->parent);
92 Py_XDECREF(self->key);
93 Py_XDECREF(self->values);
94 Py_XDECREF(self->children);
95 self->ob_type->tp_free(s);
98 static PyObject *Config_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
101 self = (Config *) type->tp_alloc(type, 0);
108 self->children = NULL;
109 return (PyObject *) self;
112 static int Config_init(PyObject *s, PyObject *args, PyObject *kwds) {
113 PyObject *key = NULL, *parent = NULL, *values = NULL, *children = NULL, *tmp;
114 Config *self = (Config *) s;
115 static char *kwlist[] = {"key", "parent", "values", "children", NULL};
117 if (!PyArg_ParseTupleAndKeywords(args, kwds, "S|OOO", kwlist,
118 &key, &parent, &values, &children))
121 if (values == NULL) {
122 values = PyTuple_New(0);
125 if (children == NULL) {
126 children = PyTuple_New(0);
133 if (parent != NULL) {
136 self->parent = parent;
139 if (values != NULL) {
142 self->values = values;
145 if (children != NULL) {
146 tmp = self->children;
148 self->children = children;
154 static PyMemberDef Config_members[] = {
155 {"Parent", T_OBJECT, offsetof(Config, parent), 0, "Parent node"},
156 {"Key", T_OBJECT_EX, offsetof(Config, key), 0, "Keyword of this node"},
157 {"Values", T_OBJECT_EX, offsetof(Config, values), 0, "Values after the key"},
158 {"Children", T_OBJECT_EX, offsetof(Config, children), 0, "Childnodes of this node"},
162 static PyTypeObject ConfigType = {
163 PyObject_HEAD_INIT(NULL)
165 "collectd.Config", /* tp_name */
166 sizeof(Config), /* tp_basicsize */
167 0, /* Will be filled in later */
168 Config_dealloc, /* tp_dealloc */
174 0, /* tp_as_number */
175 0, /* tp_as_sequence */
176 0, /* tp_as_mapping */
182 0, /* tp_as_buffer */
183 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
184 "Cool help text later", /* tp_doc */
187 0, /* tp_richcompare */
188 0, /* tp_weaklistoffset */
192 Config_members, /* tp_members */
196 0, /* tp_descr_get */
197 0, /* tp_descr_set */
198 0, /* tp_dictoffset */
199 Config_init, /* tp_init */
201 Config_new /* tp_new */
204 static PyObject *cpy_register_generic(cpy_callback_t **list_head, PyObject *args, PyObject *kwds) {
206 const char *name = NULL;
207 PyObject *callback = NULL, *data = NULL;
208 static char *kwlist[] = {"callback", "data", "name", NULL};
210 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
211 if (PyCallable_Check(callback) == 0) {
212 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
218 mod = PyObject_GetAttrString(callback, "__module__");
219 if (mod != NULL) name = PyString_AsString(mod);
221 PyErr_SetString(PyExc_ValueError, "No module name specified and "
222 "callback function does not have a \"__module__\" attribute.");
228 c = malloc(sizeof(*c));
229 c->name = strdup(name);
230 c->callback = callback;
232 c->next = *list_head;
237 static PyObject *cpy_register_config(PyObject *self, PyObject *args, PyObject *kwds) {
238 return cpy_register_generic(&cpy_config_callbacks, args, kwds);
241 static PyObject *cpy_register_init(PyObject *self, PyObject *args, PyObject *kwds) {
242 return cpy_register_generic(&cpy_init_callbacks, args, kwds);
245 static PyObject *cpy_register_log(PyObject *self, PyObject *args, PyObject *kwds) {
246 cpy_callback_t *c = NULL;
247 user_data_t *user_data = NULL;
248 const char *name = NULL;
249 PyObject *callback = NULL, *data = NULL;
250 static char *kwlist[] = {"callback", "data", "name", NULL};
252 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
253 if (PyCallable_Check(callback) == 0) {
254 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
260 mod = PyObject_GetAttrString(callback, "__module__");
261 if (mod != NULL) name = PyString_AsString(mod);
263 PyErr_SetString(PyExc_ValueError, "No module name specified and "
264 "callback function does not have a \"__module__\" attribute.");
270 c = malloc(sizeof(*c));
271 c->name = strdup(name);
272 c->callback = callback;
275 user_data = malloc(sizeof(*user_data));
276 user_data->free_func = cpy_destroy_user_data;
278 plugin_register_log(name, cpy_log_callback, user_data);
282 static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args, PyObject *kwds) {
283 return cpy_register_generic(&cpy_shutdown_callbacks, args, kwds);
286 static PyObject *cpy_Error(PyObject *self, PyObject *args) {
288 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
289 Py_BEGIN_ALLOW_THREADS
290 plugin_log(LOG_ERR, "%s", text);
295 static PyObject *cpy_Warning(PyObject *self, PyObject *args) {
297 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
298 Py_BEGIN_ALLOW_THREADS
299 plugin_log(LOG_WARNING, "%s", text);
304 static PyObject *cpy_Notice(PyObject *self, PyObject *args) {
306 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
307 Py_BEGIN_ALLOW_THREADS
308 plugin_log(LOG_NOTICE, "%s", text);
313 static PyObject *cpy_Info(PyObject *self, PyObject *args) {
315 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
316 Py_BEGIN_ALLOW_THREADS
317 plugin_log(LOG_INFO, "%s", text);
322 static PyObject *cpy_Debug(PyObject *self, PyObject *args) {
325 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
326 plugin_log(LOG_DEBUG, "%s", text);
331 static PyMethodDef cpy_methods[] = {
332 {"Debug", cpy_Debug, METH_VARARGS, "This is an unhelpful text."},
333 {"Info", cpy_Info, METH_VARARGS, "This is an unhelpful text."},
334 {"Notice", cpy_Notice, METH_VARARGS, "This is an unhelpful text."},
335 {"Warning", cpy_Warning, METH_VARARGS, "This is an unhelpful text."},
336 {"Error", cpy_Error, METH_VARARGS, "This is an unhelpful text."},
337 {"register_log", (PyCFunction) cpy_register_log, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
338 {"register_init", (PyCFunction) cpy_register_init, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
339 {"register_config", (PyCFunction) cpy_register_config, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
340 {"register_shutdown", (PyCFunction) cpy_register_shutdown, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."},
344 static int cpy_shutdown(void) {
348 /* This can happen if the module was loaded but not configured. */
350 PyEval_RestoreThread(state);
352 for (c = cpy_shutdown_callbacks; c; c = c->next) {
354 ret = PyObject_CallObject(c->callback, NULL); /* New reference. */
356 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
358 PyErr_Print(); /* FIXME */
366 static int cpy_init(void) {
370 PyEval_InitThreads();
371 cpy_have_threads = 1;
372 /* Now it's finally OK to use python threads. */
373 for (c = cpy_init_callbacks; c; c = c->next) {
375 ret = PyObject_CallObject(c->callback, NULL); /* New reference. */
377 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
379 PyErr_Print(); /* FIXME */
383 state = PyEval_SaveThread();
387 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
389 PyObject *item, *values, *children, *tmp;
394 values = PyTuple_New(ci->values_num); /* New reference. */
395 for (i = 0; i < ci->values_num; ++i) {
396 if (ci->values[i].type == OCONFIG_TYPE_STRING) {
397 PyTuple_SET_ITEM(values, i, PyString_FromString(ci->values[i].value.string));
398 } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
399 PyTuple_SET_ITEM(values, i, PyFloat_FromDouble(ci->values[i].value.number));
400 } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
401 PyTuple_SET_ITEM(values, i, PyBool_FromLong(ci->values[i].value.boolean));
405 item = PyObject_CallFunction((PyObject *) &ConfigType, "sONO", ci->key, parent, values, Py_None);
408 children = PyTuple_New(ci->children_num); /* New reference. */
409 for (i = 0; i < ci->children_num; ++i) {
410 PyTuple_SET_ITEM(children, i, cpy_oconfig_to_pyconfig(ci->children + i, item));
412 tmp = ((Config *) item)->children;
413 ((Config *) item)->children = children;
418 static int cpy_config(oconfig_item_t *ci) {
424 /* Ok in theory we shouldn't do initialization at this point
425 * but we have to. In order to give python scripts a chance
426 * to register a config callback we need to be able to execute
427 * python code during the config callback so we have to start
428 * the interpreter here. */
429 /* Do *not* use the python "thread" module at this point! */
432 PyType_Ready(&ConfigType);
433 sys = PyImport_ImportModule("sys"); /* New reference. */
435 ERROR("python module: Unable to import \"sys\" module.");
436 /* Just print the default python exception text to stderr. */
440 sys_path = PyObject_GetAttrString(sys, "path"); /* New reference. */
442 if (sys_path == NULL) {
443 ERROR("python module: Unable to read \"sys.path\".");
447 module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
448 PyModule_AddObject(module, "Config", (PyObject *) &ConfigType); /* Steals a reference. */
449 PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG);
450 PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO);
451 PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE);
452 PyModule_AddIntConstant(module, "LOG_WARNING", LOG_WARNING);
453 PyModule_AddIntConstant(module, "LOG_ERROR", LOG_ERR);
454 for (i = 0; i < ci->children_num; ++i) {
455 oconfig_item_t *item = ci->children + i;
457 if (strcasecmp(item->key, "ModulePath") == 0) {
459 PyObject *dir_object;
461 if (cf_util_get_string(item, &dir) != 0)
463 dir_object = PyString_FromString(dir); /* New reference. */
464 if (dir_object == NULL) {
465 ERROR("python plugin: Unable to convert \"%s\" to "
466 "a python object.", dir);
471 if (PyList_Append(sys_path, dir_object) != 0) {
472 ERROR("python plugin: Unable to append \"%s\" to "
473 "python module path.", dir);
476 Py_DECREF(dir_object);
478 } else if (strcasecmp(item->key, "Import") == 0) {
479 char *module_name = NULL;
482 if (cf_util_get_string(item, &module_name) != 0)
484 module = PyImport_ImportModule(module_name); /* New reference. */
485 if (module == NULL) {
486 ERROR("python plugin: Error importing module \"%s\".", module_name);
491 } else if (strcasecmp(item->key, "Module") == 0) {
496 if (cf_util_get_string(item, &name) != 0)
498 for (c = cpy_config_callbacks; c; c = c->next) {
499 if (strcasecmp(c->name, name) == 0)
503 WARNING("python plugin: Found a configuration for the \"%s\" plugin, "
504 "but the plugin isn't loaded or didn't register "
505 "a configuration callback.", name);
511 ret = PyObject_CallFunction(c->callback, "N",
512 cpy_oconfig_to_pyconfig(item, NULL)); /* New reference. */
514 ret = PyObject_CallFunction(c->callback, "NO",
515 cpy_oconfig_to_pyconfig(item, NULL), c->data); /* New reference. */
521 WARNING("python plugin: Ignoring unknown config key \"%s\".", item->key);
528 void module_register(void) {
529 plugin_register_complex_config("python", cpy_config);
530 plugin_register_init("python", cpy_init);
531 // plugin_register_read("python", cna_read);
532 plugin_register_shutdown("python", cpy_shutdown);