From f131f0347f58929bdb992ca4dabcb40120757ffc Mon Sep 17 00:00:00 2001 From: Sven Trenkel Date: Sat, 17 Oct 2009 20:06:51 +0200 Subject: [PATCH] Added a workaround to module loading to allow Python scripts to do imports. Redid the python thread handling. Added log callback. --- src/owniptc/libiptc.c | 39 --------------- src/plugin.c | 12 ++++- src/python.c | 114 +++++++++++++++++++++++++++++++++++++++--- 3 files changed, 119 insertions(+), 46 deletions(-) diff --git a/src/owniptc/libiptc.c b/src/owniptc/libiptc.c index f7a6640f..8169f4a6 100644 --- a/src/owniptc/libiptc.c +++ b/src/owniptc/libiptc.c @@ -1488,45 +1488,6 @@ TC_NEXT_RULE(const STRUCT_ENTRY *prev, TC_HANDLE_T *handle) return r->entry; } -/* How many rules in this chain? */ -static unsigned int -TC_NUM_RULES(const char *chain, TC_HANDLE_T *handle) -{ - struct chain_head *c; - iptc_fn = TC_NUM_RULES; - CHECK(*handle); - - c = iptcc_find_label(chain, *handle); - if (!c) { - errno = ENOENT; - return (unsigned int)-1; - } - - return c->num_rules; -} - -static const STRUCT_ENTRY * -TC_GET_RULE(const char *chain, unsigned int n, TC_HANDLE_T *handle) -{ - struct chain_head *c; - struct rule_head *r; - - iptc_fn = TC_GET_RULE; - - CHECK(*handle); - - c = iptcc_find_label(chain, *handle); - if (!c) { - errno = ENOENT; - return NULL; - } - - r = iptcc_get_rule_num(c, n); - if (!r) - return NULL; - return r->entry; -} - /* Returns a pointer to the target name of this position. */ static const char *standard_target_map(int verdict) { diff --git a/src/plugin.c b/src/plugin.c index 5d882e64..97885a3b 100644 --- a/src/plugin.c +++ b/src/plugin.c @@ -280,7 +280,17 @@ static int plugin_load_file (char *file) lt_dlinit (); lt_dlerror (); /* clear errors */ - if ((dlh = lt_dlopen (file)) == NULL) + /* XXX BUG FIXME */ + if (strstr(file, "python") != NULL) { + lt_dladvise advise; + lt_dladvise_init(&advise); + lt_dladvise_global(&advise); + dlh = lt_dlopenadvise(file, advise); + lt_dladvise_destroy(&advise); + } else { + dlh = lt_dlopen (file); + } + if (dlh == NULL) { const char *error = lt_dlerror (); diff --git a/src/python.c b/src/python.c index 8d11d76b..1fda642d 100644 --- a/src/python.c +++ b/src/python.c @@ -14,19 +14,69 @@ typedef struct cpy_callback_s { /* This is our global thread state. Python saves some stuff in thread-local * storage. So if we allow the interpreter to run in the background * (the scriptwriters might have created some threads from python), we have - * to save the state so we can resume it later from a different thread. - - * Technically the Global Interpreter Lock (GIL) and thread states can be - * manipulated independently. But to keep stuff from getting too complex - * we'll just use PyEval_SaveTread and PyEval_RestoreThreas which takes - * care of the thread states as well as the GIL. */ + * to save the state so we can resume it later after shutdown. */ static PyThreadState *state; +/* Serves the same purpose as PyEval_ThreadsInitialized but doesn't require + * Python 2.4. */ + +static int cpy_have_threads; + +/* These two macros are basicly Py_BEGIN_ALLOW_THREADS and Py_BEGIN_ALLOW_THREADS + * from the other direction. If a Python thread calls a C function + * Py_BEGIN_ALLOW_THREADS is used to allow other python threads to run because + * we don't intend to call any Python functions. + * + * These two macros are used whenever a C thread intends to call some Python + * function, usually because some registered callback was triggered. + * Just like Py_BEGIN_ALLOW_THREADS it opens a block so these macros have to be + * used in pairs. They aquire the GIL, create a new Python thread state and swap + * the current thread state with the new one. This means this thread is now allowed + * to execute Python code. */ + +#define CPY_LOCK_THREADS {\ + PyGILState_STATE gil_state;\ + if (cpy_have_threads)\ + gil_state = PyGILState_Ensure(); + +#define CPY_RELEASE_THREADS \ + if (cpy_have_threads)\ + PyGILState_Release(gil_state);\ +} + + static cpy_callback_t *cpy_config_callbacks; static cpy_callback_t *cpy_init_callbacks; static cpy_callback_t *cpy_shutdown_callbacks; +static void cpy_destroy_user_data(void *data) { + cpy_callback_t *c = data; + free(c->name); + Py_DECREF(c->callback); + Py_XDECREF(c->data); + free(c); +} + +static void cpy_log_callback(int severity, const char *message, user_data_t *data) { + cpy_callback_t * c = data->data; + PyObject *ret; + + CPY_LOCK_THREADS + if (c->data == NULL) + ret = PyObject_CallFunction(c->callback, "is", severity, message); /* New reference. */ + else + ret = PyObject_CallFunction(c->callback, "isO", severity, message, c->data); /* New reference. */ + + if (ret == NULL) { + /* FIXME */ + PyErr_Print(); + } else { + Py_DECREF(ret); + } + CPY_RELEASE_THREADS +} + typedef struct { PyObject_HEAD /* No semicolon! */ PyObject *parent; @@ -192,6 +242,43 @@ static PyObject *cpy_register_init(PyObject *self, PyObject *args, PyObject *kwd return cpy_register_generic(&cpy_init_callbacks, args, kwds); } +static PyObject *cpy_register_log(PyObject *self, PyObject *args, PyObject *kwds) { + 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|Oz", kwlist, &callback, &data, &name) == 0) return NULL; + if (PyCallable_Check(callback) == 0) { + PyErr_SetString(PyExc_TypeError, "callback needs a be a callable object."); + return NULL; + } + if (name == NULL) { + PyObject *mod; + + mod = PyObject_GetAttrString(callback, "__module__"); + if (mod != NULL) name = PyString_AsString(mod); + if (name == NULL) { + PyErr_SetString(PyExc_ValueError, "No module name specified and " + "callback function does not have a \"__module__\" attribute."); + return NULL; + } + } + Py_INCREF(callback); + Py_XINCREF(data); + c = malloc(sizeof(*c)); + c->name = strdup(name); + 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; + plugin_register_log(name, cpy_log_callback, user_data); + Py_RETURN_NONE; +} + static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args, PyObject *kwds) { return cpy_register_generic(&cpy_shutdown_callbacks, args, kwds); } @@ -199,28 +286,36 @@ static PyObject *cpy_register_shutdown(PyObject *self, PyObject *args, PyObject static PyObject *cpy_Error(PyObject *self, PyObject *args) { const char *text; if (PyArg_ParseTuple(args, "s", &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, "s", &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, "s", &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, "s", &text) == 0) return NULL; + Py_BEGIN_ALLOW_THREADS plugin_log(LOG_INFO, "%s", text); + Py_END_ALLOW_THREADS Py_RETURN_NONE; } @@ -239,6 +334,7 @@ static PyMethodDef cpy_methods[] = { {"Notice", cpy_Notice, METH_VARARGS, "This is an unhelpful text."}, {"Warning", cpy_Warning, METH_VARARGS, "This is an unhelpful text."}, {"Error", cpy_Error, METH_VARARGS, "This is an unhelpful text."}, + {"register_log", (PyCFunction) cpy_register_log, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."}, {"register_init", (PyCFunction) cpy_register_init, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."}, {"register_config", (PyCFunction) cpy_register_config, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."}, {"register_shutdown", (PyCFunction) cpy_register_shutdown, METH_VARARGS | METH_KEYWORDS, "This is an unhelpful text."}, @@ -272,6 +368,7 @@ static int cpy_init(void) { PyObject *ret; PyEval_InitThreads(); + cpy_have_threads = 1; /* Now it's finally OK to use python threads. */ for (c = cpy_init_callbacks; c; c = c->next) { if (c->data == NULL) @@ -349,6 +446,11 @@ static int cpy_config(oconfig_item_t *ci) { } module = Py_InitModule("collectd", cpy_methods); /* Borrowed reference. */ PyModule_AddObject(module, "Config", (PyObject *) &ConfigType); /* Steals a reference. */ + PyModule_AddIntConstant(module, "LOG_DEBUG", LOG_DEBUG); + PyModule_AddIntConstant(module, "LOG_INFO", LOG_INFO); + PyModule_AddIntConstant(module, "LOG_NOTICE", LOG_NOTICE); + PyModule_AddIntConstant(module, "LOG_WARNING", LOG_WARNING); + PyModule_AddIntConstant(module, "LOG_ERROR", LOG_ERR); for (i = 0; i < ci->children_num; ++i) { oconfig_item_t *item = ci->children + i; -- 2.30.2