From a3221304b5f36c9ff76a1f307524d55133b2d403 Mon Sep 17 00:00:00 2001 From: "Alan T. DeKok" Date: Fri, 27 May 2011 12:58:39 +0200 Subject: [PATCH] Added "foreach" to unlang There's no "break" yet. --- man/man5/unlang.5 | 26 +++++++++++++++- src/main/modcall.c | 91 ++++++++++++++++++++++++++++++++++++++++++++++++++++-- src/main/xlat.c | 45 +++++++++++++++++++++++++++ 3 files changed, 158 insertions(+), 4 deletions(-) diff --git a/man/man5/unlang.5 b/man/man5/unlang.5 index 64ebbba..93b75f5 100644 --- a/man/man5/unlang.5 +++ b/man/man5/unlang.5 @@ -10,7 +10,7 @@ .RE .sp .. -.TH unlang 5 "19 May 2010" "" "FreeRADIUS Processing un-language" +.TH unlang 5 "27 May 2011" "" "FreeRADIUS Processing un-language" .SH NAME unlang \- FreeRADIUS Processing un\-language .SH DESCRIPTION @@ -91,6 +91,30 @@ returned false, and if the specified condition evaluates to true. .br } .DE +.IP foreach +.br +Loops over a named variable, running the block for each copy of the +named variable. The return value of the block is the return value of +the last statement executed. There is currently no way to exit the +loop early. + +The attribute name is just the name, e.g. reply:Reply-Message, with +none of the usual variable referenced %{...}. This is because it is a +reference to the attribute, and not an expansion of the attribute. + +Inside of the "foreach" block, the attribute which is being looped +over can be referenced as "Foreach-Variable-#". Where "#" is the +depth of the loop, starting at "0". e.g. "Foreach-Variable-0". The +loops can be nested up to eight (8) deep, though this is not +recommended. + +.DS + foreach Attribute-Name { +.br + ... +.br + } +.DE .IP switch .br Evaluate the given string, and choose the first matching "case" diff --git a/src/main/modcall.c b/src/main/modcall.c index f1908dd..aeb00a0 100644 --- a/src/main/modcall.c +++ b/src/main/modcall.c @@ -51,6 +51,7 @@ struct modcallable { enum { MOD_SINGLE = 1, MOD_GROUP, MOD_LOAD_BALANCE, MOD_REDUNDANT_LOAD_BALANCE, #ifdef WITH_UNLANG MOD_IF, MOD_ELSE, MOD_ELSIF, MOD_UPDATE, MOD_SWITCH, MOD_CASE, + MOD_FOREACH, #endif MOD_POLICY, MOD_REFERENCE, MOD_XLAT } type; int method; @@ -339,6 +340,7 @@ static const char *group_name[] = { "update", "switch", "case", + "foreach", #endif "policy" }; @@ -479,6 +481,52 @@ int modcall(int component, modcallable *c, REQUEST *request) } goto handle_result; } + + if (child->type == MOD_FOREACH) { + int i, depth = -1; + VALUE_PAIR *vp; + modgroup *g = mod_callabletogroup(child); + + for (i = 0; i < 8; i++) { + if (!request_data_get(request, radius_get_vp, i)) { + depth = i; + break; + } + } + + if (depth < 0) { + RDEBUG("ERROR: Foreach Nesting too deep!"); + myresult = RLM_MODULE_FAIL; + goto handle_result; + } + + if (radius_get_vp(request, child->name, &vp)) { + RDEBUG2("Foreach %s", child->name); + while (vp) { + request_data_add(request, modcall, + depth, vp, NULL); + + myresult = modcall(component, + g->children, + request); + if (myresult == MOD_ACTION_RETURN) { + myresult = RLM_MODULE_OK; + break; + } + vp = pairfind(vp->next, + vp->attribute, + vp->vendor); + } + + /* + * Delete the cached attribute. + */ + request_data_get(request, radius_get_vp, depth); + } + + myresult = RLM_MODULE_OK; + goto handle_result; + } #endif if (child->type == MOD_REFERENCE) { @@ -552,6 +600,7 @@ int modcall(int component, modcallable *c, REQUEST *request) case MOD_ELSE: case MOD_ELSIF: case MOD_CASE: + case MOD_FOREACH: #endif case MOD_GROUP: case MOD_POLICY: /* same as MOD_GROUP */ @@ -757,6 +806,7 @@ int modcall(int component, modcallable *c, REQUEST *request) case MOD_ELSE: case MOD_ELSIF: case MOD_CASE: + case MOD_FOREACH: #endif case MOD_GROUP: case MOD_POLICY: /* same as MOD_GROUP */ @@ -794,10 +844,9 @@ int modcall(int component, modcallable *c, REQUEST *request) */ if (!stack.children[stack.pointer]) { do_return: - rad_assert(stack.pointer > 0); myresult = stack.result[stack.pointer]; + if (stack.pointer == 0) break; stack.pointer--; - if (stack.pointer == 0) break; RDEBUG2("%.*s- %s %s returns %s", @@ -1432,7 +1481,7 @@ static modcallable *do_compile_modswitch(modcallable *parent, } if (!cf_item_find_next(cs, NULL)) { - cf_log_err(cf_sectiontoitem(cs), "'switch' statments cannot be empty."); + cf_log_err(cf_sectiontoitem(cs), "'switch' statements cannot be empty."); return NULL; } @@ -1479,6 +1528,33 @@ static modcallable *do_compile_modswitch(modcallable *parent, csingle->type = MOD_SWITCH; return csingle; } + +static modcallable *do_compile_modforeach(modcallable *parent, + int component, CONF_SECTION *cs, + const char *name2) +{ + modcallable *csingle; + + component = component; /* -Wunused */ + + if (!cf_section_name2(cs)) { + cf_log_err(cf_sectiontoitem(cs), + "You must specify an attribute to loop over in 'foreach'."); + return NULL; + } + + if (!cf_item_find_next(cs, NULL)) { + cf_log_err(cf_sectiontoitem(cs), "'foreach' blocks cannot be empty."); + return NULL; + } + + csingle= do_compile_modgroup(parent, component, cs, + GROUPTYPE_SIMPLE, GROUPTYPE_SIMPLE); + if (!csingle) return NULL; + csingle->name = name2; + csingle->type = MOD_FOREACH; + return csingle; +} #endif static modcallable *do_compile_modserver(modcallable *parent, @@ -1800,6 +1876,15 @@ static modcallable *do_compile_modsingle(modcallable *parent, } return csingle; + + } else if (strcmp(modrefname, "foreach") == 0) { + *modname = name2; + + csingle = do_compile_modforeach(parent, component, cs, + name2); + if (!csingle) return NULL; + + return csingle; #endif } /* else it's something like sql { fail = 1 ...} */ diff --git a/src/main/xlat.c b/src/main/xlat.c index 3e3edad..57d3b5b 100644 --- a/src/main/xlat.c +++ b/src/main/xlat.c @@ -53,6 +53,19 @@ static const char * const internal_xlat[] = {"check", "outer.reply", "outer.control", NULL}; +#ifdef WITH_UNLANG +static const char * const xlat_foreach_names[] = {"Foreach-Variable-0", + "Foreach-Variable-1", + "Foreach-Variable-2", + "Foreach-Variable-3", + "Foreach-Variable-4", + "Foreach-Variable-5", + "Foreach-Variable-6", + "Foreach-Variable-7", + "Foreach-Variable-8", + "Foreach-Variable-9", + NULL}; +#endif #if REQUEST_MAX_REGEX > 8 #error Please fix the following line @@ -445,6 +458,30 @@ static size_t xlat_integer(UNUSED void *instance, REQUEST *request, return snprintf(out, outlen, "%u", vp->vp_integer); } + +#ifdef WITH_UNLANG +/* + * Dynamically translate for check:, request:, reply:, etc. + */ +static size_t xlat_foreach(void *instance, REQUEST *request, + char *fmt, char *out, size_t outlen, + RADIUS_ESCAPE_STRING func) +{ + VALUE_PAIR *vp; + + /* + * See modcall, "FOREACH" for how this works. + */ + vp = request_data_reference(request, radius_get_vp, *(int*) instance); + if (!vp) { + *out = '\0'; + return 0; + } + + return valuepair2str(out, outlen, vp, vp->type, func); +} +#endif + /* * Print data as string, if possible. */ @@ -702,6 +739,14 @@ int xlat_register(const char *module, RAD_XLAT_FUNC func, void *instance) c = xlat_find(internal_xlat[i]); rad_assert(c != NULL); c->internal = TRUE; + +#ifdef WITH_UNLANG + xlat_register(xlat_foreach_names[i], + xlat_foreach, &xlat_inst[i]); + c = xlat_find(xlat_foreach_names[i]); + rad_assert(c != NULL); + c->internal = TRUE; +#endif } /* -- 2.1.4