]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
Rework subrequests to behave more like parallel child requests
authorArran Cudbard-Bell <a.cudbardb@freeradius.org>
Wed, 24 Mar 2021 22:30:40 +0000 (22:30 +0000)
committerArran Cudbard-Bell <a.cudbardb@freeradius.org>
Wed, 31 Mar 2021 15:10:41 +0000 (16:10 +0100)
14 files changed:
src/lib/unlang/all.mk
src/lib/unlang/base.c
src/lib/unlang/call.c
src/lib/unlang/detach.c [new file with mode: 0644]
src/lib/unlang/interpret_priv.h
src/lib/unlang/module.c
src/lib/unlang/parallel.c
src/lib/unlang/subrequest.c
src/lib/unlang/subrequest.h
src/lib/unlang/subrequest_child.c [new file with mode: 0644]
src/lib/unlang/subrequest_child_priv.h [new file with mode: 0644]
src/lib/unlang/subrequest_priv.h
src/lib/unlang/unlang_priv.h
src/modules/rlm_eap/rlm_eap.c

index b4abc0a4d420ef727ff23c37ba00eef6ea1a8e7e..3b9eaa06d6634a11fd1a484c6554d1a6d92a5301 100644 (file)
@@ -5,6 +5,7 @@ SOURCES :=      base.c \
                caller.c \
                compile.c \
                condition.c \
+               detach.c \
                foreach.c \
                function.c \
                group.c \
@@ -17,6 +18,7 @@ SOURCES       :=      base.c \
                parallel.c \
                return.c \
                subrequest.c \
+               subrequest_child.c \
                switch.c \
                tmpl.c \
                xlat.c \
index 3bb5240c33462408d7cdcc3fc101489eb38ec6ee..77b458a8d4260ccc90fdc9e2be06ce69f2e5c8bf 100644 (file)
@@ -30,6 +30,10 @@ RCSID("$Id$")
 
 static uint32_t instance_count;
 
+/** Different operations the interpreter can execute
+ */
+unlang_op_t unlang_ops[UNLANG_TYPE_MAX];
+
 /** Return whether a section has unlang data associated with it
  *
  * @param[in] cs       to check.
@@ -92,6 +96,7 @@ int unlang_init_global(void)
        unlang_parallel_init();
        unlang_return_init();
        if (unlang_subrequest_op_init() < 0) return -1;
+       unlang_detach_init();
        unlang_switch_init();
        unlang_call_init();
        unlang_caller_init();
index d917683941ba4cdd4d95e5d570c174d47c672c7c..d45238c254114252444d0b1cbe64a46965dda015 100644 (file)
@@ -49,10 +49,6 @@ static unlang_action_t unlang_call_frame_init(rlm_rcode_t *p_result, request_t *
        g = unlang_generic_to_group(frame->instruction);
        gext = unlang_group_to_call(g);
 
-       /*
-        *      Push OUR subsection onto the childs stack frame.
-        */
-
        /*
         *      Work out the current request type.
         */
diff --git a/src/lib/unlang/detach.c b/src/lib/unlang/detach.c
new file mode 100644 (file)
index 0000000..e2d4722
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ *   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/detach.c
+ * @brief Unlang detach keyword
+ *
+ * @copyright 2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
+ */
+RCSID("$Id$")
+
+#include "unlang_priv.h"
+
+/** Signal a child to detach
+ *
+ */
+static unlang_action_t unlang_detach(rlm_rcode_t *p_result, request_t *request, UNUSED unlang_stack_frame_t *frame)
+{
+       /*
+        *      Signal all frames in the child's stack
+        *      that it's time to detach.
+        */
+       unlang_interpret_signal(request, FR_SIGNAL_DETACH);
+
+       /*
+        *      Detach failed...
+        */
+       if (unlikely(request->parent)) {
+               *p_result = RLM_MODULE_FAIL;
+               return UNLANG_ACTION_CALCULATE_RESULT;
+       }
+
+       return UNLANG_ACTION_CALCULATE_RESULT;
+}
+
+/** Initialise subrequest ops
+ *
+ */
+void unlang_detach_init(void)
+{
+       unlang_register(UNLANG_TYPE_DETACH,
+                       &(unlang_op_t){
+                               .name = "detach",
+                               .interpret = unlang_detach,
+                       });
+}
index 0ba456b094c0ffaf646ef3fde88eb348ba1b3415..e504fd93aa082e2c742379ffb04987fda23f8ad3 100644 (file)
 /**
  * $Id$
  *
- * @file unlang/interpret.h
- * @brief Declarations for the unlang interpreter.
+ * @file unlang/interpret_priv.h
+ * @brief Private declarations for the unlang interpreter.
  *
- * @copyright 2019 The FreeRADIUS server project
+ * @copyright 2021 The FreeRADIUS server project
  */
 
 #include <freeradius-devel/util/event.h>
 #include <freeradius-devel/unlang/interpret.h>
+#include "interpret_priv.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -37,7 +38,7 @@ struct unlang_interpret_s {
        void                    *uctx;
 };
 
