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