From: Arran Cudbard-Bell Date: Thu, 18 Jan 2018 21:25:00 +0000 (-0700) Subject: Plumb in xlat instances X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=10cf7f848ce2ae3e0230db78132be90c247fee05;p=thirdparty%2Ffreeradius-server.git Plumb in xlat instances Delay instantiation of xlats until xlat_instantiate() is called. This means the boostrap phase of the server is fully complete by the time we start creating xlat instances. --- diff --git a/src/include/xlat.h b/src/include/xlat.h index 782ec4f3126..6edf1e08fc7 100644 --- a/src/include/xlat.h +++ b/src/include/xlat.h @@ -40,6 +40,26 @@ typedef enum { XLAT_ACTION_FAIL //!< An xlat function failed. } xlat_action_t; +/** Instance data for an xlat expansion node + * + */ +typedef struct { + xlat_exp_t const *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 const *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; + + extern FR_NAME_NUMBER const xlat_action_table[]; typedef size_t (*xlat_escape_t)(REQUEST *request, char *out, size_t outlen, char const *in, void *arg); @@ -215,15 +235,17 @@ void xlat_free(void); /* * xlat_inst.c */ -int xlat_thread_instantiate(void); +int xlat_instantiate_ephemeral(xlat_exp_t *root); -int xlat_instatiate_request(xlat_exp_t *root); +xlat_thread_inst_t *xlat_thread_instance_find(xlat_exp_t const *node); + +int xlat_thread_instantiate(void); -int xlat_instantiate(xlat_exp_t *root); +int xlat_instantiate(void); -int xlat_inst_init(void); +int xlat_bootstrap(xlat_exp_t *root); -void xlat_inst_free(void); +void xlat_instances_free(void); #ifdef __cplusplus } diff --git a/src/main/process.c b/src/main/process.c index 387f47f7043..b91a86dce97 100644 --- a/src/main/process.c +++ b/src/main/process.c @@ -366,7 +366,12 @@ int radius_event_start(UNUSED bool have_children) if (!spawn_workers) { if (modules_thread_instantiate(main_config.config, event_list) < 0) { ERROR("Failed to instantiate thread-specific data for modules"); - return 0; + return -1; + } + + if (xlat_thread_instantiate() < 0) { + ERROR("Failed to instantiate thread-specific data for xlats"); + return -1; } } diff --git a/src/main/radiusd.c b/src/main/radiusd.c index 3d68370cb8a..d190654044d 100644 --- a/src/main/radiusd.c +++ b/src/main/radiusd.c @@ -31,6 +31,7 @@ RCSID("$Id$") #include #include +#include #include #include #include @@ -129,6 +130,17 @@ static int talloc_config_set(main_config_t *config) return 0; } +/** Create module and xlat per-thread instances + * + */ +static int thread_instantiate(void *ctx, fr_event_list_t *el) +{ + if (modules_thread_instantiate(ctx, el) < 0) return -1; + if (xlat_thread_instantiate() < 0) return -1; + + return 0; +} + /* * The main guy. */ @@ -526,6 +538,11 @@ int main(int argc, char *argv[]) */ radius_pid = getpid(); + /* + * Initialise the interpreter, registering operations. + */ + if (unlang_initialize() < 0) exit(EXIT_FAILURE); + /* * Initialize Auth-Type, etc. in the virtual servers * before loading the modules. Some modules need those @@ -558,6 +575,11 @@ int main(int argc, char *argv[]) */ if (modules_instantiate(main_config.config) < 0) exit(EXIT_FAILURE); + /* + * Instantiate "permanent" xlats + */ + if (xlat_instantiate() < 0) exit(EXIT_FAILURE); + /* * Everything seems to have loaded OK, exit gracefully. */ @@ -612,7 +634,7 @@ int main(int argc, char *argv[]) sc = fr_schedule_create(NULL, el, &default_log, rad_debug_lvl, networks, workers, - (fr_schedule_thread_instantiate_t) modules_thread_instantiate, + thread_instantiate, main_config.config); if (!sc) { exit(EXIT_FAILURE); @@ -785,6 +807,11 @@ int main(int argc, char *argv[]) talloc_free(global_state); /* Free state entries */ cleanup: + /* + * Free xlat instance data, and call any detach methods + */ + xlat_instances_free(); + /* * Detach modules, connection pools, registered xlats / paircompares / maps. */ diff --git a/src/main/unit_test_module.c b/src/main/unit_test_module.c index 8ba8c29bc89..cb22b693cf5 100644 --- a/src/main/unit_test_module.c +++ b/src/main/unit_test_module.c @@ -825,6 +825,11 @@ int main(int argc, char *argv[]) cf_section_add(main_config.config, cf_section_alloc(main_config.config, main_config.config, "server", "unit_test")); + /* + * Initialise the interpreter, registering operations. + */ + if (unlang_initialize() < 0) exit(EXIT_FAILURE); + /* * Initialize Auth-Type, etc. in the virtual servers * before loading the modules. Some modules need those @@ -845,6 +850,11 @@ int main(int argc, char *argv[]) */ if (modules_instantiate(main_config.config) < 0) goto exit_failure; + /* + * Call xlat instantiation functions. + */ + if (xlat_instantiate() < 0) exit(EXIT_FAILURE); + /* * Create a dummy event list */ @@ -855,6 +865,7 @@ int main(int argc, char *argv[]) * Perform any thread specific instantiation */ if (modules_thread_instantiate(main_config.config, el) < 0) goto exit_failure; + if (xlat_thread_instantiate() < 0) goto exit_failure; /* * And then load the virtual servers. @@ -1056,13 +1067,21 @@ finish: talloc_free(request); talloc_free(state); - xlat_unregister("poke"); - /* * Free the event list. */ talloc_free(el); + /* + * Free xlat instance data, and call any detach methods + */ + xlat_instances_free(); + + /* + * Unregister poke *after* freeing instances that depend on it + */ + xlat_unregister("poke"); + /* * Detach modules, connection pools, registered xlats / paircompares / maps. */ diff --git a/src/main/virtual_servers.c b/src/main/virtual_servers.c index 829b0b687f6..3ae6bcf8bea 100644 --- a/src/main/virtual_servers.c +++ b/src/main/virtual_servers.c @@ -460,8 +460,6 @@ int virtual_servers_bootstrap(CONF_SECTION *config) virtual_server_root = config; - (void) unlang_initialize(); - if (virtual_servers) { /* * Check the talloc hierarchy is sane diff --git a/src/main/xlat_eval.c b/src/main/xlat_eval.c index 2011013f7c0..be485991ed2 100644 --- a/src/main/xlat_eval.c +++ b/src/main/xlat_eval.c @@ -566,12 +566,11 @@ xlat_action_t xlat_frame_eval_repeat(TALLOC_CTX *ctx, fr_cursor_t *out, case XLAT_FUNC_ASYNC: { - xlat_action_t action; + xlat_action_t action; + xlat_thread_inst_t *thread_inst; - /* 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); + thread_inst = xlat_thread_instance_find(node); + action = node->xlat->func.async(ctx, out, request, node->inst, thread_inst->data, result); switch (action) { case XLAT_ACTION_PUSH_CHILD: case XLAT_ACTION_YIELD: @@ -1188,7 +1187,7 @@ static ssize_t _xlat_eval(TALLOC_CTX *ctx, char **out, size_t outlen, REQUEST *r /* * Give better errors than the old code. */ - len = xlat_tokenize_request(ctx, request, fmt, &node); + len = xlat_tokenize_ephemeral(ctx, request, fmt, &node); if (len == 0) { if (*out) { **out = '\0'; diff --git a/src/main/xlat_func.c b/src/main/xlat_func.c index e301240cd9c..7b0e253b5d9 100644 --- a/src/main/xlat_func.c +++ b/src/main/xlat_func.c @@ -35,6 +35,7 @@ RCSID("$Id$") #include "xlat_priv.h" static rbtree_t *xlat_root = NULL; +static bool freeing_tree = false; #ifdef WITH_UNLANG static char const * const xlat_foreach_names[] = {"Foreach-Variable-0", @@ -608,6 +609,29 @@ xlat_t *xlat_func_find(char const *name) return found; } +/** Remove an xlat function from the function tree + * + * @param[in] xlat to free. + * @return 0 + */ +static int _xlat_free(xlat_t *xlat) +{ + bool found; + + if (!xlat_root || freeing_tree) return 0; + + found = rbtree_deletebydata(xlat_root, xlat); + if (!fr_cond_assert(found)) return -1; + + /* + * Automatically remove the tree + * if all xlats have been de-registered. + */ + if (rbtree_num_elements(xlat_root) == 0) TALLOC_FREE(xlat_root); + + return 0; +} + /** Register an xlat function. * * @param[in] mod_inst Instance of module that's registering the xlat function. @@ -661,6 +685,7 @@ int xlat_register(void *mod_inst, char const *name, } else { c = talloc_zero(xlat_root, xlat_t); c->name = talloc_typed_strdup(c, name); + talloc_set_destructor(c, _xlat_free); new = true; } @@ -685,27 +710,6 @@ int xlat_register(void *mod_inst, char const *name, return 0; } -/** Remove an xlat function from the function tree - * - * @param[in] xlat to free. - * @return 0 - */ -static int _xlat_free(xlat_t *xlat) -{ - xlat_t *c; - - if (!xlat_root) return 0; - - c = rbtree_finddata(xlat_root, xlat); - if (!c) return 0; - - rbtree_deletebydata(xlat_root, c); - - if (rbtree_num_elements(xlat_root) == 0) TALLOC_FREE(xlat_root); - - return 0; -} - /** Register an async xlat * * All functions registered must be async_safe. @@ -775,6 +779,7 @@ int xlat_async_register(TALLOC_CTX *ctx, } else { c = talloc_zero(ctx, xlat_t); c->name = talloc_typed_strdup(c, name); + talloc_set_destructor(c, _xlat_free); new = true; } @@ -793,8 +798,6 @@ int xlat_async_register(TALLOC_CTX *ctx, c->async_safe = false; /* async safe in this case means it might yield */ c->uctx = uctx; - talloc_set_destructor(c, _xlat_free); - DEBUG3("%s: %s", c->name, __FUNCTION__); if (new && !rbtree_insert(xlat_root, c)) { @@ -816,17 +819,16 @@ int xlat_async_register(TALLOC_CTX *ctx, void xlat_unregister(char const *name) { xlat_t *c; - xlat_t find; + xlat_t find = { .name = name }; if (!name || !xlat_root) return; - find.name = name; c = rbtree_finddata(xlat_root, &find); if (!c) return; - rbtree_deletebydata(xlat_root, c); + (void) talloc_get_type_abort(c, xlat_t); - if (rbtree_num_elements(xlat_root) == 0) TALLOC_FREE(xlat_root); + talloc_free(c); /* Should also remove from tree */ } static int _xlat_unregister_callback(void *mod_inst, void *data) @@ -843,8 +845,6 @@ void xlat_unregister_module(void *instance) if (!xlat_root) return; /* All xlats have already been freed */ rbtree_walk(xlat_root, RBTREE_DELETE_ORDER, _xlat_unregister_callback, instance); - - if (rbtree_num_elements(xlat_root) == 0) TALLOC_FREE(xlat_root); } /* @@ -863,7 +863,6 @@ typedef struct xlat_redundant_t { CONF_SECTION const *cs; } xlat_redundant_t; - static ssize_t xlat_redundant(TALLOC_CTX *ctx, char **out, NDEBUG_UNUSED size_t outlen, void const *mod_inst, UNUSED void const *xlat_inst, REQUEST *request, char const *fmt) @@ -1149,12 +1148,14 @@ int xlat_init(void) return 0; } -/** De-register all xlat functions, used mainly for debugging. +/** De-register all xlat functions we created * */ void xlat_free(void) { + freeing_tree = true; TALLOC_FREE(xlat_root); + freeing_tree = false; } diff --git a/src/main/xlat_inst.c b/src/main/xlat_inst.c index c7246b86f4c..48dfed09101 100644 --- a/src/main/xlat_inst.c +++ b/src/main/xlat_inst.c @@ -35,6 +35,7 @@ RCSID("$Id$") /** Holds instance data created by xlat_instantiate */ static rbtree_t *xlat_inst_tree; +static bool freeing_tree = false; /** Holds thread specific instance data created by xlat_instantiate */ @@ -55,7 +56,10 @@ static int _xlat_inst_free(xlat_inst_t *inst) /* * Remove permanent data from the instance tree. */ - if (!inst->node->ephemeral) rbtree_deletebydata(xlat_inst_tree, inst); + if (!inst->node->ephemeral && !freeing_tree) { + rbtree_deletebydata(xlat_inst_tree, inst); + if (rbtree_num_elements(xlat_inst_tree) == 0) TALLOC_FREE(xlat_inst_tree); + } if (inst->node->xlat->detach) (void) inst->node->xlat->detach(inst->data, inst->node->xlat->uctx); @@ -128,7 +132,7 @@ static void _xlat_thread_inst_tree_free(void *to_free) /** Create thread instances where needed * - * @param[in] node to perform thread instantiation for. + * @param[in] node to allocate instance data for. * @return * - 0 on success. The node/thread specific data will be inserted * into xlat_thread_inst_tree. @@ -137,11 +141,18 @@ static void _xlat_thread_inst_tree_free(void *to_free) 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)); +#ifdef HAVE_TALLOC_POOLED_OBJECT + if (node->xlat->thread_inst_size) { + MEM(thread_inst = talloc_pooled_object(NULL, xlat_thread_inst_t, node->xlat->thread_inst_size)) + } else +#endif + MEM(thread_inst = talloc_zero(NULL, xlat_thread_inst_t)); +#ifdef HAVE_TALLOC_POOLED_OBJECT + } +#endif thread_inst->node = node; rad_assert(node->type == XLAT_FUNC); @@ -159,61 +170,9 @@ static xlat_thread_inst_t *xlat_thread_inst_alloc(xlat_exp_t *node) #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. @@ -226,13 +185,23 @@ static xlat_inst_t *xlat_inst_alloc(xlat_exp_t *node) rad_assert(node->type == XLAT_FUNC); rad_assert(!node->inst); +#ifdef HAVE_TALLOC_POOLED_OBJECT + if (node->xlat->inst_size) { + MEM(inst = talloc_pooled_object(node, xlat_inst_t, node->xlat->inst_size)) + } else +#endif + MEM(inst = talloc_zero(node, xlat_inst_t)); +#ifdef HAVE_TALLOC_POOLED_OBJECT + } +#endif + + inst->node = node; + /* * 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)); @@ -245,20 +214,12 @@ static xlat_inst_t *xlat_inst_alloc(xlat_exp_t *node) #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 + * + * @note Epehemeral xlats must not be shared between requests. * * @param[in] node to create "ephemeral" instance data for. * @param[in] uctx UNUSED. @@ -266,19 +227,40 @@ static xlat_inst_t *xlat_inst_alloc(xlat_exp_t *node) * - 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) +static int _xlat_instantiate_ephemeral_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) { + /* + * Instantiate immediately unlike permanent XLATs + * Where it's a separate phase. + */ + if (node->xlat->instantiate && + (node->xlat->instantiate(node->inst->data, node, node->xlat->uctx) < 0)) { + error: TALLOC_FREE(node->inst); return -1; } + /* + * Create a thread instance too. + */ + node->thread_inst = xlat_thread_inst_alloc(node); + if (!node->thread_inst) goto error; + talloc_set_destructor(node->thread_inst, (int (*)(xlat_thread_inst_t *))_xlat_thread_inst_free); + + if (node->xlat->thread_instantiate && + node->xlat->thread_instantiate(node->inst, node->thread_inst->data, node, node->xlat->uctx) < 0) goto error; + + /* + * Mark this up as an ephemeral node, so the destructors + * don't search for it in the xlat_inst_tree. + */ + node->ephemeral = true; + return 0; } @@ -288,12 +270,138 @@ static int _xlat_instantiate_request_walker(xlat_exp_t *node, UNUSED void *uctx) * * @param[in] root of xlat tree to create instance data for. */ -int xlat_instatiate_request(xlat_exp_t *root) +int xlat_instantiate_ephemeral(xlat_exp_t *root) +{ + return xlat_eval_walk(root, _xlat_instantiate_ephemeral_walker, XLAT_FUNC, NULL); +} + +/** Walker callback for xlat_inst_tree + * + */ +static int _xlat_thread_instantiate(UNUSED void *ctx, void *data) +{ + xlat_thread_inst_t *thread_inst; + xlat_exp_t *node = talloc_get_type_abort(data, xlat_exp_t); + + thread_inst = xlat_thread_inst_alloc(data); + if (!thread_inst) return -1; + + if (node->xlat->thread_instantiate) { + int ret; + + ret = node->xlat->thread_instantiate(node->inst, thread_inst->data, node, node->xlat->uctx); + if (ret < 0) { + talloc_free(thread_inst); + return -1; + } + } + + rbtree_insert(xlat_thread_inst_tree, thread_inst); + + return 0; +} + +/** Retrieve xlat/thread specific instance data + * + * @param[in] node to find thread specific data for. + * @return + * - Thread specific data on success. + * - NULL if the xlat has no thread instance data (should not happen). + */ +xlat_thread_inst_t *xlat_thread_instance_find(xlat_exp_t const *node) { - return xlat_eval_walk(root, _xlat_instantiate_request_walker, XLAT_FUNC, NULL); + xlat_thread_inst_t *found; + + rad_assert(xlat_thread_inst_tree); + + rad_assert(node->type == XLAT_FUNC); + + if (node->ephemeral) return node->thread_inst; + + { + xlat_thread_inst_t find = { .node = node }; + + found = rbtree_finddata(xlat_thread_inst_tree, &find); + } + + rad_assert(found); + + return found; +} + +/** Create thread specific instance tree and create thread instances + * + * This should be called directly after the modules_thread_instantiate() function. + * + * Memory will be freed automatically when the thread exits. + */ +int xlat_thread_instantiate(void) +{ + int ret; + + if (!xlat_inst_tree) return 0; + + 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) { + _xlat_thread_inst_tree_free(xlat_thread_inst_tree); /* Destroy the thread_inst_tree if instantiation fails */ + return -1; + } + + return 0; +} + +/** Walk over #xlat_exp_t that require instantiation + * + * @param[in] ctx UNUSED. + * @param[in] data node to perform + */ +static int _xlat_instantiate_walker(UNUSED void *ctx, void *data) +{ + xlat_inst_t *inst = talloc_get_type_abort(data, xlat_inst_t); + + if (inst->node->xlat->instantiate && + (inst->node->xlat->instantiate(inst->data, inst->node, inst->node->xlat->uctx) < 0)) return -1; + + return 0; +} + +/** Initialise the xlat inst code + * + */ +static int xlat_instantiate_init(void) +{ + xlat_inst_tree = rbtree_create(NULL, _xlat_inst_cmp, NULL, RBTREE_FLAG_NONE); + if (!xlat_inst_tree) return -1; + + return 0; +} + +/** Call instantiation functions for "permanent" xlats + * + * Should be called after module instantiation is complete. + */ +int xlat_instantiate(void) +{ + if (!xlat_inst_tree) xlat_instantiate_init(); + + return rbtree_walk(xlat_inst_tree, RBTREE_PRE_ORDER, _xlat_instantiate_walker, NULL); } /** Callback for creating "permanent" instance data for a #xlat_exp_t + * + * This function records the #xlat_exp_t requiring instantiation but does + * not call the instantiation function. This is to allow for a clear separation + * between the module instantiation phase and the xlat instantiation phase. * * @param[in] node to create "permanent" instance data for. * @param[in] uctx UNUSED. @@ -301,9 +409,8 @@ int xlat_instatiate_request(xlat_exp_t *root) * - 0 if instantiation functions were successful. * - -1 if either instantiation function failed. */ -static int _xlat_instantiate_walker(xlat_exp_t *node, UNUSED void *uctx) +static int _xlat_bootstrap_walker(xlat_exp_t *node, UNUSED void *uctx) { - xlat_thread_inst_t *thread_inst; bool ret; rad_assert(!node->inst && !node->thread_inst); @@ -311,23 +418,12 @@ static int _xlat_instantiate_walker(xlat_exp_t *node, UNUSED void *uctx) 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; } @@ -339,36 +435,31 @@ static int _xlat_instantiate_walker(xlat_exp_t *node, UNUSED void *uctx) * * @param[in] root of xlat tree to create instance data for. */ -int xlat_instantiate(xlat_exp_t *root) +int xlat_bootstrap(xlat_exp_t *root) { - return xlat_eval_walk(root, _xlat_instantiate_walker, XLAT_FUNC, NULL); + if (!xlat_inst_tree) xlat_instantiate_init(); + + return xlat_eval_walk(root, _xlat_bootstrap_walker, XLAT_FUNC, NULL); } -/** Initialise the xlat inst code - * - * Call xlat_inst_free when done. - */ -int xlat_inst_init(void) +static int _xlat_instance_free_walker(UNUSED void *ctx, void *data) { - 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; + return talloc_free(data); } -/** Free the main xlat instance tree +/** Walk over all registered instance data and free them explicitly * - * @note Will not free thread/expansion specific data. This will be freed as threads - * exit. + * This must be called before any modules or xlats are deregistered/unloaded and before + * the mainconfig is freed, as the xlat_t need to still exist in order to call + * the detach functions within them. */ -void xlat_inst_free(void) +void xlat_instances_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); + if (!xlat_inst_tree) return; + + freeing_tree = true; + rbtree_walk(xlat_inst_tree, RBTREE_DELETE_ORDER, _xlat_instance_free_walker, NULL); + freeing_tree = false; + + TALLOC_FREE(xlat_inst_tree); } diff --git a/src/main/xlat_priv.h b/src/main/xlat_priv.h index f5967712218..f013d3ca834 100644 --- a/src/main/xlat_priv.h +++ b/src/main/xlat_priv.h @@ -88,26 +88,6 @@ typedef enum { 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. @@ -162,7 +142,7 @@ 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); +ssize_t xlat_tokenize_ephemeral(TALLOC_CTX *ctx, REQUEST *request, char const *fmt, xlat_exp_t **head); /* * xlat_func.c diff --git a/src/main/xlat_tokenize.c b/src/main/xlat_tokenize.c index c005699a383..5522e067f76 100644 --- a/src/main/xlat_tokenize.c +++ b/src/main/xlat_tokenize.c @@ -759,8 +759,12 @@ size_t xlat_snprint(char *buffer, size_t bufsize, xlat_exp_t const *node) * @param[in] request the input request. Memory will be attached here. * @param[in] fmt the format string to expand. * @param[out] head the head of the xlat list / tree structure. + * @return + * - <= -1 on error. Return value is negative offset of where parsing + * error occured. + * - >= 0 on success. The number of bytes parsed. */ -ssize_t xlat_tokenize_request(TALLOC_CTX *ctx, REQUEST *request, char const *fmt, xlat_exp_t **head) +ssize_t xlat_tokenize_ephemeral(TALLOC_CTX *ctx, REQUEST *request, char const *fmt, xlat_exp_t **head) { ssize_t slen; char *tokens; @@ -813,6 +817,16 @@ ssize_t xlat_tokenize_request(TALLOC_CTX *ctx, REQUEST *request, char const *fmt */ (void) talloc_steal(*head, tokens); + /* + * Create ephemeral instance data for the xlat + */ + if (xlat_instantiate_ephemeral(*head) < 0) { + talloc_free(*head); + + REDEBUG("Failed performing ephemeral instantiation for xlat"); + return -1; + } + return slen; } @@ -822,9 +836,26 @@ ssize_t xlat_tokenize_request(TALLOC_CTX *ctx, REQUEST *request, char const *fmt * @param[in] fmt the format string to expand. * @param[out] head the head of the xlat list / tree structure. * @param[out] error where to write a point to error messages. + * @return + * - <0 on error. + * - 0 on success. */ ssize_t xlat_tokenize(TALLOC_CTX *ctx, char *fmt, xlat_exp_t **head, char const **error) { - return xlat_tokenize_literal(ctx, fmt, head, false, error); + int ret; + + ret = xlat_tokenize_literal(ctx, fmt, head, false, error); + if (ret < 0) return ret; + + /* + * Add nodes that need to be bootstrapped to + * the registry. + */ + if (xlat_bootstrap(*head) < 0) { + TALLOC_FREE(*head); + return -1; + } + + return ret; }