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