The signal enum needs to be in its own header, because it's used by the proto module state machines and the unlang interpreter, but the two really shouldn't know about each other.
*/
typedef int (*module_thread_detach_t)(void *thread);
+
+/** 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_signal_t.
+ *
+ * A module may call unlang_yeild(), but still need to do something on FR_SIGNAL_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_signal_t action);
+
/** Struct exported by a rlm_* module
*
* Determines the capabilities of the module, and maps internal functions
CONF_SECTION *virtual_server_find(char const *name);
void fr_request_async_bootstrap(REQUEST *request, fr_event_list_t *el); /* for unit_test_module */
+/*
+ * modules_unlang.c
+ */
+
+rlm_rcode_t module_unlang_push_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);
#ifdef __cplusplus
}
#endif
#include <freeradius-devel/clients.h>
#include <freeradius-devel/listen.h>
+#include <freeradius-devel/signal.h>
#ifdef __cplusplus
extern "C" {
#endif
-typedef enum fr_state_action_t { /* server action */
- FR_ACTION_INVALID = 0,
- FR_ACTION_RUN,
- FR_ACTION_DONE, //!< Request is completed. If a module is signalled
- ///< with this, the module should stop processing
- ///< the request and cleanup.
- FR_ACTION_DUP, //!< A duplicate request was received.
-} fr_state_action_t;
-
/*
* Function handler for requests.
*/
-typedef void (*fr_request_process_t)(REQUEST *, fr_state_action_t);
+typedef void (*fr_request_process_t)(REQUEST *, fr_state_signal_t);
extern time_t fr_start_time;
*/
void request_delete(REQUEST *request);
-
#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 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
+ */
+#ifndef _FR_SIGNAL_H
+#define _FR_SIGNAL_H
+/**
+ * $Id$
+ *
+ * @file include/signal.h
+ * @brief Signals that can be sent to a request.
+ *
+ * @copyright 2018 The FreeRADIUS server project
+ * @copyright 2018 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
+ */
+RCSIDH(signal_h, "$Id$")
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Signals that can be generated/processed by request signal handlers
+ *
+ */
+typedef enum fr_state_signal_t { /* server action */
+ FR_SIGNAL_INVALID = 0,
+ FR_SIGNAL_RUN,
+ FR_SIGNAL_DONE, //!< Request is completed. If a module is signalled
+ ///< with this, the module should stop processing
+ ///< the request and cleanup.
+ FR_SIGNAL_DUP, //!< A duplicate request was received.
+} fr_state_signal_t;
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* _FR_SIGNAL_H */
*/
#include <freeradius-devel/radiusd.h>
#include <freeradius-devel/modules.h>
+#include <freeradius-devel/signal.h>
/** Returned by #unlang_op_t calls, determine the next action of the interpreter
*
* @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);
+typedef unlang_action_t (*unlang_op_call_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.
+ * or just cleanup the state of the original #unlang_op_call_t.
*
* @param[in] request The current request.
- * @param[in] resume_ctx A structure allocated by the initial #unlang_op_func_t to store
+ * @param[in] rctx A structure allocated by the initial #unlang_op_call_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);
+typedef void (*unlang_op_signal_t)(REQUEST *request, void *rctx, fr_state_signal_t action);
/** Function to call when a request becomes resumable
*
* 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
+ * @param[in] rctx A structure allocated by the initial #unlang_op_call_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);
+typedef void (*unlang_op_resumable_t)(REQUEST *request, void *rctx);
/** 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
+ * @param[in] rctx A structure allocated by the initial #unlang_op_call_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);
+typedef unlang_action_t (*unlang_op_resume_t)(REQUEST *request, rlm_rcode_t *presult, void *rctx);
/** An unlang operation
*
*/
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
+ unlang_op_call_t func; //!< Called when we start the operation.
+
+ unlang_op_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
+ unlang_op_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
+ unlang_op_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);
void unlang_resumable(REQUEST *request);
-void unlang_signal(REQUEST *request, fr_state_action_t action);
+void unlang_signal(REQUEST *request, fr_state_signal_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 */
* mean it turned a result.
* - XLAT_ACTION_FAIL the xlat function failed.
*/
-typedef xlat_action_t (*xlat_resume_callback_t)(TALLOC_CTX *ctx, fr_cursor_t *out,
- REQUEST *request, void const *xlat_inst, void *xlat_thread_inst,
- fr_cursor_t *in, void *rctx);
+typedef xlat_action_t (*xlat_func_resume_t)(TALLOC_CTX *ctx, fr_cursor_t *out,
+ REQUEST *request, void const *xlat_inst, void *xlat_thread_inst,
+ fr_cursor_t *in, void *rctx);
+
+/** A callback when the request gets a fr_state_signal_t.
+ *
+ * @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 (*xlat_func_signal_t)(REQUEST *request, void *instance, void *thread,
+ void *rctx, int action);
/** Allocate new instance data for an xlat instance
*
void xlat_instances_free(void);
+/*
+ * xlat_unlang.c
+ */
+void xlat_unlang_push(TALLOC_CTX *ctx, fr_value_box_t **out,
+ REQUEST *request, xlat_exp_t const *exp, bool top_frame);
+
+xlat_action_t xlat_unlang_yield(REQUEST *request,
+ xlat_func_resume_t callback, xlat_func_signal_t signal,
+ void *rctx);
#ifdef __cplusplus
}
#endif
map_proc.c \
map.c \
modules.c \
+ modules_unlang.c \
regex.c \
request.c \
trigger.c \
--- /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 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 modules_unlang.c
+ * @brief Defines functions for calling modules asynchronously
+ *
+ * @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/modpriv.h>
+#include <freeradius-devel/parser.h>
+#include <freeradius-devel/unlang.h>
+#include <freeradius-devel/xlat.h>
+#include "unlang_priv.h"
+
+/** 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 module_unlang_push_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
+ */
+ xlat_unlang_push(ctx, out, request, xlat, true);
+
+ /*
+ * Execute the xlat frame we just pushed onto the stack.
+ */
+ return unlang_run(request);
+}
unlang_compile_t *unlang_ctx, CONF_PAIR const *cp)
{
unlang_t *c;
- unlang_xlat_inline_t *mx;
+ xlat_unlang_inline_t *mx;
- mx = talloc_zero(parent, unlang_xlat_inline_t);
+ mx = talloc_zero(parent, xlat_unlang_inline_t);
- c = unlang_xlat_inline_to_generic(mx);
+ c = xlat_unlang_inline_to_generic(mx);
c->parent = parent;
c->next = NULL;
c->name = "expand";
* @param[in] request The current request.
* @param[in] callback to call on unlang_resumable().
* @param[in] signal call on unlang_action().
- * @param[in] resume_ctx to pass to the callbacks.
+ * @param[in] rctx to pass to the callbacks.
* @return
* unlang_resume_t on success
* NULL on error
*/
-static unlang_resume_t *unlang_resume_alloc(REQUEST *request,
- void *callback,
- fr_unlang_module_signal_t signal, void *resume_ctx)
+unlang_resume_t *unlang_resume_alloc(REQUEST *request, void *callback, void *signal, void *rctx)
{
unlang_resume_t *mr;
unlang_stack_t *stack = request->stack;
*/
mr->callback = callback;
mr->signal = signal;
- mr->resume_ctx = resume_ctx;
+ mr->rctx = rctx;
/*
* Replaces the current stack frame with a RESUME frame.
* @param[in] request The current request.
* @param[in] action to signal.
*/
-void unlang_signal(REQUEST *request, fr_state_action_t action)
+void unlang_signal(REQUEST *request, fr_state_signal_t action)
{
unlang_stack_frame_t *frame;
unlang_stack_t *stack = request->stack;
*/
if (!unlang_ops[mr->parent->type].signal) return;
- unlang_ops[mr->parent->type].signal(request, mr->resume_ctx, action);
+ unlang_ops[mr->parent->type].signal(request, mr->rctx, action);
}
int unlang_stack_depth(REQUEST *request)
if (mr->parent->type != UNLANG_TYPE_PARALLEL) goto next;
- state = mr->resume_ctx;
+ state = mr->rctx;
/*
* Find the child and mark it resumable
* the original frame which was used to
* create this resumption frame.
*/
- action = unlang_ops[mr->parent->type].resume(request, presult, mr->resume_ctx);
+ action = unlang_ops[mr->parent->type].resume(request, presult, mr->rctx);
/*
* Leave mr alone, it will be freed when the request is done.
* @param[in] request The current request.
* @param[in] callback to call on unlang_resumable().
* @param[in] cancel to call on unlang_action().
- * @param[in] resume_ctx to pass to the callbacks.
+ * @param[in] rctx to pass to the callbacks.
* @return
* - RLM_MODULE_YIELD on success.
* - RLM_MODULE_FAIL (or asserts) if the current frame is not a module call or
* resume frame.
*/
rlm_rcode_t unlang_module_yield(REQUEST *request, fr_unlang_module_resume_t callback,
- fr_unlang_module_signal_t cancel, void *resume_ctx)
+ fr_unlang_module_signal_t cancel, void *rctx)
{
unlang_stack_t *stack = request->stack;
unlang_stack_frame_t *frame = &stack->frame[stack->depth];
switch (frame->instruction->type) {
case UNLANG_TYPE_MODULE_CALL:
- mr = unlang_resume_alloc(request, callback, cancel, resume_ctx);
+ mr = unlang_resume_alloc(request, callback, cancel, rctx);
if (!fr_cond_assert(mr)) {
return RLM_MODULE_FAIL;
}
*/
mr->callback = callback;
mr->signal = signal;
- mr->resume_ctx = resume_ctx;
+ mr->rctx = rctx;
return RLM_MODULE_YIELD;
}
}
-/** Yield a request back to the interpreter from within a module
- *
- * This passes control of the request back to the unlang interpreter, setting
- * callbacks to execute when the request is 'signalled' asynchronously, or whatever
- * timer or I/O event the module was waiting for occurs.
- *
- * @note The module function which calls #unlang_module_yield should return control
- * of the C stack to the unlang interpreter immediately after calling #unlang_module_yield.
- * A common pattern is to use ``return unlang_module_yield(...)``.
- *
- * @param[in] request The current request.
- * @param[in] callback to call on unlang_resumable().
- * @param[in] signal to call on unlang_action().
- * @param[in] resume_ctx to pass to the callbacks.
- * @return always returns RLM_MODULE_YIELD.
- */
-xlat_action_t unlang_xlat_yield(REQUEST *request,
- xlat_resume_callback_t callback, fr_unlang_module_signal_t signal,
- void *resume_ctx)
-{
- unlang_stack_t *stack = request->stack;
- unlang_stack_frame_t *frame = &stack->frame[stack->depth];
- unlang_resume_t *mr;
-
- rad_assert(stack->depth > 0);
-
- switch (frame->instruction->type) {
- case UNLANG_TYPE_XLAT:
- {
- mr = unlang_resume_alloc(request, callback, signal, resume_ctx);
- if (!fr_cond_assert(mr)) {
- return XLAT_ACTION_FAIL;
- }
- }
- return XLAT_ACTION_YIELD;
-
- case UNLANG_TYPE_RESUME:
- mr = talloc_get_type_abort(frame->instruction, unlang_resume_t);
- rad_assert(mr->parent->type == UNLANG_TYPE_XLAT);
-
- /*
- * Re-use the current RESUME frame, but override
- * the callbacks and context.
- */
- mr->callback = callback;
- mr->signal = signal;
- mr->resume_ctx = resume_ctx;
- return XLAT_ACTION_YIELD;
-
- default:
- rad_assert(0);
- return XLAT_ACTION_FAIL;
- }
-}
-
/** Get information about the interpreter state
*
*/
return UNLANG_ACTION_PUSHED_CHILD;
}
-/** Allocates and initializes an unlang_resume_t
- *
- * @param[in] request The current request.
- * @param[in] callback to call on unlang_resumable().
- * @param[in] signal call on unlang_action().
- * @param[in] resume_ctx to pass to the callbacks.
- * @return
- * unlang_resume_t on success
- * NULL on error
- */
-static unlang_resume_t *unlang_resume_alloc(REQUEST *request,
- void *callback,
- fr_unlang_module_signal_t signal, void *resume_ctx)
-{
- unlang_resume_t *mr;
- unlang_stack_t *stack = request->stack;
- unlang_stack_frame_t *frame = &stack->frame[stack->depth];
-
- mr = talloc_zero(request, unlang_resume_t);
- if (!mr) return NULL;
-
- /*
- * Remember the parent.
- */
- mr->parent = frame->instruction;
-
- /*
- * Initialize parent ptr, next ptr, name, debug_name,
- * type, actions, etc.
- */
- memcpy(&mr->self, frame->instruction, sizeof(mr->self));
-
- /*
- * But note that we're of type RESUME
- */
- mr->self.type = UNLANG_TYPE_RESUME;
-
- /*
- * Fill in the signal handlers and resumption ctx
- */
- mr->callback = callback;
- mr->signal = signal;
- mr->resume_ctx = resume_ctx;
-
- /*
- * Replaces the current stack frame with a RESUME frame.
- */
- frame->instruction = unlang_resume_to_generic(mr);
-
- return mr;
-}
-
/** Continue after creating a subrequest.
*
* Just run some "unlang", but don't do anything else.
* is waiting for something to happen.
*/
if (action != FR_IO_ACTION_RUN) {
- unlang_signal(request, (fr_state_action_t) action);
+ unlang_signal(request, (fr_state_signal_t) action);
return FR_IO_DONE;
}
/** Send a signal from parent request to subrequest
*
*/
-static void unlang_subrequest_signal(UNUSED REQUEST *request, void *ctx, fr_state_action_t action)
+static void unlang_subrequest_signal(UNUSED REQUEST *request, void *ctx, fr_state_signal_t action)
{
REQUEST *child = talloc_get_type_abort(ctx, REQUEST);
/** Resume a subrequest
*
*/
-static unlang_action_t unlang_subrequest_resume(UNUSED REQUEST *request, rlm_rcode_t *presult, void *resume_ctx)
+static unlang_action_t unlang_subrequest_resume(UNUSED REQUEST *request, rlm_rcode_t *presult, void *rctx)
{
- REQUEST *child = talloc_get_type_abort(resume_ctx, REQUEST);
+ REQUEST *child = talloc_get_type_abort(rctx, REQUEST);
unlang_stack_t *stack = request->stack;
unlang_stack_frame_t *frame;
#ifndef NDEBUG
(void) talloc_get_type_abort(mr, unlang_resume_t);
rad_assert(mr->callback == NULL);
- rad_assert(mr->resume_ctx == child);
+ rad_assert(mr->rctx == child);
#endif
/*
* stopped. This tells any child modules
* to clean up timers, etc.
*/
- unlang_signal(state->children[i].child, FR_ACTION_DONE);
+ unlang_signal(state->children[i].child, FR_SIGNAL_DONE);
TALLOC_FREE(state->children[i].child);
/* FALL-THROUGH */
/** Send a signal from parent request to all of it's children
*
*/
-static void unlang_parallel_signal(UNUSED REQUEST *request, void *resume_ctx, fr_state_action_t action)
+static void unlang_parallel_signal(UNUSED REQUEST *request, void *rctx, fr_state_signal_t action)
{
int i;
- unlang_parallel_t *state = talloc_get_type_abort(resume_ctx, unlang_parallel_t);
+ unlang_parallel_t *state = talloc_get_type_abort(rctx, unlang_parallel_t);
/*
* Signal all of the children, if they exist.
}
}
-static void unlang_parallel_resumable(REQUEST *request, UNUSED void *resume_ctx)
+static void unlang_parallel_resumable(REQUEST *request, UNUSED void *rctx)
{
unlang_stack_t *stack;
unlang_stack_frame_t *frame;
mr = unlang_generic_to_resume(frame->instruction);
(void) talloc_get_type_abort(mr, unlang_resume_t);
- state = mr->resume_ctx;
+ state = mr->rctx;
/*
* Find the child and mark it resumable
rad_assert(found);
}
-static unlang_action_t unlang_parallel_resume(REQUEST *request, rlm_rcode_t *presult, void *resume_ctx)
+static unlang_action_t unlang_parallel_resume(REQUEST *request, rlm_rcode_t *presult, void *rctx)
{
- unlang_parallel_t *state = talloc_get_type_abort(resume_ctx, unlang_parallel_t);
+ unlang_parallel_t *state = talloc_get_type_abort(rctx, unlang_parallel_t);
unlang_stack_t *stack = request->stack;
unlang_stack_frame_t *frame = &stack->frame[stack->depth];
(void) talloc_get_type_abort(mr, unlang_resume_t);
rad_assert(mr->callback == NULL);
- rad_assert(mr->resume_ctx == state);
+ rad_assert(mr->rctx == state);
#endif
/*
* If there is no #fr_unlang_module_signal_t callback defined, the action is ignored.
*
* @param[in] request The current request.
- * @param[in] resume_ctx createed by #unlang_module_call.
+ * @param[in] rctx createed by #unlang_module_call.
* @param[in] action to signal.
*/
-static void unlang_module_signal(REQUEST *request, void *resume_ctx, fr_state_action_t action)
+static void unlang_module_signal(REQUEST *request, void *rctx, fr_state_signal_t action)
{
unlang_stack_frame_t *frame;
unlang_stack_t *stack = request->stack;
((fr_unlang_module_signal_t)mr->signal)(request,
mc->module_instance->dl_inst->data, ms->thread->data,
- resume_ctx, action);
+ rctx, action);
}
-static unlang_action_t unlang_module_resume(REQUEST *request, rlm_rcode_t *presult, UNUSED void *resume_ctx)
+static unlang_action_t unlang_module_resume(REQUEST *request, rlm_rcode_t *presult, UNUSED void *rctx)
{
unlang_stack_t *stack = request->stack;
unlang_stack_frame_t *frame = &stack->frame[stack->depth];
safe_lock(mc->module_instance);
*presult = request->rcode = ((fr_unlang_module_resume_t)mr->callback)(request,
mc->module_instance->dl_inst->data,
- ms->thread->data, mr->resume_ctx);
+ ms->thread->data, mr->rctx);
safe_unlock(mc->module_instance);
if (*presult != RLM_MODULE_YIELD) ms->thread->active_callers--;
*/
typedef struct {
unlang_t self;
+
unlang_t *parent; //!< The original instruction.
void *callback; //!< 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
+ void *rctx; //!< Context data for the callback. Usually represents
///< the function's internal state at the time of
///< yielding.
} unlang_resume_t;
int exec;
char *xlat_name;
xlat_exp_t *exp; //!< First xlat node to execute.
-} unlang_xlat_inline_t;
+} xlat_unlang_inline_t;
/** A module stack entry
*
return (unlang_t *)p;
}
-static inline unlang_xlat_inline_t *unlang_generic_to_xlat_inline(unlang_t *p)
+static inline xlat_unlang_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);
+ return talloc_get_type_abort(p, xlat_unlang_inline_t);
}
-static inline unlang_t *unlang_xlat_inline_to_generic(unlang_xlat_inline_t *p)
+static inline unlang_t *xlat_unlang_inline_to_generic(xlat_unlang_inline_t *p)
{
return (unlang_t *)p;
}
bool do_next_sibling, bool top_frame);
rlm_rcode_t unlang_run(REQUEST *request);
+unlang_resume_t *unlang_resume_alloc(REQUEST *request, void *callback, void *signal, void *rctx);
+
void unlang_op_initialize(void);
#ifdef __cplusplus
* when it yielded.
*/
xlat_action_t xlat_frame_eval_resume(TALLOC_CTX *ctx, fr_cursor_t *out,
- xlat_resume_callback_t resume, xlat_exp_t const *exp,
+ xlat_func_resume_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_eval.c
*/
xlat_action_t xlat_frame_eval_resume(TALLOC_CTX *ctx, fr_cursor_t *out,
- xlat_resume_callback_t resume, xlat_exp_t const *exp,
+ xlat_func_resume_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,
* @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)
+void xlat_unlang_push(TALLOC_CTX *ctx, fr_value_box_t **out,
+ REQUEST *request, xlat_exp_t const *exp, bool top_frame)
{
unlang_stack_state_xlat_t *state;
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,
+static unlang_action_t xlat_unlang(REQUEST *request,
rlm_rcode_t *presult, UNUSED int *priority)
{
unlang_stack_t *stack = request->stack;
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);
+ 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);
}
rad_assert(child);
frame->repeat = true;
- unlang_push_xlat(xs->ctx, &xs->rhead, request, child, false);
+ xlat_unlang_push(xs->ctx, &xs->rhead, request, child, false);
return UNLANG_ACTION_PUSHED_CHILD;
case XLAT_ACTION_YIELD:
return UNLANG_ACTION_CALCULATE_RESULT;
}
+/** Yield a request back to the interpreter from within a module
+ *
+ * This passes control of the request back to the unlang interpreter, setting
+ * callbacks to execute when the request is 'signalled' asynchronously, or whatever
+ * timer or I/O event the module was waiting for occurs.
+ *
+ * @note The module function which calls #unlang_module_yield should return control
+ * of the C stack to the unlang interpreter immediately after calling #unlang_module_yield.
+ * A common pattern is to use ``return unlang_module_yield(...)``.
+ *
+ * @param[in] request The current request.
+ * @param[in] callback to call on unlang_resumable().
+ * @param[in] signal to call on unlang_action().
+ * @param[in] rctx to pass to the callbacks.
+ * @return always returns RLM_MODULE_YIELD.
+ */
+xlat_action_t xlat_unlang_yield(REQUEST *request,
+ xlat_func_resume_t callback, xlat_func_signal_t signal,
+ void *rctx)
+{
+ unlang_stack_t *stack = request->stack;
+ unlang_stack_frame_t *frame = &stack->frame[stack->depth];
+ unlang_resume_t *mr;
+
+ rad_assert(stack->depth > 0);
+
+ switch (frame->instruction->type) {
+ case UNLANG_TYPE_XLAT:
+ {
+ mr = unlang_resume_alloc(request, callback, signal, rctx);
+ if (!fr_cond_assert(mr)) {
+ return XLAT_ACTION_FAIL;
+ }
+ }
+ return XLAT_ACTION_YIELD;
+
+ case UNLANG_TYPE_RESUME:
+ mr = talloc_get_type_abort(frame->instruction, unlang_resume_t);
+ rad_assert(mr->parent->type == UNLANG_TYPE_XLAT);
+
+ /*
+ * Re-use the current RESUME frame, but override
+ * the callbacks and context.
+ */
+ mr->callback = callback;
+ mr->signal = signal;
+ mr->rctx = rctx;
+ return XLAT_ACTION_YIELD;
+
+ default:
+ rad_assert(0);
+ return XLAT_ACTION_FAIL;
+ }
+}
+
/** Called when we're ready to resume processing the request
*
* @param[in] request to resume processing.
* - RLM_MODULE_FAIL on failure.
* - RLM_MODULE_YIELD if additional asynchronous operations
* need to be performed.
- * @param[in] resume_ctx provided by xlat function.
+ * @param[in] rctx provided by xlat function.
* @return
* - UNLANG_ACTION_YIELD if yielding.
* - UNLANG_ACTION_CALCULATE_RESULT if done.
*/
-static unlang_action_t unlang_xlat_resume(REQUEST *request, rlm_rcode_t *presult, void *resume_ctx)
+static unlang_action_t xlat_unlang_resume(REQUEST *request, rlm_rcode_t *presult, void *rctx)
{
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_action_t xa;
- xa = xlat_frame_eval_resume(xs->ctx, &xs->values, mr->callback, xs->exp, request, &xs->result, resume_ctx);
+ xa = xlat_frame_eval_resume(xs->ctx, &xs->values, mr->callback, xs->exp, request, &xs->result, rctx);
switch (xa) {
case XLAT_ACTION_YIELD:
*presult = RLM_MODULE_YIELD;
/** Evaluates "naked" xlats in the config
*
*/
-static unlang_action_t unlang_xlat_inline(REQUEST *request,
+static unlang_action_t xlat_unlang_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);
+ xlat_unlang_inline_t *mx = unlang_generic_to_xlat_inline(instruction);
if (!mx->exec) {
TALLOC_CTX *pool;
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);
+ xlat_unlang_push(pool, &state->result, request, mx->exp, false);
return UNLANG_ACTION_PUSHED_CHILD;
} else {
RDEBUG("`%s`", mx->xlat_name);
unlang_op_register(UNLANG_TYPE_XLAT,
&(unlang_op_t){
.name = "xlat_eval",
- .func = unlang_xlat,
- .resume = unlang_xlat_resume,
+ .func = xlat_unlang,
+ .resume = xlat_unlang_resume,
.debug_braces = false
});
unlang_op_register(UNLANG_TYPE_XLAT_INLINE,
&(unlang_op_t){
.name = "xlat_inline",
- .func = unlang_xlat_inline,
+ .func = xlat_unlang_inline,
.debug_braces = false
});
}
* is waiting for something to happen.
*/
if (action != FR_IO_ACTION_RUN) {
- unlang_signal(request, (fr_state_action_t) action);
+ unlang_signal(request, (fr_state_signal_t) action);
return FR_IO_DONE;
}
* @param[in] action If something has signalled that the request should stop
* being processed.
*/
-static void request_running(REQUEST *request, fr_state_action_t action)
+static void request_running(REQUEST *request, fr_state_signal_t action)
{
CONF_SECTION *unlang;
char const *verb;
/*
* Async (in the same thread, tho) signal to be done.
*/
- if (action == FR_ACTION_DONE) goto done;
+ if (action == FR_SIGNAL_DONE) goto done;
/*
* We ignore all other actions.
*/
- if (action != FR_ACTION_RUN) return;
+ if (action != FR_SIGNAL_RUN) return;
switch (request->request_state) {
case REQUEST_INIT:
* @param[in] action If something has signalled that the request should stop
* being processed.
*/
-static void request_queued(REQUEST *request, fr_state_action_t action)
+static void request_queued(REQUEST *request, fr_state_signal_t action)
{
REQUEST_VERIFY(request);
switch (action) {
- case FR_ACTION_RUN:
+ case FR_SIGNAL_RUN:
request->process = request_running;
request->process(request, action);
break;
- case FR_ACTION_DONE:
+ case FR_SIGNAL_DONE:
(void) fr_heap_extract(request->backlog, request);
request_delete(request);
break;
* is waiting for something to happen.
*/
if (action != FR_IO_ACTION_RUN) {
- unlang_signal(request, (fr_state_action_t) action);
+ unlang_signal(request, (fr_state_signal_t) action);
return FR_IO_DONE;
}
* is waiting for something to happen.
*/
if (action != FR_IO_ACTION_RUN) {
- unlang_signal(request, (fr_state_action_t) action);
+ unlang_signal(request, (fr_state_signal_t) action);
return FR_IO_DONE;
}
* is waiting for something to happen.
*/
if (action != FR_IO_ACTION_RUN) {
- unlang_signal(request, (fr_state_action_t) action);
+ unlang_signal(request, (fr_state_signal_t) action);
return FR_IO_DONE;
}
* is waiting for something to happen.
*/
if (action != FR_IO_ACTION_RUN) {
- unlang_signal(request, (fr_state_action_t) action);
+ unlang_signal(request, (fr_state_signal_t) action);
return FR_IO_DONE;
}
* is waiting for something to happen.
*/
if (action != FR_IO_ACTION_RUN) {
- unlang_signal(request, (fr_state_action_t) action);
+ unlang_signal(request, (fr_state_signal_t) action);
return FR_IO_DONE;
}
fr_pair_add(&packet->vps, vp);
}
-static void tacacs_running(REQUEST *request, fr_state_action_t action)
+static void tacacs_running(REQUEST *request, fr_state_signal_t action)
{
rlm_rcode_t rcode;
CONF_SECTION *unlang;
REQUEST_VERIFY(request);
switch (action) {
- case FR_ACTION_DONE:
+ case FR_SIGNAL_DONE:
goto done;
default:
}
}
-static void tacacs_queued(REQUEST *request, fr_state_action_t action)
+static void tacacs_queued(REQUEST *request, fr_state_signal_t action)
{
REQUEST_VERIFY(request);
switch (action) {
- case FR_ACTION_RUN:
+ case FR_SIGNAL_RUN:
request->process = tacacs_running;
request->process(request, action);
break;
- case FR_ACTION_DONE:
+ case FR_SIGNAL_DONE:
(void) fr_heap_extract(request->backlog, request);
request_delete(request);
break;
}
static void delay_cancel(REQUEST *request, UNUSED void *instance, UNUSED void *thread, void *ctx,
- fr_state_action_t action)
+ fr_state_signal_t action)
{
- if (action != FR_ACTION_DONE) return;
+ if (action != FR_SIGNAL_DONE) return;
RDEBUG2("Cancelling delay");
}
static void mod_radius_signal(REQUEST *request, void *instance, void *thread, void *ctx,
- fr_state_action_t action)
+ fr_state_signal_t action)
{
rlm_radius_t const *inst = talloc_get_type_abort_const(instance, rlm_radius_t);
rlm_radius_thread_t *t = talloc_get_type_abort(thread, rlm_radius_thread_t);
* the IO modules to do additional debugging if
* necessary.
*/
- if (action == FR_ACTION_DONE) {
+ if (action == FR_SIGNAL_DONE) {
talloc_free(link);
return;
}
* synchronous proxying. Ignore the dup, and rely on the
* IO submodule to time it's own retransmissions.
*/
- if ((action == FR_ACTION_DUP) && !inst->synchronous) return;
+ if ((action == FR_SIGNAL_DUP) && !inst->synchronous) return;
if (!inst->io->signal) return;
*
*/
typedef rlm_rcode_t (*fr_radius_io_push_t)(void *instance, REQUEST *request, rlm_radius_link_t *link, void *thread);
-typedef void (*fr_radius_io_signal_t)(REQUEST *request, void *instance, void *thread, rlm_radius_link_t *link, fr_state_action_t action);
+typedef void (*fr_radius_io_signal_t)(REQUEST *request, void *instance, void *thread, rlm_radius_link_t *link, fr_state_signal_t action);
typedef int (*fr_radius_io_instantiate_t)(rlm_radius_t *inst, void *io_instance, CONF_SECTION *cs);
}
-static void mod_signal(REQUEST *request, UNUSED void *instance, UNUSED void *thread, rlm_radius_link_t *link, fr_state_action_t action)
+static void mod_signal(REQUEST *request, UNUSED void *instance, UNUSED void *thread, rlm_radius_link_t *link, fr_state_signal_t action)
{
rlm_radius_udp_request_t *u = link->request_io_ctx;
struct timeval now;
- if (action != FR_ACTION_DUP) return;
+ if (action != FR_SIGNAL_DUP) return;
/*
* Sychronous mode means that we don't do any
/** Handle asynchronous cancellation of a request
*
- * If we're signalled that the request has been cancelled (FR_ACTION_DONE).
+ * If we're signalled that the request has been cancelled (FR_SIGNAL_DONE).
* Cleanup any pending state and release the connection handle back into the pool.
*
* @param[in] request being cancelled.
* @param[in] ctx rlm_rest_handle_t currently used by the request.
* @param[in] action What happened.
*/
-void rest_io_action(REQUEST *request, void *instance, void *thread, void *ctx, fr_state_action_t action)
+void rest_io_action(REQUEST *request, void *instance, void *thread, void *ctx, fr_state_signal_t action)
{
rlm_rest_handle_t *randle = talloc_get_type_abort(ctx, rlm_rest_handle_t);
rlm_rest_thread_t *t = thread;
CURLMcode ret;
- if (action != FR_ACTION_DONE) return;
+ if (action != FR_SIGNAL_DONE) return;
RDEBUG("Forcefully cancelling pending REST request");
/*
* Async IO helpers
*/
-void rest_io_action(REQUEST *request, void *instance, void *thread, void *ctx, fr_state_action_t action);
+void rest_io_action(REQUEST *request, void *instance, void *thread, void *ctx, fr_state_signal_t action);
int rest_io_request_enqueue(rlm_rest_thread_t *thread, REQUEST *request, void *handle);
int rest_io_init(rlm_rest_thread_t *thread);
ret = rest_io_request_enqueue(t, request, handle);
if (ret < 0) goto error;
- return unlang_xlat_yield(request, rest_xlat_resume, NULL, rctx);
+ return xlat_unlang_yield(request, rest_xlat_resume, NULL, rctx);
}
static rlm_rcode_t mod_authorize_result(REQUEST *request, void *instance, void *thread, void *ctx)