The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
rlm_cache_redis.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: d5ad02d98725df5611c7d6fdc5b7d7727500418b $
19  * @file rlm_cache_redis.c
20  * @brief redis based cache.
21  *
22  * @copyright 2015 Arran Cudbard-Bell (a.cudbardb@freeradius.org)
23  */
24 #define LOG_PREFIX "cache - redis"
25 
26 #include <freeradius-devel/server/base.h>
27 #include <freeradius-devel/util/debug.h>
28 #include <freeradius-devel/util/value.h>
29 
30 #include "../../rlm_cache.h"
31 #include <freeradius-devel/redis/base.h>
32 #include <freeradius-devel/redis/cluster.h>
36 };
37 
38 typedef struct {
39  fr_redis_conf_t conf; //!< Connection parameters for the Redis server.
40  //!< Must be first field in this struct.
41 
42  tmpl_t *created_attr; //!< LHS of the Cache-Created map.
43  tmpl_t *expires_attr; //!< LHS of the Cache-Expires map.
44 
47 
48 static fr_dict_t const *dict_freeradius;
49 
52  { .out = &dict_freeradius, .proto = "freeradius" },
53  { NULL }
54 };
55 
58 
61  { .out = &attr_cache_created, .name = "Cache-Created", .type = FR_TYPE_DATE, .dict = &dict_freeradius },
62  { .out = &attr_cache_expires, .name = "Cache-Expires", .type = FR_TYPE_DATE, .dict = &dict_freeradius },
63  { NULL }
64 };
65 
66 /** Create a new rlm_cache_redis instance
67  *
68  * @param[in] mctx Data required for instantiation.
69  * @return
70  * - 0 on success.
71  * - -1 on failure.
72  */
73 static int mod_instantiate(module_inst_ctx_t const *mctx)
74 {
75  rlm_cache_redis_t *driver = talloc_get_type_abort(mctx->mi->data, rlm_cache_redis_t);
76  char buffer[256];
77 
78  buffer[0] = '\0';
79 
80  if (cf_section_rules_push(mctx->mi->conf, driver_config) < 0) return -1;
81  if (cf_section_parse(driver, driver, mctx->mi->conf) < 0) return -1;
82 
83  snprintf(buffer, sizeof(buffer), "rlm_cache (%s)", mctx->mi->parent->name);
84 
85  driver->cluster = fr_redis_cluster_alloc(driver, mctx->mi->conf, &driver->conf, true,
86  buffer, "modules.cache.pool", NULL);
87  if (!driver->cluster) {
88  ERROR("Cluster failure");
89  return -1;
90  }
91 
92  /*
93  * These never change, so do it once on instantiation
94  */
95  if (tmpl_afrom_attr_str(driver, NULL, &driver->created_attr, "&Cache-Created", NULL) <= 0) {
96  ERROR("Cache-Created attribute not defined");
97  return -1;
98  }
99 
100  if (tmpl_afrom_attr_str(driver, NULL, &driver->expires_attr, "&Cache-Expires", NULL) <= 0) {
101  ERROR("Cache-Expires attribute not defined");
102  return -1;
103  }
104 
105  return 0;
106 }
107 
108 static int mod_load(void)
109 {
111  return 0;
112 }
113 
115 {
116  talloc_free(c);
117 }
118 
119 /** Locate a cache entry in redis
120  *
121  * @copydetails cache_entry_find_t
122  */
124  UNUSED rlm_cache_config_t const *config, void *instance,
125  request_t *request, UNUSED void *handle, fr_value_box_t const *key)
126 {
127  rlm_cache_redis_t *driver = instance;
128  size_t i;
129 
131  fr_redis_conn_t *conn;
132  fr_redis_rcode_t status;
133  redisReply *reply = NULL;
134  int s_ret;
135 
136  map_list_t head;
137 #ifdef HAVE_TALLOC_ZERO_POOLED_OBJECT
138  size_t pool_size = 0;
139 #endif
141 
142  map_list_init(&head);
143  for (s_ret = fr_redis_cluster_state_init(&state, &conn, driver->cluster, request, (uint8_t const *)key->vb_strvalue, key->vb_length, false);
144  s_ret == REDIS_RCODE_TRY_AGAIN; /* Continue */
145  s_ret = fr_redis_cluster_state_next(&state, &conn, driver->cluster, request, status, &reply)) {
146  /*
147  * Grab all the data for this hash, should return an array
148  * of alternating keys/values which we then convert into maps.
149  */
150  RDEBUG3("LRANGE %pV 0 -1", key);
151  reply = redisCommand(conn->handle, "LRANGE %b 0 -1", key->vb_strvalue, key->vb_length);
152  status = fr_redis_command_status(conn, reply);
153  }
154  if (s_ret != REDIS_RCODE_SUCCESS) {
155  RERROR("Failed retrieving entry for key \"%pV\"", key);
156 
157  error:
158  fr_redis_reply_free(&reply);
159  return CACHE_ERROR;
160  }
161 
162  if (!fr_cond_assert(reply)) goto error;
163 
164  if (reply->type != REDIS_REPLY_ARRAY) {
165  REDEBUG("Bad result type, expected array, got %s",
166  fr_table_str_by_value(redis_reply_types, reply->type, "<UNKNOWN>"));
167  goto error;
168  }
169 
170  RDEBUG3("Entry contains %zu elements", reply->elements);
171 
172  if (reply->elements == 0) {
173  fr_redis_reply_free(&reply);
174  return CACHE_MISS;
175  }
176 
177  if (reply->elements % 3) {
178  REDEBUG("Invalid number of reply elements (%zu). "
179  "Reply must contain triplets of keys operators and values",
180  reply->elements);
181  goto error;
182  }
183 
184 #ifdef HAVE_TALLOC_ZERO_POOLED_OBJECT
185  /*
186  * We can get a pretty good idea of the required size of the pool
187  */
188  for (i = 0; i < reply->elements; i += 3) {
189  pool_size += sizeof(map_t) + (sizeof(tmpl_t) * 2);
190  if (reply->element[i]->type == REDIS_REPLY_STRING) pool_size += reply->element[i]->len + 1;
191  }
192 
193  /*
194  * reply->elements gives us the number of chunks, as the maps are triplets, and there
195  * are three chunks per map
196  */
197 
198  c = talloc_zero_pooled_object(NULL, rlm_cache_entry_t, reply->elements, pool_size);
199 #else
200  c = talloc_zero(NULL, rlm_cache_entry_t);
201 #endif
202  map_list_init(&c->maps);
203  /*
204  * Convert the key/value pairs back into maps
205  */
206  for (i = 0; i < reply->elements; i += 3) {
207  if (fr_redis_reply_to_map(c, &head, request,
208  reply->element[i], reply->element[i + 1], reply->element[i + 2]) < 0) {
209  talloc_free(c);
210  fr_redis_reply_free(&reply);
211  return CACHE_ERROR;
212  }
213  }
214  fr_redis_reply_free(&reply);
215 
216  /*
217  * Pull out the cache created date
218  */
219  if (tmpl_attr_tail_da(map_list_head(&head)->lhs) == attr_cache_created) {
220  map_t *map;
221 
222  c->created = tmpl_value(map_list_head(&head)->rhs)->vb_date;
223 
224  map = map_list_pop_head(&head);
225  talloc_free(map);
226  }
227 
228  /*
229  * Pull out the cache expires date
230  */
231  if (tmpl_attr_tail_da(map_list_head(&head)->lhs) == attr_cache_expires) {
232  map_t *map;
233 
234  c->expires = tmpl_value(map_list_head(&head)->rhs)->vb_date;
235 
236  map = map_list_pop_head(&head);
237  talloc_free(map);
238  }
239 
240  if (unlikely(fr_value_box_copy(c, &c->key, key) < 0)) goto error;
241 
242  map_list_move(&c->maps, &head);
243  *out = c;
244 
245  return CACHE_OK;
246 }
247 
248 
249 /** Insert a new entry into the data store
250  *
251  * @copydetails cache_entry_insert_t
252  */
254  request_t *request, UNUSED void *handle, const rlm_cache_entry_t *c)
255 {
256  rlm_cache_redis_t *driver = instance;
257  TALLOC_CTX *pool;
258 
259  map_t *map = NULL;
260 
261  fr_redis_conn_t *conn;
263  fr_redis_rcode_t status;
264  redisReply *reply = NULL;
265  int s_ret;
266 
267  static char const command[] = "RPUSH";
268  char const **argv;
269  size_t *argv_len;
270  char const **argv_p;
271  size_t *argv_len_p;
272 
273  unsigned int pipelined = 0; /* How many commands pending in the pipeline */
274  redisReply *replies[5]; /* Should have the same number of elements as pipelined commands */
275  size_t reply_cnt = 0, i;
276 
277  int cnt;
278 
279  tmpl_t expires_value;
280  map_t expires = {
281  .op = T_OP_SET,
282  .lhs = driver->expires_attr,
283  .rhs = &expires_value,
284  };
285 
286  tmpl_t created_value;
287  map_t created = {
288  .op = T_OP_SET,
289  .lhs = driver->created_attr,
290  .rhs = &created_value,
291  };
292 
293  /*
294  * Encode the entry created date
295  */
296  tmpl_init_shallow(&created_value, TMPL_TYPE_DATA, T_BARE_WORD, "<TEMP>", 6, NULL);
297  fr_value_box_init(&created_value.data.literal, FR_TYPE_DATE, NULL, true);
298  tmpl_value(&created_value)->vb_date = c->created;
299 
300  /*
301  * Encode the entry expiry time
302  *
303  * Although Redis objects expire on their own, we still need this
304  * to ignore entries that were created before the last epoch.
305  */
306  tmpl_init_shallow(&expires_value, TMPL_TYPE_DATA, T_BARE_WORD, "<TEMP>", 6, NULL);
307  fr_value_box_init(&expires_value.data.literal, FR_TYPE_DATE, NULL, true);
308  tmpl_value(&expires_value)->vb_date = c->expires;
309 
310  cnt = map_list_num_elements(&c->maps) + 2;
311 
312  /*
313  * The majority of serialized entries should be under 1k.
314  *
315  * @todo We should really calculate this using some sort of moving average.
316  */
317  pool = talloc_pool(request, 1024);
318  if (!pool) return CACHE_ERROR;
319 
320  argv_p = argv = talloc_array(pool, char const *, (cnt * 3) + 2); /* pair = 3 + cmd + key */
321  argv_len_p = argv_len = talloc_array(pool, size_t, (cnt * 3) + 2); /* pair = 3 + cmd + key */
322 
323  *argv_p++ = command;
324  *argv_len_p++ = sizeof(command) - 1;
325 
326  *argv_p++ = (char const *)c->key.vb_strvalue;
327  *argv_len_p++ = c->key.vb_length;
328 
329  /*
330  * Add the maps to the command string in reverse order
331  */
332  if (fr_redis_tuple_from_map(pool, argv_p, argv_len_p, &created) < 0) {
333  REDEBUG("Failed encoding map as Redis K/V pair");
334  talloc_free(pool);
335  return CACHE_ERROR;
336  }
337  argv_p += 3;
338  argv_len_p += 3;
339  if (fr_redis_tuple_from_map(pool, argv_p, argv_len_p, &expires) < 0) {
340  REDEBUG("Failed encoding map as Redis K/V pair");
341  talloc_free(pool);
342  return CACHE_ERROR;
343  }
344  argv_p += 3;
345  argv_len_p += 3;
346  while ((map = map_list_next(&c->maps, map))) {
347  if (fr_redis_tuple_from_map(pool, argv_p, argv_len_p, map) < 0) {
348  REDEBUG("Failed encoding map as Redis K/V pair");
349  talloc_free(pool);
350  return CACHE_ERROR;
351  }
352  argv_p += 3;
353  argv_len_p += 3;
354  }
355 
356  RDEBUG3("Pipelining commands");
357 
358  for (s_ret = fr_redis_cluster_state_init(&state, &conn, driver->cluster, request, (uint8_t const *)c->key.vb_strvalue, c->key.vb_length, false);
359  s_ret == REDIS_RCODE_TRY_AGAIN; /* Continue */
360  s_ret = fr_redis_cluster_state_next(&state, &conn, driver->cluster, request, status, &reply)) {
361  /*
362  * Start the transaction, as we need to set an expiry time too.
363  */
364  if (fr_unix_time_ispos(c->expires)) {
365  RDEBUG3("MULTI");
366  if (redisAppendCommand(conn->handle, "MULTI") != REDIS_OK) {
367  append_error:
368  REXDENT();
369  RERROR("Failed appending Redis command to output buffer: %s", conn->handle->errstr);
370  talloc_free(pool);
371  return CACHE_ERROR;
372  }
373  pipelined++;
374  }
375 
376  RDEBUG3("DEL \"%pV\"", &c->key);
377 
378  if (redisAppendCommand(conn->handle, "DEL %b", (uint8_t const *)c->key.vb_strvalue, c->key.vb_length) != REDIS_OK) goto append_error;
379  pipelined++;
380 
381  if (RDEBUG_ENABLED3) {
382  RDEBUG3("argv command");
383  RINDENT();
384  for (i = 0; i < talloc_array_length(argv); i++) {
385  RDEBUG3("%pV", fr_box_strvalue_len(argv[i], argv_len[i]));
386  }
387  REXDENT();
388  }
389  redisAppendCommandArgv(conn->handle, talloc_array_length(argv), argv, argv_len);
390  pipelined++;
391 
392  /*
393  * Set the expiry time and close out the transaction.
394  */
395  if (fr_unix_time_ispos(c->expires)) {
396  RDEBUG3("EXPIREAT \"%pV\" %" PRIu64,
397  &c->key,
399  if (redisAppendCommand(conn->handle, "EXPIREAT %b %" PRIu64, (uint8_t const *)c->key.vb_strvalue, (size_t)c->key.vb_length,
400  fr_unix_time_to_sec(c->expires)) != REDIS_OK) goto append_error;
401  pipelined++;
402  RDEBUG3("EXEC");
403  if (redisAppendCommand(conn->handle, "EXEC") != REDIS_OK) goto append_error;
404  pipelined++;
405  }
406 
407  reply_cnt = fr_redis_pipeline_result(&pipelined, &status,
408  replies, NUM_ELEMENTS(replies),
409  conn);
410  reply = replies[0];
411  }
412  talloc_free(pool);
413 
414  if (s_ret != REDIS_RCODE_SUCCESS) {
415  RPERROR("Failed inserting entry");
416  return CACHE_ERROR;
417  }
418 
419  RDEBUG3("Command results");
420  RINDENT();
421  if (RDEBUG_ENABLED3) for (i = 0; i < reply_cnt; i++) fr_redis_reply_print(L_DBG_LVL_3, replies[i], request, i);
422  fr_redis_pipeline_free(replies, reply_cnt);
423  REXDENT();
424 
425  return CACHE_OK;
426 }
427 
428 /** Call delete the cache entry from redis
429  *
430  * @copydetails cache_entry_expire_t
431  */
433  request_t *request, UNUSED void *handle, fr_value_box_t const *key)
434 {
435  rlm_cache_redis_t *driver = instance;
437  fr_redis_conn_t *conn;
438  fr_redis_rcode_t status;
439  redisReply *reply = NULL;
440  int s_ret;
441  cache_status_t cache_status;
442 
443  for (s_ret = fr_redis_cluster_state_init(&state, &conn, driver->cluster, request, (uint8_t const *)key->vb_strvalue, key->vb_length, false);
444  s_ret == REDIS_RCODE_TRY_AGAIN; /* Continue */
445  s_ret = fr_redis_cluster_state_next(&state, &conn, driver->cluster, request, status, &reply)) {
446  reply = redisCommand(conn->handle, "DEL %b", (uint8_t const *)key->vb_strvalue, key->vb_length);
447  status = fr_redis_command_status(conn, reply);
448  }
449 
450  if (s_ret != REDIS_RCODE_SUCCESS) {
451  RERROR("Failed expiring entry");
452  error:
453  fr_redis_reply_free(&reply);
454  return CACHE_ERROR;
455  }
456  if (!fr_cond_assert(reply)) goto error;
457 
458  if (reply->type == REDIS_REPLY_INTEGER) {
459  cache_status = CACHE_MISS;
460  if (reply->integer) cache_status = CACHE_OK; /* Affected */
461  fr_redis_reply_free(&reply);
462  return cache_status;
463  }
464 
465  REDEBUG("Bad result type, expected integer, got %s",
466  fr_table_str_by_value(redis_reply_types, reply->type, "<UNKNOWN>"));
467  fr_redis_reply_free(&reply);
468 
469  return CACHE_ERROR;
470 }
471 
474  .common = {
475  .magic = MODULE_MAGIC_INIT,
476  .name = "cache_redis",
477  .onload = mod_load,
478  .instantiate = mod_instantiate,
479  .inst_size = sizeof(rlm_cache_redis_t),
481  },
482  .free = cache_entry_free,
483  .find = cache_entry_find,
484  .insert = cache_entry_insert,
485  .expire = cache_entry_expire,
486 };
static int const char char buffer[256]
Definition: acutest.h:574
#define unlikely(_x)
Definition: build.h:379
#define UNUSED
Definition: build.h:313
#define NUM_ELEMENTS(_t)
Definition: build.h:335
int cf_section_parse(TALLOC_CTX *ctx, void *base, CONF_SECTION *cs)
Parse a configuration section into user-supplied variables.
Definition: cf_parse.c:985
#define CONF_PARSER_TERMINATOR
Definition: cf_parse.h:627
#define cf_section_rules_push(_cs, _rule)
Definition: cf_parse.h:659
Defines a CONF_PAIR to C data type mapping.
Definition: cf_parse.h:564
fr_redis_rcode_t fr_redis_cluster_state_next(fr_redis_cluster_state_t *state, fr_redis_conn_t **conn, fr_redis_cluster_t *cluster, request_t *request, fr_redis_rcode_t status, redisReply **reply)
Get the next connection to attempt a command against.
Definition: cluster.c:1863
fr_redis_rcode_t fr_redis_cluster_state_init(fr_redis_cluster_state_t *state, fr_redis_conn_t **conn, fr_redis_cluster_t *cluster, request_t *request, uint8_t const *key, size_t key_len, bool read_only)
Resolve a key to a pool, and reserve a connection in that pool.
Definition: cluster.c:1741
fr_redis_cluster_t * fr_redis_cluster_alloc(TALLOC_CTX *ctx, CONF_SECTION *module, fr_redis_conf_t *conf, bool triggers_enabled, char const *log_prefix, char const *trigger_prefix, fr_pair_list_t *trigger_args)
Allocate and initialise a new cluster structure.
Definition: cluster.c:2261
A redis cluster.
Definition: cluster.c:251
Redis connection sequence state.
Definition: cluster.h:49
#define fr_cond_assert(_x)
Calls panic_action ifndef NDEBUG, else logs error and evaluates to value of _x.
Definition: debug.h:139
#define ERROR(fmt,...)
Definition: dhcpclient.c:41
fr_dict_attr_t const ** out
Where to write a pointer to the resolved fr_dict_attr_t.
Definition: dict.h:267
fr_dict_t const ** out
Where to write a pointer to the loaded/resolved fr_dict_t.
Definition: dict.h:280
Specifies an attribute which must be present for the module to function.
Definition: dict.h:266
Specifies a dictionary which must be loaded/loadable for the module to function.
Definition: dict.h:279
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:63
if(rcode > 0)
Definition: fd_read.h:9
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition: log.h:443
#define RDEBUG_ENABLED3
True if request debug level 1-3 messages are enabled.
Definition: log.h:335
#define RDEBUG3(fmt,...)
Definition: log.h:343
#define RERROR(fmt,...)
Definition: log.h:298
#define RPERROR(fmt,...)
Definition: log.h:302
#define RINDENT()
Indent R* messages by one level.
Definition: log.h:430
talloc_free(reap)
@ L_DBG_LVL_3
3rd highest priority debug messages (-xxx | -Xx).
Definition: log.h:72
struct map_s map_t
Definition: map.h:33
@ FR_TYPE_DATE
Unix time stamp, always has value >2^31.
Definition: merged_model.c:111
unsigned char uint8_t
Definition: merged_model.c:30
module_instance_t * mi
Instance of the module being instantiated.
Definition: module_ctx.h:51
Temporary structure to hold arguments for instantiation calls.
Definition: module_ctx.h:50
static const conf_parser_t config[]
Definition: base.c:183
#define REDEBUG(fmt,...)
Definition: radclient.h:52
static void fr_redis_pipeline_free(redisReply *reply[], size_t num)
Definition: base.h:70
redisContext * handle
Hiredis context used when issuing commands.
Definition: base.h:101
#define REDIS_COMMON_CONFIG
Definition: base.h:133
int fr_redis_tuple_from_map(TALLOC_CTX *pool, char const *out[], size_t out_len[], map_t *map)
Add a single map pair to an existing command string as three elements.
Definition: redis.c:459
void fr_redis_reply_print(fr_log_lvl_t lvl, redisReply *reply, request_t *request, int idx)
Print the response data in a useful treelike form.
Definition: redis.c:141
static void fr_redis_reply_free(redisReply **reply)
Wrap freeReplyObject so we consistently check for NULL pointers.
Definition: base.h:64
void fr_redis_version_print(void)
Print the version of libhiredis the server was built against.
Definition: redis.c:53
fr_redis_rcode_t fr_redis_command_status(fr_redis_conn_t *conn, redisReply *reply)
Check the reply for errors.
Definition: redis.c:71
fr_table_num_sorted_t const redis_reply_types[]
Definition: redis.c:30
fr_redis_rcode_t fr_redis_pipeline_result(unsigned int *pipelined, fr_redis_rcode_t *rcode, redisReply *out[], size_t out_len, fr_redis_conn_t *conn)
Simplifies handling of pipelined commands with Redis cluster.
Definition: redis.c:535
fr_redis_rcode_t
Codes are ordered inversely by priority.
Definition: base.h:87
@ REDIS_RCODE_SUCCESS
Operation was successful.
Definition: base.h:88
@ REDIS_RCODE_TRY_AGAIN
Try the operation again.
Definition: base.h:90
int fr_redis_reply_to_map(TALLOC_CTX *ctx, map_list_t *out, request_t *request, redisReply *key, redisReply *op, redisReply *value)
Convert a pair of redis reply objects to a map.
Definition: redis.c:365
Configuration parameters for a redis connection.
Definition: base.h:109
Connection handle, holding a redis context.
Definition: base.h:100
fr_value_box_t key
Key used to identify entry.
Definition: rlm_cache.h:73
map_list_t maps
Head of the maps list.
Definition: rlm_cache.h:78
fr_unix_time_t created
When the entry was created.
Definition: rlm_cache.h:75
module_t common
Common fields for all loadable modules.
Definition: rlm_cache.h:258
cache_status_t
Definition: rlm_cache.h:39
@ CACHE_ERROR
Fatal error.
Definition: rlm_cache.h:41
@ CACHE_OK
Cache entry found/updated.
Definition: rlm_cache.h:42
@ CACHE_MISS
Cache entry notfound.
Definition: rlm_cache.h:43
fr_unix_time_t expires
When the entry expires.
Definition: rlm_cache.h:76
Configuration for the rlm_cache module.
Definition: rlm_cache.h:51
Definition: rlm_cache.h:72
static cache_status_t cache_entry_insert(UNUSED rlm_cache_config_t const *config, void *instance, request_t *request, UNUSED void *handle, const rlm_cache_entry_t *c)
Insert a new entry into the data store.
static int mod_load(void)
fr_redis_conf_t conf
Connection parameters for the Redis server.
static fr_dict_attr_t const * attr_cache_created
static void cache_entry_free(rlm_cache_entry_t *c)
fr_dict_attr_autoload_t rlm_cache_redis_dict_attr[]
rlm_cache_driver_t rlm_cache_redis
tmpl_t * expires_attr
LHS of the Cache-Expires map.
static fr_dict_t const * dict_freeradius
static conf_parser_t driver_config[]
static cache_status_t cache_entry_expire(UNUSED rlm_cache_config_t const *config, void *instance, request_t *request, UNUSED void *handle, fr_value_box_t const *key)
Call delete the cache entry from redis.
static fr_dict_attr_t const * attr_cache_expires
static cache_status_t cache_entry_find(rlm_cache_entry_t **out, UNUSED rlm_cache_config_t const *config, void *instance, request_t *request, UNUSED void *handle, fr_value_box_t const *key)
Locate a cache entry in redis.
fr_redis_cluster_t * cluster
fr_dict_autoload_t rlm_cache_redis_dict[]
tmpl_t * created_attr
LHS of the Cache-Created map.
static int mod_instantiate(module_inst_ctx_t const *mctx)
Create a new rlm_cache_redis instance.
char const * name
Instance name e.g. user_database.
Definition: module.h:335
CONF_SECTION * conf
Module's instance configuration.
Definition: module.h:329
void * data
Module's instance data.
Definition: module.h:271
module_instance_t const * parent
Parent module's instance (if any).
Definition: module.h:337
#define tmpl_value(_tmpl)
Definition: tmpl.h:948
static fr_dict_attr_t const * tmpl_attr_tail_da(tmpl_t const *vpt)
Return the last attribute reference da.
Definition: tmpl.h:812
ssize_t tmpl_afrom_attr_str(TALLOC_CTX *ctx, tmpl_attr_error_t *err, tmpl_t **out, char const *name, tmpl_rules_t const *rules))
Parse a string into a TMPL_TYPE_ATTR_* type tmpl_t.
@ TMPL_TYPE_DATA
Value in native boxed format.
Definition: tmpl.h:142
tmpl_t * tmpl_init_shallow(tmpl_t *vpt, tmpl_type_t type, fr_token_t quote, char const *name, ssize_t len, tmpl_rules_t const *t_rules))
Initialise a tmpl without copying the input name string.
PUBLIC int snprintf(char *string, size_t length, char *format, va_alist)
Definition: snprintf.c:689
Value pair map.
Definition: map.h:77
fr_token_t op
The operator that controls insertion of the dst attribute.
Definition: map.h:82
#define fr_table_str_by_value(_table, _number, _def)
Convert an integer to a string.
Definition: table.h:772
#define talloc_zero_pooled_object(_ctx, _type, _num_subobjects, _total_subobjects_size)
Definition: talloc.h:177
#define fr_unix_time_ispos(_a)
Definition: time.h:372
static int64_t fr_unix_time_to_sec(fr_unix_time_t delta)
Definition: time.h:506
@ T_BARE_WORD
Definition: token.h:120
@ T_OP_SET
Definition: token.h:84
static fr_slen_t head
Definition: xlat.h:406
int fr_value_box_copy(TALLOC_CTX *ctx, fr_value_box_t *dst, const fr_value_box_t *src)
Copy value data verbatim duplicating any buffers.
Definition: value.c:3740
#define fr_box_strvalue_len(_val, _len)
Definition: value.h:286
#define fr_value_box_init(_vb, _type, _enumv, _tainted)
Initialise a fr_value_box_t.
Definition: value.h:587
static size_t char ** out
Definition: value.h:997