]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Plumb in xlat instances
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Thu, 18 Jan 2018 21:25:00 +0000 (14:25 -0700)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Thu, 18 Jan 2018 21:30:42 +0000 (14:30 -0700)
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.

src/include/xlat.h
src/main/process.c
src/main/radiusd.c
src/main/unit_test_module.c
src/main/virtual_servers.c
src/main/xlat_eval.c
src/main/xlat_func.c
src/main/xlat_inst.c
src/main/xlat_priv.h
src/main/xlat_tokenize.c

index 782ec4f312699763288fecffcf5be836ee1c1a4a..6edf1e08fc7a693aee97d5df02399c68e9e6548f 100644 (file)
@@ -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
 }
index 387f47f70435606507c33e3f284470dfbd2f9a87..b91a86dce972bcd2ce06d14f7b7e4499602d428c 100644 (file)
@@ -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;
                }
        }
 
index 3d68370cb8a2165d9aeb85dd1300da7eb31fa324..d190654044d38f926a7f4ee3d5c6bbeca798b77a 100644 (file)
@@ -31,6 +31,7 @@ RCSID("$Id$")
 
 #include <freeradius-devel/radiusd.h>
 #include <freeradius-devel/modules.h>
+#include <freeradius-devel/unlang.h>
 #include <freeradius-devel/state.h>
 #include <freeradius-devel/map_proc.h>
 #include <freeradius-devel/rad_assert.h>
@@ -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.
         */
index 8ba8c29bc8985a3a5f792ccecdf1aecd6e7f81df..cb22b693cf555e86d555471e237d70dbebe86b43 100644 (file)
@@ -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.
         */
index 829b0b687f695279f06c1632b624f79fa3330cfd..3ae6bcf8bea41f405a19a7a6503381dd0bb44da6 100644 (file)
@@ -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
index 2011013f7c08d3e2224cebef8d0c1d247c5fb75d..be485991ed2a4ad4f776a339843fd87f9881b276 100644 (file)
@@ -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';
index e301240cd9c93425ee3de31ce6c2c2c7e47e9384..7b0e253b5d9424ec686e766c3372c7f5470db529 100644 (file)
@@ -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;
 }
 
 
index c7246b86f4c023f02602eea376ad6cc494081e23..48dfed091015080d6a22a9442efe93cfd6fd75ef 100644 (file)
@@ -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);
 }
index f5967712218a1c9e964f3fba33bed68a9fb1db08..f013d3ca83463bc4895d2bfe027b562a0b8f52d4 100644 (file)
@@ -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
index c005699a383936474b620ea181ee0d12a6a2c5b5..5522e067f76f6bb583f63963e91c95ff1f1d3659 100644 (file)
@@ -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;
 }