-static inline void unlang_interpret_child_init(request_t *request)
+static inline void interpret_child_init(request_t *request)
 {
        unlang_interpret_t *intp = unlang_interpret_get(request);
 
index ebb277f91b520be295dd58b23a3fcffe02856b06..50550902cda846b220b50d8593152a6c965f303b 100644 (file)
@@ -418,38 +418,6 @@ request_t *unlang_module_subrequest_alloc(request_t *parent, fr_dict_t const *na
        return unlang_io_subrequest_alloc(parent, namespace, UNLANG_NORMAL_CHILD);
 }
 
-/** Yield, spawning a child request, and resuming once the child request is complete
- *
- * @param[in] p_result         Final rcode from when evaluation of the child request finishes.
- * @param[out] child           to yield to.  The child knows about the parent,
- *                             which is why the parent isn't passed explicitly.
- * @param[in] resume           function to call when the child has finished executing.
- * @param[in] signal           function to call if a signal is received.
- * @param[in] session          control values.  Whether we restore/store session info.
- * @param[in] rctx             to pass to the resume() and signal() callbacks.
- * @return
- *     - UNLANG_ACTION_YIELD.
- */
-unlang_action_t unlang_module_yield_to_subrequest(rlm_rcode_t *p_result, request_t *child,
-                                                 unlang_module_resume_t resume,
-                                                 unlang_module_signal_t signal,
-                                                 unlang_subrequest_session_t const *session,
-                                                 void *rctx)
-{
-       /*
-        *      Push the resumption point BEFORE adding the subrequest
-        *      to the parents stack.
-        */
-       (void) unlang_module_yield(child->parent, resume, signal, rctx);
-
-       /*
-        *      Push the subrequest and immediately run it.
-        */
-       if (unlang_subrequest_push(p_result, child, session, UNLANG_SUB_FRAME) < 0) return UNLANG_ACTION_STOP_PROCESSING;
-
-       return UNLANG_ACTION_YIELD;
-}
-
 /** 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
index 5a292756ec4e95e47ef5863f47cbd42f46877400..814f41259f9ccf5b633c1a6c006ca8789ff99ecd 100644 (file)
@@ -354,7 +354,7 @@ static unlang_action_t unlang_parallel_process(rlm_rcode_t *p_result, request_t
                         *      Detach the child, and insert
                         *      it into the backlog.
                         */
-                       if (unlang_detached_child_init(child) < 0) {
+                       if (unlang_subrequest_child_detach(child) < 0) {
                                talloc_free(child);
 
                                *p_result = RLM_MODULE_FAIL;
@@ -382,8 +382,9 @@ 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;
-                       unlang_interpret_child_init(child);
+
                }
+               interpret_child_init(child);
 
                /*
                 *      Push the first instruction for
index b174f446428fb460f06019c12c57759e8a9a1288..d39ada461e3eeb724aee500463178f8f4f2f4932 100644 (file)
@@ -28,197 +28,101 @@ RCSID("$Id$")
 #include "unlang_priv.h"
 #include "interpret_priv.h"
 #include "subrequest_priv.h"
-
-/** Parameters for initialising the subrequest
- *
- * State of one level of nesting within an xlat expansion.
- */
-typedef struct {
-       rlm_rcode_t                     *p_result;                      //!< Where to store the result.
-       request_t                       *child;                         //!< Pre-allocated child request.
-       bool                            free_child;                     //!< Whether we should free the child after
-                                                                       ///< it completes.
-       bool                            detachable;                     //!< Whether the request can be detached.
-       unlang_subrequest_session_t     session;                        //!< Session configuration.
-} unlang_frame_state_subrequest_t;
-
-static fr_dict_t const *dict_freeradius;
-
-extern fr_dict_autoload_t subrequest_dict[];
-fr_dict_autoload_t subrequest_dict[] = {
-       { .out = &dict_freeradius, .proto = "freeradius" },
-       { NULL }
-};
-
-static fr_dict_attr_t const *request_attr_request_lifetime;
-
-extern fr_dict_attr_autoload_t subrequest_dict_attr[];
-fr_dict_attr_autoload_t subrequest_dict_attr[] = {
-       { .out = &request_attr_request_lifetime, .name = "Request-Lifetime", .type = FR_TYPE_UINT32, .dict = &dict_freeradius },
-       { NULL }
-};
-
-typedef struct {
-       rlm_rcode_t     rcode;          //!< frame->result from before detach was called
-} unlang_frame_state_detach_t;
-
-static void unlang_max_request_time(UNUSED fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
-{
-       request_t *request = talloc_get_type_abort(uctx, request_t);
-
-       RDEBUG("Reached Request-Lifetime.  Forcibly stopping request");
-
-       /*
-        *      The request is scheduled and isn't running.  Remove it
-        *      from the backlog.
-        */
-       if (unlang_request_is_scheduled(request)) {
-               fr_assert(request->backlog != NULL);
-               (void) fr_heap_extract(request->backlog, request);
-       }
-
-       talloc_free(request);
-}
+#include "subrequest_child_priv.h"
 
 /** Send a signal from parent request to subrequest
  *
  */
-static void unlang_subrequest_signal(UNUSED request_t *request, unlang_stack_frame_t *frame, fr_state_signal_t action)
+static void unlang_subrequest_parent_signal(UNUSED request_t *request, unlang_stack_frame_t *frame,
+                                           fr_state_signal_t action)
 {
        unlang_frame_state_subrequest_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_subrequest_t);
        request_t                       *child = talloc_get_type_abort(state->child, request_t);
 
+       /*
+        *      Parent should never receive a detach
+        *      signal whilst the child is running.
+        *
+        *      Only the child receives a detach
+        *      signal when the detach keyword is used.
+        */
+       fr_assert(action != FR_SIGNAL_DETACH);
+
+       /*
+        *      Forward other signals to the child
+        */
        unlang_interpret_signal(child, action);
 }
 
-
-/** Process a subrequest until it either detaches, or is done.
+/** Parent being resumed after a child completes
  *
  */
-static unlang_action_t unlang_subrequest_process(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
+static unlang_action_t unlang_subrequest_parent_resume(rlm_rcode_t *p_result, request_t *request,
+                                                      unlang_stack_frame_t *frame)
 {
-       unlang_frame_state_subrequest_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_subrequest_t);
-       request_t                       *child = talloc_get_type_abort(state->child, request_t);
-       unlang_group_t                  *g = unlang_generic_to_group(frame->instruction);
-       rlm_rcode_t                     rcode;
-
-       fr_assert(child != NULL);
-
-       rcode = unlang_interpret(child);
-       if (rcode != RLM_MODULE_YIELD) {
-               if (!fr_cond_assert(rcode < NUM_ELEMENTS(frame->instruction->actions))) return UNLANG_ACTION_STOP_PROCESSING;
-
-               if (state->session.enable) fr_state_store_in_parent(child,
-                                                                   state->session.unique_ptr,
-                                                                   state->session.unique_int);
-
-               /*
-                *      Copy the subrequest's reply list into
-                *      the parent, creating any n
-                */
-
-       calculate_result:
-               if (child->reply) {
-                       unlang_subrequest_t     *gext;
-
-                       /*
-                        *      Copy reply attributes into the specified
-                        *      destination.
-                        */
-                       gext = unlang_group_to_subrequest(g);
-                       if (gext->dst) {
-                               tmpl_attr_extent_t      *extent = NULL;
-                               fr_dlist_head_t         leaf;
-                               fr_dlist_head_t         interior;
-
-                               fr_dlist_talloc_init(&leaf, tmpl_attr_extent_t, entry);
-                               fr_dlist_talloc_init(&interior, tmpl_attr_extent_t, entry);
-
-                               /*
-                                *      Find out what we need to build and build it
-                                */
-                               if ((tmpl_extents_find(state, &leaf, &interior, request, gext->dst) < 0) ||
-                                   (tmpl_extents_build_to_leaf(&leaf, &interior, gext->dst) < 0)) {
-                                       RPDEBUG("Discarding subrequest attributes - Failed allocating groups");
-                                       fr_dlist_talloc_free(&leaf);
-                                       fr_dlist_talloc_free(&interior);
-                                       goto done;
-                               }
-                               while ((extent = fr_dlist_tail(&leaf))) {
-                                       fr_pair_list_copy(extent->list_ctx, extent->list, &child->reply_pairs);
-                                       fr_dlist_talloc_free_tail(&leaf);
-                               }
-                       }
-               }
-
-       done:
-               if (state->free_child) {
-                       unlang_subrequest_free(&child);
-                       state->child = NULL;
-                       frame->signal = NULL;
-               }
-
-               /*
-                *      Pass the result back to the module
-                *      that created the subrequest, or
-                *      use it to modify the current section
-                *      rcode.
-                */
-               if (state->p_result) *state->p_result = rcode;
+       unlang_group_t                          *g = unlang_generic_to_group(frame->instruction);
+       unlang_frame_state_subrequest_t         *state = talloc_get_type_abort(frame->state,
+                                                                              unlang_frame_state_subrequest_t);
+       request_t                               *child = state->child;
+       unlang_subrequest_t                     *gext;
 
-               *p_result = rcode;
-
-               return UNLANG_ACTION_CALCULATE_RESULT;
-       }
+       RDEBUG3("Subrequest complete");
 
        /*
-        *      The child has yielded, BUT has also detached itself.
-        *      We know this because it has reached into our state,
-        *      and removed itself as a child.  We therefore just keep
-        *      running, and don't return yield.
+        *      Child detached
         */
        if (!state->child) {
-               rcode = RLM_MODULE_NOOP;
-               goto calculate_result;
+               RDEBUG3("Child has detached");
+               return UNLANG_ACTION_CALCULATE_RESULT;
        }
 
        /*
-        *      Else the child yielded, so we have to yield.  Set up
-        *      the resume frame and continue.
+        *      If there's a no destination tmpl, we're done.
         */
-       return UNLANG_ACTION_YIELD;
-}
-
-
-static unlang_action_t unlang_subrequest_start(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
-{
-       unlang_frame_state_subrequest_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_subrequest_t);
-       request_t                       *child = state->child;
+       if (!child->reply) {
+               unlang_subrequest_free(&child);
+               return UNLANG_ACTION_CALCULATE_RESULT;
+       }
 
        /*
-        *      Restore state from the parent to the
-        *      subrequest.
+        *      Otherwise... copy reply attributes into the
+        *      specified destination.
         */
-       if (state->session.enable) fr_state_restore_to_child(child,
-                                                            state->session.unique_ptr,
-                                                            state->session.unique_int);
+       gext = unlang_group_to_subrequest(g);
+       if (gext->dst) {
+               tmpl_attr_extent_t      *extent = NULL;
+               fr_dlist_head_t         leaf;
+               fr_dlist_head_t         interior;
 
-       REQUEST_VERIFY(child);
+               fr_dlist_talloc_init(&leaf, tmpl_attr_extent_t, entry);
+               fr_dlist_talloc_init(&interior, tmpl_attr_extent_t, entry);
 
-       RDEBUG2("Creating subrequest (%s)", child->name);
-       log_request_pair_list(L_DBG_LVL_1, request, NULL, &child->request_pairs, NULL);
+               /*
+                *      Find out what we need to build and build it
+                */
+               if ((tmpl_extents_find(state, &leaf, &interior, request, gext->dst) < 0) ||
+                   (tmpl_extents_build_to_leaf(&leaf, &interior, gext->dst) < 0)) {
+                       RPDEBUG("Discarding subrequest attributes - Failed allocating groups");
+                       fr_dlist_talloc_free(&leaf);
+                       fr_dlist_talloc_free(&interior);
+                       *p_result = RLM_MODULE_FAIL;
+                       return UNLANG_ACTION_CALCULATE_RESULT;
+               }
+               while ((extent = fr_dlist_tail(&leaf))) {
+                       fr_pair_list_copy(extent->list_ctx, extent->list, &child->reply_pairs);
+                       fr_dlist_talloc_free_tail(&leaf);
+               }
+       }
 
-       frame->process = unlang_subrequest_process;
-       return unlang_subrequest_process(p_result, request, frame);
+       unlang_subrequest_free(&child);
+       return UNLANG_ACTION_CALCULATE_RESULT;
 }
 
-
-static unlang_action_t unlang_subrequest_state_init(rlm_rcode_t *p_result, request_t *request,
-                                                   unlang_stack_frame_t *frame)
+static unlang_action_t unlang_subrequest_parent_init(rlm_rcode_t *p_result, request_t *request,
+                                                    unlang_stack_frame_t *frame)
 {
        unlang_frame_state_subrequest_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_subrequest_t);
        request_t                       *child;
-
        rlm_rcode_t                     rcode;
        fr_pair_t                       *vp;
 
@@ -255,7 +159,6 @@ static unlang_action_t unlang_subrequest_state_init(rlm_rcode_t *p_result, reque
         *      Set the packet type.
         */
        MEM(vp = fr_pair_afrom_da(child->request_ctx, gext->attr_packet_type));
-
        if (gext->type_enum) {
                child->packet->code = vp->vp_uint32 = gext->type_enum->value->vb_uint32;
        } else {
@@ -317,25 +220,20 @@ static unlang_action_t unlang_subrequest_state_init(rlm_rcode_t *p_result, reque
                }
        }
 
+       /*
+        *      Setup the child so it'll inform us when
+        *      it resumes, or if it detaches.
+        */
+       if (unlang_subrequest_child_push_resume(child, state) < 0) goto fail;
+
        /*
         *      Push the first instruction the child's
         *      going to run.
         */
        if (unlang_interpret_push(child, g->children, frame->result,
-                                 UNLANG_NEXT_SIBLING, UNLANG_TOP_FRAME) < 0) goto fail;
+                                 UNLANG_NEXT_SIBLING, UNLANG_SUB_FRAME) < 0) goto fail;
 
-       /*
-        *      Probably not a great idea to set state->p_result to
-        *      p_result, as it could be a pointer to an rlm_rcode_t
-        *      somewhere on the stack which could be invalidated
-        *      between unlang_subrequest being called and
-        *      unlang_subrequest_resume being called.
-        *
-        *      ...so we just set it to NULL and interpret
-        *      that as use the p_result that was passed
-        *      in to the currently executing function.
-        */
-       state->p_result = NULL;
+       state->p_result = p_result;
        state->free_child = true;
        state->detachable = true;
 
@@ -347,123 +245,9 @@ static unlang_action_t unlang_subrequest_state_init(rlm_rcode_t *p_result, reque
        state->session.unique_ptr = frame->instruction;
        state->session.unique_int = 0;
 
-       frame->process = unlang_subrequest_start;
-       return unlang_subrequest_start(p_result, request, frame);
-}
+       frame->process = unlang_subrequest_parent_resume;
 
-/** Initialize a detached child
- *
- *  Detach it from the parent, set up it's lifetime, and mark it as
- *  runnable.
- */
-int unlang_detached_child_init(request_t *request)
-{
-       fr_pair_t               *vp;
-
-       if (request_detach(request) < 0) {
-               ERROR("Failed detaching child");
-               return -1;
-       }
-
-       /*
-        *      Set Request Lifetime
-        */
-       vp = fr_pair_find_by_da(&request->control_pairs, request_attr_request_lifetime);
-       if (!vp || (vp->vp_uint32 > 0)) {
-               fr_time_delta_t when = 0;
-               const fr_event_timer_t **ev_p;
-
-               if (!vp) {
-                       when += fr_time_delta_from_sec(30); /* default to 30s if not set */
-
-               } else if (vp->vp_uint32 > 3600) {
-                       RWDEBUG("Request-Timeout can be no more than 3600 seconds");
-                       when += fr_time_delta_from_sec(3600);
-
-               } else if (vp->vp_uint32 < 5) {
-                       RWDEBUG("Request-Timeout can be no less than 5 seconds");
-                       when += fr_time_delta_from_sec(5);
-
-               } else {
-                       when += fr_time_delta_from_sec(vp->vp_uint32);
-               }
-
-               ev_p = talloc_size(request, sizeof(*ev_p));
-               memset(ev_p, 0, sizeof(*ev_p));
-
-               (void) fr_event_timer_in(request, request->el, ev_p, when, unlang_max_request_time, request);
-       }
-
-       /*
-        *      Mark the child as runnable.
-        */
-       fr_assert(request->parent == NULL);
-       unlang_interpret_child_init(request);
-
-       return 0;
-}
-
-static unlang_action_t unlang_detach(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
-{
-       unlang_frame_state_detach_t     *state = talloc_get_type_abort(frame->state, unlang_frame_state_detach_t);
-       unlang_stack_frame_t            *parent_frame;
-       unlang_frame_state_subrequest_t *parent_state;
-
-       /*
-        *      We've detached, yielded, and now are continuing
-        *      processing.  There's nothing more to do, so just
-        *      continue.
-        */
-       if (!request->parent) {
-               *p_result = state->rcode;
-               return UNLANG_ACTION_CALCULATE_RESULT;
-       }
-
-       /*
-        *      First time through, detach from the parent.
-        */
-       RDEBUG2("detach");
-
-       fr_assert(request->parent != NULL);
-
-       /*
-        *      Get the PARENT's stack.
-        */
-       parent_frame = frame_current(request->parent);
-       parent_state = talloc_get_type_abort(frame->state, unlang_frame_state_subrequest_t);
-
-       if (!parent_state->detachable) {
-               RWDEBUG("Ignoring 'detach' as the request is not detachable");
-               return UNLANG_ACTION_CALCULATE_RESULT;
-       }
-
-       /*
-        *      If we can't detach the child OR we can't insert it
-        *      into the backlog, stop processing it.
-        */
-       if (unlang_detached_child_init(request) < 0) {
-               return UNLANG_ACTION_STOP_PROCESSING;
-       }
-
-       /*
-        *      The parent frame no longer has a child, and therefore
-        *      can't be signaled.
-        */
-       fr_assert(parent_state->child == request);
-       parent_state->child = NULL;
-       parent_frame->signal = NULL;
-
-       /*
-        *      Pass through whatever the previous instruction had as
-        *      the result.
-        */
-       state->rcode = *p_result;
-
-       /*
-        *      Yield to the parent, who will discover that there's no
-        *      child, and return.
-        */
-       return UNLANG_ACTION_YIELD;
+       return unlang_subrequest_child_run(child);      /* returns UNLANG_ACTION_YIELD */
 }
 
 /** Free a child request, detaching it from its parent and freeing allocated memory
@@ -477,126 +261,27 @@ void unlang_subrequest_free(request_t **child)
        *child = NULL;
 }
 
-static unlang_group_t *subrequest_instruction;
-
-/** Push a pre-existing child back onto the stack as a subrequest
- *
- * The child *MUST* have been allocated with unlang_io_subrequest_alloc, or something
- * that calls it.
- * Instruction *MUST* belong to the same virtual server as is set in the child.
- *
- * After the child is no longer required it *MUST* be freed with #unlang_subrequest_free.
- * It's not enough to free it with talloc_free.
+/** Initialise subrequest ops
  *
- * @param[in] out              Where to write the result of the subrequest.
- * @param[in] child            to push.
- * @param[in] session          control values.  Whether we restore/store session info.
- * @param[in] top_frame                Set to UNLANG_TOP_FRAME if the interpreter should return.
- *                             Set to UNLANG_SUB_FRAME if the interprer should continue.
- * @return
- *     - 0 on success.
- *     - -1 on failure.
  */
-int unlang_subrequest_push(rlm_rcode_t *out, request_t *child,
-                          unlang_subrequest_session_t const *session, bool top_frame)
-{
-       unlang_stack_t                  *stack = child->parent->stack;
-       unlang_stack_frame_t            *frame;
-       unlang_frame_state_subrequest_t *state;
-
-       /*
-        *      Push a new subrequest frame onto the stack
-        */
-       if (unlang_interpret_push(child->parent, &subrequest_instruction->self,
-                                 RLM_MODULE_UNKNOWN, UNLANG_NEXT_STOP, top_frame) < 0) return -1;
-
-       frame = &stack->frame[stack->depth];
-
-       /*
-        *      Allocate a state for the subrequest
-        *      This lets us override the normal request
-        *      the subrequest instruction would alloc.
-        */
-       state = talloc_get_type_abort(frame->state, unlang_frame_state_subrequest_t);
-       state->p_result = out;
-       state->child = child;
-       state->free_child = false;
-       state->detachable = false;
-       if (session) state->session = *session;
-
-       frame->process = unlang_subrequest_start;
-
-       return 0;
-}
-
 int unlang_subrequest_op_init(void)
 {
-       unlang_subrequest_t     *gctx;
-
-       if (fr_dict_autoload(subrequest_dict) < 0) {
-               PERROR("%s", __FUNCTION__);
-               return -1;
-       }
-       if (fr_dict_attr_autoload(subrequest_dict_attr) < 0) {
-               PERROR("%s", __FUNCTION__);
-               return -1;
-       }
-
-       /*
-        *      Needs to be dynamically allocated
-        *      so that talloc_get_type works
-        *      correctly.
-        */
-       gctx = talloc(NULL, unlang_subrequest_t);
-       if (!gctx) {
-               ERROR("%s: Out of memory", __FUNCTION__);
-               return -1;
-       }
-       *gctx = (unlang_subrequest_t){
-               .group = {
-                       .self = {
-                               .type = UNLANG_TYPE_SUBREQUEST,
-                               .name = "subrequest",
-                               .debug_name = "subrequest",
-                               .actions = {
-                                       [RLM_MODULE_REJECT]     = 0,
-                                       [RLM_MODULE_FAIL]       = MOD_ACTION_RETURN,    /* Exit out of nested levels */
-                                       [RLM_MODULE_OK]         = 0,
-                                       [RLM_MODULE_HANDLED]    = 0,
-                                       [RLM_MODULE_INVALID]    = 0,
-                                       [RLM_MODULE_DISALLOW]   = 0,
-                                       [RLM_MODULE_NOTFOUND]   = 0,
-                                       [RLM_MODULE_NOOP]       = 0,
-                                       [RLM_MODULE_UPDATED]    = 0
-                               }
-                       }
-               }
-       };
-
-       subrequest_instruction = unlang_subrequest_to_group(gctx);
        unlang_register(UNLANG_TYPE_SUBREQUEST,
-                          &(unlang_op_t){
+                       &(unlang_op_t){
                                .name = "subrequest",
-                               .interpret = unlang_subrequest_state_init,
-                               .signal = unlang_subrequest_signal,
+                               .interpret = unlang_subrequest_parent_init,
+                               .signal = unlang_subrequest_parent_signal,
                                .debug_braces = true,
                                .frame_state_size = sizeof(unlang_frame_state_subrequest_t),
                                .frame_state_name = "unlang_frame_state_subrequest_t",
-                          });
+                       });
 
-       unlang_register(UNLANG_TYPE_DETACH,
-                          &(unlang_op_t){
-                               .name = "detach",
-                               .interpret = unlang_detach,
-                               .frame_state_size = sizeof(unlang_frame_state_detach_t),
-                               .frame_state_name = "unlang_frame_state_detach_t",
-                          });
+       if (unlang_subrequest_child_op_init() < 0) return -1;
 
        return 0;
 }
 
 void unlang_subrequest_op_free(void)
 {
-       talloc_free(subrequest_instruction);
-       fr_dict_autofree(subrequest_dict);
+       unlang_subrequest_child_op_free();
 }
index 51b8016e73aeb5cc86e54fd8dfee74688119acd1..c530286663df3b7016b7d8bc4d36cde2d9897929 100644 (file)
  * @file unlang/subrequest.h
  *
  * @copyright 2019 The FreeRADIUS server project
+ * @copyright 2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
  */
 #ifdef __cplusplus
 extern "C" {
 #endif
 
+#include <freeradius-devel/server/request.h>
+#include <freeradius-devel/server/rcode.h>
+#include <stdbool.h>
+
 typedef struct {
        bool            enable;                         //!< Whether we should store/restore sessions.
        void const      *unique_ptr;                    //!< Session unique ptr identifier.
        int             unique_int;                     //!< Session unique int identifier.
 } unlang_subrequest_session_t;
 
+unlang_action_t        unlang_subrequest_child_run(request_t *child);
+
+int            unlang_subrequest_child_detach(request_t *request);
+
+int            unlang_subrequest_child_push(rlm_rcode_t *out, request_t *child,
+                                            unlang_subrequest_session_t const *session,
+                                            bool top_frame);
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/lib/unlang/subrequest_child.c b/src/lib/unlang/subrequest_child.c
new file mode 100644 (file)
index 0000000..2c8dce5
--- /dev/null
@@ -0,0 +1,360 @@
+/*
+ *   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/subrequest.c
+ * @brief Unlang "subrequest" and "detach" keyword evaluation.
+ *
+ * @copyright 2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
+ */
+#include <freeradius-devel/server/state.h>
+#include "interpret_priv.h"
+#include "subrequest_child_priv.h"
+#include "subrequest_priv.h"
+#include "unlang_priv.h"
+
+/** Holds a synthesised instruction that we insert into the parent request
+ *
+ */
+static unlang_subrequest_t     *subrequest_instruction;
+
+static fr_dict_t const *dict_freeradius;
+
+extern fr_dict_autoload_t subrequest_dict[];
+fr_dict_autoload_t subrequest_dict[] = {
+       { .out = &dict_freeradius, .proto = "freeradius" },
+       { NULL }
+};
+
+static fr_dict_attr_t const *request_attr_request_lifetime;
+
+extern fr_dict_attr_autoload_t subrequest_dict_attr[];
+fr_dict_attr_autoload_t subrequest_dict_attr[] = {
+       { .out = &request_attr_request_lifetime, .name = "Request-Lifetime", .type = FR_TYPE_UINT32, .dict = &dict_freeradius },
+       { NULL }
+};
+
+/** Event handler to free a detached child
+ *
+ */
+static void unlang_detached_max_request_time(UNUSED fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
+{
+       request_t *request = talloc_get_type_abort(uctx, request_t);
+
+       RDEBUG("Reached Request-Lifetime.  Forcibly stopping request");
+
+       /*
+        *      The request is scheduled and isn't running.  Remove it
+        *      from the backlog.
+        */
+       if (unlang_request_is_scheduled(request)) {
+               fr_assert(request->backlog != NULL);
+               (void) fr_heap_extract(request->backlog, request);
+       }
+
+       talloc_free(request);
+}
+
+/** Initialize a detached child
+ *
+ *  Detach it from the parent, set up it's lifetime, and mark it as
+ *  runnable.
+ */
+int unlang_subrequest_child_detach(request_t *request)
+{
+       fr_pair_t               *vp;
+
+       if (request_detach(request) < 0) {
+               ERROR("Failed detaching child");
+               return -1;
+       }
+
+       /*
+        *      Set Request Lifetime
+        */
+       vp = fr_pair_find_by_da(&request->control_pairs, request_attr_request_lifetime);
+       if (!vp || (vp->vp_uint32 > 0)) {
+               fr_time_delta_t when = 0;
+               const fr_event_timer_t **ev_p;
+
+               if (!vp) {
+                       when += fr_time_delta_from_sec(30); /* default to 30s if not set */
+
+               } else if (vp->vp_uint32 > 3600) {
+                       RWDEBUG("Request-Timeout can be no more than 3600 seconds");
+                       when += fr_time_delta_from_sec(3600);
+
+               } else if (vp->vp_uint32 < 5) {
+                       RWDEBUG("Request-Timeout can be no less than 5 seconds");
+                       when += fr_time_delta_from_sec(5);
+
+               } else {
+                       when += fr_time_delta_from_sec(vp->vp_uint32);
+               }
+
+               ev_p = talloc_size(request, sizeof(*ev_p));
+               memset(ev_p, 0, sizeof(*ev_p));
+
+               if (fr_event_timer_in(request, request->el, ev_p, when,
+                                     unlang_detached_max_request_time, request) < 0) {
+                       talloc_free(ev_p);
+                       return -1;
+               }
+       }
+
+       return 0;
+}
+
+/** Process a detach signal in the child
+ *
+ * This processes any detach signals the child receives
+ * The child doesn't actually do the detaching
+ */
+static void unlang_subrequest_child_signal(request_t *request, fr_state_signal_t action, UNUSED void *uctx)
+{
+       unlang_frame_state_subrequest_t *state;
+
+       /*
+        *      Ignore signals which aren't detach,
+        *      and ignore the signal if we have no parent.
+        */
+       if ((action != FR_SIGNAL_DETACH) || !request->parent) return;
+
+       state = talloc_get_type_abort(frame_current(request->parent), unlang_frame_state_subrequest_t);
+
+       /*
+        *      Ignore signals if the parent didn't want the child
+        *      to be detached.
+        */
+       if (!state->detachable) return;
+
+       /*
+        *      Place child's state back inside the parent
+        */
+       if (state->session.enable) fr_state_store_in_parent(request,
+                                                           state->session.unique_ptr,
+                                                           state->session.unique_int);
+
+       if (!fr_cond_assert(unlang_subrequest_child_detach(request) == 0)) {
+               REDEBUG("Child could not be detached");
+               return;
+       }
+
+       /*
+        *      Indicate to the parent there's no longer a child
+        */
+       state->child = NULL;
+
+       /*
+        *      Tell the parent to resume
+        */
+       unlang_interpret_mark_runnable(request->parent);
+}
+
+/** When the child is done, tell the parent that we've exited.
+ *
+ * This is pushed as a frame at the top of the child's stack, so when
+ * the child is done executing, it runs this to inform the parent
+ * that its done.
+ */
+static unlang_action_t unlang_subrequest_child_done(rlm_rcode_t *p_result,
+                                                   UNUSED int *p_priority, request_t *request, void *uctx)
+{
+       unlang_frame_state_subrequest_t *state = talloc_get_type_abort(uctx, unlang_frame_state_subrequest_t);
+
+       /*
+        *      Child was detached, nothing more to do.
+        */
+       if (!request->parent) return UNLANG_ACTION_CALCULATE_RESULT;
+
+       /*
+        *      Place child state back inside the parent
+        */
+       if (state->session.enable) fr_state_store_in_parent(request,
+                                                           state->session.unique_ptr,
+                                                           state->session.unique_int);
+       /*
+        *      Record the child's result
+        */
+       if (state->p_result) *state->p_result = *p_result;
+
+       /*
+        *      Resume the parent
+        */
+       unlang_interpret_mark_runnable(request->parent);
+
+       return UNLANG_ACTION_CALCULATE_RESULT;
+}
+
+/** Push a resumption frame onto a child's stack
+ *
+ * This is necessary so that the child informs its parent when it's done/detached
+ * and so that the child responds to detach signals.
+ */
+int unlang_subrequest_child_push_resume(request_t *child, unlang_frame_state_subrequest_t *state)
+{
+       /*
+        *      Push a resume frame into the child
+        */
+       if (unlang_interpret_push_function(child, NULL,
+                                          unlang_subrequest_child_done,
+                                          unlang_subrequest_child_signal, UNLANG_TOP_FRAME, state) < 0) return -1;
+
+       return_point_set(frame_current(child)); /* Stop return going through the resumption frame */
+
+       return 0;
+}
+
+/** Function to call when the parent is ready to execute the child
+ *
+ * @param[in] child to begin executing.
+ */
+unlang_action_t unlang_subrequest_child_run(request_t *child)
+{
+       unlang_frame_state_subrequest_t *state;
+
+       /*
+        *      No parent means this is a pre-detached child
+        *      so the parent should continue executing.
+        */
+       if (!child->parent) return UNLANG_ACTION_CALCULATE_RESULT;
+
+       state = talloc_get_type_abort(frame_current(child->parent)->state, unlang_frame_state_subrequest_t);
+
+       /*
+        *      Ensure we restore the session state information
+        *      into the child.
+        */
+       if (state->session.enable) fr_state_restore_to_child(child,
+                                                            state->session.unique_ptr,
+                                                            state->session.unique_int);
+       /*
+        *      Ensures the child is setup correctly and adds
+        *      it into the runnable queue of whatever owns
+        *      the interpreter.
+        */
+       interpret_child_init(child);
+
+       return UNLANG_ACTION_YIELD;
+}
+
+/** Push a pre-existing child back onto the stack as a subrequest
+ *
+ * The child *MUST* have been allocated with unlang_io_subrequest_alloc, or something
+ * that calls it.
+ *
+ * After the child is no longer required it *MUST* be freed with #unlang_subrequest_free.
+ * It's not enough to free it with talloc_free.
+ *
+ * This function should be called _before_ pushing any additional frames onto the child's
+ * stack for it to execute.
+ *
+ * @param[in] out              Where to write the result of the subrequest.
+ * @param[in] child            to push.
+ * @param[in] session          control values.  Whether we restore/store session info.
+ * @param[in] top_frame                Set to UNLANG_TOP_FRAME if the interpreter should return.
+ *                             Set to UNLANG_SUB_FRAME if the interprer should continue.
+ * @return
+ *     - 0 on success.
+ *     - -1 on failure.
+ */
+int unlang_subrequest_child_push(rlm_rcode_t *out, request_t *child,
+                                unlang_subrequest_session_t const *session,
+                                bool top_frame)
+{
+       unlang_frame_state_subrequest_t *state;
+
+       /*
+        *      Push a new subrequest frame onto the stack
+        *
+        *      This allocates memory for the frame state
+        *      which we fill in below.
+        */
+       if (unlang_interpret_push(child->parent, &subrequest_instruction->group.self,
+                                 RLM_MODULE_UNKNOWN, UNLANG_NEXT_STOP, top_frame) < 0) {
+               return -1;
+       }
+
+       /*
+        *      Setup the state for the subrequest
+        */
+       state = talloc_get_type_abort(frame_current(child->parent)->state, unlang_frame_state_subrequest_t);
+       state->p_result = out;
+       state->child = child;
+       state->session = *session;
+
+       /*
+        *      Push a resumption frame onto the stack
+        *      so the child calls its parent when it's
+        *      complete.
+        */
+       if (unlang_subrequest_child_push_resume(child, state) < 0) return -1;
+
+       return 0;
+}
+
+int unlang_subrequest_child_op_init(void)
+{
+       if (fr_dict_autoload(subrequest_dict) < 0) {
+               PERROR("%s", __FUNCTION__);
+               return -1;
+       }
+       if (fr_dict_attr_autoload(subrequest_dict_attr) < 0) {
+               PERROR("%s", __FUNCTION__);
+               return -1;
+       }
+
+       /*
+        *      Needs to be dynamically allocated
+        *      so that talloc_get_type works
+        *      correctly.
+        */
+       subrequest_instruction = talloc(NULL, unlang_subrequest_t);
+       if (!subrequest_instruction) {
+               ERROR("%s: Out of memory", __FUNCTION__);
+               return -1;
+       }
+       *subrequest_instruction = (unlang_subrequest_t){
+               .group = {
+                       .self = {
+                               .type = UNLANG_TYPE_SUBREQUEST,
+                               .name = "subrequest",
+                               .debug_name = "subrequest",
+                               .actions = {
+                                       [RLM_MODULE_REJECT]     = 0,
+                                       [RLM_MODULE_FAIL]       = 0,
+                                       [RLM_MODULE_OK]         = 0,
+                                       [RLM_MODULE_HANDLED]    = 0,
+                                       [RLM_MODULE_INVALID]    = 0,
+                                       [RLM_MODULE_DISALLOW]   = 0,
+                                       [RLM_MODULE_NOTFOUND]   = 0,
+                                       [RLM_MODULE_NOOP]       = 0,
+                                       [RLM_MODULE_UPDATED]    = 0
+                               }
+                       }
+               }
+       };
+
+       return 0;
+}
+
+void unlang_subrequest_child_op_free(void)
+{
+       fr_dict_autofree(subrequest_dict);
+       talloc_free(subrequest_instruction);
+}
diff --git a/src/lib/unlang/subrequest_child_priv.h b/src/lib/unlang/subrequest_child_priv.h
new file mode 100644 (file)
index 0000000..6023833
--- /dev/null
@@ -0,0 +1,40 @@
+#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/subrequest_child_priv.h
+ *
+ * @copyright 2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
+ */
+#include <freeradius-devel/server/request.h>
+#include "subrequest_priv.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int    unlang_subrequest_child_push_resume(request_t *child, unlang_frame_state_subrequest_t *state);
+
+int    unlang_subrequest_child_op_init(void);
+
+void   unlang_subrequest_child_op_free(void);
+
+#ifdef __cplusplus
+}
+#endif
index 9bc62ff06706b1c3f6204316de227a1cf07ffc02..ebc16fd8b764aba8243ac86133ed4e6723fd67cb 100644 (file)
@@ -28,6 +28,7 @@ extern "C" {
 
 #include <freeradius-devel/server/tmpl.h>
 #include <freeradius-devel/util/dict.h>
+#include "unlang_priv.h"
 
 typedef struct {
        unlang_group_t          group;
@@ -44,6 +45,18 @@ typedef struct {
                                                        ///< if the packet-type is static.
 } unlang_subrequest_t;
 
+/** Parameters for initialising the subrequest (parent's frame state)
+ *
+ */
+typedef struct {
+       rlm_rcode_t                     *p_result;                      //!< Where to store the result.
+       request_t                       *child;                         //!< Pre-allocated child request.
+       bool                            free_child;                     //!< Whether we should free the child after
+                                                                       ///< it completes.
+       bool                            detachable;                     //!< Whether the request can be detached.
+       unlang_subrequest_session_t     session;                        //!< Session configuration.
+} unlang_frame_state_subrequest_t;
+
 /** Cast a group structure to the subrequest keyword extension
  *
  */
@@ -62,11 +75,11 @@ static inline unlang_group_t *unlang_subrequest_to_group(unlang_subrequest_t *su
 
 void   unlang_subrequest_free(request_t **child);
 
-int unlang_subrequest_push(rlm_rcode_t *out, request_t *child,
+int unlang_subrequest_child_push(rlm_rcode_t *out, request_t *child,
                           unlang_subrequest_session_t const *session, bool top_frame)
                           CC_HINT(warn_unused_result);
 
-int unlang_detached_child_init(request_t *request);
+int unlang_subrequest_detach_child(request_t *request);
 
 #ifdef __cplusplus
 }
index a4818f98fa5fece71459625e17ce4ee6a2164d14..376c31128bf93b0c03611b3c215cd4ba30338333 100644 (file)
@@ -512,6 +512,8 @@ int         unlang_subrequest_op_init(void);
 
 void           unlang_subrequest_op_free(void);
 
+void           unlang_detach_init(void);
+
 void           unlang_switch_init(void);
 
 void           unlang_tmpl_init(void);
index b3b3f32331b310d043ecabbb2c62a2abda080d23..d4d110d64a856d602964fcfa050f98736a78d1e0 100644 (file)
@@ -351,7 +351,6 @@ static void mod_authenticate_cancel(UNUSED module_ctx_t const *mctx, request_t *
 
        eap_session = talloc_get_type_abort(rctx, eap_session_t);
 
-       (void)fr_cond_assert(request_detach(eap_session->subrequest) == 0);
        TALLOC_FREE(eap_session->subrequest);
 
        /*
@@ -382,7 +381,6 @@ static unlang_action_t mod_authenticate_result(rlm_rcode_t *p_result, UNUSED mod
        /*
         *      Cleanup the subrequest
         */
-       (void)fr_cond_assert(request_detach(eap_session->subrequest) == 0);
        TALLOC_FREE(eap_session->subrequest);
 
        /*
@@ -625,13 +623,36 @@ static unlang_action_t eap_method_select(rlm_rcode_t *p_result, module_ctx_t con
        }
 
        /*
-        *      Push the submodule into the child's stack
+        *      Push a resumption frame for the parent
+        *      This will get executed when the child is
+        *      done (after the subrequest frame in the
+        *      parent gets popped).
         */
-       if (unlang_module_push(NULL,    /* rcode should bubble up and be returned by yield_to_subrequest */
-                              eap_session->subrequest, method->submodule_inst, eap_session->process, true) < 0) {
+       (void)unlang_module_yield(request, mod_authenticate_result_async, mod_authenticate_cancel, eap_session);
+
+       /*
+        *      This sets up a subrequest frame in the parent
+        *      and a resumption frame in the child.
+        *
+        *      This must be done before pushing frames onto
+        *      the child's stack.
+        */
+       if (unlang_subrequest_child_push(&eap_session->submodule_rcode, eap_session->subrequest,
+                                        &(unlang_subrequest_session_t){ .enable = true, .unique_ptr = eap_session },
+                                        UNLANG_SUB_FRAME) < 0) {
+       child_fail:
+               unlang_interpet_frame_discard(request); /* Ensure the yield frame doesn't stick around */
                goto fail;
        }
 
+       /*
+        *      Push the EAP submodule into the child's stack
+        */
+       if (unlang_module_push(NULL,    /* rcode should bubble up and be set in eap_session->submodule_rcode */
+                              eap_session->subrequest, method->submodule_inst, eap_session->process, true) < 0) {
+               goto child_fail;
+       }
+
        if (eap_session->identity) {
                fr_pair_t       *identity;
 
@@ -653,13 +674,7 @@ static unlang_action_t eap_method_select(rlm_rcode_t *p_result, module_ctx_t con
                type_vp->vp_uint32 = eap_session->type;
        }
 
-       /*
-        *      Yield to the subrequest, and start executing it
-        */
-       return unlang_module_yield_to_subrequest(&eap_session->submodule_rcode, eap_session->subrequest,
-                                                mod_authenticate_result_async, mod_authenticate_cancel,
-                                                &(unlang_subrequest_session_t){ .enable = true, .unique_ptr = eap_session },
-                                                eap_session);
+       return unlang_subrequest_child_run(eap_session->subrequest);
 }
 
 static unlang_action_t mod_authenticate(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)