• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

proftpd / proftpd / 30135626854

25 Jul 2026 12:15AM UTC coverage: 93.034% (+0.6%) from 92.428%
30135626854

push

github

51363 of 55209 relevant lines covered (93.03%)

219.82 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

78.1
/src/redis.c
1
/*
2
 * ProFTPD - FTP server daemon
3
 * Copyright (c) 2017-2026 The ProFTPD Project team
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 2 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program; if not, see <https://www.gnu.org/licenses/>.
17
 *
18
 * As a special exemption, The ProFTPD Project team and other respective
19
 * copyright holders give permission to link this program with OpenSSL, and
20
 * distribute the resulting executable, without including the source code for
21
 * OpenSSL in the source distribution.
22
 */
23

24
/* Redis management */
25

26
#include "conf.h"
27

28
#if defined(PR_USE_REDIS)
29

30
#include <hiredis/hiredis.h>
31

32
#if defined(HAVE_HIREDIS_HIREDIS_SSL_H)
33
# include <hiredis/hiredis_ssl.h>
34
#endif /* HAVE_HIREDIS_HIREDIS_SSL_H */
35

36
#if defined(HAVE_HIREDIS_REDISINITIATESSL)
37
# define PR_USE_REDIS_SSL
38
#endif /* HAVE_HIREDIS_REDISINITIATESSL */
39

40
#if !defined(REDIS_CONNECT_RETRIES)
41
# define REDIS_CONNECT_RETRIES        10
42
#endif /* REDIS_CONNECT_RETRIES */
43

44
/* When scanning for keys/lists, how many items to request per command? */
45
#define PR_REDIS_SCAN_SIZE        100
46

47
struct redis_rec {
48
  pool *pool;
49
  module *owner;
50
  redisContext *ctx;
51
#if defined(PR_USE_REDIS_SSL)
52
  redisSSLContext *ssl_ctx;
53
#endif /* PR_USE_REDIS_SSL */
54
  unsigned long flags;
55

56
  /* For tracking the number of "opens"/"closes" on a shared redis_rec,
57
   * as the same struct might be used by multiple modules in the same
58
   * session, each module doing a conn_get()/conn_close().
59
   */
60
  unsigned int refcount;
61

62
  /* Redis server version. */
63
  unsigned int major_version;
64
  unsigned int minor_version;
65
  unsigned int patch_version;
66

67
  /* Table mapping modules to their namespaces */
68
  pr_table_t *namespace_tab;
69
};
70

71
static array_header *redis_sentinels = NULL;
72
static const char *redis_sentinel_master = NULL;
73

74
static const char *redis_server = NULL;
75
static int redis_port = -1;
76
static int redis_use_ssl = FALSE;
77
static unsigned long redis_flags = 0UL;
78
static const char *redis_username = NULL;
79
static const char *redis_password = NULL;
80
static const char *redis_db_idx = NULL;
81
#if defined(PR_USE_REDIS_SSL)
82
static const char *redis_ssl_cacert = NULL;
83
static const char *redis_ssl_cert = NULL;
84
static const char *redis_ssl_key = NULL;
85
#endif /* PR_USE_REDIS_SSL */
86

87
static pr_redis_t *sess_redis = NULL;
88

89
static unsigned long redis_connect_millis = 500;
90
static unsigned long redis_io_millis = 500;
91

92
static const char *trace_channel = "redis";
93

94
static const char *get_reply_type(int reply_type);
95

96
static void millis2timeval(struct timeval *tv, unsigned long millis) {
97
  tv->tv_sec = (millis / 1000);
132✔
98
  tv->tv_usec = (millis - (tv->tv_sec * 1000)) * 1000;
132✔
99
}
132✔
100

101
static const char *redis_strerror(pool *p, redisContext *ctx, int rerrno) {
102
  const char *err_text;
×
103

×
104
  switch (ctx->err) {
105
    case REDIS_ERR_IO:
×
106
      err_text = pstrcat(p, "[io] ", strerror(rerrno), NULL);
×
107
      break;
×
108

×
109
    case REDIS_ERR_EOF:
110
      err_text = pstrcat(p, "[eof] ", ctx->errstr, NULL);
×
111
      break;
×
112

×
113
    case REDIS_ERR_PROTOCOL:
114
      err_text = pstrcat(p, "[protocol] ", ctx->errstr, NULL);
×
115
      break;
×
116

×
117
    case REDIS_ERR_OOM:
118
      err_text = pstrcat(p, "[oom] ", ctx->errstr, NULL);
×
119
      break;
×
120

×
121
    case REDIS_ERR_OTHER:
122
      err_text = pstrcat(p, "[other] ", ctx->errstr, NULL);
×
123
      break;
×
124

×
125
    case REDIS_OK:
126
    default:
127
      err_text = "OK";
128
      break;
129
  }
130

131
  return err_text;
132
}
×
133

134
static int conn_reconnect(pool *p, pr_redis_t *redis) {
135
  int xerrno = 0;
×
136
#if defined(HAVE_HIREDIS_REDISRECONNECT)
×
137
  register unsigned int i;
138

×
139
  if (redis->flags & PR_REDIS_CONN_FL_NO_RECONNECT) {
140
    errno = EPERM;
×
141
    return -1;
×
142
  }
×
143

144
  /* Use the already-provided REDIS_CONNECT_RETRIES from <hiredis/hiredis.h>
145
   * rather than defining our own.
146
   *
147
   * Currently that is a rather low number (3), so I do not feel the need
148
   * for retry delays or exponential backoff at this time.
149
   */
150
  for (i = 0; i < REDIS_CONNECT_RETRIES; i++) {
151
    int res;
×
152

×
153
    pr_trace_msg(trace_channel, 9, "attempt #%u to reconnect", i+1);
154

×
155
    res = redisReconnect(redis->ctx);
156
    xerrno = errno;
×
157
    if (res == REDIS_OK) {
×
158
      pr_trace_msg(trace_channel, 9, "attempt #%u to reconnect succeeded", i+1);
×
159
      return 0;
×
160
    }
×
161

162
    pr_trace_msg(trace_channel, 9, "attempt #%u to reconnect failed: %s",
163
      i+ 1, redis_strerror(p, redis->ctx, xerrno));
×
164
  }
165
#else
166
  xerrno = ENOSYS;
167
#endif /* No redisReconnect() */
168

169
  errno = xerrno;
170
  return -1;
×
171
}
×
172

173
static redisReply *handle_reply(pr_redis_t *redis, const char *cmd,
174
    redisReply *reply) {
422✔
175
  int xerrno;
176
  pool *tmp_pool;
422✔
177

422✔
178
  if (reply != NULL) {
179
    return reply;
422✔
180
  }
181

182
  xerrno = errno;
183
  tmp_pool = make_sub_pool(redis->pool);
×
184
  pr_trace_msg(trace_channel, 2, "error executing %s command: %s", cmd,
×
185
    redis_strerror(tmp_pool, redis->ctx, xerrno));
×
186

187
  if (redis->ctx->err == REDIS_ERR_IO ||
188
      redis->ctx->err == REDIS_ERR_EOF) {
×
189
    int res;
190

×
191
    res = conn_reconnect(tmp_pool, redis);
192
    if (res < 0) {
×
193
      pr_trace_msg(trace_channel, 9, "failed to reconnect: %s",
×
194
        strerror(errno));
×
195
    }
196
  }
197

198
  destroy_pool(tmp_pool);
199
  errno = xerrno;
×
200
  return NULL;
×
201
}
×
202

203
static int ping_server(pr_redis_t *redis) {
204
  const char *cmd;
65✔
205
  redisReply *reply;
65✔
206

65✔
207
  cmd = "PING";
208
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
65✔
209
  reply = redisCommand(redis->ctx, "%s", cmd);
65✔
210
  reply = handle_reply(redis, cmd, reply);
65✔
211
  if (reply == NULL) {
65✔
212
    return -1;
65✔
213
  }
214

215
  /* We COULD assert a "PONG" response here, but really, anything is OK. */
216
  pr_trace_msg(trace_channel, 7, "%s reply: %s", cmd, reply->str);
217
  freeReplyObject(reply);
65✔
218
  return 0;
65✔
219
}
65✔
220

221
static void parse_redis_version(pr_redis_t *redis, redisReply *info) {
222
  pool *tmp_pool;
65✔
223
  unsigned int major, minor, patch;
65✔
224
  char *text, *version_text;
65✔
225

65✔
226
  if (info->type != REDIS_REPLY_STRING) {
227
    pr_trace_msg(trace_channel, 1, "expected STRING reply for INFO, got %s",
65✔
228
      get_reply_type(info->type));
×
229
    return;
230
  }
×
231

232
  tmp_pool = make_sub_pool(redis->pool);
233
  pr_pool_tag(tmp_pool, "Redis version parsing pool");
65✔
234
  text = pstrndup(tmp_pool, info->str, info->len);
65✔
235

65✔
236
  /* Scan the entire INFO string for "redis_version:N.N.N". */
237

238
  version_text = strstr(text, "redis_version:");
239
  if (version_text == NULL) {
65✔
240
    pr_trace_msg(trace_channel, 1, "no `redis_version` found in INFO reply");
65✔
241
    destroy_pool(tmp_pool);
×
242
    return;
×
243
  }
×
244

245
  if (sscanf(version_text, "redis_version:%u.%u.%u", &major, &minor,
246
      &patch) == 3) {
65✔
247
    redis->major_version = major;
248
    redis->minor_version = minor;
65✔
249
    redis->patch_version = patch;
65✔
250

65✔
251
    pr_trace_msg(trace_channel, 9,
252
      "parsed Redis version %u (major), %u (minor), %u (patch) out of INFO",
65✔
253
      redis->major_version, redis->minor_version, redis->patch_version);
254

255
  } else {
256
    pr_trace_msg(trace_channel, 1, "failed to scan Redis version '%s'",
257
      version_text);
×
258
  }
259

260
  destroy_pool(tmp_pool);
261
}
65✔
262

263
static int stat_server(pr_redis_t *redis, const char *section) {
264
  const char *cmd;
65✔
265
  redisReply *reply;
65✔
266

65✔
267
  cmd = "INFO";
268
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
65✔
269
  reply = redisCommand(redis->ctx, "%s %s", cmd, section);
65✔
270
  reply = handle_reply(redis, cmd, reply);
65✔
271
  if (reply == NULL) {
65✔
272
    return -1;
65✔
273
  }
274

275
  if (pr_trace_get_level(trace_channel) >= 25) {
276
    pr_trace_msg(trace_channel, 25, "%s reply: %s", cmd, reply->str);
65✔
277

×
278
  } else {
279
    pr_trace_msg(trace_channel, 7, "%s reply: (text, %lu bytes)", cmd,
280
      (unsigned long) reply->len);
65✔
281
  }
65✔
282

283
  if (redis->major_version == 0 &&
284
      (strcmp(section, "server") == 0 || strcmp(section, "") == 0)) {
65✔
285
    /* We are particularly interested in the Redis server version; we key
65✔
286
     * off of this version to detect when to change our command semantics,
287
     * such as for AUTH.
288
     *
289
     * Thus we parse the server version out of the "server" info, unless
290
     * the version is already known.
291
     */
292
    parse_redis_version(redis, reply);
293
  }
65✔
294

295
  freeReplyObject(reply);
296
  return 0;
65✔
297
}
65✔
298

299
pr_redis_t *pr_redis_conn_get(pool *p, unsigned long flags) {
300
  if (p == NULL) {
4✔
301
    errno = EINVAL;
4✔
302
    return NULL;
1✔
303
  }
1✔
304

305
  if (sess_redis != NULL) {
306
    sess_redis->refcount++;
3✔
307
    return sess_redis;
1✔
308
  }
1✔
309

310
  return pr_redis_conn_new(p, NULL, flags);
311
}
2✔
312

313
static int set_conn_options(pr_redis_t *redis) {
314
  int res, xerrno;
65✔
315
  struct timeval tv;
65✔
316
  pool *tmp_pool;
65✔
317

65✔
318
  tmp_pool = make_sub_pool(redis->pool);
319

65✔
320
  millis2timeval(&tv, redis_io_millis);
321
  res = redisSetTimeout(redis->ctx, tv);
65✔
322
  xerrno = errno;
65✔
323

65✔
324
  if (res == REDIS_ERR) {
325
    pr_trace_msg(trace_channel, 4,
65✔
326
      "error setting %lu ms timeout: %s", redis_io_millis,
×
327
      redis_strerror(tmp_pool, redis->ctx, xerrno));
328
  }
329

330
#if HIREDIS_MAJOR >= 0 && \
331
    HIREDIS_MINOR >= 12
332
  res = redisEnableKeepAlive(redis->ctx);
333
  xerrno = errno;
65✔
334

65✔
335
  if (res == REDIS_ERR) {
336
    pr_trace_msg(trace_channel, 4,
65✔
337
      "error setting keepalive: %s",
×
338
      redis_strerror(tmp_pool, redis->ctx, xerrno));
339
  }
340
#endif /* HiRedis 0.12.0 and later */
341

342
  destroy_pool(tmp_pool);
65✔
343
  return 0;
65✔
344
}
345

346
static void sess_redis_cleanup(void *data) {
65✔
347
  sess_redis = NULL;
65✔
348
}
65✔
349

350
static pr_redis_t *make_redis_conn(pool *p, const char *host, int port,
67✔
351
    int use_ssl) {
352
  int uses_ip = TRUE, xerrno;
67✔
353
  pr_redis_t *redis;
67✔
354
  pool *sub_pool;
67✔
355
  redisContext *ctx;
67✔
356
  struct timeval tv;
67✔
357

358
  millis2timeval(&tv, redis_connect_millis);
67✔
359

360
  /* If the given redis "server" string starts with a '/' character, assume
361
   * that it is a Unix socket path.
362
   */
363
  if (*host == '/') {
67✔
364
    uses_ip = FALSE;
×
365
    ctx = redisConnectUnixWithTimeout(host, tv);
×
366

367
  } else {
368
    ctx = redisConnectWithTimeout(host, port, tv);
67✔
369
  }
370

371
  xerrno = errno;
67✔
372

373
  if (ctx == NULL) {
67✔
374
    errno = ENOMEM;
×
375
    return NULL;
×
376
  }
377

378
  if (ctx->err != 0) {
67✔
379
    pool *tmp_pool;
2✔
380

381
    tmp_pool = make_sub_pool(p);
2✔
382

2✔
383
    if (uses_ip == TRUE) {
2✔
384
      pr_trace_msg(trace_channel, 3,
2✔
385
        "error connecting to %s#%d: %s", host, port,
2✔
386
        redis_strerror(tmp_pool, ctx, xerrno));
387

×
388
    } else {
×
389
      pr_trace_msg(trace_channel, 3,
×
390
        "error connecting to '%s': %s", host,
×
391
        redis_strerror(tmp_pool, ctx, xerrno));
392
    }
×
393

×
394
    destroy_pool(tmp_pool);
×
395
    redisFree(ctx);
×
396
    errno = EIO;
397
    return NULL;
×
398
  }
×
399

×
400
  sub_pool = make_sub_pool(p);
×
401
  pr_pool_tag(sub_pool, "Redis connection pool");
402

×
403
  redis = pcalloc(sub_pool, sizeof(pr_redis_t));
×
404
  redis->pool = sub_pool;
×
405
  redis->ctx = ctx;
×
406

407
#if defined(PR_USE_REDIS_SSL)
×
408
  if (use_ssl == TRUE) {
×
409
    redisSSLContext *ssl_ctx;
×
410
    redisSSLContextError ssl_ctx_err;
×
411

412
    ssl_ctx = redisCreateSSLContext(redis_ssl_cacert, NULL,
413
      redis_ssl_cert, redis_ssl_key, host, &ssl_ctx_err);
2✔
414
    if (ssl_ctx == NULL) {
2✔
415
      pr_trace_msg(trace_channel, 3,
416
        "error creating SSL context: %s", redisSSLContextGetError(ssl_ctx_err));
417

418
    } else {
×
419
      int res;
420

421
      res = redisInitiateSSLWithContext(redis->ctx, ssl_ctx);
422
      if (res != REDIS_OK) {
2✔
423
        pr_trace_msg(trace_channel, 3,
2✔
424
          "TLS handshake error with '%s:%d': %s", host, port,
2✔
425
          redis->ctx->errstr);
426

427
        redisFreeSSLContext(ssl_ctx);
65✔
428
        redisFree(redis->ctx);
65✔
429
        destroy_pool(sub_pool);
430
        errno = EIO;
65✔
431
        return NULL;
65✔
432
      }
65✔
433

434
      pr_trace_msg(trace_channel, 17, "established TLS connection with '%s:%d'",
435
        host, port);
436
      redis->ssl_ctx = ssl_ctx;
437
    }
438
  }
439
#endif /* PR_USE_REDIS_SSL */
440

441
  return redis;
442
}
443

444
static int discover_redis_master(pool *p, const char *host, int port,
445
    const char *master, int use_ssl) {
446
  int res = 0, xerrno = 0;
447
  pool *tmp_pool;
448
  pr_redis_t *redis;
449
  pr_netaddr_t *addr = NULL;
450

451
  tmp_pool = make_sub_pool(p);
452

453
  redis = make_redis_conn(tmp_pool, host, port, use_ssl);
454
  xerrno = errno;
455

456
  if (redis == NULL) {
457
    destroy_pool(tmp_pool);
458

459
    errno = xerrno;
460
    return -1;
461
  }
462

463
  if (master == NULL) {
464
    array_header *masters = NULL;
465

466
    res = pr_redis_sentinel_get_masters(tmp_pool, redis, &masters);
467
    if (res < 0) {
468
      pr_trace_msg(trace_channel, 14, "error getting masters from Sentinel: %s",
65✔
469
        strerror(errno));
470

471
    } else {
1✔
472
      master = ((char **) masters->elts)[0];
473
      pr_trace_msg(trace_channel, 17, "discovered master '%s'", master);
1✔
474
    }
1✔
475
  }
1✔
476

1✔
477
  res = pr_redis_sentinel_get_master_addr(tmp_pool, redis, master, &addr);
478
  xerrno = errno;
1✔
479

480
  if (res < 0) {
1✔
481
    pr_redis_conn_destroy(redis);
1✔
482
    destroy_pool(tmp_pool);
483

1✔
484
    errno = xerrno;
1✔
485
    return -1;
486
  }
1✔
487

1✔
488
  redis_server = pstrdup(p, pr_netaddr_get_ipstr(addr));
489
  redis_port = ntohs(pr_netaddr_get_port(addr));
490

×
491
  pr_redis_conn_destroy(redis);
×
492
  destroy_pool(tmp_pool);
493

×
494
  errno = xerrno;
×
495
  return res;
×
496
}
497

498
pr_redis_t *pr_redis_conn_new(pool *p, module *m, unsigned long flags) {
499
  int default_port, res, xerrno;
×
500
  pr_redis_t *redis;
×
501
  const char *default_host;
502

503
  if (p == NULL) {
504
    errno = EINVAL;
×
505
    return NULL;
×
506
  }
507

×
508
  default_host = redis_server;
×
509
  default_port = redis_port;
×
510

511
  /* Do we have a list of Sentinels configured?  If so, try those first. */
×
512
  if (redis_sentinels != NULL) {
×
513
    register unsigned int i;
514

515
    pr_trace_msg(trace_channel, 17,
×
516
      "querying Sentinels (%u count) for Redis server",
×
517
      redis_sentinels->nelts);
518

×
519
    for (i = 0; i < redis_sentinels->nelts; i++) {
×
520
      pr_netaddr_t *addr;
521
      const char *sentinel;
×
522
      int port;
×
523

524
      addr = ((pr_netaddr_t **) redis_sentinels->elts)[i];
525
      sentinel = pr_netaddr_get_ipstr(addr);
68✔
526
      port = ntohs(pr_netaddr_get_port(addr));
68✔
527

68✔
528
      if (discover_redis_master(p, sentinel, port,
68✔
529
          redis_sentinel_master, redis_use_ssl) == 0) {
530
        pr_trace_msg(trace_channel, 17,
68✔
531
          "discovered Redis server %s:%d using Sentinel #%u (%s:%d)",
1✔
532
          redis_server, redis_port, i+1, sentinel, port);
1✔
533
      }
534
    }
535
  }
67✔
536

67✔
537
  /* If the Sentinels failed to provide a usable host, fall back to the
538
   * default.
539
   */
67✔
540
  if (redis_server == NULL) {
1✔
541
    redis_server = default_host;
542
    redis_port = default_port;
1✔
543
  }
544

545
  if (redis_server == NULL) {
546
    pr_trace_msg(trace_channel, 9, "%s",
3✔
547
      "unable to create new Redis connection: No server configured");
1✔
548
    errno = EPERM;
1✔
549
    return NULL;
1✔
550
  }
551

1✔
552
  redis = make_redis_conn(p, redis_server, redis_port, redis_use_ssl);
1✔
553
  if (redis == NULL) {
1✔
554
    return NULL;
555
  }
1✔
556

557
  redis->owner = m;
×
558
  redis->refcount = 1;
559
  redis->flags = flags;
560

561
  /* The namespace table is null; it will be created if/when callers
562
   * configure namespace prefixes.
563
   */
564
  redis->namespace_tab = NULL;
565

566
  /* Set some of the desired behavior flags on the connection */
567
  res = set_conn_options(redis);
67✔
568
  if (res < 0) {
1✔
569
    xerrno = errno;
1✔
570

571
    pr_redis_conn_destroy(redis);
572
    errno = xerrno;
67✔
573
    return NULL;
1✔
574
  }
575

1✔
576
  res = ping_server(redis);
1✔
577
  if (res < 0) {
578
    xerrno = errno;
579

66✔
580
    pr_redis_conn_destroy(redis);
66✔
581
    errno = xerrno;
582
    return NULL;
583
  }
584

65✔
585
  /* Make sure we are connected to the configured server by querying
65✔
586
   * some stats/info from it.
65✔
587
   */
588
  res = stat_server(redis, "server");
589
  if (res < 0) {
590
    xerrno = errno;
591

65✔
592
    pr_redis_conn_destroy(redis);
593
    errno = xerrno;
594
    return NULL;
65✔
595
  }
65✔
596

×
597
  if (redis_password != NULL) {
598
    if (redis_username != NULL) {
×
599
      res = pr_redis_auth2(redis, redis_username, redis_password);
×
600

×
601
    } else {
602
      res = pr_redis_auth(redis, redis_password);
603
    }
65✔
604

65✔
605
    if (res < 0) {
×
606
      xerrno = errno;
607

×
608
      pr_redis_conn_destroy(redis);
×
609
      errno = xerrno;
×
610
      return NULL;
611
    }
612
  }
613

614
  if (redis_db_idx != NULL) {
615
    res = pr_redis_select(redis, redis_db_idx);
65✔
616
    if (res < 0) {
65✔
617
      xerrno = errno;
×
618

619
      pr_redis_conn_destroy(redis);
×
620
      errno = xerrno;
×
621
      return NULL;
×
622
    }
623
  }
624

65✔
625
  if (sess_redis == NULL) {
×
626
    sess_redis = redis;
×
627

628
    /* Register a cleanup on this redis, so that when it is destroyed, we
629
     * clear this sess_redis pointer, lest it remaining dangling.
×
630
     */
631
    register_cleanup2(redis->pool, NULL, sess_redis_cleanup);
632
  }
×
633

×
634
  return redis;
635
}
×
636

×
637
/* Return TRUE if we actually closed the connection, FALSE if we simply
×
638
 * decremented the refcount.
639
 */
640
int pr_redis_conn_close(pr_redis_t *redis) {
641
  int closed = FALSE;
65✔
642

×
643
  if (redis == NULL) {
×
644
    errno = EINVAL;
×
645
    return -1;
646
  }
×
647

×
648
  if (redis->refcount > 0) {
×
649
    redis->refcount--;
650
  }
651

652
  if (redis->refcount == 0) {
65✔
653
    if (redis->ctx != NULL) {
65✔
654
      const char *cmd = NULL;
655
      redisReply *reply;
656

657
      cmd = "QUIT";
658
      pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
65✔
659
      reply = redisCommand(redis->ctx, "%s", cmd);
660
      if (reply != NULL) {
661
        freeReplyObject(reply);
662
      }
663

664
#if defined(PR_USE_REDIS_SSL)
665
      if (redis->ssl_ctx) {
666
        redisFreeSSLContext(redis->ssl_ctx);
667
        redis->ssl_ctx = NULL;
67✔
668
      }
67✔
669
#endif /* PR_USE_REDIS_SSL */
670

67✔
671
      redisFree(redis->ctx);
1✔
672
      redis->ctx = NULL;
1✔
673
    }
674

675
    if (redis->namespace_tab != NULL) {
66✔
676
      (void) pr_table_empty(redis->namespace_tab);
66✔
677
      (void) pr_table_free(redis->namespace_tab);
678
      redis->namespace_tab = NULL;
679
    }
66✔
680

65✔
681
    closed = TRUE;
65✔
682
  }
65✔
683

684
  return closed;
65✔
685
}
65✔
686

65✔
687
/* Return TRUE if we actually closed the connection, FALSE if we simply
65✔
688
 * decremented the refcount.
65✔
689
 */
690
int pr_redis_conn_destroy(pr_redis_t *redis) {
691
  int closed, destroyed = FALSE;
692

693
  if (redis == NULL) {
694
    errno = EINVAL;
695
    return -1;
696
  }
697

698
  closed = pr_redis_conn_close(redis);
65✔
699
  if (closed < 0) {
65✔
700
    return -1;
701
  }
702

65✔
703
  if (closed == TRUE) {
3✔
704
    if (redis == sess_redis) {
3✔
705
      sess_redis = NULL;
3✔
706
    }
707

708
    destroy_pool(redis->pool);
709
    destroyed = TRUE;
710
  }
711

712
  return destroyed;
713
}
714

715
static int modptr_cmp_cb(const void *k1, size_t ksz1, const void *k2,
716
    size_t ksz2) {
717

67✔
718
  /* Return zero to indicate a match, non-zero otherwise. */
67✔
719
  return (((module *) k1) == ((module *) k2) ? 0 : 1);
720
}
67✔
721

1✔
722
static unsigned int modptr_hash_cb(const void *k, size_t ksz) {
1✔
723
  unsigned int key = 0;
724

725
  /* XXX Yes, this is a bit hacky for "hashing" a pointer value. */
66✔
726

66✔
727
  memcpy(&key, k, sizeof(key));
728
  key ^= (key >> 16);
729

730
  return key;
66✔
731
}
65✔
732

65✔
733
int pr_redis_conn_set_namespace(pr_redis_t *redis, module *m,
734
    const void *prefix, size_t prefixsz) {
735

65✔
736
  if (redis == NULL ||
65✔
737
      m == NULL) {
738
    errno = EINVAL;
739
    return -1;
740
  }
741

742
  if (prefix != NULL &&
5✔
743
      prefixsz == 0) {
744
    errno = EINVAL;
745
    return -1;
746
  }
5✔
747

748
  if (redis->namespace_tab == NULL) {
749
    pr_table_t *tab;
8✔
750

8✔
751
    tab = pr_table_alloc(redis->pool, 0);
752

753
    (void) pr_table_ctl(tab, PR_TABLE_CTL_SET_KEY_CMP, modptr_cmp_cb);
754
    (void) pr_table_ctl(tab, PR_TABLE_CTL_SET_KEY_HASH, modptr_hash_cb);
8✔
755
    redis->namespace_tab = tab;
8✔
756
  }
757

8✔
758
  if (prefix != NULL) {
759
    int count;
760
    void *val;
9✔
761
    size_t valsz;
762

763
    valsz = prefixsz;
9✔
764
    val = palloc(redis->pool, valsz);
9✔
765
    memcpy(val, prefix, prefixsz);
2✔
766

2✔
767
    count = pr_table_kexists(redis->namespace_tab, m, sizeof(module *));
768
    if (count <= 0) {
769
      (void) pr_table_kadd(redis->namespace_tab, m, sizeof(module *), val,
7✔
770
        valsz);
7✔
771

1✔
772
    } else {
1✔
773
      (void) pr_table_kset(redis->namespace_tab, m, sizeof(module *), val,
774
        valsz);
775
    }
6✔
776

3✔
777
  } else {
778
    /* A NULL prefix means the caller is removing their namespace mapping. */
3✔
779
    (void) pr_table_kremove(redis->namespace_tab, m, sizeof(module *), NULL);
780
  }
3✔
781

3✔
782
  return 0;
3✔
783
}
784

785
int pr_redis_conn_get_version(pr_redis_t *redis, unsigned int *major_version,
6✔
786
    unsigned int *minor_version, unsigned int *patch_version) {
3✔
787

3✔
788
  if (redis == NULL) {
3✔
789
    errno = EINVAL;
790
    return -1;
3✔
791
  }
3✔
792

3✔
793
  if (major_version == NULL &&
794
      minor_version == NULL &&
3✔
795
      patch_version == NULL) {
3✔
796
    errno = EINVAL;
3✔
797
    return -1;
798
  }
799

800
  if (major_version != NULL) {
×
801
    *major_version = redis->major_version;
802
  }
803

804
  if (minor_version != NULL) {
805
    *minor_version = redis->minor_version;
806
  }
3✔
807

808
  if (patch_version != NULL) {
809
    *patch_version = redis->patch_version;
810
  }
811

812
  return 0;
5✔
813
}
814

815
int pr_redis_add(pr_redis_t *redis, module *m, const char *key, void *value,
5✔
816
    size_t valuesz, time_t expires) {
1✔
817
  int res;
1✔
818

819
  if (key == NULL) {
820
    errno = EINVAL;
4✔
821
    return -1;
4✔
822
  }
823

1✔
824
  res = pr_redis_kadd(redis, m, key, strlen(key), value, valuesz, expires);
1✔
825
  if (res < 0) {
826
    int xerrno = errno;
827

3✔
828
    pr_trace_msg(trace_channel, 2,
3✔
829
      "error adding key '%s', value (%lu bytes): %s", key,
830
      (unsigned long) valuesz, strerror(xerrno));
831

3✔
832
    errno = xerrno;
1✔
833
    return -1;
834
  }
835

3✔
836
  return 0;
1✔
837
}
838

839
int pr_redis_decr(pr_redis_t *redis, module *m, const char *key, uint32_t decr,
840
    uint64_t *value) {
841
  int res;
842

7✔
843
  if (key == NULL) {
844
    errno = EINVAL;
7✔
845
    return -1;
846
  }
7✔
847

3✔
848
  res = pr_redis_kdecr(redis, m, key, strlen(key), decr, value);
3✔
849
  if (res < 0) {
850
    int xerrno = errno;
851

8✔
852
    pr_trace_msg(trace_channel, 2,
4✔
853
      "error decrementing key '%s' by %lu: %s", key,
1✔
854
      (unsigned long) decr, strerror(xerrno));
855

1✔
856
    errno = xerrno;
857
    return -1;
858
  }
859

1✔
860
  return 0;
1✔
861
}
862

863
void *pr_redis_get(pool *p, pr_redis_t *redis, module *m, const char *key,
864
    size_t *valuesz) {
865
  void *ptr = NULL;
866

8✔
867
  if (key == NULL) {
868
    errno = EINVAL;
8✔
869
    return NULL;
870
  }
8✔
871

3✔
872
  ptr = pr_redis_kget(p, redis, m, key, strlen(key), valuesz);
3✔
873
  if (ptr == NULL) {
874
    int xerrno = errno;
875

5✔
876
    pr_trace_msg(trace_channel, 2,
5✔
877
      "error getting data for key '%s': %s", key, strerror(xerrno));
3✔
878

879
    errno = xerrno;
3✔
880
    return NULL;
881
  }
882

883
  return ptr;
3✔
884
}
3✔
885

