From: Arran Cudbard-Bell Date: Sat, 6 Apr 2019 00:24:46 +0000 (-0400) Subject: Move module event handling and declarations out of the various places they were scatt... X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=57ff2dc5edd7f8b7201af7ca4445e74977650a61;p=thirdparty%2Ffreeradius-server.git Move module event handling and declarations out of the various places they were scattered, and into module.h, module_priv.h, and module.c in the unlang library --- diff --git a/src/lib/server/module.h b/src/lib/server/module.h index c73ca0ced30..122274e967e 100644 --- a/src/lib/server/module.h +++ b/src/lib/server/module.h @@ -136,67 +136,6 @@ typedef int (*module_thread_t)(CONF_SECTION const *mod_cs, void *instance, fr_ev */ 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 @@ -319,29 +258,6 @@ int virtual_server_namespace_register(char const *namespace, fr_virtual_server_ 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 diff --git a/src/lib/unlang/base.h b/src/lib/unlang/base.h index 31c4c4143a7..e3d5a5d5a24 100644 --- a/src/lib/unlang/base.h +++ b/src/lib/unlang/base.h @@ -18,14 +18,16 @@ /** * $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 #include #include #include +#include /** Returned by #unlang_op_t calls, determine the next action of the interpreter * diff --git a/src/lib/unlang/compile.c b/src/lib/unlang/compile.c index 47694ee33de..9623c465cf2 100644 --- a/src/lib/unlang/compile.c +++ b/src/lib/unlang/compile.c @@ -29,6 +29,7 @@ RCSID("$Id$") #include #include #include "unlang_priv.h" +#include "module_priv.h" /* Here's where we recognize all of our keywords: first the rcodes, then the * actions */ diff --git a/src/lib/unlang/condition.c b/src/lib/unlang/condition.c index 79132f05a13..ef112e9e5d4 100644 --- a/src/lib/unlang/condition.c +++ b/src/lib/unlang/condition.c @@ -25,7 +25,7 @@ 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) diff --git a/src/lib/unlang/foreach.c b/src/lib/unlang/foreach.c index 9408cc32892..9259d7b4f8b 100644 --- a/src/lib/unlang/foreach.c +++ b/src/lib/unlang/foreach.c @@ -25,7 +25,7 @@ RCSID("$Id$") #include "unlang_priv.h" -#include "return.h" +#include "return_priv.h" #define unlang_break unlang_return diff --git a/src/lib/unlang/group.c b/src/lib/unlang/group.c index 3ef49ae0c85..9d79ff5359b 100644 --- a/src/lib/unlang/group.c +++ b/src/lib/unlang/group.c @@ -25,7 +25,7 @@ RCSID("$Id$") #include "unlang_priv.h" -#include "group.h" +#include "group_priv.h" #define unlang_policy unlang_group diff --git a/src/lib/unlang/group.h b/src/lib/unlang/group_priv.h similarity index 100% rename from src/lib/unlang/group.h rename to src/lib/unlang/group_priv.h diff --git a/src/lib/unlang/interpret.c b/src/lib/unlang/interpret.c index e3def407fee..06640138a2a 100644 --- a/src/lib/unlang/interpret.c +++ b/src/lib/unlang/interpret.c @@ -30,7 +30,8 @@ RCSID("$Id$") #include #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 }, @@ -924,294 +925,6 @@ void *unlang_stack_alloc(TALLOC_CTX *ctx) 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 @@ -1472,64 +1185,6 @@ static unlang_action_t unlang_resume(REQUEST *request, rlm_rcode_t *presult, int 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 * */ diff --git a/src/lib/unlang/load_balance.c b/src/lib/unlang/load_balance.c index 6667b2d785b..6cf6548467f 100644 --- a/src/lib/unlang/load_balance.c +++ b/src/lib/unlang/load_balance.c @@ -23,6 +23,7 @@ * @copyright 2006-2019 The FreeRADIUS server project */ #include "unlang_priv.h" +#include "module_priv.h" #define unlang_redundant_load_balance unlang_load_balance diff --git a/src/lib/unlang/module.c b/src/lib/unlang/module.c index 8f4dbe7a77e..5063cb75dca 100644 --- a/src/lib/unlang/module.c +++ b/src/lib/unlang/module.c @@ -32,6 +32,395 @@ RCSID("$Id$") #include #include #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 @@ -227,49 +616,6 @@ static unlang_action_t unlang_module_resume(REQUEST *request, rlm_rcode_t *presu } } -/** 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, diff --git a/src/lib/unlang/module.h b/src/lib/unlang/module.h new file mode 100644 index 00000000000..7ffb53aefa9 --- /dev/null +++ b/src/lib/unlang/module.h @@ -0,0 +1,113 @@ +#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); diff --git a/src/lib/unlang/module_priv.h b/src/lib/unlang/module_priv.h new file mode 100644 index 00000000000..7c0c1b90827 --- /dev/null +++ b/src/lib/unlang/module_priv.h @@ -0,0 +1,57 @@ +#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 diff --git a/src/lib/unlang/parallel.c b/src/lib/unlang/parallel.c index 096331ac8fd..b4eb158c283 100644 --- a/src/lib/unlang/parallel.c +++ b/src/lib/unlang/parallel.c @@ -25,7 +25,8 @@ 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. * diff --git a/src/lib/unlang/parallel.h b/src/lib/unlang/parallel_priv.h similarity index 100% rename from src/lib/unlang/parallel.h rename to src/lib/unlang/parallel_priv.h diff --git a/src/lib/unlang/return.c b/src/lib/unlang/return.c index 870b7d905f1..b9743bab5d0 100644 --- a/src/lib/unlang/return.c +++ b/src/lib/unlang/return.c @@ -25,7 +25,7 @@ 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) { diff --git a/src/lib/unlang/return.h b/src/lib/unlang/return_priv.h similarity index 100% rename from src/lib/unlang/return.h rename to src/lib/unlang/return_priv.h diff --git a/src/lib/unlang/switch.c b/src/lib/unlang/switch.c index 20aee1448de..504eb17f10d 100644 --- a/src/lib/unlang/switch.c +++ b/src/lib/unlang/switch.c @@ -25,7 +25,7 @@ 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) diff --git a/src/lib/unlang/unlang_priv.h b/src/lib/unlang/unlang_priv.h index 6da0f806074..985d0e844fc 100644 --- a/src/lib/unlang/unlang_priv.h +++ b/src/lib/unlang/unlang_priv.h @@ -22,6 +22,8 @@ * @brief Private interpreter structures and functions * * @author Alan DeKok + * + * @copyright 2016-2019 The FreeRADIUS server project */ #include /* Need CONF_* definitions */ #include @@ -167,15 +169,6 @@ typedef struct { }; } 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 @@ -309,12 +302,6 @@ extern char const *const comp2str[]; * * @{ */ -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)); @@ -322,11 +309,6 @@ static inline unlang_group_t *unlang_generic_to_group(unlang_t *p) 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; diff --git a/src/lib/unlang/xlat.c b/src/lib/unlang/xlat.c index d72bce3a169..e3a3bcf0986 100644 --- a/src/lib/unlang/xlat.c +++ b/src/lib/unlang/xlat.c @@ -119,7 +119,7 @@ static int _unlang_xlat_event_free(unlang_xlat_event_t *ev) * * @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) diff --git a/src/modules/rlm_delay/rlm_delay.c b/src/modules/rlm_delay/rlm_delay.c index 1de19f23021..bcfe8bcfd16 100644 --- a/src/modules/rlm_delay/rlm_delay.c +++ b/src/modules/rlm_delay/rlm_delay.c @@ -141,7 +141,7 @@ static void mod_delay_cancel(REQUEST *request, UNUSED void *instance, UNUSED voi 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) @@ -173,7 +173,7 @@ static rlm_rcode_t CC_HINT(nonnull) mod_delay(void *instance, UNUSED void *threa 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; } @@ -211,7 +211,7 @@ static void xlat_delay_cancel(REQUEST *request, UNUSED void *instance, UNUSED vo 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,