The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
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: c8efa28e7637bb174298605681b77f810b9c4e87 $
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
26
27RCSID("$Id: c8efa28e7637bb174298605681b77f810b9c4e87 $")
28
29#include <freeradius-devel/server/rcode.h>
30#include <freeradius-devel/server/signal.h>
31#include <freeradius-devel/server/state.h>
32#include <freeradius-devel/server/request.h>
33#include <freeradius-devel/util/table.h>
34
35#include "action.h"
36#include "interpret.h"
37#include "subrequest.h"
38#include "interpret_priv.h"
39#include "unlang_priv.h"
40#include "parallel_priv.h"
41#include "child_request_priv.h"
42
43/** Cancel a specific child
44 *
45 * For most states we just change the current state to CANCELLED. For the RUNNABLE state
46 * we need to signal the child to cancel itself.
47 *
48 * We don't free any requests here, we just mark them up so their rcodes are ignored when
49 * the parent is resumed, the parent then frees the child, once we're sure its done being
50 * run through the intepreter.
51 */
52static inline CC_HINT(always_inline) void unlang_parallel_cancel_child(unlang_parallel_state_t *state, unlang_child_request_t *cr)
53{
54 request_t *child = cr->request;
55 request_t *request = child->parent; /* For debug messages */
56 unlang_child_request_state_t child_state = cr->state;
57
58 switch (cr->state) {
59 case CHILD_INIT:
61 fr_assert(!cr->request);
62 break;
63
64 case CHILD_EXITED:
65 cr->state = CHILD_CANCELLED; /* Don't process its return code */
66 break;
67
68 case CHILD_RUNNABLE: /* Don't check runnable_id, may be yielded */
69 /*
70 * Signal the child to stop
71 *
72 * The signal function cleans up the request
73 * and signals anything that was tracking it
74 * that it's now complete.
75 */
77
78 /*
79 * We don't free the request here, we wait
80 * until it signals us that it's done.
81 */
82 break;
83
84 case CHILD_DONE:
86 break;
87
88 case CHILD_DETACHED: /* Can't signal detached requests*/
89 fr_assert(!cr->request);
90 return;
91
92 case CHILD_CANCELLED:
93 break;
94
95 case CHILD_FREED:
96 return;
97 }
98
99 RDEBUG3("parallel - child %s (%d/%d) CANCELLED, previously %s",
100 cr->name, cr->num, state->num_children,
101 fr_table_str_by_value(unlang_child_states_table, child_state, "<INVALID>"));
102}
103
104/** Send a signal from parent request to all of it's children
105 *
106 */
108 unlang_stack_frame_t *frame, fr_signal_t action)
109{
110 unlang_parallel_state_t *state = talloc_get_type_abort(frame->state, unlang_parallel_state_t);
111 unsigned int i;
112
113 /*
114 * Signal any runnable children to get them to exit
115 */
116 if (action == FR_SIGNAL_CANCEL) {
117 for (i = 0; i < state->num_children; i++) unlang_parallel_cancel_child(state, &state->children[i]);
118
119 return;
120 }
121
122 /*
123 * Signal all of the runnable/running children.
124 */
125 for (i = 0; i < state->num_children; i++) {
126 if (state->children[i].state != CHILD_RUNNABLE) continue;
127
128 unlang_interpret_signal(state->children[i].request, action);
129 }
130}
131
132
134{
135 unlang_parallel_state_t *state = talloc_get_type_abort(frame->state, unlang_parallel_state_t);
136 unsigned int i;
137 int priority;
138 rlm_rcode_t result;
139
140 fr_assert(state->num_runnable == 0);
141
142 for (i = 0; i < state->num_children; i++) {
143 if (state->children[i].state != CHILD_EXITED) continue;
144
146
147 RDEBUG3("parallel - child %s (%d/%d) DONE",
148 state->children[i].name,
149 i + 1, state->num_children);
150
151 state->children[i].state = CHILD_DONE;
152
153 priority = ((unlang_stack_t *)state->children[i].request->stack)->priority;
154 result = ((unlang_stack_t *)state->children[i].request->stack)->result;
155
156 /*
157 * Return isn't allowed to make it back
158 * to the parent... Not sure this is
159 * the correct behaviour, but it's what
160 * was there before.
161 */
162 if (priority == MOD_ACTION_RETURN) {
163 priority = 0;
164 } else if (priority == MOD_ACTION_REJECT) {
165 result = RLM_MODULE_REJECT;
166 priority = 0;
167 }
168
169 /*
170 * Do priority over-ride.
171 */
172 if (priority > state->priority) {
173 state->result = result;
174 state->priority = priority;
175
176 RDEBUG4("** [%i] %s - over-riding result from higher priority to (%s %d)",
177 stack_depth_current(request), __FUNCTION__,
178 fr_table_str_by_value(mod_rcode_table, result, "<invalid>"),
179 priority);
180 }
181 }
182
183 /*
184 * Reap the children....
185 */
186 for (i = 0; i < state->num_children; i++) {
187 if (!state->children[i].request) continue;
188
190
192
193 state->children[i].state = CHILD_FREED;
194 }
195
196 *p_result = state->result;
198}
199
201{
202 unlang_t const *instruction;
204 unlang_parallel_t *gext;
206
207 int i;
208
210 if (!g->num_children) {
211 *p_result = RLM_MODULE_NOOP;
213 }
214
215 gext = unlang_group_to_parallel(g);
216
217 /*
218 * Allocate an array for the children.
219 */
220 MEM(frame->state = state = _talloc_zero_pooled_object(request,
222 (sizeof(state->children[0]) * g->num_children),
223 "unlang_parallel_state_t",
224 g->num_children,
225 (talloc_array_length(request->name) * 2)));
226 if (!state) {
227 *p_result = RLM_MODULE_FAIL;
229 }
230
231 (void) talloc_set_type(state, unlang_parallel_state_t);
232 state->result = RLM_MODULE_FAIL;
233 state->priority = -1; /* as-yet unset */
234 state->detach = gext->detach;
235 state->clone = gext->clone;
236 state->num_children = g->num_children;
237
238 /*
239 * Initialize all of the children.
240 */
241 for (i = 0, instruction = g->children; instruction != NULL; i++, instruction = instruction->next) {
242 request_t *child;
243
244 child = unlang_io_subrequest_alloc(request,
245 request->proto_dict, state->detach);
246 child->packet->code = request->packet->code;
247
248 RDEBUG3("parallel - child %s (%d/%d) INIT",
249 child->name,
250 i + 1, state->num_children);
251
252 if (state->clone) {
253 /*
254 * Note that we do NOT copy the
255 * Session-State list! That
256 * contains state information for
257 * the parent.
258 */
259 if ((fr_pair_list_copy(child->request_ctx,
260 &child->request_pairs,
261 &request->request_pairs) < 0) ||
262 (fr_pair_list_copy(child->reply_ctx,
263 &child->reply_pairs,
264 &request->reply_pairs) < 0) ||
265 (fr_pair_list_copy(child->control_ctx,
266 &child->control_pairs,
267 &request->control_pairs) < 0)) {
268 REDEBUG("failed copying lists to child");
269 error:
270
271 /*
272 * Remove all previously
273 * spawned children.
274 */
275 for (--i; i >= 0; i--) {
277 state->children[i].state = CHILD_FREED;
278 }
279
281 }
282 }
283
284 /*
285 * Initialise our frame state, and push the first
286 * instruction onto the child's stack.
287 *
288 * This instruction will mark the parent as runnable
289 * when it is executed.
290 *
291 * We only do this if the requests aren't detached.
292 * If they are detached, this repeat function would
293 * be immediately disabled, so no point...
294 */
295 if (!state->detach) {
296 if (unlang_child_request_init(state, &state->children[i], child, NULL, &state->num_runnable,
297 frame_current(request)->instruction, false) < 0) goto error;
298 fr_assert(state->children[i].state == CHILD_INIT);
299 } else {
300 state->children[i].num = i;
301 state->children[i].request = child;
302 }
303
304 /*
305 * Push the first instruction for the child to run,
306 * which in case of parallel, is the child's
307 * subsection within the parallel block.
308 */
309 if (unlang_interpret_push(child,
310 instruction, RLM_MODULE_FAIL,
312 state->detach ? UNLANG_TOP_FRAME : UNLANG_SUB_FRAME) < 0) goto error;
313 }
314
315 /*
316 * Now we're sure all the children are initialised
317 * start them running.
318 */
319 if (state->detach) {
320 for (i = 0; i < (int)state->num_children; i++) {
321 if (RDEBUG_ENABLED3) {
322 request_t *parent = request;
323
324 request = state->children[i].request;
325 RDEBUG3("parallel - child %s (%d/%d) DETACHED",
326 request->name,
327 i + 1, state->num_children);
328 request = parent;
329 }
330
331 /*
332 * Adds to the runnable queue
333 */
335
336 /*
337 * Converts to a detached request
338 */
340 }
341
342 /*
343 * We are now done, all the children are detached
344 * so we don't need to wait around for them to complete.
345 */
347 }
348
349 for (i = 0; i < (int)state->num_children; i++) {
350 /*
351 * Ensure we restore the session state information
352 * into the child.
353 */
354 if (state->children[i].config.session_unique_ptr) {
356 state->children[i].config.session_unique_ptr,
357 state->children[i].num);
358 }
359
360 /*
361 * Ensures the child is setup correctly and adds
362 * it into the runnable queue of whatever owns
363 * the interpreter.
364 */
366 state->children[i].state = CHILD_RUNNABLE;
367 }
368
369 /*
370 * Don't call this function again when the parent resumes,
371 * instead call a function to process the results
372 * of the children.
373 */
375
376 /*
377 * Yield to the children
378 *
379 * They scamper off to play on their own when they're all done,
380 * the last one tells the parent, so it can resume,
381 * and gather up the results, and mercilessly reap the children.
382 */
383 return UNLANG_ACTION_YIELD;
384}
385
387{
389 &(unlang_op_t){
390 .name = "parallel",
391 .interpret = unlang_parallel,
392 .signal = unlang_parallel_signal,
394 });
395}
Unlang interpreter actions.
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:41
#define RCSID(id)
Definition build.h:485
#define UNUSED
Definition build.h:317
fr_table_num_ordered_t const unlang_child_states_table[]
int unlang_child_request_init(TALLOC_CTX *ctx, unlang_child_request_t *out, request_t *child, rlm_rcode_t *p_result, unsigned int *sibling_count, void const *unique_session_ptr, bool free_child)
Initialize a child request.
struct unlang_child_request_t::@100 config
request_t * request
Child request. The actual request the child will run.
char const * name
Cache the request name.
unlang_child_request_state_t state
State of the child.
unlang_child_request_state_t
Parallel child states.
@ CHILD_RUNNABLE
Running/runnable.
@ CHILD_DETACHED
Child has detached, we can't signal it or communicate with it anymore.
@ CHILD_INIT
Initial state, has no request allocated.
@ CHILD_EXITED
Child has run to completion, and is waiting to be reaped.
@ CHILD_CANCELLED
Child was cancelled.
@ CHILD_FREED
The child has been freed.
@ CHILD_DONE
The child has been processed by the parent the request should still exist, and should be freed.
int num
The child number.
Each child has a state, a number, a request, and a count of their siblings.
fr_table_num_sorted_t const mod_rcode_table[]
Definition compile.c:78
#define MEM(x)
Definition debug.h:36
bool unlang_request_is_scheduled(request_t const *request)
Return whether a request is currently scheduled.
Definition interpret.c:1313
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:143
void unlang_interpret_signal(request_t *request, fr_signal_t action)
Send a signal (usually stop) to a request.
Definition interpret.c:1147
Declarations for the unlang interpreter.
#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 RDEBUG4(fmt,...)
Definition log.h:344
void unlang_register(int type, unlang_op_t *op)
Register an operation with the interpreter.
Definition base.c:63
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
@ MOD_ACTION_RETURN
Definition mod_action.h:40
@ MOD_ACTION_REJECT
Definition mod_action.h:41
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:2329
static unlang_action_t unlang_parallel(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Definition parallel.c:200
void unlang_parallel_init(void)
Definition parallel.c:386
static unlang_action_t unlang_parallel_resume(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Definition parallel.c:133
static void unlang_parallel_cancel_child(unlang_parallel_state_t *state, unlang_child_request_t *cr)
Cancel a specific child.
Definition parallel.c:52
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:107
Declarations for the unlang "parallel" keyword.
bool detach
are we creating the child detached
static unlang_parallel_t * unlang_group_to_parallel(unlang_group_t *g)
Cast a group structure to the parallel keyword extension.
unsigned int num_runnable
How many children are complete.
bool clone
are the children cloned
bool detach
are we creating the child detached
unlang_child_request_t children[]
Array of children.
unsigned int num_children
How many children are executing.
#define fr_assert(_expr)
Definition rad_assert.h:38
#define REDEBUG(fmt,...)
Definition radclient.h:52
#define RETURN_MODULE_FAIL
Definition rcode.h:57
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
#define REQUEST_VERIFY(_x)
Definition request.h:305
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:857
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
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:288
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:772
Private interpreter structures and functions.
unlang_t * next
Next node (executed on UNLANG_ACTION_EXECUTE_NEXT et al).
void * state
Stack frame specialisations.
#define UNLANG_NEXT_STOP
Definition unlang_priv.h:97
static unlang_group_t * unlang_generic_to_group(unlang_t const *p)
static unlang_stack_frame_t * frame_current(request_t *request)
static int stack_depth_current(request_t *request)
@ UNLANG_TYPE_PARALLEL
execute statements in parallel
Definition unlang_priv.h:53
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_t const * instruction
The unlang node we're evaluating.
@ UNLANG_OP_FLAG_DEBUG_BRACES
Print debug braces.
@ UNLANG_OP_FLAG_RCODE_SET
Set request->rcode to the result of this operation.
@ UNLANG_OP_FLAG_NO_CANCEL
Must not be cancelled.
unlang_t * children
Children beneath this group.
Generic representation of a grouping.
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.
static fr_slen_t parent
Definition pair.h:845