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: 47dbf6292397779aaf1d60d67379d095bc5b2eac $
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/unlang/mod_action.h>
27
28#include "unlang_priv.h"
29#include "child_request_priv.h"
30
31typedef struct {
32 unlang_child_request_t *cr; //!< A pointer to memory in the parent's frame state
33 ///< allocated for this child to write results to.
35
37 { L("CANCELLED"), CHILD_CANCELLED },
38 { L("DETACH"), CHILD_DETACHED },
39 { L("DONE"), CHILD_DONE },
40 { L("EXITED"), CHILD_EXITED },
41 { L("INIT"), CHILD_INIT },
42 { L("RUNNABLE"), CHILD_RUNNABLE }
43};
45
46/** Process a detach signal in the child
47 *
48 * This processes any detach signals the child receives
49 * The child doesn't actually do the detaching
50 */
52{
55
56 /*
57 * We're already detached so we don't
58 * need to notify the parent we're
59 * waking up, and we don't need to detach
60 * again...
61 */
62 if (request_is_detached(request)) return;
63
64 state = talloc_get_type_abort(frame->state, unlang_frame_state_child_request_t);
65 cr = state->cr; /* Can't use talloc_get_type_abort, may be an array element */
66
67 /*
68 * Ignore signals which aren't detached, and
69 * are ignore the signal if we have no parent.
70 */
71 switch (action) {
73 /*
74 * Place child's state back inside the parent
75 */
76 if (cr->config.session_unique_ptr) fr_state_store_in_parent(request,
77 cr->config.session_unique_ptr,
78 cr->num);
79
80 RDEBUG3("Detached - Removing subrequest from parent, and marking parent as runnable");
81
82 /*
83 * Indicate to the parent there's no longer a child
84 */
86
87 /*
88 * Don't run the request-done frame, the request will have been
89 * detached by whatever signalled us, so we can't inform the parent
90 * when we exit.
91 */
92 repeatable_clear(frame);
93
94 frame->p_result = &frame->scratch_result;
95
96 /*
97 * Tell the parent to resume if all the request's siblings are done
98 */
99 if (!cr->sibling_count || (--(*cr->sibling_count) == 0)) unlang_interpret_mark_runnable(request->parent);
100 break;
101
102 /*
103 * This frame is not cancellable, so FR_SIGNAL_CANCEL
104 * does nothing. If the child is cancelled in its
105 * entirety, then its stack will unwind up to this point
106 * and unlang_subrequest_child_done will mark the
107 * parent as runnable. We don't need to do anything here.
108 *
109 * We set the state for debugging purposes, and to reduce
110 * the amount of work we do in unlang_subrequest_child_done.
111 */
112 case FR_SIGNAL_CANCEL:
114 break;
115
116 default:
117 return;
118 }
119}
120
121/** When the child is done, tell the parent that we've exited.
122 *
123 * This is pushed as a frame at the top of the child's stack, so when
124 * the child is done executing, it runs this to inform the parent
125 * that its done.
126 */
128{
130 unlang_child_request_t *cr = state->cr; /* Can't use talloc_get_type_abort, may be an array element */
131
132 /*
133 * The repeat function for this frame should've
134 * been cleared, so this function should not run
135 * for detached requests.
136 */
138
139 switch (cr->state) {
140 case CHILD_RUNNABLE:
141 /*
142 * Place child state back inside the parent
143 */
144 if (cr->config.session_unique_ptr) {
146 cr->config.session_unique_ptr,
147 cr->num);
148 }
149 if (cr->p_result) *cr->p_result = cr->result;
150 break;
151
152 case CHILD_CANCELLED:
153 /*
154 * Child session state is no longer consistent
155 * after cancellation, so discard it.
156 */
157 if (cr->config.session_unique_ptr) {
158 fr_state_discard_child(request->parent, cr->config.session_unique_ptr, cr->num);
159 }
160 break;
161
162 default:
163 fr_assert_msg(0, "child %s resumed top frame with invalid state %s",
164 request->name,
166 }
167
168 cr->state = CHILD_EXITED;
169
170 /*
171 * Tell the parent to resume if all the request's siblings are done
172 */
173 if (!cr->sibling_count || (--(*cr->sibling_count) == 0)) {
174 RDEBUG3("All children have exited, marking parent %s as runnable", request->parent->name);
175 unlang_interpret_mark_runnable(request->parent);
176 } else {
177 RDEBUG3("Child %s exited, %u sibling(s) remaining",
178 request->name,
179 *cr->sibling_count);
180 }
181
183}
184
185/** Push a resumption frame onto a child's stack
186 *
187 * Push a frame onto the stack of the child to inform the parent when it's complete.
188 * An additional frame is pushed onto the child's stack by the 'run' function which
189 * executes in the context of the parent.
190 *
191 * @param[in] cr state for this child request. This is a pointer
192 * to a structure in the parent's frame state.
193 * @return
194 * - 0 on success.
195 * - -1 on failure.
196 */
198{
199 request_t *child = cr->request;
202
203 static unlang_t inform_parent = {
205 .name = "child-request",
206 .debug_name = "child-request-resume",
207 .actions = DEFAULT_MOD_ACTIONS,
208 };
209
210 /* Sets up the frame for us to use immediately */
211 if (unlikely(unlang_interpret_push_instruction(&cr->result, child, &inform_parent,
212 FRAME_CONF(RLM_MODULE_NOOP, true)) < 0)) {
213 return -1;
214 }
215
216 frame = frame_current(child);
217 state = frame->state;
218 state->cr = cr;
219 repeatable_set(frame); /* Run this on the way back up */
220
221 return 0;
222}
223
224/** Initialize a child request
225 *
226 * This initializes the child request result and configuration structure,
227 * and pushes a resumption frame onto the child's stack.
228 *
229 * @param[in] ctx Memory to use for any additional memory allocated
230 * to the unlang_child_request_t.
231 * @param[out] out Child request to initialize.
232 * @param[in] child The child request to initialize.
233 * @param[in] p_result Where to write out the rcode from the child.
234 * @param[in,out] sibling_count If non-null the number of siblings. This is incremented
235 * for each child created.
236 * @param[in] unique_session_ptr Unique session pointer for this child.
237 * If NULL session data won't be stored/restored for the child.
238 * @param[in] free_child Free the child when done?
239 * @return
240 * - 0 on success.
241 * - -1 on failure.
242 */
244 unlang_result_t *p_result, unsigned int *sibling_count, void const *unique_session_ptr, bool free_child)
245{
247 .name = talloc_bstrdup(ctx, child->name),
248 .num = sibling_count ? (*sibling_count)++ : 0,
249 .request = child,
250 .state = CHILD_INIT,
251 .sibling_count = sibling_count,
252 .config = {
253 .session_unique_ptr = unique_session_ptr,
254 .free_child = free_child
255 },
256 .p_result = p_result
257 };
258
260}
261
263{
265 .name = "child-request",
267
268 /*
269 * Frame can't be cancelled, because children need to
270 * write out status to the parent. If we don't do this,
271 * then all children must be detachable and must detach
272 * so they don't try and write out status to a "done"
273 * parent.
274 *
275 * It's easier to allow the child/parent relationship
276 * to end normally so that non-detachable requests are
277 * guaranteed the parent still exists.
278 */
280
281 .interpret = unlang_child_request_done,
283
284 .frame_state_size = sizeof(unlang_frame_state_child_request_t),
285 .frame_state_type = "unlang_frame_state_child_request_t"
286 });
287
288 return 0;
289}
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:228
#define unlikely(_x)
Definition build.h:402
#define UNUSED
Definition build.h:336
#define NUM_ELEMENTS(_t)
Definition build.h:358
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:212
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition interpret.c:1636
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:1192
#define FRAME_CONF(_default_rcode, _top_frame)
Definition interpret.h:152
#define RDEBUG3(fmt,...)
Definition log.h:355
void unlang_register(unlang_op_t *op)
Register an operation with the interpreter.
Definition base.c:56
#define DEFAULT_MOD_ACTIONS
Definition mod_action.h:73
#define fr_assert(_expr)
Definition rad_assert.h:37
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition rcode.h:54
#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:1008
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:912
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:589
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:68
@ UNLANG_OP_FLAG_NO_FORCE_UNWIND
Must not be cancelled.
@ UNLANG_OP_FLAG_INTERNAL
it's not a real keyword
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.
static size_t char ** out
Definition value.h:1030