The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
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: 92746932b6e2ed7a89f82add5bca019563080f95 $
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"
27 #include "subrequest_child_priv.h"
28 
29 /** Holds a synthesised instruction that we insert into the parent request
30  *
31  */
33 
34 static fr_dict_t const *dict_freeradius;
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 {
69  fr_pair_t *vp;
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,
98  unlang_detached_max_request_time, request) < 0) {
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  */
112 static 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 
139  if (!fr_cond_assert(unlang_subrequest_lifetime_set(request) == 0)) {
140  REDEBUG("Child could not be detached");
141  return;
142  }
143  FALL_THROUGH;
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,
232  ~(FR_SIGNAL_DETACH | FR_SIGNAL_CANCEL),
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  */
245  unlang_stack_frame_t *frame)
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  */
267  unlang_stack_frame_t *frame)
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  */
291  interpret_child_init(child);
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 {
334  unlang_stack_frame_t *frame;
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 
359  if (!fr_cond_assert_msg(stack_depth_current(child) == 0,
360  "Child stack depth must be 0 (not %u), 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,
420  [RLM_MODULE_DISALLOW] = 0,
421  [RLM_MODULE_NOTFOUND] = 0,
422  [RLM_MODULE_NOOP] = 0,
423  [RLM_MODULE_UPDATED] = 0
424  },
425  .retry = RETRY_INIT,
426  },
427  }
428  }
429  };
430 
431  return 0;
432 }
433 
435 {
438 }
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:320
#define UNUSED
Definition: build.h:313
fr_dcursor_eval_t void const * uctx
Definition: dcursor.h:546
#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 fr_dict_autofree(_to_free)
Definition: dict.h:850
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition: dict.h:267
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition: dict.h:280
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:4090
#define fr_dict_autoload(_to_load)
Definition: dict.h:847
Specifies an attribute which must be present for the module to function.
Definition: dict.h:266
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition: dict.h:279
#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
fr_event_list_t * unlang_interpret_event_list(request_t *request)
Get the event list for the current interpreter.
Definition: interpret.c:1764
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition: interpret.c:1359
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:1196
#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.
Definition: merged_model.c:99
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
Definition: signal.h:48
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
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.
Definition: unlang_priv.h:296
#define UNLANG_NEXT_STOP
Definition: unlang_priv.h:92
static void return_point_set(unlang_stack_frame_t *frame)
Definition: unlang_priv.h:348
static int stack_depth_current(request_t *request)
Definition: unlang_priv.h:393
@ UNLANG_TYPE_SUBREQUEST
create a child subrequest
Definition: unlang_priv.h:64
unlang_t self
Definition: unlang_priv.h:146
static void repeatable_set(unlang_stack_frame_t *frame)
Definition: unlang_priv.h:345
unlang_process_t process
function to call for interpreting this stack frame
Definition: unlang_priv.h:284
unlang_type_t type
The specialisation of this node.
Definition: unlang_priv.h:117
static unlang_stack_frame_t * frame_current(request_t *request)
Definition: unlang_priv.h:386
Our interpreter stack, as distinct from the C stack.
Definition: unlang_priv.h:280
#define RETRY_INIT
Definition: retry.h:39
static size_t char ** out
Definition: value.h:997