Is there any chance to get updated python module upstream?
Paul P Komkoff Jr
i at stingr.net
Fri Mar 2 21:04:08 CET 2007
Replying to Peter Nixon:
> I have started doing some tests with the python module. Please send me your
> patches. If they work, I would be happy to commit them to cvs...
I am sitting for more than a year in production with this patch
applied.
Basically, it's a complete rewrite, using latest python C api docs.
--
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
--- a/src/modules/rlm_python/rlm_python.c
+++ b/src/modules/rlm_python/rlm_python.c
@@ -33,6 +33,11 @@
#include "modules.h"
#include "conffile.h"
+#define Pyx_BLOCK_THREADS {PyGILState_STATE __gstate = PyGILState_Ensure();
+#define Pyx_UNBLOCK_THREADS PyGILState_Release(__gstate);}
+
+
+
static const char rcsid[] = "$Id: rlm_python.c,v 1.6 2004/02/26 19:04:34 aland Exp $";
/*
@@ -42,55 +47,43 @@ static const char rcsid[] = "$Id: rlm_py
* a lot cleaner to do so, and a pointer to the structure can
* be used as the instance handle.
*/
-typedef struct rlm_python_t {
- /* Config section */
- /* Names of modules */
- char
- *mod_instantiate,
- *mod_authorize,
- *mod_authenticate,
- *mod_preacct,
- *mod_accounting,
- *mod_checksimul,
- *mod_detach,
-
- /* Names of functions */
- *func_instantiate,
- *func_authorize,
- *func_authenticate,
- *func_preacct,
- *func_accounting,
- *func_checksimul,
- *func_detach;
-
-
- /* End Config section */
-
-
- /* Python objects for modules */
- PyObject
- *pModule_builtin,
- *pModule_instantiate,
- *pModule_authorize,
- *pModule_authenticate,
- *pModule_preacct,
- *pModule_accounting,
- *pModule_checksimul,
- *pModule_detach,
-
-
- /* Functions */
-
- *pFunc_instantiate,
- *pFunc_authorize,
- *pFunc_authenticate,
- *pFunc_preacct,
- *pFunc_accounting,
- *pFunc_checksimul,
- *pFunc_detach;
+struct rlm_python_t {
+ char *mod_instantiate;
+ char *mod_authorize;
+ char *mod_authenticate;
+ char *mod_preacct;
+ char *mod_accounting;
+ char *mod_checksimul;
+ char *mod_detach;
+
+ /* Names of functions */
+ char *func_instantiate;
+ char *func_authorize;
+ char *func_authenticate;
+ char *func_preacct;
+ char *func_accounting;
+ char *func_checksimul;
+ char *func_detach;
+
+ PyObject *pModule_instantiate;
+ PyObject *pModule_authorize;
+ PyObject *pModule_authenticate;
+ PyObject *pModule_preacct;
+ PyObject *pModule_accounting;
+ PyObject *pModule_checksimul;
+ PyObject *pModule_detach;
+
+ /* Functions */
+ PyObject *pFunc_instantiate;
+ PyObject *pFunc_authorize;
+ PyObject *pFunc_authenticate;
+ PyObject *pFunc_preacct;
+ PyObject *pFunc_accounting;
+ PyObject *pFunc_checksimul;
+ PyObject *pFunc_detach;
+};
-} rlm_python_t;
/*
* A mapping of configuration file names to internal variables.
@@ -103,50 +96,78 @@ typedef struct rlm_python_t {
*/
static CONF_PARSER module_config[] = {
{ "mod_instantiate", PW_TYPE_STRING_PTR,
- offsetof(rlm_python_t, mod_instantiate), NULL, NULL},
+ offsetof(struct rlm_python_t, mod_instantiate), NULL, NULL},
{ "func_instantiate", PW_TYPE_STRING_PTR,
- offsetof(rlm_python_t, func_instantiate), NULL, NULL},
+ offsetof(struct rlm_python_t, func_instantiate), NULL, NULL},
{ "mod_authorize", PW_TYPE_STRING_PTR,
- offsetof(rlm_python_t, mod_authorize), NULL, NULL},
+ offsetof(struct rlm_python_t, mod_authorize), NULL, NULL},
{ "func_authorize", PW_TYPE_STRING_PTR,
- offsetof(rlm_python_t, func_authorize), NULL, NULL},
+ offsetof(struct rlm_python_t, func_authorize), NULL, NULL},
{ "mod_authenticate", PW_TYPE_STRING_PTR,
- offsetof(rlm_python_t, mod_authenticate), NULL, NULL},
+ offsetof(struct rlm_python_t, mod_authenticate), NULL, NULL},
{ "func_authenticate", PW_TYPE_STRING_PTR,
- offsetof(rlm_python_t, func_authenticate), NULL, NULL},
+ offsetof(struct rlm_python_t, func_authenticate), NULL, NULL},
{ "mod_preacct", PW_TYPE_STRING_PTR,
- offsetof(rlm_python_t, mod_preacct), NULL, NULL},
+ offsetof(struct rlm_python_t, mod_preacct), NULL, NULL},
{ "func_preacct", PW_TYPE_STRING_PTR,
- offsetof(rlm_python_t, func_preacct), NULL, NULL},
+ offsetof(struct rlm_python_t, func_preacct), NULL, NULL},
{ "mod_accounting", PW_TYPE_STRING_PTR,
- offsetof(rlm_python_t, mod_accounting), NULL, NULL},
+ offsetof(struct rlm_python_t, mod_accounting), NULL, NULL},
{ "func_accounting", PW_TYPE_STRING_PTR,
- offsetof(rlm_python_t, func_accounting), NULL, NULL},
+ offsetof(struct rlm_python_t, func_accounting), NULL, NULL},
{ "mod_checksimul", PW_TYPE_STRING_PTR,
- offsetof(rlm_python_t, mod_checksimul), NULL, NULL},
+ offsetof(struct rlm_python_t, mod_checksimul), NULL, NULL},
{ "func_checksimul", PW_TYPE_STRING_PTR,
- offsetof(rlm_python_t, func_checksimul), NULL, NULL},
+ offsetof(struct rlm_python_t, func_checksimul), NULL, NULL},
{ "mod_detach", PW_TYPE_STRING_PTR,
- offsetof(rlm_python_t, mod_detach), NULL, NULL},
+ offsetof(struct rlm_python_t, mod_detach), NULL, NULL},
{ "func_detach", PW_TYPE_STRING_PTR,
- offsetof(rlm_python_t, func_detach), NULL, NULL},
+ offsetof(struct rlm_python_t, func_detach), NULL, NULL},
{ NULL, -1, 0, NULL, NULL } /* end the list */
};
+static struct {
+ const char* name;
+ int value;
+} radiusd_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 },
+};
+
+
+/* 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(const PyObject *self, PyObject *args) {
+static PyObject *python_radlog(const PyObject *module, PyObject *args) {
int status;
char *msg;
@@ -154,409 +175,333 @@ static PyObject *radlog_py(const PyObjec
return NULL;
}
- radlog(status, msg);
+ radlog(status, "%s", msg);
+ Py_INCREF(Py_None);
+
return Py_None;
}
static PyMethodDef radiusd_methods[] = {
- {"radlog", (PyCFunction)radlog_py, METH_VARARGS, "freeradius radlog()."},
+ {"radlog", (PyCFunction) &python_radlog, METH_VARARGS, "freeradius radlog()."},
{NULL, NULL, 0, NULL}
};
-/*
- * Do any per-module initialization. e.g. set up connections
- * to external databases, read configuration files, set up
- * dictionary entries, etc.
- *
- * Try to avoid putting too much stuff in here - it's better to
- * do it in instantiate() where it is not global.
- */
-static int python_init(void)
-{
- /*
- * Initialize Python interpreter. Fatal error if this fails.
- */
- Py_Initialize();
-
- radlog(L_DBG, "python_init done");
+static void python_error() {
+ PyObject *pType = NULL;
+ PyObject *pValue = NULL;
+ PyObject *pTraceback = NULL;
+ PyObject *pStr1 = NULL;
+ PyObject *pStr2 = NULL;
- return 0;
-}
-
-/* Extract string representation of Python error. */
-static void python_error(void) {
- PyObject *pType, *pValue, *pTraceback, *pStr1, *pStr2;
+ Pyx_BLOCK_THREADS
PyErr_Fetch(&pType, &pValue, &pTraceback);
- pStr1 = PyObject_Str(pType);
- pStr2 = PyObject_Str(pValue);
-
- radlog(L_ERR, "%s: %s\n",
- PyString_AsString(pStr1), PyString_AsString(pStr2));
-}
-
-/* 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;
-
- /* If the Python function gave us None for the tuple, then just return. */
- if (pValue == Py_None) {
- return;
- }
-
- if (!PyTuple_Check(pValue)) {
- radlog(L_ERR, "%s: non-tuple passed", function_name);
- }
-
- /* 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);
- }
- }
+ 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
}
-/* 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?
- */
-
-static int python_function(REQUEST *request,
- PyObject *pFunc, const char *function_name)
+static int python_init()
{
-#define BUF_SIZE 1024
+ int i;
- char buf[BUF_SIZE]; /* same size as vp_print buffer */
+ Py_SetProgramName("radiusd");
- VALUE_PAIR *vp;
-
- PyObject *pValue, *pValuePairContainer, **pValueHolder, **pValueHolderPtr;
- int i, n_tuple, return_value;
-
- /* Return with "OK, continue" if the function is not defined. */
- if (pFunc == NULL) {
- return RLM_MODULE_OK;
- }
-
- /* 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.
- */
+ Py_Initialize();
- if (NULL == (pValueHolder = pValueHolderPtr =
- malloc(sizeof(PyObject *) * n_tuple))) {
+ PyEval_InitThreads(); // This also grabs a lock
- radlog(L_ERR, "%s: malloc of %d bytes failed\n",
- function_name, sizeof(PyObject *) * n_tuple);
+ if ((radiusd_module = Py_InitModule3("radiusd", radiusd_methods, "FreeRADIUS Module.")) == NULL)
+ goto failed;
- return -1;
- }
+ 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 (n_tuple == 0) {
- pValuePairContainer = Py_None;
- }
- else {
- pValuePairContainer = PyTuple_New(n_tuple);
+ PyEval_ReleaseLock(); // Drop lock grabbed by InitThreads
- i = 0;
- for (vp = request->packet->vps; vp; vp = vp->next) {
- PyObject *pValuePair, *pString1, *pString2;
-
- /* The inside tuple has two only: */
- pValuePair = PyTuple_New(2);
-
- /* 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);
- }
-
- pString1 = PyString_FromString(buf);
- PyTuple_SetItem(pValuePair, 0, pString1);
-
-
- /* 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);
-
- /* Put the tuple inside the container */
- PyTuple_SetItem(pValuePairContainer, i++, pValuePair);
-
- /* Store the pointer in our malloc() storage */
- *pValueHolderPtr++ = pValuePair;
- }
- }
+ radlog(L_DBG, "python_init done");
+ return 0;
- /* Call Python function.
- */
+failed:
+ python_error();
+ Py_Finalize();
+ return -1;
+}
- if (pFunc && PyCallable_Check(pFunc)) {
- PyObject *pArgs;
+static int python_destroy() {
+ Pyx_BLOCK_THREADS
+ Py_XDECREF(radiusd_module);
+ Py_Finalize();
+ Pyx_UNBLOCK_THREADS
- /* call the function with a singleton tuple containing the
- * container tuple.
- */
-
- if ((pArgs = PyTuple_New(1)) == NULL) {
- radlog(L_ERR, "%s: could not create tuple", function_name);
- return -1;
- }
- if ((PyTuple_SetItem(pArgs, 0, pValuePairContainer)) != 0) {
- radlog(L_ERR, "%s: could not set tuple item", function_name);
- return -1;
- }
-
- if ((pValue = PyObject_CallObject(pFunc, pArgs)) == NULL) {
- radlog(L_ERR, "%s: function call failed", function_name);
- python_error();
- return -1;
- }
-
- /* 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);
- }
-
-
- /* Decrease reference counts for the argument and return tuple */
- Py_DECREF(pArgs);
- Py_DECREF(pValue);
- }
+ return 0;
+}
- /* 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++;
- }
- free(pValueHolder);
- Py_DECREF(pValuePairContainer);
+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);
+ }
+ }
+}
- /* 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?
- */
+/* 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 ((return_value == RLM_MODULE_REJECT) && (request != NULL)) {
- pairfree(&(request->reply->vps));
- }
+static int python_function(REQUEST *request, PyObject *pFunc, const char *funcname) {
+ char buf[1024];
+ 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;
+ PyObject *pStr;
+ /* The inside tuple has two only: */
+ if ((pPair = PyTuple_New(2)) == NULL)
+ goto failed;
+ /* Put the tuple inside the container */
+ PyTuple_SET_ITEM(pArgs, i, pPair);
+ /* The name. logic from vp_prints, lib/print.c */
+ if (vp->flags.has_tag)
+ snprintf(buf, sizeof(buf), "%s:%d", vp->name, vp->flags.tag);
+ else
+ strcpy(buf, vp->name);
+ if ((pStr = PyString_FromString(buf)) == NULL)
+ goto failed;
+ PyTuple_SET_ITEM(pPair, 0, pStr);
+ vp_prints_value(buf, sizeof(buf), vp, 1);
+ if ((pStr = PyString_FromString(buf)) == NULL)
+ goto failed;
+ PyTuple_SET_ITEM(pPair, 1, pStr);
+ }
+ }
+
+ /* Call Python function. */
+ pRet = PyObject_CallFunctionObjArgs(pFunc, pArgs, NULL);
+
+ if (pRet == NULL)
+ goto failed;
+
+ if (request == NULL)
+ goto okay;
+ /* 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_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 the specified by the Python module */
- return return_value;
+ return -1;
}
-
-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 },
-};
-
/*
* 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;
- }
- }
- return 0;
+static int python_load_function(char *module, const char *func, PyObject **pModule, PyObject **pFunc) {
+ const char funcname[] = "python_load_function";
+ PyGILState_STATE gstate;
+
+ *pFunc = NULL;
+ *pModule = NULL;
+ gstate = PyGILState_Ensure();
+
+ if (module != NULL && func != NULL) {
+ if ((*pModule = PyImport_ImportModule(module)) == NULL) {
+ radlog(L_ERR, "rlm_python:%s: module '%s' is not found", funcname, module);
+ goto failed;
+ }
+ if ((*pFunc = PyObject_GetAttrString(*pModule, func)) == NULL) {
+ radlog(L_ERR, "rlm_python:%s: function '%s.%s' is not found", funcname, module, func);
+ goto failed;
+ }
+ if (!PyCallable_Check(*pFunc)) {
+ radlog(L_ERR, "rlm_python:%s: function '%s.%s' is not callable", funcname, module, func);
+ goto failed;
+ }
+ }
+ PyGILState_Release(gstate);
+ return 0;
+failed:
+ python_error();
+ radlog(L_ERR, "rlm_python:%s: failed to import python function '%s.%s'", funcname, module, func);
+ Py_XDECREF(*pFunc);
+ *pFunc = NULL;
+ Py_XDECREF(*pModule);
+ PyGILState_Release(gstate);
+ *pModule = NULL;
+ return -1;
+}
+
+static void python_objclear(PyObject **ob) {
+ if (*ob != NULL) {
+ Pyx_BLOCK_THREADS
+ Py_DECREF(*ob);
+ Pyx_UNBLOCK_THREADS
+ *ob = NULL;
+ }
+}
+
+static void python_instance_clear(struct rlm_python_t *data) {
+ python_objclear(&data->pFunc_instantiate);
+ python_objclear(&data->pFunc_authorize);
+ python_objclear(&data->pFunc_authenticate);
+ python_objclear(&data->pFunc_preacct);
+ python_objclear(&data->pFunc_accounting);
+ python_objclear(&data->pFunc_checksimul);
+ python_objclear(&data->pFunc_detach);
+
+ python_objclear(&data->pModule_instantiate);
+ python_objclear(&data->pModule_authorize);
+ python_objclear(&data->pModule_authenticate);
+ python_objclear(&data->pModule_preacct);
+ python_objclear(&data->pModule_accounting);
+ python_objclear(&data->pModule_checksimul);
+ python_objclear(&data->pModule_detach);
}
-
/*
* Do any per-module initialization that is separate to each
* configured instance of the module. e.g. set up connections
@@ -568,107 +513,94 @@ static int load_python_function(const ch
* 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;
-
- /*
- * 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;
- }
+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 the configuration parameters can't be parsed, then
+ * fail.
+ */
+ if (cf_section_parse(conf, data, module_config) < 0) {
+ free(data);
+ return -1;
+ }
+
+ /*
+ * Import user modules.
+ */
+ if (python_load_function(data->mod_instantiate,
+ data->func_instantiate,
+ &data->pModule_instantiate,
+ &data->pFunc_instantiate) < 0)
+ goto failed;
+
+ if (python_load_function(data->mod_authenticate,
+ data->func_authenticate,
+ &data->pModule_authenticate,
+ &data->pFunc_authenticate) < 0)
+ goto failed;
+
+ if (python_load_function(data->mod_authorize,
+ data->func_authorize,
+ &data->pModule_authorize,
+ &data->pFunc_authorize) < 0)
+ goto failed;
+
+ if (python_load_function(data->mod_preacct,
+ data->func_preacct,
+ &data->pModule_preacct,
+ &data->pFunc_preacct) < 0)
+ goto failed;
+
+ if (python_load_function(data->mod_accounting,
+ data->func_accounting,
+ &data->pModule_accounting,
+ &data->pFunc_accounting) < 0)
+ goto failed;
+
+ if (python_load_function(data->mod_checksimul,
+ data->func_checksimul,
+ &data->pModule_checksimul,
+ &data->pFunc_checksimul) < 0)
+ goto failed;
+
+ if (python_load_function(data->mod_detach,
+ data->func_detach,
+ &data->pModule_detach,
+ &data->pFunc_detach) < 0)
+ goto failed;
+
+ *instance = data;
+ /* Call the instantiate function. No request. Use the return value. */
+
+ return python_function(NULL, data->pFunc_instantiate, "instantiate");
+failed:
+ python_error();
+ python_instance_clear(data);
+ return -1;
+}
+
+static int python_detach(void *instance) {
+ struct rlm_python_t *data = (struct rlm_python_t *) instance;
+ int ret;
- /*
- * 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;
- }
+ ret = python_function(NULL, data->pFunc_detach, "detach");
- /*
- * 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");
- }
-
-
- /*
- * Import user modules.
- */
-
- if (load_python_function(data->mod_instantiate, data->func_instantiate,
- &data->pModule_instantiate, &data->pFunc_instantiate)==-1) {
- /* TODO: check if we need to cleanup data */
- return -1;
- }
+ python_instance_clear(data);
- if (load_python_function(data->mod_authenticate, data->func_authenticate,
- &data->pModule_authenticate, &data->pFunc_authenticate)==-1) {
- /* TODO: check if we need to cleanup data */
- return -1;
- }
-
- if (load_python_function(data->mod_authorize, data->func_authorize,
- &data->pModule_authorize, &data->pFunc_authorize)==-1) {
- /* TODO: check if we need to cleanup data */
- return -1;
- }
-
- if (load_python_function(data->mod_preacct, data->func_preacct,
- &data->pModule_preacct, &data->pFunc_preacct)==-1) {
- /* TODO: check if we need to cleanup data */
- return -1;
- }
-
- if (load_python_function(data->mod_accounting, data->func_accounting,
- &data->pModule_accounting, &data->pFunc_accounting)==-1) {
- /* TODO: check if we need to cleanup data */
- return -1;
- }
-
- if (load_python_function(data->mod_checksimul, data->func_checksimul,
- &data->pModule_checksimul, &data->pFunc_checksimul)==-1) {
- /* TODO: check if we need to cleanup data */
- return -1;
- }
-
- if (load_python_function(data->mod_detach, data->func_detach,
- &data->pModule_detach, &data->pFunc_detach)==-1) {
- /* TODO: check if we need to cleanup data */
- return -1;
- }
-
- *instance=data;
-
- /* Call the instantiate function. No request. Use the return value. */
- return python_function(NULL, data->pFunc_instantiate, "instantiate");
+ free(data);
+ return ret;
}
+
/* Wrapper functions */
static int python_authorize(void *instance, REQUEST *request)
{
@@ -710,57 +642,6 @@ static int python_checksimul(void *insta
}
-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;
-}
-
/*
* The module name should be the only globally exported symbol.
* That is, everything else should be 'static'.
@@ -786,5 +667,5 @@ module_t rlm_python = {
NULL /* post-auth */
},
python_detach, /* detach */
- NULL, /* destroy */
+ python_destroy, /* destroy */
};
More information about the Freeradius-Devel
mailing list