The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
finally.c
Go to the documentation of this file.
1/*
2 * This program is free software; you can redistribute it and/or modify
3 * it under the terms of the GNU General Public License as published by
4 * the Free Software Foundation; either version 2 of the License, or
5 * (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software
14 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15 */
16
17/**
18 * $Id: f65ba4085c06e70f0af5928499c59235da853247 $
19 *
20 * @file unlang/finally.c
21 * @brief Unlang "finally" keyword evaluation. Used for running policy after a virtual server.
22 *
23 * @copyright 2025 Network RAIDUS SAS (legal@networkradius.com)
24 */
25
26RCSID("$Id: f65ba4085c06e70f0af5928499c59235da853247 $")
27
28#include <freeradius-devel/server/state.h>
29#include <freeradius-devel/server/signal.h>
30#include <freeradius-devel/server/pair.h>
31#include <freeradius-devel/server/rcode.h>
32#include <freeradius-devel/unlang/unlang_priv.h>
33#include <freeradius-devel/unlang/action.h>
34#include <freeradius-devel/unlang/finally.h>
35#include <freeradius-devel/unlang/interpret.h>
36#include <freeradius-devel/util/timer.h>
37
38typedef struct {
39 fr_time_delta_t min_time; //!< minimum time to run the finally instruction.
41 unlang_result_t result; //!< Result of the finally instruction. We discard this.
42 rlm_rcode_t original_rcode; //!< The original request rcode when we entered.
43 unlang_t *instruction; //!< to run on timeout
45
47{
48 unlang_frame_state_finally_t *state = talloc_get_type_abort(ctx, unlang_frame_state_finally_t);
49 request_t *request = talloc_get_type_abort(state->request, request_t);
50
51 RDEBUG("Timeout reached, exiting finally section");
52
53 /*
54 * Cancels all frames (other than other finally sections)
55 * and marks the request runnable again.
56 */
58}
59
61{
62 unlang_frame_state_finally_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_finally_t);
63
64 /*
65 * Reset the request->rcode, so that any other
66 * finally sections have access to the original
67 * rcode like 'timeout'.
68 */
69 request->rcode = state->original_rcode;
70
72}
73
75{
76 unlang_frame_state_finally_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_finally_t);
77
78 state->original_rcode = request->rcode;;
79
80 /*
81 * Ensure the request has at least min_time to continue
82 * executing before we cancel it.
83 */
84 if (fr_time_delta_lt(state->min_time, fr_timer_remaining(request->timeout))) {
86 unlang_interpret_event_list(request)->tl, &request->timeout,
87 state->min_time, false, unlang_timeout_handler, state) < 0)) {
88 unlang_interpret_signal(request, FR_SIGNAL_CANCEL); /* also stops the request and does cleanups */
89 return UNLANG_ACTION_FAIL;
90 }
91 }
92
93 /*
94 * Finally should be transparent to allow the rcode from
95 * process module to propegate back up, if there are no
96 * modules called.
97 */
100 unlang_interpret_signal(request, FR_SIGNAL_CANCEL); /* also stops the request and does cleanups */
101 }
102
104
105 /*
106 * Set a timer to cancel the request. If we don't do this
107 * then the request may never finish.
108 */
110}
111
112/** Push a finally instructtion on the stack, to be evaluated as the stack is unwound
113 *
114 * @param[in] request to push timeout onto
115 * @param[in] instruction to run as we unwind
116 * @param[in] min_time max time to wait for the finally instruction to finish.
117 * This only applies if the request timeout timer has already
118 * fired, or has less than max_time to execute. i.e. this is
119 * a guarantee of a minimum amount of time for the finally
120 * instruction to run.
121 * @param[in] top_frame Set to UNLANG_TOP_FRAME if the interpreter should return.
122 * Set to UNLANG_SUB_FRAME if the interprer should continue.
123 *
124 * @return
125 * - 0 on success.
126 * - -1 on failure.
127 */
128int unlang_finally_push_instruction(request_t *request, void *instruction, fr_time_delta_t min_time, bool top_frame)
129{
130 /** Static instruction for performing xlat evaluations
131 *
132 */
133 static unlang_t finally_instruction = {
135 .name = "finally",
136 .debug_name = "finally",
137 .actions = {
138 .actions = {
139 [RLM_MODULE_REJECT] = 0,
140 [RLM_MODULE_FAIL] = MOD_ACTION_RETURN, /* Exit out of nested levels */
141 [RLM_MODULE_OK] = 0,
142 [RLM_MODULE_HANDLED] = 0,
143 [RLM_MODULE_INVALID] = 0,
146 [RLM_MODULE_NOOP] = 0,
147 [RLM_MODULE_TIMEOUT] = MOD_ACTION_RETURN, /* Exit out of nested levels */
149 },
150 .retry = RETRY_INIT,
151 },
152 };
153
155 unlang_stack_t *stack = request->stack;
157
158 /*
159 * Push a new finally frame onto the stack
160 *
161 * This frame is uncancellable, and will always
162 * execute before the request completes.
163 *
164 * Its children are very much cancellable though
165 * and will be cancelled if min_time or the request
166 * timer expires.
167 */
168 if (unlang_interpret_push(NULL, request, &finally_instruction,
169 FRAME_CONF(RLM_MODULE_NOT_SET, top_frame), UNLANG_NEXT_STOP) < 0) return -1;
170 frame = &stack->frame[stack->depth];
171
172 /*
173 * Allocate its state
174 */
175 MEM(frame->state = state = talloc_zero(stack, unlang_frame_state_finally_t));
176 state->instruction = instruction;
177 state->request = request;
178 state->min_time = min_time;
179
180 frame_repeat(frame, unlang_finally); /* execute immediately... or when unwinding */
181
182 return 0;
183
184}
185
187{
189 &(unlang_op_t){
190 .name = "finally",
191 .interpret = unlang_finally,
192 .flag = UNLANG_OP_FLAG_NO_FORCE_UNWIND, /* No debug braces, the thing that's pushed in unlang finally should have braces */
193 .frame_state_size = sizeof(unlang_frame_state_finally_t),
194 .frame_state_type = "unlang_frame_state_finally_t",
195 });
196}
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
@ UNLANG_ACTION_PUSHED_CHILD
unlang_t pushed a new child onto the stack, execute it instead of continuing.
Definition action.h:39
@ UNLANG_ACTION_FAIL
Encountered an unexpected error.
Definition action.h:36
@ UNLANG_ACTION_CALCULATE_RESULT
Calculate a new section rlm_rcode_t value.
Definition action.h:37
#define RCSID(id)
Definition build.h:485
#define unlikely(_x)
Definition build.h:383
#define UNUSED
Definition build.h:317
#define MEM(x)
Definition debug.h:36
void unlang_finally_init(void)
Definition finally.c:186
unlang_result_t result
Result of the finally instruction. We discard this.
Definition finally.c:41
static void unlang_timeout_handler(UNUSED fr_timer_list_t *tl, UNUSED fr_time_t now, void *ctx)
Definition finally.c:46
rlm_rcode_t original_rcode
The original request rcode when we entered.
Definition finally.c:42
static unlang_action_t unlang_finally_resume(UNUSED unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Definition finally.c:60
fr_time_delta_t min_time
minimum time to run the finally instruction.
Definition finally.c:39
unlang_t * instruction
to run on timeout
Definition finally.c:43
static unlang_action_t unlang_finally(UNUSED unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Definition finally.c:74
int unlang_finally_push_instruction(request_t *request, void *instruction, fr_time_delta_t min_time, bool top_frame)
Push a finally instructtion on the stack, to be evaluated as the stack is unwound.
Definition finally.c:128
TALLOC_CTX * unlang_interpret_frame_talloc_ctx(request_t *request)
Get a talloc_ctx which is valid only for this frame.
Definition interpret.c:1661
int unlang_interpret_push_instruction(unlang_result_t *p_result, request_t *request, void *instruction, unlang_frame_conf_t const *conf)
Push an instruction onto the request stack for later interpretation.
Definition interpret.c:1166
void unlang_interpret_signal(request_t *request, fr_signal_t action)
Send a signal (usually stop) to a request.
Definition interpret.c:1396
int unlang_interpret_push(unlang_result_t *result_p, request_t *request, unlang_t const *instruction, unlang_frame_conf_t const *conf, bool do_next_sibling)
Push a new frame onto the stack.
Definition interpret.c:283
fr_event_list_t * unlang_interpret_event_list(request_t *request)
Get the event list for the current interpreter.
Definition interpret.c:2013
#define FRAME_CONF(_default_rcode, _top_frame)
Definition interpret.h:153
#define UNLANG_SUB_FRAME
Definition interpret.h:37
void unlang_register(int type, unlang_op_t *op)
Register an operation with the interpreter.
Definition base.c:63
static char * stack[MAX_STACK]
Definition radmin.c:159
@ MOD_ACTION_RETURN
Definition mod_action.h:43
#define RDEBUG(fmt,...)
Definition radclient.h:53
rlm_rcode_t
Return codes indicating the result of the module call.
Definition rcode.h:40
@ RLM_MODULE_INVALID
The module considers the request invalid.
Definition rcode.h:45
@ RLM_MODULE_OK
The module is OK, continue.
Definition rcode.h:43
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition rcode.h:42
@ RLM_MODULE_DISALLOW
Reject the request (user is locked out).
Definition rcode.h:46
@ RLM_MODULE_REJECT
Immediately reject the request.
Definition rcode.h:41
@ RLM_MODULE_TIMEOUT
Module (or section) timed out.
Definition rcode.h:50
@ RLM_MODULE_NOTFOUND
User not found.
Definition rcode.h:47
@ RLM_MODULE_UPDATED
OK (pairs modified).
Definition rcode.h:49
@ RLM_MODULE_NOT_SET
Error resolving rcode (should not be returned by modules).
Definition rcode.h:52
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition rcode.h:48
@ RLM_MODULE_HANDLED
The module handled the request, so stop.
Definition rcode.h:44
@ FR_SIGNAL_CANCEL
Request has been cancelled.
Definition signal.h:40
#define fr_time_delta_lt(_a, _b)
Definition time.h:285
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
"server local" time.
Definition time.h:69
fr_time_delta_t fr_timer_remaining(fr_timer_t *ev)
Return time delta between now and when the timer should fire.
Definition timer.c:729
An event timer list.
Definition timer.c:50
#define fr_timer_in(...)
Definition timer.h:87
void * state
Stack frame specialisations.
#define UNLANG_NEXT_STOP
Definition unlang_priv.h:97
@ UNLANG_TYPE_FINALLY
run at the end of a virtual server.
Definition unlang_priv.h:77
static void frame_repeat(unlang_stack_frame_t *frame, unlang_process_t process)
Mark the current stack frame up for repeat, and set a new process function.
@ UNLANG_OP_FLAG_NO_FORCE_UNWIND
Must not be cancelled.
unlang_type_t type
The specialisation of this node.
An unlang operation.
A node in a graph of unlang_op_t (s) that we execute.
Our interpreter stack, as distinct from the C stack.
An unlang stack associated with a request.
#define RETRY_INIT
Definition retry.h:39