Coverage Report

Created: 2026-06-30 07:16

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/freeradius-server/src/lib/unlang/xlat.c
Line
Count
Source
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: 1073c8a7bd467c2910720bef8efcd49622c4d882 $
19
 *
20
 * @file unlang/xlat.c
21
 * @brief Integration between the unlang interpreter and xlats
22
 *
23
 * @copyright 2018 The FreeRADIUS server project
24
 * @copyright 2018 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
25
 */
26
RCSID("$Id: 1073c8a7bd467c2910720bef8efcd49622c4d882 $")
27
28
#include <freeradius-devel/server/base.h>
29
30
#include <ctype.h>
31
#include <freeradius-devel/unlang/xlat_priv.h>
32
#include <freeradius-devel/util/debug.h>
33
#include "unlang_priv.h"  /* Fixme - Should create a proper semi-public interface for the interpret */
34
35
/** State of an xlat expansion
36
 *
37
 * State of one level of nesting within an xlat expansion.
38
 */
39
typedef struct {
40
  TALLOC_CTX    *ctx;       //!< to allocate boxes and values in.
41
  TALLOC_CTX    *event_ctx;     //!< for temporary events
42
  xlat_exp_head_t const *head;              //!< of the xlat list
43
  xlat_exp_t const  *exp;       //!< current one we're evaluating
44
  fr_dcursor_t    values;       //!< Values aggregated so far.
45
46
  rindent_t   indent;       //!< indentation
47
48
  void      *env_data;      //!< Expanded per call environment tmpls.
49
  /*
50
   *  For func and alternate
51
   */
52
  fr_value_box_list_t out;        //!< Head of the result of a nested
53
                ///< expansion.
54
  xlat_func_t   resume;       //!< called on resume
55
  xlat_func_signal_t  signal;       //!< called on signal
56
  fr_signal_t   sigmask;      //!< Signals to block
57
  void      *rctx;        //!< for resume / signal
58
59
  unlang_result_t   *p_result;      //!< If set, where to record the result
60
                ///< of the execution.
61
} unlang_frame_state_xlat_t;
62
63
/** Wrap an #fr_timer_t providing data needed for unlang events
64
 *
65
 */
66
typedef struct {
67
  request_t     *request;   //!< Request this event pertains to.
68
  int       fd;     //!< File descriptor to wait on.
69
  fr_unlang_xlat_timeout_t  timeout;    //!< Function to call on timeout.
70
  fr_unlang_xlat_fd_event_t fd_read;    //!< Function to call when FD is readable.
71
  fr_unlang_xlat_fd_event_t fd_write;   //!< Function to call when FD is writable.
72
  fr_unlang_xlat_fd_event_t fd_error;   //!< Function to call when FD has errored.
73
  xlat_inst_t     *inst;      //!< xlat instance data.
74
  xlat_thread_inst_t    *thread;    //!< Thread specific xlat instance.
75
  void const      *rctx;      //!< rctx data to pass to callbacks.
76
  fr_timer_t      *ev;      //!< Event in this worker's event heap.
77
} unlang_xlat_event_t;
78
79
typedef struct {
80
  request_t     *request;
81
  xlat_inst_t     *inst;      //!< xlat instance data.
82
  xlat_thread_inst_t    *thread;    //!< Thread specific xlat instance.
83
84
  fr_unlang_xlat_retry_t    retry_cb;   //!< callback to run on timeout
85
  void        *rctx;      //!< rctx data to pass to timeout callback
86
87
  fr_timer_t      *ev;      //!< retry timer just for this xlat
88
  fr_retry_t      retry;      //!< retry timers, etc.
89
} unlang_xlat_retry_t;
90
91
/** Frees an unlang event, removing it from the request's event loop
92
 *
93
 * @param[in] ev  The event to free.
94
 *
95
 * @return 0
96
 */
97
static int _unlang_xlat_event_free(unlang_xlat_event_t *ev)
98
0
{
99
0
  FR_TIMER_DELETE(&(ev->ev));
100
101
0
  if (ev->fd >= 0) {
102
0
    (void) fr_event_fd_delete(unlang_interpret_event_list(ev->request), ev->fd, FR_EVENT_FILTER_IO);
103
0
  }
104
105
0
  return 0;
106
0
}
107
108
/** Call the callback registered for a timeout event
109
 *
110
 * @param[in] tl  the event timer was inserted into.
111
 * @param[in] now The current time, as held by the event_list.
112
 * @param[in] uctx  unlang_module_event_t structure holding callbacks.
113
 *
114
 */
