Porting rlm_python to 2.0.0
Paul P Komkoff Jr
i at stingr.net
Fri Feb 1 23:05:50 CET 2008
Replying to Alan DeKok:
> A global variable is OK...
OK, I'm almost done.
This should work. It countains various TODOs all over the place (which
I will close later). I ran couple of trivial tests but not much,
however, if there's anyone here struggling to make their python app
working with 2.0 - please give this a try.
Alan, can you please apply this to 2.0 and HEAD? Since this module is
marked stable now, I want it to be fixed asap...
My development git tree is at
git://arcane.stingr.net/git/freeradius-dev, branch rlm_python
Also accessible via gitweb at
http://arcane.stingr.net/git/?p=freeradius-dev/.git;a=shortlog;h=rlm_python
--
Paul P 'Stingray' Komkoff Jr // http://stingr.net/key <- my pgp key
This message represents the official view of the voices in my head
-------------- next part --------------
diff --git a/src/modules/rlm_python/rlm_python.c b/src/modules/rlm_python/rlm_python.c
index b3fcd5a..9c5636a 100644
--- a/src/modules/rlm_python/rlm_python.c
+++ b/src/modules/rlm_python/rlm_python.c
@@ -16,47 +16,44 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*
- * Copyright 2000,2006 The FreeRADIUS server project
+ * Copyright 2000 The FreeRADIUS server project
* Copyright 2002 Miguel A.L. Paraz <mparaz at mparaz.com>
* Copyright 2002 Imperium Technology, Inc.
+ * - rewritten by Paul P. Komkoff Jr <i at stingr.net>
*/
#include <freeradius-devel/ident.h>
RCSID("$Id$")
-
#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/modules.h>
#include <Python.h>
-/*
- * Define a structure for our module configuration.
- *
- * These variables do not need to be in a structure, but it's
- * a lot cleaner to do so, and a pointer to the structure can
- * be used as the instance handle.
- */
-typedef struct rlm_python_t {
+#define Pyx_BLOCK_THREADS {PyGILState_STATE __gstate = PyGILState_Ensure();
+#define Pyx_UNBLOCK_THREADS PyGILState_Release(__gstate);}
-#define RLM_PYTHON_STRUCT(foo) char *mod_##foo; \
- char *func_##foo; \
- PyObject *pModule_##foo; \
- PyObject *pFunc_##foo
-
- RLM_PYTHON_STRUCT(instantiate);
- RLM_PYTHON_STRUCT(authorize);
- RLM_PYTHON_STRUCT(authenticate);
- RLM_PYTHON_STRUCT(preacct);
- RLM_PYTHON_STRUCT(accounting);
- RLM_PYTHON_STRUCT(checksimul);
- RLM_PYTHON_STRUCT(preproxy);
- RLM_PYTHON_STRUCT(postproxy);
- RLM_PYTHON_STRUCT(postauth);
- RLM_PYTHON_STRUCT(detach);
-
- PyObject *pModule_builtin;
+/* TODO: The only needed thing here is function. Anything else is required for
+ * initialization only. I will remove it, putting a symbolic constant here instead.
+ */
-} rlm_python_t;
+struct py_function_def {
+ PyObject *module;
+ PyObject *function;
+
+ char* module_name;
+ char* function_name;
+};
+
+struct rlm_python_t {
+ struct py_function_def
+ instantiate,
+ authorize,
+ authenticate,
+ preacct,
+ accounting,
+ checksimul,
+ detach;
+};
/*
* A mapping of configuration file names to internal variables.
@@ -67,429 +64,458 @@ typedef struct rlm_python_t {
* to the strdup'd string into 'config.string'. This gets around
* buffer over-flows.
*/
-#define RLM_PYTHON_CONF(foo) { "mod_"#foo, PW_TYPE_STRING_PTR, \
- offsetof(rlm_python_t, mod_##foo), NULL, NULL}, \
- { "func_"#foo, PW_TYPE_STRING_PTR, \
- offsetof(rlm_python_t, func_##foo), NULL, NULL}
-
-static const CONF_PARSER module_config[] = {
- RLM_PYTHON_CONF(instantiate),
- RLM_PYTHON_CONF(authorize),
- RLM_PYTHON_CONF(authenticate),
- RLM_PYTHON_CONF(preacct),
- RLM_PYTHON_CONF(accounting),
- RLM_PYTHON_CONF(checksimul),
- RLM_PYTHON_CONF(preproxy),
- RLM_PYTHON_CONF(postproxy),
- RLM_PYTHON_CONF(postauth),
- RLM_PYTHON_CONF(detach),
-
- { NULL, -1, 0, NULL, NULL } /* end the list */
+static CONF_PARSER module_config[] = {
+
+#define A(x) { "mod_" #x, PW_TYPE_STRING_PTR, offsetof(struct rlm_python_t, x.module_name) }, \
+ { "func_" #x, PW_TYPE_STRING_PTR, offsetof(struct rlm_python_t, x.function_name) },
+
+ A(instantiate)
+ A(authorize)
+ A(authenticate)
+ A(preacct)
+ A(accounting)
+ A(checksimul)
+ A(detach)
+
+#undef A
+
+ { NULL, -1, 0, NULL, NULL } /* end the list */
+};
+
+static struct {
+ const char *name;
+ int value;
+} radiusd_constants[] = {
+
+#define A(x) { #x, x },
+
+ A(L_DBG)
+ A(L_AUTH)
+ A(L_INFO)
+ A(L_ERR)
+ A(L_PROXY)
+ A(L_CONS)
+ A(RLM_MODULE_REJECT)
+ A(RLM_MODULE_FAIL)
+ A(RLM_MODULE_OK)
+ A(RLM_MODULE_HANDLED)
+ A(RLM_MODULE_INVALID)
+ A(RLM_MODULE_USERLOCK)
+ A(RLM_MODULE_NOTFOUND)
+ A(RLM_MODULE_NOOP)
+ A(RLM_MODULE_UPDATED)
+ A(RLM_MODULE_NUMCODES)
+
+#undef A
+
+ { 0 },
};
+
+/* Let assume that radiusd module is only one since we have only one intepreter */
+
+static PyObject *radiusd_module = NULL;
+
/*
* radiusd Python functions
*/
/* radlog wrapper */
-static PyObject *radlog_py(UNUSED const PyObject *self, PyObject *args) {
- int status;
- char *msg;
+static PyObject *python_radlog(PyObject *module, PyObject *args) {
+ int status;
+ char *msg;
- if (!PyArg_ParseTuple(args, "is", &status, &msg)) {
- return NULL;
- }
+ if (!PyArg_ParseTuple(args, "is", &status, &msg)) {
+ return NULL;
+ }
- radlog(status, msg);
- return Py_None;
+ radlog(status, "%s", msg);
+ Py_INCREF(Py_None);
+
+ return Py_None;
}
static PyMethodDef radiusd_methods[] = {
- {"radlog", (PyCFunction)radlog_py, METH_VARARGS, "freeradius radlog()."},
- {NULL, NULL, 0, NULL}
+ { "radlog", &python_radlog, METH_VARARGS,
+ "radiusd.radlog(level, msg)\n\n" \
+ "Print a message using radiusd logging system. level should be one of the\n" \
+ "constants L_DBG, L_AUTH, L_INFO, L_ERR, L_PROXY, L_CONS\n"
+ },
+ { 0 },
};
+static void python_error() {
+ PyObject
+ *pType = NULL,
+ *pValue = NULL,
+ *pTraceback = NULL,
+ *pStr1 = NULL,
+ *pStr2 = NULL;
+
+ Pyx_BLOCK_THREADS
+
+ PyErr_Fetch(&pType, &pValue, &pTraceback);
+ if (pType == NULL || pValue == NULL)
+ goto failed;
+ if ((pStr1 = PyObject_Str(pType)) == NULL || (pStr2 = PyObject_Str(pValue)) == NULL)
+ goto failed;
+ radlog(L_ERR, "rlm_python:EXCEPT:%s: %s", PyString_AsString(pStr1), PyString_AsString(pStr2));
+
+failed:
+ Py_XDECREF(pStr1);
+ Py_XDECREF(pStr2);
+ Py_XDECREF(pType);
+ Py_XDECREF(pValue);
+ Py_XDECREF(pTraceback);
+
+ Pyx_UNBLOCK_THREADS
+}
-/* Extract string representation of Python error. */
-static void python_error(void) {
- PyObject *pType, *pValue, *pTraceback, *pStr1, *pStr2;
+static int python_init_really() {
+ int i;
- PyErr_Fetch(&pType, &pValue, &pTraceback);
- pStr1 = PyObject_Str(pType);
- pStr2 = PyObject_Str(pValue);
+ Py_SetProgramName("radiusd");
+ Py_Initialize();
+ PyEval_InitThreads(); // This also grabs a lock
- radlog(L_ERR, "%s: %s\n",
- PyString_AsString(pStr1), PyString_AsString(pStr2));
-}
+ if ((radiusd_module = Py_InitModule3("radiusd", radiusd_methods,
+ "FreeRADIUS Module.")) == NULL)
+ goto failed;
-/* Tuple to value pair conversion */
-static void add_vp_tuple(VALUE_PAIR **vpp, PyObject *pValue,
- const char *function_name) {
- int i, outertuplesize;
- VALUE_PAIR *vp;
+ for (i = 0; radiusd_constants[i].name; i++)
+ if ((PyModule_AddIntConstant(radiusd_module, radiusd_constants[i].name,
+ radiusd_constants[i].value)) < 0)
+ goto failed;
- /* If the Python function gave us None for the tuple, then just return. */
- if (pValue == Py_None) {
- return;
- }
+ PyEval_ReleaseLock(); // Drop lock grabbed by InitThreads
- if (!PyTuple_Check(pValue)) {
- radlog(L_ERR, "%s: non-tuple passed", function_name);
- }
+ radlog(L_DBG, "python_init done");
+ return 0;
- /* Get the tuple size. */
- outertuplesize = PyTuple_Size(pValue);
-
- for (i = 0; i < outertuplesize; i++) {
- PyObject *pTupleElement = PyTuple_GetItem(pValue, i);
-
- if ((pTupleElement != NULL) &&
- (PyTuple_Check(pTupleElement))) {
-
- /* Check if it's a pair */
- int tuplesize;
-
- if ((tuplesize = PyTuple_Size(pTupleElement)) != 2) {
- radlog(L_ERR, "%s: tuple element %d is a tuple "
- " of size %d. must be 2\n", function_name,
- i, tuplesize);
- }
- else {
- PyObject *pString1, *pString2;
-
- pString1 = PyTuple_GetItem(pTupleElement, 0);
- pString2 = PyTuple_GetItem(pTupleElement, 1);
-
- /* xxx PyString_Check does not compile here */
- if ((pString1 != NULL) &&
- (pString2 != NULL) &&
- PyObject_TypeCheck(pString1,&PyString_Type) &&
- PyObject_TypeCheck(pString2,&PyString_Type)) {
-
-
- const char *s1, *s2;
-
- /* pairmake() will convert and find any
- * errors in the pair.
- */
-
- s1 = PyString_AsString(pString1);
- s2 = PyString_AsString(pString2);
-
- if ((s1 != NULL) && (s2 != NULL)) {
- radlog(L_DBG, "%s: %s = %s ",
- function_name, s1, s2);
-
- /* xxx Might need to support other T_OP */
- vp = pairmake(s1, s2, T_OP_EQ);
- if (vp != NULL) {
- pairadd(vpp, vp);
- radlog(L_DBG, "%s: s1, s2 OK\n",
- function_name);
- }
- else {
- radlog(L_DBG, "%s: s1, s2 FAILED\n",
- function_name);
- }
- }
- else {
- radlog(L_ERR, "%s: string conv failed\n",
- function_name);
- }
-
- }
- else {
- radlog(L_ERR, "%s: tuple element %d must be "
- "(string, string)", function_name, i);
- }
- }
- }
- else {
- radlog(L_ERR, "%s: tuple element %d is not a tuple\n",
- function_name, i);
- }
- }
+failed:
+ python_error();
+ Py_XDECREF(radiusd_module);
+ radiusd_module = NULL;
+ Py_Finalize();
+ return -1;
+}
+static int python_init() {
+ if (!radiusd_module)
+ return python_init_really();
+
+ return 0;
}
-/* This is the core Python function that the others wrap around.
- * Pass the value-pair print strings in a tuple.
- * xxx We're not checking the errors. If we have errors, what do we do?
- */
+#if 0
-static int python_function(REQUEST *request,
- PyObject *pFunc, const char *function_name)
-{
-#define BUF_SIZE 1024
+static int python_destroy() {
+ Pyx_BLOCK_THREADS
+ Py_XDECREF(radiusd_module);
+ Py_Finalize();
+ Pyx_UNBLOCK_THREADS
- char buf[BUF_SIZE]; /* same size as vp_print buffer */
+ return 0;
+}
- VALUE_PAIR *vp;
+/* This will need reconsidering in a future. Maybe we'll need to have our own
+ * reference counting for radiusd_module
+ */
- PyObject *pValue, *pValuePairContainer, **pValueHolder, **pValueHolderPtr;
- int i, n_tuple, return_value;
+#endif
- /* Return with "OK, continue" if the function is not defined. */
- if (pFunc == NULL) {
- return RLM_MODULE_OK;
- }
+/* TODO: Convert this function to accept any iterable objects? */
+
+static void python_vptuple(VALUE_PAIR **vpp, PyObject *pValue, const char *funcname) {
+ int i;
+ int tuplesize;
+ VALUE_PAIR *vp;
+
+ /* If the Python function gave us None for the tuple, then just return. */
+ if (pValue == Py_None)
+ return;
+
+ if (!PyTuple_CheckExact(pValue)) {
+ radlog(L_ERR, "rlm_python:%s: non-tuple passed", funcname);
+ return;
+ }
+ /* Get the tuple tuplesize. */
+ tuplesize = PyTuple_GET_SIZE(pValue);
+ for (i = 0; i < tuplesize; i++) {
+ PyObject *pTupleElement = PyTuple_GET_ITEM(pValue, i);
+ PyObject *pStr1;
+ PyObject *pStr2;
+ int pairsize;
+ const char *s1;
+ const char *s2;
+
+ if (!PyTuple_CheckExact(pTupleElement)) {
+ radlog(L_ERR, "rlm_python:%s: tuple element %d is not a tuple", funcname, i);
+ continue;
+ }
+ /* Check if it's a pair */
+ if ((pairsize = PyTuple_GET_SIZE(pTupleElement)) != 2) {
+ radlog(L_ERR, "rlm_python:%s: tuple element %d is a tuple of size %d. Must be 2", funcname, i, pairsize);
+ continue;
+ }
+ pStr1 = PyTuple_GET_ITEM(pTupleElement, 0);
+ pStr2 = PyTuple_GET_ITEM(pTupleElement, 1);
+ if ((!PyString_CheckExact(pStr1)) || (!PyString_CheckExact(pStr2))) {
+ radlog(L_ERR, "rlm_python:%s: tuple element %d must be as (str, str)", funcname, i);
+ continue;
+ }
+ s1 = PyString_AsString(pStr1);
+ s2 = PyString_AsString(pStr2);
+ /* xxx Might need to support other T_OP */
+ vp = pairmake(s1, s2, T_OP_EQ);
+ if (vp != NULL) {
+ pairadd(vpp, vp);
+ radlog(L_DBG, "rlm_python:%s: '%s' = '%s'", funcname, s1, s2);
+ } else {
+ radlog(L_DBG, "rlm_python:%s: Failed: '%s' = '%s'", funcname, s1, s2);
+ }
+ }
+}
- /* Default return value is "OK, continue" */
- return_value = RLM_MODULE_OK;
-
- /* We will pass a tuple containing (name, value) tuples
- * We can safely use the Python function to build up a tuple,
- * since the tuple is not used elsewhere.
- *
- * Determine the size of our tuple by walking through the packet.
- * If request is NULL, pass None.
- */
- n_tuple = 0;
-
- if (request != NULL) {
- for (vp = request->packet->vps; vp; vp = vp->next) {
- n_tuple++;
- }
- }
- /* Create the tuple and a holder for the pointers, so that we can
- * decref more efficiently later without the overhead of reading
- * the tuple.
- *
- * We use malloc() instead of the Python memory allocator since we
- * are not embedded.
- */
+/* This is the core Python function that the others wrap around.
+ * Pass the value-pair print strings in a tuple.
+ * xxx We're not checking the errors. If we have errors, what do we do?
+ */
- if (NULL == (pValueHolder = pValueHolderPtr =
- malloc(sizeof(PyObject *) * n_tuple))) {
+static int python_populate_vptuple(PyObject *pPair, VALUE_PAIR *vp) {
+ PyObject*
+ pStr = NULL;
- radlog(L_ERR, "%s: malloc of %d bytes failed\n",
- function_name, sizeof(PyObject *) * n_tuple);
+ char buf[1024];
- return -1;
- }
+ /* Look at the vp_print_name? */
- if (n_tuple == 0) {
- pValuePairContainer = Py_None;
- }
- else {
- pValuePairContainer = PyTuple_New(n_tuple);
+ if (vp->flags.has_tag)
+ pStr = PyString_FromFormat("%s:%d", vp->name, vp->flags.tag);
+ else
+ pStr = PyString_FromString(vp->name);
- i = 0;
- for (vp = request->packet->vps; vp; vp = vp->next) {
- PyObject *pValuePair, *pString1, *pString2;
+ if (pStr == NULL)
+ goto failed;
- /* The inside tuple has two only: */
- pValuePair = PyTuple_New(2);
+ PyTuple_SET_ITEM(pPair, 0, pStr);
- /* The name. logic from vp_prints, lib/print.c */
- if (vp->flags.has_tag) {
- snprintf(buf, BUF_SIZE, "%s:%d", vp->name, vp->flags.tag);
- }
- else {
- strcpy(buf, vp->name);
- }
+ vp_prints_value(buf, sizeof(buf), vp, 1);
- pString1 = PyString_FromString(buf);
- PyTuple_SetItem(pValuePair, 0, pString1);
+ if ((pStr = PyString_FromString(buf)) == NULL)
+ goto failed;
+ PyTuple_SET_ITEM(pPair, 1, pStr);
- /* The value. Use delimiter - don't know what that means */
- vp_prints_value(buf, sizeof(buf), vp, 1);
- pString2 = PyString_FromString(buf);
- PyTuple_SetItem(pValuePair, 1, pString2);
+ return 0;
- /* Put the tuple inside the container */
- PyTuple_SetItem(pValuePairContainer, i++, pValuePair);
+failed:
+ return -1;
+}
- /* Store the pointer in our malloc() storage */
- *pValueHolderPtr++ = pValuePair;
- }
+static int python_function(REQUEST *request, PyObject *pFunc, const char *funcname) {
+ VALUE_PAIR *vp;
+ PyObject *pRet = NULL;
+ PyObject *pArgs = NULL;
+ int tuplelen;
+ int ret;
+
+ PyGILState_STATE gstate;
+
+ /* Return with "OK, continue" if the function is not defined. */
+ if (pFunc == NULL)
+ return RLM_MODULE_OK;
+
+ /* Default return value is "OK, continue" */
+ ret = RLM_MODULE_OK;
+
+ /* We will pass a tuple containing (name, value) tuples
+ * We can safely use the Python function to build up a tuple,
+ * since the tuple is not used elsewhere.
+ *
+ * Determine the size of our tuple by walking through the packet.
+ * If request is NULL, pass None.
+ */
+ tuplelen = 0;
+ if (request != NULL) {
+ for (vp = request->packet->vps; vp; vp = vp->next)
+ tuplelen++;
+ }
+
+ gstate = PyGILState_Ensure();
+
+ if (tuplelen == 0) {
+ Py_INCREF(Py_None);
+ pArgs = Py_None;
+ } else {
+ int i = 0;
+ if ((pArgs = PyTuple_New(tuplelen)) == NULL)
+ goto failed;
+ for (vp = request->packet->vps; vp != NULL; vp = vp->next, i++) {
+ PyObject *pPair;
+
+ /* The inside tuple has two only: */
+ if ((pPair = PyTuple_New(2)) == NULL)
+ goto failed;
+
+ if (python_populate_vptuple(pPair, vp) == 0) {
+ /* Put the tuple inside the container */
+ PyTuple_SET_ITEM(pArgs, i, pPair);
+ } else {
+ Py_INCREF(Py_None);
+ PyTuple_SET_ITEM(pArgs, i, Py_None);
+ Py_DECREF(pPair);
+ }
+ }
+ }
+
+ /* Call Python function. */
+ pRet = PyObject_CallFunctionObjArgs(pFunc, pArgs, NULL);
+
+ if (pRet == NULL)
+ goto failed;
+
+ if (request == NULL)
+ goto okay;
+ /* The function returns either:
+ * 1. (returnvalue, replyTuple, configTuple), where
+ * - returnvalue is one of the constants RLM_*
+ * - replyTuple and configTuple are tuples of string tuples of size 2
+ *
+ * 2. the function return value alone
+ *
+ * 3. None - default return value is set
+ *
+ * xxx This code is messy!
+ */
+ if (PyTuple_CheckExact(pRet)) {
+ PyObject *pTupleInt;
+
+ if (PyTuple_GET_SIZE(pRet) != 3) {
+ radlog(L_ERR, "rlm_python:%s: tuple must be (return, replyTuple, configTuple)", funcname);
+ goto failed;
}
+ pTupleInt = PyTuple_GET_ITEM(pRet, 0);
+ if (!PyInt_CheckExact(pTupleInt)) {
+ radlog(L_ERR, "rlm_python:%s: first tuple element not an integer", funcname);
+ goto failed;
+ }
+ /* Now have the return value */
+ ret = PyInt_AsLong(pTupleInt);
+ /* Reply item tuple */
+ python_vptuple(&request->reply->vps, PyTuple_GET_ITEM(pRet, 1), funcname);
+ /* Config item tuple */
+ python_vptuple(&request->config_items, PyTuple_GET_ITEM(pRet, 2), funcname);
+ } else
+ if (PyInt_CheckExact(pRet)) {
+ /* Just an integer */
+ ret = PyInt_AsLong(pRet);
+ } else
+ if (pRet == Py_None) {
+ /* returned 'None', return value defaults to "OK, continue." */
+ ret = RLM_MODULE_OK;
+ } else {
+ /* Not tuple or None */
+ radlog(L_ERR, "rlm_python:%s: function did not return a tuple or None", funcname);
+ goto failed;
+ }
+
+ if (ret == RLM_MODULE_REJECT && request != NULL)
+ pairfree(&request->reply->vps);
+
+okay:
+ Py_DECREF(pArgs);
+ Py_DECREF(pRet);
+ PyGILState_Release(gstate);
+ return ret;
+
+failed:
+ python_error();
+ Py_XDECREF(pArgs);
+ Py_XDECREF(pRet);
+ PyGILState_Release(gstate);
+
+ return -1;
+}
- /* Call Python function.
- */
-
- if (pFunc && PyCallable_Check(pFunc)) {
- PyObject *pArgs;
-
- /* call the function with a singleton tuple containing the
- * container tuple.
- */
-
- if ((pArgs = PyTuple_New(1)) == NULL) {
- Py_DECREF(pValuePairContainer);
- free(pValueHolder);
- radlog(L_ERR, "%s: could not create tuple", function_name);
- return -1;
- }
- if ((PyTuple_SetItem(pArgs, 0, pValuePairContainer)) != 0) {
- Py_DECREF(pValuePairContainer);
- free(pValueHolder);
- Py_DECREF(pArgs);
- radlog(L_ERR, "%s: could not set tuple item", function_name);
- return -1;
- }
-
- if ((pValue = PyObject_CallObject(pFunc, pArgs)) == NULL) {
- Py_DECREF(pValuePairContainer);
- free(pValueHolder);
- Py_DECREF(pArgs);
- radlog(L_ERR, "%s: function call failed", function_name);
- python_error();
- return -1;
- }
+/*
+ * Import a user module and load a function from it
+ */
- /* The function returns either:
- * 1. tuple containing the integer return value,
- * then the integer reply code (or None to not set),
- * then the string tuples to build the reply with.
- * (returnvalue, (p1, s1), (p2, s2))
- *
- * 2. the function return value alone
- *
- * 3. None - default return value is set
- *
- * xxx This code is messy!
- */
-
- if (PyTuple_Check(pValue)) {
- PyObject *pTupleInt;
-
- if (PyTuple_Size(pValue) != 3) {
- radlog(L_ERR, "%s: tuple must be " \
- "(return, replyTuple, configTuple)",
- function_name);
-
- }
- else {
- pTupleInt = PyTuple_GetItem(pValue, 0);
-
- if ((pTupleInt == NULL) || !PyInt_Check(pTupleInt)) {
- radlog(L_ERR, "%s: first tuple element not an integer",
- function_name);
- }
- else {
- /* Now have the return value */
- return_value = PyInt_AsLong(pTupleInt);
-
- /* Reply item tuple */
- add_vp_tuple(&request->reply->vps,
- PyTuple_GetItem(pValue, 1), function_name);
-
- /* Config item tuple */
- add_vp_tuple(&request->config_items,
- PyTuple_GetItem(pValue, 2), function_name);
- }
- }
- }
- else if (PyInt_Check(pValue)) {
- /* Just an integer */
- return_value = PyInt_AsLong(pValue);
- }
- else if (pValue == Py_None) {
- /* returned 'None', return value defaults to "OK, continue." */
- return_value = RLM_MODULE_OK;
- }
- else {
- /* Not tuple or None */
- radlog(L_ERR, "%s function did not return a tuple or None\n",
- function_name);
- }
+static int python_load_function(struct py_function_def * def) {
+ const char *funcname = "python_load_function";
+ PyGILState_STATE gstate;
+ gstate = PyGILState_Ensure();
- /* Decrease reference counts for the argument and return tuple */
- Py_DECREF(pArgs);
- Py_DECREF(pValue);
+ if (def->module_name != NULL && def->function_name != NULL) {
+ if ((def->module = PyImport_ImportModule(def->module_name)) == NULL) {
+ radlog(L_ERR, "rlm_python:%s: module '%s' is not found", funcname, def->module_name);
+ goto failed;
}
- /* Decrease reference count for the tuples passed, the
- * container tuple, and the return value.
- */
-
- pValueHolderPtr = pValueHolder;
- i = n_tuple;
- while (i--) {
- /* Can't write as pValueHolderPtr since Py_DECREF is a macro */
- Py_DECREF(*pValueHolderPtr);
- pValueHolderPtr++;
+ if ((def->function = PyObject_GetAttrString(def->module, def->function_name)) == NULL) {
+ radlog(L_ERR, "rlm_python:%s: function '%s.%s' is not found", funcname, def->module_name, def->function_name);
+ goto failed;
}
- free(pValueHolder);
- Py_DECREF(pValuePairContainer);
-
- /* pDict and pFunc are borrowed and must not be Py_DECREF-ed */
- /* Free pairs if we are rejecting.
- * xxx Shouldn't the core do that?
- */
-
- if ((return_value == RLM_MODULE_REJECT) && (request != NULL)) {
- pairfree(&(request->reply->vps));
+ if (!PyCallable_Check(def->function)) {
+ radlog(L_ERR, "rlm_python:%s: function '%s.%s' is not callable", funcname, def->module_name, def->function_name);
+ goto failed;
}
+ }
+ PyGILState_Release(gstate);
+ return 0;
+
+failed:
+ python_error();
+ radlog(L_ERR, "rlm_python:%s: failed to import python function '%s.%s'", funcname, def->module_name, def->function_name);
+ Py_XDECREF(def->function);
+ def->function = NULL;
+ Py_XDECREF(def->module);
+ def->module = NULL;
+ PyGILState_Release(gstate);
+ return -1;
+}
- /* Return the specified by the Python module */
- return return_value;
+static void python_objclear(PyObject **ob) {
+ if (*ob != NULL) {
+ Pyx_BLOCK_THREADS
+ Py_DECREF(*ob);
+ Pyx_UNBLOCK_THREADS
+ *ob = NULL;
+ }
}
+static void free_and_null(char **p) {
+ if (*p != NULL) {
+ free(*p);
+ *p = NULL;
+ }
+}
-static struct varlookup {
- const char* name;
- int value;
-} constants[] = {
- { "L_DBG", L_DBG },
- { "L_AUTH", L_AUTH },
- { "L_INFO", L_INFO },
- { "L_ERR", L_ERR },
- { "L_PROXY", L_PROXY },
- { "L_CONS", L_CONS },
- { "RLM_MODULE_REJECT", RLM_MODULE_REJECT },
- { "RLM_MODULE_FAIL", RLM_MODULE_FAIL },
- { "RLM_MODULE_OK", RLM_MODULE_OK },
- { "RLM_MODULE_HANDLED", RLM_MODULE_HANDLED },
- { "RLM_MODULE_INVALID", RLM_MODULE_INVALID },
- { "RLM_MODULE_USERLOCK",RLM_MODULE_USERLOCK },
- { "RLM_MODULE_NOTFOUND",RLM_MODULE_NOTFOUND },
- { "RLM_MODULE_NOOP", RLM_MODULE_NOOP },
- { "RLM_MODULE_UPDATED", RLM_MODULE_UPDATED },
- { "RLM_MODULE_NUMCODES",RLM_MODULE_NUMCODES },
- { NULL, 0 },
-};
+static void python_funcdef_clear(struct py_function_def *def) {
+ python_objclear(&def->function);
+ python_objclear(&def->module);
+ free_and_null(&def->function_name);
+ free_and_null(&def->module_name);
+}
-/*
- * Import a user module and load a function from it
- */
-static int load_python_function(const char* module, const char* func,
- PyObject** pyModule, PyObject** pyFunc) {
-
- if ((module==NULL) || (func==NULL)) {
- *pyFunc=NULL;
- *pyModule=NULL;
- } else {
- PyObject *pName;
-
- pName = PyString_FromString(module);
- Py_INCREF(pName);
- *pyModule = PyImport_Import(pName);
- Py_DECREF(pName);
- if (*pyModule != NULL) {
- PyObject *pDict;
-
- pDict = PyModule_GetDict(*pyModule);
- /* pDict: borrowed reference */
-
- *pyFunc = PyDict_GetItemString(pDict, func);
- /* pFunc: Borrowed reference */
- } else {
- python_error();
-
- radlog(L_ERR, "Failed to import python module \"%s\"\n", module);
- return -1;
- }
- }
+static void python_instance_clear(struct rlm_python_t *data) {
- return 0;
-}
+#define A(x) python_funcdef_clear(&data->x);
+ A(instantiate)
+ A(authorize)
+ A(authenticate)
+ A(preacct)
+ A(accounting)
+ A(checksimul)
+ A(detach)
+
+#undef A
+}
/*
* Do any per-module initialization that is separate to each
@@ -502,152 +528,80 @@ static int load_python_function(const char* module, const char* func,
* in *instance otherwise put a null pointer there.
*
*/
-static int python_instantiate(CONF_SECTION *conf, void **instance)
-{
- rlm_python_t *data;
- PyObject *module;
- int idx;
-
- /*
- * Initialize Python interpreter. Fatal error if this fails.
- */
- Py_Initialize();
-
- /*
- * Set up a storage area for instance data
- */
- data = rad_malloc(sizeof(*data));
- if (!data) {
- return -1;
- }
- memset(data, 0, sizeof(*data));
-
- /*
- * If the configuration parameters can't be parsed, then
- * fail.
- */
- if (cf_section_parse(conf, data, module_config) < 0) {
- free(data);
- return -1;
- }
-
-
- /*
- * Setup our 'radiusd' module.
- */
-
- /* Code */
- if ((module = data->pModule_builtin =
- Py_InitModule3("radiusd", radiusd_methods,
- "FreeRADIUS Module.")) == NULL) {
-
- radlog(L_ERR, "Python Py_InitModule3 failed");
- free(data);
- return -1;
- }
-
- /*
- * Load constants into module
- */
- for (idx=0; constants[idx].name; idx++)
- if ((PyModule_AddIntConstant(module, constants[idx].name, constants[idx].value)) == -1) {
-
- radlog(L_ERR, "Python AddIntConstant failed");
- }
+static int python_instantiate(CONF_SECTION *conf, void **instance) {
+ struct rlm_python_t *data = NULL;
+
+ /*
+ * Set up a storage area for instance data
+ */
+ if ((data = malloc(sizeof(*data))) == NULL)
+ return -1;
+ bzero(data, sizeof(*data));
+
+
+
+ if (python_init() != 0) {
+ free(data);
+ return -1;
+ }
+
+ /*
+ * If the configuration parameters can't be parsed, then
+ * fail.
+ */
+ if (cf_section_parse(conf, data, module_config) < 0) {
+ free(data);
+ return -1;
+ }
+
+#define A(x) { if (python_load_function(&data->x) < 0) \
+ goto failed; }
+
+ A(instantiate)
+ A(authenticate)
+ A(authorize)
+ A(preacct)
+ A(accounting)
+ A(checksimul)
+ A(detach)
+
+#undef A
+
+ *instance = data;
+ /* Call the instantiate function. No request. Use the return value. */
+
+ return python_function(NULL, data->instantiate.function, "instantiate");
+failed:
+ python_error();
+ python_instance_clear(data);
+ free(data);
+ return -1;
+}
- /*
- * Import user modules.
- */
-#define RLM_PYTHON_LOAD(foo) if (load_python_function(data->mod_##foo, data->func_##foo, \
- &data->pModule_##foo, &data->pFunc_##foo)==-1) { \
- /* TODO: check if we need to cleanup data */ \
- return -1; \
- }
+static int python_detach(void *instance) {
+ struct rlm_python_t *data = (struct rlm_python_t *) instance;
+ int ret;
- RLM_PYTHON_LOAD(instantiate);
- RLM_PYTHON_LOAD(authenticate);
- RLM_PYTHON_LOAD(authorize);
- RLM_PYTHON_LOAD(preacct);
- RLM_PYTHON_LOAD(accounting);
- RLM_PYTHON_LOAD(checksimul);
- RLM_PYTHON_LOAD(preproxy);
- RLM_PYTHON_LOAD(postproxy);
- RLM_PYTHON_LOAD(detach);
+ ret = python_function(NULL, data->detach.function, "detach");
- *instance=data;
+ python_instance_clear(data);
- /* Call the instantiate function. No request. Use the return value. */
- return python_function(NULL, data->pFunc_instantiate, "instantiate");
+ free(data);
+ return ret;
}
-#define RLM_PYTHON_FUNC(foo) static int python_##foo(void *instance, REQUEST *request) \
-{ \
- return python_function(request, \
- ((struct rlm_python_t *)instance)->pFunc_##foo, \
- #foo); \
+#define A(x) static int python_##x(void *instance, REQUEST *request) { \
+ return python_function(request, ((struct rlm_python_t *)instance)->x.function, #x); \
}
+A(authenticate)
+A(authorize)
+A(preacct)
+A(accounting)
+A(checksimul)
-RLM_PYTHON_FUNC(authorize)
-RLM_PYTHON_FUNC(authenticate)
-RLM_PYTHON_FUNC(preacct)
-RLM_PYTHON_FUNC(accounting)
-RLM_PYTHON_FUNC(checksimul)
-RLM_PYTHON_FUNC(preproxy)
-RLM_PYTHON_FUNC(postproxy)
-RLM_PYTHON_FUNC(postauth)
-
-static int python_detach(void *instance)
-{
- int return_value;
-
- /* Default return value is failure */
- return_value = -1;
-
- if (((rlm_python_t *)instance)->pFunc_detach &&
- PyCallable_Check(((rlm_python_t *)instance)->pFunc_detach)) {
-
- PyObject *pArgs, *pValue;
-
- /* call the function with an empty tuple */
-
- pArgs = PyTuple_New(0);
- pValue = PyObject_CallObject(((rlm_python_t *)instance)->pFunc_detach,
- pArgs);
-
- if (pValue == NULL) {
- python_error();
- return -1;
- }
- else {
- if (!PyInt_Check(pValue)) {
- radlog(L_ERR, "detach: return value not an integer");
- }
- else {
- return_value = PyInt_AsLong(pValue);
- }
- }
-
- /* Decrease reference counts for the argument and return tuple */
- Py_DECREF(pArgs);
- Py_DECREF(pValue);
- }
-
- free(instance);
-
-#if 0
- /* xxx test delete module object so it will be reloaded later.
- * xxx useless since we can't SIGHUP reliably, anyway.
- */
- PyObject_Del(((struct rlm_python_t *)instance)->pModule_accounting);
-#endif
-
- radlog(L_DBG, "python_detach done");
-
- /* Return the specified by the Python module */
- return return_value;
-}
+#undef A
/*
* The module name should be the only globally exported symbol.
@@ -663,15 +617,15 @@ module_t rlm_python = {
"python",
RLM_TYPE_THREAD_SAFE, /* type */
python_instantiate, /* instantiation */
- python_detach, /* detach */
+ python_detach,
{
python_authenticate, /* authentication */
python_authorize, /* authorization */
python_preacct, /* preaccounting */
python_accounting, /* accounting */
python_checksimul, /* checksimul */
- python_preproxy, /* pre-proxy */
- python_postproxy, /* post-proxy */
- python_postauth /* post-auth */
- },
+ NULL, /* pre-proxy */
+ NULL, /* post-proxy */
+ NULL /* post-auth */
+ }
};
More information about the Freeradius-Devel
mailing list