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.
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.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 * Copyright 2000,2006 The FreeRADIUS server project
23 #include <freeradius-devel/ident.h>
26 #include <freeradius-devel/radiusd.h>
27 #include <freeradius-devel/modpriv.h>
28 #include <freeradius-devel/modcall.h>
29 #include <freeradius-devel/rad_assert.h>
32 /* mutually-recursive static functions need a prototype up front */
33 static modcallable *do_compile_modgroup(modcallable *,
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)
45 /* Here are our basic types: modcallable, modgroup, and modsingle. For an
46 * explanation of what they are all about, see ../../doc/README.failover */
49 struct modcallable *next;
51 enum { MOD_SINGLE = 1, MOD_GROUP, MOD_LOAD_BALANCE, MOD_REDUNDANT_LOAD_BALANCE,
53 MOD_IF, MOD_ELSE, MOD_ELSIF, MOD_UPDATE, MOD_SWITCH, MOD_CASE,
55 MOD_POLICY, MOD_REFERENCE, MOD_XLAT } type;
57 int actions[RLM_MODULE_NUMCODES];
60 #define GROUPTYPE_SIMPLE 0
61 #define GROUPTYPE_REDUNDANT 1
62 #define GROUPTYPE_APPEND 2
63 #define GROUPTYPE_COUNT 3
66 modcallable mc; /* self */
67 int grouptype; /* after mc */
68 modcallable *children;
75 module_instance_t *modinst;
90 static const FR_NAME_NUMBER grouptype_table[] = {
91 { "", GROUPTYPE_SIMPLE },
92 { "redundant ", GROUPTYPE_REDUNDANT },
93 { "append ", GROUPTYPE_APPEND },
97 /* Simple conversions: modsingle and modgroup are subclasses of modcallable,
98 * so we often want to go back and forth between them. */
99 static modsingle *mod_callabletosingle(modcallable *p)
101 rad_assert(p->type==MOD_SINGLE);
102 return (modsingle *)p;
104 static modgroup *mod_callabletogroup(modcallable *p)
106 rad_assert((p->type > MOD_SINGLE) && (p->type <= MOD_POLICY));
108 return (modgroup *)p;
110 static modcallable *mod_singletocallable(modsingle *p)
112 return (modcallable *)p;
114 static modcallable *mod_grouptocallable(modgroup *p)
116 return (modcallable *)p;
119 static modref *mod_callabletoref(modcallable *p)
121 rad_assert(p->type==MOD_REFERENCE);
124 static modcallable *mod_reftocallable(modref *p)
126 return (modcallable *)p;
129 static modxlat *mod_callabletoxlat(modcallable *p)
131 rad_assert(p->type==MOD_XLAT);
134 static modcallable *mod_xlattocallable(modxlat *p)
136 return (modcallable *)p;
139 /* modgroups are grown by adding a modcallable to the end */
140 /* FIXME: This is O(N^2) */
141 static void add_child(modgroup *g, modcallable *c)
143 modcallable **head = &g->children;
144 modcallable *node = *head;
145 modcallable **last = head;
154 rad_assert(c->next == NULL);
156 c->parent = mod_grouptocallable(g);
159 /* Here's where we recognize all of our keywords: first the rcodes, then the
161 static const FR_NAME_NUMBER rcode_table[] = {
162 { "reject", RLM_MODULE_REJECT },
163 { "fail", RLM_MODULE_FAIL },
164 { "ok", RLM_MODULE_OK },
165 { "handled", RLM_MODULE_HANDLED },
166 { "invalid", RLM_MODULE_INVALID },
167 { "userlock", RLM_MODULE_USERLOCK },
168 { "notfound", RLM_MODULE_NOTFOUND },
169 { "noop", RLM_MODULE_NOOP },
170 { "updated", RLM_MODULE_UPDATED },
176 * Compile action && rcode for later use.
178 static int compile_action(modcallable *c, CONF_PAIR *cp)
181 const char *attr, *value;
183 attr = cf_pair_attr(cp);
184 value = cf_pair_value(cp);
185 if (!value) return 0;
187 if (!strcasecmp(value, "return"))
188 action = MOD_ACTION_RETURN;
190 else if (!strcasecmp(value, "break"))
191 action = MOD_ACTION_RETURN;
193 else if (!strcasecmp(value, "reject"))
194 action = MOD_ACTION_REJECT;
196 else if (strspn(value, "0123456789")==strlen(value)) {
197 action = atoi(value);
200 * Don't allow priority zero, for future use.
202 if (action == 0) return 0;
204 cf_log_err(cf_pairtoitem(cp), "Unknown action '%s'.\n",
209 if (strcasecmp(attr, "default") != 0) {
212 rcode = fr_str2int(rcode_table, attr, -1);
214 cf_log_err(cf_pairtoitem(cp),
215 "Unknown module rcode '%s'.\n",
219 c->actions[rcode] = action;
221 } else { /* set all unset values to the default */
224 for (i = 0; i < RLM_MODULE_NUMCODES; i++) {
225 if (!c->actions[i]) c->actions[i] = action;
232 /* Some short names for debugging output */
233 static const char * const comp2str[] = {
249 #ifdef HAVE_PTHREAD_H
251 * Lock the mutex for the module
253 static void safe_lock(module_instance_t *instance)
256 pthread_mutex_lock(instance->mutex);
260 * Unlock the mutex for the module
262 static void safe_unlock(module_instance_t *instance)
265 pthread_mutex_unlock(instance->mutex);
269 * No threads: these functions become NULL's.
271 #define safe_lock(foo)
272 #define safe_unlock(foo)
275 static int call_modsingle(int component, modsingle *sp, REQUEST *request)
280 rad_assert(request != NULL);
283 * If the request should stop, refuse to do anything.
285 blocked = (request->master_state == REQUEST_STOP_PROCESSING);
286 if (blocked) return RLM_MODULE_NOOP;
288 RDEBUG3(" modsingle[%s]: calling %s (%s) for request %d",
289 comp2str[component], sp->modinst->name,
290 sp->modinst->entry->name, request->number);
292 if (sp->modinst->dead) {
293 myresult = RLM_MODULE_FAIL;
297 safe_lock(sp->modinst);
300 * For logging unresponsive children.
302 request->module = sp->modinst->name;
304 myresult = sp->modinst->entry->module->methods[component](
305 sp->modinst->insthandle, request);
307 request->module = "";
308 safe_unlock(sp->modinst);
311 * Wasn't blocked, and now is. Complain!
313 blocked = (request->master_state == REQUEST_STOP_PROCESSING);
315 radlog(L_INFO, "WARNING: Module %s became unblocked for request %u",
316 sp->modinst->entry->name, request->number);
320 RDEBUG3(" modsingle[%s]: returned from %s (%s) for request %d",
321 comp2str[component], sp->modinst->name,
322 sp->modinst->entry->name, request->number);
328 static int default_component_results[RLM_COMPONENT_COUNT] = {
329 RLM_MODULE_REJECT, /* AUTH */
330 RLM_MODULE_NOTFOUND, /* AUTZ */
331 RLM_MODULE_NOOP, /* PREACCT */
332 RLM_MODULE_NOOP, /* ACCT */
333 RLM_MODULE_FAIL, /* SESS */
334 RLM_MODULE_NOOP, /* PRE_PROXY */
335 RLM_MODULE_NOOP, /* POST_PROXY */
336 RLM_MODULE_NOOP /* POST_AUTH */
339 RLM_MODULE_NOOP, /* RECV_COA_TYPE */
340 RLM_MODULE_NOOP /* SEND_COA_TYPE */
345 static const char *group_name[] = {
349 "load-balance group",
350 "redundant-load-balance group",
362 static const char *modcall_spaces = "++++++++++++++++++++++++++++++++";
364 #define MODCALL_STACK_MAX (32)
367 * Don't call the modules recursively. Instead, do them
368 * iteratively, and manage the call stack ourselves.
370 typedef struct modcall_stack {
373 int priority[MODCALL_STACK_MAX];
374 int result[MODCALL_STACK_MAX];
375 modcallable *children[MODCALL_STACK_MAX];
376 modcallable *start[MODCALL_STACK_MAX];
381 * Call a module, iteratively, with a local stack, rather than
382 * recursively. What did Paul Graham say about Lisp...?
384 int modcall(int component, modcallable *c, REQUEST *request)
386 int myresult, mypriority;
388 modcallable *parent, *child;
390 int if_taken, was_if;
392 if ((component < 0) || (component >= RLM_COMPONENT_COUNT)) {
393 return RLM_MODULE_FAIL;
397 stack.priority[0] = 0;
398 stack.children[0] = c;
399 stack.start[0] = NULL;
400 myresult = stack.result[0] = default_component_results[component];
402 was_if = if_taken = FALSE;
406 * A module has taken too long to process the request,
407 * and we've been told to stop processing it.
409 if ((request->master_state == REQUEST_STOP_PROCESSING) ||
411 (request->parent->master_state == REQUEST_STOP_PROCESSING))) {
412 myresult = RLM_MODULE_FAIL;
416 child = stack.children[stack.pointer];
418 myresult = stack.result[stack.pointer];
421 parent = child->parent;
424 if ((child->type == MOD_ELSE) || (child->type == MOD_ELSIF)) {
425 myresult = stack.result[stack.pointer];
427 if (!was_if) { /* error */
428 RDEBUG2("%.*s ... skipping %s for request %d: No preceding \"if\"",
429 stack.pointer + 1, modcall_spaces,
430 group_name[child->type],
435 RDEBUG2("%.*s ... skipping %s for request %d: Preceding \"if\" was taken",
436 stack.pointer + 1, modcall_spaces,
437 group_name[child->type],
444 * "if" or "elsif". Evaluate the condition.
446 if ((child->type == MOD_IF) || (child->type == MOD_ELSIF)) {
447 int condition = TRUE;
448 const char *p = child->name;
450 RDEBUG2("%.*s? %s %s",
451 stack.pointer + 1, modcall_spaces,
452 (child->type == MOD_IF) ? "if" : "elsif",
455 if (radius_evaluate_condition(request, myresult,
456 0, &p, TRUE, &condition)) {
457 RDEBUG2("%.*s? %s %s -> %s",
458 stack.pointer + 1, modcall_spaces,
459 (child->type == MOD_IF) ? "if" : "elsif",
460 child->name, (condition != FALSE) ? "TRUE" : "FALSE");
463 * This should never happen, the
464 * condition is checked when the
465 * module section is loaded.
471 * If the condition fails to match, go
472 * immediately to the next entry in the
479 } /* else process it as a simple group */
482 if (child->type == MOD_UPDATE) {
484 modgroup *g = mod_callabletogroup(child);
486 rcode = radius_update_attrlist(request, g->cs,
487 g->vps, child->name);
488 if (rcode != RLM_MODULE_UPDATED) {
490 goto handle_priority;
493 /* else leave myresult && mypriority alone */
498 if (child->type == MOD_REFERENCE) {
499 modref *mr = mod_callabletoref(child);
500 const char *server = request->server;
502 if (server == mr->ref_name) {
503 RDEBUG("WARNING: Suppressing recursive call to server %s", server);
504 myresult = RLM_MODULE_NOOP;
505 goto handle_priority;
508 request->server = mr->ref_name;
509 RDEBUG("server %s { # nested call", mr->ref_name);
510 myresult = indexed_modcall(component, 0, request);
511 RDEBUG("} # server %s with nested call", mr->ref_name);
512 request->server = server;
513 goto handle_priority;
516 if (child->type == MOD_XLAT) {
517 modxlat *mx = mod_callabletoxlat(child);
521 radius_xlat(buffer, sizeof(buffer),
522 mx->xlat_name, request, NULL);
524 RDEBUG("`%s`", mx->xlat_name);
525 radius_exec_program(mx->xlat_name, request,
527 request->packet->vps,
531 goto skip; /* don't change anything on the stack */
535 * Child is a group that has children of it's own.
537 if (child->type != MOD_SINGLE) {
541 modcallable *null_case;
543 modgroup *g = mod_callabletogroup(child);
548 * Catastrophic error. This SHOULD have
549 * been caught when we were reading in the
554 if (stack.pointer >= MODCALL_STACK_MAX) {
555 radlog(L_ERR, "Internal sanity check failed: module stack is too deep");
559 stack.priority[stack.pointer] = stack.priority[stack.pointer - 1];
560 stack.result[stack.pointer] = stack.result[stack.pointer - 1];
561 switch (child->type) {
571 case MOD_POLICY: /* same as MOD_GROUP */
572 stack.children[stack.pointer] = g->children;
576 * See the "camel book" for why
579 * If (rand(0..n) < 1), pick the
580 * current realm. We add a scale
581 * factor of 65536, to avoid
584 case MOD_LOAD_BALANCE:
585 case MOD_REDUNDANT_LOAD_BALANCE:
587 for(p = g->children; p; p = p->next) {
596 if ((count * (fr_rand() & 0xffff)) < (uint32_t) 0x10000) {
600 stack.children[stack.pointer] = q;
605 if (!strchr(child->name, '%')) {
606 VALUE_PAIR *vp = NULL;
608 radius_get_vp(request, child->name,
611 vp_prints_value(buffer,
618 radius_xlat(buffer, sizeof(buffer),
619 child->name, request, NULL);
621 null_case = q = NULL;
622 for(p = g->children; p; p = p->next) {
624 if (!null_case) null_case = p;
627 if (strcmp(buffer, p->name) == 0) {
633 if (!q) q = null_case;
635 stack.children[stack.pointer] = q;
640 RDEBUG2("Internal sanity check failed in modcall %d", child->type);
641 exit(1); /* internal sanity check failure */
646 stack.start[stack.pointer] = stack.children[stack.pointer];
648 RDEBUG2("%.*s- entering %s %s {...}",
649 stack.pointer, modcall_spaces,
650 group_name[child->type],
651 child->name ? child->name : "");
654 * Catch the special case of a NULL group.
656 if (!stack.children[stack.pointer]) {
658 * Print message for NULL group
660 RDEBUG2("%.*s- %s %s returns %s",
661 stack.pointer + 1, modcall_spaces,
662 group_name[child->type],
663 child->name ? child->name : "",
664 fr_int2str(rcode_table,
665 stack.result[stack.pointer],
671 * The child may be a group, so we want to
672 * recurse into it's children, rather than
673 * falling through to the code below.
679 * Process a stand-alone child, and fall through
680 * to dealing with it's parent.
682 sp = mod_callabletosingle(child);
684 myresult = call_modsingle(child->method, sp, request);
687 mypriority = child->actions[myresult];
690 RDEBUG2("%.*s[%s] returns %s",
691 stack.pointer + 1, modcall_spaces,
692 child->name ? child->name : "",
693 fr_int2str(rcode_table, myresult, "??"));
696 * This is a bit of a hack...
698 if (component != RLM_COMPONENT_SESS) request->simul_max = myresult;
701 * FIXME: Allow modules to push a modcallable
702 * onto this stack. This should simplify
703 * configuration a LOT!
705 * Once we do that, we can't do load-time
706 * checking of the maximum stack depth, and we've
707 * got to cache the stack pointer before storing
710 * Also, if the stack changed, we need to set
711 * children[ptr] to NULL, and process the next
712 * entry on the stack, rather than falling
713 * through to finalize the processing of this
716 * Don't put "myresult" on the stack here,
717 * we have to do so with priority.
721 * We roll back up the stack at this point.
725 * The child's action says return. Do so.
727 if ((child->actions[myresult] == MOD_ACTION_RETURN) &&
729 stack.result[stack.pointer] = myresult;
730 stack.children[stack.pointer] = NULL;
735 * If "reject", break out of the loop and return
738 if (child->actions[myresult] == MOD_ACTION_REJECT) {
739 stack.children[stack.pointer] = NULL;
740 stack.result[stack.pointer] = RLM_MODULE_REJECT;
745 * Otherwise, the action is a number, the
746 * preference level of this return code. If no
747 * higher preference has been seen yet, remember
750 if (mypriority >= stack.priority[stack.pointer]) {
754 stack.result[stack.pointer] = myresult;
755 stack.priority[stack.pointer] = mypriority;
759 * No parent, we must be done.
763 rad_assert(stack.pointer == 0);
764 myresult = stack.result[0];
768 rad_assert(child != NULL);
771 * Go to the "next" child, whatever that is.
773 switch (parent->type) {
781 case MOD_POLICY: /* same as MOD_GROUP */
782 stack.children[stack.pointer] = child->next;
788 case MOD_LOAD_BALANCE:
789 stack.children[stack.pointer] = NULL;
792 case MOD_REDUNDANT_LOAD_BALANCE:
794 stack.children[stack.pointer] = child->next;
796 modgroup *g = mod_callabletogroup(parent);
798 stack.children[stack.pointer] = g->children;
800 if (stack.children[stack.pointer] == stack.start[stack.pointer]) {
801 stack.children[stack.pointer] = NULL;
805 RDEBUG2("Internal sanity check failed in modcall next %d", child->type);
810 * No child, we're done this group, and we return
811 * "myresult" to the caller by pushing it back up
814 if (!stack.children[stack.pointer]) {
816 rad_assert(stack.pointer > 0);
817 myresult = stack.result[stack.pointer];
820 if (stack.pointer == 0) break;
822 RDEBUG2("%.*s- %s %s returns %s",
823 stack.pointer + 1, modcall_spaces,
824 group_name[parent->type],
825 parent->name ? parent->name : "",
826 fr_int2str(rcode_table, myresult, "??"));
829 if ((parent->type == MOD_IF) ||
830 (parent->type == MOD_ELSIF)) {
831 if_taken = was_if = TRUE;
833 if_taken = was_if = FALSE;
840 child = stack.children[stack.pointer];
841 parent = child->parent;
845 } /* loop until done */
852 static const char *action2str(int action)
855 if(action==MOD_ACTION_RETURN)
857 if(action==MOD_ACTION_REJECT)
859 snprintf(buf, sizeof buf, "%d", action);
863 /* If you suspect a bug in the parser, you'll want to use these dump
864 * functions. dump_tree should reproduce a whole tree exactly as it was found
865 * in radiusd.conf, but in long form (all actions explicitly defined) */
866 static void dump_mc(modcallable *c, int indent)
870 if(c->type==MOD_SINGLE) {
871 modsingle *single = mod_callabletosingle(c);
872 DEBUG("%.*s%s {", indent, "\t\t\t\t\t\t\t\t\t\t\t",
873 single->modinst->name);
875 modgroup *g = mod_callabletogroup(c);
877 DEBUG("%.*s%s {", indent, "\t\t\t\t\t\t\t\t\t\t\t",
878 group_name[c->type]);
879 for(p = g->children;p;p = p->next)
880 dump_mc(p, indent+1);
883 for(i = 0; i<RLM_MODULE_NUMCODES; ++i) {
884 DEBUG("%.*s%s = %s", indent+1, "\t\t\t\t\t\t\t\t\t\t\t",
885 fr_int2str(rcode_table, i, "??"),
886 action2str(c->actions[i]));
889 DEBUG("%.*s}", indent, "\t\t\t\t\t\t\t\t\t\t\t");
892 static void dump_tree(int comp, modcallable *c)
894 RDEBUG("[%s]", comp2str[comp]);
898 #define dump_tree(a, b)
901 /* These are the default actions. For each component, the group{} block
902 * behaves like the code from the old module_*() function. redundant{} and
903 * append{} are based on my guesses of what they will be used for. --Pac. */
905 defaultactions[RLM_COMPONENT_COUNT][GROUPTYPE_COUNT][RLM_MODULE_NUMCODES] =
911 MOD_ACTION_RETURN, /* reject */
913 MOD_ACTION_RETURN, /* ok */
914 MOD_ACTION_RETURN, /* handled */
916 MOD_ACTION_RETURN, /* userlock */
917 MOD_ACTION_RETURN, /* notfound */
923 MOD_ACTION_RETURN, /* reject */
925 MOD_ACTION_RETURN, /* ok */
926 MOD_ACTION_RETURN, /* handled */
927 MOD_ACTION_RETURN, /* invalid */
928 MOD_ACTION_RETURN, /* userlock */
929 MOD_ACTION_RETURN, /* notfound */
930 MOD_ACTION_RETURN, /* noop */
931 MOD_ACTION_RETURN /* updated */
935 MOD_ACTION_RETURN, /* reject */
937 MOD_ACTION_RETURN, /* ok */
938 MOD_ACTION_RETURN, /* handled */
939 MOD_ACTION_RETURN, /* invalid */
940 MOD_ACTION_RETURN, /* userlock */
942 MOD_ACTION_RETURN, /* noop */
943 MOD_ACTION_RETURN /* updated */
950 MOD_ACTION_RETURN, /* reject */
951 MOD_ACTION_RETURN, /* fail */
953 MOD_ACTION_RETURN, /* handled */
954 MOD_ACTION_RETURN, /* invalid */
955 MOD_ACTION_RETURN, /* userlock */
962 MOD_ACTION_RETURN, /* reject */
964 MOD_ACTION_RETURN, /* ok */
965 MOD_ACTION_RETURN, /* handled */
966 MOD_ACTION_RETURN, /* invalid */
967 MOD_ACTION_RETURN, /* userlock */
968 MOD_ACTION_RETURN, /* notfound */
969 MOD_ACTION_RETURN, /* noop */
970 MOD_ACTION_RETURN /* updated */
974 MOD_ACTION_RETURN, /* reject */
976 MOD_ACTION_RETURN, /* ok */
977 MOD_ACTION_RETURN, /* handled */
978 MOD_ACTION_RETURN, /* invalid */
979 MOD_ACTION_RETURN, /* userlock */
981 MOD_ACTION_RETURN, /* noop */
982 MOD_ACTION_RETURN /* updated */
989 MOD_ACTION_RETURN, /* reject */
990 MOD_ACTION_RETURN, /* fail */
992 MOD_ACTION_RETURN, /* handled */
993 MOD_ACTION_RETURN, /* invalid */
994 MOD_ACTION_RETURN, /* userlock */
995 MOD_ACTION_RETURN, /* notfound */
1001 MOD_ACTION_RETURN, /* reject */
1003 MOD_ACTION_RETURN, /* ok */
1004 MOD_ACTION_RETURN, /* handled */
1005 MOD_ACTION_RETURN, /* invalid */
1006 MOD_ACTION_RETURN, /* userlock */
1007 MOD_ACTION_RETURN, /* notfound */
1008 MOD_ACTION_RETURN, /* noop */
1009 MOD_ACTION_RETURN /* updated */
1013 MOD_ACTION_RETURN, /* reject */
1015 MOD_ACTION_RETURN, /* ok */
1016 MOD_ACTION_RETURN, /* handled */
1017 MOD_ACTION_RETURN, /* invalid */
1018 MOD_ACTION_RETURN, /* userlock */
1020 MOD_ACTION_RETURN, /* noop */
1021 MOD_ACTION_RETURN /* updated */
1028 MOD_ACTION_RETURN, /* reject */
1029 MOD_ACTION_RETURN, /* fail */
1031 MOD_ACTION_RETURN, /* handled */
1032 MOD_ACTION_RETURN, /* invalid */
1033 MOD_ACTION_RETURN, /* userlock */
1034 MOD_ACTION_RETURN, /* notfound */
1042 MOD_ACTION_RETURN, /* ok */
1043 MOD_ACTION_RETURN, /* handled */
1052 MOD_ACTION_RETURN, /* reject */
1054 MOD_ACTION_RETURN, /* ok */
1055 MOD_ACTION_RETURN, /* handled */
1056 MOD_ACTION_RETURN, /* invalid */
1057 MOD_ACTION_RETURN, /* userlock */
1059 MOD_ACTION_RETURN, /* noop */
1060 MOD_ACTION_RETURN /* updated */
1067 MOD_ACTION_RETURN, /* reject */
1069 MOD_ACTION_RETURN, /* ok */
1070 MOD_ACTION_RETURN, /* handled */
1071 MOD_ACTION_RETURN, /* invalid */
1072 MOD_ACTION_RETURN, /* userlock */
1073 MOD_ACTION_RETURN, /* notfound */
1074 MOD_ACTION_RETURN, /* noop */
1075 MOD_ACTION_RETURN /* updated */
1079 MOD_ACTION_RETURN, /* reject */
1081 MOD_ACTION_RETURN, /* ok */
1082 MOD_ACTION_RETURN, /* handled */
1083 MOD_ACTION_RETURN, /* invalid */
1084 MOD_ACTION_RETURN, /* userlock */
1085 MOD_ACTION_RETURN, /* notfound */
1086 MOD_ACTION_RETURN, /* noop */
1087 MOD_ACTION_RETURN /* updated */
1091 MOD_ACTION_RETURN, /* reject */
1093 MOD_ACTION_RETURN, /* ok */
1094 MOD_ACTION_RETURN, /* handled */
1095 MOD_ACTION_RETURN, /* invalid */
1096 MOD_ACTION_RETURN, /* userlock */
1097 MOD_ACTION_RETURN, /* notfound */
1098 MOD_ACTION_RETURN, /* noop */
1099 MOD_ACTION_RETURN /* updated */
1106 MOD_ACTION_RETURN, /* reject */
1107 MOD_ACTION_RETURN, /* fail */
1109 MOD_ACTION_RETURN, /* handled */
1110 MOD_ACTION_RETURN, /* invalid */
1111 MOD_ACTION_RETURN, /* userlock */
1118 MOD_ACTION_RETURN, /* reject */
1120 MOD_ACTION_RETURN, /* ok */
1121 MOD_ACTION_RETURN, /* handled */
1122 MOD_ACTION_RETURN, /* invalid */
1123 MOD_ACTION_RETURN, /* userlock */
1124 MOD_ACTION_RETURN, /* notfound */
1125 MOD_ACTION_RETURN, /* noop */
1126 MOD_ACTION_RETURN /* updated */
1130 MOD_ACTION_RETURN, /* reject */
1132 MOD_ACTION_RETURN, /* ok */
1133 MOD_ACTION_RETURN, /* handled */
1134 MOD_ACTION_RETURN, /* invalid */
1135 MOD_ACTION_RETURN, /* userlock */
1137 MOD_ACTION_RETURN, /* noop */
1138 MOD_ACTION_RETURN /* updated */
1145 MOD_ACTION_RETURN, /* reject */
1146 MOD_ACTION_RETURN, /* fail */
1148 MOD_ACTION_RETURN, /* handled */
1149 MOD_ACTION_RETURN, /* invalid */
1150 MOD_ACTION_RETURN, /* userlock */
1157 MOD_ACTION_RETURN, /* reject */
1159 MOD_ACTION_RETURN, /* ok */
1160 MOD_ACTION_RETURN, /* handled */
1161 MOD_ACTION_RETURN, /* invalid */
1162 MOD_ACTION_RETURN, /* userlock */
1163 MOD_ACTION_RETURN, /* notfound */
1164 MOD_ACTION_RETURN, /* noop */
1165 MOD_ACTION_RETURN /* updated */
1169 MOD_ACTION_RETURN, /* reject */
1171 MOD_ACTION_RETURN, /* ok */
1172 MOD_ACTION_RETURN, /* handled */
1173 MOD_ACTION_RETURN, /* invalid */
1174 MOD_ACTION_RETURN, /* userlock */
1176 MOD_ACTION_RETURN, /* noop */
1177 MOD_ACTION_RETURN /* updated */
1184 MOD_ACTION_RETURN, /* reject */
1185 MOD_ACTION_RETURN, /* fail */
1187 MOD_ACTION_RETURN, /* handled */
1188 MOD_ACTION_RETURN, /* invalid */
1189 MOD_ACTION_RETURN, /* userlock */
1196 MOD_ACTION_RETURN, /* reject */
1198 MOD_ACTION_RETURN, /* ok */
1199 MOD_ACTION_RETURN, /* handled */
1200 MOD_ACTION_RETURN, /* invalid */
1201 MOD_ACTION_RETURN, /* userlock */
1202 MOD_ACTION_RETURN, /* notfound */
1203 MOD_ACTION_RETURN, /* noop */
1204 MOD_ACTION_RETURN /* updated */
1208 MOD_ACTION_RETURN, /* reject */
1210 MOD_ACTION_RETURN, /* ok */
1211 MOD_ACTION_RETURN, /* handled */
1212 MOD_ACTION_RETURN, /* invalid */
1213 MOD_ACTION_RETURN, /* userlock */
1215 MOD_ACTION_RETURN, /* noop */
1216 MOD_ACTION_RETURN /* updated */
1225 MOD_ACTION_RETURN, /* reject */
1226 MOD_ACTION_RETURN, /* fail */
1228 MOD_ACTION_RETURN, /* handled */
1229 MOD_ACTION_RETURN, /* invalid */
1230 MOD_ACTION_RETURN, /* userlock */
1237 MOD_ACTION_RETURN, /* reject */
1239 MOD_ACTION_RETURN, /* ok */
1240 MOD_ACTION_RETURN, /* handled */
1241 MOD_ACTION_RETURN, /* invalid */
1242 MOD_ACTION_RETURN, /* userlock */
1243 MOD_ACTION_RETURN, /* notfound */
1244 MOD_ACTION_RETURN, /* noop */
1245 MOD_ACTION_RETURN /* updated */
1249 MOD_ACTION_RETURN, /* reject */
1251 MOD_ACTION_RETURN, /* ok */
1252 MOD_ACTION_RETURN, /* handled */
1253 MOD_ACTION_RETURN, /* invalid */
1254 MOD_ACTION_RETURN, /* userlock */
1256 MOD_ACTION_RETURN, /* noop */
1257 MOD_ACTION_RETURN /* updated */
1264 MOD_ACTION_RETURN, /* reject */
1265 MOD_ACTION_RETURN, /* fail */
1267 MOD_ACTION_RETURN, /* handled */
1268 MOD_ACTION_RETURN, /* invalid */
1269 MOD_ACTION_RETURN, /* userlock */
1276 MOD_ACTION_RETURN, /* reject */
1278 MOD_ACTION_RETURN, /* ok */
1279 MOD_ACTION_RETURN, /* handled */
1280 MOD_ACTION_RETURN, /* invalid */
1281 MOD_ACTION_RETURN, /* userlock */
1282 MOD_ACTION_RETURN, /* notfound */
1283 MOD_ACTION_RETURN, /* noop */
1284 MOD_ACTION_RETURN /* updated */
1288 MOD_ACTION_RETURN, /* reject */
1290 MOD_ACTION_RETURN, /* ok */
1291 MOD_ACTION_RETURN, /* handled */
1292 MOD_ACTION_RETURN, /* invalid */
1293 MOD_ACTION_RETURN, /* userlock */
1295 MOD_ACTION_RETURN, /* noop */
1296 MOD_ACTION_RETURN /* updated */
1304 static modcallable *do_compile_modupdate(modcallable *parent,
1305 int component, CONF_SECTION *cs,
1309 const char *vp_name;
1311 modcallable *csingle;
1313 VALUE_PAIR *head, **tail;
1315 static const char *attrlist_names[] = {
1316 "request", "reply", "proxy-request", "proxy-reply",
1317 "config", "control",
1318 "coa", "coa-reply", "disconnect", "disconnect-reply",
1322 component = component; /* -Wunused */
1324 if (!cf_section_name2(cs)) {
1325 cf_log_err(cf_sectiontoitem(cs),
1326 "Require list name for 'update'.\n");
1331 if (strncmp(vp_name, "outer.", 6) == 0) {
1335 for (i = 0; attrlist_names[i] != NULL; i++) {
1336 if (strcmp(vp_name, attrlist_names[i]) == 0) {
1343 cf_log_err(cf_sectiontoitem(cs),
1344 "Unknown attribute list \"%s\"",
1353 * Walk through the children of the update section,
1354 * ensuring that they're all known attributes.
1356 for (ci=cf_item_find_next(cs, NULL);
1358 ci=cf_item_find_next(cs, ci)) {
1362 if (cf_item_is_section(ci)) {
1363 cf_log_err(ci, "\"update\" sections cannot have subsections");
1367 if (!cf_item_is_pair(ci)) continue;
1369 cp = cf_itemtopair(ci); /* can't return NULL */
1370 vp = cf_pairtovp(cp);
1373 cf_log_err(ci, "ERROR: %s", fr_strerror());
1377 if ((vp->operator != T_OP_EQ) &&
1378 (vp->operator != T_OP_CMP_EQ) &&
1379 (vp->operator != T_OP_ADD) &&
1380 (vp->operator != T_OP_SUB) &&
1381 (vp->operator != T_OP_LE) &&
1382 (vp->operator != T_OP_GE) &&
1383 (vp->operator != T_OP_CMP_FALSE) &&
1384 (vp->operator != T_OP_SET)) {
1387 cf_log_err(ci, "Invalid operator for attribute");
1392 * A few more sanity checks. The enforcement of
1393 * <= or >= can only happen for integer
1396 if ((vp->operator == T_OP_LE) ||
1397 (vp->operator == T_OP_GE)) {
1398 if ((vp->type != PW_TYPE_BYTE) &&
1399 (vp->type != PW_TYPE_SHORT) &&
1400 (vp->type != PW_TYPE_INTEGER)) {
1403 cf_log_err(ci, "Enforcment of <= or >= is possible only for integer attributes");
1413 cf_log_err(cf_sectiontoitem(cs),
1414 "ERROR: update %s section cannot be empty",
1419 g = rad_malloc(sizeof(*g)); /* never fails */
1420 memset(g, 0, sizeof(*g));
1421 csingle = mod_grouptocallable(g);
1423 csingle->parent = parent;
1424 csingle->next = NULL;
1425 csingle->name = name2;
1426 csingle->type = MOD_UPDATE;
1427 csingle->method = component;
1429 g->grouptype = GROUPTYPE_SIMPLE;
1438 static modcallable *do_compile_modswitch(modcallable *parent,
1439 int component, CONF_SECTION *cs)
1441 modcallable *csingle;
1443 int had_seen_default = FALSE;
1445 component = component; /* -Wunused */
1447 if (!cf_section_name2(cs)) {
1448 cf_log_err(cf_sectiontoitem(cs),
1449 "You must specify a variable to switch over for 'switch'.");
1453 if (!cf_item_find_next(cs, NULL)) {
1454 cf_log_err(cf_sectiontoitem(cs), "'switch' statments cannot be empty.");
1459 * Walk through the children of the switch section,
1460 * ensuring that they're all 'case' statements
1462 for (ci=cf_item_find_next(cs, NULL);
1464 ci=cf_item_find_next(cs, ci)) {
1465 CONF_SECTION *subcs;
1466 const char *name1, *name2;
1468 if (!cf_item_is_section(ci)) {
1469 if (!cf_item_is_pair(ci)) continue;
1471 cf_log_err(ci, "\"switch\" sections can only have \"case\" subsections");
1475 subcs = cf_itemtosection(ci); /* can't return NULL */
1476 name1 = cf_section_name1(subcs);
1478 if (strcmp(name1, "case") != 0) {
1479 cf_log_err(ci, "\"switch\" sections can only have \"case\" subsections");
1483 name2 = cf_section_name2(subcs);
1484 if (!name2 && !had_seen_default) {
1485 had_seen_default = TRUE;
1489 if (!name2 || (name2[0] == '\0')) {
1490 cf_log_err(ci, "\"case\" sections must have a name");
1495 csingle= do_compile_modgroup(parent, component, cs,
1496 GROUPTYPE_SIMPLE, GROUPTYPE_SIMPLE);
1497 if (!csingle) return NULL;
1498 csingle->type = MOD_SWITCH;
1503 static modcallable *do_compile_modserver(modcallable *parent,
1504 int component, CONF_ITEM *ci,
1509 modcallable *csingle;
1510 CONF_SECTION *subcs;
1513 subcs = cf_section_sub_find_name2(cs, comp2str[component], NULL);
1515 cf_log_err(ci, "Server %s has no %s section",
1516 server, comp2str[component]);
1520 mr = rad_malloc(sizeof(*mr));
1521 memset(mr, 0, sizeof(*mr));
1523 csingle = mod_reftocallable(mr);
1524 csingle->parent = parent;
1525 csingle->next = NULL;
1526 csingle->name = name;
1527 csingle->type = MOD_REFERENCE;
1528 csingle->method = component;
1530 memcpy(csingle->actions, defaultactions[component][GROUPTYPE_SIMPLE],
1531 sizeof(csingle->actions));
1533 mr->ref_name = strdup(server);
1539 static modcallable *do_compile_modxlat(modcallable *parent,
1540 int component, const char *fmt)
1542 modcallable *csingle;
1545 mx = rad_malloc(sizeof(*mx));
1546 memset(mx, 0, sizeof(*mx));
1548 csingle = mod_xlattocallable(mx);
1549 csingle->parent = parent;
1550 csingle->next = NULL;
1551 csingle->name = "expand";
1552 csingle->type = MOD_XLAT;
1553 csingle->method = component;
1555 memcpy(csingle->actions, defaultactions[component][GROUPTYPE_SIMPLE],
1556 sizeof(csingle->actions));
1558 mx->xlat_name = strdup(fmt);
1559 if (fmt[0] != '%') {
1563 strcpy(mx->xlat_name, fmt + 1);
1564 p = strrchr(mx->xlat_name, '`');
1572 * redundant, etc. can refer to modules or groups, but not much else.
1574 static int all_children_are_modules(CONF_SECTION *cs, const char *name)
1578 for (ci=cf_item_find_next(cs, NULL);
1580 ci=cf_item_find_next(cs, ci)) {
1582 * If we're a redundant, etc. group, then the
1583 * intention is to call modules, rather than
1584 * processing logic. These checks aren't
1585 * *strictly* necessary, but they keep the users
1586 * from doing crazy things.
1588 if (cf_item_is_section(ci)) {
1589 CONF_SECTION *subcs = cf_itemtosection(ci);
1590 const char *name1 = cf_section_name1(subcs);
1592 if ((strcmp(name1, "if") == 0) ||
1593 (strcmp(name1, "else") == 0) ||
1594 (strcmp(name1, "elsif") == 0) ||
1595 (strcmp(name1, "update") == 0) ||
1596 (strcmp(name1, "switch") == 0) ||
1597 (strcmp(name1, "case") == 0)) {
1598 cf_log_err(ci, "%s sections cannot contain a \"%s\" statement",
1605 if (cf_item_is_pair(ci)) {
1606 CONF_PAIR *cp = cf_itemtopair(ci);
1607 if (cf_pair_value(cp) != NULL) {
1609 "Entry with no value is invalid");
1620 * Compile one entry of a module call.
1622 static modcallable *do_compile_modsingle(modcallable *parent,
1623 int component, CONF_ITEM *ci,
1625 const char **modname)
1630 const char *modrefname;
1632 modcallable *csingle;
1633 module_instance_t *this;
1634 CONF_SECTION *cs, *subcs, *modules;
1636 if (cf_item_is_section(ci)) {
1639 cs = cf_itemtosection(ci);
1640 modrefname = cf_section_name1(cs);
1641 name2 = cf_section_name2(cs);
1642 if (!name2) name2 = "_UnNamedGroup";
1645 * group{}, redundant{}, or append{} may appear
1646 * where a single module instance was expected.
1647 * In that case, we hand it off to
1650 if (strcmp(modrefname, "group") == 0) {
1652 return do_compile_modgroup(parent, component, cs,
1656 } else if (strcmp(modrefname, "redundant") == 0) {
1659 if (!all_children_are_modules(cs, modrefname)) {
1663 return do_compile_modgroup(parent, component, cs,
1664 GROUPTYPE_REDUNDANT,
1667 } else if (strcmp(modrefname, "append") == 0) {
1669 return do_compile_modgroup(parent, component, cs,
1673 } else if (strcmp(modrefname, "load-balance") == 0) {
1676 if (!all_children_are_modules(cs, modrefname)) {
1680 csingle= do_compile_modgroup(parent, component, cs,
1683 if (!csingle) return NULL;
1684 csingle->type = MOD_LOAD_BALANCE;
1687 } else if (strcmp(modrefname, "redundant-load-balance") == 0) {
1690 if (!all_children_are_modules(cs, modrefname)) {
1694 csingle= do_compile_modgroup(parent, component, cs,
1695 GROUPTYPE_REDUNDANT,
1697 if (!csingle) return NULL;
1698 csingle->type = MOD_REDUNDANT_LOAD_BALANCE;
1702 } else if (strcmp(modrefname, "if") == 0) {
1703 if (!cf_section_name2(cs)) {
1704 cf_log_err(ci, "'if' without condition.");
1709 csingle= do_compile_modgroup(parent, component, cs,
1712 if (!csingle) return NULL;
1713 csingle->type = MOD_IF;
1715 if (!radius_evaluate_condition(NULL, 0, 0, modname,
1717 modcallable_free(&csingle);
1724 } else if (strcmp(modrefname, "elsif") == 0) {
1726 ((parent->type == MOD_LOAD_BALANCE) ||
1727 (parent->type == MOD_REDUNDANT_LOAD_BALANCE))) {
1728 cf_log_err(ci, "'elsif' cannot be used in this section section.");
1732 if (!cf_section_name2(cs)) {
1733 cf_log_err(ci, "'elsif' without condition.");
1738 csingle= do_compile_modgroup(parent, component, cs,
1741 if (!csingle) return NULL;
1742 csingle->type = MOD_ELSIF;
1744 if (!radius_evaluate_condition(NULL, 0, 0, modname,
1746 modcallable_free(&csingle);
1753 } else if (strcmp(modrefname, "else") == 0) {
1755 ((parent->type == MOD_LOAD_BALANCE) ||
1756 (parent->type == MOD_REDUNDANT_LOAD_BALANCE))) {
1757 cf_log_err(ci, "'else' cannot be used in this section section.");
1761 if (cf_section_name2(cs)) {
1762 cf_log_err(ci, "Cannot have conditions on 'else'.");
1767 csingle= do_compile_modgroup(parent, component, cs,
1770 if (!csingle) return NULL;
1771 csingle->type = MOD_ELSE;
1774 } else if (strcmp(modrefname, "update") == 0) {
1777 csingle = do_compile_modupdate(parent, component, cs,
1779 if (!csingle) return NULL;
1783 } else if (strcmp(modrefname, "switch") == 0) {
1786 csingle = do_compile_modswitch(parent, component, cs);
1787 if (!csingle) return NULL;
1791 } else if (strcmp(modrefname, "case") == 0) {
1797 * FIXME: How to tell that the parent can only
1798 * be a "switch" statement?
1801 cf_log_err(ci, "\"case\" statements may only appear within a \"switch\" section");
1805 csingle= do_compile_modgroup(parent, component, cs,
1808 if (!csingle) return NULL;
1809 csingle->type = MOD_CASE;
1810 csingle->name = cf_section_name2(cs); /* may be NULL */
1813 * Set all of it's codes to return, so that
1814 * when we pick a 'case' statement, we don't
1815 * fall through to processing the next one.
1817 for (i = 0; i < RLM_MODULE_NUMCODES; i++) {
1818 csingle->actions[i] = MOD_ACTION_RETURN;
1823 } /* else it's something like sql { fail = 1 ...} */
1825 } else if (!cf_item_is_pair(ci)) { /* CONF_DATA or some such */
1829 * Else it's a module reference, with updated return
1834 CONF_PAIR *cp = cf_itemtopair(ci);
1835 modrefname = cf_pair_attr(cp);
1838 * Actions (ok = 1), etc. are orthoganal to just
1839 * about everything else.
1841 if (cf_pair_value(cp) != NULL) {
1842 cf_log_err(ci, "Entry is not a reference to a module");
1846 if (((modrefname[0] == '%') && (modrefname[1] == '{')) ||
1847 (modrefname[0] == '`')) {
1848 return do_compile_modxlat(parent, component,
1853 * See if the module is a virtual one. If so,
1854 * return that, rather than doing anything here.
1857 cs = cf_section_find("instantiate");
1858 if (cs) subcs = cf_section_sub_find_name2(cs, NULL,
1861 (cs = cf_section_find("policy")) != NULL) {
1864 snprintf(buffer, sizeof(buffer), "%s.%s",
1865 modrefname, comp2str[component]);
1868 * Prefer name.section, then name.
1870 subcs = cf_section_sub_find_name2(cs, NULL,
1873 subcs = cf_section_sub_find_name2(cs, NULL,
1879 * Allow policies to over-ride module names.
1880 * i.e. the "sql" policy can do some extra things,
1881 * and then call the "sql" module.
1883 for (loop = cf_item_parent(ci);
1885 loop = cf_item_parent(cf_sectiontoitem(loop))) {
1886 if (loop == subcs) {
1892 DEBUG2(" Module: Loading virtual module %s",
1896 * redundant foo {} is a single.
1898 if (cf_section_name2(subcs)) {
1899 return do_compile_modsingle(parent,
1901 cf_sectiontoitem(subcs),
1906 * foo {} is a group.
1908 return do_compile_modgroup(parent,
1918 * Not a virtual module. It must be a real module.
1920 modules = cf_section_find("modules");
1923 if (modules && cf_section_sub_find_name2(modules, NULL, modrefname)) {
1924 this = find_module_instance(modules, modrefname, 1);
1932 * Maybe it's module.method
1934 p = strrchr(modrefname, '.');
1935 if (p) for (i = RLM_COMPONENT_AUTH;
1936 i < RLM_COMPONENT_COUNT;
1938 if (strcmp(p + 1, comp2str[i]) == 0) {
1941 strlcpy(buffer, modrefname, sizeof(buffer));
1942 buffer[p - modrefname] = '\0';
1945 this = find_module_instance(cf_section_find("modules"), buffer, 1);
1947 !this->entry->module->methods[i]) {
1949 cf_log_err(ci, "Module %s has no such method %s", buffer, comp2str[i]);
1957 if (strncmp(modrefname, "server[", 7) == 0) {
1960 strlcpy(buffer, modrefname + 7, sizeof(buffer));
1961 p = strrchr(buffer, ']');
1962 if (!p || p[1] != '\0' || (p == buffer)) {
1963 cf_log_err(ci, "Invalid server reference in \"%s\".", modrefname);
1968 cs = cf_section_sub_find_name2(NULL, "server", buffer);
1970 cf_log_err(ci, "No such server \"%s\".", buffer);
1974 return do_compile_modserver(parent, component, ci,
1975 modrefname, cs, buffer);
1979 cf_log_err(ci, "Failed to find \"%s\" in the \"modules\" section.", modrefname);
1984 * We know it's all OK, allocate the structures, and fill
1987 single = rad_malloc(sizeof(*single));
1988 memset(single, 0, sizeof(*single));
1989 csingle = mod_singletocallable(single);
1990 csingle->parent = parent;
1991 csingle->next = NULL;
1992 if (!parent || (component != RLM_COMPONENT_AUTH)) {
1993 memcpy(csingle->actions, defaultactions[component][grouptype],
1994 sizeof csingle->actions);
1995 } else { /* inside Auth-Type has different rules */
1996 memcpy(csingle->actions, defaultactions[RLM_COMPONENT_AUTZ][grouptype],
1997 sizeof csingle->actions);
1999 rad_assert(modrefname != NULL);
2000 csingle->name = modrefname;
2001 csingle->type = MOD_SINGLE;
2002 csingle->method = component;
2005 * Singles can override the actions, virtual modules cannot.
2007 * FIXME: We may want to re-visit how to do this...
2008 * maybe a csingle as a ref?
2010 if (cf_item_is_section(ci)) {
2013 cs = cf_itemtosection(ci);
2014 for (csi=cf_item_find_next(cs, NULL);
2016 csi=cf_item_find_next(cs, csi)) {
2018 if (cf_item_is_section(csi)) {
2019 cf_log_err(csi, "Subsection of module instance call not allowed");
2020 modcallable_free(&csingle);
2024 if (!cf_item_is_pair(csi)) continue;
2026 if (!compile_action(csingle, cf_itemtopair(csi))) {
2027 modcallable_free(&csingle);
2034 * Bail out if the module in question does not supply the
2037 if (!this->entry->module->methods[component]) {
2038 cf_log_err(ci, "\"%s\" modules aren't allowed in '%s' sections -- they have no such method.", this->entry->module->name,
2039 comp2str[component]);
2040 modcallable_free(&csingle);
2044 single->modinst = this;
2045 *modname = this->entry->module->name;
2049 modcallable *compile_modsingle(modcallable *parent,
2050 int component, CONF_ITEM *ci,
2051 const char **modname)
2053 modcallable *ret = do_compile_modsingle(parent, component, ci,
2056 dump_tree(component, ret);
2062 * Internal compile group code.
2064 static modcallable *do_compile_modgroup(modcallable *parent,
2065 int component, CONF_SECTION *cs,
2066 int grouptype, int parentgrouptype)
2073 g = rad_malloc(sizeof(*g));
2074 memset(g, 0, sizeof(*g));
2075 g->grouptype = grouptype;
2077 c = mod_grouptocallable(g);
2079 c->type = MOD_GROUP;
2081 memset(c->actions, 0, sizeof(c->actions));
2084 * Remember the name for printing, etc.
2086 * FIXME: We may also want to put the names into a
2087 * rbtree, so that groups can reference each other...
2089 c->name = cf_section_name2(cs);
2091 c->name = cf_section_name1(cs);
2092 if (strcmp(c->name, "group") == 0) {
2095 c->type = MOD_POLICY;
2101 * Loop over the children of this group.
2103 for (ci=cf_item_find_next(cs, NULL);
2105 ci=cf_item_find_next(cs, ci)) {
2108 * Sections are references to other groups, or
2109 * to modules with updated return codes.
2111 if (cf_item_is_section(ci)) {
2112 const char *junk = NULL;
2113 modcallable *single;
2114 CONF_SECTION *subcs = cf_itemtosection(ci);
2116 single = do_compile_modsingle(c, component, ci,
2119 cf_log_err(ci, "Failed to parse \"%s\" subsection.",
2120 cf_section_name1(subcs));
2121 modcallable_free(&c);
2124 add_child(g, single);
2126 } else if (!cf_item_is_pair(ci)) { /* CONF_DATA */
2130 const char *attr, *value;
2131 CONF_PAIR *cp = cf_itemtopair(ci);
2133 attr = cf_pair_attr(cp);
2134 value = cf_pair_value(cp);
2137 * A CONF_PAIR is either a module
2138 * instance with no actions
2142 modcallable *single;
2143 const char *junk = NULL;
2145 single = do_compile_modsingle(c,
2152 "Failed to parse \"%s\" entry.",
2154 modcallable_free(&c);
2157 add_child(g, single);
2160 * Or a module instance with action.
2162 } else if (!compile_action(c, cp)) {
2163 modcallable_free(&c);
2165 } /* else it worked */
2170 * Set the default actions, if they haven't already been
2173 for (i = 0; i < RLM_MODULE_NUMCODES; i++) {
2174 if (!c->actions[i]) {
2175 if (!parent || (component != RLM_COMPONENT_AUTH)) {
2176 c->actions[i] = defaultactions[component][parentgrouptype][i];
2177 } else { /* inside Auth-Type has different rules */
2178 c->actions[i] = defaultactions[RLM_COMPONENT_AUTZ][parentgrouptype][i];
2184 * FIXME: If there are no children, return NULL?
2186 return mod_grouptocallable(g);
2189 modcallable *compile_modgroup(modcallable *parent,
2190 int component, CONF_SECTION *cs)
2192 modcallable *ret = do_compile_modgroup(parent, component, cs,
2195 dump_tree(component, ret);
2199 void add_to_modcallable(modcallable **parent, modcallable *this,
2200 int component, const char *name)
2204 rad_assert(this != NULL);
2206 if (*parent == NULL) {
2209 g = rad_malloc(sizeof *g);
2210 memset(g, 0, sizeof(*g));
2211 g->grouptype = GROUPTYPE_SIMPLE;
2212 c = mod_grouptocallable(g);
2215 defaultactions[component][GROUPTYPE_SIMPLE],
2216 sizeof(c->actions));
2217 rad_assert(name != NULL);
2219 c->type = MOD_GROUP;
2220 c->method = component;
2223 *parent = mod_grouptocallable(g);
2225 g = mod_callabletogroup(*parent);
2231 void modcallable_free(modcallable **pc)
2233 modcallable *c, *loop, *next;
2235 if (c->type != MOD_SINGLE) {
2236 modgroup *g = mod_callabletogroup(c);
2238 for(loop = g->children;
2242 modcallable_free(&loop);