115
static void unlang_xlat_event_timeout_handler(UNUSED fr_timer_list_t *tl, fr_time_t now, void *uctx)
116
0
{
117
0
  unlang_xlat_event_t *ev = talloc_get_type_abort(uctx, unlang_xlat_event_t);
118
119
  /*
120
   *  If the timeout's fired then the xlat must necessarily
121
   *  be yielded, so it's fine to pass in its rctx.
122
   *
123
   *  It should be able to free the rctx if it wants to.
124
   *  We never free it explicitly, and instead rely on
125
   *  talloc parenting.
126
   */
127
0
  ev->timeout(XLAT_CTX(ev->inst->data,
128
0
           ev->thread->data,
129
0
           NULL,
130
0
           ev->thread->mctx, NULL,
131
0
           UNCONST(void *, ev->rctx)),
132
0
           ev->request, now);
133
134
  /* Remove old references from the request */
135
0
  talloc_free(ev);
136
0
}
137
138
/** Add a timeout for an xlat handler
139
 *
140
 * @note The timeout is automatically removed when the xlat is cancelled or resumed.
141
 *
142
 * @param[in] request the request
143
 * @param[in] callback  to run when the timeout hits
144
 * @param[in] rctx  passed to the callback
145
 * @param[in] when  when the timeout fires
146
 * @return
147
 *  - <0 on error
148
 *  - 0 on success
149
 */
150
int unlang_xlat_timeout_add(request_t *request,
151
          fr_unlang_xlat_timeout_t callback, void const *rctx, fr_time_t when)
152
0
{
153
0
  unlang_stack_t      *stack = request->stack;
154
0
  unlang_stack_frame_t    *frame = &stack->frame[stack->depth];
155
0
  unlang_xlat_event_t   *ev;
156
0
  unlang_frame_state_xlat_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_xlat_t);
157
158
0
  fr_assert(stack->depth > 0);
159
0
  fr_assert(frame->instruction->type == UNLANG_TYPE_XLAT);
160
161
0
  if (!state->event_ctx) MEM(state->event_ctx = talloc_zero(state, bool));
162
163
0
  MEM(ev = talloc_zero(state->event_ctx, unlang_xlat_event_t));
164
0
  ev->request = request;
165
0
  ev->fd = -1;
166
0
  ev->timeout = callback;
167
0
  fr_assert(state->exp->type == XLAT_FUNC);
168
0
  ev->inst = state->exp->call.inst;
169
0
  ev->thread = xlat_thread_instance_find(state->exp);
170
0
  ev->rctx = rctx;
171
172
0
  if (fr_timer_at(request, unlang_interpret_event_list(request)->tl,
173
0
      &ev->ev, when,
174
0
      false, unlang_xlat_event_timeout_handler, ev) < 0) {
175
0
    RPEDEBUG("Failed inserting event");
176
0
    talloc_free(ev);
177
0
    return -1;
178
0
  }
179
180
0
  talloc_set_destructor(ev, _unlang_xlat_event_free);
181
182
0
  return 0;
183
0
}
184
185
/** Push a pre-compiled xlat onto the stack for evaluation
186
 *
187
 * @param[in] ctx   To allocate value boxes and values in.
188
 * @param[out] p_result   If set, rcodes and priorities will be written here and
189
 *        not evaluated by the unlang interpreter.
190
 * @param[out] out    Where to write the result of the expansion.
191
 * @param[in] request   to push xlat onto.
192
 * @param[in] xlat    head of list
193
 * @param[in] node    to evaluate.
194
 * @param[in] top_frame   Set to UNLANG_TOP_FRAME if the interpreter should return.
195
 *        Set to UNLANG_SUB_FRAME if the interpreter should continue.
196
 * @return
197
 *  - 0 on success.
198
 *  - -1 on failure.
199
 */
200
static int unlang_xlat_push_internal(TALLOC_CTX *ctx, unlang_result_t *p_result, fr_value_box_list_t *out,
201
             request_t *request, xlat_exp_head_t const *xlat, xlat_exp_t *node, bool top_frame)