886
char *pr_redis_get_str(pool *p, pr_redis_t *redis, module *m, const char *key) {
887
  char *ptr = NULL;
888

889
  if (key == NULL) {
890
    errno = EINVAL;
9✔
891
    return NULL;
892
  }
9✔
893

894
  ptr = pr_redis_kget_str(p, redis, m, key, strlen(key));
9✔
895
  if (ptr == NULL) {
4✔
896
    int xerrno = errno;
4✔
897

898
    pr_trace_msg(trace_channel, 2,
899
      "error getting data for key '%s': %s", key, strerror(xerrno));
5✔
900

5✔
901
    errno = xerrno;
3✔
902
    return NULL;
903
  }
3✔
904

905
  return ptr;
906
}
3✔
907

3✔
908
int pr_redis_incr(pr_redis_t *redis, module *m, const char *key, uint32_t incr,
909
    uint64_t *value) {
910
  int res;
911

912
  if (key == NULL) {
913
    errno = EINVAL;
7✔
914
    return -1;
7✔
915
  }
916

7✔
917
  res = pr_redis_kincr(redis, m, key, strlen(key), incr, value);
4✔
918
  if (res < 0) {
4✔
919
    int xerrno = errno;
920

921
    pr_trace_msg(trace_channel, 2,
3✔
922
      "error incrementing key '%s' by %lu: %s", key,
3✔
923
      (unsigned long) incr, strerror(xerrno));
2✔
924

925
    errno = xerrno;
2✔
926
    return -1;
927
  }
928

2✔
929
  return 0;
2✔
930
}
931

932
int pr_redis_remove(pr_redis_t *redis, module *m, const char *key) {
933
  int res;
934

935
  if (key == NULL) {
8✔
936
    errno = EINVAL;
937
    return -1;
8✔
938
  }
939

8✔
940
  res = pr_redis_kremove(redis, m, key, strlen(key));
3✔
941
  if (res < 0) {
3✔
942
    int xerrno = errno;
943

944
    pr_trace_msg(trace_channel, 2,
5✔
945
      "error removing key '%s': %s", key, strerror(xerrno));
5✔
946

3✔
947
    errno = xerrno;
948
    return -1;
3✔
949
  }
950

951
  return 0;
952
}
3✔
953

3✔
954
int pr_redis_rename(pr_redis_t *redis, module *m, const char *from,
955
    const char *to) {
956
  int res;
957

958
  if (from == NULL ||
959
      to == NULL) {
82✔
960
    errno = EINVAL;
82✔
961
    return -1;
962
  }
82✔
963

3✔
964
  res = pr_redis_krename(redis, m, from, strlen(from), to, strlen(to));
3✔
965
  if (res < 0) {
966
    int xerrno = errno;
967

79✔
968
    pr_trace_msg(trace_channel, 2,
79✔
969
      "error renaming key '%s' to '%s': %s", from, to, strerror(xerrno));
44✔
970

971
    errno = xerrno;
44✔
972
    return -1;
973
  }
974

44✔
975
  return 0;
44✔
976
}
977

978
int pr_redis_set(pr_redis_t *redis, module *m, const char *key, void *value,
979
    size_t valuesz, time_t expires) {
980
  int res;
981

6✔
982
  if (key == NULL) {
983
    errno = EINVAL;
6✔
984
    return -1;
985
  }
6✔
986

6✔
987
  res = pr_redis_kset(redis, m, key, strlen(key), value, valuesz, expires);
4✔
988
  if (res < 0) {
4✔
989
    int xerrno = errno;
990

991
    pr_trace_msg(trace_channel, 2,
2✔
992
      "error setting key '%s', value (%lu bytes): %s", key,
2✔
993
      (unsigned long) valuesz, strerror(xerrno));
1✔
994

995
    errno = xerrno;
1✔
996
    return -1;
997
  }
998

1✔
999
  return 0;
1✔
1000
}
1001

1002
/* Hash operations */
1003
int pr_redis_hash_count(pr_redis_t *redis, module *m, const char *key,
1004
    uint64_t *count) {
1005
  int res;
14✔
1006

1007
  if (key == NULL) {
14✔
1008
    errno = EINVAL;
1009
    return -1;
14✔
1010
  }
3✔
1011

3✔
1012
  res = pr_redis_hash_kcount(redis, m, key, strlen(key), count);
1013
  if (res < 0) {
1014
    int xerrno = errno;
11✔
1015

11✔
1016
    pr_trace_msg(trace_channel, 2,
1✔
1017
      "error counting hash using key '%s': %s", key, strerror(xerrno));
1018

1✔
1019
    errno = xerrno;
1020
    return -1;
1021
  }
1022

1✔
1023
  return 0;
1✔
1024
}
1025

1026
int pr_redis_hash_delete(pr_redis_t *redis, module *m, const char *key,
1027
    const char *field) {
1028
  int res;
1029

1030
  if (key == NULL ||
7✔
1031
      field == NULL) {
1032
    errno = EINVAL;
7✔
1033
    return -1;
1034
  }
7✔
1035

3✔
1036
  res = pr_redis_hash_kdelete(redis, m, key, strlen(key), field, strlen(field));
3✔
1037
  if (res < 0) {
1038
    int xerrno = errno;
1039

4✔
1040
    pr_trace_msg(trace_channel, 2,
4✔
1041
      "error deleting field from hash using key '%s', field '%s': %s", key,
1✔
1042
      field, strerror(xerrno));
1043

1✔
1044
    errno = xerrno;
1045
    return -1;
1046
  }
1✔
1047

1✔
1048
  return 0;
1049
}
1050

1051
int pr_redis_hash_exists(pr_redis_t *redis, module *m, const char *key,
1052
    const char *field) {
1053
  int res;
6✔
1054

1055
  if (key == NULL ||
6✔
1056
      field == NULL) {
1057
    errno = EINVAL;
6✔
1058
    return -1;
6✔
1059
  }
4✔
1060

4✔
1061
  res = pr_redis_hash_kexists(redis, m, key, strlen(key), field, strlen(field));
1062
  if (res < 0) {
1063
    int xerrno = errno;
2✔
1064

2✔
1065
    pr_trace_msg(trace_channel, 2,
1✔
1066
      "error checking existence of hash using key '%s', field '%s': %s", key,
1067
      field, strerror(xerrno));
1✔
1068

1069
    errno = xerrno;
1070
    return -1;
1071
  }
1✔
1072

1✔
1073
  return res;
1074
}
1075

1076
int pr_redis_hash_get(pool *p, pr_redis_t *redis, module *m, const char *key,
1077
    const char *field, void **value, size_t *valuesz) {
1078
  int res;
6✔
1079

1080
  if (key == NULL ||
6✔
1081
      field == NULL) {
1082
    errno = EINVAL;
6✔
1083
    return -1;
6✔
1084
  }
4✔
1085

4✔
1086
  res = pr_redis_hash_kget(p, redis, m, key, strlen(key), field, strlen(field),
1087
    value, valuesz);
1088
  if (res < 0) {
2✔
1089
    int xerrno = errno;
2✔
1090

×
1091
    pr_trace_msg(trace_channel, 2,
1092
      "error getting field from hash using key '%s', field '%s': %s", key,
×
1093
      field, strerror(xerrno));
1094

1095
    errno = xerrno;
1096
    return -1;
×
1097
  }
×
1098

1099
  return 0;
1100
}
1101

1102
int pr_redis_hash_getall(pool *p, pr_redis_t *redis, module *m,
1103
    const char *key, pr_table_t **hash) {
8✔
1104
  int res;
1105

8✔
1106
  if (key == NULL) {
1107
    errno = EINVAL;
8✔
1108
    return -1;
8✔
1109
  }
5✔
1110

5✔
1111
  res = pr_redis_hash_kgetall(p, redis, m, key, strlen(key), hash);
1112
  if (res < 0) {
1113
    int xerrno = errno;
3✔
1114

1115
    pr_trace_msg(trace_channel, 2,
3✔
1116
      "error getting entire hash using key '%s': %s", key, strerror(xerrno));
2✔
1117

1118
    errno = xerrno;
2✔
1119
    return -1;
1120
  }
1121

1122
  return 0;
2✔
1123
}
2✔
1124

1125
int pr_redis_hash_incr(pr_redis_t *redis, module *m, const char *key,
1126
    const char *field, int32_t incr, int64_t *value) {
1127
  int res;
1128

1129
  if (key == NULL ||
7✔
1130
      field == NULL) {
1131
    errno = EINVAL;
7✔
1132
    return -1;
1133
  }
7✔
1134

4✔
1135
  res = pr_redis_hash_kincr(redis, m, key, strlen(key), field, strlen(field),
4✔
1136
    incr, value);
1137
  if (res < 0) {
1138
    int xerrno = errno;
3✔
1139

3✔
1140
    pr_trace_msg(trace_channel, 2,
2✔
1141
      "error incrementing field in hash using key '%s', field '%s': %s", key,
1142
      field, strerror(xerrno));
2✔
1143

1144
    errno = xerrno;
1145
    return -1;
2✔
1146
  }
2✔
1147

1148
  return 0;
1149
}
1150

1151
int pr_redis_hash_keys(pool *p, pr_redis_t *redis, module *m, const char *key,
1152
    array_header **fields) {
8✔
1153
  int res;
1154

8✔
1155
  if (key == NULL) {
1156
    errno = EINVAL;
8✔
1157
    return -1;
8✔
1158
  }
4✔
1159

4✔
1160
  res = pr_redis_hash_kkeys(p, redis, m, key, strlen(key), fields);
1161
  if (res < 0) {
1162
    int xerrno = errno;
4✔
1163

1164
    pr_trace_msg(trace_channel, 2,
4✔
1165
      "error obtaining keys from hash using key '%s': %s", key,
1✔
1166
      strerror(xerrno));
1167

1✔
1168
    errno = xerrno;
1169
    return -1;
1170
  }
1171

1✔
1172
  return 0;
1✔
1173
}
1174

1175
int pr_redis_hash_remove(pr_redis_t *redis, module *m, const char *key) {
1176
  int res;
1177

1178
  if (key == NULL) {
7✔
1179
    errno = EINVAL;
1180
    return -1;
7✔
1181
  }
1182

7✔
1183
  res = pr_redis_hash_kremove(redis, m, key, strlen(key));
4✔
1184
  if (res < 0) {
4✔
1185
    int xerrno = errno;
1186

1187
    pr_trace_msg(trace_channel, 2,
3✔
1188
      "error removing hash using key '%s': %s", key, strerror(xerrno));
3✔
1189

2✔
1190
    errno = xerrno;
1191
    return -1;
2✔
1192
  }
1193

1194
  return 0;
1195
}
2✔
1196

2✔
1197
int pr_redis_hash_set(pr_redis_t *redis, module *m, const char *key,
1198
    const char *field, void *value, size_t valuesz) {
1199
  int res;
1200

1201
  if (key == NULL ||
1202
      field == NULL) {
5✔
1203
    errno = EINVAL;
5✔
1204
    return -1;
1205
  }
5✔
1206

3✔
1207
  res = pr_redis_hash_kset(redis, m, key, strlen(key), field, strlen(field),
3✔
1208
    value, valuesz);
1209
  if (res < 0) {
1210
    int xerrno = errno;
4✔
1211

2✔
1212
    pr_trace_msg(trace_channel, 2,
1✔
1213
      "error setting field in hash using key '%s', field '%s': %s", key, field,
1214
      strerror(xerrno));
1✔
1215

1216
    errno = xerrno;
1217
    return -1;
1✔
1218
  }
1✔
1219

1220
  return 0;
1221
}
1222

1223
int pr_redis_hash_setall(pr_redis_t *redis, module *m, const char *key,
1224
    pr_table_t *hash) {
18✔
1225
  int res;
1226

18✔
1227
  if (key == NULL) {
1228
    errno = EINVAL;
18✔
1229
    return -1;
18✔
1230
  }
4✔
1231

4✔
1232
  res = pr_redis_hash_ksetall(redis, m, key, strlen(key), hash);
1233
  if (res < 0) {
1234
    int xerrno = errno;
14✔
1235

1236
    pr_trace_msg(trace_channel, 2,
14✔
1237
      "error setting hash using key '%s': %s", key, strerror(xerrno));
2✔
1238

1239
    errno = xerrno;
2✔
1240
    return -1;
1241
  }
1242

1243
  return 0;
2✔
1244
}
2✔
1245

1246
int pr_redis_hash_values(pool *p, pr_redis_t *redis, module *m,
1247
    const char *key, array_header **values) {
1248
  int res;
1249

1250
  if (key == NULL) {
6✔
1251
    errno = EINVAL;
1252
    return -1;
6✔
1253
  }
1254

6✔
1255
  res = pr_redis_hash_kvalues(p, redis, m, key, strlen(key), values);
3✔
1256
  if (res < 0) {
3✔
1257
    int xerrno = errno;
1258

1259
    pr_trace_msg(trace_channel, 2,
3✔
1260
      "error getting values of hash using key '%s': %s", key, strerror(xerrno));
3✔
1261

2✔
1262
    errno = xerrno;
1263
    return -1;
2✔
1264
  }
1265

1266
  return 0;
2✔
1267
}
2✔
1268

1269
/* List operations */
1270
int pr_redis_list_append(pr_redis_t *redis, module *m, const char *key,
1271
    void *value, size_t valuesz) {
1272
  int res;
1273

7✔
1274
  if (key == NULL) {
1275
    errno = EINVAL;
7✔
1276
    return -1;
1277
  }
7✔
1278

4✔
1279
  res = pr_redis_list_kappend(redis, m, key, strlen(key), value, valuesz);
4✔
1280
  if (res < 0) {
1281
    int xerrno = errno;
1282

3✔
1283
    pr_trace_msg(trace_channel, 2,
3✔
1284
      "error appending to list using key '%s': %s", key, strerror(xerrno));
2✔
1285

1286
    errno = xerrno;
2✔
1287
    return -1;
1288
  }
1289

2✔
1290
  return 0;
2✔
1291
}
1292

1293
int pr_redis_list_count(pr_redis_t *redis, module *m, const char *key,
1294
    uint64_t *count) {
1295
  int res;
1296

1297
  if (key == NULL) {
19✔
1298
    errno = EINVAL;
1299
    return -1;
19✔
1300
  }
1301

19✔
1302
  res = pr_redis_list_kcount(redis, m, key, strlen(key), count);
3✔
1303
  if (res < 0) {
3✔
1304
    int xerrno = errno;
1305

1306
    pr_trace_msg(trace_channel, 2,
32✔
1307
      "error counting list using key '%s': %s", key, strerror(xerrno));
16✔
1308

2✔
1309
    errno = xerrno;
1310
    return -1;
2✔
1311
  }
1312

1313
  return 0;
2✔
1314
}
2✔
1315

1316
int pr_redis_list_delete(pr_redis_t *redis, module *m, const char *key,
1317
    void *value, size_t valuesz) {
1318
  int res;
1319

1320
  if (value == NULL) {
6✔
1321
    errno = EINVAL;
1322
    return -1;
6✔
1323
  }
1324

6✔
1325
  res = pr_redis_list_kdelete(redis, m, key, strlen(key), value, valuesz);
3✔
1326
  if (res < 0) {
3✔
1327
    int xerrno = errno;
1328

1329
    pr_trace_msg(trace_channel, 2,
3✔
1330
      "error deleting item from list using key '%s': %s", key,
3✔
1331
      strerror(xerrno));
1✔
1332

1333
    errno = xerrno;
1✔
1334
    return -1;
1335
  }
1336

1✔
1337
  return 0;
1✔
1338
}
1339

1340
int pr_redis_list_exists(pr_redis_t *redis, module *m, const char *key,
1341
    unsigned int idx) {
1342
  int res;
1343

7✔
1344
  if (key == NULL) {
1345
    errno = EINVAL;
7✔
1346
    return -1;
1347
  }
7✔
1348

4✔
1349
  res = pr_redis_list_kexists(redis, m, key, strlen(key), idx);
4✔
1350
  if (res < 0) {
1351
    int xerrno = errno;
1352

3✔
1353
    pr_trace_msg(trace_channel, 2,
3✔
1354
      "error checking item at index %u in list using key '%s': %s", idx, key,
2✔
1355
      strerror(xerrno));
1356

2✔
1357
    errno = xerrno;
1358
    return -1;
1359
  }
1360

2✔
1361
  return res;
2✔
1362
}
1363

1364
int pr_redis_list_get(pool *p, pr_redis_t *redis, module *m, const char *key,
1365
    unsigned int idx, void **value, size_t *valuesz) {
1366
  int res;
1367

6✔
1368
  if (key == NULL) {
1369
    errno = EINVAL;
6✔
1370
    return -1;
1371
  }
6✔
1372

3✔
1373
  res = pr_redis_list_kget(p, redis, m, key, strlen(key), idx, value, valuesz);
3✔
1374
  if (res < 0) {
1375
    int xerrno = errno;
1376

3✔
1377
    pr_trace_msg(trace_channel, 2,
3✔
1378
      "error getting item at index %u in list using key '%s': %s", idx, key,
1✔
1379
      strerror(xerrno));
1380

1✔
1381
    errno = xerrno;
1382
    return -1;
1383
  }
1384

1✔
1385
  return res;
1✔
1386
}
1387

1388
int pr_redis_list_getall(pool *p, pr_redis_t *redis, module *m, const char *key,
1389
    array_header **values, array_header **valueszs) {
1390
  int res;
1391

8✔
1392
  if (key == NULL) {
1393
    errno = EINVAL;
8✔
1394
    return -1;
1395
  }
8✔
1396

4✔
1397
  res = pr_redis_list_kgetall(p, redis, m, key, strlen(key), values, valueszs);
4✔
1398
  if (res < 0) {
1399
    int xerrno = errno;
1400

4✔
1401
    pr_trace_msg(trace_channel, 2,
4✔
1402
      "error getting items in list using key '%s': %s", key, strerror(xerrno));
3✔
1403

1404
    errno = xerrno;
3✔
1405
    return -1;
1406
  }
1407

1408
  return res;
3✔
1409
}
3✔
1410

1411
int pr_redis_list_pop(pool *p, pr_redis_t *redis, module *m, const char *key,
1412
    void **value, size_t *valuesz, int flags) {
1413
  int res;
1414

1415
  if (key == NULL) {
7✔
1416
    errno = EINVAL;
1417
    return -1;
7✔
1418
  }
1419

7✔
1420
  res = pr_redis_list_kpop(p, redis, m, key, strlen(key), value, valuesz,
4✔
1421
    flags);
4✔
1422
  if (res < 0) {
1423
    int xerrno = errno;
1424

3✔
1425
    pr_trace_msg(trace_channel, 2,
3✔
1426
      "error popping item from list using key '%s': %s", key, strerror(xerrno));
2✔
1427

1428
    errno = xerrno;
2✔
1429
    return -1;
1430
  }
1431

2✔
1432
  return res;
2✔
1433
}
1434

1435
int pr_redis_list_push(pr_redis_t *redis, module *m, const char *key,
1436
    void *value, size_t valuesz, int flags) {
1437
  int res;
1438

11✔
1439
  if (key == NULL) {
1440
    errno = EINVAL;
11✔
1441
    return -1;
1442
  }
11✔
1443

4✔
1444
  res = pr_redis_list_kpush(redis, m, key, strlen(key), value, valuesz, flags);
4✔
1445
  if (res < 0) {
1446
    int xerrno = errno;
1447

7✔
1448
    pr_trace_msg(trace_channel, 2,
1449
      "error pushing item into list using key '%s': %s", key, strerror(xerrno));
7✔
1450

5✔
1451
    errno = xerrno;
1452
    return -1;
5✔
1453
  }
1454

1455
  return 0;
5✔
1456
}
5✔
1457

1458
int pr_redis_list_remove(pr_redis_t *redis, module *m, const char *key) {
1459
  int res;
1460

1461
  if (key == NULL) {
1462
    errno = EINVAL;
8✔
1463
    return -1;
1464
  }
8✔
1465

1466
  res = pr_redis_list_kremove(redis, m, key, strlen(key));
8✔
1467
  if (res < 0) {
3✔
1468
    int xerrno = errno;
3✔
1469

1470
    pr_trace_msg(trace_channel, 2,
1471
      "error removing list using key '%s': %s", key, strerror(xerrno));
5✔
1472

5✔
1473
    errno = xerrno;
3✔
1474
    return -1;
1475
  }
3✔
1476

1477
  return 0;
1478
}
3✔
1479

3✔
1480
int pr_redis_list_rotate(pool *p, pr_redis_t *redis, module *m,
1481
    const char *key, void **value, size_t *valuesz) {
1482
  int res;
1483

1484
  if (key == NULL) {
1485
    errno = EINVAL;
15✔
1486
    return -1;
15✔
1487
  }
1488

15✔
1489
  res = pr_redis_list_krotate(p, redis, m, key, strlen(key), value, valuesz);
3✔
1490
  if (res < 0) {
3✔
1491
    int xerrno = errno;
1492

1493
    pr_trace_msg(trace_channel, 2,
24✔
1494
      "error rotating list using key '%s': %s", key, strerror(xerrno));
12✔
1495

5✔
1496
    errno = xerrno;
1497
    return -1;
5✔
1498
  }
1499

1500
  return 0;
5✔
1501
}
5✔
1502

1503
int pr_redis_list_set(pr_redis_t *redis, module *m, const char *key,
1504
    unsigned int idx, void *value, size_t valuesz) {
1505
  int res;
1506

1507
  if (key == NULL) {
10✔
1508
    errno = EINVAL;
1509
    return -1;
10✔
1510
  }
1511

10✔
1512
  res = pr_redis_list_kset(redis, m, key, strlen(key), idx, value, valuesz);
4✔
1513
  if (res < 0) {
4✔
1514
    int xerrno = errno;
1515

1516
    pr_trace_msg(trace_channel, 2,
6✔
1517
      "error setting item in list using key '%s', index %u: %s", key, idx,
6✔
1518
      strerror(xerrno));
3✔
1519

1520
    errno = xerrno;
3✔
1521
    return -1;
1522
  }
1523

3✔
1524
  return 0;
3✔
1525
}
1526

1527
int pr_redis_list_setall(pr_redis_t *redis, module *m, const char *key,
1528
    array_header *values, array_header *valueszs) {
1529
  int res;
1530

8✔
1531
  if (key == NULL) {
1532
    errno = EINVAL;
8✔
1533
    return -1;
1534
  }
8✔
1535

3✔
1536
  res = pr_redis_list_ksetall(redis, m, key, strlen(key), values, valueszs);
3✔
1537
  if (res < 0) {
1538
    int xerrno = errno;
1539

5✔
1540
    pr_trace_msg(trace_channel, 2,
5✔
1541
      "error setting items in list using key '%s': %s", key, strerror(xerrno));
4✔
1542

1543
    errno = xerrno;
4✔
1544
    return -1;
1545
  }
1546

1547
  return 0;
4✔
1548
}
4✔
1549

1550
/* Set operations */
1551
int pr_redis_set_add(pr_redis_t *redis, module *m, const char *key,
1552
    void *value, size_t valuesz) {
1553
  int res;
1554

9✔
1555
  if (key == NULL) {
1556
    errno = EINVAL;
9✔
1557
    return -1;
1558
  }
9✔
1559

3✔
1560
  res = pr_redis_set_kadd(redis, m, key, strlen(key), value, valuesz);
3✔
1561
  if (res < 0) {
1562
    int xerrno = errno;
1563

6✔
1564
    pr_trace_msg(trace_channel, 2,
6✔
1565
      "error adding item to set using key '%s': %s", key, strerror(xerrno));
5✔
1566

1567
    errno = xerrno;
5✔
1568
    return -1;
1569
  }
1570

5✔
1571
  return 0;
5✔
1572
}
1573

1574
int pr_redis_set_count(pr_redis_t *redis, module *m, const char *key,
1575
    uint64_t *count) {
1576
  int res;
1577

1578
  if (key == NULL) {
14✔
1579
    errno = EINVAL;
1580
    return -1;
14✔
1581
  }
1582

14✔
1583
  res = pr_redis_set_kcount(redis, m, key, strlen(key), count);
3✔
1584
  if (res < 0) {
3✔
1585
    int xerrno = errno;
1586

1587
    pr_trace_msg(trace_channel, 2,
11✔
1588
      "error counting set using key '%s': %s", key, strerror(xerrno));
11✔
1589

3✔
1590
    errno = xerrno;
1591
    return -1;
3✔
1592
  }
1593

1594
  return 0;
3✔
1595
}
3✔
1596

1597
int pr_redis_set_delete(pr_redis_t *redis, module *m, const char *key,
1598
    void *value, size_t valuesz) {
1599
  int res;
1600

1601
  if (key == NULL) {
6✔
1602
    errno = EINVAL;
1603
    return -1;
6✔
1604
  }
1605

6✔
1606
  res = pr_redis_set_kdelete(redis, m, key, strlen(key), value, valuesz);
3✔
1607
  if (res < 0) {
3✔
1608
    int xerrno = errno;
1609

1610
    pr_trace_msg(trace_channel, 2,
3✔
1611
      "error deleting item from set using key '%s': %s", key, strerror(xerrno));
3✔
1612

1✔
1613
    errno = xerrno;
1614
    return -1;
1✔
1615
  }
1616

1617
  return 0;
1✔
1618
}
1✔
1619

1620
int pr_redis_set_exists(pr_redis_t *redis, module *m, const char *key,
1621
    void *value, size_t valuesz) {
1622
  int res;
1623

1624
  if (key == NULL) {
7✔
1625
    errno = EINVAL;
1626
    return -1;
7✔
1627
  }
1628

7✔
1629
  res = pr_redis_set_kexists(redis, m, key, strlen(key), value, valuesz);
3✔
1630
  if (res < 0) {
3✔
1631
    int xerrno = errno;
1632

1633
    pr_trace_msg(trace_channel, 2,
4✔
1634
      "error checking item in set using key '%s': %s", key, strerror(xerrno));
4✔
1635

3✔
1636
    errno = xerrno;
1637
    return -1;
3✔
1638
  }
1639

1640
  return res;
3✔
1641
}
3✔
1642

1643
int pr_redis_set_getall(pool *p, pr_redis_t *redis, module *m, const char *key,
1644
    array_header **values, array_header **valueszs) {
1645
  int res;
1646

1647
  if (key == NULL) {
7✔
1648
    errno = EINVAL;
1649
    return -1;
7✔
1650
  }
1651

7✔
1652
  res = pr_redis_set_kgetall(p, redis, m, key, strlen(key), values, valueszs);
3✔
1653
  if (res < 0) {
3✔
1654
    int xerrno = errno;
1655

1656
    pr_trace_msg(trace_channel, 2,
4✔
1657
      "error getting items in set using key '%s': %s", key, strerror(xerrno));
4✔
1658

2✔
1659
    errno = xerrno;
1660
    return -1;
2✔
1661
  }
1662

1663
  return res;
2✔
1664
}
2✔
1665

1666
int pr_redis_set_remove(pr_redis_t *redis, module *m, const char *key) {
1667
  int res;
1668

1669
  if (key == NULL) {
1670
    errno = EINVAL;
7✔
1671
    return -1;
1672
  }
7✔
1673

1674
  res = pr_redis_set_kremove(redis, m, key, strlen(key));
7✔
1675
  if (res < 0) {
4✔
1676
    int xerrno = errno;
4✔
1677

1678
    pr_trace_msg(trace_channel, 2,
1679
      "error removing set using key '%s': %s", key, strerror(xerrno));
3✔
1680

3✔
1681
    errno = xerrno;
2✔
1682
    return -1;
1683
  }
2✔
1684

1685
  return 0;
1686
}
2✔
1687

2✔
1688
int pr_redis_set_setall(pr_redis_t *redis, module *m, const char *key,
1689
    array_header *values, array_header *valueszs) {
1690
  int res;
1691

1692
  if (key == NULL) {
1693
    errno = EINVAL;
10✔
1694
    return -1;
10✔
1695
  }
1696

10✔
1697
  res = pr_redis_set_ksetall(redis, m, key, strlen(key), values, valueszs);
3✔
1698
  if (res < 0) {
3✔
1699
    int xerrno = errno;
1700

1701
    pr_trace_msg(trace_channel, 2,
14✔
1702
      "error setting items in set using key '%s': %s", key, strerror(xerrno));
7✔
1703

4✔
1704
    errno = xerrno;
1705
    return -1;
4✔
1706
  }
1707

1708
  return 0;
4✔
1709
}
4✔
1710

1711
/* Sorted Set operations */
1712
int pr_redis_sorted_set_add(pr_redis_t *redis, module *m, const char *key,
1713
    void *value, size_t valuesz, float score) {
1714
  int res;
1715

9✔
1716
  if (key == NULL) {
1717
    errno = EINVAL;
9✔
1718
    return -1;
1719
  }
9✔
1720

3✔
1721
  res = pr_redis_sorted_set_kadd(redis, m, key, strlen(key), value, valuesz,
3✔
1722
    score);
1723
  if (res < 0) {
1724
    int xerrno = errno;
6✔
1725

6✔
1726
    pr_trace_msg(trace_channel, 2,
5✔
1727
      "error adding item with score %0.3f to sorted set using key '%s': %s",
1728
      score, key, strerror(xerrno));
5✔
1729

1730
    errno = xerrno;
1731
    return -1;
5✔
1732
  }
5✔
1733

1734
  return 0;
1735
}
1736

1737
int pr_redis_sorted_set_count(pr_redis_t *redis, module *m, const char *key,
1738
    uint64_t *count) {
1739
  int res;
17✔
1740

1741
  if (key == NULL) {
17✔
1742
    errno = EINVAL;
1743
    return -1;
17✔
1744
  }
3✔
1745

3✔
1746
  res = pr_redis_sorted_set_kcount(redis, m, key, strlen(key), count);
1747
  if (res < 0) {
1748
    int xerrno = errno;
14✔
1749

1750
    pr_trace_msg(trace_channel, 2,
14✔
1751
      "error counting sorted set using key '%s': %s", key, strerror(xerrno));
3✔
1752

1753
    errno = xerrno;
3✔
1754
    return -1;
1755
  }
1756

1757
  return 0;
3✔
1758
}
3✔
1759

1760
int pr_redis_sorted_set_delete(pr_redis_t *redis, module *m, const char *key,
1761
    void *value, size_t valuesz) {
1762
  int res;
1763

1764
  if (key == NULL) {
6✔
1765
    errno = EINVAL;
1766
    return -1;
6✔
1767
  }
1768

6✔
1769
  res = pr_redis_sorted_set_kdelete(redis, m, key, strlen(key), value, valuesz);
3✔
1770
  if (res < 0) {
3✔
1771
    int xerrno = errno;
1772

1773
    pr_trace_msg(trace_channel, 2,
3✔
1774
      "error deleting item from sorted set using key '%s': %s", key,
3✔
1775
      strerror(xerrno));
1✔
1776

1777
    errno = xerrno;
1✔
1778
    return -1;
1779
  }
1780

1✔
1781
  return 0;
1✔
1782
}
1783

1784
int pr_redis_sorted_set_exists(pr_redis_t *redis, module *m, const char *key,
1785
    void *value, size_t valuesz) {
1786
  int res;
1787

7✔
1788
  if (key == NULL) {
1789
    errno = EINVAL;
7✔
1790
    return -1;
1791
  }
7✔
1792

3✔
1793
  res = pr_redis_sorted_set_kexists(redis, m, key, strlen(key), value, valuesz);
3✔
1794
  if (res < 0) {
1795
    int xerrno = errno;
1796

4✔
1797
    pr_trace_msg(trace_channel, 2,
4✔
1798
      "error checking item in sorted set using key '%s': %s", key,
3✔
1799
      strerror(xerrno));
1800

3✔
1801
    errno = xerrno;
1802
    return -1;
1803
  }
1804

3✔
1805
  return res;
3✔
1806
}
1807

