From: Arran Cudbard-Bell Date: Fri, 19 Jan 2018 23:33:07 +0000 (-0700) Subject: Move async xlat fully out of the interpreter X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=436ca75023c33bf20fe2e83eea3f33b1d4357310;p=thirdparty%2Ffreeradius-server.git Move async xlat fully out of the interpreter 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. --- diff --git a/src/include/xlat.h b/src/include/xlat.h index 8a852532b8a..3dd1ff3716e 100644 --- a/src/include/xlat.h +++ b/src/include/xlat.h @@ -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)); diff --git a/src/main/libfreeradius-server.mk b/src/main/libfreeradius-server.mk index 7b0d76c8753..460c06db06a 100644 --- a/src/main/libfreeradius-server.mk +++ b/src/main/libfreeradius-server.mk @@ -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) diff --git a/src/main/unlang_op.c b/src/main/unlang_op.c index dc24fe32371..2c01b061f44 100644 --- a/src/main/unlang_op.c +++ b/src/main/unlang_op.c @@ -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 - }); } diff --git a/src/main/unlang_priv.h b/src/main/unlang_priv.h index 89e0b7ab1de..b5032a84e97 100644 --- a/src/main/unlang_priv.h +++ b/src/main/unlang_priv.h @@ -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 diff --git a/src/main/xlat_eval.c b/src/main/xlat_eval.c index be485991ed2..a8edb55094c 100644 --- a/src/main/xlat_eval.c +++ b/src/main/xlat_eval.c @@ -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; } + diff --git a/src/main/xlat_func.c b/src/main/xlat_func.c index dcb49678710..c3af9421665 100644 --- a/src/main/xlat_func.c +++ b/src/main/xlat_func.c @@ -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__); diff --git a/src/main/xlat_inst.c b/src/main/xlat_inst.c index 230945c3d74..46d31509d2e 100644 --- a/src/main/xlat_inst.c +++ b/src/main/xlat_inst.c @@ -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)); diff --git a/src/main/xlat_priv.h b/src/main/xlat_priv.h index f013d3ca834..fc783e5d71e 100644 --- a/src/main/xlat_priv.h +++ b/src/main/xlat_priv.h @@ -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 index 00000000000..cd41158a3ff --- /dev/null +++ b/src/main/xlat_unlang.c @@ -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 + */ +RCSID("$Id$") + +#include +#include + +#include +#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 + }); +}