202
0
{
203
  /** Static instruction for performing xlat evaluations
204
   *
205
   */
206
0
  static unlang_t xlat_instruction = {
207
0
    .type = UNLANG_TYPE_XLAT,
208
0
    .name = "xlat",
209
0
    .debug_name = "xlat",
210
0
    .actions = MOD_ACTIONS_FAIL_TIMEOUT_RETURN,
211
0
  };
212
213
0
  unlang_frame_state_xlat_t *state;
214
0
  unlang_stack_t      *stack = request->stack;
215
0
  unlang_stack_frame_t    *frame;
216
217
  /*
218
   *  Push a new xlat eval frame onto the stack
219
   */
220
0
  if (unlang_interpret_push(p_result, request, &xlat_instruction,
221
0
          FRAME_CONF(RLM_MODULE_NOT_SET, top_frame), UNLANG_NEXT_STOP) < 0) return -1;
222
0
  frame = &stack->frame[stack->depth];
223
224
  /*
225
   *  Allocate its state, and setup a cursor for the xlat nodes
226
   */
227
0
  MEM(frame->state = state = talloc_zero(stack, unlang_frame_state_xlat_t));
228
0
  state->head = xlat;
229
0
  state->exp = node;
230
0
  state->p_result = p_result;
231
0
  state->ctx = ctx;
232
233
0
  if (node) switch (node->type) {
234
0
  case XLAT_GROUP:
235
0
  case XLAT_BOX:
236
0
    break;
237
238
0
  case XLAT_TMPL:
239
0
    if (tmpl_is_data(node->vpt)) break;
240
0
    FALL_THROUGH;
241
242
0
  default:
243
0
    RDEBUG("| %s", node->fmt);
244
0
    break;
245
0
  }
246
247
  /*
248
   *  Initialise the input and output lists
249
   */
250
0
  fr_dcursor_init(&state->values, fr_value_box_list_dlist_head(out));
251
0
  fr_value_box_list_init(&state->out);
252
253
0
  return 0;
254
0
}
255
256
/** Push a pre-compiled xlat onto the stack for evaluation
257
 *
258
 * @param[in] ctx   To allocate value boxes and values in.
259
 * @param[out] p_result         The frame result
260
 * @param[out] out    Where to write the result of the expansion.
261
 * @param[in] request   to push xlat onto.
262
 * @param[in] xlat    to evaluate.
263
 * @param[in] top_frame   Set to UNLANG_TOP_FRAME if the interpreter should return.
264
 *        Set to UNLANG_SUB_FRAME if the interpreter should continue.
265
 * @return
266
 *  - 0 on success.
267
 *  - -1 on failure.
268
 */
269
int unlang_xlat_push(TALLOC_CTX *ctx, unlang_result_t *p_result, fr_value_box_list_t *out,
270
         request_t *request, xlat_exp_head_t const *xlat, bool top_frame)
271
0
{
272
0
  (void) talloc_get_type_abort_const(xlat, xlat_exp_head_t);
273
274
0
  return unlang_xlat_push_internal(ctx, p_result, out, request, xlat, xlat_exp_head(xlat), top_frame);
275
0
}
276
277
/** Push a pre-compiled xlat onto the stack for evaluation
278
 *
279
 * @param[in] ctx   To allocate value boxes and values in.
280
 * @param[out] p_result   If set, and execution succeeds, true will be written
281
 *        here.  If execution fails, false will be written.
282
 * @param[out] out    Where to write the result of the expansion.
283
 * @param[in] request   to push xlat onto.
284
 * @param[in] node    to evaluate.  Only this node will be evaluated.
285
 * @return
286
 *  - 0 on success.
287
 *  - -1 on failure.
288
 */
289
int unlang_xlat_push_node(TALLOC_CTX *ctx, unlang_result_t *p_result, fr_value_box_list_t *out,
290
        request_t *request, xlat_exp_t *node)
291
0
{
292
0
  return unlang_xlat_push_internal(ctx, p_result, out, request, NULL, node, UNLANG_TOP_FRAME);
293
0
}
294
295
static unlang_action_t unlang_xlat_repeat(unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame)
296
0
{
297
0
  unlang_frame_state_xlat_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_xlat_t);
298
0
  xlat_action_t     xa;
299
0
  xlat_exp_head_t const   *child = NULL;
300
301
  /*
302
   *  If the xlat is a function with a method_env, expand it before calling the function.
303
   */
