From c1de581752a273564d423234143cab3188e091fe Mon Sep 17 00:00:00 2001 From: pacman Date: Thu, 21 Dec 2000 06:31:21 +0000 Subject: [PATCH] This is the "configurable failover" patch, providing a more flexible module calling sequence. --- doc/configurable_failover | 214 ++++++++++++ src/include/modcall.h | 34 ++ src/include/modpriv.h | 34 ++ src/main/Makefile | 9 +- src/main/modcall.c | 803 ++++++++++++++++++++++++++++++++++++++++++++++ src/main/modules.c | 375 ++++------------------ 6 files changed, 1153 insertions(+), 316 deletions(-) create mode 100644 doc/configurable_failover create mode 100644 src/include/modcall.h create mode 100644 src/include/modpriv.h create mode 100644 src/main/modcall.c diff --git a/doc/configurable_failover b/doc/configurable_failover new file mode 100644 index 0000000..cf1755b --- /dev/null +++ b/doc/configurable_failover @@ -0,0 +1,214 @@ +Before configurable failover, we had this: + +authorize { + preprocess + files +} + +which instructed module_authorize to first pass the request through +rlm_preprocess, and if that returned success, pass it through rlm_files, +and if that returned success, module_authorize itself would then return +success. Processing was strictly linear and if one module failed, the whole +function would fail immediately. + +Configurable failover provides more flexibility. It takes advantage of the +tree structure of radiusd.conf to support a config language that allows you +to specify groups of modules that should work together in ways other than +execute-in-order-return-on-fail. Basically you can redesign the flow of +module_authorize to fit your needs, without touching C code, just by altering +radiusd.conf. + +I will soon explain this new language in detail, but first, if you just want +to know how to make failover happen without understanding why it works or how +to tweak it, here's your quick fix: just put a redundant{} block around those +module instances which refer to redundant databases. Example: + + modules { + sql sql1 { + server="myfirstserver.example" + # Insert other necessary parameters here + } + sql sql2 { + server="mysecondserver.example" + # Insert other necessary parameters here + } + } + authenticate { + authtype SQL { + redundant { + sql1 + sql2 + } + } + } + +OK, now for the exhaustive documentation. The things unexpectedly CAPITALIZED +are the key terms... + +The fundamental object is called a MODCALLABLE, because it is something that +can be passed a specific radius request and returns one of the RLM_MODULE_* +results. It is a function - if you can accept the fact that pieces of +radiusd.conf are functions. There are two kinds of MODCALLABLEs: GROUPs and +SINGLEs. + +A SINGLE is a reference to a module instance that was set up in the modules{} +section of radiusd.conf, like "preprocess" or "sql1". When a SINGLE is +called, the corresponding function in the rlm is invoked, and whichever +RLM_MODULE_* it returns becomes the RESULT of the SINGLE. + +A GROUP is a section of radiusd.conf that includes some MODCALLABLEs. +Examples of GROUPs above include "authorize{...}", which implements the C +function module_authorize, and "redundant{...}", which contains two SINGLEs +that refer to a couple of redundant databases. Note that a GROUP can contain +other GROUPs - "authtype SQL{...}" is also a GROUP, which implements the C +function module_authenticate when Auth-Type is set to SQL. + +Now here's the fun part - what happens when a GROUP is called? It simply runs +through all of its children in order, and calls each one, whether it is +another GROUP or a SINGLE. It then looks at the RESULT of that child, and +takes some ACTION, which is basically either "return that RESULT immediately" +or "Keep going". In the first example, any "bad" RESULT from the preprocess +module causes an immediate return, and any "good" RESULT causes the +authorize{...} GROUP to proceed to the files module. + +We can see the exact rules by writing them out the long way: + +authorize { + preprocess { + notfound = 1 + noop = 2 + ok = 3 + updated = 4 + fail = return + reject = return + userlock = return + invalid = return + handled = return + } + files { + notfound = 1 + noop = 2 + ok = 3 + updated = 4 + fail = return + reject = return + userlock = return + invalid = return + handled = return + } +} + +This is the same as the first example, with the default behavior explicitly +spelled out. Each SINGLE becomes its own section, containing a list of +RESULTs that it may return and what ACTION should follow from them. So +preprocess is called, and if it returns for example RLM_MODULE_REJECT, then +the reject=return rule is applied, and the authorize{...} GROUP itself +immediately returns RLM_MODULE_REJECT. + +If preprocess returns RLM_MODULE_NOOP, the corresponding ACTION is "2". An +integer ACTION serves two purposes - first, it tells the parent GROUP to go +on to the next module. Second, it is a hint as to how desirable this RESULT +is as a candidate for the GROUP's own RESULT. So files is called... suppose +it returns RLM_MODULE_NOTFOUND. The ACTION for notfound inside the files{...} +block is "1". We have now reached the end of the authorize{...} GROUP and we +look at the RESULTs we accumulated along the way - there is a noop with +preference level 2, and a notfound with preference level 1, so the +authorize{...} GROUP as a whole returns RLM_MODULE_NOOP, which makes sense +because to say the user was not found at all would be a lie, since preprocess +apparently found him, or else it would have returned RLM_MODULE_NOTFOUND too. + +[Take a deep breath - the worst is over] + +That RESULT preference/desirability stuff is pretty complex, but my hope is +that it will be complex enough to handle the needs of everyone's real-world +imperfect systems, while staying out of sight most of the time since the +defaults will be right for the most common configurations. + +So where does redundant{...} fit in with all that? Well, redundant{...} is +simply a group that changes the default ACTIONs to something like + + fail = 1 + everythingelse = return + +so that when one module fails, we keep trying until we find one that doesn't +fail, then return whatever it returned. And at the end, if they all failed, +the redundant GROUP as a whole returns RLM_MODULE_FAIL, just as you'd want it +to (I hope). + +There are two other kinds of grouping: group{...} which does not have any +specialized default ACTIONs, and append{...}, which should be used when you +have separate but similarly structured databases that are guaranteed not to +overlap. + +That's all that really needs to be said. But now a few random notes: + +1. GROUPs may have RESULT=ACTION specifiers too! It would look like this: + + authorize { + preprocess + redundant { + sql1 + sql2 + notfound = return + } + files + } + +which would prevent rlm_files from being called if neither of the SQL +instances could find the user. + +2. redundant{...} and append{...} are just shortcuts. You could write + group { + sql1 { + fail = 1 + notfound = 2 + noop = return + ok = return + updated = return + reject = return + userlock = return + invalid = return + handled = return + } + sql2 { + fail = 1 + notfound = 2 + noop = return + ok = return + updated = return + reject = return + userlock = return + invalid = return + handled = return + } + } + instead of + redundant { + sql1 + sql2 + } + but the latter is just a whole lot easier to read. + +3. "authenticate{...}" itself is not a GROUP, even though it contains a list +of authtype GROUPs, because its semantics are totally different - it uses +Auth-Type to decide which of its members to call, and their order is +irrelevant. + +4. The default rules are context-sensitive - for authorize, the defaults are +what you saw above - notfound, noop, ok, and updated are considered +success, and anything else has an ACTION of "return". For authenticate, the +default is to return on success *or* reject, and only try the second and +following items if the first one fails. You can read all the default ACTIONs +in modcall.c (int defaultactions[][][]), or just trust me. They do the right +thing. + +5. There are some rules that can't be implemented in this language - things +like "if the absolutelypositivelymandatory module returns notfound, the group +should immediately return reject". It would be possible to extend the +language to include that, as "notfound = return-reject", and the obvious +followup feature would be "notfound = 1-reject", "noop = 2-ok", "ok = 3-ok", +etc. But I don't feel justified adding that complexity in the first draft. +There are already enough things here that may never see real-world usage. +Like append{...} + +-- Pac. 9/18/2000 diff --git a/src/include/modcall.h b/src/include/modcall.h new file mode 100644 index 0000000..b45e064 --- /dev/null +++ b/src/include/modcall.h @@ -0,0 +1,34 @@ +/* modcall.h: the outside interface to the module-calling tree. Includes + * functions to build the tree from the config file, and to call it by + * feeding it REQUESTs. + * + * Version: $Id$ */ + +#include "conffile.h" /* Need CONF_* definitions */ + +/* + * For each authorize/authtype/etc, we have an ordered + * tree of instances to call. This data structure keeps track + * of that order. + */ +typedef struct modcallable modcallable; + +int modcall(int component, modcallable *c, REQUEST *request); + +/* Parse a module-method's config section (e.g. authorize{}) into a tree that + * may be called with modcall() */ +modcallable *compile_modgroup(int component, CONF_SECTION *cs, + const char *filename); + +/* Create a single modcallable node that references a module instance. This + * may be a CONF_SECTION containing action specifiers like "notfound = return" + * or a simple CONF_PAIR, in which case the default actions are used. */ +modcallable *compile_modsingle(int component, CONF_ITEM *ci, + const char *filename, char **modname); + +/* Add an entry to the end of a modgroup, creating it first if necessary */ +void add_to_modcallable(modcallable **parent, modcallable *this, + int component, int lineno); + +/* Free a tree returned by compile_modgroup or compile_modsingle */ +void modcallable_free(modcallable **pc); diff --git a/src/include/modpriv.h b/src/include/modpriv.h new file mode 100644 index 0000000..442f929 --- /dev/null +++ b/src/include/modpriv.h @@ -0,0 +1,34 @@ +/* modpriv.h: Stuff needed by both modules.c and modcall.c, but should not be + * accessed from anywhere else. + * + * Version: $Id$ */ +#include "radiusd.h" +#include "modules.h" +#include "ltdl.h" + +/* + * Keep track of which modules we've loaded. + */ +typedef struct module_list_t { + struct module_list_t *next; + char name[MAX_STRING_LEN]; + module_t *module; + lt_dlhandle handle; +} module_list_t; + +/* + * Per-instance data structure, to correlate the modules + * with the instance names (may NOT be the module names!), + * and the per-instance data structures. + */ +typedef struct module_instance_t { + struct module_instance_t *next; + char name[MAX_STRING_LEN]; + module_list_t *entry; + void *insthandle; +#if HAVE_PTHREAD_H + pthread_mutex_t *mutex; +#endif +} module_instance_t; + +module_instance_t *find_module_instance(const char *instname); diff --git a/src/main/Makefile b/src/main/Makefile index 938682b..3cfd8a1 100644 --- a/src/main/Makefile +++ b/src/main/Makefile @@ -6,8 +6,8 @@ include ../../Make.inc SERVER_OBJS = radiusd.o files.o util.o acct.o nas.o log.o valuepair.o \ version.o proxy.o exec.o auth.o timestr.o conffile.o \ - modules.o radutmp.o xlat.o threads.o smux.o radius_snmp.o \ - client.o request_list.o + modules.o modcall.o radutmp.o xlat.o threads.o smux.o \ + radius_snmp.o client.o request_list.o INCLUDES = ../include/radiusd.h ../include/conf.h ../include/autoconf.h CFLAGS += -I../include @@ -31,7 +31,7 @@ radiusd: $(SERVER_OBJS) ../lib/libradius.a $(SERVER_OBJS) $(LIBS) $(LCRYPT) \ $(PTHREADLIB) $(LIBLTDL) $(MODULE_LIBS) -radiusd.o: radiusd.c $(INCLUDES) ../include/request_list.h ../include/modules.h +radiusd.o: radiusd.c $(INCLUDES) ../include/request_list.h ../include/modules.h ../include/modcall.h ../include/modpriv.h $(CC) $(CFLAGS) -c radiusd.c acct.o: acct.c $(INCLUDES) ../include/modules.h @@ -58,6 +58,9 @@ timestr.o: timestr.c $(INCLUDES) modules.o: modules.c $(INCLUDES) $(CC) $(CFLAGS) $(INCLTDL) -c modules.c +modcall.o: modcall.c $(INCLUDES) + $(CC) $(CFLAGS) $(INCLTDL) -c modcall.c + radutmp.o: radutmp.c $(INCLUDES) $(CC) $(CFLAGS) -c radutmp.c diff --git a/src/main/modcall.c b/src/main/modcall.c new file mode 100644 index 0000000..6dc9ac4 --- /dev/null +++ b/src/main/modcall.c @@ -0,0 +1,803 @@ +#include +#include +#include +#include "radiusd.h" +#include "conffile.h" +#include "modpriv.h" +#include "modules.h" +#include "modcall.h" + +/* Actions may be a positive integer (the highest one returned in the group + * will be returned), or the keyword "return", represented here by + * MOD_ACTION_RETURN, to cause an immediate return. */ +#define MOD_ACTION_RETURN (-1) + +/* Here are our basic types: modcallable, modgroup, and modsingle. For an + * explanation of what they are all about, see ../../doc/README.failover */ +struct modcallable { + struct modcallable *next; + int actions[RLM_MODULE_NUMCODES]; + int lineno; + enum { MOD_SINGLE, MOD_GROUP } type; +}; + +typedef struct { + modcallable mc; + modcallable *children; +} modgroup; + +typedef struct { + modcallable mc; + module_instance_t *modinst; +} modsingle; + +/* Simple conversions: modsingle and modgroup are subclasses of modcallable, + * so we often want to go back and forth between them. */ +static modsingle *mod_callabletosingle(modcallable *p) +{ + assert(p->type==MOD_SINGLE); + return (modsingle *)p; +} +static modgroup *mod_callabletogroup(modcallable *p) +{ + assert(p->type==MOD_GROUP); + return (modgroup *)p; +} +static modcallable *mod_singletocallable(modsingle *p) +{ + return (modcallable *)p; +} +static modcallable *mod_grouptocallable(modgroup *p) +{ + return (modcallable *)p; +} + +/* modgroups are grown by adding a modcallable to the end */ +static void add_child(modgroup *g, modcallable *c) +{ + modcallable **head = &g->children; + modcallable *node = *head; + modcallable **last = head; + + while (node) { + last = &node->next; + node = node->next; + } + + assert(c->next == NULL); + *last = c; +} + +/* Here's where we recognize all of our keywords: first the rcodes, then the + * actions */ +static int str2rcode(const char *s, const char *filename, int lineno) +{ + if(!strcasecmp(s, "reject")) + return RLM_MODULE_REJECT; + else if(!strcasecmp(s, "fail")) + return RLM_MODULE_FAIL; + else if(!strcasecmp(s, "ok")) + return RLM_MODULE_OK; + else if(!strcasecmp(s, "handled")) + return RLM_MODULE_HANDLED; + else if(!strcasecmp(s, "invalid")) + return RLM_MODULE_INVALID; + else if(!strcasecmp(s, "userlock")) + return RLM_MODULE_USERLOCK; + else if(!strcasecmp(s, "notfound")) + return RLM_MODULE_NOTFOUND; + else if(!strcasecmp(s, "noop")) + return RLM_MODULE_NOOP; + else if(!strcasecmp(s, "updated")) + return RLM_MODULE_UPDATED; + else { + radlog(L_ERR|L_CONS, + "%s[%d] Unknown module rcode '%s'.\n", + filename, lineno, s); + exit(1); + } +} + +static const char *rcode2str[] = { + "reject", + "fail", + "ok", + "handled", + "invalid", + "userlock", + "notfound", + "noop", + "updated" +}; + +static int str2action(const char *s, const char *filename, int lineno) +{ + if(!strcasecmp(s, "return")) + return MOD_ACTION_RETURN; + else if(strspn(s, "0123456789")==strlen(s)) + return atoi(s); + else { + radlog(L_ERR|L_CONS, + "%s[%d] Unknown action '%s'.\n", + filename, lineno, s); + exit(1); + } +} + +static const char *action2str(int action) +{ + static char buf[32]; + if(action==MOD_ACTION_RETURN) + return "return"; + snprintf(buf, sizeof buf, "%d", action); + return buf; +} + +/* Some short names for debugging output */ +static const char *comp2str[] = { + "auth", + "autz", + "preacct", + "acct", + "sess" +}; + +#if HAVE_PTHREAD_H +/* + * Lock the mutex for the module + */ +static void safe_lock(module_instance_t *instance) +{ + if (instance->mutex) pthread_mutex_lock(instance->mutex); +} + +/* + * Unlock the mutex for the module + */ +static void safe_unlock(module_instance_t *instance) +{ + if (instance->mutex) pthread_mutex_unlock(instance->mutex); +} +#else +/* + * No threads: these functions become NULL's. + */ +#define safe_lock(foo) +#define safe_unlock(foo) +#endif + +static int call_modsingle(int component, modsingle *sp, REQUEST *request, + int default_result) +{ + int myresult = default_result; + + safe_lock(sp->modinst); + switch(component) { + case RLM_COMPONENT_AUTZ: + myresult = sp->modinst->entry->module->authorize( + sp->modinst->insthandle, request); + break; + case RLM_COMPONENT_AUTH: + myresult = sp->modinst->entry->module->authenticate( + sp->modinst->insthandle, request); + break; + case RLM_COMPONENT_PREACCT: + myresult = sp->modinst->entry->module->preaccounting( + sp->modinst->insthandle, request); + break; + case RLM_COMPONENT_ACCT: + myresult = sp->modinst->entry->module->accounting( + sp->modinst->insthandle, request); + break; + case RLM_COMPONENT_SESS: + myresult = sp->modinst->entry->module->checksimul( + sp->modinst->insthandle, request); + break; + } + safe_unlock(sp->modinst); + + return myresult; +} + +static int call_modgroup(int component, modgroup *g, REQUEST *request, + int default_result) +{ + int myresult = default_result; + int myresultpref; + modcallable *p; + + /* Assign the lowest possible preference to the default return code */ + myresultpref = 0; + + /* Loop over the children */ + for(p = g->children; p; p = p->next) { + int r = RLM_MODULE_FAIL; + + /* Call this child by recursing into modcall */ + r = modcall(component, p, request); + + DEBUG2("modcall[%s]: action for %s is %s", + comp2str[component], rcode2str[r], + action2str(p->actions[r])); + + /* Find an action to go with the child's result. If "return", + * break out of the loop so the rest of the children in the + * list will be skipped. */ + if(p->actions[r] == MOD_ACTION_RETURN) { + myresult = r; + break; + } + + /* Otherwise, the action is a number, the preference level of + * this return code. If no higher preference has been seen + * yet, remember this one. */ + if(p->actions[r] >= myresultpref) { + myresult = r; + myresultpref = p->actions[r]; + } + } + + return myresult; +} + +int modcall(int component, modcallable *c, REQUEST *request) +{ + int myresult; + + /* Choose a default return value appropriate for the component */ + switch(component) { + case RLM_COMPONENT_AUTZ: myresult = RLM_MODULE_NOTFOUND;break; + case RLM_COMPONENT_AUTH: myresult = RLM_MODULE_REJECT; break; + case RLM_COMPONENT_PREACCT:myresult = RLM_MODULE_NOOP; break; + case RLM_COMPONENT_ACCT: myresult = RLM_MODULE_NOOP; break; + case RLM_COMPONENT_SESS: myresult = RLM_MODULE_FAIL; break; + default: myresult = RLM_MODULE_FAIL; + } + + if(!c) { + DEBUG2("modcall[%s]: Null object returns %s", + comp2str[component], rcode2str[myresult]); + return myresult; + } + + if(c->type==MOD_GROUP) { + modgroup *g = mod_callabletogroup(c); + + DEBUG2("modcall[%s]: Entering group at line %d", + comp2str[component], c->lineno); + + myresult = call_modgroup(component, g, request, myresult); + + DEBUG2("modcall[%s]: Group at line %d returns %s", + comp2str[component], c->lineno, rcode2str[myresult]); + } else { + modsingle *sp = mod_callabletosingle(c); + + myresult = call_modsingle(component, sp, request, myresult); + + DEBUG2("modcall[%s]: Module at line %d returns %s", + comp2str[component], c->lineno, rcode2str[myresult]); + } + + return myresult; +} + +/* If you suspect a bug in the parser, you'll want to use these dump + * functions. dump_tree should reproduce a whole tree exactly as it was found + * in radiusd.conf, but in long form (all actions explicitly defined) */ +static void dump_mc(modcallable *c, int indent) +{ + int i; + + if(c->type==MOD_SINGLE) { + modsingle *single = mod_callabletosingle(c); + DEBUG("%.*s%s {", indent, "\t\t\t\t\t\t\t\t\t\t\t", + single->modinst->name); + } else { + modgroup *g = mod_callabletogroup(c); + modcallable *p; + DEBUG("%.*sgroup {", indent, "\t\t\t\t\t\t\t\t\t\t\t"); + for(p = g->children;p;p = p->next) + dump_mc(p, indent+1); + } + + for(i = 0; iactions[i])); + } + + DEBUG("%.*s}", indent, "\t\t\t\t\t\t\t\t\t\t\t"); +} + +static void dump_tree(int comp, modcallable *c) +{ + DEBUG("[%s]", comp2str[comp]); + dump_mc(c, 0); +} + +#define GROUPTYPE_SIMPLEGROUP 0 +#define GROUPTYPE_REDUNDANT 1 +#define GROUPTYPE_APPEND 2 +#define GROUPTYPE_COUNT 3 + +/* These are the default actions. For each component, the group{} block + * behaves like the code from the old module_*() function. redundant{} and + * append{} are based on my guesses of what they will be used for. --Pac. */ +static int +defaultactions[RLM_COMPONENT_COUNT][GROUPTYPE_COUNT][RLM_MODULE_NUMCODES] = +{ + /* authenticate */ + { + /* group */ + { + MOD_ACTION_RETURN, /* reject */ + 1, /* fail */ + MOD_ACTION_RETURN, /* ok */ + MOD_ACTION_RETURN, /* handled */ + 1, /* invalid */ + MOD_ACTION_RETURN, /* userlock */ + MOD_ACTION_RETURN, /* notfound */ + 1, /* noop */ + 1 /* updated */ + }, + /* redundant */ + { + MOD_ACTION_RETURN, /* reject */ + 1, /* fail */ + MOD_ACTION_RETURN, /* ok */ + MOD_ACTION_RETURN, /* handled */ + MOD_ACTION_RETURN, /* invalid */ + MOD_ACTION_RETURN, /* userlock */ + MOD_ACTION_RETURN, /* notfound */ + MOD_ACTION_RETURN, /* noop */ + MOD_ACTION_RETURN /* updated */ + }, + /* append */ + { + MOD_ACTION_RETURN, /* reject */ + 1, /* fail */ + MOD_ACTION_RETURN, /* ok */ + MOD_ACTION_RETURN, /* handled */ + MOD_ACTION_RETURN, /* invalid */ + MOD_ACTION_RETURN, /* userlock */ + 2, /* notfound */ + MOD_ACTION_RETURN, /* noop */ + MOD_ACTION_RETURN /* updated */ + } + }, + /* authorize */ + { + /* group */ + { + MOD_ACTION_RETURN, /* reject */ + MOD_ACTION_RETURN, /* fail */ + 3, /* ok */ + MOD_ACTION_RETURN, /* handled */ + MOD_ACTION_RETURN, /* invalid */ + MOD_ACTION_RETURN, /* userlock */ + 1, /* notfound */ + 2, /* noop */ + 4 /* updated */ + }, + /* redundant */ + { + MOD_ACTION_RETURN, /* reject */ + 1, /* fail */ + MOD_ACTION_RETURN, /* ok */ + MOD_ACTION_RETURN, /* handled */ + MOD_ACTION_RETURN, /* invalid */ + MOD_ACTION_RETURN, /* userlock */ + MOD_ACTION_RETURN, /* notfound */ + MOD_ACTION_RETURN, /* noop */ + MOD_ACTION_RETURN /* updated */ + }, + /* append */ + { + MOD_ACTION_RETURN, /* reject */ + 1, /* fail */ + MOD_ACTION_RETURN, /* ok */ + MOD_ACTION_RETURN, /* handled */ + MOD_ACTION_RETURN, /* invalid */ + MOD_ACTION_RETURN, /* userlock */ + 2, /* notfound */ + MOD_ACTION_RETURN, /* noop */ + MOD_ACTION_RETURN /* updated */ + } + }, + /* preacct */ + { + /* group */ + { + MOD_ACTION_RETURN, /* reject */ + MOD_ACTION_RETURN, /* fail */ + 2, /* ok */ + MOD_ACTION_RETURN, /* handled */ + MOD_ACTION_RETURN, /* invalid */ + MOD_ACTION_RETURN, /* userlock */ + MOD_ACTION_RETURN, /* notfound */ + 1, /* noop */ + 3 /* updated */ + }, + /* redundant */ + { + MOD_ACTION_RETURN, /* reject */ + 1, /* fail */ + MOD_ACTION_RETURN, /* ok */ + MOD_ACTION_RETURN, /* handled */ + MOD_ACTION_RETURN, /* invalid */ + MOD_ACTION_RETURN, /* userlock */ + MOD_ACTION_RETURN, /* notfound */ + MOD_ACTION_RETURN, /* noop */ + MOD_ACTION_RETURN /* updated */ + }, + /* append */ + { + MOD_ACTION_RETURN, /* reject */ + 1, /* fail */ + MOD_ACTION_RETURN, /* ok */ + MOD_ACTION_RETURN, /* handled */ + MOD_ACTION_RETURN, /* invalid */ + MOD_ACTION_RETURN, /* userlock */ + 2, /* notfound */ + MOD_ACTION_RETURN, /* noop */ + MOD_ACTION_RETURN /* updated */ + } + }, + /* accounting */ + { + /* group */ + { + MOD_ACTION_RETURN, /* reject */ + MOD_ACTION_RETURN, /* fail */ + 2, /* ok */ + MOD_ACTION_RETURN, /* handled */ + MOD_ACTION_RETURN, /* invalid */ + MOD_ACTION_RETURN, /* userlock */ + MOD_ACTION_RETURN, /* notfound */ + 1, /* noop */ + 3 /* updated */ + }, + /* redundant */ + { + 1, /* reject */ + 1, /* fail */ + 3, /* ok */ + MOD_ACTION_RETURN, /* handled */ + 1, /* invalid */ + 1, /* userlock */ + 1, /* notfound */ + 2, /* noop */ + 4 /* updated */ + }, + /* append */ + { + MOD_ACTION_RETURN, /* reject */ + 1, /* fail */ + MOD_ACTION_RETURN, /* ok */ + MOD_ACTION_RETURN, /* handled */ + MOD_ACTION_RETURN, /* invalid */ + MOD_ACTION_RETURN, /* userlock */ + 2, /* notfound */ + MOD_ACTION_RETURN, /* noop */ + MOD_ACTION_RETURN /* updated */ + } + }, + /* checksimul */ + { + /* group */ + { + MOD_ACTION_RETURN, /* reject */ + 1, /* fail */ + MOD_ACTION_RETURN, /* ok */ + MOD_ACTION_RETURN, /* handled */ + MOD_ACTION_RETURN, /* invalid */ + MOD_ACTION_RETURN, /* userlock */ + MOD_ACTION_RETURN, /* notfound */ + MOD_ACTION_RETURN, /* noop */ + MOD_ACTION_RETURN /* updated */ + }, + /* redundant */ + { + MOD_ACTION_RETURN, /* reject */ + 1, /* fail */ + MOD_ACTION_RETURN, /* ok */ + MOD_ACTION_RETURN, /* handled */ + MOD_ACTION_RETURN, /* invalid */ + MOD_ACTION_RETURN, /* userlock */ + MOD_ACTION_RETURN, /* notfound */ + MOD_ACTION_RETURN, /* noop */ + MOD_ACTION_RETURN /* updated */ + }, + /* append */ + { + MOD_ACTION_RETURN, /* reject */ + 1, /* fail */ + MOD_ACTION_RETURN, /* ok */ + MOD_ACTION_RETURN, /* handled */ + MOD_ACTION_RETURN, /* invalid */ + MOD_ACTION_RETURN, /* userlock */ + MOD_ACTION_RETURN, /* notfound */ + MOD_ACTION_RETURN, /* noop */ + MOD_ACTION_RETURN /* updated */ + } + } +}; + +/* Bail out if the module in question does not supply the wanted component */ +static void sanity_check(int component, module_instance_t *inst, int lineno, + const char *filename) +{ + switch (component) { + case RLM_COMPONENT_AUTH: + if (!inst->entry->module->authenticate) { + radlog(L_ERR|L_CONS, + "%s[%d] Module %s does not contain " + "an 'authenticate' entry\n", + filename, lineno, + inst->entry->module->name); + exit(1); + } + break; + case RLM_COMPONENT_AUTZ: + if (!inst->entry->module->authorize) { + radlog(L_ERR|L_CONS, + "%s[%d] Module %s does not contain " + "an 'authorize' entry\n", + filename, lineno, + inst->entry->module->name); + exit(1); + } + break; + case RLM_COMPONENT_PREACCT: + if (!inst->entry->module->preaccounting) { + radlog(L_ERR|L_CONS, + "%s[%d] Module %s does not contain " + "a 'preacct' entry\n", + filename, lineno, + inst->entry->module->name); + exit(1); + } + break; + case RLM_COMPONENT_ACCT: + if (!inst->entry->module->accounting) { + radlog(L_ERR|L_CONS, + "%s[%d] Module %s does not contain " + "an 'accounting' entry\n", + filename, lineno, + inst->entry->module->name); + exit(1); + } + break; + case RLM_COMPONENT_SESS: + if (!inst->entry->module->checksimul) { + radlog(L_ERR|L_CONS, + "%s[%d] Module %s does not contain " + "a 'checksimul' entry\n", + filename, lineno, + inst->entry->module->name); + exit(1); + } + break; + default: + radlog(L_ERR|L_CONS, "%s[%d] Unknown component %d.\n", + filename, lineno, component); + exit(1); + } +} + +/* Parse a CONF_SECTION containing only result=action pairs */ +static void override_actions(modcallable *c, CONF_SECTION *cs, + const char *filename) +{ + CONF_ITEM *ci; + CONF_PAIR *cp; + const char *attr, *value; + int lineno, rcode, action; + + for(ci=cf_item_find_next(cs, NULL); ci; ci=cf_item_find_next(cs, ci)) { + if(cf_item_is_section(ci)) { + radlog(L_ERR|L_CONS, + "%s[%d] Subsection of module instance call " + "not allowed\n", filename, + cf_section_lineno(cf_itemtosection(ci))); + exit(1); + } + cp = cf_itemtopair(ci); + attr = cf_pair_attr(cp); + value = cf_pair_value(cp); + lineno = cf_pair_lineno(cp); + rcode = str2rcode(attr, filename, lineno); + action = str2action(value, filename, lineno); + c->actions[rcode] = action; + } +} + +static modcallable *do_compile_modsingle(int component, CONF_ITEM *ci, + const char *filename, int grouptype, + char **modname) +{ + int lineno; + const char *modrefname; + modsingle *single; + modcallable *csingle; + module_instance_t *this; + + if(cf_item_is_section(ci)) { + CONF_SECTION *cs = cf_itemtosection(ci); + lineno = cf_section_lineno(cs); + modrefname = cf_section_name1(cs); + } else { + CONF_PAIR *cp = cf_itemtopair(ci); + lineno = cf_pair_lineno(cp); + modrefname = cf_pair_attr(cp); + } + + single = rad_malloc(sizeof *single); + csingle = mod_singletocallable(single); + csingle->next = NULL; + memcpy(csingle->actions, + defaultactions[component][grouptype], + sizeof csingle->actions); + csingle->lineno = lineno; + csingle->type = MOD_SINGLE; + + if(cf_item_is_section(ci)) { + /* override default actions with what's in the CONF_SECTION */ + override_actions(csingle, cf_itemtosection(ci), filename); + } + + this = find_module_instance(modrefname); + if (this == NULL) { + exit(1); /* FIXME */ + } + + sanity_check(component, this, csingle->lineno, filename); + + single->modinst = this; + *modname = this->entry->name; + return csingle; +} + +modcallable *compile_modsingle(int component, CONF_ITEM *ci, + const char *filename, char **modname) +{ + return do_compile_modsingle(component, ci, filename, + GROUPTYPE_SIMPLEGROUP, modname); +} + +static modcallable *do_compile_modgroup(int component, CONF_SECTION *cs, + const char *filename, int grouptype, + int parentgrouptype) +{ + modgroup *g; + modcallable *c; + CONF_ITEM *ci; + + g = rad_malloc(sizeof *g); + + c = mod_grouptocallable(g); + c->next = NULL; + memcpy(c->actions, defaultactions[component][parentgrouptype], + sizeof c->actions); + c->lineno = cf_section_lineno(cs); + c->type = MOD_GROUP; + g->children = NULL; + + for(ci=cf_item_find_next(cs, NULL); ci; ci=cf_item_find_next(cs, ci)) { + if(cf_item_is_section(ci)) { + CONF_SECTION *scs = cf_itemtosection(ci); + const char *name1; + modcallable *childgroup; + + name1 = cf_section_name1(scs); + + /* subsections may be group{}, redundant{}, or + * append{}... */ + if(!strcmp(name1, "group")) { + childgroup = do_compile_modgroup(component, + scs, filename, GROUPTYPE_SIMPLEGROUP, + grouptype); + add_child(g, childgroup); + } else if(!strcmp(name1, "redundant")) { + childgroup = do_compile_modgroup(component, + scs, filename, GROUPTYPE_REDUNDANT, + grouptype); + add_child(g, childgroup); + } else if(!strcmp(name1, "append")) { + childgroup = do_compile_modgroup(component, + scs, filename, GROUPTYPE_APPEND, + grouptype); + add_child(g, childgroup); + } else { + /* ...or a module instance with some actions + * specified. */ + modcallable *single; + char *junk; + + single = do_compile_modsingle(component, + cf_sectiontoitem(scs), filename, + grouptype, &junk); + add_child(g, single); + } + } else { + const char *attr, *value; + CONF_PAIR *cp = cf_itemtopair(ci); + int lineno; + + attr = cf_pair_attr(cp); + value = cf_pair_value(cp); + lineno = cf_pair_lineno(cp); + + /* A CONF_PAIR is either a module instance with no + * actions specified... */ + if(value[0]==0) { + modcallable *single; + char *junk; + + single = do_compile_modsingle(component, + cf_pairtoitem(cp), filename, + grouptype, &junk); + add_child(g, single); + } else { + /* ...or an action to be applied to this + * group. */ + int rcode, action; + rcode = str2rcode(attr, filename, lineno); + action = str2action(value, filename, lineno); + + c->actions[rcode] = action; + } + } + } + return mod_grouptocallable(g); +} + +modcallable *compile_modgroup(int component, CONF_SECTION *cs, + const char *filename) +{ + modcallable *ret = do_compile_modgroup(component, cs, filename, + GROUPTYPE_SIMPLEGROUP, + GROUPTYPE_SIMPLEGROUP); + /*dump_tree(component, ret);*/ + return ret; +} + +void add_to_modcallable(modcallable **parent, modcallable *this, + int component, int lineno) +{ + modgroup *g; + + if(!*parent) { + modcallable *c; + + g = rad_malloc(sizeof *g); + c = mod_grouptocallable(g); + c->next = NULL; + memcpy(c->actions, + defaultactions[component][GROUPTYPE_SIMPLEGROUP], + sizeof c->actions); + c->lineno = lineno; + c->type = MOD_GROUP; + g->children = NULL; + + *parent = mod_grouptocallable(g); + } else { + g = mod_callabletogroup(*parent); + } + + add_child(g, this); +} + +void modcallable_free(modcallable **pc) +{ + modcallable *c, *loop, *next; + c = *pc; + if(c->type==MOD_GROUP) { + for(loop=mod_callabletogroup(c)->children ; loop ; loop=next) { + next = loop->next; + modcallable_free(&loop); + } + } + free(c); + *pc = NULL; +} diff --git a/src/main/modules.c b/src/main/modules.c index 83d65d4..c76ba8a 100644 --- a/src/main/modules.c +++ b/src/main/modules.c @@ -18,65 +18,32 @@ static const char rcsid[] = "$Id$"; #include #include "radiusd.h" +#include "modpriv.h" #include "modules.h" +#include "modcall.h" #include "conffile.h" #include "ltdl.h" /* - * Keep track of which modules we've loaded. - */ -typedef struct module_list_t { - struct module_list_t *next; - char name[MAX_STRING_LEN]; - module_t *module; - lt_dlhandle handle; -} module_list_t; - -/* * Internal list of all of the modules we have loaded. */ static module_list_t *module_list = NULL; /* - * Per-instance data structure, to correlate the modules - * with the instance names (may NOT be the module names!), - * and the per-instance data structures. - */ -typedef struct module_instance_t { - struct module_instance_t *next; - char name[MAX_STRING_LEN]; - module_list_t *entry; - void *insthandle; -#if HAVE_PTHREAD_H - pthread_mutex_t *mutex; -#endif -} module_instance_t; - -/* * Internal list of each module instance. */ static module_instance_t *module_instance_list = NULL; -/* - * For each authorize/authtype/etc, we have an ordered - * list of instances to call. This data structure keeps track - * of that order. - */ -typedef struct config_module_t { - struct config_module_t *next; - module_instance_t *instance; -} config_module_t; - -typedef struct indexed_config_module_t { - struct indexed_config_module_t *next; +typedef struct indexed_modcallable { + struct indexed_modcallable *next; int idx; - config_module_t *modulelist; -} indexed_config_module_t; + modcallable *modulelist; +} indexed_modcallable; /* * For each component, keep an ordered list of ones to call. */ -static indexed_config_module_t *components[RLM_COMPONENT_COUNT]; +static indexed_modcallable *components[RLM_COMPONENT_COUNT]; /* * The component names. @@ -102,27 +69,14 @@ static const char *subcomponent_names[RLM_COMPONENT_COUNT] = "sesstype" }; -static void config_list_free(config_module_t **cf) +static void indexed_modcallable_free(indexed_modcallable **cf) { - config_module_t *c, *next; + indexed_modcallable *c, *next; c = *cf; while (c) { next = c->next; - free(c); - c = next; - } - *cf = NULL; -} - -static void indexed_config_list_free(indexed_config_module_t **cf) -{ - indexed_config_module_t *c, *next; - - c = *cf; - while (c) { - next = c->next; - config_list_free(&c->modulelist); + modcallable_free(&c->modulelist); free(c); c = next; } @@ -163,7 +117,7 @@ static void module_list_free(void) * Delete the internal component pointers. */ for (i = 0; i < RLM_COMPONENT_COUNT; i++) { - indexed_config_list_free(&components[i]); + indexed_modcallable_free(&components[i]); } instance_list_free(&module_instance_list); @@ -298,7 +252,7 @@ static module_list_t *linkto_module(const char *module_name, /* * Find a module instance. */ -static module_instance_t *find_module_instance(const char *instname) +module_instance_t *find_module_instance(const char *instname) { CONF_SECTION *cs, *inst_cs; const char *name1, *name2; @@ -389,7 +343,7 @@ static module_instance_t *find_module_instance(const char *instname) free(node); return NULL; } - + /* * We're done. Fill in the rest of the data structure, * and link it to the module instance list. @@ -423,9 +377,9 @@ static module_instance_t *find_module_instance(const char *instname) return node; } -static indexed_config_module_t *lookup_by_index(indexed_config_module_t *head, int idx) +static indexed_modcallable *lookup_by_index(indexed_modcallable *head, int idx) { - indexed_config_module_t *p; + indexed_modcallable *p; for (p = head; p != NULL; p = p->next) { if( p->idx == idx) @@ -434,43 +388,11 @@ static indexed_config_module_t *lookup_by_index(indexed_config_module_t *head, i return NULL; } -/* - * Add one entry at the end of the config_module_t list. - */ -static void add_to_list(int comp, module_instance_t *instance, int idx) +static indexed_modcallable *new_sublist(int comp, int idx) { - indexed_config_module_t *subcomp; - config_module_t *node; - config_module_t **last; - config_module_t **head; - - /* Step 1 - find the list corresponding to the given index. The - * caller is responsible for ensuring that one exists by calling - * new_sublist before calling add_to_list. */ - subcomp = lookup_by_index(components[comp], idx); - assert(subcomp); - - /* Step 2 - walk to the end of that list */ - head = &subcomp->modulelist; - last = head; - - for (node = *head; node != NULL; node = node->next) { - last = &node->next; - } - - /* Step 3 - put a new config_module_t there */ - node = (config_module_t *) rad_malloc(sizeof(config_module_t)); - node->next = NULL; - node->instance = instance; - - *last = node; -} - -static indexed_config_module_t *new_sublist(int comp, int idx) -{ - indexed_config_module_t **head = &components[comp]; - indexed_config_module_t *node = *head; - indexed_config_module_t **last = head; + indexed_modcallable **head = &components[comp]; + indexed_modcallable *node = *head; + indexed_modcallable **last = head; while (node) { /* It is an error to try to create a sublist that already @@ -499,71 +421,30 @@ static indexed_config_module_t *new_sublist(int comp, int idx) return node; } -/* Bail out if the module in question does not supply the wanted component */ -static void sanity_check(int comp, module_t *mod, const char *filename, - int lineno) +static int indexed_modcall(int comp, int idx, REQUEST *request) { - switch (comp) { - case RLM_COMPONENT_AUTH: - if (!mod->authenticate) { - radlog(L_ERR|L_CONS, - "%s[%d] Module %s does not contain " - "an 'authenticate' entry\n", - filename, lineno, mod->name); - exit(1); - } - break; - case RLM_COMPONENT_AUTZ: - if (!mod->authorize) { - radlog(L_ERR|L_CONS, - "%s[%d] Module %s does not contain " - "an 'authorize' entry\n", - filename, lineno, mod->name); - exit(1); - } - break; - case RLM_COMPONENT_PREACCT: - if (!mod->preaccounting) { - radlog(L_ERR|L_CONS, - "%s[%d] Module %s does not contain " - "a 'preacct' entry\n", - filename, lineno, mod->name); - exit(1); - } - break; - case RLM_COMPONENT_ACCT: - if (!mod->accounting) { - radlog(L_ERR|L_CONS, - "%s[%d] Module %s does not contain " - "an 'accounting' entry\n", - filename, lineno, mod->name); - exit(1); - } - break; - case RLM_COMPONENT_SESS: - if (!mod->checksimul) { - radlog(L_ERR|L_CONS, - "%s[%d] Module %s does not contain " - "a 'checksimul' entry\n", - filename, lineno, mod->name); - exit(1); + indexed_modcallable *this; + + this = lookup_by_index(components[comp], idx); + if (!this) { + /* Return a default value appropriate for the component */ + switch(comp) { + case RLM_COMPONENT_AUTZ: return RLM_MODULE_NOTFOUND; + case RLM_COMPONENT_AUTH: return RLM_MODULE_REJECT; + case RLM_COMPONENT_PREACCT: return RLM_MODULE_NOOP; + case RLM_COMPONENT_ACCT: return RLM_MODULE_NOOP; + case RLM_COMPONENT_SESS: return RLM_MODULE_FAIL; + default: return RLM_MODULE_FAIL; } - break; - default: - radlog(L_ERR|L_CONS, "%s[%d] Unknown component %d.\n", - filename, lineno, comp); - exit(1); } + return modcall(comp, this->modulelist, request); } /* Load a flat module list, as found inside an authtype{} block */ static void load_subcomponent_section(CONF_SECTION *cs, int comp, const char *filename) { - module_instance_t *this; - CONF_ITEM *modref; - int modreflineno; - const char *modrefname; int idx; + indexed_modcallable *subcomp; static int meaningless_counter = 1; @@ -574,16 +455,13 @@ static void load_subcomponent_section(CONF_SECTION *cs, int comp, const char *fi * nor checked for uniqueness, but all that could be fixed in a few * minutes, if anyone finds a real use for indexed config of * components other than auth. */ - switch (comp) { - case RLM_COMPONENT_AUTH: + if (comp==RLM_COMPONENT_AUTH) idx = new_authtype_value(cf_section_name2(cs)); - break; - default: + else idx = meaningless_counter++; - break; - } - if (!new_sublist(comp, idx)) { + subcomp = new_sublist(comp, idx); + if (!subcomp) { radlog(L_ERR|L_CONS, "%s[%d] %s %s already configured - skipping", filename, cf_section_lineno(cs), @@ -591,40 +469,17 @@ static void load_subcomponent_section(CONF_SECTION *cs, int comp, const char *fi return; } - for(modref=cf_item_find_next(cs, NULL) - ; modref ; - modref=cf_item_find_next(cs, modref)) { - - if(cf_item_is_section(modref)) { - CONF_SECTION *scs; - scs = cf_itemtosection(modref); - modreflineno = cf_section_lineno(scs); - modrefname = cf_section_name1(scs); - } else { - CONF_PAIR *cp; - cp = cf_itemtopair(modref); - modreflineno = cf_pair_lineno(cp); - modrefname = cf_pair_attr(cp); - } - - this = find_module_instance(modrefname); - if (this == NULL) { - /* find_module_instance logs any errors */ - exit(1); - } - - sanity_check(comp, this->entry->module, filename, modreflineno); - add_to_list(comp, this, idx); - } + subcomp->modulelist = compile_modgroup(comp, cs, filename); } static void load_component_section(CONF_SECTION *cs, int comp, const char *filename) { - module_instance_t *this; + modcallable *this; CONF_ITEM *modref; int modreflineno; - const char *modrefname; int idx; + indexed_modcallable *subcomp; + char *modname; for(modref=cf_item_find_next(cs, NULL) ; modref ; @@ -633,53 +488,44 @@ static void load_component_section(CONF_SECTION *cs, int comp, const char *filen if(cf_item_is_section(modref)) { CONF_SECTION *scs; scs = cf_itemtosection(modref); + if (!strcmp(cf_section_name1(scs), subcomponent_names[comp])) { load_subcomponent_section(scs, comp, filename); continue; } + modreflineno = cf_section_lineno(scs); - modrefname = cf_section_name1(scs); } else { CONF_PAIR *cp; cp = cf_itemtopair(modref); modreflineno = cf_pair_lineno(cp); - modrefname = cf_pair_attr(cp); } - /* - * Find an instance for this module. - * This means link to one if it already exists, - * or instantiate one, or load the library and - * instantiate/link. - */ - this = find_module_instance(modrefname); - if (this == NULL) { - /* find_module_instance logs any errors */ - exit(1); - } - - sanity_check(comp, this->entry->module, filename, modreflineno); + this = compile_modsingle(comp, modref, filename, &modname); - switch (comp) { - case RLM_COMPONENT_AUTH: - idx = new_authtype_value(this->name); - break; - default: + if (comp==RLM_COMPONENT_AUTH) { + idx = new_authtype_value(modname); + } else { /* See the comment in new_sublist() for explanation * of the special index 0 */ idx = 0; - break; } - if (!new_sublist(comp, idx)) { + subcomp = new_sublist(comp, idx); + if (!subcomp) { radlog(L_ERR|L_CONS, "%s[%d] %s %s already configured - skipping", filename, modreflineno, subcomponent_names[comp], - this->name); + modname); + modcallable_free(&this); continue; } - add_to_list(comp, this, idx); + + /* If subcomp->modulelist is NULL, add_to_modcallable will + * create it */ + add_to_modcallable(&subcomp->modulelist, this, + comp, modreflineno); } } @@ -751,52 +597,13 @@ int setup_modules(void) return 0; } -#if HAVE_PTHREAD_H -/* - * Lock the mutex for the module - */ -static void safe_lock(module_instance_t *instance) -{ - if (instance->mutex) pthread_mutex_lock(instance->mutex); -} - -/* - * Unlock the mutex for the module - */ -static void safe_unlock(module_instance_t *instance) -{ - if (instance->mutex) pthread_mutex_unlock(instance->mutex); -} -#else -/* - * No threads: these functions become NULL's. - */ -#define safe_lock(foo) -#define safe_unlock(foo) -#endif - /* * Call all authorization modules until one returns * somethings else than RLM_MODULE_OK */ int module_authorize(REQUEST *request) { - config_module_t *this; - int rcode = RLM_MODULE_OK; - - this = lookup_by_index(components[RLM_COMPONENT_AUTZ], 0)->modulelist; - rcode = RLM_MODULE_OK; - - while (this && rcode == RLM_MODULE_OK) { - DEBUG2(" authorize: %s", this->instance->entry->module->name); - safe_lock(this->instance); - rcode = (this->instance->entry->module->authorize)( - this->instance->insthandle, request); - safe_unlock(this->instance); - this = this->next; - } - - return rcode; + return indexed_modcall(RLM_COMPONENT_AUTZ, 0, request); } /* @@ -804,47 +611,15 @@ int module_authorize(REQUEST *request) */ int module_authenticate(int auth_type, REQUEST *request) { - config_module_t *this; - int rcode = RLM_MODULE_FAIL; - - this = lookup_by_index(components[RLM_COMPONENT_AUTH], - auth_type)->modulelist; - - while (this && rcode == RLM_MODULE_FAIL) { - DEBUG2(" authenticate: %s", - this->instance->entry->module->name); - safe_lock(this->instance); - rcode = (this->instance->entry->module->authenticate)( - this->instance->insthandle, request); - safe_unlock(this->instance); - this = this->next; - } - - return rcode; + return indexed_modcall(RLM_COMPONENT_AUTH, auth_type, request); } - /* * Do pre-accounting for ALL configured sessions */ int module_preacct(REQUEST *request) { - config_module_t *this; - int rcode; - - this = lookup_by_index(components[RLM_COMPONENT_PREACCT], 0)->modulelist; - rcode = RLM_MODULE_OK; - - while (this && (rcode == RLM_MODULE_OK)) { - DEBUG2(" preacct: %s", this->instance->entry->module->name); - safe_lock(this->instance); - rcode = (this->instance->entry->module->preaccounting) - (this->instance->insthandle, request); - safe_unlock(this->instance); - this = this->next; - } - - return rcode; + return indexed_modcall(RLM_COMPONENT_PREACCT, 0, request); } /* @@ -852,22 +627,7 @@ int module_preacct(REQUEST *request) */ int module_accounting(REQUEST *request) { - config_module_t *this; - int rcode; - - this = lookup_by_index(components[RLM_COMPONENT_ACCT], 0)->modulelist; - rcode = RLM_MODULE_OK; - - while (this && (rcode == RLM_MODULE_OK)) { - DEBUG2(" accounting: %s", this->instance->entry->module->name); - safe_lock(this->instance); - rcode = (this->instance->entry->module->accounting) - (this->instance->insthandle, request); - safe_unlock(this->instance); - this = this->next; - } - - return rcode; + return indexed_modcall(RLM_COMPONENT_ACCT, 0, request); } /* @@ -877,7 +637,6 @@ int module_accounting(REQUEST *request) */ int module_checksimul(REQUEST *request, int maxsimul) { - config_module_t *this; int rcode; if(!components[RLM_COMPONENT_SESS]) @@ -890,17 +649,7 @@ int module_checksimul(REQUEST *request, int maxsimul) request->simul_max = maxsimul; request->simul_mpp = 1; - this = lookup_by_index(components[RLM_COMPONENT_SESS], 0)->modulelist; - rcode = RLM_MODULE_FAIL; - - while (this && (rcode == RLM_MODULE_FAIL)) { - DEBUG2(" checksimul: %s", this->instance->entry->module->name); - safe_lock(this->instance); - rcode = (this->instance->entry->module->checksimul) - (this->instance->insthandle, request); - safe_unlock(this->instance); - this = this->next; - } + rcode = indexed_modcall(RLM_COMPONENT_SESS, 0, request); if(rcode != RLM_MODULE_OK) { /* FIXME: Good spot for a *rate-limited* warning to the log */ -- 2.1.4