]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Move async xlat fully out of the interpreter
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Fri, 19 Jan 2018 23:33:07 +0000 (16:33 -0700)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Fri, 19 Jan 2018 23:34:37 +0000 (16:34 -0700)
There's still good separation between the xlat interpreter and the unlang interpeter though, which is nice, as it means we can write a "synchronous" loop around the new async xlat interpreter for use in utilities.

src/include/xlat.h
src/main/libfreeradius-server.mk
src/main/unlang_op.c
src/main/unlang_priv.h
src/main/xlat_eval.c
src/main/xlat_func.c
src/main/xlat_inst.c
src/main/xlat_priv.h
src/main/xlat_unlang.c [new file with mode: 0644]

index 8a852532b8a2d389a5ad7c576388e7289e6122e3..3dd1ff3716e3fa1618151fc3640301aae333d0a5 100644 (file)
@@ -183,14 +183,6 @@ typedef int (*xlat_detach_t)(void *xlat_inst, void *uctx);
  */
 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,
-                                      fr_cursor_t *result);
-
-xlat_action_t  xlat_frame_eval(TALLOC_CTX *ctx, fr_cursor_t *out, xlat_exp_t const **child,
-                               REQUEST *request, xlat_exp_t const **in);
-
 ssize_t                xlat_eval(char *out, size_t outlen, REQUEST *request, char const *fmt, xlat_escape_t escape,
                          void const *escape_ctx)
                          CC_HINT(nonnull (1 ,3 ,4));
index 7b0d76c875335d228a239a866370522944848637..460c06db06a27e56cad06e6a4329ebc11d29aa5f 100644 (file)
@@ -28,7 +28,8 @@ SOURCES       :=      cond_eval.c \
                xlat_eval.c \
                xlat_func.c \
                xlat_inst.c \
-               xlat_tokenize.c
+               xlat_tokenize.c \
+               xlat_unlang.c
 
 # This lets the linker determine which version of the SSLeay functions to use.
 TGT_LDLIBS     := $(LIBS) $(GPERFTOOLS_LIBS)
index dc24fe323714c53e38e2cd86a97902a1dba5c2a6..2c01b061f44b9d9dca87095ed12d9004312699cb 100644 (file)
@@ -714,213 +714,6 @@ static unlang_action_t unlang_call(REQUEST *request,
        return UNLANG_ACTION_PUSHED_CHILD;
 }
 
