xref: /redis-3.2.3/src/redis-cli.c (revision d6bc17c2)
1 /* Redis CLI (command line interface)
2  *
3  * Copyright (c) 2009-2012, Salvatore Sanfilippo <antirez at gmail dot com>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions are met:
8  *
9  *   * Redistributions of source code must retain the above copyright notice,
10  *     this list of conditions and the following disclaimer.
11  *   * Redistributions in binary form must reproduce the above copyright
12  *     notice, this list of conditions and the following disclaimer in the
13  *     documentation and/or other materials provided with the distribution.
14  *   * Neither the name of Redis nor the names of its contributors may be used
15  *     to endorse or promote products derived from this software without
16  *     specific prior written permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
19  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
20  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
22  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28  * POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #include "fmacros.h"
32 #include "version.h"
33 
34 #include <stdio.h>
35 #include <string.h>
36 #include <stdlib.h>
37 #include <signal.h>
38 #include <unistd.h>
39 #include <time.h>
40 #include <ctype.h>
41 #include <errno.h>
42 #include <sys/stat.h>
43 #include <sys/time.h>
44 #include <assert.h>
45 #include <fcntl.h>
46 #include <limits.h>
47 #include <math.h>
48 
49 #include <hiredis.h>
50 #include <sds.h> /* use sds.h from hiredis, so that only one set of sds functions will be present in the binary */
51 #include "zmalloc.h"
52 #include "linenoise.h"
53 #include "help.h"
54 #include "anet.h"
55 #include "ae.h"
56 
57 #define UNUSED(V) ((void) V)
58 
59 #define OUTPUT_STANDARD 0
60 #define OUTPUT_RAW 1
61 #define OUTPUT_CSV 2
62 #define REDIS_CLI_KEEPALIVE_INTERVAL 15 /* seconds */
63 #define REDIS_CLI_DEFAULT_PIPE_TIMEOUT 30 /* seconds */
64 #define REDIS_CLI_HISTFILE_ENV "REDISCLI_HISTFILE"
65 #define REDIS_CLI_HISTFILE_DEFAULT ".rediscli_history"
66 
67 /* --latency-dist palettes. */
68 int spectrum_palette_color_size = 19;
69 int spectrum_palette_color[] = {0,233,234,235,237,239,241,243,245,247,144,143,142,184,226,214,208,202,196};
70 
71 int spectrum_palette_mono_size = 13;
72 int spectrum_palette_mono[] = {0,233,234,235,237,239,241,243,245,247,249,251,253};
73 
74 /* The actual palette in use. */
75 int *spectrum_palette;
76 int spectrum_palette_size;
77 
78 static redisContext *context;
79 static struct config {
80     char *hostip;
81     int hostport;
82     char *hostsocket;
83     long repeat;
84     long interval;
85     int dbnum;
86     int interactive;
87     int shutdown;
88     int monitor_mode;
89     int pubsub_mode;
90     int latency_mode;
91     int latency_dist_mode;
92     int latency_history;
93     int lru_test_mode;
94     long long lru_test_sample_size;
95     int cluster_mode;
96     int cluster_reissue_command;
97     int slave_mode;
98     int pipe_mode;
99     int pipe_timeout;
100     int getrdb_mode;
101     int stat_mode;
102     int scan_mode;
103     int intrinsic_latency_mode;
104     int intrinsic_latency_duration;
105     char *pattern;
106     char *rdb_filename;
107     int bigkeys;
108     int stdinarg; /* get last arg from stdin. (-x option) */
109     char *auth;
110     int output; /* output mode, see OUTPUT_* defines */
111     sds mb_delim;
112     char prompt[128];
113     char *eval;
114     int eval_ldb;
115     int eval_ldb_sync;  /* Ask for synchronous mode of the Lua debugger. */
116     int eval_ldb_end;   /* Lua debugging session ended. */
117     int last_cmd_type;
118 } config;
119 
120 static volatile sig_atomic_t force_cancel_loop = 0;
121 static void usage(void);
122 static void slaveMode(void);
123 char *redisGitSHA1(void);
124 char *redisGitDirty(void);
125 
126 /*------------------------------------------------------------------------------
127  * Utility functions
128  *--------------------------------------------------------------------------- */
129 
130 static long long ustime(void) {
131     struct timeval tv;
132     long long ust;
133 
134     gettimeofday(&tv, NULL);
135     ust = ((long long)tv.tv_sec)*1000000;
136     ust += tv.tv_usec;
137     return ust;
138 }
139 
140 static long long mstime(void) {
141     return ustime()/1000;
142 }
143 
144 static void cliRefreshPrompt(void) {
145     int len;
146 
147     if (config.eval_ldb) return;
148     if (config.hostsocket != NULL)
149         len = snprintf(config.prompt,sizeof(config.prompt),"redis %s",
150                        config.hostsocket);
151     else
152         len = anetFormatAddr(config.prompt, sizeof(config.prompt),
153                            config.hostip, config.hostport);
154     /* Add [dbnum] if needed */
155     if (config.dbnum != 0 && config.last_cmd_type != REDIS_REPLY_ERROR)
156         len += snprintf(config.prompt+len,sizeof(config.prompt)-len,"[%d]",
157             config.dbnum);
158     snprintf(config.prompt+len,sizeof(config.prompt)-len,"> ");
159 }
160 
161 static sds getHistoryPath() {
162     char *path = NULL;
163     sds historyPath = NULL;
164 
165     /* check the env for a histfile override */
166     path = getenv(REDIS_CLI_HISTFILE_ENV);
167     if (path != NULL && *path != '\0') {
168         if (!strcmp("/dev/null", path)) {
169             return NULL;
170         }
171 
172         /* if the env is set, return it */
173         historyPath = sdscatprintf(sdsempty(), "%s", path);
174     } else {
175         char *home = getenv("HOME");
176         if (home != NULL && *home != '\0') {
177             /* otherwise, return the default */
178             historyPath = sdscatprintf(sdsempty(), "%s/%s", home, REDIS_CLI_HISTFILE_DEFAULT);
179         }
180     }
181 
182     return historyPath;
183 }
184 
185 /*------------------------------------------------------------------------------
186  * Help functions
187  *--------------------------------------------------------------------------- */
188 
189 #define CLI_HELP_COMMAND 1
190 #define CLI_HELP_GROUP 2
191 
192 typedef struct {
193     int type;
194     int argc;
195     sds *argv;
196     sds full;
197 
198     /* Only used for help on commands */
199     struct commandHelp *org;
200 } helpEntry;
201 
202 static helpEntry *helpEntries;
203 static int helpEntriesLen;
204 
205 static sds cliVersion(void) {
206     sds version;
207     version = sdscatprintf(sdsempty(), "%s", REDIS_VERSION);
208 
209     /* Add git commit and working tree status when available */
210     if (strtoll(redisGitSHA1(),NULL,16)) {
211         version = sdscatprintf(version, " (git:%s", redisGitSHA1());
212         if (strtoll(redisGitDirty(),NULL,10))
213             version = sdscatprintf(version, "-dirty");
214         version = sdscat(version, ")");
215     }
216     return version;
217 }
218 
219 static void cliInitHelp(void) {
220     int commandslen = sizeof(commandHelp)/sizeof(struct commandHelp);
221     int groupslen = sizeof(commandGroups)/sizeof(char*);
222     int i, len, pos = 0;
223     helpEntry tmp;
224 
225     helpEntriesLen = len = commandslen+groupslen;
226     helpEntries = malloc(sizeof(helpEntry)*len);
227 
228     for (i = 0; i < groupslen; i++) {
229         tmp.argc = 1;
230         tmp.argv = malloc(sizeof(sds));
231         tmp.argv[0] = sdscatprintf(sdsempty(),"@%s",commandGroups[i]);
232         tmp.full = tmp.argv[0];
233         tmp.type = CLI_HELP_GROUP;
234         tmp.org = NULL;
235         helpEntries[pos++] = tmp;
236     }
237 
238     for (i = 0; i < commandslen; i++) {
239         tmp.argv = sdssplitargs(commandHelp[i].name,&tmp.argc);
240         tmp.full = sdsnew(commandHelp[i].name);
241         tmp.type = CLI_HELP_COMMAND;
242         tmp.org = &commandHelp[i];
243         helpEntries[pos++] = tmp;
244     }
245 }
246 
247 /* Output command help to stdout. */
248 static void cliOutputCommandHelp(struct commandHelp *help, int group) {
249     printf("\r\n  \x1b[1m%s\x1b[0m \x1b[90m%s\x1b[0m\r\n", help->name, help->params);
250     printf("  \x1b[33msummary:\x1b[0m %s\r\n", help->summary);
251     printf("  \x1b[33msince:\x1b[0m %s\r\n", help->since);
252     if (group) {
253         printf("  \x1b[33mgroup:\x1b[0m %s\r\n", commandGroups[help->group]);
254     }
255 }
256 
257 /* Print generic help. */
258 static void cliOutputGenericHelp(void) {
259     sds version = cliVersion();
260     printf(
261         "redis-cli %s\r\n"
262         "Type: \"help @<group>\" to get a list of commands in <group>\r\n"
263         "      \"help <command>\" for help on <command>\r\n"
264         "      \"help <tab>\" to get a list of possible help topics\r\n"
265         "      \"quit\" to exit\r\n",
266         version
267     );
268     sdsfree(version);
269 }
270 
271 /* Output all command help, filtering by group or command name. */
272 static void cliOutputHelp(int argc, char **argv) {
273     int i, j, len;
274     int group = -1;
275     helpEntry *entry;
276     struct commandHelp *help;
277 
278     if (argc == 0) {
279         cliOutputGenericHelp();
280         return;
281     } else if (argc > 0 && argv[0][0] == '@') {
282         len = sizeof(commandGroups)/sizeof(char*);
283         for (i = 0; i < len; i++) {
284             if (strcasecmp(argv[0]+1,commandGroups[i]) == 0) {
285                 group = i;
286                 break;
287             }
288         }
289     }
290 
291     assert(argc > 0);
292     for (i = 0; i < helpEntriesLen; i++) {
293         entry = &helpEntries[i];
294         if (entry->type != CLI_HELP_COMMAND) continue;
295 
296         help = entry->org;
297         if (group == -1) {
298             /* Compare all arguments */
299             if (argc == entry->argc) {
300                 for (j = 0; j < argc; j++) {
301                     if (strcasecmp(argv[j],entry->argv[j]) != 0) break;
302                 }
303                 if (j == argc) {
304                     cliOutputCommandHelp(help,1);
305                 }
306             }
307         } else {
308             if (group == help->group) {
309                 cliOutputCommandHelp(help,0);
310             }
311         }
312     }
313     printf("\r\n");
314 }
315 
316 static void completionCallback(const char *buf, linenoiseCompletions *lc) {
317     size_t startpos = 0;
318     int mask;
319     int i;
320     size_t matchlen;
321     sds tmp;
322 
323     if (strncasecmp(buf,"help ",5) == 0) {
324         startpos = 5;
325         while (isspace(buf[startpos])) startpos++;
326         mask = CLI_HELP_COMMAND | CLI_HELP_GROUP;
327     } else {
328         mask = CLI_HELP_COMMAND;
329     }
330 
331     for (i = 0; i < helpEntriesLen; i++) {
332         if (!(helpEntries[i].type & mask)) continue;
333 
334         matchlen = strlen(buf+startpos);
335         if (strncasecmp(buf+startpos,helpEntries[i].full,matchlen) == 0) {
336             tmp = sdsnewlen(buf,startpos);
337             tmp = sdscat(tmp,helpEntries[i].full);
338             linenoiseAddCompletion(lc,tmp);
339             sdsfree(tmp);
340         }
341     }
342 }
343 
344 /*------------------------------------------------------------------------------
345  * Networking / parsing
346  *--------------------------------------------------------------------------- */
347 
348 /* Send AUTH command to the server */
349 static int cliAuth(void) {
350     redisReply *reply;
351     if (config.auth == NULL) return REDIS_OK;
352 
353     reply = redisCommand(context,"AUTH %s",config.auth);
354     if (reply != NULL) {
355         freeReplyObject(reply);
356         return REDIS_OK;
357     }
358     return REDIS_ERR;
359 }
360 
361 /* Send SELECT dbnum to the server */
362 static int cliSelect(void) {
363     redisReply *reply;
364     if (config.dbnum == 0) return REDIS_OK;
365 
366     reply = redisCommand(context,"SELECT %d",config.dbnum);
367     if (reply != NULL) {
368         int result = REDIS_OK;
369         if (reply->type == REDIS_REPLY_ERROR) result = REDIS_ERR;
370         freeReplyObject(reply);
371         return result;
372     }
373     return REDIS_ERR;
374 }
375 
376 /* Connect to the server. If force is not zero the connection is performed
377  * even if there is already a connected socket. */
378 static int cliConnect(int force) {
379     if (context == NULL || force) {
380         if (context != NULL) {
381             redisFree(context);
382         }
383 
384         if (config.hostsocket == NULL) {
385             context = redisConnect(config.hostip,config.hostport);
386         } else {
387             context = redisConnectUnix(config.hostsocket);
388         }
389 
390         if (context->err) {
391             fprintf(stderr,"Could not connect to Redis at ");
392             if (config.hostsocket == NULL)
393                 fprintf(stderr,"%s:%d: %s\n",config.hostip,config.hostport,context->errstr);
394             else
395                 fprintf(stderr,"%s: %s\n",config.hostsocket,context->errstr);
396             redisFree(context);
397             context = NULL;
398             return REDIS_ERR;
399         }
400 
401         /* Set aggressive KEEP_ALIVE socket option in the Redis context socket
402          * in order to prevent timeouts caused by the execution of long
403          * commands. At the same time this improves the detection of real
404          * errors. */
405         anetKeepAlive(NULL, context->fd, REDIS_CLI_KEEPALIVE_INTERVAL);
406 
407         /* Do AUTH and select the right DB. */
408         if (cliAuth() != REDIS_OK)
409             return REDIS_ERR;
410         if (cliSelect() != REDIS_OK)
411             return REDIS_ERR;
412     }
413     return REDIS_OK;
414 }
415 
416 static void cliPrintContextError(void) {
417     if (context == NULL) return;
418     fprintf(stderr,"Error: %s\n",context->errstr);
419 }
420 
421 static sds cliFormatReplyTTY(redisReply *r, char *prefix) {
422     sds out = sdsempty();
423     switch (r->type) {
424     case REDIS_REPLY_ERROR:
425         out = sdscatprintf(out,"(error) %s\n", r->str);
426     break;
427     case REDIS_REPLY_STATUS:
428         out = sdscat(out,r->str);
429         out = sdscat(out,"\n");
430     break;
431     case REDIS_REPLY_INTEGER:
432         out = sdscatprintf(out,"(integer) %lld\n",r->integer);
433     break;
434     case REDIS_REPLY_STRING:
435         /* If you are producing output for the standard output we want
436         * a more interesting output with quoted characters and so forth */
437         out = sdscatrepr(out,r->str,r->len);
438         out = sdscat(out,"\n");
439     break;
440     case REDIS_REPLY_NIL:
441         out = sdscat(out,"(nil)\n");
442     break;
443     case REDIS_REPLY_ARRAY:
444         if (r->elements == 0) {
445             out = sdscat(out,"(empty list or set)\n");
446         } else {
447             unsigned int i, idxlen = 0;
448             char _prefixlen[16];
449             char _prefixfmt[16];
450             sds _prefix;
451             sds tmp;
452 
453             /* Calculate chars needed to represent the largest index */
454             i = r->elements;
455             do {
456                 idxlen++;
457                 i /= 10;
458             } while(i);
459 
460             /* Prefix for nested multi bulks should grow with idxlen+2 spaces */
461             memset(_prefixlen,' ',idxlen+2);
462             _prefixlen[idxlen+2] = '\0';
463             _prefix = sdscat(sdsnew(prefix),_prefixlen);
464 
465             /* Setup prefix format for every entry */
466             snprintf(_prefixfmt,sizeof(_prefixfmt),"%%s%%%ud) ",idxlen);
467 
468             for (i = 0; i < r->elements; i++) {
469                 /* Don't use the prefix for the first element, as the parent
470                  * caller already prepended the index number. */
471                 out = sdscatprintf(out,_prefixfmt,i == 0 ? "" : prefix,i+1);
472 
473                 /* Format the multi bulk entry */
474                 tmp = cliFormatReplyTTY(r->element[i],_prefix);
475                 out = sdscatlen(out,tmp,sdslen(tmp));
476                 sdsfree(tmp);
477             }
478             sdsfree(_prefix);
479         }
480     break;
481     default:
482         fprintf(stderr,"Unknown reply type: %d\n", r->type);
483         exit(1);
484     }
485     return out;
486 }
487 
488 int isColorTerm(void) {
489     char *t = getenv("TERM");
490     return t != NULL && strstr(t,"xterm") != NULL;
491 }
492 
493 /* Helpe  function for sdsCatColorizedLdbReply() appending colorize strings
494  * to an SDS string. */
495 sds sdscatcolor(sds o, char *s, size_t len, char *color) {
496     if (!isColorTerm()) return sdscatlen(o,s,len);
497 
498     int bold = strstr(color,"bold") != NULL;
499     int ccode = 37; /* Defaults to white. */
500     if (strstr(color,"red")) ccode = 31;
501     else if (strstr(color,"red")) ccode = 31;
502     else if (strstr(color,"green")) ccode = 32;
503     else if (strstr(color,"yellow")) ccode = 33;
504     else if (strstr(color,"blue")) ccode = 34;
505     else if (strstr(color,"magenta")) ccode = 35;
506     else if (strstr(color,"cyan")) ccode = 36;
507     else if (strstr(color,"white")) ccode = 37;
508 
509     o = sdscatfmt(o,"\033[%i;%i;49m",bold,ccode);
510     o = sdscatlen(o,s,len);
511     o = sdscat(o,"\033[0m");
512     return o;
513 }
514 
515 /* Colorize Lua debugger status replies according to the prefix they
516  * have. */
517 sds sdsCatColorizedLdbReply(sds o, char *s, size_t len) {
518     char *color = "white";
519 
520     if (strstr(s,"<debug>")) color = "bold";
521     if (strstr(s,"<redis>")) color = "green";
522     if (strstr(s,"<reply>")) color = "cyan";
523     if (strstr(s,"<error>")) color = "red";
524     if (strstr(s,"<hint>")) color = "bold";
525     if (strstr(s,"<value>") || strstr(s,"<retval>")) color = "magenta";
526     if (len > 4 && isdigit(s[3])) {
527         if (s[1] == '>') color = "yellow"; /* Current line. */
528         else if (s[2] == '#') color = "bold"; /* Break point. */
529     }
530     return sdscatcolor(o,s,len,color);
531 }
532 
533 static sds cliFormatReplyRaw(redisReply *r) {
534     sds out = sdsempty(), tmp;
535     size_t i;
536 
537     switch (r->type) {
538     case REDIS_REPLY_NIL:
539         /* Nothing... */
540         break;
541     case REDIS_REPLY_ERROR:
542         out = sdscatlen(out,r->str,r->len);
543         out = sdscatlen(out,"\n",1);
544         break;
545     case REDIS_REPLY_STATUS:
546     case REDIS_REPLY_STRING:
547         if (r->type == REDIS_REPLY_STATUS && config.eval_ldb) {
548             /* The Lua debugger replies with arrays of simple (status)
549              * strings. We colorize the output for more fun if this
550              * is a debugging session. */
551 
552             /* Detect the end of a debugging session. */
553             if (strstr(r->str,"<endsession>") == r->str) {
554                 config.eval_ldb = 0;
555                 config.eval_ldb_end = 1; /* Signal the caller session ended. */
556                 config.output = OUTPUT_STANDARD;
557                 cliRefreshPrompt();
558             } else {
559                 out = sdsCatColorizedLdbReply(out,r->str,r->len);
560             }
561         } else {
562             out = sdscatlen(out,r->str,r->len);
563         }
564         break;
565     case REDIS_REPLY_INTEGER:
566         out = sdscatprintf(out,"%lld",r->integer);
567         break;
568     case REDIS_REPLY_ARRAY:
569         for (i = 0; i < r->elements; i++) {
570             if (i > 0) out = sdscat(out,config.mb_delim);
571             tmp = cliFormatReplyRaw(r->element[i]);
572             out = sdscatlen(out,tmp,sdslen(tmp));
573             sdsfree(tmp);
574         }
575         break;
576     default:
577         fprintf(stderr,"Unknown reply type: %d\n", r->type);
578         exit(1);
579     }
580     return out;
581 }
582 
583 static sds cliFormatReplyCSV(redisReply *r) {
584     unsigned int i;
585 
586     sds out = sdsempty();
587     switch (r->type) {
588     case REDIS_REPLY_ERROR:
589         out = sdscat(out,"ERROR,");
590         out = sdscatrepr(out,r->str,strlen(r->str));
591     break;
592     case REDIS_REPLY_STATUS:
593         out = sdscatrepr(out,r->str,r->len);
594     break;
595     case REDIS_REPLY_INTEGER:
596         out = sdscatprintf(out,"%lld",r->integer);
597     break;
598     case REDIS_REPLY_STRING:
599         out = sdscatrepr(out,r->str,r->len);
600     break;
601     case REDIS_REPLY_NIL:
602         out = sdscat(out,"NIL");
603     break;
604     case REDIS_REPLY_ARRAY:
605         for (i = 0; i < r->elements; i++) {
606             sds tmp = cliFormatReplyCSV(r->element[i]);
607             out = sdscatlen(out,tmp,sdslen(tmp));
608             if (i != r->elements-1) out = sdscat(out,",");
609             sdsfree(tmp);
610         }
611     break;
612     default:
613         fprintf(stderr,"Unknown reply type: %d\n", r->type);
614         exit(1);
615     }
616     return out;
617 }
618 
619 static int cliReadReply(int output_raw_strings) {
620     void *_reply;
621     redisReply *reply;
622     sds out = NULL;
623     int output = 1;
624 
625     if (redisGetReply(context,&_reply) != REDIS_OK) {
626         if (config.shutdown) {
627             redisFree(context);
628             context = NULL;
629             return REDIS_OK;
630         }
631         if (config.interactive) {
632             /* Filter cases where we should reconnect */
633             if (context->err == REDIS_ERR_IO &&
634                 (errno == ECONNRESET || errno == EPIPE))
635                 return REDIS_ERR;
636             if (context->err == REDIS_ERR_EOF)
637                 return REDIS_ERR;
638         }
639         cliPrintContextError();
640         exit(1);
641         return REDIS_ERR; /* avoid compiler warning */
642     }
643 
644     reply = (redisReply*)_reply;
645 
646     config.last_cmd_type = reply->type;
647 
648     /* Check if we need to connect to a different node and reissue the
649      * request. */
650     if (config.cluster_mode && reply->type == REDIS_REPLY_ERROR &&
651         (!strncmp(reply->str,"MOVED",5) || !strcmp(reply->str,"ASK")))
652     {
653         char *p = reply->str, *s;
654         int slot;
655 
656         output = 0;
657         /* Comments show the position of the pointer as:
658          *
659          * [S] for pointer 's'
660          * [P] for pointer 'p'
661          */
662         s = strchr(p,' ');      /* MOVED[S]3999 127.0.0.1:6381 */
663         p = strchr(s+1,' ');    /* MOVED[S]3999[P]127.0.0.1:6381 */
664         *p = '\0';
665         slot = atoi(s+1);
666         s = strchr(p+1,':');    /* MOVED 3999[P]127.0.0.1[S]6381 */
667         *s = '\0';
668         sdsfree(config.hostip);
669         config.hostip = sdsnew(p+1);
670         config.hostport = atoi(s+1);
671         if (config.interactive)
672             printf("-> Redirected to slot [%d] located at %s:%d\n",
673                 slot, config.hostip, config.hostport);
674         config.cluster_reissue_command = 1;
675         cliRefreshPrompt();
676     }
677 
678     if (output) {
679         if (output_raw_strings) {
680             out = cliFormatReplyRaw(reply);
681         } else {
682             if (config.output == OUTPUT_RAW) {
683                 out = cliFormatReplyRaw(reply);
684                 out = sdscat(out,"\n");
685             } else if (config.output == OUTPUT_STANDARD) {
686                 out = cliFormatReplyTTY(reply,"");
687             } else if (config.output == OUTPUT_CSV) {
688                 out = cliFormatReplyCSV(reply);
689                 out = sdscat(out,"\n");
690             }
691         }
692         fwrite(out,sdslen(out),1,stdout);
693         sdsfree(out);
694     }
695     freeReplyObject(reply);
696     return REDIS_OK;
697 }
698 
699 static int cliSendCommand(int argc, char **argv, int repeat) {
700     char *command = argv[0];
701     size_t *argvlen;
702     int j, output_raw;
703 
704     if (!config.eval_ldb && /* In debugging mode, let's pass "help" to Redis. */
705         (!strcasecmp(command,"help") || !strcasecmp(command,"?"))) {
706         cliOutputHelp(--argc, ++argv);
707         return REDIS_OK;
708     }
709 
710     if (context == NULL) return REDIS_ERR;
711 
712     output_raw = 0;
713     if (!strcasecmp(command,"info") ||
714         (argc >= 2 && !strcasecmp(command,"debug") &&
715                       (!strcasecmp(argv[1],"jemalloc") ||
716                        !strcasecmp(argv[1],"htstats"))) ||
717         (argc == 2 && !strcasecmp(command,"cluster") &&
718                       (!strcasecmp(argv[1],"nodes") ||
719                        !strcasecmp(argv[1],"info"))) ||
720         (argc == 2 && !strcasecmp(command,"client") &&
721                        !strcasecmp(argv[1],"list")) ||
722         (argc == 3 && !strcasecmp(command,"latency") &&
723                        !strcasecmp(argv[1],"graph")) ||
724         (argc == 2 && !strcasecmp(command,"latency") &&
725                        !strcasecmp(argv[1],"doctor")))
726     {
727         output_raw = 1;
728     }
729 
730     if (!strcasecmp(command,"shutdown")) config.shutdown = 1;
731     if (!strcasecmp(command,"monitor")) config.monitor_mode = 1;
732     if (!strcasecmp(command,"subscribe") ||
733         !strcasecmp(command,"psubscribe")) config.pubsub_mode = 1;
734     if (!strcasecmp(command,"sync") ||
735         !strcasecmp(command,"psync")) config.slave_mode = 1;
736 
737     /* Setup argument length */
738     argvlen = malloc(argc*sizeof(size_t));
739     for (j = 0; j < argc; j++)
740         argvlen[j] = sdslen(argv[j]);
741 
742     while(repeat--) {
743         redisAppendCommandArgv(context,argc,(const char**)argv,argvlen);
744         while (config.monitor_mode) {
745             if (cliReadReply(output_raw) != REDIS_OK) exit(1);
746             fflush(stdout);
747         }
748 
749         if (config.pubsub_mode) {
750             if (config.output != OUTPUT_RAW)
751                 printf("Reading messages... (press Ctrl-C to quit)\n");
752             while (1) {
753                 if (cliReadReply(output_raw) != REDIS_OK) exit(1);
754             }
755         }
756 
757         if (config.slave_mode) {
758             printf("Entering slave output mode...  (press Ctrl-C to quit)\n");
759             slaveMode();
760             config.slave_mode = 0;
761             free(argvlen);
762             return REDIS_ERR;  /* Error = slaveMode lost connection to master */
763         }
764 
765         if (cliReadReply(output_raw) != REDIS_OK) {
766             free(argvlen);
767             return REDIS_ERR;
768         } else {
769             /* Store database number when SELECT was successfully executed. */
770             if (!strcasecmp(command,"select") && argc == 2) {
771                 config.dbnum = atoi(argv[1]);
772                 cliRefreshPrompt();
773             } else if (!strcasecmp(command,"auth") && argc == 2) {
774                 cliSelect();
775             }
776         }
777         if (config.interval) usleep(config.interval);
778         fflush(stdout); /* Make it grep friendly */
779     }
780 
781     free(argvlen);
782     return REDIS_OK;
783 }
784 
785 /* Send a command reconnecting the link if needed. */
786 static redisReply *reconnectingRedisCommand(redisContext *c, const char *fmt, ...) {
787     redisReply *reply = NULL;
788     int tries = 0;
789     va_list ap;
790 
791     assert(!c->err);
792     while(reply == NULL) {
793         while (c->err & (REDIS_ERR_IO | REDIS_ERR_EOF)) {
794             printf("\r\x1b[0K"); /* Cursor to left edge + clear line. */
795             printf("Reconnecting... %d\r", ++tries);
796             fflush(stdout);
797 
798             redisFree(c);
799             c = redisConnect(config.hostip,config.hostport);
800             usleep(1000000);
801         }
802 
803         va_start(ap,fmt);
804         reply = redisvCommand(c,fmt,ap);
805         va_end(ap);
806 
807         if (c->err && !(c->err & (REDIS_ERR_IO | REDIS_ERR_EOF))) {
808             fprintf(stderr, "Error: %s\n", c->errstr);
809             exit(1);
810         } else if (tries > 0) {
811             printf("\r\x1b[0K"); /* Cursor to left edge + clear line. */
812         }
813     }
814 
815     context = c;
816     return reply;
817 }
818 
819 /*------------------------------------------------------------------------------
820  * User interface
821  *--------------------------------------------------------------------------- */
822 
823 static int parseOptions(int argc, char **argv) {
824     int i;
825 
826     for (i = 1; i < argc; i++) {
827         int lastarg = i==argc-1;
828 
829         if (!strcmp(argv[i],"-h") && !lastarg) {
830             sdsfree(config.hostip);
831             config.hostip = sdsnew(argv[++i]);
832         } else if (!strcmp(argv[i],"-h") && lastarg) {
833             usage();
834         } else if (!strcmp(argv[i],"--help")) {
835             usage();
836         } else if (!strcmp(argv[i],"-x")) {
837             config.stdinarg = 1;
838         } else if (!strcmp(argv[i],"-p") && !lastarg) {
839             config.hostport = atoi(argv[++i]);
840         } else if (!strcmp(argv[i],"-s") && !lastarg) {
841             config.hostsocket = argv[++i];
842         } else if (!strcmp(argv[i],"-r") && !lastarg) {
843             config.repeat = strtoll(argv[++i],NULL,10);
844         } else if (!strcmp(argv[i],"-i") && !lastarg) {
845             double seconds = atof(argv[++i]);
846             config.interval = seconds*1000000;
847         } else if (!strcmp(argv[i],"-n") && !lastarg) {
848             config.dbnum = atoi(argv[++i]);
849         } else if (!strcmp(argv[i],"-a") && !lastarg) {
850             config.auth = argv[++i];
851         } else if (!strcmp(argv[i],"--raw")) {
852             config.output = OUTPUT_RAW;
853         } else if (!strcmp(argv[i],"--no-raw")) {
854             config.output = OUTPUT_STANDARD;
855         } else if (!strcmp(argv[i],"--csv")) {
856             config.output = OUTPUT_CSV;
857         } else if (!strcmp(argv[i],"--latency")) {
858             config.latency_mode = 1;
859         } else if (!strcmp(argv[i],"--latency-dist")) {
860             config.latency_dist_mode = 1;
861         } else if (!strcmp(argv[i],"--mono")) {
862             spectrum_palette = spectrum_palette_mono;
863             spectrum_palette_size = spectrum_palette_mono_size;
864         } else if (!strcmp(argv[i],"--latency-history")) {
865             config.latency_mode = 1;
866             config.latency_history = 1;
867         } else if (!strcmp(argv[i],"--lru-test") && !lastarg) {
868             config.lru_test_mode = 1;
869             config.lru_test_sample_size = strtoll(argv[++i],NULL,10);
870         } else if (!strcmp(argv[i],"--slave")) {
871             config.slave_mode = 1;
872         } else if (!strcmp(argv[i],"--stat")) {
873             config.stat_mode = 1;
874         } else if (!strcmp(argv[i],"--scan")) {
875             config.scan_mode = 1;
876         } else if (!strcmp(argv[i],"--pattern") && !lastarg) {
877             config.pattern = argv[++i];
878         } else if (!strcmp(argv[i],"--intrinsic-latency") && !lastarg) {
879             config.intrinsic_latency_mode = 1;
880             config.intrinsic_latency_duration = atoi(argv[++i]);
881         } else if (!strcmp(argv[i],"--rdb") && !lastarg) {
882             config.getrdb_mode = 1;
883             config.rdb_filename = argv[++i];
884         } else if (!strcmp(argv[i],"--pipe")) {
885             config.pipe_mode = 1;
886         } else if (!strcmp(argv[i],"--pipe-timeout") && !lastarg) {
887             config.pipe_timeout = atoi(argv[++i]);
888         } else if (!strcmp(argv[i],"--bigkeys")) {
889             config.bigkeys = 1;
890         } else if (!strcmp(argv[i],"--eval") && !lastarg) {
891             config.eval = argv[++i];
892         } else if (!strcmp(argv[i],"--ldb")) {
893             config.eval_ldb = 1;
894             config.output = OUTPUT_RAW;
895         } else if (!strcmp(argv[i],"--ldb-sync-mode")) {
896             config.eval_ldb = 1;
897             config.eval_ldb_sync = 1;
898             config.output = OUTPUT_RAW;
899         } else if (!strcmp(argv[i],"-c")) {
900             config.cluster_mode = 1;
901         } else if (!strcmp(argv[i],"-d") && !lastarg) {
902             sdsfree(config.mb_delim);
903             config.mb_delim = sdsnew(argv[++i]);
904         } else if (!strcmp(argv[i],"-v") || !strcmp(argv[i], "--version")) {
905             sds version = cliVersion();
906             printf("redis-cli %s\n", version);
907             sdsfree(version);
908             exit(0);
909         } else {
910             if (argv[i][0] == '-') {
911                 fprintf(stderr,
912                     "Unrecognized option or bad number of args for: '%s'\n",
913                     argv[i]);
914                 exit(1);
915             } else {
916                 /* Likely the command name, stop here. */
917                 break;
918             }
919         }
920     }
921 
922     /* --ldb requires --eval. */
923     if (config.eval_ldb && config.eval == NULL) {
924         fprintf(stderr,"Options --ldb and --ldb-sync-mode require --eval.\n");
925         fprintf(stderr,"Try %s --help for more information.\n", argv[0]);
926         exit(1);
927     }
928     return i;
929 }
930 
931 static sds readArgFromStdin(void) {
932     char buf[1024];
933     sds arg = sdsempty();
934 
935     while(1) {
936         int nread = read(fileno(stdin),buf,1024);
937 
938         if (nread == 0) break;
939         else if (nread == -1) {
940             perror("Reading from standard input");
941             exit(1);
942         }
943         arg = sdscatlen(arg,buf,nread);
944     }
945     return arg;
946 }
947 
948 static void usage(void) {
949     sds version = cliVersion();
950     fprintf(stderr,
951 "redis-cli %s\n"
952 "\n"
953 "Usage: redis-cli [OPTIONS] [cmd [arg [arg ...]]]\n"
954 "  -h <hostname>      Server hostname (default: 127.0.0.1).\n"
955 "  -p <port>          Server port (default: 6379).\n"
956 "  -s <socket>        Server socket (overrides hostname and port).\n"
957 "  -a <password>      Password to use when connecting to the server.\n"
958 "  -r <repeat>        Execute specified command N times.\n"
959 "  -i <interval>      When -r is used, waits <interval> seconds per command.\n"
960 "                     It is possible to specify sub-second times like -i 0.1.\n"
961 "  -n <db>            Database number.\n"
962 "  -x                 Read last argument from STDIN.\n"
963 "  -d <delimiter>     Multi-bulk delimiter in for raw formatting (default: \\n).\n"
964 "  -c                 Enable cluster mode (follow -ASK and -MOVED redirections).\n"
965 "  --raw              Use raw formatting for replies (default when STDOUT is\n"
966 "                     not a tty).\n"
967 "  --no-raw           Force formatted output even when STDOUT is not a tty.\n"
968 "  --csv              Output in CSV format.\n"
969 "  --stat             Print rolling stats about server: mem, clients, ...\n"
970 "  --latency          Enter a special mode continuously sampling latency.\n"
971 "  --latency-history  Like --latency but tracking latency changes over time.\n"
972 "                     Default time interval is 15 sec. Change it using -i.\n"
973 "  --latency-dist     Shows latency as a spectrum, requires xterm 256 colors.\n"
974 "                     Default time interval is 1 sec. Change it using -i.\n"
975 "  --lru-test <keys>  Simulate a cache workload with an 80-20 distribution.\n"
976 "  --slave            Simulate a slave showing commands received from the master.\n"
977 "  --rdb <filename>   Transfer an RDB dump from remote server to local file.\n"
978 "  --pipe             Transfer raw Redis protocol from stdin to server.\n"
979 "  --pipe-timeout <n> In --pipe mode, abort with error if after sending all data.\n"
980 "                     no reply is received within <n> seconds.\n"
981 "                     Default timeout: %d. Use 0 to wait forever.\n"
982 "  --bigkeys          Sample Redis keys looking for big keys.\n"
983 "  --scan             List all keys using the SCAN command.\n"
984 "  --pattern <pat>    Useful with --scan to specify a SCAN pattern.\n"
985 "  --intrinsic-latency <sec> Run a test to measure intrinsic system latency.\n"
986 "                     The test will run for the specified amount of seconds.\n"
987 "  --eval <file>      Send an EVAL command using the Lua script at <file>.\n"
988 "  --ldb              Used with --eval enable the Redis Lua debugger.\n"
989 "  --ldb-sync-mode    Like --ldb but uses the synchronous Lua debugger, in\n"
990 "                     this mode the server is blocked and script changes are\n"
991 "                     are not rolled back from the server memory.\n"
992 "  --help             Output this help and exit.\n"
993 "  --version          Output version and exit.\n"
994 "\n"
995 "Examples:\n"
996 "  cat /etc/passwd | redis-cli -x set mypasswd\n"
997 "  redis-cli get mypasswd\n"
998 "  redis-cli -r 100 lpush mylist x\n"
999 "  redis-cli -r 100 -i 1 info | grep used_memory_human:\n"
1000 "  redis-cli --eval myscript.lua key1 key2 , arg1 arg2 arg3\n"
1001 "  redis-cli --scan --pattern '*:12345*'\n"
1002 "\n"
1003 "  (Note: when using --eval the comma separates KEYS[] from ARGV[] items)\n"
1004 "\n"
1005 "When no command is given, redis-cli starts in interactive mode.\n"
1006 "Type \"help\" in interactive mode for information on available commands.\n"
1007 "\n",
1008         version, REDIS_CLI_DEFAULT_PIPE_TIMEOUT);
1009     sdsfree(version);
1010     exit(1);
1011 }
1012 
1013 /* Turn the plain C strings into Sds strings */
1014 static char **convertToSds(int count, char** args) {
1015   int j;
1016   char **sds = zmalloc(sizeof(char*)*count);
1017 
1018   for(j = 0; j < count; j++)
1019     sds[j] = sdsnew(args[j]);
1020 
1021   return sds;
1022 }
1023 
1024 static int issueCommandRepeat(int argc, char **argv, long repeat) {
1025     while (1) {
1026         config.cluster_reissue_command = 0;
1027         if (cliSendCommand(argc,argv,repeat) != REDIS_OK) {
1028             cliConnect(1);
1029 
1030             /* If we still cannot send the command print error.
1031              * We'll try to reconnect the next time. */
1032             if (cliSendCommand(argc,argv,repeat) != REDIS_OK) {
1033                 cliPrintContextError();
1034                 return REDIS_ERR;
1035             }
1036          }
1037          /* Issue the command again if we got redirected in cluster mode */
1038          if (config.cluster_mode && config.cluster_reissue_command) {
1039             cliConnect(1);
1040          } else {
1041              break;
1042         }
1043     }
1044     return REDIS_OK;
1045 }
1046 
1047 static int issueCommand(int argc, char **argv) {
1048     return issueCommandRepeat(argc, argv, config.repeat);
1049 }
1050 
1051 /* Split the user provided command into multiple SDS arguments.
1052  * This function normally uses sdssplitargs() from sds.c which is able
1053  * to understand "quoted strings", escapes and so forth. However when
1054  * we are in Lua debugging mode and the "eval" command is used, we want
1055  * the remaining Lua script (after "e " or "eval ") to be passed verbatim
1056  * as a single big argument. */
1057 static sds *cliSplitArgs(char *line, int *argc) {
1058     if (config.eval_ldb && (strstr(line,"eval ") == line ||
1059                             strstr(line,"e ") == line))
1060     {
1061         sds *argv = sds_malloc(sizeof(sds)*2);
1062         *argc = 2;
1063         int len = strlen(line);
1064         int elen = line[1] == ' ' ? 2 : 5; /* "e " or "eval "? */
1065         argv[0] = sdsnewlen(line,elen-1);
1066         argv[1] = sdsnewlen(line+elen,len-elen);
1067         return argv;
1068     } else {
1069         return sdssplitargs(line,argc);
1070     }
1071 }
1072 
1073 static void repl(void) {
1074     sds historyfile = NULL;
1075     int history = 0;
1076     char *line;
1077     int argc;
1078     sds *argv;
1079 
1080     config.interactive = 1;
1081     linenoiseSetMultiLine(1);
1082     linenoiseSetCompletionCallback(completionCallback);
1083 
1084     /* Only use history when stdin is a tty. */
1085     if (isatty(fileno(stdin))) {
1086         historyfile = getHistoryPath();
1087         if (historyfile != NULL) {
1088             history = 1;
1089             linenoiseHistoryLoad(historyfile);
1090         }
1091     }
1092 
1093     cliRefreshPrompt();
1094     while((line = linenoise(context ? config.prompt : "not connected> ")) != NULL) {
1095         if (line[0] != '\0') {
1096             argv = cliSplitArgs(line,&argc);
1097             if (history) linenoiseHistoryAdd(line);
1098             if (historyfile) linenoiseHistorySave(historyfile);
1099 
1100             if (argv == NULL) {
1101                 printf("Invalid argument(s)\n");
1102                 free(line);
1103                 continue;
1104             } else if (argc > 0) {
1105                 if (strcasecmp(argv[0],"quit") == 0 ||
1106                     strcasecmp(argv[0],"exit") == 0)
1107                 {
1108                     exit(0);
1109                 } else if (strcasecmp(argv[0],"restart") == 0) {
1110                     if (config.eval) {
1111                         config.eval_ldb = 1;
1112                         config.output = OUTPUT_RAW;
1113                         return; /* Return to evalMode to restart the session. */
1114                     } else {
1115                         printf("Use 'restart' only in Lua debugging mode.");
1116                     }
1117                 } else if (argc == 3 && !strcasecmp(argv[0],"connect")) {
1118                     sdsfree(config.hostip);
1119                     config.hostip = sdsnew(argv[1]);
1120                     config.hostport = atoi(argv[2]);
1121                     cliRefreshPrompt();
1122                     cliConnect(1);
1123                 } else if (argc == 1 && !strcasecmp(argv[0],"clear")) {
1124                     linenoiseClearScreen();
1125                 } else {
1126                     long long start_time = mstime(), elapsed;
1127                     int repeat, skipargs = 0;
1128 
1129                     repeat = atoi(argv[0]);
1130                     if (argc > 1 && repeat) {
1131                         skipargs = 1;
1132                     } else {
1133                         repeat = 1;
1134                     }
1135 
1136                     issueCommandRepeat(argc-skipargs, argv+skipargs, repeat);
1137 
1138                     /* If our debugging session ended, show the EVAL final
1139                      * reply. */
1140                     if (config.eval_ldb_end) {
1141                         config.eval_ldb_end = 0;
1142                         cliReadReply(0);
1143                         printf("\n(Lua debugging session ended%s)\n\n",
1144                             config.eval_ldb_sync ? "" :
1145                             " -- dataset changes rolled back");
1146                     }
1147 
1148                     elapsed = mstime()-start_time;
1149                     if (elapsed >= 500) {
1150                         printf("(%.2fs)\n",(double)elapsed/1000);
1151                     }
1152                 }
1153             }
1154             /* Free the argument vector */
1155             sdsfreesplitres(argv,argc);
1156         }
1157         /* linenoise() returns malloc-ed lines like readline() */
1158         free(line);
1159     }
1160     exit(0);
1161 }
1162 
1163 static int noninteractive(int argc, char **argv) {
1164     int retval = 0;
1165     if (config.stdinarg) {
1166         argv = zrealloc(argv, (argc+1)*sizeof(char*));
1167         argv[argc] = readArgFromStdin();
1168         retval = issueCommand(argc+1, argv);
1169     } else {
1170         retval = issueCommand(argc, argv);
1171     }
1172     return retval;
1173 }
1174 
1175 /*------------------------------------------------------------------------------
1176  * Eval mode
1177  *--------------------------------------------------------------------------- */
1178 
1179 static int evalMode(int argc, char **argv) {
1180     sds script = NULL;
1181     FILE *fp;
1182     char buf[1024];
1183     size_t nread;
1184     char **argv2;
1185     int j, got_comma, keys;
1186     int retval = REDIS_OK;
1187 
1188     while(1) {
1189         if (config.eval_ldb) {
1190             printf(
1191             "Lua debugging session started, please use:\n"
1192             "quit    -- End the session.\n"
1193             "restart -- Restart the script in debug mode again.\n"
1194             "help    -- Show Lua script debugging commands.\n\n"
1195             );
1196         }
1197 
1198         sdsfree(script);
1199         script = sdsempty();
1200         got_comma = 0;
1201         keys = 0;
1202 
1203         /* Load the script from the file, as an sds string. */
1204         fp = fopen(config.eval,"r");
1205         if (!fp) {
1206             fprintf(stderr,
1207                 "Can't open file '%s': %s\n", config.eval, strerror(errno));
1208             exit(1);
1209         }
1210         while((nread = fread(buf,1,sizeof(buf),fp)) != 0) {
1211             script = sdscatlen(script,buf,nread);
1212         }
1213         fclose(fp);
1214 
1215         /* If we are debugging a script, enable the Lua debugger. */
1216         if (config.eval_ldb) {
1217             redisReply *reply = redisCommand(context,
1218                     config.eval_ldb_sync ?
1219                     "SCRIPT DEBUG sync": "SCRIPT DEBUG yes");
1220             if (reply) freeReplyObject(reply);
1221         }
1222 
1223         /* Create our argument vector */
1224         argv2 = zmalloc(sizeof(sds)*(argc+3));
1225         argv2[0] = sdsnew("EVAL");
1226         argv2[1] = script;
1227         for (j = 0; j < argc; j++) {
1228             if (!got_comma && argv[j][0] == ',' && argv[j][1] == 0) {
1229                 got_comma = 1;
1230                 continue;
1231             }
1232             argv2[j+3-got_comma] = sdsnew(argv[j]);
1233             if (!got_comma) keys++;
1234         }
1235         argv2[2] = sdscatprintf(sdsempty(),"%d",keys);
1236 
1237         /* Call it */
1238         int eval_ldb = config.eval_ldb; /* Save it, may be reverteed. */
1239         retval = issueCommand(argc+3-got_comma, argv2);
1240         if (eval_ldb) {
1241             if (!config.eval_ldb) {
1242                 /* If the debugging session ended immediately, there was an
1243                  * error compiling the script. Show it and don't enter
1244                  * the REPL at all. */
1245                 printf("Eval debugging session can't start:\n");
1246                 cliReadReply(0);
1247                 break; /* Return to the caller. */
1248             } else {
1249                 strncpy(config.prompt,"lua debugger> ",sizeof(config.prompt));
1250                 repl();
1251                 /* Restart the session if repl() returned. */
1252                 cliConnect(1);
1253                 printf("\n");
1254             }
1255         } else {
1256             break; /* Return to the caller. */
1257         }
1258     }
1259     return retval;
1260 }
1261 
1262 /*------------------------------------------------------------------------------
1263  * Latency and latency history modes
1264  *--------------------------------------------------------------------------- */
1265 
1266 #define LATENCY_SAMPLE_RATE 10 /* milliseconds. */
1267 #define LATENCY_HISTORY_DEFAULT_INTERVAL 15000 /* milliseconds. */
1268 static void latencyMode(void) {
1269     redisReply *reply;
1270     long long start, latency, min = 0, max = 0, tot = 0, count = 0;
1271     long long history_interval =
1272         config.interval ? config.interval/1000 :
1273                           LATENCY_HISTORY_DEFAULT_INTERVAL;
1274     double avg;
1275     long long history_start = mstime();
1276 
1277     if (!context) exit(1);
1278     while(1) {
1279         start = mstime();
1280         reply = reconnectingRedisCommand(context,"PING");
1281         if (reply == NULL) {
1282             fprintf(stderr,"\nI/O error\n");
1283             exit(1);
1284         }
1285         latency = mstime()-start;
1286         freeReplyObject(reply);
1287         count++;
1288         if (count == 1) {
1289             min = max = tot = latency;
1290             avg = (double) latency;
1291         } else {
1292             if (latency < min) min = latency;
1293             if (latency > max) max = latency;
1294             tot += latency;
1295             avg = (double) tot/count;
1296         }
1297         printf("\x1b[0G\x1b[2Kmin: %lld, max: %lld, avg: %.2f (%lld samples)",
1298             min, max, avg, count);
1299         fflush(stdout);
1300         if (config.latency_history && mstime()-history_start > history_interval)
1301         {
1302             printf(" -- %.2f seconds range\n", (float)(mstime()-history_start)/1000);
1303             history_start = mstime();
1304             min = max = tot = count = 0;
1305         }
1306         usleep(LATENCY_SAMPLE_RATE * 1000);
1307     }
1308 }
1309 
1310 /*------------------------------------------------------------------------------
1311  * Latency distribution mode -- requires 256 colors xterm
1312  *--------------------------------------------------------------------------- */
1313 
1314 #define LATENCY_DIST_DEFAULT_INTERVAL 1000 /* milliseconds. */
1315 
1316 /* Structure to store samples distribution. */
1317 struct distsamples {
1318     long long max;   /* Max latency to fit into this interval (usec). */
1319     long long count; /* Number of samples in this interval. */
1320     int character;   /* Associated character in visualization. */
1321 };
1322 
1323 /* Helper function for latencyDistMode(). Performs the spectrum visualization
1324  * of the collected samples targeting an xterm 256 terminal.
1325  *
1326  * Takes an array of distsamples structures, ordered from smaller to bigger
1327  * 'max' value. Last sample max must be 0, to mean that it olds all the
1328  * samples greater than the previous one, and is also the stop sentinel.
1329  *
1330  * "tot' is the total number of samples in the different buckets, so it
1331  * is the SUM(samples[i].conut) for i to 0 up to the max sample.
1332  *
1333  * As a side effect the function sets all the buckets count to 0. */
1334 void showLatencyDistSamples(struct distsamples *samples, long long tot) {
1335     int j;
1336 
1337      /* We convert samples into a index inside the palette
1338      * proportional to the percentage a given bucket represents.
1339      * This way intensity of the different parts of the spectrum
1340      * don't change relative to the number of requests, which avoids to
1341      * pollute the visualization with non-latency related info. */
1342     printf("\033[38;5;0m"); /* Set foreground color to black. */
1343     for (j = 0; ; j++) {
1344         int coloridx =
1345             ceil((float) samples[j].count / tot * (spectrum_palette_size-1));
1346         int color = spectrum_palette[coloridx];
1347         printf("\033[48;5;%dm%c", (int)color, samples[j].character);
1348         samples[j].count = 0;
1349         if (samples[j].max == 0) break; /* Last sample. */
1350     }
1351     printf("\033[0m\n");
1352     fflush(stdout);
1353 }
1354 
1355 /* Show the legend: different buckets values and colors meaning, so
1356  * that the spectrum is more easily readable. */
1357 void showLatencyDistLegend(void) {
1358     int j;
1359 
1360     printf("---------------------------------------------\n");
1361     printf(". - * #          .01 .125 .25 .5 milliseconds\n");
1362     printf("1,2,3,...,9      from 1 to 9     milliseconds\n");
1363     printf("A,B,C,D,E        10,20,30,40,50  milliseconds\n");
1364     printf("F,G,H,I,J        .1,.2,.3,.4,.5       seconds\n");
1365     printf("K,L,M,N,O,P,Q,?  1,2,4,8,16,30,60,>60 seconds\n");
1366     printf("From 0 to 100%%: ");
1367     for (j = 0; j < spectrum_palette_size; j++) {
1368         printf("\033[48;5;%dm ", spectrum_palette[j]);
1369     }
1370     printf("\033[0m\n");
1371     printf("---------------------------------------------\n");
1372 }
1373 
1374 static void latencyDistMode(void) {
1375     redisReply *reply;
1376     long long start, latency, count = 0;
1377     long long history_interval =
1378         config.interval ? config.interval/1000 :
1379                           LATENCY_DIST_DEFAULT_INTERVAL;
1380     long long history_start = ustime();
1381     int j, outputs = 0;
1382 
1383     struct distsamples samples[] = {
1384         /* We use a mostly logarithmic scale, with certain linear intervals
1385          * which are more interesting than others, like 1-10 milliseconds
1386          * range. */
1387         {10,0,'.'},         /* 0.01 ms */
1388         {125,0,'-'},        /* 0.125 ms */
1389         {250,0,'*'},        /* 0.25 ms */
1390         {500,0,'#'},        /* 0.5 ms */
1391         {1000,0,'1'},       /* 1 ms */
1392         {2000,0,'2'},       /* 2 ms */
1393         {3000,0,'3'},       /* 3 ms */
1394         {4000,0,'4'},       /* 4 ms */
1395         {5000,0,'5'},       /* 5 ms */
1396         {6000,0,'6'},       /* 6 ms */
1397         {7000,0,'7'},       /* 7 ms */
1398         {8000,0,'8'},       /* 8 ms */
1399         {9000,0,'9'},       /* 9 ms */
1400         {10000,0,'A'},      /* 10 ms */
1401         {20000,0,'B'},      /* 20 ms */
1402         {30000,0,'C'},      /* 30 ms */
1403         {40000,0,'D'},      /* 40 ms */
1404         {50000,0,'E'},      /* 50 ms */
1405         {100000,0,'F'},     /* 0.1 s */
1406         {200000,0,'G'},     /* 0.2 s */
1407         {300000,0,'H'},     /* 0.3 s */
1408         {400000,0,'I'},     /* 0.4 s */
1409         {500000,0,'J'},     /* 0.5 s */
1410         {1000000,0,'K'},    /* 1 s */
1411         {2000000,0,'L'},    /* 2 s */
1412         {4000000,0,'M'},    /* 4 s */
1413         {8000000,0,'N'},    /* 8 s */
1414         {16000000,0,'O'},   /* 16 s */
1415         {30000000,0,'P'},   /* 30 s */
1416         {60000000,0,'Q'},   /* 1 minute */
1417         {0,0,'?'},          /* > 1 minute */
1418     };
1419 
1420     if (!context) exit(1);
1421     while(1) {
1422         start = ustime();
1423         reply = reconnectingRedisCommand(context,"PING");
1424         if (reply == NULL) {
1425             fprintf(stderr,"\nI/O error\n");
1426             exit(1);
1427         }
1428         latency = ustime()-start;
1429         freeReplyObject(reply);
1430         count++;
1431 
1432         /* Populate the relevant bucket. */
1433         for (j = 0; ; j++) {
1434             if (samples[j].max == 0 || latency <= samples[j].max) {
1435                 samples[j].count++;
1436                 break;
1437             }
1438         }
1439 
1440         /* From time to time show the spectrum. */
1441         if (count && (ustime()-history_start)/1000 > history_interval) {
1442             if ((outputs++ % 20) == 0)
1443                 showLatencyDistLegend();
1444             showLatencyDistSamples(samples,count);
1445             history_start = ustime();
1446             count = 0;
1447         }
1448         usleep(LATENCY_SAMPLE_RATE * 1000);
1449     }
1450 }
1451 
1452 /*------------------------------------------------------------------------------
1453  * Slave mode
1454  *--------------------------------------------------------------------------- */
1455 
1456 /* Sends SYNC and reads the number of bytes in the payload. Used both by
1457  * slaveMode() and getRDB(). */
1458 unsigned long long sendSync(int fd) {
1459     /* To start we need to send the SYNC command and return the payload.
1460      * The hiredis client lib does not understand this part of the protocol
1461      * and we don't want to mess with its buffers, so everything is performed
1462      * using direct low-level I/O. */
1463     char buf[4096], *p;
1464     ssize_t nread;
1465 
1466     /* Send the SYNC command. */
1467     if (write(fd,"SYNC\r\n",6) != 6) {
1468         fprintf(stderr,"Error writing to master\n");
1469         exit(1);
1470     }
1471 
1472     /* Read $<payload>\r\n, making sure to read just up to "\n" */
1473     p = buf;
1474     while(1) {
1475         nread = read(fd,p,1);
1476         if (nread <= 0) {
1477             fprintf(stderr,"Error reading bulk length while SYNCing\n");
1478             exit(1);
1479         }
1480         if (*p == '\n' && p != buf) break;
1481         if (*p != '\n') p++;
1482     }
1483     *p = '\0';
1484     if (buf[0] == '-') {
1485         printf("SYNC with master failed: %s\n", buf);
1486         exit(1);
1487     }
1488     return strtoull(buf+1,NULL,10);
1489 }
1490 
1491 static void slaveMode(void) {
1492     int fd = context->fd;
1493     unsigned long long payload = sendSync(fd);
1494     char buf[1024];
1495     int original_output = config.output;
1496 
1497     fprintf(stderr,"SYNC with master, discarding %llu "
1498                    "bytes of bulk transfer...\n", payload);
1499 
1500     /* Discard the payload. */
1501     while(payload) {
1502         ssize_t nread;
1503 
1504         nread = read(fd,buf,(payload > sizeof(buf)) ? sizeof(buf) : payload);
1505         if (nread <= 0) {
1506             fprintf(stderr,"Error reading RDB payload while SYNCing\n");
1507             exit(1);
1508         }
1509         payload -= nread;
1510     }
1511     fprintf(stderr,"SYNC done. Logging commands from master.\n");
1512 
1513     /* Now we can use hiredis to read the incoming protocol. */
1514     config.output = OUTPUT_CSV;
1515     while (cliReadReply(0) == REDIS_OK);
1516     config.output = original_output;
1517 }
1518 
1519 /*------------------------------------------------------------------------------
1520  * RDB transfer mode
1521  *--------------------------------------------------------------------------- */
1522 
1523 /* This function implements --rdb, so it uses the replication protocol in order
1524  * to fetch the RDB file from a remote server. */
1525 static void getRDB(void) {
1526     int s = context->fd;
1527     int fd;
1528     unsigned long long payload = sendSync(s);
1529     char buf[4096];
1530 
1531     fprintf(stderr,"SYNC sent to master, writing %llu bytes to '%s'\n",
1532         payload, config.rdb_filename);
1533 
1534     /* Write to file. */
1535     if (!strcmp(config.rdb_filename,"-")) {
1536         fd = STDOUT_FILENO;
1537     } else {
1538         fd = open(config.rdb_filename, O_CREAT|O_WRONLY, 0644);
1539         if (fd == -1) {
1540             fprintf(stderr, "Error opening '%s': %s\n", config.rdb_filename,
1541                 strerror(errno));
1542             exit(1);
1543         }
1544     }
1545 
1546     while(payload) {
1547         ssize_t nread, nwritten;
1548 
1549         nread = read(s,buf,(payload > sizeof(buf)) ? sizeof(buf) : payload);
1550         if (nread <= 0) {
1551             fprintf(stderr,"I/O Error reading RDB payload from socket\n");
1552             exit(1);
1553         }
1554         nwritten = write(fd, buf, nread);
1555         if (nwritten != nread) {
1556             fprintf(stderr,"Error writing data to file: %s\n",
1557                 strerror(errno));
1558             exit(1);
1559         }
1560         payload -= nread;
1561     }
1562     close(s); /* Close the file descriptor ASAP as fsync() may take time. */
1563     fsync(fd);
1564     fprintf(stderr,"Transfer finished with success.\n");
1565     exit(0);
1566 }
1567 
1568 /*------------------------------------------------------------------------------
1569  * Bulk import (pipe) mode
1570  *--------------------------------------------------------------------------- */
1571 
1572 #define PIPEMODE_WRITE_LOOP_MAX_BYTES (128*1024)
1573 static void pipeMode(void) {
1574     int fd = context->fd;
1575     long long errors = 0, replies = 0, obuf_len = 0, obuf_pos = 0;
1576     char ibuf[1024*16], obuf[1024*16]; /* Input and output buffers */
1577     char aneterr[ANET_ERR_LEN];
1578     redisReader *reader = redisReaderCreate();
1579     redisReply *reply;
1580     int eof = 0; /* True once we consumed all the standard input. */
1581     int done = 0;
1582     char magic[20]; /* Special reply we recognize. */
1583     time_t last_read_time = time(NULL);
1584 
1585     srand(time(NULL));
1586 
1587     /* Use non blocking I/O. */
1588     if (anetNonBlock(aneterr,fd) == ANET_ERR) {
1589         fprintf(stderr, "Can't set the socket in non blocking mode: %s\n",
1590             aneterr);
1591         exit(1);
1592     }
1593 
1594     /* Transfer raw protocol and read replies from the server at the same
1595      * time. */
1596     while(!done) {
1597         int mask = AE_READABLE;
1598 
1599         if (!eof || obuf_len != 0) mask |= AE_WRITABLE;
1600         mask = aeWait(fd,mask,1000);
1601 
1602         /* Handle the readable state: we can read replies from the server. */
1603         if (mask & AE_READABLE) {
1604             ssize_t nread;
1605 
1606             /* Read from socket and feed the hiredis reader. */
1607             do {
1608                 nread = read(fd,ibuf,sizeof(ibuf));
1609                 if (nread == -1 && errno != EAGAIN && errno != EINTR) {
1610                     fprintf(stderr, "Error reading from the server: %s\n",
1611                         strerror(errno));
1612                     exit(1);
1613                 }
1614                 if (nread > 0) {
1615                     redisReaderFeed(reader,ibuf,nread);
1616                     last_read_time = time(NULL);
1617                 }
1618             } while(nread > 0);
1619 
1620             /* Consume replies. */
1621             do {
1622                 if (redisReaderGetReply(reader,(void**)&reply) == REDIS_ERR) {
1623                     fprintf(stderr, "Error reading replies from server\n");
1624                     exit(1);
1625                 }
1626                 if (reply) {
1627                     if (reply->type == REDIS_REPLY_ERROR) {
1628                         fprintf(stderr,"%s\n", reply->str);
1629                         errors++;
1630                     } else if (eof && reply->type == REDIS_REPLY_STRING &&
1631                                       reply->len == 20) {
1632                         /* Check if this is the reply to our final ECHO
1633                          * command. If so everything was received
1634                          * from the server. */
1635                         if (memcmp(reply->str,magic,20) == 0) {
1636                             printf("Last reply received from server.\n");
1637                             done = 1;
1638                             replies--;
1639                         }
1640                     }
1641                     replies++;
1642                     freeReplyObject(reply);
1643                 }
1644             } while(reply);
1645         }
1646 
1647         /* Handle the writable state: we can send protocol to the server. */
1648         if (mask & AE_WRITABLE) {
1649             ssize_t loop_nwritten = 0;
1650 
1651             while(1) {
1652                 /* Transfer current buffer to server. */
1653                 if (obuf_len != 0) {
1654                     ssize_t nwritten = write(fd,obuf+obuf_pos,obuf_len);
1655 
1656                     if (nwritten == -1) {
1657                         if (errno != EAGAIN && errno != EINTR) {
1658                             fprintf(stderr, "Error writing to the server: %s\n",
1659                                 strerror(errno));
1660                             exit(1);
1661                         } else {
1662                             nwritten = 0;
1663                         }
1664                     }
1665                     obuf_len -= nwritten;
1666                     obuf_pos += nwritten;
1667                     loop_nwritten += nwritten;
1668                     if (obuf_len != 0) break; /* Can't accept more data. */
1669                 }
1670                 /* If buffer is empty, load from stdin. */
1671                 if (obuf_len == 0 && !eof) {
1672                     ssize_t nread = read(STDIN_FILENO,obuf,sizeof(obuf));
1673 
1674                     if (nread == 0) {
1675                         /* The ECHO sequence starts with a "\r\n" so that if there
1676                          * is garbage in the protocol we read from stdin, the ECHO
1677                          * will likely still be properly formatted.
1678                          * CRLF is ignored by Redis, so it has no effects. */
1679                         char echo[] =
1680                         "\r\n*2\r\n$4\r\nECHO\r\n$20\r\n01234567890123456789\r\n";
1681                         int j;
1682 
1683                         eof = 1;
1684                         /* Everything transferred, so we queue a special
1685                          * ECHO command that we can match in the replies
1686                          * to make sure everything was read from the server. */
1687                         for (j = 0; j < 20; j++)
1688                             magic[j] = rand() & 0xff;
1689                         memcpy(echo+21,magic,20);
1690                         memcpy(obuf,echo,sizeof(echo)-1);
1691                         obuf_len = sizeof(echo)-1;
1692                         obuf_pos = 0;
1693                         printf("All data transferred. Waiting for the last reply...\n");
1694                     } else if (nread == -1) {
1695                         fprintf(stderr, "Error reading from stdin: %s\n",
1696                             strerror(errno));
1697                         exit(1);
1698                     } else {
1699                         obuf_len = nread;
1700                         obuf_pos = 0;
1701                     }
1702                 }
1703                 if ((obuf_len == 0 && eof) ||
1704                     loop_nwritten > PIPEMODE_WRITE_LOOP_MAX_BYTES) break;
1705             }
1706         }
1707 
1708         /* Handle timeout, that is, we reached EOF, and we are not getting
1709          * replies from the server for a few seconds, nor the final ECHO is
1710          * received. */
1711         if (eof && config.pipe_timeout > 0 &&
1712             time(NULL)-last_read_time > config.pipe_timeout)
1713         {
1714             fprintf(stderr,"No replies for %d seconds: exiting.\n",
1715                 config.pipe_timeout);
1716             errors++;
1717             break;
1718         }
1719     }
1720     redisReaderFree(reader);
1721     printf("errors: %lld, replies: %lld\n", errors, replies);
1722     if (errors)
1723         exit(1);
1724     else
1725         exit(0);
1726 }
1727 
1728 /*------------------------------------------------------------------------------
1729  * Find big keys
1730  *--------------------------------------------------------------------------- */
1731 
1732 #define TYPE_STRING 0
1733 #define TYPE_LIST   1
1734 #define TYPE_SET    2
1735 #define TYPE_HASH   3
1736 #define TYPE_ZSET   4
1737 #define TYPE_NONE   5
1738 
1739 static redisReply *sendScan(unsigned long long *it) {
1740     redisReply *reply = redisCommand(context, "SCAN %llu", *it);
1741 
1742     /* Handle any error conditions */
1743     if(reply == NULL) {
1744         fprintf(stderr, "\nI/O error\n");
1745         exit(1);
1746     } else if(reply->type == REDIS_REPLY_ERROR) {
1747         fprintf(stderr, "SCAN error: %s\n", reply->str);
1748         exit(1);
1749     } else if(reply->type != REDIS_REPLY_ARRAY) {
1750         fprintf(stderr, "Non ARRAY response from SCAN!\n");
1751         exit(1);
1752     } else if(reply->elements != 2) {
1753         fprintf(stderr, "Invalid element count from SCAN!\n");
1754         exit(1);
1755     }
1756 
1757     /* Validate our types are correct */
1758     assert(reply->element[0]->type == REDIS_REPLY_STRING);
1759     assert(reply->element[1]->type == REDIS_REPLY_ARRAY);
1760 
1761     /* Update iterator */
1762     *it = strtoull(reply->element[0]->str, NULL, 10);
1763 
1764     return reply;
1765 }
1766 
1767 static int getDbSize(void) {
1768     redisReply *reply;
1769     int size;
1770 
1771     reply = redisCommand(context, "DBSIZE");
1772 
1773     if(reply == NULL || reply->type != REDIS_REPLY_INTEGER) {
1774         fprintf(stderr, "Couldn't determine DBSIZE!\n");
1775         exit(1);
1776     }
1777 
1778     /* Grab the number of keys and free our reply */
1779     size = reply->integer;
1780     freeReplyObject(reply);
1781 
1782     return size;
1783 }
1784 
1785 static int toIntType(char *key, char *type) {
1786     if(!strcmp(type, "string")) {
1787         return TYPE_STRING;
1788     } else if(!strcmp(type, "list")) {
1789         return TYPE_LIST;
1790     } else if(!strcmp(type, "set")) {
1791         return TYPE_SET;
1792     } else if(!strcmp(type, "hash")) {
1793         return TYPE_HASH;
1794     } else if(!strcmp(type, "zset")) {
1795         return TYPE_ZSET;
1796     } else if(!strcmp(type, "none")) {
1797         return TYPE_NONE;
1798     } else {
1799         fprintf(stderr, "Unknown type '%s' for key '%s'\n", type, key);
1800         exit(1);
1801     }
1802 }
1803 
1804 static void getKeyTypes(redisReply *keys, int *types) {
1805     redisReply *reply;
1806     unsigned int i;
1807 
1808     /* Pipeline TYPE commands */
1809     for(i=0;i<keys->elements;i++) {
1810         redisAppendCommand(context, "TYPE %s", keys->element[i]->str);
1811     }
1812 
1813     /* Retrieve types */
1814     for(i=0;i<keys->elements;i++) {
1815         if(redisGetReply(context, (void**)&reply)!=REDIS_OK) {
1816             fprintf(stderr, "Error getting type for key '%s' (%d: %s)\n",
1817                 keys->element[i]->str, context->err, context->errstr);
1818             exit(1);
1819         } else if(reply->type != REDIS_REPLY_STATUS) {
1820             fprintf(stderr, "Invalid reply type (%d) for TYPE on key '%s'!\n",
1821                 reply->type, keys->element[i]->str);
1822             exit(1);
1823         }
1824 
1825         types[i] = toIntType(keys->element[i]->str, reply->str);
1826         freeReplyObject(reply);
1827     }
1828 }
1829 
1830 static void getKeySizes(redisReply *keys, int *types,
1831                         unsigned long long *sizes)
1832 {
1833     redisReply *reply;
1834     char *sizecmds[] = {"STRLEN","LLEN","SCARD","HLEN","ZCARD"};
1835     unsigned int i;
1836 
1837     /* Pipeline size commands */
1838     for(i=0;i<keys->elements;i++) {
1839         /* Skip keys that were deleted */
1840         if(types[i]==TYPE_NONE)
1841             continue;
1842 
1843         redisAppendCommand(context, "%s %s", sizecmds[types[i]],
1844             keys->element[i]->str);
1845     }
1846 
1847     /* Retreive sizes */
1848     for(i=0;i<keys->elements;i++) {
1849         /* Skip keys that dissapeared between SCAN and TYPE */
1850         if(types[i] == TYPE_NONE) {
1851             sizes[i] = 0;
1852             continue;
1853         }
1854 
1855         /* Retreive size */
1856         if(redisGetReply(context, (void**)&reply)!=REDIS_OK) {
1857             fprintf(stderr, "Error getting size for key '%s' (%d: %s)\n",
1858                 keys->element[i]->str, context->err, context->errstr);
1859             exit(1);
1860         } else if(reply->type != REDIS_REPLY_INTEGER) {
1861             /* Theoretically the key could have been removed and
1862              * added as a different type between TYPE and SIZE */
1863             fprintf(stderr,
1864                 "Warning:  %s on '%s' failed (may have changed type)\n",
1865                  sizecmds[types[i]], keys->element[i]->str);
1866             sizes[i] = 0;
1867         } else {
1868             sizes[i] = reply->integer;
1869         }
1870 
1871         freeReplyObject(reply);
1872     }
1873 }
1874 
1875 static void findBigKeys(void) {
1876     unsigned long long biggest[5] = {0}, counts[5] = {0}, totalsize[5] = {0};
1877     unsigned long long sampled = 0, total_keys, totlen=0, *sizes=NULL, it=0;
1878     sds maxkeys[5] = {0};
1879     char *typename[] = {"string","list","set","hash","zset"};
1880     char *typeunit[] = {"bytes","items","members","fields","members"};
1881     redisReply *reply, *keys;
1882     unsigned int arrsize=0, i;
1883     int type, *types=NULL;
1884     double pct;
1885 
1886     /* Total keys pre scanning */
1887     total_keys = getDbSize();
1888 
1889     /* Status message */
1890     printf("\n# Scanning the entire keyspace to find biggest keys as well as\n");
1891     printf("# average sizes per key type.  You can use -i 0.1 to sleep 0.1 sec\n");
1892     printf("# per 100 SCAN commands (not usually needed).\n\n");
1893 
1894     /* New up sds strings to keep track of overall biggest per type */
1895     for(i=0;i<TYPE_NONE; i++) {
1896         maxkeys[i] = sdsempty();
1897         if(!maxkeys[i]) {
1898             fprintf(stderr, "Failed to allocate memory for largest key names!\n");
1899             exit(1);
1900         }
1901     }
1902 
1903     /* SCAN loop */
1904     do {
1905         /* Calculate approximate percentage completion */
1906         pct = 100 * (double)sampled/total_keys;
1907 
1908         /* Grab some keys and point to the keys array */
1909         reply = sendScan(&it);
1910         keys  = reply->element[1];
1911 
1912         /* Reallocate our type and size array if we need to */
1913         if(keys->elements > arrsize) {
1914             types = zrealloc(types, sizeof(int)*keys->elements);
1915             sizes = zrealloc(sizes, sizeof(unsigned long long)*keys->elements);
1916 
1917             if(!types || !sizes) {
1918                 fprintf(stderr, "Failed to allocate storage for keys!\n");
1919                 exit(1);
1920             }
1921 
1922             arrsize = keys->elements;
1923         }
1924 
1925         /* Retreive types and then sizes */
1926         getKeyTypes(keys, types);
1927         getKeySizes(keys, types, sizes);
1928 
1929         /* Now update our stats */
1930         for(i=0;i<keys->elements;i++) {
1931             if((type = types[i]) == TYPE_NONE)
1932                 continue;
1933 
1934             totalsize[type] += sizes[i];
1935             counts[type]++;
1936             totlen += keys->element[i]->len;
1937             sampled++;
1938 
1939             if(biggest[type]<sizes[i]) {
1940                 printf(
1941                    "[%05.2f%%] Biggest %-6s found so far '%s' with %llu %s\n",
1942                    pct, typename[type], keys->element[i]->str, sizes[i],
1943                    typeunit[type]);
1944 
1945                 /* Keep track of biggest key name for this type */
1946                 maxkeys[type] = sdscpy(maxkeys[type], keys->element[i]->str);
1947                 if(!maxkeys[type]) {
1948                     fprintf(stderr, "Failed to allocate memory for key!\n");
1949                     exit(1);
1950                 }
1951 
1952                 /* Keep track of the biggest size for this type */
1953                 biggest[type] = sizes[i];
1954             }
1955 
1956             /* Update overall progress */
1957             if(sampled % 1000000 == 0) {
1958                 printf("[%05.2f%%] Sampled %llu keys so far\n", pct, sampled);
1959             }
1960         }
1961 
1962         /* Sleep if we've been directed to do so */
1963         if(sampled && (sampled %100) == 0 && config.interval) {
1964             usleep(config.interval);
1965         }
1966 
1967         freeReplyObject(reply);
1968     } while(it != 0);
1969 
1970     if(types) zfree(types);
1971     if(sizes) zfree(sizes);
1972 
1973     /* We're done */
1974     printf("\n-------- summary -------\n\n");
1975 
1976     printf("Sampled %llu keys in the keyspace!\n", sampled);
1977     printf("Total key length in bytes is %llu (avg len %.2f)\n\n",
1978        totlen, totlen ? (double)totlen/sampled : 0);
1979 
1980     /* Output the biggest keys we found, for types we did find */
1981     for(i=0;i<TYPE_NONE;i++) {
1982         if(sdslen(maxkeys[i])>0) {
1983             printf("Biggest %6s found '%s' has %llu %s\n", typename[i], maxkeys[i],
1984                biggest[i], typeunit[i]);
1985         }
1986     }
1987 
1988     printf("\n");
1989 
1990     for(i=0;i<TYPE_NONE;i++) {
1991         printf("%llu %ss with %llu %s (%05.2f%% of keys, avg size %.2f)\n",
1992            counts[i], typename[i], totalsize[i], typeunit[i],
1993            sampled ? 100 * (double)counts[i]/sampled : 0,
1994            counts[i] ? (double)totalsize[i]/counts[i] : 0);
1995     }
1996 
1997     /* Free sds strings containing max keys */
1998     for(i=0;i<TYPE_NONE;i++) {
1999         sdsfree(maxkeys[i]);
2000     }
2001 
2002     /* Success! */
2003     exit(0);
2004 }
2005 
2006 /*------------------------------------------------------------------------------
2007  * Stats mode
2008  *--------------------------------------------------------------------------- */
2009 
2010 /* Return the specified INFO field from the INFO command output "info".
2011  * A new buffer is allocated for the result, that needs to be free'd.
2012  * If the field is not found NULL is returned. */
2013 static char *getInfoField(char *info, char *field) {
2014     char *p = strstr(info,field);
2015     char *n1, *n2;
2016     char *result;
2017 
2018     if (!p) return NULL;
2019     p += strlen(field)+1;
2020     n1 = strchr(p,'\r');
2021     n2 = strchr(p,',');
2022     if (n2 && n2 < n1) n1 = n2;
2023     result = malloc(sizeof(char)*(n1-p)+1);
2024     memcpy(result,p,(n1-p));
2025     result[n1-p] = '\0';
2026     return result;
2027 }
2028 
2029 /* Like the above function but automatically convert the result into
2030  * a long. On error (missing field) LONG_MIN is returned. */
2031 static long getLongInfoField(char *info, char *field) {
2032     char *value = getInfoField(info,field);
2033     long l;
2034 
2035     if (!value) return LONG_MIN;
2036     l = strtol(value,NULL,10);
2037     free(value);
2038     return l;
2039 }
2040 
2041 /* Convert number of bytes into a human readable string of the form:
2042  * 100B, 2G, 100M, 4K, and so forth. */
2043 void bytesToHuman(char *s, long long n) {
2044     double d;
2045 
2046     if (n < 0) {
2047         *s = '-';
2048         s++;
2049         n = -n;
2050     }
2051     if (n < 1024) {
2052         /* Bytes */
2053         sprintf(s,"%lldB",n);
2054         return;
2055     } else if (n < (1024*1024)) {
2056         d = (double)n/(1024);
2057         sprintf(s,"%.2fK",d);
2058     } else if (n < (1024LL*1024*1024)) {
2059         d = (double)n/(1024*1024);
2060         sprintf(s,"%.2fM",d);
2061     } else if (n < (1024LL*1024*1024*1024)) {
2062         d = (double)n/(1024LL*1024*1024);
2063         sprintf(s,"%.2fG",d);
2064     }
2065 }
2066 
2067 static void statMode(void) {
2068     redisReply *reply;
2069     long aux, requests = 0;
2070     int i = 0;
2071 
2072     while(1) {
2073         char buf[64];
2074         int j;
2075 
2076         reply = reconnectingRedisCommand(context,"INFO");
2077         if (reply->type == REDIS_REPLY_ERROR) {
2078             printf("ERROR: %s\n", reply->str);
2079             exit(1);
2080         }
2081 
2082         if ((i++ % 20) == 0) {
2083             printf(
2084 "------- data ------ --------------------- load -------------------- - child -\n"
2085 "keys       mem      clients blocked requests            connections          \n");
2086         }
2087 
2088         /* Keys */
2089         aux = 0;
2090         for (j = 0; j < 20; j++) {
2091             long k;
2092 
2093             sprintf(buf,"db%d:keys",j);
2094             k = getLongInfoField(reply->str,buf);
2095             if (k == LONG_MIN) continue;
2096             aux += k;
2097         }
2098         sprintf(buf,"%ld",aux);
2099         printf("%-11s",buf);
2100 
2101         /* Used memory */
2102         aux = getLongInfoField(reply->str,"used_memory");
2103         bytesToHuman(buf,aux);
2104         printf("%-8s",buf);
2105 
2106         /* Clients */
2107         aux = getLongInfoField(reply->str,"connected_clients");
2108         sprintf(buf,"%ld",aux);
2109         printf(" %-8s",buf);
2110 
2111         /* Blocked (BLPOPPING) Clients */
2112         aux = getLongInfoField(reply->str,"blocked_clients");
2113         sprintf(buf,"%ld",aux);
2114         printf("%-8s",buf);
2115 
2116         /* Requets */
2117         aux = getLongInfoField(reply->str,"total_commands_processed");
2118         sprintf(buf,"%ld (+%ld)",aux,requests == 0 ? 0 : aux-requests);
2119         printf("%-19s",buf);
2120         requests = aux;
2121 
2122         /* Connections */
2123         aux = getLongInfoField(reply->str,"total_connections_received");
2124         sprintf(buf,"%ld",aux);
2125         printf(" %-12s",buf);
2126 
2127         /* Children */
2128         aux = getLongInfoField(reply->str,"bgsave_in_progress");
2129         aux |= getLongInfoField(reply->str,"aof_rewrite_in_progress") << 1;
2130         aux |= getLongInfoField(reply->str,"loading") << 2;
2131         switch(aux) {
2132         case 0: break;
2133         case 1:
2134             printf("SAVE");
2135             break;
2136         case 2:
2137             printf("AOF");
2138             break;
2139         case 3:
2140             printf("SAVE+AOF");
2141             break;
2142         case 4:
2143             printf("LOAD");
2144             break;
2145         }
2146 
2147         printf("\n");
2148         freeReplyObject(reply);
2149         usleep(config.interval);
2150     }
2151 }
2152 
2153 /*------------------------------------------------------------------------------
2154  * Scan mode
2155  *--------------------------------------------------------------------------- */
2156 
2157 static void scanMode(void) {
2158     redisReply *reply;
2159     unsigned long long cur = 0;
2160 
2161     do {
2162         if (config.pattern)
2163             reply = redisCommand(context,"SCAN %llu MATCH %s",
2164                 cur,config.pattern);
2165         else
2166             reply = redisCommand(context,"SCAN %llu",cur);
2167         if (reply == NULL) {
2168             printf("I/O error\n");
2169             exit(1);
2170         } else if (reply->type == REDIS_REPLY_ERROR) {
2171             printf("ERROR: %s\n", reply->str);
2172             exit(1);
2173         } else {
2174             unsigned int j;
2175 
2176             cur = strtoull(reply->element[0]->str,NULL,10);
2177             for (j = 0; j < reply->element[1]->elements; j++)
2178                 printf("%s\n", reply->element[1]->element[j]->str);
2179         }
2180         freeReplyObject(reply);
2181     } while(cur != 0);
2182 
2183     exit(0);
2184 }
2185 
2186 /*------------------------------------------------------------------------------
2187  * LRU test mode
2188  *--------------------------------------------------------------------------- */
2189 
2190 /* Return an integer from min to max (both inclusive) using a power-law
2191  * distribution, depending on the value of alpha: the greater the alpha
2192  * the more bias towards lower values.
2193  *
2194  * With alpha = 6.2 the output follows the 80-20 rule where 20% of
2195  * the returned numbers will account for 80% of the frequency. */
2196 long long powerLawRand(long long min, long long max, double alpha) {
2197     double pl, r;
2198 
2199     max += 1;
2200     r = ((double)rand()) / RAND_MAX;
2201     pl = pow(
2202         ((pow(max,alpha+1) - pow(min,alpha+1))*r + pow(min,alpha+1)),
2203         (1.0/(alpha+1)));
2204     return (max-1-(long long)pl)+min;
2205 }
2206 
2207 /* Generates a key name among a set of lru_test_sample_size keys, using
2208  * an 80-20 distribution. */
2209 void LRUTestGenKey(char *buf, size_t buflen) {
2210     snprintf(buf, buflen, "lru:%lld\n",
2211         powerLawRand(1, config.lru_test_sample_size, 6.2));
2212 }
2213 
2214 #define LRU_CYCLE_PERIOD 1000 /* 1000 milliseconds. */
2215 #define LRU_CYCLE_PIPELINE_SIZE 250
2216 static void LRUTestMode(void) {
2217     redisReply *reply;
2218     char key[128];
2219     long long start_cycle;
2220     int j;
2221 
2222     srand(time(NULL)^getpid());
2223     while(1) {
2224         /* Perform cycles of 1 second with 50% writes and 50% reads.
2225          * We use pipelining batching writes / reads N times per cycle in order
2226          * to fill the target instance easily. */
2227         start_cycle = mstime();
2228         long long hits = 0, misses = 0;
2229         while(mstime() - start_cycle < 1000) {
2230             /* Write cycle. */
2231             for (j = 0; j < LRU_CYCLE_PIPELINE_SIZE; j++) {
2232                 LRUTestGenKey(key,sizeof(key));
2233                 redisAppendCommand(context, "SET %s val",key);
2234             }
2235             for (j = 0; j < LRU_CYCLE_PIPELINE_SIZE; j++)
2236                 redisGetReply(context, (void**)&reply);
2237 
2238             /* Read cycle. */
2239             for (j = 0; j < LRU_CYCLE_PIPELINE_SIZE; j++) {
2240                 LRUTestGenKey(key,sizeof(key));
2241                 redisAppendCommand(context, "GET %s",key);
2242             }
2243             for (j = 0; j < LRU_CYCLE_PIPELINE_SIZE; j++) {
2244                 if (redisGetReply(context, (void**)&reply) == REDIS_OK) {
2245                     switch(reply->type) {
2246                         case REDIS_REPLY_ERROR:
2247                             printf("%s\n", reply->str);
2248                             break;
2249                         case REDIS_REPLY_NIL:
2250                             misses++;
2251                             break;
2252                         default:
2253                             hits++;
2254                             break;
2255                     }
2256                 }
2257             }
2258 
2259             if (context->err) {
2260                 fprintf(stderr,"I/O error during LRU test\n");
2261                 exit(1);
2262             }
2263         }
2264         /* Print stats. */
2265         printf(
2266             "%lld Gets/sec | Hits: %lld (%.2f%%) | Misses: %lld (%.2f%%)\n",
2267             hits+misses,
2268             hits, (double)hits/(hits+misses)*100,
2269             misses, (double)misses/(hits+misses)*100);
2270     }
2271     exit(0);
2272 }
2273 
2274 /*------------------------------------------------------------------------------
2275  * Intrisic latency mode.
2276  *
2277  * Measure max latency of a running process that does not result from
2278  * syscalls. Basically this software should provide an hint about how much
2279  * time the kernel leaves the process without a chance to run.
2280  *--------------------------------------------------------------------------- */
2281 
2282 /* This is just some computation the compiler can't optimize out.
2283  * Should run in less than 100-200 microseconds even using very
2284  * slow hardware. Runs in less than 10 microseconds in modern HW. */
2285 unsigned long compute_something_fast(void) {
2286     unsigned char s[256], i, j, t;
2287     int count = 1000, k;
2288     unsigned long output = 0;
2289 
2290     for (k = 0; k < 256; k++) s[k] = k;
2291 
2292     i = 0;
2293     j = 0;
2294     while(count--) {
2295         i++;
2296         j = j + s[i];
2297         t = s[i];
2298         s[i] = s[j];
2299         s[j] = t;
2300         output += s[(s[i]+s[j])&255];
2301     }
2302     return output;
2303 }
2304 
2305 static void intrinsicLatencyModeStop(int s) {
2306     UNUSED(s);
2307     force_cancel_loop = 1;
2308 }
2309 
2310 static void intrinsicLatencyMode(void) {
2311     long long test_end, run_time, max_latency = 0, runs = 0;
2312 
2313     run_time = config.intrinsic_latency_duration*1000000;
2314     test_end = ustime() + run_time;
2315     signal(SIGINT, intrinsicLatencyModeStop);
2316 
2317     while(1) {
2318         long long start, end, latency;
2319 
2320         start = ustime();
2321         compute_something_fast();
2322         end = ustime();
2323         latency = end-start;
2324         runs++;
2325         if (latency <= 0) continue;
2326 
2327         /* Reporting */
2328         if (latency > max_latency) {
2329             max_latency = latency;
2330             printf("Max latency so far: %lld microseconds.\n", max_latency);
2331         }
2332 
2333         double avg_us = (double)run_time/runs;
2334         double avg_ns = avg_us * 10e3;
2335         if (force_cancel_loop || end > test_end) {
2336             printf("\n%lld total runs "
2337                 "(avg latency: "
2338                 "%.4f microseconds / %.2f nanoseconds per run).\n",
2339                 runs, avg_us, avg_ns);
2340             printf("Worst run took %.0fx longer than the average latency.\n",
2341                 max_latency / avg_us);
2342             exit(0);
2343         }
2344     }
2345 }
2346 
2347 /*------------------------------------------------------------------------------
2348  * Program main()
2349  *--------------------------------------------------------------------------- */
2350 
2351 int main(int argc, char **argv) {
2352     int firstarg;
2353 
2354     config.hostip = sdsnew("127.0.0.1");
2355     config.hostport = 6379;
2356     config.hostsocket = NULL;
2357     config.repeat = 1;
2358     config.interval = 0;
2359     config.dbnum = 0;
2360     config.interactive = 0;
2361     config.shutdown = 0;
2362     config.monitor_mode = 0;
2363     config.pubsub_mode = 0;
2364     config.latency_mode = 0;
2365     config.latency_dist_mode = 0;
2366     config.latency_history = 0;
2367     config.lru_test_mode = 0;
2368     config.lru_test_sample_size = 0;
2369     config.cluster_mode = 0;
2370     config.slave_mode = 0;
2371     config.getrdb_mode = 0;
2372     config.stat_mode = 0;
2373     config.scan_mode = 0;
2374     config.intrinsic_latency_mode = 0;
2375     config.pattern = NULL;
2376     config.rdb_filename = NULL;
2377     config.pipe_mode = 0;
2378     config.pipe_timeout = REDIS_CLI_DEFAULT_PIPE_TIMEOUT;
2379     config.bigkeys = 0;
2380     config.stdinarg = 0;
2381     config.auth = NULL;
2382     config.eval = NULL;
2383     config.eval_ldb = 0;
2384     config.eval_ldb_end = 0;
2385     config.eval_ldb_sync = 0;
2386     config.last_cmd_type = -1;
2387 
2388     spectrum_palette = spectrum_palette_color;
2389     spectrum_palette_size = spectrum_palette_color_size;
2390 
2391     if (!isatty(fileno(stdout)) && (getenv("FAKETTY") == NULL))
2392         config.output = OUTPUT_RAW;
2393     else
2394         config.output = OUTPUT_STANDARD;
2395     config.mb_delim = sdsnew("\n");
2396     cliInitHelp();
2397 
2398     firstarg = parseOptions(argc,argv);
2399     argc -= firstarg;
2400     argv += firstarg;
2401 
2402     /* Latency mode */
2403     if (config.latency_mode) {
2404         if (cliConnect(0) == REDIS_ERR) exit(1);
2405         latencyMode();
2406     }
2407 
2408     /* Latency distribution mode */
2409     if (config.latency_dist_mode) {
2410         if (cliConnect(0) == REDIS_ERR) exit(1);
2411         latencyDistMode();
2412     }
2413 
2414     /* Slave mode */
2415     if (config.slave_mode) {
2416         if (cliConnect(0) == REDIS_ERR) exit(1);
2417         slaveMode();
2418     }
2419 
2420     /* Get RDB mode. */
2421     if (config.getrdb_mode) {
2422         if (cliConnect(0) == REDIS_ERR) exit(1);
2423         getRDB();
2424     }
2425 
2426     /* Pipe mode */
2427     if (config.pipe_mode) {
2428         if (cliConnect(0) == REDIS_ERR) exit(1);
2429         pipeMode();
2430     }
2431 
2432     /* Find big keys */
2433     if (config.bigkeys) {
2434         if (cliConnect(0) == REDIS_ERR) exit(1);
2435         findBigKeys();
2436     }
2437 
2438     /* Stat mode */
2439     if (config.stat_mode) {
2440         if (cliConnect(0) == REDIS_ERR) exit(1);
2441         if (config.interval == 0) config.interval = 1000000;
2442         statMode();
2443     }
2444 
2445     /* Scan mode */
2446     if (config.scan_mode) {
2447         if (cliConnect(0) == REDIS_ERR) exit(1);
2448         scanMode();
2449     }
2450 
2451     /* LRU test mode */
2452     if (config.lru_test_mode) {
2453         if (cliConnect(0) == REDIS_ERR) exit(1);
2454         LRUTestMode();
2455     }
2456 
2457     /* Intrinsic latency mode */
2458     if (config.intrinsic_latency_mode) intrinsicLatencyMode();
2459 
2460     /* Start interactive mode when no command is provided */
2461     if (argc == 0 && !config.eval) {
2462         /* Ignore SIGPIPE in interactive mode to force a reconnect */
2463         signal(SIGPIPE, SIG_IGN);
2464 
2465         /* Note that in repl mode we don't abort on connection error.
2466          * A new attempt will be performed for every command send. */
2467         cliConnect(0);
2468         repl();
2469     }
2470 
2471     /* Otherwise, we have some arguments to execute */
2472     if (cliConnect(0) != REDIS_OK) exit(1);
2473     if (config.eval) {
2474         return evalMode(argc,argv);
2475     } else {
2476         return noninteractive(argc,convertToSds(argc,argv));
2477     }
2478 }
2479