/src/h2o/deps/hiredis/hiredis.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2009-2011, Salvatore Sanfilippo <antirez at gmail dot com> |
3 | | * Copyright (c) 2010-2014, Pieter Noordhuis <pcnoordhuis at gmail dot com> |
4 | | * Copyright (c) 2015, Matt Stancliff <matt at genges dot com>, |
5 | | * Jan-Erik Rediger <janerik at fnordig dot com> |
6 | | * |
7 | | * All rights reserved. |
8 | | * |
9 | | * Redistribution and use in source and binary forms, with or without |
10 | | * modification, are permitted provided that the following conditions are met: |
11 | | * |
12 | | * * Redistributions of source code must retain the above copyright notice, |
13 | | * this list of conditions and the following disclaimer. |
14 | | * * Redistributions in binary form must reproduce the above copyright |
15 | | * notice, this list of conditions and the following disclaimer in the |
16 | | * documentation and/or other materials provided with the distribution. |
17 | | * * Neither the name of Redis nor the names of its contributors may be used |
18 | | * to endorse or promote products derived from this software without |
19 | | * specific prior written permission. |
20 | | * |
21 | | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
22 | | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
23 | | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
24 | | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE |
25 | | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
26 | | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
27 | | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
28 | | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN |
29 | | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
30 | | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
31 | | * POSSIBILITY OF SUCH DAMAGE. |
32 | | */ |
33 | | |
34 | | #include "fmacros.h" |
35 | | #include <string.h> |
36 | | #include <stdlib.h> |
37 | | #include <unistd.h> |
38 | | #include <assert.h> |
39 | | #include <errno.h> |
40 | | #include <ctype.h> |
41 | | |
42 | | #include "hiredis.h" |
43 | | #include "net.h" |
44 | | #include "sds.h" |
45 | | |
46 | | static redisReply *createReplyObject(int type); |
47 | | static void *createStringObject(const redisReadTask *task, char *str, size_t len); |
48 | | static void *createArrayObject(const redisReadTask *task, int elements); |
49 | | static void *createIntegerObject(const redisReadTask *task, long long value); |
50 | | static void *createNilObject(const redisReadTask *task); |
51 | | |
52 | | /* Default set of functions to build the reply. Keep in mind that such a |
53 | | * function returning NULL is interpreted as OOM. */ |
54 | | static redisReplyObjectFunctions defaultFunctions = { |
55 | | createStringObject, |
56 | | createArrayObject, |
57 | | createIntegerObject, |
58 | | createNilObject, |
59 | | freeReplyObject |
60 | | }; |
61 | | |
62 | | /* Create a reply object */ |
63 | 0 | static redisReply *createReplyObject(int type) { |
64 | 0 | redisReply *r = calloc(1,sizeof(*r)); |
65 | |
|
66 | 0 | if (r == NULL) |
67 | 0 | return NULL; |
68 | | |
69 | 0 | r->type = type; |
70 | 0 | return r; |
71 | 0 | } |
72 | | |
73 | | /* Free a reply object */ |
74 | 0 | void freeReplyObject(void *reply) { |
75 | 0 | redisReply *r = reply; |
76 | 0 | size_t j; |
77 | |
|
78 | 0 | if (r == NULL) |
79 | 0 | return; |
80 | | |
81 | 0 | switch(r->type) { |
82 | 0 | case REDIS_REPLY_INTEGER: |
83 | 0 | break; /* Nothing to free */ |
84 | 0 | case REDIS_REPLY_ARRAY: |
85 | 0 | if (r->element != NULL) { |
86 | 0 | for (j = 0; j < r->elements; j++) |
87 | 0 | if (r->element[j] != NULL) |
88 | 0 | freeReplyObject(r->element[j]); |
89 | 0 | free(r->element); |
90 | 0 | } |
91 | 0 | break; |
92 | 0 | case REDIS_REPLY_ERROR: |
93 | 0 | case REDIS_REPLY_STATUS: |
94 | 0 | case REDIS_REPLY_STRING: |
95 | 0 | if (r->str != NULL) |
96 | 0 | free(r->str); |
97 | 0 | break; |
98 | 0 | } |
99 | 0 | free(r); |
100 | 0 | } |
101 | | |
102 | 0 | static void *createStringObject(const redisReadTask *task, char *str, size_t len) { |
103 | 0 | redisReply *r, *parent; |
104 | 0 | char *buf; |
105 | |
|
106 | 0 | r = createReplyObject(task->type); |
107 | 0 | if (r == NULL) |
108 | 0 | return NULL; |
109 | | |
110 | 0 | buf = malloc(len+1); |
111 | 0 | if (buf == NULL) { |
112 | 0 | freeReplyObject(r); |
113 | 0 | return NULL; |
114 | 0 | } |
115 | | |
116 | 0 | assert(task->type == REDIS_REPLY_ERROR || |
117 | 0 | task->type == REDIS_REPLY_STATUS || |
118 | 0 | task->type == REDIS_REPLY_STRING); |
119 | | |
120 | | /* Copy string value */ |
121 | 0 | memcpy(buf,str,len); |
122 | 0 | buf[len] = '\0'; |
123 | 0 | r->str = buf; |
124 | 0 | r->len = len; |
125 | |
|
126 | 0 | if (task->parent) { |
127 | 0 | parent = task->parent->obj; |
128 | 0 | assert(parent->type == REDIS_REPLY_ARRAY); |
129 | 0 | parent->element[task->idx] = r; |
130 | 0 | } |
131 | 0 | return r; |
132 | 0 | } |
133 | | |
134 | 0 | static void *createArrayObject(const redisReadTask *task, int elements) { |
135 | 0 | redisReply *r, *parent; |
136 | |
|
137 | 0 | r = createReplyObject(REDIS_REPLY_ARRAY); |
138 | 0 | if (r == NULL) |
139 | 0 | return NULL; |
140 | | |
141 | 0 | if (elements > 0) { |
142 | 0 | r->element = calloc(elements,sizeof(redisReply*)); |
143 | 0 | if (r->element == NULL) { |
144 | 0 | freeReplyObject(r); |
145 | 0 | return NULL; |
146 | 0 | } |
147 | 0 | } |
148 | | |
149 | 0 | r->elements = elements; |
150 | |
|
151 | 0 | if (task->parent) { |
152 | 0 | parent = task->parent->obj; |
153 | 0 | assert(parent->type == REDIS_REPLY_ARRAY); |
154 | 0 | parent->element[task->idx] = r; |
155 | 0 | } |
156 | 0 | return r; |
157 | 0 | } |
158 | | |
159 | 0 | static void *createIntegerObject(const redisReadTask *task, long long value) { |
160 | 0 | redisReply *r, *parent; |
161 | |
|
162 | 0 | r = createReplyObject(REDIS_REPLY_INTEGER); |
163 | 0 | if (r == NULL) |
164 | 0 | return NULL; |
165 | | |
166 | 0 | r->integer = value; |
167 | |
|
168 | 0 | if (task->parent) { |
169 | 0 | parent = task->parent->obj; |
170 | 0 | assert(parent->type == REDIS_REPLY_ARRAY); |
171 | 0 | parent->element[task->idx] = r; |
172 | 0 | } |
173 | 0 | return r; |
174 | 0 | } |
175 | | |
176 | 0 | static void *createNilObject(const redisReadTask *task) { |
177 | 0 | redisReply *r, *parent; |
178 | |
|
179 | 0 | r = createReplyObject(REDIS_REPLY_NIL); |
180 | 0 | if (r == NULL) |
181 | 0 | return NULL; |
182 | | |
183 | 0 | if (task->parent) { |
184 | 0 | parent = task->parent->obj; |
185 | 0 | assert(parent->type == REDIS_REPLY_ARRAY); |
186 | 0 | parent->element[task->idx] = r; |
187 | 0 | } |
188 | 0 | return r; |
189 | 0 | } |
190 | | |
191 | | /* Return the number of digits of 'v' when converted to string in radix 10. |
192 | | * Implementation borrowed from link in redis/src/util.c:string2ll(). */ |
193 | 0 | static uint32_t countDigits(uint64_t v) { |
194 | 0 | uint32_t result = 1; |
195 | 0 | for (;;) { |
196 | 0 | if (v < 10) return result; |
197 | 0 | if (v < 100) return result + 1; |
198 | 0 | if (v < 1000) return result + 2; |
199 | 0 | if (v < 10000) return result + 3; |
200 | 0 | v /= 10000U; |
201 | 0 | result += 4; |
202 | 0 | } |
203 | 0 | } |
204 | | |
205 | | /* Helper that calculates the bulk length given a certain string length. */ |
206 | 0 | static size_t bulklen(size_t len) { |
207 | 0 | return 1+countDigits(len)+2+len+2; |
208 | 0 | } |
209 | | |
210 | 0 | int redisvFormatCommand(char **target, const char *format, va_list ap) { |
211 | 0 | const char *c = format; |
212 | 0 | char *cmd = NULL; /* final command */ |
213 | 0 | int pos; /* position in final command */ |
214 | 0 | sds curarg, newarg; /* current argument */ |
215 | 0 | int touched = 0; /* was the current argument touched? */ |
216 | 0 | char **curargv = NULL, **newargv = NULL; |
217 | 0 | int argc = 0; |
218 | 0 | int totlen = 0; |
219 | 0 | int error_type = 0; /* 0 = no error; -1 = memory error; -2 = format error */ |
220 | 0 | int j; |
221 | | |
222 | | /* Abort if there is not target to set */ |
223 | 0 | if (target == NULL) |
224 | 0 | return -1; |
225 | | |
226 | | /* Build the command string accordingly to protocol */ |
227 | 0 | curarg = sdsempty(); |
228 | 0 | if (curarg == NULL) |
229 | 0 | return -1; |
230 | | |
231 | 0 | while(*c != '\0') { |
232 | 0 | if (*c != '%' || c[1] == '\0') { |
233 | 0 | if (*c == ' ') { |
234 | 0 | if (touched) { |
235 | 0 | newargv = realloc(curargv,sizeof(char*)*(argc+1)); |
236 | 0 | if (newargv == NULL) goto memory_err; |
237 | 0 | curargv = newargv; |
238 | 0 | curargv[argc++] = curarg; |
239 | 0 | totlen += bulklen(sdslen(curarg)); |
240 | | |
241 | | /* curarg is put in argv so it can be overwritten. */ |
242 | 0 | curarg = sdsempty(); |
243 | 0 | if (curarg == NULL) goto memory_err; |
244 | 0 | touched = 0; |
245 | 0 | } |
246 | 0 | } else { |
247 | 0 | newarg = sdscatlen(curarg,c,1); |
248 | 0 | if (newarg == NULL) goto memory_err; |
249 | 0 | curarg = newarg; |
250 | 0 | touched = 1; |
251 | 0 | } |
252 | 0 | } else { |
253 | 0 | char *arg; |
254 | 0 | size_t size; |
255 | | |
256 | | /* Set newarg so it can be checked even if it is not touched. */ |
257 | 0 | newarg = curarg; |
258 | |
|
259 | 0 | switch(c[1]) { |
260 | 0 | case 's': |
261 | 0 | arg = va_arg(ap,char*); |
262 | 0 | size = strlen(arg); |
263 | 0 | if (size > 0) |
264 | 0 | newarg = sdscatlen(curarg,arg,size); |
265 | 0 | break; |
266 | 0 | case 'b': |
267 | 0 | arg = va_arg(ap,char*); |
268 | 0 | size = va_arg(ap,size_t); |
269 | 0 | if (size > 0) |
270 | 0 | newarg = sdscatlen(curarg,arg,size); |
271 | 0 | break; |
272 | 0 | case '%': |
273 | 0 | newarg = sdscat(curarg,"%"); |
274 | 0 | break; |
275 | 0 | default: |
276 | | /* Try to detect printf format */ |
277 | 0 | { |
278 | 0 | static const char intfmts[] = "diouxX"; |
279 | 0 | static const char flags[] = "#0-+ "; |
280 | 0 | char _format[16]; |
281 | 0 | const char *_p = c+1; |
282 | 0 | size_t _l = 0; |
283 | 0 | va_list _cpy; |
284 | | |
285 | | /* Flags */ |
286 | 0 | while (*_p != '\0' && strchr(flags,*_p) != NULL) _p++; |
287 | | |
288 | | /* Field width */ |
289 | 0 | while (*_p != '\0' && isdigit(*_p)) _p++; |
290 | | |
291 | | /* Precision */ |
292 | 0 | if (*_p == '.') { |
293 | 0 | _p++; |
294 | 0 | while (*_p != '\0' && isdigit(*_p)) _p++; |
295 | 0 | } |
296 | | |
297 | | /* Copy va_list before consuming with va_arg */ |
298 | 0 | va_copy(_cpy,ap); |
299 | | |
300 | | /* Integer conversion (without modifiers) */ |
301 | 0 | if (strchr(intfmts,*_p) != NULL) { |
302 | 0 | va_arg(ap,int); |
303 | 0 | goto fmt_valid; |
304 | 0 | } |
305 | | |
306 | | /* Double conversion (without modifiers) */ |
307 | 0 | if (strchr("eEfFgGaA",*_p) != NULL) { |
308 | 0 | va_arg(ap,double); |
309 | 0 | goto fmt_valid; |
310 | 0 | } |
311 | | |
312 | | /* Size: char */ |
313 | 0 | if (_p[0] == 'h' && _p[1] == 'h') { |
314 | 0 | _p += 2; |
315 | 0 | if (*_p != '\0' && strchr(intfmts,*_p) != NULL) { |
316 | 0 | va_arg(ap,int); /* char gets promoted to int */ |
317 | 0 | goto fmt_valid; |
318 | 0 | } |
319 | 0 | goto fmt_invalid; |
320 | 0 | } |
321 | | |
322 | | /* Size: short */ |
323 | 0 | if (_p[0] == 'h') { |
324 | 0 | _p += 1; |
325 | 0 | if (*_p != '\0' && strchr(intfmts,*_p) != NULL) { |
326 | 0 | va_arg(ap,int); /* short gets promoted to int */ |
327 | 0 | goto fmt_valid; |
328 | 0 | } |
329 | 0 | goto fmt_invalid; |
330 | 0 | } |
331 | | |
332 | | /* Size: long long */ |
333 | 0 | if (_p[0] == 'l' && _p[1] == 'l') { |
334 | 0 | _p += 2; |
335 | 0 | if (*_p != '\0' && strchr(intfmts,*_p) != NULL) { |
336 | 0 | va_arg(ap,long long); |
337 | 0 | goto fmt_valid; |
338 | 0 | } |
339 | 0 | goto fmt_invalid; |
340 | 0 | } |
341 | | |
342 | | /* Size: long */ |
343 | 0 | if (_p[0] == 'l') { |
344 | 0 | _p += 1; |
345 | 0 | if (*_p != '\0' && strchr(intfmts,*_p) != NULL) { |
346 | 0 | va_arg(ap,long); |
347 | 0 | goto fmt_valid; |
348 | 0 | } |
349 | 0 | goto fmt_invalid; |
350 | 0 | } |
351 | | |
352 | 0 | fmt_invalid: |
353 | 0 | va_end(_cpy); |
354 | 0 | goto format_err; |
355 | | |
356 | 0 | fmt_valid: |
357 | 0 | _l = (_p+1)-c; |
358 | 0 | if (_l < sizeof(_format)-2) { |
359 | 0 | memcpy(_format,c,_l); |
360 | 0 | _format[_l] = '\0'; |
361 | 0 | newarg = sdscatvprintf(curarg,_format,_cpy); |
362 | | |
363 | | /* Update current position (note: outer blocks |
364 | | * increment c twice so compensate here) */ |
365 | 0 | c = _p-1; |
366 | 0 | } |
367 | |
|
368 | 0 | va_end(_cpy); |
369 | 0 | break; |
370 | 0 | } |
371 | 0 | } |
372 | | |
373 | 0 | if (newarg == NULL) goto memory_err; |
374 | 0 | curarg = newarg; |
375 | |
|
376 | 0 | touched = 1; |
377 | 0 | c++; |
378 | 0 | } |
379 | 0 | c++; |
380 | 0 | } |
381 | | |
382 | | /* Add the last argument if needed */ |
383 | 0 | if (touched) { |
384 | 0 | newargv = realloc(curargv,sizeof(char*)*(argc+1)); |
385 | 0 | if (newargv == NULL) goto memory_err; |
386 | 0 | curargv = newargv; |
387 | 0 | curargv[argc++] = curarg; |
388 | 0 | totlen += bulklen(sdslen(curarg)); |
389 | 0 | } else { |
390 | 0 | sdsfree(curarg); |
391 | 0 | } |
392 | | |
393 | | /* Clear curarg because it was put in curargv or was free'd. */ |
394 | 0 | curarg = NULL; |
395 | | |
396 | | /* Add bytes needed to hold multi bulk count */ |
397 | 0 | totlen += 1+countDigits(argc)+2; |
398 | | |
399 | | /* Build the command at protocol level */ |
400 | 0 | cmd = malloc(totlen+1); |
401 | 0 | if (cmd == NULL) goto memory_err; |
402 | | |
403 | 0 | pos = sprintf(cmd,"*%d\r\n",argc); |
404 | 0 | for (j = 0; j < argc; j++) { |
405 | 0 | pos += sprintf(cmd+pos,"$%zu\r\n",sdslen(curargv[j])); |
406 | 0 | memcpy(cmd+pos,curargv[j],sdslen(curargv[j])); |
407 | 0 | pos += sdslen(curargv[j]); |
408 | 0 | sdsfree(curargv[j]); |
409 | 0 | cmd[pos++] = '\r'; |
410 | 0 | cmd[pos++] = '\n'; |
411 | 0 | } |
412 | 0 | assert(pos == totlen); |
413 | 0 | cmd[pos] = '\0'; |
414 | |
|
415 | 0 | free(curargv); |
416 | 0 | *target = cmd; |
417 | 0 | return totlen; |
418 | | |
419 | 0 | format_err: |
420 | 0 | error_type = -2; |
421 | 0 | goto cleanup; |
422 | | |
423 | 0 | memory_err: |
424 | 0 | error_type = -1; |
425 | 0 | goto cleanup; |
426 | | |
427 | 0 | cleanup: |
428 | 0 | if (curargv) { |
429 | 0 | while(argc--) |
430 | 0 | sdsfree(curargv[argc]); |
431 | 0 | free(curargv); |
432 | 0 | } |
433 | |
|
434 | 0 | sdsfree(curarg); |
435 | | |
436 | | /* No need to check cmd since it is the last statement that can fail, |
437 | | * but do it anyway to be as defensive as possible. */ |
438 | 0 | if (cmd != NULL) |
439 | 0 | free(cmd); |
440 | |
|
441 | 0 | return error_type; |
442 | 0 | } |
443 | | |
444 | | /* Format a command according to the Redis protocol. This function |
445 | | * takes a format similar to printf: |
446 | | * |
447 | | * %s represents a C null terminated string you want to interpolate |
448 | | * %b represents a binary safe string |
449 | | * |
450 | | * When using %b you need to provide both the pointer to the string |
451 | | * and the length in bytes as a size_t. Examples: |
452 | | * |
453 | | * len = redisFormatCommand(target, "GET %s", mykey); |
454 | | * len = redisFormatCommand(target, "SET %s %b", mykey, myval, myvallen); |
455 | | */ |
456 | 0 | int redisFormatCommand(char **target, const char *format, ...) { |
457 | 0 | va_list ap; |
458 | 0 | int len; |
459 | 0 | va_start(ap,format); |
460 | 0 | len = redisvFormatCommand(target,format,ap); |
461 | 0 | va_end(ap); |
462 | | |
463 | | /* The API says "-1" means bad result, but we now also return "-2" in some |
464 | | * cases. Force the return value to always be -1. */ |
465 | 0 | if (len < 0) |
466 | 0 | len = -1; |
467 | |
|
468 | 0 | return len; |
469 | 0 | } |
470 | | |
471 | | /* Format a command according to the Redis protocol using an sds string and |
472 | | * sdscatfmt for the processing of arguments. This function takes the |
473 | | * number of arguments, an array with arguments and an array with their |
474 | | * lengths. If the latter is set to NULL, strlen will be used to compute the |
475 | | * argument lengths. |
476 | | */ |
477 | | int redisFormatSdsCommandArgv(sds *target, int argc, const char **argv, |
478 | | const size_t *argvlen) |
479 | 0 | { |
480 | 0 | sds cmd; |
481 | 0 | unsigned long long totlen; |
482 | 0 | int j; |
483 | 0 | size_t len; |
484 | | |
485 | | /* Abort on a NULL target */ |
486 | 0 | if (target == NULL) |
487 | 0 | return -1; |
488 | | |
489 | | /* Calculate our total size */ |
490 | 0 | totlen = 1+countDigits(argc)+2; |
491 | 0 | for (j = 0; j < argc; j++) { |
492 | 0 | len = argvlen ? argvlen[j] : strlen(argv[j]); |
493 | 0 | totlen += bulklen(len); |
494 | 0 | } |
495 | | |
496 | | /* Use an SDS string for command construction */ |
497 | 0 | cmd = sdsempty(); |
498 | 0 | if (cmd == NULL) |
499 | 0 | return -1; |
500 | | |
501 | | /* We already know how much storage we need */ |
502 | 0 | cmd = sdsMakeRoomFor(cmd, totlen); |
503 | 0 | if (cmd == NULL) |
504 | 0 | return -1; |
505 | | |
506 | | /* Construct command */ |
507 | 0 | cmd = sdscatfmt(cmd, "*%i\r\n", argc); |
508 | 0 | for (j=0; j < argc; j++) { |
509 | 0 | len = argvlen ? argvlen[j] : strlen(argv[j]); |
510 | 0 | cmd = sdscatfmt(cmd, "$%u\r\n", len); |
511 | 0 | cmd = sdscatlen(cmd, argv[j], len); |
512 | 0 | cmd = sdscatlen(cmd, "\r\n", sizeof("\r\n")-1); |
513 | 0 | } |
514 | |
|
515 | 0 | assert(sdslen(cmd)==totlen); |
516 | | |
517 | 0 | *target = cmd; |
518 | 0 | return totlen; |
519 | 0 | } |
520 | | |
521 | 0 | void redisFreeSdsCommand(sds cmd) { |
522 | 0 | sdsfree(cmd); |
523 | 0 | } |
524 | | |
525 | | /* Format a command according to the Redis protocol. This function takes the |
526 | | * number of arguments, an array with arguments and an array with their |
527 | | * lengths. If the latter is set to NULL, strlen will be used to compute the |
528 | | * argument lengths. |
529 | | */ |
530 | 0 | int redisFormatCommandArgv(char **target, int argc, const char **argv, const size_t *argvlen) { |
531 | 0 | char *cmd = NULL; /* final command */ |
532 | 0 | int pos; /* position in final command */ |
533 | 0 | size_t len; |
534 | 0 | int totlen, j; |
535 | | |
536 | | /* Abort on a NULL target */ |
537 | 0 | if (target == NULL) |
538 | 0 | return -1; |
539 | | |
540 | | /* Calculate number of bytes needed for the command */ |
541 | 0 | totlen = 1+countDigits(argc)+2; |
542 | 0 | for (j = 0; j < argc; j++) { |
543 | 0 | len = argvlen ? argvlen[j] : strlen(argv[j]); |
544 | 0 | totlen += bulklen(len); |
545 | 0 | } |
546 | | |
547 | | /* Build the command at protocol level */ |
548 | 0 | cmd = malloc(totlen+1); |
549 | 0 | if (cmd == NULL) |
550 | 0 | return -1; |
551 | | |
552 | 0 | pos = sprintf(cmd,"*%d\r\n",argc); |
553 | 0 | for (j = 0; j < argc; j++) { |
554 | 0 | len = argvlen ? argvlen[j] : strlen(argv[j]); |
555 | 0 | pos += sprintf(cmd+pos,"$%zu\r\n",len); |
556 | 0 | memcpy(cmd+pos,argv[j],len); |
557 | 0 | pos += len; |
558 | 0 | cmd[pos++] = '\r'; |
559 | 0 | cmd[pos++] = '\n'; |
560 | 0 | } |
561 | 0 | assert(pos == totlen); |
562 | 0 | cmd[pos] = '\0'; |
563 | |
|
564 | 0 | *target = cmd; |
565 | 0 | return totlen; |
566 | 0 | } |
567 | | |
568 | 0 | void redisFreeCommand(char *cmd) { |
569 | 0 | free(cmd); |
570 | 0 | } |
571 | | |
572 | 0 | void __redisSetError(redisContext *c, int type, const char *str) { |
573 | 0 | size_t len; |
574 | |
|
575 | 0 | c->err = type; |
576 | 0 | if (str != NULL) { |
577 | 0 | len = strlen(str); |
578 | 0 | len = len < (sizeof(c->errstr)-1) ? len : (sizeof(c->errstr)-1); |
579 | 0 | memcpy(c->errstr,str,len); |
580 | 0 | c->errstr[len] = '\0'; |
581 | 0 | } else { |
582 | | /* Only REDIS_ERR_IO may lack a description! */ |
583 | 0 | assert(type == REDIS_ERR_IO); |
584 | 0 | __redis_strerror_r(errno, c->errstr, sizeof(c->errstr)); |
585 | 0 | } |
586 | 0 | } |
587 | | |
588 | 0 | redisReader *redisReaderCreate(void) { |
589 | 0 | return redisReaderCreateWithFunctions(&defaultFunctions); |
590 | 0 | } |
591 | | |
592 | 0 | static redisContext *redisContextInit(void) { |
593 | 0 | redisContext *c; |
594 | |
|
595 | 0 | c = calloc(1,sizeof(redisContext)); |
596 | 0 | if (c == NULL) |
597 | 0 | return NULL; |
598 | | |
599 | 0 | c->err = 0; |
600 | 0 | c->errstr[0] = '\0'; |
601 | 0 | c->obuf = sdsempty(); |
602 | 0 | c->reader = redisReaderCreate(); |
603 | 0 | c->tcp.host = NULL; |
604 | 0 | c->tcp.source_addr = NULL; |
605 | 0 | c->unix_sock.path = NULL; |
606 | 0 | c->timeout = NULL; |
607 | |
|
608 | 0 | if (c->obuf == NULL || c->reader == NULL) { |
609 | 0 | redisFree(c); |
610 | 0 | return NULL; |
611 | 0 | } |
612 | | |
613 | 0 | return c; |
614 | 0 | } |
615 | | |
616 | 0 | void redisFree(redisContext *c) { |
617 | 0 | if (c == NULL) |
618 | 0 | return; |
619 | 0 | if (c->fd > 0) |
620 | 0 | close(c->fd); |
621 | 0 | if (c->obuf != NULL) |
622 | 0 | sdsfree(c->obuf); |
623 | 0 | if (c->reader != NULL) |
624 | 0 | redisReaderFree(c->reader); |
625 | 0 | if (c->tcp.host) |
626 | 0 | free(c->tcp.host); |
627 | 0 | if (c->tcp.source_addr) |
628 | 0 | free(c->tcp.source_addr); |
629 | 0 | if (c->unix_sock.path) |
630 | 0 | free(c->unix_sock.path); |
631 | 0 | if (c->timeout) |
632 | 0 | free(c->timeout); |
633 | 0 | free(c); |
634 | 0 | } |
635 | | |
636 | 0 | int redisFreeKeepFd(redisContext *c) { |
637 | 0 | int fd = c->fd; |
638 | 0 | c->fd = -1; |
639 | 0 | redisFree(c); |
640 | 0 | return fd; |
641 | 0 | } |
642 | | |
643 | 0 | int redisReconnect(redisContext *c) { |
644 | 0 | c->err = 0; |
645 | 0 | memset(c->errstr, '\0', strlen(c->errstr)); |
646 | |
|
647 | 0 | if (c->fd > 0) { |
648 | 0 | close(c->fd); |
649 | 0 | } |
650 | |
|
651 | 0 | sdsfree(c->obuf); |
652 | 0 | redisReaderFree(c->reader); |
653 | |
|
654 | 0 | c->obuf = sdsempty(); |
655 | 0 | c->reader = redisReaderCreate(); |
656 | |
|
657 | 0 | if (c->connection_type == REDIS_CONN_TCP) { |
658 | 0 | return redisContextConnectBindTcp(c, c->tcp.host, c->tcp.port, |
659 | 0 | c->timeout, c->tcp.source_addr); |
660 | 0 | } else if (c->connection_type == REDIS_CONN_UNIX) { |
661 | 0 | return redisContextConnectUnix(c, c->unix_sock.path, c->timeout); |
662 | 0 | } else { |
663 | | /* Something bad happened here and shouldn't have. There isn't |
664 | | enough information in the context to reconnect. */ |
665 | 0 | __redisSetError(c,REDIS_ERR_OTHER,"Not enough information to reconnect"); |
666 | 0 | } |
667 | | |
668 | 0 | return REDIS_ERR; |
669 | 0 | } |
670 | | |
671 | | /* Connect to a Redis instance. On error the field error in the returned |
672 | | * context will be set to the return value of the error function. |
673 | | * When no set of reply functions is given, the default set will be used. */ |
674 | 0 | redisContext *redisConnect(const char *ip, int port) { |
675 | 0 | redisContext *c; |
676 | |
|
677 | 0 | c = redisContextInit(); |
678 | 0 | if (c == NULL) |
679 | 0 | return NULL; |
680 | | |
681 | 0 | c->flags |= REDIS_BLOCK; |
682 | 0 | redisContextConnectTcp(c,ip,port,NULL); |
683 | 0 | return c; |
684 | 0 | } |
685 | | |
686 | 0 | redisContext *redisConnectWithTimeout(const char *ip, int port, const struct timeval tv) { |
687 | 0 | redisContext *c; |
688 | |
|
689 | 0 | c = redisContextInit(); |
690 | 0 | if (c == NULL) |
691 | 0 | return NULL; |
692 | | |
693 | 0 | c->flags |= REDIS_BLOCK; |
694 | 0 | redisContextConnectTcp(c,ip,port,&tv); |
695 | 0 | return c; |
696 | 0 | } |
697 | | |
698 | 0 | redisContext *redisConnectNonBlock(const char *ip, int port) { |
699 | 0 | redisContext *c; |
700 | |
|
701 | 0 | c = redisContextInit(); |
702 | 0 | if (c == NULL) |
703 | 0 | return NULL; |
704 | | |
705 | 0 | c->flags &= ~REDIS_BLOCK; |
706 | 0 | redisContextConnectTcp(c,ip,port,NULL); |
707 | 0 | return c; |
708 | 0 | } |
709 | | |
710 | | redisContext *redisConnectBindNonBlock(const char *ip, int port, |
711 | 0 | const char *source_addr) { |
712 | 0 | redisContext *c = redisContextInit(); |
713 | 0 | c->flags &= ~REDIS_BLOCK; |
714 | 0 | redisContextConnectBindTcp(c,ip,port,NULL,source_addr); |
715 | 0 | return c; |
716 | 0 | } |
717 | | |
718 | | redisContext *redisConnectBindNonBlockWithReuse(const char *ip, int port, |
719 | 0 | const char *source_addr) { |
720 | 0 | redisContext *c = redisContextInit(); |
721 | 0 | c->flags &= ~REDIS_BLOCK; |
722 | 0 | c->flags |= REDIS_REUSEADDR; |
723 | 0 | redisContextConnectBindTcp(c,ip,port,NULL,source_addr); |
724 | 0 | return c; |
725 | 0 | } |
726 | | |
727 | 0 | redisContext *redisConnectUnix(const char *path) { |
728 | 0 | redisContext *c; |
729 | |
|
730 | 0 | c = redisContextInit(); |
731 | 0 | if (c == NULL) |
732 | 0 | return NULL; |
733 | | |
734 | 0 | c->flags |= REDIS_BLOCK; |
735 | 0 | redisContextConnectUnix(c,path,NULL); |
736 | 0 | return c; |
737 | 0 | } |
738 | | |
739 | 0 | redisContext *redisConnectUnixWithTimeout(const char *path, const struct timeval tv) { |
740 | 0 | redisContext *c; |
741 | |
|
742 | 0 | c = redisContextInit(); |
743 | 0 | if (c == NULL) |
744 | 0 | return NULL; |
745 | | |
746 | 0 | c->flags |= REDIS_BLOCK; |
747 | 0 | redisContextConnectUnix(c,path,&tv); |
748 | 0 | return c; |
749 | 0 | } |
750 | | |
751 | 0 | redisContext *redisConnectUnixNonBlock(const char *path) { |
752 | 0 | redisContext *c; |
753 | |
|
754 | 0 | c = redisContextInit(); |
755 | 0 | if (c == NULL) |
756 | 0 | return NULL; |
757 | | |
758 | 0 | c->flags &= ~REDIS_BLOCK; |
759 | 0 | redisContextConnectUnix(c,path,NULL); |
760 | 0 | return c; |
761 | 0 | } |
762 | | |
763 | 0 | redisContext *redisConnectFd(int fd) { |
764 | 0 | redisContext *c; |
765 | |
|
766 | 0 | c = redisContextInit(); |
767 | 0 | if (c == NULL) |
768 | 0 | return NULL; |
769 | | |
770 | 0 | c->fd = fd; |
771 | 0 | c->flags |= REDIS_BLOCK | REDIS_CONNECTED; |
772 | 0 | return c; |
773 | 0 | } |
774 | | |
775 | | /* Set read/write timeout on a blocking socket. */ |
776 | 0 | int redisSetTimeout(redisContext *c, const struct timeval tv) { |
777 | 0 | if (c->flags & REDIS_BLOCK) |
778 | 0 | return redisContextSetTimeout(c,tv); |
779 | 0 | return REDIS_ERR; |
780 | 0 | } |
781 | | |
782 | | /* Enable connection KeepAlive. */ |
783 | 0 | int redisEnableKeepAlive(redisContext *c) { |
784 | 0 | if (redisKeepAlive(c, REDIS_KEEPALIVE_INTERVAL) != REDIS_OK) |
785 | 0 | return REDIS_ERR; |
786 | 0 | return REDIS_OK; |
787 | 0 | } |
788 | | |
789 | | /* Use this function to handle a read event on the descriptor. It will try |
790 | | * and read some bytes from the socket and feed them to the reply parser. |
791 | | * |
792 | | * After this function is called, you may use redisContextReadReply to |
793 | | * see if there is a reply available. */ |
794 | 0 | int redisBufferRead(redisContext *c) { |
795 | 0 | char buf[1024*16]; |
796 | 0 | int nread; |
797 | | |
798 | | /* Return early when the context has seen an error. */ |
799 | 0 | if (c->err) |
800 | 0 | return REDIS_ERR; |
801 | | |
802 | 0 | nread = read(c->fd,buf,sizeof(buf)); |
803 | 0 | if (nread == -1) { |
804 | 0 | if ((errno == EAGAIN && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) { |
805 | | /* Try again later */ |
806 | 0 | } else { |
807 | 0 | __redisSetError(c,REDIS_ERR_IO,NULL); |
808 | 0 | return REDIS_ERR; |
809 | 0 | } |
810 | 0 | } else if (nread == 0) { |
811 | 0 | __redisSetError(c,REDIS_ERR_EOF,"Server closed the connection"); |
812 | 0 | return REDIS_ERR; |
813 | 0 | } else { |
814 | 0 | if (redisReaderFeed(c->reader,buf,nread) != REDIS_OK) { |
815 | 0 | __redisSetError(c,c->reader->err,c->reader->errstr); |
816 | 0 | return REDIS_ERR; |
817 | 0 | } |
818 | 0 | } |
819 | 0 | return REDIS_OK; |
820 | 0 | } |
821 | | |
822 | | /* Write the output buffer to the socket. |
823 | | * |
824 | | * Returns REDIS_OK when the buffer is empty, or (a part of) the buffer was |
825 | | * successfully written to the socket. When the buffer is empty after the |
826 | | * write operation, "done" is set to 1 (if given). |
827 | | * |
828 | | * Returns REDIS_ERR if an error occurred trying to write and sets |
829 | | * c->errstr to hold the appropriate error string. |
830 | | */ |
831 | 0 | int redisBufferWrite(redisContext *c, int *done) { |
832 | 0 | int nwritten; |
833 | | |
834 | | /* Return early when the context has seen an error. */ |
835 | 0 | if (c->err) |
836 | 0 | return REDIS_ERR; |
837 | | |
838 | 0 | if (sdslen(c->obuf) > 0) { |
839 | 0 | nwritten = write(c->fd,c->obuf,sdslen(c->obuf)); |
840 | 0 | if (nwritten == -1) { |
841 | 0 | if ((errno == EAGAIN && !(c->flags & REDIS_BLOCK)) || (errno == EINTR)) { |
842 | | /* Try again later */ |
843 | 0 | } else { |
844 | 0 | __redisSetError(c,REDIS_ERR_IO,NULL); |
845 | 0 | return REDIS_ERR; |
846 | 0 | } |
847 | 0 | } else if (nwritten > 0) { |
848 | 0 | if (nwritten == (signed)sdslen(c->obuf)) { |
849 | 0 | sdsfree(c->obuf); |
850 | 0 | c->obuf = sdsempty(); |
851 | 0 | } else { |
852 | 0 | sdsrange(c->obuf,nwritten,-1); |
853 | 0 | } |
854 | 0 | } |
855 | 0 | } |
856 | 0 | if (done != NULL) *done = (sdslen(c->obuf) == 0); |
857 | 0 | return REDIS_OK; |
858 | 0 | } |
859 | | |
860 | | /* Internal helper function to try and get a reply from the reader, |
861 | | * or set an error in the context otherwise. */ |
862 | 0 | int redisGetReplyFromReader(redisContext *c, void **reply) { |
863 | 0 | if (redisReaderGetReply(c->reader,reply) == REDIS_ERR) { |
864 | 0 | __redisSetError(c,c->reader->err,c->reader->errstr); |
865 | 0 | return REDIS_ERR; |
866 | 0 | } |
867 | 0 | return REDIS_OK; |
868 | 0 | } |
869 | | |
870 | 0 | int redisGetReply(redisContext *c, void **reply) { |
871 | 0 | int wdone = 0; |
872 | 0 | void *aux = NULL; |
873 | | |
874 | | /* Try to read pending replies */ |
875 | 0 | if (redisGetReplyFromReader(c,&aux) == REDIS_ERR) |
876 | 0 | return REDIS_ERR; |
877 | | |
878 | | /* For the blocking context, flush output buffer and read reply */ |
879 | 0 | if (aux == NULL && c->flags & REDIS_BLOCK) { |
880 | | /* Write until done */ |
881 | 0 | do { |
882 | 0 | if (redisBufferWrite(c,&wdone) == REDIS_ERR) |
883 | 0 | return REDIS_ERR; |
884 | 0 | } while (!wdone); |
885 | | |
886 | | /* Read until there is a reply */ |
887 | 0 | do { |
888 | 0 | if (redisBufferRead(c) == REDIS_ERR) |
889 | 0 | return REDIS_ERR; |
890 | 0 | if (redisGetReplyFromReader(c,&aux) == REDIS_ERR) |
891 | 0 | return REDIS_ERR; |
892 | 0 | } while (aux == NULL); |
893 | 0 | } |
894 | | |
895 | | /* Set reply object */ |
896 | 0 | if (reply != NULL) *reply = aux; |
897 | 0 | return REDIS_OK; |
898 | 0 | } |
899 | | |
900 | | |
901 | | /* Helper function for the redisAppendCommand* family of functions. |
902 | | * |
903 | | * Write a formatted command to the output buffer. When this family |
904 | | * is used, you need to call redisGetReply yourself to retrieve |
905 | | * the reply (or replies in pub/sub). |
906 | | */ |
907 | 0 | int __redisAppendCommand(redisContext *c, const char *cmd, size_t len) { |
908 | 0 | sds newbuf; |
909 | |
|
910 | 0 | newbuf = sdscatlen(c->obuf,cmd,len); |
911 | 0 | if (newbuf == NULL) { |
912 | 0 | __redisSetError(c,REDIS_ERR_OOM,"Out of memory"); |
913 | 0 | return REDIS_ERR; |
914 | 0 | } |
915 | | |
916 | 0 | c->obuf = newbuf; |
917 | 0 | return REDIS_OK; |
918 | 0 | } |
919 | | |
920 | 0 | int redisAppendFormattedCommand(redisContext *c, const char *cmd, size_t len) { |
921 | |
|
922 | 0 | if (__redisAppendCommand(c, cmd, len) != REDIS_OK) { |
923 | 0 | return REDIS_ERR; |
924 | 0 | } |
925 | | |
926 | 0 | return REDIS_OK; |
927 | 0 | } |
928 | | |
929 | 0 | int redisvAppendCommand(redisContext *c, const char *format, va_list ap) { |
930 | 0 | char *cmd; |
931 | 0 | int len; |
932 | |
|
933 | 0 | len = redisvFormatCommand(&cmd,format,ap); |
934 | 0 | if (len == -1) { |
935 | 0 | __redisSetError(c,REDIS_ERR_OOM,"Out of memory"); |
936 | 0 | return REDIS_ERR; |
937 | 0 | } else if (len == -2) { |
938 | 0 | __redisSetError(c,REDIS_ERR_OTHER,"Invalid format string"); |
939 | 0 | return REDIS_ERR; |
940 | 0 | } |
941 | | |
942 | 0 | if (__redisAppendCommand(c,cmd,len) != REDIS_OK) { |
943 | 0 | free(cmd); |
944 | 0 | return REDIS_ERR; |
945 | 0 | } |
946 | | |
947 | 0 | free(cmd); |
948 | 0 | return REDIS_OK; |
949 | 0 | } |
950 | | |
951 | 0 | int redisAppendCommand(redisContext *c, const char *format, ...) { |
952 | 0 | va_list ap; |
953 | 0 | int ret; |
954 | |
|
955 | 0 | va_start(ap,format); |
956 | 0 | ret = redisvAppendCommand(c,format,ap); |
957 | 0 | va_end(ap); |
958 | 0 | return ret; |
959 | 0 | } |
960 | | |
961 | 0 | int redisAppendCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) { |
962 | 0 | sds cmd; |
963 | 0 | int len; |
964 | |
|
965 | 0 | len = redisFormatSdsCommandArgv(&cmd,argc,argv,argvlen); |
966 | 0 | if (len == -1) { |
967 | 0 | __redisSetError(c,REDIS_ERR_OOM,"Out of memory"); |
968 | 0 | return REDIS_ERR; |
969 | 0 | } |
970 | | |
971 | 0 | if (__redisAppendCommand(c,cmd,len) != REDIS_OK) { |
972 | 0 | sdsfree(cmd); |
973 | 0 | return REDIS_ERR; |
974 | 0 | } |
975 | | |
976 | 0 | sdsfree(cmd); |
977 | 0 | return REDIS_OK; |
978 | 0 | } |
979 | | |
980 | | /* Helper function for the redisCommand* family of functions. |
981 | | * |
982 | | * Write a formatted command to the output buffer. If the given context is |
983 | | * blocking, immediately read the reply into the "reply" pointer. When the |
984 | | * context is non-blocking, the "reply" pointer will not be used and the |
985 | | * command is simply appended to the write buffer. |
986 | | * |
987 | | * Returns the reply when a reply was successfully retrieved. Returns NULL |
988 | | * otherwise. When NULL is returned in a blocking context, the error field |
989 | | * in the context will be set. |
990 | | */ |
991 | 0 | static void *__redisBlockForReply(redisContext *c) { |
992 | 0 | void *reply; |
993 | |
|
994 | 0 | if (c->flags & REDIS_BLOCK) { |
995 | 0 | if (redisGetReply(c,&reply) != REDIS_OK) |
996 | 0 | return NULL; |
997 | 0 | return reply; |
998 | 0 | } |
999 | 0 | return NULL; |
1000 | 0 | } |
1001 | | |
1002 | 0 | void *redisvCommand(redisContext *c, const char *format, va_list ap) { |
1003 | 0 | if (redisvAppendCommand(c,format,ap) != REDIS_OK) |
1004 | 0 | return NULL; |
1005 | 0 | return __redisBlockForReply(c); |
1006 | 0 | } |
1007 | | |
1008 | 0 | void *redisCommand(redisContext *c, const char *format, ...) { |
1009 | 0 | va_list ap; |
1010 | 0 | va_start(ap,format); |
1011 | 0 | void *reply = redisvCommand(c,format,ap); |
1012 | 0 | va_end(ap); |
1013 | 0 | return reply; |
1014 | 0 | } |
1015 | | |
1016 | 0 | void *redisCommandArgv(redisContext *c, int argc, const char **argv, const size_t *argvlen) { |
1017 | 0 | if (redisAppendCommandArgv(c,argc,argv,argvlen) != REDIS_OK) |
1018 | 0 | return NULL; |
1019 | 0 | return __redisBlockForReply(c); |
1020 | 0 | } |