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