1 #include <Python.h>
2 #include <structmember.h>
4 #if HAVE_PTHREAD_H
5 # include <pthread.h>
6 #endif
8 #include "collectd.h"
9 #include "common.h"
11 #include "cpython.h"
13 typedef struct cpy_callback_s {
14 char *name;
15 PyObject *callback;
16 PyObject *data;
17 struct cpy_callback_s *next;
18 } cpy_callback_t;
20 static char log_doc[] = "This function sends a string to all logging plugins.";
22 static char flush_doc[] = "flush([plugin][, timeout][, identifier]) -> None\n"
23 "\n"
24 "Flushes the cache of another plugin.";
26 static char unregister_doc[] = "Unregisters a callback. This function needs exactly one parameter either\n"
27 "the function to unregister or the callback identifier to unregister.";
29 static char reg_log_doc[] = "register_log(callback[, data][, name]) -> identifier\n"
30 "\n"
31 "Register a callback function for log messages.\n"
32 "\n"
33 "'callback' is a callable object that will be called every time something\n"
34 " is logged.\n"
35 "'data' is an optional object that will be passed back to the callback\n"
36 " function every time it is called.\n"
37 "'name' is an optional identifier for this callback. The default name\n"
38 " is 'python.<module>.<name>'. If 'name' contains a '.' it\n"
39 " replaces both module and name, otherwise it replaces only name.\n"
40 " Every callback needs a unique identifier, so if you want to\n"
41 " register one function multiple time you need to specify a name\n"
42 " here.\n"
43 "'identifier' is the full identifier assigned to this callback.\n"
44 "\n"
45 "The callback function will be called with two or three parameters:\n"
46 "severity: An integer that should be compared to the LOG_ constants.\n"
47 "message: The text to be logged.\n"
48 "data: The optional data parameter passed to the register function.\n"
49 " If the parameter was obmitted it will be obmitted here, too.";
51 static char reg_init_doc[] = "register_init(callback[, data][, name]) -> identifier\n"
52 "\n"
53 "Register a callback function that will be executed once after the config.\n"
54 "file has been read, all plugins heve been loaded and the collectd has\n"
55 "forked into the backgroud.\n"
56 "\n"
57 "'callback' is a callable object that will be executed.\n"
58 "'data' is an optional object that will be passed back to the callback\n"
59 " function when it is called.\n"
60 "'name' is an optional identifier for this callback. The default name\n"
61 " is 'python.<module>.<name>'. If 'name' contains a '.' it\n"
62 " replaces both module and name, otherwise it replaces only name.\n"
63 " Every callback needs a unique identifier, so if you want to\n"
64 " register one function multiple time you need to specify a name\n"
65 " here.\n"
66 "'identifier' is the full identifier assigned to this callback.\n"
67 "\n"
68 "The callback function will be called without parameters, except for\n"
69 "data if it was supplied.";
71 static char reg_config_doc[] = "register_config(callback[, data][, name]) -> identifier\n"
72 "\n"
73 "Register a callback function for config file entries.\n"
74 "'callback' is a callable object that will be called for every config block.\n"
75 "'data' is an optional object that will be passed back to the callback\n"
76 " function every time it is called.\n"
77 "'name' is an optional identifier for this callback. The default name\n"
78 " is 'python.<module>'. Every callback needs a unique identifier,\n"
79 " so if you want to register one function multiple time you need to\n"
80 " specify a name here.\n"
81 "'identifier' is the full identifier assigned to this callback.\n"
82 "\n"
83 "The callback function will be called with one or two parameters:\n"
84 "config: A Config object.\n"
85 "data: The optional data parameter passed to the register function.\n"
86 " If the parameter was obmitted it will be obmitted here, too.";
88 static char reg_read_doc[] = "register_read(callback[, interval][, data][, name]) -> identifier\n"
89 "\n"
90 "Register a callback function for reading data. It will just be called\n"
91 "in a fixed interval to signal that it's time to dispatch new values.\n"
92 "'callback' is a callable object that will be called every time something\n"
93 " is logged.\n"
94 "'interval' is the number of seconds between between calls to the callback\n"
95 " function. Full float precision is supported here.\n"
96 "'data' is an optional object that will be passed back to the callback\n"
97 " function every time it is called.\n"
98 "'name' is an optional identifier for this callback. The default name\n"
99 " is 'python.<module>.<name>'. If 'name' contains a '.' it\n"
100 " replaces both module and name, otherwise it replaces only name.\n"
101 " Every callback needs a unique identifier, so if you want to\n"
102 " register one function multiple time you need to specify a name\n"
103 " here.\n"
104 "'identifier' is the full identifier assigned to this callback.\n"
105 "\n"
106 "The callback function will be called without parameters, except for\n"
107 "data if it was supplied.";
109 static char reg_write_doc[] = "register_write(callback[, data][, name]) -> identifier\n"
110 "\n"
111 "Register a callback function to receive values dispatched by other plugins.\n"
112 "'callback' is a callable object that will be called every time a value\n"
113 " is dispatched.\n"
114 "'data' is an optional object that will be passed back to the callback\n"
115 " function every time it is called.\n"
116 "'name' is an optional identifier for this callback. The default name\n"
117 " is 'python.<module>.<name>'. If 'name' contains a '.' it\n"
118 " replaces both module and name, otherwise it replaces only name.\n"
119 " Every callback needs a unique identifier, so if you want to\n"
120 " register one function multiple time you need to specify a name\n"
121 " here.\n"
122 "'identifier' is the full identifier assigned to this callback.\n"
123 "\n"
124 "The callback function will be called with one or two parameters:\n"
125 "values: A Values object which is a copy of the dispatched values.\n"
126 "data: The optional data parameter passed to the register function.\n"
127 " If the parameter was obmitted it will be obmitted here, too.";
129 static char reg_notification_doc[] = "register_notification(callback[, data][, name]) -> identifier\n"
130 "\n"
131 "Register a callback function for notifications.\n"
132 "'callback' is a callable object that will be called every time a notification\n"
133 " is dispatched.\n"
134 "'data' is an optional object that will be passed back to the callback\n"
135 " function every time it is called.\n"
136 "'name' is an optional identifier for this callback. The default name\n"
137 " is 'python.<module>.<name>'. If 'name' contains a '.' it\n"
138 " replaces both module and name, otherwise it replaces only name.\n"
139 " Every callback needs a unique identifier, so if you want to\n"
140 " register one function multiple time you need to specify a name\n"
141 " here.\n"
142 "'identifier' is the full identifier assigned to this callback.\n"
143 "\n"
144 "The callback function will be called with one or two parameters:\n"
145 "notification: A copy of the notification that was dispatched.\n"
146 "data: The optional data parameter passed to the register function.\n"
147 " If the parameter was obmitted it will be obmitted here, too.";
149 static char reg_flush_doc[] = "register_flush(callback[, data][, name]) -> identifier\n"
150 "\n"
151 "Register a callback function for flush messages.\n"
152 "'callback' is a callable object that will be called every time a plugin\n"
153 " requests a flush for either this or all plugins.\n"
154 "'data' is an optional object that will be passed back to the callback\n"
155 " function every time it is called.\n"
156 "'name' is an optional identifier for this callback. The default name\n"
157 " is 'python.<module>'. Every callback needs a unique identifier,\n"
158 " so if you want to register one function multiple time you need to\n"
159 " specify a name here.\n"
160 "'identifier' is the full identifier assigned to this callback.\n"
161 "\n"
162 "The callback function will be called with two or three parameters:\n"
163 "timeout: ???.\n"
164 "id: ???.\n"
165 "data: The optional data parameter passed to the register function.\n"
166 " If the parameter was obmitted it will be obmitted here, too.";
168 static char reg_shutdown_doc[] = "register_shutdown(callback[, data][, name]) -> identifier\n"
169 "\n"
170 "Register a callback function for collectd shutdown.\n"
171 "'callback' is a callable object that will be called once collectd is\n"
172 " shutting down.\n"
173 "'data' is an optional object that will be passed back to the callback\n"
174 " function if it is called.\n"
175 "'name' is an optional identifier for this callback. The default name\n"
176 " is 'python.<module>.<name>'. If 'name' contains a '.' it\n"
177 " replaces both module and name, otherwise it replaces only name.\n"
178 " Every callback needs a unique identifier, so if you want to\n"
179 " register one function multiple time you need to specify a name\n"
180 " here.\n"
181 "'identifier' is the full identifier assigned to this callback.\n"
182 "\n"
183 "The callback function will be called with no parameters except for\n"
184 " data if it was supplied.";
187 static int do_interactive = 0;
189 /* This is our global thread state. Python saves some stuff in thread-local
190 * storage. So if we allow the interpreter to run in the background
191 * (the scriptwriters might have created some threads from python), we have
192 * to save the state so we can resume it later after shutdown. */
194 static PyThreadState *state;
196 static PyObject *cpy_format_exception;
198 static cpy_callback_t *cpy_config_callbacks;
199 static cpy_callback_t *cpy_init_callbacks;
200 static cpy_callback_t *cpy_shutdown_callbacks;
202 static void cpy_destroy_user_data(void *data) {
203 cpy_callback_t *c = data;
204 free(c->name);
205 Py_DECREF(c->callback);
206 Py_XDECREF(c->data);
207 free(c);
208 }
210 /* You must hold the GIL to call this function!
211 * But if you managed to extract the callback parameter then you probably already do. */
213 static void cpy_build_name(char *buf, size_t size, PyObject *callback, const char *name, int short_name) {
214 const char *module;
215 PyObject *mod = NULL, *n = NULL;
217 if (name != NULL && (strchr(name, '.') != NULL || short_name)) {
218 snprintf(buf, size, "python.%s", name);
219 return;
220 }
222 mod = PyObject_GetAttrString(callback, "__module__"); /* New reference. */
223 if (mod != NULL)
224 module = PyString_AsString(mod);
225 else
226 module = "collectd";
228 if (short_name) {
229 snprintf(buf, size, "python.%s", module);
230 Py_XDECREF(mod);
231 return;
232 }
234 if (name != NULL) {
235 snprintf(buf, size, "python.%s.%s", module, name);
236 Py_XDECREF(mod);
237 return;
238 }
240 n = PyObject_GetAttrString(callback, "__name__"); /* New reference. */
241 if (n != NULL)
242 name = PyString_AsString(n);
244 if (name != NULL)
245 snprintf(buf, size, "python.%s.%s", module, name);
246 else
247 snprintf(buf, size, "python.%s.%p", module, callback);
248 Py_XDECREF(mod);
249 Py_XDECREF(n);
250 }
252 static void cpy_log_exception(const char *context) {
253 int l = 0, i;
254 const char *typename = NULL, *message = NULL;
255 PyObject *type, *value, *traceback, *tn, *m, *list;
257 PyErr_Fetch(&type, &value, &traceback);
258 PyErr_NormalizeException(&type, &value, &traceback);
259 if (type == NULL) return;
260 tn = PyObject_GetAttrString(type, "__name__"); /* New reference. */
261 m = PyObject_GetAttrString(value, "message"); /* New reference. */
262 if (tn != NULL)
263 typename = PyString_AsString(tn);
264 if (m != NULL)
265 message = PyString_AsString(m);
266 if (typename == NULL)
267 typename = "NamelessException";
268 if (message == NULL)
269 message = "N/A";
270 ERROR("Unhandled python exception in %s: %s: %s", context, typename, message);
271 Py_XDECREF(tn);
272 Py_XDECREF(m);
273 if (!cpy_format_exception) {
274 PyErr_Clear();
275 Py_XDECREF(type);
276 Py_XDECREF(value);
277 Py_XDECREF(traceback);
278 return;
279 }
280 if (!traceback) {
281 PyErr_Clear();
282 return;
283 }
284 list = PyObject_CallFunction(cpy_format_exception, "NNN", type, value, traceback); /* New reference. */
285 if (list)
286 l = PyObject_Length(list);
287 for (i = 0; i < l; ++i) {
288 char *s;
289 PyObject *line;
291 line = PyList_GET_ITEM(list, i); /* Borrowed reference. */
292 s = strdup(PyString_AsString(line));
293 Py_DECREF(line);
294 if (s[strlen(s) - 1] == '\n')
295 s[strlen(s) - 1] = 0;
296 ERROR("%s", s);
297 free(s);
298 }
299 Py_XDECREF(list);
300 PyErr_Clear();
301 }
303 static int cpy_read_callback(user_data_t *data) {
304 cpy_callback_t *c = data->data;
305 PyObject *ret;
307 CPY_LOCK_THREADS
308 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
309 if (ret == NULL) {
310 cpy_log_exception("read callback");
311 } else {
312 Py_DECREF(ret);
313 }
314 CPY_RELEASE_THREADS
315 return 0;
316 }
318 static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_list, user_data_t *data) {
319 int i;
320 cpy_callback_t *c = data->data;
321 PyObject *ret, *v, *list;
323 CPY_LOCK_THREADS
324 list = PyList_New(value_list->values_len); /* New reference. */
325 if (list == NULL) {
326 cpy_log_exception("write callback");
327 CPY_RETURN_FROM_THREADS 0;
328 }
329 for (i = 0; i < value_list->values_len; ++i) {
330 if (ds->ds->type == DS_TYPE_COUNTER) {
331 if ((long) value_list->values[i].counter == value_list->values[i].counter)
332 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].counter));
333 else
334 PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].counter));
335 } else if (ds->ds->type == DS_TYPE_GAUGE) {
336 PyList_SetItem(list, i, PyFloat_FromDouble(value_list->values[i].gauge));
337 } else if (ds->ds->type == DS_TYPE_DERIVE) {
338 if ((long) value_list->values[i].derive == value_list->values[i].derive)
339 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].derive));
340 else
341 PyList_SetItem(list, i, PyLong_FromLongLong(value_list->values[i].derive));
342 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
343 if ((long) value_list->values[i].absolute == value_list->values[i].absolute)
344 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].absolute));
345 else
346 PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].absolute));
347 } else {
348 ERROR("cpy_write_callback: Unknown value type %d.", ds->ds->type);
349 Py_DECREF(list);
350 CPY_RETURN_FROM_THREADS 0;
351 }
352 if (PyErr_Occurred() != NULL) {
353 cpy_log_exception("value building for write callback");
354 CPY_RETURN_FROM_THREADS 0;
355 }
356 }
357 v = PyObject_CallFunction((PyObject *) &ValuesType, "sOssssdi", value_list->type, list,
358 value_list->plugin_instance, value_list->type_instance, value_list->plugin,
359 value_list->host, (double) value_list->time, value_list->interval);
360 Py_DECREF(list);
361 ret = PyObject_CallFunctionObjArgs(c->callback, v, c->data, (void *) 0); /* New reference. */
362 if (ret == NULL) {
363 cpy_log_exception("write callback");
364 } else {
365 Py_DECREF(ret);
366 }
367 CPY_RELEASE_THREADS
368 return 0;
369 }
371 static int cpy_notification_callback(const notification_t *notification, user_data_t *data) {
372 cpy_callback_t *c = data->data;
373 PyObject *ret, *n;
375 CPY_LOCK_THREADS
376 n = PyObject_CallFunction((PyObject *) &NotificationType, "ssssssdi", notification->type, notification->message,
377 notification->plugin_instance, notification->type_instance, notification->plugin,
378 notification->host, (double) notification->time, notification->severity);
379 ret = PyObject_CallFunctionObjArgs(c->callback, n, c->data, (void *) 0); /* New reference. */
380 if (ret == NULL) {
381 cpy_log_exception("notification callback");
382 } else {
383 Py_DECREF(ret);
384 }
385 CPY_RELEASE_THREADS
386 return 0;
387 }
389 static void cpy_log_callback(int severity, const char *message, user_data_t *data) {
390 cpy_callback_t * c = data->data;
391 PyObject *ret;
393 CPY_LOCK_THREADS
394 if (c->data == NULL)
395 ret = PyObject_CallFunction(c->callback, "is", severity, message); /* New reference. */
396 else
397 ret = PyObject_CallFunction(c->callback, "isO", severity, message, c->data); /* New reference. */
399 if (ret == NULL) {
400 /* FIXME */
401 /* Do we really want to trigger a log callback because a log callback failed?
402 * Probably not. */
403 PyErr_Print();
404 } else {
405 Py_DECREF(ret);
406 }
407 CPY_RELEASE_THREADS
408 }
410 static void cpy_flush_callback(int timeout, const char *id, user_data_t *data) {
411 cpy_callback_t * c = data->data;
412 PyObject *ret;
414 CPY_LOCK_THREADS
415 if (c->data == NULL)
416 ret = PyObject_CallFunction(c->callback, "is", timeout, id); /* New reference. */
417 else
418 ret = PyObject_CallFunction(c->callback, "isO", timeout, id, c->data); /* New reference. */
420 if (ret == NULL) {
421 cpy_log_exception("flush callback");
422 } else {
423 Py_DECREF(ret);
424 }
425 CPY_RELEASE_THREADS
426 }
428 static PyObject *cpy_register_generic(cpy_callback_t **list_head, PyObject *args, PyObject *kwds, int short_name) {
429 char buf[512];
430 cpy_callback_t *c;
431 const char *name = NULL;
432 PyObject *callback = NULL, *data = NULL, *mod = NULL;
433 static char *kwlist[] = {"callback", "data", "name", NULL};
435 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
436 if (PyCallable_Check(callback) == 0) {
437 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
438 return NULL;
439 }
440 cpy_build_name(buf, sizeof(buf), callback, name, short_name);
442 Py_INCREF(callback);
443 Py_XINCREF(data);
444 c = malloc(sizeof(*c));
445 c->name = strdup(buf);
446 c->callback = callback;
447 c->data = data;
448 c->next = *list_head;
449 *list_head = c;
450 Py_XDECREF(mod);
451 return PyString_FromString(buf);
452 }
454 static PyObject *cpy_flush(cpy_callback_t **list_head, PyObject *args, PyObject *kwds) {
455 int timeout = -1;
456 const char *plugin = NULL, *identifier = NULL;
457 static char *kwlist[] = {"plugin", "timeout", "identifier", NULL};
459 if (PyArg_ParseTupleAndKeywords(args, kwds, "|ziz", kwlist, &plugin, &timeout, &identifier) == 0) return NULL;
460 Py_BEGIN_ALLOW_THREADS
461 plugin_flush(plugin, timeout, identifier);
462 Py_END_ALLOW_THREADS
463 Py_RETURN_NONE;
464 }
466 static PyObject *cpy_register_config(PyObject *self, PyObject *args, PyObject *kwds) {
467 return cpy_register_generic(&cpy_config_callbacks, args, kwds, 1);
468 }
470 static PyObject *cpy_register_init(PyObject *self, PyObject *args, PyObject *kwds) {
471 return cpy_register_generic(&cpy_init_callbacks, args, kwds, 0);
472 }
474 typedef int reg_function_t(const char *name, void *callback, void *data);
476 static PyObject *cpy_register_generic_userdata(void *reg, void *handler, PyObject *args, PyObject *kwds, int short_name) {
477 char buf[512];
478 reg_function_t *register_function = (reg_function_t *) reg;
479 cpy_callback_t *c = NULL;
480 user_data_t *user_data = NULL;
481 const char *name = NULL;
482 PyObject *callback = NULL, *data = NULL;
483 static char *kwlist[] = {"callback", "data", "name", NULL};
485 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
486 if (PyCallable_Check(callback) == 0) {
487 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
488 return NULL;
489 }
490 cpy_build_name(buf, sizeof(buf), callback, name, short_name);
492 Py_INCREF(callback);
493 Py_XINCREF(data);
494 c = malloc(sizeof(*c));
495 c->name = strdup(buf);
496 c->callback = callback;
497 c->data = data;
498 c->next = NULL;
499 user_data = malloc(sizeof(*user_data));
500 user_data->free_func = cpy_destroy_user_data;
501 user_data->data = c;
502 register_function(buf, handler, user_data);
503 return PyString_FromString(buf);
504 }
506 static PyObject *cpy_register_read(PyObject *self, PyObject *args, PyObject *kwds) {
507 char buf[512];
508 cpy_callback_t *c = NULL;
509 user_data_t *user_data = NULL;
510 double interval = 0;
511 const char *name = NULL;
512 PyObject *callback = NULL, *data = NULL;
513 struct timespec ts;
514 static char *kwlist[] = {"callback", "interval", "data", "name", NULL};
516 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|dOz", kwlist, &callback, &interval, &data, &name) == 0) return NULL;
517 if (PyCallable_Check(callback) == 0) {
518 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
519 return NULL;
520 }
521 cpy_build_name(buf, sizeof(buf), callback, name, 0);
523 Py_INCREF(callback);
524 Py_XINCREF(data);
525 c = malloc(sizeof(*c));
526 c->name = strdup(buf);
527 c->callback = callback;
528 c->data = data;
529 c->next = NULL;
530 user_data = malloc(sizeof(*user_data));
531 user_data->free_func = cpy_destroy_user_data;
532 user_data->data = c;
533 ts.tv_sec = interval;
534 ts.tv_nsec = (interval - ts.tv_sec) * 1000000000;
535 plugin_register_complex_read(buf, cpy_read_callback, &ts, user_data);
536 return PyString_FromString(buf);
537 }
539 static PyObject *cpy_register_log(PyObject *self, PyObject *args, PyObject *kwds) {
540 return cpy_register_generic_userdata(plugin_register_log, cpy_log_callback, args, kwds, 0);
541 }
543 static PyObject *cpy_register_write(PyObject *self, PyObject *args, PyObject *kwds) {
544 return cpy_register_generic_userdata(plugin_register_write, cpy_write_callback, args, kwds, 0);
545 }
547 static PyObject *cpy_register_notification(PyObject *self, PyObject *args, PyObject *kwds) {
548 return cpy_register_generic_userdata(plugin_register_notification, cpy_notification_callback, args, kwds, 0);
549 }
551 static PyObject *cpy_register_flush(PyObject *self, PyObject *args, PyObject *kwds) {
552 return cpy_register_generic_userdata(plugin_register_flush, cpy_flush_callback, args, kwds, 1);
553 }
555 static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args, PyObject *kwds) {
556 return cpy_register_generic(&cpy_shutdown_callbacks, args, kwds, 0);
557 }
559 static PyObject *cpy_error(PyObject *self, PyObject *args) {
560 const char *text;
561 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
562 Py_BEGIN_ALLOW_THREADS
563 plugin_log(LOG_ERR, "%s", text);
564 Py_END_ALLOW_THREADS
565 Py_RETURN_NONE;
566 }
568 static PyObject *cpy_warning(PyObject *self, PyObject *args) {
569 const char *text;
570 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
571 Py_BEGIN_ALLOW_THREADS
572 plugin_log(LOG_WARNING, "%s", text);
573 Py_END_ALLOW_THREADS
574 Py_RETURN_NONE;
575 }
577 static PyObject *cpy_notice(PyObject *self, PyObject *args) {
578 const char *text;
579 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
580 Py_BEGIN_ALLOW_THREADS
581 plugin_log(LOG_NOTICE, "%s", text);
582 Py_END_ALLOW_THREADS
583 Py_RETURN_NONE;
584 }
586 static PyObject *cpy_info(PyObject *self, PyObject *args) {
587 const char *text;
588 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
589 Py_BEGIN_ALLOW_THREADS
590 plugin_log(LOG_INFO, "%s", text);
591 Py_END_ALLOW_THREADS
592 Py_RETURN_NONE;
593 }
595 static PyObject *cpy_debug(PyObject *self, PyObject *args) {
596 #ifdef COLLECT_DEBUG
597 const char *text;
598 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
599 plugin_log(LOG_DEBUG, "%s", text);
600 #endif
601 Py_RETURN_NONE;
602 }
604 static PyObject *cpy_unregister_generic(cpy_callback_t **list_head, PyObject *arg, const char *desc, int short_name) {
605 char buf[512];
606 const char *name;
607 cpy_callback_t *prev = NULL, *tmp;
609 if (PyString_Check(arg)) {
610 name = PyString_AsString(arg);
611 } else {
612 if (!PyCallable_Check(arg)) {
613 PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
614 return NULL;
615 }
616 cpy_build_name(buf, sizeof(buf), arg, NULL, short_name);
617 name = buf;
618 }
619 for (tmp = *list_head; tmp; prev = tmp, tmp = tmp->next)
620 if (strcmp(name, tmp->name) == 0)
621 break;
623 if (tmp == NULL) {
624 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
625 return NULL;
626 }
627 /* Yes, this is actually save. To call this function the calles has to
628 * hold the GIL. Well, save as long as there is only one GIL anyway ... */
629 if (prev == NULL)
630 *list_head = tmp->next;
631 else
632 prev->next = tmp->next;
633 cpy_destroy_user_data(tmp);
634 Py_RETURN_NONE;
635 }
637 typedef int cpy_unregister_function_t(const char *name);
639 static PyObject *cpy_unregister_generic_userdata(cpy_unregister_function_t *unreg, PyObject *arg, const char *desc, int short_name) {
640 char buf[512];
641 const char *name;
643 if (PyString_Check(arg)) {
644 name = PyString_AsString(arg);
645 } else {
646 if (!PyCallable_Check(arg)) {
647 PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
648 return NULL;
649 }
650 cpy_build_name(buf, sizeof(buf), arg, NULL, short_name);
651 name = buf;
652 }
653 if (unreg(name) == 0)
654 Py_RETURN_NONE;
655 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
656 return NULL;
657 }
659 static PyObject *cpy_unregister_log(PyObject *self, PyObject *arg) {
660 return cpy_unregister_generic_userdata(plugin_unregister_log, arg, "log", 0);
661 }
663 static PyObject *cpy_unregister_init(PyObject *self, PyObject *arg) {
664 return cpy_unregister_generic(&cpy_init_callbacks, arg, "init", 0);
665 }
667 static PyObject *cpy_unregister_config(PyObject *self, PyObject *arg) {
668 return cpy_unregister_generic(&cpy_config_callbacks, arg, "config", 1);
669 }
671 static PyObject *cpy_unregister_read(PyObject *self, PyObject *arg) {
672 return cpy_unregister_generic_userdata(plugin_unregister_read, arg, "read", 0);
673 }
675 static PyObject *cpy_unregister_write(PyObject *self, PyObject *arg) {
676 return cpy_unregister_generic_userdata(plugin_unregister_write, arg, "write", 0);
677 }
679 static PyObject *cpy_unregister_notification(PyObject *self, PyObject *arg) {
680 return cpy_unregister_generic_userdata(plugin_unregister_notification, arg, "notification", 0);
681 }
683 static PyObject *cpy_unregister_flush(PyObject *self, PyObject *arg) {
684 return cpy_unregister_generic_userdata(plugin_unregister_flush, arg, "flush", 1);
685 }
687 static PyObject *cpy_unregister_shutdown(PyObject *self, PyObject *arg) {
688 return cpy_unregister_generic(&cpy_shutdown_callbacks, arg, "shutdown", 0);
689 }
691 static PyMethodDef cpy_methods[] = {
692 {"debug", cpy_debug, METH_VARARGS, log_doc},
693 {"info", cpy_info, METH_VARARGS, log_doc},
694 {"notice", cpy_notice, METH_VARARGS, log_doc},
695 {"warning", cpy_warning, METH_VARARGS, log_doc},
696 {"error", cpy_error, METH_VARARGS, log_doc},
697 {"flush", (PyCFunction) cpy_flush, METH_VARARGS | METH_KEYWORDS, flush_doc},
698 {"register_log", (PyCFunction) cpy_register_log, METH_VARARGS | METH_KEYWORDS, reg_log_doc},
699 {"register_init", (PyCFunction) cpy_register_init, METH_VARARGS | METH_KEYWORDS, reg_init_doc},
700 {"register_config", (PyCFunction) cpy_register_config, METH_VARARGS | METH_KEYWORDS, reg_config_doc},
701 {"register_read", (PyCFunction) cpy_register_read, METH_VARARGS | METH_KEYWORDS, reg_read_doc},
702 {"register_write", (PyCFunction) cpy_register_write, METH_VARARGS | METH_KEYWORDS, reg_write_doc},
703 {"register_notification", (PyCFunction) cpy_register_notification, METH_VARARGS | METH_KEYWORDS, reg_notification_doc},
704 {"register_flush", (PyCFunction) cpy_register_flush, METH_VARARGS | METH_KEYWORDS, reg_flush_doc},
705 {"register_shutdown", (PyCFunction) cpy_register_shutdown, METH_VARARGS | METH_KEYWORDS, reg_shutdown_doc},
706 {"unregister_log", cpy_unregister_log, METH_O, unregister_doc},
707 {"unregister_init", cpy_unregister_init, METH_O, unregister_doc},
708 {"unregister_config", cpy_unregister_config, METH_O, unregister_doc},
709 {"unregister_read", cpy_unregister_read, METH_O, unregister_doc},
710 {"unregister_write", cpy_unregister_write, METH_O, unregister_doc},
711 {"unregister_notification", cpy_unregister_notification, METH_O, unregister_doc},
712 {"unregister_flush", cpy_unregister_flush, METH_O, unregister_doc},
713 {"unregister_shutdown", cpy_unregister_shutdown, METH_O, unregister_doc},
714 {0, 0, 0, 0}
715 };
717 static int cpy_shutdown(void) {
718 cpy_callback_t *c;
719 PyObject *ret;
721 /* This can happen if the module was loaded but not configured. */
722 if (state != NULL)
723 PyEval_RestoreThread(state);
725 for (c = cpy_shutdown_callbacks; c; c = c->next) {
726 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
727 if (ret == NULL)
728 cpy_log_exception("shutdown callback");
729 else
730 Py_DECREF(ret);
731 }
732 Py_Finalize();
733 return 0;
734 }
736 static void *cpy_interactive(void *data) {
737 CPY_LOCK_THREADS
738 if (PyImport_ImportModule("readline") == NULL) {
739 /* This interactive session will suck. */
740 cpy_log_exception("interactive session init");
741 }
742 PyRun_InteractiveLoop(stdin, "<stdin>");
743 CPY_RELEASE_THREADS
744 NOTICE("python: Interactive interpreter exited, stopping collectd ...");
745 raise(SIGINT);
746 return NULL;
747 }
749 static int cpy_init(void) {
750 cpy_callback_t *c;
751 PyObject *ret;
752 static pthread_t thread;
754 PyEval_InitThreads();
755 /* Now it's finally OK to use python threads. */
756 for (c = cpy_init_callbacks; c; c = c->next) {
757 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
758 if (ret == NULL)
759 cpy_log_exception("init callback");
760 else
761 Py_DECREF(ret);
762 }
763 state = PyEval_SaveThread();
764 if (do_interactive) {
765 if (pthread_create(&thread, NULL, cpy_interactive, NULL)) {
766 ERROR("python: Error creating thread for interactive interpreter.");
767 }
768 }
770 return 0;
771 }
773 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
774 int i;
775 PyObject *item, *values, *children, *tmp;
777 if (parent == NULL)
778 parent = Py_None;
780 values = PyTuple_New(ci->values_num); /* New reference. */
781 for (i = 0; i < ci->values_num; ++i) {
782 if (ci->values[i].type == OCONFIG_TYPE_STRING) {
783 PyTuple_SET_ITEM(values, i, PyString_FromString(ci->values[i].value.string));
784 } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
785 PyTuple_SET_ITEM(values, i, PyFloat_FromDouble(ci->values[i].value.number));
786 } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
787 PyTuple_SET_ITEM(values, i, PyBool_FromLong(ci->values[i].value.boolean));
788 }
789 }
791 item = PyObject_CallFunction((PyObject *) &ConfigType, "sONO", ci->key, parent, values, Py_None);
792 if (item == NULL)
793 return NULL;
794 children = PyTuple_New(ci->children_num); /* New reference. */
795 for (i = 0; i < ci->children_num; ++i) {
796 PyTuple_SET_ITEM(children, i, cpy_oconfig_to_pyconfig(ci->children + i, item));
797 }
798 tmp = ((Config *) item)->children;
799 ((Config *) item)->children = children;
800 Py_XDECREF(tmp);
801 return item;
802 }
804 static int cpy_config(oconfig_item_t *ci) {
805 int i;
806 PyObject *sys, *tb;
807 PyObject *sys_path;
808 PyObject *module;
810 /* Ok in theory we shouldn't do initialization at this point
811 * but we have to. In order to give python scripts a chance
812 * to register a config callback we need to be able to execute
813 * python code during the config callback so we have to start
814 * the interpreter here. */
815 /* Do *not* use the python "thread" module at this point! */
816 Py_Initialize();
818 PyType_Ready(&ConfigType);
819 PyType_Ready(&PluginDataType);
820 ValuesType.tp_base = &PluginDataType;
821 PyType_Ready(&ValuesType);
822 NotificationType.tp_base = &PluginDataType;
823 PyType_Ready(&NotificationType);
824 sys = PyImport_ImportModule("sys"); /* New reference. */
825 if (sys == NULL) {
826 cpy_log_exception("python initialization");
827 return 1;
828 }
829 sys_path = PyObject_GetAttrString(sys, "path"); /* New reference. */
830 Py_DECREF(sys);
831 if (sys_path == NULL) {
832 cpy_log_exception("python initialization");
833 return 1;
834 }
835 module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
836 PyModule_AddObject(module, "Config", (PyObject *) &ConfigType); /* Steals a reference. */
837 PyModule_AddObject(module, "Values", (PyObject *) &ValuesType); /* Steals a reference. */
838 PyModule_AddObject(module, "Notification", (PyObject *) &NotificationType); /* Steals a reference. */
839 PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG);
840 PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO);
841 PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE);
842 PyModule_AddIntConstant(module, "LOG_WARNING", LOG_WARNING);
843 PyModule_AddIntConstant(module, "LOG_ERROR", LOG_ERR);
844 PyModule_AddIntConstant(module, "NOTIF_FAILURE", NOTIF_FAILURE);
845 PyModule_AddIntConstant(module, "NOTIF_WARNING", NOTIF_WARNING);
846 PyModule_AddIntConstant(module, "NOTIF_OKAY", NOTIF_OKAY);
847 for (i = 0; i < ci->children_num; ++i) {
848 oconfig_item_t *item = ci->children + i;
850 if (strcasecmp(item->key, "Interactive") == 0) {
851 if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_BOOLEAN)
852 continue;
853 do_interactive = item->values[0].value.boolean;
854 } else if (strcasecmp(item->key, "LogTraces") == 0) {
855 if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_BOOLEAN)
856 continue;
857 if (!item->values[0].value.boolean) {
858 Py_XDECREF(cpy_format_exception);
859 cpy_format_exception = NULL;
860 continue;
861 }
862 if (cpy_format_exception)
863 continue;
864 tb = PyImport_ImportModule("traceback"); /* New reference. */
865 if (tb == NULL) {
866 cpy_log_exception("python initialization");
867 continue;
868 }
869 cpy_format_exception = PyObject_GetAttrString(tb, "format_exception"); /* New reference. */
870 Py_DECREF(tb);
871 if (cpy_format_exception == NULL)
872 cpy_log_exception("python initialization");
873 } else if (strcasecmp(item->key, "ModulePath") == 0) {
874 char *dir = NULL;
875 PyObject *dir_object;
877 if (cf_util_get_string(item, &dir) != 0)
878 continue;
879 dir_object = PyString_FromString(dir); /* New reference. */
880 if (dir_object == NULL) {
881 ERROR("python plugin: Unable to convert \"%s\" to "
882 "a python object.", dir);
883 free(dir);
884 cpy_log_exception("python initialization");
885 continue;
886 }
887 if (PyList_Append(sys_path, dir_object) != 0) {
888 ERROR("python plugin: Unable to append \"%s\" to "
889 "python module path.", dir);
890 cpy_log_exception("python initialization");
891 }
892 Py_DECREF(dir_object);
893 free(dir);
894 } else if (strcasecmp(item->key, "Import") == 0) {
895 char *module_name = NULL;
896 PyObject *module;
898 if (cf_util_get_string(item, &module_name) != 0)
899 continue;
900 module = PyImport_ImportModule(module_name); /* New reference. */
901 if (module == NULL) {
902 ERROR("python plugin: Error importing module \"%s\".", module_name);
903 cpy_log_exception("importing module");
904 PyErr_Print();
905 }
906 free(module_name);
907 Py_XDECREF(module);
908 } else if (strcasecmp(item->key, "Module") == 0) {
909 char *name = NULL;
910 cpy_callback_t *c;
911 PyObject *ret;
913 if (cf_util_get_string(item, &name) != 0)
914 continue;
915 for (c = cpy_config_callbacks; c; c = c->next) {
916 if (strcasecmp(c->name + 7, name) == 0)
917 break;
918 }
919 if (c == NULL) {
920 WARNING("python plugin: Found a configuration for the \"%s\" plugin, "
921 "but the plugin isn't loaded or didn't register "
922 "a configuration callback.", name);
923 free(name);
924 continue;
925 }
926 free(name);
927 if (c->data == NULL)
928 ret = PyObject_CallFunction(c->callback, "N",
929 cpy_oconfig_to_pyconfig(item, NULL)); /* New reference. */
930 else
931 ret = PyObject_CallFunction(c->callback, "NO",
932 cpy_oconfig_to_pyconfig(item, NULL), c->data); /* New reference. */
933 if (ret == NULL)
934 cpy_log_exception("loading module");
935 else
936 Py_DECREF(ret);
937 } else {
938 WARNING("python plugin: Ignoring unknown config key \"%s\".", item->key);
939 }
940 }
941 Py_DECREF(sys_path);
942 return 0;
943 }
945 void module_register(void) {
946 plugin_register_complex_config("python", cpy_config);
947 plugin_register_init("python", cpy_init);
948 plugin_register_shutdown("python", cpy_shutdown);
949 }