-/** Static instruction for performing xlat evaluations
- *
- */
-static unlang_t xlat_instruction = {
-       .type = UNLANG_TYPE_XLAT,
-       .name = "xlat",
-       .debug_name = "xlat",
-       .actions = {
-               [RLM_MODULE_REJECT]     = 0,
-               [RLM_MODULE_FAIL]       = MOD_ACTION_RETURN,    /* Exit out of nested levels */
-               [RLM_MODULE_OK]         = 0,
-               [RLM_MODULE_HANDLED]    = 0,
-               [RLM_MODULE_INVALID]    = 0,
-               [RLM_MODULE_USERLOCK]   = 0,
-               [RLM_MODULE_NOTFOUND]   = 0,
-               [RLM_MODULE_NOOP]       = 0,
-               [RLM_MODULE_UPDATED]    = 0
-       },
-};
-
-/** Push a pre-compiled xlat onto the stack for evaluation
- *
- * @param[in] ctx              To allocate value boxes and values in.
- * @param[out] out             Where to write the result of the expansion.
- * @param[in] request          to push xlat onto.
- * @param[in] exp              node to evaluate.
- * @param[in] top_frame                Set to UNLANG_TOP_FRAME if this is the shallowest nesting level.
- *                             Set to UNLANG_SUB_FRAME if this is a nested expansion.
- */
-static void unlang_push_xlat(TALLOC_CTX *ctx, fr_value_box_t **out,
-                            REQUEST *request, xlat_exp_t const *exp, bool top_frame)
-{
-
-       unlang_stack_state_xlat_t       *state;
-       unlang_stack_t                  *stack = request->stack;
-       unlang_stack_frame_t            *frame;
-
-       /*
-        *      Push a new xlat eval frame onto the stack
-        */
-       unlang_push(stack, &xlat_instruction, RLM_MODULE_UNKNOWN, UNLANG_NEXT_STOP, top_frame);
-       frame = &stack->frame[stack->depth];
-
-       /*
-        *      Allocate its state, and setup a cursor for the xlat nodes
-        */
-       frame->state = state = talloc_zero(stack, unlang_stack_state_xlat_t);
-       state->exp = exp;
-
-       fr_cursor_init(&state->values, out);
-
-       state->ctx = ctx;
-}
-
-/** Push a pre-compiled xlat and resumption state onto the stack for evaluation
- *
- * In order to use the async unlang processor the calling module needs to establish
- * a resumption point, as the call to an xlat function may require yielding control
- * back to the interpreter.
- *
- * To simplify the calling conventions, this function is provided to first push a
- * resumption stack frame for the module, and then push an xlat stack frame.
- *
- * After pushing those frames the function updates the stack pointer to jump over
- * the resumption frame and execute the xlat interpreter.
- *
- * When the xlat interpreter finishes, and pops the xlat frame, the unlang interpreter
- * will then call the module resumption frame, allowing the module to continue exectuion.
- *
- * @param[in] ctx              To allocate value boxes and values in.
- * @param[out] out             Where to write the result of the expansion.
- * @param[in] request          The current request.
- * @param[in] xlat             to evaluate.
- * @param[in] callback         to call on unlang_resumable().
- * @param[in] signal           to call on unlang_action().
- * @param[in] uctx             to pass to the callbacks.
- * @return
- *     - RLM_MODULE_YIELD if the xlat would perform blocking I/O
- *     - A return code representing the result of the xla
- */
-rlm_rcode_t unlang_push_module_xlat(TALLOC_CTX *ctx, fr_value_box_t **out,
-                                   REQUEST *request, xlat_exp_t const *xlat,
-                                   fr_unlang_module_resume_t callback,
-                                   fr_unlang_module_signal_t signal, void *uctx)
-{
-       /*
-        *      Push the resumption point
-        */
-       (void) unlang_module_yield(request, callback, signal, uctx);
-
-       /*
-        *      Push the xlat function
-        */
-       unlang_push_xlat(ctx, out, request, xlat, true);
-
-       /*
-        *      Execute the xlat frame we just pushed onto the stack.
-        */
-       return unlang_run(request);
-}
-
-/** Stub function for calling the xlat interpreter
- *
- * Calls the xlat interpreter and translates its wants and needs into
- * unlang_action_t codes.
- */
-static unlang_action_t unlang_xlat(REQUEST *request,
-                                  rlm_rcode_t *presult, UNUSED int *priority)
-{
-       unlang_stack_t                  *stack = request->stack;
-       unlang_stack_frame_t            *frame = &stack->frame[stack->depth];
-       unlang_stack_state_xlat_t       *xs = talloc_get_type_abort(frame->state, unlang_stack_state_xlat_t);
-       xlat_exp_t const                *child = NULL;
-       xlat_action_t                   xa;
-
-       if (frame->repeat) {
-               fr_cursor_init(&xs->result, &xs->rhead);
-               xa = xlat_frame_eval_repeat(xs->ctx, &xs->values,
-                                           &child, &xs->alternate,
-                                           request, &xs->exp,
-                                           &xs->result);
-       } else {
-               xa = xlat_frame_eval(xs->ctx, &xs->values, &child, request, &xs->exp);
-       }
-
-       switch (xa) {
-       case XLAT_ACTION_PUSH_CHILD:
-               rad_assert(child);
-
-               frame->repeat = true;
-               unlang_push_xlat(xs->ctx, &xs->rhead, request, child, false);
-               return UNLANG_ACTION_PUSHED_CHILD;
-
-       case XLAT_ACTION_YIELD:
-               return UNLANG_ACTION_YIELD;
-
-       case XLAT_ACTION_DONE:
-               return UNLANG_ACTION_CALCULATE_RESULT;
-
-       case XLAT_ACTION_FAIL:
-               *presult = RLM_MODULE_FAIL;
-               return UNLANG_ACTION_CALCULATE_RESULT;
-       }
-
-       rad_assert(0);
-       return UNLANG_ACTION_CALCULATE_RESULT;
-}
-
-static unlang_action_t unlang_xlat_resume(REQUEST *request, UNUSED rlm_rcode_t *presult, UNUSED void *resume_ctx)
-{
-       unlang_stack_t                  *stack = request->stack;
-       unlang_stack_frame_t            *frame = &stack->frame[stack->depth];
-       unlang_t                        *instruction = frame->instruction;
-       unlang_resume_t                 *mr = unlang_generic_to_resume(instruction);
-       unlang_stack_state_xlat_t       *xs = talloc_get_type_abort(frame->state, unlang_stack_state_xlat_t);
-       xlat_action_t                   xa;
-
-       xa = ((xlat_resume_callback_t)mr->callback)(xs->ctx, &xs->values, request, NULL, NULL,
-                                                   &xs->result, mr->resume_ctx);
-       switch (xa) {
-       case XLAT_ACTION_YIELD:
-               return UNLANG_ACTION_YIELD;
-
-       case XLAT_ACTION_DONE:
-               return UNLANG_ACTION_CALCULATE_RESULT;
-
-       case XLAT_ACTION_PUSH_CHILD:
-               rad_assert(0);
-               /* FALL-THROUGH */
-
-       case XLAT_ACTION_FAIL:
-               *presult = RLM_MODULE_FAIL;
-               return UNLANG_ACTION_CALCULATE_RESULT;
-       }
-
-       rad_assert(0);
-       return UNLANG_ACTION_CALCULATE_RESULT;
-}
-
-/** Evaluates "naked" xlats in the config
- *
- */
-static unlang_action_t unlang_xlat_inline(REQUEST *request,
-                                         UNUSED rlm_rcode_t *presult, UNUSED int *priority)
-{
-       unlang_stack_t          *stack = request->stack;
-       unlang_stack_frame_t    *frame = &stack->frame[stack->depth];
-       unlang_t                *instruction = frame->instruction;
-       unlang_xlat_inline_t    *mx = unlang_generic_to_xlat_inline(instruction);
-
-       if (!mx->exec) {
-               TALLOC_CTX *pool;
-               unlang_stack_state_xlat_inline_t *state;
-
-               MEM(frame->state = state = talloc_zero(stack, unlang_stack_state_xlat_inline_t));
-               MEM(pool = talloc_pool(frame->state, 1024));    /* Pool to absorb some allocs */
-
-               unlang_push_xlat(pool, &state->result, request, mx->exp, false);
-               return UNLANG_ACTION_PUSHED_CHILD;
-       } else {
-               RDEBUG("`%s`", mx->xlat_name);
-               radius_exec_program(request, NULL, 0, NULL, request, mx->xlat_name, request->packet->vps,
-                                   false, true, EXEC_TIMEOUT);
-               return UNLANG_ACTION_CONTINUE;
-       }
-}
-
 static unlang_action_t unlang_subrequest(REQUEST *request,
                                         rlm_rcode_t *presult, int *priority)
 {
@@ -2127,19 +1920,4 @@ void unlang_op_initialize(void)
                                .debug_braces = true
                           });
 #endif