1808
int pr_redis_sorted_set_getn(pool *p, pr_redis_t *redis, module *m,
1809
    const char *key, unsigned int offset, unsigned int len,
1810
    array_header **values, array_header **valueszs, int flags) {
1811
  int res;
7✔
1812

1813
  if (key == NULL) {
7✔
1814
    errno = EINVAL;
1815
    return -1;
7✔
1816
  }
3✔
1817

3✔
1818
  res = pr_redis_sorted_set_kgetn(p, redis, m, key, strlen(key), offset, len,
1819
    values, valueszs, flags);
1820
  if (res < 0) {
4✔
1821
    int xerrno = errno;
4✔
1822

2✔
1823
    pr_trace_msg(trace_channel, 2,
1824
      "error getting %u %s from sorted set using key '%s': %s", len,
2✔
1825
      len != 1 ? "items" : "item", key, strerror(xerrno));
1826

1827
    errno = xerrno;
1828
    return -1;
2✔
1829
  }
2✔
1830

1831
  return res;
1832
}
1833

1834
int pr_redis_sorted_set_incr(pr_redis_t *redis, module *m, const char *key,
1835
    void *value, size_t valuesz, float incr, float *score) {
10✔
1836
  int res;
1837

1838
  if (key == NULL) {
10✔
1839
    errno = EINVAL;
1840
    return -1;
10✔
1841
  }
4✔
1842

4✔
1843
  res = pr_redis_sorted_set_kincr(redis, m, key, strlen(key), value, valuesz,
1844
    incr, score);
1845
  if (res < 0) {
6✔
1846
    int xerrno = errno;
1847

6✔
1848
    pr_trace_msg(trace_channel, 2,
3✔
1849
      "error incrementing item by %0.3f in sorted set using key '%s': %s",
1850
      incr, key, strerror(xerrno));
3✔
1851

1852
    errno = xerrno;
1853
    return -1;
1854
  }
3✔
1855

3✔
1856
  return res;
1857
}
1858

1859
int pr_redis_sorted_set_remove(pr_redis_t *redis, module *m, const char *key) {
1860
  int res;
1861

8✔
1862
  if (key == NULL) {
1863
    errno = EINVAL;
8✔
1864
    return -1;
1865
  }
8✔
1866

3✔
1867
  res = pr_redis_sorted_set_kremove(redis, m, key, strlen(key));
3✔
1868
  if (res < 0) {
1869
    int xerrno = errno;
1870

5✔
1871
    pr_trace_msg(trace_channel, 2,
1872
      "error removing sorted set using key '%s': %s", key, strerror(xerrno));
5✔
1873

4✔
1874
    errno = xerrno;
1875
    return -1;
4✔
1876
  }
1877

1878
  return 0;
1879
}
4✔
1880

4✔
1881
int pr_redis_sorted_set_score(pr_redis_t *redis, module *m, const char *key,
1882
    void *value, size_t valuesz, float *score) {
1883
  int res;
1884

1885
  if (key == NULL) {
1886
    errno = EINVAL;
5✔
1887
    return -1;
5✔
1888
  }
1889

5✔
1890
  res = pr_redis_sorted_set_kscore(redis, m, key, strlen(key), value, valuesz,
3✔
1891
    score);
3✔
1892
  if (res < 0) {
1893
    int xerrno = errno;
1894

4✔
1895
    pr_trace_msg(trace_channel, 2,
2✔
1896
      "error getting score for item in sorted set using key '%s': %s", key,
1✔
1897
      strerror(xerrno));
1898

1✔
1899
    errno = xerrno;
1900
    return -1;
1901
  }
1✔
1902

1✔
1903
  return res;
1904
}
1905

1906
int pr_redis_sorted_set_set(pr_redis_t *redis, module *m, const char *key,
1907
    void *value, size_t valuesz, float score) {
1908
  int res;
8✔
1909

1910
  if (key == NULL) {
8✔
1911
    errno = EINVAL;
1912
    return -1;
8✔
1913
  }
3✔
1914

3✔
1915
  res = pr_redis_sorted_set_kset(redis, m, key, strlen(key), value, valuesz,
1916
    score);
1917
  if (res < 0) {
5✔
1918
    int xerrno = errno;
1919

5✔
1920
    pr_trace_msg(trace_channel, 2,
4✔
1921
      "error setting item to score %0.3f in sorted set using key '%s': %s",
1922
      score, key, strerror(xerrno));
4✔
1923

1924
    errno = xerrno;
1925
    return -1;
1926
  }
4✔
1927

4✔
1928
  return 0;
1929
}
1930

1931
int pr_redis_sorted_set_setall(pr_redis_t *redis, module *m, const char *key,
1932
    array_header *values, array_header *valueszs, array_header *scores) {
1933
  int res;
7✔
1934

1935
  if (key == NULL) {
7✔
1936
    errno = EINVAL;
1937
    return -1;
7✔
1938
  }
3✔
1939

3✔
1940
  res = pr_redis_sorted_set_ksetall(redis, m, key, strlen(key), values,
1941
    valueszs, scores);
1942
  if (res < 0) {
4✔
1943
    int xerrno = errno;
1944

4✔
1945
    pr_trace_msg(trace_channel, 2,
3✔
1946
      "error setting items in sorted set using key '%s': %s", key,
1947
      strerror(xerrno));
3✔
1948

1949
    errno = xerrno;
1950
    return -1;
1951
  }
3✔
1952

3✔
1953
  return 0;
1954
}
1955

1956
static const char *get_namespace_key(pool *p, pr_redis_t *redis, module *m,
1957
    const char *key, size_t *keysz) {
1958

12✔
1959
  if (m != NULL &&
1960
      redis->namespace_tab != NULL) {
12✔
1961
    const char *prefix = NULL;
1962
    size_t prefixsz = 0;
12✔
1963

3✔
1964
    prefix = pr_table_kget(redis->namespace_tab, m, sizeof(module *),
3✔
1965
      &prefixsz);
1966
    if (prefix != NULL) {
1967
      char *new_key;
9✔
1968
      size_t new_keysz;
1969

9✔
1970
      pr_trace_msg(trace_channel, 25,
8✔
1971
        "using namespace prefix '%s' for module 'mod_%s.c'", prefix, m->name);
1972

8✔
1973
      /* Since the given key may not be text, we cannot simply use pstrcat()
1974
       * to prepend our namespace value.
1975
       */
1976
      new_keysz = prefixsz + *keysz;
8✔
1977
      new_key = palloc(p, new_keysz);
8✔
1978
      memcpy(new_key, prefix, prefixsz);
1979
      memcpy(new_key + prefixsz, key, *keysz);
1980

1981
      key = new_key;
1982
      *keysz = new_keysz;
1983
    }
281✔
1984
  }
1985

1986
  return key;
281✔
1987
}
281✔
1988

3✔
1989
static const char *get_reply_type(int reply_type) {
3✔
1990
  const char *type_name;
1991

3✔
1992
  switch (reply_type) {
1993
    case REDIS_REPLY_STRING:
3✔
1994
      type_name = "STRING";
3✔
1995
      break;
3✔
1996

1997
    case REDIS_REPLY_ARRAY:
3✔
1998
      type_name = "ARRAY";
1999
      break;
2000

2001
    case REDIS_REPLY_INTEGER:
2002
      type_name = "INTEGER";
2003
      break;
3✔
2004

3✔
2005
    case REDIS_REPLY_NIL:
3✔
2006
      type_name = "NIL";
3✔
2007
      break;
2008

3✔
2009
    case REDIS_REPLY_STATUS:
3✔
2010
      type_name = "STATUS";
2011
      break;
2012

2013
    case REDIS_REPLY_ERROR:
281✔
2014
      type_name = "ERROR";
2015
      break;
2016

13✔
2017
    default:
13✔
2018
      type_name = "unknown";
2019
  }
13✔
2020

2021
  return type_name;
2022
}
2023

2024
int pr_redis_command(pr_redis_t *redis, const array_header *args,
×
2025
    int reply_type) {
×
2026
  register unsigned int i;
×
2027
  pool *tmp_pool = NULL;
2028
  array_header *arglens;
×
2029
  const char *cmd = NULL;
×
2030
  redisReply *reply;
×
2031
  int redis_reply_type;
2032

×
2033
  if (redis == NULL ||
×
2034
      args == NULL ||
×
2035
      args->nelts == 0) {
2036
    errno = EINVAL;
×
2037
    return -1;
×
2038
  }
×
2039

2040
  switch (reply_type) {
12✔
2041
    case PR_REDIS_REPLY_TYPE_STRING:
12✔
2042
      redis_reply_type = REDIS_REPLY_STRING;
12✔
2043
      break;
2044

×
2045
    case PR_REDIS_REPLY_TYPE_INTEGER:
×
2046
      redis_reply_type = REDIS_REPLY_INTEGER;
2047
      break;
2048

13✔
2049
    case PR_REDIS_REPLY_TYPE_NIL:
2050
      redis_reply_type = REDIS_REPLY_NIL;
2051
      break;
7✔
2052

2053
    case PR_REDIS_REPLY_TYPE_ARRAY:
7✔
2054
      redis_reply_type = REDIS_REPLY_ARRAY;
7✔
2055
      break;
7✔
2056

7✔
2057
    case PR_REDIS_REPLY_TYPE_STATUS:
7✔
2058
      redis_reply_type = REDIS_REPLY_STATUS;
7✔
2059
      break;
2060

7✔
2061
    case PR_REDIS_REPLY_TYPE_ERROR:
7✔
2062
      redis_reply_type = REDIS_REPLY_ERROR;
5✔
2063
      break;
3✔
2064

3✔
2065
    default:
2066
      errno = EINVAL;
2067
      return -1;
4✔
2068
  }
2069

2070
  tmp_pool = make_sub_pool(redis->pool);
2071
  pr_pool_tag(tmp_pool, "Redis Command pool");
2072

2073
  arglens = make_array(tmp_pool, args->nelts, sizeof(size_t));
2074
  for (i = 0; i < args->nelts; i++) {
2075
    pr_signals_handle();
2076
    *((size_t *) push_array(arglens)) = strlen(((char **) args->elts)[i]);
2077
  }
2078

2079
  cmd = ((char **) args->elts)[0];
2080
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
2081
  reply = redisCommandArgv(redis->ctx, args->nelts, args->elts, arglens->elts);
2082
  reply = handle_reply(redis, cmd, reply);
2083
  if (reply == NULL) {
2084
    destroy_pool(tmp_pool);
2085
    errno = EIO;
2086
    return -1;
2087
  }
2088

2089
  if (reply->type != redis_reply_type) {
2090
    pr_trace_msg(trace_channel, 2,
2091
      "expected %s reply for %s, got %s", get_reply_type(redis_reply_type), cmd,
2092
      get_reply_type(reply->type));
1✔
2093

1✔
2094
    if (reply->type == REDIS_REPLY_ERROR) {
1✔
2095
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
2096
    }
2097

3✔
2098
    freeReplyObject(reply);
3✔
2099
    destroy_pool(tmp_pool);
2100
    errno = EINVAL;
3✔
2101
    return -1;
10✔
2102
  }
4✔
2103

4✔
2104
  switch (reply->type) {
2105
    case REDIS_REPLY_STRING:
2106
    case REDIS_REPLY_STATUS:
3✔
2107
    case REDIS_REPLY_ERROR:
3✔
2108
      pr_trace_msg(trace_channel, 7, "%s %s reply: %.*s", cmd,
3✔
2109
        get_reply_type(reply->type), (int) reply->len, reply->str);
3✔
2110
      break;
3✔
2111

×
2112
    case REDIS_REPLY_INTEGER:
×
2113
      pr_trace_msg(trace_channel, 7, "%s INTEGER reply: %lld", cmd,
×
2114
        reply->integer);
2115
      break;
2116

3✔
2117
    case REDIS_REPLY_NIL:
×
2118
      pr_trace_msg(trace_channel, 7, "%s NIL reply", cmd);
2119
      break;
2120

2121
    case REDIS_REPLY_ARRAY:
×
2122
      pr_trace_msg(trace_channel, 7, "%s ARRAY reply: (%lu %s)", cmd,
×
2123
        (unsigned long) reply->elements,
2124
        reply->elements != 1 ? "elements" : "element");
2125
      break;
×
2126

×
2127
    default:
×
2128
      break;
×
2129
  }
2130

2131
  freeReplyObject(reply);
3✔
2132
  destroy_pool(tmp_pool);
2✔
2133
  return 0;
2134
}
2135

2✔
2136
int pr_redis_auth2(pr_redis_t *redis, const char *username,
2✔
2137
    const char *password) {
2✔
2138
  const char *cmd;
2139
  pool *tmp_pool;
1✔
2140
  redisReply *reply;
1✔
2141

2142
  if (redis == NULL ||
1✔
2143
      username == NULL ||
2144
      password == NULL) {
×
2145
    errno = EINVAL;
×
2146
    return -1;
×
2147
  }
2148

×
2149
  tmp_pool = make_sub_pool(redis->pool);
×
2150
  pr_pool_tag(tmp_pool, "Redis AUTH pool");
2151

×
2152
  cmd = "AUTH";
×
2153
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
2154

2155
  /* Redis 6.x changed the AUTH semantics, now requiring a username. */
2156
  if (redis->major_version >= 6) {
2157
    reply = redisCommand(redis->ctx, "%s %s %s", cmd, username, password);
2158

3✔
2159
  } else {
3✔
2160
    reply = redisCommand(redis->ctx, "%s %s", cmd, password);
3✔
2161
  }
2162

2163
  reply = handle_reply(redis, cmd, reply);
7✔
2164
  if (reply == NULL) {
2165
    pr_trace_msg(trace_channel, 2,
7✔
2166
      "error authenticating client: %s", strerror(errno));
7✔
2167
    destroy_pool(tmp_pool);
7✔
2168
    errno = EIO;
2169
    return -1;
7✔
2170
  }
7✔
2171

2172
  if (reply->type != REDIS_REPLY_STRING &&
5✔
2173
      reply->type != REDIS_REPLY_STATUS) {
5✔
2174
    pr_trace_msg(trace_channel, 2,
2175
      "expected STRING or STATUS reply for %s, got %s", cmd,
2176
      get_reply_type(reply->type));
2✔
2177

2✔
2178
    if (reply->type == REDIS_REPLY_ERROR) {
2179
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
2✔
2180
    }
2✔
2181

2182
    freeReplyObject(reply);
2183
    destroy_pool(tmp_pool);
2✔
2184
    errno = EINVAL;
2✔
2185
    return -1;
2186
  }
2187

×
2188
  pr_trace_msg(trace_channel, 7, "%s reply: %.*s", cmd, (int) reply->len,
2189
    reply->str);
2190

2✔
2191
  freeReplyObject(reply);
2✔
2192
  destroy_pool(tmp_pool);
×
2193
  return 0;
×
2194
}
×
2195

×
2196
int pr_redis_auth(pr_redis_t *redis, const char *password) {
×
2197
  return pr_redis_auth2(redis, "default", password);
2198
}
2199

2✔
2200
int pr_redis_select(pr_redis_t *redis, const char *db_idx) {
2201
  const char *cmd;
1✔
2202
  pool *tmp_pool;
2203
  redisReply *reply;
2204

2205
  if (redis == NULL ||
1✔
2206
      db_idx == NULL) {
1✔
2207
    errno = EINVAL;
2208
    return -1;
2209
  }
1✔
2210

1✔
2211
  tmp_pool = make_sub_pool(redis->pool);
1✔
2212
  pr_pool_tag(tmp_pool, "Redis SELECT pool");
1✔
2213

2214
  cmd = "SELECT";
2215
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
1✔
2216
  reply = redisCommand(redis->ctx, "%s %s", cmd, db_idx);
2217
  reply = handle_reply(redis, cmd, reply);
2218
  if (reply == NULL) {
1✔
2219
    pr_trace_msg(trace_channel, 2,
1✔
2220
      "error selecting database '%s': %s", db_idx, strerror(errno));
1✔
2221
    destroy_pool(tmp_pool);
2222
    errno = EIO;
2223
    return -1;
3✔
2224
  }
3✔
2225

2226
  if (reply->type != REDIS_REPLY_STRING &&
2227
      reply->type != REDIS_REPLY_STATUS) {
7✔
2228
    pr_trace_msg(trace_channel, 2,
7✔
2229
      "expected STRING or STATUS reply for %s, got %s", cmd,
7✔
2230
      get_reply_type(reply->type));
7✔
2231

2232
    if (reply->type == REDIS_REPLY_ERROR) {
7✔
2233
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
7✔
2234
    }
2✔
2235

2✔
2236
    freeReplyObject(reply);
2237
    destroy_pool(tmp_pool);
2238
    errno = EINVAL;
5✔
2239
    return -1;
5✔
2240
  }
2241

5✔
2242
  pr_trace_msg(trace_channel, 7, "%s reply: %.*s", cmd, (int) reply->len,
5✔
2243
    reply->str);
5✔
2244

5✔
2245
  freeReplyObject(reply);
5✔
2246
  destroy_pool(tmp_pool);
×
2247
  return 0;
×
2248
}
×
2249

×
2250
int pr_redis_kadd(pr_redis_t *redis, module *m, const char *key, size_t keysz,
×
2251
    void *value, size_t valuesz, time_t expires) {
2252
  return pr_redis_kset(redis, m, key, keysz, value, valuesz, expires);
2253
}
5✔
2254

2255
int pr_redis_kdecr(pr_redis_t *redis, module *m, const char *key, size_t keysz,
3✔
2256
    uint32_t decr, uint64_t *value) {
2257
  pool *tmp_pool = NULL;
2258
  const char *cmd = NULL;
2259
  redisReply *reply;
3✔
2260

3✔
2261
  if (redis == NULL ||
2262
      m == NULL ||
2263
      key == NULL ||
3✔
2264
      decr == 0) {
3✔
2265
    errno = EINVAL;
3✔
2266
    return -1;
3✔
2267
  }
2268

2269
  tmp_pool = make_sub_pool(redis->pool);
2✔
2270
  pr_pool_tag(tmp_pool, "Redis DECRBY pool");
2271

2272
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
2✔
2273

2✔
2274
  cmd = "DECRBY";
2✔
2275
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
2276
  reply = redisCommand(redis->ctx, "%s %b %lu", cmd, key, keysz,
2277
    (unsigned long) decr);
4✔
2278
  reply = handle_reply(redis, cmd, reply);
2279
  if (reply == NULL) {
4✔
2280
    pr_trace_msg(trace_channel, 2,
2281
      "error decrementing key (%lu bytes) by %lu using %s: %s",
2282
      (unsigned long) keysz, (unsigned long) decr, cmd, strerror(errno));
5✔
2283
    destroy_pool(tmp_pool);
2284
    errno = EIO;
5✔
2285
    return -1;
5✔
2286
  }
5✔
2287

2288
  if (reply->type != REDIS_REPLY_INTEGER) {
5✔
2289
    pr_trace_msg(trace_channel, 2,
5✔
2290
      "expected INTEGER reply for %s, got %s", cmd,
5✔
2291
      get_reply_type(reply->type));
5✔
2292

1✔
2293
    if (reply->type == REDIS_REPLY_ERROR) {
1✔
2294
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
2295
    }
2296

4✔
2297
    freeReplyObject(reply);
4✔
2298
    destroy_pool(tmp_pool);
2299
    errno = EINVAL;
4✔
2300
    return -1;
2301
  }
4✔
2302

4✔
2303
  /* Note: DECRBY will automatically set the key value to zero if it does
4✔
2304
   * not already exist.  To detect a nonexistent key, then, we look to
2305
   * see if the return value is exactly our requested decrement.  If so,
4✔
2306
   * REMOVE the auto-created key, and return ENOENT.
4✔
2307
   */
×
2308
  if ((decr * -1) == (uint32_t) reply->integer) {
2309
    freeReplyObject(reply);
×
2310
    destroy_pool(tmp_pool);
×
2311
    (void) pr_redis_kremove(redis, m, key, keysz);
×
2312
    errno = ENOENT;
×
2313
    return -1;
2314
  }
2315

4✔
2316
  pr_trace_msg(trace_channel, 7, "%s reply: %lld", cmd, reply->integer);
1✔
2317

2318
  if (value != NULL) {
2319
    *value = (uint64_t) reply->integer;
2320
  }
1✔
2321

1✔
2322
  freeReplyObject(reply);
2323
  destroy_pool(tmp_pool);
2324
  return 0;
1✔
2325
}
1✔
2326

1✔
2327
void *pr_redis_kget(pool *p, pr_redis_t *redis, module *m, const char *key,
1✔
2328
    size_t keysz, size_t *valuesz) {
2329
  const char *cmd;
2330
  pool *tmp_pool;
2331
  redisReply *reply;
2332
  char *data = NULL;
2333

2334
  if (p == NULL ||
2335
      redis == NULL ||
3✔
2336
      m == NULL ||
1✔
2337
      key == NULL ||
1✔
2338
      valuesz == NULL) {
1✔
2339
    errno = EINVAL;
1✔
2340
    return NULL;
1✔
2341
  }
2342

2343
  tmp_pool = make_sub_pool(redis->pool);
2✔
2344
  pr_pool_tag(tmp_pool, "Redis GET pool");
2345

2✔
2346
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
1✔
2347

2348
  cmd = "GET";
2349
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
2✔
2350
  reply = redisCommand(redis->ctx, "%s %b", cmd, key, keysz);
2✔
2351
  reply = handle_reply(redis, cmd, reply);
2✔
2352
  if (reply == NULL) {
2353
    pr_trace_msg(trace_channel, 2,
2354
      "error getting data for key (%lu bytes) using %s: %s",
5✔
2355
      (unsigned long) keysz, cmd, strerror(errno));
2356
    destroy_pool(tmp_pool);
5✔
2357
    errno = EIO;
5✔
2358
    return NULL;
5✔
2359
  }
5✔
2360

2361
  if (reply->type == REDIS_REPLY_NIL) {
5✔
2362
    pr_trace_msg(trace_channel, 7, "%s reply: nil", cmd);
5✔
2363
    freeReplyObject(reply);
5✔
2364
    destroy_pool(tmp_pool);
5✔
2365
    errno = ENOENT;
2366
    return NULL;
1✔
2367
  }
1✔
2368

2369
  if (reply->type != REDIS_REPLY_STRING) {
2370
    pr_trace_msg(trace_channel, 2,
4✔
2371
      "expected STRING reply for %s, got %s", cmd, get_reply_type(reply->type));
4✔
2372
    freeReplyObject(reply);
2373
    destroy_pool(tmp_pool);
4✔
2374
    errno = EINVAL;
2375
    return NULL;
4✔
2376
  }
4✔
2377

4✔
2378
  pr_trace_msg(trace_channel, 7, "%s reply: %.*s", cmd, (int) reply->len,
4✔
2379
    reply->str);
4✔
2380

×
2381
  if (valuesz != NULL) {
2382
    *valuesz = (uint64_t) reply->len;
×
2383
  }
×
2384

×
2385
  data = palloc(p, reply->len);
×
2386
  memcpy(data, reply->str, reply->len);
2387

2388
  freeReplyObject(reply);
4✔
2389
  destroy_pool(tmp_pool);
2✔
2390
  return data;
2✔
2391
}
2✔
2392

2✔
2393
char *pr_redis_kget_str(pool *p, pr_redis_t *redis, module *m, const char *key,
2✔
2394
    size_t keysz) {
2395
  const char *cmd;
2396
  pool *tmp_pool;
2✔
2397
  redisReply *reply;
×
2398
  char *data = NULL;
2399

×
2400
  if (p == NULL ||
×
2401
      redis == NULL ||
×
2402
      m == NULL ||
×
2403
      key == NULL) {
2404
    errno = EINVAL;
2405
    return NULL;
2✔
2406
  }
2407

2408
  tmp_pool = make_sub_pool(redis->pool);
2✔
2409
  pr_pool_tag(tmp_pool, "Redis GET pool");
2✔
2410

2411
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
2412

2✔
2413
  cmd = "GET";
2✔
2414
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
2415
  reply = redisCommand(redis->ctx, "%s %b", cmd, key, keysz);
2✔
2416
  reply = handle_reply(redis, cmd, reply);
2✔
2417
  if (reply == NULL) {
2✔
2418
    pr_trace_msg(trace_channel, 2,
2419
      "error getting data for key (%lu bytes) using %s: %s",
2420
      (unsigned long) keysz, cmd, strerror(errno));
3✔
2421
    destroy_pool(tmp_pool);
2422
    errno = EIO;
3✔
2423
    return NULL;
3✔
2424
  }
3✔
2425

3✔
2426
  if (reply->type == REDIS_REPLY_NIL) {
2427
    pr_trace_msg(trace_channel, 7, "%s reply: nil", cmd);
3✔
2428
    freeReplyObject(reply);
3✔
2429
    destroy_pool(tmp_pool);
3✔
2430
    errno = ENOENT;
3✔
2431
    return NULL;
×
2432
  }
×
2433

2434
  if (reply->type != REDIS_REPLY_STRING) {
2435
    pr_trace_msg(trace_channel, 2,
3✔
2436
      "expected STRING reply for %s, got %s", cmd, get_reply_type(reply->type));
3✔
2437
    freeReplyObject(reply);
2438
    destroy_pool(tmp_pool);
3✔
2439
    errno = EINVAL;
2440
    return NULL;
3✔
2441
  }
3✔
2442

3✔
2443
  pr_trace_msg(trace_channel, 7, "%s reply: %.*s", cmd, (int) reply->len,
3✔
2444
    reply->str);
3✔
2445

×
2446
  data = pstrndup(p, reply->str, reply->len);
2447

×
2448
  freeReplyObject(reply);
×
2449
  destroy_pool(tmp_pool);
×
2450
  return data;
×
2451
}
2452

2453
int pr_redis_kincr(pr_redis_t *redis, module *m, const char *key, size_t keysz,
3✔
2454
    uint32_t incr, uint64_t *value) {
2✔
2455
  pool *tmp_pool = NULL;
2✔
2456
  const char *cmd = NULL;
2✔
2457
  redisReply *reply;
2✔
2458

2✔
2459
  if (redis == NULL ||
2460
      m == NULL ||
2461
      key == NULL ||
1✔
2462
      incr == 0) {
×
2463
    errno = EINVAL;
2464
    return -1;
×
2465
  }
×
2466

×
2467
  tmp_pool = make_sub_pool(redis->pool);
×
2468
  pr_pool_tag(tmp_pool, "Redis INCRBY pool");
2469

2470
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
1✔
2471

2472
  cmd = "INCRBY";
2473
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
1✔
2474
  reply = redisCommand(redis->ctx, "%s %b %lu", cmd, key, keysz,
2475
    (unsigned long) incr);
1✔
2476
  reply = handle_reply(redis, cmd, reply);
1✔
2477
  if (reply == NULL) {
1✔
2478
    pr_trace_msg(trace_channel, 2,
2479
      "error incrementing key (%lu bytes) by %lu using %s: %s",
2480
      (unsigned long) keysz, (unsigned long) incr, cmd, strerror(errno));
5✔
2481
    destroy_pool(tmp_pool);
2482
    errno = EIO;
5✔
2483
    return -1;
5✔
2484
  }
5✔
2485

2486
  if (reply->type != REDIS_REPLY_INTEGER) {
5✔
2487
    pr_trace_msg(trace_channel, 2,
5✔
2488
      "expected INTEGER reply for %s, got %s", cmd,
5✔
2489
      get_reply_type(reply->type));
5✔
2490

1✔
2491
    if (reply->type == REDIS_REPLY_ERROR) {
1✔
2492
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
2493
    }
2494

4✔
2495
    freeReplyObject(reply);
4✔
2496
    destroy_pool(tmp_pool);
2497
    errno = EINVAL;
4✔
2498
    return -1;
2499
  }
4✔
2500

4✔
2501
  /* Note: INCRBY will automatically set the key value to zero if it does
4✔
2502
   * not already exist.  To detect a nonexistent key, then, we look to
2503
   * see if the return value is exactly our requested increment.  If so,
4✔
2504
   * REMOVE the auto-created key, and return ENOENT.
4✔
2505
   */
×
2506
  if (incr == (uint32_t) reply->integer) {
2507
    freeReplyObject(reply);
×
2508
    destroy_pool(tmp_pool);
×
2509
    (void) pr_redis_kremove(redis, m, key, keysz);
×
2510
    errno = ENOENT;
×
2511
    return -1;
2512
  }
2513

4✔
2514
  pr_trace_msg(trace_channel, 7, "%s reply: %lld", cmd, reply->integer);
1✔
2515

2516
  if (value != NULL) {
2517
    *value = (uint64_t) reply->integer;
2518
  }
1✔
2519

1✔
2520
  freeReplyObject(reply);
2521
  destroy_pool(tmp_pool);
2522
  return 0;
1✔
2523
}
1✔
2524

1✔
2525
int pr_redis_kremove(pr_redis_t *redis, module *m, const char *key,
1✔
2526
    size_t keysz) {
2527
  int xerrno = 0;
2528
  pool *tmp_pool = NULL;
2529
  const char *cmd = NULL;
2530
  redisReply *reply;
2531
  long long count;
2532

2533
  if (redis == NULL ||
3✔
2534
      m == NULL ||
1✔
2535
      key == NULL) {
1✔
2536
    errno = EINVAL;
1✔
2537
    return -1;
1✔
2538
  }
1✔
2539

2540
  tmp_pool = make_sub_pool(redis->pool);
2541
  pr_pool_tag(tmp_pool, "Redis DEL pool");
2✔
2542

2543
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
2✔
2544

1✔
2545
  cmd = "DEL";
2546
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
2547
  reply = redisCommand(redis->ctx, "%s %b", cmd, key, keysz);
2✔
2548
  xerrno = errno;
2✔
2549

2✔
2550
  reply = handle_reply(redis, cmd, reply);
2551
  if (reply == NULL) {
2552
    pr_trace_msg(trace_channel, 2,
107✔
2553
      "error removing key (%lu bytes): %s", (unsigned long) keysz,
2554
      strerror(errno));
107✔
2555
    destroy_pool(tmp_pool);
107✔
2556
    errno = xerrno;
107✔
2557
    return -1;
107✔
2558
  }
107✔
2559

2560
  if (reply->type != REDIS_REPLY_INTEGER) {
107✔
2561
    pr_trace_msg(trace_channel, 2,
107✔
2562
      "expected INTEGER reply for %s, got %s", cmd,
2563
      get_reply_type(reply->type));
×
2564
    freeReplyObject(reply);
×
2565
    destroy_pool(tmp_pool);
2566
    errno = EINVAL;
2567
    return -1;
107✔
2568
  }
107✔
2569

2570
  pr_trace_msg(trace_channel, 7, "%s reply: %lld", cmd, reply->integer);
107✔
2571
  count = reply->integer;
2572

107✔
2573
  freeReplyObject(reply);
107✔
2574
  destroy_pool(tmp_pool);
107✔
2575

107✔
2576
  if (count == 0) {
2577
    /* No keys removed. */
107✔
2578
    errno = ENOENT;
107✔
2579
    return -1;
×
2580
  }
2581

2582
  return 0;
×
2583
}
×
2584

