]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Use new module method resolution function
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Fri, 7 Jun 2024 00:30:52 +0000 (20:30 -0400)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Sat, 8 Jun 2024 19:11:01 +0000 (15:11 -0400)
This supports static and dynamic modules
Pass in call_env_ctx_t to parsing callbacks instead of the section name, module instance data directly

22 files changed:
src/lib/server/module_rlm.c
src/lib/server/module_rlm.h
src/lib/server/section.h
src/lib/server/virtual_servers.c
src/lib/server/virtual_servers.h
src/lib/unlang/call_env.c
src/lib/unlang/call_env.h
src/lib/unlang/compile.c
src/lib/unlang/module.c
src/lib/unlang/xlat_inst.c
src/lib/unlang/xlat_redundant.c
src/modules/rlm_cache/rlm_cache.c
src/modules/rlm_detail/rlm_detail.c
src/modules/rlm_files/rlm_files.c
src/modules/rlm_ldap/rlm_ldap.c
src/modules/rlm_linelog/rlm_linelog.c
src/modules/rlm_smtp/rlm_smtp.c
src/modules/rlm_sql/rlm_sql.c
src/modules/rlm_sqlcounter/rlm_sqlcounter.c
src/modules/rlm_winbind/rlm_winbind.c
src/tests/keywords/radius.conf
src/tests/keywords/xlat-redundant

index c52826050656d2975d1cab97dcfc5ea159d882fa..d2fc96d964c6e6bd400eacf49da153866c890bbd 100644 (file)
  * @brief Defines functions for rlm module (re-)initialisation.
  *
  * @copyright 2003,2006,2016 The FreeRADIUS server project
- * @copyright 2016 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
+ * @copyright 2016,2024 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
  * @copyright 2000 Alan DeKok (aland@freeradius.org)
  * @copyright 2000 Alan Curry (pacman@world.std.com)
  */
+
 RCSID("$Id$")
 
 #include <freeradius-devel/server/cf_file.h>
@@ -35,16 +36,27 @@ RCSID("$Id$")
 #include <freeradius-devel/server/module.h>
 #include <freeradius-devel/server/module_rlm.h>
 #include <freeradius-devel/server/pair.h>
+#include <freeradius-devel/server/section.h>
+#include <freeradius-devel/server/tmpl.h>
 #include <freeradius-devel/server/virtual_servers.h>
 
 #include <freeradius-devel/util/atexit.h>
 #include <freeradius-devel/util/debug.h>
 #include <freeradius-devel/util/dlist.h>
+#include <freeradius-devel/util/rb.h>
+#include <freeradius-devel/util/sbuff.h>
+#include <freeradius-devel/util/strerror.h>
+#include <freeradius-devel/util/talloc.h>
+#include <freeradius-devel/util/token.h>
+#include <freeradius-devel/util/value.h>
 
 #include <freeradius-devel/unlang/compile.h>
+
 #include <freeradius-devel/unlang/xlat_func.h>
 #include <freeradius-devel/unlang/xlat_redundant.h>
 
+#include <pthread.h>
+
 /** Lookup virtual module by name
  */
 static fr_rb_tree_t *module_rlm_virtual_name_tree;
@@ -242,6 +254,41 @@ int module_rlm_sibling_section_find(CONF_SECTION **out, CONF_SECTION *module, ch
        return 1;
 }
 
+xlat_t *module_rlm_xlat_register(TALLOC_CTX *ctx, module_inst_ctx_t const *mctx,
+                                char const *name, xlat_func_t func, fr_type_t return_type)
+{
+       module_instance_t       *mi = mctx->mi;
+       module_rlm_instance_t   *mri = talloc_get_type_abort(mi->uctx, module_rlm_instance_t);
+       module_rlm_xlat_t       *mrx;
+       xlat_t                  *x;
+       char                    inst_name[256];
+
+       fr_assert_msg(name != mctx->mi->name, "`name` must not be the same as the module "
+                     "instance name.  Pass a NULL `name` arg if this is required");
+
+       if (!name) {
+               name = mctx->mi->name;
+       } else {
+               if ((size_t)snprintf(inst_name, sizeof(inst_name), "%s.%s", mctx->mi->name, name) >= sizeof(inst_name)) {
+                       ERROR("%s: Instance name too long", __FUNCTION__);
+                       return NULL;
+               }
+               name = inst_name;
+       }
+
+       x = xlat_func_register(ctx, name, func, return_type);
+       if (unlikely(x == NULL)) return NULL;
+
+       xlat_mctx_set(x, mctx);
+
+       MEM(mrx = talloc(mi, module_rlm_xlat_t));
+       mrx->xlat = x;
+
+       fr_rb_insert(&mri->xlats, mrx);
+
+       return x;
+}
+
 /** Initialise a module specific connection pool
  *
  * @see fr_pool_init
@@ -426,361 +473,347 @@ bool module_rlm_section_type_set(request_t *request, fr_dict_attr_t const *type_
        }
 }
 
-/** Find an existing module instance and verify it implements the specified method
- *
- * Extracts the method from the module name where the format is @verbatim <module>.<method> @endverbatim
- * and ensures the module implements the specified method.
+/** Iterate over an array of named module methods, looking for matches
  *
- * @param[out] method          the method function we will call
- * @param[out] method_env      the module_call_env to evaluate when compiling the method.
- * @param[out] name1           name1 of the method being called
- * @param[out] name2           name2 of the method being called
- * @param[in] name             The name of the module we're attempting to find, possibly concatenated with the method
+ * @param[in] bindings         A terminated array of module method bindings.
+ *                             pre-sorted using #section_name_cmp with name2
+ *                             sublists populated.
+ * @param[in] section          name1 of the method being called can be one of the following:
+ *                             - An itenfier.
+ *                             - CF_IDENT_ANY if the method is a wildcard.
+ *                             name2 of the method being called can be one of the following:
+ *                             - An itenfier.
+ *                             - NULL to match section names with only a name1.
+ *                             - CF_IDENT_ANY if the method is a wildcard.
  * @return
- *     - The module instance on success.
- *     - NULL on not found
- *
- *  If the module exists but the method doesn't exist, then `method` is set to NULL.
+ *     - The module_method_name_t on success.
+ *     - NULL on not found.
  */
