The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
request.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 2 of the License, or
5  * (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16 
17 /**
18  * $Id: 91cfebee5ad5df016cc62e07cc05f50d46f556a1 $
19  *
20  * @brief Functions for allocating requests and storing internal data in them.
21  * @file src/lib/server/request.c
22  *
23  * @copyright 2015 The FreeRADIUS server project
24  */
25 RCSID("$Id: 91cfebee5ad5df016cc62e07cc05f50d46f556a1 $")
26 
27 #include <freeradius-devel/server/request.h>
28 #include <freeradius-devel/server/request_data.h>
29 #include <freeradius-devel/unlang/interpret.h>
30 
31 #include <freeradius-devel/util/debug.h>
32 #include <freeradius-devel/util/atexit.h>
33 
35 
36 static fr_dict_t const *dict_freeradius;
37 
40  { .out = &dict_freeradius, .proto = "freeradius" },
41  { NULL }
42 };
43 
50 
53  { .out = &request_attr_root, .name = "root", .type = FR_TYPE_GROUP, .dict = &dict_freeradius },
54  { .out = &request_attr_request, .name = "request", .type = FR_TYPE_GROUP, .dict = &dict_freeradius },
55  { .out = &request_attr_reply, .name = "reply", .type = FR_TYPE_GROUP, .dict = &dict_freeradius },
56  { .out = &request_attr_control, .name = "control", .type = FR_TYPE_GROUP, .dict = &dict_freeradius },
57  { .out = &request_attr_state, .name = "session-state", .type = FR_TYPE_GROUP, .dict = &dict_freeradius },
58  { .out = &request_attr_local, .name = "local-variables", .type = FR_TYPE_GROUP, .dict = &dict_freeradius },
59  { NULL }
60 };
61 
62 /** The thread local free list
63  *
64  * Any entries remaining in the list will be freed when the thread is joined
65  */
66 static _Thread_local fr_dlist_head_t *request_free_list; /* macro */
67 
68 #ifndef NDEBUG
69 static int _state_ctx_free(fr_pair_t *state)
70 {
71  DEBUG4("state-ctx %p freed", state);
72 
73  return 0;
74 }
75 #endif
76 
77 static inline void CC_HINT(always_inline) request_log_init_orphan(request_t *request)
78 {
79  /*
80  * These may be changed later by request_pre_handler
81  */
82  request->log.lvl = fr_debug_lvl; /* Default to global debug level */
83  if (!request->log.dst) {
84  request->log.dst = talloc_zero(request, log_dst_t);
85  } else {
86  memset(request->log.dst, 0, sizeof(*request->log.dst));
87  }
88  request->log.dst->func = vlog_request;
89  request->log.dst->uctx = &default_log;
90  request->log.dst->lvl = fr_debug_lvl;
91 }
92 
93 /** Prepend another logging destination to the list.
94  *
95 
96  * @param request the request
97  * @param log_dst the logging destination
98  * @param lvl the new request debug lvl
99  */
101 {
102  log_dst_t *dst;
103 
104  if (lvl == L_DBG_LVL_DISABLE) {
105  while (request->log.dst) {
106  dst = request->log.dst->next;
107  talloc_free(request->log.dst);
108  request->log.dst = dst;
109  }
110  request->log.lvl = L_DBG_LVL_OFF;
111  return;
112  }
113 
114  /*
115  * Remove a particular log destination.
116  */
117  if (lvl == L_DBG_LVL_OFF) {
118  log_dst_t **last;
119 
120  last = &request->log.dst;
121  while (*last) {
122  dst = *last;
123  if (((fr_log_t *)dst->uctx)->parent == log_dst) {
124  *last = dst->next;
125  talloc_free(dst);
126  if (!request->log.dst) request->log.lvl = L_DBG_LVL_OFF;
127  return;
128  }
129 
130  last = &(dst->next);
131  }
132 
133  return;
134  }
135 
136  /*
137  * Change the debug level of an existing destination.
138  */
139  for (dst = request->log.dst; dst != NULL; dst = dst->next) {
140  if (((fr_log_t *)dst->uctx)->parent == log_dst) {
141  dst->lvl = lvl;
142  if (lvl > request->log.lvl) request->log.lvl = lvl;
143  return;
144  }
145  }
146 
147  /*
148  * Not found, add a new log destination.
149  */
150  MEM(dst = talloc_zero(request, log_dst_t));
151 
152  dst->func = vlog_request;
153  dst->uctx = log_dst;
154 
155  dst->lvl = lvl;
156  if (lvl > request->log.lvl) request->log.lvl = lvl;
157  dst->next = request->log.dst;
158 
159  request->log.dst = dst;
160 }
161 
162 static inline void CC_HINT(always_inline) request_log_init_child(request_t *child, request_t const *parent)
163 {
164  /*
165  * Copy debug information.
166  */
167  memcpy(&(child->log), &(parent->log), sizeof(child->log));
168  child->log.indent.unlang = 0; /* Apart from the indent which we reset */
169  child->log.indent.module = 0; /* Apart from the indent which we reset */
170  child->log.lvl = parent->log.lvl;
171 }
172 
173 static inline void CC_HINT(always_inline) request_log_init_detachable(request_t *child, request_t const *parent)
174 {
176 
177  /*
178  * Ensure that we use our own version of the logging
179  * information, and not the original request one.
180  */
181  child->log.dst = talloc_zero(child, log_dst_t);
182  memcpy(child->log.dst, parent->log.dst, sizeof(*child->log.dst));
183 }
184 
185 static inline CC_HINT(always_inline) int request_detachable_init(request_t *child, request_t *parent)
186 {
187  /*
188  * Associate the child with the parent, using the child's
189  * pointer as a unique identifier. Free it if the parent
190  * goes away, but don't persist it across
191  * challenge-response boundaries.
192  */
193  if (request_data_talloc_add(parent, child, 0, request_t, child, true, true, false) < 0) return -1;
194 
195  return 0;
196 }
197 
198 static inline CC_HINT(always_inline) int request_child_init(request_t *child, request_t *parent)
199 {
200  child->number = parent->child_number++;
201  if (!child->dict) child->dict = parent->dict;
202 
203  if ((parent->seq_start == 0) || (parent->number == parent->seq_start)) {
204  child->name = talloc_typed_asprintf(child, "%s.%" PRIu64, parent->name, child->number);
205  } else {
206  child->name = talloc_typed_asprintf(child, "(%s,%" PRIu64 ").%" PRIu64,
207  parent->name, parent->seq_start, child->number);
208  }
209  child->seq_start = 0; /* children always start with their own sequence */
210  child->parent = parent;
211 
212  /*
213  * For new server support.
214  *
215  * FIXME: Key instead off of a "virtual server" data structure.
216  *
217  * FIXME: Permit different servers for inner && outer sessions?
218  */
219  child->packet = fr_packet_alloc(child, true);
220  if (!child->packet) {
221  talloc_free(child);
222  return -1;
223  }
224 
225  child->reply = fr_packet_alloc(child, false);
226  if (!child->reply) {
227  talloc_free(child);
228  return -1;
229  }
230 
231  return 0;
232 }
233 
234 /** Setup logging and other fields for a request
235  *
236  * @param[in] file the request was allocated in.
237  * @param[in] line the request was allocated on.
238  * @param[in] request to (re)-initialise.
239  * @param[in] type of request to initialise.
240  * @param[in] args Other optional arguments.
241  */
242 static inline CC_HINT(always_inline) int request_init(char const *file, int line,
243  request_t *request, request_type_t type,
244  request_init_args_t const *args)
245 {
246 
247  /*
248  * Sanity checks for different requests types
249  */
250  switch (type) {
252  if (!fr_cond_assert_msg(!args->parent, "External requests must NOT have a parent")) return -1;
253  break;
254 
256  break;
257 
259  fr_assert_fail("Detached requests should start as type == REQUEST_TYPE_INTERNAL, "
260  "args->detachable and be detached later");
261  return -1;
262  }
263 
264  *request = (request_t){
265 #ifndef NDEBUG
266  .magic = REQUEST_MAGIC,
267 #endif
268  .type = type,
269  .master_state = REQUEST_ACTIVE,
270  .dict = args->namespace,
271  .component = "<pre-core>",
272  .flags = {
273  .detachable = args->detachable
274  },
275  .alloc_file = file,
276  .alloc_line = line
277  };
278 
279 
280  /*
281  * Initialise the stack
282  */
283  MEM(request->stack = unlang_interpret_stack_alloc(request));
284 
285  /*
286  * Initialise the request data list
287  */
288  request_data_list_init(&request->data);
289 
290  {
291  fr_pair_t *vp = NULL, *pair_root;
292 
293  /*
294  * Alloc the pair root this is a
295  * special pair which does not
296  * free its children when it is
297  * freed.
298  */
299  pair_root = fr_pair_root_afrom_da(request, request_attr_root);
300  if (unlikely(!pair_root)) return -1;
301  request->pair_root = pair_root;
302 
303  /*
304  * Copy all the pair lists over into
305  * the request. We then check for
306  * the any uninitialised lists and
307  * create them locally.
308  */
309  memcpy(&request->pair_list, &args->pair_list, sizeof(request->pair_list));
310 
311 #define list_init(_ctx, _list) \
312  do { \
313  vp = fr_pair_afrom_da(_ctx, request_attr_##_list); \
314  if (unlikely(!vp)) { \
315  talloc_free(pair_root); \
316  memset(&request->pair_list, 0, sizeof(request->pair_list)); \
317  return -1; \
318  } \
319  fr_pair_append(&pair_root->children, vp); \
320  request->pair_list._list = vp; \
321  } while(0)
322 
323  if (!request->pair_list.request) list_init(request->pair_root, request);
324  if (!request->pair_list.reply) list_init(request->pair_root, reply);
325  if (!request->pair_list.control) list_init(request->pair_root, control);
326  if (!request->pair_list.local) list_init(request->pair_root, local);
327  if (!request->pair_list.state) {
328  list_init(NULL, state);
329 #ifndef NDEBUG
330  talloc_set_destructor(request->pair_list.state, _state_ctx_free);
331 #endif
332  }
333  }
334 
335  /*
336  * Initialise packets and additional
337  * fields if this is going to be a
338  * child request.
339  */
340  if (args->parent) {
341  if (request_child_init(request, args->parent) < 0) return -1;
342 
343  if (args->detachable) {
344  if (request_detachable_init(request, args->parent) < 0) return -1;
345  request_log_init_detachable(request, args->parent);
346  } else {
347  request_log_init_child(request, args->parent);
348  }
349  } else {
350  request_log_init_orphan(request);
351  }
352  return 0;
353 }
354 
355 /** Callback for freeing a request struct
356  *
357  * @param[in] request to free or return to the free list.
358  * @return
359  * - 0 in the request was freed.
360  * - -1 if the request was inserted into the free list.
361  */
362 static int _request_free(request_t *request)
363 {
364  fr_assert_msg(!fr_heap_entry_inserted(request->time_order_id),
365  "alloced %s:%i: %s still in the time_order heap ID %i",
366  request->alloc_file,
367  request->alloc_line,
368  request->name ? request->name : "(null)", request->time_order_id);
369  fr_assert_msg(!fr_heap_entry_inserted(request->runnable_id),
370  "alloced %s:%i: %s still in the runnable heap ID %i",
371  request->alloc_file,
372  request->alloc_line,
373  request->name ? request->name : "(null)", request->runnable_id);
374 
375  RDEBUG3("Request freed (%p)", request);
376 
377  /*
378  * Reinsert into the free list if it's not already
379  * in the free list.
380  *
381  * If it *IS* already in the free list, then free it.
382  */
383  if (unlikely(fr_dlist_entry_in_list(&request->free_entry))) {
384  fr_dlist_entry_unlink(&request->free_entry); /* Don't trust the list head to be available */
385  goto really_free;
386  }
387 
388  /*
389  * We keep a buffer of <active> + N requests per
390  * thread, to avoid spurious allocations.
391  */
393  fr_dlist_head_t *free_list;
394 
395  if (request->session_state_ctx) {
396  fr_assert(talloc_parent(request->session_state_ctx) != request); /* Should never be directly parented */
397  TALLOC_FREE(request->session_state_ctx); /* Not parented from the request */
398  }
399  free_list = request_free_list;
400 
401  /*
402  * Reinitialise the request
403  */
404  talloc_free_children(request);
405 
406  memset(request, 0, sizeof(*request));
407  request->component = "free_list";
408 #ifndef NDEBUG
409  /*
410  * So we don't trip heap asserts
411  * if the request is freed out of
412  * the free list.
413  */
414  request->time_order_id = FR_HEAP_INDEX_INVALID;
415  request->runnable_id = FR_HEAP_INDEX_INVALID;
416 #endif
417 
418  /*
419  * Reinsert into the free list
420  */
421  fr_dlist_insert_head(free_list, request);
422  request_free_list = free_list;
423 
424  return -1; /* Prevent free */
425  }
426 
427 
428  /*
429  * Ensure anything that might reference the request is
430  * freed before it is.
431  */
432  talloc_free_children(request);
433 
434 really_free:
435  /*
436  * state_ctx is parented separately.
437  */
438  if (request->session_state_ctx) TALLOC_FREE(request->session_state_ctx);
439 
440 #ifndef NDEBUG
441  request->magic = 0x01020304; /* set the request to be nonsense */
442 #endif
443 
444  return 0;
445 }
446 
447 /** Free any free requests when the thread is joined
448  *
449  */
450 static int _request_free_list_free_on_exit(void *arg)
451 {
452  fr_dlist_head_t *list = talloc_get_type_abort(arg, fr_dlist_head_t);
453  request_t *request;
454 
455  /*
456  * See the destructor for why this works
457  */
458  while ((request = fr_dlist_head(list))) if (talloc_free(request) < 0) return -1;
459  return talloc_free(list);
460 }
461 
462 static inline CC_HINT(always_inline) request_t *request_alloc_pool(TALLOC_CTX *ctx)
463 {
464  request_t *request;
465 
466  /*
467  * Only allocate requests in the NULL
468  * ctx. There's no scenario where it's
469  * appropriate to allocate them in a
470  * pool, and using a strict talloc
471  * hierarchy means that child requests
472  * cannot be returned to a free list
473  * and would have to be freed.
474  */
475  MEM(request = talloc_pooled_object(ctx, request_t,
476  1 + /* Stack pool */
477  UNLANG_STACK_MAX + /* Stack Frames */
478  2 + /* packets */
479  10, /* extra */
480  (UNLANG_FRAME_PRE_ALLOC * UNLANG_STACK_MAX) + /* Stack memory */
481  (sizeof(fr_pair_t) * 5) + /* pair lists and root*/
482  (sizeof(fr_packet_t) * 2) + /* packets */
483  128 /* extra */
484  ));
485  fr_assert(ctx != request);
486 
487  return request;
488 }
489 
490 /** Create a new request_t data structure
491  *
492  * @param[in] file where the request was allocated.
493  * @param[in] line where the request was allocated.
494  * @param[in] ctx to bind the request to.
495  * @param[in] type what type of request to alloc.
496  * @param[in] args Optional arguments.
497  * @return
498  * - A request on success.
499  * - NULL on error.
500  */
501 request_t *_request_alloc(char const *file, int line, TALLOC_CTX *ctx,
503 {
504  request_t *request;
505  fr_dlist_head_t *free_list;
506 
507  if (!args) args = &default_args;
508 
509  /*
510  * Setup the free list, or return the free
511  * list for this thread.
512  */
513  if (unlikely(!request_free_list)) {
514  MEM(free_list = talloc(NULL, fr_dlist_head_t));
515  fr_dlist_init(free_list, request_t, free_entry);
517  } else {
518  free_list = request_free_list;
519  }
520 
521  request = fr_dlist_head(free_list);
522  if (!request) {
523  /*
524  * Must be allocated with in the NULL ctx
525  * as chunk is returned to the free list.
526  */
527  request = request_alloc_pool(NULL);
528  talloc_set_destructor(request, _request_free);
529  } else {
530  /*
531  * Remove from the free list, as we're
532  * about to use it!
533  */
534  fr_dlist_remove(free_list, request);
535  }
536 
537  if (request_init(file, line, request, type, args) < 0) {
538  talloc_free(request);
539  return NULL;
540  }
541 
542  /*
543  * Initialise entry in free list
544  */
545  fr_dlist_entry_init(&request->free_entry); /* Needs to be initialised properly, else bad things happen */
546 
547  /*
548  * This is only used by src/lib/io/worker.c
549  */
550  fr_dlist_entry_init(&request->listen_entry);
551 
552  /*
553  * Bind lifetime to a parent.
554  *
555  * If the parent is freed the destructor
556  * will fire, and return the request
557  * to a "top level" free list.
558  */
559  if (ctx) talloc_link_ctx(ctx, request);
560 
561  return request;
562 }
563 
564 static int _request_local_free(request_t *request)
565 {
566  /*
567  * Ensure anything that might reference the request is
568  * freed before it is.
569  */
570  talloc_free_children(request);
571 
572  /*
573  * state_ctx is parented separately.
574  *
575  * The reason why it's OK to do this, is if the state attributes
576  * need to persist across requests, they will already have been
577  * moved to a fr_state_entry_t, with the state pointers in the
578  * request being set to NULL, before the request is freed/
579  *
580  * Note also that we do NOT call TALLOC_FREE(), which
581  * sets state_ctx=NULL. We don't control the order in
582  * which talloc frees the children. And the parents
583  * state_ctx pointer needs to stick around so that all of
584  * the children can check it.
585  *
586  * If this assertion hits, it means that someone didn't
587  * call fr_state_store_in_parent()
588  */
589  if (request->session_state_ctx) {
590  fr_assert(!request->parent || (request->session_state_ctx != request->parent->session_state_ctx));
591 
592  talloc_free(request->session_state_ctx);
593  }
594 
595 #ifndef NDEBUG
596  request->magic = 0x01020304; /* set the request to be nonsense */
597 #endif
598 
599  return 0;
600 }
601 
602 /** Allocate a request that's not in the free list
603  *
604  * This can be useful if modules need a persistent request for their own purposes
605  * which needs to be outside of the normal free list, so that it can be freed
606  * when the module requires, not when the thread destructor runs.
607  */
608 request_t *_request_local_alloc(char const *file, int line, TALLOC_CTX *ctx,
610 {
611  request_t *request;
612 
613  if (!args) args = &default_args;
614 
615  request = request_alloc_pool(ctx);
616  if (request_init(file, line, request, type, args) < 0) return NULL;
617 
618  talloc_set_destructor(request, _request_local_free);
619 
620  return request;
621 }
622 
623 /** Replace the session_state_ctx with a new one.
624  *
625  * NOTHING should rewrite request->session_state_ctx.
626  *
627  * It's now a pair, and is stored in request->pair_root.
628  * So it's wrong for anyone other than this function to play games with it.
629  *
630  * @param[in] request to replace the state of.
631  * @param[in] new_state state to assign to the request.
632  * May be NULL in which case a new_state state will
633  * be alloced and assigned.
634  *
635  * @return the fr_pair_t containing the old state list.
636  */
638 {
639  fr_pair_t *old = request->session_state_ctx;
640 
641  fr_assert(request->session_state_ctx != NULL);
642  fr_assert(request->session_state_ctx != new_state);
643 
644  fr_pair_remove(&request->pair_root->children, old);
645 
646  /*
647  * Save (or delete) the existing state, and re-initialize
648  * it with a brand new one.
649  */
650  if (!new_state) MEM(new_state = fr_pair_afrom_da(NULL, request_attr_state));
651 
652  request->session_state_ctx = new_state;
653 
654  fr_pair_append(&request->pair_root->children, new_state);
655 
656  return old;
657 }
658 
659 /** Unlink a subrequest from its parent
660  *
661  * @note This should be used for requests in preparation for freeing them.
662  *
663  * @param[in] child request to unlink.
664  * @return
665  * - 0 on success.
666  * - -1 on failure.
667  */
669 {
670  request_t *request = child->parent;
671 
672  /*
673  * Already detached or not detachable
674  */
675  if (request_is_detached(child)) return 0;
676 
677  if (!request_is_detachable(child)) {
678  fr_strerror_const("Request is not detachable");
679  return -1;
680  }
681 
682  /*
683  * Unlink the child from the parent.
684  */
685  request_data_get(request, child, 0);
686 
687  child->parent = NULL;
688 
689  /*
690  * Request is now detached
691  */
692  child->type = REQUEST_TYPE_DETACHED;
693 
694  /*
695  * ...and is no longer detachable.
696  */
697  child->flags.detachable = 0;
698 
699  return 0;
700 }
701 
702 static int _request_global_free(UNUSED void *uctx)
703 {
705  return 0;
706 }
707 
708 static int _request_global_init(UNUSED void *uctx)
709 {
710  if (fr_dict_autoload(request_dict) < 0) {
711  PERROR("%s", __FUNCTION__);
712  return -1;
713  }
715  PERROR("%s", __FUNCTION__);
717  return -1;
718  }
719  return 0;
720 }
721 
723 {
724  int ret;
725  fr_atexit_global_once_ret(&ret, _request_global_init, _request_global_free, NULL);
726  return ret;
727 }
728 
729 #ifdef WITH_VERIFY_PTR
730 /*
731  * Verify a packet.
732  */
733 static void packet_verify(char const *file, int line,
734  request_t const *request, fr_packet_t const *packet, fr_pair_list_t *list, char const *type)
735 {
736  TALLOC_CTX *parent;
737 
738  fr_fatal_assert_msg(packet, "CONSISTENCY CHECK FAILED %s[%i]: fr_packet_t %s pointer was NULL",
739  file, line, type);
740 
741  parent = talloc_parent(packet);
742  if (parent != request) {
743  fr_log_talloc_report(packet);
745 
746 
747  fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%i]: Expected fr_packet_t %s to be parented "
748  "by %p (%s), but parented by %p (%s)",
749  file, line, type, request, talloc_get_name(request),
750  parent, parent ? talloc_get_name(parent) : "NULL");
751  }
752 
753  /*
754  * Enforce nesting at the top level. This catches minor programming bugs in the server core.
755  *
756  * If we care more, we could do these checks recursively. But the tmpl_tokenize code already
757  * enforces parent / child namespaces. So the end user shouldn't be able to break the parenting.
758  *
759  * This code really only checks for programming bugs where the C code creates a pair, and then
760  * adds it to the wrong list. This was happening during the transition from flat to nested, as
761  * the code was in the middle of being fixed. It should only happen now if the programmer
762  * forgets, and uses the wrong APIs.
763  */
764  fr_pair_list_foreach(list, vp) {
765  if (vp->da->flags.is_raw) continue;
766 
767  if (vp->da->flags.internal) continue;
768 
769  if (vp->da->depth > 1) {
770  fr_fatal_assert_fail("CONSISTENCY CHECK FAILED %s[%i]: Expected fr_pair_t %s to be parented "
771  "by (%s), but it is instead at the top-level %s list",
772  file, line, vp->da->name, vp->da->parent->name, type);
773  }
774  }
775 
776  PACKET_VERIFY(packet);
777 }
778 
779 /*
780  * Catch horrible talloc errors.
781  */
782 void request_verify(char const *file, int line, request_t const *request)
783 {
784  request_data_t *rd = NULL;
785 
786  fr_fatal_assert_msg(request, "CONSISTENCY CHECK FAILED %s[%i]: request_t pointer was NULL", file, line);
787 
788  (void) talloc_get_type_abort_const(request, request_t);
789 
790  fr_assert(request->magic == REQUEST_MAGIC);
791 
792  fr_fatal_assert_msg(talloc_get_size(request) == sizeof(request_t),
793  "CONSISTENCY CHECK FAILED %s[%i]: expected request_t size of %zu bytes, got %zu bytes",
794  file, line, sizeof(request_t), talloc_get_size(request));
795 
796  (void)talloc_get_type_abort(request->request_ctx, fr_pair_t);
797  fr_pair_list_verify(file, line, request->request_ctx, &request->request_pairs);
798  (void)talloc_get_type_abort(request->reply_ctx, fr_pair_t);
799  fr_pair_list_verify(file, line, request->reply_ctx, &request->reply_pairs);
800  (void)talloc_get_type_abort(request->control_ctx, fr_pair_t);
801  fr_pair_list_verify(file, line, request->control_ctx, &request->control_pairs);
802  (void)talloc_get_type_abort(request->session_state_ctx, fr_pair_t);
803 
804 #ifndef NDEBUG
805  {
806  TALLOC_CTX *parent = talloc_parent(request->session_state_ctx);
807 
808  fr_assert_msg((parent == NULL) || (parent == talloc_null_ctx()),
809  "session_state_ctx must not be parented by another chunk, but is parented by %s",
810  talloc_get_name(talloc_parent(request->session_state_ctx)));
811  }
812 #endif
813 
814  fr_pair_list_verify(file, line, request->session_state_ctx, &request->session_state_pairs);
815  fr_pair_list_verify(file, line, request->local_ctx, &request->local_pairs);
816 
817  if (request->packet) {
818  packet_verify(file, line, request, request->packet, &request->request_pairs, "request");
819  }
820  if (request->reply) {
821  packet_verify(file, line, request, request->reply, &request->reply_pairs, "reply");
822  }
823 
824  if (request->async) {
825  (void) talloc_get_type_abort(request->async, fr_async_t);
826  fr_assert(talloc_parent(request->async) == request);
827  }
828 
829  while ((rd = fr_dlist_next(&request->data, rd))) {
830  (void) talloc_get_type_abort(rd, request_data_t);
831 
832  if (request_data_persistable(rd)) {
833  fr_assert(request->session_state_ctx);
834  fr_assert(talloc_parent(rd) == request->session_state_ctx);
835  } else {
836  fr_assert(talloc_parent(rd) == request);
837  }
838  }
839 }
840 #endif
int const char * file
Definition: acutest.h:702
va_list args
Definition: acutest.h:770
int const char int line
Definition: acutest.h:702
#define fr_atexit_thread_local(_name, _free, _uctx)
Definition: atexit.h:221
void request_verify(UNUSED char const *file, UNUSED int line, UNUSED request_t *request)
#define RCSID(id)
Definition: build.h:446
#define unlikely(_x)
Definition: build.h:379
#define UNUSED
Definition: build.h:313
static fr_control_t * control
Definition: control_test.c:50
int fr_log_talloc_report(TALLOC_CTX const *ctx)
Generate a talloc memory report for a context and print to stderr/stdout.
Definition: debug.c:1147
#define fr_fatal_assert_fail(_msg,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition: debug.h:191
#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
#define fr_assert_fail(_msg,...)
Calls panic_action ifndef NDEBUG, else logs error.
Definition: debug.h:216
#define fr_cond_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition: debug.h:156
#define fr_fatal_assert_msg(_x, _fmt,...)
Calls panic_action ifndef NDEBUG, else logs error and causes the server to exit immediately with code...
Definition: debug.h:184
#define fr_dict_autofree(_to_free)
Definition: dict.h:674
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition: dict.h:250
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition: dict.h:263
int fr_dict_attr_autoload(fr_dict_attr_autoload_t const *to_load)
Process a dict_attr_autoload element to load/verify a dictionary attribute.
Definition: dict_util.c:3647
#define fr_dict_autoload(_to_load)
Definition: dict.h:671
Specifies an attribute which must be present for the module to function.
Definition: dict.h:249
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition: dict.h:262
#define fr_dlist_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition: dlist.h:260
static void * fr_dlist_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Definition: dlist.h:555
static bool fr_dlist_entry_in_list(fr_dlist_t const *entry)
Check if a list entry is part of a list.
Definition: dlist.h:163
static void fr_dlist_entry_unlink(fr_dlist_t *entry)
Remove an item from the dlist when we don't have access to the head.
Definition: dlist.h:146
static unsigned int fr_dlist_num_elements(fr_dlist_head_t const *head)
Return the number of elements in the dlist.
Definition: dlist.h:939
static void * fr_dlist_head(fr_dlist_head_t const *list_head)
Return the HEAD item of a list or NULL if the list is empty.
Definition: dlist.h:486
static void * fr_dlist_remove(fr_dlist_head_t *list_head, void *ptr)
Remove an item from the list.
Definition: dlist.h:638
static int fr_dlist_insert_head(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the head of a list.
Definition: dlist.h:338
static void fr_dlist_entry_init(fr_dlist_t *entry)
Initialise a linked list without metadata.
Definition: dlist.h:138
Head of a doubly linked list.
Definition: dlist.h:51
static bool fr_heap_entry_inserted(fr_heap_index_t heap_idx)
Check if an entry is inserted into a heap.
Definition: heap.h:124
#define FR_HEAP_INDEX_INVALID
Definition: heap.h:83
void * unlang_interpret_stack_alloc(TALLOC_CTX *ctx)
Allocate a new unlang stack.
Definition: interpret.c:1046
#define UNLANG_STACK_MAX
The maximum depth of the stack.
Definition: interpret.h:38
#define UNLANG_FRAME_PRE_ALLOC
How much memory we pre-alloc for each frame.
Definition: interpret.h:39
Minimal data structure to use the new code.
Definition: listen.h:58
void vlog_request(fr_log_type_t type, fr_log_lvl_t lvl, request_t *request, char const *file, int line, char const *fmt, va_list ap, void *uctx)
Send a log message to its destination, possibly including fields from the request.
Definition: log.c:295
#define PERROR(_fmt,...)
Definition: log.h:228
fr_log_lvl_t lvl
Log messages with lvl >= to this should be logged.
Definition: log.h:73
#define RDEBUG3(fmt,...)
Definition: log.h:343
#define DEBUG4(_fmt,...)
Definition: log.h:267
void * uctx
Context to pass to the logging function.
Definition: log.h:72
log_dst_t * next
Next logging destination.
Definition: log.h:74
log_func_t func
Function to call to log to this destination.
Definition: log.h:71
Definition: log.h:70
talloc_free(reap)
int fr_debug_lvl
Definition: log.c:42
fr_log_t default_log
Definition: log.c:290
fr_log_lvl_t
Definition: log.h:67
@ L_DBG_LVL_DISABLE
Don't print messages.
Definition: log.h:68
@ L_DBG_LVL_OFF
No debug messages.
Definition: log.h:69
fr_packet_t * fr_packet_alloc(TALLOC_CTX *ctx, bool new_vector)
Allocate a new fr_packet_t.
Definition: packet.c:38
@ FR_TYPE_GROUP
A grouping of other attributes.
Definition: merged_model.c:124
fr_pair_t * fr_pair_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
Dynamically allocate a new attribute and assign a fr_dict_attr_t.
Definition: pair.c:278
int fr_pair_append(fr_pair_list_t *list, fr_pair_t *to_add)
Add a VP to the end of the list.
Definition: pair.c:1340
fr_pair_t * fr_pair_root_afrom_da(TALLOC_CTX *ctx, fr_dict_attr_t const *da)
A special allocation function which disables child autofree.
Definition: pair.c:238
struct request_s request_t
Definition: radclient-ng.c:53
static int request_child_init(request_t *child, request_t *parent)
Definition: request.c:198
fr_dict_attr_t const * request_attr_request
Definition: request.c:45
static int _request_global_init(UNUSED void *uctx)
Definition: request.c:708
static void request_log_init_orphan(request_t *request)
Definition: request.c:77
static request_init_args_t default_args
Definition: request.c:34
static int _request_local_free(request_t *request)
Definition: request.c:564
#define list_init(_ctx, _list)
static request_t * request_alloc_pool(TALLOC_CTX *ctx)
Definition: request.c:462
fr_dict_autoload_t request_dict[]
Definition: request.c:39
static fr_dict_t const * dict_freeradius
Definition: request.c:36
fr_dict_attr_t const * request_attr_control
Definition: request.c:47
static int request_detachable_init(request_t *child, request_t *parent)
Definition: request.c:185
fr_dict_attr_t const * request_attr_local
Definition: request.c:49
fr_dict_attr_t const * request_attr_state
Definition: request.c:48
fr_dict_attr_t const * request_attr_reply
Definition: request.c:46
fr_dict_attr_autoload_t request_dict_attr[]
Definition: request.c:52
static int _request_free_list_free_on_exit(void *arg)
Free any free requests when the thread is joined.
Definition: request.c:450
static _Thread_local fr_dlist_head_t * request_free_list
The thread local free list.
Definition: request.c:66
void request_log_prepend(request_t *request, fr_log_t *log_dst, fr_log_lvl_t lvl)
Prepend another logging destination to the list.
Definition: request.c:100
int request_global_init(void)
Definition: request.c:722
request_t * _request_local_alloc(char const *file, int line, TALLOC_CTX *ctx, request_type_t type, request_init_args_t const *args)
Allocate a request that's not in the free list.
Definition: request.c:608
int request_detach(request_t *child)
Unlink a subrequest from its parent.
Definition: request.c:668
static int _state_ctx_free(fr_pair_t *state)
Definition: request.c:69
request_t * _request_alloc(char const *file, int line, TALLOC_CTX *ctx, request_type_t type, request_init_args_t const *args)
Create a new request_t data structure.
Definition: request.c:501
fr_dict_attr_t const * request_attr_root
Definition: request.c:44
static int request_init(char const *file, int line, request_t *request, request_type_t type, request_init_args_t const *args)
Setup logging and other fields for a request.
Definition: request.c:242
static int _request_free(request_t *request)
Callback for freeing a request struct.
Definition: request.c:362
static void request_log_init_child(request_t *child, request_t const *parent)
Definition: request.c:162
static void request_log_init_detachable(request_t *child, request_t const *parent)
Definition: request.c:173
fr_pair_t * request_state_replace(request_t *request, fr_pair_t *new_state)
Replace the session_state_ctx with a new one.
Definition: request.c:637
static int _request_global_free(UNUSED void *uctx)
Definition: request.c:702
#define request_is_detached(_x)
Definition: request.h:160
#define REQUEST_MAGIC
Definition: request.h:57
request_type_t
Definition: request.h:151
@ REQUEST_TYPE_EXTERNAL
A request received on the wire.
Definition: request.h:152
@ REQUEST_TYPE_INTERNAL
A request generated internally.
Definition: request.h:153
@ REQUEST_TYPE_DETACHED
A request that was generated internally, but is now detached (not associated with a parent request....
Definition: request.h:154
#define request_is_detachable(_x)
Definition: request.h:161
@ REQUEST_ACTIVE
Request is active (running or runnable)
Definition: request.h:61
Optional arguments for initialising requests.
Definition: request.h:254
void * request_data_get(request_t *request, void const *unique_ptr, int unique_int)
Get opaque data from a request.
Definition: request_data.c:292
void request_data_list_init(fr_dlist_head_t *data)
Definition: request_data.c:92
Per-request opaque data, added by modules.
Definition: request_data.c:33
#define request_data_talloc_add(_request, _unique_ptr, _unique_int, _type, _opaque, _free_on_replace, _free_on_parent, _persist)
Add opaque data to a request_t.
Definition: request_data.h:86
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
fr_aka_sim_id_type_t type
fr_pair_t * vp
Definition: log.h:96
Stores an attribute, a value and various bits of other data.
Definition: pair.h:68
fr_dict_attr_t const *_CONST da
Dictionary attribute defines the attribute number, vendor and type of the pair.
Definition: pair.h:69
void * talloc_null_ctx(void)
Retrieve the current talloc NULL ctx.
Definition: talloc.c:53
int talloc_link_ctx(TALLOC_CTX *parent, TALLOC_CTX *child)
Link two different parent and child contexts, so the child is freed before the parent.
Definition: talloc.c:171
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
Definition: talloc.c:492
#define talloc_get_type_abort_const
Definition: talloc.h:271
#define talloc_pooled_object(_ctx, _type, _num_subobjects, _total_subobjects_size)
Definition: talloc.h:169
#define PACKET_VERIFY(_x)
Definition: packet.h:42
fr_pair_t * fr_pair_remove(fr_pair_list_t *list, fr_pair_t *vp)
Remove fr_pair_t from a list without freeing.
Definition: pair_inline.c:94
#define fr_pair_list_foreach(_list_head, _iter)
Iterate over the contents of a fr_pair_list_t.
Definition: pair.h:260
static fr_slen_t parent
Definition: pair.h:844
#define fr_strerror_const(_msg)
Definition: strerror.h:223