×
2585
int pr_redis_krename(pr_redis_t *redis, module *m, const char *from,
2586
    size_t fromsz, const char *to, size_t tosz) {
2587
  int xerrno = 0;
107✔
2588
  pool *tmp_pool = NULL;
×
2589
  const char *cmd = NULL;
2590
  redisReply *reply;
2591

×
2592
  if (redis == NULL ||
×
2593
      m == NULL ||
×
2594
      from == NULL ||
×
2595
      fromsz == 0 ||
2596
      to == NULL ||
2597
      tosz == 0) {
107✔
2598
    errno = EINVAL;
107✔
2599
    return -1;
2600
  }
107✔
2601

107✔
2602
  tmp_pool = make_sub_pool(redis->pool);
2603
  pr_pool_tag(tmp_pool, "Redis RENAME pool");
107✔
2604

2605
  from = get_namespace_key(tmp_pool, redis, m, from, &fromsz);
58✔
2606
  to = get_namespace_key(tmp_pool, redis, m, to, &tosz);
58✔
2607

2608
  cmd = "RENAME";
2609
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
2610
  reply = redisCommand(redis->ctx, "%s %b %b", cmd, from, fromsz, to, tosz);
2611
  xerrno = errno;
2612

2✔
2613
  reply = handle_reply(redis, cmd, reply);
2614
  if (reply == NULL) {
2✔
2615
    pr_trace_msg(trace_channel, 2,
2✔
2616
      "error renaming key (from %lu bytes, to %lu bytes): %s",
2✔
2617
      (unsigned long) fromsz, (unsigned long) tosz, strerror(errno));
2✔
2618
    destroy_pool(tmp_pool);
2619
    errno = xerrno;
2✔
2620
    return -1;
2✔
2621
  }
2✔
2622

2✔
2623
  if (reply->type != REDIS_REPLY_STRING &&
2✔
2624
      reply->type != REDIS_REPLY_STATUS) {
2✔
2625
    xerrno = EINVAL;
×
2626

×
2627
    pr_trace_msg(trace_channel, 2,
2628
      "expected STRING or STATUS reply for %s, got %s", cmd,
2629
      get_reply_type(reply->type));
2✔
2630

2✔
2631
    if (reply->type == REDIS_REPLY_ERROR) {
2632
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
2✔
2633

2✔
2634
      /* Note: In order to provide ENOENT semantics here, we have to be
2635
       * naughty, and assume the contents of this error message.
2✔
2636
       */
2✔
2637
      if (strstr(reply->str, "no such key") != NULL) {
2✔
2638
        xerrno = ENOENT;
2✔
2639
      }
2640
    }
2✔
2641

2✔
2642
    freeReplyObject(reply);
×
2643
    destroy_pool(tmp_pool);
2644
    errno = xerrno;
2645
    return -1;
×
2646
  }
×
2647

×
2648
  pr_trace_msg(trace_channel, 7, "%s reply: %.*s", cmd, (int) reply->len,
2649
    reply->str);
2650

2✔
2651
  freeReplyObject(reply);
2652
  destroy_pool(tmp_pool);
1✔
2653
  return 0;
2654
}
1✔
2655

2656
int pr_redis_kset(pr_redis_t *redis, module *m, const char *key, size_t keysz,
2657
    void *value, size_t valuesz, time_t expires) {
2658
  pool *tmp_pool = NULL;
1✔
2659
  const char *cmd = NULL;
1✔
2660
  redisReply *reply;
2661

2662
  /* XXX Should we allow null values to be added, thus allowing use of keys
2663
   * as sentinels?
2664
   */
1✔
2665
  if (redis == NULL ||
1✔
2666
      m == NULL ||
2667
      key == NULL ||
2668
      value == NULL) {
2669
    errno = EINVAL;
1✔
2670
    return -1;
1✔
2671
  }
1✔
2672

1✔
2673
  tmp_pool = make_sub_pool(redis->pool);
2674
  pr_pool_tag(tmp_pool, "Redis SET pool");
2675

1✔
2676
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
2677

2678
  if (expires > 0) {
1✔
2679
    cmd = "SETEX";
1✔
2680
    pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
1✔
2681
    reply = redisCommand(redis->ctx, "%s %b %lu %b", cmd, key, keysz,
2682
      (unsigned long) expires, value, valuesz);
2683

15✔
2684
  } else {
2685
    cmd = "SET";
15✔
2686
    pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
15✔
2687
    reply = redisCommand(redis->ctx, "%s %b %b", cmd, key, keysz, value,
15✔
2688
      valuesz);
2689
  }
2690

2691
  reply = handle_reply(redis, cmd, reply);
2692
  if (reply == NULL) {
15✔
2693
    pr_trace_msg(trace_channel, 2,
15✔
2694
      "error adding key (%lu bytes), value (%lu bytes) using %s: %s",
15✔
2695
      (unsigned long) keysz, (unsigned long) valuesz, cmd, strerror(errno));
15✔
2696
    destroy_pool(tmp_pool);
2✔
2697
    errno = EIO;
2✔
2698
    return -1;
2699
  }
2700

13✔
2701
  pr_trace_msg(trace_channel, 7, "%s reply: %s", cmd, reply->str);
13✔
2702
  freeReplyObject(reply);
2703
  destroy_pool(tmp_pool);
13✔
2704
  return 0;
2705
}
13✔
2706

2✔
2707
int pr_redis_hash_kcount(pr_redis_t *redis, module *m, const char *key,
2✔
2708
    size_t keysz, uint64_t *count) {
2✔
2709
  int xerrno = 0;
2710
  pool *tmp_pool = NULL;
2711
  const char *cmd = NULL;
2712
  redisReply *reply;
11✔
2713

11✔
2714
  if (redis == NULL ||
11✔
2715
      m == NULL ||
2716
      key == NULL ||
2717
      keysz == 0 ||
2718
      count == NULL) {
13✔
2719
    errno = EINVAL;
13✔
2720
    return -1;
×
2721
  }
2722

×
2723
  tmp_pool = make_sub_pool(redis->pool);
×
2724
  pr_pool_tag(tmp_pool, "Redis HLEN pool");
×
2725

×
2726
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
2727

2728
  cmd = "HLEN";
13✔
2729
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
13✔
2730
  reply = redisCommand(redis->ctx, "%s %b", cmd, key, keysz);
13✔
2731
  xerrno = errno;
13✔
2732

2733
  reply = handle_reply(redis, cmd, reply);
2734
  if (reply == NULL) {
4✔
2735
    pr_trace_msg(trace_channel, 2,
2736
      "error getting count of hash using key (%lu bytes): %s",
4✔
2737
      (unsigned long) keysz, strerror(errno));
4✔
2738
    destroy_pool(tmp_pool);
4✔
2739
    errno = xerrno;
4✔
2740
    return -1;
2741
  }
4✔
2742

4✔
2743
  if (reply->type != REDIS_REPLY_INTEGER) {
4✔
2744
    pr_trace_msg(trace_channel, 2,
4✔
2745
      "expected INTEGER reply for %s, got %s", cmd,
2746
      get_reply_type(reply->type));
1✔
2747

1✔
2748
    if (reply->type == REDIS_REPLY_ERROR) {
2749
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
2750
    }
3✔
2751

3✔
2752
    freeReplyObject(reply);
2753
    destroy_pool(tmp_pool);
3✔
2754
    errno = EINVAL;
2755
    return -1;
3✔
2756
  }
3✔
2757

3✔
2758
  pr_trace_msg(trace_channel, 7, "%s reply: %lld", cmd, reply->integer);
3✔
2759
  *count = reply->integer;
2760

3✔
2761
  freeReplyObject(reply);
3✔
2762
  destroy_pool(tmp_pool);
×
2763
  return 0;
2764
}
2765

×
2766
int pr_redis_hash_kdelete(pr_redis_t *redis, module *m, const char *key,
×
2767
    size_t keysz, const char *field, size_t fieldsz) {
×
2768
  int xerrno = 0, exists = FALSE;
2769
  pool *tmp_pool = NULL;
2770
  const char *cmd = NULL;
3✔
2771
  redisReply *reply;
×
2772

2773
  if (redis == NULL ||
2774
      m == NULL ||
2775
      key == NULL ||
×
2776
      keysz == 0 ||
×
2777
      field == NULL ||
2778
      fieldsz == 0) {
2779
    errno = EINVAL;
×
2780
    return -1;
×
2781
  }
×
2782

×
2783
  tmp_pool = make_sub_pool(redis->pool);
2784
  pr_pool_tag(tmp_pool, "Redis HDEL pool");
2785

3✔
2786
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
3✔
2787

2788
  cmd = "HDEL";
3✔
2789
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
3✔
2790
  reply = redisCommand(redis->ctx, "%s %b %b", cmd, key, keysz, field, fieldsz);
3✔
2791
  xerrno = errno;
2792

2793
  reply = handle_reply(redis, cmd, reply);
2✔
2794
  if (reply == NULL) {
2795
    pr_trace_msg(trace_channel, 2,
2✔
2796
      "error getting count of hash using key (%lu bytes): %s",
2✔
2797
      (unsigned long) keysz, strerror(errno));
2✔
2798
    destroy_pool(tmp_pool);
2✔
2799
    errno = xerrno;
2800
    return -1;
2✔
2801
  }
2✔
2802

2✔
2803
  if (reply->type != REDIS_REPLY_INTEGER) {
2✔
2804
    pr_trace_msg(trace_channel, 2,
2✔
2805
      "expected INTEGER reply for %s, got %s", cmd,
2✔
2806
      get_reply_type(reply->type));
×
2807

×
2808
    if (reply->type == REDIS_REPLY_ERROR) {
2809
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
2810
    }
2✔
2811

2✔
2812
    freeReplyObject(reply);
2813
    destroy_pool(tmp_pool);
2✔
2814
    errno = EINVAL;
2815
    return -1;
2✔
2816
  }
2✔
2817

2✔
2818
  pr_trace_msg(trace_channel, 7, "%s reply: %lld", cmd, reply->integer);
2✔
2819
  exists = reply->integer ? TRUE : FALSE;
2820

2✔
2821
  freeReplyObject(reply);
2✔
2822
  destroy_pool(tmp_pool);
×
2823

2824
  if (exists == FALSE) {
2825
    errno = ENOENT;
×
2826
    return -1;
×
2827
  }
×
2828

2829
  return 0;
2830
}
2✔
2831

×
2832
int pr_redis_hash_kexists(pr_redis_t *redis, module *m, const char *key,
2833
    size_t keysz, const char *field, size_t fieldsz) {
2834
  int xerrno = 0, exists = FALSE;
2835
  pool *tmp_pool = NULL;
×
2836
  const char *cmd = NULL;
×
2837
  redisReply *reply;
2838

2839
  if (redis == NULL ||
×
2840
      m == NULL ||
×
2841
      key == NULL ||
×
2842
      keysz == 0 ||
×
2843
      field == NULL ||
2844
      fieldsz == 0) {
2845
    errno = EINVAL;
2✔
2846
    return -1;
2✔
2847
  }
2848

2✔
2849
  tmp_pool = make_sub_pool(redis->pool);
2✔
2850
  pr_pool_tag(tmp_pool, "Redis HEXISTS pool");
2851

2✔
2852
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
1✔
2853

1✔
2854
  cmd = "HEXISTS";
2855
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
2856
  reply = redisCommand(redis->ctx, "%s %b %b", cmd, key, keysz, field, fieldsz);
2857
  xerrno = errno;
2858

2859
  reply = handle_reply(redis, cmd, reply);
6✔
2860
  if (reply == NULL) {
2861
    pr_trace_msg(trace_channel, 2,
6✔
2862
      "error getting count of hash using key (%lu bytes): %s",
6✔
2863
      (unsigned long) keysz, strerror(errno));
6✔
2864
    destroy_pool(tmp_pool);
6✔
2865
    errno = xerrno;
2866
    return -1;
6✔
2867
  }
6✔
2868

6✔
2869
  if (reply->type != REDIS_REPLY_INTEGER) {
6✔
2870
    pr_trace_msg(trace_channel, 2,
6✔
2871
      "expected INTEGER reply for %s, got %s", cmd,
6✔
2872
      get_reply_type(reply->type));
×
2873

×
2874
    if (reply->type == REDIS_REPLY_ERROR) {
2875
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
2876
    }
6✔
2877

6✔
2878
    freeReplyObject(reply);
2879
    destroy_pool(tmp_pool);
6✔
2880
    errno = EINVAL;
2881
    return -1;
6✔
2882
  }
6✔
2883

6✔
2884
  pr_trace_msg(trace_channel, 7, "%s reply: %lld", cmd, reply->integer);
6✔
2885
  exists = reply->integer ? TRUE : FALSE;
2886

6✔
2887
  freeReplyObject(reply);
6✔
2888
  destroy_pool(tmp_pool);
×
2889
  return exists;
2890
}
2891

×
2892
int pr_redis_hash_kget(pool *p, pr_redis_t *redis, module *m, const char *key,
×
2893
    size_t keysz, const char *field, size_t fieldsz, void **value,
×
2894
    size_t *valuesz) {
2895
  int xerrno = 0, exists = FALSE;
2896
  pool *tmp_pool = NULL;
6✔
2897
  const char *cmd = NULL;
×
2898
  redisReply *reply;
2899

2900
  if (p == NULL ||
2901
      redis == NULL ||
×
2902
      m == NULL ||
×
2903
      key == NULL ||
2904
      keysz == 0 ||
2905
      field == NULL ||
×
2906
      fieldsz == 0 ||
×
2907
      value == NULL) {
×
2908
    errno = EINVAL;
×
2909
    return -1;
2910
  }
2911

6✔
2912
  tmp_pool = make_sub_pool(redis->pool);
6✔
2913
  pr_pool_tag(tmp_pool, "Redis HGET pool");
2914

6✔
2915
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
6✔
2916

6✔
2917
  cmd = "HGET";
2918
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
2919
  reply = redisCommand(redis->ctx, "%s %b %b", cmd, key, keysz, field, fieldsz);
3✔
2920
  xerrno = errno;
2921

2922
  reply = handle_reply(redis, cmd, reply);
3✔
2923
  if (reply == NULL) {
3✔
2924
    pr_trace_msg(trace_channel, 2,
3✔
2925
      "error getting item for field in hash using key (%lu bytes): %s",
3✔
2926
      (unsigned long) keysz, strerror(errno));
2927
    destroy_pool(tmp_pool);
3✔
2928
    errno = xerrno;
3✔
2929
    return -1;
3✔
2930
  }
3✔
2931

3✔
2932
  if (reply->type != REDIS_REPLY_STRING &&
3✔
2933
      reply->type != REDIS_REPLY_NIL) {
3✔
2934
    pr_trace_msg(trace_channel, 2,
3✔
2935
      "expected STRING or NIL reply for %s, got %s", cmd,
1✔
2936
      get_reply_type(reply->type));
1✔
2937

2938
    if (reply->type == REDIS_REPLY_ERROR) {
2939
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
2✔
2940
    }
2✔
2941

2942
    freeReplyObject(reply);
2✔
2943
    destroy_pool(tmp_pool);
2944
    errno = EINVAL;
2✔
2945
    return -1;
2✔
2946
  }
2✔
2947

2✔
2948
  if (reply->type == REDIS_REPLY_STRING) {
2949
    pr_trace_msg(trace_channel, 7, "%s reply: (%lu bytes)", cmd,
2✔
2950
      (unsigned long) reply->len);
2✔
2951

×
2952
    *value = palloc(p, reply->len);
2953
    memcpy(*value, reply->str, reply->len);
2954

×
2955
    if (valuesz != NULL) {
×
2956
      *valuesz = reply->len;
×
2957
    }
2958

2959
    exists = TRUE;
2✔
2960

2961
  } else {
×
2962
    pr_trace_msg(trace_channel, 7, "%s reply: nil", cmd);
2963
  }
2964

2965
  freeReplyObject(reply);
×
2966
  destroy_pool(tmp_pool);
×
2967

2968
  if (exists == FALSE) {
2969
    errno = ENOENT;
×
2970
    return -1;
×
2971
  }
×
2972

×
2973
  return 0;
2974
}
2975

2✔
2976
int pr_redis_hash_kgetall(pool *p, pr_redis_t *redis, module *m,
1✔
2977
    const char *key, size_t keysz, pr_table_t **hash) {
1✔
2978
  int res, xerrno = 0;
2979
  pool *tmp_pool = NULL;
1✔
2980
  const char *cmd = NULL;
1✔
2981
  redisReply *reply;
2982

1✔
2983
  if (p == NULL ||
1✔
2984
      redis == NULL ||
2985
      m == NULL ||
2986
      key == NULL ||
2987
      keysz == 0 ||
2988
      hash == NULL) {
2989
    errno = EINVAL;
1✔
2990
    return -1;
2991
  }
2992

2✔
2993
  tmp_pool = make_sub_pool(redis->pool);
2✔
2994
  pr_pool_tag(tmp_pool, "Redis HGETALL pool");
2995

2✔
2996
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
1✔
2997

1✔
2998
  cmd = "HGETALL";
2999
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
3000
  reply = redisCommand(redis->ctx, "%s %b", cmd, key, keysz);
3001
  xerrno = errno;
3002

3003
  reply = handle_reply(redis, cmd, reply);
3✔
3004
  if (reply == NULL) {
3005
    pr_trace_msg(trace_channel, 2,
3✔
3006
      "error getting hash using key (%lu bytes): %s",
3✔
3007
      (unsigned long) keysz, strerror(errno));
3✔
3008
    destroy_pool(tmp_pool);
3✔
3009
    errno = xerrno;
3010
    return -1;
3✔
3011
  }
3✔
3012

3✔
3013
  if (reply->type != REDIS_REPLY_ARRAY) {
3✔
3014
    pr_trace_msg(trace_channel, 2,
3✔
3015
      "expected ARRAY reply for %s, got %s", cmd, get_reply_type(reply->type));
3✔
3016

1✔
3017
    if (reply->type == REDIS_REPLY_ERROR) {
1✔
3018
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
3019
    }
3020

2✔
3021
    freeReplyObject(reply);
2✔
3022
    destroy_pool(tmp_pool);
3023
    errno = EINVAL;
2✔
3024
    return -1;
3025
  }
2✔
3026

2✔
3027
  if (reply->elements > 0) {
2✔
3028
    register unsigned int i;
2✔
3029

3030
    pr_trace_msg(trace_channel, 7, "%s reply: %lu %s", cmd,
2✔
3031
      (unsigned long) reply->elements,
2✔
3032
      reply->elements != 1 ? "elements" : "element");
×
3033

3034
    *hash = pr_table_alloc(p, 0);
3035

×
3036
    for (i = 0; i < reply->elements; i += 2) {
×
3037
      redisReply *key_elt, *value_elt;
×
3038
      void *key_data = NULL, *value_data = NULL;
3039
      size_t key_datasz = 0, value_datasz = 0;
3040

2✔
3041
      key_elt = reply->element[i];
×
3042
      if (key_elt->type == REDIS_REPLY_STRING) {
3043
        key_datasz = key_elt->len;
3044
        key_data = palloc(p, key_datasz);
×
3045
        memcpy(key_data, key_elt->str, key_datasz);
×
3046

3047
      } else {
3048
        pr_trace_msg(trace_channel, 2,
×
3049
          "expected STRING element at index %u, got %s", i,
×
3050
          get_reply_type(key_elt->type));
×
3051
      }
×
3052

3053
      value_elt = reply->element[i+1];
3054
      if (value_elt->type == REDIS_REPLY_STRING) {
2✔
3055
        value_datasz = value_elt->len;
1✔
3056
        value_data = palloc(p, value_datasz);
3057
        memcpy(value_data, value_elt->str, value_datasz);
1✔
3058

3059
      } else {
3060
        pr_trace_msg(trace_channel, 2,
3061
          "expected STRING element at index %u, got %s", i + 2,
1✔
3062
          get_reply_type(value_elt->type));
3063
      }
3✔
3064

2✔
3065
      if (key_data != NULL &&
2✔
3066
          value_data != NULL) {
2✔
3067
        if (pr_table_kadd(*hash, key_data, key_datasz, value_data,
3068
            value_datasz) < 0) {
2✔
3069
          pr_trace_msg(trace_channel, 2,
2✔
3070
            "error adding key (%lu bytes), value (%lu bytes) to hash: %s",
2✔
3071
            (unsigned long) key_datasz, (unsigned long) value_datasz,
2✔
3072
            strerror(errno));
2✔
3073

3074
        } else {
3075
          pr_trace_msg(trace_channel, 25,
×
3076
            "added key (%lu bytes), value (%lu bytes) to hash",
3077
            (unsigned long) key_datasz, (unsigned long) value_datasz);
3078
        }
3079
      }
3080
    }
2✔
3081

2✔
3082
    res = 0;
2✔
3083

2✔
3084
  } else {
2✔
3085
    xerrno = ENOENT;
3086
    res = -1;
3087
  }
×
3088

3089
  freeReplyObject(reply);
3090
  destroy_pool(tmp_pool);
3091

3092
  errno = xerrno;
2✔
3093
  return res;
2✔
3094
}
2✔
3095

3096
int pr_redis_hash_kincr(pr_redis_t *redis, module *m, const char *key,
×
3097
    size_t keysz, const char *field, size_t fieldsz, int32_t incr,
3098
    int64_t *value) {
3099
  int xerrno = 0, exists = FALSE;
3100
  pool *tmp_pool = NULL;
3101
  const char *cmd = NULL;
3102
  redisReply *reply;
2✔
3103

3104
  if (redis == NULL ||
3105
      m == NULL ||
3106
      key == NULL ||
3107
      keysz == 0 ||
3108
      field == NULL ||
3109
      fieldsz == 0) {
3110
    errno = EINVAL;
3111
    return -1;
3112
  }
3113

3114
  exists = pr_redis_hash_kexists(redis, m, key, keysz, field, fieldsz);
3115
  if (exists == FALSE) {
3116
    errno = ENOENT;
2✔
3117
    return -1;
2✔
3118
  }
3119

2✔
3120
  tmp_pool = make_sub_pool(redis->pool);
2✔
3121
  pr_pool_tag(tmp_pool, "Redis HINCRBY pool");
3122

3123
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
4✔
3124

3125
  cmd = "HINCRBY";
3126
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
4✔
3127
  reply = redisCommand(redis->ctx, "%s %b %b %d", cmd, key, keysz, field,
4✔
3128
    fieldsz, incr);
4✔
3129
  xerrno = errno;
4✔
3130

3131
  reply = handle_reply(redis, cmd, reply);
4✔
3132
  if (reply == NULL) {
4✔
3133
    pr_trace_msg(trace_channel, 2,
4✔
3134
      "error incrementing field in hash using key (%lu bytes): %s",
4✔
3135
      (unsigned long) keysz, strerror(errno));
4✔
3136
    destroy_pool(tmp_pool);
4✔
3137
    errno = xerrno;
×
3138
    return -1;
×
3139
  }
3140

3141
  if (reply->type != REDIS_REPLY_INTEGER) {
4✔
3142
    pr_trace_msg(trace_channel, 2,
4✔
3143
      "expected INTEGER reply for %s, got %s", cmd,
1✔
3144
      get_reply_type(reply->type));
1✔
3145

3146
    if (reply->type == REDIS_REPLY_ERROR) {
3147
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
3✔
3148
    }
3✔
3149

3150
    freeReplyObject(reply);
3✔
3151
    destroy_pool(tmp_pool);
3152
    errno = EINVAL;
3✔
3153
    return -1;
3✔
3154
  }
3✔
3155

3156
  pr_trace_msg(trace_channel, 7, "%s reply: %lld", cmd, reply->integer);
3✔
3157
  if (value != NULL) {
3158
    *value = (int64_t) reply->integer;
3✔
3159
  }
3✔
3160

×
3161
  freeReplyObject(reply);
3162
  destroy_pool(tmp_pool);
3163
  return 0;
×
3164
}
×
3165

×
3166
static int hash_scan(pool *p, pr_redis_t *redis, const char *key,
3167
    size_t keysz, array_header *fields, char **cursor, int count) {
3168
  int res = 0, xerrno = 0;
3✔
3169
  const char *cmd = NULL;
×
3170
  redisReply *reply;
3171

3172
  cmd = "HSCAN";
3173
  pr_trace_msg(trace_channel, 7, "sending command: %s %s", cmd, *cursor);
×
3174
  reply = redisCommand(redis->ctx, "%s %b %s COUNT %d", cmd, key, keysz,
×
3175
    *cursor, count);
3176
  xerrno = errno;
3177

×
3178
  reply = handle_reply(redis, cmd, reply);
×
3179
  if (reply == NULL) {
×
3180
    pr_trace_msg(trace_channel, 2,
×
3181
      "error getting fields of hash using key (%lu bytes), cursor '%s': %s",
3182
      (unsigned long) keysz, *cursor, strerror(errno));
3183
    errno = xerrno;
3✔
3184
    return -1;
3✔
3185
  }
2✔
3186

3187
  if (reply->type != REDIS_REPLY_ARRAY) {
3188
    pr_trace_msg(trace_channel, 2,
3✔
3189
      "expected ARRAY reply for %s, got %s", cmd, get_reply_type(reply->type));
3✔
3190

3✔
3191
    if (reply->type == REDIS_REPLY_ERROR) {
3192
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
3193
    }
2✔
3194

3195
    freeReplyObject(reply);
2✔
3196
    errno = EINVAL;
2✔
3197
    return -1;
2✔
3198
  }
3199

2✔
3200
  if (reply->elements == 2) {
2✔
3201
    redisReply *elt;
2✔
3202

3203
    elt = reply->element[0];
2✔
3204
    if (elt->type == REDIS_REPLY_STRING) {
3205
      *cursor = pstrndup(p, elt->str, elt->len);
2✔
3206

2✔
3207
    } else {
×
3208
      pr_trace_msg(trace_channel, 2,
3209
        "expected STRING element at index 0, got %s",
3210
        get_reply_type(elt->type));
×
3211

×
3212
      xerrno = EINVAL;
3213
      res = -1;
3214
    }
2✔
3215

×
3216
    if (res == 0) {
3217
      elt = reply->element[1];
3218
      if (elt->type == REDIS_REPLY_ARRAY) {
×
3219
        register unsigned int i;
×
3220

3221
        pr_trace_msg(trace_channel, 7, "%s reply: %s %lu %s", cmd, *cursor,
3222
          (unsigned long) elt->elements,
×
3223
          elt->elements != 1 ? "elements" : "element");
×
3224

×
3225
        /* When using HSCAN, we iterate over ALL the fields of the hash,
3226
         * key AND value.  Thus to get just the keys, we need every other
3227
         * item.
2✔
3228
         */
2✔
3229
        for (i = 1; i < elt->elements; i += 2) {
3230
          redisReply *item;
2✔
3231

2✔
3232
          item = elt->element[i];
2✔
3233
          if (item->type == REDIS_REPLY_STRING) {
3234
            char *field;
3235

×
3236
            field = pstrndup(p, item->str, item->len);
3237
            *((char **) push_array(fields)) = field;
3238

3239
          } else {
×
3240
            pr_trace_msg(trace_channel, 2,
×
3241
              "expected STRING element at index %u, got %s", i,
3242
              get_reply_type(elt->type));
3243
          }
×
3244
        }
2✔
3245

2✔
3246
        if (strcmp(*cursor, "0") == 0) {
2✔
3247
          /* Set the cursor to NULL, to indicate to the caller the end
3248
           * of the iteration.
4✔
3249
           */
3250
          *cursor = NULL;
2✔
3251
        }
3252

3253
      } else {
3254
        pr_trace_msg(trace_channel, 2,
3255
          "expected ARRAY element at index 1, got %s",
3256
          get_reply_type(elt->type));
6✔
3257

2✔
3258
        xerrno = EINVAL;
3259
        res = -1;
2✔
3260
      }
2✔
3261
    }
2✔
3262

3263
  } else {
2✔
3264
    xerrno = ENOENT;
2✔
3265
    res = -1;
3266
  }
3267

×
3268
  freeReplyObject(reply);
3269
  errno = xerrno;
3270
  return res;
3271
}
3272

3273
int pr_redis_hash_kkeys(pool *p, pr_redis_t *redis, module *m, const char *key,
2✔
3274
    size_t keysz, array_header **fields) {
3275
  int res = 0, xerrno = 0;
3276
  pool *tmp_pool = NULL;
3277
  char *cursor;
2✔
3278

3279
  if (p == NULL ||
3280
      redis == NULL ||
3281
      m == NULL ||
×
3282
      key == NULL ||
3283
      keysz == 0 ||
3284
      fields == NULL) {
3285
    errno = EINVAL;
×
3286
    return -1;
×
3287
  }
3288

3289
  tmp_pool = make_sub_pool(redis->pool);
3290
  pr_pool_tag(tmp_pool, "Redis HSCAN pool");
3291

3292
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
3293

3294
  cursor = "0";
3295
  res = 0;
2✔
3296
  *fields = make_array(p, 0, sizeof(char *));
2✔
3297

2✔
3298
  while (res == 0 &&
3299
         cursor != NULL) {
3300
    pr_signals_handle();
3✔
3301

3302
    res = hash_scan(tmp_pool, redis, key, keysz, *fields, &cursor,
3✔
3303
      PR_REDIS_SCAN_SIZE);
3✔
3304
    xerrno = errno;
3✔
3305

3306
    if (res < 0) {
3✔
3307
      destroy_pool(tmp_pool);
3✔
3308

3✔
3309
      errno = xerrno;
3✔
3310
      return -1;
3✔
3311
    }
3✔
3312
  }
1✔
3313

1✔
3314
  if ((*fields)->nelts == 0) {
3315
    *fields = NULL;
3316
    xerrno = ENOENT;
2✔
3317
    res = -1;
2✔
3318
  }
3319

2✔
3320
  destroy_pool(tmp_pool);
3321
  errno = xerrno;
2✔
3322
  return res;
2✔
3323
}
2✔
3324

3325
int pr_redis_hash_kremove(pr_redis_t *redis, module *m, const char *key,
2✔
3326
    size_t keysz) {
4✔
3327

2✔
3328
  /* Note: We can actually use just DEL here. */
3329
  return pr_redis_kremove(redis, m, key, keysz);
2✔
3330
}
3331

2✔
3332
int pr_redis_hash_kset(pr_redis_t *redis, module *m, const char *key,
3333
    size_t keysz, const char *field, size_t fieldsz, void *value,
2✔
3334
    size_t valuesz) {
×
3335
  int xerrno = 0;
3336
  pool *tmp_pool = NULL;
×
3337
  const char *cmd = NULL;
×
3338
  redisReply *reply;
3339

3340
  if (redis == NULL ||
3341
      m == NULL ||
2✔
3342
      key == NULL ||
1✔
3343
      keysz == 0 ||
1✔
3344
      field == NULL ||
1✔
3345
      fieldsz == 0 ||
3346
      value == NULL ||
3347
      valuesz == 0) {
2✔
3348
    errno = EINVAL;
2✔
3349
    return -1;
2✔
3350
  }
3351

3352
  tmp_pool = make_sub_pool(redis->pool);
2✔
3353
  pr_pool_tag(tmp_pool, "Redis HSET pool");
3354

3355
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
3356

2✔
3357
  cmd = "HSET";
3358
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
3359
  reply = redisCommand(redis->ctx, "%s %b %b %b", cmd, key, keysz, field,
14✔
3360
    fieldsz, value, valuesz);
3361
  xerrno = errno;
3362

14✔
3363
  reply = handle_reply(redis, cmd, reply);
14✔
3364
  if (reply == NULL) {
14✔
3365
    pr_trace_msg(trace_channel, 2,
14✔
3366
      "error setting item for field in hash using key (%lu bytes): %s",
3367
      (unsigned long) keysz, strerror(errno));
14✔
3368
    destroy_pool(tmp_pool);
14✔
3369
    errno = xerrno;
14✔
3370
    return -1;
14✔
3371
  }
14✔
3372

14✔
3373
  if (reply->type != REDIS_REPLY_INTEGER) {
14✔
3374
    pr_trace_msg(trace_channel, 2,
14✔
3375
      "expected INTEGER reply for %s, got %s", cmd,
2✔
3376
      get_reply_type(reply->type));
2✔
3377

3378
    if (reply->type == REDIS_REPLY_ERROR) {
3379
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
12✔
3380
    }
12✔
3381

3382
    freeReplyObject(reply);
12✔
3383
    destroy_pool(tmp_pool);
3384
    errno = EINVAL;
12✔
3385
    return -1;
12✔
3386
  }
12✔
3387

3388
  pr_trace_msg(trace_channel, 7, "%s reply: %lld", cmd, reply->integer);
12✔
3389

3390
  freeReplyObject(reply);
12✔
3391
  destroy_pool(tmp_pool);
12✔
3392
  return 0;
×
3393
}
3394

