The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
parallel.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: db419c166c597fcbe94de8b3300e3f9eae3b8399 $
19  *
20  * @file unlang/parallel.c
21  * @brief Implementation of the unlang "parallel" keyword.
22  *
23  * @copyright 2006-2019 The FreeRADIUS server project
24  */
25 RCSID("$Id: db419c166c597fcbe94de8b3300e3f9eae3b8399 $")
26 
27 #include "function.h"
28 #include "interpret_priv.h"
29 #include "module_priv.h"
30 #include "parallel_priv.h"
31 #include "subrequest_priv.h"
32 
33 
34 /** Cancel a specific child
35  *
36  */
37 static inline CC_HINT(always_inline) void unlang_parallel_cancel_child(unlang_parallel_state_t *state, int i)
38 {
39  request_t *child = state->children[i].request;
40  request_t *request = child->parent; /* For debug messages */
41 
42  switch (state->children[i].state) {
43  case CHILD_INIT:
44  state->children[i].state = CHILD_CANCELLED;
45  fr_assert(!state->children[i].request);
46  break;
47 
48  case CHILD_EXITED:
49  state->children[i].state = CHILD_CANCELLED;
50  TALLOC_FREE(state->children[i].request);
51  break;
52 
53  case CHILD_RUNNABLE: /* Don't check runnable_id, may be yielded */
54  /*
55  * Signal the child to stop
56  *
57  * The signal function cleans up the request
58  * and signals anything that was tracking it
59  * that it's now complete.
60  */
61  unlang_interpret_signal(child, FR_SIGNAL_CANCEL);
62 
63  /*
64  * Free it.
65  */
66  TALLOC_FREE(state->children[i].request);
67  break;
68 
69  case CHILD_DONE:
70  fr_assert(!fr_heap_entry_inserted(child->runnable_id));
71 
72  /*
73  * Completed children just get freed
74  */
75  TALLOC_FREE(state->children[i].request);
76  break;
77 
78  case CHILD_DETACHED:
79  case CHILD_CANCELLED:
80  return;
81  }
82 
83  RDEBUG3("parallel - child %s (%d/%d) CANCELLED",
84  state->children[i].name,
85  i + 1, state->num_children);
86  state->children[i].state = CHILD_CANCELLED;
87 }
88 
89 #if 0
90 /** Cancel all the child's siblings
91  *
92  * Siblings will be excluded from final result calculation for the parallel section.
93  */
94 static void unlang_parallel_cancel_siblings(request_t *request)
95 {
96  unlang_stack_t *stack = request->parent->stack;
97  unlang_stack_frame_t *frame = &stack->frame[stack->depth];
98  unlang_parallel_state_t *state = talloc_get_type_abort(frame->state, unlang_parallel_state_t);
99  int i;
100 
101  for (i = 0; i < state->num_children; i++) {
102  if (state->children[i].request == request) continue; /* Don't cancel this one */
103 
105  }
106 }
107 #endif
108 
109 /** Signal handler to deal with UNLANG_ACTION_DETACH
110  *
111  * When a request detaches we need
112  */
113 static void unlang_parallel_child_signal(request_t *request, UNUSED fr_signal_t action, void *uctx)
114 {
115  unlang_parallel_child_t *child = uctx;
116  unlang_stack_frame_t *frame;
118 
119  frame = frame_current(request->parent);
120  state = talloc_get_type_abort(frame->state, unlang_parallel_state_t);
121 
122  RDEBUG3("parallel - child %s (%d/%d) DETACHED",
123  request->name,
124  child->num + 1, state->num_children);
125 
126  child->state = CHILD_DETACHED;
127  child->request = NULL;
128  state->num_complete++;
129 
130  /*
131  * All children exited, resume the parent
132  */
133  if (state->num_complete == state->num_children) {
134  RDEBUG3("Signalling parent %s that all children have EXITED or DETACHED", request->parent->name);
135  unlang_interpret_mark_runnable(request->parent);
136  }
137 
138  return;
139 }
140 
141 /** When the chld is done, tell the parent that we've exited.
142  *
143  */
144 static unlang_action_t unlang_parallel_child_done(UNUSED rlm_rcode_t *p_result, UNUSED int *p_priority, request_t *request, void *uctx)
145 {
146  unlang_parallel_child_t *child = uctx;
147 
148  /*
149  * If we have a parent, then we're running synchronously
150  * with it. Tell the parent that we've exited, and it
151  * can continue.
152  *
153  * Otherwise we're a detached child, and we don't tell
154  * the parent anything. Because we have that kind of
155  * relationship.
156  */
157  if (child->state == CHILD_RUNNABLE) {
158  /*
159  * Reach into the parent to get the unlang_parallel_state_t
160  * for the whole parallel block.
161  */
162  unlang_stack_frame_t *frame = frame_current(request->parent);
163  unlang_parallel_state_t *state = talloc_get_type_abort(frame->state, unlang_parallel_state_t);
164 
165  RDEBUG3("parallel - child %s (%d/%d) EXITED",
166  request->name,
167  child->num + 1, state->num_children);
168 
169  child->state = CHILD_EXITED;
170  state->num_complete++;
171 
172  /*
173  * All children exited, resume the parent
174  */
175  if (state->num_complete == state->num_children) {
176  RDEBUG3("Signalling parent %s that all children have EXITED or DETACHED", request->parent->name);
177  unlang_interpret_mark_runnable(request->parent);
178  }
179  }
180 
181  /*
182  * Don't change frame->result, it's the result of the child.
183  */
185 }
186 
188 {
189  unlang_parallel_state_t *state = talloc_get_type_abort(frame->state, unlang_parallel_state_t);
190 
191  int i, priority;
192  rlm_rcode_t result;
193 
194  for (i = 0; i < state->num_children; i++) {
195  if (!state->children[i].request) continue;
196 
197  fr_assert(state->children[i].state == CHILD_EXITED);
198  REQUEST_VERIFY(state->children[i].request);
199 
200  RDEBUG3("parallel - child %s (%d/%d) DONE",
201  state->children[i].name,
202  i + 1, state->num_children);
203 
204  state->children[i].state = CHILD_DONE;
205 
206  priority = ((unlang_stack_t *)state->children[i].request->stack)->priority;
207  result = ((unlang_stack_t *)state->children[i].request->stack)->result;
208 
209  /*
210  * Return isn't allowed to make it back
211  * to the parent... Not sure this is
212  * the correct behaviour, but it's what
213  * was there before.
214  */
215  if (priority == MOD_ACTION_RETURN) {
216  priority = 0;
217  } else if (priority == MOD_ACTION_REJECT) {
218  result = RLM_MODULE_REJECT;
219  priority = 0;
220  }
221 
222  /*
223  * Do priority over-ride.
224  */
225  if (priority > state->priority) {
226  state->result = result;
227  state->priority = priority;
228 
229  RDEBUG4("** [%i] %s - over-riding result from higher priority to (%s %d)",
230  stack_depth_current(request), __FUNCTION__,
231  fr_table_str_by_value(mod_rcode_table, result, "<invalid>"),
232  priority);
233  }
234  }
235 
236  /*
237  * Reap the children....
238  */
239  for (i = 0; i < state->num_children; i++) {
240  if (!state->children[i].request) continue;
241 
242  fr_assert(!fr_heap_entry_inserted(state->children[i].request->runnable_id));
243  TALLOC_FREE(state->children[i].request);
244  }
245 
246  *p_result = state->result;
248 }
249 
250 /** Run one or more sub-sections from the parallel section.
251  *
252  */
254 {
255  unlang_parallel_state_t *state = talloc_get_type_abort(frame->state, unlang_parallel_state_t);
256  request_t *child;
257  int i;
258 
259  /*
260  * If the children should be created detached, we return
261  * "noop". This function then creates the children,
262  * detaches them, and returns.
263  */
264  if (state->detach) {
265  state->priority = 0;
266  state->result = RLM_MODULE_NOOP;
267  }
268 
269  /*
270  * Loop over all the children.
271  *
272  * We always service the parallel section from top to
273  * bottom, and we always service all of it.
274  */
275  for (i = 0; i < state->num_children; i++) {
276  fr_assert(state->children[i].instruction != NULL);
277  child = unlang_io_subrequest_alloc(request,
278  request->dict, state->detach);
279  child->packet->code = request->packet->code;
280 
281  RDEBUG3("parallel - child %s (%d/%d) INIT",
282  child->name,
283  i + 1, state->num_children);
284 
285  if (state->clone) {
286  /*
287  * Note that we do NOT copy the
288  * Session-State list! That
289  * contains state information for
290  * the parent.
291  */
292  if ((fr_pair_list_copy(child->request_ctx,
293  &child->request_pairs,
294  &request->request_pairs) < 0) ||
295  (fr_pair_list_copy(child->reply_ctx,
296  &child->reply_pairs,
297  &request->reply_pairs) < 0) ||
298  (fr_pair_list_copy(child->control_ctx,
299  &child->control_pairs,
300  &request->control_pairs) < 0)) {
301  REDEBUG("failed copying lists to clone");
302  error:
303  /*
304  * Detached children which have
305  * already been created are
306  * allowed to continue.
307  */
308  if (!state->detach) {
309  /*
310  * Remove the current child
311  *
312  * Should also free detached children
313  */
315 
316  /*
317  * Remove all previously
318  * spawned children.
319  */
320  for (--i; i >= 0; i--) unlang_interpret_request_done(child);
321  }
322 
324  }
325  }
326 
327  /*
328  * Child starts detached, the parent knows
329  * and can exit immediately once all
330  * the children are initialised.
331  */
332  if (state->detach) {
333  if (RDEBUG_ENABLED3) {
334  request_t *parent = request;
335 
336  request = child;
337  RDEBUG3("parallel - child %s (%d/%d) DETACHED",
338  request->name,
339  i + 1, state->num_children);
340  request = parent;
341  }
342 
343  state->children[i].state = CHILD_DETACHED;
344 
345  /*
346  * Detach the child, and insert
347  * it into the backlog.
348  */
349  if ((unlang_subrequest_lifetime_set(child) < 0) || (request_detach(child) < 0)) {
350  request = child;
351  RPEDEBUG("Failed detaching request");
352  talloc_free(child);
353 
355  }
356  /*
357  * If the children don't start detached
358  * push a function onto the stack to
359  * notify the parent when the child is
360  * done.
361  */
362  } else {
363  unlang_stack_frame_t *child_frame;
364 
365  if (unlang_function_push(child,
366  NULL,
369  ~FR_SIGNAL_DETACH,
371  &state->children[i]) < 0) goto error;
372  child_frame = frame_current(child);
373  return_point_set(child_frame); /* Don't unwind this frame */
374 
375  state->children[i].num = i;
376  state->children[i].name = talloc_bstrdup(state, child->name);
377  state->children[i].request = child;
378  state->children[i].state = CHILD_RUNNABLE;
379 
380  }
381  interpret_child_init(child);
382 
383  /*
384  * Push the first instruction for
385  * the child to run.
386  */
387  if (unlang_interpret_push(child,
390  state->detach ? UNLANG_TOP_FRAME : UNLANG_SUB_FRAME) < 0) goto error;
391  }
392 
393  /*
394  * If all children start detached,
395  * then we're done.
396  */
397  if (state->detach) return UNLANG_ACTION_CALCULATE_RESULT;
398 
399  /*
400  * Don't call this function again when
401  * the parent resumes, instead call
402  * a function to process the results
403  * of the children.
404  */
406 
407  /*
408  * Yield to the children
409  *
410  * They scamper off to play on their
411  * own when they're all done, the last
412  * one tells the parent, so it can resume,
413  * and gather up all the results.
414  */
415  return UNLANG_ACTION_YIELD;
416 }
417 
418 /** Send a signal from parent request to all of it's children
419  *
420  */
422  unlang_stack_frame_t *frame, fr_signal_t action)
423 {
424  unlang_parallel_state_t *state = talloc_get_type_abort(frame->state, unlang_parallel_state_t);
425  int i;
426 
427  if (action == FR_SIGNAL_CANCEL) {
428  for (i = 0; i < state->num_children; i++) unlang_parallel_cancel_child(state, i);
429 
430  return;
431  }
432 
433  /*
434  * Signal all of the runnable/running children.
435  */
436  for (i = 0; i < state->num_children; i++) {
437  if (state->children[i].state != CHILD_RUNNABLE) continue;
438 
439  unlang_interpret_signal(state->children[i].request, action);
440  }
441 }
442 
444 {
445  unlang_t const *instruction;
446  unlang_group_t *g;
447  unlang_parallel_t *gext;
449 
450  int i;
451 
453  if (!g->num_children) {
454  *p_result = RLM_MODULE_NOOP;
456  }
457 
458  gext = unlang_group_to_parallel(g);
459 
460  /*
461  * Allocate an array for the children.
462  */
463  MEM(frame->state = state = _talloc_zero_pooled_object(request,
464  sizeof(unlang_parallel_state_t) +
465  (sizeof(state->children[0]) * g->num_children),
466  "unlang_parallel_state_t",
467  g->num_children,
468  (talloc_array_length(request->name) * 2)));
469  if (!state) {
470  *p_result = RLM_MODULE_FAIL;
472  }
473 
474  (void) talloc_set_type(state, unlang_parallel_state_t);
475  state->result = RLM_MODULE_FAIL;
476  state->priority = -1; /* as-yet unset */
477  state->detach = gext->detach;
478  state->clone = gext->clone;
479  state->num_children = g->num_children;
480 
481  /*
482  * Initialize all of the children.
483  */
484  for (i = 0, instruction = g->children; instruction != NULL; i++, instruction = instruction->next) {
485  state->children[i].state = CHILD_INIT;
486  state->children[i].instruction = instruction;
487  }
488 
490  return unlang_parallel_process(p_result, request, frame);
491 }
492 
494 {
496  &(unlang_op_t){
497  .name = "parallel",
498  .interpret = unlang_parallel,
499  .signal = unlang_parallel_signal,
500  .debug_braces = true
501  });
502 }
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 RCSID(id)
Definition: build.h:444
#define UNUSED
Definition: build.h:313
fr_table_num_sorted_t const mod_rcode_table[]
Definition: compile.c:72
#define unlang_function_push(_request, _func, _repeat, _signal, _sigmask, _top_frame, _uctx)
Push a generic function onto the unlang stack.
Definition: function.h:111
static bool fr_heap_entry_inserted(fr_heap_index_t heap_idx)
Check if an entry is inserted into a heap.
Definition: heap.h:124
void unlang_interpret_request_done(request_t *request)
Indicate to the caller of the interpreter that this request is complete.
Definition: interpret.c:1062
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition: interpret.c:1340
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:1184
#define UNLANG_SUB_FRAME
Definition: interpret.h:36
#define UNLANG_TOP_FRAME
Definition: interpret.h:35
Private declarations for the unlang interpreter.
static void interpret_child_init(request_t *request)
#define RDEBUG_ENABLED3
True if request debug level 1-3 messages are enabled.
Definition: log.h:335
#define RDEBUG3(fmt,...)
Definition: log.h:343
#define RPEDEBUG(fmt,...)
Definition: log.h:376
#define RDEBUG4(fmt,...)
Definition: log.h:344
void unlang_register(int type, unlang_op_t *op)
Register an operation with the interpreter.
Definition: base.c:65
request_t * unlang_io_subrequest_alloc(request_t *parent, fr_dict_t const *namespace, bool detachable)
Allocate a child request based on the parent.
Definition: io.c:39
talloc_free(reap)
static char * stack[MAX_STACK]
Definition: radmin.c:158
Declarations for the unlang module interface.
int fr_pair_list_copy(TALLOC_CTX *ctx, fr_pair_list_t *to, fr_pair_list_t const *from)
Duplicate a list of pairs.
Definition: pair.c:2316
static unlang_action_t unlang_parallel(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Definition: parallel.c:443
void unlang_parallel_init(void)
Definition: parallel.c:493
static unlang_action_t unlang_parallel_resume(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Definition: parallel.c:187
static unlang_action_t unlang_parallel_child_done(UNUSED rlm_rcode_t *p_result, UNUSED int *p_priority, request_t *request, void *uctx)
When the chld is done, tell the parent that we've exited.
Definition: parallel.c:144
static void unlang_parallel_signal(UNUSED request_t *request, unlang_stack_frame_t *frame, fr_signal_t action)
Send a signal from parent request to all of it's children.
Definition: parallel.c:421
static unlang_action_t unlang_parallel_process(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Run one or more sub-sections from the parallel section.
Definition: parallel.c:253
static void unlang_parallel_child_signal(request_t *request, UNUSED fr_signal_t action, void *uctx)
Signal handler to deal with UNLANG_ACTION_DETACH.
Definition: parallel.c:113
static void unlang_parallel_cancel_child(unlang_parallel_state_t *state, int i)
Cancel a specific child.
Definition: parallel.c:37
Declarations for the unlang "parallel" keyword.
bool detach
are we creating the child detached
Definition: parallel_priv.h:66
char * name
Cache the request name.
Definition: parallel_priv.h:55
unlang_parallel_child_t children[]
Array of children.
Definition: parallel_priv.h:69
int num_complete
How many children are complete.
Definition: parallel_priv.h:64
int num_children
How many children are executing.
Definition: parallel_priv.h:63
request_t * request
Child request.
Definition: parallel_priv.h:54
int num
The child number.
Definition: parallel_priv.h:51
bool clone
are the children cloned
Definition: parallel_priv.h:67
unlang_t const * instruction
broken out of g->children
Definition: parallel_priv.h:56
bool detach
are we creating the child detached
Definition: parallel_priv.h:74
static unlang_parallel_t * unlang_group_to_parallel(unlang_group_t *g)
Cast a group structure to the parallel keyword extension.
Definition: parallel_priv.h:81
@ CHILD_RUNNABLE
Running/runnable.
Definition: parallel_priv.h:40
@ CHILD_DETACHED
Child has detached.
Definition: parallel_priv.h:42
@ CHILD_INIT
Initial state.
Definition: parallel_priv.h:39
@ CHILD_EXITED
Child has exited.
Definition: parallel_priv.h:41
@ CHILD_CANCELLED
Child was cancelled.
Definition: parallel_priv.h:43
@ CHILD_DONE
The child has completed.
Definition: parallel_priv.h:44
unlang_parallel_child_state_t state
State of the child.
Definition: parallel_priv.h:53
Each parallel child has a state, and an associated request.
Definition: parallel_priv.h:50
#define REDEBUG(fmt,...)
Definition: radclient.h:52
rlm_rcode_t
Return codes indicating the result of the module call.
Definition: rcode.h:40
@ RLM_MODULE_FAIL
Module failed, don't reply.
Definition: rcode.h:42
@ RLM_MODULE_REJECT
Immediately reject the request.
Definition: rcode.h:41
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition: rcode.h:48
int request_detach(request_t *child)
Unlink a subrequest from its parent.
Definition: request.c:664
#define REQUEST_VERIFY(_x)
Definition: request.h:275
fr_signal_t
Definition: signal.h:48
RETURN_MODULE_FAIL
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
int unlang_subrequest_lifetime_set(request_t *request)
Initialize a detached child.
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition: table.h:253
char * talloc_bstrdup(TALLOC_CTX *ctx, char const *in)
Binary safe strdup function.
Definition: talloc.c:424
unlang_t * next
Next node (executed on UNLANG_ACTION_EXECUTE_NEXT et al).
Definition: unlang_priv.h:124
void * state
Stack frame specialisations.
Definition: unlang_priv.h:304
#define UNLANG_NEXT_STOP
Definition: unlang_priv.h:102
static unlang_group_t * unlang_generic_to_group(unlang_t const *p)
Definition: unlang_priv.h:539
#define MOD_ACTION_REJECT
Definition: unlang_priv.h:45
#define MOD_ACTION_RETURN
Definition: unlang_priv.h:44
static void return_point_set(unlang_stack_frame_t *frame)
Definition: unlang_priv.h:356
static int stack_depth_current(request_t *request)
Definition: unlang_priv.h:401
@ UNLANG_TYPE_PARALLEL
execute statements in parallel
Definition: unlang_priv.h:62
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.
Definition: unlang_priv.h:526
unlang_t const * instruction
The unlang node we're evaluating.
Definition: unlang_priv.h:289
unlang_process_t process
function to call for interpreting this stack frame
Definition: unlang_priv.h:292
unlang_t * children
Children beneath this group.
Definition: unlang_priv.h:157
static unlang_stack_frame_t * frame_current(request_t *request)
Definition: unlang_priv.h:394
Generic representation of a grouping.
Definition: unlang_priv.h:155
An unlang operation.
Definition: unlang_priv.h:214
A node in a graph of unlang_op_t (s) that we execute.
Definition: unlang_priv.h:122
Our interpreter stack, as distinct from the C stack.
Definition: unlang_priv.h:288
An unlang stack associated with a request.
Definition: unlang_priv.h:322
static fr_slen_t parent
Definition: pair.h:844