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: bea7c6cf3a5e53ae71ccbd4dd500112d12483888 $
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
29#include "lib/server/rcode.h"
30#include "lib/util/talloc.h"
31#include "unlang_priv.h"
32#include "child_request_priv.h"
33
34typedef struct {
35 unlang_child_request_t *cr; //!< A pointer to memory in the parent's frame state
36 ///< allocated for this child to write results to.
38
40 { L("CANCELLED"), CHILD_CANCELLED },
41 { L("DETACH"), CHILD_DETACHED },
42 { L("DONE"), CHILD_DONE },
43 { L("EXITED"), CHILD_EXITED },
44 { L("INIT"), CHILD_INIT },
45 { L("RUNNABLE"), CHILD_RUNNABLE }
46};
48
49/** Process a detach signal in the child
50 *
51 * This processes any detach signals the child receives
52 * The child doesn't actually do the detaching
53 */
55{
58
59 /*
60 * We're already detached so we don't
61 * need to notify the parent we're
62 * waking up, and we don't need to detach
63 * again...
64 */
65 if (request_is_detached(request)) return;
66
67 state = talloc_get_type_abort(frame->state, unlang_frame_state_child_request_t);
68 cr = state->cr; /* Can't use talloc_get_type_abort, may be an array element */
69
70 /*
71 * Ignore signals which aren't detach, and ar
72 * and ignore the signal if we have no parent.
73 */
74 switch (action) {
76 /*
77 * Place child's state back inside the parent
78 */
79 if (cr->config.session_unique_ptr) fr_state_store_in_parent(request,
80 cr->config.session_unique_ptr,
81 cr->num);
82
83 RDEBUG3("Detached - Removing subrequest from parent, and marking parent as runnable");
84
85 /*
86 * Indicate to the parent there's no longer a child
87 */
89
90 /*
91 * Don't run the request-done frame, the request will have been
92 * detached by whatever signalled us, so we can't inform the parent
93 * when we exit.
94 */
95 repeatable_clear(frame);
96
97 /*
98 * Tell the parent to resume if all the request's siblings are done
99 */
100 if (!cr->sibling_count || (--(*cr->sibling_count) == 0)) unlang_interpret_mark_runnable(request->parent);
101 break;
102
103 /*
104 * This frame is not cancellable, so FR_SIGNAL_CANCEL
105 * does nothing. If the child is cancelled in its
106 * entirety, then its stack will unwind up to this point
107 * and unlang_subrequest_child_done will mark the
108 * parent as runnable. We don't need to do anything here.
109 *
110 * We set the state for debugging purposes, and to reduce
111 * the amount of work we do in unlang_subrequest_child_done.
112 */
113 case FR_SIGNAL_CANCEL:
115 break;
116
117 default:
118 return;
119 }
120}
121
122/** When the child is done, tell the parent that we've exited.
123 *
124 * This is pushed as a frame at the top of the child's stack, so when
125 * the child is done executing, it runs this to inform the parent
126 * that its done.
127 */
129{
131 unlang_child_request_t *cr = state->cr; /* Can't use talloc_get_type_abort, may be an array element */
132
133 /*
134 * The repeat function for this frame should've
135 * been cleared, so this function should not run
136 * for detached requests.
137 */
139
140 switch (cr->state) {
141 case CHILD_RUNNABLE:
142 /*
143 * Place child state back inside the parent
144 */
145 if (cr->config.session_unique_ptr) {
147 cr->config.session_unique_ptr,
148 cr->num);
149 }
150
151 /*
152 * Record the child's result and the last
153 * priority. For parallel, this lets one
154 * child be used to control the rcode of
155 * the parallel keyword.
156 */
157 cr->result.rcode = *p_result;
158 cr->result.priority = frame->priority;
159 if (cr->result.p_result) *(cr->result.p_result) = cr->result.rcode;
160 break;
161
162 case CHILD_CANCELLED:
163 /*
164 * Child session state is no longer consistent
165 * after cancellation, so discard it.
166 */
167 if (cr->config.session_unique_ptr) {
168 fr_state_discard_child(request->parent, cr->config.session_unique_ptr, cr->num);
169 }
170 break;
171
172 default:
173 fr_assert_msg(0, "child %s resumed top frame with invalid state %s",
174 request->name,
176 }
177
178 cr->state = CHILD_EXITED;
179
180 /*
181 * Tell the parent to resume if all the request's siblings are done
182 */
183 if (!cr->sibling_count || (--(*cr->sibling_count) == 0)) {
184 RDEBUG3("All children have exited, marking parent %s as runnable", request->parent->name);
185 unlang_interpret_mark_runnable(request->parent);
186 } else {
187 RDEBUG3("Child %s exited, %u sibling(s) remaining",
188 request->name,
189 *cr->sibling_count);
190 }
191
193}
194
195/** Push a resumption frame onto a child's stack
196 *
197 * Push a frame onto the stack of the child to inform the parent when it's complete.
198 * An additional frame is pushed onto the child's stack by the 'run' function which
199 * executes in the context of the parent.
200 *
201 * @param[in] cr state for this child request. This is a pointer
202 * to a structure in the parent's frame state.
203 * @return
204 * - 0 on success.
205 * - -1 on failure.
206 */
208{
209 request_t *child = cr->request;
212
213 static unlang_t inform_parent = {
215 .name = "child-request",
216 .debug_name = "child-request-resume",
217 .actions = {
218 .actions = {
219 [RLM_MODULE_REJECT] = 0,
220 [RLM_MODULE_FAIL] = 0,
221 [RLM_MODULE_OK] = 0,
222 [RLM_MODULE_HANDLED] = 0,
223 [RLM_MODULE_INVALID] = 0,
226 [RLM_MODULE_NOOP] = 0,
228 },
229 .retry = RETRY_INIT,
230 }
231 };
232
233 /* Sets up the frame for us to use immediately */
234 if (unlikely(unlang_interpret_push_instruction(child, &inform_parent, RLM_MODULE_NOOP, true) < 0)) {
235 return -1;
236 }
237
238 frame = frame_current(child);
239 state = frame->state;
240 state->cr = cr;
241 repeatable_set(frame); /* Run this on the way back up */
242
243 return 0;
244}
245
246/** Initialize a child request
247 *
248 * This initializes the child request result and configuration structure,
249 * and pushes a resumption frame onto the child's stack.
250 *
251 * @param[in] ctx Memory to use for any additional memory allocated
252 * to the unlang_child_request_t.
253 * @param[out] out Child request to initialize.
254 * @param[in] child The child request to initialize.
255 * @param[in] p_result Where to write out the rcode from the child.
256 * @param[in,out] sibling_count If non-null the bumber of siblings. This is incremented
257 * for each child created.
258 * @param[in] unique_session_ptr Unique session pointer for this child.
259 * If NULL session data won't be stored/restored for the child.
260 * @param[in] free_child Free the child when done?
261 * @return
262 * - 0 on success.
263 * - -1 on failure.
264 */
266 rlm_rcode_t *p_result, unsigned int *sibling_count, void const *unique_session_ptr, bool free_child)
267{
269 .name = talloc_bstrdup(ctx, child->name),
270 .num = sibling_count ? (*sibling_count)++ : 0,
271 .request = child,
272 .state = CHILD_INIT,
273 .sibling_count = sibling_count,
274 .config = {
275 .session_unique_ptr = unique_session_ptr,
276 .free_child = free_child
277 },
278 .result = {
279 .p_result = p_result,
280 .rcode = RLM_MODULE_NOT_SET
281 }
282 };
283
285}
286
288{
290 &(unlang_op_t){
291 .name = "child-request",
292 .interpret = unlang_child_request_done,
294 /*
295 * Frame can't be cancelled, because children need to
296 * write out status to the parent. If we don't do this,
297 * then all children must be detachable and must detach
298 * so they don't try and write out status to a "done"
299 * parent.
300 *
301 * It's easier to allow the child/parent relationship
302 * to end normally so that non-detachable requests are
303 * guaranteed the parent still exists.
304 */
306 .frame_state_size = sizeof(unlang_frame_state_child_request_t),
307 .frame_state_type = "unlang_frame_state_child_request_t"
308 });
309
310 return 0;
311}
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
static unlang_action_t unlang_child_request_done(rlm_rcode_t *p_result, request_t *request, unlang_stack_frame_t *frame)
When the child is done, tell the parent that we've exited.
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)
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.
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.
static int unlang_child_request_stack_init(unlang_child_request_t *cr)
Push a resumption frame onto a child's stack.
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.
struct unlang_child_request_t::@101 result
unlang_child_request_state_t state
State of the child.
unsigned int * sibling_count
Number of siblings.
@ 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
int unlang_interpret_push_instruction(request_t *request, void *instruction, rlm_rcode_t default_rcode, bool top_frame)
Push an instruction onto the request stack for later interpretation.
Definition interpret.c:949
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition interpret.c:1360
#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
Return codes returned by modules and virtual server sections.
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:52
@ 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
Functions which we wish were included in the standard talloc distribution.
Private interpreter structures and functions.
static void repeatable_clear(unlang_stack_frame_t *frame)
void * state
Stack frame specialisations.
int priority
Result priority.
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_CANCEL
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