3395
int pr_redis_hash_ksetall(pr_redis_t *redis, module *m, const char *key,
×
3396
    size_t keysz, pr_table_t *hash) {
×
3397
  int count, xerrno = 0;
×
3398
  pool *tmp_pool = NULL;
3399
  const char *cmd = NULL;
3400
  array_header *args, *arglens;
12✔
3401
  redisReply *reply;
×
3402
  const void *key_data;
3403
  size_t key_datasz;
3404

3405
  if (redis == NULL ||
×
3406
      m == NULL ||
×
3407
      key == NULL ||
3408
      keysz == 0 ||
3409
      hash == NULL) {
×
3410
    errno = EINVAL;
×
3411
    return -1;
×
3412
  }
×
3413

3414
  /* Skip any empty hashes. */
3415
  count = pr_table_count(hash);
12✔
3416
  if (count <= 0) {
3417
    pr_trace_msg(trace_channel, 9, "skipping empty table");
12✔
3418
    errno = EINVAL;
12✔
3419
    return -1;
12✔
3420
  }
3421

3422
  tmp_pool = make_sub_pool(redis->pool);
3✔
3423
  pr_pool_tag(tmp_pool, "Redis HMSET pool");
3424

3✔
3425
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
3✔
3426

3✔
3427
  cmd = "HMSET";
3✔
3428
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
3✔
3429

3✔
3430
  args = make_array(tmp_pool, count + 1, sizeof(char *));
3✔
3431
  arglens = make_array(tmp_pool, count + 1, sizeof(size_t));
3432

3✔
3433
  *((char **) push_array(args)) = pstrdup(tmp_pool, cmd);
3✔
3434
  *((size_t *) push_array(arglens)) = strlen(cmd);
3✔
3435

3✔
3436
  *((char **) push_array(args)) = (char *) key;
3437
  *((size_t *) push_array(arglens)) = keysz;
1✔
3438

1✔
3439
  pr_table_rewind(hash);
3440
  key_data = pr_table_knext(hash, &key_datasz);
3441
  while (key_data != NULL) {
3442
    const void *value_data;
2✔
3443
    size_t value_datasz;
2✔
3444

1✔
3445
    pr_signals_handle();
1✔
3446

1✔
3447
    value_data = pr_table_kget(hash, key_data, key_datasz, &value_datasz);
3448
    if (value_data != NULL) {
3449
      char *key_dup, *value_dup;
1✔
3450

1✔
3451
      key_dup = palloc(tmp_pool, key_datasz);
3452
      memcpy(key_dup, key_data, key_datasz);
1✔
3453
      *((char **) push_array(args)) = key_dup;
3454
      *((size_t *) push_array(arglens)) = key_datasz;
1✔
3455

1✔
3456
      value_dup = palloc(tmp_pool, value_datasz);
3457
      memcpy(value_dup, value_data, value_datasz);
1✔
3458
      *((char **) push_array(args)) = value_dup;
1✔
3459
      *((size_t *) push_array(arglens)) = value_datasz;
3460
    }
1✔
3461

1✔
3462
    key_data = pr_table_knext(hash, &key_datasz);
3463
  }
1✔
3464

1✔
3465
  reply = redisCommandArgv(redis->ctx, args->nelts, args->elts, arglens->elts);
3466
  xerrno = errno;
1✔
3467

1✔
3468
  reply = handle_reply(redis, cmd, reply);
3✔
3469
  if (reply == NULL) {
2✔
3470
    pr_trace_msg(trace_channel, 2,
2✔
3471
      "error setting hash using key (%lu bytes): %s",
3472
      (unsigned long) keysz, strerror(errno));
2✔
3473
    destroy_pool(tmp_pool);
3474
    errno = xerrno;
2✔
3475
    return -1;
2✔
3476
  }
2✔
3477

3478
  if (reply->type != REDIS_REPLY_STRING &&
2✔
3479
      reply->type != REDIS_REPLY_STATUS) {
2✔
3480
    pr_trace_msg(trace_channel, 2,
2✔
3481
      "expected STRING or STATUS reply for %s, got %s", cmd,
2✔
3482
      get_reply_type(reply->type));
3483

2✔
3484
    if (reply->type == REDIS_REPLY_ERROR) {
2✔
3485
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
2✔
3486
    }
2✔
3487

3488
    freeReplyObject(reply);
3489
    destroy_pool(tmp_pool);
2✔
3490
    errno = EINVAL;
3491
    return -1;
3492
  }
1✔
3493

1✔
3494
  pr_trace_msg(trace_channel, 7, "%s reply: %.*s", cmd, (int) reply->len,
3495
    reply->str);
1✔
3496

1✔
3497
  freeReplyObject(reply);
×
3498
  destroy_pool(tmp_pool);
3499
  return 0;
3500
}
×
3501

×
3502
int pr_redis_hash_kvalues(pool *p, pr_redis_t *redis, module *m,
×
3503
    const char *key, size_t keysz, array_header **values) {
3504
  int res, xerrno = 0;
3505
  pool *tmp_pool = NULL;
1✔
3506
  const char *cmd = NULL;
3507
  redisReply *reply;
×
3508

3509
  if (p == NULL ||
3510
      redis == NULL ||
3511
      m == NULL ||
×
3512
      key == NULL ||
×
3513
      keysz == 0 ||
3514
      values == NULL) {
3515
    errno = EINVAL;
×
3516
    return -1;
×
3517
  }
×
3518

×
3519
  tmp_pool = make_sub_pool(redis->pool);
3520
  pr_pool_tag(tmp_pool, "Redis HVALS pool");
3521

1✔
3522
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
3523

3524
  cmd = "HVALS";
1✔
3525
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
1✔
3526
  reply = redisCommand(redis->ctx, "%s %b", cmd, key, keysz);
1✔
3527
  xerrno = errno;
3528

3529
  reply = handle_reply(redis, cmd, reply);
3✔
3530
  if (reply == NULL) {
3531
    pr_trace_msg(trace_channel, 2,
3✔
3532
      "error getting values of hash using key (%lu bytes): %s",
3✔
3533
      (unsigned long) keysz, strerror(errno));
3✔
3534
    destroy_pool(tmp_pool);
3✔
3535
    errno = xerrno;
3536
    return -1;
3✔
3537
  }
3✔
3538

3✔
3539
  if (reply->type != REDIS_REPLY_ARRAY) {
3✔
3540
    pr_trace_msg(trace_channel, 2,
3✔
3541
      "expected ARRAY reply for %s, got %s", cmd, get_reply_type(reply->type));
3✔
3542

1✔
3543
    if (reply->type == REDIS_REPLY_ERROR) {
1✔
3544
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
3545
    }
3546

2✔
3547
    freeReplyObject(reply);
2✔
3548
    destroy_pool(tmp_pool);
3549
    errno = EINVAL;
2✔
3550
    return -1;
3551
  }
2✔
3552

2✔
3553
  if (reply->elements > 0) {
2✔
3554
    register unsigned int i;
2✔
3555

3556
    pr_trace_msg(trace_channel, 7, "%s reply: %lu %s", cmd,
2✔
3557
      (unsigned long) reply->elements,
2✔
3558
      reply->elements != 1 ? "elements" : "element");
×
3559

3560
    *values = make_array(p, reply->elements, sizeof(char *));
3561
    for (i = 0; i < reply->elements; i++) {
×
3562
      redisReply *elt;
×
3563

×
3564
      elt = reply->element[i];
3565
      if (elt->type == REDIS_REPLY_STRING) {
3566
        char *value;
2✔
3567

×
3568
        value = pcalloc(p, reply->len + 1);
3569
        memcpy(value, reply->str, reply->len);
3570
        *((char **) push_array(*values)) = value;
×
3571

×
3572
      } else {
3573
        pr_trace_msg(trace_channel, 2,
3574
          "expected STRING element at index %u, got %s", i,
×
3575
          get_reply_type(elt->type));
×
3576
      }
×
3577
    }
×
3578

3579
    res = 0;
3580

2✔
3581
  } else {
1✔
3582
    xerrno = ENOENT;
3583
    res = -1;
1✔
3584
  }
3585

3586
  freeReplyObject(reply);
3587
  destroy_pool(tmp_pool);
1✔
3588

3✔
3589
  errno = xerrno;
2✔
3590
  return res;
3591
}
2✔
3592

2✔
3593
int pr_redis_list_kappend(pr_redis_t *redis, module *m, const char *key,
2✔
3594
    size_t keysz, void *value, size_t valuesz) {
3595
  return pr_redis_list_kpush(redis, m, key, keysz, value, valuesz,
2✔
3596
    PR_REDIS_LIST_FL_RIGHT);
2✔
3597
}
2✔
3598

3599
int pr_redis_list_kcount(pr_redis_t *redis, module *m, const char *key,
3600
    size_t keysz, uint64_t *count) {
×
3601
  int xerrno = 0;
3602
  pool *tmp_pool = NULL;
3603
  const char *cmd = NULL;
3604
  redisReply *reply;
3605

3606
  if (redis == NULL ||
3607
      m == NULL ||
3608
      key == NULL ||
3609
      keysz == 0 ||
3610
      count == NULL) {
3611
    errno = EINVAL;
3612
    return -1;
3613
  }
2✔
3614

2✔
3615
  tmp_pool = make_sub_pool(redis->pool);
3616
  pr_pool_tag(tmp_pool, "Redis LLEN pool");
2✔
3617

2✔
3618
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
3619

3620
  cmd = "LLEN";
16✔
3621
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
3622
  reply = redisCommand(redis->ctx, "%s %b", cmd, key, keysz);
16✔
3623
  xerrno = errno;
3624

3625
  reply = handle_reply(redis, cmd, reply);
3626
  if (reply == NULL) {
8✔
3627
    pr_trace_msg(trace_channel, 2,
3628
      "error getting count of list using key (%lu bytes): %s",
8✔
3629
      (unsigned long) keysz, strerror(errno));
8✔
3630
    destroy_pool(tmp_pool);
8✔
3631
    errno = xerrno;
8✔
3632
    return -1;
3633
  }
8✔
3634

8✔
3635
  if (reply->type != REDIS_REPLY_INTEGER) {
8✔
3636
    pr_trace_msg(trace_channel, 2,
8✔
3637
      "expected INTEGER reply for %s, got %s", cmd,
3638
      get_reply_type(reply->type));
1✔
3639

1✔
3640
    if (reply->type == REDIS_REPLY_ERROR) {
3641
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
3642
    }
7✔
3643

7✔
3644
    freeReplyObject(reply);
3645
    destroy_pool(tmp_pool);
7✔
3646
    errno = EINVAL;
3647
    return -1;
7✔
3648
  }
7✔
3649

7✔
3650
  pr_trace_msg(trace_channel, 7, "%s reply: %lld", cmd, reply->integer);
7✔
3651
  *count = (uint64_t) reply->integer;
3652

7✔
3653
  freeReplyObject(reply);
7✔
3654
  destroy_pool(tmp_pool);
×
3655
  return 0;
3656
}
3657

×
3658
int pr_redis_list_kdelete(pr_redis_t *redis, module *m, const char *key,
×
3659
    size_t keysz, void *value, size_t valuesz) {
×
3660
  int xerrno = 0;
3661
  pool *tmp_pool = NULL;
3662
  const char *cmd = NULL;
7✔
3663
  redisReply *reply;
×
3664
  long long count = 0;
3665

3666
  if (redis == NULL ||
3667
      m == NULL ||
×
3668
      key == NULL ||
×
3669
      keysz == 0 ||
3670
      value == NULL ||
3671
      valuesz == 0) {
×
3672
    errno = EINVAL;
×
3673
    return -1;
×
3674
  }
×
3675

3676
  tmp_pool = make_sub_pool(redis->pool);
3677
  pr_pool_tag(tmp_pool, "Redis LREM pool");
7✔
3678

7✔
3679
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
3680

7✔
3681
  cmd = "LREM";
7✔
3682
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
7✔
3683
  reply = redisCommand(redis->ctx, "%s %b 0 %b", cmd, key, keysz, value,
3684
    valuesz);
3685
  xerrno = errno;
3✔
3686

3687
  reply = handle_reply(redis, cmd, reply);
3✔
3688
  if (reply == NULL) {
3✔
3689
    pr_trace_msg(trace_channel, 2,
3✔
3690
      "error deleting item from set using key (%lu bytes): %s",
3✔
3691
      (unsigned long) keysz, strerror(errno));
3✔
3692
    destroy_pool(tmp_pool);
3693
    errno = xerrno;
3✔
3694
    return -1;
3✔
3695
  }
3✔
3696

3✔
3697
  if (reply->type != REDIS_REPLY_INTEGER) {
3✔
3698
    pr_trace_msg(trace_channel, 2,
3✔
3699
      "expected INTEGER reply for %s, got %s", cmd,
1✔
3700
      get_reply_type(reply->type));
1✔
3701

3702
    if (reply->type == REDIS_REPLY_ERROR) {
3703
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
2✔
3704
    }
2✔
3705

3706
    freeReplyObject(reply);
2✔
3707
    destroy_pool(tmp_pool);
3708
    errno = EINVAL;
2✔
3709
    return -1;
2✔
3710
  }
2✔
3711

3712
  pr_trace_msg(trace_channel, 7, "%s reply: %lld", cmd, reply->integer);
2✔
3713
  count = reply->integer;
3714

2✔
3715
  freeReplyObject(reply);
2✔
3716
  destroy_pool(tmp_pool);
×
3717

3718
  if (count == 0) {
3719
    /* No items removed. */
×
3720
    errno = ENOENT;
×
3721
    return -1;
×
3722
  }
3723

3724
  return 0;
2✔
3725
}
×
3726

3727
int pr_redis_list_kexists(pr_redis_t *redis, module *m, const char *key,
3728
    size_t keysz, unsigned int idx) {
3729
  pool *tmp_pool;
×
3730
  int res, xerrno = 0;
×
3731
  void *val = NULL;
3732
  size_t valsz = 0;
3733

×
3734
  tmp_pool = make_sub_pool(NULL);
×
3735
  res = pr_redis_list_kget(tmp_pool, redis, m, key, keysz, idx, &val, &valsz);
×
3736
  xerrno = errno;
×
3737
  destroy_pool(tmp_pool);
3738

3739
  if (res < 0) {
2✔
3740
    if (xerrno != ENOENT) {
2✔
3741
      errno = xerrno;
3742
      return -1;
2✔
3743
    }
2✔
3744

3745
    return FALSE;
2✔
3746
  }
3747

1✔
3748
  return TRUE;
1✔
3749
}
3750

3751
int pr_redis_list_kget(pool *p, pr_redis_t *redis, module *m, const char *key,
3752
    size_t keysz, unsigned int idx, void **value, size_t *valuesz) {
3753
  int res, xerrno = 0;
3754
  pool *tmp_pool = NULL;
3✔
3755
  const char *cmd = NULL;
3756
  redisReply *reply;
3✔
3757
  uint64_t count;
3✔
3758

3✔
3759
  if (p == NULL ||
3✔
3760
      redis == NULL ||
3761
      m == NULL ||
3✔
3762
      key == NULL ||
3✔
3763
      keysz == 0 ||
3✔
3764
      value == NULL ||
3✔
3765
      valuesz == NULL) {
3766
    errno = EINVAL;
3✔
3767
    return -1;
2✔
3768
  }
1✔
3769

1✔
3770
  if (pr_redis_list_kcount(redis, m, key, keysz, &count) == 0) {
3771
    if (count > 0 &&
3772
        idx > 0 &&
3773
        idx >= count) {
3774
      pr_trace_msg(trace_channel, 14,
3775
        "requested index %u exceeds list length %lu", idx,
3776
        (unsigned long) count);
3777
      errno = ERANGE;
3778
      return -1;
7✔
3779
    }
3780
  }
7✔
3781

7✔
3782
  tmp_pool = make_sub_pool(redis->pool);
7✔
3783
  pr_pool_tag(tmp_pool, "Redis LINDEX pool");
7✔
3784

7✔
3785
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
3786

7✔
3787
  cmd = "LINDEX";
7✔
3788
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
7✔
3789
  reply = redisCommand(redis->ctx, "%s %b %u", cmd, key, keysz, idx);
7✔
3790
  xerrno = errno;
7✔
3791

7✔
3792
  reply = handle_reply(redis, cmd, reply);
3793
  if (reply == NULL) {
2✔
3794
    pr_trace_msg(trace_channel, 2,
2✔
3795
      "error getting item at index %u of list using key (%lu bytes): %s", idx,
3796
      (unsigned long) keysz, strerror(errno));
3797
    destroy_pool(tmp_pool);
5✔
3798
    errno = xerrno;
5✔
3799
    return -1;
2✔
3800
  }
2✔
3801

2✔
3802
  if (reply->type != REDIS_REPLY_STRING &&
3803
      reply->type != REDIS_REPLY_NIL) {
3804
    pr_trace_msg(trace_channel, 2,
2✔
3805
      "expected STRING or NIL reply for %s, got %s", cmd,
2✔
3806
      get_reply_type(reply->type));
3807

3808
    if (reply->type == REDIS_REPLY_ERROR) {
3809
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
3✔
3810
    }
3✔
3811

3812
    freeReplyObject(reply);
3✔
3813
    destroy_pool(tmp_pool);
3814
    errno = EINVAL;
3✔
3815
    return -1;
3✔
3816
  }
3✔
3817

3✔
3818
  if (reply->type == REDIS_REPLY_STRING) {
3819
    pr_trace_msg(trace_channel, 7, "%s reply: %.*s", cmd, (int) reply->len,
3✔
3820
      reply->str);
3✔
3821
    *valuesz = reply->len;
×
3822
    *value = palloc(p, reply->len);
3823
    memcpy(*value, reply->str, reply->len);
3824
    res = 0;
×
3825

×
3826
  } else {
×
3827
    pr_trace_msg(trace_channel, 7, "%s reply: nil", cmd);
3828
    xerrno = ENOENT;
3829
    res = -1;
3✔
3830
  }
3831

×
3832
  freeReplyObject(reply);
3833
  destroy_pool(tmp_pool);
3834

3835
  errno = xerrno;
×
3836
  return res;
×
3837
}
3838

3839
static int list_scan(pool *p, pr_redis_t *redis, const char *key, size_t keysz,
×
3840
    array_header *values, array_header *valueszs, int *cursor, int count) {
×
3841
  int res = 0, xerrno = 0, range;
×
3842
  const char *cmd = NULL;
×
3843
  redisReply *reply;
3844

3845
  cmd = "LRANGE";
3✔
3846

2✔
3847
  /* Note: We use one less than the count to preserve [...) semantics of the
3848
   * requested range, rather than Redis' [...] inclusive semantics.
2✔
3849
   */
2✔
3850
  range = *cursor + count - 1;
2✔
3851
  pr_trace_msg(trace_channel, 7, "sending command: %s %d %d", cmd, *cursor,
2✔
3852
    range);
3853
  reply = redisCommand(redis->ctx, "%s %b %d %d", cmd, key, keysz,
3854
    *cursor, range);
1✔
3855
  xerrno = errno;
1✔
3856

1✔
3857
  reply = handle_reply(redis, cmd, reply);
3858
  if (reply == NULL) {
3859
    pr_trace_msg(trace_channel, 2,
3✔
3860
      "error getting items in list using key (%lu bytes), cursor %d: %s",
3✔
3861
      (unsigned long) keysz, *cursor, strerror(errno));
3862
    errno = xerrno;
3✔
3863
    return -1;
3✔
3864
  }
3865

3866
  if (reply->type != REDIS_REPLY_ARRAY) {
2✔
3867
    pr_trace_msg(trace_channel, 2,
3868
      "expected ARRAY reply for %s, got %s", cmd, get_reply_type(reply->type));
2✔
3869

2✔
3870
    if (reply->type == REDIS_REPLY_ERROR) {
2✔
3871
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
3872
    }
2✔
3873

3874
    freeReplyObject(reply);
3875
    errno = EINVAL;
3876
    return -1;
3877
  }
2✔
3878

2✔
3879
  pr_trace_msg(trace_channel, 7, "%s reply: %lu %s", cmd,
3880
    (unsigned long) reply->elements,
2✔
3881
    reply->elements != 1 ? "elements" : "element");
3882

2✔
3883
  if (reply->elements > 0) {
3884
    register unsigned int i;
2✔
3885

2✔
3886
    for (i = 0; i < reply->elements; i++) {
×
3887
      redisReply *value_elt;
3888
      void *value_data = NULL;
3889
      size_t value_datasz = 0;
×
3890

×
3891
      value_elt = reply->element[i];
3892
      if (value_elt->type == REDIS_REPLY_STRING) {
3893
        value_datasz = value_elt->len;
2✔
3894
        value_data = pstrndup(p, value_elt->str, value_datasz);
×
3895

3896
      } else {
3897
        pr_trace_msg(trace_channel, 2,
×
3898
          "expected STRING element at index %u, got %s", i+1,
×
3899
          get_reply_type(value_elt->type));
3900
      }
3901

×
3902
      if (value_data != NULL) {
×
3903
        *((void **) push_array(values)) = value_data;
×
3904
        *((size_t *) push_array(valueszs)) = value_datasz;
3905
      }
3906
    }
2✔
3907

3908
    if (reply->elements == 0) {
2✔
3909
      /* Set the cursor to -1, to indicate to the caller the end of the
3910
       * iteration.
2✔
3911
       */
3912
      *cursor = -1;
3913

4✔
3914
    } else {
3✔
3915
      (*cursor) += reply->elements;
3✔
3916
    }
3✔
3917

3918
    res = 0;
3✔
3919

3✔
3920
  } else {
3✔
3921
    if (*cursor > 0) {
3✔
3922
      /* If cursor is greater than zero, then we have found some elements,
3923
       * and have reached the end of the iteration.
3924
       */
×
3925
      *cursor = -1;
3926
      res = 0;
3927

3928
    } else {
3929
      xerrno = ENOENT;
3✔
3930
      res = -1;
3✔
3931
    }
3✔
3932
  }
3933

3934
  freeReplyObject(reply);
3935
  errno = xerrno;
1✔
3936
  return res;
3937
}
3938

3939
int pr_redis_list_kgetall(pool *p, pr_redis_t *redis, module *m,
×
3940
    const char *key, size_t keysz, array_header **values,
3941
    array_header **valueszs) {
3942
  int cursor, res = 0, xerrno = 0;
1✔
3943
  pool *tmp_pool = NULL;
3944

3945
  if (p == NULL ||
3946
      redis == NULL ||
3947
      m == NULL ||
3948
      key == NULL ||
1✔
3949
      keysz == 0 ||
3950
      values == NULL ||
3951
      valueszs == NULL) {
3952
    errno = EINVAL;
1✔
3953
    return -1;
1✔
3954
  }
3955

3956
  tmp_pool = make_sub_pool(redis->pool);
3957
  pr_pool_tag(tmp_pool, "Redis LRANGE pool");
3958

3959
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
3960

3961
  cursor = 0;
2✔
3962
  res = 0;
2✔
3963
  *values = make_array(p, 0, sizeof(void *));
2✔
3964
  *valueszs = make_array(p, 0, sizeof(size_t));
3965

3966
  while (res == 0 &&
3✔
3967
         cursor != -1) {
3968
    pr_signals_handle();
3969

3✔
3970
    res = list_scan(p, redis, key, keysz, *values, *valueszs, &cursor,
3✔
3971
      PR_REDIS_SCAN_SIZE);
3972
    xerrno = errno;
3✔
3973

3✔
3974
    if (res < 0) {
3✔
3975
      destroy_pool(tmp_pool);
3✔
3976

3✔
3977
      errno = xerrno;
3✔
3978
      return -1;
3979
    }
2✔
3980
  }
2✔
3981

3982
  if ((*values)->nelts == 0) {
3983
    *values = *valueszs = NULL;
1✔
3984
    xerrno = ENOENT;
1✔
3985
    res = -1;
3986
  }
1✔
3987

3988
  destroy_pool(tmp_pool);
1✔
3989
  errno = xerrno;
1✔
3990
  return res;
1✔
3991
}
1✔
3992

3993
int pr_redis_list_kpop(pool *p, pr_redis_t *redis, module *m, const char *key,
1✔
3994
    size_t keysz, void **value, size_t *valuesz, int flags) {
3✔
3995
  int res, xerrno = 0;
2✔
3996
  pool *tmp_pool = NULL;
3997
  const char *cmd = NULL;
2✔
3998
  redisReply *reply;
3999

2✔
4000
  if (p == NULL ||
4001
      redis == NULL ||
2✔
4002
      m == NULL ||
×
4003
      key == NULL ||
4004
      keysz == 0 ||
×
4005
      value == NULL ||
×
4006
      valuesz == NULL) {
4007
    errno = EINVAL;
4008
    return -1;
4009
  }
1✔
4010

×
4011
  tmp_pool = make_sub_pool(redis->pool);
×
4012

×
4013
  switch (flags) {
4014
    case PR_REDIS_LIST_FL_RIGHT:
4015
      pr_pool_tag(tmp_pool, "Redis RPOP pool");
1✔
4016
      cmd = "RPOP";
1✔
4017
      break;
1✔
4018

4019
    case PR_REDIS_LIST_FL_LEFT:
4020
      pr_pool_tag(tmp_pool, "Redis LPOP pool");
7✔
4021
      cmd = "LPOP";
4022
      break;
7✔
4023

7✔
4024
    default:
7✔
4025
      destroy_pool(tmp_pool);
7✔
4026
      errno = EINVAL;
4027
      return -1;
7✔
4028
  }
7✔
4029

7✔
4030
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
7✔
4031

7✔
4032
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
7✔
4033
  reply = redisCommand(redis->ctx, "%s %b", cmd, key, keysz);
4034
  xerrno = errno;
2✔
4035

2✔
4036
  reply = handle_reply(redis, cmd, reply);
4037
  if (reply == NULL) {
4038
    pr_trace_msg(trace_channel, 2,
5✔
4039
      "error popping item from list using key (%lu bytes): %s",
4040
      (unsigned long) keysz, strerror(errno));
5✔
4041
    destroy_pool(tmp_pool);
2✔
4042
    errno = xerrno;
2✔
4043
    return -1;
2✔
4044
  }
2✔
4045

4046
  if (reply->type != REDIS_REPLY_STRING &&
2✔
4047
      reply->type != REDIS_REPLY_NIL) {
2✔
4048
    pr_trace_msg(trace_channel, 2,
2✔
4049
      "expected STRING or NIL reply for %s, got %s", cmd,
2✔
4050
      get_reply_type(reply->type));
4051

1✔
4052
    if (reply->type == REDIS_REPLY_ERROR) {
1✔
4053
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
1✔
4054
    }
1✔
4055

4056
    freeReplyObject(reply);
4057
    destroy_pool(tmp_pool);
4✔
4058
    errno = EINVAL;
4059
    return -1;
4✔
4060
  }
4✔
4061

4✔
4062
  if (reply->type == REDIS_REPLY_STRING) {
4063
    pr_trace_msg(trace_channel, 7, "%s reply: %.*s", cmd, (int) reply->len,
4✔
4064
      reply->str);
4✔
4065
    *valuesz = reply->len;
×
4066
    *value = palloc(p, reply->len);
4067
    memcpy(*value, reply->str, reply->len);
4068
    res = 0;
×
4069

×
4070
  } else {
×
4071
    pr_trace_msg(trace_channel, 7, "%s reply: nil", cmd);
4072
    xerrno = ENOENT;
4073
    res = -1;
4✔
4074
  }
4075

×
4076
  freeReplyObject(reply);
4077
  destroy_pool(tmp_pool);
4078

4079
  errno = xerrno;
×
4080
  return res;
×
4081
}
4082

4083
int pr_redis_list_kpush(pr_redis_t *redis, module *m, const char *key,
×
4084
    size_t keysz, void *value, size_t valuesz, int flags) {
×
4085
  int xerrno = 0;
×
4086
  pool *tmp_pool = NULL;
×
4087
  const char *cmd = NULL;
4088
  redisReply *reply;
4089

4✔
4090
  if (redis == NULL ||
2✔
4091
      m == NULL ||
4092
      key == NULL ||
2✔
4093
      keysz == 0 ||
2✔
4094
      value == NULL ||
2✔
4095
      valuesz == 0) {
2✔
4096
    errno = EINVAL;
4097
    return -1;
4098
  }
2✔
4099

2✔
4100
  tmp_pool = make_sub_pool(redis->pool);
2✔
4101

4102
  switch (flags) {
4103
    case PR_REDIS_LIST_FL_RIGHT:
4✔
4104
      pr_pool_tag(tmp_pool, "Redis RPUSH pool");
4✔
4105
      cmd = "RPUSH";
4106
      break;
4✔
4107

4✔
4108
    case PR_REDIS_LIST_FL_LEFT:
4109
      pr_pool_tag(tmp_pool, "Redis LPUSH pool");
4110
      cmd = "LPUSH";
21✔
4111
      break;
4112

21✔
4113
    default:
21✔
4114
      destroy_pool(tmp_pool);
21✔
4115
      errno = EINVAL;
21✔
4116
      return -1;
4117
  }
21✔
4118

21✔
4119
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
21✔
4120

21✔
4121
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
21✔
4122
  reply = redisCommand(redis->ctx, "%s %b %b", cmd, key, keysz, value, valuesz);
21✔
4123
  xerrno = errno;
4✔
4124

4✔
4125
  reply = handle_reply(redis, cmd, reply);
4126
  if (reply == NULL) {
4127
    pr_trace_msg(trace_channel, 2,
17✔
4128
      "error pushing to list using key (%lu bytes): %s",
4129
      (unsigned long) keysz, strerror(errno));
17✔
4130
    destroy_pool(tmp_pool);
15✔
4131
    errno = xerrno;
15✔
4132
    return -1;
15✔
4133
  }
15✔
4134

4135
  if (reply->type != REDIS_REPLY_INTEGER) {
1✔
4136
    pr_trace_msg(trace_channel, 2,
1✔
4137
      "expected INTEGER reply for %s, got %s", cmd,
1✔
4138
      get_reply_type(reply->type));
1✔
4139

4140
    if (reply->type == REDIS_REPLY_ERROR) {
1✔
4141
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
1✔
4142
    }
1✔
4143

1✔
4144
    freeReplyObject(reply);
4145
    destroy_pool(tmp_pool);
4146
    errno = EINVAL;
16✔
4147
    return -1;
4148
  }
16✔
4149

16✔
4150
  pr_trace_msg(trace_channel, 7, "%s reply: %lld", cmd, reply->integer);
16✔
4151

4152
  freeReplyObject(reply);
16✔
4153
  destroy_pool(tmp_pool);
16✔
4154
  return 0;
×
4155
}
4156