304
0
  if ((state->exp->type == XLAT_FUNC) && state->exp->call.inst->call_env && !state->env_data) {
305
0
    unlang_action_t ua = call_env_expand(state, request, NULL, &state->env_data,
306
0
                 state->exp->call.inst->call_env);
307
0
    switch (ua) {
308
0
    case UNLANG_ACTION_FAIL:
309
0
      goto fail;
310
311
0
    case UNLANG_ACTION_PUSHED_CHILD:
312
0
      frame_repeat(frame, unlang_xlat_repeat);
313
0
      return UNLANG_ACTION_PUSHED_CHILD;
314
315
0
    default:
316
0
      break;
317
0
    }
318
0
  }
319
320
0
  xa = xlat_frame_eval_repeat(state->ctx, &state->values, &child,
321
0
            request, state->head, &state->exp, state->env_data, &state->out);
322
0
  switch (xa) {
323
0
  case XLAT_ACTION_PUSH_CHILD:
324
0
    fr_assert(child);
325
326
0
    repeatable_set(frame);  /* Was cleared by the interpreter */
327
328
    /*
329
     *  Clear out the results of any previous expansions
330
     *  at this level.  A frame may be used to evaluate
331
     *  multiple sibling nodes.
332
     */
333
0
    fr_value_box_list_talloc_free(&state->out);
334
0
    if (unlang_xlat_push(state->ctx, p_result, &state->out, request, child, false) < 0) {
335
0
      REXDENT();
336
0
      RETURN_UNLANG_ACTION_FATAL;
337
0
    }
338
0
    return UNLANG_ACTION_PUSHED_CHILD;
339
340
0
  case XLAT_ACTION_PUSH_UNLANG:
341
0
    repeatable_set(frame);  /* Call the xlat code on the way back down */
342
0
    return UNLANG_ACTION_PUSHED_CHILD;
343
344
0
  case XLAT_ACTION_YIELD:
345
0
    if (!state->resume) {
346
0
      RWDEBUG("Missing call to unlang_xlat_yield()");
347
0
      goto fail;
348
0
    }
349
0
    repeatable_set(frame);
350
0
    return UNLANG_ACTION_YIELD;
351
352
0
  case XLAT_ACTION_DONE:
353
0
    *p_result = UNLANG_RESULT_RCODE(RLM_MODULE_OK);
354
0
    REXDENT();
355
0
    return UNLANG_ACTION_CALCULATE_RESULT;
356
357
0
  case XLAT_ACTION_FAIL:
358
0
  fail:
359
0
    REXDENT();
360
0
    return UNLANG_ACTION_FAIL;
361
362
0
  default:
363
0
    fr_assert(0);
364
0
    goto fail;
365
0
  }
366
0
}
367
368
/** Stub function for calling the xlat interpreter
369
 *
370
 * Calls the xlat interpreter and translates its wants and needs into
371
 * unlang_action_t codes.
372
 */
373
static unlang_action_t unlang_xlat(UNUSED unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame)
374
0
{
375
0
  unlang_frame_state_xlat_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_xlat_t);
376
0
  xlat_action_t     xa;
377
0
  xlat_exp_head_t const   *child = NULL;
378
379
0
  RINDENT_SAVE(state, request);
380
0
  RINDENT();
381
382
0
  xa = xlat_frame_eval(state->ctx, &state->values, &child, request, state->head, &state->exp);
383
0
  switch (xa) {
384
0
  case XLAT_ACTION_PUSH_CHILD:
385
0
    fr_assert(child);
386
387
0
    frame_repeat(frame, unlang_xlat_repeat);
388
389
    /*
390
     *  Clear out the results of any previous expansions
391
     *  at this level.  A frame may be used to evaluate
392
     *  multiple sibling nodes.
393
     */
394
0
    fr_value_box_list_talloc_free(&state->out);
395
0
    if (unlang_xlat_push(state->ctx, p_result, &state->out, request, child, false) < 0) {
396
0
      RINDENT_RESTORE(request, state);
397
0
      RETURN_UNLANG_ACTION_FATAL;
398
0
    }
399
0
    return UNLANG_ACTION_PUSHED_CHILD;
400
401
0
  case XLAT_ACTION_PUSH_UNLANG:
402
0
    repeatable_set(frame);  /* Call the xlat code on the way back down */
403
0
    return UNLANG_ACTION_PUSHED_CHILD;
404
405
0
  case XLAT_ACTION_YIELD:
406
0
    if (!state->resume) {
407
0
      RWDEBUG("Missing call to unlang_xlat_yield()");
408
0
      goto fail;
409
0
    }
410
0
    repeatable_set(frame);
411
0
    return UNLANG_ACTION_YIELD;
412
413
0
  case XLAT_ACTION_DONE:
414
0
    *p_result = UNLANG_RESULT_RCODE(RLM_MODULE_OK);
415
0
    RINDENT_RESTORE(request, state);
416
0
    return UNLANG_ACTION_CALCULATE_RESULT;
417
418
0
  case XLAT_ACTION_FAIL:
419
0
  fail:
420
0
    RINDENT_RESTORE(request, state);
421
0
    return UNLANG_ACTION_FAIL;
422
423
0
  default:
424
0
    fr_assert(0);
425
0
    goto fail;
426
0
  }
