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