-module_instance_t *module_rlm_by_name_and_method(module_method_t *method, call_env_method_t const **method_env,
-                                                char const **name1, char const **name2,
-                                                virtual_server_t const *vs, char const *name)
+static CC_HINT(nonnull)
+module_method_binding_t const *module_binding_find(module_method_binding_t const *bindings, section_name_t const *section)
 {
-       char                            *p, *q, *inst_name;
-       size_t                          len;
-       int                             j;
-       module_instance_t               *mi;
-       module_method_binding_t const   *methods;
-       char const                      *method_name1, *method_name2;
-       module_rlm_t const              *mrlm;
-
-       if (method) *method = NULL;
-
-       method_name1 = method_name2 = NULL;
-       if (name1) {
-               method_name1 = *name1;
-               *name1 = NULL;
-       }
-       if (name2) {
-               method_name2 = *name2;
-               *name2 = NULL;
-       }
+       module_method_binding_t const *p;
 
        /*
-        *      Module names are allowed to contain '.'
-        *      so we search for the bare module name first.
+        *      This could potentially be improved by using a binary search
+        *      but given the small number of items, reduced branches and
+        *      sequential access just scanning the list, it's probably not
+        *      worth it.
         */
-       mi = module_rlm_static_by_name(NULL, name);
-       if (mi) {
-               section_name_t const    **allowed_list;
-
-               if (!method) return mi;
+       for (p = bindings; p->section; p++) {
+               switch (section_name_match(p->section, section)) {
+               case 1:         /* match */
+                       return p;
 
-               mrlm = module_rlm_from_module(mi->exported);
-
-               /*
-                *      We're not searching for a named method, OR the
-                *      module has no named methods.  Try to return a
-                *      method based on the component.
-                */
-               if (!method_name1 || !mrlm->bindings) goto return_component;
+               case -1:        /* name1 didn't match, skip to the end of the sub-list */
+                       p = fr_dlist_tail(&p->same_name1);
+                       break;
 
-               /*
-                *      Walk through the module, finding a matching
-                *      method.
-                */
-               for (j = 0; mrlm->bindings[j].section; j++) {
-                       methods = &mrlm->bindings[j];
+               case 0:         /* name1 did match - see if we can find a matching name2 */
+               {
+                       fr_dlist_head_t const *same_name1 = &p->same_name1;
 
-                       /*
-                        *      Wildcard match name1, we're
-                        *      done.
-                        */
-                       if (methods->section->name1 == CF_IDENT_ANY) {
-                       found:
-                               *method = methods->method;
-                               if (method_env) *method_env = methods->method_env;
-                               if (name1) *name1 = method_name1;
-                               if (name2) *name2 = method_name2;
-                               return mi;
+                       while ((p = fr_dlist_next(same_name1, p))) {
+                               if (section_name2_match(p->section, section)) return p;
                        }
-
-                       /*
-                        *      If name1 doesn't match, skip it.
-                        */
-                       if (strcasecmp(methods->section->name1, method_name1) != 0) continue;
-
-                       /*
-                        *      The module can declare a
-                        *      wildcard for name2, in which
-                        *      case it's a match.
-                        */
-                       if (methods->section->name2 == CF_IDENT_ANY) goto found;
-
-                       /*
-                        *      No name2 is also a match to no name2.
-                        */
-                       if (!methods->section->name2 && !method_name2) goto found;
-
-                       /*
-                        *      Don't do strcmp on NULLs
-                        */
-                       if (!methods->section->name2 || !method_name2) continue;
-
-                       if (strcasecmp(methods->section->name2, method_name2) == 0) goto found;
+                       p = fr_dlist_tail(same_name1);
                }
-
-               if (!vs) goto skip_section_method;
-
-               /*
-                *      No match for "recv Access-Request", or
-                *      whatever else the section is.  Let's see if
-                *      the section has a list of allowed methods.
-                */
-               allowed_list = virtual_server_section_methods(vs, method_name1, method_name2);
-               if (!allowed_list) goto return_component;
-
-               /*
-                *      Walk over allowed methods for this section,
-                *      (implicitly ordered by priority), and see if
-                *      the allowed method matches any of the module
-                *      methods.  This process lets us reference a
-                *      module as "foo" in the configuration.  If the
-                *      module exports a "recv bar" method, and the
-                *      virtual server has a "recv bar" processing
-                *      section, then they should match.
-                *
-                *      Unfortunately, this process is O(N*M).
-                *      Luckily, we only do it if all else fails, so
-                *      it's mostly OK.
-                *
-                *      Note that the "allowed" list CANNOT include
-                *      CF_IDENT_ANY.  Only the module can do that.
-                *      If the "allowed" list exported CF_IDENT_ANY,
-                *      then any module method would match, which is
-                *      bad.
-                */
-               for (j = 0; allowed_list[j]; j++) {
-                       int k;
-                       section_name_t const *allowed = allowed_list[j];
-
-                       for (k = 0; mrlm->bindings[k].section; k++) {
-                               methods = &mrlm->bindings[k];
-
-                               fr_assert(methods->section->name1 != CF_IDENT_ANY); /* should have been caught above */
-
-                               if (strcasecmp(methods->section->name1, allowed->name1) != 0) continue;
-
-                               /*
-                                *      The module matches "recv *",
-                                *      call this method.
-                                */
-                               if (methods->section->name2 == CF_IDENT_ANY) {
-                               found_allowed:
-                                       *method = methods->method;
-                                       return mi;
-                               }
-
-                               /*
-                                *      No name2 is also a match to no name2.
-                                */
-                               if (!methods->section->name2 && !allowed->name2) goto found_allowed;
-
-                               /*
-                                *      Don't do strcasecmp on NULLs
-                                */
-                               if (!methods->section->name2 || !allowed->name2) continue;
-
-                               if (strcasecmp(methods->section->name2, allowed->name2) == 0) goto found_allowed;
-                       }
+                       break;
                }
-
-       return_component:
-               /*
-                *      Didn't find a matching method.  Just return
-                *      the module.
-                */
-               return mi;
+#ifdef __clang_analyzer__
+               /* Will never be NULL, worse case, p doesn't change*/
+               if (!p) break;
+#endif
        }
 
-skip_section_method:
+       return NULL;
+}
 
-       /*
-        *      Find out if the instance name contains
-        *      a method, if it doesn't, then the module
-        *      doesn't exist.
-        */
-       p = strchr(name, '.');
-       if (!p) {
-               fr_strerror_printf("No such module '%s'", name);
-               return NULL;
+/** Dump the available bindings for the module into the strerror stack
+ *
+ * @param[in] mmb      bindings to push onto the strerror stack.
+ */
+static void module_rlm_methods_to_strerror(module_method_binding_t const *mmb)
+{
+       module_method_binding_t const *mmb_p;
+
+       if (!mmb || !mmb[0].section) {
+               fr_strerror_const_push("Module provides no methods");
+               return;
        }
 
-       /*
-        *      The module name may have a '.' in it, AND it may have
-        *      a method <sigh> So we try to find out which is which.
-        */
-       inst_name = talloc_strdup(NULL, name);
-       p = inst_name + (p - name);
+       fr_strerror_const_push("Available methods are:");
 
-       /*
-        *      Loop over the '.' portions, gradually looking up a
-        *      longer string, in order to find the full module name.
-        */
-       do {
-               *p = '\0';
+       for (mmb_p = mmb; mmb_p->section; mmb_p++) {
+               char const *name1 = section_name_str(mmb_p->section->name1);
+               char const *name2 = section_name_str(mmb_p->section->name2);
 
-               mi = module_instance_by_name(rlm_modules_static, NULL, inst_name);
-               if (mi) break;
+               fr_strerror_printf_push("  %s%s%s",
+                                       name1, name2 ? "." : "", name2 ? name2 : "");
+       }
+}
 
-               /*
-                *      Find the next '.'
-                */
-               *p = '.';
-               p = strchr(p + 1, '.');
-       } while (p);
+/** Find an existing module instance and verify it implements the specified method
+ *
+ * Extracts the method from the module name where the format is @verbatim <module>[.<method1>[.<method2>]] @endverbatim
+ * and ensures the module implements the specified method.
+ *
+ * @param[in] ctx              to allocate the dynamic module key tmpl from.
+ * @param[out] mmc_out         the result from resolving the module method,
+ *                             plus the key tmpl for dynamic modules.
+ *                             This is not allocated from the ctx to save the runtime
+ *                             dereference.
+ * @param[in] vs               Virtual server to search for alternative module names in.
+ * @param[in] section          Section name containing the module call.
+ * @param[in] name             The module method call i.e. module[<key>][.<method>]
+ * @param[in] t_rules          for resolving the dynamic module key.
+ * @return
+ *     - The module instance on success.
+ *     - NULL on not found
+ *
+ *  If the module exists but the method doesn't exist, then `method` is set to NULL.
+ */
+fr_slen_t module_rlm_by_name_and_method(TALLOC_CTX *ctx, module_method_call_t *mmc_out,
+                                       virtual_server_t const *vs, section_name_t const *section, fr_sbuff_t *name,
+                                       tmpl_rules_t const *t_rules)
+{
+       fr_sbuff_term_t const           *dyn_tt = &FR_SBUFF_TERMS(
+                                               L(""),
+                                               L("\t"),
+                                               L("\n"),
+                                               L(" "),
+                                               L("[")
+                                       );
+
+       fr_sbuff_term_t const           *elem_tt = &FR_SBUFF_TERMS(
+                                               L(""),
+                                               L("\t"),
+                                               L("\n"),
+                                               L(" "),
+                                               L(".")
+                                       );
+
+       fr_sbuff_t                      *elem1;
+       module_method_call_t            *mmc;
+       module_method_call_t            mmc_tmp;
+       module_method_binding_t const   *mmb;
+
+       fr_sbuff_marker_t               meth_start;
+
+       fr_slen_t                       slen;
+       fr_sbuff_t                      our_name = FR_SBUFF(name);
+
+       mmc = mmc_out ? mmc_out : &mmc_tmp;
+       if (mmc_out) memset(mmc_out, 0, sizeof(*mmc_out));
 
        /*
-        *      No such module, we're done.
+        *      Advance until the start of the dynamic selector
+        *      (if it exists).
         */
-       if (!mi) {
-               fr_strerror_printf("Failed to find module '%s'", inst_name);
-               talloc_free(inst_name);
-               return NULL;
+       if (fr_sbuff_adv_until(&our_name, SIZE_MAX, dyn_tt, '\0') == 0) {
+               fr_strerror_printf("Invalid module method name");
+               return fr_sbuff_error(&our_name);
        }
 
-       mrlm = module_rlm_from_module(mi->exported);
+       FR_SBUFF_TALLOC_THREAD_LOCAL(&elem1, MODULE_INSTANCE_LEN_MAX, (MODULE_INSTANCE_LEN_MAX + 1) * 10);
 
        /*
-        *      We have a module, but the caller doesn't care about
-        *      method or names, so just return the module.
+        *      If the method string contains a '['
+        *
+        *      Search for a dynamic module method, e.g. `elem1[<key>]`.
         */
-       if (!method || !method_name1 || !method_name2) goto finish;
+       if (fr_sbuff_is_char(&our_name, '[')) {
+               fr_sbuff_marker_t end, s_end;
+               fr_sbuff_marker(&end, &our_name);
+
+               slen = tmpl_afrom_substr(ctx, &mmc->key, &our_name, T_BARE_WORD, NULL, t_rules);
+               if (slen < 0) {
+                       fr_strerror_const_push("Invalid dynamic module selector expression");
+                       talloc_free(mmc);
+                       return slen;
+               }
 
-       /*
-        *      We MAY have two names.
-        */
-       p++;
-       q = strchr(p, '.');
-       /*
-        *      We've found the module, but it has no named methods.
-        */
-       if (!mrlm->bindings) {
-               *name1 = name + (p - inst_name);
-               *name2 = NULL;
-               goto finish;
-       }
+               if (!fr_sbuff_is_char(&our_name, ']')) {
+                       fr_strerror_const_push("Missing terminating ']' for dynamic module selector");
+               error:
+                       talloc_free(mmc);
+                       return fr_sbuff_error(&our_name);
+               }
+               fr_sbuff_marker(&s_end, &our_name);
 
+               fr_sbuff_set_to_start(&our_name);
+               slen = fr_sbuff_out_bstrncpy(elem1, &our_name, fr_sbuff_ahead(&end));
+               if (slen < 0) {
+                       fr_strerror_const("Module method string too long");
+                       goto error;
+               }
+               mmc->mi = module_instance_by_name(rlm_modules_dynamic, NULL, elem1->start);
+               if (!mmc->mi) {
+                       fr_strerror_printf("No such dynamic module '%s'", elem1->start);
+                       goto error;
+               }
+               mmc->rlm = module_rlm_from_module(mmc->mi->exported);
+
+               fr_sbuff_set(&our_name, &s_end);
+               fr_sbuff_advance(&our_name, 1); /* Skip the ']' */
        /*
-        *      We have "module.METHOD", but METHOD doesn't match
-        *      "authorize", "authenticate", etc.  Let's see if it
-        *      matches anything else.
+        *      With elem1.elem2.elem3
+        *
+        *      Search for a static module matching one of the following:
+        *
+        *      - elem1.elem2.elem3
+        *      - elem1.elem2
+        *      - elem1
         */
-       if (!q) {
-               for (j = 0; mrlm->bindings[j].section; j++) {
-                       methods = &mrlm->bindings[j];
-
-                       /*
-                        *      If we do not have the second $method, then ignore it!
-                        */
-                       if (methods->section->name2 && (methods->section->name2 != CF_IDENT_ANY)) continue;
-
-                       /*
-                        *      Wildcard match name1, we're
-                        *      done.
-                        */
-                       if (!methods->section->name1 || (methods->section->name1 == CF_IDENT_ANY)) goto found_name1;
+       } else {
+               char *p;
 
-                       /*
-                        *      If name1 doesn't match, skip it.
-                        */
-                       if (strcasecmp(methods->section->name1, p) != 0) continue;
+               fr_sbuff_set_to_start(&our_name);
 
-               found_name1:
-                       /*
-                        *      We've matched "*", or "name1" or
-                        *      "name1 *".  Return that.
-                        */
-                       *name1 = name + (p - inst_name);
-                       *name2 = NULL;
-                       *method = methods->method;
-                       if (method_env) *method_env = methods->method_env;
-                       break;
+               slen = fr_sbuff_out_bstrncpy_until(elem1, &our_name, SIZE_MAX, dyn_tt, NULL);
+               if (slen == 0) {
+                       fr_strerror_const("Invalid module name");
+                       goto error;
+               }
+               if (slen < 0) {
+                       fr_strerror_const("Module method string too long");
+                       goto error;
                }
 
                /*
-                *      Return the found module.
+                *      Now we have a mutable buffer, we can start chopping
+                *      it up to find the module.
                 */
-               goto finish;
-       }
+               for (;;) {
+                       mmc->mi = (module_instance_t *)module_rlm_static_by_name(NULL, elem1->start);
+                       if (mmc->mi) {
+                               mmc->rlm = module_rlm_from_module(mmc->mi->exported);
+                               break;  /* Done */
+                       }
 
-       /*
-        *      We CANNOT have '.' in method names.
-        */
-       if (strchr(q + 1, '.') != 0) goto finish;
+                       p = strrchr(elem1->start, '.');
+                       if (!p) break;  /* No more '.' */
+                       *p = '\0';      /* Chop off the last '.' */
+               }
+
+               if (!mmc->mi) {
+                       fr_strerror_printf("No such module '%pV'", fr_box_strvalue_len(our_name.start, slen));
+                       return -1;
+               }
 
-       len = q - p;
+               fr_sbuff_set_to_start(&our_name);
+               fr_sbuff_advance(&our_name, strlen(elem1->start));      /* Advance past the module name */
+               if (fr_sbuff_is_char(&our_name, '.')) {
+                       fr_sbuff_advance(&our_name, 1);                 /* Static module method, search directly */
+               } else {
+                       fr_sbuff_marker(&meth_start, &our_name);        /* for the errors... */
+                       goto by_section;                                /* Get the method dynamically from the section*/
+               }
+       }
 
        /*
-        *      Trim the '.'.
+        *      For both cases, the buffer should be pointing
+        *      at the start of the method string.
         */
-       if (*q == '.' && *(q + 1)) q++;
+       fr_sbuff_marker(&meth_start, &our_name);
 
        /*
-        *      We have "module.METHOD1.METHOD2".
+        *      If a module method was provided, search for it in the named
+        *      methods provided by the module.
+        *
+        *      The method name should be either:
         *
-        *      Loop over the method names, seeing if we have a match.
+        *      - name1
+        *      - name1.name2
         */
-       for (j = 0; mrlm->bindings[j].section; j++) {
-               methods = &mrlm->bindings[j];
+       {
+               section_name_t  method;
+               fr_sbuff_t      *elem2;
 
-               /*
-                *      If name1 doesn't match, skip it.
-                */
-               if (strncasecmp(methods->section->name1, p, len) != 0) continue;
+               fr_sbuff_set_to_start(elem1);   /* May have used this already for module lookups */
 
-               /*
-                *      It may have been a partial match, like "rec",
-                *      instead of "recv".  In which case check if it
-                *      was a FULL match.
-                */
-               if (strlen(methods->section->name1) != len) continue;
+               slen = fr_sbuff_out_bstrncpy_until(elem1, &our_name, SIZE_MAX, elem_tt, NULL);
+               if (slen < 0) {
+                       fr_strerror_const("Module method string too long");
+                       return fr_sbuff_error(&our_name);
+               }
+               if (slen == 0) goto by_section; /* This works for both dynamic and static modules */
 
-               /*
-                *      The module can declare a
-                *      wildcard for name2, in which
-                *      case it's a match.
-                */
-               if (!methods->section->name2 || (methods->section->name2 == CF_IDENT_ANY)) goto found_name2;
+               FR_SBUFF_TALLOC_THREAD_LOCAL(&elem2, MODULE_INSTANCE_LEN_MAX, MODULE_INSTANCE_LEN_MAX);
 
-               /*
-                *      Don't do strcmp on NULLs
-                */
-               if (!methods->section->name2) continue;
+               if (fr_sbuff_is_char(&our_name, '.')) {
+                       fr_sbuff_advance(&our_name, 1);
+                       if (fr_sbuff_out_bstrncpy_until(elem2, &our_name, SIZE_MAX, elem_tt, NULL) < 0) {
+                               fr_strerror_const("Module method string too long");
+                               goto error;
+                       }
+               }
 
-               if (strcasecmp(methods->section->name2, q) != 0) continue;
+               method = (section_name_t) {
+                       .name1 = elem1->start,
+                       .name2 = fr_sbuff_used(elem2) ? elem2->start : NULL
+               };
+
+               mmb = module_binding_find(mmc->rlm->bindings, &method);
+               if (!mmb) {
+                       fr_strerror_printf("Module \"%s\" does not have method %s%s%s",
+                                          mmc->mi->name,
+                                          method.name1,
+                                          method.name2 ? "." : "",
+                                          method.name2 ? method.name2 : ""
+                                          );
+
+                       module_rlm_methods_to_strerror(mmc->rlm->bindings);
+                       return fr_sbuff_error(&meth_start);
+               }
+               mmc->mmb = *mmb;        /* For locality of reference and fewer derefs */
+               if (mmc_out) section_name_dup(ctx, &mmc->asked, &method);
 
-       found_name2:
-               /*
-                *      Update name1/name2 with the methods
-                *      that were found.
-                */
-               *name1 = methods->section->name1;
-               *name2 = name + (q - inst_name);
-               *method = methods->method;
-               if (method_env) *method_env = methods->method_env;
-               goto finish;
+               return fr_sbuff_set(name, &our_name);
        }
 
-       *name1 = name + (p - inst_name);
-       *name2 = NULL;
+by_section:
+       /*
+        *      First look for the section name in the module's
+        *      bindings.  If that fails, look for the alt
+        *      section names from the virtual server section.
+        *
+        *      If that fails, we're done.
+        */
+       mmb = module_binding_find(mmc->rlm->bindings, section);
+       if (!mmb) {
+               section_name_t const **alt_p = virtual_server_section_methods(vs, section);
+               if (alt_p) {
+                       for (; *alt_p; alt_p++) {
+                               mmb = module_binding_find(mmc->rlm->bindings, *alt_p);
+                               if (mmb) {
+                                       if (mmc_out) section_name_dup(ctx, &mmc->asked, *alt_p);
+                                       break;
+                               }
+                       }
+               }
+       } else {
+               if (mmc_out) section_name_dup(ctx, &mmc->asked, section);
+       }
+       if (!mmb) {
+               fr_strerror_printf("Module \"%s\" has no method for section %s %s { ... }, i.e. %s%s%s",
+                                  mmc->mi->name,
+                                  section->name1,
+                                  section->name2 ? section->name2 : "",
+                                  section->name1,
+                                  section->name2 ? "." : "",
+                                  section->name2 ? section->name2 : ""
+                                  );
+               module_rlm_methods_to_strerror(mmc->rlm->bindings);
+
+               return fr_sbuff_error(&meth_start);
+       }
+       mmc->mmb = *mmb;        /* For locality of reference and fewer derefs */
 
-finish:
-       talloc_free(inst_name);
-       return mi;
+       return fr_sbuff_set(name, &our_name);
 }
 
-CONF_SECTION *module_rlm_by_name_virtual(char const *asked_name)
+CONF_SECTION *module_rlm_virtual_by_name(char const *asked_name)
 {
        module_rlm_virtual_t *inst;
 
@@ -793,6 +826,11 @@ CONF_SECTION *module_rlm_by_name_virtual(char const *asked_name)
        return inst->cs;
 }
 
+module_instance_t *module_rlm_dynamic_by_name(module_instance_t const *parent, char const *asked_name)
+{
+       return module_instance_by_name(rlm_modules_dynamic, parent, asked_name);
+}
+
 module_instance_t *module_rlm_static_by_name(module_instance_t const *parent, char const *asked_name)
 {
        return module_instance_by_name(rlm_modules_static, parent, asked_name);
@@ -809,7 +847,6 @@ static int module_rlm_bootstrap_virtual(CONF_SECTION *cs)
 {
        char const              *name;
        bool                    all_same;
-       module_t const          *last = NULL;
        CONF_ITEM               *sub_ci = NULL;
        CONF_PAIR               *cp;
        module_instance_t       *mi;
@@ -858,50 +895,45 @@ static int module_rlm_bootstrap_virtual(CONF_SECTION *cs)
         */
        all_same = (strcmp(cf_section_name1(cs), "group") != 0);
 
-       /*
-        *      Ensure that the modules we reference here exist.
-        */
-       while ((sub_ci = cf_item_next(cs, sub_ci))) {
-               if (cf_item_is_pair(sub_ci)) {
-                       cp = cf_item_to_pair(sub_ci);
-                       if (cf_pair_value(cp)) {
-                               cf_log_err(sub_ci, "Cannot set return codes in a %s block", cf_section_name1(cs));
-                               return -1;
-                       }
+       {
+               module_t const          *last = NULL;
 
-                       /*
-                        *      Allow "foo.authorize" in subsections.
-                        *
-                        *      Note that we don't care what the method is, just that it exists.
-                        *
-                        *      This check is needed only because we
-                        *      want to know if we need to register a
-                        *      redundant xlat for the virtual module.
-                        */
-                       mi = module_rlm_by_name_and_method(NULL, NULL, NULL, NULL, NULL, cf_pair_attr(cp));
-                       if (!mi) {
-                               cf_log_err(sub_ci, "Module instance \"%s\" referenced in %s block, does not exist",
-                                          cf_pair_attr(cp), cf_section_name1(cs));
-                               return -1;
-                       }
+               /*
+               *       Ensure that the modules we reference here exist.
+               */
+               while ((sub_ci = cf_item_next(cs, sub_ci))) {
+                       if (cf_item_is_pair(sub_ci)) {
+                               cp = cf_item_to_pair(sub_ci);
+                               if (cf_pair_value(cp)) {
+                                       cf_log_err(sub_ci, "Cannot set return codes in a %s block", cf_section_name1(cs));
+                                       return -1;
+                               }
+
+                               mi = module_rlm_static_by_name(NULL, cf_pair_attr(cp));
+                               if (!mi) {
+                                       cf_log_perr(sub_ci, "Failed resolving module reference '%s' in %s block",
+                                                   cf_pair_attr(cp), cf_section_name1(cs));
+                                       return -1;
+                               }
 
-                       if (all_same) {
-                               if (!last) {
-                                       last = mi->exported;
-                               } else if (last != mi->exported) {
-                                       last = NULL;
-                                       all_same = false;
+                               if (all_same) {
+                                       if (!last) {
+                                               last = mi->exported;
+                                       } else if (last != mi->exported) {
+                                               last = NULL;
+                                               all_same = false;
+                                       }
                                }
+                       } else {
+                               all_same = false;
                        }
-               } else {
-                       all_same = false;
-               }
 
-               /*
-                *      Don't check subsections for now.  That check
-                *      happens later in the unlang compiler.
-                */
-       } /* loop over things in a virtual module section */
+                       /*
+                       *       Don't check subsections for now.  That check
+                       *       happens later in the unlang compiler.
+                       */
+               } /* loop over things in a virtual module section */
+       }
 
        inst = talloc_zero(cs, module_rlm_virtual_t);
        if (!inst) return -1;
@@ -1095,6 +1127,16 @@ static int module_method_validate(module_instance_t *mi)
        return 0;
 }
 
+/** Compare xlat functions registered to a module
+ */
+static int8_t module_rlm_xlat_cmp(void const *one, void const *two)
+{
+       module_rlm_xlat_t const *a = one;
+       module_rlm_xlat_t const *b = two;
+
+       return xlat_func_cmp(a->xlat, b->xlat);
+}
+
 /** Allocate a rlm module instance
  *
  * These have extra space allocated to hold the dlist of associated xlats.
@@ -1123,7 +1165,7 @@ module_instance_t *module_rlm_instance_alloc(module_list_t *ml,
 
        MEM(mri = talloc(mi, module_rlm_instance_t));
        module_instance_uctx_set(mi, mri);
-       fr_rb_inline_init(&mri->xlats, module_rlm_xlat_t, node, xlat_func_cmp, NULL);
+       fr_rb_inline_init(&mri->xlats, module_rlm_xlat_t, node, module_rlm_xlat_cmp, NULL);
 
        return mi;
 }
index 75b0e95cb3baa64986b141794f6e45a68480133a..09d281103bbeef90311a45b6a06906a794c60f81 100644 (file)
@@ -63,6 +63,9 @@ typedef struct {
                                                                ///< onto the stack for execution.  So we need
                                                                ///< to use the common type here.
        module_rlm_t const              *rlm;                   //!< Cached module_rlm_t.
+       section_name_t                  asked;                  //!< The actual <name1>.<name2> used for the module call.
+                                                               ///< This was either the override the user specified,
+                                                               ///< or the name of the section.
        module_method_binding_t         mmb;                    //!< Method we're calling.
        tmpl_t                          *key;                   //!< Dynamic key, only set for dynamic modules.
 } module_method_call_t;
@@ -122,13 +125,12 @@ bool              module_rlm_section_type_set(request_t *request, fr_dict_attr_t const *type
  *
  * @{
  */
-module_instance_t      *module_rlm_by_name_and_method(module_method_t *method, call_env_method_t const ** method_env,
-                                                      char const **name1, char const **name2,
-                                                      virtual_server_t const *vs, char const *asked_name);
+fr_slen_t              module_rlm_by_name_and_method(TALLOC_CTX *ctx, module_method_call_t *mmc_out,
+                                                     virtual_server_t const *vs, section_name_t const *section, fr_sbuff_t *name,
+                                                     tmpl_rules_t const *t_rules) CC_HINT(nonnull(5));
 
 module_instance_t      *module_rlm_dynamic_by_name(module_instance_t const *parent, char const *name);
 
-CONF_SECTION           *module_rlm_by_name_virtual(char const *asked_name);
 module_instance_t      *module_rlm_static_by_name(module_instance_t const *parent, char const *name);
 
 CONF_SECTION           *module_rlm_virtual_by_name(char const *name);
index ac10afd2b0632f8b9b29d962ac901975a0746d04..7c8c1ae778cd16c3a88080d07a96f37d197ea5d1 100644 (file)
@@ -91,6 +91,26 @@ name2:
        return section_name2_match(a, b);
 }
 
+/** Return a printable string for the section name
+ *
+ * @param[in] name             Section name.
+ */
+static inline char const *section_name_str(char const *name)
+{
+       if (name == NULL) return "NULL";
+       if (name == CF_IDENT_ANY) return "*";
+       return name;
+}
+
+static inline void section_name_dup(TALLOC_CTX *ctx, section_name_t *dst, section_name_t const *src)
+{
+       dst->name1 = src->name1;
+       dst->name2 = src->name2;
+
+       if (dst->name1 && (dst->name1 != CF_IDENT_ANY)) dst->name1 = talloc_typed_strdup(ctx, src->name1);
+       if (dst->name2 && (dst->name2 != CF_IDENT_ANY)) dst->name2 = talloc_typed_strdup(ctx, src->name2);
+}
+
 int8_t section_name_cmp(void const *one, void const *two);
 
 #ifdef __cplusplus
index d51403d093dbd5735c8a50d4dffc19fd30cb5830..708a7ef1a70912d1141ff1ddcbadac9140eed5dc 100644 (file)
@@ -24,8 +24,6 @@
  * @copyright 2000 Alan DeKok (aland@freeradius.org)
  * @copyright 2000 Alan Curry (pacman@world.std.com)
  */
-
-
 RCSID("$Id$")
 
 #include <freeradius-devel/protocol/freeradius/freeradius.internal.h>
@@ -38,6 +36,7 @@ RCSID("$Id$")
 #include <freeradius-devel/server/modpriv.h>
 #include <freeradius-devel/server/process.h>
 #include <freeradius-devel/server/protocol.h>
+#include <freeradius-devel/server/section.h>
 #include <freeradius-devel/server/virtual_servers.h>
 
 #include <freeradius-devel/unlang/compile.h>
@@ -110,8 +109,6 @@ static CONF_SECTION *virtual_server_root;
 
 static fr_rb_tree_t *listen_addr_root = NULL;
 
-static int8_t server_section_name_cmp(void const *one, void const *two);
-
 static int namespace_on_read(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, conf_parser_t const *rule);
 static int server_on_read(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, UNUSED conf_parser_t const *rule);
 
@@ -498,6 +495,14 @@ static int listen_parse(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent,
        return 0;
 }
 
+static int8_t virtual_server_compile_name_cmp(void const *a, void const *b)
+{
+       virtual_server_compile_t const *sa = a;
+       virtual_server_compile_t const *sb = b;
+
+       return section_name_cmp(sa->section, sb->section);
+}
+
 /** Callback to validate the server section
  *
  * @param[in] ctx      to allocate data in.
@@ -523,7 +528,7 @@ static int server_parse(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent,
                return -1;
        }
 
-       MEM(server->sections = fr_rb_alloc(server, server_section_name_cmp, NULL));
+       MEM(server->sections = fr_rb_alloc(server, virtual_server_compile_name_cmp, NULL));
        server->server_cs = server_cs;
 
        /*
@@ -1010,24 +1015,6 @@ int virtual_server_compile_sections(virtual_server_t const *vs, tmpl_rules_t con
        return found;
 }
 
-static int8_t server_section_name_cmp(void const *one, void const *two)
-{
-       virtual_server_compile_t const *a = one;
-       virtual_server_compile_t const *b = two;
-       int ret;
-
-       ret = strcmp(a->section->name1, b->section->name1);
-       ret = CMP(ret, 0);
-       if (ret != 0) return ret;
-
-       if (a->section->name2 == b->section->name2) return 0;
-       if ((a->section->name2 == CF_IDENT_ANY) && (b->section->name2 != CF_IDENT_ANY)) return -1;
-       if ((a->section->name2 != CF_IDENT_ANY) && (b->section->name2 == CF_IDENT_ANY)) return +1;
-
-       ret = strcmp(a->section->name2, b->section->name2);
-       return CMP(ret, 0);
-}
-
 /** Register name1 / name2 as allowed processing sections
  *
  *  This function is called from the virtual server bootstrap routine,
@@ -1078,7 +1065,7 @@ int virtual_server_section_register(virtual_server_t *vs, virtual_server_compile
 /** Find the component for a section
  *
  */
-section_name_t const **virtual_server_section_methods(virtual_server_t const *vs, char const *name1, char const *name2)
+section_name_t const **virtual_server_section_methods(virtual_server_t const *vs, section_name_t const *section)
 {
        virtual_server_compile_t *entry;
 
@@ -1086,10 +1073,10 @@ section_name_t const **virtual_server_section_methods(virtual_server_t const *vs
         *      Look up the specific name first.  That way we can
         *      define both "accounting on", and "accounting *".
         */
-       if (name2 != CF_IDENT_ANY) {
+       if (section->name2 != CF_IDENT_ANY) {
                entry = fr_rb_find(vs->sections,
                                   &(virtual_server_compile_t) {
-                                       .section = SECTION_NAME(name1, name2)
+                                       .section = section
                                   });
                if (entry) return entry->methods;
        }
@@ -1099,7 +1086,7 @@ section_name_t const **virtual_server_section_methods(virtual_server_t const *vs
         */
        entry = fr_rb_find(vs->sections,
                           &(virtual_server_compile_t) {
-                               .section = SECTION_NAME(name1, CF_IDENT_ANY)
+                               .section = SECTION_NAME(section->name1, CF_IDENT_ANY)
                           });
        if (!entry) return NULL;
 
index 7c9d570c6bcad747e5afce1a84068f5581b3eb5d..224c9a9c8efa06bbdfebb5a80042c78612ccecc2 100644 (file)
@@ -112,9 +112,9 @@ typedef struct {
 
 int            virtual_server_section_register(virtual_server_t *vs, virtual_server_compile_t const *entry) CC_HINT(nonnull);
 
-int            virtual_server_compile_sections(virtual_server_t const *vs, tmpl_rules_t const *rules) CC_HINT(nonnull);
+section_name_t const **virtual_server_section_methods(virtual_server_t const *vs, section_name_t const *section) CC_HINT(nonnull);
 
-section_name_t const **virtual_server_section_methods(virtual_server_t const *vs, char const *name1, char const *name2) CC_HINT(nonnull(1));
+int            virtual_server_compile_sections(virtual_server_t const *vs, tmpl_rules_t const *rules) CC_HINT(nonnull);
 
 unlang_action_t        virtual_server_push(request_t *request, CONF_SECTION *server_cs, bool top_frame) CC_HINT(nonnull);
 
index dd133e4e4fc0ee397918678950e22a89519121d6..4cc75f4c0b49d84874672b27697e0a526fbf511a 100644 (file)
@@ -29,6 +29,7 @@ RCSID("$Id$")
 #include <freeradius-devel/server/cf_util.h>
 #include <freeradius-devel/server/tmpl.h>
 #include <freeradius-devel/server/request.h>
+#include <freeradius-devel/server/section.h>
 #include <freeradius-devel/unlang/tmpl.h>
 #include <freeradius-devel/unlang/function.h>
 #include <freeradius-devel/unlang/interpret.h>
@@ -53,6 +54,12 @@ struct call_env_parsed_s {
 };
 FR_DLIST_FUNCS(call_env_parsed, call_env_parsed_t, entry)
 
+#if defined(DEBUG_CALL_ENV)
+#  define CALL_ENV_DEBUG(_ci, fmt, ...) cf_log_debug(_ci, fmt, ##__VA_ARGS__)
+#else
+#  define CALL_ENV_DEBUG(_ci, ...)
+#endif
+
 /** Parse the result of call_env tmpl expansion
  */
 static inline CC_HINT(always_inline)
@@ -358,15 +365,14 @@ int call_env_parsed_valid(call_env_parsed_t const *parsed, CONF_ITEM const *ci,
  * @param[out] out             Where to write the result of parsing.
  * @param[in] t_rules          we're parsing attributes with.  Contains the default dictionary and nested 'caller' tmpl_rules_t.
  * @param[in] ci               The #CONF_SECTION or #CONF_PAIR to parse.
- * @param[in] data             module / xlat instance data of the module / xlat allocating this call_env
+ * @param[in] cec              information about the call.
  * @param[in] rule             Parse rules - How the #CONF_PAIR or #CONF_SECTION should be converted.
  * @return
  *     - 0 on success.
  *     - -1 on failure.
  */
 int call_env_parse_pair(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci,
-                       UNUSED char const *section_name1, UNUSED char const *section_name2,
-                       UNUSED void const *data, UNUSED call_env_parser_t const *rule)
+                       UNUSED call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
 {
        CONF_PAIR const *to_parse = cf_item_to_pair(ci);
        tmpl_t          *parsed_tmpl;
@@ -394,48 +400,53 @@ int call_env_parse_pair(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules,
  * @param[in] name             Module name for error messages.
  * @param[in] t_rules          controlling how the call env is parsed.
  * @param[in] cs               Module config.
- * @param[in] section_name1    Name 1 from calling section for module calls
- * @param[in] section_name2    Name 2 from calling section for module calls
- * @param[in] data             module / xlat instance data of the module / xlat allocating this call_env
+ * @param[in] cec              information about the call.
  * @param[in] rule             to parse.
  * @return
  *     - 0 on success;
  *     - <0 on failure;
  */
 static int call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *parsed, char const *name, tmpl_rules_t const *t_rules,
-                         CONF_SECTION const *cs, char const *section_name1, char const *section_name2,
-                         void const *data, call_env_parser_t const *rule) {
+                         CONF_SECTION const *cs,
+                         call_env_ctx_t const *cec, call_env_parser_t const *rule) {
        CONF_PAIR const         *cp, *next;
        call_env_parsed_t       *call_env_parsed = NULL;
        ssize_t                 count, multi_index;
+       call_env_parser_t const *rule_p = rule;
+
+       while (rule_p->name) {
+               CALL_ENV_DEBUG(cs, "%s: Parsing call env data for %s", name, section_name_str(rule_p->name));
 
-       while (rule->name) {
-               if (call_env_is_subsection(rule->flags)) {
+               if (call_env_is_subsection(rule_p->flags)) {
                        CONF_SECTION const *subcs;
-                       subcs = cf_section_find(cs, rule->name, rule->section.ident2);
-                       if (!subcs && !call_env_parse_missing(rule->flags)) {
-                               if (!call_env_required(rule->flags)) goto next;
-                               cf_log_err(cs, "Module %s missing required section \"%s\"", name, rule->name);
+                       subcs = cf_section_find(cs, rule_p->name, rule_p->section.name2);
+                       if (!subcs && !call_env_parse_missing(rule_p->flags)) {
+                               if (!call_env_required(rule_p->flags)) goto next;
+                               cf_log_err(cs, "Module %s missing required section \"%s\"", name, rule_p->name);
                                return -1;
                        }
 
                        /*
                         *      Hand off to custom parsing function if there is one...
                         */
-                       if (rule->section.func) {
+                       if (rule_p->section.func) {
                                /*
                                 *      Record our position so we can process any new entries
                                 *      after the callback returns.
                                 */
                                call_env_parsed_t *last = call_env_parsed_tail(parsed);
 
-                               if (rule->section.func(ctx, parsed, t_rules, cf_section_to_item(subcs), section_name1,
-                                                      section_name2, data, rule) < 0) {
-                                       cf_log_perr(cs, "Failed parsing configuration section %s", rule->name);
+                               CALL_ENV_DEBUG(cs, "%s: Calling subsection callback %p", name, rule_p->section.func);
+
+                               if (rule_p->section.func(ctx, parsed, t_rules, cf_section_to_item(subcs), cec, rule_p) < 0) {
+                                       cf_log_perr(cs, "Failed parsing configuration section %s", rule_p->name);
                                        talloc_free(call_env_parsed);
                                        return -1;
                                }
 
+                               CALL_ENV_DEBUG(subcs, "%s: Callback returned %u parsed call envs", name,
+                                              call_env_parsed_num_elements(parsed));
+
                                /*
                                 *      We _could_ fix up count and multi_index on behalf of
                                 *      the callback, but there's no guarantee that all call_env_parsed_t
@@ -443,25 +454,28 @@ static int call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *parsed, char
                                 */
                                call_env_parsed = last;
                                while ((call_env_parsed = call_env_parsed_next(parsed, call_env_parsed))) {
-                                       if (call_env_parsed_valid(call_env_parsed, cf_section_to_item(subcs), rule) < 0) {
-                                               cf_log_err(cf_section_to_item(subcs), "Invalid data produced by %s", rule->name);
+                                       CALL_ENV_DEBUG(subcs, "%s: Checking parsed env", name, rule_p->section.func);
+                                       if (call_env_parsed_valid(call_env_parsed, cf_section_to_item(subcs), rule_p) < 0) {
+                                               cf_log_err(cf_section_to_item(subcs), "Invalid data produced by %s", rule_p->name);
                                                return -1;
                                        }
                                }
                                goto next;
                        }
 
-                       if (call_env_parse(ctx, parsed, name, t_rules, subcs, section_name1, section_name2,
-                                          data, rule->section.subcs) < 0) return -1;
+                       if (call_env_parse(ctx, parsed, name, t_rules, subcs, cec, rule_p->section.subcs) < 0) {
+                               CALL_ENV_DEBUG(cs, "%s: Recursive call failed", name, rule_p->name);
+                               return -1;
+                       }
                        goto next;
                }
 
-               cp = cf_pair_find(cs, rule->name);
+               cp = cf_pair_find(cs, rule_p->name);
 
-               if (!cp && !rule->pair.dflt) {
-                       if (!call_env_required(rule->flags)) goto next;
+               if (!cp && !rule_p->pair.dflt) {
+                       if (!call_env_required(rule_p->flags)) goto next;
 
-                       cf_log_err(cs, "Missing required config item '%s'", rule->name);
+                       cf_log_err(cs, "Missing required config item '%s'", rule_p->name);
                        return -1;
                }
 
@@ -469,32 +483,32 @@ static int call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *parsed, char
                 *      Check for additional conf pairs and error
                 *      if there is one and multi is not allowed.
                 */
-               if (!call_env_multi(rule->flags) && ((next = cf_pair_find_next(cs, cp, rule->name)))) {
-                       cf_log_err(cf_pair_to_item(next), "Invalid duplicate configuration item '%s'", rule->name);
+               if (!call_env_multi(rule_p->flags) && ((next = cf_pair_find_next(cs, cp, rule_p->name)))) {
+                       cf_log_err(cf_pair_to_item(next), "Invalid duplicate configuration item '%s'", rule_p->name);
                        return -1;
                }
 
-               count = cf_pair_count(cs, rule->name);
+               count = cf_pair_count(cs, rule_p->name);
                if (count == 0) count = 1;
 
                for (multi_index = 0; multi_index < count; multi_index++) {
                        CONF_PAIR               *tmp_cp = NULL;
                        CONF_PAIR const         *to_parse;
                        tmpl_rules_t            our_rules = {};
-                       fr_type_t               type = rule->pair.cast_type;
-                       call_env_parse_pair_t   func = rule->pair.func ? rule->pair.func : call_env_parse_pair;
+                       fr_type_t               type = rule_p->pair.cast_type;
+                       call_env_parse_pair_t   func = rule_p->pair.func ? rule_p->pair.func : call_env_parse_pair;
 
                        if (t_rules) {
                                our_rules.parent = t_rules->parent;
                                our_rules.attr.dict_def = t_rules->attr.dict_def;
-                               our_rules.escape = rule->pair.escape;   /* Escape rules will now get embedded in the tmpl_t and used at evaluation */
+                               our_rules.escape = rule_p->pair.escape; /* Escape rules will now get embedded in the tmpl_t and used at evaluation */
                        }
 
                        our_rules.attr.list_def = request_attr_request;
                        our_rules.cast = ((type == FR_TYPE_VOID) ? FR_TYPE_NULL : type);
-                       our_rules.literals_safe_for = rule->pair.literals_safe_for;
+                       our_rules.literals_safe_for = rule_p->pair.literals_safe_for;
 
-                       call_env_parsed = call_env_parsed_alloc(ctx, rule);
+                       call_env_parsed = call_env_parsed_alloc(ctx, rule_p);
                        call_env_parsed->count = count;
                        call_env_parsed->multi_index = multi_index;
 
@@ -505,18 +519,18 @@ static int call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *parsed, char
                         *      we can't do that here.
                         */
                        if (cp) {
-                               if (call_env_force_quote(rule->flags)) {
+                               if (call_env_force_quote(rule_p->flags)) {
                                        to_parse = tmp_cp = cf_pair_alloc(NULL,
                                                                          cf_pair_attr(cp), cf_pair_value(cp), cf_pair_operator(cp),
                                                                          cf_pair_attr_quote(cp),
-                                                                         call_env_force_quote(rule->flags) ? rule->pair.dflt_quote : cf_pair_value_quote(cp));
+                                                                         call_env_force_quote(rule_p->flags) ? rule_p->pair.dflt_quote : cf_pair_value_quote(cp));
                                } else {
                                        to_parse = cp;
                                }
                        } else {
                                to_parse = tmp_cp = cf_pair_alloc(NULL,
-                                                                 rule->name, rule->pair.dflt, T_OP_EQ,
-                                                                 T_BARE_WORD, rule->pair.dflt_quote);
+                                                                 rule_p->name, rule_p->pair.dflt, T_OP_EQ,
+                                                                 T_BARE_WORD, rule_p->pair.dflt_quote);
                        }
 
                        /*
@@ -524,9 +538,9 @@ static int call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *parsed, char
                         *      would, or produce a custom structure, which will be copied into the
                         *      result structure.
                         */
-                       if (unlikely(func(ctx, &call_env_parsed->data, &our_rules, cf_pair_to_item(to_parse), section_name1, section_name2, data, rule) < 0)) {
+                       if (unlikely(func(ctx, &call_env_parsed->data, &our_rules, cf_pair_to_item(to_parse), cec, rule_p) < 0)) {
                        error:
-                               cf_log_perr(to_parse, "Failed to parse configuration item '%s = %s'", rule->name, cf_pair_value(to_parse));
+                               cf_log_perr(to_parse, "Failed to parse configuration item '%s = %s'", rule_p->name, cf_pair_value(to_parse));
                                talloc_free(call_env_parsed);
                                talloc_free(tmp_cp);
                                return -1;
@@ -539,17 +553,19 @@ static int call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *parsed, char
                        /*
                         *      Ensure only valid data is produced.
                         */
-                       if (call_env_parsed_valid(call_env_parsed, cf_pair_to_item(to_parse), rule) < 0) goto error;
+                       if (call_env_parsed_valid(call_env_parsed, cf_pair_to_item(to_parse), rule_p) < 0) goto error;
 
                        call_env_parsed_insert_tail(parsed, call_env_parsed);
                next_pair:
                        talloc_free(tmp_cp);
-                       cp = cf_pair_find_next(cs, cp, rule->name);
+                       cp = cf_pair_find_next(cs, cp, rule_p->name);
                }
        next:
-               rule++;
+               rule_p++;
        }
 
+       CALL_ENV_DEBUG(cs, "Returning afer processing %u rules", (unsigned int)(rule_p - rule));
+
        return 0;
 }
 
@@ -572,7 +588,7 @@ static size_t call_env_count(size_t *names_len, CONF_SECTION const *cs, call_env
        while (call_env->name) {
                if (call_env_is_subsection(call_env->flags)) {
                        CONF_SECTION const *subcs;
-                       subcs = cf_section_find(cs, call_env->name, call_env->section.ident2);
+                       subcs = cf_section_find(cs, call_env->name, call_env->section.name2);
                        if (!subcs) goto next;
 
                        /*
@@ -710,14 +726,13 @@ void call_env_parsed_free(call_env_parsed_head_t *parsed, call_env_parsed_t *ptr
  * @param[in] call_env_method  containing the call_env_pair_t to evaluate against the specified CONF_SECTION.
  * @param[in] t_rules          that control how call_env_pair_t are parsed.
  * @param[in] cs               to parse in the context of the call.
- * @param[in] data             module / xlat instance data of the module / xlat allocating this call_env
+ * @param[in] cec              information about how the call is being made.
  * @return
  *     - A new call_env_t on success.
  *     - NULL on failure.
  */
 call_env_t *call_env_alloc(TALLOC_CTX *ctx, char const *name, call_env_method_t const *call_env_method,
-                          tmpl_rules_t const *t_rules, CONF_SECTION *cs, char const *section_name1,
-                          char const *section_name2, void const *data)
+                          tmpl_rules_t const *t_rules, CONF_SECTION *cs, call_env_ctx_t const *cec)
 {
        unsigned int    count;
        size_t          names_len;
@@ -747,8 +762,7 @@ call_env_t *call_env_alloc(TALLOC_CTX *ctx, char const *name, call_env_method_t
        MEM(call_env = talloc_pooled_object(ctx, call_env_t, count * 4, (sizeof(call_env_parser_t) + sizeof(tmpl_t)) * count + names_len * 2));
        call_env->method = call_env_method;
        call_env_parsed_init(&call_env->parsed);
-       if (call_env_parse(call_env, &call_env->parsed, name, t_rules, cs, section_name1, section_name2,
-                          data, call_env_method->env) < 0) {
+       if (call_env_parse(call_env, &call_env->parsed, name, t_rules, cs, cec, call_env_method->env) < 0) {
                talloc_free(call_env);
                return NULL;
        }
index 068b25f899c3910df1a19d2b24d50004a01d9e7e..c65a79bd868d8d6eb3a41310e7e2db9e58698577 100644 (file)
@@ -34,6 +34,7 @@ extern "C" {
 typedef struct call_env_parser_s       call_env_parser_t;
 typedef struct call_env_parsed_s       call_env_parsed_t;
 typedef struct call_env_method_s       call_env_method_t;
+typedef struct call_env_ctx_s          call_env_ctx_t;
 typedef struct call_env_s              call_env_t;
 
 FR_DLIST_TYPES(call_env_parsed)
@@ -43,6 +44,7 @@ FR_DLIST_TYPEDEFS(call_env_parsed, call_env_parsed_head_t, call_env_parsed_entry
 #include <freeradius-devel/unlang/action.h>
 #include <freeradius-devel/server/cf_parse.h>
 #include <freeradius-devel/server/dl_module.h>
+#include <freeradius-devel/server/module.h>
 #include <freeradius-devel/server/request.h>
 #include <freeradius-devel/server/tmpl.h>
 
@@ -135,13 +137,13 @@ DIAG_ON(attributes)
  * @param[out] out             Where to write the result of parsing.
  * @param[in] t_rules          we're parsing attributes with.  Contains the default dictionary and nested 'caller' tmpl_rules_t.
  * @param[in] ci               The #CONF_SECTION or #CONF_PAIR to parse.
- * @param[in] data             module / xlat instance data of the module / xlat allocating this call_env
+ * @param[in] cec              information about how the call env is being used.
  * @param[in] rule             Parse rules - How the #CONF_PAIR or #CONF_SECTION should be converted.
  * @return
  *     - 0 on success.
  *     - -1 on failure.
  */
-typedef int (*call_env_parse_pair_t)(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, char const *section_name1, char const *section_name2, void const *data, call_env_parser_t const *rule);
+typedef int (*call_env_parse_pair_t)(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, call_env_ctx_t const *cec, call_env_parser_t const *rule);
 
 /** Callback for performing custom parsing of a #CONF_SECTION
  *
@@ -153,12 +155,14 @@ typedef int (*call_env_parse_pair_t)(TALLOC_CTX *ctx, void *out, tmpl_rules_t co
  * @param[out] out             Where to write the result of parsing.
  * @param[in] t_rules          we're parsing attributes with.  Contains the default dictionary and nested 'caller' tmpl_rules_t.
  * @param[in] ci               The #CONF_SECTION or #CONF_PAIR to parse.
+ * @param[in] cec              information about how the call env is being used.
  * @param[in] rule             Parse rules - How the #CONF_PAIR or #CONF_SECTION should be converted.
  * @return
  *     - 0 on success.
  *     - -1 on failure.
  */
-typedef int (*call_env_parse_section_t)(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, char const *section_name1, char const *section_name2, void const *data, call_env_parser_t const *rule);
+typedef int (*call_env_parse_section_t)(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci,
+                                       call_env_ctx_t const *cec, call_env_parser_t const *rule);
 
 /** Per method call config
  *
@@ -199,7 +203,7 @@ struct call_env_parser_s {
                } pair;
 
                struct {
-                       char const                      *ident2;        //!< Second identifier for a section
+                       char const                      *name2;         //!< Second identifier for a section
                        call_env_parser_t const         *subcs;         //!< Nested definitions for subsection.
 
                        call_env_parse_section_t        func;           //!< Callback for parsing CONF_SECTION.
@@ -209,6 +213,21 @@ struct call_env_parser_s {
        void const *uctx;                               //!< User context for callback functions.
 };
 
+typedef enum {
+       CALL_ENV_CTX_TYPE_MODULE = 1,                   //!< The callenv is registered to a module method.
+       CALL_ENV_CTX_TYPE_XLAT                          //!< The callenv is registered to an xlat.
+} call_env_ctx_type_t;
+
+struct call_env_ctx_s {
+       call_env_ctx_type_t                             type;           //!< Type of callenv ctx.
+
+       module_instance_t const                         *mi;            //!< Module instance that the callenv is registered to.
+                                                                       ///< Available for both module methods, and xlats.
+
+       section_name_t const                            *asked;         //!< The actual name1/name2 that resolved to a
+                                                                       ///< module_method_binding_t.
+};
+
 #define CALL_ENV_TERMINATOR { NULL }
 
 /** Helper macro for populating the size/type fields of a #call_env_method_t from the output structure type
@@ -375,21 +394,21 @@ typedef void _mismatch_flags;             //!< Dummy type used to indicate bad flags.
 
 /** Specify a call_env_parser_t which defines a nested subsection
  */
-#define FR_CALL_ENV_SUBSECTION(_name, _ident2, _flags, _subcs ) \
+#define FR_CALL_ENV_SUBSECTION(_name, _name2, _flags, _subcs ) \
        .name = _name, \
        .flags = CALL_ENV_FLAG_SUBSECTION | (_flags), \
        .section = { \
-               .ident2 = _ident2, \
+               .name2 = _name2, \
                .subcs = _subcs, \
        }
 
 /** Specify a call_env_parser_t which parses a subsection using a callback function
  */
-#define FR_CALL_ENV_SUBSECTION_FUNC(_name, _ident2, _flags, _func) \
+#define FR_CALL_ENV_SUBSECTION_FUNC(_name, _name2, _flags, _func) \
        .name = _name, \
        .flags = CALL_ENV_FLAG_SUBSECTION | (_flags), \
        .section = { \
-               .ident2 = _ident2, \
+               .name2 = _name2, \
                .func = _func \
        }
 
@@ -402,8 +421,8 @@ unlang_action_t call_env_expand(TALLOC_CTX *ctx, request_t *request, call_env_re
 /** @name Functions that implement standard parsing behaviour which can be called by callbacks
  * @{
  */
-int call_env_parse_pair(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, char const *section_name1,
-                       char const *section_name2, void const *data, call_env_parser_t const *rule);
+int call_env_parse_pair(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci,
+                       call_env_ctx_t const *cec, call_env_parser_t const *rule);
 /** @} */
 
 /** @name Functions to be used by the section callbacks to add parsed data.
@@ -426,8 +445,7 @@ void call_env_parsed_free(call_env_parsed_head_t *parsed, call_env_parsed_t *ptr
  * @{
  */
 call_env_t *call_env_alloc(TALLOC_CTX *ctx, char const *name, call_env_method_t const *call_env_method,
-                          tmpl_rules_t const *rules, CONF_SECTION *cs, char const *section_name1,
-                          char const *section_name2, void const *data) CC_HINT(nonnull(3,4,5));
+                          tmpl_rules_t const *rules, CONF_SECTION *cs, call_env_ctx_t const *cec) CC_HINT(nonnull(3,4,5));
 /** @} */
 
 #ifdef __cplusplus
index e1137a0cf5b62a53f6c6b5bbdc0606badf1fd520..629f0812a82cff80962881e91871894377e5cda9 100644 (file)
@@ -312,9 +312,9 @@ static void unlang_dump(unlang_t *instruction, int depth)
 
                case UNLANG_TYPE_MODULE:
                {
-                       unlang_module_t *single = unlang_generic_to_module(c);
+                       unlang_module_t *m = unlang_generic_to_module(c);
 
-                       DEBUG("%.*s%s", depth, unlang_spaces, single->mi->name);
+                       DEBUG("%.*s%s", depth, unlang_spaces, m->mmc.mi->name);
                }
                        break;
 
@@ -4361,7 +4361,7 @@ static CONF_SECTION *virtual_module_find_cs(CONF_ITEM *ci,
         *
         *      Return it to the caller, with the updated method.
         */
-       subcs = module_rlm_by_name_virtual(virtual_name);
+       subcs = module_rlm_virtual_by_name(virtual_name);
        if (subcs) goto check_for_loop;
 
        /*
@@ -4420,30 +4420,29 @@ check_for_loop:
        return subcs;
 }
 
-static unlang_t *compile_module(unlang_t *parent, unlang_compile_t *unlang_ctx,
-                               CONF_ITEM *ci, module_instance_t *inst, module_method_t method,
-                               call_env_method_t const *method_env, char const *realname)
+static unlang_t *compile_module(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM *ci, char const *name)
 {
-       unlang_t *c;
-       unlang_module_t *single;
-
-       /*
-        *      Check if the module in question has the necessary
-        *      component.
-        */
-       if (!method) {
-               cf_log_err(ci, "Failed compiling %s - no method matching calling section found", inst->name);
+       unlang_t        *c;
+       unlang_module_t *m;
+       fr_slen_t       slen;
+
+       MEM(m = talloc_zero(parent, unlang_module_t));
+       slen = module_rlm_by_name_and_method(m, &m->mmc,
+                                            unlang_ctx->vs,
+                                            &(section_name_t){ .name1 = unlang_ctx->section_name1, .name2 = unlang_ctx->section_name2 },
+                                            &FR_SBUFF_IN(name, strlen(name)),
+                                            unlang_ctx->rules);
+       if (slen < 0) {
+               cf_log_perr(ci, "Failed compiling module call");
+               talloc_free(m);
                return NULL;
        }
 
-       MEM(single = talloc_zero(parent, unlang_module_t));
-       single->mi = inst;
-       single->method = method;
-       c = unlang_module_to_generic(single);
+       c = unlang_module_to_generic(m);
        c->parent = parent;
        c->next = NULL;
 
-       c->name = talloc_typed_strdup(c, realname);
+       c->name = talloc_typed_strdup(c, name);
        c->debug_name = c->name;
        c->type = UNLANG_TYPE_MODULE;
        c->ci = ci;
@@ -4451,7 +4450,7 @@ static unlang_t *compile_module(unlang_t *parent, unlang_compile_t *unlang_ctx,
        /*
         *      Set the default actions for this module.
         */
-       c->actions = inst->actions;
+       c->actions = m->mmc.mi->actions;
 
        /*
         *      Add in the default actions for this section.
@@ -4461,22 +4460,27 @@ static unlang_t *compile_module(unlang_t *parent, unlang_compile_t *unlang_ctx,
        /*
         *      Parse the method environment for this module / method
         */
-       if (method_env) {
+       if (m->mmc.mmb.method_env) {
+               call_env_method_t const *method_env = m->mmc.mmb.method_env;
+
                fr_assert_msg(method_env->inst_size, "Method environment for module %s, method %s %s declared, "
                              "but no inst_size set",
-                             inst->name, unlang_ctx->section_name1, unlang_ctx->section_name2);
+                             m->mmc.mi->name, unlang_ctx->section_name1, unlang_ctx->section_name2);
 
                if (!unlang_ctx->rules) {
-                       cf_log_err(ci, "Failed compiling %s - no rules", inst->name);
+                       cf_log_err(ci, "Failed compiling %s - no rules",  m->mmc.mi->name);
                        goto error;
                }
-               single->call_env = call_env_alloc(single, single->self.name, method_env,
-                                                 unlang_ctx->rules, inst->conf,
-                                                 unlang_ctx->section_name1, unlang_ctx->section_name2,
-                                                 single->mi->data);
-               if (!single->call_env) {
+               m->call_env = call_env_alloc(m, m->self.name, method_env,
+                                            unlang_ctx->rules, m->mmc.mi->conf,
+                                            &(call_env_ctx_t){
+                                               .type = CALL_ENV_CTX_TYPE_MODULE,
+                                               .mi = m->mmc.mi,
+                                               .asked = &m->mmc.asked
+                                            });
+               if (!m->call_env) {
                error:
-                       talloc_free(c);
+                       talloc_free(m);
                        return NULL;
                }
        }
@@ -4487,7 +4491,7 @@ static unlang_t *compile_module(unlang_t *parent, unlang_compile_t *unlang_ctx,
         */
        if (cf_item_is_section(ci) &&
            !unlang_compile_actions(&c->actions, cf_item_to_section(ci),
-                                   (inst->exported->flags & MODULE_TYPE_RETRY) != 0)) goto error;
+                                   (m->mmc.mi->exported->flags & MODULE_TYPE_RETRY) != 0)) goto error;
 
        return c;
 }
@@ -4533,15 +4537,13 @@ static int unlang_pair_keywords_len = NUM_ELEMENTS(unlang_pair_keywords);
 static unlang_t *compile_item(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM *ci)
 {
        char const              *name, *p;
-       module_instance_t       *inst;
        CONF_SECTION            *cs, *subcs, *modules;
        char const              *realname;
        unlang_compile_t        unlang_ctx2;
-       module_method_t         method;
        bool                    policy;
        unlang_op_compile_t     compile;
        unlang_t                *c;
-       call_env_method_t const *method_env = NULL;
+       bool                    ignore_notfound = false;
 
        if (cf_item_is_section(ci)) {
                cs = cf_item_to_section(ci);
@@ -4695,14 +4697,21 @@ check_for_module:
         *      Not a function.  It must be a real module.
         */
        modules = cf_section_find(cf_root(ci), "modules", NULL);
-       if (!modules) goto fail;
+       if (!modules) {
+               cf_log_err(ci, "Failed compiling \"%s\" as a module or policy as no modules are enabled", name);
+               cf_log_err(ci, "Please verify that modules { ... }  section is present in the server configuration", name);
+               return NULL;
+       }
 
        realname = name;
 
        /*
         *      Try to load the optional module.
         */
-       if (realname[0] == '-') realname++;
+       if (*realname == '-') {
+               ignore_notfound = true;
+               realname++;
+       }
 
        /*
         *      Set the child compilation context BEFORE parsing the
@@ -4711,38 +4720,15 @@ check_for_module:
         *      name2, etc.
         */
        UPDATE_CTX2;
-       inst = module_rlm_by_name_and_method(&method, &method_env,
-                                               &unlang_ctx2.section_name1, &unlang_ctx2.section_name2,
-                                               unlang_ctx2.vs, realname);
-       if (inst) {
-               c = compile_module(parent, &unlang_ctx2, ci, inst, method, method_env, realname);
-               goto allocate_number;
-       }
+       c = compile_module(parent, &unlang_ctx2, ci, realname);
+       if (c) goto allocate_number;
 
-       /*
-        *      We were asked to MAYBE load it and it
-        *      doesn't exist.  Return a soft error.
-        */
-       if (realname != name) {
-               cf_log_warn(ci, "Ignoring \"%s\" as the \"%s\" module is not enabled.", name, realname);
+       if (ignore_notfound) {
+               cf_log_warn(ci, "Ignoring \"%s\" as the \"%s\" module is not enabled, "
+                           "or the method does not exist", name, realname);
                return UNLANG_IGNORE;
        }
 
-       /*
-        *      The module exists, but it does not have the
-        *      named method.
-        */
-       if (!unlang_ctx2.section_name1) {
-               cf_log_err(ci, "Failed compiling %s - %s", name, fr_strerror());
-               return NULL;
-       }
-
-       /*
-        *      Can't de-reference it to anything.  Ugh.
-        */
-fail:
-       cf_log_err(ci, "Failed to find \"%s\" as a module or policy.", name);
-       cf_log_err(ci, "Please verify that the configuration exists in mods-enabled/%s.", name);
        return NULL;
 }
 
index ef256b622c23dd466faab02f1eb42ec13e29c0c2..3a46c63c12d091031adbb2f060698d4f9b35a1c1 100644 (file)
@@ -45,7 +45,7 @@ typedef struct {
        unlang_module_fd_event_t        fd_read;        //!< Function to call when FD is readable.
        unlang_module_fd_event_t        fd_write;       //!< Function to call when FD is writable.
        unlang_module_fd_event_t        fd_error;       //!< Function to call when FD has errored.
-       module_instance_t               *mi;            //!< Module instance to pass to callbacks.
+       module_instance_t const         *mi;            //!< Module instance to pass to callbacks.
                                                        ///< Use mi->data to get instance data.
        void                            *thread;        //!< Thread specific module instance.
        void                            *env_data;      //!< Per call environment data.
@@ -130,12 +130,12 @@ int unlang_module_timeout_add(request_t *request, unlang_module_timeout_t callba
        unlang_stack_t                  *stack = request->stack;
        unlang_stack_frame_t            *frame = &stack->frame[stack->depth];
        unlang_module_event_t           *ev;
-       unlang_module_t                 *mc;
+       unlang_module_t                 *m;
        unlang_frame_state_module_t     *state = talloc_get_type_abort(frame->state, unlang_frame_state_module_t);
 
        fr_assert(stack->depth > 0);
        fr_assert(frame->instruction->type == UNLANG_TYPE_MODULE);
-       mc = unlang_generic_to_module(frame->instruction);
+       m = unlang_generic_to_module(frame->instruction);
 
        ev = talloc(request, unlang_module_event_t);
        if (!ev) return -1;
@@ -144,7 +144,7 @@ int unlang_module_timeout_add(request_t *request, unlang_module_timeout_t callba
                .request = request,
                .fd = -1,
                .timeout = callback,
-               .mi = mc->mi,
+               .mi = m->mmc.mi,
                .thread = state->thread,
                .env_data = state->env_data,
                .rctx = rctx
@@ -250,14 +250,14 @@ int unlang_module_fd_add(request_t *request,
        unlang_stack_t                  *stack = request->stack;
        unlang_stack_frame_t            *frame = &stack->frame[stack->depth];
        unlang_module_event_t           *ev;
-       unlang_module_t                 *mc;
+       unlang_module_t                 *m;
        unlang_frame_state_module_t     *state = talloc_get_type_abort(frame->state,
                                                                       unlang_frame_state_module_t);
 
        fr_assert(stack->depth > 0);
 
        fr_assert(frame->instruction->type == UNLANG_TYPE_MODULE);
-       mc = unlang_generic_to_module(frame->instruction);
+       m = unlang_generic_to_module(frame->instruction);
 
        ev = talloc_zero(request, unlang_module_event_t);
        if (!ev) return -1;
@@ -267,7 +267,7 @@ int unlang_module_fd_add(request_t *request,
        ev->fd_read = read;
        ev->fd_write = write;
        ev->fd_error = error;
-       ev->mi = mc->mi;
+       ev->mi = m->mmc.mi;
        ev->thread = state->thread;
        ev->env_data = state->env_data;
        ev->rctx = rctx;
@@ -355,8 +355,12 @@ int unlang_module_push(rlm_rcode_t *p_result, request_t *request,
                                .retry = RETRY_INIT,
                        },
                },
-               .mi = mi,
-               .method = method
+               .mmc = {
+                       .mi = mi,
+                       .mmb = {
+                               .method = method
+                       }
+               }
        };
 
        /*
@@ -519,13 +523,13 @@ unlang_action_t unlang_module_yield_to_section(rlm_rcode_t *p_result,
                                               unlang_module_signal_t signal, fr_signal_t sigmask, void *rctx)
 {
        if (!subcs) {
-               unlang_stack_t          *stack = request->stack;
-               unlang_stack_frame_t    *frame = &stack->frame[stack->depth];
-               unlang_module_t         *mc;
+               unlang_stack_t                  *stack = request->stack;
+               unlang_stack_frame_t            *frame = &stack->frame[stack->depth];
+               unlang_module_t                 *m;
                unlang_frame_state_module_t     *state;
 
                fr_assert(frame->instruction->type == UNLANG_TYPE_MODULE);
-               mc = unlang_generic_to_module(frame->instruction);
+               m = unlang_generic_to_module(frame->instruction);
 
                /*
                 *      Be transparent to the resume function.
@@ -536,7 +540,7 @@ unlang_action_t unlang_module_yield_to_section(rlm_rcode_t *p_result,
                state = talloc_get_type_abort(frame->state, unlang_frame_state_module_t);
 
                return resume(p_result,
-                             MODULE_CTX(mc->mi, module_thread(mc->mi)->data,
+                             MODULE_CTX(m->mmc.mi, module_thread(m->mmc.mi)->data,
                                         state->env_data, rctx),
                              request);
        }
@@ -625,7 +629,7 @@ static inline CC_HINT(always_inline) void safe_unlock(module_instance_t *mi)
 static void unlang_module_signal(request_t *request, unlang_stack_frame_t *frame, fr_signal_t action)
 {
        unlang_frame_state_module_t     *state = talloc_get_type_abort(frame->state, unlang_frame_state_module_t);
-       unlang_module_t                 *mc = unlang_generic_to_module(frame->instruction);
+       unlang_module_t                 *m = unlang_generic_to_module(frame->instruction);
        char const                      *caller;
 
        if (!state->signal) return;
@@ -634,10 +638,10 @@ static void unlang_module_signal(request_t *request, unlang_stack_frame_t *frame
         *      Async calls can't push anything onto the unlang stack, so we just use a local "caller" here.
         */
        caller = request->module;
-       request->module = mc->mi->name;
-       safe_lock(mc->mi);
-       if (!(action & state->sigmask)) state->signal(MODULE_CTX(mc->mi, state->thread->data, state->env_data, state->rctx), request, action);
-       safe_unlock(mc->mi);
+       request->module = m->mmc.mi->name;
+       safe_lock(m->mmc.mi);
+       if (!(action & state->sigmask)) state->signal(MODULE_CTX(m->mmc.mi, state->thread->data, state->env_data, state->rctx), request, action);
+       safe_unlock(m->mmc.mi);
        request->module = caller;
 
        /*
@@ -696,8 +700,8 @@ static unlang_action_t unlang_module_resume_done(rlm_rcode_t *p_result, request_
 static unlang_action_t unlang_module_resume(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
 {
        unlang_frame_state_module_t     *state = talloc_get_type_abort(frame->state, unlang_frame_state_module_t);
-       unlang_module_t                 *mc = unlang_generic_to_module(frame->instruction);
-       module_method_t         resume;
+       unlang_module_t                 *m = unlang_generic_to_module(frame->instruction);
+       module_method_t                 resume;
        unlang_action_t                 ua;
 
        /*
@@ -724,16 +728,16 @@ static unlang_action_t unlang_module_resume(rlm_rcode_t *p_result, request_t *re
        /*
         *      Lock is noop unless instance->mutex is set.
         */
-       safe_lock(mc->mi);
-       ua = resume(&state->rcode, MODULE_CTX(mc->mi, state->thread->data,
+       safe_lock(m->mmc.mi);
+       ua = resume(&state->rcode, MODULE_CTX(m->mmc.mi, state->thread->data,
                                              state->env_data, state->rctx), request);
-       safe_unlock(mc->mi);
+       safe_unlock(m->mmc.mi);
 
        if (request->master_state == REQUEST_STOP_PROCESSING) ua = UNLANG_ACTION_STOP_PROCESSING;
 
        switch (ua) {
        case UNLANG_ACTION_STOP_PROCESSING:
-               RWARN("Module %s or worker signalled to stop processing request", mc->mi->module->exported->name);
+               RWARN("Module %s or worker signalled to stop processing request", m->mmc.mi->name);
                if (state->p_result) *state->p_result = state->rcode;
                state->thread->active_callers--;
                *p_result = state->rcode;
@@ -874,7 +878,7 @@ static void unlang_module_event_retry_handler(UNUSED fr_event_list_t *el, fr_tim
 
 static unlang_action_t unlang_module(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
 {
-       unlang_module_t                 *mc;
+       unlang_module_t                 *m;
        unlang_frame_state_module_t     *state = talloc_get_type_abort(frame->state, unlang_frame_state_module_t);
        unlang_action_t                 ua;
        fr_time_t                       now = fr_time_wrap(0);
@@ -890,26 +894,26 @@ static unlang_action_t unlang_module(rlm_rcode_t *p_result, request_t *request,
         *      Process a stand-alone child, and fall through
         *      to dealing with it's parent.
         */
-       mc = unlang_generic_to_module(frame->instruction);
-       fr_assert(mc);
+       m = unlang_generic_to_module(frame->instruction);
+       fr_assert(m);
 
        RDEBUG4("[%i] %s - %s (%s)", stack_depth_current(request), __FUNCTION__,
-               mc->mi->module->exported->name, mc->mi->name);
+               m->mmc.mi->module->exported->name, m->mmc.mi->name);
 
        state->p_result = NULL;
 
        /*
         *      Return administratively configured return code
         */
-       if (mc->mi->force) {
-               state->rcode = mc->mi->code;
+       if (m->mmc.mi->force) {
+               state->rcode = m->mmc.mi->code;
                ua = UNLANG_ACTION_CALCULATE_RESULT;
                goto done;
        }
 
-       if (mc->call_env) {
+       if (m->mmc.mmb.method_env) {
                if (!state->env_data) {
-                       ua = call_env_expand(state, request, &state->env_result, &state->env_data, mc->call_env);
+                       ua = call_env_expand(state, request, &state->env_result, &state->env_data, m->call_env);
                        switch (ua) {
                        case UNLANG_ACTION_FAIL:
                                goto fail;
@@ -932,7 +936,7 @@ static unlang_action_t unlang_module(rlm_rcode_t *p_result, request_t *request,
        /*
         *      Grab the thread/module specific data if any exists.
         */
-       state->thread = module_thread(mc->mi);
+       state->thread = module_thread(m->mmc.mi);
        fr_assert(state->thread != NULL);
 
        /*
@@ -951,12 +955,12 @@ static unlang_action_t unlang_module(rlm_rcode_t *p_result, request_t *request,
         */
        if (fr_time_delta_ispos(frame->instruction->actions.retry.irt)) now = fr_time();
 
-       request->module = mc->mi->name;
-       safe_lock(mc->mi);      /* Noop unless instance->mutex set */
-       ua = mc->method(&state->rcode,
-                       MODULE_CTX(mc->mi, state->thread->data, state->env_data, NULL),
-                       request);
-       safe_unlock(mc->mi);
+       request->module = m->mmc.mi->name;
+       safe_lock(m->mmc.mi);   /* Noop unless instance->mutex set */
+       ua = m->mmc.mmb.method(&state->rcode,
+                              MODULE_CTX(m->mmc.mi, state->thread->data, state->env_data, NULL),
+                              request);
+       safe_unlock(m->mmc.mi);
 
        if (request->master_state == REQUEST_STOP_PROCESSING) ua = UNLANG_ACTION_STOP_PROCESSING;
 
@@ -966,7 +970,7 @@ static unlang_action_t unlang_module(rlm_rcode_t *p_result, request_t *request,
         *      must have been blocked.
         */
        case UNLANG_ACTION_STOP_PROCESSING:
-               RWARN("Module %s became unblocked", mc->mi->name);
+               RWARN("Module %s became unblocked", m->mmc.mi->name);
                if (state->p_result) *state->p_result = state->rcode;
                *p_result = state->rcode;
                request->module = state->previous_module;
index 33bcef8cf38f4ded54e8f02ab7dc0085a4283642..8ee0c51352a0ab98ff64d91fbb8217480f331f68 100644 (file)
@@ -23,6 +23,7 @@
  * @copyright 2018-2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
  * @copyright 2018 The FreeRADIUS server project
  */
+#include "lib/unlang/call_env.h"
 RCSID("$Id$")
 
 #include <freeradius-devel/io/schedule.h>
@@ -272,7 +273,11 @@ static xlat_inst_t *xlat_inst_alloc(xlat_exp_t *node)
                                                                        .dict_def = call->dict,
                                                                        .list_def = request_attr_request
                                                        }
-                                             }, call->func->mctx->mi->conf, NULL, NULL, call->func->mctx->mi->data);
+                                             }, call->func->mctx->mi->conf,
+                                             &(call_env_ctx_t){
+                                                       .type = CALL_ENV_CTX_TYPE_XLAT,
+                                                       .mi = call->func->mctx->mi
+                                             });
                if (!xi->call_env) {
                        talloc_free(xi);
                        return NULL;
index cfb3455edc63ece60d5dd351dca5eab750c0e051..7652a637766a2cbbe7426d38bf369f72b8f6b18e 100644 (file)
  *
  * @copyright 2023 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
  */
-
 RCSID("$Id$")
 
+#include <talloc.h>
+
 #include <freeradius-devel/unlang/interpret.h>
 #include <freeradius-devel/unlang/xlat_redundant.h>
 #include <freeradius-devel/unlang/xlat_func.h>
 #include <freeradius-devel/unlang/xlat_priv.h>
+
+#include <freeradius-devel/server/cf_util.h>
+#include <freeradius-devel/server/module.h>
+#include <freeradius-devel/server/module_rlm.h>
+#include <freeradius-devel/unlang/xlat.h>
+
+#include <freeradius-devel/util/dlist.h>
 #include <freeradius-devel/util/rand.h>
+#include <freeradius-devel/util/rb.h>
+#include <freeradius-devel/util/sbuff.h>
+
 
 /*
  *     Internal redundant handler for xlats
@@ -50,7 +61,7 @@ typedef enum {
 
 typedef struct {
        fr_dlist_t                      entry;          //!< Entry in the redundant function list.
-       xlat_t                          *func;          //!< Resolved xlat function.
+       xlat_t const                    *func;          //!< Resolved xlat function.
 } xlat_redundant_func_t;
 
 typedef struct {
@@ -193,7 +204,7 @@ static xlat_action_t xlat_redundant(TALLOC_CTX *ctx, fr_dcursor_t *out,
  * @param[in] args     Arguments to the function.  Will be copied,
  *                     and freed when the new xlat node is freed.
  */
-static xlat_exp_t *xlat_exp_func_alloc(TALLOC_CTX *ctx, xlat_t *func, xlat_exp_head_t const *args)
+static xlat_exp_t *xlat_exp_func_alloc(TALLOC_CTX *ctx, xlat_t const *func, xlat_exp_head_t const *args)
 {
        xlat_exp_t *node;
 
@@ -311,6 +322,16 @@ static xlat_arg_parser_t const xlat_redundant_args[] = {
        XLAT_ARG_PARSER_TERMINATOR
 };
 
+static inline CC_HINT(always_inline)
+void xlat_redundant_add_xlat(xlat_redundant_t *xr, xlat_t const *x)
+{
+       xlat_redundant_func_t *xrf;
+
+       MEM(xrf = talloc_zero(xr, xlat_redundant_func_t));
+       xrf->func = x;
+       fr_dlist_insert_tail(&xr->funcs, xrf);
+}
+
 /** Registers a redundant xlat
  *
  * These xlats wrap the xlat methods of the modules in a redundant section,
@@ -330,18 +351,18 @@ int xlat_register_redundant(CONF_SECTION *cs)
        };
        static size_t xlat_redundant_type_table_len = NUM_ELEMENTS(xlat_redundant_type_table);
 
-       char const              *name1, *name2;
+       char const              *name1;
        xlat_redundant_type_t   xr_type;
        xlat_redundant_t        *xr;
-       xlat_func_flags_t       flags = XLAT_FUNC_FLAG_NONE;
-       bool                    can_be_pure = false;
+       xlat_func_flags_t       default_flags;
        xlat_arg_parser_t const *args = NULL;
 
        fr_type_t               return_type = FR_TYPE_NULL;
-       bool                    first = true;
 
-       xlat_t                  *xlat;
        CONF_ITEM               *ci = NULL;
+       int                     children = 0, i;
+       module_instance_t       **mri_arr;              /* Temporary array of module instances */
+       module_instance_t       **mri_p;
 
        name1 = cf_section_name1(cs);
        xr_type = fr_table_value_by_str(xlat_redundant_type_table, name1, XLAT_REDUNDANT_INVALID);
@@ -351,110 +372,199 @@ int xlat_register_redundant(CONF_SECTION *cs)
                return -1;
 
        case XLAT_REDUNDANT:
-               can_be_pure = true;     /* Can be pure */
+               default_flags = XLAT_FUNC_FLAG_PURE;    /* Can be pure */
                break;
 
        case XLAT_LOAD_BALANCE:
-               can_be_pure = false;    /* Can never be pure because of random selection */
+               default_flags = XLAT_FUNC_FLAG_NONE;    /* Can never be pure because of random selection */
                break;
 
        case XLAT_REDUNDANT_LOAD_BALANCE:
-               can_be_pure = false;    /* Can never be pure because of random selection */
+               default_flags = XLAT_FUNC_FLAG_NONE;    /* Can never be pure because of random selection */
                break;
        }
 
-       name2 = cf_section_name2(cs);
-       if (xlat_func_find(name2, talloc_array_length(name2) - 1)) {
-               cf_log_err(cs, "An expansion is already registered for this name");
-               return -1;
-       }
+       /*
+        *      Count the children
+        */
+       while ((ci = cf_item_next(cs, ci))) {
+               if (!cf_item_is_pair(ci)) continue;
 
-       MEM(xr = talloc_zero(cs, xlat_redundant_t));
-       xr->type = xr_type;
-       xr->cs = cs;
-       fr_dlist_talloc_init(&xr->funcs, xlat_redundant_func_t, entry);
+               children++;
+       }
 
        /*
-        *      Count the number of children for load-balance, and
-        *      also find out a little bit more about the old xlats.
+        *      There must be at least one child.
         *
-        *      These are just preemptive checks, the majority of
-        *      the work is done when a redundant xlat is
-        *      instantiated.  There we create an xlat node for
-        *      each of the children of the section.
+        *      It's useful to allow a redundant section with
+        *      only one child, for debugging.
         */
-       while ((ci = cf_item_next(cs, ci))) {
-               xlat_redundant_func_t   *xrf;
-               char const              *mod_func_name;
-               xlat_t                  *mod_func;
+       if (children == 0) {
+               cf_log_err(cs, "%s %s { ... } section must contain at least one module",
+                          cf_section_name1(cs), cf_section_name2(cs));
+               return -1;
+       }
+
+       /*
+        *      Resolve all the modules in the redundant section.
+        */
+       MEM(mri_arr = talloc_array(NULL, module_instance_t *, children));
+       for (ci = cf_item_next(cs, NULL), i = 0;
+            ci;
+            ci = cf_item_next(cs, ci), i++) {
+               char const *name;
 
                if (!cf_item_is_pair(ci)) continue;
 
-               mod_func_name = cf_pair_attr(cf_item_to_pair(ci));
+               name = cf_pair_attr(cf_item_to_pair(ci));
 
-               /*
-                *      This is ok, it just means the module
-                *      doesn't have an xlat method.
-                *
-                *      If there are ordering issues we could
-                *      move this check to the instantiation
-                *      function.
-                */
-               mod_func = xlat_func_find(mod_func_name, talloc_array_length(mod_func_name) - 1);
-               if (!mod_func) {
-                       talloc_free(xr);
-                       return 1;
+               mri_arr[i] = module_rlm_static_by_name(NULL, name);
+               if (!mri_arr[i]) {
+                       cf_log_err(ci, "Module '%s' not found.  Referenced in %s %s { ... } section",
+                                  name, cf_section_name1(cs), cf_section_name2(cs));
+               error:
+                       talloc_free(mri_arr);
+                       return -1;
                }
+       }
 
-               if (!args) {
-                       args = mod_func->args;
-               } else {
-                       fr_assert(args == mod_func->args);
-               }
+       /*
+        *      Iterate over the xlats registered for the
+        *      first module, verifying that the other
+        *      module instances have all registered the
+        *      similarly named xlat functions.
+        *
+        *      We ignore any xlat functions that aren't
+        *      available in all the modules.
+        */
+       {
+               fr_rb_iter_inorder_t            iter;
+               fr_sbuff_t                      *name;
+               fr_sbuff_marker_t               name_start;
+               xlat_t                          *xlat;
+               module_rlm_xlat_t const         *mrx;
+               module_rlm_instance_t           *mri;
+
+               FR_SBUFF_TALLOC_THREAD_LOCAL(&name, 128, SIZE_MAX);
 
                /*
-                *      Degrade to a void return type if
-                *      we have mixed types in a redundant
-                *      section.
+                *      Prepopulate the name buffer with  <section_name2>.
+                *      as every function wil be registered with this
+                *      prefix.
                 */
-               if (!first) {
-                       if (mod_func->return_type != return_type) return_type = FR_TYPE_VOID;
-               } else {
-                       return_type = mod_func->return_type;
-                       first = false;
+               if ((fr_sbuff_in_bstrcpy_buffer(name, cf_section_name2(cs)) <= 0) ||
+                    (fr_sbuff_in_char(name, '.') <= 0)) {
+                       cf_log_perr(cs, "Name too long");
+                       return -1;
                }
 
-               MEM(xrf = talloc_zero(xr, xlat_redundant_func_t));
-               xrf->func = mod_func;
-               fr_dlist_insert_tail(&xr->funcs, xrf);
+               fr_sbuff_marker(&name_start, name);
+
+               mri = talloc_get_type_abort(mri_arr[0]->uctx, module_rlm_instance_t);
 
                /*
-                *      Figure out pure status.  If any of
-                *      the children are un-pure then the
-                *      whole redundant xlat is un-pure,
-                *      same with async.
+                *      Iterate over the xlats registered to the first
+                *      module instance, searching for them in all the
+                *      other module instances.
                 */
-               if (can_be_pure && mod_func->flags.pure) flags |= XLAT_FUNC_FLAG_PURE;
-       }
+               for (mrx = fr_rb_iter_init_inorder(&iter, &mri->xlats);
+                    mrx;
+                    mrx = fr_rb_iter_next_inorder(&iter)) {
+                       xlat_func_flags_t       flags = default_flags;
+                       char const              *name_p;
+
+                       (void)talloc_get_type(mrx, module_rlm_xlat_t);
+
+                       /*
+                        *      Allocate the redundant xlat structure
+                        *      (somehwat prematurely) with the expectation
+                        *      that the virtually every module of the same
+                        *      type will have registered the same xlat
+                        *      functions.
+                        */
+                       MEM(xr = talloc_zero(NULL, xlat_redundant_t));
+                       xr->type = xr_type;
+                       xr->cs = cs;
+                       fr_dlist_talloc_init(&xr->funcs, xlat_redundant_func_t, entry);
+
+                       xlat_redundant_add_xlat(xr, mrx->xlat);
+
+                       /*
+                        *      We're only interested in xlat functions
+                        *      that are available in all the module
+                        *      instances.
+                        */
+                       for (mri_p = mri_arr + 1; mri_p < mri_arr + children; mri_p++) {
+                               module_rlm_xlat_t *found;
+
+                               found = talloc_get_type_abort(fr_rb_find(&mri->xlats, mrx), module_rlm_xlat_t);
+                               if (!found) continue;
+
+                               xlat_redundant_add_xlat(xr, mrx->xlat);
+
+                               if (!found->xlat->flags.pure) flags &= ~XLAT_FUNC_FLAG_PURE;
+                       }
 
-       /*
-        *      At least one module xlat has to exist.
-        */
-       if (!fr_dlist_num_elements(&xr->funcs)) {
-               talloc_free(xr);
-               return 1;
-       }
+                       /*
+                        *      Warn, but allow about redundant/failover expansions
+                        *      that are neither redundant, no failover.
+                        *
+                        *      It's sometime useful to comment out multiples during
+                        *      testing.
+                        */
+                       if (fr_dlist_num_elements(&xr->funcs) == 1) {
+                               cf_log_debug(cs, "%s expansion has no alternates, only %s",
+                                            fr_table_str_by_value(xlat_redundant_type_table, xr->type, "<INVALID>"),
+                                            mrx->xlat->name);
 
-       xlat = xlat_func_register(NULL, name2, xlat_redundant, return_type);
-       if (unlikely(xlat == NULL)) {
-               ERROR("Registering xlat for %s section failed",
-                     fr_table_str_by_value(xlat_redundant_type_table, xr->type, "<INVALID>"));
-               talloc_free(xr);
-               return -1;
+                       }
+                       /*
+                        *      Where the xlat name is in the format <mod>.<name2>
+                        *      then the redundant xlat will be <section_name2>.<xlat_name>.
+                        *
+                        *      Where the xlat has no '.', it's likely just the module
+                        *      name, in which case we just use <section_name2>.
+                        */
+                       name_p = strchr(mrx->xlat->name, '.');
+                       if (name_p) {
+                               name_p++;
+                               fr_sbuff_set(name, &name_start);        /* Reset the aggregation buffer to the '.' */
+                               if (fr_sbuff_in_bstrncpy(name, name_p, strlen(name_p)) < 0) {
+                                       cf_log_perr(cs, "Name too long");
+                                       goto error;
+                               }
+                               name_p = fr_sbuff_start(name);
+                       } else {
+                               name_p = cf_section_name2(cs);
+                       }
+
+                       /*
+                        *      Register the new redundant xlat, and hang it off of
+                        *      the first module instance in the section.
+                        *
+                        *      This isn't great, but at least the xlat should
+                        *      get unregistered at about the right time.
+                        */
+                       xlat = xlat_func_register(mri_arr[0], name_p, xlat_redundant, return_type);
+                       if (unlikely(xlat == NULL)) {
+                               cf_log_err(cs, "Registering expansion for %s section failed",
+                                          fr_table_str_by_value(xlat_redundant_type_table, xr->type, "<INVALID>"));
+                               talloc_free(xr);
+                               return -1;
+                       }
+                       talloc_steal(xlat, xr); /* redundant xlat should own its own config */
+
+                       cf_log_debug(cs, "Registered %s expansion \"%s\" with %u alternates",
+                                    fr_table_str_by_value(xlat_redundant_type_table, xr->type, "<INVALID>"),
+                                    xlat->name, fr_dlist_num_elements(&xr->funcs));
+
+                       xlat_func_flags_set(xlat, flags);
+                       xlat_func_instantiate_set(xlat, xlat_redundant_instantiate, xlat_redundant_inst_t, NULL, xr);
+                       if (args) xlat_func_args_set(xlat, xlat_redundant_args);
+               }
        }
-       xlat_func_flags_set(xlat, flags);
-       xlat_func_instantiate_set(xlat, xlat_redundant_instantiate, xlat_redundant_inst_t, NULL, xr);
-       if (args) xlat_func_args_set(xlat, xlat_redundant_args);
+
+       talloc_free(mri_arr);
 
        return 0;
 }
index ccf92aec4420d1590c28416a1c287611d5564f0f..1e9171bb74688f8ee9ae28c4205ef9b8a2bac836 100644 (file)
@@ -43,8 +43,8 @@ RCSID("$Id$")
 extern module_rlm_t rlm_cache;
 
 int submodule_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, conf_parser_t const *rule);
-static int cache_key_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, char const *section_name1, char const *section_name2, void const *data, call_env_parser_t const *rule);
-static int cache_update_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, char const *section_name1, char const *section_name2, void const *data, call_env_parser_t const *rule);
+static int cache_key_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, call_env_ctx_t const *cec, call_env_parser_t const *rule);
+static int cache_update_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, call_env_ctx_t const *cec, call_env_parser_t const *rule);
 
 static const conf_parser_t module_config[] = {
        { FR_CONF_OFFSET_TYPE_FLAGS("driver", FR_TYPE_VOID, 0, rlm_cache_t, driver_submodule), .dflt = "rbtree",
@@ -118,10 +118,10 @@ int submodule_parse(TALLOC_CTX *ctx, void *out, void *parent, CONF_ITEM *ci, con
 }
 
 static int cache_key_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci,
-                          char const *section_name1, char const *section_name2, void const *data,
+                          call_env_ctx_t const *cec,
                           call_env_parser_t const *rule)
 {
-       rlm_cache_t const       *inst = talloc_get_type_abort_const(data, rlm_cache_t);
+       rlm_cache_t const       *inst = talloc_get_type_abort_const(cec->mi->data, rlm_cache_t);
        call_env_parse_pair_t   func = inst->driver->key_parse ? inst->driver->key_parse : call_env_parse_pair;
        tmpl_t                  *key_tmpl;
        fr_type_t               cast;
@@ -130,8 +130,7 @@ static int cache_key_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rul
         *      Call the custom key parse function, OR the standard call_env_parse_pair
         *      function, depending on whether the driver calls a custom parsing function.
         */
-       if (unlikely((ret = func(ctx, &key_tmpl, t_rules, ci, section_name1, section_name2,
-                                inst->driver_submodule->data, rule)) < 0)) return ret;
+       if (unlikely((ret = func(ctx, &key_tmpl, t_rules, ci, inst->driver_submodule->data, rule)) < 0)) return ret;
        *((tmpl_t **)out) = key_tmpl;
 
        /*
@@ -1418,8 +1417,8 @@ static int cache_verify(map_t *map, void *uctx)
 }
 
 static int cache_update_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules,
-                                     CONF_ITEM *ci, UNUSED char const *section_name1, UNUSED char const *section_name2,
-                                     UNUSED void const *data, UNUSED call_env_parser_t const *rule)
+                                     CONF_ITEM *ci,
+                                     UNUSED call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
 {
        CONF_SECTION            *update = cf_item_to_section(ci);
        call_env_parsed_t       *parsed;
index 62c7e1c8e15e5ce202b89b618683730e4f423df4..07bf835a47c791156e8f8682e00f70252b101f6e 100644 (file)
@@ -402,11 +402,11 @@ static unlang_action_t CC_HINT(nonnull) mod_post_auth(rlm_rcode_t *p_result, mod
        return detail_do(p_result, mctx, request, request->reply, &request->reply_pairs, false);
 }
 
-static int call_env_filename_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci,
-                                  UNUSED char const *section_name1, UNUSED char const *section_name2,
-                                  void const *data, UNUSED call_env_parser_t const *rule)
+static int call_env_filename_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules,
+                                  CONF_ITEM *ci,
+                                  call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
 {
-       rlm_detail_t const      *inst = talloc_get_type_abort_const(data, rlm_detail_t);
+       rlm_detail_t const      *inst = talloc_get_type_abort_const(cec->mi->data, rlm_detail_t);
        tmpl_t                  *parsed;
        CONF_PAIR const         *to_parse = cf_item_to_pair(ci);
        tmpl_rules_t            our_rules;
@@ -427,8 +427,8 @@ static int call_env_filename_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t cons
 }
 
 static int call_env_suppress_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules,
-                                  CONF_ITEM *ci, UNUSED char const *section_name1, UNUSED char const *section_name2,
-                                  UNUSED void const *data, UNUSED call_env_parser_t const *rule)
+                                  CONF_ITEM *ci,
+                                  UNUSED call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
 {
        CONF_SECTION const      *cs = cf_item_to_section(ci);
        CONF_SECTION const      *parent = cf_item_to_section(cf_parent(ci));
index 79cd5ee539b09f0f823b67fecb63dcbe3862539e..8efe980671298ab544097eaccb8279d89a6ba113 100644 (file)
@@ -627,13 +627,12 @@ static unlang_action_t CC_HINT(nonnull) mod_files(rlm_rcode_t *p_result, module_
  *
  */
 static int call_env_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci,
-                         UNUSED char const *section_name1, UNUSED char const *section_name2,
-                         void const *data, UNUSED call_env_parser_t const *rule)
+                         call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
 {
-       rlm_files_t const       *inst = talloc_get_type_abort_const(data, rlm_files_t);
-       CONF_PAIR const         *to_parse = cf_item_to_pair(ci);
-       rlm_files_data_t        *files_data;
-       fr_type_t               keytype;
+       rlm_files_t const               *inst = talloc_get_type_abort_const(cec->mi->data, rlm_files_t);
+       CONF_PAIR const                 *to_parse = cf_item_to_pair(ci);
+       rlm_files_data_t                *files_data;
+       fr_type_t                       keytype;
 
        MEM(files_data = talloc_zero(ctx, rlm_files_data_t));
 
index 2319938300a2174d3a37da8be4a0099cc390e6b3..242f39d387d3ee84f1568d6851263e56fe31f31b 100644 (file)
@@ -77,9 +77,9 @@ typedef struct {
        map_list_t      *profile_map;                   //!< List of maps to apply to the profile.
 } ldap_xlat_profile_call_env_t;
 
-static int ldap_update_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, char const *section_name1, char const *section_name2, void const *data, call_env_parser_t const *rule);
+static int ldap_update_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, call_env_ctx_t const *cec, call_env_parser_t const *rule);
 
-static int ldap_group_filter_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, UNUSED char const *section_name1, UNUSED char const *section_name2, void const *data, UNUSED call_env_parser_t const *rule);
+static int ldap_group_filter_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci, call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule);
 
 static const call_env_parser_t sasl_call_env[] = {
        { FR_CALL_ENV_OFFSET("mech", FR_TYPE_STRING, CALL_ENV_FLAG_NONE, ldap_auth_call_env_t, user_sasl_mech) },
@@ -2230,8 +2230,8 @@ static int parse_sub_section(module_inst_ctx_t const *mctx,
 }
 
 static int ldap_update_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules,
-                                    CONF_ITEM *ci, UNUSED char const *section_name1, UNUSED char const *section_name2,
-                                    UNUSED void const *data, call_env_parser_t const *rule)
+                                    CONF_ITEM *ci,
+                                    UNUSED call_env_ctx_t const *cec, call_env_parser_t const *rule)
 {
        map_list_t                      *maps;
        CONF_SECTION                    *update = cf_item_to_section(ci);
@@ -2309,12 +2309,11 @@ static int ldap_update_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *ou
 }
 
 static int ldap_group_filter_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, UNUSED CONF_ITEM *ci,
-                                  UNUSED char const *section_name1, UNUSED char const *section_name2,
-                                  void const *data, UNUSED call_env_parser_t const *rule)
+                                  call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
 {
-       rlm_ldap_t const        *inst = talloc_get_type_abort_const(data, rlm_ldap_t);
-       char const              *filters[] = { inst->group.obj_filter, inst->group.obj_membership_filter };
-       tmpl_t                  *parsed;
+       rlm_ldap_t const                *inst = talloc_get_type_abort_const(cec->mi->data, rlm_ldap_t);
+       char const                      *filters[] = { inst->group.obj_filter, inst->group.obj_membership_filter };
+       tmpl_t                          *parsed;
 
        if (fr_ldap_filter_to_tmpl(ctx, t_rules, filters, NUM_ELEMENTS(filters), &parsed) < 0) return -1;
 
index 7207f935233b4641f63cf73b62c26d5f41af7f95..c421f67f8687e9ba1267248a70b626c76a3d3e00 100644 (file)
@@ -65,8 +65,7 @@ RCSID("$Id$")
 
 static int linelog_escape_func(fr_value_box_t *vb, UNUSED void *uctx);
 static int call_env_filename_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci,
-                                  UNUSED char const *section_name1, UNUSED char const *section_name2,
-                                  void const *data, UNUSED call_env_parser_t const *rule);
+                                  call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule);
 typedef enum {
        LINELOG_DST_INVALID = 0,
        LINELOG_DST_FILE,                               //!< Log to a file.
@@ -876,13 +875,12 @@ build_vector:
  *     Custom call env parser for filenames - sets the correct escaping function
  */
 static int call_env_filename_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci,
-                                  UNUSED char const *section_name1, UNUSED char const *section_name2,
-                                  void const *data, UNUSED call_env_parser_t const *rule)
+                                  call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
 {
-       rlm_linelog_t const     *inst = talloc_get_type_abort_const(data, rlm_linelog_t);
-       tmpl_t                  *parsed;
-       CONF_PAIR const         *to_parse = cf_item_to_pair(ci);
-       tmpl_rules_t            our_rules;
+       rlm_linelog_t const             *inst = talloc_get_type_abort_const(cec->mi->data, rlm_linelog_t);
+       tmpl_t                          *parsed;
+       CONF_PAIR const                 *to_parse = cf_item_to_pair(ci);
+       tmpl_rules_t                    our_rules;
 
        /*
         *      If we're not logging to a file destination, do nothing
index 3fa81f000f3af87d88ab512da880beb86ca3f900..8dcf707aa3b939f936143308cc6e656dc843ad13 100644 (file)
@@ -942,8 +942,8 @@ static int mod_thread_detach(module_thread_inst_ctx_t const *mctx)
  *
  */
 static int smtp_header_section_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules,
-                                    CONF_ITEM *ci, UNUSED char const *section_name1, UNUSED char const *section_name2,
-                                    UNUSED void const *data, UNUSED call_env_parser_t const *rule)
+                                    CONF_ITEM *ci,
+                                    UNUSED call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
 {
        CONF_SECTION const      *cs = cf_item_to_section(ci);
        CONF_ITEM const         *item = NULL;
index 2bc7858942df03206ae6ac7ae596d63ca27ff406..fff93a8002223103b0788ac8a13202fe7122748f 100644 (file)
@@ -120,10 +120,10 @@ static const call_env_method_t authorize_method_env = {
 };
 
 static int logfile_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *cc,
-                                 char const *section_name1, char const *section_name2, void const *data, call_env_parser_t const *rule);
+                                 call_env_ctx_t const *cec, call_env_parser_t const *rule);
 
 static int query_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules, CONF_ITEM *cc,
-                               char const *section_name1, char const *section_name2, void const *data, call_env_parser_t const *rule);
+                               call_env_ctx_t const *cec, call_env_parser_t const *rule);
 
 typedef struct {
        fr_value_box_t  filename;
@@ -1848,8 +1848,8 @@ static unlang_action_t CC_HINT(nonnull) mod_sql_redundant(rlm_rcode_t *p_result,
 }
 
 static int logfile_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules,
-                                 CONF_ITEM *ci, char const *section_name1, char const *section_name2,
-                                 UNUSED void const *data, UNUSED call_env_parser_t const *rule)
+                                 CONF_ITEM *ci,
+                                 UNUSED call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
 {
        CONF_SECTION const      *subcs = NULL, *subsubcs = NULL;
        CONF_PAIR const         *to_parse = NULL;
@@ -1858,6 +1858,8 @@ static int logfile_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out,
        tmpl_rules_t            our_rules;
        char                    *section2, *p;
 
+       fr_assert(cec->type == CALL_ENV_CTX_TYPE_MODULE);
+
        /*
         *      The call env subsection which calls this has CF_IDENT_ANY as its name
         *      which results in finding the first child section of the module config.
@@ -1875,10 +1877,10 @@ static int logfile_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out,
         *      falling back to
         *              <module> { logfile }
         */
-       subcs = cf_section_find(cf_item_to_section(ci), section_name1, CF_IDENT_ANY);
+       subcs = cf_section_find(cf_item_to_section(ci), cec->asked->name1, CF_IDENT_ANY);
        if (subcs) {
-               if (section_name2) {
-                       section2 = talloc_strdup(NULL, section_name2);
+               if (cec->asked->name2) {
+                       section2 = talloc_strdup(NULL, cec->asked->name2);
                        p = section2;
                        while (*p != '\0') {
                                *(p) = tolower((uint8_t)*p);
@@ -1923,10 +1925,10 @@ static int logfile_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out,
 }
 
 static int query_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tmpl_rules_t const *t_rules,
-                               CONF_ITEM *ci, UNUSED char const *section_name1, char const *section_name2,
-                               void const *data, UNUSED call_env_parser_t const *rule)
+                               CONF_ITEM *ci,
+                               call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
 {
-       rlm_sql_t const         *inst = talloc_get_type_abort_const(data, rlm_sql_t);
+       rlm_sql_t const         *inst = talloc_get_type_abort_const(cec->mi->data, rlm_sql_t);
        CONF_SECTION const      *subcs = NULL;
        CONF_PAIR const         *to_parse = NULL;
        tmpl_t                  *parsed_tmpl;
@@ -1935,7 +1937,7 @@ static int query_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tm
        char                    *section2, *p;
        ssize_t                 count, slen, multi_index = 0;
 
-       if (!section_name2) return -1;
+       fr_assert(cec->type == CALL_ENV_CTX_TYPE_MODULE);
 
        /*
         *      Find the instance(s) of "query" to parse
@@ -1943,15 +1945,19 @@ static int query_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tm
         *      If the module call is from `accounting Start` then it should be
         *              <module> { accounting { start { query } } }
         */
-       section2 = talloc_strdup(NULL, section_name2);
+       section2 = talloc_strdup(NULL, section_name_str(cec->asked->name2));
        p = section2;
        while (*p != '\0') {
                *(p) = tolower((uint8_t)*p);
                p++;
        }
        subcs = cf_section_find(cf_item_to_section(ci), section2, CF_IDENT_ANY);
+       if (!subcs) {
+               cf_log_debug(ci, "No query found for \"%s\", this query will be disabled...", section2);
+               talloc_free(section2);
+               return 0;
+       }
        talloc_free(section2);
-       if (!subcs) return 0;
 
        /*
         *      Use module specific escape functions
@@ -1966,11 +1972,14 @@ static int query_call_env_parse(TALLOC_CTX *ctx, call_env_parsed_head_t *out, tm
 
        while ((to_parse = cf_pair_find_next(subcs, to_parse, "query"))) {
                MEM(parsed_env = call_env_parsed_add(ctx, out,
-                                                    &(call_env_parser_t){ FR_CALL_ENV_PARSE_ONLY_OFFSET("query", FR_TYPE_STRING, CALL_ENV_FLAG_CONCAT | CALL_ENV_FLAG_MULTI, sql_redundant_call_env_t, query)}));
+                                                    &(call_env_parser_t){
+                                                       FR_CALL_ENV_PARSE_ONLY_OFFSET("query", FR_TYPE_STRING, CALL_ENV_FLAG_CONCAT | CALL_ENV_FLAG_MULTI,
+                                                                                     sql_redundant_call_env_t, query)
+                                                    }));
 
                slen = tmpl_afrom_substr(parsed_env, &parsed_tmpl,
-                                     &FR_SBUFF_IN(cf_pair_value(to_parse), talloc_array_length(cf_pair_value(to_parse)) - 1),
-                                     cf_pair_value_quote(to_parse), NULL, &our_rules);
+                                        &FR_SBUFF_IN(cf_pair_value(to_parse), talloc_array_length(cf_pair_value(to_parse)) - 1),
+                                        cf_pair_value_quote(to_parse), NULL, &our_rules);
                if (slen <= 0) {
                        cf_canonicalize_error(to_parse, slen, "Failed parsing query", cf_pair_value(to_parse));
                error:
@@ -2215,8 +2224,8 @@ static int mod_bootstrap(module_inst_ctx_t const *mctx)
         *      The xlat escape function needs access to inst - so
         *      argument parser details need to be defined here
         */
-       sql_xlat_arg = talloc_zero_array(xlat, xlat_arg_parser_t, 2);
-       uctx = talloc_zero(sql_xlat_arg, rlm_sql_escape_uctx_t);
+       MEM(sql_xlat_arg = talloc_zero_array(xlat, xlat_arg_parser_t, 2));
+       MEM(uctx = talloc_zero(sql_xlat_arg, rlm_sql_escape_uctx_t));
        *uctx = (rlm_sql_escape_uctx_t){ .sql = inst, .handle = NULL };
        sql_xlat_arg[0] = (xlat_arg_parser_t){
                .type = FR_TYPE_STRING,
index 83ce70c732ded367581aff1b4c7da4f4f0bca59b..1bc7cc21b3b278573c57c448e3e61a7437f14748 100644 (file)
@@ -443,10 +443,9 @@ static unlang_action_t CC_HINT(nonnull) mod_authorize(rlm_rcode_t *p_result, mod
 /** Custom call_env parser to tokenize the SQL query xlat used for counter retrieval
  */
 static int call_env_query_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci,
-                               UNUSED char const *section_name1, UNUSED char const *section_name2,
-                               void const *data, UNUSED call_env_parser_t const *rule)
+                               call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
 {
-       rlm_sqlcounter_t const  *inst = talloc_get_type_abort_const(data, rlm_sqlcounter_t);
+       rlm_sqlcounter_t const  *inst = talloc_get_type_abort_const(cec->mi->data, rlm_sqlcounter_t);
        CONF_PAIR const         *to_parse = cf_item_to_pair(ci);
        char                    *query;
        xlat_exp_head_t         *ex;
index 688982fd48532c696eed580c1eccc1a545d64e07..8d1fb7990f1f11a74cd2b0eae6c0634c1d2630ab 100644 (file)
  * @copyright 2016 The FreeRADIUS server project
  * @copyright 2016 Matthew Newton (matthew@newtoncomputing.co.uk)
  */
-
 RCSID("$Id$")
 
 #include <freeradius-devel/server/base.h>
 #include <freeradius-devel/server/module_rlm.h>
+#include <freeradius-devel/unlang/call_env.h>
 #include <freeradius-devel/unlang/xlat_func.h>
 #include <freeradius-devel/util/debug.h>
 
@@ -438,8 +438,7 @@ static const call_env_method_t winbind_autz_method_env = {
 };
 
 static int domain_call_env_parse(TALLOC_CTX *ctx, void *out, tmpl_rules_t const *t_rules, CONF_ITEM *ci,
-                                UNUSED char const *section_name1, UNUSED char const *section_name2,
-                                UNUSED void const *data, UNUSED call_env_parser_t const *rule)
+                                UNUSED call_env_ctx_t const *cec, UNUSED call_env_parser_t const *rule)
 {
        CONF_PAIR const                 *to_parse = cf_item_to_pair(ci);
        tmpl_t                          *parsed_tmpl = NULL;
index 037e4ce7999618eff57cec5e9fb492965b74cb34..346b8fdc9e6004d23ad6ced4ddb16f7ae1ab3786 100644 (file)
@@ -99,8 +99,8 @@ $TEMPLATE template_test
        }
 
        redundant redundant_test {
-               test1.passthrough
-               test2.passthrough
+               test1
+               test2
        }
 
 }
index a042226f3edb6b42e5803f8a11f795fd5cbf8510..5d84ae7b911ff1c60e09048f83a9d48e4f5189a9 100644 (file)
@@ -13,7 +13,7 @@ if (!(%concat(%test2.passthrough(foo, bar), '|') == "foo|bar")) {
 #
 #  The config has a "redundant" block for test1 and test2.
 #
-if (!(%concat(%redundant_test(foo, bar), '|') == "foo|bar")) {
+if (!(%concat(%redundant_test.passthrough(foo, bar), '|') == "foo|bar")) {
        test_fail
 }