perl -i -npe "s/[ \t]+$//g" `find src -name "*.[ch]" -print`
[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 #define MOD_ACTION_RETURN (-1)
41
42 /* Here are our basic types: modcallable, modgroup, and modsingle. For an
43  * explanation of what they are all about, see ../../doc/README.failover */
44 struct modcallable {
45         struct modcallable *next;
46         int actions[RLM_MODULE_NUMCODES];
47         const char *name;
48         enum { MOD_SINGLE, MOD_GROUP } type;
49 };
50
51 typedef struct {
52         modcallable mc;
53         modcallable *children;
54 } modgroup;
55
56 typedef struct {
57         modcallable mc;
58         module_instance_t *modinst;
59 } modsingle;
60
61 /* Simple conversions: modsingle and modgroup are subclasses of modcallable,
62  * so we often want to go back and forth between them. */
63 static modsingle *mod_callabletosingle(modcallable *p)
64 {
65         rad_assert(p->type==MOD_SINGLE);
66         return (modsingle *)p;
67 }
68 static modgroup *mod_callabletogroup(modcallable *p)
69 {
70         rad_assert(p->type==MOD_GROUP);
71         return (modgroup *)p;
72 }
73 static modcallable *mod_singletocallable(modsingle *p)
74 {
75         return (modcallable *)p;
76 }
77 static modcallable *mod_grouptocallable(modgroup *p)
78 {
79         return (modcallable *)p;
80 }
81
82 /* modgroups are grown by adding a modcallable to the end */
83 static void add_child(modgroup *g, modcallable *c)
84 {
85         modcallable **head = &g->children;
86         modcallable *node = *head;
87         modcallable **last = head;
88
89         while (node) {
90                 last = &node->next;
91                 node = node->next;
92         }
93
94         rad_assert(c->next == NULL);
95         *last = c;
96 }
97
98 /* Here's where we recognize all of our keywords: first the rcodes, then the
99  * actions */
100 static LRAD_NAME_NUMBER rcode_table[] = {
101         { "reject",     RLM_MODULE_REJECT       },
102         { "fail",       RLM_MODULE_FAIL         },
103         { "ok",         RLM_MODULE_OK           },
104         { "handled",    RLM_MODULE_HANDLED      },
105         { "invalid",    RLM_MODULE_INVALID      },
106         { "userlock",   RLM_MODULE_USERLOCK     },
107         { "notfound",   RLM_MODULE_NOTFOUND     },
108         { "noop",       RLM_MODULE_NOOP         },
109         { "updated",    RLM_MODULE_UPDATED      },
110         { NULL, 0 }
111 };
112
113 static int str2rcode(const char *s, const char *filename, int lineno)
114 {
115         int rcode;
116
117         rcode = lrad_str2int(rcode_table, s, -1);
118         if (rcode < 0) {
119                 radlog(L_ERR|L_CONS,
120                        "%s[%d] Unknown module rcode '%s'.\n",
121                        filename, lineno, s);
122                 exit(1);
123         }
124
125         return rcode;
126 }
127
128 static int str2action(const char *s, const char *filename, int lineno)
129 {
130         if(!strcasecmp(s, "return"))
131                 return MOD_ACTION_RETURN;
132         else if (strspn(s, "0123456789")==strlen(s)) {
133                 int rcode = atoi(s);
134
135                 /*
136                  *      Don't allow priority zero, for future use.
137                  */
138                 if (rcode == 0) {
139                         radlog(L_ERR|L_CONS,
140                                "%s[%d] Invalid action '%s'.\n",
141                                filename, lineno, s);
142                         exit(1);
143                 }
144                 return rcode;
145         } else {
146                 radlog(L_ERR|L_CONS,
147                                 "%s[%d] Unknown action '%s'.\n",
148                                 filename, lineno, s);
149                 exit(1);
150         }
151         return -1;
152 }
153
154 #if 0
155 static const char *action2str(int action)
156 {
157         static char buf[32];
158         if(action==MOD_ACTION_RETURN)
159                 return "return";
160         snprintf(buf, sizeof buf, "%d", action);
161         return buf;
162 }
163 #endif
164
165 /* Some short names for debugging output */
166 static const char *comp2str[] = {
167         "authenticate",
168         "authorize",
169         "preacct",
170         "accounting",
171         "session",
172         "pre-proxy",
173         "post-proxy",
174         "post-auth"
175 };
176
177 #ifdef HAVE_PTHREAD_H
178 /*
179  *      Lock the mutex for the module
180  */
181 static void safe_lock(module_instance_t *instance)
182 {
183         if (instance->mutex)
184                 pthread_mutex_lock(instance->mutex);
185 }
186
187 /*
188  *      Unlock the mutex for the module
189  */
190 static void safe_unlock(module_instance_t *instance)
191 {
192         if (instance->mutex)
193                 pthread_mutex_unlock(instance->mutex);
194 }
195 #else
196 /*
197  *      No threads: these functions become NULL's.
198  */
199 #define safe_lock(foo)
200 #define safe_unlock(foo)
201 #endif
202
203 static int call_modsingle(int component, modsingle *sp, REQUEST *request,
204                           int default_result)
205 {
206         int myresult = default_result;
207
208         DEBUG3("  modsingle[%s]: calling %s (%s) for request %d",
209                comp2str[component], sp->modinst->name,
210                sp->modinst->entry->name, request->number);
211         safe_lock(sp->modinst);
212         myresult = sp->modinst->entry->module->methods[component](
213                         sp->modinst->insthandle, request);
214         safe_unlock(sp->modinst);
215         DEBUG3("  modsingle[%s]: returned from %s (%s) for request %d",
216                comp2str[component], sp->modinst->name,
217                sp->modinst->entry->name, request->number);
218
219         return myresult;
220 }
221
222 static int call_modgroup(int component, modgroup *g, REQUEST *request,
223                 int default_result)
224 {
225         int myresult = default_result;
226         int myresultpref;
227         modcallable *p;
228
229         /* Assign the lowest possible preference to the default return code */
230         myresultpref = 0;
231
232         /* Loop over the children */
233         for(p = g->children; p; p = p->next) {
234                 int r = RLM_MODULE_FAIL;
235
236                 /* Call this child by recursing into modcall */
237                 r = modcall(component, p, request);
238
239 #if 0
240                 DEBUG2("%s: action for %s is %s",
241                         comp2str[component], lrad_int2str(rcode_table, r, "??"),
242                         action2str(p->actions[r]));
243 #endif
244
245                 /* Find an action to go with the child's result. If "return",
246                  * break out of the loop so the rest of the children in the
247                  * list will be skipped. */
248                 if(p->actions[r] == MOD_ACTION_RETURN) {
249                         myresult = r;
250                         break;
251                 }
252
253                 /* Otherwise, the action is a number, the preference level of
254                  * this return code. If no higher preference has been seen
255                  * yet, remember this one. */
256                 if(p->actions[r] >= myresultpref) {
257                         myresult = r;
258                         myresultpref = p->actions[r];
259                 }
260         }
261
262         return myresult;
263 }
264
265 int modcall(int component, modcallable *c, REQUEST *request)
266 {
267         int myresult;
268
269         /* Choose a default return value appropriate for the component */
270         switch(component) {
271         case RLM_COMPONENT_AUTZ:
272                 myresult = RLM_MODULE_NOTFOUND;
273                 break;
274         case RLM_COMPONENT_AUTH:
275                 myresult = RLM_MODULE_REJECT;
276                 break;
277         case RLM_COMPONENT_PREACCT:
278                 myresult = RLM_MODULE_NOOP;
279                 break;
280         case RLM_COMPONENT_ACCT:
281                 myresult = RLM_MODULE_NOOP;
282                 break;
283         case RLM_COMPONENT_SESS:
284                 myresult = RLM_MODULE_FAIL;
285                 break;
286         case RLM_COMPONENT_PRE_PROXY:
287                 myresult = RLM_MODULE_NOOP;
288                 break;
289         case RLM_COMPONENT_POST_PROXY:
290                 myresult = RLM_MODULE_NOOP;
291                 break;
292         case RLM_COMPONENT_POST_AUTH:
293                 myresult = RLM_MODULE_NOOP;
294                 break;
295         default:
296                 myresult = RLM_MODULE_FAIL;
297                 break;
298         }
299
300         if(c == NULL) {
301                 DEBUG2("modcall[%s]: NULL object returns %s for request %d",
302                        comp2str[component],
303                        lrad_int2str(rcode_table, myresult, "??"),
304                        request->number);
305                 return myresult;
306         }
307
308         if(c->type==MOD_GROUP) {
309                 modgroup *g = mod_callabletogroup(c);
310
311                 DEBUG2("modcall: entering group %s for request %d",
312                        c->name, request->number);
313
314                 myresult = call_modgroup(component, g, request, myresult);
315
316                 DEBUG2("modcall: group %s returns %s for request %d",
317                         c->name,
318                        lrad_int2str(rcode_table, myresult, "??"),
319                        request->number);
320         } else {
321                 modsingle *sp = mod_callabletosingle(c);
322
323                 myresult = call_modsingle(component, sp, request, myresult);
324
325                 DEBUG2("  modcall[%s]: module \"%s\" returns %s for request %d",
326                        comp2str[component], c->name,
327                        lrad_int2str(rcode_table, myresult, "??"),
328                        request->number);
329         }
330
331         return myresult;
332 }
333
334 #if 0
335 /* If you suspect a bug in the parser, you'll want to use these dump
336  * functions. dump_tree should reproduce a whole tree exactly as it was found
337  * in radiusd.conf, but in long form (all actions explicitly defined) */
338 static void dump_mc(modcallable *c, int indent)
339 {
340         int i;
341
342         if(c->type==MOD_SINGLE) {
343                 modsingle *single = mod_callabletosingle(c);
344                 DEBUG("%.*s%s {", indent, "\t\t\t\t\t\t\t\t\t\t\t",
345                         single->modinst->name);
346         } else {
347                 modgroup *g = mod_callabletogroup(c);
348                 modcallable *p;
349                 DEBUG("%.*sgroup {", indent, "\t\t\t\t\t\t\t\t\t\t\t");
350                 for(p = g->children;p;p = p->next)
351                         dump_mc(p, indent+1);
352         }
353
354         for(i = 0; i<RLM_MODULE_NUMCODES; ++i) {
355                 DEBUG("%.*s%s = %s", indent+1, "\t\t\t\t\t\t\t\t\t\t\t",
356                       lrad_int2str(rcode_table, i, "??"),
357                       action2str(c->actions[i]));
358         }
359
360         DEBUG("%.*s}", indent, "\t\t\t\t\t\t\t\t\t\t\t");
361 }
362
363 static void dump_tree(int comp, modcallable *c)
364 {
365         DEBUG("[%s]", comp2str[comp]);
366         dump_mc(c, 0);
367 }
368 #else
369 static void dump_tree(int comp UNUSED, modcallable *c UNUSED)
370 {
371         return;
372 }
373 #endif
374
375 #define GROUPTYPE_SIMPLEGROUP 0
376 #define GROUPTYPE_REDUNDANT 1
377 #define GROUPTYPE_APPEND 2
378 #define GROUPTYPE_COUNT 3
379
380 /* These are the default actions. For each component, the group{} block
381  * behaves like the code from the old module_*() function. redundant{} and
382  * append{} are based on my guesses of what they will be used for. --Pac. */
383 static int
384 defaultactions[RLM_COMPONENT_COUNT][GROUPTYPE_COUNT][RLM_MODULE_NUMCODES] =
385 {
386         /* authenticate */
387         {
388                 /* group */
389                 {
390                         MOD_ACTION_RETURN,      /* reject   */
391                         1,                      /* fail     */
392                         MOD_ACTION_RETURN,      /* ok       */
393                         MOD_ACTION_RETURN,      /* handled  */
394                         1,                      /* invalid  */
395                         MOD_ACTION_RETURN,      /* userlock */
396                         MOD_ACTION_RETURN,      /* notfound */
397                         1,                      /* noop     */
398                         1                       /* updated  */
399                 },
400                 /* redundant */
401                 {
402                         MOD_ACTION_RETURN,      /* reject   */
403                         1,                      /* fail     */
404                         MOD_ACTION_RETURN,      /* ok       */
405                         MOD_ACTION_RETURN,      /* handled  */
406                         MOD_ACTION_RETURN,      /* invalid  */
407                         MOD_ACTION_RETURN,      /* userlock */
408                         MOD_ACTION_RETURN,      /* notfound */
409                         MOD_ACTION_RETURN,      /* noop     */
410                         MOD_ACTION_RETURN       /* updated  */
411                 },
412                 /* append */
413                 {
414                         MOD_ACTION_RETURN,      /* reject   */
415                         1,                      /* fail     */
416                         MOD_ACTION_RETURN,      /* ok       */
417                         MOD_ACTION_RETURN,      /* handled  */
418                         MOD_ACTION_RETURN,      /* invalid  */
419                         MOD_ACTION_RETURN,      /* userlock */
420                         2,                      /* notfound */
421                         MOD_ACTION_RETURN,      /* noop     */
422                         MOD_ACTION_RETURN       /* updated  */
423                 }
424         },
425         /* authorize */
426         {
427                 /* group */
428                 {
429                         MOD_ACTION_RETURN,      /* reject   */
430                         MOD_ACTION_RETURN,      /* fail     */
431                         3,                      /* ok       */
432                         MOD_ACTION_RETURN,      /* handled  */
433                         MOD_ACTION_RETURN,      /* invalid  */
434                         MOD_ACTION_RETURN,      /* userlock */
435                         1,                      /* notfound */
436                         2,                      /* noop     */
437                         4                       /* updated  */
438                 },
439                 /* redundant */
440                 {
441                         MOD_ACTION_RETURN,      /* reject   */
442                         1,                      /* fail     */
443                         MOD_ACTION_RETURN,      /* ok       */
444                         MOD_ACTION_RETURN,      /* handled  */
445                         MOD_ACTION_RETURN,      /* invalid  */
446                         MOD_ACTION_RETURN,      /* userlock */
447                         MOD_ACTION_RETURN,      /* notfound */
448                         MOD_ACTION_RETURN,      /* noop     */
449                         MOD_ACTION_RETURN       /* updated  */
450                 },
451                 /* append */
452                 {
453                         MOD_ACTION_RETURN,      /* reject   */
454                         1,                      /* fail     */
455                         MOD_ACTION_RETURN,      /* ok       */
456                         MOD_ACTION_RETURN,      /* handled  */
457                         MOD_ACTION_RETURN,      /* invalid  */
458                         MOD_ACTION_RETURN,      /* userlock */
459                         2,                      /* notfound */
460                         MOD_ACTION_RETURN,      /* noop     */
461                         MOD_ACTION_RETURN       /* updated  */
462                 }
463         },
464         /* preacct */
465         {
466                 /* group */
467                 {
468                         MOD_ACTION_RETURN,      /* reject   */
469                         MOD_ACTION_RETURN,      /* fail     */
470                         2,                      /* ok       */
471                         MOD_ACTION_RETURN,      /* handled  */
472                         MOD_ACTION_RETURN,      /* invalid  */
473                         MOD_ACTION_RETURN,      /* userlock */
474                         MOD_ACTION_RETURN,      /* notfound */
475                         1,                      /* noop     */
476                         3                       /* updated  */
477                 },
478                 /* redundant */
479                 {
480                         MOD_ACTION_RETURN,      /* reject   */
481                         1,                      /* fail     */
482                         MOD_ACTION_RETURN,      /* ok       */
483                         MOD_ACTION_RETURN,      /* handled  */
484                         MOD_ACTION_RETURN,      /* invalid  */
485                         MOD_ACTION_RETURN,      /* userlock */
486                         MOD_ACTION_RETURN,      /* notfound */
487                         MOD_ACTION_RETURN,      /* noop     */
488                         MOD_ACTION_RETURN       /* updated  */
489                 },
490                 /* append */
491                 {
492                         MOD_ACTION_RETURN,      /* reject   */
493                         1,                      /* fail     */
494                         MOD_ACTION_RETURN,      /* ok       */
495                         MOD_ACTION_RETURN,      /* handled  */
496                         MOD_ACTION_RETURN,      /* invalid  */
497                         MOD_ACTION_RETURN,      /* userlock */
498                         2,                      /* notfound */
499                         MOD_ACTION_RETURN,      /* noop     */
500                         MOD_ACTION_RETURN       /* updated  */
501                 }
502         },
503         /* accounting */
504         {
505                 /* group */
506                 {
507                         MOD_ACTION_RETURN,      /* reject   */
508                         MOD_ACTION_RETURN,      /* fail     */
509                         2,                      /* ok       */
510                         MOD_ACTION_RETURN,      /* handled  */
511                         MOD_ACTION_RETURN,      /* invalid  */
512                         MOD_ACTION_RETURN,      /* userlock */
513                         MOD_ACTION_RETURN,      /* notfound */
514                         1,                      /* noop     */
515                         3                       /* updated  */
516                 },
517                 /* redundant */
518                 {
519                         1,                      /* reject   */
520                         1,                      /* fail     */
521                         3,                      /* ok       */
522                         MOD_ACTION_RETURN,      /* handled  */
523                         1,                      /* invalid  */
524                         1,                      /* userlock */
525                         1,                      /* notfound */
526                         2,                      /* noop     */
527                         4                       /* updated  */
528                 },
529                 /* append */
530                 {
531                         MOD_ACTION_RETURN,      /* reject   */
532                         1,                      /* fail     */
533                         MOD_ACTION_RETURN,      /* ok       */
534                         MOD_ACTION_RETURN,      /* handled  */
535                         MOD_ACTION_RETURN,      /* invalid  */
536                         MOD_ACTION_RETURN,      /* userlock */
537                         2,                      /* notfound */
538                         MOD_ACTION_RETURN,      /* noop     */
539                         MOD_ACTION_RETURN       /* updated  */
540                 }
541         },
542         /* checksimul */
543         {
544                 /* group */
545                 {
546                         MOD_ACTION_RETURN,      /* reject   */
547                         1,                      /* fail     */
548                         MOD_ACTION_RETURN,      /* ok       */
549                         MOD_ACTION_RETURN,      /* handled  */
550                         MOD_ACTION_RETURN,      /* invalid  */
551                         MOD_ACTION_RETURN,      /* userlock */
552                         MOD_ACTION_RETURN,      /* notfound */
553                         MOD_ACTION_RETURN,      /* noop     */
554                         MOD_ACTION_RETURN       /* updated  */
555                 },
556                 /* redundant */
557                 {
558                         MOD_ACTION_RETURN,      /* reject   */
559                         1,                      /* fail     */
560                         MOD_ACTION_RETURN,      /* ok       */
561                         MOD_ACTION_RETURN,      /* handled  */
562                         MOD_ACTION_RETURN,      /* invalid  */
563                         MOD_ACTION_RETURN,      /* userlock */
564                         MOD_ACTION_RETURN,      /* notfound */
565                         MOD_ACTION_RETURN,      /* noop     */
566                         MOD_ACTION_RETURN       /* updated  */
567                 },
568                 /* append */
569                 {
570                         MOD_ACTION_RETURN,      /* reject   */
571                         1,                      /* fail     */
572                         MOD_ACTION_RETURN,      /* ok       */
573                         MOD_ACTION_RETURN,      /* handled  */
574                         MOD_ACTION_RETURN,      /* invalid  */
575                         MOD_ACTION_RETURN,      /* userlock */
576                         MOD_ACTION_RETURN,      /* notfound */
577                         MOD_ACTION_RETURN,      /* noop     */
578                         MOD_ACTION_RETURN       /* updated  */
579                 }
580         },
581         /* pre-proxy */
582         {
583                 /* group */
584                 {
585                         MOD_ACTION_RETURN,      /* reject   */
586                         MOD_ACTION_RETURN,      /* fail     */
587                         3,                      /* ok       */
588                         MOD_ACTION_RETURN,      /* handled  */
589                         MOD_ACTION_RETURN,      /* invalid  */
590                         MOD_ACTION_RETURN,      /* userlock */
591                         1,                      /* notfound */
592                         2,                      /* noop     */
593                         4                       /* updated  */
594                 },
595                 /* redundant */
596                 {
597                         MOD_ACTION_RETURN,      /* reject   */
598                         1,                      /* fail     */
599                         MOD_ACTION_RETURN,      /* ok       */
600                         MOD_ACTION_RETURN,      /* handled  */
601                         MOD_ACTION_RETURN,      /* invalid  */
602                         MOD_ACTION_RETURN,      /* userlock */
603                         MOD_ACTION_RETURN,      /* notfound */
604                         MOD_ACTION_RETURN,      /* noop     */
605                         MOD_ACTION_RETURN       /* updated  */
606                 },
607                 /* append */
608                 {
609                         MOD_ACTION_RETURN,      /* reject   */
610                         1,                      /* fail     */
611                         MOD_ACTION_RETURN,      /* ok       */
612                         MOD_ACTION_RETURN,      /* handled  */
613                         MOD_ACTION_RETURN,      /* invalid  */
614                         MOD_ACTION_RETURN,      /* userlock */
615                         2,                      /* notfound */
616                         MOD_ACTION_RETURN,      /* noop     */
617                         MOD_ACTION_RETURN       /* updated  */
618                 }
619         },
620         /* post-proxy */
621         {
622                 /* group */
623                 {
624                         MOD_ACTION_RETURN,      /* reject   */
625                         MOD_ACTION_RETURN,      /* fail     */
626                         3,                      /* ok       */
627                         MOD_ACTION_RETURN,      /* handled  */
628                         MOD_ACTION_RETURN,      /* invalid  */
629                         MOD_ACTION_RETURN,      /* userlock */
630                         1,                      /* notfound */
631                         2,                      /* noop     */
632                         4                       /* updated  */
633                 },
634                 /* redundant */
635                 {
636                         MOD_ACTION_RETURN,      /* reject   */
637                         1,                      /* fail     */
638                         MOD_ACTION_RETURN,      /* ok       */
639                         MOD_ACTION_RETURN,      /* handled  */
640                         MOD_ACTION_RETURN,      /* invalid  */
641                         MOD_ACTION_RETURN,      /* userlock */
642                         MOD_ACTION_RETURN,      /* notfound */
643                         MOD_ACTION_RETURN,      /* noop     */
644                         MOD_ACTION_RETURN       /* updated  */
645                 },
646                 /* append */
647                 {
648                         MOD_ACTION_RETURN,      /* reject   */
649                         1,                      /* fail     */
650                         MOD_ACTION_RETURN,      /* ok       */
651                         MOD_ACTION_RETURN,      /* handled  */
652                         MOD_ACTION_RETURN,      /* invalid  */
653                         MOD_ACTION_RETURN,      /* userlock */
654                         2,                      /* notfound */
655                         MOD_ACTION_RETURN,      /* noop     */
656                         MOD_ACTION_RETURN       /* updated  */
657                 }
658         },
659         /* post-auth */
660         {
661                 /* group */
662                 {
663                         MOD_ACTION_RETURN,      /* reject   */
664                         MOD_ACTION_RETURN,      /* fail     */
665                         3,                      /* ok       */
666                         MOD_ACTION_RETURN,      /* handled  */
667                         MOD_ACTION_RETURN,      /* invalid  */
668                         MOD_ACTION_RETURN,      /* userlock */
669                         1,                      /* notfound */
670                         2,                      /* noop     */
671                         4                       /* updated  */
672                 },
673                 /* redundant */
674                 {
675                         MOD_ACTION_RETURN,      /* reject   */
676                         1,                      /* fail     */
677                         MOD_ACTION_RETURN,      /* ok       */
678                         MOD_ACTION_RETURN,      /* handled  */
679                         MOD_ACTION_RETURN,      /* invalid  */
680                         MOD_ACTION_RETURN,      /* userlock */
681                         MOD_ACTION_RETURN,      /* notfound */
682                         MOD_ACTION_RETURN,      /* noop     */
683                         MOD_ACTION_RETURN       /* updated  */
684                 },
685                 /* append */
686                 {
687                         MOD_ACTION_RETURN,      /* reject   */
688                         1,                      /* fail     */
689                         MOD_ACTION_RETURN,      /* ok       */
690                         MOD_ACTION_RETURN,      /* handled  */
691                         MOD_ACTION_RETURN,      /* invalid  */
692                         MOD_ACTION_RETURN,      /* userlock */
693                         2,                      /* notfound */
694                         MOD_ACTION_RETURN,      /* noop     */
695                         MOD_ACTION_RETURN       /* updated  */
696                 }
697         }
698 };
699
700 /* Bail out if the module in question does not supply the wanted component */
701 static void sanity_check(int component, module_instance_t *inst,
702                          const char *filename)
703 {
704         if (!inst->entry->module->methods[component]) {
705                 radlog(L_ERR|L_CONS,
706                                 "%s: \"%s\" modules aren't allowed in '%s' sections -- they have no such method.",
707                                 filename, inst->entry->module->name,
708                                 component_names[component]);
709                 exit(1);
710         }
711 }
712
713 /* Parse a CONF_SECTION containing only result=action pairs */
714 static void override_actions(modcallable *c, CONF_SECTION *cs,
715                 const char *filename)
716 {
717         CONF_ITEM *ci;
718         CONF_PAIR *cp;
719         const char *attr, *value;
720         int lineno, rcode, action;
721
722         for(ci=cf_item_find_next(cs, NULL); ci != NULL; ci=cf_item_find_next(cs, ci)) {
723                 if(cf_item_is_section(ci)) {
724                         radlog(L_ERR|L_CONS,
725                                         "%s[%d] Subsection of module instance call "
726                                         "not allowed\n", filename,
727                                         cf_section_lineno(cf_itemtosection(ci)));
728                         exit(1);
729                 }
730                 cp = cf_itemtopair(ci);
731                 attr = cf_pair_attr(cp);
732                 value = cf_pair_value(cp);
733                 lineno = cf_pair_lineno(cp);
734                 rcode = str2rcode(attr, filename, lineno);
735                 action = str2action(value, filename, lineno);
736                 c->actions[rcode] = action;
737         }
738 }
739
740 static modcallable *do_compile_modsingle(int component, CONF_ITEM *ci,
741                 const char *filename, int grouptype,
742                 const char **modname)
743 {
744         int lineno;
745         const char *modrefname;
746         modsingle *single;
747         modcallable *csingle;
748         module_instance_t *this;
749
750         if (cf_item_is_section(ci)) {
751                 CONF_SECTION *cs = cf_itemtosection(ci);
752
753                 lineno = cf_section_lineno(cs);
754                 modrefname = cf_section_name1(cs);
755
756                 /* group{}, redundant{}, or append{} may appear where a
757                  * single module instance was expected - in that case, we
758                  * hand it off to compile_modgroup */
759                 if (strcmp(modrefname, "group") == 0) {
760                         *modname = "UnnamedGroup";
761                         return do_compile_modgroup(component, cs, filename,
762                                         GROUPTYPE_SIMPLEGROUP, grouptype);
763                 } else if (strcmp(modrefname, "redundant") == 0) {
764                         *modname = "UnnamedGroup";
765                         return do_compile_modgroup(component, cs, filename,
766                                         GROUPTYPE_REDUNDANT, grouptype);
767                 } else if (strcmp(modrefname, "append") == 0) {
768                         *modname = "UnnamedGroup";
769                         return do_compile_modgroup(component, cs, filename,
770                                         GROUPTYPE_APPEND, grouptype);
771                 }
772         } else {
773                 CONF_PAIR *cp = cf_itemtopair(ci);
774                 lineno = cf_pair_lineno(cp);
775                 modrefname = cf_pair_attr(cp);
776         }
777
778         single = rad_malloc(sizeof(*single));
779         csingle = mod_singletocallable(single);
780         csingle->next = NULL;
781         memcpy(csingle->actions,
782                         defaultactions[component][grouptype],
783                         sizeof csingle->actions);
784         rad_assert(modrefname != NULL);
785         csingle->name = modrefname;
786         csingle->type = MOD_SINGLE;
787
788         if (cf_item_is_section(ci)) {
789                 /* override default actions with what's in the CONF_SECTION */
790                 override_actions(csingle, cf_itemtosection(ci), filename);
791         }
792
793         this = find_module_instance(modrefname);
794         if (this == NULL) {
795                 exit(1); /* FIXME */
796         }
797
798         sanity_check(component, this, filename);
799
800         single->modinst = this;
801         *modname = this->entry->module->name;
802         return csingle;
803 }
804
805 modcallable *compile_modsingle(int component, CONF_ITEM *ci,
806                 const char *filename, const char **modname)
807 {
808         modcallable *ret = do_compile_modsingle(component, ci, filename,
809                 GROUPTYPE_SIMPLEGROUP,
810                 modname);
811         dump_tree(component, ret);
812         return ret;
813 }
814
815 static modcallable *do_compile_modgroup(int component, CONF_SECTION *cs,
816                 const char *filename, int grouptype,
817                 int parentgrouptype)
818 {
819         modgroup *g;
820         modcallable *c;
821         CONF_ITEM *ci;
822
823         g = rad_malloc(sizeof *g);
824
825         c = mod_grouptocallable(g);
826         c->next = NULL;
827         memcpy(c->actions, defaultactions[component][parentgrouptype],
828                 sizeof c->actions);
829         c->name = cf_section_name1(cs);
830         rad_assert(c->name != NULL);
831         c->type = MOD_GROUP;
832         g->children = NULL;
833
834         for (ci=cf_item_find_next(cs, NULL); ci != NULL; ci=cf_item_find_next(cs, ci)) {
835                 if(cf_item_is_section(ci)) {
836                         const char *junk;
837                         modcallable *single;
838                         single = do_compile_modsingle(component, ci, filename,
839                                         grouptype, &junk);
840                         add_child(g, single);
841                 } else {
842                         const char *attr, *value;
843                         CONF_PAIR *cp = cf_itemtopair(ci);
844                         int lineno;
845
846                         attr = cf_pair_attr(cp);
847                         value = cf_pair_value(cp);
848                         lineno = cf_pair_lineno(cp);
849
850                         /* A CONF_PAIR is either a module instance with no
851                          * actions specified... */
852                         if(value[0]==0) {
853                                 modcallable *single;
854                                 const char *junk;
855
856                                 single = do_compile_modsingle(component,
857                                                 cf_pairtoitem(cp), filename,
858                                                 grouptype, &junk);
859                                 add_child(g, single);
860                         } else {
861                                 /* ...or an action to be applied to this
862                                  * group. */
863                                 int rcode, action;
864                                 rcode = str2rcode(attr, filename, lineno);
865                                 action = str2action(value, filename, lineno);
866
867                                 c->actions[rcode] = action;
868                         }
869                 }
870         }
871         return mod_grouptocallable(g);
872 }
873
874 modcallable *compile_modgroup(int component, CONF_SECTION *cs,
875                 const char *filename)
876 {
877         modcallable *ret = do_compile_modgroup(component, cs, filename,
878                         GROUPTYPE_SIMPLEGROUP,
879                         GROUPTYPE_SIMPLEGROUP);
880         dump_tree(component, ret);
881         return ret;
882 }
883
884 void add_to_modcallable(modcallable **parent, modcallable *this,
885                 int component, char *name)
886 {
887         modgroup *g;
888
889         if (*parent == NULL) {
890                 modcallable *c;
891
892                 g = rad_malloc(sizeof *g);
893                 c = mod_grouptocallable(g);
894                 c->next = NULL;
895                 memcpy(c->actions,
896                                 defaultactions[component][GROUPTYPE_SIMPLEGROUP],
897                                 sizeof c->actions);
898                 rad_assert(name != NULL);
899                 c->name = name;
900                 c->type = MOD_GROUP;
901                 g->children = NULL;
902
903                 *parent = mod_grouptocallable(g);
904         } else {
905                 g = mod_callabletogroup(*parent);
906         }
907
908         add_child(g, this);
909 }
910
911 void modcallable_free(modcallable **pc)
912 {
913         modcallable *c, *loop, *next;
914         c = *pc;
915         if(c->type==MOD_GROUP) {
916                 for(loop=mod_callabletogroup(c)->children ; loop ; loop=next) {
917                         next = loop->next;
918                         modcallable_free(&loop);
919                 }
920         }
921         free(c);
922         *pc = NULL;
923 }