+typedef int reg_function_t(const char *name, void *callback, void *data);
+
+static PyObject *cpy_register_generic_userdata(void *reg, void *handler, PyObject *args, PyObject *kwds) {
+ char buf[512];
+ reg_function_t *register_function = (reg_function_t *) reg;
+ cpy_callback_t *c = NULL;
+ user_data_t *user_data = NULL;
+ const char *name = NULL;
+ PyObject *callback = NULL, *data = NULL;
+ static char *kwlist[] = {"callback", "data", "name", NULL};
+
+ if (PyArg_ParseTupleAndKeywords(args, kwds, "O|Oet", kwlist, &callback, &data, NULL, &name) == 0) return NULL;
+ if (PyCallable_Check(callback) == 0) {
+ PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
+ return NULL;
+ }
+ cpy_build_name(buf, sizeof(buf), callback, name);
+
+ Py_INCREF(callback);
+ Py_XINCREF(data);
+ c = malloc(sizeof(*c));
+ c->name = strdup(buf);
+ c->callback = callback;
+ c->data = data;
+ c->next = NULL;
+ user_data = malloc(sizeof(*user_data));
+ user_data->free_func = cpy_destroy_user_data;
+ user_data->data = c;
+ register_function(buf, handler, user_data);
+ return cpy_string_to_unicode_or_bytes(buf);
+}
+
+static PyObject *cpy_register_read(PyObject *self, PyObject *args, PyObject *kwds) {
+ char buf[512];
+ cpy_callback_t *c = NULL;
+ user_data_t *user_data = NULL;
+ double interval = 0;
+ const char *name = NULL;
+ PyObject *callback = NULL, *data = NULL;
+ struct timespec ts;
+ static char *kwlist[] = {"callback", "interval", "data", "name", NULL};
+
+ if (PyArg_ParseTupleAndKeywords(args, kwds, "O|dOet", kwlist, &callback, &interval, &data, NULL, &name) == 0) return NULL;
+ if (PyCallable_Check(callback) == 0) {
+ PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object.");
+ return NULL;
+ }
+ cpy_build_name(buf, sizeof(buf), callback, name);
+
+ Py_INCREF(callback);
+ Py_XINCREF(data);
+ c = malloc(sizeof(*c));
+ c->name = strdup(buf);
+ c->callback = callback;
+ c->data = data;
+ c->next = NULL;
+ user_data = malloc(sizeof(*user_data));
+ user_data->free_func = cpy_destroy_user_data;
+ user_data->data = c;
+ ts.tv_sec = interval;
+ ts.tv_nsec = (interval - ts.tv_sec) * 1000000000;
+ plugin_register_complex_read(/* group = */ NULL, buf,
+ cpy_read_callback, &ts, user_data);
+ return cpy_string_to_unicode_or_bytes(buf);
+}
+
+static PyObject *cpy_register_log(PyObject *self, PyObject *args, PyObject *kwds) {
+ return cpy_register_generic_userdata((void *) plugin_register_log,
+ (void *) cpy_log_callback, args, kwds);
+}
+
+static PyObject *cpy_register_write(PyObject *self, PyObject *args, PyObject *kwds) {
+ return cpy_register_generic_userdata((void *) plugin_register_write,
+ (void *) cpy_write_callback, args, kwds);
+}
+
+static PyObject *cpy_register_notification(PyObject *self, PyObject *args, PyObject *kwds) {
+ return cpy_register_generic_userdata((void *) plugin_register_notification,
+ (void *) cpy_notification_callback, args, kwds);
+}
+
+static PyObject *cpy_register_flush(PyObject *self, PyObject *args, PyObject *kwds) {
+ return cpy_register_generic_userdata((void *) plugin_register_flush,
+ (void *) cpy_flush_callback, args, kwds);
+}
+
+static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args, PyObject *kwds) {
+ return cpy_register_generic(&cpy_shutdown_callbacks, args, kwds);
+}
+
+static PyObject *cpy_error(PyObject *self, PyObject *args) {
+ const char *text;
+ if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
+ Py_BEGIN_ALLOW_THREADS
+ plugin_log(LOG_ERR, "%s", text);
+ Py_END_ALLOW_THREADS
+ Py_RETURN_NONE;
+}
+
+static PyObject *cpy_warning(PyObject *self, PyObject *args) {
+ const char *text;
+ if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
+ Py_BEGIN_ALLOW_THREADS
+ plugin_log(LOG_WARNING, "%s", text);
+ Py_END_ALLOW_THREADS
+ Py_RETURN_NONE;
+}
+
+static PyObject *cpy_notice(PyObject *self, PyObject *args) {
+ const char *text;
+ if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
+ Py_BEGIN_ALLOW_THREADS
+ plugin_log(LOG_NOTICE, "%s", text);
+ Py_END_ALLOW_THREADS
+ Py_RETURN_NONE;
+}
+
+static PyObject *cpy_info(PyObject *self, PyObject *args) {
+ const char *text;
+ if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
+ Py_BEGIN_ALLOW_THREADS
+ plugin_log(LOG_INFO, "%s", text);
+ Py_END_ALLOW_THREADS
+ Py_RETURN_NONE;
+}
+
+static PyObject *cpy_debug(PyObject *self, PyObject *args) {
+#ifdef COLLECT_DEBUG
+ const char *text;
+ if (PyArg_ParseTuple(args, "et", NULL, &text) == 0) return NULL;
+ Py_BEGIN_ALLOW_THREADS
+ plugin_log(LOG_DEBUG, "%s", text);
+ Py_END_ALLOW_THREADS
+#endif
+ Py_RETURN_NONE;
+}
+
+static PyObject *cpy_unregister_generic(cpy_callback_t **list_head, PyObject *arg, const char *desc) {
+ char buf[512];
+ const char *name;
+ cpy_callback_t *prev = NULL, *tmp;
+
+ Py_INCREF(arg);
+ name = cpy_unicode_or_bytes_to_string(&arg);
+ if (name == NULL) {
+ PyErr_Clear();
+ if (!PyCallable_Check(arg)) {
+ PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
+ Py_DECREF(arg);
+ return NULL;
+ }
+ cpy_build_name(buf, sizeof(buf), arg, NULL);
+ name = buf;
+ }
+ for (tmp = *list_head; tmp; prev = tmp, tmp = tmp->next)
+ if (strcmp(name, tmp->name) == 0)
+ break;
+
+ Py_DECREF(arg);
+ if (tmp == NULL) {
+ PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
+ return NULL;
+ }
+ /* Yes, this is actually save. To call this function the caller has to
+ * hold the GIL. Well, save as long as there is only one GIL anyway ... */
+ if (prev == NULL)
+ *list_head = tmp->next;
+ else
+ prev->next = tmp->next;
+ cpy_destroy_user_data(tmp);
+ Py_RETURN_NONE;
+}
+
+typedef int cpy_unregister_function_t(const char *name);
+
+static PyObject *cpy_unregister_generic_userdata(cpy_unregister_function_t *unreg, PyObject *arg, const char *desc) {
+ char buf[512];
+ const char *name;
+
+ Py_INCREF(arg);
+ name = cpy_unicode_or_bytes_to_string(&arg);
+ if (name == NULL) {
+ PyErr_Clear();
+ if (!PyCallable_Check(arg)) {
+ PyErr_SetString(PyExc_TypeError, "This function needs a string or a callable object as its only parameter.");
+ Py_DECREF(arg);
+ return NULL;
+ }
+ cpy_build_name(buf, sizeof(buf), arg, NULL);
+ name = buf;
+ }
+ if (unreg(name) == 0) {
+ Py_DECREF(arg);
+ Py_RETURN_NONE;
+ }
+ PyErr_Format(PyExc_RuntimeError, "Unable to unregister %s callback '%s'.", desc, name);
+ Py_DECREF(arg);
+ return NULL;
+}
+
+static PyObject *cpy_unregister_log(PyObject *self, PyObject *arg) {
+ return cpy_unregister_generic_userdata(plugin_unregister_log, arg, "log");
+}
+
+static PyObject *cpy_unregister_init(PyObject *self, PyObject *arg) {
+ return cpy_unregister_generic(&cpy_init_callbacks, arg, "init");
+}
+
+static PyObject *cpy_unregister_config(PyObject *self, PyObject *arg) {
+ return cpy_unregister_generic(&cpy_config_callbacks, arg, "config");
+}
+
+static PyObject *cpy_unregister_read(PyObject *self, PyObject *arg) {
+ return cpy_unregister_generic_userdata(plugin_unregister_read, arg, "read");
+}
+
+static PyObject *cpy_unregister_write(PyObject *self, PyObject *arg) {
+ return cpy_unregister_generic_userdata(plugin_unregister_write, arg, "write");
+}
+
+static PyObject *cpy_unregister_notification(PyObject *self, PyObject *arg) {
+ return cpy_unregister_generic_userdata(plugin_unregister_notification, arg, "notification");
+}
+
+static PyObject *cpy_unregister_flush(PyObject *self, PyObject *arg) {
+ return cpy_unregister_generic_userdata(plugin_unregister_flush, arg, "flush");
+}
+
+static PyObject *cpy_unregister_shutdown(PyObject *self, PyObject *arg) {
+ return cpy_unregister_generic(&cpy_shutdown_callbacks, arg, "shutdown");
+}
+