#endif
#include <freeradius-devel/unlang/base.h>
#include <freeradius-devel/unlang/xlat.h>
+#include <freeradius-devel/unlang/xlat_register.h>
#include <freeradius-devel/util/atexit.h>
#include <freeradius-devel/util/base64.h>
#include <freeradius-devel/util/calc.h>
*/
#include <freeradius-devel/server/base.h>
+#include <freeradius-devel/unlang/xlat_register.h>
+
#include "base.h"
#include "attrs.h"
#include <freeradius-devel/util/perm.h>
#include <freeradius-devel/util/sem.h>
+#include <freeradius-devel/unlang/xlat_register.h>
+
#include <sys/stat.h>
#include <pwd.h>
#include <grp.h>
/*
* Initialize the xlats before we load the configuration files, so that we can later call xlat_register().
*/
- xlat_init();
+ xlat_register_init();
if (stat(config->raddb_dir, &statbuf) < 0) {
ERROR("Error checking raddb_dir \"%s\": %s", config->raddb_dir, fr_syserror(errno));
#include <freeradius-devel/server/module_rlm.h>
#include <freeradius-devel/server/radmin.h>
#include <freeradius-devel/server/request_data.h>
+#include <freeradius-devel/unlang/xlat_register.h>
/** Heap of all lists/modules used to get a common index with module_thread_inst_list
*
#include <freeradius-devel/server/pair.h>
#include <freeradius-devel/server/virtual_servers.h>
#include <freeradius-devel/util/atexit.h>
+#include <freeradius-devel/unlang/xlat_register.h>
/** Lookup virtual module by name
*/
xlat_expr.c \
xlat_inst.c \
xlat_pair.c \
- xlat_tokenize.c \
- xlat_purify.c
+ xlat_purify.c \
+ xlat_register.c \
+ xlat_tokenize.c
HEADERS := $(subst src/lib/,,$(wildcard src/lib/unlang/*.h))
RCSID("$Id$")
#include <freeradius-devel/server/request_data.h>
+#include <freeradius-devel/unlang/xlat_register.h>
#include "foreach_priv.h"
#include "return_priv.h"
#include <freeradius-devel/server/base.h>
#include <freeradius-devel/server/modpriv.h>
+#include <freeradius-devel/unlang/xlat_register.h>
#include "interpret_priv.h"
#include "module_priv.h"
int xlat_resolve(xlat_exp_head_t *head, xlat_res_rules_t const *xr_rules);
-xlat_t *xlat_register_module(TALLOC_CTX *ctx, module_inst_ctx_t const *mctx,
- char const *name, xlat_func_t func, fr_type_t return_type);
-xlat_t *xlat_register(TALLOC_CTX *ctx, char const *name, xlat_func_t func, fr_type_t return_type) CC_HINT(nonnull(2));
-
-int xlat_func_args_set(xlat_t *xlat, xlat_arg_parser_t const args[]) CC_HINT(nonnull);
-
-int xlat_func_mono_set(xlat_t *xlat, xlat_arg_parser_t const *arg) CC_HINT(nonnull);
-
-void xlat_func_flags_set(xlat_t *x, xlat_flags_t const *flags) CC_HINT(nonnull);
-
-/** Set a callback for global instantiation of xlat functions
- *
- * @param[in] _xlat function to set the callback for (as returned by xlat_register).
- * @param[in] _instantiate A instantiation callback.
- * @param[in] _inst_struct The instance struct to pre-allocate.
- * @param[in] _detach A destructor callback.
- * @param[in] _uctx to pass to _instantiate and _detach callbacks.
- */
-#define xlat_async_instantiate_set(_xlat, _instantiate, _inst_struct, _detach, _uctx) \
- _xlat_async_instantiate_set(_xlat, _instantiate, #_inst_struct, sizeof(_inst_struct), _detach, _uctx)
-void _xlat_async_instantiate_set(xlat_t const *xlat,
- xlat_instantiate_t instantiate, char const *inst_type, size_t inst_size,
- xlat_detach_t detach,
- void *uctx);
-
-/** Set a callback for thread-specific instantiation of xlat functions
- *
- * @param[in] _xlat function to set the callback for (as returned by xlat_register).
- * @param[in] _instantiate A instantiation callback.
- * @param[in] _inst_struct The instance struct to pre-allocate.
- * @param[in] _detach A destructor callback.
- * @param[in] _uctx to pass to _instantiate and _detach callbacks.
- */
-#define xlat_async_thread_instantiate_set(_xlat, _instantiate, _inst_struct, _detach, _uctx) \
- _xlat_async_thread_instantiate_set(_xlat, _instantiate, #_inst_struct, sizeof(_inst_struct), _detach, _uctx)
-void _xlat_async_thread_instantiate_set(xlat_t const *xlat,
- xlat_thread_instantiate_t thread_instantiate,
- char const *thread_inst_type, size_t thread_inst_size,
- xlat_thread_detach_t thread_detach,
- void *uctx);
-
-void xlat_unregister(char const *name);
-void xlat_unregister_module(dl_module_inst_t const *inst);
-int xlat_register_redundant(CONF_SECTION *cs);
-/** @hidecallgraph */
-int xlat_init(void);
-void xlat_free(void);
-
void xlat_debug_attr_list(request_t *request, fr_pair_list_t const *list);
void xlat_debug_attr_vp(request_t *request, fr_pair_t *vp, tmpl_t const *vpt);
/*
xlat_action_t unlang_xlat_yield(request_t *request,
xlat_func_t callback, xlat_func_signal_t signal,
void *rctx);
+
+/*
+ * xlat_builtin.c
+ */
+int xlat_init(void);
+void xlat_free(void);
+
#ifdef __cplusplus
}
#endif
#include <freeradius-devel/server/base.h>
#include <freeradius-devel/server/tmpl_dcursor.h>
#include <freeradius-devel/unlang/xlat_priv.h>
+#include <freeradius-devel/unlang/xlat_register.h>
#include <freeradius-devel/unlang/xlat.h>
#include <freeradius-devel/io/test_point.h>
#ifdef HAVE_REGEX_PCRE2
#endif
-
-static fr_rb_tree_t *xlat_root = NULL;
-
static char const hextab[] = "0123456789abcdef";
/** Return a VP from the specified request.
return ret;
}
-
-/*
- * Compare two xlat_t structs, based ONLY on the module name.
- */
-static int8_t xlat_cmp(void const *one, void const *two)
-{
- xlat_t const *a = one, *b = two;
- size_t a_len, b_len;
- int ret;
-
- a_len = strlen(a->name);
- b_len = strlen(b->name);
-
- ret = CMP(a_len, b_len);
- if (ret != 0) return ret;
-
- ret = memcmp(a->name, b->name, a_len);
- return CMP(ret, 0);
-}
-
-
-/*
- * find the appropriate registered xlat function.
- */
-xlat_t *xlat_func_find(char const *in, ssize_t inlen)
-{
- char buffer[256];
-
- if (!xlat_root) return NULL;
-
- if (inlen < 0) return fr_rb_find(xlat_root, &(xlat_t){ .name = in });
-
- if ((size_t) inlen >= sizeof(buffer)) return NULL;
-
- memcpy(buffer, in, inlen);
- buffer[inlen] = '\0';
-
- return fr_rb_find(xlat_root, &(xlat_t){ .name = buffer });
-}
-
-
-/** Remove an xlat function from the function tree
- *
- * @param[in] xlat to free.
- * @return 0
- */
-static int _xlat_func_talloc_free(xlat_t *xlat)
-{
- if (!xlat_root) return 0;
-
- fr_rb_delete(xlat_root, xlat);
- if (fr_rb_num_elements(xlat_root) == 0) TALLOC_FREE(xlat_root);
-
- return 0;
-}
-
-
-/** Callback for the rbtree to clear out any xlats still registered
- *
- */
-static void _xlat_func_tree_free(void *xlat)
-{
- talloc_free(xlat);
-}
-
-#if 0
-/** Compare two argument entries to see if they're equivalent
- *
- * @note Does not check escape function or uctx pointers.
- *
- * @param[in] a First argument structure.
- * @param[in] b Second argument structure.
- * @return
- * - 1 if a > b
- * - 0 if a == b
- * - -1 if a < b
- */
-static int xlat_arg_cmp_no_escape(xlat_arg_parser_t const *a, xlat_arg_parser_t const *b)
-{
- int8_t ret;
-
- ret = CMP(a->required, b->required);
- if (ret != 0) return ret;
-
- ret = CMP(a->concat, b->concat);
- if (ret != 0) return ret;
-
- ret = CMP(a->single, b->single);
- if (ret != 0) return ret;
-
- ret = CMP(a->variadic, b->variadic);
- if (ret != 0) return ret;
-
- ret = CMP(a->always_escape, b->always_escape);
- if (ret != 0) return ret;
-
- return CMP(a->type, b->type);
-}
-
-/** Compare two argument lists to see if they're equivalent
- *
- * @note Does not check escape function or uctx pointers.
- *
- * @param[in] a First argument structure.
- * @param[in] b Second argument structure.
- * @return
- * - 1 if a > b
- * - 0 if a == b
- * - -1 if a < b
- */
-static int xlat_arg_cmp_list_no_escape(xlat_arg_parser_t const a[], xlat_arg_parser_t const b[])
-{
- xlat_arg_parser_t const *arg_a_p;
- xlat_arg_parser_t const *arg_b_p;
-
- for (arg_a_p = a, arg_b_p = b;
- (arg_a_p->type != FR_TYPE_NULL) && (arg_b_p->type != FR_TYPE_NULL);
- arg_a_p++, arg_b_p++) {
- int8_t ret;
-
- ret = xlat_arg_cmp_no_escape(arg_a_p, arg_b_p);
- if (ret != 0) return ret;
- }
-
- return CMP(arg_a_p, arg_b_p); /* Check we ended at the same point */
-}
-#endif
-
-/** Register an xlat function for a module
- *
- * @param[in] ctx Used to automate deregistration of the xlat fnction.
- * @param[in] mctx Instantiation context from the module.
- * Will be duplicated and passed to future xlat calls.
- * @param[in] name of the xlat.
- * @param[in] func to register.
- * @param[in] return_type what type of output the xlat function will produce.
- * @return
- * - A handle for the newly registered xlat function on success.
- * - NULL on failure.
- */
-xlat_t *xlat_register_module(TALLOC_CTX *ctx, module_inst_ctx_t const *mctx,
- char const *name, xlat_func_t func, fr_type_t return_type)
-{
- xlat_t *c;
- module_inst_ctx_t *our_mctx = NULL;
-
- fr_assert(xlat_root);
-
- if (!*name) {
- ERROR("%s: Invalid xlat name", __FUNCTION__);
- return NULL;
- }
-
- /*
- * If it already exists, replace the instance.
- */
- c = fr_rb_find(xlat_root, &(xlat_t){ .name = name });
- if (c) {
- if (c->internal) {
- ERROR("%s: Cannot re-define internal expansion %s", __FUNCTION__, name);
- return NULL;
- }
-
- if (c->func != func) {
- ERROR("%s: Cannot change callback function for %s", __FUNCTION__, name);
- return NULL;
- }
-
- return c;
- }
-
- /*
- * Doesn't exist. Create it.
- */
- MEM(c = talloc(ctx, xlat_t));
- if (mctx) {
- MEM(our_mctx = talloc_zero(c, module_inst_ctx_t)); /* Original won't stick around */
- memcpy(our_mctx, mctx, sizeof(*our_mctx));
- }
- *c = (xlat_t){
- .name = talloc_typed_strdup(c, name),
- .func = func,
- .return_type = return_type,
- .mctx = our_mctx,
- .input_type = XLAT_INPUT_UNPROCESSED /* set default - will be overridden if args are registered */
- };
- talloc_set_destructor(c, _xlat_func_talloc_free);
- DEBUG3("%s: %s", __FUNCTION__, c->name);
-
- if (fr_rb_replace(NULL, xlat_root, c) < 0) {
- ERROR("%s: Failed inserting xlat registration for %s", __FUNCTION__, c->name);
- talloc_free(c);
- return NULL;
- }
-
- return c;
-}
-
-/** Register an xlat function
- *
- * @param[in] ctx Used to automate deregistration of the xlat fnction.
- * @param[in] name of the xlat.
- * @param[in] func to register.
- * @param[in] return_type what type of output the xlat function will produce.
- * @return
- * - A handle for the newly registered xlat function on success.
- * - NULL on failure.
- */
-xlat_t *xlat_register(TALLOC_CTX *ctx, char const *name, xlat_func_t func, fr_type_t return_type)
-{
- return xlat_register_module(ctx, NULL, name, func, return_type);
-}
-
-/** Verify xlat arg specifications are valid
- *
- * @param[in] arg specification to validate.
- * @param[in] last Is this the last argument in the list.
- */
-static inline int xlat_arg_parser_validate(xlat_arg_parser_t const *arg, bool last)
-{
- if (arg->concat) {
- if (!fr_cond_assert_msg((arg->type == FR_TYPE_STRING) || (arg->type == FR_TYPE_OCTETS),
- "concat type must be string or octets")) return -1;
-
- if (!fr_cond_assert_msg(!arg->single, "concat and single are mutually exclusive")) return -1;
- }
-
- if (arg->single) {
- if (!fr_cond_assert_msg(!arg->concat, "single and concat are mutually exclusive")) return -1;
- }
-
- if (arg->variadic) {
- if (!fr_cond_assert_msg(last, "variadic can only be set on the last argument")) return -1;
- }
-
- if (arg->always_escape) {
- if (!fr_cond_assert_msg(arg->func, "always_escape requires an escape func")) return -1;
- }
-
- if (arg->uctx) {
- if (!fr_cond_assert_msg(arg->func, "uctx requires an escape func")) return -1;
- }
-
- switch (arg->type) {
- case FR_TYPE_LEAF:
- case FR_TYPE_VOID:
- break;
-
- default:
- fr_assert_fail("type must be a leaf box type");
- return -1;
- }
-
- return 0;
-}
-
-/** Register the arguments of an xlat
- *
- * For xlats that take multiple arguments
- *
- * @param[in,out] x to have it's arguments registered
- * @param[in] args to be registered
- * @return
- * - 0 on success.
- * - < 0 on failure.
- */
-int xlat_func_args_set(xlat_t *x, xlat_arg_parser_t const args[])
-{
- xlat_arg_parser_t const *arg_p = args;
- bool seen_optional = false;
-
- for (arg_p = args; arg_p->type != FR_TYPE_NULL; arg_p++) {
- if (xlat_arg_parser_validate(arg_p, (arg_p + 1)->type == FR_TYPE_NULL) < 0) return -1;
-
- if (arg_p->required) {
- if (!fr_cond_assert_msg(!seen_optional,
- "required arguments must be at the "
- "start of the argument list")) return -1;
- } else {
- seen_optional = true;
- }
- }
- x->args = args;
- x->input_type = XLAT_INPUT_ARGS;
-
- return 0;
-}
-
-/** Register the argument of an xlat
- *
- * For xlats that take all their input as a single argument
- *
- * @param[in,out] x to have it's arguments registered
- * @param[in] args to be registered
- * @return
- * - 0 on success.
- * - < 0 on failure.
- */
-int xlat_func_mono_set(xlat_t *x, xlat_arg_parser_t const args[])
-{
- if (xlat_func_args_set(x, args) < 0) return -1;
- x->input_type = XLAT_INPUT_MONO;
-
- return 0;
-}
-
-/** Specify flags that alter the xlat's behaviour
- *
- * @param[in] x xlat to set flags for.
- * @param[in] flags to set.
- */
-void xlat_func_flags_set(xlat_t *x, xlat_flags_t const *flags)
-{
- /*
- * If the function is async, it can't be pure. But
- * non-pure functions don't need to be async.
- */
- fr_assert(!flags->needs_resolving);
-
- x->flags = *flags;
-}
-
-/** Set global instantiation/detach callbacks
- *
- * @param[in] xlat to set instantiation callbacks for.
- * @param[in] instantiate Instantiation function. Called whenever a xlat is
- * compiled.
- * @param[in] inst_type Name of the instance structure.
- * @param[in] inst_size The size of the instance struct.
- * Pre-allocated for use by the instantiate function.
- * If 0, no memory will be allocated.
- * @param[in] detach Called when an xlat_exp_t is freed.
- * @param[in] uctx Passed to the instantiation function.
- */
-void _xlat_async_instantiate_set(xlat_t const *xlat,
- xlat_instantiate_t instantiate, char const *inst_type, size_t inst_size,
- xlat_detach_t detach,
- void *uctx)
-{
- xlat_t *c = UNCONST(xlat_t *, xlat);
-
- c->instantiate = instantiate;
- c->inst_type = inst_type;
- c->inst_size = inst_size;
- c->detach = detach;
- c->uctx = uctx;
-}
-
-/** Register an async xlat
- *
- * All functions registered must be !pure
- *
- * @param[in] xlat to set instantiation callbacks for.
- * @param[in] thread_instantiate Instantiation function. Called for every compiled xlat
- * every time a thread is started.
- * @param[in] thread_inst_type Name of the thread instance structure.
- * @param[in] thread_inst_size The size of the thread instance struct.
- * Pre-allocated for use by the instantiate function.
- * If 0, no memory will be allocated.
- * @param[in] thread_detach Called when the thread is freed.
- * @param[in] uctx Passed to the thread instantiate function.
- */
-void _xlat_async_thread_instantiate_set(xlat_t const *xlat,
- xlat_thread_instantiate_t thread_instantiate,
- char const *thread_inst_type, size_t thread_inst_size,
- xlat_thread_detach_t thread_detach,
- void *uctx)
-{
- xlat_t *c = UNCONST(xlat_t *, xlat);
-
- /*
- * Pure functions can't use any thread-local
- * variables. They MUST operate only on constant
- * instantiation data, and on their (possibly constant)
- * inputs.
- */
- fr_assert(!c->flags.pure);
-
- c->thread_instantiate = thread_instantiate;
- c->thread_inst_type = thread_inst_type;
- c->thread_inst_size = thread_inst_size;
- c->thread_detach = thread_detach;
- c->thread_uctx = uctx;
-}
-
-/** Unregister an xlat function
- *
- * We can only have one function to call per name, so the passing of "func"
- * here is extraneous.
- *
- * @param[in] name xlat to unregister.
- */
-void xlat_unregister(char const *name)
-{
- xlat_t *c;
-
- if (!name || !xlat_root) return;
-
- c = fr_rb_find(xlat_root, &(xlat_t){ .name = name });
- if (!c) return;
-
- (void) talloc_get_type_abort(c, xlat_t);
-
- talloc_free(c); /* Should also remove from tree */
-}
-
-void xlat_unregister_module(dl_module_inst_t const *inst)
-{
- xlat_t *c;
- fr_rb_iter_inorder_t iter;
-
- if (!xlat_root) return; /* All xlats have already been freed */
-
- for (c = fr_rb_iter_init_inorder(&iter, xlat_root);
- c;
- c = fr_rb_iter_next_inorder(&iter)) {
- if (!c->mctx) continue;
- if (c->mctx->inst != inst) continue;
-
- fr_rb_iter_delete_inorder(&iter);
- }
-}
-
/*
* Internal redundant handler for xlats
*/
{
xlat_t *xlat;
- if (xlat_root) return 0;
+ if (xlat_register_init() < 0) return -1;
/*
* Lookup attributes used by virtual xlat expansions.
*/
unlang_xlat_init();
- /*
- * Create the function tree
- */
- xlat_root = fr_rb_inline_talloc_alloc(NULL, xlat_t, node, xlat_cmp, _xlat_func_tree_free);
- if (!xlat_root) {
- ERROR("%s: Failed to create tree", __FUNCTION__);
- return -1;
- }
-
/*
* Define encode/decode xlats for the various protocols.
*/
return xlat_register_expressions();
}
-
/** De-register all xlat functions we created
*
*/
void xlat_free(void)
{
- fr_rb_tree_t *xr = xlat_root; /* Make sure the tree can't be freed multiple times */
-
- if (!xr) return;
-
- xlat_root = NULL;
- talloc_free(xr);
+ xlat_register_free();
xlat_eval_free();
}
#include <freeradius-devel/unlang/xlat_priv.h>
#include <freeradius-devel/util/calc.h>
#include <freeradius-devel/server/tmpl_dcursor.h>
+#include <freeradius-devel/unlang/xlat_register.h>
#undef XLAT_DEBUG
#ifdef DEBUG_XLAT
--- /dev/null
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ *
+ * @file xlat_register.c
+ * @brief Registration API for xlat functions
+ *
+ * @copyright 2023 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
+ */
+
+RCSID("$Id$")
+
+#include <freeradius-devel/unlang/xlat_priv.h>
+#include <freeradius-devel/unlang/xlat.h>
+#include <freeradius-devel/unlang/xlat_register.h>
+
+static fr_rb_tree_t *xlat_root = NULL;
+
+/*
+ * Compare two xlat_t structs, based ONLY on the module name.
+ */
+static int8_t xlat_cmp(void const *one, void const *two)
+{
+ xlat_t const *a = one, *b = two;
+ size_t a_len, b_len;
+ int ret;
+
+ a_len = strlen(a->name);
+ b_len = strlen(b->name);
+
+ ret = CMP(a_len, b_len);
+ if (ret != 0) return ret;
+
+ ret = memcmp(a->name, b->name, a_len);
+ return CMP(ret, 0);
+}
+
+/*
+ * find the appropriate registered xlat function.
+ */
+xlat_t *xlat_func_find(char const *in, ssize_t inlen)
+{
+ char buffer[256];
+
+ if (!xlat_root) return NULL;
+
+ if (inlen < 0) return fr_rb_find(xlat_root, &(xlat_t){ .name = in });
+
+ if ((size_t) inlen >= sizeof(buffer)) return NULL;
+
+ memcpy(buffer, in, inlen);
+ buffer[inlen] = '\0';
+
+ return fr_rb_find(xlat_root, &(xlat_t){ .name = buffer });
+}
+
+/** Remove an xlat function from the function tree
+ *
+ * @param[in] xlat to free.
+ * @return 0
+ */
+static int _xlat_func_talloc_free(xlat_t *xlat)
+{
+ if (!xlat_root) return 0;
+
+ fr_rb_delete(xlat_root, xlat);
+ if (fr_rb_num_elements(xlat_root) == 0) TALLOC_FREE(xlat_root);
+
+ return 0;
+}
+
+
+/** Callback for the rbtree to clear out any xlats still registered
+ *
+ */
+static void _xlat_func_tree_free(void *xlat)
+{
+ talloc_free(xlat);
+}
+
+#if 0
+/** Compare two argument entries to see if they're equivalent
+ *
+ * @note Does not check escape function or uctx pointers.
+ *
+ * @param[in] a First argument structure.
+ * @param[in] b Second argument structure.
+ * @return
+ * - 1 if a > b
+ * - 0 if a == b
+ * - -1 if a < b
+ */
+static int xlat_arg_cmp_no_escape(xlat_arg_parser_t const *a, xlat_arg_parser_t const *b)
+{
+ int8_t ret;
+
+ ret = CMP(a->required, b->required);
+ if (ret != 0) return ret;
+
+ ret = CMP(a->concat, b->concat);
+ if (ret != 0) return ret;
+
+ ret = CMP(a->single, b->single);
+ if (ret != 0) return ret;
+
+ ret = CMP(a->variadic, b->variadic);
+ if (ret != 0) return ret;
+
+ ret = CMP(a->always_escape, b->always_escape);
+ if (ret != 0) return ret;
+
+ return CMP(a->type, b->type);
+}
+
+/** Compare two argument lists to see if they're equivalent
+ *
+ * @note Does not check escape function or uctx pointers.
+ *
+ * @param[in] a First argument structure.
+ * @param[in] b Second argument structure.
+ * @return
+ * - 1 if a > b
+ * - 0 if a == b
+ * - -1 if a < b
+ */
+static int xlat_arg_cmp_list_no_escape(xlat_arg_parser_t const a[], xlat_arg_parser_t const b[])
+{
+ xlat_arg_parser_t const *arg_a_p;
+ xlat_arg_parser_t const *arg_b_p;
+
+ for (arg_a_p = a, arg_b_p = b;
+ (arg_a_p->type != FR_TYPE_NULL) && (arg_b_p->type != FR_TYPE_NULL);
+ arg_a_p++, arg_b_p++) {
+ int8_t ret;
+
+ ret = xlat_arg_cmp_no_escape(arg_a_p, arg_b_p);
+ if (ret != 0) return ret;
+ }
+
+ return CMP(arg_a_p, arg_b_p); /* Check we ended at the same point */
+}
+#endif
+
+/** Register an xlat function for a module
+ *
+ * @param[in] ctx Used to automate deregistration of the xlat fnction.
+ * @param[in] mctx Instantiation context from the module.
+ * Will be duplicated and passed to future xlat calls.
+ * @param[in] name of the xlat.
+ * @param[in] func to register.
+ * @param[in] return_type what type of output the xlat function will produce.
+ * @return
+ * - A handle for the newly registered xlat function on success.
+ * - NULL on failure.
+ */
+xlat_t *xlat_register_module(TALLOC_CTX *ctx, module_inst_ctx_t const *mctx,
+ char const *name, xlat_func_t func, fr_type_t return_type)
+{
+ xlat_t *c;
+ module_inst_ctx_t *our_mctx = NULL;
+
+ fr_assert(xlat_root);
+
+ if (!*name) {
+ ERROR("%s: Invalid xlat name", __FUNCTION__);
+ return NULL;
+ }
+
+ /*
+ * If it already exists, replace the instance.
+ */
+ c = fr_rb_find(xlat_root, &(xlat_t){ .name = name });
+ if (c) {
+ if (c->internal) {
+ ERROR("%s: Cannot re-define internal expansion %s", __FUNCTION__, name);
+ return NULL;
+ }
+
+ if (c->func != func) {
+ ERROR("%s: Cannot change callback function for %s", __FUNCTION__, name);
+ return NULL;
+ }
+
+ return c;
+ }
+
+ /*
+ * Doesn't exist. Create it.
+ */
+ MEM(c = talloc(ctx, xlat_t));
+ if (mctx) {
+ MEM(our_mctx = talloc_zero(c, module_inst_ctx_t)); /* Original won't stick around */
+ memcpy(our_mctx, mctx, sizeof(*our_mctx));
+ }
+ *c = (xlat_t){
+ .name = talloc_typed_strdup(c, name),
+ .func = func,
+ .return_type = return_type,
+ .mctx = our_mctx,
+ .input_type = XLAT_INPUT_UNPROCESSED /* set default - will be overridden if args are registered */
+ };
+ talloc_set_destructor(c, _xlat_func_talloc_free);
+ DEBUG3("%s: %s", __FUNCTION__, c->name);
+
+ if (fr_rb_replace(NULL, xlat_root, c) < 0) {
+ ERROR("%s: Failed inserting xlat registration for %s", __FUNCTION__, c->name);
+ talloc_free(c);
+ return NULL;
+ }
+
+ return c;
+}
+
+/** Register an xlat function
+ *
+ * @param[in] ctx Used to automate deregistration of the xlat fnction.
+ * @param[in] name of the xlat.
+ * @param[in] func to register.
+ * @param[in] return_type what type of output the xlat function will produce.
+ * @return
+ * - A handle for the newly registered xlat function on success.
+ * - NULL on failure.
+ */
+xlat_t *xlat_register(TALLOC_CTX *ctx, char const *name, xlat_func_t func, fr_type_t return_type)
+{
+ return xlat_register_module(ctx, NULL, name, func, return_type);
+}
+
+/** Verify xlat arg specifications are valid
+ *
+ * @param[in] arg specification to validate.
+ * @param[in] last Is this the last argument in the list.
+ */
+static inline int xlat_arg_parser_validate(xlat_arg_parser_t const *arg, bool last)
+{
+ if (arg->concat) {
+ if (!fr_cond_assert_msg((arg->type == FR_TYPE_STRING) || (arg->type == FR_TYPE_OCTETS),
+ "concat type must be string or octets")) return -1;
+
+ if (!fr_cond_assert_msg(!arg->single, "concat and single are mutually exclusive")) return -1;
+ }
+
+ if (arg->single) {
+ if (!fr_cond_assert_msg(!arg->concat, "single and concat are mutually exclusive")) return -1;
+ }
+
+ if (arg->variadic) {
+ if (!fr_cond_assert_msg(last, "variadic can only be set on the last argument")) return -1;
+ }
+
+ if (arg->always_escape) {
+ if (!fr_cond_assert_msg(arg->func, "always_escape requires an escape func")) return -1;
+ }
+
+ if (arg->uctx) {
+ if (!fr_cond_assert_msg(arg->func, "uctx requires an escape func")) return -1;
+ }
+
+ switch (arg->type) {
+ case FR_TYPE_LEAF:
+ case FR_TYPE_VOID:
+ break;
+
+ default:
+ fr_assert_fail("type must be a leaf box type");
+ return -1;
+ }
+
+ return 0;
+}
+
+/** Register the arguments of an xlat
+ *
+ * For xlats that take multiple arguments
+ *
+ * @param[in,out] x to have it's arguments registered
+ * @param[in] args to be registered
+ * @return
+ * - 0 on success.
+ * - < 0 on failure.
+ */
+int xlat_func_args_set(xlat_t *x, xlat_arg_parser_t const args[])
+{
+ xlat_arg_parser_t const *arg_p = args;
+ bool seen_optional = false;
+
+ for (arg_p = args; arg_p->type != FR_TYPE_NULL; arg_p++) {
+ if (xlat_arg_parser_validate(arg_p, (arg_p + 1)->type == FR_TYPE_NULL) < 0) return -1;
+
+ if (arg_p->required) {
+ if (!fr_cond_assert_msg(!seen_optional,
+ "required arguments must be at the "
+ "start of the argument list")) return -1;
+ } else {
+ seen_optional = true;
+ }
+ }
+ x->args = args;
+ x->input_type = XLAT_INPUT_ARGS;
+
+ return 0;
+}
+
+/** Register the argument of an xlat
+ *
+ * For xlats that take all their input as a single argument
+ *
+ * @param[in,out] x to have it's arguments registered
+ * @param[in] args to be registered
+ * @return
+ * - 0 on success.
+ * - < 0 on failure.
+ */
+int xlat_func_mono_set(xlat_t *x, xlat_arg_parser_t const args[])
+{
+ if (xlat_func_args_set(x, args) < 0) return -1;
+ x->input_type = XLAT_INPUT_MONO;
+
+ return 0;
+}
+
+/** Specify flags that alter the xlat's behaviour
+ *
+ * @param[in] x xlat to set flags for.
+ * @param[in] flags to set.
+ */
+void xlat_func_flags_set(xlat_t *x, xlat_flags_t const *flags)
+{
+ /*
+ * If the function is async, it can't be pure. But
+ * non-pure functions don't need to be async.
+ */
+ fr_assert(!flags->needs_resolving);
+
+ x->flags = *flags;
+}
+
+/** Set global instantiation/detach callbacks
+ *
+ * @param[in] xlat to set instantiation callbacks for.
+ * @param[in] instantiate Instantiation function. Called whenever a xlat is
+ * compiled.
+ * @param[in] inst_type Name of the instance structure.
+ * @param[in] inst_size The size of the instance struct.
+ * Pre-allocated for use by the instantiate function.
+ * If 0, no memory will be allocated.
+ * @param[in] detach Called when an xlat_exp_t is freed.
+ * @param[in] uctx Passed to the instantiation function.
+ */
+void _xlat_async_instantiate_set(xlat_t const *xlat,
+ xlat_instantiate_t instantiate, char const *inst_type, size_t inst_size,
+ xlat_detach_t detach,
+ void *uctx)
+{
+ xlat_t *c = UNCONST(xlat_t *, xlat);
+
+ c->instantiate = instantiate;
+ c->inst_type = inst_type;
+ c->inst_size = inst_size;
+ c->detach = detach;
+ c->uctx = uctx;
+}
+
+/** Register an async xlat
+ *
+ * All functions registered must be !pure
+ *
+ * @param[in] xlat to set instantiation callbacks for.
+ * @param[in] thread_instantiate Instantiation function. Called for every compiled xlat
+ * every time a thread is started.
+ * @param[in] thread_inst_type Name of the thread instance structure.
+ * @param[in] thread_inst_size The size of the thread instance struct.
+ * Pre-allocated for use by the instantiate function.
+ * If 0, no memory will be allocated.
+ * @param[in] thread_detach Called when the thread is freed.
+ * @param[in] uctx Passed to the thread instantiate function.
+ */
+void _xlat_async_thread_instantiate_set(xlat_t const *xlat,
+ xlat_thread_instantiate_t thread_instantiate,
+ char const *thread_inst_type, size_t thread_inst_size,
+ xlat_thread_detach_t thread_detach,
+ void *uctx)
+{
+ xlat_t *c = UNCONST(xlat_t *, xlat);
+
+ /*
+ * Pure functions can't use any thread-local
+ * variables. They MUST operate only on constant
+ * instantiation data, and on their (possibly constant)
+ * inputs.
+ */
+ fr_assert(!c->flags.pure);
+
+ c->thread_instantiate = thread_instantiate;
+ c->thread_inst_type = thread_inst_type;
+ c->thread_inst_size = thread_inst_size;
+ c->thread_detach = thread_detach;
+ c->thread_uctx = uctx;
+}
+
+/** Unregister an xlat function
+ *
+ * We can only have one function to call per name, so the passing of "func"
+ * here is extraneous.
+ *
+ * @param[in] name xlat to unregister.
+ */
+void xlat_unregister(char const *name)
+{
+ xlat_t *c;
+
+ if (!name || !xlat_root) return;
+
+ c = fr_rb_find(xlat_root, &(xlat_t){ .name = name });
+ if (!c) return;
+
+ (void) talloc_get_type_abort(c, xlat_t);
+
+ talloc_free(c); /* Should also remove from tree */
+}
+
+void xlat_unregister_module(dl_module_inst_t const *inst)
+{
+ xlat_t *c;
+ fr_rb_iter_inorder_t iter;
+
+ if (!xlat_root) return; /* All xlats have already been freed */
+
+ for (c = fr_rb_iter_init_inorder(&iter, xlat_root);
+ c;
+ c = fr_rb_iter_next_inorder(&iter)) {
+ if (!c->mctx) continue;
+ if (c->mctx->inst != inst) continue;
+
+ fr_rb_iter_delete_inorder(&iter);
+ }
+}
+
+int xlat_register_init(void)
+{
+ if (xlat_root) return 0;
+
+ /*
+ * Create the function tree
+ */
+ xlat_root = fr_rb_inline_talloc_alloc(NULL, xlat_t, node, xlat_cmp, _xlat_func_tree_free);
+ if (!xlat_root) {
+ ERROR("%s: Failed to create tree", __FUNCTION__);
+ return -1;
+ }
+
+ return 0;
+}
+
+void xlat_register_free(void)
+{
+ fr_rb_tree_t *xr = xlat_root; /* Make sure the tree can't be freed multiple times */
+
+ if (!xr) return;
+
+ xlat_root = NULL;
+ talloc_free(xr);
+}
--- /dev/null
+#pragma once
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/**
+ * $Id$
+ *
+ * @file lib/unlang/xlat_register.h
+ * @brief Registration API for xlat functions.
+ *
+ * @copyright 2023 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
+ */
+RCSIDH(xlat_register_h, "$Id$")
+
+#include <freeradius-devel/unlang/xlat.h>
+
+xlat_t *xlat_register_module(TALLOC_CTX *ctx, module_inst_ctx_t const *mctx,
+ char const *name, xlat_func_t func, fr_type_t return_type);
+xlat_t *xlat_register(TALLOC_CTX *ctx, char const *name, xlat_func_t func, fr_type_t return_type) CC_HINT(nonnull(2));
+
+int xlat_func_args_set(xlat_t *xlat, xlat_arg_parser_t const args[]) CC_HINT(nonnull);
+
+int xlat_func_mono_set(xlat_t *xlat, xlat_arg_parser_t const *arg) CC_HINT(nonnull);
+
+void xlat_func_flags_set(xlat_t *x, xlat_flags_t const *flags) CC_HINT(nonnull);
+
+/** Set a callback for global instantiation of xlat functions
+ *
+ * @param[in] _xlat function to set the callback for (as returned by xlat_register).
+ * @param[in] _instantiate A instantiation callback.
+ * @param[in] _inst_struct The instance struct to pre-allocate.
+ * @param[in] _detach A destructor callback.
+ * @param[in] _uctx to pass to _instantiate and _detach callbacks.
+ */
+#define xlat_async_instantiate_set(_xlat, _instantiate, _inst_struct, _detach, _uctx) \
+ _xlat_async_instantiate_set(_xlat, _instantiate, #_inst_struct, sizeof(_inst_struct), _detach, _uctx)
+void _xlat_async_instantiate_set(xlat_t const *xlat,
+ xlat_instantiate_t instantiate, char const *inst_type, size_t inst_size,
+ xlat_detach_t detach,
+ void *uctx);
+
+/** Set a callback for thread-specific instantiation of xlat functions
+ *
+ * @param[in] _xlat function to set the callback for (as returned by xlat_register).
+ * @param[in] _instantiate A instantiation callback.
+ * @param[in] _inst_struct The instance struct to pre-allocate.
+ * @param[in] _detach A destructor callback.
+ * @param[in] _uctx to pass to _instantiate and _detach callbacks.
+ */
+#define xlat_async_thread_instantiate_set(_xlat, _instantiate, _inst_struct, _detach, _uctx) \
+ _xlat_async_thread_instantiate_set(_xlat, _instantiate, #_inst_struct, sizeof(_inst_struct), _detach, _uctx)
+void _xlat_async_thread_instantiate_set(xlat_t const *xlat,
+ xlat_thread_instantiate_t thread_instantiate,
+ char const *thread_inst_type, size_t thread_inst_size,
+ xlat_thread_detach_t thread_detach,
+ void *uctx);
+
+void xlat_unregister(char const *name);
+void xlat_unregister_module(dl_module_inst_t const *inst);
+int xlat_register_redundant(CONF_SECTION *cs);
+/** @hidecallgraph */
+
+int xlat_register_init(void);
+void xlat_register_free(void);
#include <freeradius-devel/server/base.h>
#include <freeradius-devel/server/module_rlm.h>
+#include <freeradius-devel/unlang/xlat_register.h>
/*
* The instance data for rlm_always is the list of fake values we are
#include <freeradius-devel/server/modpriv.h>
#include <freeradius-devel/server/dl_module.h>
#include <freeradius-devel/util/debug.h>
+#include <freeradius-devel/unlang/xlat_register.h>
#include "rlm_cache.h"
#include <freeradius-devel/server/password.h>
#include <freeradius-devel/server/module_rlm.h>
#include <freeradius-devel/radius/radius.h>
+#include <freeradius-devel/unlang/xlat_register.h>
typedef struct {
fr_dict_enum_value_t *auth_type;
#include <freeradius-devel/tls/log.h>
#include <freeradius-devel/tls/strerror.h>
#include <freeradius-devel/util/debug.h>
+#include <freeradius-devel/unlang/xlat_register.h>
#include <freeradius-devel/tls/openssl_user_macros.h>
#include <openssl/crypto.h>
#include <freeradius-devel/server/module_rlm.h>
#include <freeradius-devel/server/map_proc.h>
#include <freeradius-devel/util/debug.h>
+#include <freeradius-devel/unlang/xlat_register.h>
/** Client field
*
#include <freeradius-devel/server/base.h>
#include <freeradius-devel/server/module_rlm.h>
+#include <freeradius-devel/unlang/xlat_register.h>
#include <ctype.h>
#include <time.h>
#include <freeradius-devel/util/debug.h>
#include <freeradius-devel/server/map_proc.h>
#include <freeradius-devel/util/time.h>
+#include <freeradius-devel/unlang/xlat_register.h>
typedef struct {
tmpl_t *delay; //!< How long we delay for.
#include <freeradius-devel/server/base.h>
#include <freeradius-devel/server/module_rlm.h>
#include <freeradius-devel/util/debug.h>
+#include <freeradius-devel/unlang/xlat_register.h>
static xlat_arg_parser_t const xlat_dict_attr_by_num_args[] = {
{ .required = true, .single = true, .type = FR_TYPE_UINT32 },
#include <freeradius-devel/server/module_rlm.h>
#include <freeradius-devel/util/debug.h>
+#include <freeradius-devel/unlang/xlat_register.h>
#include <ctype.h>
#include <freeradius-devel/server/tmpl.h>
#include <freeradius-devel/unlang/interpret.h>
#include <freeradius-devel/util/debug.h>
+#include <freeradius-devel/unlang/xlat_register.h>
/*
* Define a structure for our module configuration.
#include <freeradius-devel/server/module_rlm.h>
#include <freeradius-devel/util/cap.h>
#include <freeradius-devel/util/debug.h>
+#include <freeradius-devel/unlang/xlat_register.h>
#include <fcntl.h>
#include <unistd.h>
#include <freeradius-devel/server/base.h>
#include <freeradius-devel/server/module_rlm.h>
+#include <freeradius-devel/unlang/xlat_register.h>
#include <idna.h>
#include <freeradius-devel/server/module_rlm.h>
#include <freeradius-devel/server/map_proc.h>
#include <freeradius-devel/util/debug.h>
+#include <freeradius-devel/unlang/xlat_register.h>
#include <freeradius-devel/json/base.h>
#include <ctype.h>
#include <freeradius-devel/server/map_proc.h>
#include <freeradius-devel/server/module_rlm.h>
+#include <freeradius-devel/unlang/xlat_register.h>
+
static CONF_PARSER sasl_mech_dynamic[] = {
{ FR_CONF_OFFSET("mech", FR_TYPE_TMPL | FR_TYPE_NOT_EMPTY, fr_ldap_sasl_t_dynamic_t, mech) },
{ FR_CONF_OFFSET("proxy", FR_TYPE_TMPL, fr_ldap_sasl_t_dynamic_t, proxy) },
#include <freeradius-devel/util/iovec.h>
#include <freeradius-devel/util/perm.h>
+#include <freeradius-devel/unlang/xlat_register.h>
+
#ifdef HAVE_FCNTL_H
# include <fcntl.h>
#endif
#include <freeradius-devel/util/misc.h>
#include <freeradius-devel/util/sha1.h>
+#include <freeradius-devel/unlang/xlat_register.h>
+
#include <sys/wait.h>
#include <ctype.h>
#include <freeradius-devel/server/base.h>
#include <freeradius-devel/server/module_rlm.h>
#include <freeradius-devel/util/debug.h>
+#include <freeradius-devel/unlang/xlat_register.h>
#include <freeradius-devel/radius/radius.h>
DIAG_OFF(DIAG_UNKNOWN_PRAGMAS)
#include <freeradius-devel/server/module_rlm.h>
#include <freeradius-devel/server/modpriv.h>
#include <freeradius-devel/util/debug.h>
+#include <freeradius-devel/unlang/xlat_register.h>
#include <freeradius-devel/redis/base.h>
#include <freeradius-devel/redis/cluster.h>
#include <freeradius-devel/util/debug.h>
#include <freeradius-devel/util/table.h>
#include <freeradius-devel/util/uri.h>
+#include <freeradius-devel/unlang/xlat_register.h>
#include <ctype.h>
#include "rest.h"
#include <freeradius-devel/server/base.h>
#include <freeradius-devel/server/module_rlm.h>
#include <freeradius-devel/soh/base.h>
+#include <freeradius-devel/unlang/xlat_register.h>
typedef struct {
bool dhcp;
#include <freeradius-devel/server/pairmove.h>
#include <freeradius-devel/util/debug.h>
#include <freeradius-devel/util/table.h>
+#include <freeradius-devel/unlang/xlat_register.h>
#include <sys/stat.h>
#include <freeradius-devel/server/module_rlm.h>
#include <freeradius-devel/server/tmpl.h>
#include <freeradius-devel/util/debug.h>
+#include <freeradius-devel/unlang/xlat_register.h>
/*
* Define a structure for our module configuration.
#include <freeradius-devel/server/base.h>
#include <freeradius-devel/server/module_rlm.h>
#include <freeradius-devel/server/log.h>
+#include <freeradius-devel/unlang/xlat_register.h>
#include <fcntl.h>
#include "io.h"
#include <freeradius-devel/server/base.h>
#include <freeradius-devel/server/module_rlm.h>
-
+#include <freeradius-devel/unlang/xlat_register.h>
#include <freeradius-devel/util/base16.h>
#include <ctype.h>
RCSID("$Id$")
#include <freeradius-devel/radius/radius.h>
+#include <freeradius-devel/unlang/xlat_register.h>
#include "rlm_yubikey.h"
#ifdef HAVE_YKCLIENT