]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Add xlat instantiation framework
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Thu, 18 Jan 2018 01:50:16 +0000 (18:50 -0700)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Thu, 18 Jan 2018 01:50:25 +0000 (18:50 -0700)
src/include/xlat.h
src/main/libfreeradius-server.mk
src/main/xlat_eval.c
src/main/xlat_func.c
src/main/xlat_inst.c [new file with mode: 0644]
src/main/xlat_priv.h [moved from src/main/xlat.h with 56% similarity]
src/main/xlat_tokenize.c

index 218943d5dc9f8c9f0bd6662f3c9d675dac1a9856..782ec4f312699763288fecffcf5be836ee1c1a4a 100644 (file)
@@ -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
index e3a576e16c83e2280f3a620b183d1fa06f15be8d..7b0d76c875335d228a239a866370522944848637 100644 (file)
@@ -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.
index 5ef64c87d3fea296b656a387f3664575835819c5..2011013f7c08d3e2224cebef8d0c1d247c5fb75d 100644 (file)
@@ -32,7 +32,7 @@ RCSID("$Id$")
 #include <freeradius-devel/rad_assert.h>
 
 #include <ctype.h>
-#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;
+}
index f865214940d7444dea0d8c9c31e6b7d02e58784f..e301240cd9c93425ee3de31ce6c2c2c7e47e9384 100644 (file)
@@ -32,7 +32,7 @@ RCSID("$Id$")
 #include <freeradius-devel/base64.h>
 
 #include <ctype.h>
-#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 (file)
index 0000000..2d44ec4
--- /dev/null
@@ -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 <a.cudbardb@freeradius.org>
+ */
+RCSID("$Id$")
+
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/rad_assert.h>
+
+#include <ctype.h>
+
+#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);
+}
similarity index 56%
rename from src/main/xlat.h
rename to src/main/xlat_priv.h
index e5f90164291b7ddee4a57dc6dc40f70a6a01ec86..f5967712218a1c9e964f3fba33bed68a9fb1db08 100644 (file)
@@ -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.
  * @copyright 2000,2006  The FreeRADIUS server project
  * @copyright 2000  Alan DeKok <aland@ox.org>
  */
+#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
index 6fee1cb6ed2fa96f0127427fe76bd992b624fbde..c005699a383936474b620ea181ee0d12a6a2c5b5 100644 (file)
@@ -30,7 +30,7 @@ RCSID("$Id$")
 #include <freeradius-devel/rad_assert.h>
 
 #include <ctype.h>
-#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";