]> git.ipfire.org Git - thirdparty/freeradius-server.git/commitdiff
add timeout keyword
authorAlan T. DeKok <aland@freeradius.org>
Mon, 14 Nov 2022 15:09:13 +0000 (10:09 -0500)
committerAlan T. DeKok <aland@freeradius.org>
Mon, 14 Nov 2022 20:01:04 +0000 (15:01 -0500)
src/lib/unlang/all.mk
src/lib/unlang/base.c
src/lib/unlang/compile.c
src/lib/unlang/timeout.c [new file with mode: 0644]
src/lib/unlang/timeout_priv.h [new file with mode: 0644]
src/lib/unlang/unlang_priv.h
src/tests/keywords/timeout [new file with mode: 0644]

index de66bda66211e5444ec99ccc4b12672733e44f49..1498d1dcd3cfd97129ac0d6a89ce41ebfd424e51 100644 (file)
@@ -21,6 +21,7 @@ SOURCES       :=      base.c \
                subrequest.c \
                subrequest_child.c \
                switch.c \
+               timeout.c \
                tmpl.c \
                xlat.c \
                xlat_builtin.c \
index 3a83b1c83849768f1afa5432b241bbe1ded34ad1..ad73c6b20315cdb25152f3772592c2d25b17bd8a 100644 (file)
@@ -101,6 +101,7 @@ int unlang_init_global(void)
        unlang_caller_init();
        unlang_tmpl_init();
        unlang_edit_init();
+       unlang_timeout_init();
 
        instance_count++;
 
index b7f34b59691387e9fad14bfd7cb21ac55a40fbc4..ab314ecabfd57feed7aa22e75e3c4d2fad390291 100644 (file)
@@ -40,6 +40,7 @@ RCSID("$Id$")
 #include "subrequest_priv.h"
 #include "switch_priv.h"
 #include "edit_priv.h"
+#include "timeout_priv.h"
 
 #define UNLANG_IGNORE ((unlang_t *) -1)
 
@@ -723,10 +724,11 @@ static void unlang_dump(unlang_t *instruction, int depth)
                case UNLANG_TYPE_LOAD_BALANCE:
                case UNLANG_TYPE_PARALLEL:
                case UNLANG_TYPE_POLICY:
-               case UNLANG_TYPE_SUBREQUEST:
-               case UNLANG_TYPE_SWITCH:
                case UNLANG_TYPE_REDUNDANT:
                case UNLANG_TYPE_REDUNDANT_LOAD_BALANCE:
+               case UNLANG_TYPE_SUBREQUEST:
+               case UNLANG_TYPE_SWITCH:
+               case UNLANG_TYPE_TIMEOUT:
                        g = unlang_generic_to_group(c);
                        DEBUG("%.*s%s {", depth, unlang_spaces, c->debug_name);
                        unlang_dump(g->children, depth + 1);
