Move operations to basic registration API. Needs more work, but at least we can now move the xlat stuff out of the interpreter.
CONF_SECTION *virtual_server_find(char const *name);
void fr_request_async_bootstrap(REQUEST *request, fr_event_list_t *el); /* for unit_test_module */
-/** A callback when the the timeout occurs
- *
- * Used when a module needs wait for an event.
- * Typically the callback is set, and then the module returns unlang_module_yield().
- *
- * @note The callback is automatically removed on unlang_resumable(), i.e. if an event
- * on a registered FD occurs before the timeout event fires.
- *
- * @param[in] request the request.
- * @param[in] instance the module instance.
- * @param[in] thread data specific to this module instance.
- * @param[in] rctx a local context for the callback.
- * @param[in] fired the time the timeout event actually fired.
- */
-typedef void (*fr_unlang_module_timeout_t)(REQUEST *request, void *instance, void *thread, void *rctx,
- struct timeval *fired);
-
-/** A callback when the FD is ready for reading
- *
- * Used when a module needs to read from an FD. Typically the callback is set, and then the
- * module returns unlang_module_yield().
- *
- * @note The callback is automatically removed on unlang_resumable(), so
- *
- * @param[in] request the current request.
- * @param[in] instance the module instance.
- * @param[in] thread data specific to this module instance.
- * @param[in] rctx a local context for the callback.
- * @param[in] fd the file descriptor.
- */
-typedef void (*fr_unlang_module_fd_event_t)(REQUEST *request, void *instance, void *thread, void *rctx, int fd);
-
-/** A callback for when the request is resumed.
- *
- * The resumed request cannot call the normal "authorize", etc. method. It needs a separate callback.
- *
- * @param[in] request the current request.
- * @param[in] instance The module instance.
- * @param[in] thread data specific to this module instance.
- * @param[in] rctx a local context for the callback.
- * @return a normal rlm_rcode_t.
- */
-typedef rlm_rcode_t (*fr_unlang_module_resume_t)(REQUEST *request, void *instance, void *thread, void *rctx);
-
-/** A callback when the request gets a fr_state_action_t.
- *
- * A module may call unlang_yeild(), but still need to do something on FR_ACTION_DUP. If so, it's
- * set here.
- *
- * @note The callback is automatically removed on unlang_resumable().
- *
- * @param[in] request The current request.
- * @param[in] instance The module instance.
- * @param[in] thread data specific to this module instance.
- * @param[in] rctx Resume ctx for the callback.
- * @param[in] action which is signalling the request.
- */
-typedef void (*fr_unlang_module_signal_t)(REQUEST *request, void *instance, void *thread,
- void *rctx, fr_state_action_t action);
-
-/*
- * In unlang_interpret.c, but here for public consumption.
- */
-void unlang_push_section(REQUEST *request, CONF_SECTION *cs, rlm_rcode_t default_action);
-
-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_callback, void *uctx);
-
-rlm_rcode_t unlang_interpret_continue(REQUEST *request);
-
-rlm_rcode_t unlang_interpret(REQUEST *request, CONF_SECTION *cs, rlm_rcode_t default_action);
-
-rlm_rcode_t unlang_interpret_synchronous(REQUEST *request, CONF_SECTION *cs, rlm_rcode_t action);
-
-int unlang_compile(CONF_SECTION *cs, rlm_components_t component);
-int unlang_compile_subsection(CONF_SECTION *server_cs, char const *name1, char const *name2, rlm_components_t component);
-
-int unlang_event_timeout_add(REQUEST *request, fr_unlang_module_timeout_t callback,
- void const *ctx, struct timeval *timeout);
-
-int unlang_event_fd_add(REQUEST *request,
- fr_unlang_module_fd_event_t read,
- fr_unlang_module_fd_event_t write,
- fr_unlang_module_fd_event_t error,
- void const *ctx, int fd);
-
-int unlang_event_timeout_delete(REQUEST *request, void const *ctx);
-
-int unlang_event_fd_delete(REQUEST *request, void const *ctx, int fd);
-
-void unlang_resumable(REQUEST *request);
-
-void unlang_signal(REQUEST *request, fr_state_action_t action);
-
-int unlang_stack_depth(REQUEST *request);
-
-rlm_rcode_t unlang_module_yield(REQUEST *request, fr_unlang_module_resume_t callback,
- fr_unlang_module_signal_t signal_callback, void *ctx);
-xlat_action_t unlang_xlat_yield(REQUEST *request, xlat_resume_callback_t callback,
- fr_unlang_module_signal_t signal_callback, void *rctx);
-
-
-int unlang_initialize(void);
-
#ifdef __cplusplus
}
#endif
--- /dev/null
+/*
+ * 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, 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#ifndef _FR_UNLANG_H
+#define _FR_UNLANG_H
+/**
+ * $Id$
+ *
+ * @file src/include/unlang.h
+ * @brief Public interface to the interpreter
+ *
+ */
+#include <freeradius-devel/radiusd.h>
+#include <freeradius-devel/modules.h>
+
+/** Returned by #unlang_op_t calls, determine the next action of the interpreter
+ *
+ * These deal exclusively with control flow.
+ */
+typedef enum {
+ UNLANG_ACTION_CALCULATE_RESULT = 1, //!< Calculate a new section #rlm_rcode_t value.
+ UNLANG_ACTION_CONTINUE, //!< Execute the next #unlang_t.
+ UNLANG_ACTION_PUSHED_CHILD, //!< #unlang_t pushed a new child onto the stack,
+ //!< execute it instead of continuing.
+ UNLANG_ACTION_BREAK, //!< Break out of the current group.
+ UNLANG_ACTION_YIELD, //!< Temporarily pause execution until an event occurs.
+ UNLANG_ACTION_STOP_PROCESSING //!< Break out of processing the current request (unwind).
+} unlang_action_t;
+
+/** Function to call when first evaluating a frame
+ *
+ * @param[in] request The current request.
+ * @param[in,out] presult Pointer to the current rcode, may be modified by the function.
+ * @param[in,out] priority Pointer to the current priority, may be modified by the function.
+ * @return an action for the interpreter to perform.
+ */
+typedef unlang_action_t (*unlang_op_func_t)(REQUEST *request, rlm_rcode_t *presult, int *priority);
+
+/** Function to call if the initial function yielded and the request was signalled
+ *
+ * This is the operation specific cancellation function. This function will usually
+ * either call a more specialised cancellation function set when something like a module yielded,
+ * or just cleanup the state of the original #unlang_op_func_t.
+ *
+ * @param[in] request The current request.
+ * @param[in] resume_ctx A structure allocated by the initial #unlang_op_func_t to store
+ * the result of the async execution.
+ * @param[in] action We're being signalled with.
+ */
+typedef void (*unlang_op_func_signal_t)(REQUEST *request, void *resume_ctx, fr_state_action_t action);
+
+/** Function to call when a request becomes resumable
+ *
+ * When an event occurs that means we can continue processing the request, this function is called
+ * first. This callback is usually used to remove timeout events, unregister interest in file
+ * descriptors, and generally cleanup after the yielding function.
+ *
+ * @param[in] request The current request.
+ * @param[in] resume_ctx A structure allocated by the initial #unlang_op_func_t to store
+ * the result of the async execution.
+ * @param[in] action We're being signalled with.
+ */
+typedef void (*unlang_op_func_resumable_t)(REQUEST *request, void *resume_ctx);
+
+/** Function to call if the initial function yielded and the request is resumable
+ *
+ * @param[in] request The current request.
+ * @param[in,out] presult Pointer to the current rcode, may be modified by the function.
+ * @param[in] resume_ctx A structure allocated by the initial #unlang_op_func_t to store
+ * the result of the async execution.
+ * @return an action for the interpreter to perform.
+ */
+typedef unlang_action_t (*unlang_op_func_resume_t)(REQUEST *request, rlm_rcode_t *presult, void *resume_ctx);
+
+/** A callback when the the timeout occurs
+ *
+ * Used when a module needs wait for an event.
+ * Typically the callback is set, and then the module returns unlang_module_yield().
+ *
+ * @note The callback is automatically removed on unlang_resumable(), i.e. if an event
+ * on a registered FD occurs before the timeout event fires.
+ *
+ * @param[in] request the request.
+ * @param[in] instance the module instance.
+ * @param[in] thread data specific to this module instance.
+ * @param[in] rctx a local context for the callback.
+ * @param[in] fired the time the timeout event actually fired.
+ */
+typedef void (*fr_unlang_module_timeout_t)(REQUEST *request, void *instance, void *thread, void *rctx,
+ struct timeval *fired);
+
+/** A callback when the FD is ready for reading
+ *
+ * Used when a module needs to read from an FD. Typically the callback is set, and then the
+ * module returns unlang_module_yield().
+ *
+ * @note The callback is automatically removed on unlang_resumable(), so
+ *
+ * @param[in] request the current request.
+ * @param[in] instance the module instance.
+ * @param[in] thread data specific to this module instance.
+ * @param[in] rctx a local context for the callback.
+ * @param[in] fd the file descriptor.
+ */
+typedef void (*fr_unlang_module_fd_event_t)(REQUEST *request, void *instance, void *thread, void *rctx, int fd);
+
+/** A callback for when the request is resumed.
+ *
+ * The resumed request cannot call the normal "authorize", etc. method. It needs a separate callback.
+ *
+ * @param[in] request the current request.
+ * @param[in] instance The module instance.
+ * @param[in] thread data specific to this module instance.
+ * @param[in] rctx a local context for the callback.
+ * @return a normal rlm_rcode_t.
+ */
+typedef rlm_rcode_t (*fr_unlang_module_resume_t)(REQUEST *request, void *instance, void *thread, void *rctx);
+
+/** A callback when the request gets a fr_state_action_t.
+ *
+ * A module may call unlang_yeild(), but still need to do something on FR_ACTION_DUP. If so, it's
+ * set here.
+ *
+ * @note The callback is automatically removed on unlang_resumable().
+ *
+ * @param[in] request The current request.
+ * @param[in] instance The module instance.
+ * @param[in] thread data specific to this module instance.
+ * @param[in] rctx Resume ctx for the callback.
+ * @param[in] action which is signalling the request.
+ */
+typedef void (*fr_unlang_module_signal_t)(REQUEST *request, void *instance, void *thread,
+ void *rctx, fr_state_action_t action);
+
+/** An unlang operation
+ *
+ * These are like the opcodes in other interpreters. Each operation, when executed
+ * will return an #unlang_action_t, which determines what the interpreter does next.
+ */
+typedef struct {
+ char const *name; //!< Name of the operation.
+ unlang_op_func_t func; //!< Called when we start the operation.
+
+ unlang_op_func_signal_t signal; //!< Called if the request is to be destroyed
+ ///< and we need to cleanup any residual state.
+
+ unlang_op_func_resumable_t resumable; //!< Called as soon as the interpreter is informed
+ ///< that a request is resumable.
+
+ unlang_op_func_resume_t resume; //!< Called if we're continuing processing
+ ///< a request.
+ bool debug_braces; //!< Whether the operation needs to print braces
+ ///< in debug mode.
+} unlang_op_t;
+
+void unlang_push_section(REQUEST *request, CONF_SECTION *cs, rlm_rcode_t default_action);
+
+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_callback, void *uctx);
+
+rlm_rcode_t unlang_interpret_continue(REQUEST *request);
+
+rlm_rcode_t unlang_interpret(REQUEST *request, CONF_SECTION *cs, rlm_rcode_t default_action);
+
+rlm_rcode_t unlang_interpret_synchronous(REQUEST *request, CONF_SECTION *cs, rlm_rcode_t action);
+
+void *unlang_stack_alloc(TALLOC_CTX *ctx);
+
+void unlang_op_register(int type, unlang_op_t *op);
+
+int unlang_compile(CONF_SECTION *cs, rlm_components_t component);
+
+int unlang_compile_subsection(CONF_SECTION *server_cs, char const *name1, char const *name2, rlm_components_t component);
+
+bool unlang_keyword(const char *name);
+
+int unlang_event_timeout_add(REQUEST *request, fr_unlang_module_timeout_t callback,
+ void const *ctx, struct timeval *timeout);
+
+int unlang_event_fd_add(REQUEST *request,
+ fr_unlang_module_fd_event_t read,
+ fr_unlang_module_fd_event_t write,
+ fr_unlang_module_fd_event_t error,
+ void const *ctx, int fd);
+
+int unlang_event_timeout_delete(REQUEST *request, void const *ctx);
+
+int unlang_event_fd_delete(REQUEST *request, void const *ctx, int fd);
+
+void unlang_resumable(REQUEST *request);
+
+void unlang_signal(REQUEST *request, fr_state_action_t action);
+
+int unlang_stack_depth(REQUEST *request);
+
+rlm_rcode_t unlang_module_yield(REQUEST *request, fr_unlang_module_resume_t callback,
+ fr_unlang_module_signal_t signal_callback, void *ctx);
+xlat_action_t unlang_xlat_yield(REQUEST *request, xlat_resume_callback_t callback,
+ fr_unlang_module_signal_t signal_callback, void *rctx);
+
+int unlang_initialize(void);
+#endif /* _FR_UNLANG_H */
#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/modpriv.h>
-#include <freeradius-devel/interpreter.h>
#include <freeradius-devel/parser.h>
+#include <freeradius-devel/unlang.h>
fr_thread_local_setup(rbtree_t *, module_thread_inst_tree)
static int module_instantiate(CONF_SECTION *root, char const *name);
-static bool is_reserved_word(const char *name)
-{
- int i;
-
- if (!name || !*name) return false;
-
- for (i = 1; unlang_ops[i].name != NULL; i++) {
- if (strcmp(name, unlang_ops[i].name) == 0) return true;
- }
-
- return false;
-}
-
/** Initialise a module specific exfile handle
*
* @see exfile_init
*/
static module_instance_t *module_bootstrap(CONF_SECTION *modules, CONF_SECTION *cs)
{
- int i;
char const *name1, *inst_name;
module_instance_t *mod_inst;
inst_name = cf_section_name2(cs);
if (!inst_name) inst_name = name1;
- /*
- * Don't allow modules to use reserved words.
- */
- for (i = 1; unlang_ops[i].name != NULL; i++) {
- if (strcmp(inst_name, unlang_ops[i].name) == 0) {
- ERROR("Module names cannot use a reserved word \"%s\"",
- unlang_ops[i].name);
- return NULL;
- }
+ if (unlang_keyword(inst_name)) {
+ ERROR("Module names cannot use a reserved word \"%s\"", inst_name);
+ return NULL;
}
/*
return -1;
}
- if (is_reserved_word(name)) {
+ if (unlang_keyword(name)) {
is_reserved:
cf_log_err(vm_cs, "Virtual modules cannot overload unlang keywords");
return -1;
name1 = cf_section_name1(subcs);
- if (is_reserved_word(name1)) {
+ if (unlang_keyword(name1)) {
cf_log_err(subcs, "Modules cannot overload unlang keywords");
return -1;
}
RCSID("$Id$")
#include <freeradius-devel/radiusd.h>
-#include <freeradius-devel/interpreter.h>
#include <freeradius-devel/rad_assert.h>
+#include <freeradius-devel/unlang.h>
/** Per-request opaque data, added by modules
*
request->module = NULL;
request->component = "<core>";
-#ifdef HAVE_TALLOC_POOLED_OBJECT
- /*
- * If we have talloc_pooled_object allocate the
- * stack as a combined chunk/pool, with memory
- * to hold at mutable data for at least a quarter
- * of the maximum number of stack frames.
- *
- * Having a dedicated pool for mutable stack data
- * means we don't have memory fragmentations issues
- * as we would if request were used as the pool.
- *
- * This number is pretty arbitrary, but it seems
- * like too low level to make into a tuneable.
- */
- request->stack = talloc_pooled_object(request, unlang_stack_t, UNLANG_STACK_MAX / 4,
- sizeof(unlang_stack_state_t));
-#else
- request->stack = talloc_zero(request, unlang_stack_t);
-#endif
+ MEM(request->stack = unlang_stack_alloc(request));
+
request->runnable_id = -1;
request->time_order_id = -1;
#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/modpriv.h>
-#include <freeradius-devel/interpreter.h>
#include <freeradius-devel/parser.h>
+#include <freeradius-devel/unlang.h>
+#include "unlang_priv.h"
/* Here's where we recognize all of our keywords: first the rcodes, then the
* actions */
return 1;
}
+
+/** Check if name is an unlang keyword
+ *
+ * @param[in] name to check.
+ * @return
+ * - true if it is a keyword.
+ * - false if it's not a keyword.
+ */
+bool unlang_keyword(const char *name)
+{
+ int i;
+
+ if (!name || !*name) return false;
+
+ for (i = 1; compile_table[i].name != NULL; i++) {
+ if (strcmp(name, compile_table[i].name) == 0) return true;
+ }
+
+ return false;
+}
#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/modpriv.h>
-#include <freeradius-devel/interpreter.h>
#include <freeradius-devel/parser.h>
#include <freeradius-devel/xlat.h>
#include <freeradius-devel/io/listen.h>
+#include "unlang_priv.h"
+
static FR_NAME_NUMBER unlang_action_table[] = {
{ "calculate-result", UNLANG_ACTION_CALCULATE_RESULT },
{ "continue", UNLANG_ACTION_CONTINUE },
#define DUMP_STACK
#endif
+/** Different operations the interpreter can execute
+ */
+unlang_op_t unlang_ops[UNLANG_TYPE_MAX];
+
/** Allocates and initializes an unlang_resume_t
*
* @param[in] request The current request.
return rcode;
}
+/** Allocate a new unlang stack
+ *
+ * @param[in] ctx to allocate stack in.
+ * @return
+ * - A new stack on success.
+ * - NULL on OOM.
+ */
+void *unlang_stack_alloc(TALLOC_CTX *ctx)
+{
+#ifdef HAVE_TALLOC_POOLED_OBJECT
+ /*
+ * If we have talloc_pooled_object allocate the
+ * stack as a combined chunk/pool, with memory
+ * to hold at mutable data for at least a quarter
+ * of the maximum number of stack frames.
+ *
+ * Having a dedicated pool for mutable stack data
+ * means we don't have memory fragmentations issues
+ * as we would if request were used as the pool.
+ *
+ * This number is pretty arbitrary, but it seems
+ * like too low level to make into a tuneable.
+ */
+ return talloc_pooled_object(ctx, unlang_stack_t, UNLANG_STACK_MAX / 4, sizeof(unlang_stack_state_t));
+#else
+ return talloc_zero(ctx, unlang_stack_t);
+#endif
+}
+
/** Wrap an #fr_event_timer_t providing data needed for unlang events
*
*/
if (request->runnable_id < 0) fr_heap_insert(request->backlog, request);
}
+/** Callback for handling resumption frames
+ *
+ * Resumption frames are added to track when a module, or other construct
+ * has yielded control back to the interpreter.
+ *
+ * This function is called when the request has been marked as resumable
+ * and a resumption frame was previously placed on the stack, i.e. when
+ * the work that caused the request to be yielded initially has completed.
+ *
+ * @param[in] request to be resumed.
+ * @param[out] presult the rcode returned by the resume function.
+ * @param[out] priority associated with the rcode.
+ */
+static unlang_action_t unlang_resume(REQUEST *request, rlm_rcode_t *presult, int *priority)
+{
+ 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_action_t action;
+
+ RDEBUG3("Resuming in %s", mr->self.debug_name);
+
+ if (!unlang_ops[mr->parent->type].resume) {
+ *presult = RLM_MODULE_FAIL;
+ return UNLANG_ACTION_CALCULATE_RESULT;
+ }
+
+ request->module = mr->self.debug_name;
+
+ /*
+ * Run the resume callback associated with
+ * the original frame which was used to
+ * create this resumption frame.
+ */
+ action = unlang_ops[mr->parent->type].resume(request, presult, mr->resume_ctx);
+
+ request->module = NULL;
+
+ /*
+ * Leave mr alone, it will be freed when the request is done.
+ */
+
+ /*
+ * Is now marked as "stop" when it wasn't before, we must have been blocked.
+ */
+ if (request->master_state == REQUEST_STOP_PROCESSING) {
+ RWARN("Module %s became unblocked", mr->self.debug_name);
+ return UNLANG_ACTION_STOP_PROCESSING;
+ }
+
+ if (*presult != RLM_MODULE_YIELD) {
+ rad_assert(*presult >= RLM_MODULE_REJECT);
+ rad_assert(*presult < RLM_MODULE_NUMCODES);
+ *priority = instruction->actions[*presult];
+ }
+
+ return action;
+}
+
/** Yield a request back to the interpreter from within a module
*
* This passes control of the request back to the unlang interpreter, setting
return 0;
}
+/** Register an operation with the interpreter
+ *
+ * The main purpose of this registration API is to avoid intermixing the xlat,
+ * condition, map APIs with the interpreter, i.e. the callbacks needed for that
+ * functionality can be in their own source files, and we don't need to include
+ * supporting types and function declarations in the interpreter.
+ *
+ * Later, this could potentially be used to register custom operations for modules.
+ *
+ * The reason why there's a function instead of accessing the unlang_op array
+ * directly, is because 'type' really needs to go away, as needing to add ops to
+ * the unlang_type_t enum breaks the pluggable module model. If there's no
+ * explicit/consistent type values we need to enumerate the operations ourselves.
+ *
+ * @param[in] type Operation identifier. Used to map compiled unlang code
+ * to operations.
+ * @param[in] op unlang_op to register.
+ */
+void unlang_op_register(int type, unlang_op_t *op)
+{
+ rad_assert(type < UNLANG_TYPE_MAX); /* Unlang max isn't a valid type */
+
+ memcpy(&unlang_ops[type], op, sizeof(unlang_ops[type]));
+}
/** Initialize the unlang compiler / interpreter.
*
{
(void) xlat_register(NULL, "interpreter", xlat_interpreter, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, true);
+ unlang_op_register(UNLANG_TYPE_RESUME, &(unlang_op_t){ .name = "resume", .func = unlang_resume });
+ unlang_op_initialize(); /* Register operations for the default keywords */
+
return 0;
}
#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/modpriv.h>
-#include <freeradius-devel/interpreter.h>
#include <freeradius-devel/parser.h>
#include <freeradius-devel/xlat.h>
#include <freeradius-devel/io/listen.h>
+#include "unlang_priv.h"
+
/*
* Some functions differ mainly in their parsing
*/
/*
* Copy the VPs from the original request, this ensures deterministic
- * behaviour if someone decides to add or remove VPs in the set were
+ * behaviour if someone decides to add or remove VPs in the set we're
* iterating over.
*/
if (tmpl_copy_vps(request, &vps, request, g->vpt) < 0) { /* nothing to loop over */
}
-/** Callback for handling resumption frames
- *
- * Resumption frames are added to track when a module, or other construct
- * has yielded control back to the interpreter.
- *
- * This function is called when the request has been marked as resumable
- * and a resumption frame was previously placed on the stack, i.e. when
- * the work that caused the request to be yielded initially has completed.
- *
- * @param[in] request to be resumed.
- * @param[out] presult the rcode returned by the resume function.
- * @param[out] priority associated with the rcode.
- */
-static unlang_action_t unlang_resume(REQUEST *request, rlm_rcode_t *presult, int *priority)
+void unlang_op_initialize(void)
{
- 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_action_t action;
-
- RDEBUG3("Resuming in %s", mr->self.debug_name);
-
- if (!unlang_ops[mr->parent->type].resume) {
- *presult = RLM_MODULE_FAIL;
- return UNLANG_ACTION_CALCULATE_RESULT;
- }
-
- request->module = mr->self.debug_name;
-
- /*
- * Run the resume callback associated with
- * the original frame which was used to
- * create this resumption frame.
- */
- action = unlang_ops[mr->parent->type].resume(request, presult, mr->resume_ctx);
-
- request->module = NULL;
-
- /*
- * Leave mr alone, it will be freed when the request is done.
- */
-
- /*
- * Is now marked as "stop" when it wasn't before, we must have been blocked.
- */
- if (request->master_state == REQUEST_STOP_PROCESSING) {
- RWARN("Module %s became unblocked", mr->self.debug_name);
- return UNLANG_ACTION_STOP_PROCESSING;
- }
+ unlang_op_register(UNLANG_TYPE_MODULE_CALL,
+ &(unlang_op_t){
+ .name = "module",
+ .func = unlang_module_call,
+ .signal = unlang_module_signal,
+ .resume = unlang_module_resume
+ });
+
+ unlang_op_register(UNLANG_TYPE_GROUP,
+ &(unlang_op_t){
+ .name = "group",
+ .func = unlang_group,
+ .debug_braces = true
+ });
+
+ unlang_op_register(UNLANG_TYPE_LOAD_BALANCE,
+ &(unlang_op_t){
+ .name = "load-balance group",
+ .func = unlang_load_balance,
+ .debug_braces = true
+ });
+
+ unlang_op_register(UNLANG_TYPE_REDUNDANT_LOAD_BALANCE,
+ &(unlang_op_t){
+ .name = "redundant-load-balance group",
+ .func = unlang_redundant_load_balance,
+ .debug_braces = true
+ });
+
+ unlang_op_register(UNLANG_TYPE_PARALLEL,
+ &(unlang_op_t){
+ .name = "parallel",
+ .func = unlang_parallel,
+ .signal = unlang_parallel_signal,
+ .resumable = unlang_parallel_resumable,
+ .resume = unlang_parallel_resume,
+ .debug_braces = true
+ });
- if (*presult != RLM_MODULE_YIELD) {
- rad_assert(*presult >= RLM_MODULE_REJECT);
- rad_assert(*presult < RLM_MODULE_NUMCODES);
- *priority = instruction->actions[*presult];
- }
-
- return action;
-}
-
-unlang_op_t unlang_ops[] = {
- [UNLANG_TYPE_MODULE_CALL] = {
- .name = "module-call",
- .func = unlang_module_call,
- .signal = unlang_module_signal,
- .resume = unlang_module_resume,
- .debug_braces = false
- },
- [UNLANG_TYPE_GROUP] = {
- .name = "group",
- .func = unlang_group,
- .debug_braces = true
- },
- [UNLANG_TYPE_LOAD_BALANCE] = {
- .name = "load-balance group",
- .func = unlang_load_balance,
- .debug_braces = true
- },
- [UNLANG_TYPE_REDUNDANT_LOAD_BALANCE] = {
- .name = "redundant-load-balance group",
- .func = unlang_redundant_load_balance,
- .debug_braces = true
- },
- [UNLANG_TYPE_PARALLEL] = {
- .name = "parallel",
- .func = unlang_parallel,
- .signal = unlang_parallel_signal,
- .resumable = unlang_parallel_resumable,
- .resume = unlang_parallel_resume,
- .debug_braces = true
- },
#ifdef WITH_UNLANG
- [UNLANG_TYPE_IF] = {
- .name = "if",
- .func = unlang_if,
- .debug_braces = true
- },
- [UNLANG_TYPE_ELSE] = {
- .name = "else",
- .func = unlang_group,
- .debug_braces = true
- },
- [UNLANG_TYPE_ELSIF] = {
- .name = "elsif",
- .func = unlang_if,
- .debug_braces = true
- },
- [UNLANG_TYPE_UPDATE] = {
- .name = "update",
- .func = unlang_update,
- .debug_braces = true
- },
- [UNLANG_TYPE_SWITCH] = {
- .name = "switch",
- .func = unlang_switch,
- .debug_braces = true
- },
- [UNLANG_TYPE_CASE] = {
- .name = "case",
- .func = unlang_case,
- .debug_braces = true
- },
- [UNLANG_TYPE_FOREACH] = {
- .name = "foreach",
- .func = unlang_foreach,
- .debug_braces = true
- },
- [UNLANG_TYPE_BREAK] = {
- .name = "break",
- .func = unlang_break,
- .debug_braces = false
- },
- [UNLANG_TYPE_RETURN] = {
- .name = "return",
- .func = unlang_return,
- .debug_braces = false
- },
- [UNLANG_TYPE_MAP] = {
- .name = "map",
- .func = unlang_map,
- .debug_braces = true
- },
- [UNLANG_TYPE_POLICY] = {
- .name = "policy",
- .func = unlang_policy,
- .debug_braces = true
- },
- [UNLANG_TYPE_SUBREQUEST] = {
- .name = "subrequest",
- .func = unlang_subrequest,
- .signal = unlang_subrequest_signal,
- .resume = unlang_subrequest_resume,
- .debug_braces = true
- },
- [UNLANG_TYPE_DETACH] = {
- .name = "detach",
- .func = unlang_detach,
- .debug_braces = false
- },
- [UNLANG_TYPE_CALL] = {
- .name = "call",
- .func = unlang_call,
- .debug_braces = true
- },
+ unlang_op_register(UNLANG_TYPE_IF,
+ &(unlang_op_t){
+ .name = "if",
+ .func = unlang_if,
+ .debug_braces = true
+ });
+
+ unlang_op_register(UNLANG_TYPE_ELSE,
+ &(unlang_op_t){
+ .name = "else",
+ .func = unlang_group,
+ .debug_braces = true
+ });
+
+ unlang_op_register(UNLANG_TYPE_ELSIF,
+ &(unlang_op_t){
+ .name = "elseif",
+ .func = unlang_if,
+ .debug_braces = true
+ });
+
+ unlang_op_register(UNLANG_TYPE_UPDATE,
+ &(unlang_op_t){
+ .name = "update",
+ .func = unlang_update,
+ .debug_braces = true
+ });
+
+ unlang_op_register(UNLANG_TYPE_SWITCH,
+ &(unlang_op_t){
+ .name = "switch",
+ .func = unlang_switch,
+ .debug_braces = true
+ });
+
+ unlang_op_register(UNLANG_TYPE_CASE,
+ &(unlang_op_t){
+ .name = "case",
+ .func = unlang_case,
+ .debug_braces = true
+ });
+
+ unlang_op_register(UNLANG_TYPE_FOREACH,
+ &(unlang_op_t){
+ .name = "foreach",
+ .func = unlang_foreach,
+ .debug_braces = true
+ });
+
+ unlang_op_register(UNLANG_TYPE_BREAK,
+ &(unlang_op_t){
+ .name = "break",
+ .func = unlang_break,
+ });
+
+ unlang_op_register(UNLANG_TYPE_RETURN,
+ &(unlang_op_t){
+ .name = "return",
+ .func = unlang_return,
+ });
+
+ unlang_op_register(UNLANG_TYPE_MAP,
+ &(unlang_op_t){
+ .name = "map",
+ .func = unlang_map,
+ });
+
+
+ unlang_op_register(UNLANG_TYPE_POLICY,
+ &(unlang_op_t){
+ .name = "policy",
+ .func = unlang_policy,
+ });
+
+
+ unlang_op_register(UNLANG_TYPE_SUBREQUEST,
+ &(unlang_op_t){
+ .name = "subrequest",
+ .func = unlang_subrequest,
+ .signal = unlang_subrequest_signal,
+ .resume = unlang_subrequest_resume,
+ .debug_braces = true
+ });
+
+ unlang_op_register(UNLANG_TYPE_DETACH,
+ &(unlang_op_t){
+ .name = "detach",
+ .func = unlang_detach,
+ });
+
+ unlang_op_register(UNLANG_TYPE_CALL,
+ &(unlang_op_t){
+ .name = "call",
+ .func = unlang_call,
+ .debug_braces = true
+ });
#endif
- [UNLANG_TYPE_XLAT_INLINE] = {
- .name = "xlat_inline",
- .func = unlang_xlat_inline,
- .debug_braces = false
- },
- [UNLANG_TYPE_XLAT] = {
- .name = "xlat_eval",
- .func = unlang_xlat,
- .resume = unlang_xlat_resume,
- .debug_braces = false
- },
- [UNLANG_TYPE_RESUME] = {
- .name = "resume",
- .func = unlang_resume,
- .debug_braces = false
- },
- [UNLANG_TYPE_MAX] = { NULL, NULL, NULL, NULL, false }
-};
+
+ 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
+ });
+}
--- /dev/null
+/*
+ * 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, 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+#ifndef _FR_UNLANG_PRIV_H
+#define _FR_UNLANG_PRIV_H
+/**
+ * $Id$
+ *
+ * @file src/main/unlang.h
+ * @brief Private interpreter structures and functions
+ *
+ * @author Alan DeKok <aland@freeradius.org>
+ */
+#include <freeradius-devel/cf_util.h> /* Need CONF_* definitions */
+#include <freeradius-devel/map_proc.h>
+#include <freeradius-devel/modpriv.h>
+#include <freeradius-devel/rad_assert.h>
+#include <freeradius-devel/unlang.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UNLANG_STACK_MAX (64)
+
+/* Actions may be a positive integer (the highest one returned in the group
+ * will be returned), or the keyword "return", represented here by
+ * MOD_ACTION_RETURN, to cause an immediate return.
+ * There's also the keyword "reject", represented here by MOD_ACTION_REJECT
+ * to cause an immediate reject. */
+#define MOD_ACTION_RETURN (-1)
+#define MOD_ACTION_REJECT (-2)
+#define MOD_PRIORITY_MAX (64)
+
+/** Types of unlang_t nodes
+ *
+ * Here are our basic types: unlang_t, unlang_group_t, and unlang_module_call_t. For an
+ * explanation of what they are all about, see doc/configurable_failover.rst
+ */
+typedef enum {
+ UNLANG_TYPE_NULL = 0, //!< Modcallable type not set.
+ UNLANG_TYPE_MODULE_CALL = 1, //!< Module method.
+ UNLANG_TYPE_GROUP, //!< Grouping section.
+ UNLANG_TYPE_LOAD_BALANCE, //!< Load balance section.
+ UNLANG_TYPE_REDUNDANT_LOAD_BALANCE, //!< Redundant load balance section.
+ UNLANG_TYPE_PARALLEL, //!< execute statements in parallel
+#ifdef WITH_UNLANG
+ UNLANG_TYPE_IF, //!< Condition.
+ UNLANG_TYPE_ELSE, //!< !Condition.
+ UNLANG_TYPE_ELSIF, //!< !Condition && Condition.
+ UNLANG_TYPE_UPDATE, //!< Update block.
+ UNLANG_TYPE_SWITCH, //!< Switch section.
+ UNLANG_TYPE_CASE, //!< Case section (within a #UNLANG_TYPE_SWITCH).
+ UNLANG_TYPE_FOREACH, //!< Foreach section.
+ UNLANG_TYPE_BREAK, //!< Break statement (within a #UNLANG_TYPE_FOREACH).
+ UNLANG_TYPE_RETURN, //!< Return statement.
+ UNLANG_TYPE_MAP, //!< Mapping section (like #UNLANG_TYPE_UPDATE, but uses
+ //!< values from a #map_proc_t call).
+ UNLANG_TYPE_SUBREQUEST, //!< create a child subrequest
+ UNLANG_TYPE_DETACH, //!< detach a child
+ UNLANG_TYPE_CALL, //!< call another virtual server
+#endif
+ UNLANG_TYPE_POLICY, //!< Policy section.
+ UNLANG_TYPE_XLAT_INLINE, //!< xlat statement, inline in "unlang"
+ UNLANG_TYPE_XLAT, //!< Represents one level of an xlat expansion.
+ UNLANG_TYPE_RESUME, //!< where to resume processing
+ UNLANG_TYPE_MAX
+} unlang_type_t;
+
+/** Allows the frame evaluator to signal the interpreter
+ *
+ */
+typedef enum {
+ UNLANG_FRAME_ACTION_POP = 1, //!< Pop the current frame, and check the next one further
+ ///< up in the stack for what to do next.
+ UNLANG_FRAME_ACTION_CONTINUE, //!< Process the next instruction at this level.
+ UNLANG_FRAME_ACTION_YIELD //!< Temporarily return control back to the caller on the C
+ ///< stack.
+} unlang_frame_action_t;
+
+typedef enum {
+ UNLANG_GROUP_TYPE_SIMPLE = 0, //!< Execute each of the children sequentially, until we execute
+ //!< all of the children, or one returns #UNLANG_ACTION_BREAK.
+ UNLANG_GROUP_TYPE_REDUNDANT, //!< Execute each of the children until one returns a 'good'
+ //!< result i.e. ok, updated, noop, then break out of the group.
+ UNLANG_GROUP_TYPE_MAX //!< Number of group types.
+} unlang_group_type_t;
+
+#define UNLANG_NEXT_STOP (false)
+#define UNLANG_NEXT_CONTINUE (true)
+
+#define UNLANG_TOP_FRAME (true)
+#define UNLANG_SUB_FRAME (false)
+
+#define UNLANG_DETACHABLE (true)
+#define UNLANG_NORMAL_CHILD (false)
+
+/** A node in a graph of #unlang_op_t (s) that we execute
+ *
+ * The interpreter acts like a turing machine, with #unlang_t nodes forming the tape
+ * and the #unlang_action_t the instructions.
+ *
+ * This is the parent 'class' for multiple #unlang_t node specialisations.
+ * The #unlang_t struct is listed first in the specialisation so that we can cast between
+ * parent/child classes without knowledge of the layout of the structures.
+ *
+ * The specialisations of the nodes describe additional details of the operation to be performed.
+ */
+typedef struct unlang_t {
+ struct unlang_t *parent; //!< Previous node.
+ struct unlang_t *next; //!< Next node (executed on #UNLANG_ACTION_CONTINUE et al).
+ char const *name; //!< Unknown...
+ char const *debug_name; //!< Printed in log messages when the node is executed.
+ unlang_type_t type; //!< The specialisation of this node.
+ int actions[RLM_MODULE_NUMCODES]; //!< Priorities for the various return codes.
+} unlang_t;
+
+/** Generic representation of a grouping
+ *
+ * Can represent IF statements, maps, update sections etc...
+ */
+typedef struct {
+ unlang_t self;
+ unlang_group_type_t group_type;
+ unlang_t *children; //!< Children beneath this group. The body of an if
+ //!< section for example.
+ unlang_t *tail; //!< of the children list.
+ CONF_SECTION *cs;
+ int num_children;
+
+ /*
+ * Hackity-hack. We should probably just have a common
+ * group header, and then have type-specific structures.
+ */
+ union {
+ struct {
+ vp_tmpl_t *vpt; //!< #UNLANG_TYPE_SWITCH, #UNLANG_TYPE_MAP, #UNLANG_TYPE_CALL
+
+ union {
+ struct {
+ vp_map_t *map; //!< #UNLANG_TYPE_UPDATE, #UNLANG_TYPE_MAP.
+ map_proc_inst_t *proc_inst; //!< Instantiation data for #UNLANG_TYPE_MAP.
+ };
+ struct {
+ void const *process; //!< #UNLANG_TYPE_CALL
+ CONF_SECTION *server_cs; //!< #UNLANG_TYPE_CALL
+ };
+ };
+ };
+ fr_cond_t *cond; //!< #UNLANG_TYPE_IF, #UNLANG_TYPE_ELSIF.
+
+ bool clone; //!< #UNLANG_TYPE_PARALLEL
+ };
+} unlang_group_t;
+
+/** A call to a module method
+ *
+ */
+typedef struct {
+ unlang_t self;
+ module_instance_t *module_instance; //!< Instance of the module we're calling.
+ module_method_t method;
+} unlang_module_call_t;
+
+/** Pushed onto the interpreter stack by a yielding module, indicates the resumption point
+ *
+ * Unlike normal coroutines in other languages, we represent resumption points as states in a state
+ * machine made up of function pointers.
+ *
+ * When a module yields, it specifies the function to call when whatever condition is
+ * required for resumption is satisfied, it also specifies the ctx for that function,
+ * which represents the internal state of the module at the time of yielding.
+ *
+ * If you want normal coroutine behaviour... ctx is arbitrary and could include a state enum,
+ * in which case the function pointer could be the same as the function that yielded, and something
+ * like Duff's device could be used to jump back to the yield point.
+ *
+ * Yield/resume are left as flexible as possible. Writing async code this way is difficult enough
+ * without being straightjacketed.
+ */
+typedef struct {
+ unlang_t self;
+ unlang_t *parent; //!< The original instruction.
+
+ void *callback; //!< Function the yielding code indicated should
+ //!< be called when the request could be resumed.
+
+ void *signal; //!< Function the yielding code indicated should
+ ///< be called if the request is destroyed in
+ ///< the middle of an async operation.
+
+ void *resume_ctx; //!< Context data for the callback. Usually represents
+ ///< the function's internal state at the time of
+ ///< yielding.
+} unlang_resume_t;
+
+/** A naked xlat
+ *
+ * @note These are vestigial and may be removed in future.
+ */
+typedef struct {
+ unlang_t self;
+ int exec;
+ char *xlat_name;
+ xlat_exp_t *exp; //!< First xlat node to execute.
+} unlang_xlat_inline_t;
+
+/** A module stack entry
+ *
+ * Represents a single module call.
+ */
+typedef struct {
+ module_thread_instance_t *thread; //!< thread-local data for this module
+} unlang_stack_state_modcall_t;
+
+/** State of a foreach loop
+ *
+ */
+typedef struct {
+ vp_cursor_t cursor; //!< Used to track our place in the list
+ ///< we're iterating over.
+ VALUE_PAIR *vps; //!< List containing the attribute(s) we're
+ ///< iterating over.
+ VALUE_PAIR *variable; //!< Attribute we update the value of.
+ int depth; //!< Level of nesting of this foreach loop.
+#ifndef NDEBUG
+ int indent; //!< for catching indentation issues
+#endif
+} unlang_stack_state_foreach_t;
+
+/** State of a redundant operation
+ *
+ */
+typedef struct {
+ unlang_t *child;
+ 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
+ * and manage the call stack ourselves.
+ *
+ * After looking at various green thread implementations, it was decided that using the existing
+ * unlang interpreter stack was the best way to perform async I/O.
+ *
+ * Each request as an unlang interpreter stack associated with it, which represents its progress
+ * through the server. Because the interpreter stack is distinct from the C stack, we can have
+ * a single system thread with many thousands of pending requests.
+ */
+typedef struct {
+ unlang_t *instruction; //!< The unlang node we're evaluating.
+ unlang_t *next; //!< The next unlang node we will evaluate
+
+ /** Stack frame specialisations
+ *
+ * These store extra (mutable) state data, for the immutable (#unlang_t)
+ * instruction. Instructions can't be used to store data because they
+ * might be shared between multiple threads.
+ *
+ * Which stack_entry specialisation to use is determined by the
+ * instruction->type.
+ */
+ void *state;
+
+ rlm_rcode_t result; //!< The result from executing the instruction.
+ int priority; //!< Result priority. When we pop this stack frame
+ ///< this priority will be compared with the one of the
+ ///< frame lower in the stack to determine if the
+ ///< result stored in the lower stack frame should
+ ///< be replaced.
+
+ unlang_type_t unwind; //!< Unwind to this one if it exists.
+ ///< This is used for break and return.
+
+ bool repeat : 1; //!< Call the action callback again on our way
+ //!< back up the stack.
+ bool top_frame : 1; //!< are we the top frame of the stack?
+
+ union {
+ unlang_stack_state_foreach_t foreach; //!< Foreach iterator state.
+ unlang_stack_state_redundant_t redundant; //!< Redundant section state.
+ };
+} unlang_stack_frame_t;
+
+/** An unlang stack associated with a request
+ *
+ */
+typedef struct {
+ int depth; //!< Current depth we're executing at.
+ unlang_stack_frame_t frame[UNLANG_STACK_MAX]; //!< The stack...
+} unlang_stack_t;
+
+/** Different operations the interpreter can execute
+ */
+extern unlang_op_t unlang_ops[];
+
+#define MOD_NUM_TYPES (UNLANG_TYPE_XLAT + 1)
+
+extern char const *const comp2str[];
+
+/** @name Conversion functions for converting #unlang_t to its specialisations
+ *
+ * Simple conversions: #unlang_module_call_t and #unlang_group_t are subclasses of #unlang_t,
+ * so we often want to go back and forth between them.
+ *
+ * @{
+ */
+static inline unlang_module_call_t *unlang_generic_to_module_call(unlang_t *p)
+{
+ rad_assert(p->type == UNLANG_TYPE_MODULE_CALL);
+ return talloc_get_type_abort(p, unlang_module_call_t);
+}
+
+static inline unlang_group_t *unlang_generic_to_group(unlang_t *p)
+{
+ rad_assert((p->type > UNLANG_TYPE_MODULE_CALL) && (p->type <= UNLANG_TYPE_POLICY));
+
+ return (unlang_group_t *)p;
+}
+
+static inline unlang_t *unlang_module_call_to_generic(unlang_module_call_t *p)
+{
+ return (unlang_t *)p;
+}
+
+static inline unlang_t *unlang_group_to_generic(unlang_group_t *p)
+{
+ return (unlang_t *)p;
+}
+
+static inline unlang_xlat_inline_t *unlang_generic_to_xlat_inline(unlang_t *p)
+{
+ rad_assert(p->type == UNLANG_TYPE_XLAT_INLINE);
+ return talloc_get_type_abort(p, unlang_xlat_inline_t);
+}
+
+static inline unlang_t *unlang_xlat_inline_to_generic(unlang_xlat_inline_t *p)
+{
+ return (unlang_t *)p;
+}
+
+static inline unlang_resume_t *unlang_generic_to_resume(unlang_t *p)
+{
+ rad_assert(p->type == UNLANG_TYPE_RESUME);
+ return talloc_get_type_abort(p, unlang_resume_t);
+}
+
+static inline unlang_t *unlang_resume_to_generic(unlang_resume_t *p)
+{
+ return (unlang_t *)p;
+}
+/* @} **/
+
+/*
+ * Internal interpreter functions needed by ops
+ */
+void unlang_push(unlang_stack_t *stack, unlang_t *program, rlm_rcode_t result,
+ bool do_next_sibling, bool top_frame);
+rlm_rcode_t unlang_run(REQUEST *request);
+
+void unlang_op_initialize(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/modpriv.h>
-#include <freeradius-devel/interpreter.h>
+#include <freeradius-devel/unlang.h>
#include <freeradius-devel/parser.h>
#include <freeradius-devel/protocol.h>
#include <freeradius-devel/io/listen.h>