Call modules iteratively, rather than recursively.
[freeradius.git] / src / main / modcall.c
1 /*
2  * modcall.c
3  *
4  * Version:     $Id$
5  *
6  *   This program is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU General Public License as published by
8  *   the Free Software Foundation; either version 2 of the License, or
9  *   (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU General Public License for more details.
15  *
16  *   You should have received a copy of the GNU General Public License
17  *   along with this program; if not, write to the Free Software
18  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * Copyright 2000  The FreeRADIUS server project
21  */
22 #include <freeradius-devel/autoconf.h>
23
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdarg.h>
27 #include <freeradius-devel/radiusd.h>
28 #include <freeradius-devel/rad_assert.h>
29 #include <freeradius-devel/conffile.h>
30 #include <freeradius-devel/modpriv.h>
31 #include <freeradius-devel/modules.h>
32 #include <freeradius-devel/modcall.h>
33
34 /* mutually-recursive static functions need a prototype up front */
35 static modcallable *do_compile_modgroup(modcallable *,
36                                         int, CONF_SECTION *, const char *,
37                                         int, int);
38
39 /* Actions may be a positive integer (the highest one returned in the group
40  * will be returned), or the keyword "return", represented here by
41  * MOD_ACTION_RETURN, to cause an immediate return.
42  * There's also the keyword "reject", represented here by MOD_ACTION_REJECT
43  * to cause an immediate reject. */
44 #define MOD_ACTION_RETURN  (-1)
45 #define MOD_ACTION_REJECT  (-2)
46
47 /* Here are our basic types: modcallable, modgroup, and modsingle. For an
48  * explanation of what they are all about, see ../../doc/README.failover */
49 struct modcallable {
50         modcallable *parent;
51         struct modcallable *next;
52         const char *name;
53         int actions[RLM_MODULE_NUMCODES];
54         enum { MOD_SINGLE, MOD_GROUP, MOD_LOAD_BALANCE, MOD_REDUNDANT_LOAD_BALANCE } type;
55 };
56
57 #define GROUPTYPE_SIMPLE        0
58 #define GROUPTYPE_REDUNDANT     1
59 #define GROUPTYPE_APPEND        2
60 #define GROUPTYPE_COUNT         3
61
62 typedef struct {
63         modcallable mc;         /* self */
64         int grouptype;  /* after mc */
65         modcallable *children;
66 } modgroup;
67
68 typedef struct {
69         modcallable mc;
70         module_instance_t *modinst;
71 } modsingle;
72
73
74 static const LRAD_NAME_NUMBER grouptype_table[] = {
75         { "", GROUPTYPE_SIMPLE },
76         { "redundant ", GROUPTYPE_REDUNDANT },
77         { "append ", GROUPTYPE_APPEND },
78         { NULL, -1 }
79 };
80
81 /* Simple conversions: modsingle and modgroup are subclasses of modcallable,
82  * so we often want to go back and forth between them. */
83 static modsingle *mod_callabletosingle(modcallable *p)
84 {
85         rad_assert(p->type==MOD_SINGLE);
86         return (modsingle *)p;
87 }
88 static modgroup *mod_callabletogroup(modcallable *p)
89 {
90         rad_assert((p->type==MOD_GROUP) ||
91                    (p->type==MOD_LOAD_BALANCE) ||
92                    (p->type==MOD_REDUNDANT_LOAD_BALANCE));
93         return (modgroup *)p;
94 }
95 static modcallable *mod_singletocallable(modsingle *p)
96 {
97         return (modcallable *)p;
98 }
99 static modcallable *mod_grouptocallable(modgroup *p)
100 {
101         return (modcallable *)p;
102 }
103
104 /* modgroups are grown by adding a modcallable to the end */
105 static void add_child(modgroup *g, modcallable *c)
106 {
107         modcallable **head = &g->children;
108         modcallable *node = *head;
109         modcallable **last = head;
110
111         if (!c) return;
112
113         while (node) {
114                 last = &node->next;
115                 node = node->next;
116         }
117
118         rad_assert(c->next == NULL);
119         *last = c;
120         c->parent = mod_grouptocallable(g);
121 }
122
123 /* Here's where we recognize all of our keywords: first the rcodes, then the
124  * actions */
125 static const LRAD_NAME_NUMBER rcode_table[] = {
126         { "reject",     RLM_MODULE_REJECT       },
127         { "fail",       RLM_MODULE_FAIL         },
128         { "ok",         RLM_MODULE_OK           },
129         { "handled",    RLM_MODULE_HANDLED      },
130         { "invalid",    RLM_MODULE_INVALID      },
131         { "userlock",   RLM_MODULE_USERLOCK     },
132         { "notfound",   RLM_MODULE_NOTFOUND     },
133         { "noop",       RLM_MODULE_NOOP         },
134         { "updated",    RLM_MODULE_UPDATED      },
135         { NULL, 0 }
136 };
137
138
139 /*
140  *      Compile action && rcode for later use.
141  */
142 static int compile_action(modcallable *c, const char *attr, const char *value,
143                           const char *filename, int lineno)
144 {
145         int action;
146
147         if (!strcasecmp(value, "return"))
148                 action = MOD_ACTION_RETURN;
149
150         else if (!strcasecmp(value, "reject"))
151                 action = MOD_ACTION_REJECT;
152
153         else if (strspn(value, "0123456789")==strlen(value)) {
154                 action = atoi(value);
155                 
156                 /*
157                  *      Don't allow priority zero, for future use.
158                  */
159                 if (action == 0) return 0;
160         } else {
161                 radlog(L_ERR|L_CONS,
162                        "%s[%d] Unknown action '%s'.\n",
163                        filename, lineno, value);
164                 return 0;
165         }
166
167         if (strcasecmp(attr, "default") != 0) {
168                 int rcode;
169
170                 rcode = lrad_str2int(rcode_table, attr, -1);
171                 if (rcode < 0) {
172                         radlog(L_ERR|L_CONS,
173                                "%s[%d] Unknown module rcode '%s'.\n",
174                                filename, lineno, attr);
175                         return 0;
176                 }
177                 c->actions[rcode] = action;
178
179         } else {                /* set all unset values to the default */
180                 int i;
181
182                 for (i = 0; i < RLM_MODULE_NUMCODES; i++) {
183                         if (!c->actions[i]) c->actions[i] = action;
184                 }
185         }
186
187         return 1;
188 }
189
190 #if 0
191 static const char *action2str(int action)
192 {
193         static char buf[32];
194         if(action==MOD_ACTION_RETURN)
195                 return "return";
196         if(action==MOD_ACTION_REJECT)
197                 return "reject";
198         snprintf(buf, sizeof buf, "%d", action);
199         return buf;
200 }
201 #endif
202
203 /* Some short names for debugging output */
204 static const char * const comp2str[] = {
205         "authenticate",
206         "authorize",
207         "preacct",
208         "accounting",
209         "session",
210         "pre-proxy",
211         "post-proxy",
212         "post-auth"
213 };
214
215 #ifdef HAVE_PTHREAD_H
216 /*
217  *      Lock the mutex for the module
218  */
219 static void safe_lock(module_instance_t *instance)
220 {
221         if (instance->mutex)
222                 pthread_mutex_lock(instance->mutex);
223 }
224
225 /*
226  *      Unlock the mutex for the module
227  */
228 static void safe_unlock(module_instance_t *instance)
229 {
230         if (instance->mutex)
231                 pthread_mutex_unlock(instance->mutex);
232 }
233 #else
234 /*
235  *      No threads: these functions become NULL's.
236  */
237 #define safe_lock(foo)
238 #define safe_unlock(foo)
239 #endif
240
241 static int call_modsingle(int component, modsingle *sp, REQUEST *request,
242                           int default_result)
243 {
244         int myresult = default_result;
245
246         DEBUG3("  modsingle[%s]: calling %s (%s) for request %d",
247                comp2str[component], sp->modinst->name,
248                sp->modinst->entry->name, request->number);
249         safe_lock(sp->modinst);
250
251         /*
252          *      For logging unresponsive children.
253          */
254         request->module = sp->modinst->name;
255         request->component = comp2str[component];
256
257         myresult = sp->modinst->entry->module->methods[component](
258                         sp->modinst->insthandle, request);
259
260         request->module = NULL;
261         safe_unlock(sp->modinst);
262         DEBUG3("  modsingle[%s]: returned from %s (%s) for request %d",
263                comp2str[component], sp->modinst->name,
264                sp->modinst->entry->name, request->number);
265
266         return myresult;
267 }
268
269
270 static int default_component_results[RLM_COMPONENT_COUNT] = {
271         RLM_MODULE_REJECT,      /* AUTH */
272         RLM_MODULE_NOTFOUND,    /* AUTZ */
273         RLM_MODULE_NOOP,        /* PREACCT */
274         RLM_MODULE_NOOP,        /* ACCT */
275         RLM_MODULE_FAIL,        /* SESS */
276         RLM_MODULE_NOOP,        /* PRE_PROXY */
277         RLM_MODULE_NOOP,        /* POST_PROXY */
278         RLM_MODULE_NOOP         /* POST_AUTH */
279 };
280
281 static const char *group_name[] = {
282         "",
283         "group",
284         "load-balance group",
285         "redundant-load-balance group"
286 };
287
288 #define MODCALL_STACK_MAX (8)
289
290 /*
291  *      Don't call the modules recursively.  Instead, do them
292  *      iteratively, and manage the call stack ourselves.
293  */
294 typedef struct modcall_stack {
295         int pointer;
296
297         int priority[MODCALL_STACK_MAX];
298         int result[MODCALL_STACK_MAX];
299         modcallable *children[MODCALL_STACK_MAX];
300         modcallable *start[MODCALL_STACK_MAX];
301 } modcall_stack;
302
303
304 int modcall(int component, modcallable *c, REQUEST *request)
305 {
306         int myresult;
307         modcall_stack stack;
308         modcallable *parent, *child;
309
310         stack.pointer = 0;
311
312         if ((component < 0) || (component >= RLM_COMPONENT_COUNT)) {
313                 return RLM_MODULE_FAIL;
314         }
315
316         if (!c) {
317                 return default_component_results[component];
318         }
319
320         stack.priority[0] = 0;
321         stack.children[0] = c;
322         myresult = stack.result[0] = default_component_results[component];
323
324         while (1) {
325                 /*
326                  *      A module has taken too long to process the request,
327                  *      and we've been told to stop processing it.
328                  */
329                 if (request->options & RAD_REQUEST_OPTION_STOP_NOW) {
330                         myresult = RLM_MODULE_FAIL;
331                         break;
332                 }
333
334                 child = stack.children[stack.pointer];
335                 parent = child->parent;
336
337                 /*
338                  *      Child is a group that has children of it's own.
339                  */
340                 if (child && (child->type != MOD_SINGLE)) {
341                         int count = 1;
342                         modcallable *p, *q;
343                         modgroup *g = mod_callabletogroup(child);
344                         
345                         stack.pointer++;
346
347                         /*
348                          *      Catastrophic error.  This SHOULD have
349                          *      been caught when we were reading in the
350                          *      conf files.
351                          *
352                          *      FIXME: Do so.
353                          */
354                         if (stack.pointer >= MODCALL_STACK_MAX) {
355                                 radlog(L_ERR, "Internal sanity check failed: module stack is too deep");
356                                 exit(1);
357                         }
358
359                         stack.priority[stack.pointer] = 0;
360                         stack.result[stack.pointer] = default_component_results[component];
361                         switch (child->type) {
362                         case MOD_GROUP:
363                                 stack.children[stack.pointer] = g->children;
364                                 break;
365                                 
366                                 /*
367                                  *      See the "camel book" for why
368                                  *      this works.
369                                  *
370                                  *      If (rand(0..n) < 1), pick the
371                                  *      current realm.  We add a scale
372                                  *      factor of 65536, to avoid
373                                  *      floating point.
374                                  */
375                         case MOD_LOAD_BALANCE:
376                         case MOD_REDUNDANT_LOAD_BALANCE:
377                                 q = NULL;
378                                 for(p = g->children; p; p = p->next) {
379                                         if (!q) {
380                                                 q = p;
381                                                 count = 1;
382                                                 continue;
383                                         }
384                                         
385                                         count++;
386                                         
387                                         if ((count * (lrad_rand() & 0xffff)) < (uint32_t) 0x10000) {
388                                                 q = p;
389                                         }
390                                 }
391                                 stack.children[stack.pointer] = q;
392                                 break;
393                                 
394                         default:
395                                 exit(1); /* internal sanity check failure */
396                                 break;
397                         }
398                         
399                         
400                         stack.start[stack.pointer] = stack.children[stack.pointer];
401
402                         DEBUG2("modcall: entering %s %s for request %d",
403                                group_name[child->type],
404                                child->name, request->number);
405
406                         /*
407                          *      Catch the special case of a NULL group.
408                          */
409                         if (!stack.children[stack.pointer]) {
410                                 /*
411                                  *      Print message for NULL group
412                                  */
413                                 DEBUG2("modcall[%s]: NULL object returns %s for request %d",
414                                        comp2str[component],
415                                        lrad_int2str(rcode_table,
416                                                     stack.result[stack.pointer],
417                                                     "??"),
418                                        request->number);
419                                 goto do_exit;
420                         }
421
422                         continue; /* child MAY be a group! */
423                 }
424
425                 /*
426                  *      Process a stand-alone child, and fall through
427                  *      to dealing with it's parent.
428                  */
429                 if (child && (child->type == MOD_SINGLE)) {
430                         modsingle *sp = mod_callabletosingle(child);
431
432                         myresult = call_modsingle(component, sp, request,
433                                                   default_component_results[component]);
434                         stack.result[stack.pointer] = myresult;
435                 }
436
437                 /*
438                  *      We roll back up the stack at this point.
439                  */
440         unroll:
441                 /*
442                  *      No parent, we must be done.
443                  */
444                 if (!parent) {
445                         rad_assert(stack.pointer == 0);
446                         myresult = stack.result[0];
447                         break;
448                 }
449
450                 rad_assert(child != NULL);
451
452                 /*
453                  *      Go to the "next" child, whatever that is.
454                  */
455                 switch (parent->type) {
456                         case MOD_GROUP:
457                                 stack.children[stack.pointer] = child->next;
458                                 break;
459
460                         case MOD_LOAD_BALANCE:
461                                 stack.children[stack.pointer] = NULL;
462                                 break;
463
464                         case MOD_REDUNDANT_LOAD_BALANCE:
465                                 if (child->next) {
466                                         stack.children[stack.pointer] = child->next;
467                                 } else {
468                                         modgroup *g = mod_callabletogroup(parent);
469                                         
470                                         stack.children[stack.pointer] = g->children;
471                                 }
472                                 if (stack.children[stack.pointer] == stack.start[stack.pointer]) {
473                                         stack.children[stack.pointer] = NULL;
474                                 }
475                                 break;
476                         default:        
477                                 exit(1);
478                 }
479
480                 /*
481                  *      The child's action says return.  Do so.
482                  */
483                 if (child->actions[myresult] == MOD_ACTION_RETURN) {
484                         stack.children[stack.pointer] = NULL;
485                 }
486         
487                 /* If "reject", break out of the loop and return reject */
488                 if (child->actions[myresult] == MOD_ACTION_REJECT) {
489                         stack.children[stack.pointer] = NULL;
490                         stack.result[stack.pointer] = RLM_MODULE_REJECT;
491                 }
492
493                 /*
494                  *      No child, we're done this group.
495                  */
496                 if (!stack.children[stack.pointer]) {
497                 do_exit:
498                         rad_assert(stack.pointer > 0);
499                         myresult = stack.result[stack.pointer];
500                         stack.pointer--;
501
502                         DEBUG2("modcall: %s %s returns %s for request %d",
503                                group_name[parent->type], parent->name,
504                                lrad_int2str(rcode_table, myresult, "??"),
505                                request->number);
506
507                         if (stack.pointer == 0) break;
508
509                         /*
510                          *      Unroll the stack.
511                          */
512                         child = stack.children[stack.pointer];
513                         parent = child->parent;
514                         goto unroll;
515                 }
516
517                 /*
518                  *      Otherwise, the action is a number, the
519                  *      preference level of this return code. If no
520                  *      higher preference has been seen yet, remember
521                  *      this one . */
522                 if (child->actions[myresult] >= stack.priority[stack.pointer]) {
523                         stack.result[stack.pointer] = myresult;
524                         stack.priority[stack.pointer] = child->actions[myresult];
525                 }
526         } /* loop until done */
527
528         return myresult;
529 }
530
531
532 #if 0
533 /* If you suspect a bug in the parser, you'll want to use these dump
534  * functions. dump_tree should reproduce a whole tree exactly as it was found
535  * in radiusd.conf, but in long form (all actions explicitly defined) */
536 static void dump_mc(modcallable *c, int indent)
537 {
538         int i;
539
540         if(c->type==MOD_SINGLE) {
541                 modsingle *single = mod_callabletosingle(c);
542                 DEBUG("%.*s%s {", indent, "\t\t\t\t\t\t\t\t\t\t\t",
543                         single->modinst->name);
544         } else {
545                 modgroup *g = mod_callabletogroup(c);
546                 modcallable *p;
547                 DEBUG("%.*sgroup {", indent, "\t\t\t\t\t\t\t\t\t\t\t");
548                 for(p = g->children;p;p = p->next)
549                         dump_mc(p, indent+1);
550         }
551
552         for(i = 0; i<RLM_MODULE_NUMCODES; ++i) {
553                 DEBUG("%.*s%s = %s", indent+1, "\t\t\t\t\t\t\t\t\t\t\t",
554                       lrad_int2str(rcode_table, i, "??"),
555                       action2str(c->actions[i]));
556         }
557
558         DEBUG("%.*s}", indent, "\t\t\t\t\t\t\t\t\t\t\t");
559 }
560
561 static void dump_tree(int comp, modcallable *c)
562 {
563         DEBUG("[%s]", comp2str[comp]);
564         dump_mc(c, 0);
565 }
566 #else
567 static void dump_tree(int comp UNUSED, modcallable *c UNUSED)
568 {
569         return;
570 }
571 #endif
572
573 /* These are the default actions. For each component, the group{} block
574  * behaves like the code from the old module_*() function. redundant{} and
575  * append{} are based on my guesses of what they will be used for. --Pac. */
576 static const int
577 defaultactions[RLM_COMPONENT_COUNT][GROUPTYPE_COUNT][RLM_MODULE_NUMCODES] =
578 {
579         /* authenticate */
580         {
581                 /* group */
582                 {
583                         MOD_ACTION_RETURN,      /* reject   */
584                         1,                      /* fail     */
585                         MOD_ACTION_RETURN,      /* ok       */
586                         MOD_ACTION_RETURN,      /* handled  */
587                         1,                      /* invalid  */
588                         MOD_ACTION_RETURN,      /* userlock */
589                         MOD_ACTION_RETURN,      /* notfound */
590                         1,                      /* noop     */
591                         1                       /* updated  */
592                 },
593                 /* redundant */
594                 {
595                         MOD_ACTION_RETURN,      /* reject   */
596                         1,                      /* fail     */
597                         MOD_ACTION_RETURN,      /* ok       */
598                         MOD_ACTION_RETURN,      /* handled  */
599                         MOD_ACTION_RETURN,      /* invalid  */
600                         MOD_ACTION_RETURN,      /* userlock */
601                         MOD_ACTION_RETURN,      /* notfound */
602                         MOD_ACTION_RETURN,      /* noop     */
603                         MOD_ACTION_RETURN       /* updated  */
604                 },
605                 /* append */
606                 {
607                         MOD_ACTION_RETURN,      /* reject   */
608                         1,                      /* fail     */
609                         MOD_ACTION_RETURN,      /* ok       */
610                         MOD_ACTION_RETURN,      /* handled  */
611                         MOD_ACTION_RETURN,      /* invalid  */
612                         MOD_ACTION_RETURN,      /* userlock */
613                         2,                      /* notfound */
614                         MOD_ACTION_RETURN,      /* noop     */
615                         MOD_ACTION_RETURN       /* updated  */
616                 }
617         },
618         /* authorize */
619         {
620                 /* group */
621                 {
622                         MOD_ACTION_RETURN,      /* reject   */
623                         MOD_ACTION_RETURN,      /* fail     */
624                         3,                      /* ok       */
625                         MOD_ACTION_RETURN,      /* handled  */
626                         MOD_ACTION_RETURN,      /* invalid  */
627                         MOD_ACTION_RETURN,      /* userlock */
628                         1,                      /* notfound */
629                         2,                      /* noop     */
630                         4                       /* updated  */
631                 },
632                 /* redundant */
633                 {
634                         MOD_ACTION_RETURN,      /* reject   */
635                         1,                      /* fail     */
636                         MOD_ACTION_RETURN,      /* ok       */
637                         MOD_ACTION_RETURN,      /* handled  */
638                         MOD_ACTION_RETURN,      /* invalid  */
639                         MOD_ACTION_RETURN,      /* userlock */
640                         MOD_ACTION_RETURN,      /* notfound */
641                         MOD_ACTION_RETURN,      /* noop     */
642                         MOD_ACTION_RETURN       /* updated  */
643                 },
644                 /* append */
645                 {
646                         MOD_ACTION_RETURN,      /* reject   */
647                         1,                      /* fail     */
648                         MOD_ACTION_RETURN,      /* ok       */
649                         MOD_ACTION_RETURN,      /* handled  */
650                         MOD_ACTION_RETURN,      /* invalid  */
651                         MOD_ACTION_RETURN,      /* userlock */
652                         2,                      /* notfound */
653                         MOD_ACTION_RETURN,      /* noop     */
654                         MOD_ACTION_RETURN       /* updated  */
655                 }
656         },
657         /* preacct */
658         {
659                 /* group */
660                 {
661                         MOD_ACTION_RETURN,      /* reject   */
662                         MOD_ACTION_RETURN,      /* fail     */
663                         2,                      /* ok       */
664                         MOD_ACTION_RETURN,      /* handled  */
665                         MOD_ACTION_RETURN,      /* invalid  */
666                         MOD_ACTION_RETURN,      /* userlock */
667                         MOD_ACTION_RETURN,      /* notfound */
668                         1,                      /* noop     */
669                         3                       /* updated  */
670                 },
671                 /* redundant */
672                 {
673                         MOD_ACTION_RETURN,      /* reject   */
674                         1,                      /* fail     */
675                         MOD_ACTION_RETURN,      /* ok       */
676                         MOD_ACTION_RETURN,      /* handled  */
677                         MOD_ACTION_RETURN,      /* invalid  */
678                         MOD_ACTION_RETURN,      /* userlock */
679                         MOD_ACTION_RETURN,      /* notfound */
680                         MOD_ACTION_RETURN,      /* noop     */
681                         MOD_ACTION_RETURN       /* updated  */
682                 },
683                 /* append */
684                 {
685                         MOD_ACTION_RETURN,      /* reject   */
686                         1,                      /* fail     */
687                         MOD_ACTION_RETURN,      /* ok       */
688                         MOD_ACTION_RETURN,      /* handled  */
689                         MOD_ACTION_RETURN,      /* invalid  */
690                         MOD_ACTION_RETURN,      /* userlock */
691                         2,                      /* notfound */
692                         MOD_ACTION_RETURN,      /* noop     */
693                         MOD_ACTION_RETURN       /* updated  */
694                 }
695         },
696         /* accounting */
697         {
698                 /* group */
699                 {
700                         MOD_ACTION_RETURN,      /* reject   */
701                         MOD_ACTION_RETURN,      /* fail     */
702                         2,                      /* ok       */
703                         MOD_ACTION_RETURN,      /* handled  */
704                         MOD_ACTION_RETURN,      /* invalid  */
705                         MOD_ACTION_RETURN,      /* userlock */
706                         MOD_ACTION_RETURN,      /* notfound */
707                         1,                      /* noop     */
708                         3                       /* updated  */
709                 },
710                 /* redundant */
711                 {
712                         1,                      /* reject   */
713                         1,                      /* fail     */
714                         MOD_ACTION_RETURN,      /* ok       */
715                         MOD_ACTION_RETURN,      /* handled  */
716                         1,                      /* invalid  */
717                         1,                      /* userlock */
718                         1,                      /* notfound */
719                         2,                      /* noop     */
720                         4                       /* updated  */
721                 },
722                 /* append */
723                 {
724                         MOD_ACTION_RETURN,      /* reject   */
725                         1,                      /* fail     */
726                         MOD_ACTION_RETURN,      /* ok       */
727                         MOD_ACTION_RETURN,      /* handled  */
728                         MOD_ACTION_RETURN,      /* invalid  */
729                         MOD_ACTION_RETURN,      /* userlock */
730                         2,                      /* notfound */
731                         MOD_ACTION_RETURN,      /* noop     */
732                         MOD_ACTION_RETURN       /* updated  */
733                 }
734         },
735         /* checksimul */
736         {
737                 /* group */
738                 {
739                         MOD_ACTION_RETURN,      /* reject   */
740                         1,                      /* fail     */
741                         MOD_ACTION_RETURN,      /* ok       */
742                         MOD_ACTION_RETURN,      /* handled  */
743                         MOD_ACTION_RETURN,      /* invalid  */
744                         MOD_ACTION_RETURN,      /* userlock */
745                         MOD_ACTION_RETURN,      /* notfound */
746                         MOD_ACTION_RETURN,      /* noop     */
747                         MOD_ACTION_RETURN       /* updated  */
748                 },
749                 /* redundant */
750                 {
751                         MOD_ACTION_RETURN,      /* reject   */
752                         1,                      /* fail     */
753                         MOD_ACTION_RETURN,      /* ok       */
754                         MOD_ACTION_RETURN,      /* handled  */
755                         MOD_ACTION_RETURN,      /* invalid  */
756                         MOD_ACTION_RETURN,      /* userlock */
757                         MOD_ACTION_RETURN,      /* notfound */
758                         MOD_ACTION_RETURN,      /* noop     */
759                         MOD_ACTION_RETURN       /* updated  */
760                 },
761                 /* append */
762                 {
763                         MOD_ACTION_RETURN,      /* reject   */
764                         1,                      /* fail     */
765                         MOD_ACTION_RETURN,      /* ok       */
766                         MOD_ACTION_RETURN,      /* handled  */
767                         MOD_ACTION_RETURN,      /* invalid  */
768                         MOD_ACTION_RETURN,      /* userlock */
769                         MOD_ACTION_RETURN,      /* notfound */
770                         MOD_ACTION_RETURN,      /* noop     */
771                         MOD_ACTION_RETURN       /* updated  */
772                 }
773         },
774         /* pre-proxy */
775         {
776                 /* group */
777                 {
778                         MOD_ACTION_RETURN,      /* reject   */
779                         MOD_ACTION_RETURN,      /* fail     */
780                         3,                      /* ok       */
781                         MOD_ACTION_RETURN,      /* handled  */
782                         MOD_ACTION_RETURN,      /* invalid  */
783                         MOD_ACTION_RETURN,      /* userlock */
784                         1,                      /* notfound */
785                         2,                      /* noop     */
786                         4                       /* updated  */
787                 },
788                 /* redundant */
789                 {
790                         MOD_ACTION_RETURN,      /* reject   */
791                         1,                      /* fail     */
792                         MOD_ACTION_RETURN,      /* ok       */
793                         MOD_ACTION_RETURN,      /* handled  */
794                         MOD_ACTION_RETURN,      /* invalid  */
795                         MOD_ACTION_RETURN,      /* userlock */
796                         MOD_ACTION_RETURN,      /* notfound */
797                         MOD_ACTION_RETURN,      /* noop     */
798                         MOD_ACTION_RETURN       /* updated  */
799                 },
800                 /* append */
801                 {
802                         MOD_ACTION_RETURN,      /* reject   */
803                         1,                      /* fail     */
804                         MOD_ACTION_RETURN,      /* ok       */
805                         MOD_ACTION_RETURN,      /* handled  */
806                         MOD_ACTION_RETURN,      /* invalid  */
807                         MOD_ACTION_RETURN,      /* userlock */
808                         2,                      /* notfound */
809                         MOD_ACTION_RETURN,      /* noop     */
810                         MOD_ACTION_RETURN       /* updated  */
811                 }
812         },
813         /* post-proxy */
814         {
815                 /* group */
816                 {
817                         MOD_ACTION_RETURN,      /* reject   */
818                         MOD_ACTION_RETURN,      /* fail     */
819                         3,                      /* ok       */
820                         MOD_ACTION_RETURN,      /* handled  */
821                         MOD_ACTION_RETURN,      /* invalid  */
822                         MOD_ACTION_RETURN,      /* userlock */
823                         1,                      /* notfound */
824                         2,                      /* noop     */
825                         4                       /* updated  */
826                 },
827                 /* redundant */
828                 {
829                         MOD_ACTION_RETURN,      /* reject   */
830                         1,                      /* fail     */
831                         MOD_ACTION_RETURN,      /* ok       */
832                         MOD_ACTION_RETURN,      /* handled  */
833                         MOD_ACTION_RETURN,      /* invalid  */
834                         MOD_ACTION_RETURN,      /* userlock */
835                         MOD_ACTION_RETURN,      /* notfound */
836                         MOD_ACTION_RETURN,      /* noop     */
837                         MOD_ACTION_RETURN       /* updated  */
838                 },
839                 /* append */
840                 {
841                         MOD_ACTION_RETURN,      /* reject   */
842                         1,                      /* fail     */
843                         MOD_ACTION_RETURN,      /* ok       */
844                         MOD_ACTION_RETURN,      /* handled  */
845                         MOD_ACTION_RETURN,      /* invalid  */
846                         MOD_ACTION_RETURN,      /* userlock */
847                         2,                      /* notfound */
848                         MOD_ACTION_RETURN,      /* noop     */
849                         MOD_ACTION_RETURN       /* updated  */
850                 }
851         },
852         /* post-auth */
853         {
854                 /* group */
855                 {
856                         MOD_ACTION_RETURN,      /* reject   */
857                         MOD_ACTION_RETURN,      /* fail     */
858                         3,                      /* ok       */
859                         MOD_ACTION_RETURN,      /* handled  */
860                         MOD_ACTION_RETURN,      /* invalid  */
861                         MOD_ACTION_RETURN,      /* userlock */
862                         1,                      /* notfound */
863                         2,                      /* noop     */
864                         4                       /* updated  */
865                 },
866                 /* redundant */
867                 {
868                         MOD_ACTION_RETURN,      /* reject   */
869                         1,                      /* fail     */
870                         MOD_ACTION_RETURN,      /* ok       */
871                         MOD_ACTION_RETURN,      /* handled  */
872                         MOD_ACTION_RETURN,      /* invalid  */
873                         MOD_ACTION_RETURN,      /* userlock */
874                         MOD_ACTION_RETURN,      /* notfound */
875                         MOD_ACTION_RETURN,      /* noop     */
876                         MOD_ACTION_RETURN       /* updated  */
877                 },
878                 /* append */
879                 {
880                         MOD_ACTION_RETURN,      /* reject   */
881                         1,                      /* fail     */
882                         MOD_ACTION_RETURN,      /* ok       */
883                         MOD_ACTION_RETURN,      /* handled  */
884                         MOD_ACTION_RETURN,      /* invalid  */
885                         MOD_ACTION_RETURN,      /* userlock */
886                         2,                      /* notfound */
887                         MOD_ACTION_RETURN,      /* noop     */
888                         MOD_ACTION_RETURN       /* updated  */
889                 }
890         }
891 };
892
893
894 /*
895  *      Compile one entry of a module call.
896  */
897 static modcallable *do_compile_modsingle(modcallable *parent,
898                                          int component, CONF_ITEM *ci,
899                                          const char *filename, int grouptype,
900                                          const char **modname)
901 {
902         int lineno;
903         const char *modrefname;
904         modsingle *single;
905         modcallable *csingle;
906         module_instance_t *this;
907
908         if (cf_item_is_section(ci)) {
909                 CONF_SECTION *cs = cf_itemtosection(ci);
910                 const char *name2 = cf_section_name2(cs);
911
912                 lineno = cf_section_lineno(cs);
913                 modrefname = cf_section_name1(cs);
914                 if (!name2) name2 = "_UnNamedGroup";
915
916                 /*
917                  *      group{}, redundant{}, or append{} may appear
918                  *      where a single module instance was expected.
919                  *      In that case, we hand it off to
920                  *      compile_modgroup
921                  */
922                 if (strcmp(modrefname, "group") == 0) {
923                         *modname = name2;
924                         return do_compile_modgroup(parent, component, cs,
925                                                    filename,
926                                                    GROUPTYPE_SIMPLE,
927                                                    grouptype);
928                 } else if (strcmp(modrefname, "redundant") == 0) {
929                         *modname = name2;
930                         return do_compile_modgroup(parent, component, cs,
931                                                    filename,
932                                                    GROUPTYPE_REDUNDANT,
933                                                    grouptype);
934                 } else if (strcmp(modrefname, "append") == 0) {
935                         *modname = name2;
936                         return do_compile_modgroup(parent, component, cs,
937                                                    filename,
938                                                    GROUPTYPE_APPEND,
939                                                    grouptype);
940                 } else if (strcmp(modrefname, "load-balance") == 0) {
941                         *modname = name2;
942                         csingle= do_compile_modgroup(parent, component, cs,
943                                                      filename,
944                                                      GROUPTYPE_SIMPLE,
945                                                      grouptype);
946                         if (!csingle) return NULL;
947                         csingle->type = MOD_LOAD_BALANCE;
948                         return csingle;
949                 } else if (strcmp(modrefname, "redundant-load-balance") == 0) {
950                         *modname = name2;
951                         csingle= do_compile_modgroup(parent, component, cs,
952                                                      filename,
953                                                      GROUPTYPE_REDUNDANT,
954                                                      grouptype);
955                         if (!csingle) return NULL;
956                         csingle->type = MOD_REDUNDANT_LOAD_BALANCE;
957                         return csingle;
958                 }
959                 /*
960                  *      Else it's a module reference, with updated return
961                  *      codes.
962                  */
963         } else {
964                 CONF_PAIR *cp = cf_itemtopair(ci);
965                 lineno = cf_pair_lineno(cp);
966                 modrefname = cf_pair_attr(cp);
967         }
968
969         /*
970          *      See if the module is a virtual one.  If so, return that,
971          *      rather than doing anything here.
972          */
973         this = find_module_instance(cf_section_find("modules"), modrefname);
974         if (!this) {
975                 CONF_SECTION *cs, *subcs;
976
977                 /*
978                  *      Then, look for it in the "instantiate" section.
979                  */
980                 if (((subcs = cf_section_find(NULL)) != NULL) &&
981                     ((cs = cf_section_sub_find_name2(subcs, "instantiate", NULL)) != NULL)) {
982                         subcs = cf_section_sub_find_name2(cs, NULL, modrefname);
983                         if (subcs) {
984                                 /*
985                                  *      As it's sole configuration, the
986                                  *      virtual module takes a section which
987                                  *      contains the 
988                                  */
989                                 return do_compile_modsingle(parent,
990                                                             component,
991                                                             cf_sectiontoitem(subcs),
992                                                             filename,
993                                                             grouptype,
994                                                             modname);
995                         }
996                 }
997         }
998         if (!this) {
999                 *modname = NULL;
1000                 radlog(L_ERR|L_CONS, "%s[%d] Failed to find module \"%s\".", filename,
1001                        lineno, modrefname);
1002                 return NULL;
1003         }
1004
1005         /*
1006          *      We know it's all OK, allocate the structures, and fill
1007          *      them in.
1008          */
1009         single = rad_malloc(sizeof(*single));
1010         memset(single, 0, sizeof(*single));
1011         csingle = mod_singletocallable(single);
1012         csingle->parent = parent;
1013         csingle->next = NULL;
1014         memcpy(csingle->actions, defaultactions[component][grouptype],
1015                sizeof csingle->actions);
1016         rad_assert(modrefname != NULL);
1017         csingle->name = modrefname;
1018         csingle->type = MOD_SINGLE;
1019
1020         /*
1021          *      Singles can override the actions, virtual modules cannot.
1022          *
1023          *      FIXME: We may want to re-visit how to do this...
1024          *      maybe a csingle as a ref?
1025          */
1026         if (cf_item_is_section(ci)) {
1027                 CONF_SECTION *cs = cf_itemtosection(ci);
1028                 CONF_PAIR *cp;
1029                 const char *attr, *value;
1030
1031                 for (ci=cf_item_find_next(cs, NULL);
1032                      ci != NULL;
1033                      ci=cf_item_find_next(cs, ci)) {
1034
1035                         if (cf_item_is_section(ci)) {
1036                                 radlog(L_ERR|L_CONS,
1037                                        "%s[%d] Subsection of module instance call "
1038                                        "not allowed\n", filename,
1039                                        cf_section_lineno(cf_itemtosection(ci)));
1040                                 modcallable_free(&csingle);
1041                                 return NULL;
1042                         }
1043
1044                         cp = cf_itemtopair(ci);
1045                         attr = cf_pair_attr(cp);
1046                         value = cf_pair_value(cp);
1047                         lineno = cf_pair_lineno(cp);
1048
1049                         if (!compile_action(csingle, attr, value, filename,
1050                                             lineno)) {
1051                                 modcallable_free(&csingle);
1052                                 return NULL;
1053                         }
1054                 }
1055         }
1056
1057         /*
1058          *      Bail out if the module in question does not supply the
1059          *      wanted component
1060          */
1061         if (!this->entry->module->methods[component]) {
1062                 radlog(L_ERR|L_CONS,
1063                        "%s[%d]: \"%s\" modules aren't allowed in '%s' sections -- they have no such method.",
1064                        filename, lineno, this->entry->module->name,
1065                        comp2str[component]);
1066                 modcallable_free(&csingle);
1067                 return NULL;
1068         }
1069
1070         single->modinst = this;
1071         *modname = this->entry->module->name;
1072         return csingle;
1073 }
1074
1075 modcallable *compile_modsingle(modcallable *parent,
1076                                int component, CONF_ITEM *ci,
1077                                const char *filename, const char **modname)
1078 {
1079         modcallable *ret = do_compile_modsingle(parent, component, ci,
1080                                                 filename,
1081                                                 GROUPTYPE_SIMPLE,
1082                                                 modname);
1083         dump_tree(component, ret);
1084         return ret;
1085 }
1086
1087
1088 /*
1089  *      Internal compile group code.
1090  */
1091 static modcallable *do_compile_modgroup(modcallable *parent,
1092                                         int component, CONF_SECTION *cs,
1093                                         const char *filename, int grouptype,
1094                                         int parentgrouptype)
1095 {
1096         int i;
1097         modgroup *g;
1098         modcallable *c;
1099         CONF_ITEM *ci;
1100
1101         g = rad_malloc(sizeof(*g));
1102         memset(g, 0, sizeof(*g));
1103         g->grouptype = grouptype;
1104
1105         c = mod_grouptocallable(g);
1106         c->parent = parent;
1107         c->next = NULL;
1108         memset(c->actions, 0, sizeof(c->actions));
1109
1110         /*
1111          *      Remember the name for printing, etc.
1112          *
1113          *      FIXME: We may also want to put the names into a
1114          *      rbtree, so that groups can reference each other...
1115          */
1116         c->name = cf_section_name2(cs);
1117         if (!c->name) c->name = "";
1118         c->type = MOD_GROUP;
1119         g->children = NULL;
1120
1121         /*
1122          *      Loop over the children of this group.
1123          */
1124         for (ci=cf_item_find_next(cs, NULL);
1125              ci != NULL;
1126              ci=cf_item_find_next(cs, ci)) {
1127
1128                 /*
1129                  *      Sections are references to other groups, or
1130                  *      to modules with updated return codes.
1131                  */
1132                 if (cf_item_is_section(ci)) {
1133                         const char *junk = NULL;
1134                         modcallable *single;
1135                         int lineno;
1136                         CONF_SECTION *subcs = cf_itemtosection(ci);
1137
1138                         lineno = cf_section_lineno(subcs);
1139
1140                         single = do_compile_modsingle(c, component, ci,
1141                                                       filename,
1142                                                       grouptype, &junk);
1143                         if (!single) {
1144                                 radlog(L_ERR|L_CONS,
1145                                        "%s[%d] Failed to parse \"%s\" subsection.\n",
1146                                        filename, lineno,
1147                                        cf_section_name1(subcs));
1148                                 modcallable_free(&c);
1149                                 return NULL;
1150                         }
1151                         add_child(g, single);
1152
1153                 } else {
1154                         const char *attr, *value;
1155                         CONF_PAIR *cp = cf_itemtopair(ci);
1156                         int lineno;
1157
1158                         attr = cf_pair_attr(cp);
1159                         value = cf_pair_value(cp);
1160                         lineno = cf_pair_lineno(cp);
1161
1162                         /*
1163                          *      A CONF_PAIR is either a module
1164                          *      instance with no actions
1165                          *      specified ...
1166                          */
1167                         if (value[0] == 0) {
1168                                 modcallable *single;
1169                                 const char *junk = NULL;
1170
1171                                 single = do_compile_modsingle(c,
1172                                                               component,
1173                                                               cf_pairtoitem(cp),
1174                                                               filename,
1175                                                               grouptype,
1176                                                               &junk);
1177                                 if (!single) {
1178                                         radlog(L_ERR|L_CONS,
1179                                                "%s[%d] Failed to parse \"%s\" entry.\n",
1180                                                filename, lineno, attr);
1181                                         modcallable_free(&c);
1182                                         return NULL;
1183                                 }
1184                                 add_child(g, single);
1185
1186                                 /*
1187                                  *      Or a module instance with action.
1188                                  */
1189                         } else if (!compile_action(c, attr, value, filename,
1190                                                    lineno)) {
1191                                 modcallable_free(&c);
1192                                 return NULL;
1193                         } /* else it worked */
1194                 }
1195         }
1196
1197         /*
1198          *      Set the default actions, if they haven't already been
1199          *      set.
1200          */
1201         for (i = 0; i < RLM_MODULE_NUMCODES; i++) {
1202                 if (!c->actions[i]) {
1203                         c->actions[i] = defaultactions[component][parentgrouptype][i];
1204                 }
1205         }
1206
1207         /*
1208          *      FIXME: If there are no children, return NULL?
1209          */
1210         return mod_grouptocallable(g);
1211 }
1212
1213 modcallable *compile_modgroup(modcallable *parent,
1214                               int component, CONF_SECTION *cs,
1215                               const char *filename)
1216 {
1217         modcallable *ret = do_compile_modgroup(parent, component, cs, filename,
1218                                                GROUPTYPE_SIMPLE,
1219                                                GROUPTYPE_SIMPLE);
1220         dump_tree(component, ret);
1221         return ret;
1222 }
1223
1224 void add_to_modcallable(modcallable **parent, modcallable *this,
1225                         int component, const char *name)
1226 {
1227         modgroup *g;
1228         
1229         rad_assert(this != NULL);
1230
1231         if (*parent == NULL) {
1232                 modcallable *c;
1233
1234                 g = rad_malloc(sizeof *g);
1235                 memset(g, 0, sizeof(*g));
1236                 g->grouptype = GROUPTYPE_SIMPLE;
1237                 c = mod_grouptocallable(g);
1238                 c->next = NULL;
1239                 memcpy(c->actions,
1240                        defaultactions[component][GROUPTYPE_SIMPLE],
1241                        sizeof(c->actions));
1242                 rad_assert(name != NULL);
1243                 c->name = name;
1244                 c->type = MOD_GROUP;
1245                 g->children = NULL;
1246
1247                 *parent = mod_grouptocallable(g);
1248         } else {
1249                 g = mod_callabletogroup(*parent);
1250         }
1251
1252         add_child(g, this);
1253 }
1254
1255 void modcallable_free(modcallable **pc)
1256 {
1257         modcallable *c, *loop, *next;
1258         c = *pc;
1259         if(c->type==MOD_GROUP) {
1260                 for(loop = mod_callabletogroup(c)->children;
1261                     loop ;
1262                     loop = next) {
1263                         next = loop->next;
1264                         modcallable_free(&loop);
1265                 }
1266         }
1267         free(c);
1268         *pc = NULL;
1269 }