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