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