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