The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
rlm_stats.c
Go to the documentation of this file.
1 /*
2  * This program is 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 (at
5  * 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: 5d9f42778cf8c7611622a9f3055fd4ed392a568d $
19  * @file rlm_stats.c
20  * @brief Keep RADIUS statistics. Eventually, also non-RADIUS statistics
21  *
22  * @copyright 2017 Network RADIUS SAS (license@networkradius.com)
23  */
24 RCSID("$Id: 5d9f42778cf8c7611622a9f3055fd4ed392a568d $")
25 
26 #include <freeradius-devel/server/base.h>
27 #include <freeradius-devel/server/module_rlm.h>
28 #include <freeradius-devel/io/listen.h>
29 #include <freeradius-devel/util/dlist.h>
30 #include <freeradius-devel/util/debug.h>
31 #include <freeradius-devel/radius/radius.h>
32 
33 #include <freeradius-devel/protocol/radius/freeradius.h>
34 
35 /*
36  * @todo - also get the statistics from the network side for
37  * that, though, we need a way to find other network
38  * sockets (i.e. not this one), and then query them for
39  * statistics.
40  */
41 
42 #include <pthread.h>
43 
44 /*
45  * @todo - MULTI_PROTOCOL - make this protocol agnostic.
46  * Perhaps keep stats in a hash table by (request->dict, request->code) ?
47  */
48 
49 typedef struct {
51  fr_dict_attr_t const *type_da; //!< FreeRADIUS-Stats4-Type
52  fr_dict_attr_t const *ipv4_da; //!< FreeRADIUS-Stats4-IPv4-Address
53  fr_dict_attr_t const *ipv6_da; //!< FreeRADIUS-Stats4-IPv6-Address
54  fr_dlist_head_t list; //!< for threads to know about each other
55 
57 } rlm_stats_t;
58 
59 typedef struct {
62  fr_ipaddr_t ipaddr; //!< IP address of this thing
63  fr_time_t created; //!< when it was created
64  fr_time_t last_packet; //!< when we last saw a packet
65  uint64_t stats[FR_RADIUS_CODE_MAX]; //!< actual statistic
67 
68 typedef struct {
70 
72  fr_dlist_t entry; //!< for threads to know about each other
73 
74  fr_time_t last_manage; //!< when we deleted old things
75 
76  fr_rb_tree_t *src; //!< stats by source
77  fr_rb_tree_t *dst; //!< stats by destination
78 
80 
83 
84 static const conf_parser_t module_config[] = {
86 };
87 
88 static fr_dict_t const *dict_radius;
89 
92  { .out = &dict_radius, .proto = "radius" },
93  { NULL }
94 };
95 
99 
102  { .out = &attr_freeradius_stats4_ipv4_address, .name = "Vendor-Specific.FreeRADIUS.Stats4.Stats4-IPv4-Address", .type = FR_TYPE_IPV4_ADDR, .dict = &dict_radius },
103  { .out = &attr_freeradius_stats4_ipv6_address, .name = "Vendor-Specific.FreeRADIUS.Stats4.Stats4-IPv6-Address", .type = FR_TYPE_IPV6_ADDR, .dict = &dict_radius },
104  { .out = &attr_freeradius_stats4_type, .name = "Vendor-Specific.FreeRADIUS.Stats4.Stats4-Type", .type = FR_TYPE_UINT32, .dict = &dict_radius },
105  { NULL }
106 };
107 
108 static void coalesce(uint64_t final_stats[FR_RADIUS_CODE_MAX], rlm_stats_thread_t *t,
109  size_t tree_offset, rlm_stats_data_t *mydata)
110 {
112  rlm_stats_thread_t *other;
113  fr_rb_tree_t **tree;
114  uint64_t local_stats[FR_RADIUS_CODE_MAX];
115 
116  tree = (fr_rb_tree_t **) (((uint8_t *) t) + tree_offset);
117 
118  /*
119  * Bootstrap with my statistics, where we don't need a
120  * lock.
121  */
122  stats = fr_rb_find(*tree, mydata);
123  if (!stats) {
124  memset(final_stats, 0, sizeof(uint64_t) * FR_RADIUS_CODE_MAX);
125  } else {
126  memcpy(final_stats, stats->stats, sizeof(stats->stats));
127  }
128 
129  /*
130  * Loop over all of the other thread instances, locking
131  * them, and adding their statistics in.
132  */
133  pthread_mutex_lock(&t->inst->mutex);
134  for (other = fr_dlist_head(&t->inst->list);
135  other != NULL;
136  other = fr_dlist_next(&t->inst->list, other)) {
137  int i;
138 
139  if (other == t) continue;
140 
141  tree = (fr_rb_tree_t **) (((uint8_t *) other) + tree_offset);
142  pthread_mutex_lock(&other->mutex);
143  stats = fr_rb_find(*tree, mydata);
144 
145  if (!stats) {
146  pthread_mutex_unlock(&other->mutex);
147  continue;
148  }
149  memcpy(&local_stats, stats->stats, sizeof(stats->stats));
150 
151  for (i = 0; i < FR_RADIUS_CODE_MAX; i++) {
152  final_stats[i] += local_stats[i];
153  }
154 
155  pthread_mutex_unlock(&other->mutex);
156  }
157  pthread_mutex_unlock(&t->inst->mutex);
158 }
159 
160 
161 /*
162  * Do the statistics
163  */
164 static unlang_action_t CC_HINT(nonnull) mod_stats(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
165 {
166  rlm_stats_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_stats_t);
167  rlm_stats_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_stats_thread_t);
168  int i;
169  uint32_t stats_type;
170 
171 
172  fr_pair_t *vp;
173  rlm_stats_data_t mydata;
174  char buffer[64];
175  uint64_t local_stats[NUM_ELEMENTS(inst->stats)];
176 
177  /*
178  * Increment counters only in "send foo" sections.
179  *
180  * i.e. only when we have a reply to send.
181  *
182  * FIXME - Nothing sets request_state anymore
183  */
184 #if 0
185  if (request->request_state == REQUEST_SEND) {
186  int src_code, dst_code;
188 
189  src_code = request->packet->code;
190  if (src_code >= FR_RADIUS_CODE_MAX) src_code = 0;
191 
192  dst_code = request->reply->code;
193  if (dst_code >= FR_RADIUS_CODE_MAX) dst_code = 0;
194 
195  pthread_mutex_lock(&t->mutex);
196  t->stats[src_code]++;
197  t->stats[dst_code]++;
198 
199  /*
200  * Update source statistics
201  */
202  mydata.ipaddr = request->packet->socket.inet.src_ipaddr;
203  stats = fr_rb_find(t->src, &mydata);
204  if (!stats) {
205  MEM(stats = talloc_zero(t, rlm_stats_data_t));
206 
207  stats->ipaddr = request->packet->socket.inet.src_ipaddr;
208  stats->created = request->async->recv_time;
209 
210  (void) fr_rb_insert(t->src, stats);
211  }
212 
213  stats->last_packet = request->async->recv_time;
214  stats->stats[src_code]++;
215  stats->stats[dst_code]++;
216 
217  /*
218  * Update destination statistics
219  */
220  mydata.ipaddr = request->packet->socket.inet.dst_ipaddr;
221  stats = fr_rb_find(t->dst, &mydata);
222  if (!stats) {
223  MEM(stats = talloc_zero(t, rlm_stats_data_t));
224 
225  stats->ipaddr = request->packet->socket.inet.dst_ipaddr;
226  stats->created = request->async->recv_time;
227 
228  (void) fr_rb_insert(t->dst, stats);
229  }
230 
231  stats->last_packet = request->async->recv_time;
232  stats->stats[src_code]++;
233  stats->stats[dst_code]++;
234  pthread_mutex_unlock(&t->mutex);
235 
236  /*
237  * @todo - periodically clean up old entries.
238  */
239 
240  if ((t->last_global_update + NSEC) > request->async->recv_time) {
242  }
243 
244  t->last_global_update = request->async->recv_time;
245 
246  pthread_mutex_lock(&inst->mutex);
247  for (i = 0; i < FR_RADIUS_CODE_MAX; i++) {
248  inst->stats[i] += t->stats[i];
249  t->stats[i] = 0;
250  }
251  pthread_mutex_unlock(&inst->mutex);
252 
254  }
255 #endif
256 
257  /*
258  * Ignore "authenticate" and anything other than Status-Server
259  */
260  if ((request->packet->code != FR_RADIUS_CODE_STATUS_SERVER)) {
262  }
263 
264  vp = fr_pair_find_by_da_nested(&request->request_pairs, NULL, attr_freeradius_stats4_type);
265  if (!vp) {
266  stats_type = FR_STATS4_TYPE_VALUE_GLOBAL;
267  } else {
268  stats_type = vp->vp_uint32;
269  }
270 
271  /*
272  * Create attributes based on the statistics.
273  */
275  vp->vp_uint32 = stats_type;
276 
277  switch (stats_type) {
278  case FR_STATS4_TYPE_VALUE_GLOBAL: /* global */
279  /*
280  * Merge our stats with the global stats, and then copy
281  * the global stats to a thread-local variable.
282  *
283  * The copy helps minimize mutex contention.
284  */
285  pthread_mutex_lock(&inst->mutex);
286  for (i = 0; i < FR_RADIUS_CODE_MAX; i++) {
287  inst->stats[i] += t->stats[i];
288  t->stats[i] = 0;
289  }
290  memcpy(&local_stats, inst->stats, sizeof(inst->stats));
291  pthread_mutex_unlock(&inst->mutex);
292  vp = NULL;
293  break;
294 
295  case FR_STATS4_TYPE_VALUE_CLIENT: /* src */
296  vp = fr_pair_find_by_da_nested(&request->request_pairs, NULL, attr_freeradius_stats4_ipv4_address);
297  if (!vp) vp = fr_pair_find_by_da_nested(&request->request_pairs, NULL, attr_freeradius_stats4_ipv6_address);
298  if (!vp) RETURN_MODULE_NOOP;
299 
300  mydata.ipaddr = vp->vp_ip;
301  coalesce(local_stats, t, offsetof(rlm_stats_thread_t, src), &mydata);
302  break;
303 
304  case FR_STATS4_TYPE_VALUE_LISTENER: /* dst */
305  vp = fr_pair_find_by_da_nested(&request->request_pairs, NULL, attr_freeradius_stats4_ipv4_address);
306  if (!vp) vp = fr_pair_find_by_da_nested(&request->request_pairs, NULL, attr_freeradius_stats4_ipv6_address);
307  if (!vp) RETURN_MODULE_NOOP;
308 
309  mydata.ipaddr = vp->vp_ip;
310  coalesce(local_stats, t, offsetof(rlm_stats_thread_t, dst), &mydata);
311  break;
312 
313  default:
314  REDEBUG("Invalid value '%d' for FreeRADIUS-Stats4-type", stats_type);
316  }
317 
318  if (vp ) {
319  vp = fr_pair_copy(request->reply_ctx, vp);
320  if (vp) {
321  fr_pair_append(&request->reply_pairs, vp);
322  }
323  }
324 
325  /*
326  * @todo - do this only for RADIUS
327  * key off of packet ID, and Stats4-Packet-Counters TLV.
328  */
329  strcpy(buffer, "FreeRADIUS-Stats4-");
330 
331  for (i = 0; i < FR_RADIUS_CODE_MAX; i++) {
332  fr_dict_attr_t const *da;
333 
334  if (!local_stats[i]) continue;
335 
336  strlcpy(buffer + 18, fr_radius_packet_name[i], sizeof(buffer) - 18);
338  if (!da) continue;
339 
340  MEM(vp = fr_pair_afrom_da(request->reply_ctx, da));
341  vp->vp_uint64 = local_stats[i];
342 
343  fr_pair_append(&request->reply_pairs, vp);
344  }
345 
347 }
348 
349 
350 static int8_t data_cmp(const void *one, const void *two)
351 {
352  rlm_stats_data_t const *a = one;
353  rlm_stats_data_t const *b = two;
354 
355  return fr_ipaddr_cmp(&a->ipaddr, &b->ipaddr);
356 }
357 
358 /** Instantiate thread data for the submodule.
359  *
360  */
362 {
363  rlm_stats_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_stats_t);
364  rlm_stats_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_stats_thread_t);
365 
366  (void) talloc_set_type(t, rlm_stats_thread_t);
367 
368  t->inst = inst;
369 
370  t->src = fr_rb_inline_talloc_alloc(t, rlm_stats_data_t, src_node, data_cmp, NULL);
371  if (unlikely(!t->src)) return -1;
372 
373  t->dst = fr_rb_inline_talloc_alloc(t, rlm_stats_data_t, dst_node, data_cmp, NULL);
374  if (unlikely(!t->dst)) {
375  TALLOC_FREE(t->src);
376  return -1;
377  }
378  pthread_mutex_init(&t->mutex, 0);
379 
380  pthread_mutex_lock(&inst->mutex);
381  fr_dlist_insert_head(&inst->list, t);
382  pthread_mutex_unlock(&inst->mutex);
383 
384  return 0;
385 }
386 
387 
388 /** Destroy thread data for the submodule.
389  *
390  */
392 {
393  rlm_stats_thread_t *t = talloc_get_type_abort(mctx->thread, rlm_stats_thread_t);
394  rlm_stats_t *inst = t->inst;
395  int i;
396 
397  pthread_mutex_lock(&inst->mutex);
398  for (i = 0; i < FR_RADIUS_CODE_MAX; i++) {
399  inst->stats[i] += t->stats[i];
400  }
401  fr_dlist_remove(&inst->list, t);
402  pthread_mutex_unlock(&inst->mutex);
403  pthread_mutex_destroy(&t->mutex);
404 
405  return 0;
406 }
407 
408 static int mod_instantiate(module_inst_ctx_t const *mctx)
409 {
410  rlm_stats_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_stats_t);
411 
412  pthread_mutex_init(&inst->mutex, NULL);
413  fr_dlist_init(&inst->list, rlm_stats_thread_t, entry);
414 
415  return 0;
416 }
417 
418 /*
419  * Only free memory we allocated. The strings allocated via
420  * cf_section_parse() do not need to be freed.
421  */
422 static int mod_detach(module_detach_ctx_t const *mctx)
423 {
424  rlm_stats_t *inst = talloc_get_type_abort(mctx->inst->data, rlm_stats_t);
425 
426  pthread_mutex_destroy(&inst->mutex);
427 
428  /* free things here */
429  return 0;
430 }
431 
432 /*
433  * The module name should be the only globally exported symbol.
434  * That is, everything else should be 'static'.
435  *
436  * If the module needs to temporarily modify it's instantiation
437  * data, the type should be changed to MODULE_TYPE_THREAD_UNSAFE.
438  * The server will then take care of ensuring that the module
439  * is single-threaded.
440  */
441 extern module_rlm_t rlm_stats;
442 
444  .common = {
445  .magic = MODULE_MAGIC_INIT,
446  .name = "stats",
447  .inst_size = sizeof(rlm_stats_t),
448  .thread_inst_size = sizeof(rlm_stats_thread_t),
449  .config = module_config,
450  .instantiate = mod_instantiate,
451  .detach = mod_detach,
452  .thread_instantiate = mod_thread_instantiate,
453  .thread_detach = mod_thread_detach
454  },
455  .method_names = (module_method_name_t[]){
456  { .name1 = CF_IDENT_ANY, .name2 = CF_IDENT_ANY, .method = mod_stats },
458  }
459 };
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition: action.h:35
static int const char char buffer[256]
Definition: acutest.h:574
strcpy(log_entry->msg, buffer)
#define RCSID(id)
Definition: build.h:444
#define unlikely(_x)
Definition: build.h:378
#define NUM_ELEMENTS(_t)
Definition: build.h:335
#define CONF_PARSER_TERMINATOR
Definition: cf_parse.h:626
Defines a CONF_PAIR to C data type mapping.
Definition: cf_parse.h:563
#define CF_IDENT_ANY
Definition: cf_util.h:78
@ FR_RADIUS_CODE_MAX
Maximum possible protocol code.
Definition: defs.h:53
@ FR_RADIUS_CODE_STATUS_SERVER
RFC2865/RFC5997 - Status Server (request)
Definition: defs.h:44
fr_dict_attr_t const * fr_dict_attr_by_name(fr_dict_attr_err_t *err, fr_dict_attr_t const *parent, char const *attr))
Locate a fr_dict_attr_t by its name.
Definition: dict_util.c:2860
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
fr_dict_attr_t const * fr_dict_root(fr_dict_t const *dict)
Return the root attribute of a dictionary.
Definition: dict_util.c:1997
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
void *_CONST data
Module instance's parsed configuration.
Definition: dl_module.h:165
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:65
dl_module_inst_t const * inst
Definition: dl_module.h:87
#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 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
Head of a doubly linked list.
Definition: dlist.h:51
Entry in a doubly linked list.
Definition: dlist.h:41
int8_t fr_ipaddr_cmp(fr_ipaddr_t const *a, fr_ipaddr_t const *b)
Compare two ip addresses.
Definition: inet.c:1332
IPv4/6 prefix.
Definition: merged_model.c:272
@ FR_TYPE_IPV4_ADDR
32 Bit IPv4 Address.
Definition: merged_model.c:86
@ FR_TYPE_UINT32
32 Bit unsigned integer.
Definition: merged_model.c:99
@ FR_TYPE_IPV6_ADDR
128 Bit IPv6 Address.
Definition: merged_model.c:88
unsigned int uint32_t
Definition: merged_model.c:33
unsigned char uint8_t
Definition: merged_model.c:30
void * thread
Thread specific instance data.
Definition: module_ctx.h:43
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
Definition: module_ctx.h:52
void * thread
Thread instance data.
Definition: module_ctx.h:62
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
Definition: module_ctx.h:42
dl_module_inst_t const * inst
Dynamic loader API handle for the module.
Definition: module_ctx.h:59
Temporary structure to hold arguments for module calls.
Definition: module_ctx.h:41
Temporary structure to hold arguments for instantiation calls.
Definition: module_ctx.h:51
Temporary structure to hold arguments for thread_instantiation calls.
Definition: module_ctx.h:58
Specifies a module method identifier.
Definition: module_method.c:36
module_t common
Common fields presented by all modules.
Definition: module_rlm.h:37
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_copy(TALLOC_CTX *ctx, fr_pair_t const *vp)
Copy a single valuepair.
Definition: pair.c:484
fr_pair_t * fr_pair_find_by_da_nested(fr_pair_list_t const *list, fr_pair_t const *prev, fr_dict_attr_t const *da)
Find a pair with a matching fr_dict_attr_t, by walking the nested fr_dict_attr_t tree.
Definition: pair.c:765
char const * fr_radius_packet_name[FR_RADIUS_CODE_MAX]
Definition: base.c:94
static rc_stats_t stats
Definition: radclient-ng.c:72
#define REDEBUG(fmt,...)
Definition: radclient.h:52
bool fr_rb_insert(fr_rb_tree_t *tree, void const *data)
Insert data into a tree.
Definition: rb.c:624
void * fr_rb_find(fr_rb_tree_t const *tree, void const *data)
Find an element in the tree, returning the data, not the node.
Definition: rb.c:576
#define fr_rb_inline_talloc_alloc(_ctx, _type, _field, _data_cmp, _data_free)
Allocs a red black that verifies elements are of a specific talloc type.
Definition: rb.h:246
The main red black tree structure.
Definition: rb.h:73
#define RETURN_MODULE_NOOP
Definition: rcode.h:62
#define RETURN_MODULE_OK
Definition: rcode.h:57
#define RETURN_MODULE_UPDATED
Definition: rcode.h:63
rlm_rcode_t
Return codes indicating the result of the module call.
Definition: rcode.h:40
@ REQUEST_SEND
Definition: request.h:71
fr_rb_tree_t * dst
stats by destination
Definition: rlm_stats.c:77
static int mod_detach(module_detach_ctx_t const *mctx)
Definition: rlm_stats.c:422
fr_dict_attr_autoload_t rlm_stats_dict_attr[]
Definition: rlm_stats.c:101
fr_dict_attr_t const * ipv4_da
FreeRADIUS-Stats4-IPv4-Address.
Definition: rlm_stats.c:52
rlm_stats_t * inst
Definition: rlm_stats.c:69
fr_time_t created
when it was created
Definition: rlm_stats.c:63
static int8_t data_cmp(const void *one, const void *two)
Definition: rlm_stats.c:350
fr_dict_attr_t const * type_da
FreeRADIUS-Stats4-Type.
Definition: rlm_stats.c:51
fr_dict_autoload_t rlm_stats_dict[]
Definition: rlm_stats.c:91
fr_time_t last_packet
when we last saw a packet
Definition: rlm_stats.c:64
uint64_t stats[FR_RADIUS_CODE_MAX]
Definition: rlm_stats.c:79
static void coalesce(uint64_t final_stats[FR_RADIUS_CODE_MAX], rlm_stats_thread_t *t, size_t tree_offset, rlm_stats_data_t *mydata)
Definition: rlm_stats.c:108
static fr_dict_attr_t const * attr_freeradius_stats4_ipv4_address
Definition: rlm_stats.c:96
static fr_dict_t const * dict_radius
Definition: rlm_stats.c:88
module_rlm_t rlm_stats
Definition: rlm_stats.c:443
pthread_mutex_t mutex
Definition: rlm_stats.c:50
fr_rb_node_t dst_node
Definition: rlm_stats.c:61
fr_ipaddr_t ipaddr
IP address of this thing.
Definition: rlm_stats.c:62
static fr_dict_attr_t const * attr_freeradius_stats4_ipv6_address
Definition: rlm_stats.c:97
fr_rb_node_t src_node
Definition: rlm_stats.c:60
static int mod_thread_instantiate(module_thread_inst_ctx_t const *mctx)
Instantiate thread data for the submodule.
Definition: rlm_stats.c:361
fr_dict_attr_t const * ipv6_da
FreeRADIUS-Stats4-IPv6-Address.
Definition: rlm_stats.c:53
fr_dlist_t entry
for threads to know about each other
Definition: rlm_stats.c:72
fr_rb_tree_t * src
stats by source
Definition: rlm_stats.c:76
fr_time_t last_global_update
Definition: rlm_stats.c:71
static fr_dict_attr_t const * attr_freeradius_stats4_type
Definition: rlm_stats.c:98
static const conf_parser_t module_config[]
Definition: rlm_stats.c:84
pthread_mutex_t mutex
Definition: rlm_stats.c:81
static int mod_thread_detach(module_thread_inst_ctx_t const *mctx)
Destroy thread data for the submodule.
Definition: rlm_stats.c:391
static int mod_instantiate(module_inst_ctx_t const *mctx)
Definition: rlm_stats.c:408
fr_dlist_head_t list
for threads to know about each other
Definition: rlm_stats.c:54
static unlang_action_t mod_stats(rlm_rcode_t *p_result, module_ctx_t const *mctx, request_t *request)
Definition: rlm_stats.c:164
fr_time_t last_manage
when we deleted old things
Definition: rlm_stats.c:74
#define MODULE_NAME_TERMINATOR
Definition: module.h:135
#define pair_update_reply(_attr, _da)
Return or allocate a fr_pair_t in the reply list.
Definition: pair.h:129
RETURN_MODULE_FAIL
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
eap_aka_sim_process_conf_t * inst
fr_pair_t * vp
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition: strlcpy.c:34
Stores an attribute, a value and various bits of other data.
Definition: pair.h:68
#define NSEC
Definition: time.h:377
"server local" time.
Definition: time.h:69
int nonnull(2, 5))