--- rlm_python.c 2004-02-26 22:04:34.000000000 +0300 +++ /home/stingray/rpmbuild/SOURCES/freeradius-rlm_python.c 2005-10-02 16:29:59.000000000 +0400 @@ -35,6 +35,13 @@ static const char rcsid[] = "$Id: rlm_python.c,v 1.6 2004/02/26 19:04:34 aland Exp $"; +static void python_error(void); +static PyObject *radiusd_module = NULL; + +#if !defined(WITH_THREAD_POOL) +#define WITH_THREAD_POOL +#endif /* WITH_THREAD_POOL */ + /* * Define a structure for our module configuration. * @@ -43,124 +50,109 @@ * 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, - + 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; - *pFunc_instantiate, - *pFunc_authorize, - *pFunc_authenticate, - *pFunc_preacct, - *pFunc_accounting, - *pFunc_checksimul, - *pFunc_detach; +#if defined(WITH_THREAD_POOL) -} rlm_python_t; +static PyInterpreterState *main_interp; +static PyThreadState *main_tstate; -/* - * A mapping of configuration file names to internal variables. - * - * Note that the string is dynamically allocated, so it MUST - * be freed. When the configuration file parse re-reads the string, - * it free's the old one, and strdup's the new one, placing the pointer - * to the strdup'd string into 'config.string'. This gets around - * buffer over-flows. - */ -static CONF_PARSER module_config[] = { - { "mod_instantiate", PW_TYPE_STRING_PTR, - offsetof(rlm_python_t, mod_instantiate), NULL, NULL}, - { "func_instantiate", PW_TYPE_STRING_PTR, - offsetof(rlm_python_t, func_instantiate), NULL, NULL}, - - { "mod_authorize", PW_TYPE_STRING_PTR, - offsetof(rlm_python_t, mod_authorize), NULL, NULL}, - { "func_authorize", PW_TYPE_STRING_PTR, - offsetof(rlm_python_t, func_authorize), NULL, NULL}, - - { "mod_authenticate", PW_TYPE_STRING_PTR, - offsetof(rlm_python_t, mod_authenticate), NULL, NULL}, - { "func_authenticate", PW_TYPE_STRING_PTR, - offsetof(rlm_python_t, func_authenticate), NULL, NULL}, - - { "mod_preacct", PW_TYPE_STRING_PTR, - offsetof(rlm_python_t, mod_preacct), NULL, NULL}, - { "func_preacct", PW_TYPE_STRING_PTR, - offsetof(rlm_python_t, func_preacct), NULL, NULL}, - - { "mod_accounting", PW_TYPE_STRING_PTR, - offsetof(rlm_python_t, mod_accounting), NULL, NULL}, - { "func_accounting", PW_TYPE_STRING_PTR, - offsetof(rlm_python_t, func_accounting), NULL, NULL}, - - { "mod_checksimul", PW_TYPE_STRING_PTR, - offsetof(rlm_python_t, mod_checksimul), NULL, NULL}, - { "func_checksimul", PW_TYPE_STRING_PTR, - offsetof(rlm_python_t, func_checksimul), NULL, NULL}, - - { "mod_detach", PW_TYPE_STRING_PTR, - offsetof(rlm_python_t, mod_detach), NULL, NULL}, - { "func_detach", PW_TYPE_STRING_PTR, - offsetof(rlm_python_t, func_detach), NULL, NULL}, +static +PyThreadState * +python_thread_start(void) { + PyThreadState *tstate = PyThreadState_New(main_interp); + PyEval_AcquireThread(tstate); + return tstate; +} +static +void +python_thread_stop(PyThreadState *tstate) { + PyThreadState_Clear(tstate); + PyThreadState_DeleteCurrent(); +} + +#endif /* WITH_THREAD_POOL */ - { NULL, -1, 0, NULL, NULL } /* end the list */ -}; /* * radiusd Python functions */ - /* radlog wrapper */ -static PyObject *radlog_py(const PyObject *self, PyObject *args) { - int status; - char *msg; - - if (!PyArg_ParseTuple(args, "is", &status, &msg)) { - return NULL; - } - - radlog(status, msg); - return Py_None; -} +static +PyObject * +python_radlog(PyObject *module, PyObject *args) { + int status; + char *msg; + + if (!PyArg_ParseTuple(args, "is", &status, &msg)) + return NULL; + radlog(status, "%s", msg); + Py_INCREF(Py_None); + return Py_None; +} + +static +PyMethodDef +radiusd_methods[] = { +{ "radlog", (PyCFunction)&python_radlog, METH_VARARGS, "freeradius radlog()."}, +{ NULL, NULL, 0, NULL} +}; -static PyMethodDef radiusd_methods[] = { - {"radlog", (PyCFunction)radlog_py, METH_VARARGS, "freeradius radlog()."}, - {NULL, NULL, 0, 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 }, }; /* @@ -171,246 +163,247 @@ * 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(); +static +int +python_init(void) { + const char funcname[] = "python_init"; + int i; - radlog(L_DBG, "python_init done"); + Py_SetProgramName("radiusd"); + + /* + * Initialize Python interpreter. Fatal error if this fails. + */ + Py_Initialize(); - return 0; + /* + * Setup our 'radiusd' module. + */ + /* Code */ + if ((radiusd_module = Py_InitModule3("radiusd", radiusd_methods, "FreeRADIUS Module.")) == NULL) + goto failed; + + /* + * Load constants into module + */ + for (i = 0; constants[i].name; i++) + if ((PyModule_AddIntConstant(radiusd_module, constants[i].name, constants[i].value)) < 0) + goto failed; +#if defined(WITH_THREAD_POOL) + PyEval_InitThreads(); + /* + * main_tstate - this is clear thread state for new thread states + * main_interp - clear interpretator + */ + main_tstate = PyEval_SaveThread(); + main_interp = main_tstate->interp; + radlog(L_DBG, "rlm_python:%s: Thread Safe mode", funcname); +#else /* WITH_THREAD_POOL */ + radlog(L_DBG, "rlm_python:%s: Single Thread mode", funcname); +#endif /* WITH_THREAD_POOL */ + return 0; +failed: + python_error(); + Py_Finalize(); + return -1; } -/* Extract string representation of Python error. */ -static void python_error(void) { - PyObject *pType, *pValue, *pTraceback, *pStr1, *pStr2; +static +int +python_destroy(void) { +#if defined(WITH_THREAD_POOL) + PyEval_RestoreThread(main_tstate); +#endif /* WITH_THREAD_POOL */ + Py_XDECREF(radiusd_module); + Py_Finalize(); + return 0; +} - PyErr_Fetch(&pType, &pValue, &pTraceback); - pStr1 = PyObject_Str(pType); - pStr2 = PyObject_Str(pValue); +/* + * A mapping of configuration file names to internal variables. + * + * Note that the string is dynamically allocated, so it MUST + * be freed. When the configuration file parse re-reads the string, + * it free's the old one, and strdup's the new one, placing the pointer + * to the strdup'd string into 'config.string'. This gets around + * buffer over-flows. + */ +static CONF_PARSER module_config[] = { +{ "mod_instantiate", PW_TYPE_STRING_PTR, offsetof(rlm_python_t, mod_instantiate), NULL, NULL }, +{ "func_instantiate", PW_TYPE_STRING_PTR, offsetof(rlm_python_t, func_instantiate), NULL, NULL }, +{ "mod_authorize", PW_TYPE_STRING_PTR, offsetof(rlm_python_t, mod_authorize), NULL, NULL }, +{ "func_authorize", PW_TYPE_STRING_PTR, offsetof(rlm_python_t, func_authorize), NULL, NULL }, +{ "mod_authenticate", PW_TYPE_STRING_PTR, offsetof(rlm_python_t, mod_authenticate), NULL, NULL }, +{ "func_authenticate", PW_TYPE_STRING_PTR, offsetof(rlm_python_t, func_authenticate), NULL, NULL }, +{ "mod_preacct", PW_TYPE_STRING_PTR, offsetof(rlm_python_t, mod_preacct), NULL, NULL }, +{ "func_preacct", PW_TYPE_STRING_PTR, offsetof(rlm_python_t, func_preacct), NULL, NULL }, +{ "mod_accounting", PW_TYPE_STRING_PTR, offsetof(rlm_python_t, mod_accounting), NULL, NULL }, +{ "func_accounting", PW_TYPE_STRING_PTR, offsetof(rlm_python_t, func_accounting), NULL, NULL }, +{ "mod_checksimul", PW_TYPE_STRING_PTR, offsetof(rlm_python_t, mod_checksimul), NULL, NULL }, +{ "func_checksimul", PW_TYPE_STRING_PTR, offsetof(rlm_python_t, func_checksimul), NULL, NULL }, +{ "mod_detach", PW_TYPE_STRING_PTR, offsetof(rlm_python_t, mod_detach), NULL, NULL }, +{ "func_detach", PW_TYPE_STRING_PTR, offsetof(rlm_python_t, func_detach), NULL, NULL }, +{ NULL, -1, 0, NULL, NULL } /* end the list */ +}; - radlog(L_ERR, "%s: %s\n", - PyString_AsString(pStr1), PyString_AsString(pStr2)); +/* Extract string representation of Python error. */ +static +void +python_error(void) { + PyObject *pType = NULL; + PyObject *pValue = NULL; + PyObject *pTraceback = NULL; + PyObject *pStr1 = NULL; + PyObject *pStr2 = NULL; + + 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); } /* 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); - } - +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; } - else { - radlog(L_ERR, "%s: tuple element %d must be " - "(string, string)", function_name, i); + /* 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); } - } - } - else { - radlog(L_ERR, "%s: tuple element %d is not a tuple\n", - function_name, i); } - } - } /* 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) -{ -#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. +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; +#if defined(WITH_THREAD_POOL) + PyThreadState *tstate; +#endif /* WITH_THREAD_POOL */ + + /* Return with "OK, continue" if the function is not defined. */ + if (pFunc == NULL) + return RLM_MODULE_OK; + +#if defined(WITH_THREAD_POOL) + if ((tstate = python_thread_start()) == NULL) + return -1; +#endif /* WITH_THREAD_POOL */ + + /* 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. */ - - 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; + 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. */ + if ((pRet = PyObject_CallFunctionObjArgs(pFunc, pArgs, NULL)) == 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)) + * (returnvalue, (p1, s1), (p2, s2)) * * 2. the function return value alone * @@ -418,144 +411,118 @@ * * xxx This code is messy! */ + if (PyTuple_CheckExact(pRet)) { + PyObject *pTupleInt; - 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); + if (PyTuple_GET_SIZE(pRet) != 3) { + radlog(L_ERR, "rlm_python:%s: tuple must be (return, replyTuple, configTuple)", funcname); + goto failed; } - 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); + 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; } - } - } - 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 */ + /* 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(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; + Py_DECREF(pRet); +#if defined(WITH_THREAD_POOL) + python_thread_stop(tstate); +#endif /* WITH_THREAD_POOL */ + return ret; +failed: + python_error(); + Py_XDECREF(pArgs); + Py_XDECREF(pRet); +#if defined(WITH_THREAD_POOL) + python_thread_stop(tstate); +#endif /* WITH_THREAD_POOL */ + 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; +static +int +python_load_function(char *module, const char *func, PyObject **pModule, PyObject **pFunc) { + const char funcname[] = "python_load_function"; + + *pFunc = NULL; + *pModule = NULL; + 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; + } } - } - - return 0; + 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); + *pModule = NULL; + return -1; } +static +void +python_objclear(PyObject **ob) { + if (*ob != NULL) { + Py_DECREF(*ob); + *ob = NULL; + } +} +static +void +python_instance_clear(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 @@ -568,197 +535,157 @@ * 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; +static +int +python_instantiate(CONF_SECTION *conf, void **instance) { + rlm_python_t *data = NULL; +#if defined(WITH_THREAD_POOL) + PyThreadState *tstate; +#endif /* WITH_THREAD_POOL */ - /* + /* * Set up a storage area for instance data */ - data = rad_malloc(sizeof(*data)); - if (!data) { - return -1; - } - memset(data, 0, sizeof(*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; - } - - - /* - * 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"); + if (cf_section_parse(conf, data, module_config) < 0) { + free(data); + return -1; } +#if defined(WITH_THREAD_POOL) + if ((tstate = python_thread_start()) == NULL) { + free(data); + return -1; + } +#endif /* WITH_THREAD_POOL */ - /* - * 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; - } - - 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 */ + /* + * 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. */ +#if defined(WITH_THREAD_POOL) + python_thread_stop(tstate); +#endif /* WITH_THREAD_POOL */ + + return python_function(NULL, data->pFunc_instantiate, "instantiate"); +failed: + python_error(); +#if defined(WITH_THREAD_POOL) + python_thread_stop(tstate); +#endif /* WITH_THREAD_POOL */ + python_instance_clear(data); return -1; - } - - *instance=data; - - /* Call the instantiate function. No request. Use the return value. */ - return python_function(NULL, data->pFunc_instantiate, "instantiate"); -} - -/* Wrapper functions */ -static int python_authorize(void *instance, REQUEST *request) -{ - return python_function(request, - ((struct rlm_python_t *)instance)->pFunc_authorize, - "authorize"); -} - -static int python_authenticate(void *instance, REQUEST *request) -{ - return python_function( - request, - ((struct rlm_python_t *)instance)->pFunc_authenticate, - "authenticate"); } -static int python_preacct(void *instance, REQUEST *request) -{ - return python_function( - request, - ((struct rlm_python_t *)instance)->pFunc_preacct, - "preacct"); -} +static +int +python_detach(void *instance) { + rlm_python_t *data = (struct rlm_python_t *)instance; +#if defined(WITH_THREAD_POOL) + PyThreadState *tstate; +#endif /* WITH_THREAD_POOL */ + int ret; + + ret = python_function(NULL, data->pFunc_detach, "detach"); + +#if defined(WITH_THREAD_POOL) + if ((tstate = python_thread_start()) == NULL) + return -1; +#endif /* WITH_THREAD_POOL */ -static int python_accounting(void *instance, REQUEST *request) -{ - return python_function( - request, - ((struct rlm_python_t *)instance)->pFunc_accounting, - "accounting"); -} + python_instance_clear(data); -static int python_checksimul(void *instance, REQUEST *request) -{ - return python_function( - request, - ((struct rlm_python_t *)instance)->pFunc_checksimul, - "checksimul"); + free(data); +#if defined(WITH_THREAD_POOL) + python_thread_stop(tstate); +#endif /* WITH_THREAD_POOL */ + return ret; } - -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; +/* Wrapper functions */ +static +int +python_authorize(void *instance, REQUEST *request) { + rlm_python_t *data = (struct rlm_python_t *)instance; + return python_function(request, data->pFunc_authorize, "authorize"); +} + +static +int +python_authenticate(void *instance, REQUEST *request) { + rlm_python_t *data = (struct rlm_python_t *)instance; + return python_function(request, data->pFunc_authenticate, "authenticate"); +} + +static +int +python_preacct(void *instance, REQUEST *request) { + rlm_python_t *data = (struct rlm_python_t *)instance; + return python_function(request, data->pFunc_preacct, "preacct"); +} + +static +int +python_accounting(void *instance, REQUEST *request) { + rlm_python_t *data = (struct rlm_python_t *)instance; + return python_function(request, data->pFunc_accounting, "accounting"); +} + +static +int +python_checksimul(void *instance, REQUEST *request) { + rlm_python_t *data = (struct rlm_python_t *)instance; + return python_function(request, data->pFunc_checksimul, "checksimul"); } /* @@ -772,7 +699,11 @@ */ module_t rlm_python = { "python", +#if defined(WITH_THREAD_POOL) RLM_TYPE_THREAD_SAFE, /* type */ +#else /* WITH_THREAD_POOL */ + RLM_TYPE_THREAD_UNSAFE, /* type */ +#endif /* WITH_THREAD_POOL */ python_init, /* initialization */ python_instantiate, /* instantiation */ { @@ -786,5 +717,5 @@ NULL /* post-auth */ }, python_detach, /* detach */ - NULL, /* destroy */ + python_destroy, /* destroy */ };