-
-       unlang_op_register(UNLANG_TYPE_XLAT_INLINE,
-                          &(unlang_op_t){
-                               .name = "xlat_inline",
-                               .func = unlang_xlat_inline,
-                               .debug_braces = false
-                          });
-
-       unlang_op_register(UNLANG_TYPE_XLAT,
-                          &(unlang_op_t){
-                               .name = "xlat_eval",
-                               .func = unlang_xlat,
-                               .resume = unlang_xlat_resume,
-                               .debug_braces = false
-                          });
 }
index 89e0b7ab1de8bb117229e4fae15cf5ac92241d9f..b5032a84e97f29788c4afdc461535f588075dcb7 100644 (file)
@@ -248,34 +248,6 @@ typedef struct {
        unlang_t                *found;
 } unlang_stack_state_redundant_t;
 
-/** Hold the result of an inline xlat expansion
- *
- */
-typedef struct {
-       fr_value_box_t          *result;                        //!< Where to store the result of the
-                                                               ///< xlat expansion. This is usually discarded.
-} unlang_stack_state_xlat_inline_t;
-
-/** State of an xlat expansion
- *
- * State of one level of nesting within an xlat expansion.
- */
-typedef struct {
-       TALLOC_CTX              *ctx;                           //!< to allocate boxes and values in.
-       xlat_exp_t const        *exp;
-       fr_cursor_t             values;                         //!< Values aggregated so far.
-
-       /*
-        *      For func and alternate
-        */
-       fr_value_box_t          *rhead;                         //!< Head of the result of a nested
-                                                               ///< expansion.
-       fr_cursor_t             result;                         //!< Result cursor, mainly useful for
-                                                               ///< asynchronous xlat functions.
-       bool                    alternate;                      //!< record which alternate branch we
-                                                               ///< previously took.
-} unlang_stack_state_xlat_t;
-
 /** Our interpreter stack, as distinct from the C stack
  *
  * We don't call the modules recursively.  Instead we iterate over a list of #unlang_t and
index be485991ed2a4ad4f776a339843fd87f9881b276..a8edb55094c05f4111389857e8d007a80d2e077d 100644 (file)
@@ -484,6 +484,35 @@ static xlat_action_t xlat_eval_pair(TALLOC_CTX *ctx, fr_cursor_t *out, REQUEST *
 static const char xlat_spaces[] = "                                                                                                                                                                                                                                                                ";
 #endif
 
+xlat_action_t xlat_frame_eval_resume(TALLOC_CTX *ctx, fr_cursor_t *out,
+                                    xlat_resume_callback_t resume, xlat_exp_t const *exp,
+                                    REQUEST *request, fr_cursor_t *result, void *rctx)
+{
+       xlat_thread_inst_t      *thread_inst = xlat_thread_instance_find(exp);
+       xlat_action_t           xa;
+
+       xa = resume(ctx, out, request, exp->inst, thread_inst->data, result, rctx);
+       switch (xa) {
+       default:
+               break;
+
+       case XLAT_ACTION_DONE:
+               fr_cursor_next(out);            /* Wind to the start of this functions output */
+               RDEBUG2("EXPAND %%{%s:%pM}", exp->xlat->name, fr_cursor_head(result));
+               if (fr_cursor_current(out)) {
+                       RDEBUG2("   --> %pM", fr_cursor_current(out));
+               } else {
+                       RDEBUG2("   -->");
+               }
+               break;
+
+       case XLAT_ACTION_FAIL:
+               REDEBUG("EXPANSION FAILED %%{%s:%pM}", exp->xlat->name, fr_cursor_head(result));
+               break;
+       }
+
+       return xa;
+}
 /** Process the result of a previous nested expansion
  *
  * @param[in] ctx              to allocate value boxes in.
@@ -572,18 +601,27 @@ xlat_action_t xlat_frame_eval_repeat(TALLOC_CTX *ctx, fr_cursor_t *out,
                        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_FAIL:
+                               REDEBUG("EXPANSION FAILED %%{%s:%pM}", node->xlat->name, fr_cursor_head(result));
+                               /* FALL-THROUGH */
+
                        case XLAT_ACTION_PUSH_CHILD:
                        case XLAT_ACTION_YIELD:
