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