@@ -1992,6 +1994,7 @@ static bool compile_action_subsection(unlang_t *c, CONF_SECTION *cs, CONF_SECTIO
        case UNLANG_TYPE_ELSE:
        case UNLANG_TYPE_ELSIF:
        case UNLANG_TYPE_GROUP:
+       case UNLANG_TYPE_TIMEOUT:
                break;
 
        default:
@@ -2731,6 +2734,54 @@ static unlang_t *compile_case(unlang_t *parent, unlang_compile_t *unlang_ctx, CO
        return c;
 }
 
+static unlang_t *compile_timeout(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_SECTION *cs)
+{
+       char const              *name2;
+       unlang_t                *c;
+       unlang_group_t          *g;
+       unlang_timeout_t        *gext;
+       fr_time_delta_t         timeout;
+
+       static unlang_ext_t const timeout_ext = {
+               .type = UNLANG_TYPE_TIMEOUT,
+               .len = sizeof(unlang_timeout_t),
+               .type_name = "unlang_timeout_t",
+       };
+
+       /*
+        *      Timeout <time ref>
+        */
+       name2 = cf_section_name2(cs);
+       if (!name2) {
+               cf_log_err(cs, "You must specify a time value for 'timeout'");
+               return NULL;
+       }
+
+       if (!cf_item_next(cs, NULL)) return UNLANG_IGNORE;
+
+       g = group_allocate(parent, cs, &timeout_ext);
+       if (!g) return NULL;
+
+       gext = unlang_group_to_timeout(g);
+
+       if (fr_time_delta_from_str(&timeout, name2, strlen(name2), FR_TIME_RES_SEC) < 0) {
+               cf_log_err(cs, "Failed parsing time delta %s - %s",
+                          name2, fr_strerror());
+               return NULL;
+       }
+       /*
+        *      Compile the contents of a "timeout".
+        */
+       c = compile_section(parent, unlang_ctx, cs, &timeout_ext);
+       if (!c) return NULL;
+
+       g = unlang_generic_to_group(c);
+       gext = unlang_group_to_timeout(g);
+       gext->timeout = timeout;
+
+       return c;
+}
+
 static unlang_t *compile_foreach(unlang_t *parent, unlang_compile_t *unlang_ctx, CONF_SECTION *cs)
 {
        fr_token_t              type;
@@ -4107,6 +4158,7 @@ static fr_table_ptr_sorted_t unlang_section_keywords[] = {
        { L("redundant-load-balance"), (void *) compile_redundant_load_balance },
        { L("subrequest"),      (void *) compile_subrequest },
        { L("switch"),          (void *) compile_switch },
+       { L("timeout"),         (void *) compile_timeout },
        { L("update"),          (void *) compile_update },
 };
 static int unlang_section_keywords_len = NUM_ELEMENTS(unlang_section_keywords);
diff --git a/src/lib/unlang/timeout.c b/src/lib/unlang/timeout.c
new file mode 100644 (file)
index 0000000..ac67167
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ *   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/timeout.c
+ * @brief Unlang "timeout" keyword evaluation.
+ *
+ * @copyright 2022 Network RADIUS SAS (legal@networkradius.com)
+ */
+RCSID("$Id$")
+
+#include "group_priv.h"
+#include "timeout_priv.h"
+
+typedef struct {
+       bool                    success;
+       int                     depth;
+       request_t               *request;
+       rindent_t               indent;
+       fr_event_timer_t const  *ev;
+} unlang_frame_state_timeout_t;
+
+static void unlang_timeout_handler(UNUSED fr_event_list_t *el, UNUSED fr_time_t now, void *ctx)
+{
+       unlang_frame_state_timeout_t    *state = talloc_get_type_abort(ctx, unlang_frame_state_timeout_t);
+       request_t                       *request = talloc_get_type_abort(state->request, request_t);
+
+       RDEBUG("Timeout reached, signalling interpreter to cancel child section.");
+
+       /*
+        *      Has to be done BEFORE cancelling the frames, as one might be yielded.
+        */
+       unlang_interpret_mark_runnable(request);
+
+       /*
+        *      Signal all lower frames to exit.
+        */
+       unlang_frame_signal(request, FR_SIGNAL_CANCEL, state->depth);
+       state->success = false;
+}
+
+static unlang_action_t unlang_timeout_resume_done(UNUSED rlm_rcode_t *p_result, UNUSED request_t *request, unlang_stack_frame_t *frame)
+{
+       unlang_frame_state_timeout_t    *state = talloc_get_type_abort(frame->state, unlang_frame_state_timeout_t);
+
+       if (!state->success) {
+               RINDENT_RESTORE(request, &state->indent);
+
+               RWDEBUG("Timeout exceeded");
+               return UNLANG_ACTION_FAIL;
+       }
+
+       RWDEBUG("Timeout did not fire");
+       return UNLANG_ACTION_CALCULATE_RESULT;
+}
+
+
+static unlang_action_t unlang_timeout(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
+{
+       unlang_group_t                  *g;
+       unlang_timeout_t                *gext;
+       unlang_frame_state_timeout_t    *state = talloc_get_type_abort(frame->state, unlang_frame_state_timeout_t);
+       fr_time_t                       timeout;
+       unlang_stack_t                  *stack = request->stack;
+
+       g = unlang_generic_to_group(frame->instruction);
+       gext = unlang_group_to_timeout(g);
+
+       state->depth = stack->depth;
+       state->request = request;
+
+       /*
+        *      Save current indentation for the error path.
+        */
+       RINDENT_SAVE(&state->indent, request);
+
+       timeout = fr_time_add(fr_time(), gext->timeout);
+
+       if (fr_event_timer_at(state, unlang_interpret_event_list(request), &state->ev, timeout,
+                             unlang_timeout_handler, state) < 0) {
+               RPEDEBUG("Failed inserting event");
+               goto fail;
+       }
+
+       if (unlang_interpret_push(request, g->children, frame->result, UNLANG_NEXT_STOP, UNLANG_SUB_FRAME) < 0) {
+       fail:
+               *p_result = RLM_MODULE_FAIL;
+               return UNLANG_ACTION_STOP_PROCESSING;
+       }
+
+       frame_repeat(frame, unlang_timeout_resume_done);
+       state->success = true;
+
+       return UNLANG_ACTION_PUSHED_CHILD;
+}
+
+
+void unlang_timeout_init(void)
+{
+       unlang_register(UNLANG_TYPE_TIMEOUT,
+                          &(unlang_op_t){
+                               .name = "timeout",
+                               .interpret = unlang_timeout,
+                               .debug_braces = true,
+                               .frame_state_size = sizeof(unlang_frame_state_timeout_t),
+                               .frame_state_type = "unlang_frame_state_timeout_t",
+                          });
+}
diff --git a/src/lib/unlang/timeout_priv.h b/src/lib/unlang/timeout_priv.h
new file mode 100644 (file)
index 0000000..7c953e0
--- /dev/null
@@ -0,0 +1,54 @@
+#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/timeout_priv.h
+ *
+ * @copyright 2022 Network RADIUS SAS (legal@networkradius.com)
+ */
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <freeradius-devel/server/tmpl.h>
+
+typedef struct {
+       unlang_group_t  group;
+       fr_time_delta_t timeout;
+} unlang_timeout_t;
+
+/** Cast a group structure to the timeout keyword extension
+ *
+ */
+static inline unlang_timeout_t *unlang_group_to_timeout(unlang_group_t *g)
+{
+       return talloc_get_type_abort(g, unlang_timeout_t);
+}
+
+/** Cast a timeout keyword extension to a group structure
+ *
+ */
+static inline unlang_group_t *unlang_timeout_to_group(unlang_timeout_t *to)
+{
+       return (unlang_group_t *)to;
+}
+
+#ifdef __cplusplus
+}
+#endif
index e97464b292c1d62c5f5e59fc60ec2feb5ccecfac..3fc80d026bbdb291ab5e4c9f89a04db05eb4b5f1 100644 (file)
@@ -76,6 +76,7 @@ typedef enum {
        UNLANG_TYPE_DETACH,                     //!< detach a child
        UNLANG_TYPE_CALL,                       //!< call another virtual server
        UNLANG_TYPE_CALLER,                     //!< conditionally check parent dictionary type
+       UNLANG_TYPE_TIMEOUT,                    //!< time-based timeouts.
        UNLANG_TYPE_POLICY,                     //!< Policy section.
        UNLANG_TYPE_XLAT,                       //!< Represents one level of an xlat expansion.
        UNLANG_TYPE_TMPL,                       //!< asynchronously expand a tmpl_t
@@ -614,6 +615,8 @@ void                unlang_switch_init(void);
 void           unlang_tmpl_init(void);
 
 void           unlang_edit_init(void);
+
+void           unlang_timeout_init(void);
  /** @} */
 
 #ifdef __cplusplus
diff --git a/src/tests/keywords/timeout b/src/tests/keywords/timeout
new file mode 100644 (file)
index 0000000..5582388
--- /dev/null
@@ -0,0 +1,16 @@
+#
+# PRE: xlat-delay
+#
+
+#
+#  Set a timeout which will fire, and cause the block to fail.
+#
+redundant {
+       timeout 0.01s {
+               &Tmp-Float-0 := "%(delay_10s:0.1)"
+       }
+
+       group {
+               success
+       }
+}