-                       case XLAT_ACTION_FAIL:
                                return action;
 
                        case XLAT_ACTION_DONE:          /* Process the result */
                                break;
                        }
 
-                       RDEBUG2("EXPAND %%{%s:...}", node->xlat->name);
+                       fr_cursor_next(out);            /* Wind to the start of this functions output */
+
+                       /*
+                        *      Print output if the function didn't yield
+                        *      else we need to print it in xlat_resume
+                        */
+                       RDEBUG2("EXPAND %%{%s:%pM}", node->xlat->name, fr_cursor_head(result));
                        if (fr_cursor_current(out)) {
-                               RDEBUG2("   --> %pV", fr_cursor_current(out));  /* Fixme - print multiple values */
+                               RDEBUG2("   --> %pM", fr_cursor_head(out));
                        } else {
                                RDEBUG2("   -->");
                        }
@@ -1297,3 +1335,4 @@ int xlat_eval_walk(xlat_exp_t *exp, xlat_walker_t walker, xlat_state_t type, voi
 
        return 0;
 }
+
index dcb49678710b1837c560aa6e6d48dc395ffd94e3..c3af94216655019204121dfa552bbe05f80bfbdc 100644 (file)
@@ -1097,6 +1097,14 @@ int xlat_init(void)
        int i;
 #endif
 
