All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
rlm_cache.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: e856bf912afe470b43e46a6472460fdd1de18c83 $
19  * @file rlm_cache.c
20  * @brief Cache values and merge them back into future requests.
21  *
22  * @copyright 2012-2014 The FreeRADIUS server project
23  */
24 RCSID("$Id: e856bf912afe470b43e46a6472460fdd1de18c83 $")
25 
26 #include <freeradius-devel/radiusd.h>
27 #include <freeradius-devel/modules.h>
28 #include <freeradius-devel/modpriv.h>
29 #include <freeradius-devel/modcall.h>
30 #include <freeradius-devel/rad_assert.h>
31 
32 #include "rlm_cache.h"
33 
34 /*
35  * A mapping of configuration file names to internal variables.
36  *
37  * Note that the string is dynamically allocated, so it MUST
38  * be freed. When the configuration file parse re-reads the string,
39  * it free's the old one, and strdup's the new one, placing the pointer
40  * to the strdup'd string into 'config.string'. This gets around
41  * buffer over-flows.
42  */
43 static const CONF_PARSER module_config[] = {
44  { FR_CONF_OFFSET("driver", PW_TYPE_STRING, rlm_cache_config_t, driver_name), .dflt = "rlm_cache_rbtree" },
46  { FR_CONF_OFFSET("ttl", PW_TYPE_INTEGER, rlm_cache_config_t, ttl), .dflt = "500" },
47  { FR_CONF_OFFSET("max_entries", PW_TYPE_INTEGER, rlm_cache_config_t, max_entries), .dflt = "0" },
48 
49  /* Should be a type which matches time_t, @fixme before 2038 */
50  { FR_CONF_OFFSET("epoch", PW_TYPE_SIGNED, rlm_cache_config_t, epoch), .dflt = "0" },
51  { FR_CONF_OFFSET("add_stats", PW_TYPE_BOOLEAN, rlm_cache_config_t, stats), .dflt = "no" },
53 };
54 
55 /** Get exclusive use of a handle to access the cache
56  *
57  */
58 static int cache_acquire(rlm_cache_handle_t **out, rlm_cache_t const *inst, REQUEST *request)
59 {
60  if (!inst->driver->acquire) return 0;
61 
62  return inst->driver->acquire(out, &inst->config, inst->driver_inst, request);
63 }
64 
65 /** Release a handle we previously acquired
66  *
67  */
68 static void cache_release(rlm_cache_t const *inst, REQUEST *request, rlm_cache_handle_t **handle)
69 {
70  if (!inst->driver->release) return;
71  if (!handle || !*handle) return;
72 
73  inst->driver->release(&inst->config, inst->driver_inst, request, *handle);
74  *handle = NULL;
75 }
76 
77 /** Reconnect an suspected inviable handle
78  *
79  */
80 static int cache_reconnect(rlm_cache_handle_t **handle, rlm_cache_t const *inst, REQUEST *request)
81 {
82  rad_assert(inst->driver->reconnect);
83 
84  return inst->driver->reconnect(handle, &inst->config, inst->driver_inst, request);
85 }
86 
87 /** Allocate a cache entry
88  *
89  * This is used so that drivers may use their own allocation functions
90  * to allocate structures larger than the normal rlm_cache_entry_t.
91  *
92  * If the driver doesn't specify a custom allocation function, the cache
93  * entry is talloced in the NULL ctx.
94  */
96 {
97  if (inst->driver->alloc) return inst->driver->alloc(&inst->config, inst->driver_inst, request);
98 
99  return talloc_zero(NULL, rlm_cache_entry_t);
100 }
101 
102 /** Free memory associated with a cache entry
103  *
104  * This does not necessarily remove the entry from the cache, cache_expire
105  * should be used for that.
106  *
107  * This function should be called when an entry that is known to have been
108  * retrieved or inserted into a data store successfully, is no longer needed.
109  *
110  * Some drivers (like rlm_cache_rbtree) don't register a free function.
111  * This means that the cache entry never needs to be explicitly freed.
112  *
113  * @param[in] inst Module instance.
114  * @param[in,out] c Cache entry to free.
115  */
117 {
118  if (!c || !*c || !inst->driver->free) return;
119 
120  inst->driver->free(*c);
121  *c = NULL;
122 }
123 
124 /** Merge a cached entry into a #REQUEST
125  *
126  * @return
127  * - #RLM_MODULE_OK if no entries were merged.
128  * - #RLM_MODULE_UPDATED if entries were merged.
129  */
130 static rlm_rcode_t cache_merge(rlm_cache_t const *inst, REQUEST *request, rlm_cache_entry_t *c) CC_HINT(nonnull);
132 {
133  VALUE_PAIR *vp;
134  vp_map_t *map;
135  int merged = 0;
136 
137  RDEBUG2("Merging cache entry into request");
138  RINDENT();
139  for (map = c->maps; map; map = map->next) {
140  /*
141  * The only reason that the application of a map entry
142  * can fail, is if the destination list or request
143  * isn't valid. For now we don't consider this fatal
144  * and continue merging the rest of the maps.
145  */
146  if (map_to_request(request, map, map_to_vp, NULL) < 0) {
147  char buffer[1024];
148 
149  map_snprint(buffer, sizeof(buffer), map);
150  REXDENT();
151  RDEBUG("Skipping %s", buffer);
152  RINDENT();
153  continue;
154  }
155  merged++;
156  }
157  REXDENT();
158 
159  if (inst->config.stats) {
160  rad_assert(request->packet != NULL);
161  vp = fr_pair_find_by_num(request->packet->vps, 0, PW_CACHE_ENTRY_HITS, TAG_ANY);
162  if (!vp) {
163  vp = fr_pair_afrom_num(request->packet, 0, PW_CACHE_ENTRY_HITS);
164  rad_assert(vp != NULL);
165  fr_pair_add(&request->packet->vps, vp);
166  }
167  vp->vp_integer = c->hits;
168  }
169 
170  return merged > 0 ?
173 }
174 
175 /** Find a cached entry.
176  *
177  * @return
178  * - #RLM_MODULE_OK on cache hit.
179  * - #RLM_MODULE_FAIL on failure.
180  * - #RLM_MODULE_NOTFOUND on cache miss.
181  */
183  rlm_cache_handle_t **handle, uint8_t const *key, size_t key_len)
184 {
185  cache_status_t ret;
186 
188 
189  *out = NULL;
190 
191  for (;;) {
192  ret = inst->driver->find(&c, &inst->config, inst->driver_inst, request, *handle, key, key_len);
193  switch (ret) {
194  case CACHE_RECONNECT:
195  RDEBUG("Reconnecting...");
196  if (cache_reconnect(handle, inst, request) == 0) continue;
197  return RLM_MODULE_FAIL;
198 
199  case CACHE_OK:
200  break;
201 
202  case CACHE_MISS:
203  if (RDEBUG_ENABLED2) {
204  char *p;
205 
206  p = fr_asprint(request, (char const *)key, key_len, '"');
207  RDEBUG("No cache entry found for \"%s\"", p);
208  talloc_free(p);
209  }
210  return RLM_MODULE_NOTFOUND;
211 
212  /* FALL-THROUGH */
213  default:
214  return RLM_MODULE_FAIL;
215 
216  }
217 
218  break;
219  }
220 
221  /*
222  * Yes, but it expired, OR the "forget all" epoch has
223  * passed. Delete it, and pretend it doesn't exist.
224  */
225  if ((c->expires < request->timestamp.tv_sec) || (c->created < inst->config.epoch)) {
226  if (RDEBUG_ENABLED2) {
227  char *p;
228 
229  p = fr_asprint(request, (char const *)key, key_len, '"');
230  RDEBUG2("Found entry for \"%s\", but it expired %li seconds ago. Removing it", p,
231  request->timestamp.tv_sec - c->expires);
232  talloc_free(p);
233  }
234 
235  inst->driver->expire(&inst->config, inst->driver_inst, request, handle, c->key, c->key_len);
236  cache_free(inst, &c);
237  return RLM_MODULE_NOTFOUND; /* Couldn't find a non-expired entry */
238  }
239 
240  if (RDEBUG_ENABLED2) {
241  char *p;
242 
243  p = fr_asprint(request, (char const *)key, key_len, '"');
244  RDEBUG2("Found entry for \"%s\"", p);
245  talloc_free(p);
246  }
247 
248  c->hits++;
249  *out = c;
250 
251  return RLM_MODULE_OK;
252 }
253 
254 /** Expire a cache entry (removing it from the datastore)
255  *
256  * @return
257  * - #RLM_MODULE_OK on success.
258  * - #RLM_MODULE_NOTFOUND if no entry existed.
259  * - #RLM_MODULE_FAIL on failure.
260  */
262  rlm_cache_handle_t **handle, uint8_t const *key, size_t key_len)
263 {
264  RDEBUG("Expiring cache entry");
265  for (;;) switch (inst->driver->expire(&inst->config, inst->driver_inst, request,
266  *handle, key, key_len)) {
267  case CACHE_RECONNECT:
268  if (cache_reconnect(handle, inst, request) == 0) continue;
269 
270  /* FALL-THROUGH */
271  default:
272  return RLM_MODULE_FAIL;
273 
274  case CACHE_OK:
275  return RLM_MODULE_OK;
276 
277  case CACHE_MISS:
278  return RLM_MODULE_NOTFOUND;
279  }
280 }
281 
282 /** Create and insert a cache entry
283  *
284  * @return
285  * - #RLM_MODULE_OK on success.
286  * - #RLM_MODULE_UPDATED if we merged the cache entry.
287  * - #RLM_MODULE_FAIL on failure.
288  */
290  uint8_t const *key, size_t key_len, int ttl)
291 {
292  vp_map_t const *map;
293  vp_map_t **last, *c_map;
294 
295  VALUE_PAIR *vp;
296  bool merge = false;
298  size_t len;
299 
300  TALLOC_CTX *pool;
301 
302  if ((inst->config.max_entries > 0) && inst->driver->count &&
303  (inst->driver->count(&inst->config, inst->driver_inst, request, handle) > inst->config.max_entries)) {
304  RWDEBUG("Cache is full: %d entries", inst->config.max_entries);
305  return RLM_MODULE_FAIL;
306  }
307 
308  c = cache_alloc(inst, request);
309  if (!c) return RLM_MODULE_FAIL;
310 
311  c->key = talloc_memdup(c, key, key_len);
312  c->key_len = key_len;
313  c->created = c->expires = request->timestamp.tv_sec;
314  c->expires += ttl;
315 
316  last = &c->maps;
317 
318  RDEBUG("Creating new cache entry");
319 
320  /*
321  * Alloc a pool so we don't have excessive mallocs when
322  * gathering VALUE_PAIRs to cache.
323  */
324  pool = talloc_pool(NULL, 1024);
325  for (map = inst->maps; map != NULL; map = map->next) {
326  VALUE_PAIR *to_cache = NULL;
327  vp_cursor_t cursor;
328 
329  rad_assert(map->lhs && map->rhs);
330 
331  /*
332  * Calling map_to_vp gives us exactly the same result,
333  * as if this were an update section.
334  */
335  if (map_to_vp(pool, &to_cache, request, map, NULL) < 0) {
336  RDEBUG("Skipping %s", map->rhs->name);
337  continue;
338  }
339 
340  for (vp = fr_cursor_init(&cursor, &to_cache);
341  vp;
342  vp = fr_cursor_next(&cursor)) {
343  /*
344  * Prevent people from accidentally caching
345  * cache control attributes.
346  */
347  if (map->rhs->type == TMPL_TYPE_LIST) switch (vp->da->attr) {
348  case PW_CACHE_TTL:
349  case PW_CACHE_STATUS_ONLY:
350  case PW_CACHE_READ_ONLY:
351  case PW_CACHE_MERGE:
352  case PW_CACHE_ENTRY_HITS:
353  RDEBUG2("Skipping %s", vp->da->name);
354  continue;
355 
356  default:
357  break;
358  }
359 
360  RINDENT();
361  if (RDEBUG_ENABLED2) map_debug_log(request, map, vp);
362  REXDENT();
363 
364  MEM(c_map = talloc_zero(c, vp_map_t));
365  c_map->op = map->op;
366 
367  /*
368  * Now we turn the VALUE_PAIRs into maps.
369  */
370  switch (map->lhs->type) {
371  /*
372  * Attributes are easy, reuse the LHS, and create a new
373  * RHS with the value_data_t from the VALUE_PAIR.
374  */
375  case TMPL_TYPE_ATTR:
376  c_map->lhs = map->lhs; /* lhs shouldn't be touched, so this is ok */
377  do_rhs:
378  MEM(c_map->rhs = tmpl_init(talloc(c_map, vp_tmpl_t),
379  TMPL_TYPE_DATA, map->rhs->name, map->rhs->len, T_BARE_WORD));
380  if (value_data_copy(c_map->rhs, &c_map->rhs->tmpl_data_value,
381  vp->da->type, &vp->data) < 0) {
382  REDEBUG("Failed copying attribute value");
383  error:
384  talloc_free(pool);
385  talloc_free(c);
386  return RLM_MODULE_FAIL;
387  }
388  c_map->rhs->tmpl_data_type = vp->da->type;
389  if (vp->da->type == PW_TYPE_STRING) {
390  c_map->rhs->quote = is_printable(vp->vp_strvalue, vp->vp_length) ?
392  }
393  break;
394 
395  /*
396  * Lists are weird... We need to fudge a new LHS template,
397  * which is a combination of the LHS list and the attribute.
398  */
399  case TMPL_TYPE_LIST:
400  {
401  char attr[256];
402 
403  MEM(c_map->lhs = tmpl_init(talloc(c_map, vp_tmpl_t),
404  TMPL_TYPE_ATTR, map->lhs->name, map->lhs->len, T_BARE_WORD));
405  c_map->lhs->tmpl_da = vp->da;
406  c_map->lhs->tmpl_tag = vp->tag;
407  c_map->lhs->tmpl_list = map->lhs->tmpl_list;
408  c_map->lhs->tmpl_num = map->lhs->tmpl_num;
409  c_map->lhs->tmpl_request = map->lhs->tmpl_request;
410 
411  /*
412  * We need to rebuild the attribute name, to be the
413  * one we copied from the source list.
414  */
415  len = tmpl_snprint(attr, sizeof(attr), c_map->lhs, NULL);
416  if (is_truncated(len, sizeof(attr))) {
417  REDEBUG("Serialized attribute too long. Must be < "
418  STRINGIFY(sizeof(attr)) " bytes, got %zu bytes", len);
419  goto error;
420  }
421  c_map->lhs->len = len;
422  c_map->lhs->name = talloc_strdup(map->lhs, attr);
423  }
424  goto do_rhs;
425 
426  default:
427  rad_assert(0);
428  }
429  *last = c_map;
430  last = &(*last)->next;
431  }
432  talloc_free_children(pool); /* reset pool state */
433  }
434  talloc_free(pool);
435 
436  /*
437  * Check to see if we need to merge the entry into the request
438  */
439  vp = fr_pair_find_by_num(request->config, 0, PW_CACHE_MERGE, TAG_ANY);
440  if (vp && (vp->vp_integer > 0)) merge = true;
441 
442  if (merge) cache_merge(inst, request, c);
443 
444  for (;;) {
445  cache_status_t ret;
446 
447  ret = inst->driver->insert(&inst->config, inst->driver_inst, request, *handle, c);
448  switch (ret) {
449  case CACHE_RECONNECT:
450  if (cache_reconnect(handle, inst, request) == 0) continue;
451  return RLM_MODULE_FAIL;
452 
453  case CACHE_OK:
454  RDEBUG("Committed entry, TTL %d seconds", ttl);
455  cache_free(inst, &c);
456  return merge ? RLM_MODULE_UPDATED :
458 
459  default:
460  talloc_free(c); /* Failed insertion - use talloc_free not the driver free */
461  return RLM_MODULE_FAIL;
462  }
463  }
464 }
465 
466 /** Update the TTL of an entry
467  *
468  * @return
469  * - #RLM_MODULE_OK on success.
470  * - #RLM_MODULE_FAIL on failure.
471  */
474 {
475  /*
476  * Call the driver's insert method to overwrite the old entry
477  */
478  if (!inst->driver->set_ttl) for (;;) {
479  cache_status_t ret;
480 
481  ret = inst->driver->insert(&inst->config, inst->driver_inst, request, *handle, c);
482  switch (ret) {
483  case CACHE_RECONNECT:
484  if (cache_reconnect(handle, inst, request) == 0) continue;
485  return RLM_MODULE_FAIL;
486 
487  case CACHE_OK:
488  RDEBUG("Updated entry TTL");
489  return RLM_MODULE_OK;
490 
491  default:
492  return RLM_MODULE_FAIL;
493  }
494  }
495 
496  /*
497  * Or call the set ttl method if the driver can do this more
498  * efficiently.
499  */
500  for (;;) {
501  cache_status_t ret;
502 
503  ret = inst->driver->set_ttl(&inst->config, inst->driver_inst, request, *handle, c);
504  switch (ret) {
505  case CACHE_RECONNECT:
506  if (cache_reconnect(handle, inst, request) == 0) continue;
507  return RLM_MODULE_FAIL;
508 
509  case CACHE_OK:
510  RDEBUG("Updated entry TTL");
511  return RLM_MODULE_OK;
512 
513  default:
514  return RLM_MODULE_FAIL;
515  }
516  }
517 }
518 
519 /** Verify that a map in the cache section makes sense
520  *
521  */
522 static int cache_verify(vp_map_t *map, void *ctx)
523 {
524  if (modcall_fixup_update(map, ctx) < 0) return -1;
525 
526  if ((map->lhs->type != TMPL_TYPE_ATTR) &&
527  (map->lhs->type != TMPL_TYPE_LIST)) {
528  cf_log_err(map->ci, "Destination must be an attribute ref or a list");
529  return -1;
530  }
531 
532  return 0;
533 }
534 
535 /** Do caching checks
536  *
537  * Since we can update ANY VP list, we do exactly the same thing for all sections
538  * (autz / auth / etc.)
539  *
540  * If you want to cache something different in different sections, configure
541  * another cache module.
542  */
543 static rlm_rcode_t mod_cache_it(void *instance, REQUEST *request) CC_HINT(nonnull);
544 static rlm_rcode_t mod_cache_it(void *instance, REQUEST *request)
545 {
546  rlm_cache_entry_t *c = NULL;
547  rlm_cache_t *inst = instance;
548 
549  rlm_cache_handle_t *handle;
550 
551  vp_cursor_t cursor;
552  VALUE_PAIR *vp;
553 
554  bool merge = true, insert = true, expire = false, set_ttl = false;
555  int exists = -1;
556 
557  uint8_t buffer[1024];
558  uint8_t const *key;
559  ssize_t key_len;
561 
562  int ttl = inst->config.ttl;
563 
564  key_len = tmpl_expand((char const **)&key, (char *)buffer, sizeof(buffer),
565  request, inst->config.key, NULL, NULL);
566  if (key_len < 0) return RLM_MODULE_FAIL;
567 
568  if (key_len == 0) {
569  REDEBUG("Zero length key string is invalid");
570  return RLM_MODULE_INVALID;
571  }
572 
573  /*
574  * If Cache-Status-Only == yes, only return whether we found a
575  * valid cache entry
576  */
577  vp = fr_pair_find_by_num(request->config, 0, PW_CACHE_STATUS_ONLY, TAG_ANY);
578  if (vp && vp->vp_integer) {
579  if (cache_acquire(&handle, inst, request) < 0) return RLM_MODULE_FAIL;
580 
581  rcode = cache_find(&c, inst, request, &handle, key, key_len);
582  if (rcode == RLM_MODULE_FAIL) goto finish;
583  rad_assert(handle);
584 
585  rcode = c ? RLM_MODULE_OK:
587  goto finish;
588  }
589 
590  /*
591  * Figure out what operation we're doing
592  */
593  vp = fr_pair_find_by_num(request->config, 0, PW_CACHE_ALLOW_MERGE, TAG_ANY);
594  if (vp) merge = (bool)vp->vp_integer;
595 
596  vp = fr_pair_find_by_num(request->config, 0, PW_CACHE_ALLOW_INSERT, TAG_ANY);
597  if (vp) insert = (bool)vp->vp_integer;
598 
599  vp = fr_pair_find_by_num(request->config, 0, PW_CACHE_TTL, TAG_ANY);
600  if (vp) {
601  if (vp->vp_signed == 0) {
602  expire = true;
603  } else if (vp->vp_signed < 0) {
604  expire = true;
605  ttl = -(vp->vp_signed);
606  /* Updating the TTL */
607  } else {
608  set_ttl = true;
609  ttl = vp->vp_signed;
610  }
611  }
612 
613  if (cache_acquire(&handle, inst, request) < 0) return RLM_MODULE_FAIL;
614 
615  /*
616  * Retrieve the cache entry and merge it with the current request
617  * recording whether the entry existed.
618  */
619  if (merge) {
620  rcode = cache_find(&c, inst, request, &handle, key, key_len);
621  switch (rcode) {
622  case RLM_MODULE_FAIL:
623  goto finish;
624 
625  case RLM_MODULE_OK:
626  rcode = cache_merge(inst, request, c);
627  exists = 1;
628  break;
629 
630  case RLM_MODULE_NOTFOUND:
631  rcode = RLM_MODULE_NOTFOUND;
632  exists = 0;
633  break;
634 
635  default:
636  rad_assert(0);
637  }
638  rad_assert(handle);
639  }
640 
641  /*
642  * Expire the entry if told to, and we either don't know whether
643  * it exists, or we know it does.
644  *
645  * We only expire if we're not inserting, as driver insert methods
646  * should perform upserts.
647  */
648  if (expire && ((exists == -1) || (exists == 1))) {
649  if (!insert) {
650  rad_assert(!set_ttl);
651  switch (cache_expire(inst, request, &handle, key, key_len)) {
652  case RLM_MODULE_FAIL:
653  rcode = RLM_MODULE_FAIL;
654  goto finish;
655 
656  case RLM_MODULE_OK:
657  if (rcode == RLM_MODULE_NOOP) rcode = RLM_MODULE_OK;
658  break;
659 
660  case RLM_MODULE_NOTFOUND:
661  if (rcode == RLM_MODULE_NOOP) rcode = RLM_MODULE_NOTFOUND;
662  break;
663 
664  default:
665  rad_assert(0);
666  break;
667  }
668  /* If it previously existed, it doesn't now */
669  }
670  /* Otherwise use insert to overwrite */
671  exists = 0;
672  }
673 
674  /*
675  * If we still don't know whether it exists or not
676  * and we need to do an insert or set_ttl operation
677  * determine that now.
678  */
679  if ((exists < 0) && (insert || set_ttl)) {
680  switch (cache_find(&c, inst, request, &handle, key, key_len)) {
681  case RLM_MODULE_FAIL:
682  rcode = RLM_MODULE_FAIL;
683  goto finish;
684 
685  case RLM_MODULE_OK:
686  exists = 1;
687  if (rcode != RLM_MODULE_UPDATED) rcode = RLM_MODULE_OK;
688  break;
689 
690  case RLM_MODULE_NOTFOUND:
691  exists = 0;
692  break;
693 
694  default:
695  rad_assert(0);
696  }
697  rad_assert(handle);
698  }
699 
700  /*
701  * We can only alter the TTL on an entry if it exists.
702  */
703  if (set_ttl && (exists == 1)) {
704  rad_assert(c);
705 
706  c->expires = request->timestamp.tv_sec + ttl;
707 
708  switch (cache_set_ttl(inst, request, &handle, c)) {
709  case RLM_MODULE_FAIL:
710  rcode = RLM_MODULE_FAIL;
711  goto finish;
712 
713  case RLM_MODULE_NOTFOUND:
714  case RLM_MODULE_OK:
715  if (rcode != RLM_MODULE_UPDATED) rcode = RLM_MODULE_OK;
716  goto finish;
717 
718  default:
719  rad_assert(0);
720  }
721  }
722 
723  /*
724  * Inserts are upserts, so we don't care about the
725  * entry state, just that we're not meant to be
726  * setting the TTL, which precludes performing an
727  * insert.
728  */
729  if (insert && (exists == 0)) {
730  switch (cache_insert(inst, request, &handle, key, key_len, ttl)) {
731  case RLM_MODULE_FAIL:
732  rcode = RLM_MODULE_FAIL;
733  goto finish;
734 
735  case RLM_MODULE_OK:
736  if (rcode != RLM_MODULE_UPDATED) rcode = RLM_MODULE_OK;
737  break;
738 
739  case RLM_MODULE_UPDATED:
740  rcode = RLM_MODULE_UPDATED;
741  break;
742 
743  default:
744  rad_assert(0);
745  }
746  rad_assert(handle);
747  goto finish;
748  }
749 
750 
751 finish:
752  cache_free(inst, &c);
753  cache_release(inst, request, &handle);
754 
755  /*
756  * Clear control attributes
757  */
758  for (vp = fr_cursor_init(&cursor, &request->config);
759  vp;
760  vp = fr_cursor_next(&cursor)) {
761  if (vp->da->vendor == 0) switch (vp->da->attr) {
762  case PW_CACHE_TTL:
763  case PW_CACHE_STATUS_ONLY:
764  case PW_CACHE_ALLOW_MERGE:
765  case PW_CACHE_ALLOW_INSERT:
766  case PW_CACHE_MERGE:
767  RDEBUG2("Removing &control:%s", vp->da->name);
768  vp = fr_cursor_remove(&cursor);
769  talloc_free(vp);
770  break;
771  }
772  }
773 
774  return rcode;
775 }
776 
777 /** Allow single attribute values to be retrieved from the cache
778  *
779  */
780 static ssize_t cache_xlat(char **out, UNUSED size_t freespace,
781  void const *mod_inst, UNUSED void const *xlat_inst,
782  REQUEST *request, char const *fmt) CC_HINT(nonnull);
783 static ssize_t cache_xlat(char **out, UNUSED size_t freespace,
784  void const *mod_inst, UNUSED void const *xlat_inst,
785  REQUEST *request, char const *fmt)
786 {
787  rlm_cache_entry_t *c = NULL;
788  rlm_cache_t const *inst = mod_inst;
789  rlm_cache_handle_t *handle = NULL;
790 
791  ssize_t slen;
792  ssize_t ret = 0;
793 
794  uint8_t buffer[1024];
795  uint8_t const *key;
796  ssize_t key_len;
797 
798  vp_tmpl_t target;
799  vp_map_t *map = NULL;
800 
801  key_len = tmpl_expand((char const **)&key, (char *)buffer, sizeof(buffer),
802  request, inst->config.key, NULL, NULL);
803  if (key_len < 0) return -1;
804 
805  slen = tmpl_from_attr_substr(&target, fmt, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false);
806  if (slen <= 0) {
807  REDEBUG("%s", fr_strerror());
808  return -1;
809  }
810 
811  if (cache_acquire(&handle, mod_inst, request) < 0) return -1;
812 
813  switch (cache_find(&c, mod_inst, request, handle, key, key_len)) {
814  case RLM_MODULE_OK: /* found */
815  break;
816 
817  case RLM_MODULE_NOTFOUND: /* not found */
818  return 0;
819 
820  default:
821  return -1;
822  }
823 
824  for (map = c->maps; map; map = map->next) {
825  if ((map->lhs->tmpl_da != target.tmpl_da) ||
826  (map->lhs->tmpl_tag != target.tmpl_tag) ||
827  (map->lhs->tmpl_list != target.tmpl_list)) continue;
828 
829  *out = value_data_asprint(request, map->rhs->tmpl_data_type, map->lhs->tmpl_da,
830  &map->rhs->tmpl_data_value, '\0');
831  ret = talloc_array_length(*out) - 1;
832  break;
833  }
834 
835  /*
836  * Check if we found a matching map
837  */
838  if (!map) return 0;
839 
840  cache_free(mod_inst, &c);
841  cache_release(mod_inst, request, &handle);
842 
843  return ret;
844 }
845 
846 /** Free any memory allocated under the instance
847  *
848  */
849 static int mod_detach(void *instance)
850 {
851  rlm_cache_t *inst = instance;
852 
853  talloc_free(inst->maps);
854 
855  /*
856  * We need to explicitly free all children, so if the driver
857  * parented any memory off the instance, their destructors
858  * run before we unload the bytecode for them.
859  *
860  * If we don't do this, we get a SEGV deep inside the talloc code
861  * when it tries to call a destructor that no longer exists.
862  */
863  talloc_free_children(inst);
864 
865  /*
866  * Decrements the reference count. The driver object won't be unloaded
867  * until all instances of rlm_cache that use it have been destroyed.
868  */
869  if (inst->handle) dlclose(inst->handle);
870 
871  return 0;
872 }
873 
874 /** Register module xlats
875  *
876  */
877 static int mod_bootstrap(CONF_SECTION *conf, void *instance)
878 {
879  rlm_cache_t *inst = instance;
880 
881  inst->cs = conf;
882 
883  inst->config.name = cf_section_name2(conf);
884  if (!inst->config.name) inst->config.name = cf_section_name1(conf);
885 
886  /*
887  * Register the cache xlat function
888  */
889  xlat_register(inst, inst->config.name, cache_xlat, NULL, NULL, 0, 0);
890 
891  return 0;
892 }
893 
894 /** Create a new rlm_cache_instance
895  *
896  */
897 static int mod_instantiate(CONF_SECTION *conf, void *instance)
898 {
899  rlm_cache_t *inst = instance;
900  CONF_SECTION *update;
901 
902  inst->cs = conf;
903 
904  rad_assert(inst->config.key);
905 
906  /*
907  * Sanity check for crazy people.
908  */
909  if (strncmp(inst->config.driver_name, "rlm_cache_", 8) != 0) {
910  cf_log_err_cs(conf, "\"%s\" is NOT an Cache driver!", inst->config.driver_name);
911  return -1;
912  }
913 
914  /*
915  * Load the appropriate driver for our database
916  */
917  inst->handle = lt_dlopenext(inst->config.driver_name);
918  if (!inst->handle) {
919  cf_log_err_cs(conf, "Could not link driver %s: %s", inst->config.driver_name, fr_strerror());
920  cf_log_err_cs(conf, "Make sure it (and all its dependent libraries!) are in the search path"
921  " of your system's ld");
922  return -1;
923  }
924 
925  inst->driver = (cache_driver_t *) dlsym(inst->handle, inst->config.driver_name);
926  if (!inst->driver) {
927  cf_log_err_cs(conf, "Could not link symbol %s: %s", inst->config.driver_name, dlerror());
928  return -1;
929  }
930 
931  DEBUG("rlm_cache (%s): Driver %s loaded and linked", inst->config.name, inst->driver->name);
932 
933  /*
934  * Non optional fields and callbacks
935  */
936  rad_assert(inst->driver->name);
937  rad_assert(inst->driver->find);
938  rad_assert(inst->driver->insert);
939  rad_assert(inst->driver->expire);
940 
941  if (inst->driver->instantiate) {
942  CONF_SECTION *cs;
943  char const *name;
944 
945  name = strrchr(inst->config.driver_name, '_');
946  if (!name) {
947  name = inst->config.driver_name;
948  } else {
949  name++;
950  }
951 
952  cs = cf_section_sub_find(conf, name);
953  if (!cs) {
954  cs = cf_section_alloc(conf, name, NULL);
955  if (!cs) return -1;
956  }
957 
958  /*
959  * It's up to the driver to register a destructor (using talloc)
960  *
961  * Should write its instance data in inst->driver,
962  * and parent it off of inst.
963  */
964  if (inst->driver->inst_size) MEM(inst->driver_inst = talloc_zero_array(inst, uint8_t,
965  inst->driver->inst_size));
966  if (inst->driver->instantiate(cs, &inst->config, inst->driver_inst) < 0) return -1;
967  }
968 
969  if (inst->config.ttl == 0) {
970  cf_log_err_cs(conf, "Must set 'ttl' to non-zero");
971  return -1;
972  }
973 
974  if (inst->config.epoch != 0) {
975  cf_log_err_cs(conf, "Must not set 'epoch' in the configuration files");
976  return -1;
977  }
978 
979  update = cf_section_sub_find(inst->cs, "update");
980  if (!update) {
981  cf_log_err_cs(conf, "Must have an 'update' section in order to cache anything.");
982  return -1;
983  }
984 
985  /*
986  * Make sure the users don't screw up too badly.
987  */
988  if (map_afrom_cs(&inst->maps, update,
990  return -1;
991  }
992 
993  if (!inst->maps) {
994  cf_log_err_cs(inst->cs, "Cache config must contain an update section, and "
995  "that section must not be empty");
996 
997  return -1;
998  }
999  return 0;
1000 }
1001 
1002 /*
1003  * The module name should be the only globally exported symbol.
1004  * That is, everything else should be 'static'.
1005  *
1006  * If the module needs to temporarily modify it's instantiation
1007  * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
1008  * The server will then take care of ensuring that the module
1009  * is single-threaded.
1010  */
1011 extern module_t rlm_cache;
1012 module_t rlm_cache = {
1014  .name = "cache",
1015  .inst_size = sizeof(rlm_cache_t),
1016  .config = module_config,
1017  .bootstrap = mod_bootstrap,
1018  .instantiate = mod_instantiate,
1019  .detach = mod_detach,
1020  .methods = {
1027  },
1028 };
size_t map_snprint(char *out, size_t outlen, vp_map_t const *map)
Print a map to a string.
Definition: map.c:1494
5 methods index for preproxy section.
Definition: modules.h:46
static void cache_free(rlm_cache_t const *inst, rlm_cache_entry_t **c)
Free memory associated with a cache entry.
Definition: rlm_cache.c:116
ssize_t tmpl_expand(char const **out, char *buff, size_t outlen, REQUEST *request, vp_tmpl_t const *vpt, xlat_escape_t escape, void *escape_ctx)
Expand a vp_tmpl_t to a string writing the result to a buffer.
Definition: tmpl.c:1479
Definition: rlm_cache.h:76
static int cache_verify(vp_map_t *map, void *ctx)
Verify that a map in the cache section makes sense.
Definition: rlm_cache.c:522
VALUE_PAIR * config
VALUE_PAIR (s) used to set per request parameters for modules and the server core at runtime...
Definition: radiusd.h:227
void map_debug_log(REQUEST *request, vp_map_t const *map, VALUE_PAIR const *vp) CC_HINT(nonnull(1
#define RINDENT()
Indent R* messages by one level.
Definition: log.h:265
int int map_to_request(REQUEST *request, vp_map_t const *map, radius_map_getvalue_t func, void *ctx)
Convert vp_map_t to VALUE_PAIR (s) and add them to a REQUEST.
Definition: map.c:1019
cache_driver_t * driver
Driver.
Definition: rlm_cache.h:68
int xlat_register(void *mod_inst, char const *name, xlat_func_t func, xlat_escape_t escape, xlat_instantiate_t instantiate, size_t inst_size, size_t buf_len)
Register an xlat function.
Definition: xlat.c:717
The module is OK, continue.
Definition: radiusd.h:91
Cache entry notfound.
Definition: rlm_cache.h:39
Metadata exported by the module.
Definition: modules.h:134
VALUE_PAIR * fr_pair_afrom_num(TALLOC_CTX *ctx, unsigned int vendor, unsigned int attr)
Create a new valuepair.
Definition: pair.c:106
#define RDEBUG_ENABLED2
True if request debug level 1-2 messages are enabled.
Definition: log.h:238
char const * name
Raw string used to create the template.
Definition: tmpl.h:190
lt_dlhandle lt_dlopenext(char const *name)
Definition: modules.c:151
#define MEM(x)
Definition: radiusd.h:396
32 Bit signed integer.
Definition: radius.h:45
vp_tmpl_t * lhs
Typically describes the attribute to add, modify or compare.
Definition: map.h:47
static const CONF_PARSER module_config[]
Definition: rlm_cache.c:43
7 methods index for postauth section.
Definition: modules.h:48
Dictionary attribute.
Definition: tmpl.h:133
static char const * name
cache_acquire_t acquire
(optional) Acquire exclusive access to a resource used to retrieve the cache entry.
Definition: rlm_cache.h:292
vp_tmpl_t * rhs
Typically describes a literal value or a src attribute to copy or compare.
Definition: map.h:48
cache_entry_insert_t insert
Add a new entry.
Definition: rlm_cache.h:286
#define CC_HINT(_x)
Definition: build.h:71
cache_reconnect_t reconnect
(optional) Re-initialise resource.
Definition: rlm_cache.h:296
#define UNUSED
Definition: libradius.h:134
#define RLM_MODULE_INIT
Definition: modules.h:86
ssize_t tmpl_from_attr_substr(vp_tmpl_t *vpt, char const *name, request_refs_t request_def, pair_lists_t list_def, bool allow_unknown, bool allow_undefined)
Parse a string into a TMPL_TYPE_ATTR_* or TMPL_TYPE_LIST type vp_tmpl_t.
Definition: tmpl.c:661
int8_t tag
Tag value used to group valuepairs.
Definition: pair.h:121
VALUE_PAIR * vps
Result of decoding the packet into VALUE_PAIRs.
Definition: libradius.h:162
#define CONF_PARSER_TERMINATOR
Definition: conffile.h:289
vp_tmpl_t * key
What to expand to get the value of the key.
Definition: rlm_cache.h:50
int32_t epoch
Time after which entries are considered valid.
Definition: rlm_cache.h:53
uint32_t max_entries
Maximum entries allowed.
Definition: rlm_cache.h:52
VALUE_PAIR * fr_cursor_init(vp_cursor_t *cursor, VALUE_PAIR *const *node)
Setup a cursor to iterate over attribute pairs.
Definition: cursor.c:60
struct rlm_cache_t rlm_cache_t
Handle needs to be reconnected.
Definition: rlm_cache.h:36
#define inst
static int cache_reconnect(rlm_cache_handle_t **handle, rlm_cache_t const *inst, REQUEST *request)
Reconnect an suspected inviable handle.
Definition: rlm_cache.c:80
The module considers the request invalid.
Definition: radiusd.h:93
rlm_cache_config_t config
Must come first because of icky hacks.
Definition: rlm_cache.h:65
static expr_map_t map[]
Definition: rlm_expr.c:169
uint32_t ttl
How long an entry is valid for.
Definition: rlm_cache.h:51
bool stats
Generate statistics.
Definition: rlm_cache.h:54
cache_status_t
Definition: rlm_cache.h:35
static rlm_rcode_t mod_cache_it(void *instance, REQUEST *request) CC_HINT(nonnull)
Do caching checks.
Definition: rlm_cache.c:544
Defines a CONF_PAIR to C data type mapping.
Definition: conffile.h:267
Abstraction to allow iterating over different configurations of VALUE_PAIRs.
Definition: pair.h:144
struct vp_map * next
The next valuepair map.
Definition: map.h:55
static void cache_release(rlm_cache_t const *inst, REQUEST *request, rlm_cache_handle_t **handle)
Release a handle we previously acquired.
Definition: rlm_cache.c:68
#define is_truncated(_ret, _max)
Definition: libradius.h:204
cache_release_t release
(optional) Release access to resource acquired with acquire callback.
Definition: rlm_cache.h:294
size_t key_len
Length of key data.
Definition: rlm_cache.h:78
int map_afrom_cs(vp_map_t **out, CONF_SECTION *cs, pair_lists_t dst_list_def, pair_lists_t src_list_def, map_validate_t validate, void *ctx, unsigned int max) CC_HINT(nonnull(1
#define rad_assert(expr)
Definition: rad_assert.h:38
time_t expires
When the entry expires.
Definition: rlm_cache.h:81
bool is_printable(void const *value, size_t len)
Check whether the string is made up of printable UTF8 chars.
Definition: misc.c:329
static int cache_acquire(rlm_cache_handle_t **out, rlm_cache_t const *inst, REQUEST *request)
Get exclusive use of a handle to access the cache.
Definition: rlm_cache.c:58
static rlm_cache_entry_t * cache_alloc(rlm_cache_t const *inst, REQUEST *request)
Allocate a cache entry.
Definition: rlm_cache.c:95
#define DEBUG(fmt,...)
Definition: log.h:175
Value in native format.
Definition: tmpl.h:138
static rlm_rcode_t cache_merge(rlm_cache_t const *inst, REQUEST *request, rlm_cache_entry_t *c) CC_HINT(nonnull)
Merge a cached entry into a REQUEST.
Definition: rlm_cache.c:131
#define MAX_ATTRMAP
Definition: rlm_cache.h:33
cache_entry_alloc_t alloc
(optional) Allocate a new entry.
Definition: rlm_cache.h:282
vp_map_t * maps
Attribute map applied to users.
Definition: rlm_cache.h:71
cache_entry_free_t free
(optional) Free memory used by an entry.
Definition: rlm_cache.h:283
static rc_stats_t stats
Definition: radclient.c:47
void fr_pair_add(VALUE_PAIR **head, VALUE_PAIR *vp)
Add a VP to the end of the list.
Definition: pair.c:659
#define STRINGIFY(x)
Definition: build.h:34
unsigned int attr
Attribute number.
Definition: dict.h:79
Attributes in incoming or internally proxied request.
Definition: tmpl.h:82
long long int hits
How many times the entry has been retrieved.
Definition: rlm_cache.h:79
void rlm_cache_handle_t
Definition: rlm_cache.h:31
static rlm_rcode_t cache_expire(rlm_cache_t const *inst, REQUEST *request, rlm_cache_handle_t **handle, uint8_t const *key, size_t key_len)
Expire a cache entry (removing it from the datastore)
Definition: rlm_cache.c:261
Attribute list.
Definition: tmpl.h:135
3 methods index for accounting section.
Definition: modules.h:44
unsigned int vendor
Vendor that defines this attribute.
Definition: dict.h:78
Stores an attribute, a value and various bits of other data.
Definition: pair.h:112
void void cf_log_err_cs(CONF_SECTION const *cs, char const *fmt,...) CC_HINT(format(printf
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition: log.h:272
A truth value.
Definition: radius.h:56
The current request.
Definition: tmpl.h:113
32 Bit unsigned integer.
Definition: radius.h:34
enum rlm_rcodes rlm_rcode_t
Return codes indicating the result of the module call.
tmpl_type_t type
What type of value tmpl refers to.
Definition: tmpl.h:188
static rs_t * conf
Definition: radsniff.c:46
cache_entry_set_ttl_t set_ttl
(Optional) Update the TTL of an entry.
Definition: rlm_cache.h:288
char const * fr_strerror(void)
Get the last library error.
Definition: log.c:212
time_t created
When the entry was created.
Definition: rlm_cache.h:80
size_t len
Length of the raw string used to create the template.
Definition: tmpl.h:191
static rlm_rcode_t cache_insert(rlm_cache_t const *inst, REQUEST *request, rlm_cache_handle_t **handle, uint8_t const *key, size_t key_len, int ttl)
Create and insert a cache entry.
Definition: rlm_cache.c:289
CONF_SECTION * cf_section_sub_find(CONF_SECTION const *, char const *name)
Find a sub-section in a section.
Definition: conffile.c:3708
cache_entry_expire_t expire
Remove an old entry.
Definition: rlm_cache.h:287
int value_data_copy(TALLOC_CTX *ctx, value_data_t *dst, PW_TYPE type, const value_data_t *src)
Copy value data verbatim duplicating any buffers.
Definition: value.c:1479
char const * cf_section_name1(CONF_SECTION const *cs)
Definition: conffile.c:3592
vp_tmpl_t * tmpl_init(vp_tmpl_t *vpt, tmpl_type_t type, char const *name, ssize_t len, FR_TOKEN quote)
Initialise stack allocated vp_tmpl_t.
Definition: tmpl.c:497
Module succeeded without doing anything.
Definition: radiusd.h:96
cache_entry_count_t count
(Optional) Number of entries currently in the cache.
Definition: rlm_cache.h:289
#define RDEBUG2(fmt,...)
Definition: log.h:244
char name[1]
Attribute name.
Definition: dict.h:89
module_t rlm_cache
Definition: rlm_cache.c:1012
uint64_t magic
Used to validate module struct.
Definition: modules.h:135
Module failed, don't reply.
Definition: radiusd.h:90
void cf_log_err(CONF_ITEM const *ci, char const *fmt,...) CC_HINT(format(printf
#define TAG_ANY
Definition: pair.h:191
FR_TOKEN op
The operator that controls insertion of the dst attribute.
Definition: map.h:50
Cache entry found/updated.
Definition: rlm_cache.h:38
int modcall_fixup_update(vp_map_t *map, void *ctx)
struct timeval timestamp
When we started processing the request.
Definition: radiusd.h:214
#define FR_CONF_OFFSET(_n, _t, _s, _f)
Definition: conffile.h:168
VALUE_PAIR * fr_cursor_next(vp_cursor_t *cursor)
Advanced the cursor to the next VALUE_PAIR.
Definition: cursor.c:263
static rlm_rcode_t cache_set_ttl(rlm_cache_t const *inst, REQUEST *request, rlm_cache_handle_t **handle, rlm_cache_entry_t *c)
Update the TTL of an entry.
Definition: rlm_cache.c:472
char const * name
Name of xlat function to register.
Definition: rlm_cache.h:48
char * fr_asprint(TALLOC_CTX *ctx, char const *in, ssize_t inlen, char quote)
Escape string that may contain binary data, and write it to a new buffer.
Definition: print.c:390
CONF_SECTION * cs
Definition: rlm_cache.h:73
size_t tmpl_snprint(char *buffer, size_t bufsize, vp_tmpl_t const *vpt, fr_dict_attr_t const *values)
Print a vp_tmpl_t to a string.
Definition: tmpl.c:1822
char const * driver_name
Driver name.
Definition: rlm_cache.h:49
RADIUS_PACKET * packet
Incoming request.
Definition: radiusd.h:221
CONF_SECTION * cf_section_alloc(CONF_SECTION *parent, char const *name1, char const *name2)
Allocate a CONF_SECTION.
Definition: conffile.c:626
void * handle
Driver handle.
Definition: rlm_cache.h:67
void * driver_inst
Driver instance data.
Definition: rlm_cache.h:69
#define REDEBUG(fmt,...)
Definition: log.h:254
int map_to_vp(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t const *map, void *uctx) CC_HINT(nonnull(2
6 methods index for postproxy section.
Definition: modules.h:47
2 methods index for preacct section.
Definition: modules.h:43
#define PW_TYPE_REQUIRED
Error out if no matching CONF_PAIR is found, and no dflt value is set.
Definition: conffile.h:200
VALUE_PAIR * fr_pair_find_by_num(VALUE_PAIR *head, unsigned int vendor, unsigned int attr, int8_t tag)
Find the pair with the matching attribute.
Definition: pair.c:639
VALUE_PAIR * fr_cursor_remove(vp_cursor_t *cursor)
Remove the current pair.
Definition: cursor.c:433
static rlm_rcode_t cache_find(rlm_cache_entry_t **out, rlm_cache_t const *inst, REQUEST *request, rlm_cache_handle_t **handle, uint8_t const *key, size_t key_len)
Find a cached entry.
Definition: rlm_cache.c:182
cache_entry_find_t find
Retrieve an existing cache entry.
Definition: rlm_cache.h:285
cache_instantiate_t instantiate
(optional) Instantiate a driver.
Definition: rlm_cache.h:281
fr_dict_attr_t const * da
Dictionary attribute defines the attribute.
Definition: pair.h:113
static int mod_detach(void *instance)
Free any memory allocated under the instance.
Definition: rlm_cache.c:849
vp_map_t * maps
Head of the maps list.
Definition: rlm_cache.h:83
String of printable characters.
Definition: radius.h:33
char const * name
Driver name.
Definition: rlm_cache.h:279
FR_TOKEN quote
What type of quoting was around the raw string.
Definition: tmpl.h:192
#define RWDEBUG(fmt,...)
Definition: log.h:251
#define PW_TYPE_TMPL
CONF_PAIR should be parsed as a template.
Definition: conffile.h:208
PW_TYPE type
Value type.
Definition: dict.h:80
1 methods index for authorize section.
Definition: modules.h:42
User not found.
Definition: radiusd.h:95
size_t inst_size
How many bytes should be allocated for the driver's instance data.
Definition: rlm_cache.h:298
#define RCSID(id)
Definition: build.h:135
static int mod_instantiate(CONF_SECTION *conf, void *instance)
Create a new rlm_cache_instance.
Definition: rlm_cache.c:897
OK (pairs modified).
Definition: radiusd.h:97
uint8_t const * key
Key used to identify entry.
Definition: rlm_cache.h:77
Value pair map.
Definition: map.h:46
#define RDEBUG(fmt,...)
Definition: log.h:243
A source or sink of value data.
Definition: tmpl.h:187
char * value_data_asprint(TALLOC_CTX *ctx, PW_TYPE type, fr_dict_attr_t const *enumv, value_data_t const *data, char quote)
Print one attribute value to a string.
Definition: value.c:1543
static int mod_bootstrap(CONF_SECTION *conf, void *instance)
Register module xlats.
Definition: rlm_cache.c:877
CONF_ITEM * ci
Config item that the map was created from.
Definition: map.h:52
char const * cf_section_name2(CONF_SECTION const *cs)
Definition: conffile.c:3601
static ssize_t cache_xlat(char **out, UNUSED size_t freespace, void const *mod_inst, UNUSED void const *xlat_inst, REQUEST *request, char const *fmt) CC_HINT(nonnull)
Allow single attribute values to be retrieved from the cache.
Definition: rlm_cache.c:783
value_data_t data
Definition: pair.h:133
Configuration for the rlm_cache module.
Definition: rlm_cache.h:47