Add missing close brace
[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                         MOD_LOG_CLOSE_BRACE();
599                         goto calculate_result;
600                 }
601
602                 result = RLM_MODULE_NOOP;
603                 MOD_LOG_CLOSE_BRACE();
604                 goto next_sibling;
605         } /* MOD_IF */
606
607         /*
608          *      Child is a group that has children of it's own.
609          */
610         if ((c->type == MOD_GROUP) || (c->type == MOD_POLICY) ||
611             (c->type == MOD_CASE)) {
612                 modgroup *g;
613
614         do_children:
615                 g = mod_callabletogroup(c);
616
617                 MOD_LOG_OPEN_BRACE(group_name[c->type]);
618                 modcall_child(request, component,
619                               depth + 1, entry, g->children,
620                               &result, &priority);
621                 MOD_LOG_CLOSE_BRACE();
622                 goto calculate_result;
623         } /* MOD_GROUP */
624
625         if (c->type == MOD_SWITCH) {
626                 modcallable *this, *found, *null_case;
627                 modgroup *g;
628                 char buffer[1024];
629
630                 MOD_LOG_OPEN_BRACE("switch");
631
632                 /*
633                  *      If there's no %, it refers to an attribute.
634                  *      Otherwise, expand it.
635                  */
636                 if (!strchr(c->name, '%')) {
637                         VALUE_PAIR *vp = NULL;
638
639                         radius_get_vp(request, c->name, &vp);
640                         if (vp) {
641                                 vp_prints_value(buffer,
642                                                 sizeof(buffer),
643                                                 vp, 0);
644                         } else {
645                                 *buffer = '\0';
646                         }
647                 } else {
648                         radius_xlat(buffer, sizeof(buffer),
649                                     c->name, request, NULL);
650                 }
651
652                 /*
653                  *      Find either the exact matching name, or the
654                  *      "case {...}" statement.
655                  */
656                 g = mod_callabletogroup(c);
657                 null_case = found = NULL;
658                 for (this = g->children; this; this = this->next) {
659                         if (!this->name) {
660                                 if (!null_case) null_case = this;
661                                 continue;
662                         }
663                         if (strcmp(buffer, this->name) == 0) {
664                                 found = this;
665                                 break;
666                         }
667                 }
668                 
669                 if (!found) found = null_case;
670                 
671                 MOD_LOG_OPEN_BRACE(group_name[c->type]);
672                 modcall_child(request, component,
673                               depth + 1, entry, found,
674                               &result, &priority);
675                 MOD_LOG_CLOSE_BRACE();
676                 goto calculate_result;
677         } /* MOD_SWITCH */
678
679         if ((c->type == MOD_LOAD_BALANCE) ||
680             (c->type == MOD_REDUNDANT_LOAD_BALANCE)) {
681                 int count;
682                 modcallable *this, *found;
683                 modgroup *g;
684
685                 MOD_LOG_OPEN_BRACE("load-balance");
686
687                 g = mod_callabletogroup(c);
688                 found = NULL;
689                 for (this = g->children; this; this = this->next) {
690                         if (!found) {
691                                 found = this;
692                                 count = 1;
693                                 continue;
694                         }
695                         count++;
696
697                         if ((count * (fr_rand() & 0xffff)) < (uint32_t) 0x10000) {
698                                 found = this;
699                         }
700                 }
701
702                 MOD_LOG_OPEN_BRACE(group_name[c->type]);
703                 
704                 if (c->type == MOD_LOAD_BALANCE) {
705                         modcall_child(request, component,
706                                       depth + 1, entry, found,
707                                       &result, &priority);
708                                                
709                 } else {
710                         int i;
711
712                         /*
713                          *      Loop over all children in this
714                          *      section.  If we get FAIL, then
715                          *      continue.  Otherwise, stop.
716                          */
717                         for (i = 1; i < count; i++) {
718                                 modcall_child(request, component,
719                                               depth + 1, entry, found,
720                                               &result, &priority);
721                                 if (c->actions[result] == MOD_ACTION_RETURN) {
722                                         priority = -1;
723                                         break;
724                                 }
725                         }
726                 }
727                 MOD_LOG_CLOSE_BRACE();
728                 goto calculate_result;
729         } /* MOD_LOAD_BALANCE */
730
731         /*
732          *      Reference another virtual server.
733          *
734          *      This should really be deleted, and replaced with a
735          *      more abstracted / functional version.
736          */
737         if (c->type == MOD_REFERENCE) {
738                 modref *mr = mod_callabletoref(c);
739                 char const *server = request->server;
740
741                 if (server == mr->ref_name) {
742                         radlog(L_INFO, "WARNING: Suppressing recursive call to server %s", server);
743                         goto next_sibling;
744                 }
745
746                 request->server = mr->ref_name;
747                 RDEBUG("server %s { # nested call", mr->ref_name);
748                 result = indexed_modcall(component, 0, request);
749                 RDEBUG("} # server %s with nested call", mr->ref_name);
750                 request->server = server;
751                 goto calculate_result;
752         } /* MOD_REFERENCE */
753
754         /*
755          *      xlat a string without doing anything else
756          *
757          *      This should really be deleted, and replaced with a
758          *      more abstracted / functional version.
759          */
760         if (c->type == MOD_XLAT) {
761                 modxlat *mx = mod_callabletoxlat(c);
762                 char buffer[128];
763
764                 if (!mx->exec) {
765                         radius_xlat(buffer, sizeof(buffer),
766                                     mx->xlat_name, request, NULL);
767                 } else {
768                         RDEBUG("`%s`", mx->xlat_name);
769                                 radius_exec_program(mx->xlat_name, request,
770                                                     0, NULL, 0,
771                                                     request->packet->vps,
772                                                     NULL, 1);
773                 }
774
775                 goto next_sibling;
776         } /* MOD_XLAT */
777         
778         /*
779          *      Add new module types here.
780          */
781
782 calculate_result:
783         /*
784          *      The child's action says return.  Do so.
785          */
786         if ((c->actions[result] == MOD_ACTION_RETURN) &&
787             (priority <= 0)) {
788                 entry->result = result;
789                 return TRUE;
790         }
791
792         /*
793          *      If "reject", break out of the loop and return
794          *      reject.
795          */
796         if (c->actions[result] == MOD_ACTION_REJECT) {
797                 entry->result = RLM_MODULE_REJECT;
798                 return TRUE;
799         }
800
801         /*
802          *      The array holds a default priority for this return
803          *      code.  Grab it in preference to any unset priority.
804          */
805         if (priority < 0) priority = c->actions[result];
806
807         /*
808          *      We're higher than any previous priority, remember this
809          *      return code and priority.
810          */
811         if (priority > entry->priority) {
812                 entry->result = result;
813                 entry->priority = priority;
814         }
815
816         /*
817          *      If we're processing a "case" statement, we return once
818          *      it's done, rather than going to the next "case" statement.
819          */
820         if (c->type == MOD_CASE) return TRUE;
821
822 next_sibling:
823         entry->c = entry->c->next;
824
825         if (entry->c) goto redo;
826
827         /*
828          *      And we're done!
829          */
830         return TRUE;
831 }
832
833
834 /**
835  * @brief Call a module, iteratively, with a local stack, rather than
836  *      recursively.  What did Paul Graham say about Lisp...?
837  */
838 int modcall(int component, modcallable *c, REQUEST *request)
839 {
840         modcall_stack_entry_t stack[MODCALL_STACK_MAX];
841
842         if ((component < 0) || (component >= RLM_COMPONENT_COUNT)) {
843                 return RLM_MODULE_FAIL;
844         }
845
846         /*
847          *      Set up the initial stack frame.
848          */
849         stack[0].c = c;
850         stack[0].result = default_component_results[component];
851         stack[0].priority = 0;
852
853         /*
854          *      Call the main handler.
855          */
856         if (!modcall_recurse(request, component, 0, &stack[0])) {
857                 return RLM_MODULE_FAIL;
858         }
859
860         /*
861          *      Return the result.
862          */
863         return stack[0].result;
864 }
865
866 #if 0
867 static const char *action2str(int action)
868 {
869         static char buf[32];
870         if(action==MOD_ACTION_RETURN)
871                 return "return";
872         if(action==MOD_ACTION_REJECT)
873                 return "reject";
874         snprintf(buf, sizeof buf, "%d", action);
875         return buf;
876 }
877
878 /* If you suspect a bug in the parser, you'll want to use these dump
879  * functions. dump_tree should reproduce a whole tree exactly as it was found
880  * in radiusd.conf, but in long form (all actions explicitly defined) */
881 static void dump_mc(modcallable *c, int indent)
882 {
883         int i;
884
885         if(c->type==MOD_SINGLE) {
886                 modsingle *single = mod_callabletosingle(c);
887                 DEBUG("%.*s%s {", indent, "\t\t\t\t\t\t\t\t\t\t\t",
888                         single->modinst->name);
889         } else if ((c->type > MOD_SINGLE) && (c->type <= MOD_POLICY)) {
890                 modgroup *g = mod_callabletogroup(c);
891                 modcallable *p;
892                 DEBUG("%.*s%s {", indent, "\t\t\t\t\t\t\t\t\t\t\t",
893                       group_name[c->type]);
894                 for(p = g->children;p;p = p->next)
895                         dump_mc(p, indent+1);
896         } /* else ignore it for now */
897
898         for(i = 0; i<RLM_MODULE_NUMCODES; ++i) {
899                 DEBUG("%.*s%s = %s", indent+1, "\t\t\t\t\t\t\t\t\t\t\t",
900                       fr_int2str(rcode_table, i, "??"),
901                       action2str(c->actions[i]));
902         }
903
904         DEBUG("%.*s}", indent, "\t\t\t\t\t\t\t\t\t\t\t");
905 }
906
907 static void dump_tree(int comp, modcallable *c)
908 {
909         RDEBUG("[%s]", comp2str[comp]);
910         dump_mc(c, 0);
911 }
912 #else
913 #define dump_tree(a, b)
914 #endif
915
916 /* These are the default actions. For each component, the group{} block
917  * behaves like the code from the old module_*() function. redundant{} and
918  * append{} are based on my guesses of what they will be used for. --Pac. */
919 static const int
920 defaultactions[RLM_COMPONENT_COUNT][GROUPTYPE_COUNT][RLM_MODULE_NUMCODES] =
921 {
922         /* authenticate */
923         {
924                 /* group */
925                 {
926                         MOD_ACTION_RETURN,      /* reject   */
927                         1,                      /* fail     */
928                         MOD_ACTION_RETURN,      /* ok       */
929                         MOD_ACTION_RETURN,      /* handled  */
930                         1,                      /* invalid  */
931                         MOD_ACTION_RETURN,      /* userlock */
932                         MOD_ACTION_RETURN,      /* notfound */
933                         1,                      /* noop     */
934                         1                       /* updated  */
935                 },
936                 /* redundant */
937                 {
938                         MOD_ACTION_RETURN,      /* reject   */
939                         1,                      /* fail     */
940                         MOD_ACTION_RETURN,      /* ok       */
941                         MOD_ACTION_RETURN,      /* handled  */
942                         MOD_ACTION_RETURN,      /* invalid  */
943                         MOD_ACTION_RETURN,      /* userlock */
944                         MOD_ACTION_RETURN,      /* notfound */
945                         MOD_ACTION_RETURN,      /* noop     */
946                         MOD_ACTION_RETURN       /* updated  */
947                 },
948                 /* append */
949                 {
950                         MOD_ACTION_RETURN,      /* reject   */
951                         1,                      /* fail     */
952                         MOD_ACTION_RETURN,      /* ok       */
953                         MOD_ACTION_RETURN,      /* handled  */
954                         MOD_ACTION_RETURN,      /* invalid  */
955                         MOD_ACTION_RETURN,      /* userlock */
956                         2,                      /* notfound */
957                         MOD_ACTION_RETURN,      /* noop     */
958                         MOD_ACTION_RETURN       /* updated  */
959                 }
960         },
961         /* authorize */
962         {
963                 /* group */
964                 {
965                         MOD_ACTION_RETURN,      /* reject   */
966                         MOD_ACTION_RETURN,      /* fail     */
967                         3,                      /* ok       */
968                         MOD_ACTION_RETURN,      /* handled  */
969                         MOD_ACTION_RETURN,      /* invalid  */
970                         MOD_ACTION_RETURN,      /* userlock */
971                         1,                      /* notfound */
972                         2,                      /* noop     */
973                         4                       /* updated  */
974                 },
975                 /* redundant */
976                 {
977                         MOD_ACTION_RETURN,      /* reject   */
978                         1,                      /* fail     */
979                         MOD_ACTION_RETURN,      /* ok       */
980                         MOD_ACTION_RETURN,      /* handled  */
981                         MOD_ACTION_RETURN,      /* invalid  */
982                         MOD_ACTION_RETURN,      /* userlock */
983                         MOD_ACTION_RETURN,      /* notfound */
984                         MOD_ACTION_RETURN,      /* noop     */
985                         MOD_ACTION_RETURN       /* updated  */
986                 },
987                 /* append */
988                 {
989                         MOD_ACTION_RETURN,      /* reject   */
990                         1,                      /* fail     */
991                         MOD_ACTION_RETURN,      /* ok       */
992                         MOD_ACTION_RETURN,      /* handled  */
993                         MOD_ACTION_RETURN,      /* invalid  */
994                         MOD_ACTION_RETURN,      /* userlock */
995                         2,                      /* notfound */
996                         MOD_ACTION_RETURN,      /* noop     */
997                         MOD_ACTION_RETURN       /* updated  */
998                 }
999         },
1000         /* preacct */
1001         {
1002                 /* group */
1003                 {
1004                         MOD_ACTION_RETURN,      /* reject   */
1005                         MOD_ACTION_RETURN,      /* fail     */
1006                         2,                      /* ok       */
1007                         MOD_ACTION_RETURN,      /* handled  */
1008                         MOD_ACTION_RETURN,      /* invalid  */
1009                         MOD_ACTION_RETURN,      /* userlock */
1010                         MOD_ACTION_RETURN,      /* notfound */
1011                         1,                      /* noop     */
1012                         3                       /* updated  */
1013                 },
1014                 /* redundant */
1015                 {
1016                         MOD_ACTION_RETURN,      /* reject   */
1017                         1,                      /* fail     */
1018                         MOD_ACTION_RETURN,      /* ok       */
1019                         MOD_ACTION_RETURN,      /* handled  */
1020                         MOD_ACTION_RETURN,      /* invalid  */
1021                         MOD_ACTION_RETURN,      /* userlock */
1022                         MOD_ACTION_RETURN,      /* notfound */
1023                         MOD_ACTION_RETURN,      /* noop     */
1024                         MOD_ACTION_RETURN       /* updated  */
1025                 },
1026                 /* append */
1027                 {
1028                         MOD_ACTION_RETURN,      /* reject   */
1029                         1,                      /* fail     */
1030                         MOD_ACTION_RETURN,      /* ok       */
1031                         MOD_ACTION_RETURN,      /* handled  */
1032                         MOD_ACTION_RETURN,      /* invalid  */
1033                         MOD_ACTION_RETURN,      /* userlock */
1034                         2,                      /* notfound */
1035                         MOD_ACTION_RETURN,      /* noop     */
1036                         MOD_ACTION_RETURN       /* updated  */
1037                 }
1038         },
1039         /* accounting */
1040         {
1041                 /* group */
1042                 {
1043                         MOD_ACTION_RETURN,      /* reject   */
1044                         MOD_ACTION_RETURN,      /* fail     */
1045                         2,                      /* ok       */
1046                         MOD_ACTION_RETURN,      /* handled  */
1047                         MOD_ACTION_RETURN,      /* invalid  */
1048                         MOD_ACTION_RETURN,      /* userlock */
1049                         MOD_ACTION_RETURN,      /* notfound */
1050                         1,                      /* noop     */
1051                         3                       /* updated  */
1052                 },
1053                 /* redundant */
1054                 {
1055                         1,                      /* reject   */
1056                         1,                      /* fail     */
1057                         MOD_ACTION_RETURN,      /* ok       */
1058                         MOD_ACTION_RETURN,      /* handled  */
1059                         1,                      /* invalid  */
1060                         1,                      /* userlock */
1061                         1,                      /* notfound */
1062                         2,                      /* noop     */
1063                         4                       /* updated  */
1064                 },
1065                 /* append */
1066                 {
1067                         MOD_ACTION_RETURN,      /* reject   */
1068                         1,                      /* fail     */
1069                         MOD_ACTION_RETURN,      /* ok       */
1070                         MOD_ACTION_RETURN,      /* handled  */
1071                         MOD_ACTION_RETURN,      /* invalid  */
1072                         MOD_ACTION_RETURN,      /* userlock */
1073                         2,                      /* notfound */
1074                         MOD_ACTION_RETURN,      /* noop     */
1075                         MOD_ACTION_RETURN       /* updated  */
1076                 }
1077         },
1078         /* checksimul */
1079         {
1080                 /* group */
1081                 {
1082                         MOD_ACTION_RETURN,      /* reject   */
1083                         1,                      /* fail     */
1084                         MOD_ACTION_RETURN,      /* ok       */
1085                         MOD_ACTION_RETURN,      /* handled  */
1086                         MOD_ACTION_RETURN,      /* invalid  */
1087                         MOD_ACTION_RETURN,      /* userlock */
1088                         MOD_ACTION_RETURN,      /* notfound */
1089                         MOD_ACTION_RETURN,      /* noop     */
1090                         MOD_ACTION_RETURN       /* updated  */
1091                 },
1092                 /* redundant */
1093                 {
1094                         MOD_ACTION_RETURN,      /* reject   */
1095                         1,                      /* fail     */
1096                         MOD_ACTION_RETURN,      /* ok       */
1097                         MOD_ACTION_RETURN,      /* handled  */
1098                         MOD_ACTION_RETURN,      /* invalid  */
1099                         MOD_ACTION_RETURN,      /* userlock */
1100                         MOD_ACTION_RETURN,      /* notfound */
1101                         MOD_ACTION_RETURN,      /* noop     */
1102                         MOD_ACTION_RETURN       /* updated  */
1103                 },
1104                 /* append */
1105                 {
1106                         MOD_ACTION_RETURN,      /* reject   */
1107                         1,                      /* fail     */
1108                         MOD_ACTION_RETURN,      /* ok       */
1109                         MOD_ACTION_RETURN,      /* handled  */
1110                         MOD_ACTION_RETURN,      /* invalid  */
1111                         MOD_ACTION_RETURN,      /* userlock */
1112                         MOD_ACTION_RETURN,      /* notfound */
1113                         MOD_ACTION_RETURN,      /* noop     */
1114                         MOD_ACTION_RETURN       /* updated  */
1115                 }
1116         },
1117         /* pre-proxy */
1118         {
1119                 /* group */
1120                 {
1121                         MOD_ACTION_RETURN,      /* reject   */
1122                         MOD_ACTION_RETURN,      /* fail     */
1123                         3,                      /* ok       */
1124                         MOD_ACTION_RETURN,      /* handled  */
1125                         MOD_ACTION_RETURN,      /* invalid  */
1126                         MOD_ACTION_RETURN,      /* userlock */
1127                         1,                      /* notfound */
1128                         2,                      /* noop     */
1129                         4                       /* updated  */
1130                 },
1131                 /* redundant */
1132                 {
1133                         MOD_ACTION_RETURN,      /* reject   */
1134                         1,                      /* fail     */
1135                         MOD_ACTION_RETURN,      /* ok       */
1136                         MOD_ACTION_RETURN,      /* handled  */
1137                         MOD_ACTION_RETURN,      /* invalid  */
1138                         MOD_ACTION_RETURN,      /* userlock */
1139                         MOD_ACTION_RETURN,      /* notfound */
1140                         MOD_ACTION_RETURN,      /* noop     */
1141                         MOD_ACTION_RETURN       /* updated  */
1142                 },
1143                 /* append */
1144                 {
1145                         MOD_ACTION_RETURN,      /* reject   */
1146                         1,                      /* fail     */
1147                         MOD_ACTION_RETURN,      /* ok       */
1148                         MOD_ACTION_RETURN,      /* handled  */
1149                         MOD_ACTION_RETURN,      /* invalid  */
1150                         MOD_ACTION_RETURN,      /* userlock */
1151                         2,                      /* notfound */
1152                         MOD_ACTION_RETURN,      /* noop     */
1153                         MOD_ACTION_RETURN       /* updated  */
1154                 }
1155         },
1156         /* post-proxy */
1157         {
1158                 /* group */
1159                 {
1160                         MOD_ACTION_RETURN,      /* reject   */
1161                         MOD_ACTION_RETURN,      /* fail     */
1162                         3,                      /* ok       */
1163                         MOD_ACTION_RETURN,      /* handled  */
1164                         MOD_ACTION_RETURN,      /* invalid  */
1165                         MOD_ACTION_RETURN,      /* userlock */
1166                         1,                      /* notfound */
1167                         2,                      /* noop     */
1168                         4                       /* updated  */
1169                 },
1170                 /* redundant */
1171                 {
1172                         MOD_ACTION_RETURN,      /* reject   */
1173                         1,                      /* fail     */
1174                         MOD_ACTION_RETURN,      /* ok       */
1175                         MOD_ACTION_RETURN,      /* handled  */
1176                         MOD_ACTION_RETURN,      /* invalid  */
1177                         MOD_ACTION_RETURN,      /* userlock */
1178                         MOD_ACTION_RETURN,      /* notfound */
1179                         MOD_ACTION_RETURN,      /* noop     */
1180                         MOD_ACTION_RETURN       /* updated  */
1181                 },
1182                 /* append */
1183                 {
1184                         MOD_ACTION_RETURN,      /* reject   */
1185                         1,                      /* fail     */
1186                         MOD_ACTION_RETURN,      /* ok       */
1187                         MOD_ACTION_RETURN,      /* handled  */
1188                         MOD_ACTION_RETURN,      /* invalid  */
1189                         MOD_ACTION_RETURN,      /* userlock */
1190                         2,                      /* notfound */
1191                         MOD_ACTION_RETURN,      /* noop     */
1192                         MOD_ACTION_RETURN       /* updated  */
1193                 }
1194         },
1195         /* post-auth */
1196         {
1197                 /* group */
1198                 {
1199                         MOD_ACTION_RETURN,      /* reject   */
1200                         MOD_ACTION_RETURN,      /* fail     */
1201                         3,                      /* ok       */
1202                         MOD_ACTION_RETURN,      /* handled  */
1203                         MOD_ACTION_RETURN,      /* invalid  */
1204                         MOD_ACTION_RETURN,      /* userlock */
1205                         1,                      /* notfound */
1206                         2,                      /* noop     */
1207                         4                       /* updated  */
1208                 },
1209                 /* redundant */
1210                 {
1211                         MOD_ACTION_RETURN,      /* reject   */
1212                         1,                      /* fail     */
1213                         MOD_ACTION_RETURN,      /* ok       */
1214                         MOD_ACTION_RETURN,      /* handled  */
1215                         MOD_ACTION_RETURN,      /* invalid  */
1216                         MOD_ACTION_RETURN,      /* userlock */
1217                         MOD_ACTION_RETURN,      /* notfound */
1218                         MOD_ACTION_RETURN,      /* noop     */
1219                         MOD_ACTION_RETURN       /* updated  */
1220                 },
1221                 /* append */
1222                 {
1223                         MOD_ACTION_RETURN,      /* reject   */
1224                         1,                      /* fail     */
1225                         MOD_ACTION_RETURN,      /* ok       */
1226                         MOD_ACTION_RETURN,      /* handled  */
1227                         MOD_ACTION_RETURN,      /* invalid  */
1228                         MOD_ACTION_RETURN,      /* userlock */
1229                         2,                      /* notfound */
1230                         MOD_ACTION_RETURN,      /* noop     */
1231                         MOD_ACTION_RETURN       /* updated  */
1232                 }
1233         }
1234 #ifdef WITH_COA
1235         ,
1236         /* recv-coa */
1237         {
1238                 /* group */
1239                 {
1240                         MOD_ACTION_RETURN,      /* reject   */
1241                         MOD_ACTION_RETURN,      /* fail     */
1242                         3,                      /* ok       */
1243                         MOD_ACTION_RETURN,      /* handled  */
1244                         MOD_ACTION_RETURN,      /* invalid  */
1245                         MOD_ACTION_RETURN,      /* userlock */
1246                         1,                      /* notfound */
1247                         2,                      /* noop     */
1248                         4                       /* updated  */
1249                 },
1250                 /* redundant */
1251                 {
1252                         MOD_ACTION_RETURN,      /* reject   */
1253                         1,                      /* fail     */
1254                         MOD_ACTION_RETURN,      /* ok       */
1255                         MOD_ACTION_RETURN,      /* handled  */
1256                         MOD_ACTION_RETURN,      /* invalid  */
1257                         MOD_ACTION_RETURN,      /* userlock */
1258                         MOD_ACTION_RETURN,      /* notfound */
1259                         MOD_ACTION_RETURN,      /* noop     */
1260                         MOD_ACTION_RETURN       /* updated  */
1261                 },
1262                 /* append */
1263                 {
1264                         MOD_ACTION_RETURN,      /* reject   */
1265                         1,                      /* fail     */
1266                         MOD_ACTION_RETURN,      /* ok       */
1267                         MOD_ACTION_RETURN,      /* handled  */
1268                         MOD_ACTION_RETURN,      /* invalid  */
1269                         MOD_ACTION_RETURN,      /* userlock */
1270                         2,                      /* notfound */
1271                         MOD_ACTION_RETURN,      /* noop     */
1272                         MOD_ACTION_RETURN       /* updated  */
1273                 }
1274         },
1275         /* send-coa */
1276         {
1277                 /* group */
1278                 {
1279                         MOD_ACTION_RETURN,      /* reject   */
1280                         MOD_ACTION_RETURN,      /* fail     */
1281                         3,                      /* ok       */
1282                         MOD_ACTION_RETURN,      /* handled  */
1283                         MOD_ACTION_RETURN,      /* invalid  */
1284                         MOD_ACTION_RETURN,      /* userlock */
1285                         1,                      /* notfound */
1286                         2,                      /* noop     */
1287                         4                       /* updated  */
1288                 },
1289                 /* redundant */
1290                 {
1291                         MOD_ACTION_RETURN,      /* reject   */
1292                         1,                      /* fail     */
1293                         MOD_ACTION_RETURN,      /* ok       */
1294                         MOD_ACTION_RETURN,      /* handled  */
1295                         MOD_ACTION_RETURN,      /* invalid  */
1296                         MOD_ACTION_RETURN,      /* userlock */
1297                         MOD_ACTION_RETURN,      /* notfound */
1298                         MOD_ACTION_RETURN,      /* noop     */
1299                         MOD_ACTION_RETURN       /* updated  */
1300                 },
1301                 /* append */
1302                 {
1303                         MOD_ACTION_RETURN,      /* reject   */
1304                         1,                      /* fail     */
1305                         MOD_ACTION_RETURN,      /* ok       */
1306                         MOD_ACTION_RETURN,      /* handled  */
1307                         MOD_ACTION_RETURN,      /* invalid  */
1308                         MOD_ACTION_RETURN,      /* userlock */
1309                         2,                      /* notfound */
1310                         MOD_ACTION_RETURN,      /* noop     */
1311                         MOD_ACTION_RETURN       /* updated  */
1312                 }
1313         }
1314 #endif
1315 };
1316
1317
1318 #ifdef WITH_UNLANG
1319 static modcallable *do_compile_modupdate(modcallable *parent,
1320                                          int component, CONF_SECTION *cs,
1321                                          const char *name2)
1322 {
1323         int i, ok = FALSE;
1324         const char *vp_name;
1325         modgroup *g;
1326         modcallable *csingle;
1327         CONF_ITEM *ci;
1328         VALUE_PAIR *head, **tail;
1329
1330         static const char *attrlist_names[] = {
1331                 "request", "reply", "proxy-request", "proxy-reply",
1332                 "config", "control",
1333                 "coa", "coa-reply", "disconnect", "disconnect-reply",
1334                 NULL
1335         };
1336
1337         component = component;  /* -Wunused */
1338
1339         if (!cf_section_name2(cs)) {
1340                 cf_log_err(cf_sectiontoitem(cs),
1341                            "Require list name for 'update'.\n");
1342                 return NULL;
1343         }
1344
1345         vp_name = name2;
1346         if (strncmp(vp_name, "outer.", 6) == 0) {
1347                 vp_name += 6;
1348         } 
1349
1350         for (i = 0; attrlist_names[i] != NULL; i++) {
1351                 if (strcmp(vp_name, attrlist_names[i]) == 0) {
1352                         ok = TRUE;
1353                         break;
1354                 }
1355         }
1356
1357         if (!ok) {
1358                 cf_log_err(cf_sectiontoitem(cs),
1359                            "Unknown attribute list \"%s\"",
1360                            name2);
1361                 return NULL;
1362         }
1363
1364         head = NULL;
1365         tail = &head;
1366
1367         /*
1368          *      Walk through the children of the update section,
1369          *      ensuring that they're all known attributes.
1370          */
1371         for (ci=cf_item_find_next(cs, NULL);
1372              ci != NULL;
1373              ci=cf_item_find_next(cs, ci)) {
1374                 CONF_PAIR *cp;
1375                 VALUE_PAIR *vp;
1376
1377                 if (cf_item_is_section(ci)) {
1378                         cf_log_err(ci, "\"update\" sections cannot have subsections");
1379                         return NULL;
1380                 }
1381
1382                 if (!cf_item_is_pair(ci)) continue;
1383
1384                 cp = cf_itemtopair(ci); /* can't return NULL */
1385                 vp = cf_pairtovp(cp);
1386                 if (!vp) {
1387                         pairfree(&head);
1388                         cf_log_err(ci, "ERROR: %s", fr_strerror());
1389                         return NULL;
1390                 }
1391
1392                 if ((vp->operator != T_OP_EQ) &&
1393                     (vp->operator != T_OP_CMP_EQ) &&
1394                     (vp->operator != T_OP_ADD) &&
1395                     (vp->operator != T_OP_SUB) &&
1396                     (vp->operator != T_OP_LE) &&
1397                     (vp->operator != T_OP_GE) &&
1398                     (vp->operator != T_OP_CMP_FALSE) &&
1399                     (vp->operator != T_OP_SET)) {
1400                         pairfree(&head);
1401                         pairfree(&vp);
1402                         cf_log_err(ci, "Invalid operator for attribute");
1403                         return NULL;
1404                 }
1405
1406                 /*
1407                  *      A few more sanity checks.  The enforcement of
1408                  *      <= or >= can only happen for integer
1409                  *      attributes.
1410                  */
1411                 if ((vp->operator == T_OP_LE) ||
1412                     (vp->operator == T_OP_GE)) {
1413                         if ((vp->type != PW_TYPE_BYTE) &&
1414                             (vp->type != PW_TYPE_SHORT) &&
1415                             (vp->type != PW_TYPE_INTEGER)) {
1416                                 pairfree(&head);
1417                                 pairfree(&vp);
1418                                 cf_log_err(ci, "Enforcment of <= or >= is possible only for integer attributes");
1419                                 return NULL;
1420                         }
1421                 }
1422
1423                 *tail = vp;
1424                 tail = &(vp->next);
1425         }
1426
1427         if (!head) {
1428                 cf_log_err(cf_sectiontoitem(cs),
1429                            "ERROR: update %s section cannot be empty",
1430                            name2);
1431                 return NULL;
1432         }
1433
1434         g = rad_malloc(sizeof(*g)); /* never fails */
1435         memset(g, 0, sizeof(*g));
1436         csingle = mod_grouptocallable(g);
1437         
1438         csingle->parent = parent;
1439         csingle->next = NULL;
1440         csingle->name = name2;
1441         csingle->type = MOD_UPDATE;
1442         csingle->method = component;
1443         
1444         g->grouptype = GROUPTYPE_SIMPLE;
1445         g->children = NULL;
1446         g->cs = cs;
1447         g->vps = head;
1448
1449         return csingle;
1450 }
1451
1452
1453 static modcallable *do_compile_modswitch(modcallable *parent,
1454                                          int component, CONF_SECTION *cs)
1455 {
1456         modcallable *csingle;
1457         CONF_ITEM *ci;
1458         int had_seen_default = FALSE;
1459
1460         component = component;  /* -Wunused */
1461
1462         if (!cf_section_name2(cs)) {
1463                 cf_log_err(cf_sectiontoitem(cs),
1464                            "You must specify a variable to switch over for 'switch'.");
1465                 return NULL;
1466         }
1467
1468         if (!cf_item_find_next(cs, NULL)) {
1469                 cf_log_err(cf_sectiontoitem(cs), "'switch' statments cannot be empty.");
1470                 return NULL;
1471         }
1472
1473         /*
1474          *      Walk through the children of the switch section,
1475          *      ensuring that they're all 'case' statements
1476          */
1477         for (ci=cf_item_find_next(cs, NULL);
1478              ci != NULL;
1479              ci=cf_item_find_next(cs, ci)) {
1480                 CONF_SECTION *subcs;
1481                 const char *name1, *name2;
1482
1483                 if (!cf_item_is_section(ci)) {
1484                         if (!cf_item_is_pair(ci)) continue;
1485
1486                         cf_log_err(ci, "\"switch\" sections can only have \"case\" subsections");
1487                         return NULL;
1488                 }
1489
1490                 subcs = cf_itemtosection(ci);   /* can't return NULL */
1491                 name1 = cf_section_name1(subcs);
1492
1493                 if (strcmp(name1, "case") != 0) {
1494                         cf_log_err(ci, "\"switch\" sections can only have \"case\" subsections");
1495                         return NULL;
1496                 }
1497
1498                 name2 = cf_section_name2(subcs);
1499                 if (!name2 && !had_seen_default) {
1500                         had_seen_default = TRUE;
1501                         continue;
1502                 }
1503
1504                 if (!name2 || (name2[0] == '\0')) {
1505                         cf_log_err(ci, "\"case\" sections must have a name");
1506                         return NULL;
1507                 }
1508         }
1509
1510         csingle= do_compile_modgroup(parent, component, cs,
1511                                      GROUPTYPE_SIMPLE, GROUPTYPE_SIMPLE);
1512         if (!csingle) return NULL;
1513         csingle->type = MOD_SWITCH;
1514         return csingle;
1515 }
1516 #endif
1517
1518 static modcallable *do_compile_modserver(modcallable *parent,
1519                                          int component, CONF_ITEM *ci,
1520                                          const char *name,
1521                                          CONF_SECTION *cs,
1522                                          const char *server)
1523 {
1524         modcallable *csingle;
1525         CONF_SECTION *subcs;
1526         modref *mr;
1527
1528         subcs = cf_section_sub_find_name2(cs, comp2str[component], NULL);
1529         if (!subcs) {
1530                 cf_log_err(ci, "Server %s has no %s section",
1531                            server, comp2str[component]);
1532                 return NULL;
1533         }
1534
1535         mr = rad_malloc(sizeof(*mr));
1536         memset(mr, 0, sizeof(*mr));
1537
1538         csingle = mod_reftocallable(mr);
1539         csingle->parent = parent;
1540         csingle->next = NULL;
1541         csingle->name = name;
1542         csingle->type = MOD_REFERENCE;
1543         csingle->method = component;
1544
1545         memcpy(csingle->actions, defaultactions[component][GROUPTYPE_SIMPLE],
1546                sizeof(csingle->actions));
1547         
1548         mr->ref_name = strdup(server);
1549         mr->ref_cs = cs;
1550
1551         return csingle;
1552 }
1553
1554 static modcallable *do_compile_modxlat(modcallable *parent,
1555                                        int component, const char *fmt)
1556 {
1557         modcallable *csingle;
1558         modxlat *mx;
1559
1560         mx = rad_malloc(sizeof(*mx));
1561         memset(mx, 0, sizeof(*mx));
1562
1563         csingle = mod_xlattocallable(mx);
1564         csingle->parent = parent;
1565         csingle->next = NULL;
1566         csingle->name = "expand";
1567         csingle->type = MOD_XLAT;
1568         csingle->method = component;
1569
1570         memcpy(csingle->actions, defaultactions[component][GROUPTYPE_SIMPLE],
1571                sizeof(csingle->actions));
1572         
1573         mx->xlat_name = strdup(fmt);
1574         if (fmt[0] != '%') {
1575                 char *p;
1576                 mx->exec = TRUE;
1577
1578                 strcpy(mx->xlat_name, fmt + 1);
1579                 p = strrchr(mx->xlat_name, '`');
1580                 if (p) *p = '\0';
1581         }
1582
1583         return csingle;
1584 }
1585
1586 /*
1587  *      redundant, etc. can refer to modules or groups, but not much else.
1588  */
1589 static int all_children_are_modules(CONF_SECTION *cs, const char *name)
1590 {
1591         CONF_ITEM *ci;
1592
1593         for (ci=cf_item_find_next(cs, NULL);
1594              ci != NULL;
1595              ci=cf_item_find_next(cs, ci)) {
1596                 /*
1597                  *      If we're a redundant, etc. group, then the
1598                  *      intention is to call modules, rather than
1599                  *      processing logic.  These checks aren't
1600                  *      *strictly* necessary, but they keep the users
1601                  *      from doing crazy things.
1602                  */
1603                 if (cf_item_is_section(ci)) {
1604                         CONF_SECTION *subcs = cf_itemtosection(ci);
1605                         const char *name1 = cf_section_name1(subcs);
1606
1607                         if ((strcmp(name1, "if") == 0) ||
1608                             (strcmp(name1, "else") == 0) ||
1609                             (strcmp(name1, "elsif") == 0) ||
1610                             (strcmp(name1, "update") == 0) ||
1611                             (strcmp(name1, "switch") == 0) ||
1612                             (strcmp(name1, "case") == 0)) {
1613                                 cf_log_err(ci, "%s sections cannot contain a \"%s\" statement",
1614                                        name, name1);
1615                                 return 0;
1616                         }
1617                         continue;
1618                 }
1619
1620                 if (cf_item_is_pair(ci)) {
1621                         CONF_PAIR *cp = cf_itemtopair(ci);
1622                         if (cf_pair_value(cp) != NULL) {
1623                                 cf_log_err(ci,
1624                                            "Entry with no value is invalid");
1625                                 return 0;
1626                         }
1627                 }
1628         }
1629
1630         return 1;
1631 }
1632
1633
1634 /*
1635  *      Compile one entry of a module call.
1636  */
1637 static modcallable *do_compile_modsingle(modcallable *parent,
1638                                          int component, CONF_ITEM *ci,
1639                                          int grouptype,
1640                                          const char **modname)
1641 {
1642 #ifdef WITH_UNLANG
1643         int result;
1644 #endif
1645         const char *modrefname;
1646         modsingle *single;
1647         modcallable *csingle;
1648         module_instance_t *this;
1649         CONF_SECTION *cs, *subcs, *modules;
1650
1651         if (cf_item_is_section(ci)) {
1652                 const char *name2;
1653
1654                 cs = cf_itemtosection(ci);
1655                 modrefname = cf_section_name1(cs);
1656                 name2 = cf_section_name2(cs);
1657                 if (!name2) name2 = "_UnNamedGroup";
1658
1659                 /*
1660                  *      group{}, redundant{}, or append{} may appear
1661                  *      where a single module instance was expected.
1662                  *      In that case, we hand it off to
1663                  *      compile_modgroup
1664                  */
1665                 if (strcmp(modrefname, "group") == 0) {
1666                         *modname = name2;
1667                         return do_compile_modgroup(parent, component, cs,
1668                                                    GROUPTYPE_SIMPLE,
1669                                                    grouptype);
1670
1671                 } else if (strcmp(modrefname, "redundant") == 0) {
1672                         *modname = name2;
1673
1674                         if (!all_children_are_modules(cs, modrefname)) {
1675                                 return NULL;
1676                         }
1677
1678                         return do_compile_modgroup(parent, component, cs,
1679                                                    GROUPTYPE_REDUNDANT,
1680                                                    grouptype);
1681
1682                 } else if (strcmp(modrefname, "append") == 0) {
1683                         *modname = name2;
1684                         return do_compile_modgroup(parent, component, cs,
1685                                                    GROUPTYPE_APPEND,
1686                                                    grouptype);
1687
1688                 } else if (strcmp(modrefname, "load-balance") == 0) {
1689                         *modname = name2;
1690
1691                         if (!all_children_are_modules(cs, modrefname)) {
1692                                 return NULL;
1693                         }
1694
1695                         csingle= do_compile_modgroup(parent, component, cs,
1696                                                      GROUPTYPE_SIMPLE,
1697                                                      grouptype);
1698                         if (!csingle) return NULL;
1699                         csingle->type = MOD_LOAD_BALANCE;
1700                         return csingle;
1701
1702                 } else if (strcmp(modrefname, "redundant-load-balance") == 0) {
1703                         *modname = name2;
1704
1705                         if (!all_children_are_modules(cs, modrefname)) {
1706                                 return NULL;
1707                         }
1708
1709                         csingle= do_compile_modgroup(parent, component, cs,
1710                                                      GROUPTYPE_REDUNDANT,
1711                                                      grouptype);
1712                         if (!csingle) return NULL;
1713                         csingle->type = MOD_REDUNDANT_LOAD_BALANCE;
1714                         return csingle;
1715
1716 #ifdef WITH_UNLANG
1717                 } else  if (strcmp(modrefname, "if") == 0) {
1718                         if (!cf_section_name2(cs)) {
1719                                 cf_log_err(ci, "'if' without condition.");
1720                                 return NULL;
1721                         }
1722
1723                         *modname = name2;
1724                         csingle= do_compile_modgroup(parent, component, cs,
1725                                                      GROUPTYPE_SIMPLE,
1726                                                      grouptype);
1727                         if (!csingle) return NULL;
1728                         csingle->type = MOD_IF;
1729
1730                         if (!radius_evaluate_condition(NULL, 0, 0, modname,
1731                                                        FALSE, &result)) {
1732                                 modcallable_free(&csingle);
1733                                 return NULL;
1734                         }
1735                         *modname = name2;
1736
1737                         return csingle;
1738
1739                 } else  if (strcmp(modrefname, "elsif") == 0) {
1740                         if (parent &&
1741                             ((parent->type == MOD_LOAD_BALANCE) ||
1742                              (parent->type == MOD_REDUNDANT_LOAD_BALANCE))) {
1743                                 cf_log_err(ci, "'elsif' cannot be used in this section.");
1744                                 return NULL;
1745                         }
1746
1747                         if (!cf_section_name2(cs)) {
1748                                 cf_log_err(ci, "'elsif' without condition.");
1749                                 return NULL;
1750                         }
1751
1752                         *modname = name2;
1753                         csingle= do_compile_modgroup(parent, component, cs,
1754                                                      GROUPTYPE_SIMPLE,
1755                                                      grouptype);
1756                         if (!csingle) return NULL;
1757                         csingle->type = MOD_ELSIF;
1758
1759                         if (!radius_evaluate_condition(NULL, 0, 0, modname,
1760                                                        FALSE, &result)) {
1761                                 modcallable_free(&csingle);
1762                                 return NULL;
1763                         }
1764                         *modname = name2;
1765
1766                         return csingle;
1767
1768                 } else  if (strcmp(modrefname, "else") == 0) {
1769                         if (parent &&
1770                             ((parent->type == MOD_LOAD_BALANCE) ||
1771                              (parent->type == MOD_REDUNDANT_LOAD_BALANCE))) {
1772                                 cf_log_err(ci, "'else' cannot be used in this section section.");
1773                                 return NULL;
1774                         }
1775
1776                         if (cf_section_name2(cs)) {
1777                                 cf_log_err(ci, "Cannot have conditions on 'else'.");
1778                                 return NULL;
1779                         }
1780
1781                         *modname = name2;
1782                         csingle= do_compile_modgroup(parent, component, cs,
1783                                                      GROUPTYPE_SIMPLE,
1784                                                      grouptype);
1785                         if (!csingle) return NULL;
1786                         csingle->type = MOD_ELSE;
1787                         return csingle;
1788
1789                 } else  if (strcmp(modrefname, "update") == 0) {
1790                         *modname = name2;
1791
1792                         csingle = do_compile_modupdate(parent, component, cs,
1793                                                        name2);
1794                         if (!csingle) return NULL;
1795
1796                         return csingle;
1797
1798                 } else  if (strcmp(modrefname, "switch") == 0) {
1799                         *modname = name2;
1800
1801                         csingle = do_compile_modswitch(parent, component, cs);
1802                         if (!csingle) return NULL;
1803
1804                         return csingle;
1805
1806                 } else  if (strcmp(modrefname, "case") == 0) {
1807                         int i;
1808
1809                         *modname = name2;
1810
1811                         /*
1812                          *      FIXME: How to tell that the parent can only
1813                          *      be a "switch" statement?
1814                          */
1815                         if (!parent) {
1816                                 cf_log_err(ci, "\"case\" statements may only appear within a \"switch\" section");
1817                                 return NULL;
1818                         }
1819
1820                         csingle= do_compile_modgroup(parent, component, cs,
1821                                                      GROUPTYPE_SIMPLE,
1822                                                      grouptype);
1823                         if (!csingle) return NULL;
1824                         csingle->type = MOD_CASE;
1825                         csingle->name = cf_section_name2(cs); /* may be NULL */
1826
1827                         /*
1828                          *      Set all of it's codes to return, so that
1829                          *      when we pick a 'case' statement, we don't
1830                          *      fall through to processing the next one.
1831                          */
1832                         for (i = 0; i < RLM_MODULE_NUMCODES; i++) {
1833                                 csingle->actions[i] = MOD_ACTION_RETURN;
1834                         }
1835
1836                         return csingle;
1837 #endif
1838                 } /* else it's something like sql { fail = 1 ...} */
1839
1840         } else if (!cf_item_is_pair(ci)) { /* CONF_DATA or some such */
1841                 return NULL;
1842
1843                 /*
1844                  *      Else it's a module reference, with updated return
1845                  *      codes.
1846                  */
1847         } else {
1848                 CONF_SECTION *loop;
1849                 CONF_PAIR *cp = cf_itemtopair(ci);
1850                 modrefname = cf_pair_attr(cp);
1851
1852                 /*
1853                  *      Actions (ok = 1), etc. are orthoganal to just
1854                  *      about everything else.
1855                  */
1856                 if (cf_pair_value(cp) != NULL) {
1857                         cf_log_err(ci, "Entry is not a reference to a module");
1858                         return NULL;
1859                 }
1860
1861                 if (((modrefname[0] == '%') && (modrefname[1] == '{')) ||
1862                     (modrefname[0] == '`')) {
1863                         return do_compile_modxlat(parent, component,
1864                                                   modrefname);
1865                 }
1866
1867                 /*
1868                  *      See if the module is a virtual one.  If so,
1869                  *      return that, rather than doing anything here.
1870                  */
1871                 subcs = NULL;
1872                 cs = cf_section_find("instantiate");
1873                 if (cs) subcs = cf_section_sub_find_name2(cs, NULL,
1874                                                           modrefname);
1875                 if (!subcs &&
1876                     (cs = cf_section_find("policy")) != NULL) {
1877                         char buffer[256];
1878                         
1879                         snprintf(buffer, sizeof(buffer), "%s.%s",
1880                                  modrefname, comp2str[component]);
1881
1882                         /*
1883                          *      Prefer name.section, then name.
1884                          */
1885                         subcs = cf_section_sub_find_name2(cs, NULL,
1886                                                           buffer);
1887                         if (!subcs) {
1888                                 subcs = cf_section_sub_find_name2(cs, NULL,
1889                                                                   modrefname);
1890                         }
1891                 }
1892
1893                 /*
1894                  *      Allow policies to over-ride module names.
1895                  *      i.e. the "sql" policy can do some extra things,
1896                  *      and then call the "sql" module.
1897                  */
1898                 for (loop = cf_item_parent(ci);
1899                      loop && subcs;
1900                      loop = cf_item_parent(cf_sectiontoitem(loop))) {
1901                         if (loop == subcs) {
1902                                 subcs = NULL;
1903                         }
1904                 }
1905
1906                 if (subcs) {
1907                         DEBUG2(" Module: Loading virtual module %s",
1908                                modrefname);
1909
1910                         /*
1911                          *      redundant foo {} is a single.
1912                          */
1913                         if (cf_section_name2(subcs)) {
1914                                 return do_compile_modsingle(parent,
1915                                                             component,
1916                                                             cf_sectiontoitem(subcs),
1917                                                             grouptype,
1918                                                             modname);
1919                         } else {
1920                                 /*
1921                                  *      foo {} is a group.
1922                                  */
1923                                 return do_compile_modgroup(parent,
1924                                                            component,
1925                                                            subcs,
1926                                                            GROUPTYPE_SIMPLE,
1927                                                            grouptype);
1928                         }
1929                 }
1930         }
1931
1932         /*
1933          *      Not a virtual module.  It must be a real module.
1934          */
1935         modules = cf_section_find("modules");
1936         this = NULL;
1937
1938         if (modules && cf_section_sub_find_name2(modules, NULL, modrefname)) {
1939                 this = find_module_instance(modules, modrefname, 1);
1940         }
1941
1942         if (!this) do {
1943                 int i;
1944                 char *p;
1945           
1946                 /*
1947                  *      Maybe it's module.method
1948                  */
1949                 p = strrchr(modrefname, '.');
1950                 if (p) for (i = RLM_COMPONENT_AUTH;
1951                             i < RLM_COMPONENT_COUNT;
1952                             i++) {
1953                         if (strcmp(p + 1, comp2str[i]) == 0) {
1954                                 char buffer[256];
1955
1956                                 strlcpy(buffer, modrefname, sizeof(buffer));
1957                                 buffer[p - modrefname] = '\0';
1958                                 component = i;
1959                                 
1960                                 this = find_module_instance(cf_section_find("modules"), buffer, 1);
1961                                 if (this &&
1962                                     !this->entry->module->methods[i]) {
1963                                         *modname = NULL;
1964                                         cf_log_err(ci, "Module %s has no such method %s", buffer, comp2str[i]);
1965                                         return NULL;
1966                                 }
1967                                 break;
1968                         }
1969                 }
1970                 if (this) break;
1971
1972                 if (strncmp(modrefname, "server[", 7) == 0) {
1973                         char buffer[256];
1974
1975                         strlcpy(buffer, modrefname + 7, sizeof(buffer));
1976                         p = strrchr(buffer, ']');
1977                         if (!p || p[1] != '\0' || (p == buffer)) {
1978                                 cf_log_err(ci, "Invalid server reference in \"%s\".", modrefname);
1979                                 return NULL;
1980                         }
1981                         *p = '\0';
1982
1983                         cs = cf_section_sub_find_name2(NULL, "server", buffer);
1984                         if (!cs) {
1985                                 cf_log_err(ci, "No such server \"%s\".", buffer);
1986                                 return NULL;
1987                         }
1988                         
1989                         return do_compile_modserver(parent, component, ci,
1990                                                     modrefname, cs, buffer);
1991                 }
1992                 
1993                 *modname = NULL;
1994                 cf_log_err(ci, "Failed to find \"%s\" in the \"modules\" section.", modrefname);
1995                 return NULL;
1996         } while (0);
1997
1998         /*
1999          *      We know it's all OK, allocate the structures, and fill
2000          *      them in.
2001          */
2002         single = rad_malloc(sizeof(*single));
2003         memset(single, 0, sizeof(*single));
2004         csingle = mod_singletocallable(single);
2005         csingle->parent = parent;
2006         csingle->next = NULL;
2007         if (!parent || (component != RLM_COMPONENT_AUTH)) {
2008                 memcpy(csingle->actions, defaultactions[component][grouptype],
2009                        sizeof csingle->actions);
2010         } else { /* inside Auth-Type has different rules */
2011                 memcpy(csingle->actions, defaultactions[RLM_COMPONENT_AUTZ][grouptype],
2012                        sizeof csingle->actions);
2013         }
2014         rad_assert(modrefname != NULL);
2015         csingle->name = modrefname;
2016         csingle->type = MOD_SINGLE;
2017         csingle->method = component;
2018
2019         /*
2020          *      Singles can override the actions, virtual modules cannot.
2021          *
2022          *      FIXME: We may want to re-visit how to do this...
2023          *      maybe a csingle as a ref?
2024          */
2025         if (cf_item_is_section(ci)) {
2026                 CONF_ITEM *csi;
2027                 
2028                 cs = cf_itemtosection(ci);
2029                 for (csi=cf_item_find_next(cs, NULL);
2030                      csi != NULL;
2031                      csi=cf_item_find_next(cs, csi)) {
2032
2033                         if (cf_item_is_section(csi)) {
2034                                 cf_log_err(csi, "Subsection of module instance call not allowed");
2035                                 modcallable_free(&csingle);
2036                                 return NULL;
2037                         }
2038
2039                         if (!cf_item_is_pair(csi)) continue;
2040
2041                         if (!compile_action(csingle, cf_itemtopair(csi))) {
2042                                 modcallable_free(&csingle);
2043                                 return NULL;
2044                         }
2045                 }
2046         }
2047
2048         /*
2049          *      Bail out if the module in question does not supply the
2050          *      wanted component
2051          */
2052         if (!this->entry->module->methods[component]) {
2053                 cf_log_err(ci, "\"%s\" modules aren't allowed in '%s' sections -- they have no such method.", this->entry->module->name,
2054                        comp2str[component]);
2055                 modcallable_free(&csingle);
2056                 return NULL;
2057         }
2058
2059         single->modinst = this;
2060         *modname = this->entry->module->name;
2061         return csingle;
2062 }
2063
2064 modcallable *compile_modsingle(modcallable *parent,
2065                                int component, CONF_ITEM *ci,
2066                                const char **modname)
2067 {
2068         modcallable *ret = do_compile_modsingle(parent, component, ci,
2069                                                 GROUPTYPE_SIMPLE,
2070                                                 modname);
2071         dump_tree(component, ret);
2072         return ret;
2073 }
2074
2075
2076 /*
2077  *      Internal compile group code.
2078  */
2079 static modcallable *do_compile_modgroup(modcallable *parent,
2080                                         int component, CONF_SECTION *cs,
2081                                         int grouptype, int parentgrouptype)
2082 {
2083         int i;
2084         modgroup *g;
2085         modcallable *c;
2086         CONF_ITEM *ci;
2087
2088         g = rad_malloc(sizeof(*g));
2089         memset(g, 0, sizeof(*g));
2090         g->grouptype = grouptype;
2091
2092         c = mod_grouptocallable(g);
2093         c->parent = parent;
2094         c->type = MOD_GROUP;
2095         c->next = NULL;
2096         memset(c->actions, 0, sizeof(c->actions));
2097
2098         /*
2099          *      Remember the name for printing, etc.
2100          *
2101          *      FIXME: We may also want to put the names into a
2102          *      rbtree, so that groups can reference each other...
2103          */
2104         c->name = cf_section_name2(cs);
2105         if (!c->name) {
2106                 c->name = cf_section_name1(cs);
2107                 if (strcmp(c->name, "group") == 0) {
2108                         c->name = "";
2109                 } else {
2110                         c->type = MOD_POLICY;
2111                 }
2112         }
2113         g->children = NULL;
2114
2115         /*
2116          *      Loop over the children of this group.
2117          */
2118         for (ci=cf_item_find_next(cs, NULL);
2119              ci != NULL;
2120              ci=cf_item_find_next(cs, ci)) {
2121
2122                 /*
2123                  *      Sections are references to other groups, or
2124                  *      to modules with updated return codes.
2125                  */
2126                 if (cf_item_is_section(ci)) {
2127                         const char *junk = NULL;
2128                         modcallable *single;
2129                         CONF_SECTION *subcs = cf_itemtosection(ci);
2130
2131                         single = do_compile_modsingle(c, component, ci,
2132                                                       grouptype, &junk);
2133                         if (!single) {
2134                                 cf_log_err(ci, "Failed to parse \"%s\" subsection.",
2135                                        cf_section_name1(subcs));
2136                                 modcallable_free(&c);
2137                                 return NULL;
2138                         }
2139                         add_child(g, single);
2140
2141                 } else if (!cf_item_is_pair(ci)) { /* CONF_DATA */
2142                         continue;
2143
2144                 } else {
2145                         const char *attr, *value;
2146                         CONF_PAIR *cp = cf_itemtopair(ci);
2147
2148                         attr = cf_pair_attr(cp);
2149                         value = cf_pair_value(cp);
2150
2151                         /*
2152                          *      A CONF_PAIR is either a module
2153                          *      instance with no actions
2154                          *      specified ...
2155                          */
2156                         if (!value) {
2157                                 modcallable *single;
2158                                 const char *junk = NULL;
2159
2160                                 single = do_compile_modsingle(c,
2161                                                               component,
2162                                                               ci,
2163                                                               grouptype,
2164                                                               &junk);
2165                                 if (!single) {
2166                                         cf_log_err(ci,
2167                                                    "Failed to parse \"%s\" entry.",
2168                                                    attr);
2169                                         modcallable_free(&c);
2170                                         return NULL;
2171                                 }
2172                                 add_child(g, single);
2173
2174                                 /*
2175                                  *      Or a module instance with action.
2176                                  */
2177                         } else if (!compile_action(c, cp)) {
2178                                 modcallable_free(&c);
2179                                 return NULL;
2180                         } /* else it worked */
2181                 }
2182         }
2183
2184         /*
2185          *      Set the default actions, if they haven't already been
2186          *      set.
2187          */
2188         for (i = 0; i < RLM_MODULE_NUMCODES; i++) {
2189                 if (!c->actions[i]) {
2190                         if (!parent || (component != RLM_COMPONENT_AUTH)) {
2191                                 c->actions[i] = defaultactions[component][parentgrouptype][i];
2192                         } else { /* inside Auth-Type has different rules */
2193                                 c->actions[i] = defaultactions[RLM_COMPONENT_AUTZ][parentgrouptype][i];
2194                         }
2195                 }
2196         }
2197
2198         /*
2199          *      FIXME: If there are no children, return NULL?
2200          */
2201         return mod_grouptocallable(g);
2202 }
2203
2204 modcallable *compile_modgroup(modcallable *parent,
2205                               int component, CONF_SECTION *cs)
2206 {
2207         modcallable *ret = do_compile_modgroup(parent, component, cs,
2208                                                GROUPTYPE_SIMPLE,
2209                                                GROUPTYPE_SIMPLE);
2210         dump_tree(component, ret);
2211         return ret;
2212 }
2213
2214 void add_to_modcallable(modcallable **parent, modcallable *this,
2215                         int component, const char *name)
2216 {
2217         modgroup *g;
2218
2219         rad_assert(this != NULL);
2220
2221         if (*parent == NULL) {
2222                 modcallable *c;
2223
2224                 g = rad_malloc(sizeof *g);
2225                 memset(g, 0, sizeof(*g));
2226                 g->grouptype = GROUPTYPE_SIMPLE;
2227                 c = mod_grouptocallable(g);
2228                 c->next = NULL;
2229                 memcpy(c->actions,
2230                        defaultactions[component][GROUPTYPE_SIMPLE],
2231                        sizeof(c->actions));
2232                 rad_assert(name != NULL);
2233                 c->name = name;
2234                 c->type = MOD_GROUP;
2235                 c->method = component;
2236                 g->children = NULL;
2237
2238                 *parent = mod_grouptocallable(g);
2239         } else {
2240                 g = mod_callabletogroup(*parent);
2241         }
2242
2243         add_child(g, this);
2244 }
2245
2246 void modcallable_free(modcallable **pc)
2247 {
2248         modcallable *c, *loop, *next;
2249
2250         if (!pc || !*pc) return;
2251
2252         c = *pc;
2253
2254         if ((c->type > MOD_SINGLE) && (c->type <= MOD_POLICY)) {
2255                 modgroup *g = mod_callabletogroup(c);
2256
2257                 if (g->children) for (loop = g->children;
2258                     loop ;
2259                     loop = next) {
2260                         next = loop->next;
2261                         modcallable_free(&loop);
2262                 }
2263                 pairfree(&g->vps);
2264         }
2265         free(c);
2266         *pc = NULL;
2267 }