subrequest.c \
subrequest_child.c \
switch.c \
+ timeout.c \
tmpl.c \
xlat.c \
xlat_builtin.c \
unlang_caller_init();
unlang_tmpl_init();
unlang_edit_init();
+ unlang_timeout_init();
instance_count++;
#include "subrequest_priv.h"
#include "switch_priv.h"
#include "edit_priv.h"
+#include "timeout_priv.h"
#define UNLANG_IGNORE ((unlang_t *) -1)
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);
case UNLANG_TYPE_ELSE:
case UNLANG_TYPE_ELSIF:
case UNLANG_TYPE_GROUP:
+ case UNLANG_TYPE_TIMEOUT:
break;
default:
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;
{ 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);
--- /dev/null
+/*
+ * 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",
+ });
+}
--- /dev/null
+#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
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
void unlang_tmpl_init(void);
void unlang_edit_init(void);
+
+void unlang_timeout_init(void);
/** @} */
#ifdef __cplusplus
--- /dev/null
+#
+# 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
+ }
+}