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: 261a4566384974a13aa8becc03a2c7e9732a261b $
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: 261a4566384974a13aa8becc03a2c7e9732a261b $")
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 = {
66 /*
67 * By default, functions don't change the section rcode.
68 * We can't make generalisations about what the intent
69 * of the function callbacks are, so isntead of having
70 * implicit, confusing behaviour, we always discard the
71 * rcode UNLESS the function explicitly sets it.
72 */
73 .actions = {
84 },
85 .retry = RETRY_INIT,
86 }
87};
88
89/** Generic signal handler
90 *
91 * @param[in] request being signalled.
92 * @param[in] frame being signalled.
93 * @param[in] action Type of signal.
94 */
95static void unlang_function_signal(request_t *request,
96 unlang_stack_frame_t *frame, fr_signal_t action)
97{
98 unlang_frame_state_func_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_func_t);
99
100 if (!state->signal || (action & state->sigmask)) return;
101
102 state->signal(request, action, state->uctx);
103}
104
105
106/*
107 * Don't let the callback mess with the current
108 * module permanently.
109 */
110#define STORE_CALLER \
111 char const *caller; \
112 caller = request->module; \
113 request->module = NULL
114
115#define RESTORE_CALLER \
116 request->module = caller;
117
118/** Call a generic function that produces a result
119 *
120 * @param[out] p_result The frame result.
121 * @param[in] request The current request.
122 * @param[in] frame The current frame.
123 */
125{
127 unlang_frame_state_func_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_func_t);
129
131
132 if (!REPEAT(state)) {
133 RDEBUG4("Repeat function is NULL, likely due to previous yield, skipping call");
135 goto done;
136 }
137
138again:
139 RDEBUG4("Calling repeat function %p (%s)", REPEAT(state), state->repeat_name);
140
141 /*
142 * Only called once...
143 */
144 REPEAT(state) = NULL;
145 state->repeat_name = NULL;
146 ua = func(p_result, request, state->uctx);
147 if (REPEAT(state)) { /* set again by func */
148 switch (ua) {
150 break;
151
153 goto again;
154
155 default:
157 }
158 }
159
160done:
162
163 return ua;
164}
165
166/** Call a generic function that produces a result
167 *
168 * @param[out] p_result The frame result.
169 * @param[in] request The current request.
170 * @param[in] frame The current frame.
171 */
173{
175 unlang_frame_state_func_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_func_t);
176
178
179 RDEBUG4("Calling function %p (%s)", FUNC(state), state->func_name);
180 ua = state->func.wres(p_result, request, state->uctx);
181 FUNC(state) = NULL;
182 state->func_name = NULL;
183 if (REPEAT(state)) {
184 switch (ua) {
186 break;
187
189 ua = call_with_result_repeat(p_result, request, frame);
190 break;
191
192 default:
194 }
195 }
197
198 return ua;
199}
200
201/** Call a generic function that produces a result
202 *
203 * @param[out] p_result The frame result.
204 * @param[in] request The current request.
205 * @param[in] frame The current frame.
206 */
208{
210 unlang_frame_state_func_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_func_t);
212
214
215 if (!REPEAT(state)) {
216 RDEBUG4("Repeat function is NULL, likely due to previous yield, skipping call");
218 goto done;
219 }
220
221again:
222 RDEBUG4("Calling repeat function %p (%s)", REPEAT(state), state->repeat_name);
223
224 /*
225 * Only called once...
226 */
227 REPEAT(state) = NULL;
228 state->repeat_name = NULL;
229 ua = func(request, state->uctx);
230 if (REPEAT(state)) { /* set again by func */
231 switch (ua) {
233 break;
234
236 goto again;
237
239 no_action_fail:
240 fr_assert_msg(0, "Function %s (%p) is not allowed to indicate failure via UNLANG_ACTION_FAIL",
241 state->repeat_name, REPEAT(state));
243 break;
244
245 default:
247 }
248 }
249
250 if (ua == UNLANG_ACTION_FAIL) goto no_action_fail;
251
252done:
254
255 return ua;
256}
257
258/** Call a generic function that produces a result
259 *
260 * @param[out] p_result The frame result.
261 * @param[in] request The current request.
262 * @param[in] frame The current frame.
263 */
265{
267 unlang_frame_state_func_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_func_t);
268
270
271 RDEBUG4("Calling function %p (%s)", FUNC(state), state->func_name);
272 ua = state->func.nres(request, state->uctx);
273 FUNC(state) = NULL;
274 state->func_name = NULL;
275 if (REPEAT(state)) {
276 switch (ua) {
278 break;
279
281 ua = call_no_result_repeat(p_result, request, frame);
282 break;
283
285 no_action_fail:
286 fr_assert_msg(0, "Function is not allowed to indicate failure via UNLANG_ACTION_FAIL");
288 break;
289
290 default:
292 }
293 }
294 if (ua == UNLANG_ACTION_FAIL) goto no_action_fail;
295
297
298 return ua;
299}
300
301/** Clear pending repeat function calls, and remove the signal handler.
302 *
303 * The function frame being modified must be at the top of the stack.
304 *
305 * @param[in] request The current request.
306 * @return
307 * - 0 on success.
308 * - -1 on failure.
309 */
311{
312 unlang_stack_t *stack = request->stack;
313 unlang_stack_frame_t *frame = &stack->frame[stack->depth];
315
316 if (frame->instruction->type != UNLANG_TYPE_FUNCTION) {
317 RERROR("Can't clear 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->signal = NULL;
324
325 repeatable_clear(frame);
326
327 return 0;
328}
329
330/** Set a new signal function for an existing function frame
331 *
332 * @private
333 *
334 * The function frame being modified must be at the top of the stack.
335 *
336 * @param[in] request The current request.
337 * @param[in] signal The signal function to set.
338 * @param[in] sigmask Signals to block.
339 * @param[in] signal_name Name of the signal function call (for debugging).
340 * @return
341 * - 0 on success.
342 * - -1 on failure.
343 */
344int _unlang_function_signal_set(request_t *request, unlang_function_signal_t signal, fr_signal_t sigmask, char const *signal_name)
345{
346 unlang_stack_t *stack = request->stack;
347 unlang_stack_frame_t *frame = &stack->frame[stack->depth];
349
350 if (frame->instruction->type != UNLANG_TYPE_FUNCTION) {
351 RERROR("Can't set repeat function on non-function frame");
352 return -1;
353 }
354
355 state = talloc_get_type_abort(frame->state, unlang_frame_state_func_t);
356
357 /*
358 * If we're inside unlang_function_call,
359 * it'll pickup state->repeat and do the right thing
360 * once the current function returns.
361 */
362 state->signal = signal;
363 state->sigmask = sigmask;
364 state->signal_name = signal_name;
365
366 return 0;
367}
368
369/** Set a new repeat function for an existing function frame
370 *
371 * @private
372 *
373 * The function frame being modified must be at the top of the stack.
374 *
375 * @param[in] request The current request.
376 * @param[in] repeat the repeat function to set.
377 * @param[in] repeat_name Name of the repeat function call (for debugging).
378 * @param[in] type Type of repeat function (with or without result).
379 * @return
380 * - 0 on success.
381 * - -1 on failure.
382 */
383int _unlang_function_repeat_set(request_t *request, void *repeat, char const *repeat_name, unlang_function_type_t type)
384{
385 unlang_stack_t *stack = request->stack;
386 unlang_stack_frame_t *frame = &stack->frame[stack->depth];
388
389 if (frame->instruction->type != UNLANG_TYPE_FUNCTION) {
390 RERROR("Can't set repeat function on non-function frame");
391 return -1;
392 }
393
394 state = talloc_get_type_abort(frame->state, unlang_frame_state_func_t);
395
396 if (unlikely(state->type != type)) {
397 fr_assert_msg(0, "Function type mismatch \"%s\"", repeat_name);
398 return -1;
399 }
400
401 /*
402 * If we're inside unlang_function_call,
403 * it'll pickup state->repeat and do the right thing
404 * once the current function returns.
405 */
406 REPEAT(state) = repeat;
407 state->repeat_name = repeat_name;
408 repeatable_set(frame);
409
410 return 0;
411}
412
413static inline CC_HINT(always_inline)
415 request_t *request,
416 void *func,
417 char const *func_name,
418 void *repeat,
419 char const *repeat_name,
420 unlang_function_signal_t signal, fr_signal_t sigmask, char const *signal_name,
422 bool top_frame,
423 void *uctx)
424{
425 unlang_stack_t *stack = request->stack;
428
429 if (!func && !repeat) {
430 fr_assert_msg(0, "function push must push at least one function!");
431 return UNLANG_ACTION_FAIL;
432 }
433
434 /*
435 * Push module's function
436 */
437 if (unlang_interpret_push(p_result, request, &function_instruction,
438 FRAME_CONF(RLM_MODULE_NOOP, top_frame), UNLANG_NEXT_STOP) < 0) {
439 return UNLANG_ACTION_FAIL;
440 }
441
442 frame = &stack->frame[stack->depth];
443
444 /*
445 * Initialize state
446 */
447 state = frame->state;
448 state->signal = signal;
449 state->sigmask = sigmask;
450 state->signal_name = signal_name;
451 state->type = type;
452 state->uctx = uctx;
453
454 FUNC(state) = func;
455 state->func_name = func_name;
456 REPEAT(state) = repeat;
457 state->repeat_name = repeat_name;
458
459 if (repeat) repeatable_set(frame); /* execute on the way back up */
460
462}
463
464/** Push a generic function onto the unlang stack with a result
465 *
466 * @private
467 *
468 * These can be pushed by any other type of unlang op to allow a submodule or function
469 * deeper in the C call stack to establish a new resumption point.
470 *
471 * @param[in] p_result Where to write the result of the function evaluation.
472 *
473 * @param[in] request The current request.
474 * @param[in] func to call going up the stack.
475 * @param[in] func_name Name of the function call (for debugging).
476 * @param[in] repeat function to call going back down the stack (may be NULL).
477 * This may be the same as func.
478 * @param[in] repeat_name Name of the repeat function call (for debugging).
479 * @param[in] signal function to call if the request is signalled.
480 * @param[in] sigmask Signals to block.
481 * @param[in] signal_name Name of the signal function call (for debugging).
482 * @param[in] top_frame Return out of the unlang interpreter when popping this frame.
483 * @param[in] uctx to pass to func(s).
484 * @return
485 * - UNLANG_ACTION_PUSHED_CHILD on success.
486 * - UNLANG_ACTION_FAIL on failure.
487 */
488unlang_action_t _unlang_function_push_with_result(unlang_result_t *p_result,
489 request_t *request,
490 unlang_function_with_result_t func, char const *func_name,
491 unlang_function_with_result_t repeat, char const *repeat_name,
492 unlang_function_signal_t signal, fr_signal_t sigmask, char const *signal_name,
493 bool top_frame, void *uctx)
494{
497
498 ua = unlang_function_push_common(p_result,
499 request,
500 func, func_name,
501 repeat, repeat_name,
502 signal, sigmask, signal_name,
503 UNLANG_FUNCTION_TYPE_WITH_RESULT, top_frame, uctx);
504
506
507 frame = frame_current(request);
508 if (!func && repeat) {
510 } else {
511 frame->process = call_with_result;
512 }
513
514 return ua;
515}
516
517/** Push a generic function onto the unlang stack
518 *
519 * @private
520 *
521 * These can be pushed by any other type of unlang op to allow a submodule or function
522 * deeper in the C call stack to establish a new resumption point.
523 *
524 * @param[in] request The current request.
525 * @param[in] func to call going up the stack.
526 * @param[in] func_name Name of the function call (for debugging).
527 * @param[in] repeat function to call going back down the stack (may be NULL).
528 * This may be the same as func.
529 * @param[in] repeat_name Name of the repeat function call (for debugging).
530 * @param[in] signal function to call if the request is signalled.
531 * @param[in] sigmask Signals to block.
532 * @param[in] signal_name Name of the signal function call (for debugging).
533 * @param[in] top_frame Return out of the unlang interpreter when popping this frame.
534 * @param[in] uctx to pass to func(s).
535 * @return
536 * - UNLANG_ACTION_PUSHED_CHILD on success.
537 * - UNLANG_ACTION_FAIL on failure.
538 */
539unlang_action_t _unlang_function_push_no_result(request_t *request,
540 unlang_function_no_result_t func, char const *func_name,
541 unlang_function_no_result_t repeat, char const *repeat_name,
542 unlang_function_signal_t signal, fr_signal_t sigmask, char const *signal_name,
543 bool top_frame, void *uctx)
544{
547
549 request,
550 func, func_name,
551 repeat, repeat_name,
552 signal, sigmask, signal_name,
553 UNLANG_FUNCTION_TYPE_NO_RESULT, top_frame, uctx);
554
556
557 frame = frame_current(request);
558 if (!func && repeat) {
560 }
561
562 /* frame->process = call_no_result - This is the default, we don't need to set it again */
563
564 return ua;
565}
566
567/** Custom frame state dumper
568 *
569 */
571{
572 unlang_frame_state_func_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_func_t);
573
574 RDEBUG2("frame state");
575 if (FUNC(state)) RDEBUG2("function %p (%s)", FUNC(state), state->func_name);
576 if (REPEAT(state)) RDEBUG2("repeat %p (%s)", REPEAT(state), state->repeat_name);
577 if (state->signal) RDEBUG2("signal %p (%s)", state->signal, state->signal_name);
578}
579
581{
583 &(unlang_op_t){
584 .name = "function",
585 .interpret = call_no_result,
586 .signal = unlang_function_signal,
587 .dump = unlang_function_dump,
589 .frame_state_size = sizeof(unlang_frame_state_func_t),
590 .frame_state_type = "unlang_frame_state_func_t",
591 });
592
593}
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_STOP_PROCESSING
Break out of processing the current request (unwind).
Definition action.h:42
@ 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:485
#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:210
static void unlang_function_dump(request_t *request, unlang_stack_frame_t *frame)
Custom frame state dumper.
Definition function.c:570
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:172
#define RESTORE_CALLER
Definition function.c:115
int unlang_function_clear(request_t *request)
Clear pending repeat function calls, and remove the signal handler.
Definition function.c:310
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:124
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:207
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:414
#define STORE_CALLER
Definition function.c:110
void unlang_function_init(void)
Definition function.c:580
static void unlang_function_signal(request_t *request, unlang_stack_frame_t *frame, fr_signal_t action)
Generic signal handler.
Definition function.c:95
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:264
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 *result_p, 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:283
#define FRAME_CONF(_default_rcode, _top_frame)
Definition interpret.h:153
#define RERROR(fmt,...)
Definition log.h:298
#define RDEBUG4(fmt,...)
Definition log.h:344
void unlang_register(int type, unlang_op_t *op)
Register an operation with the interpreter.
Definition base.c:63
static char * stack[MAX_STACK]
Definition radmin.c:159
@ MOD_ACTION_NOT_SET
Definition mod_action.h:40
static bool done
Definition radclient.c:81
#define RDEBUG2(fmt,...)
Definition radclient.h:54
@ 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_TIMEOUT
Module (or section) timed out.
Definition rcode.h:50
@ 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
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:97
static unlang_stack_frame_t * frame_current(request_t *request)
@ UNLANG_TYPE_FUNCTION
Internal call to a function or submodule.
Definition unlang_priv.h:48
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.
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.
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.
#define RETRY_INIT
Definition retry.h:39