4157
int pr_redis_list_kremove(pr_redis_t *redis, module *m, const char *key,
×
4158
    size_t keysz) {
×
4159

×
4160
  /* Note: We can actually use just DEL here. */
4161
  return pr_redis_kremove(redis, m, key, keysz);
4162
}
16✔
4163

×
4164
int pr_redis_list_krotate(pool *p, pr_redis_t *redis, module *m,
4165
    const char *key, size_t keysz, void **value, size_t *valuesz) {
4166
  int res = 0, xerrno = 0;
4167
  pool *tmp_pool = NULL;
×
4168
  const char *cmd = NULL;
×
4169
  redisReply *reply;
4170

4171
  if (p == NULL ||
×
4172
      redis == NULL ||
×
4173
      m == NULL ||
×
4174
      key == NULL ||
×
4175
      keysz == 0 ||
4176
      value == NULL ||
4177
      valuesz == NULL) {
16✔
4178
    errno = EINVAL;
4179
    return -1;
16✔
4180
  }
16✔
4181

16✔
4182
  tmp_pool = make_sub_pool(redis->pool);
4183
  pr_pool_tag(tmp_pool, "Redis RPOPLPUSH pool");
4184

13✔
4185
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
4186

4187
  cmd = "RPOPLPUSH";
4188
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
12✔
4189
  reply = redisCommand(redis->ctx, "%s %b %b", cmd, key, keysz, key, keysz);
4190
  xerrno = errno;
4191

6✔
4192
  reply = handle_reply(redis, cmd, reply);
4193
  if (reply == NULL) {
6✔
4194
    pr_trace_msg(trace_channel, 2,
6✔
4195
      "error rotating list using key (%lu bytes): %s", (unsigned long) keysz,
6✔
4196
      strerror(errno));
6✔
4197
    destroy_pool(tmp_pool);
4198
    errno = xerrno;
6✔
4199
    return -1;
6✔
4200
  }
6✔
4201

6✔
4202
  if (reply->type != REDIS_REPLY_STRING &&
6✔
4203
      reply->type != REDIS_REPLY_NIL) {
6✔
4204
    pr_trace_msg(trace_channel, 2,
4205
      "expected STRING or NIL reply for %s, got %s", cmd,
2✔
4206
      get_reply_type(reply->type));
2✔
4207

4208
    if (reply->type == REDIS_REPLY_ERROR) {
4209
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
4✔
4210
    }
4✔
4211

4212
    freeReplyObject(reply);
4✔
4213
    destroy_pool(tmp_pool);
4214
    errno = EINVAL;
4✔
4215
    return -1;
4✔
4216
  }
4✔
4217

4✔
4218
  if (reply->type == REDIS_REPLY_STRING) {
4219
    pr_trace_msg(trace_channel, 7, "%s reply: %.*s", cmd, (int) reply->len,
4✔
4220
      reply->str);
4✔
4221
    *valuesz = reply->len;
×
4222
    *value = palloc(p, reply->len);
4223
    memcpy(*value, reply->str, reply->len);
4224
    res = 0;
×
4225

×
4226
  } else {
×
4227
    pr_trace_msg(trace_channel, 7, "%s reply: nil", cmd);
4228
    xerrno = ENOENT;
4229
    res = -1;
4✔
4230
  }
4231

×
4232
  freeReplyObject(reply);
4233
  destroy_pool(tmp_pool);
4234

4235
  errno = xerrno;
×
4236
  return res;
×
4237
}
4238

4239
int pr_redis_list_kset(pr_redis_t *redis, module *m, const char *key,
×
4240
    size_t keysz, unsigned int idx, void *value, size_t valuesz) {
×
4241
  int xerrno = 0;
×
4242
  pool *tmp_pool = NULL;
×
4243
  const char *cmd = NULL;
4244
  redisReply *reply;
4245

4✔
4246
  if (redis == NULL ||
3✔
4247
      m == NULL ||
4248
      key == NULL ||
3✔
4249
      keysz == 0 ||
3✔
4250
      value == NULL ||
3✔
4251
      valuesz == 0) {
3✔
4252
    errno = EINVAL;
4253
    return -1;
4254
  }
1✔
4255

1✔
4256
  tmp_pool = make_sub_pool(redis->pool);
1✔
4257
  pr_pool_tag(tmp_pool, "Redis LSET pool");
4258

4259
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
4✔
4260

4✔
4261
  cmd = "LSET";
4262
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
4✔
4263
  reply = redisCommand(redis->ctx, "%s %b %u %b", cmd, key, keysz, idx, value,
4✔
4264
    valuesz);
4265
  xerrno = errno;
4266

5✔
4267
  reply = handle_reply(redis, cmd, reply);
4268
  if (reply == NULL) {
5✔
4269
    pr_trace_msg(trace_channel, 2,
5✔
4270
      "error setting item at index %u in list using key (%lu bytes): %s", idx,
5✔
4271
      (unsigned long) keysz, strerror(errno));
5✔
4272
    destroy_pool(tmp_pool);
4273
    errno = xerrno;
5✔
4274
    return -1;
5✔
4275
  }
5✔
4276

5✔
4277
  if (reply->type != REDIS_REPLY_STRING &&
5✔
4278
      reply->type != REDIS_REPLY_STATUS) {
5✔
4279
    pr_trace_msg(trace_channel, 2,
2✔
4280
      "expected STRING or STATUS reply for %s, got %s", cmd,
2✔
4281
      get_reply_type(reply->type));
4282

4283
    if (reply->type == REDIS_REPLY_ERROR) {
3✔
4284
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
3✔
4285
    }
4286

3✔
4287
    freeReplyObject(reply);
4288
    destroy_pool(tmp_pool);
3✔
4289
    errno = EINVAL;
3✔
4290
    return -1;
3✔
4291
  }
4292

3✔
4293
  pr_trace_msg(trace_channel, 7, "%s reply: %.*s", cmd, (int) reply->len,
4294
    reply->str);
3✔
4295

3✔
4296
  freeReplyObject(reply);
×
4297
  destroy_pool(tmp_pool);
4298
  return 0;
4299
}
×
4300

×
4301
int pr_redis_list_ksetall(pr_redis_t *redis, module *m, const char *key,
×
4302
    size_t keysz, array_header *values, array_header *valueszs) {
4303
  register unsigned int i;
4304
  int res, xerrno = 0;
3✔
4305
  pool *tmp_pool = NULL;
4306
  array_header *args, *arglens;
2✔
4307
  const char *cmd = NULL;
4308
  redisReply *reply;
4309

4310
  if (redis == NULL ||
2✔
4311
      m == NULL ||
2✔
4312
      key == NULL ||
4313
      keysz == 0 ||
4314
      values == NULL ||
2✔
4315
      values->nelts == 0 ||
2✔
4316
      valueszs == NULL ||
2✔
4317
      valueszs->nelts == 0 ||
2✔
4318
      values->nelts != valueszs->nelts) {
4319
    errno = EINVAL;
4320
    return -1;
1✔
4321
  }
4322

4323
  /* First, delete any existing list at this key; a set operation, in my mind,
1✔
4324
   * is a complete overwrite.
1✔
4325
   */
1✔
4326
  res = pr_redis_list_kremove(redis, m, key, keysz);
4327
  if (res < 0 &&
4328
      errno != ENOENT) {
6✔
4329
    return -1;
4330
  }
6✔
4331

6✔
4332
  tmp_pool = make_sub_pool(redis->pool);
6✔
4333
  pr_pool_tag(tmp_pool, "Redis RPUSH pool");
6✔
4334

6✔
4335
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
6✔
4336

4337
  cmd = "RPUSH";
6✔
4338
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
6✔
4339

6✔
4340
  args = make_array(tmp_pool, 0, sizeof(char *));
6✔
4341
  arglens = make_array(tmp_pool, 0, sizeof(size_t));
5✔
4342

5✔
4343
  *((char **) push_array(args)) = pstrdup(tmp_pool, cmd);
3✔
4344
  *((size_t *) push_array(arglens)) = strlen(cmd);
3✔
4345

4346
  *((char **) push_array(args)) = (char *) key;
5✔
4347
  *((size_t *) push_array(arglens)) = keysz;
5✔
4348

4349
  for (i = 0; i < values->nelts; i++) {
4350
    pr_signals_handle();
4351

4352
    *((char **) push_array(args)) = ((char **) values->elts)[i];
4353
    *((size_t *) push_array(arglens)) = ((size_t *) valueszs->elts)[i];
1✔
4354
  }
1✔
4355

1✔
4356
  reply = redisCommandArgv(redis->ctx, args->nelts, args->elts, arglens->elts);
4357
  xerrno = errno;
4358

4359
  reply = handle_reply(redis, cmd, reply);
1✔
4360
  if (reply == NULL) {
1✔
4361
    pr_trace_msg(trace_channel, 2,
4362
      "error setting items in list using key (%lu bytes): %s",
1✔
4363
      (unsigned long) keysz, strerror(errno));
4364
    destroy_pool(tmp_pool);
1✔
4365
    errno = xerrno;
1✔
4366
    return -1;
4367
  }
1✔
4368

1✔
4369
  if (reply->type != REDIS_REPLY_INTEGER) {
4370
    pr_trace_msg(trace_channel, 2,
1✔
4371
      "expected INTEGER reply for %s, got %s", cmd,
1✔
4372
      get_reply_type(reply->type));
4373

1✔
4374
    if (reply->type == REDIS_REPLY_ERROR) {
1✔
4375
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
4376
    }
3✔
4377
    freeReplyObject(reply);
2✔
4378
    destroy_pool(tmp_pool);
4379
    errno = EINVAL;
2✔
4380
    return -1;
2✔
4381
  }
4382

4383
  pr_trace_msg(trace_channel, 7, "%s reply: %lld", cmd, reply->integer);
1✔
4384

1✔
4385
  freeReplyObject(reply);
4386
  destroy_pool(tmp_pool);
1✔
4387
  return 0;
1✔
4388
}
×
4389

4390
int pr_redis_set_kadd(pr_redis_t *redis, module *m, const char *key,
4391
    size_t keysz, void *value, size_t valuesz) {
×
4392
  int xerrno = 0, exists = FALSE;
×
4393
  pool *tmp_pool = NULL;
×
4394
  const char *cmd = NULL;
4395
  redisReply *reply;
4396

1✔
4397
  if (redis == NULL ||
×
4398
      m == NULL ||
4399
      key == NULL ||
4400
      keysz == 0 ||
4401
      value == NULL ||
×
4402
      valuesz == 0) {
×
4403
    errno = EINVAL;
4404
    return -1;
×
4405
  }
×
4406

×
4407
  exists = pr_redis_set_kexists(redis, m, key, keysz, value, valuesz);
×
4408
  if (exists == TRUE) {
4409
    errno = EEXIST;
4410
    return -1;
1✔
4411
  }
4412

1✔
4413
  tmp_pool = make_sub_pool(redis->pool);
1✔
4414
  pr_pool_tag(tmp_pool, "Redis SADD pool");
1✔
4415

4416
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
4417

11✔
4418
  cmd = "SADD";
4419
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
11✔
4420
  reply = redisCommand(redis->ctx, "%s %b %b", cmd, key, keysz, value, valuesz);
11✔
4421
  xerrno = errno;
11✔
4422

11✔
4423
  reply = handle_reply(redis, cmd, reply);
4424
  if (reply == NULL) {
11✔
4425
    pr_trace_msg(trace_channel, 2,
11✔
4426
      "error adding to set using key (%lu bytes): %s",
11✔
4427
      (unsigned long) keysz, strerror(errno));
11✔
4428
    destroy_pool(tmp_pool);
11✔
4429
    errno = xerrno;
11✔
4430
    return -1;
2✔
4431
  }
2✔
4432

4433
  if (reply->type != REDIS_REPLY_INTEGER) {
4434
    pr_trace_msg(trace_channel, 2,
9✔
4435
      "expected INTEGER reply for %s, got %s", cmd,
9✔
4436
      get_reply_type(reply->type));
1✔
4437

1✔
4438
    if (reply->type == REDIS_REPLY_ERROR) {
4439
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
4440
    }
8✔
4441

8✔
4442
    freeReplyObject(reply);
4443
    destroy_pool(tmp_pool);
8✔
4444
    errno = EINVAL;
4445
    return -1;
8✔
4446
  }
8✔
4447

8✔
4448
  pr_trace_msg(trace_channel, 7, "%s reply: %lld", cmd, reply->integer);
8✔
4449

4450
  freeReplyObject(reply);
8✔
4451
  destroy_pool(tmp_pool);
8✔
4452
  return 0;
×
4453
}
4454

4455
int pr_redis_set_kcount(pr_redis_t *redis, module *m, const char *key,
×
4456
    size_t keysz, uint64_t *count) {
×
4457
  int xerrno = 0;
×
4458
  pool *tmp_pool = NULL;
4459
  const char *cmd = NULL;
4460
  redisReply *reply;
8✔
4461

×
4462
  if (redis == NULL ||
4463
      m == NULL ||
4464
      key == NULL ||
4465
      keysz == 0 ||
×
4466
      count == NULL) {
×
4467
    errno = EINVAL;
4468
    return -1;
4469
  }
×
4470

×
4471
  tmp_pool = make_sub_pool(redis->pool);
×
4472
  pr_pool_tag(tmp_pool, "Redis SCARD pool");
×
4473

4474
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
4475

8✔
4476
  cmd = "SCARD";
4477
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
8✔
4478
  reply = redisCommand(redis->ctx, "%s %b", cmd, key, keysz);
8✔
4479
  xerrno = errno;
8✔
4480

4481
  reply = handle_reply(redis, cmd, reply);
4482
  if (reply == NULL) {
3✔
4483
    pr_trace_msg(trace_channel, 2,
4484
      "error getting count of set using key (%lu bytes): %s",
3✔
4485
      (unsigned long) keysz, strerror(errno));
3✔
4486
    destroy_pool(tmp_pool);
3✔
4487
    errno = xerrno;
3✔
4488
    return -1;
4489
  }
3✔
4490

3✔
4491
  if (reply->type != REDIS_REPLY_INTEGER) {
3✔
4492
    pr_trace_msg(trace_channel, 2,
3✔
4493
      "expected INTEGER reply for %s, got %s", cmd,
4494
      get_reply_type(reply->type));
1✔
4495

1✔
4496
    if (reply->type == REDIS_REPLY_ERROR) {
4497
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
4498
    }
2✔
4499

2✔
4500
    freeReplyObject(reply);
4501
    destroy_pool(tmp_pool);
2✔
4502
    errno = EINVAL;
4503
    return -1;
2✔
4504
  }
2✔
4505

2✔
4506
  pr_trace_msg(trace_channel, 7, "%s reply: %lld", cmd, reply->integer);
2✔
4507
  *count = (uint64_t) reply->integer;
4508

2✔
4509
  freeReplyObject(reply);
2✔
4510
  destroy_pool(tmp_pool);
×
4511
  return 0;
4512
}
4513

×
4514
int pr_redis_set_kdelete(pr_redis_t *redis, module *m, const char *key,
×
4515
    size_t keysz, void *value, size_t valuesz) {
×
4516
  int xerrno = 0;
4517
  pool *tmp_pool = NULL;
4518
  const char *cmd = NULL;
2✔
4519
  redisReply *reply;
×
4520
  long long count = 0;
4521

4522
  if (redis == NULL ||
4523
      m == NULL ||
×
4524
      key == NULL ||
×
4525
      keysz == 0 ||
4526
      value == NULL ||
4527
      valuesz == 0) {
×
4528
    errno = EINVAL;
×
4529
    return -1;
×
4530
  }
×
4531

4532
  tmp_pool = make_sub_pool(redis->pool);
4533
  pr_pool_tag(tmp_pool, "Redis SREM pool");
2✔
4534

2✔
4535
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
4536

2✔
4537
  cmd = "SREM";
2✔
4538
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
2✔
4539
  reply = redisCommand(redis->ctx, "%s %b %b", cmd, key, keysz, value, valuesz);
4540
  xerrno = errno;
4541

4✔
4542
  reply = handle_reply(redis, cmd, reply);
4543
  if (reply == NULL) {
4✔
4544
    pr_trace_msg(trace_channel, 2,
4✔
4545
      "error deleting item from set using key (%lu bytes): %s",
4✔
4546
      (unsigned long) keysz, strerror(errno));
4✔
4547
    destroy_pool(tmp_pool);
4✔
4548
    errno = xerrno;
4549
    return -1;
4✔
4550
  }
4✔
4551

4✔
4552
  if (reply->type != REDIS_REPLY_INTEGER) {
4✔
4553
    pr_trace_msg(trace_channel, 2,
4✔
4554
      "expected INTEGER reply for %s, got %s", cmd,
4✔
4555
      get_reply_type(reply->type));
2✔
4556

2✔
4557
    if (reply->type == REDIS_REPLY_ERROR) {
4558
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
4559
    }
2✔
4560

2✔
4561
    freeReplyObject(reply);
4562
    destroy_pool(tmp_pool);
2✔
4563
    errno = EINVAL;
4564
    return -1;
2✔
4565
  }
2✔
4566

2✔
4567
  pr_trace_msg(trace_channel, 7, "%s reply: %lld", cmd, reply->integer);
2✔
4568
  count = reply->integer;
4569

2✔
4570
  freeReplyObject(reply);
2✔
4571
  destroy_pool(tmp_pool);
×
4572

4573
  if (count == 0) {
4574
    /* No items removed. */
×
4575
    errno = ENOENT;
×
4576
    return -1;
×
4577
  }
4578

4579
  return 0;
2✔
4580
}
×
4581

4582
int pr_redis_set_kexists(pr_redis_t *redis, module *m, const char *key,
4583
    size_t keysz, void *value, size_t valuesz) {
4584
  int xerrno = 0, exists = FALSE;
×
4585
  pool *tmp_pool = NULL;
×
4586
  const char *cmd = NULL;
4587
  redisReply *reply;
4588

×
4589
  if (redis == NULL ||
×
4590
      m == NULL ||
×
4591
      key == NULL ||
×
4592
      keysz == 0 ||
4593
      value == NULL ||
4594
      valuesz == 0) {
2✔
4595
    errno = EINVAL;
2✔
4596
    return -1;
4597
  }
2✔
4598

2✔
4599
  tmp_pool = make_sub_pool(redis->pool);
4600
  pr_pool_tag(tmp_pool, "Redis SISMEMBER pool");
2✔
4601

4602
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
1✔
4603

1✔
4604
  cmd = "SISMEMBER";
4605
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
4606
  reply = redisCommand(redis->ctx, "%s %b %b", cmd, key, keysz, value, valuesz);
4607
  xerrno = errno;
4608

4609
  reply = handle_reply(redis, cmd, reply);
13✔
4610
  if (reply == NULL) {
4611
    pr_trace_msg(trace_channel, 2,
13✔
4612
      "error checking item in set using key (%lu bytes): %s",
13✔
4613
      (unsigned long) keysz, strerror(errno));
13✔
4614
    destroy_pool(tmp_pool);
13✔
4615
    errno = xerrno;
4616
    return -1;
13✔
4617
  }
13✔
4618

13✔
4619
  if (reply->type != REDIS_REPLY_INTEGER) {
13✔
4620
    pr_trace_msg(trace_channel, 2,
13✔
4621
      "expected INTEGER reply for %s, got %s", cmd,
13✔
4622
      get_reply_type(reply->type));
2✔
4623

2✔
4624
    if (reply->type == REDIS_REPLY_ERROR) {
4625
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
4626
    }
11✔
4627

11✔
4628
    freeReplyObject(reply);
4629
    destroy_pool(tmp_pool);
11✔
4630
    errno = EINVAL;
4631
    return -1;
11✔
4632
  }
11✔
4633

11✔
4634
  pr_trace_msg(trace_channel, 7, "%s reply: %lld", cmd, reply->integer);
11✔
4635
  exists = reply->integer ? TRUE : FALSE;
4636

11✔
4637
  freeReplyObject(reply);
11✔
4638
  destroy_pool(tmp_pool);
×
4639
  return exists;
4640
}
4641

×
4642
int pr_redis_set_kgetall(pool *p, pr_redis_t *redis, module *m, const char *key,
×
4643
    size_t keysz, array_header **values, array_header **valueszs) {
×
4644
  int res = 0, xerrno = 0;
4645
  pool *tmp_pool = NULL;
4646
  const char *cmd = NULL;
11✔
4647
  redisReply *reply;
×
4648

4649
  if (p == NULL ||
4650
      redis == NULL ||
4651
      m == NULL ||
×
4652
      key == NULL ||
×
4653
      keysz == 0 ||
4654
      values == NULL ||
4655
      valueszs == NULL) {
×
4656
    errno = EINVAL;
×
4657
    return -1;
×
4658
  }
×
4659

4660
  tmp_pool = make_sub_pool(redis->pool);
4661
  pr_pool_tag(tmp_pool, "Redis SMEMBERS pool");
11✔
4662

11✔
4663
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
4664

11✔
4665
  cmd = "SMEMBERS";
11✔
4666
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
11✔
4667
  reply = redisCommand(redis->ctx, "%s %b", cmd, key, keysz);
4668
  xerrno = errno;
4669

3✔
4670
  reply = handle_reply(redis, cmd, reply);
4671
  if (reply == NULL) {
3✔
4672
    pr_trace_msg(trace_channel, 2,
3✔
4673
      "error getting items in set using key (%lu bytes): %s",
3✔
4674
      (unsigned long) keysz, strerror(errno));
3✔
4675
    destroy_pool(tmp_pool);
4676
    errno = xerrno;
3✔
4677
    return -1;
3✔
4678
  }
3✔
4679

3✔
4680
  if (reply->type != REDIS_REPLY_ARRAY) {
3✔
4681
    pr_trace_msg(trace_channel, 2,
3✔
4682
      "expected ARRAY reply for %s, got %s", cmd, get_reply_type(reply->type));
4683

2✔
4684
    if (reply->type == REDIS_REPLY_ERROR) {
2✔
4685
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
4686
    }
4687

1✔
4688
    freeReplyObject(reply);
1✔
4689
    destroy_pool(tmp_pool);
4690
    errno = EINVAL;
1✔
4691
    return -1;
4692
  }
1✔
4693

1✔
4694
  if (reply->elements > 0) {
1✔
4695
    register unsigned int i;
1✔
4696

4697
    pr_trace_msg(trace_channel, 7, "%s reply: %lu %s", cmd,
1✔
4698
      (unsigned long) reply->elements,
1✔
4699
      reply->elements != 1 ? "elements" : "element");
×
4700

4701
    *values = make_array(p, 0, sizeof(void *));
4702
    *valueszs = make_array(p, 0, sizeof(size_t));
×
4703

×
4704
    for (i = 0; i < reply->elements; i++) {
×
4705
      redisReply *value_elt;
4706
      void *value_data = NULL;
4707
      size_t value_datasz = 0;
1✔
4708

×
4709
      value_elt = reply->element[i];
4710
      if (value_elt->type == REDIS_REPLY_STRING) {
4711
        value_datasz = value_elt->len;
×
4712
        value_data = palloc(p, value_datasz);
×
4713
        memcpy(value_data, value_elt->str, value_datasz);
4714

4715
      } else {
×
4716
        pr_trace_msg(trace_channel, 2,
×
4717
          "expected STRING element at index %u, got %s", i + 1,
×
4718
          get_reply_type(value_elt->type));
×
4719
      }
4720

4721
      if (value_data != NULL) {
1✔
4722
        *((void **) push_array(*values)) = value_data;
1✔
4723
        *((size_t *) push_array(*valueszs)) = value_datasz;
4724
      }
1✔
4725
    }
4726

4727
    res = 0;
4728

1✔
4729
  } else {
1✔
4730
    xerrno = ENOENT;
4731
    res = -1;
4✔
4732
  }
3✔
4733

3✔
4734
  freeReplyObject(reply);
3✔
4735
  destroy_pool(tmp_pool);
4736

3✔
4737
  errno = xerrno;
3✔
4738
  return res;
3✔
4739
}
3✔
4740

3✔
4741
int pr_redis_set_kremove(pr_redis_t *redis, module *m, const char *key,
4742
    size_t keysz) {
4743

×
4744
  /* Note: We can actually use just DEL here. */
4745
  return pr_redis_kremove(redis, m, key, keysz);
4746
}
4747

4748
int pr_redis_set_ksetall(pr_redis_t *redis, module *m, const char *key,
×
4749
    size_t keysz, array_header *values, array_header *valueszs) {
3✔
4750
  register unsigned int i;
3✔
4751
  int res, xerrno = 0;
4752
  pool *tmp_pool = NULL;
4753
  array_header *args, *arglens;
4754
  const char *cmd = NULL;
4755
  redisReply *reply;
4756

4757
  if (redis == NULL ||
4758
      m == NULL ||
4759
      key == NULL ||
4760
      keysz == 0 ||
4761
      values == NULL ||
1✔
4762
      values->nelts == 0 ||
1✔
4763
      valueszs == NULL ||
4764
      valueszs->nelts == 0 ||
1✔
4765
      values->nelts != valueszs->nelts) {
1✔
4766
    errno = EINVAL;
4767
    return -1;
4768
  }
8✔
4769

4770
  /* First, delete any existing set at this key; a set operation, in my mind,
4771
   * is a complete overwrite.
4772
   */
7✔
4773
  res = pr_redis_set_kremove(redis, m, key, keysz);
4774
  if (res < 0 &&
4775
      errno != ENOENT) {
6✔
4776
    return -1;
4777
  }
6✔
4778

6✔
4779
  tmp_pool = make_sub_pool(redis->pool);
6✔
4780
  pr_pool_tag(tmp_pool, "Redis SADD pool");
6✔
4781

6✔
4782
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
6✔
4783

4784
  cmd = "SADD";
6✔
4785
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
6✔
4786

6✔
4787
  args = make_array(tmp_pool, 0, sizeof(char *));
6✔
4788
  arglens = make_array(tmp_pool, 0, sizeof(size_t));
5✔
4789

5✔
4790
  *((char **) push_array(args)) = pstrdup(tmp_pool, cmd);
3✔
4791
  *((size_t *) push_array(arglens)) = strlen(cmd);
3✔
4792

4793
  *((char **) push_array(args)) = (char *) key;
5✔
4794
  *((size_t *) push_array(arglens)) = keysz;
5✔
4795

4796
  for (i = 0; i < values->nelts; i++) {
4797
    pr_signals_handle();
4798

4799
    *((char **) push_array(args)) = ((char **) values->elts)[i];
4800
    *((size_t *) push_array(arglens)) = ((size_t *) valueszs->elts)[i];
1✔
4801
  }
1✔
4802

1✔
4803
  reply = redisCommandArgv(redis->ctx, args->nelts, args->elts, arglens->elts);
4804
  xerrno = errno;
4805

4806
  reply = handle_reply(redis, cmd, reply);
1✔
4807
  if (reply == NULL) {
1✔
4808
    pr_trace_msg(trace_channel, 2,
4809
      "error setting items in set using key (%lu bytes): %s",
1✔
4810
      (unsigned long) keysz, strerror(errno));
4811
    destroy_pool(tmp_pool);
1✔
4812
    errno = xerrno;
1✔
4813
    return -1;
4814
  }
1✔
4815

1✔
4816
  if (reply->type != REDIS_REPLY_INTEGER) {
4817
    pr_trace_msg(trace_channel, 2,
1✔
4818
      "expected INTEGER reply for %s, got %s", cmd,
1✔
4819
      get_reply_type(reply->type));
4820

1✔
4821
    if (reply->type == REDIS_REPLY_ERROR) {
1✔
4822
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
4823
    }
3✔
4824
    freeReplyObject(reply);
2✔
4825
    destroy_pool(tmp_pool);
4826
    errno = EINVAL;
2✔
4827
    return -1;
2✔
4828
  }
4829

4830
  pr_trace_msg(trace_channel, 7, "%s reply: %lld", cmd, reply->integer);
1✔
4831

1✔
4832
  freeReplyObject(reply);
4833
  destroy_pool(tmp_pool);
1✔
4834
  return 0;
1✔
4835
}
×
4836

4837
int pr_redis_sorted_set_kadd(pr_redis_t *redis, module *m, const char *key,
4838
    size_t keysz, void *value, size_t valuesz, float score) {
×
4839
  int xerrno = 0, exists = FALSE;
×
4840
  pool *tmp_pool = NULL;
×
4841
  const char *cmd = NULL;
4842
  redisReply *reply;
4843

1✔
4844
  if (redis == NULL ||
×
4845
      m == NULL ||
4846
      key == NULL ||
4847
      keysz == 0 ||
4848
      value == NULL ||
×
4849
      valuesz == 0) {
×
4850
    errno = EINVAL;
4851
    return -1;
×
4852
  }
×
4853

×
4854
  /* Note: We should probably detect the server version, and instead of using
×
4855
   * a separate existence check, if server >= 3.0.2, use the NX/XX flags of
4856
   * the ZADD command.
4857
   */
1✔
4858
  exists = pr_redis_sorted_set_kexists(redis, m, key, keysz, value, valuesz);
4859
  if (exists == TRUE) {
1✔
4860
    errno = EEXIST;
1✔
4861
    return -1;
1✔
4862
  }
4863

4864
  tmp_pool = make_sub_pool(redis->pool);
14✔
4865
  pr_pool_tag(tmp_pool, "Redis ZADD pool");
4866

14✔
4867
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
14✔
4868

14✔
4869
  cmd = "ZADD";
14✔
4870
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
4871
  reply = redisCommand(redis->ctx, "%s %b %f %b", cmd, key, keysz, score,
14✔
4872
    value, valuesz);
14✔
4873
  xerrno = errno;
14✔
4874

14✔
4875
  reply = handle_reply(redis, cmd, reply);
14✔
4876
  if (reply == NULL) {
14✔
4877
    pr_trace_msg(trace_channel, 2,
2✔
4878
      "error adding to sorted set using key (%lu bytes): %s",
2✔
4879
      (unsigned long) keysz, strerror(errno));
4880
    destroy_pool(tmp_pool);
4881
    errno = xerrno;
4882
    return -1;
4883
  }
4884

4885
  if (reply->type != REDIS_REPLY_INTEGER) {
12✔
4886
    pr_trace_msg(trace_channel, 2,
12✔
4887
      "expected INTEGER reply for %s, got %s", cmd,
1✔
4888
      get_reply_type(reply->type));
1✔
4889

4890
    if (reply->type == REDIS_REPLY_ERROR) {
4891
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
11✔
4892
    }
11✔
4893
    freeReplyObject(reply);
4894
    destroy_pool(tmp_pool);
11✔
4895
    errno = EINVAL;
4896
    return -1;
11✔
4897
  }
11✔
4898

11✔
4899
  pr_trace_msg(trace_channel, 7, "%s reply: %lld", cmd, reply->integer);
4900

11✔
4901
  freeReplyObject(reply);
4902
  destroy_pool(tmp_pool);
11✔
4903
  return 0;
11✔
4904
}
×
4905

