The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
subrequest_child.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: a571a2eff92bb86749ba752cfe30388c291908ec $
19 *
20 * @file unlang/subrequest.c
21 * @brief Unlang "subrequest" and "detach" keyword evaluation.
22 *
23 * @copyright 2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
24 */
25#include <freeradius-devel/server/state.h>
26#include "interpret_priv.h"
28
29/** Holds a synthesised instruction that we insert into the parent request
30 *
31 */
33
35
38 { .out = &dict_freeradius, .proto = "freeradius" },
39 { NULL }
40};
41
43
46 { .out = &request_attr_request_lifetime, .name = "Request-Lifetime", .type = FR_TYPE_UINT32, .dict = &dict_freeradius },
47 { NULL }
48};
49
50/** Event handler to free a detached child
51 *
52 */
54{
55 request_t *request = talloc_get_type_abort(uctx, request_t);
56
57 RDEBUG("Reached Request-Lifetime. Forcibly stopping request");
58
59 unlang_interpret_signal(request, FR_SIGNAL_CANCEL); /* Request should now be freed */
60}
61
62/** Initialize a detached child
63 *
64 * Detach it from the parent, set up it's lifetime, and mark it as
65 * runnable.
66 */
68{
70
71 /*
72 * Set Request Lifetime
73 */
74 vp = fr_pair_find_by_da(&request->control_pairs, NULL, request_attr_request_lifetime);
75 if (!vp || (vp->vp_uint32 > 0)) {
77 const fr_event_timer_t **ev_p;
78
79 if (!vp) {
80 when = fr_time_delta_add(when, fr_time_delta_from_sec(30)); /* default to 30s if not set */
81
82 } else if (vp->vp_uint32 > 3600) {
83 RWDEBUG("Request-Timeout can be no more than 3600 seconds");
84 when = fr_time_delta_add(when, fr_time_delta_from_sec(3600));
85
86 } else if (vp->vp_uint32 < 5) {
87 RWDEBUG("Request-Timeout can be no less than 5 seconds");
89
90 } else {
91 when = fr_time_delta_from_sec(vp->vp_uint32);
92 }
93
94 ev_p = talloc_size(request, sizeof(*ev_p));
95 memset(ev_p, 0, sizeof(*ev_p));
96
97 if (fr_event_timer_in(request, unlang_interpret_event_list(request), ev_p, when,
99 talloc_free(ev_p);
100 return -1;
101 }
102 }
103
104 return 0;
105}
106
107/** Process a detach signal in the child
108 *
109 * This processes any detach signals the child receives
110 * The child doesn't actually do the detaching
111 */
112static void unlang_subrequest_child_signal(request_t *request, fr_signal_t action, UNUSED void *uctx)
113{
115
116 /*
117 * We're already detached so we don't
118 * need to notify the parent we're
119 * waking up, and we don't need to detach
120 * again...
121 */
122 if (!request->parent) return;
123
124 state = talloc_get_type_abort(frame_current(request->parent)->state, unlang_frame_state_subrequest_t);
125
126 /*
127 * Ignore signals which aren't detach, and ar
128 * and ignore the signal if we have no parent.
129 */
130 switch (action) {
131 case FR_SIGNAL_DETACH:
132 /*
133 * Place child's state back inside the parent
134 */
135 if (state->session.enable) fr_state_store_in_parent(request,
136 state->session.unique_ptr,
137 state->session.unique_int);
138
140 REDEBUG("Child could not be detached");
141 return;
142 }
144
145 case FR_SIGNAL_CANCEL:
146 RDEBUG3("Removing subrequest from parent, and marking parent as runnable");
147
148 /*
149 * Indicate to the parent there's no longer a child
150 */
151 state->child = NULL;
152
153 /*
154 * Tell the parent to resume
155 */
156 unlang_interpret_mark_runnable(request->parent);
157 break;
158
159 default:
160 return;
161 }
162}
163
164/** When the child is done, tell the parent that we've exited.
165 *
166 * This is pushed as a frame at the top of the child's stack, so when
167 * the child is done executing, it runs this to inform the parent
168 * that its done.
169 */
171 UNUSED int *p_priority, request_t *request, void *uctx)
172{
174
175 /*
176 * Child was detached, nothing more to do.
177 *
178 * This frame was left on the stack when the child
179 * detached. It's normally meant to trigger a
180 * resume in the parent, but as there is no parent
181 * it just becomes a noop and gets popped.
182 *
183 * This is cheaper/less complex then rooting it
184 * out at detach time and unsetting the repeat
185 * function.
186 */
187 if (!request->parent) return UNLANG_ACTION_CALCULATE_RESULT;
188
189 /*
190 * 'state' in this context, is the frame state
191 * in the parent request, so we cannot examine it
192 * until AFTER we've determined there is still
193 * a parent, else the memory could've been freed.
194 *
195 * i.e. don't move the get_type_abort call onto
196 * the same line as the state variable declaration.
197 */
198 state = talloc_get_type_abort(uctx, unlang_frame_state_subrequest_t);
199
200 /*
201 * Place child state back inside the parent
202 */
203 if (state->session.enable) fr_state_store_in_parent(request,
204 state->session.unique_ptr,
205 state->session.unique_int);
206 /*
207 * Record the child's result
208 */
209 if (state->p_result) *state->p_result = *p_result;
210
211 /*
212 * Resume the parent
213 */
214 unlang_interpret_mark_runnable(request->parent);
215
217}
218
219/** Push a resumption frame onto a child's stack
220 *
221 * This is necessary so that the child informs its parent when it's done/detached
222 * and so that the child responds to detach signals.
223 */
225{
226 /*
227 * Push a resume frame into the child
228 */
229 if (unlang_function_push(child, NULL,
234 state) < 0) return -1;
235
236 return_point_set(frame_current(child)); /* Stop return going through the resumption frame */
237
238 return 0;
239}
240
241/** Function to run in the context of the parent on resumption
242 *
243 */
246{
247 unlang_frame_state_subrequest_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_subrequest_t);
248
249 if (*p_result == RLM_MODULE_NOT_SET) *p_result = RLM_MODULE_NOOP;
250
252
254}
255
256/** Function called by the unlang interpreter to start the child running
257 *
258 * The reason why we do this on the unlang stack is so that _this_ frame
259 * is marked as resumable in the parent, not whatever frame was previously
260 * being processed by the interpreter when the parent was called.
261 *
262 * i.e. after calling unlang_subrequest_child_push, the code in the parent
263 * can call UNLANG_ACTION_PUSHED_CHILD, which will result in _this_ frame
264 * being executed, and _this_ frame can yield.
265 */
268{
269 unlang_frame_state_subrequest_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_subrequest_t);
270 request_t *child = state->child;
271
272 /*
273 * No parent means this is a pre-detached child
274 * so the parent should continue executing.
275 */
276 if (!child || !child->parent) return UNLANG_ACTION_CALCULATE_RESULT;
277
278
279 /*
280 * Ensure we restore the session state information
281 * into the child.
282 */
283 if (state->session.enable) fr_state_restore_to_child(child,
284 state->session.unique_ptr,
285 state->session.unique_int);
286 /*
287 * Ensures the child is setup correctly and adds
288 * it into the runnable queue of whatever owns
289 * the interpreter.
290 */
292
293 /*
294 * Don't run this function again on resumption
295 */
297 repeatable_set(frame);
298
299 return UNLANG_ACTION_YIELD;
300}
301
302/** Push a pre-existing child back onto the stack as a subrequest
303 *
304 * The child *MUST* have been allocated with unlang_io_subrequest_alloc, or something
305 * that calls it.
306 *
307 * After the child is no longer required it *MUST* be freed with #unlang_subrequest_detach_and_free.
308 * It's not enough to free it with talloc_free.
309 *
310 * This function should be called _before_ pushing any additional frames onto the child's
311 * stack for it to execute.
312 *
313 * The parent should return UNLANG_ACTION_PUSHED_CHILD, when it's done setting up the
314 * child request. It should NOT return UNLANG_ACTION_YIELD.
315 *
316 * @param[in] out Where to write the result of the subrequest.
317 * @param[in] child to push.
318 * @param[in] session control values. Whether we restore/store session info.
319 * @param[in] free_child automatically free the child when it's finished executing.
320 * This is useful if extracting the result from the child is
321 * done using the child's stack, and so the parent never needs
322 * to access it.
323 * @param[in] top_frame Set to UNLANG_TOP_FRAME if the interpreter should return.
324 * Set to UNLANG_SUB_FRAME if the interprer should continue.
325 * @return
326 * - 0 on success.
327 * - -1 on failure.
328 */
330 unlang_subrequest_session_t const *session,
331 bool free_child, bool top_frame)
332{
335
336 /*
337 * Push a new subrequest frame onto the stack
338 *
339 * This allocates memory for the frame state
340 * which we fill in below.
341 */
343 RLM_MODULE_NOT_SET, UNLANG_NEXT_STOP, top_frame) < 0) {
344 return -1;
345 }
346
347 frame = frame_current(child->parent);
349
350 /*
351 * Setup the state for the subrequest
352 */
353 state = talloc_get_type_abort(frame_current(child->parent)->state, unlang_frame_state_subrequest_t);
354 state->p_result = out;
355 state->child = child;
356 state->session = *session;
357 state->free_child = free_child;
358
360 "Child stack depth must be 0 (not %d), when subrequest_child_push is called",
361 stack_depth_current(child))) return -1;
362
363 /*
364 * Push a resumption frame onto the stack
365 * so the child calls its parent when it's
366 * complete.
367 */
368 if (unlang_subrequest_child_push_resume(child, state) < 0) return -1;
369
370 return 0;
371}
372
374{
375 /*
376 * Ensures the child is setup correctly and adds
377 * it into the runnable queue of whatever owns
378 * the interpreter.
379 */
380 interpret_child_init(request);
381
382 if ((unlang_subrequest_lifetime_set(request) < 0) || (request_detach(request) < 0)) {
383 RPEDEBUG("Failed detaching request");
384 return -1;
385 }
386
387 return 0;
388}
389
391{
393 PERROR("%s", __FUNCTION__);
394 return -1;
395 }
397 PERROR("%s", __FUNCTION__);
398 return -1;
399 }
400
401 /*
402 * Needs to be dynamically allocated
403 * so that talloc_get_type works
404 * correctly.
405 */
408 .group = {
409 .self = {
411 .name = "subrequest",
412 .debug_name = "subrequest",
413 .actions = {
414 .actions = {
415 [RLM_MODULE_REJECT] = 0,
416 [RLM_MODULE_FAIL] = 0,
417 [RLM_MODULE_OK] = 0,
418 [RLM_MODULE_HANDLED] = 0,
419 [RLM_MODULE_INVALID] = 0,
422 [RLM_MODULE_NOOP] = 0,
424 },
425 .retry = RETRY_INIT,
426 },
427 }
428 }
429 };
430
431 return 0;
432}
433
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
@ UNLANG_ACTION_CALCULATE_RESULT
Calculate a new section rlm_rcode_t value.
Definition action.h:37
@ UNLANG_ACTION_YIELD
Temporarily pause execution until an event occurs.
Definition action.h:42
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition build.h:322
#define UNUSED
Definition build.h:315
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:139
#define fr_cond_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition debug.h:156
#define MEM(x)
Definition debug.h:36
#define fr_dict_autofree(_to_free)
Definition dict.h:852
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition dict.h:268
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition dict.h:281
int fr_dict_attr_autoload(fr_dict_attr_autoload_t const *to_load)
Process a dict_attr_autoload element to load/verify a dictionary attribute.
Definition dict_util.c:4094
#define fr_dict_autoload(_to_load)
Definition dict.h:849
Specifies an attribute which must be present for the module to function.
Definition dict.h:267
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition dict.h:280
#define fr_event_timer_in(...)
Definition event.h:255
#define unlang_function_push(_request, _func, _repeat, _signal, _sigmask, _top_frame, _uctx)
Push a generic function onto the unlang stack.
Definition function.h:111
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition interpret.c:1363
int unlang_interpret_push(request_t *request, unlang_t const *instruction, rlm_rcode_t default_rcode, bool do_next_sibling, bool top_frame)
Push a new frame onto the stack.
Definition interpret.c:161
void unlang_interpret_signal(request_t *request, fr_signal_t action)
Send a signal (usually stop) to a request.
Definition interpret.c:1200
fr_event_list_t * unlang_interpret_event_list(request_t *request)
Get the event list for the current interpreter.
Definition interpret.c:1768
#define UNLANG_TOP_FRAME
Definition interpret.h:35
Private declarations for the unlang interpreter.
static void interpret_child_init(request_t *request)
#define PERROR(_fmt,...)
Definition log.h:228
#define RWDEBUG(fmt,...)
Definition log.h:361
#define RDEBUG3(fmt,...)
Definition log.h:343
#define RPEDEBUG(fmt,...)
Definition log.h:376
talloc_free(reap)
Stores all information relating to an event list.
Definition event.c:411
A timer event.
Definition event.c:102
@ FR_TYPE_UINT32
32 Bit unsigned integer.
fr_pair_t * fr_pair_find_by_da(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find the first pair with a matching da.
Definition pair.c:693
#define REDEBUG(fmt,...)
Definition radclient.h:52
#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_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:51
@ 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
int request_detach(request_t *child)
Unlink a subrequest from its parent.
Definition request.c:668
void fr_state_restore_to_child(request_t *child, void const *unique_ptr, int unique_int)
Restore subrequest data from a parent request.
Definition state.c:858
void fr_state_store_in_parent(request_t *child, void const *unique_ptr, int unique_int)
Store subrequest's session-state list and persistable request data in its parent.
Definition state.c:812
fr_signal_t
Signals that can be generated/processed by request signal handlers.
Definition signal.h:38
@ FR_SIGNAL_CANCEL
Request has been cancelled.
Definition signal.h:40
@ FR_SIGNAL_DETACH
Request is being detached from its parent.
Definition signal.h:45
fr_pair_t * vp
Stores an attribute, a value and various bits of other data.
Definition pair.h:68
void unlang_subrequest_detach_and_free(request_t **child)
Free a child request, detaching it from its parent and freeing allocated memory.
Definition subrequest.c:272
int unique_int
Session unique int identifier.
Definition subrequest.h:37
void const * unique_ptr
Session unique ptr identifier.
Definition subrequest.h:36
bool enable
Whether we should store/restore sessions.
Definition subrequest.h:35
void unlang_subrequest_child_op_free(void)
static void unlang_subrequest_child_signal(request_t *request, fr_signal_t action, UNUSED void *uctx)
Process a detach signal in the child.
fr_dict_attr_autoload_t subrequest_dict_attr[]
int unlang_subrequest_lifetime_set(request_t *request)
Initialize a detached child.
int unlang_subrequest_child_push_resume(request_t *child, unlang_frame_state_subrequest_t *state)
Push a resumption frame onto a child's stack.
static void unlang_detached_max_request_time(UNUSED fr_event_list_t *el, UNUSED fr_time_t now, void *uctx)
Event handler to free a detached child.
static fr_dict_t const * dict_freeradius
static unlang_action_t unlang_subrequest_calculate_result(rlm_rcode_t *p_result, UNUSED request_t *request, unlang_stack_frame_t *frame)
Function to run in the context of the parent on resumption.
static unlang_subrequest_t * subrequest_instruction
Holds a synthesised instruction that we insert into the parent request.
static fr_dict_attr_t const * request_attr_request_lifetime
int unlang_subrequest_child_push_and_detach(request_t *request)
fr_dict_autoload_t subrequest_dict[]
int unlang_subrequest_child_op_init(void)
unlang_action_t unlang_subrequest_child_run(UNUSED rlm_rcode_t *p_result, UNUSED request_t *request, unlang_stack_frame_t *frame)
Function called by the unlang interpreter to start the child running.
int unlang_subrequest_child_push(rlm_rcode_t *out, request_t *child, unlang_subrequest_session_t const *session, bool free_child, bool top_frame)
Push a pre-existing child back onto the stack as a subrequest.
static unlang_action_t unlang_subrequest_child_done(rlm_rcode_t *p_result, UNUSED int *p_priority, request_t *request, void *uctx)
When the child is done, tell the parent that we've exited.
bool free_child
Whether we should free the child after it completes.
unlang_subrequest_session_t session
Session configuration.
request_t * child
Pre-allocated child request.
unlang_group_t group
rlm_rcode_t * p_result
Where to store the result.
Parameters for initialising the subrequest (parent's frame state)
static fr_time_delta_t fr_time_delta_add(fr_time_delta_t a, fr_time_delta_t b)
Definition time.h:255
static fr_time_delta_t fr_time_delta_from_sec(int64_t sec)
Definition time.h:590
#define fr_time_delta_wrap(_time)
Definition time.h:152
A time delta, a difference in time measured in nanoseconds.
Definition time.h:80
"server local" time.
Definition time.h:69
static fr_event_list_t * el
void * state
Stack frame specialisations.
#define UNLANG_NEXT_STOP
Definition unlang_priv.h:92
static unlang_stack_frame_t * frame_current(request_t *request)
static void return_point_set(unlang_stack_frame_t *frame)
static int stack_depth_current(request_t *request)
@ UNLANG_TYPE_SUBREQUEST
create a child subrequest
Definition unlang_priv.h:64
static void repeatable_set(unlang_stack_frame_t *frame)
unlang_process_t process
function to call for interpreting this stack frame
unlang_type_t type
The specialisation of this node.
Our interpreter stack, as distinct from the C stack.
#define RETRY_INIT
Definition retry.h:39
static size_t char ** out
Definition value.h:997