*/
typedef int (*module_thread_detach_t)(fr_event_list_t *el, 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
void fr_request_async_bootstrap(REQUEST *request, fr_event_list_t *el); /* for unit_test_module */
-/*
- * unlang_module.c
- */
-int unlang_event_module_timeout_add(REQUEST *request, fr_unlang_module_timeout_t callback,
- void const *ctx, struct timeval *timeout);
-
-int unlang_module_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 *rctx, int fd);
-
-int unlang_event_timeout_delete(REQUEST *request, void const *ctx);
-
-int unlang_event_fd_delete(REQUEST *request, void const *rctx, int fd);
-
-rlm_rcode_t unlang_module_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);
-
-rlm_rcode_t unlang_module_yield(REQUEST *request, fr_unlang_module_resume_t callback,
- fr_unlang_module_signal_t signal_callback, void *ctx);
#ifdef __cplusplus
}
#endif
/**
* $Id$
*
- * @file lib/unlang/base.h
- * @brief Public interface to the interpreter
+ * @file unlang/base.h
+ * @brief Public interface to the unlang interpreter
*
+ * @copyright 2016-2019 The FreeRADIUS server project
*/
#include <freeradius-devel/server/base.h>
#include <freeradius-devel/server/components.h>
#include <freeradius-devel/server/signal.h>
#include <freeradius-devel/server/tmpl.h>
+#include <freeradius-devel/unlang/module.h>
/** Returned by #unlang_op_t calls, determine the next action of the interpreter
*
#include <freeradius-devel/server/parser.h>
#include <freeradius-devel/unlang/base.h>
#include "unlang_priv.h"
+#include "module_priv.h"
/* Here's where we recognize all of our keywords: first the rcodes, then the
* actions */
RCSID("$Id$")
#include "unlang_priv.h"
-#include "group.h"
+#include "group_priv.h"
static unlang_action_t unlang_if(REQUEST *request,
rlm_rcode_t *presult, int *priority)
RCSID("$Id$")
#include "unlang_priv.h"
-#include "return.h"
+#include "return_priv.h"
#define unlang_break unlang_return
RCSID("$Id$")
#include "unlang_priv.h"
-#include "group.h"
+#include "group_priv.h"
#define unlang_policy unlang_group
#include <freeradius-devel/server/xlat.h>
#include "unlang_priv.h"
-#include "parallel.h"
+#include "parallel_priv.h"
+#include "module_priv.h"
static FR_NAME_NUMBER unlang_action_table[] = {
{ "calculate-result", UNLANG_ACTION_CALCULATE_RESULT },
return stack;
}
-/** Wrap an #fr_event_timer_t providing data needed for unlang events
- *
- */
-typedef struct {
- REQUEST *request; //!< Request this event pertains to.
- int fd; //!< File descriptor to wait on.
- fr_unlang_module_timeout_t timeout; //!< Function to call on timeout.
- fr_unlang_module_fd_event_t fd_read; //!< Function to call when FD is readable.
- fr_unlang_module_fd_event_t fd_write; //!< Function to call when FD is writable.
- fr_unlang_module_fd_event_t fd_error; //!< Function to call when FD has errored.
- void const *inst; //!< Module instance to pass to callbacks.
- void *thread; //!< Thread specific module instance.
- void const *ctx; //!< ctx data to pass to callbacks.
- fr_event_timer_t const *ev; //!< Event in this worker's event heap.
-} unlang_event_t;
-
-/** Frees an unlang event, removing it from the request's event loop
- *
- * @param[in] ev The event to free.
- *
- * @return 0
- */
-static int _unlang_event_free(unlang_event_t *ev)
-{
- if (ev->ev) {
- (void) fr_event_timer_delete(ev->request->el, &(ev->ev));
- return 0;
- }
-
- if (ev->fd >= 0) {
- (void) fr_event_fd_delete(ev->request->el, ev->fd, FR_EVENT_FILTER_IO);
- }
-
- return 0;
-}
-
-/** Call the callback registered for a timeout event
- *
- * @param[in] el the event timer was inserted into.
- * @param[in] now The current time, as held by the event_list.
- * @param[in] ctx unlang_event_t structure holding callbacks.
- *
- */
-static void unlang_event_timeout_handler(UNUSED fr_event_list_t *el, struct timeval *now, void *ctx)
-{
- unlang_event_t *ev = talloc_get_type_abort(ctx, unlang_event_t);
- void *mutable_ctx;
- void *mutable_inst;
-
- memcpy(&mutable_ctx, &ev->ctx, sizeof(mutable_ctx));
- memcpy(&mutable_inst, &ev->inst, sizeof(mutable_inst));
-
- ev->timeout(ev->request, mutable_inst, ev->thread, mutable_ctx, now);
- talloc_free(ev);
-}
-
-/** Call the callback registered for a read I/O event
- *
- * @param[in] el containing the event (not passed to the callback).
- * @param[in] fd the I/O event occurred on.
- * @param[in] flags from kevent.
- * @param[in] ctx unlang_event_t structure holding callbacks.
- */
-static void unlang_event_fd_read_handler(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, void *ctx)
-{
- unlang_event_t *ev = talloc_get_type_abort(ctx, unlang_event_t);
- void *mutable_ctx;
- void *mutable_inst;
-
- rad_assert(ev->fd == fd);
-
- memcpy(&mutable_ctx, &ev->ctx, sizeof(mutable_ctx));
- memcpy(&mutable_inst, &ev->inst, sizeof(mutable_inst));
-
- ev->fd_read(ev->request, mutable_inst, ev->thread, mutable_ctx, fd);
-}
-
-/** Call the callback registered for a write I/O event
- *
- * @param[in] el containing the event (not passed to the callback).
- * @param[in] fd the I/O event occurred on.
- * @param[in] flags from kevent.
- * @param[in] ctx unlang_event_t structure holding callbacks.
- */
-static void unlang_event_fd_write_handler(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, void *ctx)
-{
- unlang_event_t *ev = talloc_get_type_abort(ctx, unlang_event_t);
- void *mutable_ctx;
- void *mutable_inst;
-
- rad_assert(ev->fd == fd);
-
- memcpy(&mutable_ctx, &ev->ctx, sizeof(mutable_ctx));
- memcpy(&mutable_inst, &ev->inst, sizeof(mutable_inst));
-
- ev->fd_write(ev->request, mutable_inst, ev->thread, mutable_ctx, fd);
-}
-
-/** Call the callback registered for an I/O error event
- *
- * @param[in] el containing the event (not passed to the callback).
- * @param[in] fd the I/O event occurred on.
- * @param[in] flags from kevent.
- * @param[in] fd_errno from kevent.
- * @param[in] ctx unlang_event_t structure holding callbacks.
- */
-static void unlang_event_fd_error_handler(UNUSED fr_event_list_t *el, int fd,
- UNUSED int flags, UNUSED int fd_errno, void *ctx)
-{
- unlang_event_t *ev = talloc_get_type_abort(ctx, unlang_event_t);
- void *mutable_ctx;
- void *mutable_inst;
-
- rad_assert(ev->fd == fd);
-
- memcpy(&mutable_ctx, &ev->ctx, sizeof(mutable_ctx));
- memcpy(&mutable_inst, &ev->inst, sizeof(mutable_inst));
-
- ev->fd_error(ev->request, mutable_inst, ev->thread, mutable_ctx, fd);
-}
-
-/** Set a timeout for the request.
- *
- * 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().
- *
- * param[in] request the current request.
- * param[in] callback to call.
- * param[in] ctx for the callback.
- * param[in] timeout when to call the timeout (i.e. now + timeout).
- * @return
- * - 0 on success.
- * - <0 on error.
- */
-int unlang_event_module_timeout_add(REQUEST *request, fr_unlang_module_timeout_t callback,
- void const *ctx, struct timeval *when)
-{
- unlang_stack_t *stack = request->stack;
- unlang_stack_frame_t *frame = &stack->frame[stack->depth];
- unlang_event_t *ev;
- unlang_module_t *sp;
- unlang_frame_state_module_t *ms = talloc_get_type_abort(frame->state,
- unlang_frame_state_module_t);
-
- rad_assert(stack->depth > 0);
- rad_assert((frame->instruction->type == UNLANG_TYPE_MODULE) ||
- (frame->instruction->type == UNLANG_TYPE_RESUME));
- sp = unlang_generic_to_module(frame->instruction);
-
- ev = talloc_zero(request, unlang_event_t);
- if (!ev) return -1;
-
- ev->request = request;
- ev->fd = -1;
- ev->timeout = callback;
- ev->inst = sp->module_instance->dl_inst->data;
- ev->thread = ms->thread;
- ev->ctx = ctx;
-
- if (fr_event_timer_insert(request, request->el, &ev->ev,
- when, unlang_event_timeout_handler, ev) < 0) {
- RPEDEBUG("Failed inserting event");
- talloc_free(ev);
- return -1;
- }
-
- (void) request_data_talloc_add(request, ctx, -1, unlang_event_t, ev, true, false, false);
-
- talloc_set_destructor(ev, _unlang_event_free);
-
- return 0;
-}
-
-/** Delete a previously set timeout callback
- *
- * @param[in] request The current request.
- * @param[in] ctx a local context for the callback.
- * @return
- * - -1 on error.
- * - 0 on success.
- */
-int unlang_event_timeout_delete(REQUEST *request, void const *ctx)
-{
- unlang_event_t *ev;
-
- ev = request_data_get(request, ctx, -1);
- if (!ev) return -1;
- talloc_free(ev);
-
- return 0;
-}
-
-/** Set a callback for the request.
- *
- * 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().
- *
- * @param[in] request The current request.
- * @param[in] read callback. Used for receiving and demuxing/decoding data.
- * @param[in] write callback. Used for writing and encoding data.
- * Where a 3rd party library is used, this should be the function
- * issuing queries, and writing data to the socket. This should
- * not be done in the module itself.
- * This allows write operations to be retried in some instances,
- * and means if the write buffer is full, the request is kept in
- * a suspended state.
- * @param[in] error callback. If the fd enters an error state. Should cleanup any
- * handles wrapping the file descriptor, and any outstanding requests.
- * @param[in] ctx for the callback.
- * @param[in] fd to watch.
- * @return
- * - 0 on success.
- * - <0 on error.
- */
-int unlang_module_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)
-{
- unlang_stack_t *stack = request->stack;
- unlang_stack_frame_t *frame = &stack->frame[stack->depth];
- unlang_event_t *ev;
- unlang_module_t *sp;
- unlang_frame_state_module_t *ms = talloc_get_type_abort(frame->state,
- unlang_frame_state_module_t);
-
- rad_assert(stack->depth > 0);
-
- rad_assert((frame->instruction->type == UNLANG_TYPE_MODULE) ||
- (frame->instruction->type == UNLANG_TYPE_RESUME));
- sp = unlang_generic_to_module(frame->instruction);
-
- ev = talloc_zero(request, unlang_event_t);
- if (!ev) return -1;
-
- ev->request = request;
- ev->fd = fd;
- ev->fd_read = read;
- ev->fd_write = write;
- ev->fd_error = error;
- ev->inst = sp->module_instance->dl_inst->data;
- ev->thread = ms->thread;
- ev->ctx = ctx;
-
- /*
- * Register for events on the file descriptor
- */
- if (fr_event_fd_insert(request, request->el, fd,
- ev->fd_read ? unlang_event_fd_read_handler : NULL,
- ev->fd_write ? unlang_event_fd_write_handler : NULL,
- ev->fd_error ? unlang_event_fd_error_handler: NULL,
- ev) < 0) {
- talloc_free(ev);
- return -1;
- }
-
- (void) request_data_talloc_add(request, ctx, fd, unlang_event_t, ev, true, false, false);
- talloc_set_destructor(ev, _unlang_event_free);
-
- return 0;
-}
-
-/** Delete a previously set file descriptor callback
- *
- * param[in] request the request
- * param[in] fd the file descriptor
- * @return
- * - 0 on success.
- * - <0 on error.
- */
-int unlang_event_fd_delete(REQUEST *request, void const *ctx, int fd)
-{
- unlang_event_t *ev;
-
- ev = request_data_get(request, ctx, fd);
- if (!ev) return -1;
-
- talloc_free(ev);
- return 0;
-}
-
-
-
/** Send a signal (usually stop) to a request
*
* This is typically called via an "async" action, i.e. an action
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
- * 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] resume Called on unlang_resumable().
- * @param[in] signal Called on unlang_action().
- * @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 resume, fr_unlang_module_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);
-
- REQUEST_VERIFY(request); /* Check the yielded request is sane */
-
- switch (frame->instruction->type) {
- case UNLANG_TYPE_MODULE:
- mr = unlang_resume_alloc(request, (void *)resume, (void *)signal, rctx);
- if (!fr_cond_assert(mr)) {
- return RLM_MODULE_FAIL;
- }
- return RLM_MODULE_YIELD;
-
- case UNLANG_TYPE_RESUME:
- mr = talloc_get_type_abort(frame->instruction, unlang_resume_t);
- rad_assert(mr->parent->type == UNLANG_TYPE_MODULE);
-
- /*
- * Re-use the current RESUME frame, but over-ride
- * the callbacks and context.
- */
- mr->resume = (void *)resume;
- mr->signal = (void *)signal;
- mr->rctx = rctx;
-
- return RLM_MODULE_YIELD;
-
- default:
- rad_assert(0);
- return RLM_MODULE_FAIL;
- }
-}
-
/** Get information about the interpreter state
*
*/
* @copyright 2006-2019 The FreeRADIUS server project
*/
#include "unlang_priv.h"
+#include "module_priv.h"
#define unlang_redundant_load_balance unlang_load_balance
#include <freeradius-devel/unlang/base.h>
#include <freeradius-devel/server/xlat.h>
#include "unlang_priv.h"
+#include "module_priv.h"
+
+/** Wrap an #fr_event_timer_t providing data needed for unlang events
+ *
+ */
+typedef struct {
+ REQUEST *request; //!< Request this event pertains to.
+ int fd; //!< File descriptor to wait on.
+ fr_unlang_module_timeout_t timeout; //!< Function to call on timeout.
+ fr_unlang_module_fd_event_t fd_read; //!< Function to call when FD is readable.
+ fr_unlang_module_fd_event_t fd_write; //!< Function to call when FD is writable.
+ fr_unlang_module_fd_event_t fd_error; //!< Function to call when FD has errored.
+ void const *inst; //!< Module instance to pass to callbacks.
+ void *thread; //!< Thread specific module instance.
+ void const *ctx; //!< ctx data to pass to callbacks.
+ fr_event_timer_t const *ev; //!< Event in this worker's event heap.
+} unlang_module_event_t;
+
+/** Call the callback registered for a read I/O event
+ *
+ * @param[in] el containing the event (not passed to the callback).
+ * @param[in] fd the I/O event occurred on.
+ * @param[in] flags from kevent.
+ * @param[in] ctx unlang_module_event_t structure holding callbacks.
+ */
+static void unlang_event_fd_read_handler(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, void *ctx)
+{
+ unlang_module_event_t *ev = talloc_get_type_abort(ctx, unlang_module_event_t);
+ void *mutable_ctx;
+ void *mutable_inst;
+
+ rad_assert(ev->fd == fd);
+
+ memcpy(&mutable_ctx, &ev->ctx, sizeof(mutable_ctx));
+ memcpy(&mutable_inst, &ev->inst, sizeof(mutable_inst));
+
+ ev->fd_read(ev->request, mutable_inst, ev->thread, mutable_ctx, fd);
+}
+
+/** Frees an unlang event, removing it from the request's event loop
+ *
+ * @param[in] ev The event to free.
+ *
+ * @return 0
+ */
+static int _unlang_event_free(unlang_module_event_t *ev)
+{
+ if (ev->ev) {
+ (void) fr_event_timer_delete(ev->request->el, &(ev->ev));
+ return 0;
+ }
+
+ if (ev->fd >= 0) {
+ (void) fr_event_fd_delete(ev->request->el, ev->fd, FR_EVENT_FILTER_IO);
+ }
+
+ return 0;
+}
+
+/** Call the callback registered for a timeout event
+ *
+ * @param[in] el the event timer was inserted into.
+ * @param[in] now The current time, as held by the event_list.
+ * @param[in] ctx unlang_module_event_t structure holding callbacks.
+ *
+ */
+static void unlang_module_event_timeout_handler(UNUSED fr_event_list_t *el, struct timeval *now, void *ctx)
+{
+ unlang_module_event_t *ev = talloc_get_type_abort(ctx, unlang_module_event_t);
+ void *mutable_ctx;
+ void *mutable_inst;
+
+ memcpy(&mutable_ctx, &ev->ctx, sizeof(mutable_ctx));
+ memcpy(&mutable_inst, &ev->inst, sizeof(mutable_inst));
+
+ ev->timeout(ev->request, mutable_inst, ev->thread, mutable_ctx, now);
+ talloc_free(ev);
+}
+
+/** Set a timeout for the request.
+ *
+ * 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().
+ *
+ * param[in] request the current request.
+ * param[in] callback to call.
+ * param[in] ctx for the callback.
+ * param[in] timeout when to call the timeout (i.e. now + timeout).
+ * @return
+ * - 0 on success.
+ * - <0 on error.
+ */
+int unlang_module_timeout_add(REQUEST *request, fr_unlang_module_timeout_t callback,
+ void const *ctx, struct timeval *when)
+{
+ unlang_stack_t *stack = request->stack;
+ unlang_stack_frame_t *frame = &stack->frame[stack->depth];
+ unlang_module_event_t *ev;
+ unlang_module_t *sp;
+ unlang_frame_state_module_t *ms = talloc_get_type_abort(frame->state,
+ unlang_frame_state_module_t);
+
+ rad_assert(stack->depth > 0);
+ rad_assert((frame->instruction->type == UNLANG_TYPE_MODULE) ||
+ (frame->instruction->type == UNLANG_TYPE_RESUME));
+ sp = unlang_generic_to_module(frame->instruction);
+
+ ev = talloc_zero(request, unlang_module_event_t);
+ if (!ev) return -1;
+
+ ev->request = request;
+ ev->fd = -1;
+ ev->timeout = callback;
+ ev->inst = sp->module_instance->dl_inst->data;
+ ev->thread = ms->thread;
+ ev->ctx = ctx;
+
+ if (fr_event_timer_insert(request, request->el, &ev->ev,
+ when, unlang_module_event_timeout_handler, ev) < 0) {
+ RPEDEBUG("Failed inserting event");
+ talloc_free(ev);
+ return -1;
+ }
+
+ (void) request_data_talloc_add(request, ctx, UNLANG_TYPE_MODULE, unlang_module_event_t, ev, true, false, false);
+
+ talloc_set_destructor(ev, _unlang_event_free);
+
+ return 0;
+}
+
+/** Delete a previously set timeout callback
+ *
+ * @param[in] request The current request.
+ * @param[in] ctx a local context for the callback.
+ * @return
+ * - -1 on error.
+ * - 0 on success.
+ */
+int unlang_module_timeout_delete(REQUEST *request, void const *ctx)
+{
+ unlang_module_event_t *ev;
+
+ ev = request_data_get(request, ctx, UNLANG_TYPE_MODULE);
+ if (!ev) return -1;
+ talloc_free(ev);
+
+ return 0;
+}
+
+/** Call the callback registered for a write I/O event
+ *
+ * @param[in] el containing the event (not passed to the callback).
+ * @param[in] fd the I/O event occurred on.
+ * @param[in] flags from kevent.
+ * @param[in] ctx unlang_module_event_t structure holding callbacks.
+ */
+static void unlang_event_fd_write_handler(UNUSED fr_event_list_t *el, int fd, UNUSED int flags, void *ctx)
+{
+ unlang_module_event_t *ev = talloc_get_type_abort(ctx, unlang_module_event_t);
+ void *mutable_ctx;
+ void *mutable_inst;
+
+ rad_assert(ev->fd == fd);
+
+ memcpy(&mutable_ctx, &ev->ctx, sizeof(mutable_ctx));
+ memcpy(&mutable_inst, &ev->inst, sizeof(mutable_inst));
+
+ ev->fd_write(ev->request, mutable_inst, ev->thread, mutable_ctx, fd);
+}
+
+/** Call the callback registered for an I/O error event
+ *
+ * @param[in] el containing the event (not passed to the callback).
+ * @param[in] fd the I/O event occurred on.
+ * @param[in] flags from kevent.
+ * @param[in] fd_errno from kevent.
+ * @param[in] ctx unlang_module_event_t structure holding callbacks.
+ */
+static void unlang_event_fd_error_handler(UNUSED fr_event_list_t *el, int fd,
+ UNUSED int flags, UNUSED int fd_errno, void *ctx)
+{
+ unlang_module_event_t *ev = talloc_get_type_abort(ctx, unlang_module_event_t);
+ void *mutable_ctx;
+ void *mutable_inst;
+
+ rad_assert(ev->fd == fd);
+
+ memcpy(&mutable_ctx, &ev->ctx, sizeof(mutable_ctx));
+ memcpy(&mutable_inst, &ev->inst, sizeof(mutable_inst));
+
+ ev->fd_error(ev->request, mutable_inst, ev->thread, mutable_ctx, fd);
+}
+
+
+/** Set a callback for the request.
+ *
+ * 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().
+ *
+ * @param[in] request The current request.
+ * @param[in] read callback. Used for receiving and demuxing/decoding data.
+ * @param[in] write callback. Used for writing and encoding data.
+ * Where a 3rd party library is used, this should be the function
+ * issuing queries, and writing data to the socket. This should
+ * not be done in the module itself.
+ * This allows write operations to be retried in some instances,
+ * and means if the write buffer is full, the request is kept in
+ * a suspended state.
+ * @param[in] error callback. If the fd enters an error state. Should cleanup any
+ * handles wrapping the file descriptor, and any outstanding requests.
+ * @param[in] ctx for the callback.
+ * @param[in] fd to watch.
+ * @return
+ * - 0 on success.
+ * - <0 on error.
+ */
+int unlang_module_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)
+{
+ unlang_stack_t *stack = request->stack;
+ unlang_stack_frame_t *frame = &stack->frame[stack->depth];
+ unlang_module_event_t *ev;
+ unlang_module_t *sp;
+ unlang_frame_state_module_t *ms = talloc_get_type_abort(frame->state,
+ unlang_frame_state_module_t);
+
+ rad_assert(stack->depth > 0);
+
+ rad_assert((frame->instruction->type == UNLANG_TYPE_MODULE) ||
+ (frame->instruction->type == UNLANG_TYPE_RESUME));
+ sp = unlang_generic_to_module(frame->instruction);
+
+ ev = talloc_zero(request, unlang_module_event_t);
+ if (!ev) return -1;
+
+ ev->request = request;
+ ev->fd = fd;
+ ev->fd_read = read;
+ ev->fd_write = write;
+ ev->fd_error = error;
+ ev->inst = sp->module_instance->dl_inst->data;
+ ev->thread = ms->thread;
+ ev->ctx = ctx;
+
+ /*
+ * Register for events on the file descriptor
+ */
+ if (fr_event_fd_insert(request, request->el, fd,
+ ev->fd_read ? unlang_event_fd_read_handler : NULL,
+ ev->fd_write ? unlang_event_fd_write_handler : NULL,
+ ev->fd_error ? unlang_event_fd_error_handler: NULL,
+ ev) < 0) {
+ talloc_free(ev);
+ return -1;
+ }
+
+ (void) request_data_talloc_add(request, ctx, fd, unlang_module_event_t, ev, true, false, false);
+ talloc_set_destructor(ev, _unlang_event_free);
+
+ return 0;
+}
+
+/** Delete a previously set file descriptor callback
+ *
+ * param[in] request the request
+ * param[in] fd the file descriptor
+ * @return
+ * - 0 on success.
+ * - <0 on error.
+ */
+int unlang_module_fd_delete(REQUEST *request, void const *ctx, int fd)
+{
+ unlang_module_event_t *ev;
+
+ ev = request_data_get(request, ctx, fd);
+ if (!ev) return -1;
+
+ talloc_free(ev);
+ return 0;
+}
+
+/** 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 talloc value boxes and values in.
+ * @param[out] out Where to write the result of the expansion.
+ * @param[in] request The current request.
+ * @param[in] exp XLAT expansion to evaluate.
+ * @param[in] resume function to call when the XLAT expansion is complete.
+ * @param[in] signal function to call if a signal is received.
+ * @param[in] rctx to pass to the resume() and signal() callbacks.
+ * @return
+ * - RLM_MODULE_YIELD.
+ */
+rlm_rcode_t unlang_module_yield_to_xlat(TALLOC_CTX *ctx, fr_value_box_t **out,
+ REQUEST *request, xlat_exp_t const *exp,
+ fr_unlang_module_resume_t resume,
+ fr_unlang_module_signal_t signal, void *rctx)
+{
+ /*
+ * Push the resumption point
+ */
+ (void) unlang_module_yield(request, resume, signal, rctx);
+
+ /*
+ * Push the xlat function
+ */
+ unlang_xlat_push(ctx, out, request, exp, true);
+
+ return RLM_MODULE_YIELD; /* This may allow us to do optimisations in future */
+}
+
+/** 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] resume Called on unlang_resumable().
+ * @param[in] signal Called on unlang_action().
+ * @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 resume, fr_unlang_module_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);
+
+ REQUEST_VERIFY(request); /* Check the yielded request is sane */
+
+ switch (frame->instruction->type) {
+ case UNLANG_TYPE_MODULE:
+ mr = unlang_resume_alloc(request, (void *)resume, (void *)signal, rctx);
+ if (!fr_cond_assert(mr)) {
+ return RLM_MODULE_FAIL;
+ }
+ return RLM_MODULE_YIELD;
+
+ case UNLANG_TYPE_RESUME:
+ mr = talloc_get_type_abort(frame->instruction, unlang_resume_t);
+ rad_assert(mr->parent->type == UNLANG_TYPE_MODULE);
+
+ /*
+ * Re-use the current RESUME frame, but over-ride
+ * the callbacks and context.
+ */
+ mr->resume = (void *)resume;
+ mr->signal = (void *)signal;
+ mr->rctx = rctx;
+
+ return RLM_MODULE_YIELD;
+
+ default:
+ rad_assert(0);
+ return RLM_MODULE_FAIL;
+ }
+}
/*
* Lock the mutex for the module
}
}
-/** 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 talloc value boxes and values in.
- * @param[out] out Where to write the result of the expansion.
- * @param[in] request The current request.
- * @param[in] exp XLAT expansion to evaluate.
- * @param[in] resume function to call when the XLAT expansion is complete.
- * @param[in] signal function to call if a signal is received.
- * @param[in] rctx to pass to the resume() and signal() callbacks.
- * @return
- * - RLM_MODULE_YIELD.
- */
-rlm_rcode_t unlang_module_push_xlat(TALLOC_CTX *ctx, fr_value_box_t **out,
- REQUEST *request, xlat_exp_t const *exp,
- fr_unlang_module_resume_t resume,
- fr_unlang_module_signal_t signal, void *rctx)
-{
- /*
- * Push the resumption point
- */
- (void) unlang_module_yield(request, resume, signal, rctx);
-
- /*
- * Push the xlat function
- */
- unlang_xlat_push(ctx, out, request, exp, true);
-
- return RLM_MODULE_YIELD; /* This may allow us to do optimisations in future */
-}
-
void unlang_module_init(void)
{
unlang_op_register(UNLANG_TYPE_MODULE,
--- /dev/null
+#pragma once
+/*
+ * 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.
+ */
+
+/**
+ * $Id$
+ *
+ * @file unlang/module.h
+ *
+ * @brief Functions to allow modules to push resumption frames onto the stack
+ * and inform the interpreter about the conditions they need to be
+ * resumed under (usually an I/O event or timer event).
+ *
+ * @copyright 2016-2019 The FreeRADIUS server project
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** 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);
+
+int unlang_module_timeout_add(REQUEST *request, fr_unlang_module_timeout_t callback,
+ void const *ctx, struct timeval *timeout);
+
+int unlang_module_timeout_delete(REQUEST *request, void const *ctx);
+
+int unlang_module_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 *rctx, int fd);
+
+
+int unlang_module_fd_delete(REQUEST *request, void const *rctx, int fd);
+
+rlm_rcode_t unlang_module_yield_to_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_module_yield(REQUEST *request, fr_unlang_module_resume_t callback,
+ fr_unlang_module_signal_t signal_callback, void *ctx);
--- /dev/null
+#pragma once
+/*
+ * 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.
+ */
+
+/**
+ * $Id$
+ *
+ * @file unlang/parallel.h
+ * @brief Private interpreter structures and functions
+ *
+ * Should be moved into parallel.c when the parallel stuff is fully extracted
+ * from interpret.c
+ *
+ * @copyright 2006-2019 The FreeRADIUS server project
+ */
+#include "unlang_priv.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** 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_t;
+
+static inline unlang_module_t *unlang_generic_to_module(unlang_t *p)
+{
+ rad_assert(p->type == UNLANG_TYPE_MODULE);
+ return talloc_get_type_abort(p, unlang_module_t);
+}
+
+static inline unlang_t *unlang_module_to_generic(unlang_module_t *p)
+{
+ return (unlang_t *)p;
+}
+
+#ifdef __cplusplus
+}
+#endif
RCSID("$Id$")
#include "unlang_priv.h"
-#include "parallel.h"
+#include "parallel_priv.h"
+#include "module_priv.h"
/** Run one or more sub-sections from the parallel section.
*
RCSID("$Id$")
#include "unlang_priv.h"
-#include "return.h"
+#include "return_priv.h"
unlang_action_t unlang_return(REQUEST *request, rlm_rcode_t *presult, int *priority)
{
RCSID("$Id$")
#include "unlang_priv.h"
-#include "group.h"
+#include "group_priv.h"
static unlang_action_t unlang_switch(REQUEST *request,
UNUSED rlm_rcode_t *presult, UNUSED int *priority)
* @brief Private interpreter structures and functions
*
* @author Alan DeKok <aland@freeradius.org>
+ *
+ * @copyright 2016-2019 The FreeRADIUS server project
*/
#include <freeradius-devel/server/cf_util.h> /* Need CONF_* definitions */
#include <freeradius-devel/server/cond_eval.h>
};
} 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_t;
-
/** Pushed onto the interpreter stack by a yielding module, xlat, or keyword to indicate a resumption point
*
* Unlike normal coroutines in other languages, we represent resumption points as states in a state
*
* @{
*/
-static inline unlang_module_t *unlang_generic_to_module(unlang_t *p)
-{
- rad_assert(p->type == UNLANG_TYPE_MODULE);
- return talloc_get_type_abort(p, unlang_module_t);
-}
-
static inline unlang_group_t *unlang_generic_to_group(unlang_t *p)
{
rad_assert((p->type > UNLANG_TYPE_MODULE) && (p->type <= UNLANG_TYPE_POLICY));
return (unlang_group_t *)p;
}
-static inline unlang_t *unlang_module_to_generic(unlang_module_t *p)
-{
- return (unlang_t *)p;
-}
-
static inline unlang_t *unlang_group_to_generic(unlang_group_t *p)
{
return (unlang_t *)p;
*
* @param[in] el the event timer was inserted into.
* @param[in] now The current time, as held by the event_list.
- * @param[in] ctx unlang_event_t structure holding callbacks.
+ * @param[in] ctx unlang_module_event_t structure holding callbacks.
*
*/
static void unlang_xlat_event_timeout_handler(UNUSED fr_event_list_t *el, struct timeval *now, void *ctx)
RDEBUG2("Cancelling delay");
- if (!fr_cond_assert(unlang_event_timeout_delete(request, ctx) == 0)) return;
+ if (!fr_cond_assert(unlang_module_timeout_delete(request, ctx) == 0)) return;
}
static rlm_rcode_t CC_HINT(nonnull) mod_delay(void *instance, UNUSED void *thread, REQUEST *request)
RDEBUG3("Current time %pV, resume time %pV", fr_box_timeval(*yielded_at), fr_box_timeval(resume_at));
- if (unlang_event_module_timeout_add(request, _delay_done, yielded_at, &resume_at) < 0) {
+ if (unlang_module_timeout_add(request, _delay_done, yielded_at, &resume_at) < 0) {
RPEDEBUG("Adding event failed");
return RLM_MODULE_FAIL;
}
RDEBUG2("Cancelling delay");
- if (!fr_cond_assert(unlang_event_timeout_delete(request, rctx) == 0)) return;
+ if (!fr_cond_assert(unlang_module_timeout_delete(request, rctx) == 0)) return;
}
static xlat_action_t xlat_delay(TALLOC_CTX *ctx, UNUSED fr_cursor_t *out,