427
0
}
428
429
/** Send a signal (usually stop) to a request that's running an xlat expansions
430
 *
431
 * This is typically called via an "async" action, i.e. an action
432
 * outside of the normal processing of the request.
433
 *
434
 * If there is no #xlat_func_signal_t callback defined, the action is ignored.
435
 *
436
 * @param[in] request   The current request.
437
 * @param[in] frame   The current stack frame.
438
 * @param[in] action    What the request should do (the type of signal).
439
 */
440
static void unlang_xlat_signal(request_t *request, unlang_stack_frame_t *frame, fr_signal_t action)
441
0
{
442
0
  unlang_frame_state_xlat_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_xlat_t);
443
444
  /*
445
   *  Delete timers, etc. when the xlat is cancelled.
446
   */
447
0
  if (action == FR_SIGNAL_CANCEL) {
448
0
    TALLOC_FREE(state->event_ctx);
449
0
  }
450
451
0
  if (!state->signal || (state->sigmask & action)) return;
452
453
0
  xlat_signal(state->signal, state->exp, request, state->rctx, action);
454
0
}
455
456
/** Called when we're ready to resume processing the request
457
 *
458
 * @param[in] p_result  the result of the xlat function.
459
 *        - RLM_MODULE_OK on success.
460
 *        - RLM_MODULE_FAIL on failure.
461
 * @param[in] request to resume processing.
462
 * @param[in] frame the current stack frame.
463
 * @return
464
 *  - UNLANG_ACTION_YIELD if additional asynchronous
465
 *    operations need to be performed.
466
 *  - UNLANG_ACTION_CALCULATE_RESULT if done.
467
 */
468
static unlang_action_t unlang_xlat_resume(unlang_result_t *p_result, request_t *request, unlang_stack_frame_t *frame)
469
0
{
470
0
  unlang_frame_state_xlat_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_xlat_t);
471
0
  xlat_action_t     xa;
472
0
  xlat_exp_head_t const   *child = NULL;
473
474
0
  fr_assert(state->resume != NULL);
475
476
  /*
477
   *  Delete timers, etc. when the xlat is resumed.
478
   */
479
0
  TALLOC_FREE(state->event_ctx);
480
481
0
  xa = xlat_frame_eval_resume(state->ctx, &state->values, &child, request, state->head, &state->exp,
482
0
            &state->out, state->resume, state->rctx);
483
0
  switch (xa) {
484
0
  case XLAT_ACTION_YIELD:
485
0
    repeatable_set(frame);
486
0
    return UNLANG_ACTION_YIELD;
487
488
0
  case XLAT_ACTION_DONE:
489
0
    *p_result = UNLANG_RESULT_RCODE(RLM_MODULE_OK);
490
0
    RINDENT_RESTORE(request, state);
491
0
    return UNLANG_ACTION_CALCULATE_RESULT;
492
493
0
  case XLAT_ACTION_PUSH_UNLANG:
494
0
    repeatable_set(frame);
495
0
    return UNLANG_ACTION_PUSHED_CHILD;
496
497
0
  case XLAT_ACTION_PUSH_CHILD:
498
0
    fr_assert(child);
499
500
0
    repeatable_set(frame);  /* Was cleared by the interpreter */
501
502
    /*
503
     *  Clear out the results of any previous expansions
504
     *  at this level.  A frame may be used to evaluate
505
     *  multiple sibling nodes.
506
     */
507
0
    fr_value_box_list_talloc_free(&state->out);
508
0
    if (unlang_xlat_push(state->ctx, state->p_result, &state->out, request, child, false) < 0) {
509
0
      RINDENT_RESTORE(request, state);
510
0
      RETURN_UNLANG_ACTION_FATAL;
511
0
    }
512
0
    return UNLANG_ACTION_PUSHED_CHILD;
513
514
0
  case XLAT_ACTION_FAIL:
515
0
    RINDENT_RESTORE(request, state);
516
0
    return UNLANG_ACTION_FAIL;
517
  /* DON'T SET DEFAULT */
518
0
  }