+       /*
+        *      Registers async xlat operations
+        */
+       xlat_unlang_init();
+
+       /*
+        *      Create the function tree
+        */
        xlat_root = rbtree_create(NULL, xlat_cmp, NULL, RBTREE_FLAG_REPLACE);
        if (!xlat_root) {
                ERROR("%s: Failed to create tree", __FUNCTION__);
index 230945c3d74f761c7900e6034aa464cb846834ae..46d31509d2e379e304d55081282f3f31eca42172 100644 (file)
@@ -126,7 +126,9 @@ static xlat_thread_inst_t *xlat_thread_inst_alloc(xlat_inst_t *inst)
 
 #ifdef HAVE_TALLOC_POOLED_OBJECT
        if (inst->node->xlat->thread_inst_size) {
-               MEM(thread_inst = talloc_pooled_object(NULL, xlat_thread_inst_t, inst->node->xlat->thread_inst_size))
+               MEM(thread_inst = talloc_pooled_object(NULL, xlat_thread_inst_t,
+                                                      1, inst->node->xlat->thread_inst_size));
+               memset(thread_inst, 0, sizeof(*thread_inst));
        } else
 #endif
                MEM(thread_inst = talloc_zero(NULL, xlat_thread_inst_t));
@@ -202,7 +204,8 @@ static xlat_inst_t *xlat_inst_alloc(xlat_exp_t *node)
 
 #ifdef HAVE_TALLOC_POOLED_OBJECT
        if (node->xlat->inst_size) {
-               MEM(inst = talloc_pooled_object(node, xlat_inst_t, node->xlat->inst_size))
+               MEM(inst = talloc_pooled_object(node, xlat_inst_t, 1, node->xlat->inst_size));
+               memset(inst, 0, sizeof(*inst));
        } else
 #endif
                MEM(inst = talloc_zero(node, xlat_inst_t));
index f013d3ca83463bc4895d2bfe027b562a0b8f52d4..fc783e5d71e2e34a5055bca4d61fd96cd9820e26 100644 (file)
@@ -152,7 +152,21 @@ xlat_t     *xlat_func_find(char const *name);
 /*
  *     xlat_eval.c
  */
-int    xlat_eval_walk(xlat_exp_t *exp, xlat_walker_t walker, xlat_state_t type, void *uctx);
+xlat_action_t  xlat_frame_eval_resume(TALLOC_CTX *ctx, fr_cursor_t *out,
+                                      xlat_resume_callback_t resume, xlat_exp_t const *exp,
+                                      REQUEST *request, fr_cursor_t *result, void *rctx);
+
+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,
+                                      fr_cursor_t *result);
+
+xlat_action_t  xlat_frame_eval(TALLOC_CTX *ctx, fr_cursor_t *out, xlat_exp_t const **child,
+                               REQUEST *request, xlat_exp_t const **in);
+
+int            xlat_eval_walk(xlat_exp_t *exp, xlat_walker_t walker, xlat_state_t type, void *uctx);
+
+void           xlat_unlang_init(void);
 
 #ifdef __cplusplus
 }
