The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
child_request.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: 323bbd2d62618f50793a7bef14d3122492c145b8 $
19 *
20 * @file unlang/child_request.c
21 * @brief Common child request management code.
22 *
23 * @copyright 2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
24 */
25#include <freeradius-devel/server/state.h>
26#include <freeradius-devel/server/request.h>
27#include <freeradius-devel/server/signal.h>
28#include <freeradius-devel/server/rcode.h>
29#include <freeradius-devel/unlang/mod_action.h>
30#include <freeradius-devel/util/talloc.h>
31
32#include "unlang_priv.h"
33#include "child_request_priv.h"
34
35typedef struct {
36 unlang_child_request_t *cr; //!< A pointer to memory in the parent's frame state
37 ///< allocated for this child to write results to.
39
41 { L("CANCELLED"), CHILD_CANCELLED },
42 { L("DETACH"), CHILD_DETACHED },
43 { L("DONE"), CHILD_DONE },
44 { L("EXITED"), CHILD_EXITED },
45 { L("INIT"), CHILD_INIT },
46 { L("RUNNABLE"), CHILD_RUNNABLE }
47};
49
50/** Process a detach signal in the child
51 *
52 * This processes any detach signals the child receives
53 * The child doesn't actually do the detaching
54 */
56{
59
60 /*
61 * We're already detached so we don't
62 * need to notify the parent we're
63 * waking up, and we don't need to detach
64 * again...
65 */
66 if (request_is_detached(request)) return;
67
68 state = talloc_get_type_abort(frame->state, unlang_frame_state_child_request_t);
69 cr = state->cr; /* Can't use talloc_get_type_abort, may be an array element */
70
71 /*
72 * Ignore signals which aren't detach, and ar
73 * and ignore the signal if we have no parent.
74 */
75 switch (action) {
77 /*
78 * Place child's state back inside the parent
79 */
80 if (cr->config.session_unique_ptr) fr_state_store_in_parent(request,
81 cr->config.session_unique_ptr,
82 cr->num);
83
84 RDEBUG3("Detached - Removing subrequest from parent, and marking parent as runnable");
85
86 /*
87 * Indicate to the parent there's no longer a child
88 */
90
91 /*
92 * Don't run the request-done frame, the request will have been
93 * detached by whatever signalled us, so we can't inform the parent
94 * when we exit.
95 */
96 repeatable_clear(frame);
97
98 frame->result_p = &frame->scratch_result;
99
100 /*
101 * Tell the parent to resume if all the request's siblings are done
102 */
103 if (!cr->sibling_count || (--(*cr->sibling_count) == 0)) unlang_interpret_mark_runnable(request->parent);
104 break;
105
106 /*
107 * This frame is not cancellable, so FR_SIGNAL_CANCEL
108 * does nothing. If the child is cancelled in its
109 * entirety, then its stack will unwind up to this point
110 * and unlang_subrequest_child_done will mark the
111 * parent as runnable. We don't need to do anything here.
112 *
113 * We set the state for debugging purposes, and to reduce
114 * the amount of work we do in unlang_subrequest_child_done.
115 */
116 case FR_SIGNAL_CANCEL:
118 break;
119
120 default:
121 return;
122 }
123}
124
125/** When the child is done, tell the parent that we've exited.
126 *
127 * This is pushed as a frame at the top of the child's stack, so when
128 * the child is done executing, it runs this to inform the parent
129 * that its done.
130 */
132{
134 unlang_child_request_t *cr = state->cr; /* Can't use talloc_get_type_abort, may be an array element */
135
136 /*
137 * The repeat function for this frame should've
138 * been cleared, so this function should not run
139 * for detached requests.
140 */
142
143 switch (cr->state) {
144 case CHILD_RUNNABLE:
145 /*
146 * Place child state back inside the parent
147 */
148 if (cr->config.session_unique_ptr) {
150 cr->config.session_unique_ptr,
151 cr->num);
152 }
153 if (cr->p_result) *cr->p_result = cr->result;
154 break;
155
156 case CHILD_CANCELLED:
157 /*
158 * Child session state is no longer consistent
159 * after cancellation, so discard it.
160 */
161 if (cr->config.session_unique_ptr) {
162 fr_state_discard_child(request->parent, cr->config.session_unique_ptr, cr->num);
163 }
164 break;
165
166 default:
167 fr_assert_msg(0, "child %s resumed top frame with invalid state %s",
168 request->name,
170 }
171
172 cr->state = CHILD_EXITED;
173
174 /*
175 * Tell the parent to resume if all the request's siblings are done
176 */
177 if (!cr->sibling_count || (--(*cr->sibling_count) == 0)) {
178 RDEBUG3("All children have exited, marking parent %s as runnable", request->parent->name);
179 unlang_interpret_mark_runnable(request->parent);
180 } else {
181 RDEBUG3("Child %s exited, %u sibling(s) remaining",
182 request->name,
183 *cr->sibling_count);
184 }
185
187}
188
189/** Push a resumption frame onto a child's stack
190 *
191 * Push a frame onto the stack of the child to inform the parent when it's complete.
192 * An additional frame is pushed onto the child's stack by the 'run' function which
193 * executes in the context of the parent.
194 *
195 * @param[in] cr state for this child request. This is a pointer
196 * to a structure in the parent's frame state.
197 * @return
198 * - 0 on success.
199 * - -1 on failure.
200 */
202{
203 request_t *child = cr->request;
206
207 static unlang_t inform_parent = {
209 .name = "child-request",
210 .debug_name = "child-request-resume",
211 .actions = {
212 .actions = {
213 [RLM_MODULE_REJECT] = 0,
214 [RLM_MODULE_FAIL] = 0,
215 [RLM_MODULE_OK] = 0,
216 [RLM_MODULE_HANDLED] = 0,
217 [RLM_MODULE_INVALID] = 0,
220 [RLM_MODULE_NOOP] = 0,
222 },
223 .retry = RETRY_INIT,
224 }
225 };
226
227 /* Sets up the frame for us to use immediately */
228 if (unlikely(unlang_interpret_push_instruction(&cr->result, child, &inform_parent,
229 FRAME_CONF(RLM_MODULE_NOOP, true)) < 0)) {
230 return -1;
231 }
232
233 frame = frame_current(child);
234 state = frame->state;
235 state->cr = cr;
236 repeatable_set(frame); /* Run this on the way back up */
237
238 return 0;
239}
240
241/** Initialize a child request
242 *
243 * This initializes the child request result and configuration structure,
244 * and pushes a resumption frame onto the child's stack.
245 *
246 * @param[in] ctx Memory to use for any additional memory allocated
247 * to the unlang_child_request_t.
248 * @param[out] out Child request to initialize.
249 * @param[in] child The child request to initialize.
250 * @param[in] p_result Where to write out the rcode from the child.
251 * @param[in,out] sibling_count If non-null the bumber of siblings. This is incremented
252 * for each child created.
253 * @param[in] unique_session_ptr Unique session pointer for this child.
254 * If NULL session data won't be stored/restored for the child.
255 * @param[in] free_child Free the child when done?
256 * @return
257 * - 0 on success.
258 * - -1 on failure.
259 */
261 unlang_result_t *p_result, unsigned int *sibling_count, void const *unique_session_ptr, bool free_child)
262{
264 .name = talloc_bstrdup(ctx, child->name),
265 .num = sibling_count ? (*sibling_count)++ : 0,
266 .request = child,
267 .state = CHILD_INIT,
268 .sibling_count = sibling_count,
269 .config = {
270 .session_unique_ptr = unique_session_ptr,
271 .free_child = free_child
272 },
273 .p_result = p_result
274 };
275
277}
278
280{
282 &(unlang_op_t){
283 .name = "child-request",
284 .interpret = unlang_child_request_done,
286 /*
287 * Frame can't be cancelled, because children need to
288 * write out status to the parent. If we don't do this,
289 * then all children must be detachable and must detach
290 * so they don't try and write out status to a "done"
291 * parent.
292 *
293 * It's easier to allow the child/parent relationship
294 * to end normally so that non-detachable requests are
295 * guaranteed the parent still exists.
296 */
298 .frame_state_size = sizeof(unlang_frame_state_child_request_t),
299 .frame_state_type = "unlang_frame_state_child_request_t"
300 });
301
302 return 0;
303}
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
#define L(_str)
Helper for initialising arrays of string literals.
Definition build.h:209
#define unlikely(_x)
Definition build.h:383
#define UNUSED
Definition build.h:317
#define NUM_ELEMENTS(_t)
Definition build.h:339
unlang_child_request_t * cr
A pointer to memory in the parent's frame state allocated for this child to write results to.
int unlang_child_request_op_init(void)
static unlang_action_t unlang_child_request_done(UNUSED unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame)
When the child is done, tell the parent that we've exited.
fr_table_num_ordered_t const unlang_child_states_table[]
static void unlang_child_request_signal(request_t *request, UNUSED unlang_stack_frame_t *frame, fr_signal_t action)
Process a detach signal in the child.
static int unlang_child_request_stack_init(unlang_child_request_t *cr)
Push a resumption frame onto a child's stack.
int unlang_child_request_init(TALLOC_CTX *ctx, unlang_child_request_t *out, request_t *child, unlang_result_t *p_result, unsigned int *sibling_count, void const *unique_session_ptr, bool free_child)
Initialize a child request.
size_t unlang_child_states_table_len
struct unlang_child_request_t::@100 config
request_t * request
Child request. The actual request the child will run.
unlang_result_t result
The result of the child request.
unlang_child_request_state_t state
State of the child.
unsigned int * sibling_count
Number of siblings.
unlang_result_t * p_result
Pointer to the result of the child request.
@ 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_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.
#define fr_assert_msg(_x, _msg,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition debug.h:210
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition interpret.c:1616
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
#define FRAME_CONF(_default_rcode, _top_frame)
Definition interpret.h:153
#define RDEBUG3(fmt,...)
Definition log.h:343
void unlang_register(int type, unlang_op_t *op)
Register an operation with the interpreter.
Definition base.c:63
#define fr_assert(_expr)
Definition rad_assert.h:38
@ 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_NOOP
Module succeeded without doing anything.
Definition rcode.h:48
@ RLM_MODULE_HANDLED
The module handled the request, so stop.
Definition rcode.h:44
#define request_is_detached(_x)
Definition request.h:186
void fr_state_discard_child(request_t *parent, void const *unique_ptr, int unique_int)
Remove state from a child.
Definition state.c:907
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:811
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
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition table.h:772
An element in an arbitrarily ordered array of name to num mappings.
Definition table.h:57
char * talloc_bstrdup(TALLOC_CTX *ctx, char const *in)
Binary safe strdup function.
Definition talloc.c:558
Private interpreter structures and functions.
static void repeatable_clear(unlang_stack_frame_t *frame)
void * state
Stack frame specialisations.
static unlang_stack_frame_t * frame_current(request_t *request)
@ UNLANG_TYPE_CHILD_REQUEST
a frame at the top of a child's request stack used to signal the parent when the child is complete.
Definition unlang_priv.h:67
@ UNLANG_OP_FLAG_NO_FORCE_UNWIND
Must not be cancelled.
static void repeatable_set(unlang_stack_frame_t *frame)
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.
#define RETRY_INIT
Definition retry.h:39
static size_t char ** out
Definition value.h:1020