From: Alan T. DeKok Date: Mon, 11 Sep 2017 19:53:57 +0000 (-0400) Subject: add "detach" keyword X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=04ad8056df4054960ace2fc5df87330239a9f0ee;p=thirdparty%2Ffreeradius-server.git add "detach" keyword and functionality. Its parent has to be "create" --- diff --git a/src/include/interpreter.h b/src/include/interpreter.h index 2c922822ce0..eabb2375cf4 100644 --- a/src/include/interpreter.h +++ b/src/include/interpreter.h @@ -68,6 +68,7 @@ typedef enum { UNLANG_TYPE_MAP, //!< Mapping section (like #UNLANG_TYPE_UPDATE, but uses //!< values from a #map_proc_t call). UNLANG_TYPE_CREATE, //!< create a child + UNLANG_TYPE_DETACH, //!< detach a child #endif UNLANG_TYPE_POLICY, //!< Policy section. UNLANG_TYPE_XLAT_INLINE, //!< xlat statement, inline in "unlang" diff --git a/src/main/unlang_compile.c b/src/main/unlang_compile.c index b6021531f70..4c9e7a11c52 100644 --- a/src/main/unlang_compile.c +++ b/src/main/unlang_compile.c @@ -2120,6 +2120,26 @@ static unlang_t *compile_break(unlang_t *parent, unlang_compile_t *unlang_ctx, C return compile_empty(parent, unlang_ctx, NULL, UNLANG_GROUP_TYPE_SIMPLE, UNLANG_GROUP_TYPE_SIMPLE, UNLANG_TYPE_BREAK, COND_TYPE_INVALID); } + +static unlang_t *compile_detach(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_ITEM const *ci) +{ + if (parent->type != UNLANG_TYPE_CREATE) { + cf_log_err(ci, "'detach' can only be used in a 'create' section"); + return NULL; + } + + /* + * This really overloads the functionality of + * cf_item_next(). + */ + if (!cf_item_next(ci, ci)) { + cf_log_err(ci, "'detach' cannot be used as the last entry in a section"); + return NULL; + } + + return compile_empty(parent, unlang_ctx, NULL, UNLANG_GROUP_TYPE_SIMPLE, UNLANG_GROUP_TYPE_SIMPLE, + UNLANG_TYPE_DETACH, COND_TYPE_INVALID); +} #endif static unlang_t *compile_xlat_inline(unlang_t *parent, @@ -2811,6 +2831,10 @@ static unlang_t *compile_item(unlang_t *parent, unlang_compile_t *unlang_ctx, CO cf_log_err(ci, "Invalid use of 'break'"); return NULL; + } else if (strcmp(modrefname, "detach") == 0) { + cf_log_err(ci, "Invalid use of 'detach'"); + return NULL; + } else if (strcmp(modrefname, "return") == 0) { cf_log_err(ci, "Invalid use of 'return'"); return NULL; @@ -2857,6 +2881,10 @@ static unlang_t *compile_item(unlang_t *parent, unlang_compile_t *unlang_ctx, CO return compile_break(parent, unlang_ctx, ci); } + if (strcmp(modrefname, "detach") == 0) { + return compile_detach(parent, unlang_ctx, ci); + } + if (strcmp(modrefname, "return") == 0) { return compile_empty(parent, unlang_ctx, NULL, UNLANG_GROUP_TYPE_SIMPLE, UNLANG_GROUP_TYPE_SIMPLE, UNLANG_TYPE_RETURN, COND_TYPE_INVALID); } diff --git a/src/main/unlang_interpret.c b/src/main/unlang_interpret.c index 3448bb22730..636d1fd8397 100644 --- a/src/main/unlang_interpret.c +++ b/src/main/unlang_interpret.c @@ -646,6 +646,33 @@ static rlm_rcode_t unlang_create_resume(UNUSED REQUEST *request, unlang_stack_t return RLM_MODULE_YIELD; } +static unlang_action_t unlang_detach(REQUEST *request, unlang_stack_t *stack, + rlm_rcode_t *presult, int *priority) +{ + unlang_stack_frame_t *frame = &stack->frame[stack->depth]; + unlang_t *instruction = frame->instruction; + + rad_assert(instruction->parent->type == UNLANG_TYPE_CREATE); + RDEBUG2("%s", unlang_ops[instruction->type].name); + + if (request_detach(request) < 0) { + ERROR("Failed detaching child"); + *presult = RLM_MODULE_FAIL; + *priority = 0; + return UNLANG_ACTION_CALCULATE_RESULT; + } + + /* + * request_detach() doesn't set the "detached" flag, but + * it does set the backlog... + */ + request->async->detached = true; + rad_assert(request->backlog != NULL); + + *presult = RLM_MODULE_YIELD; + return UNLANG_ACTION_CALCULATE_RESULT; +} + static unlang_action_t unlang_create(REQUEST *request, unlang_stack_t *stack, rlm_rcode_t *presult, int *priority) { @@ -716,6 +743,33 @@ static unlang_action_t unlang_create(REQUEST *request, unlang_stack_t *stack, return UNLANG_ACTION_CALCULATE_RESULT; } + /* + * As a special case, if the child is "detach", detach + * the child, insert the child into the runnable queue, + * and keep going with the parent. + */ + { + unlang_stack_t *child_stack = child->stack; + unlang_stack_frame_t *child_frame = &child_stack->frame[child_stack->depth]; + unlang_t *child_instruction = child_frame->instruction; + + if (child_instruction->type == UNLANG_TYPE_DETACH) { + rad_assert(child->backlog != NULL); + fr_heap_insert(child->backlog, child); + + /* + * Tell the interpreter to skip the "detach" + * stack frame when it continues. + */ + child_frame->instruction = child_frame->next; + if (child_frame->instruction) child_frame->next = child_frame->instruction->next; + + *presult = RLM_MODULE_NOOP; + *priority = 0; + return UNLANG_ACTION_CALCULATE_RESULT; + } /* else the child yielded, so we have to yield */ + } + /* * Create the "resume" stack frame, and have it replace our stack frame. */ @@ -1727,6 +1781,11 @@ unlang_op_t unlang_ops[] = { .func = unlang_create, .debug_braces = true }, + [UNLANG_TYPE_DETACH] = { + .name = "detach", + .func = unlang_detach, + .debug_braces = false + }, #endif [UNLANG_TYPE_XLAT_INLINE] = { .name = "xlat_inline", @@ -1851,6 +1910,20 @@ resume_subsection: case UNLANG_ACTION_CALCULATE_RESULT: if (result == RLM_MODULE_YIELD) { + /* + * Detach is magic. The parent + * "create" function takes care + * of bumping the instruction + * pointer... + */ + if (frame->instruction->type == UNLANG_TYPE_DETACH) { + RDEBUG4("** [%i] %s - detaching child with current (%s %d)", stack->depth, __FUNCTION__, + fr_int2str(mod_rcode_table, frame->result, ""), + frame->priority); + DUMP_STACK; + return RLM_MODULE_YIELD; + } + rad_assert(frame->instruction->type == UNLANG_TYPE_RESUME); frame->resume = true; RDEBUG4("** [%i] %s - yielding with current (%s %d)", stack->depth, __FUNCTION__,