All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
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: dc98b8b33cbe19e7a31296925c2e9c46143715a3 $
19  * @file redis.c
20  * @brief Common functions for interacting with Redis via hiredis
21  *
22  * @copyright 2015 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
23  * @copyright 2000,2006,2015 The FreeRADIUS server project
24  * @copyright 2011 TekSavvy Solutions <gabe@teksavvy.com>
25  */
26 #include "redis.h"
27 #include <freeradius-devel/rad_assert.h>
28 
30  { "string", REDIS_REPLY_STRING },
31  { "integer", REDIS_REPLY_INTEGER },
32  { "array", REDIS_REPLY_ARRAY },
33  { "nil", REDIS_REPLY_NIL },
34  { "status", REDIS_REPLY_STATUS },
35  { "error", REDIS_REPLY_ERROR },
36  { NULL, -1 }
37 };
38 
40  { "try again", REDIS_RCODE_TRY_AGAIN },
41  { "move", REDIS_RCODE_MOVE },
42  { "ask", REDIS_RCODE_ASK },
43  { "success", REDIS_RCODE_SUCCESS },
44  { "error", REDIS_RCODE_ERROR },
45  { "reconnect", REDIS_RCODE_RECONNECT },
46  { NULL, -1 }
47 };
48 
49 /** Print the version of libhiredis the server was built against
50  *
51  */
53 {
54  static bool version_done;
55 
56  if (!version_done) {
57  version_done = true;
58 
59  INFO("libfreeradius-redis: libhiredis version: %i.%i.%i", HIREDIS_MAJOR, HIREDIS_MINOR, HIREDIS_PATCH);
60  }
61 }
62 
63 /** Check the reply for errors
64  *
65  * @param conn used to issue the command.
66  * @param reply to process.
67  * @return
68  * - REDIS_RCODE_TRY_AGAIN - If the operation should be retries.
69  * - REDIS_RCODE_MOVED - If the key has been permanently moved.
70  * - REDIS_RCODE_ASK - If the key has been temporarily moved.
71  * - REDIS_RCODE_SUCCESS - if no errors.
72  * - REDIS_RCODE_ERROR - on command/server error.
73  * - REDIS_RCODE_NO_SCRIPT - script specified by evalsha doesn't exist.
74  * - REDIS_RCODE_RECONNECT - on connection error (probably needs reconnecting).
75  */
77 {
78  size_t i = 0;
79 
80  if (!reply) switch (conn->handle->err) {
81  case REDIS_OK:
82  break;
83 
84  case REDIS_ERR_IO:
85  case REDIS_ERR_EOF:
86  case REDIS_ERR_OTHER:
87  fr_strerror_printf("Connection error: %s", conn->handle->errstr);
88  return REDIS_RCODE_RECONNECT;
89 
90  default:
91  case REDIS_ERR_PROTOCOL:
92  fr_strerror_printf("Command error: %s", conn->handle->errstr);
93  return REDIS_RCODE_ERROR;
94  }
95 
96  if (reply) switch (reply->type) {
97  case REDIS_REPLY_STATUS:
98  return REDIS_RCODE_SUCCESS;
99 
100  case REDIS_REPLY_ERROR:
101  fr_strerror_printf("Server error: %s", reply->str);
102  if (strncmp(REDIS_ERROR_MOVED_STR, reply->str, sizeof(REDIS_ERROR_MOVED_STR) - 1) == 0) {
103  return REDIS_RCODE_MOVE;
104  }
105  if (strncmp(REDIS_ERROR_ASK_STR, reply->str, sizeof(REDIS_ERROR_ASK_STR) - 1) == 0) {
106  return REDIS_RCODE_ASK;
107  }
108  if (strncmp(REDIS_ERROR_TRY_AGAIN_STR, reply->str, sizeof(REDIS_ERROR_TRY_AGAIN_STR) - 1) == 0) {
109  return REDIS_RCODE_TRY_AGAIN;
110  }
111  if (strncmp(REDIS_ERROR_NO_SCRIPT_STR, reply->str, sizeof(REDIS_ERROR_NO_SCRIPT_STR) - 1) == 0) {
112  return REDIS_RCODE_NO_SCRIPT;
113  }
114  return REDIS_RCODE_ERROR;
115 
116  /*
117  * Recurse to check for nested errors
118  */
119  case REDIS_REPLY_ARRAY:
120  for (i = 0; i < reply->elements; i++) {
121  int ret;
122 
123  ret = fr_redis_command_status(conn, reply->element[i]);
124  if (ret < 0) return ret;
125  }
126  default:
127  break;
128  }
129  return REDIS_RCODE_SUCCESS;
130 }
131 
132 /** Print the response data in a useful treelike form
133  *
134  * @param lvl to print data at.
135  * @param reply to print.
136  * @param request The current request.
137  * @param idx Response number.
138  */
139 void fr_redis_reply_print(log_lvl_t lvl, redisReply *reply, REQUEST *request, int idx)
140 {
141  size_t i = 0;
142 
143  if (!reply) return;
144 
145  switch (reply->type) {
146  case REDIS_REPLY_ERROR:
147  REDEBUG("(%i) error : %s", idx, reply->str);
148  break;
149 
150  case REDIS_REPLY_STATUS:
151  RDEBUGX(lvl, "(%i) status : %s", idx, reply->str);
152  break;
153 
154  case REDIS_REPLY_STRING:
155  RDEBUGX(lvl, "(%i) string : %s", idx, reply->str);
156  break;
157 
158  case REDIS_REPLY_INTEGER:
159  RDEBUGX(lvl, "(%i) integer : %lld", idx, reply->integer);
160  break;
161 
162  case REDIS_REPLY_NIL:
163  RDEBUGX(lvl, "(%i) nil", idx);
164  break;
165 
166  case REDIS_REPLY_ARRAY:
167  RDEBUGX(lvl, "(%i) array[%zu]", idx, reply->elements);
168  for (i = 0; i < reply->elements; i++) {
169  RINDENT();
170  fr_redis_reply_print(lvl, reply->element[i], request, i);
171  REXDENT();
172  }
173  break;
174  }
175 }
176 
177 /** Convert a string or integer type to #value_data_t of specified type
178  *
179  * Will work with REDIS_REPLY_STRING (which is converted to #PW_TYPE_STRING
180  * then cast to dst_type), or REDIS_REPLY_INTEGER (which is converted to
181  * #PW_TYPE_INTEGER64, then cast to dst_type).
182  *
183  * @note Any unsupported types will trigger an assert. You must check the
184  * reply type prior to calling this function.
185  *
186  * @param[in,out] ctx to allocate any buffers in.
187  * @param[out] out Where to write the cast type.
188  * @param[in] reply to process.
189  * @param[in] dst_type to convert to.
190  * @param[in] dst_enumv Used to convert string types to integers for attributes
191  * with enumerated values.
192  * @return
193  * - 1 if we received a NIL reply. Out will remain uninitialized.
194  * - 0 on success.
195  * - -1 on cast or parse failure.
196  */
197 int fr_redis_reply_to_value_data(TALLOC_CTX *ctx, value_data_t *out, redisReply *reply,
198  PW_TYPE dst_type, fr_dict_attr_t const *dst_enumv)
199 {
200  value_data_t in;
201  PW_TYPE src_type = 0;
202 
203  memset(&in, 0, sizeof(in));
204 
205  switch (reply->type) {
206  case REDIS_REPLY_NIL:
207  return 1;
208 
209  /*
210  * Try and convert the integer to the smallest
211  * and simplest type possible, to give the cast
212  * the greatest chance of success.
213  */
214  case REDIS_REPLY_INTEGER:
215  if (reply->integer < INT32_MIN) { /* 64bit signed (not supported)*/
216  fr_strerror_printf("Signed 64bit integers are not supported");
217  return -1;
218  }
219  if (reply->integer < 0) { /* 32bit signed (supported) */
220  src_type = PW_TYPE_SIGNED;
221  in.sinteger = (int32_t) reply->integer;
222  in.length = sizeof(in.sinteger);
223  }
224  else if (reply->integer > UINT32_MAX) { /* 64bit unsigned (supported) */
225  src_type = PW_TYPE_INTEGER64;
226  in.integer64 = (uint64_t) reply->integer;
227  in.length = sizeof(in.integer64);
228  }
229  else if (reply->integer > UINT16_MAX) { /* 32bit unsigned (supported) */
230  src_type = PW_TYPE_INTEGER;
231  in.integer = (uint32_t) reply->integer;
232  in.length = sizeof(in.integer);
233  }
234  else if (reply->integer > UINT8_MAX) { /* 16bit unsigned (supported) */
235  src_type = PW_TYPE_SHORT;
236  in.ushort = (uint16_t) reply->integer;
237  in.length = sizeof(in.ushort);
238  }
239  else if (reply->integer >= 0) { /* 8bit unsigned (supported) */
240  src_type = PW_TYPE_BYTE;
241  in.byte = (uint8_t) reply->integer;
242  in.length = sizeof(in.byte);
243  } else {
244  rad_assert(0);
245  }
246  break;
247 
248  case REDIS_REPLY_STRING:
249  src_type = PW_TYPE_STRING;
250  in.ptr = reply->str;
251  in.length = reply->len;
252  break;
253 
254  case REDIS_REPLY_ARRAY:
255  case REDIS_REPLY_STATUS:
256  case REDIS_REPLY_ERROR:
257  rad_assert(0);
258  }
259 
260  if (src_type == dst_type) {
261  if (value_data_copy(ctx, out, src_type, &in) < 0) return -1;
262  } else {
263  if (value_data_cast(ctx, out, dst_type, dst_enumv, src_type, NULL, &in) < 0) return -1;
264  }
265  return 0;
266 }
267 
268 /** Convert a pair of redis reply objects to a map
269  *
270  * The maps can then be applied using #map_to_request.
271  *
272  * @param[in,out] ctx to allocate maps in.
273  * @param[out] out Where to write the head of the new maps list.
274  * @param[in] request The current request.
275  * @param[in] key to process.
276  * @param[in] op to process.
277  * @param[in] value to process.
278  * @return
279  * - 0 on success.
280  * - -1 on failure.
281  */
282 int fr_redis_reply_to_map(TALLOC_CTX *ctx, vp_map_t **out, REQUEST *request,
283  redisReply *key, redisReply *op, redisReply *value)
284 {
285  vp_map_t *map = NULL;
286  ssize_t slen;
287 
288  *out = NULL;
289 
290  if (key->type != REDIS_REPLY_STRING) {
291  REDEBUG("Bad key type, expected string, got %s",
292  fr_int2str(redis_reply_types, key->type, "<UNKNOWN>"));
293  error:
294  TALLOC_FREE(map);
295  return -1;
296  }
297 
298  if (op->type != REDIS_REPLY_STRING) {
299  REDEBUG("Bad key type, expected string, got %s",
300  fr_int2str(redis_reply_types, op->type, "<UNKNOWN>"));
301  goto error;
302  }
303 
304  RDEBUG3("Got key : %s", key->str);
305  RDEBUG3("Got op : %s", op->str);
306 
307  if (RDEBUG_ENABLED3) {
308  char *p;
309 
310  p = fr_asprint(NULL, value->str, value->len, '"');
311  RDEBUG3("Got value : %s", p);
312  talloc_free(p);
313  }
314 
315  map = talloc_zero(ctx, vp_map_t);
316  slen = tmpl_afrom_attr_str(map, &map->lhs, key->str, REQUEST_CURRENT, PAIR_LIST_REQUEST, false, false);
317  if (slen < 0) {
318  REMARKER(key->str, -slen, fr_strerror());
319  goto error;
320  }
321 
322  map->op = fr_str2int(fr_tokens_table, op->str, T_INVALID);
323  if (map->op == T_INVALID) {
324  REDEBUG("Invalid operator \"%s\"", op->str);
325  goto error;
326  }
327 
328  switch (value->type) {
329  case REDIS_REPLY_STRING:
330  case REDIS_REPLY_INTEGER:
331  {
332  value_data_t vpt;
333 
334  /* Logs own errors */
335  if (fr_redis_reply_to_value_data(map, &vpt, value,
336  map->lhs->tmpl_da->type, map->lhs->tmpl_da) < 0) {
337  REDEBUG("Failed converting Redis data: %s", fr_strerror());
338  goto error;
339  }
340 
341  /* This will only fail only memory allocation errors */
342  if (tmpl_afrom_value_data(map, &map->rhs, &vpt,
343  map->lhs->tmpl_da->type, map->lhs->tmpl_da, true) < 0) {
344  goto error;
345  }
346  }
347  break;
348 
349  default:
350  REDEBUG("Bad value type, expected string or integer, got %s",
351  fr_int2str(redis_reply_types, value->type, "<UNKNOWN>"));
352  goto error;
353 
354  }
355  VERIFY_MAP(map);
356 
357  *out = map;
358 
359  return 0;
360 }
361 
362 /** Add a single map pair to an existing command string as three elements
363  *
364  * - Integer types will be encoded as integers.
365  * - Strings and octets will be encoded in their raw form.
366  * - Other types will be converted to their printable form and will be encoded as strings.
367  *
368  * @note lhs must be a #TMPL_TYPE_ATTR.
369  * @note rhs must be a #TMPL_TYPE_DATA.
370  *
371  * @param pool to allocate any buffers in.
372  * @param out Where to write pointers to the member of the tuple. Unused elements should be
373  * a multiple of three, and it should have at least three unused elements.
374  * @param out_len Where to write the size of the data pointed to by the equivalent index
375  * in the out array.
376  * @param map to convert.
377  * @return
378  * 0 on success.
379  * -1 on failure.
380  */
381 int fr_redis_tuple_from_map(TALLOC_CTX *pool, char const *out[], size_t out_len[], vp_map_t *map)
382 {
383  char *new;
384 
385  char key_buf[256];
386  char *key;
387  size_t key_len;
388 
389  rad_assert(map->lhs->type == TMPL_TYPE_ATTR);
390  rad_assert(map->rhs->type == TMPL_TYPE_DATA);
391 
392  key_len = tmpl_snprint(key_buf, sizeof(key_buf), map->lhs, map->lhs->tmpl_da);
393  if (is_truncated(key_len, sizeof(key_buf))) {
394  fr_strerror_printf("Key too long. Must be < " STRINGIFY(sizeof(key_buf)) " "
395  "bytes, got %zu bytes", key_len);
396  return -1;
397  }
398  key = talloc_bstrndup(pool, key_buf, key_len);
399  if (!key) return -1;
400 
401  switch (map->rhs->tmpl_data_type) {
402  case PW_TYPE_STRING:
403  case PW_TYPE_OCTETS:
404  out[2] = map->rhs->tmpl_data_value.ptr;
405  out_len[2] = map->rhs->tmpl_data_length;
406  break;
407 
408  /*
409  * For everything else we get the string representation
410  */
411  default:
412  {
413  char value[256];
414  size_t len;
415 
416  len = value_data_snprint(value, sizeof(value), map->rhs->tmpl_data_type, map->lhs->tmpl_da,
417  &map->rhs->tmpl_data_value, '\0');
418  new = talloc_bstrndup(pool, value, len);
419  if (!new) {
420  talloc_free(key);
421  return -1;
422  }
423  out[2] = new;
424  out_len[2] = len;
425  break;
426  }
427  }
428 
429  out[0] = key;
430  out_len[0] = key_len;
431  out[1] = fr_int2str(fr_tokens_table, map->op, NULL);
432  out_len[1] = strlen(out[1]);
433 
434  return 0;
435 }
436 
437 /** Simplifies handling of pipelined commands with Redis cluster
438  *
439  * Retrieve all available pipelined responses, and write them to the array.
440  *
441  * On encountering an error, all previously retrieved responses are freed, and the reply
442  * containing the error is written to the first element of out. All responses after the
443  * error are also freed.
444  *
445  * If the number of responses != pipelined, that's also an error, a very serious one,
446  * in libhiredis or Redis. We can't really do much here apart from error out.
447  *
448  * @param[out] rcode Status of the first errored response, or REDIS_RCODE_SUCCESS
449  * if all responses were processed.
450  * @param[out] out Where to write the replies from pipelined commands.
451  * Will contain exactly 1 element on error, else the number passed in pipelined.
452  * @param[in] out_len number of elements in out.
453  * @param[in] conn the pipelined commands were issued on.
454  * @param[in] pipelined Number of pipelined commands we sent to the server.
455  * @return
456  * - #REDIS_RCODE_SUCCESS on success.
457  * - #REDIS_RCODE_ERROR on command/response mismatch or command error.
458  * - REDIS_RCODE_* on other errors;
459  */
460 fr_redis_rcode_t fr_redis_pipeline_result(fr_redis_rcode_t *rcode, redisReply *out[], size_t out_len,
461  fr_redis_conn_t *conn, int pipelined)
462 {
463  size_t i;
464  redisReply **out_p = out;
466  redisReply *reply = NULL;
467 
468  rad_assert(out_len >= (size_t)pipelined);
469 
470 #ifdef NDEBUG
471  if (pipelined > out_len) {
472  for (i = 0; i < (size_t)pipelined; i++) {
473  if (redisGetReply(conn->handle, (void **)&reply) != REDIS_OK) break;
474  fr_redis_reply_free(reply);
475  }
476 
477  fr_strerror_printf("Too many pipelined commands");
478  out[0] = NULL;
479  return REDIS_RCODE_ERROR;
480  }
481 #endif
482 
483  for (i = 0; i < (size_t)pipelined; i++) {
484  bool maybe_more = false;
485 
486  /*
487  * we don't need to check the return code here,
488  * as it's also stored in the conn->handle.
489  */
490  reply = NULL; /* redisGetReply doesn't NULLify reply on error *sigh* */
491  if (redisGetReply(conn->handle, (void **)&reply) == REDIS_OK) maybe_more = true;
492  status = fr_redis_command_status(conn, reply);
493  *out_p++ = reply;
494 
495  /*
496  * Bail out of processing responses,
497  * free the remaining ones (leaving this one intact)
498  * pass control back to the cluster code.
499  */
500  if (maybe_more && (status != REDIS_RCODE_SUCCESS)) {
501  size_t j;
502  error:
503  /*
504  * Free everything that came before the bad reply
505  */
506  for (j = 0; j < i; j++) {
507  fr_redis_reply_free(out[j]);
508  out[j] = NULL;
509  }
510 
511  /*
512  * ...and drain the rest of the pipelined responses
513  */
514  for (j = i + 1; j < (size_t)pipelined; j++) {
515  redisReply *to_clear;
516 
517  if (redisGetReply(conn->handle, (void **)&to_clear) != REDIS_OK) break;
518  fr_redis_reply_free(to_clear);
519  }
520 
521  out[0] = reply;
522  if (rcode) *rcode = status;
523  return reply ? 1 : 0;
524  }
525  }
526 
527  if (i != (size_t)pipelined) {
528  fr_strerror_printf("Expected %i responses, got %zu", pipelined, i);
529  status = REDIS_RCODE_ERROR;
530  goto error;
531  }
532 
533  if (rcode) *rcode = status;
534  return i;
535 }
536 
537 /** Get the version of Redis running on the remote server
538  *
539  * This can be useful for some modules, as it allows adaptive behaviour, or early termination.
540  *
541  * @param[out] out Where to write the version string.
542  * @param[in] out_len Length of the version string buffer.
543  * @param[in] conn Used to query the version string.
544  * @return
545  * - #REDIS_RCODE_SUCCESS on success.
546  * - #REDIS_RCODE_ERROR on command/response mismatch or command error.
547  * - REDIS_RCODE_* on other errors;
548  */
549 fr_redis_rcode_t fr_redis_get_version(char *out, size_t out_len, fr_redis_conn_t *conn)
550 {
551  redisReply *reply;
552  fr_redis_rcode_t status;
553  char *p, *q;
554 
555  rad_assert(out_len > 0);
556  out[0] = '\0';
557 
558  reply = redisCommand(conn->handle, "INFO SERVER");
559  status = fr_redis_command_status(conn, reply);
560  if (status != REDIS_RCODE_SUCCESS) return status;
561 
562  if (reply->type != REDIS_REPLY_STRING) {
563  fr_strerror_printf("Bad value type, expected string or integer, got %s",
564  fr_int2str(redis_reply_types, reply->type, "<UNKNOWN>"));
565  error:
566  fr_redis_reply_free(reply);
567  return REDIS_RCODE_ERROR;
568  }
569 
570  p = strstr(reply->str, "redis_version:");
571  if (!p) {
572  fr_strerror_printf("Response did not contain version string");
573  goto error;
574  }
575 
576  p = strchr(p, ':');
577  rad_assert(p);
578  p++;
579 
580  q = strstr(p, "\r\n");
581  if (!q) q = p + strlen(p);
582 
583  if ((size_t)(q - p) >= out_len) {
584  fr_strerror_printf("Version string %zu bytes, expected < %zu bytes", q - p, out_len);
585  goto error;
586  }
587  strlcpy(out, p, (q - p) + 1);
588 
589  return REDIS_RCODE_SUCCESS;
590 }
591 
592 /** Convert version string into a 32bit unsigned integer for comparisons
593  *
594  * @param[in] version string to parse.
595  * @return 32bit unsigned integer representing the version string.
596  */
597 uint32_t fr_redis_version_num(char const *version)
598 {
599  unsigned long num;
600  uint32_t ret;
601  char const *p = version;
602  char *q;
603 
604  num = strtoul(p, &q, 10);
605  if (num > UINT8_MAX) {
606  fr_strerror_printf("Major version number %lu greater than " STRINGIFY(UINT8_MAX), num);
607  return 0;
608  }
609 
610  if ((p == q) || (q[0] != '.')) {
611  fr_strerror_printf("Trailing garbage in Redis version \"%s\"", q);
612  return 0;
613  }
614  ret = num << 24;
615  p = q + 1;
616 
617  num = strtoul(p, &q, 10);
618  if (num > UINT8_MAX) {
619  fr_strerror_printf("Minor version number %lu greater than " STRINGIFY(UINT8_MAX), num);
620  return 0;
621  }
622 
623  if ((p == q) || (q[0] != '.')) {
624  fr_strerror_printf("Trailing garbage in Redis version \"%s\"", q);
625  return 0;
626  }
627  ret |= num << 16;
628  p = q + 1;
629 
630  num = strtoul(p, &q, 10);
631  if (num > UINT16_MAX) {
632  fr_strerror_printf("Minor version number %lu greater than " STRINGIFY(UINT16_MAX), num);
633  return 0;
634  }
635 
636  if ((p == q) || (q[0] != '\0')) {
637  fr_strerror_printf("Trailing garbage in Redis version \"%s\"", q);
638  return 0;
639  }
640 
641  return ret | num;
642 }
#define RINDENT()
Indent R* messages by one level.
Definition: log.h:265
#define RDEBUG_ENABLED3
True if request debug level 1-3 messages are enabled.
Definition: log.h:239
FR_NAME_NUMBER const redis_rcodes[]
Definition: redis.c:39
Dictionary attribute.
Definition: dict.h:77
int tmpl_afrom_value_data(TALLOC_CTX *ctx, vp_tmpl_t **out, value_data_t *data, PW_TYPE type, fr_dict_attr_t const *enumv, bool steal)
Create a vp_tmpl_t from a value_data_t.
Definition: tmpl.c:595
32 Bit signed integer.
Definition: radius.h:45
vp_tmpl_t * lhs
Typically describes the attribute to add, modify or compare.
Definition: map.h:47
FR_NAME_NUMBER const redis_reply_types[]
Definition: redis.c:29
Script doesn't exist.
Definition: redis.h:75
#define INFO(fmt,...)
Definition: log.h:143
Dictionary attribute.
Definition: tmpl.h:133
vp_tmpl_t * rhs
Typically describes a literal value or a src attribute to copy or compare.
Definition: map.h:48
#define REMARKER(_m, _i, _e)
Output string with error marker, showing where format error occurred.
Definition: log.h:306
ssize_t tmpl_afrom_attr_str(TALLOC_CTX *ctx, vp_tmpl_t **out, char const *name, request_refs_t request_def, pair_lists_t list_def, bool allow_unknown, bool allow_undefined)
Parse a string into a TMPL_TYPE_ATTR_* or TMPL_TYPE_LIST type vp_tmpl_t.
Definition: tmpl.c:956
const FR_NAME_NUMBER fr_tokens_table[]
Definition: token.c:30
fr_connection_pool_t * pool
Pool associated with this node.
Definition: cluster.c:235
#define fr_redis_reply_free(_p)
Wrap freeReplyObject so we consistently check for NULL pointers.
Definition: redis.h:56
static expr_map_t map[]
Definition: rlm_expr.c:169
void fr_redis_reply_print(log_lvl_t lvl, redisReply *reply, REQUEST *request, int idx)
Print the response data in a useful treelike form.
Definition: redis.c:139
int fr_redis_reply_to_map(TALLOC_CTX *ctx, vp_map_t **out, REQUEST *request, redisReply *key, redisReply *op, redisReply *value)
Convert a pair of redis reply objects to a map.
Definition: redis.c:282
unsigned int version
Definition: proto_bfd.c:192
#define is_truncated(_ret, _max)
Definition: libradius.h:204
#define rad_assert(expr)
Definition: rad_assert.h:38
int fr_str2int(FR_NAME_NUMBER const *table, char const *name, int def)
Definition: token.c:451
redisContext * handle
Hiredis context used when issuing commands.
Definition: redis.h:81
8 Bit unsigned integer.
Definition: radius.h:42
int value_data_cast(TALLOC_CTX *ctx, value_data_t *dst, PW_TYPE dst_type, fr_dict_attr_t const *dst_enumv, PW_TYPE src_type, fr_dict_attr_t const *src_enumv, value_data_t const *src)
Convert one type of value_data_t to another.
Definition: value.c:1073
Value in native format.
Definition: tmpl.h:138
uint32_t fr_redis_version_num(char const *version)
Convert version string into a 32bit unsigned integer for comparisons.
Definition: redis.c:597
void fr_redis_version_print(void)
Print the version of libhiredis the server was built against.
Definition: redis.c:52
#define STRINGIFY(x)
Definition: build.h:34
Attributes in incoming or internally proxied request.
Definition: tmpl.h:82
#define REDIS_ERROR_TRY_AGAIN_STR
Definition: redis.h:42
Unrecoverable library/server error.
Definition: redis.h:69
fr_redis_rcode_t fr_redis_command_status(fr_redis_conn_t *conn, redisReply *reply)
Check the reply for errors.
Definition: redis.c:76
Attempt operation on an alternative node.
Definition: redis.h:73
#define REXDENT()
Exdent (unindent) R* messages by one level.
Definition: log.h:272
The current request.
Definition: tmpl.h:113
fr_redis_rcode_t fr_redis_get_version(char *out, size_t out_len, fr_redis_conn_t *conn)
Get the version of Redis running on the remote server.
Definition: redis.c:549
32 Bit unsigned integer.
Definition: radius.h:34
tmpl_type_t type
What type of value tmpl refers to.
Definition: tmpl.h:188
char const * fr_strerror(void)
Get the last library error.
Definition: log.c:212
int value_data_copy(TALLOC_CTX *ctx, value_data_t *dst, PW_TYPE type, const value_data_t *src)
Copy value data verbatim duplicating any buffers.
Definition: value.c:1479
Attempt operation on an alternative node with remap.
Definition: redis.h:74
64 Bit unsigned integer.
Definition: radius.h:51
#define REDIS_ERROR_MOVED_STR
Definition: redis.h:40
size_t length
Length of value data.
Definition: pair.h:87
char * talloc_bstrndup(void const *t, char const *in, size_t inlen)
Binary safe strndup function.
Definition: missing.c:632
FR_TOKEN op
The operator that controls insertion of the dst attribute.
Definition: map.h:50
enum log_lvl log_lvl_t
Transitory error, caller should retry the operation with a new connection.
Definition: redis.h:71
int fr_redis_tuple_from_map(TALLOC_CTX *pool, char const *out[], size_t out_len[], vp_map_t *map)
Add a single map pair to an existing command string as three elements.
Definition: redis.c:381
Common functions for interacting with Redis via hiredis.
char * fr_asprint(TALLOC_CTX *ctx, char const *in, ssize_t inlen, char quote)
Escape string that may contain binary data, and write it to a new buffer.
Definition: print.c:390
size_t tmpl_snprint(char *buffer, size_t bufsize, vp_tmpl_t const *vpt, fr_dict_attr_t const *values)
Print a vp_tmpl_t to a string.
Definition: tmpl.c:1822
#define REDIS_ERROR_NO_SCRIPT_STR
Definition: redis.h:43
Try the operation again.
Definition: redis.h:70
void fr_strerror_printf(char const *,...) CC_HINT(format(printf
#define REDEBUG(fmt,...)
Definition: log.h:254
fr_redis_rcode_t fr_redis_pipeline_result(fr_redis_rcode_t *rcode, redisReply *out[], size_t out_len, fr_redis_conn_t *conn, int pipelined)
Simplifies handling of pipelined commands with Redis cluster.
Definition: redis.c:460
fr_redis_rcode_t
Codes are ordered inversely by priority.
Definition: redis.h:67
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition: strlcpy.c:38
int fr_redis_reply_to_value_data(TALLOC_CTX *ctx, value_data_t *out, redisReply *reply, PW_TYPE dst_type, fr_dict_attr_t const *dst_enumv)
Convert a string or integer type to value_data_t of specified type.
Definition: redis.c:197
char const * fr_int2str(FR_NAME_NUMBER const *table, int number, char const *def)
Definition: token.c:506
#define REDIS_ERROR_ASK_STR
Definition: redis.h:41
String of printable characters.
Definition: radius.h:33
Operation was successfull.
Definition: redis.h:68
#define RDEBUGX(_l, fmt,...)
Definition: log.h:242
Value pair map.
Definition: map.h:46
#define VERIFY_MAP(_x)
Definition: map.h:59
16 Bit unsigned integer.
Definition: radius.h:43
Raw octets.
Definition: radius.h:38
Connection handle, holding a redis context.
Definition: redis.h:80
PW_TYPE
Internal data types used within libfreeradius.
Definition: radius.h:31
#define RDEBUG3(fmt,...)
Definition: log.h:245
size_t value_data_snprint(char *out, size_t outlen, PW_TYPE type, fr_dict_attr_t const *enumv, value_data_t const *data, char quote)
Print the value of an attribute to a string.
Definition: value.c:1727