diff --git a/src/main/xlat_unlang.c b/src/main/xlat_unlang.c
new file mode 100644 (file)
index 0000000..cd41158
--- /dev/null
@@ -0,0 +1,293 @@
+/*
+ *   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_unlang.c
+ * @brief Integration between the unlang interpreter and xlats
+ *
+ * @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 "unlang_priv.h"       /* Fixme - Should create a proper semi-public interface for the interpret */
+
+/** Hold the result of an inline xlat expansion
+ *
+ */
+typedef struct {
+       fr_value_box_t          *result;                        //!< Where to store the result of the
+                                                               ///< xlat expansion. This is usually discarded.
+} unlang_stack_state_xlat_inline_t;
+
+/** State of an xlat expansion
+ *
+ * State of one level of nesting within an xlat expansion.
+ */
+typedef struct {
+       TALLOC_CTX              *ctx;                           //!< to allocate boxes and values in.
+       xlat_exp_t const        *exp;
+       fr_cursor_t             values;                         //!< Values aggregated so far.
+
+       /*
+        *      For func and alternate
+        */
+       fr_value_box_t          *rhead;                         //!< Head of the result of a nested
+                                                               ///< expansion.
+       fr_cursor_t             result;                         //!< Result cursor, mainly useful for
+                                                               ///< asynchronous xlat functions.
+       bool                    alternate;                      //!< record which alternate branch we
+                                                               ///< previously took.
+} unlang_stack_state_xlat_t;
+
+/** Static instruction for performing xlat evaluations
+ *
+ */
+static unlang_t xlat_instruction = {
+       .type = UNLANG_TYPE_XLAT,
+       .name = "xlat",
+       .debug_name = "xlat",
+       .actions = {
+               [RLM_MODULE_REJECT]     = 0,
+               [RLM_MODULE_FAIL]       = MOD_ACTION_RETURN,    /* Exit out of nested levels */
+               [RLM_MODULE_OK]         = 0,
+               [RLM_MODULE_HANDLED]    = 0,
+               [RLM_MODULE_INVALID]    = 0,
+               [RLM_MODULE_USERLOCK]   = 0,
+               [RLM_MODULE_NOTFOUND]   = 0,
+               [RLM_MODULE_NOOP]       = 0,
+               [RLM_MODULE_UPDATED]    = 0
+       },
+};
+
+/** Push a pre-compiled xlat onto the stack for evaluation
+ *
+ * @param[in] ctx              To allocate value boxes and values in.
+ * @param[out] out             Where to write the result of the expansion.
+ * @param[in] request          to push xlat onto.
+ * @param[in] exp              node to evaluate.
+ * @param[in] top_frame                Set to UNLANG_TOP_FRAME if this is the shallowest nesting level.
+ *                             Set to UNLANG_SUB_FRAME if this is a nested expansion.
+ */
+static void unlang_push_xlat(TALLOC_CTX *ctx, fr_value_box_t **out,
+                            REQUEST *request, xlat_exp_t const *exp, bool top_frame)
+{
+
+       unlang_stack_state_xlat_t       *state;
+       unlang_stack_t                  *stack = request->stack;
+       unlang_stack_frame_t            *frame;
+
+       /*
+        *      Push a new xlat eval frame onto the stack
+        */
+       unlang_push(stack, &xlat_instruction, RLM_MODULE_UNKNOWN, UNLANG_NEXT_STOP, top_frame);
+       frame = &stack->frame[stack->depth];
+
+       /*
+        *      Allocate its state, and setup a cursor for the xlat nodes
+        */
+       frame->state = state = talloc_zero(stack, unlang_stack_state_xlat_t);
+       state->exp = exp;
+
+       fr_cursor_init(&state->values, out);
+
+       state->ctx = ctx;
+}
+
+/** Push a pre-compiled xlat and resumption state onto the stack for evaluation
+ *
+ * In order to use the async unlang processor the calling module needs to establish
+ * a resumption point, as the call to an xlat function may require yielding control
+ * back to the interpreter.
+ *
+ * To simplify the calling conventions, this function is provided to first push a
+ * resumption stack frame for the module, and then push an xlat stack frame.
+ *
+ * After pushing those frames the function updates the stack pointer to jump over
+ * the resumption frame and execute the xlat interpreter.
+ *
+ * When the xlat interpreter finishes, and pops the xlat frame, the unlang interpreter
+ * will then call the module resumption frame, allowing the module to continue exectuion.
+ *
+ * @param[in] ctx              To allocate value boxes and values in.
+ * @param[out] out             Where to write the result of the expansion.
+ * @param[in] request          The current request.
+ * @param[in] xlat             to evaluate.
+ * @param[in] callback         to call on unlang_resumable().
+ * @param[in] signal           to call on unlang_action().
+ * @param[in] uctx             to pass to the callbacks.
+ * @return
+ *     - RLM_MODULE_YIELD if the xlat would perform blocking I/O
+ *     - A return code representing the result of the xla
+ */
+rlm_rcode_t unlang_push_module_xlat(TALLOC_CTX *ctx, fr_value_box_t **out,
+                                   REQUEST *request, xlat_exp_t const *xlat,
+                                   fr_unlang_module_resume_t callback,
+                                   fr_unlang_module_signal_t signal, void *uctx)
+{
+       /*
+        *      Push the resumption point
+        */
+       (void) unlang_module_yield(request, callback, signal, uctx);
+
+       /*
+        *      Push the xlat function
+        */
+       unlang_push_xlat(ctx, out, request, xlat, true);
+
+       /*
+        *      Execute the xlat frame we just pushed onto the stack.
+        */
+       return unlang_run(request);
+}
+
+/** Stub function for calling the xlat interpreter
+ *
+ * Calls the xlat interpreter and translates its wants and needs into
+ * unlang_action_t codes.
+ */
+static unlang_action_t unlang_xlat(REQUEST *request,
+                                  rlm_rcode_t *presult, UNUSED int *priority)
+{
+       unlang_stack_t                  *stack = request->stack;
+       unlang_stack_frame_t            *frame = &stack->frame[stack->depth];
+       unlang_stack_state_xlat_t       *xs = talloc_get_type_abort(frame->state, unlang_stack_state_xlat_t);
+       xlat_exp_t const                *child = NULL;
+       xlat_action_t                   xa;
+
+       if (frame->repeat) {
+               fr_cursor_init(&xs->result, &xs->rhead);
+               xa = xlat_frame_eval_repeat(xs->ctx, &xs->values,
+                                           &child, &xs->alternate,
+                                           request, &xs->exp,
+                                           &xs->result);
+       } else {
+               xa = xlat_frame_eval(xs->ctx, &xs->values, &child, request, &xs->exp);
+       }
+
+       switch (xa) {
+       case XLAT_ACTION_PUSH_CHILD:
+               rad_assert(child);
+
+               frame->repeat = true;
+               unlang_push_xlat(xs->ctx, &xs->rhead, request, child, false);
+               return UNLANG_ACTION_PUSHED_CHILD;
+
+       case XLAT_ACTION_YIELD:
+               return UNLANG_ACTION_YIELD;
+
+       case XLAT_ACTION_DONE:
+               return UNLANG_ACTION_CALCULATE_RESULT;
+
+       case XLAT_ACTION_FAIL:
+               *presult = RLM_MODULE_FAIL;
+               return UNLANG_ACTION_CALCULATE_RESULT;
+       }
+
+       rad_assert(0);
+       return UNLANG_ACTION_CALCULATE_RESULT;
+}
+
+static unlang_action_t unlang_xlat_resume(REQUEST *request, rlm_rcode_t *presult, UNUSED void *resume_ctx)
+{
+       unlang_stack_t                  *stack = request->stack;
+       unlang_stack_frame_t            *frame = &stack->frame[stack->depth];
+       unlang_t                        *instruction = frame->instruction;
+       unlang_resume_t                 *mr = unlang_generic_to_resume(instruction);
+       unlang_stack_state_xlat_t       *xs = talloc_get_type_abort(frame->state, unlang_stack_state_xlat_t);
+       xlat_action_t                   xa;
+
+       xa = xlat_frame_eval_resume(xs->ctx, &xs->values, mr->callback, xs->exp, request, &xs->result, mr->resume_ctx);
+       switch (xa) {
+       case XLAT_ACTION_YIELD:
+               *presult = RLM_MODULE_YIELD;
+               return UNLANG_ACTION_YIELD;
+
+       case XLAT_ACTION_DONE:
+               *presult = RLM_MODULE_OK;
+               return UNLANG_ACTION_CALCULATE_RESULT;
+
+       case XLAT_ACTION_PUSH_CHILD:
+               rad_assert(0);
+               /* FALL-THROUGH */
+
+       case XLAT_ACTION_FAIL:
+               *presult = RLM_MODULE_FAIL;
+               return UNLANG_ACTION_CALCULATE_RESULT;
+       /* Don't set default */
+       }
+
+       /* garbage xlat action */
+
+       rad_assert(0);
+       return UNLANG_ACTION_CALCULATE_RESULT;
+}
+
+/** Evaluates "naked" xlats in the config
+ *
+ */
+static unlang_action_t unlang_xlat_inline(REQUEST *request,
+                                         UNUSED rlm_rcode_t *presult, UNUSED int *priority)
+{
+       unlang_stack_t          *stack = request->stack;
+       unlang_stack_frame_t    *frame = &stack->frame[stack->depth];
+       unlang_t                *instruction = frame->instruction;
+       unlang_xlat_inline_t    *mx = unlang_generic_to_xlat_inline(instruction);
+
+       if (!mx->exec) {
+               TALLOC_CTX *pool;
+               unlang_stack_state_xlat_inline_t *state;
+
+               MEM(frame->state = state = talloc_zero(stack, unlang_stack_state_xlat_inline_t));
+               MEM(pool = talloc_pool(frame->state, 1024));    /* Pool to absorb some allocs */
+
+               unlang_push_xlat(pool, &state->result, request, mx->exp, false);
+               return UNLANG_ACTION_PUSHED_CHILD;
+       } else {
+               RDEBUG("`%s`", mx->xlat_name);
+               radius_exec_program(request, NULL, 0, NULL, request, mx->xlat_name, request->packet->vps,
+                                   false, true, EXEC_TIMEOUT);
+               return UNLANG_ACTION_CONTINUE;
+       }
+}
+
+/** Register xlat operation with the interpreter
+ *
+ */
+void xlat_unlang_init(void)
+{
+       unlang_op_register(UNLANG_TYPE_XLAT,
+                          &(unlang_op_t){
+                               .name = "xlat_eval",
+                               .func = unlang_xlat,
+                               .resume = unlang_xlat_resume,
+                               .debug_braces = false
+                          });
+
+
+       unlang_op_register(UNLANG_TYPE_XLAT_INLINE,
+                          &(unlang_op_t){
+                               .name = "xlat_inline",
+                               .func = unlang_xlat_inline,
+                               .debug_braces = false
+                          });
+}