Restore v3.0.x behaviour in rlm_cache
[freeradius.git] / src / modules / rlm_cache / rlm_cache.c
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, version 2 if the
4  *   License as published by the Free Software Foundation.
5  *
6  *   This program is distributed in the hope that it will be useful,
7  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
8  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9  *   GNU General Public License for more details.
10  *
11  *   You should have received a copy of the GNU General Public License
12  *   along with this program; if not, write to the Free Software
13  *   Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
14  */
15
16 /**
17  * $Id$
18  * @file rlm_cache.c
19  * @brief Cache values and merge them back into future requests.
20  *
21  * @copyright 2012-2014 The FreeRADIUS server project
22  */
23 RCSID("$Id$")
24
25 #include <freeradius-devel/radiusd.h>
26 #include <freeradius-devel/modules.h>
27 #include <freeradius-devel/modpriv.h>
28 #include <freeradius-devel/modcall.h>
29 #include <freeradius-devel/rad_assert.h>
30
31 #include "rlm_cache.h"
32
33 /*
34  *      A mapping of configuration file names to internal variables.
35  *
36  *      Note that the string is dynamically allocated, so it MUST
37  *      be freed.  When the configuration file parse re-reads the string,
38  *      it free's the old one, and strdup's the new one, placing the pointer
39  *      to the strdup'd string into 'config.string'.  This gets around
40  *      buffer over-flows.
41  */
42 static const CONF_PARSER module_config[] = {
43         { "driver", FR_CONF_OFFSET(PW_TYPE_STRING, rlm_cache_t, driver_name), "rlm_cache_rbtree" },
44         { "key", FR_CONF_OFFSET(PW_TYPE_STRING | PW_TYPE_REQUIRED | PW_TYPE_XLAT, rlm_cache_t, key), NULL },
45         { "ttl", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_cache_t, ttl), "500" },
46         { "max_entries", FR_CONF_OFFSET(PW_TYPE_INTEGER, rlm_cache_t, max_entries), "0" },
47
48         /* Should be a type which matches time_t, @fixme before 2038 */
49         { "epoch", FR_CONF_OFFSET(PW_TYPE_SIGNED, rlm_cache_t, epoch), "0" },
50         { "add_stats", FR_CONF_OFFSET(PW_TYPE_BOOLEAN, rlm_cache_t, stats), "no" },
51
52         { NULL, -1, 0, NULL, NULL }             /* end the list */
53 };
54
55 static int cache_acquire(rlm_cache_handle_t **out, rlm_cache_t *inst, REQUEST *request)
56 {
57         if (!inst->module->acquire) return 0;
58
59         return inst->module->acquire(out, inst, request);
60 }
61
62 static void cache_release(rlm_cache_t *inst, REQUEST *request, rlm_cache_handle_t **handle)
63 {
64         if (!inst->module->release) return;
65         if (!*handle) return;
66
67         inst->module->release(inst, request, handle);
68 }
69
70 static int cache_reconnect(rlm_cache_t *inst, REQUEST *request, rlm_cache_handle_t **handle)
71 {
72         rad_assert(inst->module->reconnect);
73
74         return inst->module->reconnect(inst, request, handle);
75 }
76
77 /** Allocate a cache entry
78  *
79  *  This is used so that drivers may use their own allocation functions
80  *  to allocate structures larger than the normal rlm_cache_entry_t.
81  *
82  *  If the driver doesn't specify a custom allocation function, the cache
83  *  entry is talloced in the NULL ctx.
84  */
85 static rlm_cache_entry_t *cache_alloc(rlm_cache_t *inst, REQUEST *request)
86 {
87         if (inst->module->alloc) return inst->module->alloc(inst, request);
88
89         return talloc_zero(NULL, rlm_cache_entry_t);
90 }
91
92 /** Free memory associated with a cache entry
93  *
94  * This does not necessarily remove the entry from the cache, cache_expire
95  * should be used for that.
96  *
97  * This function should be called when an entry that is known to have been
98  * retrieved or inserted into a data store successfully, is no longer needed.
99  *
100  * Some drivers (like rlm_cache_rbtree) don't register a free function.
101  * This means that the cache entry never needs to be explicitly freed.
102  *
103  * @param c Cache entry to free.
104  * @param inst Module instance.
105  */
106 static void cache_free(rlm_cache_t *inst, rlm_cache_entry_t **c)
107 {
108         if (!*c || !inst->module->free) return;
109
110         inst->module->free(*c);
111         *c = NULL;
112 }
113
114 /*
115  *      Merge a cached entry into a REQUEST.
116  */
117 static void CC_HINT(nonnull) cache_merge(rlm_cache_t *inst, REQUEST *request, rlm_cache_entry_t *c)
118 {
119         VALUE_PAIR *vp;
120
121         vp = pairfind(request->config_items, PW_CACHE_MERGE, 0, TAG_ANY);
122         if (vp && (vp->vp_integer == 0)) {
123                 RDEBUG2("Told not to merge entry into request");
124                 return;
125         }
126
127         RDEBUG2("Merging cache entry into request");
128
129         if (c->control) {
130                 rdebug_pair_list(L_DBG_LVL_2, request, c->control, "&control:");
131                 radius_pairmove(request, &request->config_items, paircopy(request, c->control), false);
132         }
133
134         if (c->packet && request->packet) {
135                 rdebug_pair_list(L_DBG_LVL_2, request, c->packet, "&request:");
136                 radius_pairmove(request, &request->packet->vps, paircopy(request->packet, c->packet), false);
137         }
138
139         if (c->reply && request->reply) {
140                 rdebug_pair_list(L_DBG_LVL_2, request, c->reply, "&reply:");
141                 radius_pairmove(request, &request->reply->vps, paircopy(request->reply, c->reply), false);
142         }
143
144         if (inst->stats) {
145                 rad_assert(request->packet != NULL);
146                 vp = pairfind(request->packet->vps, PW_CACHE_ENTRY_HITS, 0, TAG_ANY);
147                 if (!vp) {
148                         vp = paircreate(request->packet, PW_CACHE_ENTRY_HITS, 0);
149                         rad_assert(vp != NULL);
150                         pairadd(&request->packet->vps, vp);
151                 }
152                 vp->vp_integer = c->hits;
153         }
154 }
155
156
157 /** Find a cached entry.
158  *
159  * @return RLM_MODULE_OK on success, RLM_MODULE_FAIL on failure, RLM_MODULE_NOTFOUND if notfound.
160  */
161 static rlm_rcode_t cache_find(rlm_cache_entry_t **out, rlm_cache_t *inst, REQUEST *request,
162                               rlm_cache_handle_t **handle, char const *key)
163 {
164         cache_status_t ret;
165
166         rlm_cache_entry_t *c;
167
168         *out = NULL;
169
170         for (;;) {
171                 ret = inst->module->find(&c, inst, request, handle, key);
172                 switch (ret) {
173                 case CACHE_RECONNECT:
174                         RDEBUG("Reconnecting...");
175                         if (cache_reconnect(inst, request, handle) == 0) continue;
176                         return RLM_MODULE_FAIL;
177
178                 case CACHE_OK:
179                         break;
180
181                 case CACHE_MISS:
182                         RDEBUG("No cache entry found for \"%s\"", key);
183                         return RLM_MODULE_NOTFOUND;
184
185                 /* FALL-THROUGH */
186                 default:
187                         return RLM_MODULE_FAIL;
188
189                 }
190
191                 break;
192         }
193
194         /*
195          *      Yes, but it expired, OR the "forget all" epoch has
196          *      passed.  Delete it, and pretend it doesn't exist.
197          */
198         if ((c->expires < request->timestamp) || (c->created < inst->epoch)) {
199                 RDEBUG("Removing expired entry");
200
201                 inst->module->expire(inst, request, handle, c);
202                 cache_free(inst, &c);
203                 return RLM_MODULE_NOTFOUND;     /* Couldn't find a non-expired entry */
204         }
205
206         RDEBUG("Found entry for \"%s\"", key);
207
208         c->hits++;
209         *out = c;
210
211         return RLM_MODULE_OK;
212 }
213
214 /** Expire a cache entry (removing it from the datastore)
215  *
216  */
217 static void cache_expire(rlm_cache_t *inst, REQUEST *request, rlm_cache_handle_t **handle, rlm_cache_entry_t **c)
218 {
219         rad_assert(*c);
220
221         for (;;) switch (inst->module->expire(inst, request, handle, *c)) {
222         case CACHE_RECONNECT:
223                 if (cache_reconnect(inst, request, handle) == 0) continue;
224
225         /* FALL-THROUGH */
226         default:
227                 cache_free(inst, c);
228                 *c = NULL;
229                 return;
230         }
231 }
232
233 /** Create and insert a cache entry.
234  *
235  * @return RLM_MODULE_OK on success, RLM_MODULE_UPDATED if we merged the cache entry and RLM_MODULE_FAIL on failure.
236  */
237 static rlm_rcode_t cache_insert(rlm_cache_t *inst, REQUEST *request, rlm_cache_handle_t **handle,
238                                 char const *key, int ttl)
239 {
240         VALUE_PAIR *vp, *to_cache;
241         vp_cursor_t src_list, cached_request, cached_reply, cached_control;
242
243         value_pair_map_t const *map;
244
245         bool merge = true;
246         rlm_cache_entry_t *c;
247
248         if ((inst->max_entries > 0) && inst->module->count &&
249             (inst->module->count(inst, request, handle) > inst->max_entries)) {
250                 RWDEBUG("Cache is full: %d entries", inst->max_entries);
251                 return RLM_MODULE_FAIL;
252         }
253
254         c = cache_alloc(inst, request);
255         if (!c) return RLM_MODULE_FAIL;
256
257         c->key = talloc_typed_strdup(c, key);
258         c->created = c->expires = request->timestamp;
259         c->expires += ttl;
260
261         RDEBUG("Creating new cache entry");
262
263         fr_cursor_init(&cached_request, &c->packet);
264         fr_cursor_init(&cached_reply, &c->reply);
265         fr_cursor_init(&cached_control, &c->control);
266
267         for (map = inst->maps; map != NULL; map = map->next) {
268                 rad_assert(map->lhs && map->rhs);
269
270                 if (map_to_vp(&to_cache, request, map, NULL) < 0) {
271                         RDEBUG("Skipping %s", map->rhs->name);
272                         continue;
273                 }
274
275                 /*
276                  *      Reparent the VPs map_to_vp may return multiple.
277                  */
278                 for (vp = fr_cursor_init(&src_list, &to_cache);
279                      vp;
280                      vp = fr_cursor_next(&src_list)) {
281                         VERIFY_VP(vp);
282
283                         /*
284                          *      Prevent people from accidentally caching
285                          *      cache control attributes.
286                          */
287                         if (map->rhs->type == TMPL_TYPE_LIST) switch (vp->da->attr) {
288                         case PW_CACHE_TTL:
289                         case PW_CACHE_STATUS_ONLY:
290                         case PW_CACHE_READ_ONLY:
291                         case PW_CACHE_MERGE:
292                         case PW_CACHE_ENTRY_HITS:
293                                 RDEBUG2("Skipping %s", vp->da->name);
294                                 continue;
295
296                         default:
297                                 break;
298                         }
299
300                         RINDENT();
301                         if (RDEBUG_ENABLED2) map_debug_log(request, map, vp);
302                         REXDENT();
303                         (void) talloc_steal(c, vp);
304
305                         vp->op = map->op;
306
307                         switch (map->lhs->tmpl_list) {
308                         case PAIR_LIST_REQUEST:
309                                 fr_cursor_insert(&cached_request, vp);
310                                 break;
311
312                         case PAIR_LIST_REPLY:
313                                 fr_cursor_insert(&cached_reply, vp);
314                                 break;
315
316                         case PAIR_LIST_CONTROL:
317                                 fr_cursor_insert(&cached_control, vp);
318                                 break;
319
320                         default:
321                                 rad_assert(0);  /* should have been caught by validation */
322                         }
323                 }
324         }
325
326         /*
327          *      Check to see if we need to merge the entry into the request
328          */
329         vp = pairfind(request->config_items, PW_CACHE_MERGE, 0, TAG_ANY);
330         if (vp && (vp->vp_integer == 0)) merge = false;
331
332         if (merge) cache_merge(inst, request, c);
333
334         for (;;) {
335                 cache_status_t ret;
336
337                 ret = inst->module->insert(inst, request, handle, c);
338                 switch (ret) {
339                 case CACHE_RECONNECT:
340                         if (cache_reconnect(inst, request, handle) == 0) continue;
341                         return RLM_MODULE_FAIL;
342
343                 case CACHE_OK:
344                         RDEBUG("Commited entry, TTL %d seconds", ttl);
345                         cache_free(inst, &c);
346                         return RLM_MODULE_UPDATED;
347
348                 default:
349                         talloc_free(c); /* Failed insertion - use talloc_free not the driver free */
350                         return RLM_MODULE_FAIL;
351                 }
352         }
353 }
354
355 /** Verify that a map in the cache section makes sense
356  *
357  */
358 static int cache_verify(value_pair_map_t *map, UNUSED void *ctx)
359 {
360         if (modcall_fixup_update(map, ctx) < 0) return -1;
361
362         if ((map->lhs->type != TMPL_TYPE_ATTR) &&
363             (map->lhs->type != TMPL_TYPE_LIST)) {
364                 cf_log_err(map->ci, "Left operand must be an attribute ref or a list");
365                 return -1;
366         }
367
368         switch (map->rhs->type) {
369         case TMPL_TYPE_EXEC:
370                 cf_log_err(map->ci, "Exec values are not allowed");
371                 return -1;
372         /*
373          *      Only =, :=, += and -= operators are supported for
374          *      cache entries.
375          */
376         case TMPL_TYPE_LITERAL:
377         case TMPL_TYPE_XLAT:
378         case TMPL_TYPE_ATTR:
379                 switch (map->op) {
380                 case T_OP_SET:
381                 case T_OP_EQ:
382                 case T_OP_SUB:
383                 case T_OP_ADD:
384                         break;
385
386                 default:
387                         cf_log_err(map->ci, "Operator \"%s\" not allowed for %s values",
388                                    fr_int2str(fr_tokens, map->op, "<INVALID>"),
389                                    fr_int2str(tmpl_types, map->rhs->type, "<INVALID>"));
390                         return -1;
391                 }
392         default:
393                 break;
394         }
395
396         return 0;
397 }
398
399 /*
400  *      Do caching checks.  Since we can update ANY VP list, we do
401  *      exactly the same thing for all sections (autz / auth / etc.)
402  *
403  *      If you want to cache something different in different sections,
404  *      configure another cache module.
405  */
406 static rlm_rcode_t CC_HINT(nonnull) mod_cache_it(void *instance, REQUEST *request)
407 {
408         rlm_cache_entry_t *c;
409         rlm_cache_t *inst = instance;
410
411         rlm_cache_handle_t *handle;
412
413         vp_cursor_t cursor;
414         VALUE_PAIR *vp;
415         char buffer[1024];
416         rlm_rcode_t rcode;
417
418         int ttl = inst->ttl;
419
420         if (radius_xlat(buffer, sizeof(buffer), request, inst->key, NULL, NULL) < 0) return RLM_MODULE_FAIL;
421
422         if (buffer[0] == '\0') {
423                 REDEBUG("Zero length key string is invalid");
424                 return RLM_MODULE_INVALID;
425         }
426
427         if (cache_acquire(&handle, inst, request) < 0) return RLM_MODULE_FAIL;
428
429         rcode = cache_find(&c, inst, request, &handle, buffer);
430         if (rcode == RLM_MODULE_FAIL) goto finish;
431         rad_assert(handle);
432
433         /*
434          *      If Cache-Status-Only == yes, only return whether we found a
435          *      valid cache entry
436          */
437         vp = pairfind(request->config_items, PW_CACHE_STATUS_ONLY, 0, TAG_ANY);
438         if (vp && vp->vp_integer) {
439                 rcode = c ? RLM_MODULE_OK:
440                             RLM_MODULE_NOTFOUND;
441                 goto finish;
442         }
443
444         /*
445          *      Update the expiry time based on the TTL.
446          *      A TTL of 0 means "delete from the cache".
447          *      A TTL < 0 means "delete from the cache and recreate the entry".
448          */
449         vp = pairfind(request->config_items, PW_CACHE_TTL, 0, TAG_ANY);
450         if (vp) ttl = vp->vp_signed;
451
452         /*
453          *      If there's no existing cache entry, go and create a new one.
454          */
455         if (!c) {
456                 if (ttl <= 0) ttl = inst->ttl;
457                 goto insert;
458         }
459
460         /*
461          *      Expire the entry if requested to do so
462          */
463         if (vp) {
464                 if (ttl == 0) {
465                         cache_expire(inst, request, &handle, &c);
466                         RDEBUG("Forcing expiry of entry");
467                         rcode = RLM_MODULE_OK;
468                         goto finish;
469                 }
470
471                 if (ttl < 0) {
472                         RDEBUG("Forcing expiry of existing entry");
473                         cache_expire(inst, request, &handle, &c);
474                         ttl *= -1;
475                         goto insert;
476                 }
477                 c->expires = request->timestamp + ttl;
478                 RDEBUG("Setting TTL to %d", ttl);
479         }
480
481         /*
482          *      Cache entry was still valid, so we merge it into the request
483          *      and return. No need to add a new entry.
484          */
485         cache_merge(inst, request, c);
486         rcode = RLM_MODULE_OK;
487
488         goto finish;
489
490 insert:
491         /*
492          *      If Cache-Read-Only == yes, then we only allow already cached entries
493          *      to be merged into the request
494          */
495         vp = pairfind(request->config_items, PW_CACHE_READ_ONLY, 0, TAG_ANY);
496         if (vp && vp->vp_integer) {
497                 rcode = RLM_MODULE_NOTFOUND;
498                 goto finish;
499         }
500
501         /*
502          *      Create a new entry.
503          */
504         rcode = cache_insert(inst, request, &handle, buffer, ttl);
505         rad_assert(handle);
506
507 finish:
508         cache_free(inst, &c);
509         cache_release(inst, request, &handle);
510
511         /*
512          *      Clear control attributes
513          */
514         for (vp = fr_cursor_init(&cursor, &request->config_items);
515              vp;
516              vp = fr_cursor_next(&cursor)) {
517                 if (vp->da->vendor == 0) switch (vp->da->attr) {
518                 case PW_CACHE_TTL:
519                 case PW_CACHE_STATUS_ONLY:
520                 case PW_CACHE_READ_ONLY:
521                 case PW_CACHE_MERGE:
522                         vp = fr_cursor_remove(&cursor);
523                         talloc_free(vp);
524                         break;
525                 }
526         }
527
528         return rcode;
529 }
530
531 /*
532  *      Allow single attribute values to be retrieved from the cache.
533  */
534 static ssize_t cache_xlat(void *instance, REQUEST *request,
535                           char const *fmt, char *out, size_t freespace)
536 {
537         rlm_cache_entry_t       *c;
538         rlm_cache_t             *inst = instance;
539         rlm_cache_handle_t      *handle;
540
541         VALUE_PAIR              *vp, *vps;
542         pair_lists_t            list;
543         DICT_ATTR const         *target;
544         char const              *p = fmt;
545         size_t                  len;
546         int                     ret = 0;
547
548         p += radius_list_name(&list, p, PAIR_LIST_REQUEST);
549         if (list == PAIR_LIST_UNKNOWN) {
550                 REDEBUG("Unknown list qualifier in \"%s\"", fmt);
551                 ret = -1;
552                 goto finish;
553         }
554
555         target = dict_attrbyname(p);
556         if (!target) {
557                 REDEBUG("Unknown attribute \"%s\"", p);
558                 return -1;
559         }
560
561         if (cache_acquire(&handle, inst, request) < 0) return -1;
562
563         switch (cache_find(&c, inst, request, handle, fmt)) {
564         case RLM_MODULE_OK:             /* found */
565                 break;
566
567         case RLM_MODULE_NOTFOUND:       /* not found */
568                 *out = '\0';
569                 return 0;
570
571         default:
572                 return -1;
573         }
574
575         switch (list) {
576         case PAIR_LIST_REQUEST:
577                 vps = c->packet;
578                 break;
579
580         case PAIR_LIST_REPLY:
581                 vps = c->reply;
582                 break;
583
584         case PAIR_LIST_CONTROL:
585                 vps = c->control;
586                 break;
587
588         case PAIR_LIST_UNKNOWN:
589
590         default:
591                 REDEBUG("Unsupported list \"%s\"", fr_int2str(pair_lists, list, "<UNKNOWN>"));
592                 ret = -1;
593                 goto finish;
594         }
595
596         vp = pairfind(vps, target->attr, target->vendor, TAG_ANY);
597         if (!vp) {
598                 RDEBUG("No instance of this attribute has been cached");
599                 *out = '\0';
600                 goto finish;
601         }
602
603         len = vp_prints_value(out, freespace, vp, 0);
604         if (is_truncated(len, freespace)) {
605                 REDEBUG("Insufficient buffer space to write cached value");
606                 ret = -1;
607                 goto finish;
608         }
609
610 finish:
611         cache_free(inst, &c);
612         cache_release(inst, request, &handle);
613
614         return ret;
615 }
616
617 /*
618  *      Only free memory we allocated.  The strings allocated via
619  *      cf_section_parse() do not need to be freed.
620  */
621 static int mod_detach(void *instance)
622 {
623         rlm_cache_t *inst = instance;
624
625         talloc_free(inst->maps);
626
627         /*
628          *  We need to explicitly free all children, so if the driver
629          *  parented any memory off the instance, their destructors
630          *  run before we unload the bytecode for them.
631          *
632          *  If we don't do this, we get a SEGV deep inside the talloc code
633          *  when it tries to call a destructor that no longer exists.
634          */
635         talloc_free_children(inst);
636
637         /*
638          *  Decrements the reference count. The driver object won't be unloaded
639          *  until all instances of rlm_cache that use it have been destroyed.
640          */
641         if (inst->handle) dlclose(inst->handle);
642
643         return 0;
644 }
645
646 /*
647  *      Instantiate the module.
648  */
649 static int mod_instantiate(CONF_SECTION *conf, void *instance)
650 {
651         rlm_cache_t *inst = instance;
652
653         inst->cs = conf;
654
655         inst->xlat_name = cf_section_name2(conf);
656         if (!inst->xlat_name) inst->xlat_name = cf_section_name1(conf);
657
658         /*
659          *      Register the cache xlat function
660          */
661         xlat_register(inst->xlat_name, cache_xlat, NULL, inst);
662
663         /*
664          *      Sanity check for crazy people.
665          */
666         if (strncmp(inst->driver_name, "rlm_cache_", 8) != 0) {
667                 ERROR("rlm_cache (%s): \"%s\" is NOT an Cache driver!", inst->xlat_name, inst->driver_name);
668                 return -1;
669         }
670
671         /*
672          *      Load the appropriate driver for our database
673          */
674         inst->handle = lt_dlopenext(inst->driver_name);
675         if (!inst->handle) {
676                 ERROR("rlm_cache (%s): Could not link driver %s: %s", inst->xlat_name, inst->driver_name, dlerror());
677                 ERROR("rlm_cache (%s): Make sure it (and all its dependent libraries!) are in the search path"
678                       "of your system's ld", inst->xlat_name);
679                 return -1;
680         }
681
682         inst->module = (cache_module_t *) dlsym(inst->handle, inst->driver_name);
683         if (!inst->module) {
684                 ERROR("rlm_cache (%s): Could not link symbol %s: %s", inst->xlat_name, inst->driver_name, dlerror());
685                 return -1;
686         }
687
688         INFO("rlm_cahe (%s): Driver %s (module %s) loaded and linked", inst->xlat_name,
689              inst->driver_name, inst->module->name);
690
691         /*
692          *      Non optional fields and callbacks
693          */
694         rad_assert(inst->module->name);
695         rad_assert(inst->module->find);
696         rad_assert(inst->module->insert);
697         rad_assert(inst->module->expire);
698
699         if (inst->module->mod_instantiate) {
700                 CONF_SECTION *cs;
701                 char const *name;
702
703                 name = strrchr(inst->driver_name, '_');
704                 if (!name) {
705                         name = inst->driver_name;
706                 } else {
707                         name++;
708                 }
709
710                 cs = cf_section_sub_find(conf, name);
711                 if (!cs) {
712                         cs = cf_section_alloc(conf, name, NULL);
713                         if (!cs) return -1;
714                 }
715
716                 /*
717                  *      It's up to the driver to register a destructor (using talloc)
718                  *
719                  *      Should write its instance data in inst->driver,
720                  *      and parent it off of inst.
721                  */
722                 if (inst->module->mod_instantiate(cs, inst) < 0) return -1;
723         }
724
725         rad_assert(inst->key && *inst->key);
726
727         if (inst->ttl == 0) {
728                 cf_log_err_cs(conf, "Must set 'ttl' to non-zero");
729                 return -1;
730         }
731
732         if (inst->epoch != 0) {
733                 cf_log_err_cs(conf, "Must not set 'epoch' in the configuration files");
734                 return -1;
735         }
736
737         /*
738          *      Make sure the users don't screw up too badly.
739          */
740         if (map_afrom_cs(&inst->maps, cf_section_sub_find(inst->cs, "update"),
741                          PAIR_LIST_REQUEST, PAIR_LIST_REQUEST, cache_verify, NULL, MAX_ATTRMAP) < 0) {
742                 return -1;
743         }
744
745         if (!inst->maps) {
746                 cf_log_err_cs(inst->cs, "Cache config must contain an update section, and "
747                               "that section must not be empty");
748
749                 return -1;
750         }
751         return 0;
752 }
753
754 /*
755  *      The module name should be the only globally exported symbol.
756  *      That is, everything else should be 'static'.
757  *
758  *      If the module needs to temporarily modify it's instantiation
759  *      data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
760  *      The server will then take care of ensuring that the module
761  *      is single-threaded.
762  */
763 module_t rlm_cache = {
764         RLM_MODULE_INIT,
765         "cache",
766         0,                              /* type */
767         sizeof(rlm_cache_t),
768         module_config,
769         mod_instantiate,                /* instantiation */
770         mod_detach,                     /* detach */
771         {
772                 NULL,                   /* authentication */
773                 mod_cache_it,           /* authorization */
774                 mod_cache_it,           /* preaccounting */
775                 mod_cache_it,           /* accounting */
776                 NULL,                   /* checksimul */
777                 mod_cache_it,           /* pre-proxy */
778                 mod_cache_it,           /* post-proxy */
779                 mod_cache_it,           /* post-auth */
780         },
781 };