6428a9476b8544bef56768550055b489c150329c
1 #include <Python.h>
2 #include <structmember.h>
4 #include "collectd.h"
5 #include "common.h"
7 typedef struct cpy_callback_s {
8 char *name;
9 PyObject *callback;
10 struct cpy_callback_s *next;
11 } cpy_callback_t;
13 /* This is our global thread state. Python saves some stuff in thread-local
14 * storage. So if we allow the interpreter to run in the background
15 * (the scriptwriters might have created some threads from python), we have
16 * to save the state so we can resume it later from a different thread.
18 * Technically the Global Interpreter Lock (GIL) and thread states can be
19 * manipulated independently. But to keep stuff from getting too complex
20 * we'll just use PyEval_SaveTread and PyEval_RestoreThreas which takes
21 * care of the thread states as well as the GIL. */
23 static PyThreadState *state;
25 static cpy_callback_t *cpy_config_callbacks;
27 typedef struct {
28 PyObject_HEAD /* No semicolon! */
29 PyObject *parent;
30 PyObject *key;
31 PyObject *values;
32 PyObject *children;
33 } Config;
35 static void Config_dealloc(PyObject *s) {
36 Config *self = (Config *) s;
38 Py_XDECREF(self->parent);
39 Py_XDECREF(self->key);
40 Py_XDECREF(self->values);
41 Py_XDECREF(self->children);
42 self->ob_type->tp_free(s);
43 }
45 static PyObject *Config_new(PyTypeObject *type, PyObject *args, PyObject *kwds) {
46 Config *self;
48 self = (Config *) type->tp_alloc(type, 0);
49 if (self == NULL)
50 return NULL;
52 self->parent = NULL;
53 self->key = NULL;
54 self->values = NULL;
55 self->children = NULL;
56 return (PyObject *) self;
57 }
59 static int Config_init(PyObject *s, PyObject *args, PyObject *kwds) {
60 PyObject *key = NULL, *parent = NULL, *values = NULL, *children = NULL, *tmp;
61 Config *self = (Config *) s;
62 static char *kwlist[] = {"key", "parent", "values", "children", NULL};
64 if (!PyArg_ParseTupleAndKeywords(args, kwds, "S|OOO", kwlist,
65 &key, &parent, &values, &children))
66 return -1;
68 if (values == NULL) {
69 values = PyTuple_New(0);
70 PyErr_Clear();
71 }
72 if (children == NULL) {
73 children = PyTuple_New(0);
74 PyErr_Clear();
75 }
76 tmp = self->key;
77 Py_INCREF(key);
78 self->key = key;
79 Py_XDECREF(tmp);
80 if (parent != NULL) {
81 tmp = self->parent;
82 Py_INCREF(parent);
83 self->parent = parent;
84 Py_XDECREF(tmp);
85 }
86 if (values != NULL) {
87 tmp = self->values;
88 Py_INCREF(values);
89 self->values = values;
90 Py_XDECREF(tmp);
91 }
92 if (children != NULL) {
93 tmp = self->children;
94 Py_INCREF(children);
95 self->children = children;
96 Py_XDECREF(tmp);
97 }
98 return 0;
99 }
101 static PyMemberDef Config_members[] = {
102 {"Parent", T_OBJECT, offsetof(Config, parent), 0, "Parent node"},
103 {"Key", T_OBJECT_EX, offsetof(Config, key), 0, "Keyword of this node"},
104 {"Values", T_OBJECT_EX, offsetof(Config, values), 0, "Values after the key"},
105 {"Children", T_OBJECT_EX, offsetof(Config, children), 0, "Childnodes of this node"},
106 {NULL}
107 };
109 static PyTypeObject ConfigType = {
110 PyObject_HEAD_INIT(NULL)
111 0, /* Always 0 */
112 "collectd.Config", /* tp_name */
113 sizeof(Config), /* tp_basicsize */
114 0, /* Will be filled in later */
115 Config_dealloc, /* tp_dealloc */
116 0, /* tp_print */
117 0, /* tp_getattr */
118 0, /* tp_setattr */
119 0, /* tp_compare */
120 0, /* tp_repr */
121 0, /* tp_as_number */
122 0, /* tp_as_sequence */
123 0, /* tp_as_mapping */
124 0, /* tp_hash */
125 0, /* tp_call */
126 0, /* tp_str */
127 0, /* tp_getattro */
128 0, /* tp_setattro */
129 0, /* tp_as_buffer */
130 Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
131 "Cool help text later", /* tp_doc */
132 0, /* tp_traverse */
133 0, /* tp_clear */
134 0, /* tp_richcompare */
135 0, /* tp_weaklistoffset */
136 0, /* tp_iter */
137 0, /* tp_iternext */
138 0, /* tp_methods */
139 Config_members, /* tp_members */
140 0, /* tp_getset */
141 0, /* tp_base */
142 0, /* tp_dict */
143 0, /* tp_descr_get */
144 0, /* tp_descr_set */
145 0, /* tp_dictoffset */
146 Config_init, /* tp_init */
147 0, /* tp_alloc */
148 Config_new /* tp_new */
149 };
151 static PyObject *cpy_register_config(PyObject *self, PyObject *args) {
152 cpy_callback_t *c;
153 const char *name = NULL;
154 PyObject *callback = NULL;
156 if (PyArg_ParseTuple(args, "O|z", &callback, &name) == 0) return NULL;
157 if (PyCallable_Check(callback) == 0) {
158 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
159 return 0;
160 }
161 if (name == NULL) {
162 PyObject *mod;
164 mod = PyObject_GetAttrString(callback, "__module__");
165 if (mod != NULL) name = PyString_AsString(mod);
166 if (name == NULL) {
167 PyErr_SetString(PyExc_ValueError, "No module name specified and "
168 "callback function does not have a \"__module__\" attribute.");
169 return 0;
170 }
171 }
172 c = malloc(sizeof(*c));
173 c->name = strdup(name);
174 c->callback = callback;
175 c->next = cpy_config_callbacks;
176 cpy_config_callbacks = c;
177 return Py_None;
178 }
180 static PyMethodDef cpy_methods[] = {
181 {"register_config", cpy_register_config, METH_VARARGS, "foo"},
182 {0, 0, 0, 0}
183 };
185 static int cpy_shutdown(void) {
186 /* This can happen if the module was loaded but not configured. */
187 if (state != NULL)
188 PyEval_RestoreThread(state);
189 Py_Finalize();
190 return 0;
191 }
193 static int cpy_init(void) {
194 PyEval_InitThreads();
195 /* Now it's finally OK to use python threads. */
196 return 0;
197 }
199 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
200 int i;
201 PyObject *item, *values, *children, *tmp;
203 if (parent == NULL)
204 parent = Py_None;
206 values = PyTuple_New(ci->values_num); /* New reference. */
207 for (i = 0; i < ci->values_num; ++i) {
208 if (ci->values[i].type == OCONFIG_TYPE_STRING) {
209 PyTuple_SET_ITEM(values, i, PyString_FromString(ci->values[i].value.string));
210 } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
211 PyTuple_SET_ITEM(values, i, PyFloat_FromDouble(ci->values[i].value.number));
212 } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
213 PyTuple_SET_ITEM(values, i, PyBool_FromLong(ci->values[i].value.boolean));
214 }
215 }
217 item = PyObject_CallFunction((PyObject *) &ConfigType, "sONO", ci->key, parent, values, Py_None);
218 if (item == NULL)
219 return NULL;
220 children = PyTuple_New(ci->children_num); /* New reference. */
221 for (i = 0; i < ci->children_num; ++i) {
222 PyTuple_SET_ITEM(children, i, cpy_oconfig_to_pyconfig(ci->children + i, item));
223 }
224 tmp = ((Config *) item)->children;
225 ((Config *) item)->children = children;
226 Py_XDECREF(tmp);
227 return item;
228 }
230 static int cpy_config(oconfig_item_t *ci) {
231 int i;
232 PyObject *sys;
233 PyObject *sys_path;
234 PyObject *module;
236 /* Ok in theory we shouldn't do initialization at this point
237 * but we have to. In order to give python scripts a chance
238 * to register a config callback we need to be able to execute
239 * python code during the config callback so we have to start
240 * the interpreter here. */
241 /* Do *not* use the python "thread" module at this point! */
242 Py_Initialize();
244 PyType_Ready(&ConfigType);
245 sys = PyImport_ImportModule("sys"); /* New reference. */
246 if (sys == NULL) {
247 ERROR("python module: Unable to import \"sys\" module.");
248 /* Just print the default python exception text to stderr. */
249 PyErr_Print();
250 return 1;
251 }
252 sys_path = PyObject_GetAttrString(sys, "path"); /* New reference. */
253 Py_DECREF(sys);
254 if (sys_path == NULL) {
255 ERROR("python module: Unable to read \"sys.path\".");
256 PyErr_Print();
257 return 1;
258 }
259 module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
260 PyModule_AddObject(module, "Config", (PyObject *) &ConfigType); /* Steals a reference. */
261 for (i = 0; i < ci->children_num; ++i) {
262 oconfig_item_t *item = ci->children + i;
264 if (strcasecmp(item->key, "ModulePath") == 0) {
265 char *dir = NULL;
266 PyObject *dir_object;
268 if (cf_util_get_string(item, &dir) != 0)
269 continue;
270 dir_object = PyString_FromString(dir); /* New reference. */
271 if (dir_object == NULL) {
272 ERROR("python plugin: Unable to convert \"%s\" to "
273 "a python object.", dir);
274 free(dir);
275 PyErr_Print();
276 continue;
277 }
278 if (PyList_Append(sys_path, dir_object) != 0) {
279 ERROR("python plugin: Unable to append \"%s\" to "
280 "python module path.", dir);
281 PyErr_Print();
282 }
283 Py_DECREF(dir_object);
284 free(dir);
285 } else if (strcasecmp(item->key, "Import") == 0) {
286 char *module_name = NULL;
287 PyObject *module;
289 if (cf_util_get_string(item, &module_name) != 0)
290 continue;
291 module = PyImport_ImportModule(module_name); /* New reference. */
292 if (module == NULL) {
293 ERROR("python plugin: Error importing module \"%s\".", module_name);
294 PyErr_Print();
295 }
296 free(module_name);
297 Py_XDECREF(module);
298 } else if (strcasecmp(item->key, "Module") == 0) {
299 char *name = NULL;
300 cpy_callback_t *c;
301 PyObject *ret;
303 if (cf_util_get_string(item, &name) != 0)
304 continue;
305 for (c = cpy_config_callbacks; c; c = c->next) {
306 if (strcasecmp(c->name, name) == 0)
307 break;
308 }
309 if (c == NULL) {
310 WARNING("python plugin: Found a configuration for the \"%s\" plugin, "
311 "but the plugin isn't loaded or didn't register "
312 "a configuration callback.", name);
313 free(name);
314 continue;
315 }
316 free(name);
317 ret = PyObject_CallFunction(c->callback, "N",
318 cpy_oconfig_to_pyconfig(item, NULL)); /* New reference. */
319 if (ret == NULL)
320 PyErr_Print();
321 else
322 Py_DECREF(ret);
323 } else {
324 WARNING("python plugin: Ignoring unknown config key \"%s\".", item->key);
325 }
326 }
327 Py_DECREF(sys_path);
328 return 0;
329 }
331 void module_register(void) {
332 plugin_register_complex_config("python", cpy_config);
333 plugin_register_init("python", cpy_init);
334 // plugin_register_read("netapp", cna_read);
335 plugin_register_shutdown("netapp", cpy_shutdown);
336 }