From: Arran Cudbard-Bell Date: Wed, 31 Mar 2021 15:06:23 +0000 (+0100) Subject: Execute process modules using the unlang stack X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=97faa2df735f26047d97872db997c0092be1fdc1;p=thirdparty%2Ffreeradius-server.git Execute process modules using the unlang stack --- diff --git a/src/bin/unit_test_module.c b/src/bin/unit_test_module.c index 2edadc0a9e7..4cff9e1559f 100644 --- a/src/bin/unit_test_module.c +++ b/src/bin/unit_test_module.c @@ -36,6 +36,7 @@ RCSID("$Id$") #include #include +#include #include #include @@ -457,87 +458,10 @@ static rlm_rcode_t mod_map_proc(UNUSED void *mod_inst, UNUSED void *proc_inst, U return RLM_MODULE_FAIL; } -static void request_run(fr_event_list_t *el, request_t *request) +static void request_run(request_t *request) { - rlm_rcode_t rcode; - module_method_t process; - void *inst; - fr_dict_enum_t *dv; - fr_heap_t *backlog; - /* - * Record the number of FDs and timers - * inserted by modules before we start - * running requests. - */ - uint64_t base_timers = fr_event_list_num_timers(el); - uint64_t base_fds = fr_event_list_num_fds(el); - - dv = fr_dict_enum_by_value(attr_packet_type, fr_box_uint32(request->packet->code)); - if (!dv) return; - - if (virtual_server_get_process_by_name(request->server_cs, dv->name, &process, &inst) < 0) { - REDEBUG("Cannot run virtual server '%s' - %s", cf_section_name2(request->server_cs), fr_strerror()); - return; - } - - MEM(backlog = fr_heap_talloc_alloc(request, fr_pointer_cmp, request_t, runnable_id)); - request->backlog = backlog; - request->el = el; - - /* - * Don't check unlang_action_t, this may have - * added detached children, in which case - * we need to run them... - */ - (void)process(&rcode, &(module_ctx_t){ .instance = inst }, request); - - while ((fr_event_list_num_timers(el) > base_timers) || - (fr_event_list_num_fds(el) > base_fds) || - (fr_heap_num_elements(backlog) > 0)) { - request_t *runnable; - bool wait_for_event; - int num_events; - - /* - * Check if we need to wait for I/O - */ - wait_for_event = (fr_heap_num_elements(backlog) == 0); - - num_events = fr_event_corral(el, fr_time(), wait_for_event); - if (num_events < 0) { - PERROR("Failed retrieving events"); - break; - } - - /* - * Service outstanding events. - */ - if (num_events > 0) fr_event_service(el); - - /* - * If there's requests in the backlog - * run all of them. - */ - while ((runnable = fr_heap_pop(backlog))) { - if (runnable->async->process(&rcode, - &(module_ctx_t){ .instance = inst }, - runnable) != UNLANG_ACTION_YIELD) { - /* - * It's a detached child, and - * it's done, free it! - */ - if ((runnable != request) && !runnable->parent) talloc_free(runnable); - } - } - }; - - DEBUG2("No more requests remaining"); - - /* - * We do NOT run detached child requests. We just ignore - * them. - */ - talloc_free(backlog); + virtual_server_entry_point_set(request); + (void)unlang_interpret_synchronous(request); } static request_t *request_clone(request_t *old) @@ -962,7 +886,7 @@ int main(int argc, char *argv[]) } if (count == 1) { - request_run(el, request); + request_run(request); } else { int i; request_t *old = request_clone(request); @@ -970,7 +894,7 @@ int main(int argc, char *argv[]) for (i = 0; i < count; i++) { request = request_clone(old); - request_run(el, request); + request_run(request); talloc_free(request); } } diff --git a/src/lib/io/worker.c b/src/lib/io/worker.c index 39e134b7285..ac8949910c6 100644 --- a/src/lib/io/worker.c +++ b/src/lib/io/worker.c @@ -83,6 +83,8 @@ struct fr_worker_s { char const *name; //!< name of this worker fr_worker_config_t config; //!< external configuration + unlang_interpret_t *intp; //!< Worker's local interpreter. + pthread_t thread_id; //!< my thread ID fr_log_t const *log; //!< log destination @@ -508,9 +510,6 @@ finished: request->async->packet_ctx = NULL; request->async->listen = NULL; #endif - - DEBUG3("Freeing request %s", request->name); - talloc_free(request); } @@ -596,6 +595,9 @@ static void worker_max_request_time(UNUSED fr_event_list_t *el, UNUSED fr_time_t * unique. */ worker_send_reply(worker, request, 1, now); + + DEBUG3("Freeing request %s", request->name); + talloc_free(request); } /* @@ -717,6 +719,11 @@ static void worker_request_bootstrap(fr_worker_t *worker, fr_channel_data_t *cd, worker_request_init(worker, request, now); + /* + * Associate our interpreter with the request + */ + unlang_interpret_set(request, worker->intp); + request->packet->timestamp = cd->request.recv_time; /* Legacy - Remove once everything looks at request->async */ /* @@ -733,7 +740,6 @@ static void worker_request_bootstrap(fr_worker_t *worker, fr_channel_data_t *cd, request->async->recv_time = cd->request.recv_time; - request->async->listen = cd->listen; request->async->packet_ctx = cd->packet_ctx; listen = request->async->listen; @@ -854,100 +860,6 @@ nak: worker_request_time_tracking_start(worker, request, now); } - -/** Run a request - * - * Until it either yields, or is done. - * - * This function is also responsible for sending replies, and - * cleaning up the request. - * - * @param[in] worker the worker - * @param[in] start the current time - */ -static inline CC_HINT(always_inline) void worker_run_request(fr_worker_t *worker, fr_time_t start) -{ - rlm_rcode_t final; - request_t *request; - fr_time_t now; - - WORKER_VERIFY; - - now = start; - - /* - * Busy-loop running requests for 0.1ms. another - * request. This change means that the worker checks the - * event loop fewer times per second, instead of after - * every request. - */ - while (((now - start) < (NSEC / 100000)) && - ((request = fr_heap_pop(worker->runnable)) != NULL)) { - unlang_action_t action; - - REQUEST_VERIFY(request); - fr_assert(request->runnable_id < 0); - fr_time_tracking_resume(&request->async->tracking, now); - - fr_assert(request->parent == NULL); - fr_assert(request->async->process != NULL); - - RDEBUG("Running request"); - - /* - * For real requests, if the channel is gone, - * just stop the request and free it. - */ - if (request->async->channel && !fr_channel_active(request->async->channel)) { - worker_stop_request(worker, request, now); - talloc_free(request); - return; - } - - /* - * Everything else, run the request. - */ - action = request->async->process(&final, &(module_ctx_t){ .instance = request->async->process_inst }, request); - - if (action == UNLANG_ACTION_YIELD) { - now = fr_time(); - fr_time_tracking_yield(&request->async->tracking, now); - - } else { - /* - * If we're running a real request, then the final - * indentation MUST be zero. Otherwise we skipped - * something! - * - * Also check that the request is NOT marked as - * "yielded", but is in fact done. - * - * @todo - check that the stack is at frame 0, otherwise - * more things have gone wrong. - */ - fr_assert_msg(request->parent || (request->log.unlang_indent == 0), - "Request %s bad log indentation - expected 0 got %u", request->name, request->log.unlang_indent); - fr_assert_msg(!unlang_interpret_is_resumable(request), - "Request %s is marked as yielded at end of processing", request->name); - - RDEBUG("Done request"); - - /* - * Only real packets are in the dedup tree. And even - * then, only some of the time. - */ - if (request_is_external(request) && request->async->listen->track_duplicates) { - (void) rbtree_delete_by_data(worker->dedup, request); - } - - now = fr_time(); - worker_send_reply(worker, request, 0, now); - now = fr_time(); /* send_reply might take a while */ - } - } -} - - /** * Track a request_t in the "runnable" heap. */ @@ -1041,6 +953,167 @@ void fr_worker_destroy(fr_worker_t *worker) talloc_free(worker); } +/** Internal request (i.e. one generated by the interpreter) is now complete + * + */ +static void _worker_request_internal_init(request_t *request, void *uctx) +{ + fr_time_t *now = fr_time(); + fr_worker_t *worker = uctx; + + worker_request_init(worker, request, now); + + /* + * Requests generated by the interpreter + * are always marked up as internal. + */ + fr_assert(request_is_internal(request)); + worker_request_time_tracking_start(worker, request, now); +} + +/** Internal request (i.e. one generated by the interpreter) is now complete + * + */ +static void _worker_request_internal_done(request_t *request, UNUSED rlm_rcode_t rcode, void *uctx) +{ + fr_time_t *now = fr_time(); + fr_worker_t *worker = uctx; + + fr_time_tracking_end(&worker->predicted, &request->async->tracking, now); + + DEBUG3("Freeing request %s", request->name); + talloc_free(request); +} + +/** External request is now complete + * + */ +static void _worker_request_external_done(request_t *request, UNUSED rlm_rcode_t rcode, void *uctx) +{ + fr_worker_t *worker = uctx; + + /* + * Only real packets are in the dedup tree. And even + * then, only some of the time. + */ + if (request->async->listen->track_duplicates) (void) rbtree_delete_by_data(worker->dedup, request); + + /* + * If we're running a real request, then the final + * indentation MUST be zero. Otherwise we skipped + * something! + * + * Also check that the request is NOT marked as + * "yielded", but is in fact done. + * + * @todo - check that the stack is at frame 0, otherwise + * more things have gone wrong. + */ + fr_assert_msg(request->parent || (request->log.unlang_indent == 0), + "Request %s bad log indentation - expected 0 got %u", request->name, request->log.unlang_indent); + fr_assert_msg(!unlang_interpret_is_resumable(request), + "Request %s is marked as yielded at end of processing", request->name); + + RDEBUG("Done request"); + + worker_send_reply(worker, request, 0, fr_time()); + + DEBUG3("Freeing request %s", request->name); + talloc_free(request); +} + +/** Request is now runnable + * + */ +static void _worker_request_runnable(request_t *request, void *uctx) +{ + fr_worker_t *worker = uctx; + + fr_heap_insert(worker->runnable, request); +} + +/** Interpreter yielded request + * + */ +static void _worker_request_yield(request_t *request, UNUSED void *uctx) +{ + fr_time_tracking_yield(&request->async->tracking, fr_time()); +} + +/** Interpreter is starting to work on request again + * + */ +static void _worker_request_resume(request_t *request, UNUSED void *uctx) +{ + fr_time_tracking_resume(&request->async->tracking, fr_time()); +} + +/** Run a request + * + * Until it either yields, or is done. + * + * This function is also responsible for sending replies, and + * cleaning up the request. + * + * @param[in] worker the worker + * @param[in] start the current time + */ +static inline CC_HINT(always_inline) void worker_run_request(fr_worker_t *worker, fr_time_t start) +{ + rlm_rcode_t final; + request_t *request; + fr_time_t now; + + WORKER_VERIFY; + + now = start; + + /* + * Busy-loop running requests for 0.1ms. another + * request. This change means that the worker checks the + * event loop fewer times per second, instead of after + * every request. + */ + while (((now - start) < (NSEC / 100000)) && + ((request = fr_heap_pop(worker->runnable)) != NULL)) { + + REQUEST_VERIFY(request); + fr_assert(request->runnable_id < 0); + + fr_assert(request->parent == NULL); + fr_assert(request->async->process != NULL); + + RDEBUG("Running request"); + + /* + * For real requests, if the channel is gone, + * just stop the request and free it. + */ + if (request->async->channel && !fr_channel_active(request->async->channel)) { + worker_stop_request(worker, request, now); + talloc_free(request); + return; + } + + /* + * Old syle... should be removed + */ + if (request->async->process) { + request->async->process(&final, + &(module_ctx_t){ + .instance = request->async->process_inst + }, request); + + /* + * New style... just call the interpreter directly + */ + } else { + (void)unlang_interpret(request); + } + + now = fr_time(); + } +} /** Create a worker * @@ -1144,6 +1217,21 @@ nomem: goto fail; } + worker->intp = unlang_interpret_init(worker, el, + &(unlang_request_func_t){ + .init_internal = _worker_request_internal_init, + .done_internal = _worker_request_internal_done, + .done_external = _worker_request_external_done, + .mark_runnable = _worker_request_runnable, + .yield = _worker_request_yield, + .resume = _worker_request_resume + }, + worker); + if (!worker->intp){ + fr_strerror_const("Failed initialising interpreter"); + goto fail; + } + thread_local_worker = worker; return worker; diff --git a/src/lib/server/module.c b/src/lib/server/module.c index f548211bfa4..c352c0835ab 100644 --- a/src/lib/server/module.c +++ b/src/lib/server/module.c @@ -1158,7 +1158,13 @@ static int _module_thread_inst_array_free(module_thread_instance_t **array) ti = talloc_get_type_abort(array[i], module_thread_instance_t); DEBUG4("Worker cleaning up %s thread instance data (%p/%p)", ti->module->name, ti, ti->data); - if (ti->module->thread_detach) (void) ti->module->thread_detach(ti->el, ti->data); + + /* + * Check for ti->module is a hack + * and should be removed along with + * starting the instance number at 0 + */ + if (ti->module && ti->module->thread_detach) (void) ti->module->thread_detach(ti->el, ti->data); talloc_free(ti); } @@ -1190,6 +1196,15 @@ int modules_thread_instantiate(TALLOC_CTX *ctx, fr_event_list_t *el) talloc_set_destructor(module_thread_inst_array, _module_thread_inst_array_free); } + /* + * Index 0 is populated with a catchall entry + * FIXME - This is only required so we can + * fake out module instance data. As soon + * as we have multiple module lists this can + * be removed. + */ + MEM(module_thread_inst_array[0] = talloc_zero(module_thread_inst_array, module_thread_instance_t)); + for (instance = rbtree_iter_init_inorder(&iter, module_instance_name_tree); instance; instance = rbtree_iter_next_inorder(&iter)) { diff --git a/src/lib/server/virtual_servers.c b/src/lib/server/virtual_servers.c index 21f70467819..b58e0ee44dd 100644 --- a/src/lib/server/virtual_servers.c +++ b/src/lib/server/virtual_servers.c @@ -507,6 +507,8 @@ int virtual_server_entry_point_set(request_t *request) if (!server) return -1; mi->name = server->process_module->name; + mi->module = (module_t *)server->process_module; + mi->number = 0; /* Hacky hack hack */ if (unlikely(track && track->dynamic && server->dynamic_client_module)) { process = (fr_process_module_t const *) server->dynamic_client_module->module->common; @@ -1415,7 +1417,11 @@ unlang_action_t process_authenticate(rlm_rcode_t *p_result, int auth_type, reque request->module = NULL; request->component = "authenticate"; - rcode = unlang_interpret_section(request, cs, RLM_MODULE_REJECT); + + if (unlang_interpret_push_section(request, cs, RLM_MODULE_REJECT, UNLANG_TOP_FRAME) < 0) { + RETURN_MODULE_FAIL; + } + rcode = unlang_interpret(request); request->component = component; request->module = module; diff --git a/src/lib/tls/cache.c b/src/lib/tls/cache.c index f4a29662d95..e8e25232f44 100644 --- a/src/lib/tls/cache.c +++ b/src/lib/tls/cache.c @@ -103,8 +103,10 @@ unlang_action_t fr_tls_cache_process(rlm_rcode_t *p_result, request_t *request, * Run it through the appropriate virtual server. * FIXME - This is still blocking. */ - rcode = unlang_interpret_synchronous(request, action, RLM_MODULE_NOOP, true); - + if (unlang_interpret_push_section(request, action, RLM_MODULE_NOOP, UNLANG_TOP_FRAME) < 0) { + RETURN_MODULE_FAIL; + } + rcode = unlang_interpret_synchronous(request); /* * Restore the original status of the request. */ diff --git a/src/lib/unlang/all.mk b/src/lib/unlang/all.mk index 4ee49335089..b4abc0a4d42 100644 --- a/src/lib/unlang/all.mk +++ b/src/lib/unlang/all.mk @@ -9,6 +9,7 @@ SOURCES := base.c \ function.c \ group.c \ interpret.c \ + interpret_synchronous.c \ io.c \ load_balance.c \ map.c \ diff --git a/src/lib/unlang/call.c b/src/lib/unlang/call.c index b9ff27b9215..30b104249e2 100644 --- a/src/lib/unlang/call.c +++ b/src/lib/unlang/call.c @@ -50,6 +50,8 @@ static unlang_action_t unlang_call_process(rlm_rcode_t *p_result, request_t *req ua = state->process(&rcode, &(module_ctx_t){ .instance = state->instance }, request); switch (ua) { case UNLANG_ACTION_YIELD: + break; + case UNLANG_ACTION_STOP_PROCESSING: case UNLANG_ACTION_PUSHED_CHILD: /* Wants to do more processing */ return ua; @@ -208,8 +210,8 @@ int unlang_call_push(request_t *request, CONF_SECTION *server_cs, .prev_request_state = request->request_state, .prev_server_cs = request->server_cs }; - frame->process = unlang_call_process; /* Skip the initialisation */ - talloc_steal(frame, c); /* Bind our temporary unlang_call_t to the frame */ + frame->process = unlang_call_process; /* Skip the initialisation */ + talloc_steal(frame->state, c); /* Bind our temporary unlang_call_t to the frame */ return 0; } diff --git a/src/lib/unlang/interpret.c b/src/lib/unlang/interpret.c index bdc39eaed90..410e9dbecdb 100644 --- a/src/lib/unlang/interpret.c +++ b/src/lib/unlang/interpret.c @@ -18,7 +18,7 @@ * $Id$ * * @file unlang/interpret.c - * @brief Execute compiled unlang structures using an iterative interpreter. + * @brief Execute compiled unlang structures using an iterative interpret. * * @copyright 2006-2016 The FreeRADIUS server project */ @@ -29,9 +29,10 @@ RCSID("$Id$") #include #include -#include "unlang_priv.h" -#include "parallel_priv.h" +#include "interpret_priv.h" #include "module_priv.h" +#include "parallel_priv.h" +#include "unlang_priv.h" static fr_table_num_ordered_t const unlang_action_table[] = { { L("unwind"), UNLANG_ACTION_UNWIND }, @@ -107,7 +108,7 @@ static void stack_dump(request_t *request) #define DUMP_STACK #endif -/** Different operations the interpreter can execute +/** Different operations the interpret can execute */ unlang_op_t unlang_ops[UNLANG_TYPE_MAX]; @@ -158,8 +159,8 @@ static inline void frame_state_init(unlang_stack_t *stack, unlang_stack_frame_t * @param[in] default_rcode The default result. * @param[in] do_next_sibling Whether to only execute the first node in the #unlang_t program * or to execute subsequent nodes. - * @param[in] top_frame Return out of the unlang interpreter when popping this frame. - * Hands execution back to whatever called the interpreter. + * @param[in] top_frame Return out of the unlang interpret when popping this frame. + * Hands execution back to whatever called the interpret. * @return * - 0 on success. * - -1 on call stack too deep. @@ -182,7 +183,7 @@ int unlang_interpret_push(request_t *request, unlang_t const *instruction, /* * This is not a cancellation point. * - * If we cancel here bad things happen inside the interpreter. + * If we cancel here bad things happen inside the interpret. */ if (stack->depth >= (UNLANG_STACK_MAX - 1)) { RERROR("Call stack is too deep"); @@ -228,8 +229,9 @@ int unlang_interpret_push(request_t *request, unlang_t const *instruction, * - UNLANG_FRAME_ACTION_NEXT evaluate more instructions. * - UNLANG_FRAME_ACTION_POP the final result has been calculated for this frame. */ -static inline unlang_frame_action_t result_calculate(request_t *request, unlang_stack_frame_t *frame, - rlm_rcode_t *result, int *priority) +static inline CC_HINT(always_inline) +unlang_frame_action_t result_calculate(request_t *request, unlang_stack_frame_t *frame, + rlm_rcode_t *result, int *priority) { unlang_t const *instruction = frame->instruction; unlang_stack_t *stack = request->stack; @@ -346,7 +348,7 @@ static inline unlang_frame_action_t result_calculate(request_t *request, unlang_ /** Cleanup any lingering frame state * */ -static inline void frame_cleanup(unlang_stack_frame_t *frame) +static inline CC_HINT(always_inline) void frame_cleanup(unlang_stack_frame_t *frame) { /* * Don't clear top_frame flag, bad things happen... @@ -364,7 +366,7 @@ static inline void frame_cleanup(unlang_stack_frame_t *frame) /** Advance to the next sibling instruction * */ -static inline void frame_next(unlang_stack_t *stack, unlang_stack_frame_t *frame) +static inline CC_HINT(always_inline) void frame_next(unlang_stack_t *stack, unlang_stack_frame_t *frame) { frame_cleanup(frame); frame->instruction = frame->next; @@ -380,7 +382,7 @@ static inline void frame_next(unlang_stack_t *stack, unlang_stack_frame_t *frame * * @param[in] stack frame to pop. */ -static inline void frame_pop(unlang_stack_t *stack) +static inline CC_HINT(always_inline) void frame_pop(unlang_stack_t *stack) { unlang_stack_frame_t *frame; @@ -506,7 +508,7 @@ unlang_frame_action_t frame_eval(request_t *request, unlang_stack_frame_t *frame /* * Yield control back to the scheduler, or whatever - * called the interpreter. + * called the interpret. */ case UNLANG_ACTION_YIELD: *result = RLM_MODULE_YIELD; /* Fixup rcode */ @@ -591,7 +593,7 @@ unlang_frame_action_t frame_eval(request_t *request, unlang_stack_frame_t *frame RDEBUG2("}"); } break; - } /* switch over return code from the interpreter function */ + } /* switch over return code from the interpret function */ frame_next(stack, frame); } @@ -604,17 +606,22 @@ unlang_frame_action_t frame_eval(request_t *request, unlang_stack_frame_t *frame return UNLANG_FRAME_ACTION_POP; } -/* - * Interpret the various types of blocks. +/** Run the interpreter for a current request + * + * @param[in] request to run. If this is an internal request + * the request may be freed by the interpreter. + * @return The final request rcode. */ -rlm_rcode_t unlang_interpret(request_t *request) +CC_HINT(hot) rlm_rcode_t unlang_interpret(request_t *request) { unlang_frame_action_t fa = UNLANG_FRAME_ACTION_NEXT; + rlm_rcode_t rcode; /* * We don't have a return code yet. */ unlang_stack_t *stack = request->stack; + unlang_interpret_t *intp = stack->intp; unlang_stack_frame_t *frame = &stack->frame[stack->depth]; /* Quiet static analysis */ stack->priority = -1; /* Reset */ @@ -625,8 +632,10 @@ rlm_rcode_t unlang_interpret(request_t *request) #endif fr_assert(!unlang_request_is_scheduled(request)); /* if we're running it, it can't be scheduled */ + fr_assert_msg(intp, "request has no interpreter associated"); - RDEBUG4("** [%i] %s - interpreter entered", stack->depth, __FUNCTION__); + RDEBUG4("** [%i] %s - interpret entered", stack->depth, __FUNCTION__); + if (is_yielded(frame)) intp->funcs.resume(request, intp->uctx); for (;;) { RDEBUG4("** [%i] %s - frame action %s", stack->depth, __FUNCTION__, @@ -733,7 +742,8 @@ rlm_rcode_t unlang_interpret(request_t *request) case UNLANG_FRAME_ACTION_YIELD: fr_assert(stack->result == RLM_MODULE_YIELD); - RDEBUG4("** [%i] %s - interpreter yielding", stack->depth, __FUNCTION__); + RDEBUG4("** [%i] %s - interpret yielding", stack->depth, __FUNCTION__); + intp->funcs.yield(request, intp->uctx); return stack->result; } break; @@ -763,7 +773,7 @@ rlm_rcode_t unlang_interpret(request_t *request) * We're at the top frame, return the result from the * stack, and get rid of the top frame. */ - RDEBUG4("** [%i] %s - interpreter exiting, returning %s", stack->depth, __FUNCTION__, + RDEBUG4("** [%i] %s - interpret exiting, returning %s", stack->depth, __FUNCTION__, fr_table_str_by_value(mod_rcode_table, frame->result, "")); stack->result = frame->result; @@ -771,7 +781,24 @@ rlm_rcode_t unlang_interpret(request_t *request) stack->depth--; DUMP_STACK; - return stack->result; + /* + * Record this now as the done functions may free + * the request. + */ + rcode = stack->result; + + /* + * This usually means the request is complete in its + * entirety. + */ + if (stack->depth == 0) { + if (request_is_internal(request)) { + intp->funcs.done_internal(request, stack->result, intp->uctx); + } else { + intp->funcs.done_external(request, stack->result, intp->uctx); + } + } + return rcode; } static unlang_group_t empty_group = { @@ -850,162 +877,6 @@ int unlang_interpret_push_instruction(request_t *request, void *instruction, rlm return 0; } -/** Call a module, iteratively, with a local stack, rather than recursively - * - * What did Paul Graham say about Lisp...? - */ -rlm_rcode_t unlang_interpret_section(request_t *request, CONF_SECTION *subcs, rlm_rcode_t default_rcode) -{ - /* - * This pushes a new frame onto the stack, which is the - * start of a new unlang section... - */ - if (unlang_interpret_push_section(request, subcs, default_rcode, UNLANG_TOP_FRAME) < 0)return RLM_MODULE_FAIL; - - return unlang_interpret(request); -} - -/** Execute an unlang section synchronously - * - * Create a temporary event loop and swap it out for the one in the request. - * Execute unlang operations until we receive a non-yield return code then return. - * - * @note The use cases for this are very limited. If you need to use it, chances - * are what you're doing could be done better using one of the thread - * event loops. - * - * @param[in] request The current request. - * @param[in] cs Section with compiled unlang associated with it. - * @param[in] action The default return code to use. - * @param[in] child_el Use a child event list - * @return One of the RLM_MODULE_* macros. - */ -rlm_rcode_t unlang_interpret_synchronous(request_t *request, CONF_SECTION *cs, rlm_rcode_t action, bool child_el) -{ - fr_event_list_t *old_el, *el = NULL; - fr_heap_t *backlog, *old_backlog; - rlm_rcode_t rcode; - char const *caller; - request_t *sub_request = NULL; - bool wait_for_event; - int iterations = 0; - - old_el = request->el; - if (child_el) { - /* - * Don't talloc from the request - * as we'll almost certainly leave holes in the memory pool. - */ - el = fr_event_list_alloc(NULL, NULL, NULL); - if (!el) { - RPERROR("Failed creating temporary event loop"); - fr_assert(0); /* Cause debug builds to die */ - return RLM_MODULE_FAIL; - } - } - - MEM(backlog = fr_heap_talloc_alloc(request, fr_pointer_cmp, request_t, runnable_id)); - old_backlog = request->backlog; - caller = request->module; - - request->backlog = backlog; - - rcode = unlang_interpret_section(request, cs, action); - wait_for_event = (rcode == RLM_MODULE_YIELD); - - while (true) { - rlm_rcode_t sub_rcode; - int num_events; - - /* - * Wait for a timer / IO event. If there's a - * failure, all kinds of bad things happen. Oh - * well. - */ - DEBUG3("Gathering events - %s", wait_for_event ? "will wait" : "Will not wait"); - num_events = fr_event_corral(request->el, fr_time(), wait_for_event); - if (num_events < 0) { - RPERROR("Failed retrieving events"); - return RLM_MODULE_FAIL; - } - - DEBUG3("%u event(s) pending%s", - num_events == -1 ? 0 : num_events, num_events == -1 ? " - event loop exiting" : ""); - - /* - * We were NOT waiting, AND there are no more - * events to run, AND there are no more requests - * to run. We can exit the loop. - */ - if (!wait_for_event && (num_events == 0) && - (fr_heap_num_elements(backlog) == 0)) { - break; - } - - /* - * This function ends up pushing a - * runnable request into the backlog, OR - * setting new timers. - */ - if (num_events > 0) { - DEBUG4("Servicing event(s)"); - fr_event_service(request->el); - } - - /* - * If there are no runnable requests, then go - * back to check the timers again. Note that we - * only wait if there are timer events left to - * service. - * - * If there WAS a timer event, but servicing that - * timer event did not result in a runnable - * request, THEN we're guaranteed that there is - * still a timer event left. - */ - sub_request = fr_heap_pop(backlog); - if (!sub_request) { - wait_for_event = (num_events > 0); - continue; - } - - /* - * Continue interpretation until there's nothing - * in the backlog. If this request YIELDs, then - * do another loop around. - */ - RDEBUG4(">>> interpreter (iteration %i)", ++iterations); - sub_rcode = unlang_interpret(sub_request); - RDEBUG4("<<< interpreter (iteration %i) - %s", iterations, - fr_table_str_by_value(mod_rcode_table, sub_rcode, "")); - if (sub_rcode == RLM_MODULE_YIELD) { - wait_for_event = true; - continue; - } - - /* - * This request is done. Clean up, and do a - * non-blocking check for more events in the next - * loop. - */ - wait_for_event = false; - if (sub_request == request) { - rcode = sub_rcode; - } else { - RDEBUG4("Cleaning up subrequest"); - talloc_free(sub_request); - } - } - - talloc_free(el); - talloc_free(backlog); - request->el = old_el; - request->backlog = old_backlog; - request->module = caller; - - return rcode; -} - /** Allocate a new unlang stack * * @param[in] ctx to allocate stack in. @@ -1051,7 +922,7 @@ void *unlang_interpret_stack_alloc(TALLOC_CTX *ctx) * @param[in] action to signal. * @param[in] limit the frame at which to stop signaling. */ -static void frame_signal(request_t *request, fr_state_signal_t action, int limit) +static inline CC_HINT(always_inline) void frame_signal(request_t *request, fr_state_signal_t action, int limit) { unlang_stack_frame_t *frame; unlang_stack_t *stack = request->stack; @@ -1113,7 +984,7 @@ void unlang_interpret_signal(request_t *request, fr_state_signal_t action) } /* - * Requests that haven't been run through the interpreter + * Requests that haven't been run through the interpret * yet should have a stack depth of zero, so we don't * need to do anything. */ @@ -1140,12 +1011,22 @@ int unlang_interpret_stack_depth(request_t *request) */ rlm_rcode_t unlang_interpret_stack_result(request_t *request) { - unlang_stack_t *stack = request->stack; return stack->result; } +/** Return whether a request is currently scheduled + * + */ +bool unlang_request_is_scheduled(request_t const *request) +{ + unlang_stack_t *stack = request->stack; + unlang_interpret_t *intp = stack->intp; + + return intp->funcs.scheduled(request, intp->uctx); +} + /** Check if a request as resumable. * * @param[in] request The current request. @@ -1174,8 +1055,11 @@ bool unlang_interpret_is_resumable(request_t *request) void unlang_interpret_mark_runnable(request_t *request) { unlang_stack_t *stack = request->stack; + unlang_interpret_t *intp = stack->intp; unlang_stack_frame_t *frame = &stack->frame[stack->depth]; + bool scheduled = unlang_request_is_scheduled(request); + /* * The request hasn't yielded, OR it's already been * marked as runnable. Don't do anything. @@ -1194,23 +1078,18 @@ void unlang_interpret_mark_runnable(request_t *request) * Multiple child request may also mark a parent request * runnable, before the parent request starts running. */ - if (!is_yielded(frame) || unlang_request_is_scheduled(request)) { + if (!is_yielded(frame) || scheduled) { RDEBUG3("Not marking resumable due to%s%s", !is_yielded(frame) ? - " it not being yielded " : "", unlang_request_is_scheduled(request) ? " it already being scheduled" : ""); + " it not being yielded " : "", scheduled ? " it already being scheduled" : ""); return; } - if (!unlang_request_is_scheduled(request)) { - RDEBUG3("Marked as resumable"); - } + RDEBUG3("Marked as resumable"); - fr_assert(request->backlog != NULL); - - fr_heap_insert(request->backlog, request); + intp->funcs.mark_runnable(request, intp->uctx); } - /** Get a talloc_ctx which is valid only for this frame * * @param[in] request The current request. @@ -1235,7 +1114,7 @@ TALLOC_CTX *unlang_interpret_frame_talloc_ctx(request_t *request) * Ensure that the memory is always cleaned up when the * request exits. And make sure that this function is safe to call from anywhere. */ - return (TALLOC_CTX *) request; + return (TALLOC_CTX *)request; } static xlat_arg_parser_t const unlang_interpret_xlat_args[] = { @@ -1358,6 +1237,52 @@ finish: return XLAT_ACTION_DONE; } +/** Initialize a unlang compiler / interpret. + * + * @param[in] ctx to bind lifetime of the interpret to. + * Shouldn't be any free order issues here as + * the interpret itself has no state. + * But event loop should be stopped before + * freeing the interpret. + * @param[in] el for any timer or I/O events. + * @param[in] funcs Callbacks to used to communicate request + * state to our owner. + * @param[in] uctx Data to pass to callbacks. + */ +unlang_interpret_t *unlang_interpret_init(TALLOC_CTX *ctx, + fr_event_list_t *el, unlang_request_func_t *funcs, void *uctx) +{ + unlang_interpret_t *intp; + + MEM(intp = talloc(ctx, unlang_interpret_t)); + *intp = (unlang_interpret_t){ + .el = el, + .funcs = *funcs, + .uctx = uctx + }; + + return intp; +} + +/** Set a specific interpreter for a request + * + */ +void unlang_interpret_set(request_t *request, unlang_interpret_t *intp) +{ + unlang_stack_t *stack = request->stack; + stack->intp = intp; +} + +/** Get the interpreter set for a request + * + */ +unlang_interpret_t *unlang_interpret_get(request_t *request) +{ + unlang_stack_t *stack = request->stack; + + return stack->intp; +} + void unlang_interpret_init_global(void) { xlat_t *xlat; diff --git a/src/lib/unlang/interpret.h b/src/lib/unlang/interpret.h index bfa29947f8c..c27f90c1ec3 100644 --- a/src/lib/unlang/interpret.h +++ b/src/lib/unlang/interpret.h @@ -28,6 +28,7 @@ extern "C" { #endif +#include #include #define UNLANG_TOP_FRAME (true) @@ -36,43 +37,107 @@ extern "C" { #define UNLANG_STACK_MAX (64) //!< The maximum depth of the stack. #define UNLANG_FRAME_PRE_ALLOC (128) //!< How much memory we pre-alloc for each frame. -/** Return whether a request is currently scheduled +/** Interpreter handle * */ -static inline bool unlang_request_is_scheduled(request_t const *request) -{ - return (request->runnable_id >= 0); -} +typedef struct unlang_interpret_s unlang_interpret_t; + +/** Signal the owner of the interpreter that this request should be initialised and executed + * + * This is called once per request, when it's about to start executing. + */ +typedef void (*unlang_request_init_t)(request_t *request, void *uctx); + +/** Signal the owner of the interpreter that this request completed processing + * + * This is called once per request, when the interpret is about to stop processing it. + */ +typedef void (*unlang_request_done_t)(request_t *request, rlm_rcode_t rcode, void *uctx); + +/** Signal the owner of the interpreter that a request has yielded + * + * This is called whenever a request has given control back to the interpeter. + */ +typedef void (*unlang_request_yield_t)(request_t *request, void *uctx); + +/** Signal the owner of the interpeter that a request is ready to be resumed + * + * This is called any time a yielded request has resumed. + */ +typedef void (*unlang_request_resume_t)(request_t *request, void *uctx); + +/** Signal the owner of the interpeter that a request is now runnable + * + * This is called any time a yielded request has been marked runnable. + */ +typedef void (*unlang_request_runnable_t)(request_t *request, void *uctx); + +/** Signal the owner of the interpeter that a request is now runnable + * + * This is called any time a yielded request has been marked runnable. + */ +typedef bool (*unlang_request_scheduled_t)(request_t const *request, void *uctx); + +/** External functions provided by the owner of the interpret + * + * These functions allow the event loop to signal the caller when a given + * request is ready to run for the first time, and when it should be resumed + * and passed back to #unlang_interpret to continue execution. + * + * This is the cleanest way to separate the interpret and the code that's + * managing requests. + * + * Test harnesses (for example) need to perform far less initialisation and + * request management than FeeRADIUS worker threads. + */ +typedef struct { + unlang_request_init_t init_internal; //!< Function called to initialise a request. + unlang_request_done_t done_internal; //!< Function called when an internal request completes. + unlang_request_done_t done_external; //!< Function called when an internal request completes. + unlang_request_yield_t yield; //!< Function called when a request yields. + unlang_request_resume_t resume; //!< Function called when a request is resumed. + unlang_request_runnable_t mark_runnable; //!< Function called when a request needs to be + ///< added back to the runnable queue. + unlang_request_scheduled_t scheduled; //!< Function to check if a request is already + ///< scheduled. +} unlang_request_func_t; + +int unlang_interpret_push_section(request_t *request, CONF_SECTION *cs, + rlm_rcode_t default_action, bool top_frame) + CC_HINT(warn_unused_result); + +int unlang_interpret_push_instruction(request_t *request, void *instruction, + rlm_rcode_t default_rcode, bool top_frame) + CC_HINT(warn_unused_result); + +unlang_interpret_t *unlang_interpret_init(TALLOC_CTX *ctx, + fr_event_list_t *el, unlang_request_func_t *func, void *uctx); -int unlang_interpret_push_section(request_t *request, CONF_SECTION *cs, - rlm_rcode_t default_action, bool top_frame) - CC_HINT(warn_unused_result); +void unlang_interpret_set(request_t *request, unlang_interpret_t *intp); -int unlang_interpret_push_instruction(request_t *request, void *instruction, - rlm_rcode_t default_rcode, bool top_frame) - CC_HINT(warn_unused_result); +unlang_interpret_t *unlang_interpret_get(request_t *request); -rlm_rcode_t unlang_interpret(request_t *request); +rlm_rcode_t unlang_interpret(request_t *request) CC_HINT(hot); -rlm_rcode_t unlang_interpret_section(request_t *request, CONF_SECTION *cs, rlm_rcode_t default_action); +rlm_rcode_t unlang_interpret_synchronous(request_t *request); -rlm_rcode_t unlang_interpret_synchronous(request_t *request, CONF_SECTION *cs, rlm_rcode_t action, bool child_el); +void *unlang_interpret_stack_alloc(TALLOC_CTX *ctx); -void *unlang_interpret_stack_alloc(TALLOC_CTX *ctx); +bool unlang_request_is_scheduled(request_t const *request); -void unlang_interpret_mark_runnable(request_t *request); +void unlang_interpret_mark_runnable(request_t *request); -bool unlang_interpret_is_resumable(request_t *request); +bool unlang_interpret_is_resumable(request_t *request); -void unlang_interpret_signal(request_t *request, fr_state_signal_t action); +void unlang_interpret_signal(request_t *request, fr_state_signal_t action); -int unlang_interpret_stack_depth(request_t *request); +int unlang_interpret_stack_depth(request_t *request); -rlm_rcode_t unlang_interpret_stack_result(request_t *request); +rlm_rcode_t unlang_interpret_stack_result(request_t *request); -TALLOC_CTX *unlang_interpret_frame_talloc_ctx(request_t *request); +TALLOC_CTX *unlang_interpret_frame_talloc_ctx(request_t *request); -void unlang_interpret_init_global(void); +void unlang_interpret_init_global(void); #ifdef __cplusplus } #endif diff --git a/src/lib/unlang/interpret_priv.h b/src/lib/unlang/interpret_priv.h new file mode 100644 index 00000000000..0ba456b094c --- /dev/null +++ b/src/lib/unlang/interpret_priv.h @@ -0,0 +1,49 @@ +#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/interpret.h + * @brief Declarations for the unlang interpreter. + * + * @copyright 2019 The FreeRADIUS server project + */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +struct unlang_interpret_s { + fr_event_list_t *el; + unlang_request_func_t funcs; + void *uctx; +}; + +static inline void unlang_interpret_child_init(request_t *request) +{ + unlang_interpret_t *intp = unlang_interpret_get(request); + + intp->funcs.init_internal(request, intp->uctx); +} + +#ifdef __cplusplus +} +#endif diff --git a/src/lib/unlang/interpret_synchronous.c b/src/lib/unlang/interpret_synchronous.c new file mode 100644 index 00000000000..f940522bffc --- /dev/null +++ b/src/lib/unlang/interpret_synchronous.c @@ -0,0 +1,246 @@ +/* + * 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 unlang/interpret_synchronous.c + * @brief Synchronous interpreter + * + * @copyright 2021 The FreeRADIUS server project + */ + +#include "interpret_priv.h" +#include + +typedef struct { + fr_heap_t *runnable; + fr_event_list_t *el; + int yielded; + unlang_interpret_t *intp; +} unlang_interpret_synchronous_t; + + +/** Internal request (i.e. one generated by the interpreter) is now complete + * + */ +static void _request_internal_init(request_t *request, void *uctx) +{ + unlang_interpret_synchronous_t *intps = uctx; + + RDEBUG3("Initialising synchronous request"); + unlang_interpret_set(request, intps->intp); + fr_heap_insert(intps->runnable, request); +} + +/** Internal request (i.e. one generated by the interpreter) is now complete + * + */ +static void _request_internal_done(request_t *request, UNUSED rlm_rcode_t rcode, UNUSED void *uctx) +{ + RDEBUG3("Freeing synchronous request"); + talloc_free(request); +} + +/** External request is now complete + * + */ +static void _request_external_done(request_t *request, UNUSED rlm_rcode_t rcode, UNUSED void *uctx) +{ + /* + * If we're running a real request, then the final + * indentation MUST be zero. Otherwise we skipped + * something! + * + * Also check that the request is NOT marked as + * "yielded", but is in fact done. + * + * @todo - check that the stack is at frame 0, otherwise + * more things have gone wrong. + */ + fr_assert_msg(request->parent || (request->log.unlang_indent == 0), + "Request %s bad log indentation - expected 0 got %u", request->name, request->log.unlang_indent); + fr_assert_msg(!unlang_interpret_is_resumable(request), + "Request %s is marked as yielded at end of processing", request->name); +} + +/** Request is now runnable + * + */ +static void _request_runnable(request_t *request, void *uctx) +{ + unlang_interpret_synchronous_t *intps = uctx; + + fr_heap_insert(intps->runnable, request); +} + +/** Interpreter yielded request + * + */ +static void _request_yield(request_t *request, UNUSED void *uctx) +{ + RDEBUG3("synchronous request yielded"); +} + +/** Interpreter is starting to work on request again + * + */ +static void _request_resume(request_t *request, UNUSED void *uctx) +{ + RDEBUG3("synchronous request resumed"); +} + +static bool _request_scheduled(request_t const *request, UNUSED void *uctx) +{ + return fr_heap_entry_inserted(request->runnable_id); +} + +/** Allocate a new temporary interpreter + * + */ +static unlang_interpret_synchronous_t *unlang_interpret_alloc(TALLOC_CTX *ctx) +{ + unlang_interpret_synchronous_t *intps; + + MEM(intps = talloc_zero(ctx, unlang_interpret_synchronous_t)); + MEM(intps->runnable = fr_heap_talloc_alloc(intps, fr_pointer_cmp, request_t, runnable_id)); + MEM(intps->el = fr_event_list_alloc(intps, NULL, NULL)); + MEM(intps->intp = unlang_interpret_init(intps, intps->el, + &(unlang_request_func_t){ + .init_internal = _request_internal_init, + .done_internal = _request_internal_done, + .done_external = _request_external_done, + .mark_runnable = _request_runnable, + .yield = _request_yield, + .resume = _request_resume, + .scheduled = _request_scheduled + }, + intps)); + + return intps; +} + +/** Execute an unlang section synchronously + * + * Create a temporary event loop and swap it out for the one in the request. + * Execute unlang operations until we receive a non-yield return code then return. + * + * @note The use cases for this are very limited. If you need to use it, chances + * are what you're doing could be done better using one of the thread + * event loops. + * + * @param[in] request The current request. + * @return One of the RLM_MODULE_* macros. + */ +rlm_rcode_t unlang_interpret_synchronous(request_t *request) +{ + fr_event_list_t *old_el; + fr_heap_t *old_backlog; + unlang_interpret_t *old_intp; + char const *caller; + + unlang_interpret_synchronous_t *intps; + + rlm_rcode_t rcode; + + request_t *sub_request = NULL; + bool dont_wait_for_event; + int iterations = 0; + + old_el = request->el; + old_backlog = request->backlog; + old_intp = unlang_interpret_get(request); + caller = request->module; + + intps = unlang_interpret_alloc(request); + request->backlog = intps->runnable; + request->el = intps->el; + unlang_interpret_set(request, intps->intp); + + rcode = unlang_interpret(request); + + while (!(dont_wait_for_event = (fr_heap_num_elements(intps->runnable) == 0)) || + (intps->yielded > 0)) { + rlm_rcode_t sub_rcode; + int num_events; + + DEBUG3("%u runnable, %u yielded", fr_heap_num_elements(intps->runnable), intps->yielded); + /* + * Wait for a timer / IO event. If there's a + * failure, all kinds of bad things happen. Oh + * well. + */ + DEBUG3("Gathering events - %s", dont_wait_for_event ? "Will not wait" : "will wait"); + num_events = fr_event_corral(request->el, fr_time(), !dont_wait_for_event); + if (num_events < 0) break; + + DEBUG3("%u event(s) pending%s", + num_events == -1 ? 0 : num_events, num_events == -1 ? " - event loop exiting" : ""); + + /* + * This function ends up pushing a + * runnable request into the backlog, OR + * setting new timers. + */ + if (num_events > 0) { + DEBUG4("Servicing event(s)"); + fr_event_service(request->el); + } + + /* + * If there are no runnable requests, then go + * back to check the timers again. Note that we + * only wait if there are timer events left to + * service. + * + * If there WAS a timer event, but servicing that + * timer event did not result in a runnable + * request, THEN we're guaranteed that there is + * still a timer event left. + */ + sub_request = fr_heap_pop(intps->runnable); + if (!sub_request) continue; + + if (unlang_interpret_is_resumable(sub_request)) intps->yielded--; + + /* + * Continue interpretation until there's nothing + * in the backlog. If this request YIELDs, then + * do another loop around. + */ + RDEBUG4(">>> interpreter (iteration %i)", ++iterations); + sub_rcode = unlang_interpret(sub_request); + RDEBUG4("<<< interpreter (iteration %i) - %s", iterations, + fr_table_str_by_value(rcode_table, sub_rcode, "")); + + if (unlang_interpret_is_resumable(sub_request)) intps->yielded++; + + if (sub_request == request) { + rcode = sub_rcode; + } else { + RDEBUG4("Cleaning up subrequest"); + talloc_free(sub_request); + } + } + + talloc_free(intps); + unlang_interpret_set(request, old_intp); + request->el = old_el; + request->backlog = old_backlog; + request->module = caller; + + return rcode; +} diff --git a/src/lib/unlang/io.c b/src/lib/unlang/io.c index 4f710aed814..e7ffcc598b7 100644 --- a/src/lib/unlang/io.c +++ b/src/lib/unlang/io.c @@ -67,7 +67,7 @@ unlang_action_t unlang_io_process_interpret(rlm_rcode_t *p_result, UNUSED module */ request_t *unlang_io_subrequest_alloc(request_t *parent, fr_dict_t const *namespace, bool detachable) { - request_t *child; + request_t *child; child = request_alloc(detachable ? NULL : parent, (&(request_init_args_t){ @@ -78,9 +78,13 @@ request_t *unlang_io_subrequest_alloc(request_t *parent, fr_dict_t const *namesp if (!child) return NULL; /* - * Push the child, and set it's top frame to be true. + * Child gets its parent's interpreter */ + ((unlang_stack_t *)child->stack)->intp = ((unlang_stack_t *)parent->stack)->intp; + /* + * Push the child, and set it's top frame to be true. + */ child->log.unlang_indent = parent->log.unlang_indent; /* diff --git a/src/lib/unlang/parallel.c b/src/lib/unlang/parallel.c index 658ecfb7c83..24666508ab5 100644 --- a/src/lib/unlang/parallel.c +++ b/src/lib/unlang/parallel.c @@ -25,6 +25,7 @@ RCSID("$Id$") #include "function.h" +#include "interpret_priv.h" #include "module_priv.h" #include "parallel_priv.h" #include "subrequest_priv.h" @@ -393,7 +394,7 @@ static unlang_action_t unlang_parallel_process(rlm_rcode_t *p_result, request_t state->children[i].name = talloc_bstrdup(state, child->name); state->children[i].request = child; state->children[i].state = CHILD_RUNNABLE; - fr_heap_insert(request->backlog, child); + unlang_interpret_child_init(child); } /* diff --git a/src/lib/unlang/subrequest.c b/src/lib/unlang/subrequest.c index f04f112920d..e77fcb3089c 100644 --- a/src/lib/unlang/subrequest.c +++ b/src/lib/unlang/subrequest.c @@ -26,6 +26,7 @@ RCSID("$Id$") #include #include "unlang_priv.h" +#include "interpret_priv.h" #include "subrequest_priv.h" /** Parameters for initialising the subrequest @@ -397,10 +398,7 @@ int unlang_detached_child_init(request_t *request) * Mark the child as runnable. */ fr_assert(request->parent == NULL); - if (fr_heap_insert(request->backlog, request) < 0) { - RPERROR("Failed inserting ourself into the backlog."); - return -1; - } + unlang_interpret_child_init(request); return 0; } diff --git a/src/lib/unlang/unlang_priv.h b/src/lib/unlang/unlang_priv.h index 6c20356d8ab..4a551a1ad5d 100644 --- a/src/lib/unlang/unlang_priv.h +++ b/src/lib/unlang/unlang_priv.h @@ -250,6 +250,8 @@ struct unlang_stack_frame_s { * */ typedef struct { + unlang_interpret_t *intp; //!< Interpreter that the request is currently + ///< associated with. int priority; //!< Current priority. rlm_rcode_t result; //!< The current stack rcode. int depth; //!< Current depth we're executing at. diff --git a/src/modules/proto_bfd/proto_bfd.c b/src/modules/proto_bfd/proto_bfd.c index 789e250d97d..035642ccff7 100644 --- a/src/modules/proto_bfd/proto_bfd.c +++ b/src/modules/proto_bfd/proto_bfd.c @@ -1369,7 +1369,11 @@ static int bfd_process(bfd_state_t *session, bfd_packet_t *bfd) request->module = NULL; DEBUG2("server %s {", cf_section_name2(request->server_cs)); - unlang_interpret_section(request, session->unlang, RLM_MODULE_NOTFOUND); + if (unlang_interpret_push_section(request, session->unlang, RLM_MODULE_NOTFOUND, UNLANG_TOP_FRAME) < 0) { + talloc_free(request); + return 0; + } + unlang_interpret(request); DEBUG("}"); /* diff --git a/src/modules/proto_ldap_sync/proto_ldap_sync.c b/src/modules/proto_ldap_sync/proto_ldap_sync.c index ca7006ddabb..9c0809301d7 100644 --- a/src/modules/proto_ldap_sync/proto_ldap_sync.c +++ b/src/modules/proto_ldap_sync/proto_ldap_sync.c @@ -835,7 +835,11 @@ static int proto_ldap_cookie_load(TALLOC_CTX *ctx, uint8_t **cookie, rad_listen_ *cookie = NULL; - rcode = unlang_interpret_synchronous(request, unlang, RLM_MODULE_NOOP, true); + if (unlang_interpret_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME) < 0) { + ret = -1; + goto finish; + } + rcode = unlang_interpret_synchronous(request); switch (rcode) { case RLM_MODULE_OK: case RLM_MODULE_UPDATED: