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