4906
int pr_redis_sorted_set_kcount(pr_redis_t *redis, module *m, const char *key,
4907
    size_t keysz, uint64_t *count) {
×
4908
  int xerrno = 0;
×
4909
  pool *tmp_pool = NULL;
×
4910
  const char *cmd = NULL;
4911
  redisReply *reply;
4912

11✔
4913
  if (redis == NULL ||
×
4914
      m == NULL ||
4915
      key == NULL ||
4916
      keysz == 0 ||
4917
      count == NULL) {
×
4918
    errno = EINVAL;
×
4919
    return -1;
4920
  }
×
4921

×
4922
  tmp_pool = make_sub_pool(redis->pool);
×
4923
  pr_pool_tag(tmp_pool, "Redis ZCARD pool");
×
4924

4925
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
4926

11✔
4927
  cmd = "ZCARD";
4928
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
11✔
4929
  reply = redisCommand(redis->ctx, "%s %b", cmd, key, keysz);
11✔
4930
  xerrno = errno;
11✔
4931

4932
  reply = handle_reply(redis, cmd, reply);
4933
  if (reply == NULL) {
3✔
4934
    pr_trace_msg(trace_channel, 2,
4935
      "error getting count of sorted set using key (%lu bytes): %s",
3✔
4936
      (unsigned long) keysz, strerror(errno));
3✔
4937
    destroy_pool(tmp_pool);
3✔
4938
    errno = xerrno;
3✔
4939
    return -1;
4940
  }
3✔
4941

3✔
4942
  if (reply->type != REDIS_REPLY_INTEGER) {
3✔
4943
    pr_trace_msg(trace_channel, 2,
3✔
4944
      "expected INTEGER reply for %s, got %s", cmd,
4945
      get_reply_type(reply->type));
1✔
4946

1✔
4947
    if (reply->type == REDIS_REPLY_ERROR) {
4948
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
4949
    }
2✔
4950

2✔
4951
    freeReplyObject(reply);
4952
    destroy_pool(tmp_pool);
2✔
4953
    errno = EINVAL;
4954
    return -1;
2✔
4955
  }
2✔
4956

2✔
4957
  pr_trace_msg(trace_channel, 7, "%s reply: %lld", cmd, reply->integer);
2✔
4958
  *count = (uint64_t) reply->integer;
4959

2✔
4960
  freeReplyObject(reply);
2✔
4961
  destroy_pool(tmp_pool);
×
4962
  return 0;
4963
}
4964

×
4965
int pr_redis_sorted_set_kdelete(pr_redis_t *redis, module *m, const char *key,
×
4966
    size_t keysz, void *value, size_t valuesz) {
×
4967
  int xerrno = 0;
4968
  pool *tmp_pool = NULL;
4969
  const char *cmd = NULL;
2✔
4970
  redisReply *reply;
×
4971
  long long count = 0;
4972

4973
  if (redis == NULL ||
4974
      m == NULL ||
×
4975
      key == NULL ||
×
4976
      keysz == 0 ||
4977
      value == NULL ||
4978
      valuesz == 0) {
×
4979
    errno = EINVAL;
×
4980
    return -1;
×
4981
  }
×
4982

4983
  tmp_pool = make_sub_pool(redis->pool);
4984
  pr_pool_tag(tmp_pool, "Redis ZREM pool");
2✔
4985

2✔
4986
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
4987

2✔
4988
  cmd = "ZREM";
2✔
4989
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
2✔
4990
  reply = redisCommand(redis->ctx, "%s %b %b", cmd, key, keysz, value, valuesz);
4991
  xerrno = errno;
4992

4✔
4993
  reply = handle_reply(redis, cmd, reply);
4994
  if (reply == NULL) {
4✔
4995
    pr_trace_msg(trace_channel, 2,
4✔
4996
      "error deleting item from sorted set using key (%lu bytes): %s",
4✔
4997
      (unsigned long) keysz, strerror(errno));
4✔
4998
    destroy_pool(tmp_pool);
4✔
4999
    errno = xerrno;
5000
    return -1;
4✔
5001
  }
4✔
5002

4✔
5003
  if (reply->type != REDIS_REPLY_INTEGER) {
4✔
5004
    pr_trace_msg(trace_channel, 2,
4✔
5005
      "expected INTEGER reply for %s, got %s", cmd,
4✔
5006
      get_reply_type(reply->type));
2✔
5007

2✔
5008
    if (reply->type == REDIS_REPLY_ERROR) {
5009
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
5010
    }
2✔
5011

2✔
5012
    freeReplyObject(reply);
5013
    destroy_pool(tmp_pool);
2✔
5014
    errno = EINVAL;
5015
    return -1;
2✔
5016
  }
2✔
5017

2✔
5018
  pr_trace_msg(trace_channel, 7, "%s reply: %lld", cmd, reply->integer);
2✔
5019
  count = reply->integer;
5020

2✔
5021
  freeReplyObject(reply);
2✔
5022
  destroy_pool(tmp_pool);
×
5023

5024
  if (count == 0) {
5025
    /* No items removed. */
×
5026
    errno = ENOENT;
×
5027
    return -1;
×
5028
  }
5029

5030
  return 0;
2✔
5031
}
×
5032

5033
int pr_redis_sorted_set_kexists(pr_redis_t *redis, module *m, const char *key,
5034
    size_t keysz, void *value, size_t valuesz) {
5035
  int xerrno = 0, exists = FALSE;
×
5036
  pool *tmp_pool = NULL;
×
5037
  const char *cmd = NULL;
5038
  redisReply *reply;
5039

×
5040
  if (redis == NULL ||
×
5041
      m == NULL ||
×
5042
      key == NULL ||
×
5043
      keysz == 0 ||
5044
      value == NULL ||
5045
      valuesz == 0) {
2✔
5046
    errno = EINVAL;
2✔
5047
    return -1;
5048
  }
2✔
5049

2✔
5050
  tmp_pool = make_sub_pool(redis->pool);
5051
  pr_pool_tag(tmp_pool, "Redis ZRANK pool");
2✔
5052

5053
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
1✔
5054

1✔
5055
  cmd = "ZRANK";
5056
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
5057
  reply = redisCommand(redis->ctx, "%s %b %b", cmd, key, keysz, value, valuesz);
5058
  xerrno = errno;
5059

5060
  reply = handle_reply(redis, cmd, reply);
20✔
5061
  if (reply == NULL) {
5062
    pr_trace_msg(trace_channel, 2,
20✔
5063
      "error checking item in sorted set using key (%lu bytes): %s",
20✔
5064
      (unsigned long) keysz, strerror(errno));
20✔
5065
    destroy_pool(tmp_pool);
20✔
5066
    errno = xerrno;
5067
    return -1;
20✔
5068
  }
20✔
5069

20✔
5070
  if (reply->type != REDIS_REPLY_INTEGER &&
20✔
5071
      reply->type != REDIS_REPLY_NIL) {
20✔
5072
    pr_trace_msg(trace_channel, 2,
20✔
5073
      "expected INTEGER or NIL reply for %s, got %s", cmd,
2✔
5074
      get_reply_type(reply->type));
2✔
5075

5076
    if (reply->type == REDIS_REPLY_ERROR) {
5077
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
18✔
5078
    }
18✔
5079

5080
    freeReplyObject(reply);
18✔
5081
    destroy_pool(tmp_pool);
5082
    errno = EINVAL;
18✔
5083
    return -1;
18✔
5084
  }
18✔
5085

18✔
5086
  if (reply->type == REDIS_REPLY_INTEGER) {
5087
    pr_trace_msg(trace_channel, 7, "%s reply: %lld", cmd, reply->integer);
18✔
5088
    exists = TRUE;
18✔
5089

×
5090
  } else {
5091
    pr_trace_msg(trace_channel, 7, "%s reply: nil", cmd);
5092
    exists = FALSE;
×
5093
  }
×
5094

×
5095
  freeReplyObject(reply);
5096
  destroy_pool(tmp_pool);
5097
  return exists;
18✔
5098
}
5099

×
5100
int pr_redis_sorted_set_kgetn(pool *p, pr_redis_t *redis, module *m,
5101
    const char *key, size_t keysz, unsigned int offset, unsigned int len,
5102
    array_header **values, array_header **valueszs, int flags) {
5103
  int res = 0, xerrno = 0;
×
5104
  pool *tmp_pool = NULL;
×
5105
  const char *cmd = NULL;
5106
  redisReply *reply;
5107

×
5108
  if (p == NULL ||
×
5109
      redis == NULL ||
×
5110
      m == NULL ||
×
5111
      key == NULL ||
5112
      keysz == 0 ||
5113
      values == NULL ||
18✔
5114
      valueszs == NULL) {
4✔
5115
    errno = EINVAL;
4✔
5116
    return -1;
5117
  }
5118

14✔
5119
  tmp_pool = make_sub_pool(redis->pool);
14✔
5120

5121
  switch (flags) {
5122
    case PR_REDIS_SORTED_SET_FL_ASC:
18✔
5123
      pr_pool_tag(tmp_pool, "Redis ZRANGE pool");
18✔
5124
      cmd = "ZRANGE";
18✔
5125
      break;
5126

5127
    case PR_REDIS_SORTED_SET_FL_DESC:
6✔
5128
      pr_pool_tag(tmp_pool, "Redis ZREVRANGE pool");
5129
      cmd = "ZREVRANGE";
5130
      break;
6✔
5131

6✔
5132
    default:
6✔
5133
      errno = EINVAL;
6✔
5134
      return -1;
5135
  }
6✔
5136

6✔
5137
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
6✔
5138

6✔
5139
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
6✔
5140

6✔
5141
  /* Since the the range is [start, stop] inclusive, and the function takes
5142
   * a length, we need to subtract one for whose items these are.  Consider
2✔
5143
   * an offset of 0, and a len of 1 -- to get just one item.  In that case,
2✔
5144
   * stop would be 0 as well.
5145
   */
5146
  reply = redisCommand(redis->ctx, "%s %b %u %u", cmd, key, keysz, offset,
4✔
5147
    offset + len - 1);
5148
  xerrno = errno;
4✔
5149

2✔
5150
  reply = handle_reply(redis, cmd, reply);
2✔
5151
  if (reply == NULL) {
2✔
5152
    pr_trace_msg(trace_channel, 2,
2✔
5153
      "error getting %u %s in sorted set using key (%lu bytes): %s", len,
5154
      len != 1 ? "items" : "item", (unsigned long) keysz, strerror(errno));
1✔
5155
    destroy_pool(tmp_pool);
1✔
5156
    errno = xerrno;
1✔
5157
    return -1;
1✔
5158
  }
5159

1✔
5160
  if (reply->type != REDIS_REPLY_ARRAY) {
1✔
5161
    pr_trace_msg(trace_channel, 2,
1✔
5162
      "expected ARRAY reply for %s, got %s", cmd, get_reply_type(reply->type));
5163

5164
    if (reply->type == REDIS_REPLY_ERROR) {
3✔
5165
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
5166
    }
3✔
5167

5168
    freeReplyObject(reply);
5169
    destroy_pool(tmp_pool);
5170
    errno = EINVAL;
5171
    return -1;
5172
  }
5173

6✔
5174
  if (reply->elements > 0) {
3✔
5175
    register unsigned int i;
3✔
5176

5177
    pr_trace_msg(trace_channel, 7, "%s reply: %lu %s", cmd,
3✔
5178
      (unsigned long) reply->elements,
3✔
5179
      reply->elements != 1 ? "elements" : "element");
×
5180

5181
    *values = make_array(p, 0, sizeof(void *));
5182
    *valueszs = make_array(p, 0, sizeof(size_t));
×
5183

×
5184
    for (i = 0; i < reply->elements; i++) {
×
5185
      redisReply *value_elt;
5186
      void *value_data = NULL;
5187
      size_t value_datasz = 0;
3✔
5188

×
5189
      value_elt = reply->element[i];
5190
      if (value_elt->type == REDIS_REPLY_STRING) {
5191
        value_datasz = value_elt->len;
×
5192
        value_data = palloc(p, value_datasz);
×
5193
        memcpy(value_data, value_elt->str, value_datasz);
5194

5195
      } else {
×
5196
        pr_trace_msg(trace_channel, 2,
×
5197
          "expected STRING element at index %u, got %s", i + 1,
×
5198
          get_reply_type(value_elt->type));
×
5199
      }
5200

5201
      if (value_data != NULL) {
3✔
5202
        *((void **) push_array(*values)) = value_data;
3✔
5203
        *((size_t *) push_array(*valueszs)) = value_datasz;
5204
      }
3✔
5205
    }
5206

5207
    res = 0;
5208

3✔
5209
  } else {
3✔
5210
    xerrno = ENOENT;
5211
    res = -1;
10✔
5212
  }
7✔
5213

7✔
5214
  freeReplyObject(reply);
7✔
5215
  destroy_pool(tmp_pool);
5216

7✔
5217
  errno = xerrno;
7✔
5218
  return res;
7✔
5219
}
7✔
5220

7✔
5221
int pr_redis_sorted_set_kincr(pr_redis_t *redis, module *m, const char *key,
5222
    size_t keysz, void *value, size_t valuesz, float incr, float *score) {
5223
  int res, xerrno, exists;
×
5224
  pool *tmp_pool = NULL;
5225
  const char *cmd = NULL;
5226
  redisReply *reply;
5227

5228
  if (redis == NULL ||
×
5229
      m == NULL ||
7✔
5230
      key == NULL ||
7✔
5231
      keysz == 0 ||
5232
      value == NULL ||
5233
      valuesz == 0 ||
5234
      score == NULL) {
5235
    errno = EINVAL;
5236
    return -1;
5237
  }
5238

5239
  exists = pr_redis_sorted_set_kexists(redis, m, key, keysz, value, valuesz);
5240
  if (exists == FALSE) {
5241
    errno = ENOENT;
3✔
5242
    return -1;
3✔
5243
  }
5244

3✔
5245
  tmp_pool = make_sub_pool(redis->pool);
3✔
5246
  pr_pool_tag(tmp_pool, "Redis ZINCRBY pool");
5247

5248
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
5✔
5249

5250
  cmd = "ZINCRBY";
5✔
5251
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
5✔
5252
  reply = redisCommand(redis->ctx, "%s %b %f %b", cmd, key, keysz, incr,
5✔
5253
    value, valuesz);
5✔
5254
  xerrno = errno;
5255

5✔
5256
  reply = handle_reply(redis, cmd, reply);
5✔
5257
  if (reply == NULL) {
5✔
5258
    pr_trace_msg(trace_channel, 2,
5✔
5259
      "error incrementing key (%lu bytes) by %0.3f in sorted set using %s: %s",
5✔
5260
      (unsigned long) keysz, incr, cmd, strerror(errno));
5✔
5261
    destroy_pool(tmp_pool);
5262
    errno = EIO;
3✔
5263
    return -1;
3✔
5264
  }
5265

5266
  if (reply->type != REDIS_REPLY_STRING) {
2✔
5267
    pr_trace_msg(trace_channel, 2,
2✔
5268
      "expected STRING reply for %s, got %s", cmd,
1✔
5269
      get_reply_type(reply->type));
1✔
5270

5271
    if (reply->type == REDIS_REPLY_ERROR) {
5272
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
1✔
5273
    }
1✔
5274

5275
    freeReplyObject(reply);
1✔
5276
    destroy_pool(tmp_pool);
5277
    errno = EINVAL;
1✔
5278
    return -1;
1✔
5279
  }
1✔
5280

5281
  pr_trace_msg(trace_channel, 7, "%s reply: %.*s", cmd, (int) reply->len,
1✔
5282
    reply->str);
5283

1✔
5284
  res = sscanf(reply->str, "%f", score);
1✔
5285
  if (res != 1) {
×
5286
    pr_trace_msg(trace_channel, 3, "error parsing '%.*s' as float",
5287
      (int) reply->len, reply->str);
5288
    xerrno = EINVAL;
×
5289
    res = -1;
×
5290

×
5291
  } else {
5292
    res = 0;
5293
  }
1✔
5294

×
5295
  freeReplyObject(reply);
5296
  destroy_pool(tmp_pool);
5297

5298
  errno = xerrno;
×
5299
  return res;
×
5300
}
5301

5302
int pr_redis_sorted_set_kremove(pr_redis_t *redis, module *m, const char *key,
×
5303
    size_t keysz) {
×
5304

×
5305
  /* Note: We can actually use just DEL here. */
×
5306
  return pr_redis_kremove(redis, m, key, keysz);
5307
}
5308

1✔
5309
int pr_redis_sorted_set_kscore(pr_redis_t *redis, module *m, const char *key,
5310
    size_t keysz, void *value, size_t valuesz, float *score) {
5311
  int res, xerrno;
1✔
5312
  pool *tmp_pool = NULL;
1✔
5313
  const char *cmd = NULL;
×
5314
  redisReply *reply;
×
5315

×
5316
  if (redis == NULL ||
×
5317
      m == NULL ||
5318
      key == NULL ||
5319
      keysz == 0 ||
5320
      value == NULL ||
5321
      valuesz == 0 ||
5322
      score == NULL) {
1✔
5323
    errno = EINVAL;
1✔
5324
    return -1;
5325
  }
1✔
5326

1✔
5327
  tmp_pool = make_sub_pool(redis->pool);
5328
  pr_pool_tag(tmp_pool, "Redis ZSCORE pool");
5329

3✔
5330
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
5331

5332
  cmd = "ZSCORE";
5333
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
2✔
5334
  reply = redisCommand(redis->ctx, "%s %b %b", cmd, key, keysz, value, valuesz);
5335
  xerrno = errno;
5336

5✔
5337
  reply = handle_reply(redis, cmd, reply);
5338
  if (reply == NULL) {
5✔
5339
    pr_trace_msg(trace_channel, 2,
5✔
5340
      "error gettin score for key (%lu bytes) using %s: %s",
5✔
5341
      (unsigned long) keysz, cmd, strerror(errno));
5✔
5342
    destroy_pool(tmp_pool);
5343
    errno = EIO;
5✔
5344
    return -1;
5✔
5345
  }
5✔
5346

5✔
5347
  if (reply->type != REDIS_REPLY_STRING &&
5✔
5348
      reply->type != REDIS_REPLY_NIL) {
5✔
5349
    pr_trace_msg(trace_channel, 2,
5350
      "expected STRING or NIL reply for %s, got %s", cmd,
3✔
5351
      get_reply_type(reply->type));
3✔
5352

5353
    if (reply->type == REDIS_REPLY_ERROR) {
5354
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
2✔
5355
    }
2✔
5356

5357
    freeReplyObject(reply);
2✔
5358
    destroy_pool(tmp_pool);
5359
    errno = EINVAL;
2✔
5360
    return -1;
2✔
5361
  }
2✔
5362

2✔
5363
  if (reply->type == REDIS_REPLY_STRING) {
5364
    pr_trace_msg(trace_channel, 7, "%s reply: %.*s", cmd, (int) reply->len,
2✔
5365
      reply->str);
2✔
5366

×
5367
    res = sscanf(reply->str, "%f", score);
5368
    if (res != 1) {
5369
      pr_trace_msg(trace_channel, 3, "error parsing '%.*s' as float",
×
5370
        (int) reply->len, reply->str);
×
5371
      xerrno = EINVAL;
×
5372
      res = -1;
5373

5374
    } else {
2✔
5375
      res = 0;
5376
    }
×
5377

5378
  } else {
5379
    pr_trace_msg(trace_channel, 7, "%s reply: nil", cmd);
5380
    xerrno = ENOENT;
×
5381
    res = -1;
×
5382
  }
5383

5384
  freeReplyObject(reply);
×
5385
  destroy_pool(tmp_pool);
×
5386

×
5387
  errno = xerrno;
×
5388
  return res;
5389
}
5390

2✔
5391
int pr_redis_sorted_set_kset(pr_redis_t *redis, module *m, const char *key,
1✔
5392
    size_t keysz, void *value, size_t valuesz, float score) {
5393
  int xerrno = 0, exists = FALSE;
5394
  pool *tmp_pool = NULL;
1✔
5395
  const char *cmd = NULL;
1✔
5396
  redisReply *reply;
×
5397

×
5398
  if (redis == NULL ||
×
5399
      m == NULL ||
×
5400
      key == NULL ||
5401
      keysz == 0 ||
5402
      value == NULL ||
5403
      valuesz == 0) {
5404
    errno = EINVAL;
5405
    return -1;
5406
  }
1✔
5407

1✔
5408
  /* Note: We should probably detect the server version, and instead of using
1✔
5409
   * a separate existence check, if server >= 3.0.2, use the NX/XX flags of
5410
   * the ZADD command.
5411
   */
2✔
5412
  exists = pr_redis_sorted_set_kexists(redis, m, key, keysz, value, valuesz);
2✔
5413
  if (exists == FALSE) {
5414
    errno = ENOENT;
2✔
5415
    return -1;
2✔
5416
  }
5417

5418
  tmp_pool = make_sub_pool(redis->pool);
4✔
5419
  pr_pool_tag(tmp_pool, "Redis ZADD pool");
5420

4✔
5421
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
4✔
5422

4✔
5423
  cmd = "ZADD";
4✔
5424
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
5425
  reply = redisCommand(redis->ctx, "%s %b %f %b", cmd, key, keysz, score,
4✔
5426
    value, valuesz);
4✔
5427
  xerrno = errno;
4✔
5428

4✔
5429
  reply = handle_reply(redis, cmd, reply);
4✔
5430
  if (reply == NULL) {
4✔
5431
    pr_trace_msg(trace_channel, 2,
2✔
5432
      "error setting item in sorted set using key (%lu bytes): %s",
2✔
5433
      (unsigned long) keysz, strerror(errno));
5434
    destroy_pool(tmp_pool);
5435
    errno = xerrno;
5436
    return -1;
5437
  }
5438

5439
  if (reply->type != REDIS_REPLY_INTEGER) {
2✔
5440
    pr_trace_msg(trace_channel, 2,
2✔
5441
      "expected INTEGER reply for %s, got %s", cmd,
1✔
5442
      get_reply_type(reply->type));
1✔
5443

5444
    if (reply->type == REDIS_REPLY_ERROR) {
5445
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
1✔
5446
    }
1✔
5447
    freeReplyObject(reply);
5448
    destroy_pool(tmp_pool);
1✔
5449
    errno = EINVAL;
5450
    return -1;
1✔
5451
  }
1✔
5452

1✔
5453
  pr_trace_msg(trace_channel, 7, "%s reply: %lld", cmd, reply->integer);
5454

1✔
5455
  freeReplyObject(reply);
5456
  destroy_pool(tmp_pool);
1✔
5457
  return 0;
1✔
5458
}
×
5459

5460
static char *f2s(pool *p, float num, size_t *len) {
5461
  int res;
×
5462
  char *s;
×
5463
  size_t sz;
×
5464

5465
  sz = 32;
5466
  s = pcalloc(p, sz + 1);
1✔
5467
  res = pr_snprintf(s, sz, "%0.3f", num);
×
5468

5469
  *len = res;
5470
  return s;
5471
}
×
5472

×
5473
int pr_redis_sorted_set_ksetall(pr_redis_t *redis, module *m, const char *key,
5474
    size_t keysz, array_header *values, array_header *valueszs,
×
5475
    array_header *scores) {
×
5476
  register unsigned int i;
×
5477
  int res, xerrno = 0;
×
5478
  pool *tmp_pool = NULL;
5479
  array_header *args, *arglens;
5480
  const char *cmd = NULL;
1✔
5481
  redisReply *reply;
5482

1✔
5483
  if (redis == NULL ||
1✔
5484
      m == NULL ||
1✔
5485
      key == NULL ||
5486
      keysz == 0 ||
5487
      values == NULL ||
2✔
5488
      values->nelts == 0 ||
2✔
5489
      valueszs == NULL ||
2✔
5490
      valueszs->nelts == 0 ||
2✔
5491
      scores == NULL ||
5492
      scores->nelts == 0) {
2✔
5493
    errno = EINVAL;
2✔
5494
    return -1;
2✔
5495
  }
5496

2✔
5497
  if (values->nelts != valueszs->nelts ||
2✔
5498
      values->nelts != scores->nelts) {
5499
    errno = EINVAL;
5500
    return -1;
9✔
5501
  }
5502

5503
  /* First, delete any existing sorted set at this key; a set operation,
9✔
5504
   * in my mind, is a complete overwrite.
9✔
5505
   */
9✔
5506
  res = pr_redis_sorted_set_kremove(redis, m, key, keysz);
9✔
5507
  if (res < 0 &&
9✔
5508
      errno != ENOENT) {
9✔
5509
    return -1;
5510
  }
9✔
5511

9✔
5512
  tmp_pool = make_sub_pool(redis->pool);
9✔
5513
  pr_pool_tag(tmp_pool, "Redis ZADD pool");
9✔
5514

8✔
5515
  key = get_namespace_key(tmp_pool, redis, m, key, &keysz);
8✔
5516

6✔
5517
  cmd = "ZADD";
6✔
5518
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
3✔
5519

3✔
5520
  args = make_array(tmp_pool, 0, sizeof(char *));
7✔
5521
  arglens = make_array(tmp_pool, 0, sizeof(size_t));
7✔
5522

5523
  *((char **) push_array(args)) = pstrdup(tmp_pool, cmd);
5524
  *((size_t *) push_array(arglens)) = strlen(cmd);
2✔
5525

5526
  *((char **) push_array(args)) = (char *) key;
1✔
5527
  *((size_t *) push_array(arglens)) = keysz;
1✔
5528

5529
  for (i = 0; i < values->nelts; i++) {
5530
    size_t scoresz = 0;
5531

5532
    pr_signals_handle();
5533

1✔
5534
    *((char **) push_array(args)) = f2s(tmp_pool, ((float *) scores->elts)[i],
1✔
5535
      &scoresz);
1✔
5536
    *((size_t *) push_array(arglens)) = scoresz;
5537

5538
    *((char **) push_array(args)) = ((char **) values->elts)[i];
5539
    *((size_t *) push_array(arglens)) = ((size_t *) valueszs->elts)[i];
1✔
5540
  }
1✔
5541

5542
  reply = redisCommandArgv(redis->ctx, args->nelts, args->elts, arglens->elts);
1✔
5543
  xerrno = errno;
5544

1✔
5545
  reply = handle_reply(redis, cmd, reply);
1✔
5546
  if (reply == NULL) {
5547
    pr_trace_msg(trace_channel, 2,
1✔
5548
      "error setting items in sorted set using key (%lu bytes): %s",
1✔
5549
      (unsigned long) keysz, strerror(errno));
5550
    destroy_pool(tmp_pool);
1✔
5551
    errno = xerrno;
1✔
5552
    return -1;
5553
  }
1✔
5554

1✔
5555
  if (reply->type != REDIS_REPLY_INTEGER) {
5556
    pr_trace_msg(trace_channel, 2,
3✔
5557
      "expected INTEGER reply for %s, got %s", cmd,
2✔
5558
      get_reply_type(reply->type));
5559

2✔
5560
    if (reply->type == REDIS_REPLY_ERROR) {
5561
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
2✔
5562
    }
5563
    freeReplyObject(reply);
2✔
5564
    destroy_pool(tmp_pool);
5565
    errno = EINVAL;
2✔
5566
    return -1;
2✔
5567
  }
5568

5569
  pr_trace_msg(trace_channel, 7, "%s reply: %lld", cmd, reply->integer);
1✔
5570

1✔
5571
  freeReplyObject(reply);
5572
  destroy_pool(tmp_pool);
1✔
5573
  return 0;
1✔
5574
}
×
5575

5576
int pr_redis_sentinel_get_master_addr(pool *p, pr_redis_t *redis,
5577
    const char *name, pr_netaddr_t **addr) {
×
5578
  int res = 0, xerrno = 0;
×
5579
  pool *tmp_pool = NULL;
×
5580
  const char *cmd = NULL;
5581
  redisReply *reply;
5582

1✔
5583
  if (p == NULL ||
×
5584
      redis == NULL ||
5585
      name == NULL ||
5586
      addr == NULL) {
5587
    errno = EINVAL;
×
5588
    return -1;
×
5589
  }
5590

×
5591
  tmp_pool = make_sub_pool(redis->pool);
×
5592
  pr_pool_tag(tmp_pool, "Redis SENTINEL pool");
×
5593

×
5594
  cmd = "SENTINEL";
5595
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
5596
  reply = redisCommand(redis->ctx, "%s get-master-addr-by-name %s", cmd, name);
1✔
5597
  xerrno = errno;
5598

1✔
5599
  reply = handle_reply(redis, cmd, reply);
1✔
5600
  if (reply == NULL) {
1✔
5601
    pr_trace_msg(trace_channel, 2,
5602
      "error getting address for master '%s': %s", name, strerror(errno));
5603
    destroy_pool(tmp_pool);
5✔
5604
    errno = EIO;
5605
    return -1;
5✔
5606
  }
5✔
5607

5✔
5608
  if (reply->type == REDIS_REPLY_NIL) {
5✔
5609
    pr_trace_msg(trace_channel, 7, "%s reply: nil", cmd);
5610
    freeReplyObject(reply);
5✔
5611
    destroy_pool(tmp_pool);
5✔
5612

3✔
5613
    errno = ENOENT;
3✔
5614
    return -1;
4✔
5615
  }
4✔
5616

5617
  if (reply->type != REDIS_REPLY_ARRAY) {
5618
    pr_trace_msg(trace_channel, 2,
1✔
5619
      "expected ARRAY reply for %s, got %s", cmd, get_reply_type(reply->type));
1✔
5620

5621
    if (reply->type == REDIS_REPLY_ERROR) {
1✔
5622
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
1✔
5623
    }
1✔
5624

1✔
5625
    freeReplyObject(reply);
5626
    destroy_pool(tmp_pool);
1✔
5627
    errno = EINVAL;
1✔
5628
    return -1;
×
5629
  }
5630

×
5631
  if (reply->elements > 0) {
×
5632
    redisReply *elt;
×
5633
    char *host = NULL;
5634
    int port = -1;
5635

1✔
5636
    pr_trace_msg(trace_channel, 7, "%s reply: %lu %s", cmd,
×
5637
      (unsigned long) reply->elements,
×
5638
      reply->elements != 1 ? "elements" : "element");
×
5639

5640
    elt = reply->element[0];
×
5641
    if (elt->type == REDIS_REPLY_STRING) {
×
5642
      host = pstrndup(tmp_pool, elt->str, elt->len);
5643

5644
    } else {
1✔
5645
      pr_trace_msg(trace_channel, 2,
1✔
5646
        "expected STRING element at index 0, got %s",
5647
        get_reply_type(elt->type));
5648
    }
1✔
5649

1✔
5650
    elt = reply->element[1];
5651
    if (elt->type == REDIS_REPLY_STRING) {
5652
      char *port_str;
1✔
5653

1✔
5654
      port_str = pstrndup(tmp_pool, elt->str, elt->len);
1✔
5655
      port = atoi(port_str);
1✔
5656

5657
    } else {
5658
      pr_trace_msg(trace_channel, 2,
×
5659
        "expected STRING element at index 1, got %s",
×
5660
        get_reply_type(elt->type));
×
5661
    }
×
5662

5663
    if (host != NULL &&
×
5664
        port != -1) {
5665
      *addr = (pr_netaddr_t *) pr_netaddr_get_addr(p, host, NULL);
5666
      if (*addr != NULL) {
5667
        pr_netaddr_set_port2(*addr, port);
×
5668

×
5669
      } else {
×
5670
        xerrno = errno;
5671
        res = -1;
5672
      }
×
5673

5674
    } else {
5675
      xerrno = ENOENT;
5676
      res = -1;
5677
    }
×
5678

×
5679
  } else {
×
5680
    xerrno = ENOENT;
5681
    res = -1;
×
5682
  }
×
5683

5684
  freeReplyObject(reply);
5685
  destroy_pool(tmp_pool);
×
5686

5687
  errno = xerrno;
5688
  return res;
5689
}
5690

