The FreeRADIUS server $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
Loading...
Searching...
No Matches
function.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: 09fe70cc6bebd98164cedd345174fe123cedae27 $
19 *
20 * @file unlang/function.c
21 * @brief Unlang "function" keyword evaluation.
22
23 * @copyright 2018,2021 The FreeRADIUS server project
24 * @copyright 2018,2021 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
25 */
26RCSID("$Id: 09fe70cc6bebd98164cedd345174fe123cedae27 $")
27
28#include "action.h"
29#include "unlang_priv.h"
30#include "function.h"
31
32#define FUNC(_state) *((void **)&state->func)
33#define REPEAT(_state) *((void **)&state->repeat)
34
35/*
36 * Some functions differ mainly in their parsing
37 */
38typedef struct {
39 union {
40 unlang_function_no_result_t nres; //!< To call when going down the stack.
41 unlang_function_with_result_t wres; //!< To call when going down the stack.
42 } func;
43 char const *func_name; //!< Debug name for the function.
44
45 union {
46 unlang_function_no_result_t nres; //!< To call when going back up the stack.
47 unlang_function_with_result_t wres; //!< To call when going back up the stack.
48 } repeat;
49 unlang_function_type_t type; //!< Record whether we need to call the
50 char const *repeat_name; //!< Debug name for the repeat function.
51
52 unlang_function_signal_t signal; //!< Signal function to call.
53 fr_signal_t sigmask; //!< Signals to block.
54 char const *signal_name; //!< Debug name for the signal function.
55 void *uctx; //!< Uctx to pass to function.
57
58/** Static instruction for allowing modules/xlats to call functions within themselves, or submodules
59 *
60 */
63 .name = "function",
64 .debug_name = "function",
65 .actions = DEFAULT_MOD_ACTIONS,
66};
67
68/** Generic signal handler
69 *
70 * @param[in] request being signalled.
71 * @param[in] frame being signalled.
72 * @param[in] action Type of signal.
73 */
74static void unlang_function_signal(request_t *request,
75 unlang_stack_frame_t *frame, fr_signal_t action)
76{
77 unlang_frame_state_func_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_func_t);
78
79 if (!state->signal || (action & state->sigmask)) return;
80
81 state->signal(request, action, state->uctx);
82}
83
84
85/*
86 * Don't let the callback mess with the current
87 * module permanently.
88 */
89#define STORE_CALLER \
90 char const *caller; \
91 caller = request->module; \
92 request->module = NULL
93
94#define RESTORE_CALLER \
95 request->module = caller;
96
97/** Call a generic function that produces a result
98 *
99 * @param[out] p_result The frame result.
100 * @param[in] request The current request.
101 * @param[in] frame The current frame.
102 */
104{
106 unlang_frame_state_func_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_func_t);
108
110
111 if (!REPEAT(state)) {
112 RDEBUG4("Repeat function is NULL, likely due to previous yield, skipping call");
114 goto done;
115 }
116
117again:
118 RDEBUG4("Calling repeat function %p (%s)", REPEAT(state), state->repeat_name);
119
120 /*
121 * Only called once...
122 */
123 func = state->repeat.wres;
124 REPEAT(state) = NULL;
125 state->repeat_name = NULL;
126 ua = func(p_result, request, state->uctx);
127 if (REPEAT(state)) { /* set again by func */
128 switch (ua) {
130 goto again;
131
132 default:
134 }
135 }
136
137done:
139
140 return ua;
141}
142
143/** Call a generic function that produces a result
144 *
145 * @param[out] p_result The frame result.
146 * @param[in] request The current request.
147 * @param[in] frame The current frame.
148 */
150{
152 unlang_frame_state_func_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_func_t);
153
155
156 RDEBUG4("Calling function %p (%s)", FUNC(state), state->func_name);
157 ua = state->func.wres(p_result, request, state->uctx);
158 FUNC(state) = NULL;
159 state->func_name = NULL;
160 if (REPEAT(state)) {
161 switch (ua) {
163 ua = call_with_result_repeat(p_result, request, frame);
164 break;
165
166 default:
168 }
169 }
171
172 return ua;
173}
174
175/** Call a generic function that produces a result
176 *
177 * @param[out] p_result The frame result.
178 * @param[in] request The current request.
179 * @param[in] frame The current frame.
180 */
182{
184 unlang_frame_state_func_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_func_t);
186
188
189 if (!REPEAT(state)) {
190 RDEBUG4("Repeat function is NULL, likely due to previous yield, skipping call");
192 goto done;
193 }
194
195again:
196 RDEBUG4("Calling repeat function %p (%s)", REPEAT(state), state->repeat_name);
197
198 /*
199 * Only called once...
200 */
201 func = state->repeat.nres;
202 REPEAT(state) = NULL;
203 state->repeat_name = NULL;
204 ua = func(request, state->uctx);
205 if (REPEAT(state)) { /* set again by func */
206 switch (ua) {
208 goto again;
209
211 no_action_fail:
212 fr_assert_msg(0, "Function %s (%p) is not allowed to indicate failure via UNLANG_ACTION_FAIL",
213 state->repeat_name, REPEAT(state));
215 break;
216
217 default:
219 }
220 }
221
222 if (ua == UNLANG_ACTION_FAIL) goto no_action_fail;
223
224done:
226
227 return ua;
228}
229
230/** Call a generic function that produces a result
231 *
232 * @param[out] p_result The frame result.
233 * @param[in] request The current request.
234 * @param[in] frame The current frame.
235 */
237{
239 unlang_frame_state_func_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_func_t);
240
242
243 RDEBUG4("Calling function %p (%s)", FUNC(state), state->func_name);
244 ua = state->func.nres(request, state->uctx);
245 FUNC(state) = NULL;
246 state->func_name = NULL;
247 if (REPEAT(state)) {
248 switch (ua) {
250 ua = call_no_result_repeat(p_result, request, frame);
251 break;
252
254 no_action_fail:
255 fr_assert_msg(0, "Function is not allowed to indicate failure via UNLANG_ACTION_FAIL");
257 break;
258
259 default:
261 }
262 }
263 if (ua == UNLANG_ACTION_FAIL) goto no_action_fail;
264
266
267 return ua;
268}
269
270/** Clear pending repeat function calls, and remove the signal handler.
271 *
272 * The function frame being modified must be at the top of the stack.
273 *
274 * @param[in] request The current request.
275 * @return
276 * - 0 on success.
277 * - -1 on failure.
278 */
280{
281 unlang_stack_t *stack = request->stack;
282 unlang_stack_frame_t *frame = &stack->frame[stack->depth];
284
285 if (frame->instruction->type != UNLANG_TYPE_FUNCTION) {
286 RERROR("Can't clear function on non-function frame");
287 return -1;
288 }
289
290 state = talloc_get_type_abort(frame->state, unlang_frame_state_func_t);
291 REPEAT(state) = NULL;
292 state->signal = NULL;
293
294 repeatable_clear(frame);
295
296 return 0;
297}
298
299/** Set a new signal function for an existing function frame
300 *
301 * @private
302 *
303 * The function frame being modified must be at the top of the stack.
304 *
305 * @param[in] request The current request.
306 * @param[in] signal The signal function to set.
307 * @param[in] sigmask Signals to block.
308 * @param[in] signal_name Name of the signal function call (for debugging).
309 * @return
310 * - 0 on success.
311 * - -1 on failure.
312 */
313int _unlang_function_signal_set(request_t *request, unlang_function_signal_t signal, fr_signal_t sigmask, char const *signal_name)
314{
315 unlang_stack_t *stack = request->stack;
316 unlang_stack_frame_t *frame = &stack->frame[stack->depth];
318
319 if (frame->instruction->type != UNLANG_TYPE_FUNCTION) {
320 RERROR("Can't set repeat function on non-function frame");
321 return -1;
322 }
323
324 state = talloc_get_type_abort(frame->state, unlang_frame_state_func_t);
325
326 /*
327 * If we're inside unlang_function_call,
328 * it'll pickup state->repeat and do the right thing
329 * once the current function returns.
330 */
331 state->signal = signal;
332 state->sigmask = sigmask;
333 state->signal_name = signal_name;
334
335 return 0;
336}
337
338/** Set a new repeat function for an existing function frame
339 *
340 * @private
341 *
342 * The function frame being modified must be at the top of the stack.
343 *
344 * @param[in] request The current request.
345 * @param[in] repeat the repeat function to set.
346 * @param[in] repeat_name Name of the repeat function call (for debugging).
347 * @param[in] type Type of repeat function (with or without result).
348 * @return
349 * - 0 on success.
350 * - -1 on failure.
351 */
352int _unlang_function_repeat_set(request_t *request, void *repeat, char const *repeat_name, unlang_function_type_t type)
353{
354 unlang_stack_t *stack = request->stack;
355 unlang_stack_frame_t *frame = &stack->frame[stack->depth];
357
358 if (frame->instruction->type != UNLANG_TYPE_FUNCTION) {
359 RERROR("Can't set repeat function on non-function frame");
360 return -1;
361 }
362
363 state = talloc_get_type_abort(frame->state, unlang_frame_state_func_t);
364
365 if (unlikely(state->type != type)) {
366 fr_assert_msg(0, "Function type mismatch \"%s\"", repeat_name);
367 return -1;
368 }
369
370 /*
371 * If we're inside unlang_function_call,
372 * it'll pickup state->repeat and do the right thing
373 * once the current function returns.
374 */
375 REPEAT(state) = repeat;
376 state->repeat_name = repeat_name;
377 repeatable_set(frame);
378
379 return 0;
380}
381
382static inline CC_HINT(always_inline)
384 request_t *request,
385 void *func,
386 char const *func_name,
387 void *repeat,
388 char const *repeat_name,
389 unlang_function_signal_t signal, fr_signal_t sigmask, char const *signal_name,
391 bool top_frame,
392 void *uctx)
393{
394 unlang_stack_t *stack = request->stack;
397
398 if (!func && !repeat) {
399 fr_assert_msg(0, "function push must push at least one function!");
400 return UNLANG_ACTION_FAIL;
401 }
402
403 /*
404 * Push module's function
405 */
406 if (unlang_interpret_push(p_result, request, &function_instruction,
407 FRAME_CONF(RLM_MODULE_NOOP, top_frame), UNLANG_NEXT_STOP) < 0) {
408 return UNLANG_ACTION_FAIL;
409 }
410
411 frame = &stack->frame[stack->depth];
412
413 /*
414 * Initialize state
415 */
416 state = frame->state;
417 state->signal = signal;
418 state->sigmask = sigmask;
419 state->signal_name = signal_name;
420 state->type = type;
421 state->uctx = uctx;
422
423 FUNC(state) = func;
424 state->func_name = func_name;
425 REPEAT(state) = repeat;
426 state->repeat_name = repeat_name;
427
428 if (repeat) repeatable_set(frame); /* execute on the way back up */
429
431}
432
433/** Push a generic function onto the unlang stack with a result
434 *
435 * @private
436 *
437 * These can be pushed by any other type of unlang op to allow a submodule or function
438 * deeper in the C call stack to establish a new resumption point.
439 *
440 * @param[in] p_result Where to write the result of the function evaluation.
441 *
442 * @param[in] request The current request.
443 * @param[in] func to call going up the stack.
444 * @param[in] func_name Name of the function call (for debugging).
445 * @param[in] repeat function to call going back down the stack (may be NULL).
446 * This may be the same as func.
447 * @param[in] repeat_name Name of the repeat function call (for debugging).
448 * @param[in] signal function to call if the request is signalled.
449 * @param[in] sigmask Signals to block.
450 * @param[in] signal_name Name of the signal function call (for debugging).
451 * @param[in] top_frame Return out of the unlang interpreter when popping this frame.
452 * @param[in] uctx to pass to func(s).
453 * @return
454 * - UNLANG_ACTION_PUSHED_CHILD on success.
455 * - UNLANG_ACTION_FAIL on failure.
456 */
457unlang_action_t _unlang_function_push_with_result(unlang_result_t *p_result,
458 request_t *request,
459 unlang_function_with_result_t func, char const *func_name,
460 unlang_function_with_result_t repeat, char const *repeat_name,
461 unlang_function_signal_t signal, fr_signal_t sigmask, char const *signal_name,
462 bool top_frame, void *uctx)
463{
466
467 ua = unlang_function_push_common(p_result,
468 request,
469 (void *) func, func_name,
470 (void *) repeat, repeat_name,
471 signal, sigmask, signal_name,
472 UNLANG_FUNCTION_TYPE_WITH_RESULT, top_frame, uctx);
473
475
476 frame = frame_current(request);
477 if (!func && repeat) {
479 } else {
480 frame->process = call_with_result;
481 }
482
483 return ua;
484}
485
486/** Push a generic function onto the unlang stack
487 *
488 * @private
489 *
490 * These can be pushed by any other type of unlang op to allow a submodule or function
491 * deeper in the C call stack to establish a new resumption point.
492 *
493 * @param[in] request The current request.
494 * @param[in] func to call going up the stack.
495 * @param[in] func_name Name of the function call (for debugging).
496 * @param[in] repeat function to call going back down the stack (may be NULL).
497 * This may be the same as func.
498 * @param[in] repeat_name Name of the repeat function call (for debugging).
499 * @param[in] signal function to call if the request is signalled.
500 * @param[in] sigmask Signals to block.
501 * @param[in] signal_name Name of the signal function call (for debugging).
502 * @param[in] top_frame Return out of the unlang interpreter when popping this frame.
503 * @param[in] uctx to pass to func(s).
504 * @return
505 * - UNLANG_ACTION_PUSHED_CHILD on success.
506 * - UNLANG_ACTION_FAIL on failure.
507 */
508unlang_action_t _unlang_function_push_no_result(request_t *request,
509 unlang_function_no_result_t func, char const *func_name,
510 unlang_function_no_result_t repeat, char const *repeat_name,
511 unlang_function_signal_t signal, fr_signal_t sigmask, char const *signal_name,
512 bool top_frame, void *uctx)
513{
516
518 request,
519 (void *) func, func_name,
520 (void *) repeat, repeat_name,
521 signal, sigmask, signal_name,
522 UNLANG_FUNCTION_TYPE_NO_RESULT, top_frame, uctx);
523
525
526 frame = frame_current(request);
527 if (!func && repeat) {
529 }
530
531 /* frame->process = call_no_result - This is the default, we don't need to set it again */
532
533 return ua;
534}
535
536/** Custom frame state dumper
537 *
538 */
540{
541 unlang_frame_state_func_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_func_t);
542
543 RDEBUG2("frame state");
544 if (FUNC(state)) RDEBUG2("function %p (%s)", FUNC(state), state->func_name);
545 if (REPEAT(state)) RDEBUG2("repeat %p (%s)", REPEAT(state), state->repeat_name);
546 if (state->signal) RDEBUG2("signal %p (%s)", state->signal, state->signal_name);
547}
548
550{
552 .name = "function",
553 .type = UNLANG_TYPE_FUNCTION,
555
556 .interpret = call_no_result,
557 .signal = unlang_function_signal,
558 .dump = unlang_function_dump,
559
560 .unlang_size = sizeof(unlang_group_t),
561 .unlang_name = "unlang_group_t",
562
563 .frame_state_size = sizeof(unlang_frame_state_func_t),
564 .frame_state_type = "unlang_frame_state_func_t",
565 });
566}
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition action.h:35
@ UNLANG_ACTION_PUSHED_CHILD
unlang_t pushed a new child onto the stack, execute it instead of continuing.
Definition action.h:39
@ UNLANG_ACTION_FAIL
Encountered an unexpected error.
Definition action.h:36
@ UNLANG_ACTION_CALCULATE_RESULT
Calculate a new section rlm_rcode_t value.
Definition action.h:37
#define RCSID(id)
Definition build.h:487
#define unlikely(_x)
Definition build.h:383
#define UNUSED
Definition build.h:317
#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:202
static void unlang_function_dump(request_t *request, unlang_stack_frame_t *frame)
Custom frame state dumper.
Definition function.c:539
char const * repeat_name
Debug name for the repeat function.
Definition function.c:50
unlang_function_type_t type
Record whether we need to call the.
Definition function.c:49
static unlang_action_t call_with_result(unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Call a generic function that produces a result.
Definition function.c:149
union unlang_frame_state_func_t::@102 repeat
#define RESTORE_CALLER
Definition function.c:94
int unlang_function_clear(request_t *request)
Clear pending repeat function calls, and remove the signal handler.
Definition function.c:279
static unlang_action_t call_with_result_repeat(unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Call a generic function that produces a result.
Definition function.c:103
static unlang_t function_instruction
Static instruction for allowing modules/xlats to call functions within themselves,...
Definition function.c:61
#define REPEAT(_state)
Definition function.c:33
unlang_function_signal_t signal
Signal function to call.
Definition function.c:52
void * uctx
Uctx to pass to function.
Definition function.c:55
union unlang_frame_state_func_t::@101 func
char const * func_name
Debug name for the function.
Definition function.c:43
fr_signal_t sigmask
Signals to block.
Definition function.c:53
static unlang_action_t call_no_result_repeat(UNUSED unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Call a generic function that produces a result.
Definition function.c:181
static unlang_action_t unlang_function_push_common(unlang_result_t *p_result, request_t *request, void *func, char const *func_name, void *repeat, char const *repeat_name, unlang_function_signal_t signal, fr_signal_t sigmask, char const *signal_name, unlang_function_type_t type, bool top_frame, void *uctx)
Definition function.c:383
#define STORE_CALLER
Definition function.c:89
void unlang_function_init(void)
Definition function.c:549
static void unlang_function_signal(request_t *request, unlang_stack_frame_t *frame, fr_signal_t action)
Generic signal handler.
Definition function.c:74
static unlang_action_t call_no_result(UNUSED unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame)
Call a generic function that produces a result.
Definition function.c:236
char const * signal_name
Debug name for the signal function.
Definition function.c:54
#define FUNC(_state)
Definition function.c:32
Declarations for generic unlang functions.
unlang_function_type_t
Definition function.h:41
@ UNLANG_FUNCTION_TYPE_NO_RESULT
Function without a result.
Definition function.h:43
@ UNLANG_FUNCTION_TYPE_WITH_RESULT
Function with a result.
Definition function.h:42
void(* unlang_function_signal_t)(request_t *request, fr_signal_t action, void *uctx)
Function to call if the request was signalled.
Definition function.h:78
unlang_action_t(* unlang_function_no_result_t)(request_t *request, void *uctx)
A generic function pushed by a module or xlat to functions deeper in the C call stack to create resum...
Definition function.h:68
unlang_action_t(* unlang_function_with_result_t)(unlang_result_t *p_result, request_t *request, void *uctx)
A generic function pushed by a module or xlat to functions deeper in the C call stack to create resum...
Definition function.h:55
int unlang_interpret_push(unlang_result_t *p_result, request_t *request, unlang_t const *instruction, unlang_frame_conf_t const *conf, bool do_next_sibling)
Push a new frame onto the stack.
Definition interpret.c:280
#define FRAME_CONF(_default_rcode, _top_frame)
Definition interpret.h:152
#define RERROR(fmt,...)
Definition log.h:298
#define RDEBUG4(fmt,...)
Definition log.h:344
void unlang_register(unlang_op_t *op)
Register an operation with the interpreter.
Definition base.c:56
static char * stack[MAX_STACK]
Definition radmin.c:159
#define DEFAULT_MOD_ACTIONS
Definition mod_action.h:73
static bool done
Definition radclient.c:83
#define RDEBUG2(fmt,...)
Definition radclient.h:54
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition rcode.h:50
fr_signal_t
Signals that can be generated/processed by request signal handlers.
Definition signal.h:38
fr_aka_sim_id_type_t type
Private interpreter structures and functions.
static void repeatable_clear(unlang_stack_frame_t *frame)
void * state
Stack frame specialisations.
#define UNLANG_NEXT_STOP
Definition unlang_priv.h:98
static unlang_stack_frame_t * frame_current(request_t *request)
@ UNLANG_TYPE_FUNCTION
Internal call to a function or submodule.
Definition unlang_priv.h:50
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_RETURN_POINT
Return point.
@ UNLANG_OP_FLAG_INTERNAL
it's not a real keyword
static void repeatable_set(unlang_stack_frame_t *frame)
unlang_process_t process
function to call for interpreting this stack frame
unlang_type_t type
The specialisation of this node.
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.