module calling sequence.
--- /dev/null
+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
--- /dev/null
+/* 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);
--- /dev/null
+/* 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);
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
$(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
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
--- /dev/null
+#include <assert.h>
+#include <string.h>
+#include <stdarg.h>
+#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; i<RLM_MODULE_NUMCODES; ++i) {
+ DEBUG("%.*s%s = %s", indent+1, "\t\t\t\t\t\t\t\t\t\t\t",
+ rcode2str[i], action2str(c->actions[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;
+}
#include <assert.h>
#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.
"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;
}
* 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);
/*
* 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;
free(node);
return NULL;
}
-
+
/*
* We're done. Fill in the rest of the data structure,
* and link it to the module instance list.
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)
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
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;
* 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),
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 ;
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);
}
}
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);
}
/*
*/
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);
}
/*
*/
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);
}
/*
*/
int module_checksimul(REQUEST *request, int maxsimul)
{
- config_module_t *this;
int rcode;
if(!components[RLM_COMPONENT_SESS])
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 */