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