×
5691
int pr_redis_sentinel_get_masters(pool *p, pr_redis_t *redis,
×
5692
    array_header **masters) {
×
5693
  int res = 0, xerrno = 0;
×
5694
  pool *tmp_pool = NULL;
×
5695
  const char *cmd = NULL;
5696
  redisReply *reply;
5697

×
5698
  if (p == NULL ||
×
5699
      redis == NULL ||
5700
      masters == NULL) {
5701
    errno = EINVAL;
5702
    return -1;
5703
  }
5704

5705
  tmp_pool = make_sub_pool(redis->pool);
5706
  pr_pool_tag(tmp_pool, "Redis SENTINEL pool");
5707

5708
  cmd = "SENTINEL";
5709
  pr_trace_msg(trace_channel, 7, "sending command: %s", cmd);
5710
  reply = redisCommand(redis->ctx, "%s masters", cmd);
5711
  xerrno = errno;
×
5712

×
5713
  reply = handle_reply(redis, cmd, reply);
5714
  if (reply == NULL) {
×
5715
    pr_trace_msg(trace_channel, 2,
×
5716
      "error getting masters: %s", strerror(errno));
5717
    destroy_pool(tmp_pool);
5718
    errno = EIO;
4✔
5719
    return -1;
5720
  }
4✔
5721

4✔
5722
  if (reply->type == REDIS_REPLY_NIL) {
4✔
5723
    pr_trace_msg(trace_channel, 7, "%s reply: nil", cmd);
4✔
5724
    freeReplyObject(reply);
5725
    destroy_pool(tmp_pool);
4✔
5726

4✔
5727
    errno = ENOENT;
5728
    return -1;
3✔
5729
  }
3✔
5730

5731
  if (reply->type != REDIS_REPLY_ARRAY) {
5732
    pr_trace_msg(trace_channel, 2,
1✔
5733
      "expected ARRAY reply for %s, got %s", cmd, get_reply_type(reply->type));
1✔
5734

5735
    if (reply->type == REDIS_REPLY_ERROR) {
1✔
5736
      pr_trace_msg(trace_channel, 2, "%s error: %s", cmd, reply->str);
1✔
5737
    }
1✔
5738

1✔
5739
    freeReplyObject(reply);
5740
    destroy_pool(tmp_pool);
1✔
5741
    errno = EINVAL;
1✔
5742
    return -1;
×
5743
  }
5744

×
5745
  if (reply->elements > 0) {
×
5746
    register unsigned int i;
×
5747

5748
    pr_trace_msg(trace_channel, 7, "%s reply: %lu %s", cmd,
5749
      (unsigned long) reply->elements,
1✔
5750
      reply->elements != 1 ? "elements" : "element");
×
5751

×
5752
    *masters = make_array(p, reply->elements, sizeof(char *));
×
5753

5754
    for (i = 0; i < reply->elements; i++) {
×
5755
      redisReply *elt;
×
5756

5757
      elt = reply->element[i];
5758
      if (elt->type == REDIS_REPLY_ARRAY) {
1✔
5759
        redisReply *info;
1✔
5760

5761
        info = elt->element[1];
5762
        *((char **) push_array(*masters)) = pstrndup(p, info->str, info->len);
1✔
5763

1✔
5764
      } else {
5765
        pr_trace_msg(trace_channel, 2,
5766
          "expected ARRAY element at index %u, got %s", i,
1✔
5767
          get_reply_type(elt->type));
1✔
5768
      }
1✔
5769
    }
1✔
5770

5771
  } else {
5772
    xerrno = ENOENT;
×
5773
    res = -1;
×
5774
  }
5775

×
5776
  freeReplyObject(reply);
5777
  destroy_pool(tmp_pool);
5778

5779
  errno = xerrno;
×
5780
  return res;
5781
}
×
5782

×
5783
int redis_set_server3(const char *server, int port, unsigned long flags,
5784
    const char *username, const char *password, const char *db_idx,
×
5785
    int use_ssl, const char *ssl_cacert, const char *ssl_cert,
×
5786
    const char *ssl_key) {
×
5787

5788
  if (server == NULL) {
×
5789
    /* By using a port of -2 specifically, we can use this function to
×
5790
     * clear the server/port, for testing purposes ONLY.
5791
     */
5792
    if (port < 1 &&
×
5793
        port != -2) {
5794
      errno = EINVAL;
5795
      return -1;
5796
    }
5797
  }
5798

5799
  redis_server = server;
5800
  redis_port = port;
5801
  redis_flags = flags;
5802
  redis_username = username;
5803
  redis_password = password;
×
5804
  redis_db_idx = db_idx;
×
5805
  redis_use_ssl = use_ssl;
5806

×
5807
#if defined(PR_USE_REDIS_SSL)
×
5808
  redis_ssl_cacert = ssl_cacert;
5809
  redis_ssl_cert = ssl_cert;
5810
  redis_ssl_key = ssl_key;
72✔
5811
#endif /* PR_USE_REDIS_SSL */
5812

5813
  return 0;
5814
}
5815

72✔
5816
int redis_set_server2(const char *server, int port, unsigned long flags,
5817
    const char *username, const char *password, const char *db_idx) {
5818
  return redis_set_server3(server, port, flags, username, password, db_idx,
5819
    FALSE, NULL, NULL, NULL);
1✔
5820
}
1✔
5821

×
5822
int redis_set_server(const char *server, int port, unsigned long flags,
×
5823
    const char *password, const char *db_idx) {
5824
  return redis_set_server2(server, port, flags, "default", password, db_idx);
5825
}
5826

72✔
5827
int redis_set_sentinels2(array_header *sentinels, const char *name,
72✔
5828
    int use_ssl, const char *ssl_cacert, const char *ssl_cert,
72✔
5829
    const char *ssl_key) {
72✔
5830

72✔
5831
  if (sentinels != NULL &&
72✔
5832
      sentinels->nelts == 0) {
72✔
5833
    errno = EINVAL;
5834
    return -1;
5835
  }
5836

5837
  redis_sentinels = sentinels;
5838
  redis_sentinel_master = name;
5839
  redis_use_ssl = use_ssl;
5840

72✔
5841
#if defined(PR_USE_REDIS_SSL)
5842
  redis_ssl_cacert = ssl_cacert;
5843
  redis_ssl_cert = ssl_cert;
72✔
5844
  redis_ssl_key = ssl_key;
5845
#endif /* PR_USE_REDIS_SSL */
72✔
5846

5847
  return 0;
5848
}
5849

72✔
5850
int redis_set_sentinels(array_header *sentinels, const char *name) {
5851
  return redis_set_sentinels2(sentinels, name, FALSE, NULL, NULL, NULL);
72✔
5852
}
5853

5854
int redis_set_timeouts(unsigned long connect_millis, unsigned long io_millis) {
2✔
5855
  redis_connect_millis = connect_millis;
5856
  redis_io_millis = io_millis;
5857

5858
  return 0;
×
5859
}
1✔
5860

×
5861
int redis_clear(void) {
×
5862
  if (sess_redis != NULL) {
5863
    pr_redis_conn_destroy(sess_redis);
5864
    sess_redis = NULL;
2✔
5865
  }
2✔
5866

2✔
5867
  return 0;
5868
}
5869

5870
int redis_init(void) {
5871
  return 0;
5872
}
5873

5874
#else
2✔
5875

5876
pr_redis_t *pr_redis_conn_get(pool *p, unsigned long flags) {
5877
  errno = ENOSYS;
2✔
5878
  return NULL;
2✔
5879
}
5880

5881
pr_redis_t *pr_redis_conn_new(pool *p, module *m, unsigned long flags) {
×
5882
  errno = ENOSYS;
×
5883
  return NULL;
×
5884
}
5885

×
5886
int pr_redis_conn_close(pr_redis_t *redis) {
5887
  errno = ENOSYS;
5888
  return -1;
68✔
5889
}
68✔
5890

×
5891
int pr_redis_conn_destroy(pr_redis_t *redis) {
×
5892
  errno = ENOSYS;
5893
  return -1;
5894
}
68✔
5895

5896
int pr_redis_conn_set_namespace(pr_redis_t *redis, module *m,
5897
    const void *prefix, size_t prefixsz) {
68✔
5898
  errno = ENOSYS;
68✔
5899
  return -1;
5900
}
5901

5902
int pr_redis_auth(pr_redis_t *redis, const char *password) {
5903
  errno = ENOSYS;
5904
  return -1;
5905
}
5906

5907
int pr_redis_select(pr_redis_t *redis, const char *db_idx) {
5908
  errno = ENOSYS;
5909
  return -1;
5910
}
5911

5912
int pr_redis_command(pr_redis_t *redis, const array_header *args,
5913
    int reply_type) {
5914
  errno = ENOSYS;
5915
  return -1;
5916
}
5917

5918
int pr_redis_add(pr_redis_t *redis, module *m, const char *key, void *value,
5919
    size_t valuesz, time_t expires) {
5920
  errno = ENOSYS;
5921
  return -1;
5922
}
5923

5924
int pr_redis_decr(pr_redis_t *redis, module *m, const char *key, uint32_t decr,
5925
    uint64_t *value) {
5926
  errno = ENOSYS;
5927
  return -1;
5928
}
5929

5930
void *pr_redis_get(pool *p, pr_redis_t *redis, module *m, const char *key,
5931
    size_t *valuesz) {
5932
  errno = ENOSYS;
5933
  return NULL;
5934
}
5935

5936
char *pr_redis_get_str(pool *p, pr_redis_t *redis, module *m, const char *key) {
5937
  errno = ENOSYS;
5938
  return NULL;
5939
}
5940

5941
int pr_redis_incr(pr_redis_t *redis, module *m, const char *key, uint32_t incr,
5942
    uint64_t *value) {
5943
  errno = ENOSYS;
5944
  return -1;
5945
}
5946

5947
int pr_redis_remove(pr_redis_t *redis, module *m, const char *key) {
5948
  errno = ENOSYS;
5949
  return -1;
5950
}
5951

5952
int pr_redis_rename(pr_redis_t *redis, module *m, const char *from,
5953
    const char *to) {
5954
  errno = ENOSYS;
5955
  return -1;
5956
}
5957

5958
int pr_redis_set(pr_redis_t *redis, module *m, const char *key, void *value,
5959
    size_t valuesz, time_t expires) {
5960
  errno = ENOSYS;
5961
  return -1;
5962
}
5963

5964
int pr_redis_hash_count(pr_redis_t *redis, module *m, const char *key,
5965
    uint64_t *count) {
5966
  errno = ENOSYS;
5967
  return -1;
5968
}
5969

5970
int pr_redis_hash_delete(pr_redis_t *redis, module *m, const char *key,
5971
    const char *field) {
5972
  errno = ENOSYS;
5973
  return -1;
5974
}
5975

5976
int pr_redis_hash_exists(pr_redis_t *redis, module *m, const char *key,
5977
    const char *field) {
5978
  errno = ENOSYS;
5979
  return -1;
5980
}
5981

5982
int pr_redis_hash_get(pool *p, pr_redis_t *redis, module *m, const char *key,
5983
    const char *field, void **value, size_t *valuesz) {
5984
  errno = ENOSYS;
5985
  return -1;
5986
}
5987

5988
int pr_redis_hash_getall(pool *p, pr_redis_t *redis, module *m,
5989
    const char *key, pr_table_t **hash) {
5990
  errno = ENOSYS;
5991
  return -1;
5992
}
5993

5994
int pr_redis_hash_incr(pr_redis_t *redis, module *m, const char *key,
5995
    const char *field, int32_t incr, int64_t *value) {
5996
  errno = ENOSYS;
5997
  return -1;
5998
}
5999

6000
int pr_redis_hash_keys(pool *p, pr_redis_t *redis, module *m, const char *key,
6001
    array_header **fields) {
6002
  errno = ENOSYS;
6003
  return -1;
6004
}
6005

6006
int pr_redis_hash_remove(pr_redis_t *redis, module *m, const char *key) {
6007
  errno = ENOSYS;
6008
  return -1;
6009
}
6010

6011
int pr_redis_hash_set(pr_redis_t *redis, module *m, const char *key,
6012
    const char *field, void *value, size_t valuesz) {
6013
  errno = ENOSYS;
6014
  return -1;
6015
}
6016

6017
int pr_redis_hash_setall(pr_redis_t *redis, module *m, const char *key,
6018
    pr_table_t *hash) {
6019
  errno = ENOSYS;
6020
  return -1;
6021
}
6022

6023
int pr_redis_hash_values(pool *p, pr_redis_t *redis, module *m,
6024
    const char *key, array_header **values) {
6025
  errno = ENOSYS;
6026
  return -1;
6027
}
6028

6029
int pr_redis_list_append(pr_redis_t *redis, module *m, const char *key,
6030
    void *value, size_t valuesz) {
6031
  errno = ENOSYS;
6032
  return -1;
6033
}
6034

6035
int pr_redis_list_count(pr_redis_t *redis, module *m, const char *key,
6036
    uint64_t *count) {
6037
  errno = ENOSYS;
6038
  return -1;
6039
}
6040

6041
int pr_redis_list_delete(pr_redis_t *redis, module *m, const char *key,
6042
    void *value, size_t valuesz) {
6043
  errno = ENOSYS;
6044
  return -1;
6045
}
6046

6047
int pr_redis_list_exists(pr_redis_t *redis, module *m, const char *key,
6048
    unsigned int idx) {
6049
  errno = ENOSYS;
6050
  return -1;
6051
}
6052

6053
int pr_redis_list_get(pool *p, pr_redis_t *redis, module *m, const char *key,
6054
    unsigned int idx, void **value, size_t *valuesz) {
6055
  errno = ENOSYS;
6056
  return -1;
6057
}
6058

6059
int pr_redis_list_getall(pool *p, pr_redis_t *redis, module *m, const char *key,
6060
    array_header **values, array_header **valueszs) {
6061
  errno = ENOSYS;
6062
  return -1;
6063
}
6064

6065
int pr_redis_list_pop(pool *p, pr_redis_t *redis, module *m, const char *key,
6066
    void **value, size_t *valuesz, int flags) {
6067
  errno = ENOSYS;
6068
  return -1;
6069
}
6070

6071
int pr_redis_list_push(pr_redis_t *redis, module *m, const char *key,
6072
    void *value, size_t valuesz, int flags) {
6073
  errno = ENOSYS;
6074
  return -1;
6075
}
6076

6077
int pr_redis_list_remove(pr_redis_t *redis, module *m, const char *key) {
6078
  errno = ENOSYS;
6079
  return -1;
6080
}
6081

6082
int pr_redis_list_rotate(pool *p, pr_redis_t *redis, module *m,
6083
    const char *key, void **value, size_t *valuesz) {
6084
  errno = ENOSYS;
6085
  return -1;
6086
}
6087

6088
int pr_redis_list_set(pr_redis_t *redis, module *m, const char *key,
6089
    unsigned int idx, void *value, size_t valuesz) {
6090
  errno = ENOSYS;
6091
  return -1;
6092
}
6093

6094
int pr_redis_list_setall(pr_redis_t *redis, module *m, const char *key,
6095
    array_header *values, array_header *valueszs) {
6096
  errno = ENOSYS;
6097
  return -1;
6098
}
6099

6100
int pr_redis_set_add(pr_redis_t *redis, module *m, const char *key,
6101
    void *value, size_t valuesz) {
6102
  errno = ENOSYS;
6103
  return -1;
6104
}
6105

6106
int pr_redis_set_count(pr_redis_t *redis, module *m, const char *key,
6107
    uint64_t *count) {
6108
  errno = ENOSYS;
6109
  return -1;
6110
}
6111

6112
int pr_redis_set_delete(pr_redis_t *redis, module *m, const char *key,
6113
    void *value, size_t valuesz) {
6114
  errno = ENOSYS;
6115
  return -1;
6116
}
6117

6118
int pr_redis_set_exists(pr_redis_t *redis, module *m, const char *key,
6119
    void *value, size_t valuesz) {
6120
  errno = ENOSYS;
6121
  return -1;
6122
}
6123

6124
int pr_redis_set_getall(pool *p, pr_redis_t *redis, module *m, const char *key,
6125
    array_header **values, array_header **valueszs) {
6126
  errno = ENOSYS;
6127
  return -1;
6128
}
6129

6130
int pr_redis_set_remove(pr_redis_t *redis, module *m, const char *key) {
6131
  errno = ENOSYS;
6132
  return -1;
6133
}
6134

6135
int pr_redis_set_setall(pr_redis_t *redis, module *m, const char *key,
6136
    array_header *values, array_header *valueszs) {
6137
  errno = ENOSYS;
6138
  return -1;
6139
}
6140

6141
int pr_redis_sorted_set_add(pr_redis_t *redis, module *m, const char *key,
6142
    void *value, size_t valuesz, float score) {
6143
  errno = ENOSYS;
6144
  return -1;
6145
}
6146

6147
int pr_redis_sorted_set_count(pr_redis_t *redis, module *m, const char *key,
6148
    uint64_t *count) {
6149
  errno = ENOSYS;
6150
  return -1;
6151
}
6152

6153
int pr_redis_sorted_set_delete(pr_redis_t *redis, module *m, const char *key,
6154
    void *value, size_t valuesz) {
6155
  errno = ENOSYS;
6156
  return -1;
6157
}
6158

6159
int pr_redis_sorted_set_exists(pr_redis_t *redis, module *m, const char *key,
6160
    void *value, size_t valuesz) {
6161
  errno = ENOSYS;
6162
  return -1;
6163
}
6164

6165
int pr_redis_sorted_set_getn(pool *p, pr_redis_t *redis, module *m,
6166
    const char *key, unsigned int offset, unsigned int len,
6167
    array_header **values, array_header **valueszs, int flags) {
6168
  errno = ENOSYS;
6169
  return -1;
6170
}
6171

6172
int pr_redis_sorted_set_incr(pr_redis_t *redis, module *m, const char *key,
6173
    void *value, size_t valuesz, float incr, float *score) {
6174
  errno = ENOSYS;
6175
  return -1;
6176
}
6177

6178
int pr_redis_sorted_set_remove(pr_redis_t *redis, module *m, const char *key) {
6179
  errno = ENOSYS;
6180
  return -1;
6181
}
6182

6183
int pr_redis_sorted_set_score(pr_redis_t *redis, module *m, const char *key,
6184
    void *value, size_t valuesz, float *score) {
6185
  errno = ENOSYS;
6186
  return -1;
6187
}
6188

6189
int pr_redis_sorted_set_set(pr_redis_t *redis, module *m, const char *key,
6190
    void *value, size_t valuesz, float score) {
6191
  errno = ENOSYS;
6192
  return -1;
6193
}
6194

6195
int pr_redis_sorted_set_setall(pr_redis_t *redis, module *m, const char *key,
6196
    array_header *values, array_header *valueszs, array_header *scores) {
6197
  errno = ENOSYS;
6198
  return -1;
6199
}
6200

6201
int pr_redis_kadd(pr_redis_t *redis, module *m, const char *key, size_t keysz,
6202
    void *value, size_t valuesz, time_t expires) {
6203
  errno = ENOSYS;
6204
  return -1;
6205
}
6206

6207
void *pr_redis_kget(pool *p, pr_redis_t *redis, module *m, const char *key,
6208
    size_t keysz, size_t *valuesz) {
6209
  errno = ENOSYS;
6210
  return NULL;
6211
}
6212

6213
char *pr_redis_kget_str(pool *p, pr_redis_t *redis, module *m, const char *key,
6214
    size_t keysz) {
6215
  errno = ENOSYS;
6216
  return NULL;
6217
}
6218

6219
int pr_redis_kremove(pr_redis_t *redis, module *m, const char *key,
6220
    size_t keysz) {
6221
  errno = ENOSYS;
6222
  return -1;
6223
}
6224

6225
int pr_redis_krename(pr_redis_t *redis, module *m, const char *from,
6226
    size_t fromsz, const char *to, size_t tosz) {
6227
  errno = ENOSYS;
6228
  return -1;
6229
}
6230

6231
int pr_redis_kset(pr_redis_t *redis, module *m, const char *key, size_t keysz,
6232
    void *value, size_t valuesz, time_t expires) {
6233
  errno = ENOSYS;
6234
  return -1;
6235
}
6236

6237
int pr_redis_hash_kcount(pr_redis_t *redis, module *m, const char *key,
6238
    size_t keysz, uint64_t *count) {
6239
  errno = ENOSYS;
6240
  return -1;
6241
}
6242

6243
int pr_redis_hash_kdelete(pr_redis_t *redis, module *m, const char *key,
6244
    size_t keysz, const char *field, size_t fieldsz) {
6245
  errno = ENOSYS;
6246
  return -1;
6247
}
6248

6249
int pr_redis_hash_kexists(pr_redis_t *redis, module *m, const char *key,
6250
    size_t keysz, const char *field, size_t fieldsz) {
6251
  errno = ENOSYS;
6252
  return -1;
6253
}
6254

6255
int pr_redis_hash_kget(pool *p, pr_redis_t *redis, module *m, const char *key,
6256
    size_t keysz, const char *field, size_t fieldsz, void **value,
6257
    size_t *valuesz) {
6258
  errno = ENOSYS;
6259
  return -1;
6260
}
6261

6262
int pr_redis_hash_kgetall(pool *p, pr_redis_t *redis, module *m,
6263
    const char *key, size_t keysz, pr_table_t **hash) {
6264
  errno = ENOSYS;
6265
  return -1;
6266
}
6267

6268
int pr_redis_hash_kincr(pr_redis_t *redis, module *m, const char *key,
6269
    size_t keysz, const char *field, size_t fieldsz, int32_t incr,
6270
    int64_t *value) {
6271
  errno = ENOSYS;
6272
  return -1;
6273
}
6274

6275
int pr_redis_hash_kkeys(pool *p, pr_redis_t *redis, module *m, const char *key,
6276
    size_t keysz, array_header **fields) {
6277
  errno = ENOSYS;
6278
  return -1;
6279
}
6280

6281
int pr_redis_hash_kremove(pr_redis_t *redis, module *m, const char *key,
6282
    size_t keysz) {
6283
  errno = ENOSYS;
6284
  return -1;
6285
}
6286

6287
int pr_redis_hash_kset(pr_redis_t *redis, module *m, const char *key,
6288
    size_t keysz, const char *field, size_t fieldsz, void *value,
6289
    size_t valuesz) {
6290
  errno = ENOSYS;
6291
  return -1;
6292
}
6293

6294
int pr_redis_hash_ksetall(pr_redis_t *redis, module *m, const char *key,
6295
    size_t keysz, pr_table_t *hash) {
6296
  errno = ENOSYS;
6297
  return -1;
6298
}
6299

6300
int pr_redis_hash_kvalues(pool *p, pr_redis_t *redis, module *m,
6301
    const char *key, size_t keysz, array_header **values) {
6302
  errno = ENOSYS;
6303
  return -1;
6304
}
6305

6306
int pr_redis_list_kappend(pr_redis_t *redis, module *m, const char *key,
6307
    size_t keysz, void *value, size_t valuesz) {
6308
  errno = ENOSYS;
6309
  return -1;
6310
}
6311

6312
int pr_redis_list_kcount(pr_redis_t *redis, module *m, const char *key,
6313
    size_t keysz, uint64_t *count) {
6314
  errno = ENOSYS;
6315
  return -1;
6316
}
6317

6318
int pr_redis_list_kdelete(pr_redis_t *redis, module *m, const char *key,
6319
    size_t keysz, void *value, size_t valuesz) {
6320
  errno = ENOSYS;
6321
  return -1;
6322
}
6323

6324
int pr_redis_list_kexists(pr_redis_t *redis, module *m, const char *key,
6325
    size_t keysz, unsigned int idx) {
6326
  errno = ENOSYS;
6327
  return -1;
6328
}
6329

6330
int pr_redis_list_kget(pool *p, pr_redis_t *redis, module *m, const char *key,
6331
    size_t keysz, unsigned int idx, void **value, size_t *valuesz) {
6332
  errno = ENOSYS;
6333
  return -1;
6334
}
6335

6336
int pr_redis_list_kgetall(pool *p, pr_redis_t *redis, module *m,
6337
    const char *key, size_t keysz, array_header **values,
6338
    array_header **valueszs) {
6339
  errno = ENOSYS;
6340
  return -1;
6341
}
6342

6343
int pr_redis_list_kpop(pool *p, pr_redis_t *redis, module *m, const char *key,
6344
    size_t keysz, void **value, size_t *valuesz, int flags) {
6345
  errno = ENOSYS;
6346
  return -1;
6347
}
6348

6349
int pr_redis_list_kpush(pr_redis_t *redis, module *m, const char *key,
6350
    size_t keysz, void *value, size_t valuesz, int flags) {
6351
  errno = ENOSYS;
6352
  return -1;
6353
}
6354

6355
int pr_redis_list_kremove(pr_redis_t *redis, module *m, const char *key,
6356
    size_t keysz) {
6357
  errno = ENOSYS;
6358
  return -1;
6359
}
6360

6361
int pr_redis_list_krotate(pool *p, pr_redis_t *redis, module *m,
6362
    const char *key, size_t keysz, void **value, size_t *valuesz) {
6363
  errno = ENOSYS;
6364
  return -1;
6365
}
6366

6367
int pr_redis_list_kset(pr_redis_t *redis, module *m, const char *key,
6368
    size_t keysz, unsigned int idx, void *value, size_t valuesz) {
6369
  errno = ENOSYS;
6370
  return -1;
6371
}
6372

6373
int pr_redis_list_ksetall(pr_redis_t *redis, module *m, const char *key,
6374
    size_t keysz, array_header *values, array_header *valueszs) {
6375
  errno = ENOSYS;
6376
  return -1;
6377
}
6378

6379
int pr_redis_set_kadd(pr_redis_t *redis, module *m, const char *key,
6380
    size_t keysz, void *value, size_t valuesz) {
6381
  errno = ENOSYS;
6382
  return -1;
6383
}
6384

6385
int pr_redis_set_kcount(pr_redis_t *redis, module *m, const char *key,
6386
    size_t keysz, uint64_t *count) {
6387
  errno = ENOSYS;
6388
  return -1;
6389
}
6390

6391
int pr_redis_set_kdelete(pr_redis_t *redis, module *m, const char *key,
6392
    size_t keysz, void *value, size_t valuesz) {
6393
  errno = ENOSYS;
6394
  return -1;
6395
}
6396

6397
int pr_redis_set_kexists(pr_redis_t *redis, module *m, const char *key,
6398
    size_t keysz, void *value, size_t valuesz) {
6399
  errno = ENOSYS;
6400
  return -1;
6401
}
6402

6403
int pr_redis_set_kgetall(pool *p, pr_redis_t *redis, module *m, const char *key,
6404
    size_t keysz, array_header **values, array_header **valueszs) {
6405
  errno = ENOSYS;
6406
  return -1;
6407
}
6408

6409
int pr_redis_set_kremove(pr_redis_t *redis, module *m, const char *key,
6410
    size_t keysz) {
6411
  errno = ENOSYS;
6412
  return -1;
6413
}
6414

6415
int pr_redis_set_ksetall(pr_redis_t *redis, module *m, const char *key,
6416
    size_t keysz, array_header *values, array_header *valueszs) {
6417
  errno = ENOSYS;
6418
  return -1;
6419
}
6420

6421
int pr_redis_sorted_set_kadd(pr_redis_t *redis, module *m, const char *key,
6422
    size_t keysz, void *value, size_t valuesz, float score) {
6423
  errno = ENOSYS;
6424
  return -1;
6425
}
6426

6427
int pr_redis_sorted_set_kcount(pr_redis_t *redis, module *m, const char *key,
6428
    size_t keysz, uint64_t *count) {
6429
  errno = ENOSYS;
6430
  return -1;
6431
}
6432

6433
int pr_redis_sorted_set_kdelete(pr_redis_t *redis, module *m, const char *key,
6434
    size_t keysz, void *value, size_t valuesz) {
6435
  errno = ENOSYS;
6436
  return -1;
6437
}
6438

6439
int pr_redis_sorted_set_kexists(pr_redis_t *redis, module *m, const char *key,
6440
    size_t keysz, void *value, size_t valuesz) {
6441
  errno = ENOSYS;
6442
  return -1;
6443
}
6444

6445
int pr_redis_sorted_set_kgetn(pool *p, pr_redis_t *redis, module *m,
6446
    const char *key, size_t keysz, unsigned int offset, unsigned int len,
6447
    array_header **values, array_header **valueszs, int flags) {
6448
  errno = ENOSYS;
6449
  return -1;
6450
}
6451

6452
int pr_redis_sorted_set_kincr(pr_redis_t *redis, module *m, const char *key,
6453
    size_t keysz, void *value, size_t valuesz, float incr, float *score) {
6454
  errno = ENOSYS;
6455
  return -1;
6456
}
6457

6458
int pr_redis_sorted_set_kremove(pr_redis_t *redis, module *m, const char *key,
6459
    size_t keysz) {
6460
  errno = ENOSYS;
6461
  return -1;
6462
}
6463

6464
int pr_redis_sorted_set_kscore(pr_redis_t *redis, module *m, const char *key,
6465
    size_t keysz, void *value, size_t valuesz, float *score) {
6466
  errno = ENOSYS;
6467
  return -1;
6468
}
6469

6470
int pr_redis_sorted_set_kset(pr_redis_t *redis, module *m, const char *key,
6471
    size_t keysz, void *value, size_t valuesz, float score) {
6472
  errno = ENOSYS;
6473
  return -1;
6474
}
6475

6476
int pr_redis_sorted_set_ksetall(pr_redis_t *redis, module *m, const char *key,
6477
    size_t keysz, array_header *values, array_header *valueszs,
6478
    array_header *scores) {
6479
  errno = ENOSYS;
6480
  return -1;
6481
}
6482

6483
int pr_redis_sentinel_get_master_addr(pool *p, pr_redis_t *redis,
6484
    const char *name, pr_netaddr_t **addr) {
6485
  errno = ENOSYS;
6486
  return -1;
6487
}
6488

6489
int pr_redis_sentinel_get_masters(pool *p, pr_redis_t *redis,
6490
    array_header **masters) {
6491
  errno = ENOSYS;
6492
  return -1;
6493
}
6494

6495
int redis_set_server(const char *server, int port, unsigned long flags,
6496
    const char *password, const char *db_idx) {
6497
  errno = ENOSYS;
6498
  return -1;
6499
}
6500

6501
int redis_set_server2(const char *server, int port, unsigned long flags,
6502
    const char *username, const char *password, const char *db_idx) {
6503
  errno = ENOSYS;
6504
  return -1;
6505
}
6506

6507
int redis_set_server3(const char *server, int port, unsigned long flags,
6508
    const char *username, const char *password, const char *db_idx,
6509
    int use_ssl, const char *ssl_cacert, const char *ssl_cert,
6510
    const char *ssl_key) {
6511
  errno = ENOSYS;
6512
  return -1;
6513
}
6514

6515
int redis_set_sentinels(array_header *sentinels, const char *name) {
6516
  errno = ENOSYS;
6517
  return -1;
6518
}
6519

6520
int redis_set_sentinels2(array_header *sentinels, const char *name, int use_ssl,
6521
    const char *ssl_cacert, const char *ssl_cert, const char *ssl_key) {
6522
  errno = ENOSYS;
6523
  return -1;
6524
}
6525

6526
int redis_set_timeouts(unsigned long conn_millis, unsigned long io_millis) {
6527
  errno = ENOSYS;
6528
  return -1;
6529
}
6530

6531
int redis_clear(void) {
6532
  errno = ENOSYS;
6533
  return -1;
6534
}
6535

6536
int redis_init(void) {
6537
  errno = ENOSYS;
6538
  return -1;
6539
}
6540

6541
#endif /* PR_USE_REDIS */
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc