Rewrite python_function wrapper at once --- commit 97c0260f921dabc7d751a99c5ca7f07a1f916e8a tree e0b6eab89ae865662aa5a09447fd7ffe805d90a1 parent 8b5cb6f9d73fc724c0ad822fc83523e13d84801e author <stingray@boxster64.stingr.net> Sun, 29 Jan 2006 15:36:23 +0300 committer <stingray@boxster64.stingr.net> Sun, 29 Jan 2006 15:36:23 +0300 src/modules/rlm_python/rlm_python.c | 331 +++++++++++++---------------------- 1 files changed, 125 insertions(+), 206 deletions(-) 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 @@ -295,214 +295,133 @@ static void python_vptuple(VALUE_PAIR ** * 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) -{ -#define BUF_SIZE 1024 - - char buf[BUF_SIZE]; /* same size as vp_print buffer */ - - 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. - */ - - if (NULL == (pValueHolder = pValueHolderPtr = - malloc(sizeof(PyObject *) * n_tuple))) { - - radlog(L_ERR, "%s: malloc of %d bytes failed\n", - function_name, sizeof(PyObject *) * n_tuple); - - return -1; - } - - if (n_tuple == 0) { - pValuePairContainer = Py_None; - } - else { - pValuePairContainer = PyTuple_New(n_tuple); - - 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; - } - } - - - /* 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) { - 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 */ - python_vptuple(&request->reply->vps, - PyTuple_GetItem(pValue, 1), function_name); - - /* Config item tuple */ - python_vptuple(&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); - } - - /* 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); - - /* 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)); - } - - /* Return the specified by the Python module */ - return return_value; +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++; + } + 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. */ + gstate = PyGILState_Ensure(); + + pRet = PyObject_CallFunctionObjArgs(pFunc, pArgs, NULL); + + PyGILState_Release(gstate); + + 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); + return ret; +failed: + python_error(); + Py_XDECREF(pArgs); + Py_XDECREF(pRet); + return -1; } - /* * Import a user module and load a function from it */ -- Paul P 'Stingray' Komkoff Jr // http://stingr.net/key <- my pgp key This message represents the official view of the voices in my head