From: Arran Cudbard-Bell Date: Thu, 18 Jan 2018 01:50:16 +0000 (-0700) Subject: Add xlat instantiation framework X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=607cac9eb452ef335fb5eff8d43d1f602c59fd81;p=thirdparty%2Ffreeradius-server.git Add xlat instantiation framework --- diff --git a/src/include/xlat.h b/src/include/xlat.h index 218943d5dc9..782ec4f3126 100644 --- a/src/include/xlat.h +++ b/src/include/xlat.h @@ -134,6 +134,35 @@ typedef int (*xlat_instantiate_t)(void *xlat_inst, xlat_exp_t const *exp, void * typedef int (*xlat_thread_instantiate_t)(void *xlat_inst, void *xlat_thread_inst, xlat_exp_t const *exp, void *uctx); +/** xlat detach callback + * + * Is called whenever an xlat_node_t is freed. + * + * Detach should close all handles associated with the xlat instance, and + * free any memory allocated during instantiate. + * + * @param[in] instance to free. + * @return + * - 0 on success. + * - -1 if detach failed. + */ +typedef int (*xlat_detach_t)(void *xlat_inst, void *uctx); + +/** xlat thread detach callback + * + * Is called whenever an xlat_node_t is freed (if ephemeral), + * or when a thread exits. + * + * Detach should close all handles associated with the xlat instance, and + * free any memory allocated during instantiate. + * + * @param[in] instance to free. + * @return + * - 0 on success. + * - -1 if detach failed. + */ +typedef int (*xlat_thread_detach_t)(void *xlat_thread_inst, void *uctx); + xlat_action_t xlat_frame_eval_repeat(TALLOC_CTX *ctx, fr_cursor_t *out, xlat_exp_t const **child, bool *alternate, REQUEST *request, xlat_exp_t const **in, @@ -172,7 +201,9 @@ int xlat_register(void *mod_inst, char const *name, int xlat_async_register(TALLOC_CTX *ctx, char const *name, xlat_func_async_t func, xlat_instantiate_t instantiate, size_t inst_size, + xlat_detach_t detach, xlat_thread_instantiate_t thread_instantiate, size_t thread_inst_size, + xlat_thread_detach_t thread_detach, void *uctx); void xlat_unregister(char const *name); @@ -181,6 +212,19 @@ int xlat_register_redundant(CONF_SECTION *cs); int xlat_init(void); void xlat_free(void); +/* + * xlat_inst.c + */ +int xlat_thread_instantiate(void); + +int xlat_instatiate_request(xlat_exp_t *root); + +int xlat_instantiate(xlat_exp_t *root); + +int xlat_inst_init(void); + +void xlat_inst_free(void); + #ifdef __cplusplus } #endif diff --git a/src/main/libfreeradius-server.mk b/src/main/libfreeradius-server.mk index e3a576e16c8..7b0d76c8753 100644 --- a/src/main/libfreeradius-server.mk +++ b/src/main/libfreeradius-server.mk @@ -27,6 +27,7 @@ SOURCES := cond_eval.c \ unlang_op.c \ xlat_eval.c \ xlat_func.c \ + xlat_inst.c \ xlat_tokenize.c # This lets the linker determine which version of the SSLeay functions to use. diff --git a/src/main/xlat_eval.c b/src/main/xlat_eval.c index 5ef64c87d3f..2011013f7c0 100644 --- a/src/main/xlat_eval.c +++ b/src/main/xlat_eval.c @@ -32,7 +32,7 @@ RCSID("$Id$") #include #include -#include "xlat.h" +#include "xlat_priv.h" FR_NAME_NUMBER const xlat_action_table[] = { { "push-child", XLAT_ACTION_PUSH_CHILD }, @@ -46,7 +46,6 @@ FR_NAME_NUMBER const xlat_action_table[] = { static size_t xlat_process(TALLOC_CTX *ctx, char **out, REQUEST *request, xlat_exp_t const * const head, xlat_escape_t escape, void const *escape_ctx); - /** One letter expansions * * @param[in] ctx to allocate boxed value, and buffers in. @@ -569,8 +568,10 @@ xlat_action_t xlat_frame_eval_repeat(TALLOC_CTX *ctx, fr_cursor_t *out, { xlat_action_t action; - /* Fixme - Pass in instance and thread instance */ - action = node->xlat->func.async(ctx, out, request, NULL, NULL, result); + /* Fixme - We should always have node->inst and node->thread_inst */ + action = node->xlat->func.async(ctx, out, request, + node->inst ? node->inst->data : NULL, + node->thread_inst ? node->thread_inst->data : NULL, result); switch (action) { case XLAT_ACTION_PUSH_CHILD: case XLAT_ACTION_YIELD: @@ -1238,3 +1239,62 @@ ssize_t xlat_aeval_compiled(TALLOC_CTX *ctx, char **out, REQUEST *request, *out = NULL; return _xlat_eval_compiled(ctx, out, 0, request, xlat, escape, escape_ctx); } + +/** Walk over all xlat nodes (depth first) in a xlat expansion, calling a callback + * + * @param[in] exp to evaluate. + * @param[in] walker callback to pass nodes to. + * @param[in] type if > 0 a mask of types to call walker for. + * @param[in] uctx to pass to walker. + * @return + * - 0 on success (walker always returned 0). + * - <0 if walker returned <0. + */ +int xlat_eval_walk(xlat_exp_t *exp, xlat_walker_t walker, xlat_state_t type, void *uctx) +{ + xlat_exp_t *node; + int ret; + + /* + * Iterate over nodes at the same depth + */ + for (node = exp; node; node = node->next) { + switch (node->type){ + case XLAT_FUNC: + if (!type || (type & XLAT_FUNC)) { + ret = walker(node, uctx); + if (ret < 0) return ret; + } + + /* + * Now evaluate the function's arguments + */ + if (node->child) { + ret = xlat_eval_walk(node->child, walker, type, uctx); + if (ret < 0) return ret; + } + break; + + case XLAT_ALTERNATE: + if (!type || (type & XLAT_ALTERNATE)) { + ret = walker(node, uctx); + if (ret < 0) return ret; + } + + /* + * Evaluate the alternate expansion path + */ + ret = xlat_eval_walk(node->alternate, walker, type, uctx); + if (ret < 0) return ret; + break; + + default: + if (!type || (type & node->type)) { + ret = walker(node, uctx); + if (ret < 0) return ret; + } + } + } + + return 0; +} diff --git a/src/main/xlat_func.c b/src/main/xlat_func.c index f865214940d..e301240cd9c 100644 --- a/src/main/xlat_func.c +++ b/src/main/xlat_func.c @@ -32,7 +32,7 @@ RCSID("$Id$") #include #include -#include "xlat.h" +#include "xlat_priv.h" static rbtree_t *xlat_root = NULL; @@ -595,7 +595,7 @@ static int xlat_cmp(void const *one, void const *two) /* * find the appropriate registered xlat function. */ -xlat_t *xlat_find(char const *name) +xlat_t *xlat_func_find(char const *name) { xlat_t find; xlat_t *found; @@ -718,12 +718,15 @@ static int _xlat_free(xlat_t *xlat) * @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] thread_instantiate thread_instantiation_function. Called whenever a * a thread is started to create thread local instance * data. * @param[in] thread_inst_size The size of the thread instance struct. * Pre-allocated for use by the thread instance function. * If 0, no memory will be allocated. + * @param[in] thread_detach Called when an xlat_exp_t is freed (if ephemeral), + * or when a thread exits. * @param[in] uctx To pass to instantiate callbacks and the xlat function * when it's called. Usually the module instance that * registered the xlat. @@ -734,7 +737,9 @@ static int _xlat_free(xlat_t *xlat) int xlat_async_register(TALLOC_CTX *ctx, char const *name, xlat_func_async_t func, xlat_instantiate_t instantiate, size_t inst_size, + xlat_detach_t detach, xlat_thread_instantiate_t thread_instantiate, size_t thread_inst_size, + xlat_thread_detach_t thread_detach, void *uctx) { xlat_t *c; @@ -775,10 +780,16 @@ int xlat_async_register(TALLOC_CTX *ctx, c->func.async = func; c->type = XLAT_FUNC_ASYNC; + c->instantiate = instantiate; c->thread_instantiate = thread_instantiate; + c->inst_size = inst_size; c->thread_inst_size = thread_inst_size; + + c->detach = detach; + c->thread_detach = thread_detach; + c->async_safe = false; /* async safe in this case means it might yield */ c->uctx = uctx; @@ -878,7 +889,7 @@ static ssize_t xlat_redundant(TALLOC_CTX *ctx, char **out, NDEBUG_UNUSED size_t name = cf_pair_attr(cf_item_to_pair(ci)); rad_assert(name != NULL); - xlat = xlat_find(name); + xlat = xlat_func_find(name); if (!xlat) continue; if (xlat->buf_len > 0) { @@ -943,7 +954,7 @@ static ssize_t xlat_load_balance(TALLOC_CTX *ctx, char **out, NDEBUG_UNUSED size name = cf_pair_attr(cf_item_to_pair(found)); rad_assert(name != NULL); - xlat = xlat_find(name); + xlat = xlat_func_find(name); if (!xlat) return -1; if (xlat->buf_len > 0) { @@ -969,7 +980,7 @@ static ssize_t xlat_load_balance(TALLOC_CTX *ctx, char **out, NDEBUG_UNUSED size name = cf_pair_attr(cf_item_to_pair(ci)); rad_assert(name != NULL); - xlat = xlat_find(name); + xlat = xlat_func_find(name); if (xlat) { ssize_t rcode; @@ -1017,7 +1028,7 @@ int xlat_register_redundant(CONF_SECTION *cs) name1 = cf_section_name1(cs); name2 = cf_section_name2(cs); - if (xlat_find(name2)) { + if (xlat_func_find(name2)) { cf_log_err(cs, "An expansion is already registered for this name"); return -1; } @@ -1060,7 +1071,7 @@ int xlat_register_redundant(CONF_SECTION *cs) * This is ok, it just means the module * doesn't have an xlat method. */ - if (!xlat_find(attr)) { + if (!xlat_func_find(attr)) { talloc_free(xr); return 1; } @@ -1105,14 +1116,14 @@ int xlat_init(void) #ifdef WITH_UNLANG for (i = 0; xlat_foreach_names[i] != NULL; i++) { xlat_register(&xlat_foreach_inst[i], xlat_foreach_names[i], xlat_foreach, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true); - c = xlat_find(xlat_foreach_names[i]); + c = xlat_func_find(xlat_foreach_names[i]); rad_assert(c != NULL); c->internal = true; } #endif #define XLAT_REGISTER(_x) xlat_register(NULL, STRINGIFY(_x), xlat_ ## _x, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true); \ - c = xlat_find(STRINGIFY(_x)); \ + c = xlat_func_find(STRINGIFY(_x)); \ rad_assert(c != NULL); \ c->internal = true @@ -1131,7 +1142,7 @@ int xlat_init(void) #endif xlat_register(&xlat_foreach_inst[0], "debug", xlat_debug, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true); - c = xlat_find("debug"); + c = xlat_func_find("debug"); rad_assert(c != NULL); c->internal = true; diff --git a/src/main/xlat_inst.c b/src/main/xlat_inst.c new file mode 100644 index 00000000000..2d44ec4dcc3 --- /dev/null +++ b/src/main/xlat_inst.c @@ -0,0 +1,374 @@ +/* + * 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_inst.c + * @brief Create instance data for xlat function calls. + * + * @copyright 2018 The FreeRADIUS server project + * @copyright 2018 Arran Cudbard-Bell + */ +RCSID("$Id$") + +#include +#include + +#include + +#include "xlat_priv.h" + +/** Holds instance data created by xlat_instantiate + */ +static rbtree_t *xlat_inst_tree; + +/** Holds thread specific instance data created by xlat_instantiate + */ +fr_thread_local_setup(rbtree_t *, xlat_thread_inst_tree) + +/** Destructor for xlat_inst_t + * + * Calls detach method if provided by xlat expansion + * + * @note This cannot be converted to a talloc destructor, + * as we need to call thread_detach *before* any of the children + * of the talloc ctx are freed. + */ +static int _xlat_inst_free(xlat_inst_t *inst) +{ + rad_assert(inst->node->type == XLAT_FUNC); + + /* + * Remove permanent data from the instance tree. + */ + if (!inst->node->ephemeral) rbtree_deletebydata(xlat_inst_tree, inst); + + if (inst->node->xlat->detach) (void) inst->node->xlat->detach(inst->data, inst->node->xlat->uctx); + + return 0; +} + +/** Compare two xlat instances based on node pointer + * + * @param[in] a First xlat expansion instance. + * @param[in] b Second xlat expansion instance. + * @return + * - +1 if a > b. + * - -1 if a < b. + * - 0 if a == b. + */ +static int _xlat_inst_cmp(void const *a, void const *b) +{ + xlat_thread_inst_t const *my_a = a, *my_b = b; + + return (my_a->node > my_b->node) - (my_a->node < my_b->node); +} + +/** Compare two thread instances based on node pointer + * + * @param[in] a First thread specific xlat expansion instance. + * @param[in] b Second thread specific xlat expansion instance. + * @return + * - +1 if a > b. + * - -1 if a < b. + * - 0 if a == b. + */ +static int _xlat_thread_inst_cmp(void const *a, void const *b) +{ + xlat_thread_inst_t const *my_a = a, *my_b = b; + + return (my_a->node > my_b->node) - (my_a->node < my_b->node); +} + +/** Destructor for xlat_thread_inst_t + * + * Calls thread_detach method if provided by xlat expansion + * + * @note This cannot be converted to a talloc destructor, + * as we need to call thread_detach *before* any of the children + * of the talloc ctx are freed. + */ +static void _xlat_thread_inst_free(void *to_free) +{ + xlat_thread_inst_t *thread_inst = talloc_get_type_abort(to_free, xlat_thread_inst_t); + + rad_assert(thread_inst->node->type == XLAT_FUNC); + + if (thread_inst->node->xlat->thread_detach) { + (void) thread_inst->node->xlat->thread_detach(thread_inst->data, thread_inst->node->xlat->uctx); + } + + talloc_free(thread_inst); +} + +/** Frees the thread local instance free and any thread local instance data + * + * @param[in] to_free Thread specific module instance tree to free. + */ +static void _xlat_thread_inst_tree_free(void *to_free) +{ + rbtree_t *thread_inst_tree = talloc_get_type_abort(to_free , rbtree_t); + + talloc_free(thread_inst_tree); +} + +/** Create thread instances where needed + * + * @param[in] node to perform thread instantiation for. + * @return + * - 0 on success. The node/thread specific data will be inserted + * into xlat_thread_inst_tree. + * - -1 on failure. + */ +static xlat_thread_inst_t *xlat_thread_inst_alloc(xlat_exp_t *node) +{ + xlat_thread_inst_t *thread_inst = NULL; + int ret; + + (void)talloc_get_type_abort(node, xlat_exp_t); + + MEM(thread_inst = talloc_zero(NULL, xlat_thread_inst_t)); + thread_inst->node = node; + + rad_assert(node->type == XLAT_FUNC); + rad_assert(!node->thread_inst); /* May be missing inst, but this is OK */ + + if (node->xlat->thread_inst_size) { + MEM(thread_inst->data = talloc_zero_array(thread_inst, uint8_t, node->xlat->thread_inst_size)); + + /* + * This is expensive, only do it if we might + * might be using it. + */ +#ifndef TALLOC_GET_TYPE_ABORT_NOOP + talloc_set_name(thread_inst, "%s_thread_inst_t", node->xlat->name); +#endif + } + + if (node->xlat->thread_instantiate) { + ret = node->xlat->thread_instantiate(node->inst, thread_inst->data, node, node->xlat->uctx); + if (ret < 0) { + talloc_free(thread_inst); + return NULL; + } + } + + return thread_inst; +} + +/** Walker callback for xlat_inst_tree + * + */ +static int _xlat_thread_instantiate(UNUSED void *ctx, void *data) +{ + xlat_thread_inst_t *thread_inst; + + thread_inst = xlat_thread_inst_alloc(data); + if (!thread_inst) return -1; + + rbtree_insert(xlat_thread_inst_tree, thread_inst); + + return 0; +} + +/** Create thread specific instance tree and create thread instances + * + * This should be called directly after the module_thread_instantiate function. + * + * Memory will be freed automatically when the thread exits. + */ +int xlat_thread_instantiate(void) +{ + int ret; + + if (!xlat_thread_inst_tree) { + MEM(xlat_thread_inst_tree = rbtree_create(NULL, _xlat_thread_inst_cmp, _xlat_thread_inst_free, 0)); + fr_thread_local_set_destructor(xlat_thread_inst_tree, + _xlat_thread_inst_tree_free, xlat_thread_inst_tree); + } + + /* + * Walk the inst tree, creating thread + * specific instances. + */ + ret = rbtree_walk(xlat_inst_tree, RBTREE_PRE_ORDER, _xlat_thread_instantiate, NULL); + if (ret < 0) { + TALLOC_FREE(xlat_thread_inst_tree); /* Destroy the thread_inst_tree if instantiation fails */ + return -1; + } + + return 0; +} + +/** Allocate instance data for an xlat expansion + * + * @param[in] node to allocate instance data for. + */ +static xlat_inst_t *xlat_inst_alloc(xlat_exp_t *node) +{ + xlat_inst_t *inst = NULL; + + rad_assert(xlat_inst_tree); /* xlat_inst_init must have been called */ + rad_assert(node->type == XLAT_FUNC); + rad_assert(!node->inst); + + /* + * Instance data is freed when the + * node is freed. + */ + MEM(inst = talloc_zero(node, xlat_inst_t)); + talloc_set_destructor(inst, _xlat_inst_free); + + if (node->xlat->inst_size) { + MEM(inst->data = talloc_zero_array(inst, uint8_t, node->xlat->inst_size)); + + /* + * This is expensive, only do it if we might + * might be using it. + */ +#ifndef TALLOC_GET_TYPE_ABORT_NOOP + talloc_set_name(inst, "%s_inst_t", node->xlat->name); +#endif + } + + if (node->xlat->instantiate) { + int ret; + + ret = node->xlat->instantiate(inst->data, node, node->xlat->uctx); + if (ret < 0) { + talloc_free(inst); + return NULL; + } + } + + return inst; +} + +/** Callback for creating "ephemeral" instance data for a #xlat_exp_t + * + * @param[in] node to create "ephemeral" instance data for. + * @param[in] uctx UNUSED. + * @return + * - 0 if instantiation functions were successful. + * - -1 if either instantiation function failed. + */ +static int _xlat_instantiate_request_walker(xlat_exp_t *node, UNUSED void *uctx) +{ + rad_assert(!node->inst && !node->thread_inst); + + node->inst = xlat_inst_alloc(node); + if (!node->inst) return -1; + + node->thread_inst = xlat_thread_inst_alloc(node); + if (!node->thread_inst) { + TALLOC_FREE(node->inst); + return -1; + } + + return 0; +} + +/** Create instance data for "ephemeral" xlats + * + * @node This must only be used for xlats created at runtime. + * + * @param[in] root of xlat tree to create instance data for. + */ +int xlat_instatiate_request(xlat_exp_t *root) +{ + return xlat_eval_walk(root, _xlat_instantiate_request_walker, XLAT_FUNC, NULL); +} + +/** Callback for creating "permanent" instance data for a #xlat_exp_t + * + * @param[in] node to create "permanent" instance data for. + * @param[in] uctx UNUSED. + * @return + * - 0 if instantiation functions were successful. + * - -1 if either instantiation function failed. + */ +static int _xlat_instantiate_walker(xlat_exp_t *node, UNUSED void *uctx) +{ + xlat_thread_inst_t *thread_inst; + bool ret; + + rad_assert(!node->inst && !node->thread_inst); + + node->inst = xlat_inst_alloc(node); + if (!node->inst) return -1; + + thread_inst = xlat_thread_inst_alloc(node); + if (!thread_inst) { + TALLOC_FREE(node->inst); + return -1; + } + + ret = rbtree_insert(xlat_inst_tree, node->inst); + if (!fr_cond_assert(ret)) { + insert_error: + TALLOC_FREE(node->inst); + talloc_free(thread_inst); + return -1; + } + + ret = rbtree_insert(xlat_thread_inst_tree, thread_inst); + if (!ret) goto insert_error; + + return 0; +} + +/** Create instance data for "permanent" xlats + * + * @note This must only be used for xlats created during startup. + * IF THIS IS CALLED FOR XLATS TOKENIZED AT RUNTIME YOU WILL LEAK LARGE AMOUNTS OF MEMORY. + * USE #xlat_instantiate_request INSTEAD. + * + * @param[in] root of xlat tree to create instance data for. + */ +int xlat_instantiate(xlat_exp_t *root) +{ + return xlat_eval_walk(root, _xlat_instantiate_walker, XLAT_FUNC, NULL); +} + +/** Initialise the xlat inst code + * + * Call xlat_inst_free when done. + */ +int xlat_inst_init(void) +{ + if (xlat_inst_tree) return 0; + + xlat_inst_tree = rbtree_create(NULL, _xlat_inst_cmp, NULL, RBTREE_FLAG_NONE); + if (!xlat_inst_tree) return -1; + + return 0; +} + +/** Free the main xlat instance tree + * + * @note Will not free thread/expansion specific data. This will be freed as threads + * exit. + */ +void xlat_inst_free(void) +{ + /* + * All xlat_exp_t should have been freed + * before xlat_inst_free is called. + */ + rad_assert(rbtree_num_elements(xlat_inst_tree) == 0); + talloc_free(xlat_inst_tree); +} diff --git a/src/main/xlat.h b/src/main/xlat_priv.h similarity index 56% rename from src/main/xlat.h rename to src/main/xlat_priv.h index e5f90164291..f5967712218 100644 --- a/src/main/xlat.h +++ b/src/main/xlat_priv.h @@ -17,7 +17,7 @@ /** * $Id$ * - * @file main/xlat.h + * @file main/xlat_priv.h * @brief String expansion ("translation"). Implements %Attribute -> value * * Private structures for the xlat tokenizer and xlat eval code. @@ -25,6 +25,12 @@ * @copyright 2000,2006 The FreeRADIUS server project * @copyright 2000 Alan DeKok */ +#ifndef _FR_XLAT_PRIV_H +#define _FR_XLAT_PRIV_H + +#ifdef __cplusplus +extern "C" { +#endif #ifdef DEBUG_XLAT # define XLAT_DEBUG RDEBUG3 @@ -52,6 +58,10 @@ typedef struct xlat_t { xlat_instantiate_t instantiate; //!< Instantiation function. xlat_thread_instantiate_t thread_instantiate; //!< Thread instantiation function. + xlat_detach_t detach; //!< Destructor for when xlat instances are freed. + xlat_thread_detach_t thread_detach; //!< Destructor for when xlat thread instance data + ///< is freed. + bool internal; //!< If true, cannot be redefined. size_t inst_size; //!< Size of instance data to pre-allocate. @@ -65,20 +75,45 @@ typedef struct xlat_t { xlat_escape_t escape; //!< Escape function to apply to dynamic input to func. } xlat_t; + typedef enum { - XLAT_LITERAL, //!< Literal string - XLAT_ONE_LETTER, //!< Literal string with %v - XLAT_FUNC, //!< xlat module - XLAT_VIRTUAL, //!< virtual attribute - XLAT_ATTRIBUTE, //!< xlat attribute + XLAT_LITERAL = 0x01, //!< Literal string + XLAT_ONE_LETTER = 0x02, //!< Literal string with %v + XLAT_FUNC = 0x04, //!< xlat module + XLAT_VIRTUAL = 0x08, //!< virtual attribute + XLAT_ATTRIBUTE = 0x10, //!< xlat attribute #ifdef HAVE_REGEX - XLAT_REGEX, //!< regex reference + XLAT_REGEX = 0x11, //!< regex reference #endif - XLAT_ALTERNATE //!< xlat conditional syntax :- + XLAT_ALTERNATE = 0x12 //!< xlat conditional syntax :- } xlat_state_t; + +/** Instance data for an xlat expansion node + * + */ +typedef struct { + xlat_exp_t *node; //!< Node this data relates to. + void *data; //!< xlat node specific instance data. +} xlat_inst_t; + +/** Thread specific instance data for xlat expansion node + * + */ +typedef struct { + xlat_exp_t *node; //!< Node this data relates to. + void *data; //!< Thread specific instance data. + + uint64_t total_calls; //! total number of times we've been called + uint64_t active_callers; //! number of active callers. i.e. number of current yields +} xlat_thread_inst_t; + +/** An xlat expansion node + * + * These nodes form a tree which represents one or more nested expansions. + */ struct xlat_exp { - char const *fmt; //!< The format string. + char const *fmt; //!< The original format string. size_t len; //!< Length of the format string. bool async_safe; //!< carried from all of the children @@ -95,7 +130,18 @@ struct xlat_exp { int regex_index; //!< for %{1} and friends. }; - xlat_t const *xlat; //!< The xlat expansion to expand format with. + + /* + * An xlat function + */ + struct { + xlat_t const *xlat; //!< The xlat expansion to expand format with. + bool ephemeral; //!< Instance data is ephemeral (not inserted) + ///< into the instance tree. + xlat_inst_t *inst; //!< Instance data for the #xlat_t. + xlat_thread_inst_t *thread_inst; //!< Thread specific instance. + ///< ONLY USED FOR EPHEMERAL XLATS. + }; }; typedef struct xlat_out { @@ -103,7 +149,33 @@ typedef struct xlat_out { size_t len; //!< Length of the output string. } xlat_out_t; +/** Walker callback for xlat_walk() + * + * @param[in] exp being evaluated. + * @param[in] uctx passed to xlat_walk. + * @return + * - 0 on success. + * - <0 if node evaluation failed. Causes xlat_walk to return the negative integer. + */ +typedef int (*xlat_walker_t)(xlat_exp_t *exp, void *uctx); + +/* + * xlat_tokenize.c + */ +ssize_t xlat_tokenize_request(TALLOC_CTX *ctx, REQUEST *request, char const *fmt, xlat_exp_t **head); + +/* + * xlat_func.c + */ +xlat_t *xlat_func_find(char const *name); -ssize_t xlat_tokenize_request(TALLOC_CTX *ctx, REQUEST *request, char const *fmt, xlat_exp_t **head); +/* + * xlat_eval.c + */ +int xlat_eval_walk(xlat_exp_t *exp, xlat_walker_t walker, xlat_state_t type, void *uctx); -xlat_t *xlat_find(char const *name); +#ifdef __cplusplus +} +#endif + +#endif diff --git a/src/main/xlat_tokenize.c b/src/main/xlat_tokenize.c index 6fee1cb6ed2..c005699a383 100644 --- a/src/main/xlat_tokenize.c +++ b/src/main/xlat_tokenize.c @@ -30,7 +30,7 @@ RCSID("$Id$") #include #include -#include "xlat.h" +#include "xlat_priv.h" #undef XLAT_DEBUG #ifdef DEBUG_XLAT @@ -261,7 +261,7 @@ static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, char *fmt, xlat_exp_t ** */ if (*q == ':') { *q = '\0'; - node->xlat = xlat_find(node->fmt); + node->xlat = xlat_func_find(node->fmt); if (node->xlat) { /* * %{mod:foo} @@ -317,7 +317,7 @@ static ssize_t xlat_tokenize_expansion(TALLOC_CTX *ctx, char *fmt, xlat_exp_t ** * Might be a virtual XLAT attribute */ if (node->attr->type == TMPL_TYPE_ATTR_UNDEFINED) { - node->xlat = xlat_find(node->attr->tmpl_unknown_name); + node->xlat = xlat_func_find(node->attr->tmpl_unknown_name); if (node->xlat && node->xlat->mod_inst && !node->xlat->internal) { talloc_free(node); *error = "Missing content in expansion";