The FreeRADIUS server $Id: a4e7f8c94e8038b7627bcf52356b7ed15955c1d7 $
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: 1f915a094e973050483243aa965c5c6458d8a7d3 $
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: 1f915a094e973050483243aa965c5c6458d8a7d3 $")
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 */
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
117 RDEBUG4("Calling repeat function %p (%s)", REPEAT(state), state->repeat_name);
118
119 /*
120 * Only called once...
121 */
122 func = state->repeat.wres;
123 REPEAT(state) = NULL;
124 state->repeat_name = NULL;
125 ua = func(p_result, request, state->uctx);
126 if (REPEAT(state)) { /* set again by func */
127 switch (ua) {
129 break;
130
131 default:
133 }
134 }
135
136done:
138
139 return ua;
140}
141
142/** Call a generic function that produces a result
143 *
144 * @param[out] p_result The frame result.
145 * @param[in] request The current request.
146 * @param[in] frame The current frame.
147 */
149{
151 unlang_frame_state_func_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_func_t);
152
154
155 RDEBUG4("Calling function %p (%s)", FUNC(state), state->func_name);
156 ua = state->func.wres(p_result, request, state->uctx);
157 FUNC(state) = NULL;
158 state->func_name = NULL;
159 if (REPEAT(state)) {
160 switch (ua) {
162 break;
163
164 default:
166 }
167 }
169
170 return ua;
171}
172
173/** Call a generic function that produces a result
174 *
175 * @param[out] p_result The frame result.
176 * @param[in] request The current request.
177 * @param[in] frame The current frame.
178 */
180{
182 unlang_frame_state_func_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_func_t);
184
186
187 if (!REPEAT(state)) {
188 RDEBUG4("Repeat function is NULL, likely due to previous yield, skipping call");
190 goto done;
191 }
192
193 RDEBUG4("Calling repeat function %p (%s)", REPEAT(state), state->repeat_name);
194
195 /*
196 * Only called once...
197 */
198 func = state->repeat.nres;
199 REPEAT(state) = NULL;
200 state->repeat_name = NULL;
201 ua = func(request, state->uctx);
202 if (REPEAT(state)) { /* set again by func */
203 switch (ua) {
205 break;
206
208 no_action_fail:
209 fr_assert_msg(0, "Function %s (%p) is not allowed to indicate failure via UNLANG_ACTION_FAIL",
210 state->repeat_name, REPEAT(state));
212 break;
213
214 default:
216 }
217 }
218
219 if (ua == UNLANG_ACTION_FAIL) goto no_action_fail;
220
221done:
223
224 return ua;
225}
226
227/** Call a generic function that produces a result
228 *
229 * @param[out] p_result The frame result.
230 * @param[in] request The current request.
231 * @param[in] frame The current frame.
232 */
234{
236 unlang_frame_state_func_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_func_t);
237
239
240 RDEBUG4("Calling function %p (%s)", FUNC(state), state->func_name);
241 ua = state->func.nres(request, state->uctx);
242 FUNC(state) = NULL;
243 state->func_name = NULL;
244 if (REPEAT(state)) {
245 switch (ua) {
247 break;
248
250 no_action_fail:
251 fr_assert_msg(0, "Function is not allowed to indicate failure via UNLANG_ACTION_FAIL");
253 break;
254
255 default:
257 }
258 }
259 if (ua == UNLANG_ACTION_FAIL) goto no_action_fail;
260
262
263 return ua;
264}
265
266/** Clear pending repeat function calls, and remove the signal handler.
267 *
268 * The function frame being modified must be at the top of the stack.
269 *
270 * @param[in] request The current request.
271 * @return
272 * - 0 on success.
273 * - -1 on failure.
274 */
276{
277 unlang_stack_t *stack = request->stack;
278 unlang_stack_frame_t *frame = &stack->frame[stack->depth];
280
281 if (frame->instruction->type != UNLANG_TYPE_FUNCTION) {
282 RERROR("Can't clear function on non-function frame");
283 return -1;
284 }
285
286 state = talloc_get_type_abort(frame->state, unlang_frame_state_func_t);
287 REPEAT(state) = NULL;
288 state->signal = NULL;
289
290 repeatable_clear(frame);
291
292 return 0;
293}
294
295/** Clear the pending repeat function, and keep the signal handler
296 *
297 * If a unlang function returns UNLANG_ACTION_CALCULATE_RESULT with a repeat
298 * function set, the repeat function is never executed, and the signal handler
299 * for the frame (if set) is called to clean up state.
300 *
301 * If the returning function does not want the cancellation callback to execute
302 * it should call `unlang_function_repeat_clear` to clear the repeat function.
303 *
304 * @param[in] request The current request.
305 * @return
306 * - 0 on success.
307 * - -1 on failure.
308 */
310{
311 unlang_stack_t *stack = request->stack;
312 unlang_stack_frame_t *frame = &stack->frame[stack->depth];
313
315
316 if (frame->instruction->type != UNLANG_TYPE_FUNCTION) {
317 RERROR("Can't clear repeat function on non-function frame");
318 return -1;
319 }
320
321 state = talloc_get_type_abort(frame->state, unlang_frame_state_func_t);
322 REPEAT(state) = NULL;
323 state->repeat_name = NULL;
324 repeatable_clear(frame);
325
326 return 0;
327}
328
329/** Set a new signal function for an existing function frame
330 *
331 * @private
332 *
333 * The function frame being modified must be at the top of the stack.
334 *
335 * @param[in] request The current request.
336 * @param[in] signal The signal function to set.
337 * @param[in] sigmask Signals to block.
338 * @param[in] signal_name Name of the signal function call (for debugging).
339 * @return
340 * - 0 on success.
341 * - -1 on failure.
342 */
343int _unlang_function_signal_set(request_t *request, unlang_function_signal_t signal, fr_signal_t sigmask, char const *signal_name)
344{
345 unlang_stack_t *stack = request->stack;
346 unlang_stack_frame_t *frame = &stack->frame[stack->depth];
348
349 if (frame->instruction->type != UNLANG_TYPE_FUNCTION) {
350 RERROR("Can't set repeat function on non-function frame");
351 return -1;
352 }
353
354 state = talloc_get_type_abort(frame->state, unlang_frame_state_func_t);
355
356 /*
357 * If we're inside unlang_function_call,
358 * it'll pickup state->repeat and do the right thing
359 * once the current function returns.
360 */
361 state->signal = signal;
362 state->sigmask = sigmask;
363 state->signal_name = signal_name;
364
365 return 0;
366}
367
368/** Set a new repeat function for an existing function frame
369 *
370 * @private
371 *
372 * The function frame being modified must be at the top of the stack.
373 *
374 * @param[in] request The current request.
375 * @param[in] repeat the repeat function to set.
376 * @param[in] repeat_name Name of the repeat function call (for debugging).
377 * @param[in] type Type of repeat function (with or without result).
378 * @return
379 * - 0 on success.
380 * - -1 on failure.
381 */
382int _unlang_function_repeat_set(request_t *request, void *repeat, char const *repeat_name, unlang_function_type_t type)
383{
384 unlang_stack_t *stack = request->stack;
385 unlang_stack_frame_t *frame = &stack->frame[stack->depth];
387
388 if (frame->instruction->type != UNLANG_TYPE_FUNCTION) {
389 RERROR("Can't set repeat function on non-function frame");
390 return -1;
391 }
392
393 state = talloc_get_type_abort(frame->state, unlang_frame_state_func_t);
394
395 if (unlikely(state->type != type)) {
396 fr_assert_msg(0, "Function type mismatch \"%s\"", repeat_name);
397 return -1;
398 }
399
400 /*
401 * If we're inside unlang_function_call,
402 * it'll pickup state->repeat and do the right thing
403 * once the current function returns.
404 */
405 REPEAT(state) = repeat;
406 state->repeat_name = repeat_name;
407 repeatable_set(frame);
408
409 return 0;
410}
411
412static inline CC_HINT(always_inline)
415 void *func,
416 char const *func_name,
417 void *repeat,
418 char const *repeat_name,
419 unlang_function_signal_t signal, fr_signal_t sigmask, char const *signal_name,
421 bool top_frame,
422 void *uctx)
423{
424 unlang_stack_t *stack = request->stack;
427
428 if (!func && !repeat) {
429 fr_assert_msg(0, "function push must push at least one function!");
430 return UNLANG_ACTION_FAIL;
431 }
432
433 /*
434 * Push module's function
435 */
437 FRAME_CONF(RLM_MODULE_NOOP, top_frame), UNLANG_NEXT_STOP) < 0) {
438 return UNLANG_ACTION_FAIL;
439 }
440
441 frame = &stack->frame[stack->depth];
442
443 /*
444 * Initialize state
445 */
446 state = frame->state;
447 state->signal = signal;
448 state->sigmask = sigmask;
449 state->signal_name = signal_name;
450 state->type = type;
451 state->uctx = uctx;
452
453 FUNC(state) = func;
454 state->func_name = func_name;
455 REPEAT(state) = repeat;
456 state->repeat_name = repeat_name;
457
458 if (repeat) repeatable_set(frame); /* execute on the way back up */
459
461}
462
463/** Push a generic function onto the unlang stack with a result
464 *
465 * @private
466 *
467 * These can be pushed by any other type of unlang op to allow a submodule or function
468 * deeper in the C call stack to establish a new resumption point.
469 *
470 * @param[in] p_result Where to write the result of the function evaluation.
471 *
472 * @param[in] request The current request.
473 * @param[in] func to call going up the stack.
474 * @param[in] func_name Name of the function call (for debugging).
475 * @param[in] repeat function to call going back down the stack (may be NULL).
476 * This may be the same as func.
477 * @param[in] repeat_name Name of the repeat function call (for debugging).
478 * @param[in] signal function to call if the request is signalled.
479 * @param[in] sigmask Signals to block.
480 * @param[in] signal_name Name of the signal function call (for debugging).
481 * @param[in] top_frame Return out of the unlang interpreter when popping this frame.
482 * @param[in] uctx to pass to func(s).
483 * @return
484 * - UNLANG_ACTION_PUSHED_CHILD on success.
485 * - UNLANG_ACTION_FAIL on failure.
486 */
487unlang_action_t _unlang_function_push_with_result(unlang_result_t *p_result,
489 unlang_function_with_result_t func, char const *func_name,
490 unlang_function_with_result_t repeat, char const *repeat_name,
491 unlang_function_signal_t signal, fr_signal_t sigmask, char const *signal_name,
492 bool top_frame, void *uctx)
493{
496
497 ua = unlang_function_push_common(p_result,
498 request,
499 (void *) func, func_name,
500 (void *) repeat, repeat_name,
501 signal, sigmask, signal_name,
502 UNLANG_FUNCTION_TYPE_WITH_RESULT, top_frame, uctx);
503
505
506 frame = frame_current(request);
507 if (!func && repeat) {
509 } else {
510 frame->process = call_with_result;
511 }
512
513 return ua;
514}
515
516/** Push a generic function onto the unlang stack
517 *
518 * @private
519 *
520 * These can be pushed by any other type of unlang op to allow a submodule or function
521 * deeper in the C call stack to establish a new resumption point.
522 *
523 * @param[in] request The current request.
524 * @param[in] func to call going up the stack.
525 * @param[in] func_name Name of the function call (for debugging).
526 * @param[in] repeat function to call going back down the stack (may be NULL).
527 * This may be the same as func.
528 * @param[in] repeat_name Name of the repeat function call (for debugging).
529 * @param[in] signal function to call if the request is signalled.
530 * @param[in] sigmask Signals to block.
531 * @param[in] signal_name Name of the signal function call (for debugging).
532 * @param[in] top_frame Return out of the unlang interpreter when popping this frame.
533 * @param[in] uctx to pass to func(s).
534 * @return
535 * - UNLANG_ACTION_PUSHED_CHILD on success.
536 * - UNLANG_ACTION_FAIL on failure.
537 */
538unlang_action_t _unlang_function_push_no_result(request_t *request,
539 unlang_function_no_result_t func, char const *func_name,
540 unlang_function_no_result_t repeat, char const *repeat_name,
541 unlang_function_signal_t signal, fr_signal_t sigmask, char const *signal_name,
542 bool top_frame, void *uctx)
543{
546
548 request,
549 (void *) func, func_name,
550 (void *) repeat, repeat_name,
551 signal, sigmask, signal_name,
552 UNLANG_FUNCTION_TYPE_NO_RESULT, top_frame, uctx);
553
555
556 frame = frame_current(request);
557 if (!func && repeat) {
559 }
560
561 /* frame->process = call_no_result - This is the default, we don't need to set it again */
562
563 return ua;
564}
565
566/** Custom frame state dumper
567 *
568 */
570{
571 unlang_frame_state_func_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_func_t);
572
573 RDEBUG2("frame state");
574 if (FUNC(state)) RDEBUG2("function %p (%s)", FUNC(state), state->func_name);
575 if (REPEAT(state)) RDEBUG2("repeat %p (%s)", REPEAT(state), state->repeat_name);
576 if (state->signal) RDEBUG2("signal %p (%s)", state->signal, state->signal_name);
577}
578
580{
582 .name = "function",
583 .type = UNLANG_TYPE_FUNCTION,
585
586 .interpret = call_no_result,
587 .signal = unlang_function_signal,
588 .dump = unlang_function_dump,
589
590 .unlang_size = sizeof(unlang_group_t),
591 .unlang_name = "unlang_group_t",
592
593 .frame_state_size = sizeof(unlang_frame_state_func_t),
594 .frame_state_type = "unlang_frame_state_func_t",
595 });
596}
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:40
@ 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:38
static uint8_t request[4]
#define RCSID(id)
Definition build.h:603
#define unlikely(_x)
Definition build.h:486
#define UNUSED
Definition build.h:415
#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:248
static void unlang_function_dump(request_t *request, unlang_stack_frame_t *frame)
Custom frame state dumper.
Definition function.c:569
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:148
union unlang_frame_state_func_t::@126 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:275
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
union unlang_frame_state_func_t::@125 func
unlang_function_signal_t signal
Signal function to call.
Definition function.c:52
void * uctx
Uctx to pass to function.
Definition function.c:55
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:179
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:413
int unlang_function_repeat_clear(request_t *request)
Clear the pending repeat function, and keep the signal handler.
Definition function.c:309
#define STORE_CALLER
Definition function.c:89
void unlang_function_init(void)
Definition function.c:579
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:233
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.
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_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
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:622
#define FRAME_CONF(_default_rcode, _top_frame)
Definition interpret.h:158
#define RERROR(fmt,...)
Definition log.h:315
#define RDEBUG4(fmt,...)
Definition log.h:361
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:158
#define DEFAULT_MOD_ACTIONS
Definition mod_action.h:73
#define RDEBUG2(fmt,...)
static bool done
Definition radclient.c:80
@ RLM_MODULE_NOOP
Module succeeded without doing anything.
Definition rcode.h:54
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)
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_TYPE_FUNCTION
Internal call to a function or submodule.
Definition unlang_priv.h:50
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.
unlang_op_flag_t
@ UNLANG_OP_FLAG_RETURN_POINT
Return point.
@ UNLANG_OP_FLAG_INTERNAL
it's not a real keyword
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.