a6f81aff49243221808b517b34c58a5937b1edbe
[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 extern int radius_get_vp(REQUEST *request, const char *name, VALUE_PAIR **vp_p);
32
33 /* mutually-recursive static functions need a prototype up front */
34 static modcallable *do_compile_modgroup(modcallable *,
35                                         int, CONF_SECTION *,
36                                         int, int);
37
38 /* Actions may be a positive integer (the highest one returned in the group
39  * will be returned), or the keyword "return", represented here by
40  * MOD_ACTION_RETURN, to cause an immediate return.
41  * There's also the keyword "reject", represented here by MOD_ACTION_REJECT
42  * to cause an immediate reject. */
43 #define MOD_ACTION_RETURN  (-1)
44 #define MOD_ACTION_REJECT  (-2)
45
46 /* Here are our basic types: modcallable, modgroup, and modsingle. For an
47  * explanation of what they are all about, see ../../doc/README.failover */
48 struct modcallable {
49         modcallable *parent;
50         struct modcallable *next;
51         const char *name;
52         enum { MOD_SINGLE = 1, MOD_GROUP, MOD_LOAD_BALANCE, MOD_REDUNDANT_LOAD_BALANCE,
53 #ifdef WITH_UNLANG
54                MOD_IF, MOD_ELSE, MOD_ELSIF, MOD_UPDATE, MOD_SWITCH, MOD_CASE,
55 #endif
56                MOD_POLICY, MOD_REFERENCE, MOD_XLAT } type;
57         int method;
58         int actions[RLM_MODULE_NUMCODES];
59 };
60
61 #define GROUPTYPE_SIMPLE        0
62 #define GROUPTYPE_REDUNDANT     1
63 #define GROUPTYPE_APPEND        2
64 #define GROUPTYPE_COUNT         3
65
66 typedef struct {
67         modcallable mc;         /* self */
68         int grouptype;  /* after mc */
69         modcallable *children;
70         CONF_SECTION *cs;
71         VALUE_PAIR *vps;
72 } modgroup;
73
74 typedef struct {
75         modcallable mc;
76         module_instance_t *modinst;
77 } modsingle;
78
79 typedef struct {
80         modcallable mc;
81         const char *ref_name;
82         CONF_SECTION *ref_cs;
83 } modref;
84
85 typedef struct {
86         modcallable mc;
87         int exec;
88         char *xlat_name;
89 } modxlat;
90
91 static const FR_NAME_NUMBER grouptype_table[] = {
92         { "", GROUPTYPE_SIMPLE },
93         { "redundant ", GROUPTYPE_REDUNDANT },
94         { "append ", GROUPTYPE_APPEND },
95         { NULL, -1 }
96 };
97
98 /* Simple conversions: modsingle and modgroup are subclasses of modcallable,
99  * so we often want to go back and forth between them. */
100 static modsingle *mod_callabletosingle(modcallable *p)
101 {
102         rad_assert(p->type==MOD_SINGLE);
103         return (modsingle *)p;
104 }
105 static modgroup *mod_callabletogroup(modcallable *p)
106 {
107         rad_assert((p->type > MOD_SINGLE) && (p->type <= MOD_POLICY));
108
109         return (modgroup *)p;
110 }
111 static modcallable *mod_singletocallable(modsingle *p)
112 {
113         return (modcallable *)p;
114 }
115 static modcallable *mod_grouptocallable(modgroup *p)
116 {
117         return (modcallable *)p;
118 }
119
120 static modref *mod_callabletoref(modcallable *p)
121 {
122         rad_assert(p->type==MOD_REFERENCE);
123         return (modref *)p;
124 }
125 static modcallable *mod_reftocallable(modref *p)
126 {
127         return (modcallable *)p;
128 }
129
130 static modxlat *mod_callabletoxlat(modcallable *p)
131 {
132         rad_assert(p->type==MOD_XLAT);
133         return (modxlat *)p;
134 }
135 static modcallable *mod_xlattocallable(modxlat *p)
136 {
137         return (modcallable *)p;
138 }
139
140 /* modgroups are grown by adding a modcallable to the end */
141 /* FIXME: This is O(N^2) */
142 static void add_child(modgroup *g, modcallable *c)
143 {
144         modcallable **head = &g->children;
145         modcallable *node = *head;
146         modcallable **last = head;
147
148         if (!c) return;
149
150         while (node) {
151                 last = &node->next;
152                 node = node->next;
153         }
154
155         rad_assert(c->next == NULL);
156         *last = c;
157         c->parent = mod_grouptocallable(g);
158 }
159
160 /* Here's where we recognize all of our keywords: first the rcodes, then the
161  * actions */
162 static const FR_NAME_NUMBER rcode_table[] = {
163         { "reject",     RLM_MODULE_REJECT       },
164         { "fail",       RLM_MODULE_FAIL         },
165         { "ok",         RLM_MODULE_OK           },
166         { "handled",    RLM_MODULE_HANDLED      },
167         { "invalid",    RLM_MODULE_INVALID      },
168         { "userlock",   RLM_MODULE_USERLOCK     },
169         { "notfound",   RLM_MODULE_NOTFOUND     },
170         { "noop",       RLM_MODULE_NOOP         },
171         { "updated",    RLM_MODULE_UPDATED      },
172         { NULL, 0 }
173 };
174
175
176 /*
177  *      Compile action && rcode for later use.
178  */
179 static int compile_action(modcallable *c, CONF_PAIR *cp)
180 {
181         int action;
182         const char *attr, *value;
183
184         attr = cf_pair_attr(cp);
185         value = cf_pair_value(cp);
186         if (!value) return 0;
187
188         if (!strcasecmp(value, "return"))
189                 action = MOD_ACTION_RETURN;
190
191         else if (!strcasecmp(value, "break"))
192                 action = MOD_ACTION_RETURN;
193
194         else if (!strcasecmp(value, "reject"))
195                 action = MOD_ACTION_REJECT;
196
197         else if (strspn(value, "0123456789")==strlen(value)) {
198                 action = atoi(value);
199
200                 /*
201                  *      Don't allow priority zero, for future use.
202                  */
203                 if (action == 0) return 0;
204         } else {
205                 cf_log_err(cf_pairtoitem(cp), "Unknown action '%s'.\n",
206                            value);
207                 return 0;
208         }
209
210         if (strcasecmp(attr, "default") != 0) {
211                 int rcode;
212
213                 rcode = fr_str2int(rcode_table, attr, -1);
214                 if (rcode < 0) {
215                         cf_log_err(cf_pairtoitem(cp),
216                                    "Unknown module rcode '%s'.\n",
217                                    attr);
218                         return 0;
219                 }
220                 c->actions[rcode] = action;
221
222         } else {                /* set all unset values to the default */
223                 int i;
224
225                 for (i = 0; i < RLM_MODULE_NUMCODES; i++) {
226                         if (!c->actions[i]) c->actions[i] = action;
227                 }
228         }
229
230         return 1;
231 }
232
233 /* Some short names for debugging output */
234 static const char * const comp2str[] = {
235         "authenticate",
236         "authorize",
237         "preacct",
238         "accounting",
239         "session",
240         "pre-proxy",
241         "post-proxy",
242         "post-auth"
243 #ifdef WITH_COA
244         ,
245         "recv-coa",
246         "send-coa"
247 #endif
248 };
249
250 #ifdef HAVE_PTHREAD_H
251 /*
252  *      Lock the mutex for the module
253  */
254 static void safe_lock(module_instance_t *instance)
255 {
256         if (instance->mutex)
257                 pthread_mutex_lock(instance->mutex);
258 }
259
260 /*
261  *      Unlock the mutex for the module
262  */
263 static void safe_unlock(module_instance_t *instance)
264 {
265         if (instance->mutex)
266                 pthread_mutex_unlock(instance->mutex);
267 }
268 #else
269 /*
270  *      No threads: these functions become NULL's.
271  */
272 #define safe_lock(foo)
273 #define safe_unlock(foo)
274 #endif
275
276 static int call_modsingle(int component, modsingle *sp, REQUEST *request)
277 {
278         int myresult;
279         int blocked;
280
281         rad_assert(request != NULL);
282
283         /*
284          *      If the request should stop, refuse to do anything.
285          */
286         blocked = (request->master_state == REQUEST_STOP_PROCESSING);
287         if (blocked) return RLM_MODULE_NOOP;
288
289         RDEBUG3("  modsingle[%s]: calling %s (%s) for request %d",
290                comp2str[component], sp->modinst->name,
291                sp->modinst->entry->name, request->number);
292
293         if (sp->modinst->dead) {
294                 myresult = RLM_MODULE_FAIL;
295                 goto fail;
296         }
297
298         safe_lock(sp->modinst);
299
300         /*
301          *      For logging unresponsive children.
302          */
303         request->module = sp->modinst->name;
304
305         myresult = sp->modinst->entry->module->methods[component](
306                         sp->modinst->insthandle, request);
307
308         request->module = "";
309         safe_unlock(sp->modinst);
310
311         /*
312          *      Wasn't blocked, and now is.  Complain!
313          */
314         blocked = (request->master_state == REQUEST_STOP_PROCESSING);
315         if (blocked) {
316                 radlog(L_INFO, "WARNING: Module %s became unblocked for request %u",
317                        sp->modinst->entry->name, request->number);
318         }
319
320  fail:
321         RDEBUG3("  modsingle[%s]: returned from %s (%s) for request %d",
322                comp2str[component], sp->modinst->name,
323                sp->modinst->entry->name, request->number);
324
325         return myresult;
326 }
327
328
329 static int default_component_results[RLM_COMPONENT_COUNT] = {
330         RLM_MODULE_REJECT,      /* AUTH */
331         RLM_MODULE_NOTFOUND,    /* AUTZ */
332         RLM_MODULE_NOOP,        /* PREACCT */
333         RLM_MODULE_NOOP,        /* ACCT */
334         RLM_MODULE_FAIL,        /* SESS */
335         RLM_MODULE_NOOP,        /* PRE_PROXY */
336         RLM_MODULE_NOOP,        /* POST_PROXY */
337         RLM_MODULE_NOOP         /* POST_AUTH */
338 #ifdef WITH_COA
339         ,
340         RLM_MODULE_NOOP,        /* RECV_COA_TYPE */
341         RLM_MODULE_NOOP         /* SEND_COA_TYPE */
342 #endif
343 };
344
345
346 static const char *group_name[] = {
347         "",
348         "single",
349         "group",
350         "load-balance group",
351         "redundant-load-balance group",
352 #ifdef WITH_UNLANG
353         "if",
354         "else",
355         "elsif",
356         "update",
357         "switch",
358         "case",
359 #endif
360         "policy"
361 };
362
363 /* Here's where we recognize all of our keywords: first the rcodes, then the
364  * actions */
365 const FR_NAME_NUMBER mod_rcode_table[] = {
366         { "reject",     RLM_MODULE_REJECT       },
367         { "fail",       RLM_MODULE_FAIL  },
368         { "ok",         RLM_MODULE_OK      },
369         { "handled",    RLM_MODULE_HANDLED      },
370         { "invalid",    RLM_MODULE_INVALID      },
371         { "userlock",   RLM_MODULE_USERLOCK     },
372         { "notfound",   RLM_MODULE_NOTFOUND     },
373         { "noop",       RLM_MODULE_NOOP  },
374         { "updated",    RLM_MODULE_UPDATED      },
375         { NULL, 0 }
376 };
377
378 static const char *modcall_spaces = "++++++++++++++++++++++++++++++++";
379
380 #define MODCALL_STACK_MAX (32)
381
382 #define MOD_LOG_OPEN_BRACE(_name) RDEBUG2("%.*s%s %s {", depth + 1, modcall_spaces, _name, c->name)
383 #define MOD_LOG_CLOSE_BRACE() RDEBUG2("%.*s} # %s %s = %s", depth + 1, modcall_spaces, \
384                                       group_name[c->type], c->name ? c->name : "", \
385                                       fr_int2str(mod_rcode_table, result, "<invalid>"))
386
387 /*
388  *      Don't call the modules recursively.  Instead, do them
389  *      iteratively, and manage the call stack ourselves.
390  */
391 typedef struct modcall_stack_entry_t {
392         int result;
393         int priority;
394         modcallable *c;
395 } modcall_stack_entry_t;
396
397
398 static int modcall_recurse(REQUEST *request, int component, int depth,
399                             modcall_stack_entry_t *entry);
400
401 /*
402  *      Call a child of a block.
403  */
404 static void modcall_child(REQUEST *request, int component, int depth,
405                           modcall_stack_entry_t *entry, modcallable *c,
406                           int *result, int *priority)
407 {
408         modcall_stack_entry_t *next;
409
410         if (depth >= MODCALL_STACK_MAX) {
411                 radlog(L_ERR, "Internal sanity check failed: module stack is too deep");
412                 exit(1);
413         }
414
415         /*
416          *      Initialize the childs stack frame.
417          */
418         next = entry + 1;
419         next->c = c;
420         next->result = entry->result;
421         next->priority = 0;
422
423         if (!modcall_recurse(request, component,
424                              depth, next)) {
425                 *result = RLM_MODULE_FAIL;
426                  return;
427         }
428
429         *result = next->result;
430         *priority = next->priority;
431
432         return;
433 }
434
435 /*
436  *      Interpret the various types of blocks.
437  */
438 static int modcall_recurse(REQUEST *request, int component, int depth,
439                             modcall_stack_entry_t *entry)
440 {
441         int if_taken, was_if;
442         modcallable *c;
443         int result, priority;
444
445         was_if = if_taken = FALSE;
446         result = RLM_MODULE_FAIL;
447
448 redo:
449         priority = -1;
450         c = entry->c;
451
452         /*
453          *      Nothing more to do.  Return the code and priority
454          *      which was set by the caller.
455          */
456         if (!c) return TRUE;
457
458         /*
459          *      We've been asked to stop.  Do so.
460          */
461         if ((request->master_state == REQUEST_STOP_PROCESSING) ||
462             (request->parent &&
463              (request->parent->master_state == REQUEST_STOP_PROCESSING))) {
464                 entry->result = RLM_MODULE_FAIL;
465                 entry->priority = 9999;
466                 return TRUE;
467         }
468
469         /*
470          *      Handle "if" conditions.
471          */
472         if (c->type == MOD_IF) {
473                 int condition;
474                 modgroup *g;
475                 const char *p;
476
477         mod_if:
478                 g = mod_callabletogroup(c);
479                 p = c->name;
480
481                 RDEBUG2("%.*s? %s %s", depth + 1, modcall_spaces,
482                         group_name[c->type], c->name);
483
484                 if (radius_evaluate_condition(request, entry->result,
485                                               0, &p, TRUE, &condition)) {
486                         RDEBUG2("%.*s? %s %s -> %s", depth + 1, modcall_spaces,
487                                 group_name[c->type],
488                                 c->name, condition ? "TRUE" : "FALSE");
489                 } else {
490                         condition = FALSE;
491                 }
492
493                 /*
494                  *      Didn't pass.  Remember that.
495                  */
496                 if (!condition) {
497                         was_if = TRUE;
498                         if_taken = FALSE;
499                         goto next_sibling;
500                 }
501
502                 /*
503                  *      We took the "if".  Go recurse into its' children.
504                  */
505                 was_if = TRUE;
506                 if_taken = TRUE;
507                 goto do_children;
508         } /* MOD_IF */
509
510         /*
511          *      "else" if the previous "if" was taken.
512          *      "if" if the previous if wasn't taken.
513          */
514         if (c->type == MOD_ELSIF) {
515                 if (!was_if) goto elsif_error;
516
517                 /*
518                  *      Like MOD_ELSE, but allow for a later "else"
519                  */
520                 if (if_taken) {
521                         RDEBUG2("%.*s ... skipping %s for request %d: Preceding \"if\" was taken",
522                                 depth + 1, modcall_spaces,
523                                 group_name[c->type], request->number);
524                         was_if = TRUE;
525                         if_taken = TRUE;
526                         goto next_sibling;
527                 }
528
529                 /*
530                  *      Check the "if" condition.
531                  */
532                 goto mod_if;
533         } /* MOD_ELSIF */
534
535         /*
536          *      "else" for a preceding "if".
537          */
538         if (c->type == MOD_ELSE) {
539                 if (!was_if) { /* error */
540                 elsif_error:
541                         RDEBUG2("%.*s ... skipping %s for request %d: No preceding \"if\"",
542                                 depth + 1, modcall_spaces,
543                                 group_name[c->type], request->number);                  
544                         goto next_sibling;
545                 }
546
547                 if (if_taken) {
548                         RDEBUG2("%.*s ... skipping %s for request %d: Preceding \"if\" was taken",
549                                 depth + 1, modcall_spaces,
550                                 group_name[c->type], request->number);
551                         was_if = FALSE;
552                         if_taken = FALSE;
553                         goto next_sibling;
554                 }
555
556                 /*
557                  *      We need to process it.  Go do that.
558                  */
559                 was_if = FALSE;
560                 if_taken = FALSE;
561                 goto do_children;
562         } /* MOD_ELSE */
563
564         /*
565          *      We're no longer processing if/else/elsif.  Reset the
566          *      trackers for those conditions.
567          */
568         was_if = FALSE;
569         if_taken = FALSE;
570
571         if (c->type == MOD_SINGLE) {
572                 modsingle *sp;
573
574                 /*
575                  *      Process a stand-alone child, and fall through
576                  *      to dealing with it's parent.
577                  */
578                 sp = mod_callabletosingle(c);
579         
580                 result = call_modsingle(c->method, sp, request);
581                 RDEBUG2("%.*s[%s] = %s", depth + 1, modcall_spaces, c->name ? c->name : "",
582                         fr_int2str(mod_rcode_table, result, "<invalid>"));
583                 goto calculate_result;
584         } /* MOD_SINGLE */
585
586         /*
587          *      Update attribute(s)
588          */
589         if (c->type == MOD_UPDATE) {
590                 int rcode;
591                 modgroup *g = mod_callabletogroup(c);
592
593                 MOD_LOG_OPEN_BRACE("update");
594                 rcode = radius_update_attrlist(request, g->cs,
595                                                g->vps, c->name);
596                 if (rcode != RLM_MODULE_UPDATED) {
597                         result = rcode;
598                         goto calculate_result;
599                 }
600
601                 result = RLM_MODULE_NOOP;
602                 MOD_LOG_CLOSE_BRACE();
603                 goto next_sibling;
604         } /* MOD_IF */
605
606         /*
607          *      Child is a group that has children of it's own.
608          */
609         if ((c->type == MOD_GROUP) || (c->type == MOD_POLICY) ||
610             (c->type == MOD_CASE)) {
611                 modgroup *g;
612
613         do_children:
614                 g = mod_callabletogroup(c);
615
616                 MOD_LOG_OPEN_BRACE(group_name[c->type]);
617                 modcall_child(request, component,
618                               depth + 1, entry, g->children,
619                               &result, &priority);
620                 MOD_LOG_CLOSE_BRACE();
621                 goto calculate_result;
622         } /* MOD_GROUP */
623
624         if (c->type == MOD_SWITCH) {
625                 modcallable *this, *found, *null_case;
626                 modgroup *g;
627                 char buffer[1024];
628
629                 MOD_LOG_OPEN_BRACE("switch");
630
631                 /*
632                  *      If there's no %, it refers to an attribute.
633                  *      Otherwise, expand it.
634                  */
635                 if (!strchr(c->name, '%')) {
636                         VALUE_PAIR *vp = NULL;
637
638                         radius_get_vp(request, c->name, &vp);
639                         if (vp) {
640                                 vp_prints_value(buffer,
641                                                 sizeof(buffer),
642                                                 vp, 0);
643                         } else {
644                                 *buffer = '\0';
645                         }
646                 } else {
647                         radius_xlat(buffer, sizeof(buffer),
648                                     c->name, request, NULL);
649                 }
650
651                 /*
652                  *      Find either the exact matching name, or the
653                  *      "case {...}" statement.
654                  */
655                 g = mod_callabletogroup(c);
656                 null_case = found = NULL;
657                 for (this = g->children; this; this = this->next) {
658                         if (!this->name) {
659                                 if (!null_case) null_case = this;
660                                 continue;
661                         }
662                         if (strcmp(buffer, this->name) == 0) {
663                                 found = this;
664                                 break;
665                         }
666                 }
667                 
668                 if (!found) found = null_case;
669                 
670                 MOD_LOG_OPEN_BRACE(group_name[c->type]);
671                 modcall_child(request, component,
672                               depth + 1, entry, found,
673                               &result, &priority);
674                 MOD_LOG_CLOSE_BRACE();
675                 goto calculate_result;
676         } /* MOD_SWITCH */
677
678         if ((c->type == MOD_LOAD_BALANCE) ||
679             (c->type == MOD_REDUNDANT_LOAD_BALANCE)) {
680                 int count;
681                 modcallable *this, *found;
682                 modgroup *g;
683
684                 MOD_LOG_OPEN_BRACE("load-balance");
685
686                 g = mod_callabletogroup(c);
687                 found = NULL;
688                 for (this = g->children; this; this = this->next) {
689                         if (!found) {
690                                 found = this;
691                                 count = 1;
692                                 continue;
693                         }
694                         count++;
695
696                         if ((count * (fr_rand() & 0xffff)) < (uint32_t) 0x10000) {
697                                 found = this;
698                         }
699                 }
700
701                 MOD_LOG_OPEN_BRACE(group_name[c->type]);
702                 
703                 if (c->type == MOD_LOAD_BALANCE) {
704                         modcall_child(request, component,
705                                       depth + 1, entry, found,
706                                       &result, &priority);
707                                                
708                 } else {
709                         int i;
710
711                         /*
712                          *      Loop over all children in this
713                          *      section.  If we get FAIL, then
714                          *      continue.  Otherwise, stop.
715                          */
716                         for (i = 1; i < count; i++) {
717                                 modcall_child(request, component,
718                                               depth + 1, entry, found,
719                                               &result, &priority);
720                                 if (c->actions[result] == MOD_ACTION_RETURN) {
721                                         priority = -1;
722                                         break;
723                                 }
724                         }
725                 }
726                 MOD_LOG_CLOSE_BRACE();
727                 goto calculate_result;
728         } /* MOD_LOAD_BALANCE */
729
730         /*
731          *      Reference another virtual server.
732          *
733          *      This should really be deleted, and replaced with a
734          *      more abstracted / functional version.
735          */
736         if (c->type == MOD_REFERENCE) {
737                 modref *mr = mod_callabletoref(c);
738                 char const *server = request->server;
739
740                 if (server == mr->ref_name) {
741                         radlog(L_INFO, "WARNING: Suppressing recursive call to server %s", server);
742                         goto next_sibling;
743                 }
744
745                 request->server = mr->ref_name;
746                 RDEBUG("server %s { # nested call", mr->ref_name);
747                 result = indexed_modcall(component, 0, request);
748                 RDEBUG("} # server %s with nested call", mr->ref_name);
749                 request->server = server;
750                 goto calculate_result;
751         } /* MOD_REFERENCE */
752
753         /*
754          *      xlat a string without doing anything else
755          *
756          *      This should really be deleted, and replaced with a
757          *      more abstracted / functional version.
758          */
759         if (c->type == MOD_XLAT) {
760                 modxlat *mx = mod_callabletoxlat(c);
761                 char buffer[128];
762
763                 if (!mx->exec) {
764                         radius_xlat(buffer, sizeof(buffer),
765                                     mx->xlat_name, request, NULL);
766                 } else {
767                         RDEBUG("`%s`", mx->xlat_name);
768                                 radius_exec_program(mx->xlat_name, request,
769                                                     0, NULL, 0,
770                                                     request->packet->vps,
771                                                     NULL, 1);
772                 }
773
774                 goto next_sibling;
775         } /* MOD_XLAT */
776         
777         /*
778          *      Add new module types here.
779          */
780
781 calculate_result:
782         /*
783          *      The child's action says return.  Do so.
784          */
785         if ((c->actions[result] == MOD_ACTION_RETURN) &&
786             (priority <= 0)) {
787                 entry->result = result;
788                 return TRUE;
789         }
790
791         /*
792          *      If "reject", break out of the loop and return
793          *      reject.
794          */
795         if (c->actions[result] == MOD_ACTION_REJECT) {
796                 entry->result = RLM_MODULE_REJECT;
797                 return TRUE;
798         }
799
800         /*
801          *      The array holds a default priority for this return
802          *      code.  Grab it in preference to any unset priority.
803          */
804         if (priority < 0) priority = c->actions[result];
805
806         /*
807          *      We're higher than any previous priority, remember this
808          *      return code and priority.
809          */
810         if (priority > entry->priority) {
811                 entry->result = result;
812                 entry->priority = priority;
813         }
814
815         /*
816          *      If we're processing a "case" statement, we return once
817          *      it's done, rather than going to the next "case" statement.
818          */
819         if (c->type == MOD_CASE) return TRUE;
820
821 next_sibling:
822         entry->c = entry->c->next;
823
824         if (entry->c) goto redo;
825
826         /*
827          *      And we're done!
828          */
829         return TRUE;
830 }
831
832
833 /**
834  * @brief Call a module, iteratively, with a local stack, rather than
835  *      recursively.  What did Paul Graham say about Lisp...?
836  */
837 int modcall(int component, modcallable *c, REQUEST *request)
838 {
839         modcall_stack_entry_t stack[MODCALL_STACK_MAX];
840
841         if ((component < 0) || (component >= RLM_COMPONENT_COUNT)) {
842                 return RLM_MODULE_FAIL;
843         }
844
845         /*
846          *      Set up the initial stack frame.
847          */
848         stack[0].c = c;
849         stack[0].result = default_component_results[component];
850         stack[0].priority = 0;
851
852         /*
853          *      Call the main handler.
854          */
855         if (!modcall_recurse(request, component, 0, &stack[0])) {
856                 return RLM_MODULE_FAIL;
857         }
858
859         /*
860          *      Return the result.
861          */
862         return stack[0].result;
863 }
864
865 #if 0
866 static const char *action2str(int action)
867 {
868         static char buf[32];
869         if(action==MOD_ACTION_RETURN)
870                 return "return";
871         if(action==MOD_ACTION_REJECT)
872                 return "reject";
873         snprintf(buf, sizeof buf, "%d", action);
874         return buf;
875 }
876
877 /* If you suspect a bug in the parser, you'll want to use these dump
878  * functions. dump_tree should reproduce a whole tree exactly as it was found
879  * in radiusd.conf, but in long form (all actions explicitly defined) */
880 static void dump_mc(modcallable *c, int indent)
881 {
882         int i;
883
884         if(c->type==MOD_SINGLE) {
885                 modsingle *single = mod_callabletosingle(c);
886                 DEBUG("%.*s%s {", indent, "\t\t\t\t\t\t\t\t\t\t\t",
887                         single->modinst->name);
888         } else if ((c->type > MOD_SINGLE) && (c->type <= MOD_POLICY)) {
889                 modgroup *g = mod_callabletogroup(c);
890                 modcallable *p;
891                 DEBUG("%.*s%s {", indent, "\t\t\t\t\t\t\t\t\t\t\t",
892                       group_name[c->type]);
893                 for(p = g->children;p;p = p->next)
894                         dump_mc(p, indent+1);
895         } /* else ignore it for now */
896
897         for(i = 0; i<RLM_MODULE_NUMCODES; ++i) {
898                 DEBUG("%.*s%s = %s", indent+1, "\t\t\t\t\t\t\t\t\t\t\t",
899                       fr_int2str(rcode_table, i, "??"),
900                       action2str(c->actions[i]));
901         }
902
903         DEBUG("%.*s}", indent, "\t\t\t\t\t\t\t\t\t\t\t");
904 }
905
906 static void dump_tree(int comp, modcallable *c)
907 {
908         RDEBUG("[%s]", comp2str[comp]);
909         dump_mc(c, 0);
910 }
911 #else
912 #define dump_tree(a, b)
913 #endif
914
915 /* These are the default actions. For each component, the group{} block
916  * behaves like the code from the old module_*() function. redundant{} and
917  * append{} are based on my guesses of what they will be used for. --Pac. */
918 static const int
919 defaultactions[RLM_COMPONENT_COUNT][GROUPTYPE_COUNT][RLM_MODULE_NUMCODES] =
920 {
921         /* authenticate */
922         {
923                 /* group */
924                 {
925                         MOD_ACTION_RETURN,      /* reject   */
926                         1,                      /* fail     */
927                         MOD_ACTION_RETURN,      /* ok       */
928                         MOD_ACTION_RETURN,      /* handled  */
929                         1,                      /* invalid  */
930                         MOD_ACTION_RETURN,      /* userlock */
931                         MOD_ACTION_RETURN,      /* notfound */
932                         1,                      /* noop     */
933                         1                       /* updated  */
934                 },
935                 /* redundant */
936                 {
937                         MOD_ACTION_RETURN,      /* reject   */
938                         1,                      /* fail     */
939                         MOD_ACTION_RETURN,      /* ok       */
940                         MOD_ACTION_RETURN,      /* handled  */
941                         MOD_ACTION_RETURN,      /* invalid  */
942                         MOD_ACTION_RETURN,      /* userlock */
943                         MOD_ACTION_RETURN,      /* notfound */
944                         MOD_ACTION_RETURN,      /* noop     */
945                         MOD_ACTION_RETURN       /* updated  */
946                 },
947                 /* append */
948                 {
949                         MOD_ACTION_RETURN,      /* reject   */
950                         1,                      /* fail     */
951                         MOD_ACTION_RETURN,      /* ok       */
952                         MOD_ACTION_RETURN,      /* handled  */
953                         MOD_ACTION_RETURN,      /* invalid  */
954                         MOD_ACTION_RETURN,      /* userlock */
955                         2,                      /* notfound */
956                         MOD_ACTION_RETURN,      /* noop     */
957                         MOD_ACTION_RETURN       /* updated  */
958                 }
959         },
960         /* authorize */
961         {
962                 /* group */
963                 {
964                         MOD_ACTION_RETURN,      /* reject   */
965                         MOD_ACTION_RETURN,      /* fail     */
966                         3,                      /* ok       */
967                         MOD_ACTION_RETURN,      /* handled  */
968                         MOD_ACTION_RETURN,      /* invalid  */
969                         MOD_ACTION_RETURN,      /* userlock */
970                         1,                      /* notfound */
971                         2,                      /* noop     */
972                         4                       /* updated  */
973                 },
974                 /* redundant */
975                 {
976                         MOD_ACTION_RETURN,      /* reject   */
977                         1,                      /* fail     */
978                         MOD_ACTION_RETURN,      /* ok       */
979                         MOD_ACTION_RETURN,      /* handled  */
980                         MOD_ACTION_RETURN,      /* invalid  */
981                         MOD_ACTION_RETURN,      /* userlock */
982                         MOD_ACTION_RETURN,      /* notfound */
983                         MOD_ACTION_RETURN,      /* noop     */
984                         MOD_ACTION_RETURN       /* updated  */
985                 },
986                 /* append */
987                 {
988                         MOD_ACTION_RETURN,      /* reject   */
989                         1,                      /* fail     */
990                         MOD_ACTION_RETURN,      /* ok       */
991                         MOD_ACTION_RETURN,      /* handled  */
992                         MOD_ACTION_RETURN,      /* invalid  */
993                         MOD_ACTION_RETURN,      /* userlock */
994                         2,                      /* notfound */
995                         MOD_ACTION_RETURN,      /* noop     */
996                         MOD_ACTION_RETURN       /* updated  */
997                 }
998         },
999         /* preacct */
1000         {
1001                 /* group */
1002                 {
1003                         MOD_ACTION_RETURN,      /* reject   */
1004                         MOD_ACTION_RETURN,      /* fail     */
1005                         2,                      /* ok       */
1006                         MOD_ACTION_RETURN,      /* handled  */
1007                         MOD_ACTION_RETURN,      /* invalid  */
1008                         MOD_ACTION_RETURN,      /* userlock */
1009                         MOD_ACTION_RETURN,      /* notfound */
1010                         1,                      /* noop     */
1011                         3                       /* updated  */
1012                 },
1013                 /* redundant */
1014                 {
1015                         MOD_ACTION_RETURN,      /* reject   */
1016                         1,                      /* fail     */
1017                         MOD_ACTION_RETURN,      /* ok       */
1018                         MOD_ACTION_RETURN,      /* handled  */
1019                         MOD_ACTION_RETURN,      /* invalid  */
1020                         MOD_ACTION_RETURN,      /* userlock */
1021                         MOD_ACTION_RETURN,      /* notfound */
1022                         MOD_ACTION_RETURN,      /* noop     */
1023                         MOD_ACTION_RETURN       /* updated  */
1024                 },
1025                 /* append */
1026                 {
1027                         MOD_ACTION_RETURN,      /* reject   */
1028                         1,                      /* fail     */
1029                         MOD_ACTION_RETURN,      /* ok       */
1030                         MOD_ACTION_RETURN,      /* handled  */
1031                         MOD_ACTION_RETURN,      /* invalid  */
1032                         MOD_ACTION_RETURN,      /* userlock */
1033                         2,                      /* notfound */
1034                         MOD_ACTION_RETURN,      /* noop     */
1035                         MOD_ACTION_RETURN       /* updated  */
1036                 }
1037         },
1038         /* accounting */
1039         {
1040                 /* group */
1041                 {
1042                         MOD_ACTION_RETURN,      /* reject   */
1043                         MOD_ACTION_RETURN,      /* fail     */
1044                         2,                      /* ok       */
1045                         MOD_ACTION_RETURN,      /* handled  */
1046                         MOD_ACTION_RETURN,      /* invalid  */
1047                         MOD_ACTION_RETURN,      /* userlock */
1048                         MOD_ACTION_RETURN,      /* notfound */
1049                         1,                      /* noop     */
1050                         3                       /* updated  */
1051                 },
1052                 /* redundant */
1053                 {
1054                         1,                      /* reject   */
1055                         1,                      /* fail     */
1056                         MOD_ACTION_RETURN,      /* ok       */
1057                         MOD_ACTION_RETURN,      /* handled  */
1058                         1,                      /* invalid  */
1059                         1,                      /* userlock */
1060                         1,                      /* notfound */
1061                         2,                      /* noop     */
1062                         4                       /* updated  */
1063                 },
1064                 /* append */
1065                 {
1066                         MOD_ACTION_RETURN,      /* reject   */
1067                         1,                      /* fail     */
1068                         MOD_ACTION_RETURN,      /* ok       */
1069                         MOD_ACTION_RETURN,      /* handled  */
1070                         MOD_ACTION_RETURN,      /* invalid  */
1071                         MOD_ACTION_RETURN,      /* userlock */
1072                         2,                      /* notfound */
1073                         MOD_ACTION_RETURN,      /* noop     */
1074                         MOD_ACTION_RETURN       /* updated  */
1075                 }
1076         },
1077         /* checksimul */
1078         {
1079                 /* group */
1080                 {
1081                         MOD_ACTION_RETURN,      /* reject   */
1082                         1,                      /* fail     */
1083                         MOD_ACTION_RETURN,      /* ok       */
1084                         MOD_ACTION_RETURN,      /* handled  */
1085                         MOD_ACTION_RETURN,      /* invalid  */
1086                         MOD_ACTION_RETURN,      /* userlock */
1087                         MOD_ACTION_RETURN,      /* notfound */
1088                         MOD_ACTION_RETURN,      /* noop     */
1089                         MOD_ACTION_RETURN       /* updated  */
1090                 },
1091                 /* redundant */
1092                 {
1093                         MOD_ACTION_RETURN,      /* reject   */
1094                         1,                      /* fail     */
1095                         MOD_ACTION_RETURN,      /* ok       */
1096                         MOD_ACTION_RETURN,      /* handled  */
1097                         MOD_ACTION_RETURN,      /* invalid  */
1098                         MOD_ACTION_RETURN,      /* userlock */
1099                         MOD_ACTION_RETURN,      /* notfound */
1100                         MOD_ACTION_RETURN,      /* noop     */
1101                         MOD_ACTION_RETURN       /* updated  */
1102                 },
1103                 /* append */
1104                 {
1105                         MOD_ACTION_RETURN,      /* reject   */
1106                         1,                      /* fail     */
1107                         MOD_ACTION_RETURN,      /* ok       */
1108                         MOD_ACTION_RETURN,      /* handled  */
1109                         MOD_ACTION_RETURN,      /* invalid  */
1110                         MOD_ACTION_RETURN,      /* userlock */
1111                         MOD_ACTION_RETURN,      /* notfound */
1112                         MOD_ACTION_RETURN,      /* noop     */
1113                         MOD_ACTION_RETURN       /* updated  */
1114                 }
1115         },
1116         /* pre-proxy */
1117         {
1118                 /* group */
1119                 {
1120                         MOD_ACTION_RETURN,      /* reject   */
1121                         MOD_ACTION_RETURN,      /* fail     */
1122                         3,                      /* ok       */
1123                         MOD_ACTION_RETURN,      /* handled  */
1124                         MOD_ACTION_RETURN,      /* invalid  */
1125                         MOD_ACTION_RETURN,      /* userlock */
1126                         1,                      /* notfound */
1127                         2,                      /* noop     */
1128                         4                       /* updated  */
1129                 },
1130                 /* redundant */
1131                 {
1132                         MOD_ACTION_RETURN,      /* reject   */
1133                         1,                      /* fail     */
1134                         MOD_ACTION_RETURN,      /* ok       */
1135                         MOD_ACTION_RETURN,      /* handled  */
1136                         MOD_ACTION_RETURN,      /* invalid  */
1137                         MOD_ACTION_RETURN,      /* userlock */
1138                         MOD_ACTION_RETURN,      /* notfound */
1139                         MOD_ACTION_RETURN,      /* noop     */
1140                         MOD_ACTION_RETURN       /* updated  */
1141                 },
1142                 /* append */
1143                 {
1144                         MOD_ACTION_RETURN,      /* reject   */
1145                         1,                      /* fail     */
1146                         MOD_ACTION_RETURN,      /* ok       */
1147                         MOD_ACTION_RETURN,      /* handled  */
1148                         MOD_ACTION_RETURN,      /* invalid  */
1149                         MOD_ACTION_RETURN,      /* userlock */
1150                         2,                      /* notfound */
1151                         MOD_ACTION_RETURN,      /* noop     */
1152                         MOD_ACTION_RETURN       /* updated  */
1153                 }
1154         },
1155         /* post-proxy */
1156         {
1157                 /* group */
1158                 {
1159                         MOD_ACTION_RETURN,      /* reject   */
1160                         MOD_ACTION_RETURN,      /* fail     */
1161                         3,                      /* ok       */
1162                         MOD_ACTION_RETURN,      /* handled  */
1163                         MOD_ACTION_RETURN,      /* invalid  */
1164                         MOD_ACTION_RETURN,      /* userlock */
1165                         1,                      /* notfound */
1166                         2,                      /* noop     */
1167                         4                       /* updated  */
1168                 },
1169                 /* redundant */
1170                 {
1171                         MOD_ACTION_RETURN,      /* reject   */
1172                         1,                      /* fail     */
1173                         MOD_ACTION_RETURN,      /* ok       */
1174                         MOD_ACTION_RETURN,      /* handled  */
1175                         MOD_ACTION_RETURN,      /* invalid  */
1176                         MOD_ACTION_RETURN,      /* userlock */
1177                         MOD_ACTION_RETURN,      /* notfound */
1178                         MOD_ACTION_RETURN,      /* noop     */
1179                         MOD_ACTION_RETURN       /* updated  */
1180                 },
1181                 /* append */
1182                 {
1183                         MOD_ACTION_RETURN,      /* reject   */
1184                         1,                      /* fail     */
1185                         MOD_ACTION_RETURN,      /* ok       */
1186                         MOD_ACTION_RETURN,      /* handled  */
1187                         MOD_ACTION_RETURN,      /* invalid  */
1188                         MOD_ACTION_RETURN,      /* userlock */
1189                         2,                      /* notfound */
1190                         MOD_ACTION_RETURN,      /* noop     */
1191                         MOD_ACTION_RETURN       /* updated  */
1192                 }
1193         },
1194         /* post-auth */
1195         {
1196                 /* group */
1197                 {
1198                         MOD_ACTION_RETURN,      /* reject   */
1199                         MOD_ACTION_RETURN,      /* fail     */
1200                         3,                      /* ok       */
1201                         MOD_ACTION_RETURN,      /* handled  */
1202                         MOD_ACTION_RETURN,      /* invalid  */
1203                         MOD_ACTION_RETURN,      /* userlock */
1204                         1,                      /* notfound */
1205                         2,                      /* noop     */
1206                         4                       /* updated  */
1207                 },
1208                 /* redundant */
1209                 {
1210                         MOD_ACTION_RETURN,      /* reject   */
1211                         1,                      /* fail     */
1212                         MOD_ACTION_RETURN,      /* ok       */
1213                         MOD_ACTION_RETURN,      /* handled  */
1214                         MOD_ACTION_RETURN,      /* invalid  */
1215                         MOD_ACTION_RETURN,      /* userlock */
1216                         MOD_ACTION_RETURN,      /* notfound */
1217                         MOD_ACTION_RETURN,      /* noop     */
1218                         MOD_ACTION_RETURN       /* updated  */
1219                 },
1220                 /* append */
1221                 {
1222                         MOD_ACTION_RETURN,      /* reject   */
1223                         1,                      /* fail     */
1224                         MOD_ACTION_RETURN,      /* ok       */
1225                         MOD_ACTION_RETURN,      /* handled  */
1226                         MOD_ACTION_RETURN,      /* invalid  */
1227                         MOD_ACTION_RETURN,      /* userlock */
1228                         2,                      /* notfound */
1229                         MOD_ACTION_RETURN,      /* noop     */
1230                         MOD_ACTION_RETURN       /* updated  */
1231                 }
1232         }
1233 #ifdef WITH_COA
1234         ,
1235         /* recv-coa */
1236         {
1237                 /* group */
1238                 {
1239                         MOD_ACTION_RETURN,      /* reject   */
1240                         MOD_ACTION_RETURN,      /* fail     */
1241                         3,                      /* ok       */
1242                         MOD_ACTION_RETURN,      /* handled  */
1243                         MOD_ACTION_RETURN,      /* invalid  */
1244                         MOD_ACTION_RETURN,      /* userlock */
1245                         1,                      /* notfound */
1246                         2,                      /* noop     */
1247                         4                       /* updated  */
1248                 },
1249                 /* redundant */
1250                 {
1251                         MOD_ACTION_RETURN,      /* reject   */
1252                         1,                      /* fail     */
1253                         MOD_ACTION_RETURN,      /* ok       */
1254                         MOD_ACTION_RETURN,      /* handled  */
1255                         MOD_ACTION_RETURN,      /* invalid  */
1256                         MOD_ACTION_RETURN,      /* userlock */
1257                         MOD_ACTION_RETURN,      /* notfound */
1258                         MOD_ACTION_RETURN,      /* noop     */
1259                         MOD_ACTION_RETURN       /* updated  */
1260                 },
1261                 /* append */
1262                 {
1263                         MOD_ACTION_RETURN,      /* reject   */
1264                         1,                      /* fail     */
1265                         MOD_ACTION_RETURN,      /* ok       */
1266                         MOD_ACTION_RETURN,      /* handled  */
1267                         MOD_ACTION_RETURN,      /* invalid  */
1268                         MOD_ACTION_RETURN,      /* userlock */
1269                         2,                      /* notfound */
1270                         MOD_ACTION_RETURN,      /* noop     */
1271                         MOD_ACTION_RETURN       /* updated  */
1272                 }
1273         },
1274         /* send-coa */
1275         {
1276                 /* group */
1277                 {
1278                         MOD_ACTION_RETURN,      /* reject   */
1279                         MOD_ACTION_RETURN,      /* fail     */
1280                         3,                      /* ok       */
1281                         MOD_ACTION_RETURN,      /* handled  */
1282                         MOD_ACTION_RETURN,      /* invalid  */
1283                         MOD_ACTION_RETURN,      /* userlock */
1284                         1,                      /* notfound */
1285                         2,                      /* noop     */
1286                         4                       /* updated  */
1287                 },
1288                 /* redundant */
1289                 {
1290                         MOD_ACTION_RETURN,      /* reject   */
1291                         1,                      /* fail     */
1292                         MOD_ACTION_RETURN,      /* ok       */
1293                         MOD_ACTION_RETURN,      /* handled  */
1294                         MOD_ACTION_RETURN,      /* invalid  */
1295                         MOD_ACTION_RETURN,      /* userlock */
1296                         MOD_ACTION_RETURN,      /* notfound */
1297                         MOD_ACTION_RETURN,      /* noop     */
1298                         MOD_ACTION_RETURN       /* updated  */
1299                 },
1300                 /* append */
1301                 {
1302                         MOD_ACTION_RETURN,      /* reject   */
1303                         1,                      /* fail     */
1304                         MOD_ACTION_RETURN,      /* ok       */
1305                         MOD_ACTION_RETURN,      /* handled  */
1306                         MOD_ACTION_RETURN,      /* invalid  */
1307                         MOD_ACTION_RETURN,      /* userlock */
1308                         2,                      /* notfound */
1309                         MOD_ACTION_RETURN,      /* noop     */
1310                         MOD_ACTION_RETURN       /* updated  */
1311                 }
1312         }
1313 #endif
1314 };
1315
1316
1317 #ifdef WITH_UNLANG
1318 static modcallable *do_compile_modupdate(modcallable *parent,
1319                                          int component, CONF_SECTION *cs,
1320                                          const char *name2)
1321 {
1322         int i, ok = FALSE;
1323         const char *vp_name;
1324         modgroup *g;
1325         modcallable *csingle;
1326         CONF_ITEM *ci;
1327         VALUE_PAIR *head, **tail;
1328
1329         static const char *attrlist_names[] = {
1330                 "request", "reply", "proxy-request", "proxy-reply",
1331                 "config", "control",
1332                 "coa", "coa-reply", "disconnect", "disconnect-reply",
1333                 NULL
1334         };
1335
1336         component = component;  /* -Wunused */
1337
1338         if (!cf_section_name2(cs)) {
1339                 cf_log_err(cf_sectiontoitem(cs),
1340                            "Require list name for 'update'.\n");
1341                 return NULL;
1342         }
1343
1344         vp_name = name2;
1345         if (strncmp(vp_name, "outer.", 6) == 0) {
1346                 vp_name += 6;
1347         } 
1348
1349         for (i = 0; attrlist_names[i] != NULL; i++) {
1350                 if (strcmp(vp_name, attrlist_names[i]) == 0) {
1351                         ok = TRUE;
1352                         break;
1353                 }
1354         }
1355
1356         if (!ok) {
1357                 cf_log_err(cf_sectiontoitem(cs),
1358                            "Unknown attribute list \"%s\"",
1359                            name2);
1360                 return NULL;
1361         }
1362
1363         head = NULL;
1364         tail = &head;
1365
1366         /*
1367          *      Walk through the children of the update section,
1368          *      ensuring that they're all known attributes.
1369          */
1370         for (ci=cf_item_find_next(cs, NULL);
1371              ci != NULL;
1372              ci=cf_item_find_next(cs, ci)) {
1373                 CONF_PAIR *cp;
1374                 VALUE_PAIR *vp;
1375
1376                 if (cf_item_is_section(ci)) {
1377                         cf_log_err(ci, "\"update\" sections cannot have subsections");
1378                         return NULL;
1379                 }
1380
1381                 if (!cf_item_is_pair(ci)) continue;
1382
1383                 cp = cf_itemtopair(ci); /* can't return NULL */
1384                 vp = cf_pairtovp(cp);
1385                 if (!vp) {
1386                         pairfree(&head);
1387                         cf_log_err(ci, "ERROR: %s", fr_strerror());
1388                         return NULL;
1389                 }
1390
1391                 if ((vp->operator != T_OP_EQ) &&
1392                     (vp->operator != T_OP_CMP_EQ) &&
1393                     (vp->operator != T_OP_ADD) &&
1394                     (vp->operator != T_OP_SUB) &&
1395                     (vp->operator != T_OP_LE) &&
1396                     (vp->operator != T_OP_GE) &&
1397                     (vp->operator != T_OP_CMP_FALSE) &&
1398                     (vp->operator != T_OP_SET)) {
1399                         pairfree(&head);
1400                         pairfree(&vp);
1401                         cf_log_err(ci, "Invalid operator for attribute");
1402                         return NULL;
1403                 }
1404
1405                 /*
1406                  *      A few more sanity checks.  The enforcement of
1407                  *      <= or >= can only happen for integer
1408                  *      attributes.
1409                  */
1410                 if ((vp->operator == T_OP_LE) ||
1411                     (vp->operator == T_OP_GE)) {
1412                         if ((vp->type != PW_TYPE_BYTE) &&
1413                             (vp->type != PW_TYPE_SHORT) &&
1414                             (vp->type != PW_TYPE_INTEGER)) {
1415                                 pairfree(&head);
1416                                 pairfree(&vp);
1417                                 cf_log_err(ci, "Enforcment of <= or >= is possible only for integer attributes");
1418                                 return NULL;
1419                         }
1420                 }
1421
1422                 *tail = vp;
1423                 tail = &(vp->next);
1424         }
1425
1426         if (!head) {
1427                 cf_log_err(cf_sectiontoitem(cs),
1428                            "ERROR: update %s section cannot be empty",
1429                            name2);
1430                 return NULL;
1431         }
1432
1433         g = rad_malloc(sizeof(*g)); /* never fails */
1434         memset(g, 0, sizeof(*g));
1435         csingle = mod_grouptocallable(g);
1436         
1437         csingle->parent = parent;
1438         csingle->next = NULL;
1439         csingle->name = name2;
1440         csingle->type = MOD_UPDATE;
1441         csingle->method = component;
1442         
1443         g->grouptype = GROUPTYPE_SIMPLE;
1444         g->children = NULL;
1445         g->cs = cs;
1446         g->vps = head;
1447
1448         return csingle;
1449 }
1450
1451
1452 static modcallable *do_compile_modswitch(modcallable *parent,
1453                                          int component, CONF_SECTION *cs)
1454 {
1455         modcallable *csingle;
1456         CONF_ITEM *ci;
1457         int had_seen_default = FALSE;
1458
1459         component = component;  /* -Wunused */
1460
1461         if (!cf_section_name2(cs)) {
1462                 cf_log_err(cf_sectiontoitem(cs),
1463                            "You must specify a variable to switch over for 'switch'.");
1464                 return NULL;
1465         }
1466
1467         if (!cf_item_find_next(cs, NULL)) {
1468                 cf_log_err(cf_sectiontoitem(cs), "'switch' statments cannot be empty.");
1469                 return NULL;
1470         }
1471
1472         /*
1473          *      Walk through the children of the switch section,
1474          *      ensuring that they're all 'case' statements
1475          */
1476         for (ci=cf_item_find_next(cs, NULL);
1477              ci != NULL;
1478              ci=cf_item_find_next(cs, ci)) {
1479                 CONF_SECTION *subcs;
1480                 const char *name1, *name2;
1481
1482                 if (!cf_item_is_section(ci)) {
1483                         if (!cf_item_is_pair(ci)) continue;
1484
1485                         cf_log_err(ci, "\"switch\" sections can only have \"case\" subsections");
1486                         return NULL;
1487                 }
1488
1489                 subcs = cf_itemtosection(ci);   /* can't return NULL */
1490                 name1 = cf_section_name1(subcs);
1491
1492                 if (strcmp(name1, "case") != 0) {
1493                         cf_log_err(ci, "\"switch\" sections can only have \"case\" subsections");
1494                         return NULL;
1495                 }
1496
1497                 name2 = cf_section_name2(subcs);
1498                 if (!name2 && !had_seen_default) {
1499                         had_seen_default = TRUE;
1500                         continue;
1501                 }
1502
1503                 if (!name2 || (name2[0] == '\0')) {
1504                         cf_log_err(ci, "\"case\" sections must have a name");
1505                         return NULL;
1506                 }
1507         }
1508
1509         csingle= do_compile_modgroup(parent, component, cs,
1510                                      GROUPTYPE_SIMPLE, GROUPTYPE_SIMPLE);
1511         if (!csingle) return NULL;
1512         csingle->type = MOD_SWITCH;
1513         return csingle;
1514 }
1515 #endif
1516
1517 static modcallable *do_compile_modserver(modcallable *parent,
1518                                          int component, CONF_ITEM *ci,
1519                                          const char *name,
1520                                          CONF_SECTION *cs,
1521                                          const char *server)
1522 {
1523         modcallable *csingle;
1524         CONF_SECTION *subcs;
1525         modref *mr;
1526
1527         subcs = cf_section_sub_find_name2(cs, comp2str[component], NULL);
1528         if (!subcs) {
1529                 cf_log_err(ci, "Server %s has no %s section",
1530                            server, comp2str[component]);
1531                 return NULL;
1532         }
1533
1534         mr = rad_malloc(sizeof(*mr));
1535         memset(mr, 0, sizeof(*mr));
1536
1537         csingle = mod_reftocallable(mr);
1538         csingle->parent = parent;
1539         csingle->next = NULL;
1540         csingle->name = name;
1541         csingle->type = MOD_REFERENCE;
1542         csingle->method = component;
1543
1544         memcpy(csingle->actions, defaultactions[component][GROUPTYPE_SIMPLE],
1545                sizeof(csingle->actions));
1546         
1547         mr->ref_name = strdup(server);
1548         mr->ref_cs = cs;
1549
1550         return csingle;
1551 }
1552
1553 static modcallable *do_compile_modxlat(modcallable *parent,
1554                                        int component, const char *fmt)
1555 {
1556         modcallable *csingle;
1557         modxlat *mx;
1558
1559         mx = rad_malloc(sizeof(*mx));
1560         memset(mx, 0, sizeof(*mx));
1561
1562         csingle = mod_xlattocallable(mx);
1563         csingle->parent = parent;
1564         csingle->next = NULL;
1565         csingle->name = "expand";
1566         csingle->type = MOD_XLAT;
1567         csingle->method = component;
1568
1569         memcpy(csingle->actions, defaultactions[component][GROUPTYPE_SIMPLE],
1570                sizeof(csingle->actions));
1571         
1572         mx->xlat_name = strdup(fmt);
1573         if (fmt[0] != '%') {
1574                 char *p;
1575                 mx->exec = TRUE;
1576
1577                 strcpy(mx->xlat_name, fmt + 1);
1578                 p = strrchr(mx->xlat_name, '`');
1579                 if (p) *p = '\0';
1580         }
1581
1582         return csingle;
1583 }
1584
1585 /*
1586  *      redundant, etc. can refer to modules or groups, but not much else.
1587  */
1588 static int all_children_are_modules(CONF_SECTION *cs, const char *name)
1589 {
1590         CONF_ITEM *ci;
1591
1592         for (ci=cf_item_find_next(cs, NULL);
1593              ci != NULL;
1594              ci=cf_item_find_next(cs, ci)) {
1595                 /*
1596                  *      If we're a redundant, etc. group, then the
1597                  *      intention is to call modules, rather than
1598                  *      processing logic.  These checks aren't
1599                  *      *strictly* necessary, but they keep the users
1600                  *      from doing crazy things.
1601                  */
1602                 if (cf_item_is_section(ci)) {
1603                         CONF_SECTION *subcs = cf_itemtosection(ci);
1604                         const char *name1 = cf_section_name1(subcs);
1605
1606                         if ((strcmp(name1, "if") == 0) ||
1607                             (strcmp(name1, "else") == 0) ||
1608                             (strcmp(name1, "elsif") == 0) ||
1609                             (strcmp(name1, "update") == 0) ||
1610                             (strcmp(name1, "switch") == 0) ||
1611                             (strcmp(name1, "case") == 0)) {
1612                                 cf_log_err(ci, "%s sections cannot contain a \"%s\" statement",
1613                                        name, name1);
1614                                 return 0;
1615                         }
1616                         continue;
1617                 }
1618
1619                 if (cf_item_is_pair(ci)) {
1620                         CONF_PAIR *cp = cf_itemtopair(ci);
1621                         if (cf_pair_value(cp) != NULL) {
1622                                 cf_log_err(ci,
1623                                            "Entry with no value is invalid");
1624                                 return 0;
1625                         }
1626                 }
1627         }
1628
1629         return 1;
1630 }
1631
1632
1633 /*
1634  *      Compile one entry of a module call.
1635  */
1636 static modcallable *do_compile_modsingle(modcallable *parent,
1637                                          int component, CONF_ITEM *ci,
1638                                          int grouptype,
1639                                          const char **modname)
1640 {
1641 #ifdef WITH_UNLANG
1642         int result;
1643 #endif
1644         const char *modrefname;
1645         modsingle *single;
1646         modcallable *csingle;
1647         module_instance_t *this;
1648         CONF_SECTION *cs, *subcs, *modules;
1649
1650         if (cf_item_is_section(ci)) {
1651                 const char *name2;
1652
1653                 cs = cf_itemtosection(ci);
1654                 modrefname = cf_section_name1(cs);
1655                 name2 = cf_section_name2(cs);
1656                 if (!name2) name2 = "_UnNamedGroup";
1657
1658                 /*
1659                  *      group{}, redundant{}, or append{} may appear
1660                  *      where a single module instance was expected.
1661                  *      In that case, we hand it off to
1662                  *      compile_modgroup
1663                  */
1664                 if (strcmp(modrefname, "group") == 0) {
1665                         *modname = name2;
1666                         return do_compile_modgroup(parent, component, cs,
1667                                                    GROUPTYPE_SIMPLE,
1668                                                    grouptype);
1669
1670                 } else if (strcmp(modrefname, "redundant") == 0) {
1671                         *modname = name2;
1672
1673                         if (!all_children_are_modules(cs, modrefname)) {
1674                                 return NULL;
1675                         }
1676
1677                         return do_compile_modgroup(parent, component, cs,
1678                                                    GROUPTYPE_REDUNDANT,
1679                                                    grouptype);
1680
1681                 } else if (strcmp(modrefname, "append") == 0) {
1682                         *modname = name2;
1683                         return do_compile_modgroup(parent, component, cs,
1684                                                    GROUPTYPE_APPEND,
1685                                                    grouptype);
1686
1687                 } else if (strcmp(modrefname, "load-balance") == 0) {
1688                         *modname = name2;
1689
1690                         if (!all_children_are_modules(cs, modrefname)) {
1691                                 return NULL;
1692                         }
1693
1694                         csingle= do_compile_modgroup(parent, component, cs,
1695                                                      GROUPTYPE_SIMPLE,
1696                                                      grouptype);
1697                         if (!csingle) return NULL;
1698                         csingle->type = MOD_LOAD_BALANCE;
1699                         return csingle;
1700
1701                 } else if (strcmp(modrefname, "redundant-load-balance") == 0) {
1702                         *modname = name2;
1703
1704                         if (!all_children_are_modules(cs, modrefname)) {
1705                                 return NULL;
1706                         }
1707
1708                         csingle= do_compile_modgroup(parent, component, cs,
1709                                                      GROUPTYPE_REDUNDANT,
1710                                                      grouptype);
1711                         if (!csingle) return NULL;
1712                         csingle->type = MOD_REDUNDANT_LOAD_BALANCE;
1713                         return csingle;
1714
1715 #ifdef WITH_UNLANG
1716                 } else  if (strcmp(modrefname, "if") == 0) {
1717                         if (!cf_section_name2(cs)) {
1718                                 cf_log_err(ci, "'if' without condition.");
1719                                 return NULL;
1720                         }
1721
1722                         *modname = name2;
1723                         csingle= do_compile_modgroup(parent, component, cs,
1724                                                      GROUPTYPE_SIMPLE,
1725                                                      grouptype);
1726                         if (!csingle) return NULL;
1727                         csingle->type = MOD_IF;
1728
1729                         if (!radius_evaluate_condition(NULL, 0, 0, modname,
1730                                                        FALSE, &result)) {
1731                                 modcallable_free(&csingle);
1732                                 return NULL;
1733                         }
1734                         *modname = name2;
1735
1736                         return csingle;
1737
1738                 } else  if (strcmp(modrefname, "elsif") == 0) {
1739                         if (parent &&
1740                             ((parent->type == MOD_LOAD_BALANCE) ||
1741                              (parent->type == MOD_REDUNDANT_LOAD_BALANCE))) {
1742                                 cf_log_err(ci, "'elsif' cannot be used in this section.");
1743                                 return NULL;
1744                         }
1745
1746                         if (!cf_section_name2(cs)) {
1747                                 cf_log_err(ci, "'elsif' without condition.");
1748                                 return NULL;
1749                         }
1750
1751                         *modname = name2;
1752                         csingle= do_compile_modgroup(parent, component, cs,
1753                                                      GROUPTYPE_SIMPLE,
1754                                                      grouptype);
1755                         if (!csingle) return NULL;
1756                         csingle->type = MOD_ELSIF;
1757
1758                         if (!radius_evaluate_condition(NULL, 0, 0, modname,
1759                                                        FALSE, &result)) {
1760                                 modcallable_free(&csingle);
1761                                 return NULL;
1762                         }
1763                         *modname = name2;
1764
1765                         return csingle;
1766
1767                 } else  if (strcmp(modrefname, "else") == 0) {
1768                         if (parent &&
1769                             ((parent->type == MOD_LOAD_BALANCE) ||
1770                              (parent->type == MOD_REDUNDANT_LOAD_BALANCE))) {
1771                                 cf_log_err(ci, "'else' cannot be used in this section section.");
1772                                 return NULL;
1773                         }
1774
1775                         if (cf_section_name2(cs)) {
1776                                 cf_log_err(ci, "Cannot have conditions on 'else'.");
1777                                 return NULL;
1778                         }
1779
1780                         *modname = name2;
1781                         csingle= do_compile_modgroup(parent, component, cs,
1782                                                      GROUPTYPE_SIMPLE,
1783                                                      grouptype);
1784                         if (!csingle) return NULL;
1785                         csingle->type = MOD_ELSE;
1786                         return csingle;
1787
1788                 } else  if (strcmp(modrefname, "update") == 0) {
1789                         *modname = name2;
1790
1791                         csingle = do_compile_modupdate(parent, component, cs,
1792                                                        name2);
1793                         if (!csingle) return NULL;
1794
1795                         return csingle;
1796
1797                 } else  if (strcmp(modrefname, "switch") == 0) {
1798                         *modname = name2;
1799
1800                         csingle = do_compile_modswitch(parent, component, cs);
1801                         if (!csingle) return NULL;
1802
1803                         return csingle;
1804
1805                 } else  if (strcmp(modrefname, "case") == 0) {
1806                         int i;
1807
1808                         *modname = name2;
1809
1810                         /*
1811                          *      FIXME: How to tell that the parent can only
1812                          *      be a "switch" statement?
1813                          */
1814                         if (!parent) {
1815                                 cf_log_err(ci, "\"case\" statements may only appear within a \"switch\" section");
1816                                 return NULL;
1817                         }
1818
1819                         csingle= do_compile_modgroup(parent, component, cs,
1820                                                      GROUPTYPE_SIMPLE,
1821                                                      grouptype);
1822                         if (!csingle) return NULL;
1823                         csingle->type = MOD_CASE;
1824                         csingle->name = cf_section_name2(cs); /* may be NULL */
1825
1826                         /*
1827                          *      Set all of it's codes to return, so that
1828                          *      when we pick a 'case' statement, we don't
1829                          *      fall through to processing the next one.
1830                          */
1831                         for (i = 0; i < RLM_MODULE_NUMCODES; i++) {
1832                                 csingle->actions[i] = MOD_ACTION_RETURN;
1833                         }
1834
1835                         return csingle;
1836 #endif
1837                 } /* else it's something like sql { fail = 1 ...} */
1838
1839         } else if (!cf_item_is_pair(ci)) { /* CONF_DATA or some such */
1840                 return NULL;
1841
1842                 /*
1843                  *      Else it's a module reference, with updated return
1844                  *      codes.
1845                  */
1846         } else {
1847                 CONF_SECTION *loop;
1848                 CONF_PAIR *cp = cf_itemtopair(ci);
1849                 modrefname = cf_pair_attr(cp);
1850
1851                 /*
1852                  *      Actions (ok = 1), etc. are orthoganal to just
1853                  *      about everything else.
1854                  */
1855                 if (cf_pair_value(cp) != NULL) {
1856                         cf_log_err(ci, "Entry is not a reference to a module");
1857                         return NULL;
1858                 }
1859
1860                 if (((modrefname[0] == '%') && (modrefname[1] == '{')) ||
1861                     (modrefname[0] == '`')) {
1862                         return do_compile_modxlat(parent, component,
1863                                                   modrefname);
1864                 }
1865
1866                 /*
1867                  *      See if the module is a virtual one.  If so,
1868                  *      return that, rather than doing anything here.
1869                  */
1870                 subcs = NULL;
1871                 cs = cf_section_find("instantiate");
1872                 if (cs) subcs = cf_section_sub_find_name2(cs, NULL,
1873                                                           modrefname);
1874                 if (!subcs &&
1875                     (cs = cf_section_find("policy")) != NULL) {
1876                         char buffer[256];
1877                         
1878                         snprintf(buffer, sizeof(buffer), "%s.%s",
1879                                  modrefname, comp2str[component]);
1880
1881                         /*
1882                          *      Prefer name.section, then name.
1883                          */
1884                         subcs = cf_section_sub_find_name2(cs, NULL,
1885                                                           buffer);
1886                         if (!subcs) {
1887                                 subcs = cf_section_sub_find_name2(cs, NULL,
1888                                                                   modrefname);
1889                         }
1890                 }
1891
1892                 /*
1893                  *      Allow policies to over-ride module names.
1894                  *      i.e. the "sql" policy can do some extra things,
1895                  *      and then call the "sql" module.
1896                  */
1897                 for (loop = cf_item_parent(ci);
1898                      loop && subcs;
1899                      loop = cf_item_parent(cf_sectiontoitem(loop))) {
1900                         if (loop == subcs) {
1901                                 subcs = NULL;
1902                         }
1903                 }
1904
1905                 if (subcs) {
1906                         DEBUG2(" Module: Loading virtual module %s",
1907                                modrefname);
1908
1909                         /*
1910                          *      redundant foo {} is a single.
1911                          */
1912                         if (cf_section_name2(subcs)) {
1913                                 return do_compile_modsingle(parent,
1914                                                             component,
1915                                                             cf_sectiontoitem(subcs),
1916                                                             grouptype,
1917                                                             modname);
1918                         } else {
1919                                 /*
1920                                  *      foo {} is a group.
1921                                  */
1922                                 return do_compile_modgroup(parent,
1923                                                            component,
1924                                                            subcs,
1925                                                            GROUPTYPE_SIMPLE,
1926                                                            grouptype);
1927                         }
1928                 }
1929         }
1930
1931         /*
1932          *      Not a virtual module.  It must be a real module.
1933          */
1934         modules = cf_section_find("modules");
1935         this = NULL;
1936
1937         if (modules && cf_section_sub_find_name2(modules, NULL, modrefname)) {
1938                 this = find_module_instance(modules, modrefname, 1);
1939         }
1940
1941         if (!this) do {
1942                 int i;
1943                 char *p;
1944           
1945                 /*
1946                  *      Maybe it's module.method
1947                  */
1948                 p = strrchr(modrefname, '.');
1949                 if (p) for (i = RLM_COMPONENT_AUTH;
1950                             i < RLM_COMPONENT_COUNT;
1951                             i++) {
1952                         if (strcmp(p + 1, comp2str[i]) == 0) {
1953                                 char buffer[256];
1954
1955                                 strlcpy(buffer, modrefname, sizeof(buffer));
1956                                 buffer[p - modrefname] = '\0';
1957                                 component = i;
1958                                 
1959                                 this = find_module_instance(cf_section_find("modules"), buffer, 1);
1960                                 if (this &&
1961                                     !this->entry->module->methods[i]) {
1962                                         *modname = NULL;
1963                                         cf_log_err(ci, "Module %s has no such method %s", buffer, comp2str[i]);
1964                                         return NULL;
1965                                 }
1966                                 break;
1967                         }
1968                 }
1969                 if (this) break;
1970
1971                 if (strncmp(modrefname, "server[", 7) == 0) {
1972                         char buffer[256];
1973
1974                         strlcpy(buffer, modrefname + 7, sizeof(buffer));
1975                         p = strrchr(buffer, ']');
1976                         if (!p || p[1] != '\0' || (p == buffer)) {
1977                                 cf_log_err(ci, "Invalid server reference in \"%s\".", modrefname);
1978                                 return NULL;
1979                         }
1980                         *p = '\0';
1981
1982                         cs = cf_section_sub_find_name2(NULL, "server", buffer);
1983                         if (!cs) {
1984                                 cf_log_err(ci, "No such server \"%s\".", buffer);
1985                                 return NULL;
1986                         }
1987                         
1988                         return do_compile_modserver(parent, component, ci,
1989                                                     modrefname, cs, buffer);
1990                 }
1991                 
1992                 *modname = NULL;
1993                 cf_log_err(ci, "Failed to find \"%s\" in the \"modules\" section.", modrefname);
1994                 return NULL;
1995         } while (0);
1996
1997         /*
1998          *      We know it's all OK, allocate the structures, and fill
1999          *      them in.
2000          */
2001         single = rad_malloc(sizeof(*single));
2002         memset(single, 0, sizeof(*single));
2003         csingle = mod_singletocallable(single);
2004         csingle->parent = parent;
2005         csingle->next = NULL;
2006         if (!parent || (component != RLM_COMPONENT_AUTH)) {
2007                 memcpy(csingle->actions, defaultactions[component][grouptype],
2008                        sizeof csingle->actions);
2009         } else { /* inside Auth-Type has different rules */
2010                 memcpy(csingle->actions, defaultactions[RLM_COMPONENT_AUTZ][grouptype],
2011                        sizeof csingle->actions);
2012         }
2013         rad_assert(modrefname != NULL);
2014         csingle->name = modrefname;
2015         csingle->type = MOD_SINGLE;
2016         csingle->method = component;
2017
2018         /*
2019          *      Singles can override the actions, virtual modules cannot.
2020          *
2021          *      FIXME: We may want to re-visit how to do this...
2022          *      maybe a csingle as a ref?
2023          */
2024         if (cf_item_is_section(ci)) {
2025                 CONF_ITEM *csi;
2026                 
2027                 cs = cf_itemtosection(ci);
2028                 for (csi=cf_item_find_next(cs, NULL);
2029                      csi != NULL;
2030                      csi=cf_item_find_next(cs, csi)) {
2031
2032                         if (cf_item_is_section(csi)) {
2033                                 cf_log_err(csi, "Subsection of module instance call not allowed");
2034                                 modcallable_free(&csingle);
2035                                 return NULL;
2036                         }
2037
2038                         if (!cf_item_is_pair(csi)) continue;
2039
2040                         if (!compile_action(csingle, cf_itemtopair(csi))) {
2041                                 modcallable_free(&csingle);
2042                                 return NULL;
2043                         }
2044                 }
2045         }
2046
2047         /*
2048          *      Bail out if the module in question does not supply the
2049          *      wanted component
2050          */
2051         if (!this->entry->module->methods[component]) {
2052                 cf_log_err(ci, "\"%s\" modules aren't allowed in '%s' sections -- they have no such method.", this->entry->module->name,
2053                        comp2str[component]);
2054                 modcallable_free(&csingle);
2055                 return NULL;
2056         }
2057
2058         single->modinst = this;
2059         *modname = this->entry->module->name;
2060         return csingle;
2061 }
2062
2063 modcallable *compile_modsingle(modcallable *parent,
2064                                int component, CONF_ITEM *ci,
2065                                const char **modname)
2066 {
2067         modcallable *ret = do_compile_modsingle(parent, component, ci,
2068                                                 GROUPTYPE_SIMPLE,
2069                                                 modname);
2070         dump_tree(component, ret);
2071         return ret;
2072 }
2073
2074
2075 /*
2076  *      Internal compile group code.
2077  */
2078 static modcallable *do_compile_modgroup(modcallable *parent,
2079                                         int component, CONF_SECTION *cs,
2080                                         int grouptype, int parentgrouptype)
2081 {
2082         int i;
2083         modgroup *g;
2084         modcallable *c;
2085         CONF_ITEM *ci;
2086
2087         g = rad_malloc(sizeof(*g));
2088         memset(g, 0, sizeof(*g));
2089         g->grouptype = grouptype;
2090
2091         c = mod_grouptocallable(g);
2092         c->parent = parent;
2093         c->type = MOD_GROUP;
2094         c->next = NULL;
2095         memset(c->actions, 0, sizeof(c->actions));
2096
2097         /*
2098          *      Remember the name for printing, etc.
2099          *
2100          *      FIXME: We may also want to put the names into a
2101          *      rbtree, so that groups can reference each other...
2102          */
2103         c->name = cf_section_name2(cs);
2104         if (!c->name) {
2105                 c->name = cf_section_name1(cs);
2106                 if (strcmp(c->name, "group") == 0) {
2107                         c->name = "";
2108                 } else {
2109                         c->type = MOD_POLICY;
2110                 }
2111         }
2112         g->children = NULL;
2113
2114         /*
2115          *      Loop over the children of this group.
2116          */
2117         for (ci=cf_item_find_next(cs, NULL);
2118              ci != NULL;
2119              ci=cf_item_find_next(cs, ci)) {
2120
2121                 /*
2122                  *      Sections are references to other groups, or
2123                  *      to modules with updated return codes.
2124                  */
2125                 if (cf_item_is_section(ci)) {
2126                         const char *junk = NULL;
2127                         modcallable *single;
2128                         CONF_SECTION *subcs = cf_itemtosection(ci);
2129
2130                         single = do_compile_modsingle(c, component, ci,
2131                                                       grouptype, &junk);
2132                         if (!single) {
2133                                 cf_log_err(ci, "Failed to parse \"%s\" subsection.",
2134                                        cf_section_name1(subcs));
2135                                 modcallable_free(&c);
2136                                 return NULL;
2137                         }
2138                         add_child(g, single);
2139
2140                 } else if (!cf_item_is_pair(ci)) { /* CONF_DATA */
2141                         continue;
2142
2143                 } else {
2144                         const char *attr, *value;
2145                         CONF_PAIR *cp = cf_itemtopair(ci);
2146
2147                         attr = cf_pair_attr(cp);
2148                         value = cf_pair_value(cp);
2149
2150                         /*
2151                          *      A CONF_PAIR is either a module
2152                          *      instance with no actions
2153                          *      specified ...
2154                          */
2155                         if (!value) {
2156                                 modcallable *single;
2157                                 const char *junk = NULL;
2158
2159                                 single = do_compile_modsingle(c,
2160                                                               component,
2161                                                               ci,
2162                                                               grouptype,
2163                                                               &junk);
2164                                 if (!single) {
2165                                         cf_log_err(ci,
2166                                                    "Failed to parse \"%s\" entry.",
2167                                                    attr);
2168                                         modcallable_free(&c);
2169                                         return NULL;
2170                                 }
2171                                 add_child(g, single);
2172
2173                                 /*
2174                                  *      Or a module instance with action.
2175                                  */
2176                         } else if (!compile_action(c, cp)) {
2177                                 modcallable_free(&c);
2178                                 return NULL;
2179                         } /* else it worked */
2180                 }
2181         }
2182
2183         /*
2184          *      Set the default actions, if they haven't already been
2185          *      set.
2186          */
2187         for (i = 0; i < RLM_MODULE_NUMCODES; i++) {
2188                 if (!c->actions[i]) {
2189                         if (!parent || (component != RLM_COMPONENT_AUTH)) {
2190                                 c->actions[i] = defaultactions[component][parentgrouptype][i];
2191                         } else { /* inside Auth-Type has different rules */
2192                                 c->actions[i] = defaultactions[RLM_COMPONENT_AUTZ][parentgrouptype][i];
2193                         }
2194                 }
2195         }
2196
2197         /*
2198          *      FIXME: If there are no children, return NULL?
2199          */
2200         return mod_grouptocallable(g);
2201 }
2202
2203 modcallable *compile_modgroup(modcallable *parent,
2204                               int component, CONF_SECTION *cs)
2205 {
2206         modcallable *ret = do_compile_modgroup(parent, component, cs,
2207                                                GROUPTYPE_SIMPLE,
2208                                                GROUPTYPE_SIMPLE);
2209         dump_tree(component, ret);
2210         return ret;
2211 }
2212
2213 void add_to_modcallable(modcallable **parent, modcallable *this,
2214                         int component, const char *name)
2215 {
2216         modgroup *g;
2217
2218         rad_assert(this != NULL);
2219
2220         if (*parent == NULL) {
2221                 modcallable *c;
2222
2223                 g = rad_malloc(sizeof *g);
2224                 memset(g, 0, sizeof(*g));
2225                 g->grouptype = GROUPTYPE_SIMPLE;
2226                 c = mod_grouptocallable(g);
2227                 c->next = NULL;
2228                 memcpy(c->actions,
2229                        defaultactions[component][GROUPTYPE_SIMPLE],
2230                        sizeof(c->actions));
2231                 rad_assert(name != NULL);
2232                 c->name = name;
2233                 c->type = MOD_GROUP;
2234                 c->method = component;
2235                 g->children = NULL;
2236
2237                 *parent = mod_grouptocallable(g);
2238         } else {
2239                 g = mod_callabletogroup(*parent);
2240         }
2241
2242         add_child(g, this);
2243 }
2244
2245 void modcallable_free(modcallable **pc)
2246 {
2247         modcallable *c, *loop, *next;
2248
2249         if (!pc || !*pc) return;
2250
2251         c = *pc;
2252
2253         if ((c->type > MOD_SINGLE) && (c->type <= MOD_POLICY)) {
2254                 modgroup *g = mod_callabletogroup(c);
2255
2256                 if (g->children) for (loop = g->children;
2257                     loop ;
2258                     loop = next) {
2259                         next = loop->next;
2260                         modcallable_free(&loop);
2261                 }
2262                 pairfree(&g->vps);
2263         }
2264         free(c);
2265         *pc = NULL;
2266 }