519
520
0
  fr_assert(0);    /* Garbage xlat action */
521
522
0
  RINDENT_RESTORE(request, state);
523
0
  return UNLANG_ACTION_FAIL;
524
0
}
525
526
/** Yield a request back to the interpreter from within a module
527
 *
528
 * This passes control of the request back to the unlang interpreter, setting
529
 * callbacks to execute when the request is 'signalled' asynchronously, or whatever
530
 * timer or I/O event the module was waiting for occurs.
531
 *
532
 * @note The module function which calls #unlang_module_yield should return control
533
 *  of the C stack to the unlang interpreter immediately after calling #unlang_xlat_yield.
534
 *  A common pattern is to use ``return unlang_xlat_yield(...)``.
535
 *
536
 * @param[in] request   The current request.
537
 * @param[in] resume    Called on unlang_interpret_mark_runnable().
538
 * @param[in] signal    Called on unlang_action().
539
 * @param[in] sigmask   Signals to block.
540
 * @param[in] rctx    to pass to the callbacks.
541
 * @return always returns XLAT_ACTION_YIELD
542
 */
543
xlat_action_t unlang_xlat_yield(request_t *request,
544
        xlat_func_t resume, xlat_func_signal_t signal, fr_signal_t sigmask,
545
        void *rctx)
546
0
{
547
0
  unlang_stack_t      *stack = request->stack;
548
0
  unlang_stack_frame_t    *frame = &stack->frame[stack->depth];
549
0
  unlang_frame_state_xlat_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_xlat_t);
550
551
0
  frame->process = unlang_xlat_resume;
552
553
  /*
554
   *  Over-ride whatever functions were there before.
555
   */
556
0
  state->resume = resume;
557
0
  state->signal = signal;
558
0
  state->sigmask = sigmask;
559
0
  state->rctx = rctx;
560
561
0
  return XLAT_ACTION_YIELD;
562
0
}
563
564
/** Frees an unlang event, removing it from the request's event loop
565
 *
566
 * @param[in] ev  The event to free.
567
 *
568
 * @return 0
569
 */
570
static int _unlang_xlat_retry_free(unlang_xlat_retry_t *ev)
571
0
{
572
0
  FR_TIMER_DELETE(&(ev->ev));
573
574
0
  return 0;
575
0
}
576
577
/** Call the callback registered for a timeout event
578
 *
579
 * @param[in] tl  the event timer was inserted into.
580
 * @param[in] now The current time, as held by the event_list.
581
 * @param[in] uctx  unlang_module_event_t structure holding callbacks.
582
 *
583
 */
