1 #include <Python.h>
2 #include <structmember.h>
4 #include <signal.h>
5 #if HAVE_PTHREAD_H
6 # include <pthread.h>
7 #endif
9 #include "collectd.h"
10 #include "common.h"
12 #include "cpython.h"
14 typedef struct cpy_callback_s {
15 char *name;
16 PyObject *callback;
17 PyObject *data;
18 struct cpy_callback_s *next;
19 } cpy_callback_t;
21 static char log_doc[] = "This function sends a string to all logging plugins.";
23 static char flush_doc[] = "flush([plugin][, timeout][, identifier]) -> None\n"
24 "\n"
25 "Flushes the cache of another plugin.";
27 static char unregister_doc[] = "Unregisters a callback. This function needs exactly one parameter either\n"
28 "the function to unregister or the callback identifier to unregister.";
30 static char reg_log_doc[] = "register_log(callback[, data][, name]) -> identifier\n"
31 "\n"
32 "Register a callback function for log messages.\n"
33 "\n"
34 "'callback' is a callable object that will be called every time something\n"
35 " is logged.\n"
36 "'data' is an optional object that will be passed back to the callback\n"
37 " function every time it is called.\n"
38 "'name' is an optional identifier for this callback. The default name\n"
39 " is 'python.<module>'.\n"
40 " Every callback needs a unique identifier, so if you want to\n"
41 " register this callback multiple time from the same module you need\n"
42 " to specify a name 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 omitted it will be omitted 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 background.\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>'.\n"
62 " Every callback needs a unique identifier, so if you want to\n"
63 " register this callback multiple time from the same module you need\n"
64 " to specify a name here.\n"
65 "'identifier' is the full identifier assigned to this callback.\n"
66 "\n"
67 "The callback function will be called without parameters, except for\n"
68 "data if it was supplied.";
70 static char reg_config_doc[] = "register_config(callback[, data][, name]) -> identifier\n"
71 "\n"
72 "Register a callback function for config file entries.\n"
73 "'callback' is a callable object that will be called for every config block.\n"
74 "'data' is an optional object that will be passed back to the callback\n"
75 " function every time it is called.\n"
76 "'name' is an optional identifier for this callback. The default name\n"
77 " is 'python.<module>'.\n"
78 " Every callback needs a unique identifier, so if you want to\n"
79 " register this callback multiple time from the same module you need\n"
80 " to 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 omitted it will be omitted 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>'.\n"
100 " Every callback needs a unique identifier, so if you want to\n"
101 " register this callback multiple time from the same module you need\n"
102 " to specify a name here.\n"
103 "'identifier' is the full identifier assigned to this callback.\n"
104 "\n"
105 "The callback function will be called without parameters, except for\n"
106 "data if it was supplied.";
108 static char reg_write_doc[] = "register_write(callback[, data][, name]) -> identifier\n"
109 "\n"
110 "Register a callback function to receive values dispatched by other plugins.\n"
111 "'callback' is a callable object that will be called every time a value\n"
112 " is dispatched.\n"
113 "'data' is an optional object that will be passed back to the callback\n"
114 " function every time it is called.\n"
115 "'name' is an optional identifier for this callback. The default name\n"
116 " is 'python.<module>'.\n"
117 " Every callback needs a unique identifier, so if you want to\n"
118 " register this callback multiple time from the same module you need\n"
119 " to specify a name here.\n"
120 "'identifier' is the full identifier assigned to this callback.\n"
121 "\n"
122 "The callback function will be called with one or two parameters:\n"
123 "values: A Values object which is a copy of the dispatched values.\n"
124 "data: The optional data parameter passed to the register function.\n"
125 " If the parameter was omitted it will be omitted here, too.";
127 static char reg_notification_doc[] = "register_notification(callback[, data][, name]) -> identifier\n"
128 "\n"
129 "Register a callback function for notifications.\n"
130 "'callback' is a callable object that will be called every time a notification\n"
131 " is dispatched.\n"
132 "'data' is an optional object that will be passed back to the callback\n"
133 " function every time it is called.\n"
134 "'name' is an optional identifier for this callback. The default name\n"
135 " is 'python.<module>'.\n"
136 " Every callback needs a unique identifier, so if you want to\n"
137 " register this callback multiple time from the same module you need\n"
138 " to specify a name here.\n"
139 "'identifier' is the full identifier assigned to this callback.\n"
140 "\n"
141 "The callback function will be called with one or two parameters:\n"
142 "notification: A copy of the notification that was dispatched.\n"
143 "data: The optional data parameter passed to the register function.\n"
144 " If the parameter was omitted it will be omitted here, too.";
146 static char reg_flush_doc[] = "register_flush(callback[, data][, name]) -> identifier\n"
147 "\n"
148 "Register a callback function for flush messages.\n"
149 "'callback' is a callable object that will be called every time a plugin\n"
150 " requests a flush for either this or all plugins.\n"
151 "'data' is an optional object that will be passed back to the callback\n"
152 " function every time it is called.\n"
153 "'name' is an optional identifier for this callback. The default name\n"
154 " is 'python.<module>'.\n"
155 " Every callback needs a unique identifier, so if you want to\n"
156 " register this callback multiple time from the same module you need\n"
157 " to specify a name here.\n"
158 "'identifier' is the full identifier assigned to this callback.\n"
159 "\n"
160 "The callback function will be called with two or three parameters:\n"
161 "timeout: Indicates that only data older than 'timeout' seconds is to\n"
162 " be flushed.\n"
163 "id: Specifies which values are to be flushed.\n"
164 "data: The optional data parameter passed to the register function.\n"
165 " If the parameter was omitted it will be omitted here, too.";
167 static char reg_shutdown_doc[] = "register_shutdown(callback[, data][, name]) -> identifier\n"
168 "\n"
169 "Register a callback function for collectd shutdown.\n"
170 "'callback' is a callable object that will be called once collectd is\n"
171 " shutting down.\n"
172 "'data' is an optional object that will be passed back to the callback\n"
173 " function if it is called.\n"
174 "'name' is an optional identifier for this callback. The default name\n"
175 " is 'python.<module>'.\n"
176 " Every callback needs a unique identifier, so if you want to\n"
177 " register this callback multiple time from the same module you need\n"
178 " to specify a name here.\n"
179 "'identifier' is the full identifier assigned to this callback.\n"
180 "\n"
181 "The callback function will be called with no parameters except for\n"
182 " data if it was supplied.";
185 static int do_interactive = 0;
187 /* This is our global thread state. Python saves some stuff in thread-local
188 * storage. So if we allow the interpreter to run in the background
189 * (the scriptwriters might have created some threads from python), we have
190 * to save the state so we can resume it later after shutdown. */
192 static PyThreadState *state;
194 static PyObject *cpy_format_exception;
196 static cpy_callback_t *cpy_config_callbacks;
197 static cpy_callback_t *cpy_init_callbacks;
198 static cpy_callback_t *cpy_shutdown_callbacks;
200 static void cpy_destroy_user_data(void *data) {
201 cpy_callback_t *c = data;
202 free(c->name);
203 Py_DECREF(c->callback);
204 Py_XDECREF(c->data);
205 free(c);
206 }
208 /* You must hold the GIL to call this function!
209 * But if you managed to extract the callback parameter then you probably already do. */
211 static void cpy_build_name(char *buf, size_t size, PyObject *callback, const char *name) {
212 const char *module = NULL;
213 PyObject *mod = NULL;
215 if (name != NULL) {
216 snprintf(buf, size, "python.%s", name);
217 return;
218 }
220 mod = PyObject_GetAttrString(callback, "__module__"); /* New reference. */
221 if (mod != NULL)
222 module = PyString_AsString(mod);
224 if (module != NULL) {
225 snprintf(buf, size, "python.%s", module);
226 Py_XDECREF(mod);
227 PyErr_Clear();
228 return;
229 }
230 Py_XDECREF(mod);
232 snprintf(buf, size, "python.%p", callback);
233 PyErr_Clear();
234 }
236 static void cpy_log_exception(const char *context) {
237 int l = 0, i;
238 const char *typename = NULL, *message = NULL;
239 PyObject *type, *value, *traceback, *tn, *m, *list;
241 PyErr_Fetch(&type, &value, &traceback);
242 PyErr_NormalizeException(&type, &value, &traceback);
243 if (type == NULL) return;
244 tn = PyObject_GetAttrString(type, "__name__"); /* New reference. */
245 m = PyObject_GetAttrString(value, "message"); /* New reference. */
246 if (tn != NULL)
247 typename = PyString_AsString(tn);
248 if (m != NULL)
249 message = PyString_AsString(m);
250 if (typename == NULL)
251 typename = "NamelessException";
252 if (message == NULL)
253 message = "N/A";
254 Py_BEGIN_ALLOW_THREADS
255 ERROR("Unhandled python exception in %s: %s: %s", context, typename, message);
256 Py_END_ALLOW_THREADS
257 Py_XDECREF(tn);
258 Py_XDECREF(m);
259 if (!cpy_format_exception) {
260 PyErr_Clear();
261 Py_XDECREF(type);
262 Py_XDECREF(value);
263 Py_XDECREF(traceback);
264 return;
265 }
266 if (!traceback) {
267 PyErr_Clear();
268 return;
269 }
270 list = PyObject_CallFunction(cpy_format_exception, "NNN", type, value, traceback); /* New reference. */
271 if (list)
272 l = PyObject_Length(list);
273 for (i = 0; i < l; ++i) {
274 char *s;
275 PyObject *line;
277 line = PyList_GET_ITEM(list, i); /* Borrowed reference. */
278 s = strdup(PyString_AsString(line));
279 Py_DECREF(line);
280 if (s[strlen(s) - 1] == '\n')
281 s[strlen(s) - 1] = 0;
282 Py_BEGIN_ALLOW_THREADS
283 ERROR("%s", s);
284 Py_END_ALLOW_THREADS
285 free(s);
286 }
287 Py_XDECREF(list);
288 PyErr_Clear();
289 }
291 static int cpy_read_callback(user_data_t *data) {
292 cpy_callback_t *c = data->data;
293 PyObject *ret;
295 CPY_LOCK_THREADS
296 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
297 if (ret == NULL) {
298 cpy_log_exception("read callback");
299 } else {
300 Py_DECREF(ret);
301 }
302 CPY_RELEASE_THREADS
303 if (ret == NULL)
304 return 1;
305 return 0;
306 }
308 static int cpy_write_callback(const data_set_t *ds, const value_list_t *value_list, user_data_t *data) {
309 int i;
310 cpy_callback_t *c = data->data;
311 PyObject *ret, *v, *list;
313 CPY_LOCK_THREADS
314 list = PyList_New(value_list->values_len); /* New reference. */
315 if (list == NULL) {
316 cpy_log_exception("write callback");
317 CPY_RETURN_FROM_THREADS 0;
318 }
319 for (i = 0; i < value_list->values_len; ++i) {
320 if (ds->ds->type == DS_TYPE_COUNTER) {
321 if ((long) value_list->values[i].counter == value_list->values[i].counter)
322 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].counter));
323 else
324 PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].counter));
325 } else if (ds->ds->type == DS_TYPE_GAUGE) {
326 PyList_SetItem(list, i, PyFloat_FromDouble(value_list->values[i].gauge));
327 } else if (ds->ds->type == DS_TYPE_DERIVE) {
328 if ((long) value_list->values[i].derive == value_list->values[i].derive)
329 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].derive));
330 else
331 PyList_SetItem(list, i, PyLong_FromLongLong(value_list->values[i].derive));
332 } else if (ds->ds->type == DS_TYPE_ABSOLUTE) {
333 if ((long) value_list->values[i].absolute == value_list->values[i].absolute)
334 PyList_SetItem(list, i, PyInt_FromLong(value_list->values[i].absolute));
335 else
336 PyList_SetItem(list, i, PyLong_FromUnsignedLongLong(value_list->values[i].absolute));
337 } else {
338 Py_BEGIN_ALLOW_THREADS
339 ERROR("cpy_write_callback: Unknown value type %d.", ds->ds->type);
340 Py_END_ALLOW_THREADS
341 Py_DECREF(list);
342 CPY_RETURN_FROM_THREADS 0;
343 }
344 if (PyErr_Occurred() != NULL) {
345 cpy_log_exception("value building for write callback");
346 CPY_RETURN_FROM_THREADS 0;
347 }
348 }
349 v = PyObject_CallFunction((void *) &ValuesType, "sOssssdi", value_list->type, list,
350 value_list->plugin_instance, value_list->type_instance, value_list->plugin,
351 value_list->host, (double) value_list->time, value_list->interval);
352 Py_DECREF(list);
353 ret = PyObject_CallFunctionObjArgs(c->callback, v, c->data, (void *) 0); /* New reference. */
354 if (ret == NULL) {
355 cpy_log_exception("write callback");
356 } else {
357 Py_DECREF(ret);
358 }
359 CPY_RELEASE_THREADS
360 return 0;
361 }
363 static int cpy_notification_callback(const notification_t *notification, user_data_t *data) {
364 cpy_callback_t *c = data->data;
365 PyObject *ret, *n;
367 CPY_LOCK_THREADS
368 n = PyObject_CallFunction((void *) &NotificationType, "ssssssdi", notification->type, notification->message,
369 notification->plugin_instance, notification->type_instance, notification->plugin,
370 notification->host, (double) notification->time, notification->severity);
371 ret = PyObject_CallFunctionObjArgs(c->callback, n, c->data, (void *) 0); /* New reference. */
372 if (ret == NULL) {
373 cpy_log_exception("notification callback");
374 } else {
375 Py_DECREF(ret);
376 }
377 CPY_RELEASE_THREADS
378 return 0;
379 }
381 static void cpy_log_callback(int severity, const char *message, user_data_t *data) {
382 cpy_callback_t * c = data->data;
383 PyObject *ret;
385 CPY_LOCK_THREADS
386 if (c->data == NULL)
387 ret = PyObject_CallFunction(c->callback, "is", severity, message); /* New reference. */
388 else
389 ret = PyObject_CallFunction(c->callback, "isO", severity, message, c->data); /* New reference. */
391 if (ret == NULL) {
392 /* FIXME */
393 /* Do we really want to trigger a log callback because a log callback failed?
394 * Probably not. */
395 PyErr_Print();
396 /* In case someone wanted to be clever, replaced stderr and failed at that. */
397 PyErr_Clear();
398 } else {
399 Py_DECREF(ret);
400 }
401 CPY_RELEASE_THREADS
402 }
404 static void cpy_flush_callback(int timeout, const char *id, user_data_t *data) {
405 cpy_callback_t * c = data->data;
406 PyObject *ret;
408 CPY_LOCK_THREADS
409 if (c->data == NULL)
410 ret = PyObject_CallFunction(c->callback, "is", timeout, id); /* New reference. */
411 else
412 ret = PyObject_CallFunction(c->callback, "isO", timeout, id, c->data); /* New reference. */
414 if (ret == NULL) {
415 cpy_log_exception("flush callback");
416 } else {
417 Py_DECREF(ret);
418 }
419 CPY_RELEASE_THREADS
420 }
422 static PyObject *cpy_register_generic(cpy_callback_t **list_head, PyObject *args, PyObject *kwds) {
423 char buf[512];
424 cpy_callback_t *c;
425 const char *name = NULL;
426 PyObject *callback = NULL, *data = NULL, *mod = NULL;
427 static char *kwlist[] = {"callback", "data", "name", NULL};
429 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
430 if (PyCallable_Check(callback) == 0) {
431 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
432 return NULL;
433 }
434 cpy_build_name(buf, sizeof(buf), callback, name);
436 Py_INCREF(callback);
437 Py_XINCREF(data);
438 c = malloc(sizeof(*c));
439 c->name = strdup(buf);
440 c->callback = callback;
441 c->data = data;
442 c->next = *list_head;
443 *list_head = c;
444 Py_XDECREF(mod);
445 return PyString_FromString(buf);
446 }
448 static PyObject *cpy_flush(cpy_callback_t **list_head, PyObject *args, PyObject *kwds) {
449 int timeout = -1;
450 const char *plugin = NULL, *identifier = NULL;
451 static char *kwlist[] = {"plugin", "timeout", "identifier", NULL};
453 if (PyArg_ParseTupleAndKeywords(args, kwds, "|ziz", kwlist, &plugin, &timeout, &identifier) == 0) return NULL;
454 Py_BEGIN_ALLOW_THREADS
455 plugin_flush(plugin, timeout, identifier);
456 Py_END_ALLOW_THREADS
457 Py_RETURN_NONE;
458 }
460 static PyObject *cpy_register_config(PyObject *self, PyObject *args, PyObject *kwds) {
461 return cpy_register_generic(&cpy_config_callbacks, args, kwds);
462 }
464 static PyObject *cpy_register_init(PyObject *self, PyObject *args, PyObject *kwds) {
465 return cpy_register_generic(&cpy_init_callbacks, args, kwds);
466 }
468 typedef int reg_function_t(const char *name, void *callback, void *data);
470 static PyObject *cpy_register_generic_userdata(void *reg, void *handler, PyObject *args, PyObject *kwds) {
471 char buf[512];
472 reg_function_t *register_function = (reg_function_t *) reg;
473 cpy_callback_t *c = NULL;
474 user_data_t *user_data = NULL;
475 const char *name = NULL;
476 PyObject *callback = NULL, *data = NULL;
477 static char *kwlist[] = {"callback", "data", "name", NULL};
479 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oz", kwlist, &callback, &data, &name) == 0) return NULL;
480 if (PyCallable_Check(callback) == 0) {
481 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
482 return NULL;
483 }
484 cpy_build_name(buf, sizeof(buf), callback, name);
486 Py_INCREF(callback);
487 Py_XINCREF(data);
488 c = malloc(sizeof(*c));
489 c->name = strdup(buf);
490 c->callback = callback;
491 c->data = data;
492 c->next = NULL;
493 user_data = malloc(sizeof(*user_data));
494 user_data->free_func = cpy_destroy_user_data;
495 user_data->data = c;
496 register_function(buf, handler, user_data);
497 return PyString_FromString(buf);
498 }
500 static PyObject *cpy_register_read(PyObject *self, PyObject *args, PyObject *kwds) {
501 char buf[512];
502 cpy_callback_t *c = NULL;
503 user_data_t *user_data = NULL;
504 double interval = 0;
505 const char *name = NULL;
506 PyObject *callback = NULL, *data = NULL;
507 struct timespec ts;
508 static char *kwlist[] = {"callback", "interval", "data", "name", NULL};
510 if (PyArg_ParseTupleAndKeywords(args, kwds, "O|dOz", kwlist, &callback, &interval, &data, &name) == 0) return NULL;
511 if (PyCallable_Check(callback) == 0) {
512 PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
513 return NULL;
514 }
515 cpy_build_name(buf, sizeof(buf), callback, name);
517 Py_INCREF(callback);
518 Py_XINCREF(data);
519 c = malloc(sizeof(*c));
520 c->name = strdup(buf);
521 c->callback = callback;
522 c->data = data;
523 c->next = NULL;
524 user_data = malloc(sizeof(*user_data));
525 user_data->free_func = cpy_destroy_user_data;
526 user_data->data = c;
527 ts.tv_sec = interval;
528 ts.tv_nsec = (interval - ts.tv_sec) * 1000000000;
529 plugin_register_complex_read(buf, cpy_read_callback, &ts, user_data);
530 return PyString_FromString(buf);
531 }
533 static PyObject *cpy_register_log(PyObject *self, PyObject *args, PyObject *kwds) {
534 return cpy_register_generic_userdata((void *) plugin_register_log,
535 (void *) cpy_log_callback, args, kwds);
536 }
538 static PyObject *cpy_register_write(PyObject *self, PyObject *args, PyObject *kwds) {
539 return cpy_register_generic_userdata((void *) plugin_register_write,
540 (void *) cpy_write_callback, args, kwds);
541 }
543 static PyObject *cpy_register_notification(PyObject *self, PyObject *args, PyObject *kwds) {
544 return cpy_register_generic_userdata((void *) plugin_register_notification,
545 (void *) cpy_notification_callback, args, kwds);
546 }
548 static PyObject *cpy_register_flush(PyObject *self, PyObject *args, PyObject *kwds) {
549 return cpy_register_generic_userdata((void *) plugin_register_flush,
550 (void *) cpy_flush_callback, args, kwds);
551 }
553 static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args, PyObject *kwds) {
554 return cpy_register_generic(&cpy_shutdown_callbacks, args, kwds);
555 }
557 static PyObject *cpy_error(PyObject *self, PyObject *args) {
558 const char *text;
559 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
560 Py_BEGIN_ALLOW_THREADS
561 plugin_log(LOG_ERR, "%s", text);
562 Py_END_ALLOW_THREADS
563 Py_RETURN_NONE;
564 }
566 static PyObject *cpy_warning(PyObject *self, PyObject *args) {
567 const char *text;
568 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
569 Py_BEGIN_ALLOW_THREADS
570 plugin_log(LOG_WARNING, "%s", text);
571 Py_END_ALLOW_THREADS
572 Py_RETURN_NONE;
573 }
575 static PyObject *cpy_notice(PyObject *self, PyObject *args) {
576 const char *text;
577 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
578 Py_BEGIN_ALLOW_THREADS
579 plugin_log(LOG_NOTICE, "%s", text);
580 Py_END_ALLOW_THREADS
581 Py_RETURN_NONE;
582 }
584 static PyObject *cpy_info(PyObject *self, PyObject *args) {
585 const char *text;
586 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
587 Py_BEGIN_ALLOW_THREADS
588 plugin_log(LOG_INFO, "%s", text);
589 Py_END_ALLOW_THREADS
590 Py_RETURN_NONE;
591 }
593 static PyObject *cpy_debug(PyObject *self, PyObject *args) {
594 #ifdef COLLECT_DEBUG
595 const char *text;
596 if (PyArg_ParseTuple(args, "s", &text) == 0) return NULL;
597 Py_BEGIN_ALLOW_THREADS
598 plugin_log(LOG_DEBUG, "%s", text);
599 Py_END_ALLOW_THREADS
600 #endif
601 Py_RETURN_NONE;
602 }
604 static PyObject *cpy_unregister_generic(cpy_callback_t **list_head, PyObject *arg, const char *desc) {
605 char buf[512];
606 const char *name;
607 cpy_callback_t *prev = NULL, *tmp;
609 if (PyUnicode_Check(arg)) {
610 arg = PyUnicode_AsEncodedString(arg, NULL, NULL);
611 if (arg == NULL)
612 return NULL;
613 name = PyString_AsString(arg);
614 Py_DECREF(arg);
615 } else if (PyString_Check(arg)) {
616 name = PyString_AsString(arg);
617 } else {
618 if (!PyCallable_Check(arg)) {
619 PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
620 return NULL;
621 }
622 cpy_build_name(buf, sizeof(buf), arg, NULL);
623 name = buf;
624 }
625 for (tmp = *list_head; tmp; prev = tmp, tmp = tmp->next)
626 if (strcmp(name, tmp->name) == 0)
627 break;
629 if (tmp == NULL) {
630 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
631 return NULL;
632 }
633 /* Yes, this is actually save. To call this function the caller has to
634 * hold the GIL. Well, save as long as there is only one GIL anyway ... */
635 if (prev == NULL)
636 *list_head = tmp->next;
637 else
638 prev->next = tmp->next;
639 cpy_destroy_user_data(tmp);
640 Py_RETURN_NONE;
641 }
643 typedef int cpy_unregister_function_t(const char *name);
645 static PyObject *cpy_unregister_generic_userdata(cpy_unregister_function_t *unreg, PyObject *arg, const char *desc) {
646 char buf[512];
647 const char *name;
649 if (PyUnicode_Check(arg)) {
650 arg = PyUnicode_AsEncodedString(arg, NULL, NULL);
651 if (arg == NULL)
652 return NULL;
653 name = PyString_AsString(arg);
654 Py_DECREF(arg);
655 } else if (PyString_Check(arg)) {
656 name = PyString_AsString(arg);
657 } else {
658 if (!PyCallable_Check(arg)) {
659 PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
660 return NULL;
661 }
662 cpy_build_name(buf, sizeof(buf), arg, NULL);
663 name = buf;
664 }
665 if (unreg(name) == 0)
666 Py_RETURN_NONE;
667 PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
668 return NULL;
669 }
671 static PyObject *cpy_unregister_log(PyObject *self, PyObject *arg) {
672 return cpy_unregister_generic_userdata(plugin_unregister_log, arg, "log");
673 }
675 static PyObject *cpy_unregister_init(PyObject *self, PyObject *arg) {
676 return cpy_unregister_generic(&cpy_init_callbacks, arg, "init");
677 }
679 static PyObject *cpy_unregister_config(PyObject *self, PyObject *arg) {
680 return cpy_unregister_generic(&cpy_config_callbacks, arg, "config");
681 }
683 static PyObject *cpy_unregister_read(PyObject *self, PyObject *arg) {
684 return cpy_unregister_generic_userdata(plugin_unregister_read, arg, "read");
685 }
687 static PyObject *cpy_unregister_write(PyObject *self, PyObject *arg) {
688 return cpy_unregister_generic_userdata(plugin_unregister_write, arg, "write");
689 }
691 static PyObject *cpy_unregister_notification(PyObject *self, PyObject *arg) {
692 return cpy_unregister_generic_userdata(plugin_unregister_notification, arg, "notification");
693 }
695 static PyObject *cpy_unregister_flush(PyObject *self, PyObject *arg) {
696 return cpy_unregister_generic_userdata(plugin_unregister_flush, arg, "flush");
697 }
699 static PyObject *cpy_unregister_shutdown(PyObject *self, PyObject *arg) {
700 return cpy_unregister_generic(&cpy_shutdown_callbacks, arg, "shutdown");
701 }
703 static PyMethodDef cpy_methods[] = {
704 {"debug", cpy_debug, METH_VARARGS, log_doc},
705 {"info", cpy_info, METH_VARARGS, log_doc},
706 {"notice", cpy_notice, METH_VARARGS, log_doc},
707 {"warning", cpy_warning, METH_VARARGS, log_doc},
708 {"error", cpy_error, METH_VARARGS, log_doc},
709 {"flush", (PyCFunction) cpy_flush, METH_VARARGS | METH_KEYWORDS, flush_doc},
710 {"register_log", (PyCFunction) cpy_register_log, METH_VARARGS | METH_KEYWORDS, reg_log_doc},
711 {"register_init", (PyCFunction) cpy_register_init, METH_VARARGS | METH_KEYWORDS, reg_init_doc},
712 {"register_config", (PyCFunction) cpy_register_config, METH_VARARGS | METH_KEYWORDS, reg_config_doc},
713 {"register_read", (PyCFunction) cpy_register_read, METH_VARARGS | METH_KEYWORDS, reg_read_doc},
714 {"register_write", (PyCFunction) cpy_register_write, METH_VARARGS | METH_KEYWORDS, reg_write_doc},
715 {"register_notification", (PyCFunction) cpy_register_notification, METH_VARARGS | METH_KEYWORDS, reg_notification_doc},
716 {"register_flush", (PyCFunction) cpy_register_flush, METH_VARARGS | METH_KEYWORDS, reg_flush_doc},
717 {"register_shutdown", (PyCFunction) cpy_register_shutdown, METH_VARARGS | METH_KEYWORDS, reg_shutdown_doc},
718 {"unregister_log", cpy_unregister_log, METH_O, unregister_doc},
719 {"unregister_init", cpy_unregister_init, METH_O, unregister_doc},
720 {"unregister_config", cpy_unregister_config, METH_O, unregister_doc},
721 {"unregister_read", cpy_unregister_read, METH_O, unregister_doc},
722 {"unregister_write", cpy_unregister_write, METH_O, unregister_doc},
723 {"unregister_notification", cpy_unregister_notification, METH_O, unregister_doc},
724 {"unregister_flush", cpy_unregister_flush, METH_O, unregister_doc},
725 {"unregister_shutdown", cpy_unregister_shutdown, METH_O, unregister_doc},
726 {0, 0, 0, 0}
727 };
729 static int cpy_shutdown(void) {
730 cpy_callback_t *c;
731 PyObject *ret;
733 /* This can happen if the module was loaded but not configured. */
734 if (state != NULL)
735 PyEval_RestoreThread(state);
737 for (c = cpy_shutdown_callbacks; c; c = c->next) {
738 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
739 if (ret == NULL)
740 cpy_log_exception("shutdown callback");
741 else
742 Py_DECREF(ret);
743 }
744 PyErr_Print();
745 Py_Finalize();
746 return 0;
747 }
749 static void cpy_int_handler(int sig) {
750 return;
751 }
753 static void *cpy_interactive(void *data) {
754 sigset_t sigset;
755 struct sigaction sig_int_action, old;
757 /* Signal handler in a plugin? Bad stuff, but the best way to
758 * handle it I guess. In an interactive session people will
759 * press Ctrl+C at some time, which will generate a SIGINT.
760 * This will cause collectd to shutdown, thus killing the
761 * interactive interpreter, and leaving the terminal in a
762 * mess. Chances are, this isn't what the user wanted to do.
763 *
764 * So this is the plan:
765 * 1. Block SIGINT in the main thread.
766 * 2. Install our own signal handler that does nothing.
767 * 3. Unblock SIGINT in the interactive thread.
768 *
769 * This will make sure that SIGINT won't kill collectd but
770 * still interrupt syscalls like sleep and pause.
771 * It does not raise a KeyboardInterrupt exception because so
772 * far nobody managed to figure out how to do that. */
773 memset (&sig_int_action, '\0', sizeof (sig_int_action));
774 sig_int_action.sa_handler = cpy_int_handler;
775 sigaction (SIGINT, &sig_int_action, &old);
777 sigemptyset(&sigset);
778 sigaddset(&sigset, SIGINT);
779 pthread_sigmask(SIG_UNBLOCK, &sigset, NULL);
780 PyEval_AcquireThread(state);
781 if (PyImport_ImportModule("readline") == NULL) {
782 /* This interactive session will suck. */
783 cpy_log_exception("interactive session init");
784 }
785 PyRun_InteractiveLoop(stdin, "<stdin>");
786 PyErr_Print();
787 PyEval_ReleaseThread(state);
788 NOTICE("python: Interactive interpreter exited, stopping collectd ...");
789 /* Restore the original collectd SIGINT handler and raise SIGINT.
790 * The main thread still has SIGINT blocked and there's nothing we
791 * can do about that so this thread will handle it. But that's not
792 * important, except that it won't interrupt the main loop and so
793 * it might take a few seconds before collectd really shuts down. */
794 sigaction (SIGINT, &old, NULL);
795 raise(SIGINT);
796 pause();
797 return NULL;
798 }
800 static int cpy_init(void) {
801 cpy_callback_t *c;
802 PyObject *ret;
803 static pthread_t thread;
804 sigset_t sigset;
806 PyEval_InitThreads();
807 /* Now it's finally OK to use python threads. */
808 for (c = cpy_init_callbacks; c; c = c->next) {
809 ret = PyObject_CallFunctionObjArgs(c->callback, c->data, (void *) 0); /* New reference. */
810 if (ret == NULL)
811 cpy_log_exception("init callback");
812 else
813 Py_DECREF(ret);
814 }
815 sigemptyset(&sigset);
816 sigaddset(&sigset, SIGINT);
817 pthread_sigmask(SIG_BLOCK, &sigset, NULL);
818 state = PyEval_SaveThread();
819 if (do_interactive) {
820 if (pthread_create(&thread, NULL, cpy_interactive, NULL)) {
821 ERROR("python: Error creating thread for interactive interpreter.");
822 }
823 }
825 return 0;
826 }
828 static PyObject *cpy_oconfig_to_pyconfig(oconfig_item_t *ci, PyObject *parent) {
829 int i;
830 PyObject *item, *values, *children, *tmp;
832 if (parent == NULL)
833 parent = Py_None;
835 values = PyTuple_New(ci->values_num); /* New reference. */
836 for (i = 0; i < ci->values_num; ++i) {
837 if (ci->values[i].type == OCONFIG_TYPE_STRING) {
838 PyTuple_SET_ITEM(values, i, PyString_FromString(ci->values[i].value.string));
839 } else if (ci->values[i].type == OCONFIG_TYPE_NUMBER) {
840 PyTuple_SET_ITEM(values, i, PyFloat_FromDouble(ci->values[i].value.number));
841 } else if (ci->values[i].type == OCONFIG_TYPE_BOOLEAN) {
842 PyTuple_SET_ITEM(values, i, PyBool_FromLong(ci->values[i].value.boolean));
843 }
844 }
846 item = PyObject_CallFunction((void *) &ConfigType, "sONO", ci->key, parent, values, Py_None);
847 if (item == NULL)
848 return NULL;
849 children = PyTuple_New(ci->children_num); /* New reference. */
850 for (i = 0; i < ci->children_num; ++i) {
851 PyTuple_SET_ITEM(children, i, cpy_oconfig_to_pyconfig(ci->children + i, item));
852 }
853 tmp = ((Config *) item)->children;
854 ((Config *) item)->children = children;
855 Py_XDECREF(tmp);
856 return item;
857 }
859 static int cpy_config(oconfig_item_t *ci) {
860 int i;
861 PyObject *sys, *tb;
862 PyObject *sys_path;
863 PyObject *module;
865 /* Ok in theory we shouldn't do initialization at this point
866 * but we have to. In order to give python scripts a chance
867 * to register a config callback we need to be able to execute
868 * python code during the config callback so we have to start
869 * the interpreter here. */
870 /* Do *not* use the python "thread" module at this point! */
871 Py_Initialize();
873 PyType_Ready(&ConfigType);
874 PyType_Ready(&PluginDataType);
875 ValuesType.tp_base = &PluginDataType;
876 PyType_Ready(&ValuesType);
877 NotificationType.tp_base = &PluginDataType;
878 PyType_Ready(&NotificationType);
879 sys = PyImport_ImportModule("sys"); /* New reference. */
880 if (sys == NULL) {
881 cpy_log_exception("python initialization");
882 return 1;
883 }
884 sys_path = PyObject_GetAttrString(sys, "path"); /* New reference. */
885 Py_DECREF(sys);
886 if (sys_path == NULL) {
887 cpy_log_exception("python initialization");
888 return 1;
889 }
890 module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */
891 PyModule_AddObject(module, "Config", (void *) &ConfigType); /* Steals a reference. */
892 PyModule_AddObject(module, "Values", (void *) &ValuesType); /* Steals a reference. */
893 PyModule_AddObject(module, "Notification", (void *) &NotificationType); /* Steals a reference. */
894 PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG);
895 PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO);
896 PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE);
897 PyModule_AddIntConstant(module, "LOG_WARNING", LOG_WARNING);
898 PyModule_AddIntConstant(module, "LOG_ERROR", LOG_ERR);
899 PyModule_AddIntConstant(module, "NOTIF_FAILURE", NOTIF_FAILURE);
900 PyModule_AddIntConstant(module, "NOTIF_WARNING", NOTIF_WARNING);
901 PyModule_AddIntConstant(module, "NOTIF_OKAY", NOTIF_OKAY);
902 for (i = 0; i < ci->children_num; ++i) {
903 oconfig_item_t *item = ci->children + i;
905 if (strcasecmp(item->key, "Interactive") == 0) {
906 if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_BOOLEAN)
907 continue;
908 do_interactive = item->values[0].value.boolean;
909 } else if (strcasecmp(item->key, "Encoding") == 0) {
910 if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_STRING)
911 continue;
912 /* Why is this even necessary? And undocumented? */
913 if (PyUnicode_SetDefaultEncoding(item->values[0].value.string))
914 cpy_log_exception("setting default encoding");
915 } else if (strcasecmp(item->key, "LogTraces") == 0) {
916 if (item->values_num != 1 || item->values[0].type != OCONFIG_TYPE_BOOLEAN)
917 continue;
918 if (!item->values[0].value.boolean) {
919 Py_XDECREF(cpy_format_exception);
920 cpy_format_exception = NULL;
921 continue;
922 }
923 if (cpy_format_exception)
924 continue;
925 tb = PyImport_ImportModule("traceback"); /* New reference. */
926 if (tb == NULL) {
927 cpy_log_exception("python initialization");
928 continue;
929 }
930 cpy_format_exception = PyObject_GetAttrString(tb, "format_exception"); /* New reference. */
931 Py_DECREF(tb);
932 if (cpy_format_exception == NULL)
933 cpy_log_exception("python initialization");
934 } else if (strcasecmp(item->key, "ModulePath") == 0) {
935 char *dir = NULL;
936 PyObject *dir_object;
938 if (cf_util_get_string(item, &dir) != 0)
939 continue;
940 dir_object = PyString_FromString(dir); /* New reference. */
941 if (dir_object == NULL) {
942 ERROR("python plugin: Unable to convert \"%s\" to "
943 "a python object.", dir);
944 free(dir);
945 cpy_log_exception("python initialization");
946 continue;
947 }
948 if (PyList_Append(sys_path, dir_object) != 0) {
949 ERROR("python plugin: Unable to append \"%s\" to "
950 "python module path.", dir);
951 cpy_log_exception("python initialization");
952 }
953 Py_DECREF(dir_object);
954 free(dir);
955 } else if (strcasecmp(item->key, "Import") == 0) {
956 char *module_name = NULL;
957 PyObject *module;
959 if (cf_util_get_string(item, &module_name) != 0)
960 continue;
961 module = PyImport_ImportModule(module_name); /* New reference. */
962 if (module == NULL) {
963 ERROR("python plugin: Error importing module \"%s\".", module_name);
964 cpy_log_exception("importing module");
965 PyErr_Print();
966 }
967 free(module_name);
968 Py_XDECREF(module);
969 } else if (strcasecmp(item->key, "Module") == 0) {
970 char *name = NULL;
971 cpy_callback_t *c;
972 PyObject *ret;
974 if (cf_util_get_string(item, &name) != 0)
975 continue;
976 for (c = cpy_config_callbacks; c; c = c->next) {
977 if (strcasecmp(c->name + 7, name) == 0)
978 break;
979 }
980 if (c == NULL) {
981 WARNING("python plugin: Found a configuration for the \"%s\" plugin, "
982 "but the plugin isn't loaded or didn't register "
983 "a configuration callback.", name);
984 free(name);
985 continue;
986 }
987 free(name);
988 if (c->data == NULL)
989 ret = PyObject_CallFunction(c->callback, "N",
990 cpy_oconfig_to_pyconfig(item, NULL)); /* New reference. */
991 else
992 ret = PyObject_CallFunction(c->callback, "NO",
993 cpy_oconfig_to_pyconfig(item, NULL), c->data); /* New reference. */
994 if (ret == NULL)
995 cpy_log_exception("loading module");
996 else
997 Py_DECREF(ret);
998 } else {
999 WARNING("python plugin: Ignoring unknown config key \"%s\".", item->key);
1000 }
1001 }
1002 Py_DECREF(sys_path);
1003 return 0;
1004 }
1006 void module_register(void) {
1007 plugin_register_complex_config("python", cpy_config);
1008 plugin_register_init("python", cpy_init);
1009 plugin_register_shutdown("python", cpy_shutdown);
1010 }