unlang_group_t *g;
unlang_timeout_t *gext;
fr_time_delta_t timeout;
+ tmpl_t *vpt = NULL;
+ fr_token_t token;
static unlang_ext_t const timeout_ext = {
.type = UNLANG_TYPE_TIMEOUT,
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;
+ token = cf_section_name2_quote(cs);
+
+ if ((token == T_BARE_WORD) && isdigit((int) *name2)) {
+ 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;
+ }
+ } else {
+ ssize_t slen;
+ tmpl_rules_t t_rules;
+
+ /*
+ * We don't allow unknown attributes here.
+ */
+ t_rules = *(unlang_ctx->rules);
+ t_rules.attr.allow_unknown = false;
+ RULES_VERIFY(&t_rules);
+
+ slen = tmpl_afrom_substr(gext, &vpt,
+ &FR_SBUFF_IN(name2, strlen(name2)),
+ token,
+ NULL,
+ &t_rules);
+ if (!vpt) {
+ char *spaces, *text;
+
+ fr_canonicalize_error(cs, &spaces, &text, slen, fr_strerror());
+
+ cf_log_err(cs, "Syntax error");
+ cf_log_err(cs, "%s", name2);
+ cf_log_err(cs, "%s^ %s", spaces, text);
+
+ talloc_free(g);
+ talloc_free(spaces);
+ talloc_free(text);
+
+ return NULL;
+ }
+
+ /*
+ * Fixup the tmpl so that we know it's somewhat sane.
+ */
+ if (!pass2_fixup_tmpl(gext, &vpt, cf_section_to_item(cs), unlang_ctx->rules->attr.dict_def)) {
+ error:
+ talloc_free(g);
+ return NULL;
+ }
+
+ if (tmpl_is_list(vpt)) {
+ cf_log_err(cs, "Cannot use list as argument for 'timeout' statement");
+ goto error;
+ }
+
+ if (tmpl_contains_regex(vpt)) {
+ cf_log_err(cs, "Cannot use regular expression as argument for 'timeout' statement");
+ goto error;
+ }
+
+ /*
+ * Attribute or data MUST be cast to TIME_DELTA.
+ */
+ if (tmpl_cast_set(vpt, FR_TYPE_TIME_DELTA) < 0) {
+ cf_log_perr(cs, "Failed setting cast type");
+ goto error;
+ }
}
+
/*
* Compile the contents of a "timeout".
*/
g = unlang_generic_to_group(c);
gext = unlang_group_to_timeout(g);
gext->timeout = timeout;
+ gext->vpt = vpt;
return c;
}
typedef struct {
bool success;
int depth;
+ fr_time_delta_t timeout;
request_t *request;
rindent_t indent;
fr_event_timer_t const *ev;
+
+ fr_value_box_list_t result;
} unlang_frame_state_timeout_t;
static void unlang_timeout_handler(UNUSED fr_event_list_t *el, UNUSED fr_time_t now, void *ctx)
return UNLANG_ACTION_CALCULATE_RESULT;
}
-
-static unlang_action_t unlang_timeout(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
+static unlang_action_t unlang_timeout_set(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;
+ unlang_group_t *g;
+ fr_time_t timeout;
/*
* Save current indentation for the error path.
*/
RINDENT_SAVE(&state->indent, request);
- timeout = fr_time_add(fr_time(), gext->timeout);
+ timeout = fr_time_add(fr_time(), state->timeout);
if (fr_event_timer_at(state, unlang_interpret_event_list(request), &state->ev, timeout,
unlang_timeout_handler, state) < 0) {
goto fail;
}
+ g = unlang_generic_to_group(frame->instruction);
+
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_PUSHED_CHILD;
}
+static unlang_action_t unlang_timeout_xlat_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);
+ fr_value_box_t *box = fr_dlist_head(&state->result);
+
+ /*
+ * compile_timeout() ensures that the tmpl is cast to time_delta, so we don't have to do any more work here.
+ */
+ state->timeout = box->vb_time_delta;
+
+ return unlang_timeout_set(p_result, request, frame);
+}
+
+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);
+ 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;
+
+ if (!gext->vpt) {
+ state->timeout = gext->timeout;
+ return unlang_timeout_set(p_result, request, frame);
+ }
+
+ fr_value_box_list_init(&state->result);
+
+ if (unlang_tmpl_push(state, &state->result, request, gext->vpt, NULL) < 0) return UNLANG_ACTION_FAIL;
+
+ frame_repeat(frame, unlang_timeout_xlat_done);
+
+ return UNLANG_ACTION_PUSHED_CHILD;
+}
+
void unlang_timeout_init(void)
{