584
static void unlang_xlat_event_retry_handler(UNUSED fr_timer_list_t *tl, fr_time_t now, void *uctx)
585
0
{
586
0
  unlang_xlat_retry_t *ev = talloc_get_type_abort(uctx, unlang_xlat_retry_t);
587
0
  request_t   *request = ev->request;
588
589
0
  switch (fr_retry_next(&ev->retry, now)) {
590
0
  case FR_RETRY_CONTINUE:
591
    /*
592
     *  Call the module retry handler, with the state of the retry.  On MRD / MRC, the
593
     *  module is made runnable again, and the "resume" function is called.
594
     */
595
0
    ev->retry_cb(XLAT_CTX(ev->inst->data,
596
0
              ev->thread->data,
597
0
              NULL,
598
0
              ev->thread->mctx, NULL,
599
0
              UNCONST(void *, ev->rctx)),
600
0
           ev->request, &ev->retry);
601
602
    /*
603
     *  Reset the timer.
604
     */
605
0
    if (fr_timer_at(ev, unlang_interpret_event_list(request)->tl, &ev->ev, ev->retry.next,
606
0
        false, unlang_xlat_event_retry_handler, ev) < 0) {
607
0
      RPEDEBUG("Failed inserting event");
608
0
      talloc_free(ev);
609
0
      unlang_interpret_mark_runnable(request);
610
0
    }
611
0
    return;
612
613
0
  case FR_RETRY_MRD:
614
0
    RDEBUG("Reached max_rtx_duration (%pVs > %pVs) - sending timeout",
615
0
      fr_box_time_delta(fr_time_sub(now, ev->retry.start)), fr_box_time_delta(ev->retry.config->mrd));
616
0
    break;
617
618
0
  case FR_RETRY_MRC:
619
0
    RDEBUG("Reached max_rtx_count %u- sending timeout",
620
0
            ev->retry.config->mrc);
621
0
    break;
622
0
  }
623
624
  /*
625
   *  Run the retry handler on MRD / MRC, too.
626
   */
627
0
  ev->retry_cb(XLAT_CTX(ev->inst->data,
628
0
            ev->thread->data,
629
0
            NULL,
630
0
            ev->thread->mctx, NULL,
631
0
            UNCONST(void *, ev->rctx)),
632
0
         ev->request, &ev->retry);
633
634
  /*
635
   *  On final timeout, always mark the request as runnable.
636
   */
637
0
  talloc_free(ev);
638
0
  unlang_interpret_mark_runnable(request);
639
0
}
640
641
642
/** Yield a request back to the interpreter, with retries
643
 *
644
 * This passes control of the request back to the unlang interpreter, setting
645
 * callbacks to execute when the request is 'signalled' asynchronously, or when
646
 * the retry timer hits.
647
 *
648
 * @note The module function which calls #unlang_module_yield_to_retry should return control
649
 *  of the C stack to the unlang interpreter immediately after calling #unlang_module_yield_to_retry.
650
 *  A common pattern is to use ``return unlang_module_yield_to_retry(...)``.
651
 *
652
 * @param[in] request   The current request.
653
 * @param[in] resume    Called on unlang_interpret_mark_runnable().
654
 * @param[in] retry   Called on when a retry timer hits
655
 * @param[in] signal    Called on unlang_action().
656
 * @param[in] sigmask   Set of signals to block.
657
 * @param[in] rctx    to pass to the callbacks.
658
 * @param[in] retry_cfg   to set up the retries
659
 * @return
660
 *  - XLAT_ACTION_YIELD on success
661
 *  - XLAT_ACTION_FAIL on failure
662
 */
663
xlat_action_t unlang_xlat_yield_to_retry(request_t *request, xlat_func_t resume, fr_unlang_xlat_retry_t retry,
664
           xlat_func_signal_t signal, fr_signal_t sigmask, void *rctx,
665
           fr_retry_config_t const *retry_cfg)
666
0
{
667
0
  unlang_stack_t      *stack = request->stack;
668
0
  unlang_stack_frame_t    *frame = &stack->frame[stack->depth];
669
0
  unlang_xlat_retry_t   *ev;
670
0
  unlang_frame_state_xlat_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_xlat_t);
671
672
0
  fr_assert(stack->depth > 0);
673
0
  fr_assert(frame->instruction->type == UNLANG_TYPE_XLAT);
674
675
0
  if (!state->event_ctx) MEM(state->event_ctx = talloc_zero(state, bool));
676
677
0
  MEM(ev = talloc_zero(state->event_ctx, unlang_xlat_retry_t));
678
679
0
  ev->request = request;
680
0
  fr_assert(state->exp->type == XLAT_FUNC);
681
0
  ev->inst = state->exp->call.inst;
682
0
  ev->thread = xlat_thread_instance_find(state->exp);
683
0
  ev->retry_cb = retry;
684
0
  ev->rctx = rctx;
685
686
0
  fr_retry_init(&ev->retry, fr_time(), retry_cfg);
687
688
0
  if (fr_timer_at(request, unlang_interpret_event_list(request)->tl,
689
0
      &ev->ev, ev->retry.next,
690
0
      false, unlang_xlat_event_retry_handler, ev) < 0) {
691
0
    RPEDEBUG("Failed inserting event");
692
0
    talloc_free(ev);
693
0
    return XLAT_ACTION_FAIL;
694
0
  }
695
696
0
  talloc_set_destructor(ev, _unlang_xlat_retry_free);
697
698
0
  return unlang_xlat_yield(request, resume, signal, sigmask, rctx);
699
0
}
700
701
/** Evaluate a "pure" (or not impure) xlat
702
 *
703
 * @param[in] ctx   To allocate value boxes and values in.
704
 * @param[out] out    Where to write the result of the expansion.
705
 * @param[in] request   to push xlat onto.
706
 * @param[in] xlat    to evaluate.
707
 * @return
708
 *  - 0 on success.
709
 *  - -1 on failure.
710
 */
711
int unlang_xlat_eval(TALLOC_CTX *ctx, fr_value_box_list_t *out, request_t *request, xlat_exp_head_t const *xlat)
712
0
{
713
0
  unlang_result_t result = UNLANG_RESULT_NOT_SET;
714
715
0
  if (xlat->flags.impure_func) {
716
0
    fr_strerror_const("Expansion requires async operations");
717
0
    return -1;
718
0
  }
719
720
0
  if (unlang_xlat_push(ctx, &result, out, request, xlat, UNLANG_TOP_FRAME) < 0) return -1;
721
722
0
  (void) unlang_interpret(request, UNLANG_REQUEST_RUNNING);
723
724
0
  if (!XLAT_RESULT_SUCCESS(&result)) return -1;
725
726
0
  return 0;
727
0
}
728
729
/** Evaluate a "pure" (or not impure) xlat
730
 *
731
 * @param[in] ctx   To allocate value boxes and values in.
732
 * @param[out] vb   output value-box
733
 * @param[in] type    expected type
734
 * @param[in] enumv   enum for type
735
 * @param[in] request   to push xlat onto.
736
 * @param[in] xlat    to evaluate.
737
 * @return
738
 *  - 0 on success.
739
 *  - -1 on failure.
740
 */
741
int unlang_xlat_eval_type(TALLOC_CTX *ctx, fr_value_box_t *vb, fr_type_t type, fr_dict_attr_t const *enumv, request_t *request, xlat_exp_head_t const *xlat)
742
0
{
743
0
  fr_value_box_t *src;
744
0
  fr_value_box_list_t list;
745
746
0
  if (fr_type_is_structural(type)) {
747
0
    fr_strerror_const("Invalid type for output of evaluation");
748
0
    return -1;
749
0
  }
750
751
0
  fr_value_box_list_init(&list);
752
753
0
  if (unlang_xlat_eval(ctx, &list, request, xlat) < 0) return -1;
754
755
0
  fr_value_box_init(vb, type, NULL, false);
756
757
0
  switch (type) {
758
0
  default:
759
    /*
760
     *  Take only the first entry from the list.
761
     */
762
0
    src = fr_value_box_list_head(&list);
763
0
    if (!src) {
764
0
      fr_strerror_const("Expression returned no results");
765
0
    fail:
766
0
      fr_value_box_list_talloc_free(&list);
767
0
      return -1;
768
0
    }
769
770
0
    if (fr_value_box_cast(ctx, vb, type, enumv, src) < 0) goto fail;
771
0
    fr_value_box_list_talloc_free(&list);
772
0
    break;
773
774
0
  case FR_TYPE_STRING:
775
0
  case FR_TYPE_OCTETS:
776
    /*
777
     *  No output: create an empty string.
778
     *
779
     *  The "concat in place" function returns an error for empty input, which is arguably not
780
     *  what we want to do here.
781
     */
782
0
    if (fr_value_box_list_empty(&list)) {
783
0
      break;
784
0
    }
785
786
0
    if (fr_value_box_list_concat_in_place(ctx, vb, &list, type, FR_VALUE_BOX_LIST_FREE_BOX, false, SIZE_MAX) < 0) {
787
0
      goto fail;
788
0
    }
789
0
    break;
790
0
  }
791
792
0
  return 0;
793
0
}
794
795
static void unlang_xlat_dump(request_t *request, unlang_stack_frame_t *frame)
796
0
{
797
0
  unlang_frame_state_xlat_t *state = talloc_get_type_abort(frame->state, unlang_frame_state_xlat_t);
798
0
  xlat_exp_t const    *exp = state->exp;
799
800
0
  if (exp) RDEBUG("expression     %s", exp->fmt);
801
0
}
802
/** Register xlat operation with the interpreter
803
 *
804
 */
805
void unlang_xlat_init(void)
806
4
{
807
4
  unlang_register(&(unlang_op_t){
808
4
      .name = "xlat",
809
4
      .type = UNLANG_TYPE_XLAT,
810
4
      .flag = UNLANG_OP_FLAG_INTERNAL,
811
812
4
      .interpret = unlang_xlat,
813
4
      .signal = unlang_xlat_signal,
814
4
      .dump = unlang_xlat_dump,
815
816
4
      .frame_state_size = sizeof(unlang_frame_state_xlat_t),
817
4
      .frame_state_type = "unlang_frame_state_xlat_t",
818
4
    });
819
4
}