redis源码笔记 - redis-cli.c

这份代码是redis的client接口,其和server端的交互使用了deps目录下的hiredis c库,同时,在这部分代码中,应用了linenoise库完成类似history命令查询、自动补全等终端控制功能。

  1 #include "fmacros.h"       //用于mac下的兼容性处理
  2 #include "version.h"       //版本信息头文件,当前版本是2.4.10
  3 
  4 #include <stdio.h>
  5 #include <string.h>
  6 #include <stdlib.h>
  7 #include <unistd.h>
  8 #include <ctype.h>
  9 #include <errno.h>
 10 #include <sys/stat.h>
 11 #include <sys/time.h>
 12 #include <assert.h>
 13 
 14 #include "hiredis.h"        //redis 客户端库的头文件
 15 #include "sds.h"
 16 #include "zmalloc.h"
 17 #include "linenoise.h"      //终端控制库的头文件
 18 #include "help.h"           //当前所有的命令文件汇总,用于tab自动补全功能的源数据
/* help entry的结构如下:

struct commandHelp {
20 char *name;          //命令名字
21 char *params;        //参数格式
22 char *summary;       //简单的解释信息
23 int group;           //命令类型(当前版本共分10种不同类型的命令)
24 char *since;         //从哪个版本开始支持此命令
25 } */

 19 
 20 #define REDIS_NOTUSED(V) ((void) V)
 21 
 22 static redisContext *context;            //维护client和server端的连接信息,包括文件描述符,错误信息等,参见deps/hiredis/hiredis.h
 23 static struct config {
 24     char *hostip;
 25     int hostport;
 26     char *hostsocket;
 27     long repeat;                         //命令重复执行次数
 28     long interval;                       //命令重复执行间隔
 29     int dbnum;                           // db no.
 30     int interactive;                     //交互模式 or 命令模式
 31     int shutdown;
 32     int monitor_mode;                    //监控模式
 33     int pubsub_mode;                     //pub sub模式
 34     int latency_mode;                    //该模式测试cli到server执行ping命令的时间间隔(应用层ping)
 35     int stdinarg; /* get last arg from stdin. (-x option) */
 36     char *auth;                          //需要鉴权时的密码信息
 37     int raw_output; /* output mode per command */      //选择该模式,将不会添加类似(interger),参见http://blog.sina.com.cn/s/blog_6262a50e0100zw83.html
 38     sds mb_delim;
 39     char prompt[128];           
 40 } config;
 41 
 42 static void usage();
 43 char *redisGitSHA1(void);
 44 char *redisGitDirty(void);
 45 
 46 /*------------------------------------------------------------------------------
 47  * Utility functions
 48  *--------------------------------------------------------------------------- */
 49 
 50 static long long mstime(void) {
 51     struct timeval tv;
 52     long long mst;
 53 
 54     gettimeofday(&tv, NULL);
 55     mst = ((long)tv.tv_sec)*1000;
 56     mst += tv.tv_usec/1000;
 57     return mst;
 58 }
 59 
 60 static void cliRefreshPrompt(void) {
 61     int len;
 62 
 63     if (config.hostsocket != NULL)
 64         len = snprintf(config.prompt,sizeof(config.prompt),"redis %s",
 65                        config.hostsocket);
 66     else
 67         len = snprintf(config.prompt,sizeof(config.prompt),"redis %s:%d",
 68                        config.hostip, config.hostport);
 69     /* Add [dbnum] if needed */
 70     if (config.dbnum != 0)
 71         len += snprintf(config.prompt+len,sizeof(config.prompt)-len,"[%d]",
 72             config.dbnum);
 73     snprintf(config.prompt+len,sizeof(config.prompt)-len,"> ");
 74 }
 75 
 76 /*------------------------------------------------------------------------------
 77  * Help functions
 78  *--------------------------------------------------------------------------- */
 79 
 80 #define CLI_HELP_COMMAND 1
 81 #define CLI_HELP_GROUP 2
 82 
 83 typedef struct {
 84     int type;
 85     int argc;
 86     sds *argv;
 87     sds full;
 88 
 89     /* Only used for help on commands */
 90     struct commandHelp *org;
 91 } helpEntry;
 92 
 93 static helpEntry *helpEntries;
 94 static int helpEntriesLen;
 95 
 96 static sds cliVersion() {
 97     sds version;
 98     version = sdscatprintf(sdsempty(), "%s", REDIS_VERSION);
 99 
100     /* Add git commit and working tree status when available */
101     if (strtoll(redisGitSHA1(),NULL,16)) {
102         version = sdscatprintf(version, " (git:%s", redisGitSHA1());
103         if (strtoll(redisGitDirty(),NULL,10))
104             version = sdscatprintf(version, "-dirty");
105         version = sdscat(version, ")");
106     }
107     return version;
108 }
109 
110 static void cliInitHelp() {
111     int commandslen = sizeof(commandHelp)/sizeof(struct commandHelp);
112     int groupslen = sizeof(commandGroups)/sizeof(char*);
113     int i, len, pos = 0;
114     helpEntry tmp;
115 
116     helpEntriesLen = len = commandslen+groupslen;
117     helpEntries = malloc(sizeof(helpEntry)*len);
118 
119     for (i = 0; i < groupslen; i++) {
120         tmp.argc = 1;
121         tmp.argv = malloc(sizeof(sds));
122         tmp.argv[0] = sdscatprintf(sdsempty(),"@%s",commandGroups[i]);
123         tmp.full = tmp.argv[0];
124         tmp.type = CLI_HELP_GROUP;
125         tmp.org = NULL;
126         helpEntries[pos++] = tmp;
127     }
128 
129     for (i = 0; i < commandslen; i++) {
130         tmp.argv = sdssplitargs(commandHelp[i].name,&tmp.argc);
131         tmp.full = sdsnew(commandHelp[i].name);
132         tmp.type = CLI_HELP_COMMAND;
133         tmp.org = &commandHelp[i];
134         helpEntries[pos++] = tmp;
135     }
136 }
137 
138 /* Output command help to stdout. */
139 static void cliOutputCommandHelp(struct commandHelp *help, int group) {
140     printf("\r\n  \x1b[1m%s\x1b[0m \x1b[90m%s\x1b[0m\r\n", help->name, help->params);
141     printf("  \x1b[33msummary:\x1b[0m %s\r\n", help->summary);
142     printf("  \x1b[33msince:\x1b[0m %s\r\n", help->since);
143     if (group) {
144         printf("  \x1b[33mgroup:\x1b[0m %s\r\n", commandGroups[help->group]);
145     }
146 }
147 
148 /* Print generic help. */
149 static void cliOutputGenericHelp() {
150     sds version = cliVersion();
151     printf(
152         "redis-cli %s\r\n"
153         "Type: \"help @<group>\" to get a list of commands in <group>\r\n"
154         "      \"help <command>\" for help on <command>\r\n"
155         "      \"help <tab>\" to get a list of possible help topics\r\n"
156         "      \"quit\" to exit\r\n",
157         version
158     );
159     sdsfree(version);
160 }
161 
162 /* Output all command help, filtering by group or command name. */
163 static void cliOutputHelp(int argc, char **argv) {
164     int i, j, len;
165     int group = -1;
166     helpEntry *entry;
167     struct commandHelp *help;
168 
169     if (argc == 0) {
170         cliOutputGenericHelp();
171         return;
172     } else if (argc > 0 && argv[0][0] == '@') {
173         len = sizeof(commandGroups)/sizeof(char*);
174         for (i = 0; i < len; i++) {
175             if (strcasecmp(argv[0]+1,commandGroups[i]) == 0) {
176                 group = i;
177                 break;
178             }
179         }
180     }
181 
182     assert(argc > 0);
183     for (i = 0; i < helpEntriesLen; i++) {
184         entry = &helpEntries[i];
185         if (entry->type != CLI_HELP_COMMAND) continue;
186 
187         help = entry->org;
188         if (group == -1) {
189             /* Compare all arguments */
190             if (argc == entry->argc) {
191                 for (j = 0; j < argc; j++) {
192                     if (strcasecmp(argv[j],entry->argv[j]) != 0) break;
193                 }
194                 if (j == argc) {
195                     cliOutputCommandHelp(help,1);
196                 }
197             }
198         } else {
199             if (group == help->group) {
200                 cliOutputCommandHelp(help,0);
201             }
202         }
203     }
204     printf("\r\n");
205 }
206 
207 static void completionCallback(const char *buf, linenoiseCompletions *lc) {
208     size_t startpos = 0;
209     int mask;
210     int i;
211     size_t matchlen;
212     sds tmp;
213 
214     if (strncasecmp(buf,"help ",5) == 0) {
215         startpos = 5;
216         while (isspace(buf[startpos])) startpos++;
217         mask = CLI_HELP_COMMAND | CLI_HELP_GROUP;
218     } else {
219         mask = CLI_HELP_COMMAND;
220     }
221 
222     for (i = 0; i < helpEntriesLen; i++) {
223         if (!(helpEntries[i].type & mask)) continue;
224 
225         matchlen = strlen(buf+startpos);
226         if (strncasecmp(buf+startpos,helpEntries[i].full,matchlen) == 0) {
227             tmp = sdsnewlen(buf,startpos);
228             tmp = sdscat(tmp,helpEntries[i].full);
229             linenoiseAddCompletion(lc,tmp);
230             sdsfree(tmp);
231         }
232     }
233 }
234 
235 /*------------------------------------------------------------------------------
236  * Networking / parsing
237  *--------------------------------------------------------------------------- */
238 
239 /* Send AUTH command to the server */
240 static int cliAuth() {
241     redisReply *reply;
242     if (config.auth == NULL) return REDIS_OK;
243 
244     reply = redisCommand(context,"AUTH %s",config.auth);
245     if (reply != NULL) {
246         freeReplyObject(reply);
247         return REDIS_OK;
248     }
249     return REDIS_ERR;
250 }
251 
252 /* Send SELECT dbnum to the server */
253 static int cliSelect() {
254     redisReply *reply;
255     if (config.dbnum == 0) return REDIS_OK;
256 
257     reply = redisCommand(context,"SELECT %d",config.dbnum);
258     if (reply != NULL) {
259         freeReplyObject(reply);
260         return REDIS_OK;
261     }
262     return REDIS_ERR;
263 }
264 
265 /* Connect to the client. If force is not zero the connection is performed
266  * even if there is already a connected socket. */
267 static int cliConnect(int force) {
268     if (context == NULL || force) {
269         if (context != NULL)
270             redisFree(context);
271 
272         if (config.hostsocket == NULL) {
273             context = redisConnect(config.hostip,config.hostport);
274         } else {
275             context = redisConnectUnix(config.hostsocket);
276         }
277 
278         if (context->err) {
279             fprintf(stderr,"Could not connect to Redis at ");
280             if (config.hostsocket == NULL)
281                 fprintf(stderr,"%s:%d: %s\n",config.hostip,config.hostport,context->errstr);
282             else
283                 fprintf(stderr,"%s: %s\n",config.hostsocket,context->errstr);
284             redisFree(context);
285             context = NULL;
286             return REDIS_ERR;
287         }
288 
289         /* Do AUTH and select the right DB. */
290         if (cliAuth() != REDIS_OK)
291             return REDIS_ERR;
292         if (cliSelect() != REDIS_OK)
293             return REDIS_ERR;
294     }
295     return REDIS_OK;
296 }
297 
298 static void cliPrintContextError() {
299     if (context == NULL) return;
300     fprintf(stderr,"Error: %s\n",context->errstr);
301 }
302 
303 static sds cliFormatReplyTTY(redisReply *r, char *prefix) {
304     sds out = sdsempty();
305     switch (r->type) {
306     case REDIS_REPLY_ERROR:
307         out = sdscatprintf(out,"(error) %s\n", r->str);
308     break;
309     case REDIS_REPLY_STATUS:
310         out = sdscat(out,r->str);
311         out = sdscat(out,"\n");
312     break;
313     case REDIS_REPLY_INTEGER:
314         out = sdscatprintf(out,"(integer) %lld\n",r->integer);
315     break;
316     case REDIS_REPLY_STRING:
317         /* If you are producing output for the standard output we want
318         * a more interesting output with quoted characters and so forth */
319         out = sdscatrepr(out,r->str,r->len);
320         out = sdscat(out,"\n");
321     break;
322     case REDIS_REPLY_NIL:
323         out = sdscat(out,"(nil)\n");
324     break;
325     case REDIS_REPLY_ARRAY:
326         if (r->elements == 0) {
327             out = sdscat(out,"(empty list or set)\n");
328         } else {
329             unsigned int i, idxlen = 0;
330             char _prefixlen[16];
331             char _prefixfmt[16];
332             sds _prefix;
333             sds tmp;
334 
335             /* Calculate chars needed to represent the largest index */
336             i = r->elements;
337             do {
338                 idxlen++;
339                 i /= 10;
340             } while(i);
341 
342             /* Prefix for nested multi bulks should grow with idxlen+2 spaces */
343             memset(_prefixlen,' ',idxlen+2);
344             _prefixlen[idxlen+2] = '\0';
345             _prefix = sdscat(sdsnew(prefix),_prefixlen);
346 
347             /* Setup prefix format for every entry */
348             snprintf(_prefixfmt,sizeof(_prefixfmt),"%%s%%%dd) ",idxlen);
349 
350             for (i = 0; i < r->elements; i++) {
351                 /* Don't use the prefix for the first element, as the parent
352                  * caller already prepended the index number. */
353                 out = sdscatprintf(out,_prefixfmt,i == 0 ? "" : prefix,i+1);
354 
355                 /* Format the multi bulk entry */
356                 tmp = cliFormatReplyTTY(r->element[i],_prefix);
357                 out = sdscatlen(out,tmp,sdslen(tmp));
358                 sdsfree(tmp);
359             }
360             sdsfree(_prefix);
361         }
362     break;
363     default:
364         fprintf(stderr,"Unknown reply type: %d\n", r->type);
365         exit(1);
366     }
367     return out;
368 }
369 
370 static sds cliFormatReplyRaw(redisReply *r) {
371     sds out = sdsempty(), tmp;
372     size_t i;
373 
374     switch (r->type) {
375     case REDIS_REPLY_NIL:
376         /* Nothing... */
377         break;
378     case REDIS_REPLY_ERROR:
379         out = sdscatlen(out,r->str,r->len);
380         out = sdscatlen(out,"\n",1);
381         break;
382     case REDIS_REPLY_STATUS:
383     case REDIS_REPLY_STRING:
384         out = sdscatlen(out,r->str,r->len);
385         break;
386     case REDIS_REPLY_INTEGER:
387         out = sdscatprintf(out,"%lld",r->integer);
388         break;
389     case REDIS_REPLY_ARRAY:
390         for (i = 0; i < r->elements; i++) {
391             if (i > 0) out = sdscat(out,config.mb_delim);
392             tmp = cliFormatReplyRaw(r->element[i]);
393             out = sdscatlen(out,tmp,sdslen(tmp));
394             sdsfree(tmp);
395         }
396         break;
397     default:
398         fprintf(stderr,"Unknown reply type: %d\n", r->type);
399         exit(1);
400     }
401     return out;
402 }
403 
404 static int cliReadReply(int output_raw_strings) {
405     void *_reply;
406     redisReply *reply;
407     sds out;
408 
409     if (redisGetReply(context,&_reply) != REDIS_OK) {
410         if (config.shutdown)
411             return REDIS_OK;
412         if (config.interactive) {
413             /* Filter cases where we should reconnect */
414             if (context->err == REDIS_ERR_IO && errno == ECONNRESET)
415                 return REDIS_ERR;
416             if (context->err == REDIS_ERR_EOF)
417                 return REDIS_ERR;
418         }
419         cliPrintContextError();
420         exit(1);
421         return REDIS_ERR; /* avoid compiler warning */
422     }
423 
424     reply = (redisReply*)_reply;
425     if (output_raw_strings) {
426         out = cliFormatReplyRaw(reply);
427     } else {
428         if (config.raw_output) {
429             out = cliFormatReplyRaw(reply);
430             out = sdscat(out,"\n");
431         } else {
432             out = cliFormatReplyTTY(reply,"");
433         }
434     }
435     fwrite(out,sdslen(out),1,stdout);
436     sdsfree(out);
437     freeReplyObject(reply);
438     return REDIS_OK;
439 }
440 
441 static int cliSendCommand(int argc, char **argv, int repeat) {
442     char *command = argv[0];
443     size_t *argvlen;
444     int j, output_raw;
445 
446     if (!strcasecmp(command,"help") || !strcasecmp(command,"?")) {
447         cliOutputHelp(--argc, ++argv);
448         return REDIS_OK;
449     }
450 
451     if (context == NULL) return REDIS_ERR;
452 
453     output_raw = 0;
454     if (!strcasecmp(command,"info") ||
455         (argc == 2 && !strcasecmp(command,"client") &&
456                        !strcasecmp(argv[1],"list")))
457 
458     {
459         output_raw = 1;
460     }
461 
462     if (!strcasecmp(command,"shutdown")) config.shutdown = 1;
463     if (!strcasecmp(command,"monitor")) config.monitor_mode = 1;
464     if (!strcasecmp(command,"subscribe") ||
465         !strcasecmp(command,"psubscribe")) config.pubsub_mode = 1;
466 
467     /* Setup argument length */
468     argvlen = malloc(argc*sizeof(size_t));
469     for (j = 0; j < argc; j++)
470         argvlen[j] = sdslen(argv[j]);
471 
472     while(repeat--) {
473         redisAppendCommandArgv(context,argc,(const char**)argv,argvlen);
474         while (config.monitor_mode) {
475             if (cliReadReply(output_raw) != REDIS_OK) exit(1);
476             fflush(stdout);
477         }
478 
479         if (config.pubsub_mode) {
480             if (!config.raw_output)
481                 printf("Reading messages... (press Ctrl-C to quit)\n");
482             while (1) {
483                 if (cliReadReply(output_raw) != REDIS_OK) exit(1);
484             }
485         }
486 
487         if (cliReadReply(output_raw) != REDIS_OK) {
488             free(argvlen);
489             return REDIS_ERR;
490         } else {
491             /* Store database number when SELECT was successfully executed. */
492             if (!strcasecmp(command,"select") && argc == 2) {
493                 config.dbnum = atoi(argv[1]);
494                 cliRefreshPrompt();
495             }
496         }
497         if (config.interval) usleep(config.interval);
498         fflush(stdout); /* Make it grep friendly */
499     }
500 
501     free(argvlen);
502     return REDIS_OK;
503 }
504 
505 /*------------------------------------------------------------------------------
506  * User interface
507  *--------------------------------------------------------------------------- */
508 
509 static int parseOptions(int argc, char **argv) {
510     int i;
511 
512     for (i = 1; i < argc; i++) {
513         int lastarg = i==argc-1;
514 
515         if (!strcmp(argv[i],"-h") && !lastarg) {
516             sdsfree(config.hostip);
517             config.hostip = sdsnew(argv[i+1]);
518             i++;
519         } else if (!strcmp(argv[i],"-h") && lastarg) {
520             usage();
521         } else if (!strcmp(argv[i],"--help")) {
522             usage();
523         } else if (!strcmp(argv[i],"-x")) {
524             config.stdinarg = 1;
525         } else if (!strcmp(argv[i],"-p") && !lastarg) {
526             config.hostport = atoi(argv[i+1]);
527             i++;
528         } else if (!strcmp(argv[i],"-s") && !lastarg) {
529             config.hostsocket = argv[i+1];
530             i++;
531         } else if (!strcmp(argv[i],"-r") && !lastarg) {
532             config.repeat = strtoll(argv[i+1],NULL,10);
533             i++;
534         } else if (!strcmp(argv[i],"-i") && !lastarg) {
535             double seconds = atof(argv[i+1]);
536             config.interval = seconds*1000000;
537             i++;
538         } else if (!strcmp(argv[i],"-n") && !lastarg) {
539             config.dbnum = atoi(argv[i+1]);
540             i++;
541         } else if (!strcmp(argv[i],"-a") && !lastarg) {
542             config.auth = argv[i+1];
543             i++;
544         } else if (!strcmp(argv[i],"--raw")) {
545             config.raw_output = 1;
546         } else if (!strcmp(argv[i],"--latency")) {
547             config.latency_mode = 1;
548         } else if (!strcmp(argv[i],"-d") && !lastarg) {
549             sdsfree(config.mb_delim);
550             config.mb_delim = sdsnew(argv[i+1]);
551             i++;
552         } else if (!strcmp(argv[i],"-v") || !strcmp(argv[i], "--version")) {
553             sds version = cliVersion();
554             printf("redis-cli %s\n", version);
555             sdsfree(version);
556             exit(0);
557         } else {
558             break;
559         }
560     }
561     return i;
562 }
563 
564 static sds readArgFromStdin(void) {
565     char buf[1024];
566     sds arg = sdsempty();
567 
568     while(1) {
569         int nread = read(fileno(stdin),buf,1024);
570 
571         if (nread == 0) break;
572         else if (nread == -1) {
573             perror("Reading from standard input");
574             exit(1);
575         }
576         arg = sdscatlen(arg,buf,nread);
577     }
578     return arg;
579 }
580 
581 static void usage() {
582     sds version = cliVersion();
583     fprintf(stderr,
584 "redis-cli %s\n"
585 "\n"
586 "Usage: redis-cli [OPTIONS] [cmd [arg [arg ...]]]\n"
587 "  -h <hostname>    Server hostname (default: 127.0.0.1)\n"
588 "  -p <port>        Server port (default: 6379)\n"
589 "  -s <socket>      Server socket (overrides hostname and port)\n"
590 "  -a <password>    Password to use when connecting to the server\n"
591 "  -r <repeat>      Execute specified command N times\n"
592 "  -i <interval>    When -r is used, waits <interval> seconds per command.\n"
593 "                   It is possible to specify sub-second times like -i 0.1.\n"
594 "  -n <db>          Database number\n"
595 "  -x               Read last argument from STDIN\n"
596 "  -d <delimiter>   Multi-bulk delimiter in for raw formatting (default: \\n)\n"
597 "  --raw            Use raw formatting for replies (default when STDOUT is not a tty)\n"
598 "  --latency        Enter a special mode continuously sampling latency.\n"
599 "  --help           Output this help and exit\n"
600 "  --version        Output version and exit\n"
601 "\n"
602 "Examples:\n"
603 "  cat /etc/passwd | redis-cli -x set mypasswd\n"
604 "  redis-cli get mypasswd\n"
605 "  redis-cli -r 100 lpush mylist x\n"
606 "  redis-cli -r 100 -i 1 info | grep used_memory_human:\n"
607 "\n"
608 "When no command is given, redis-cli starts in interactive mode.\n"
609 "Type \"help\" in interactive mode for information on available commands.\n"
610 "\n",
611         version);
612     sdsfree(version);
613     exit(1);
614 }
615 
616 /* Turn the plain C strings into Sds strings */
617 static char **convertToSds(int count, char** args) {
618   int j;
619   char **sds = zmalloc(sizeof(char*)*count);
620 
621   for(j = 0; j < count; j++)
622     sds[j] = sdsnew(args[j]);
623 
624   return sds;
625 }
626 
627 #define LINE_BUFLEN 4096
628 static void repl() {
629     sds historyfile = NULL;
630     int history = 0;
631     char *line;
632     int argc;
633     sds *argv;
634 
635     config.interactive = 1;
636     linenoiseSetCompletionCallback(completionCallback);
637 
638     /* Only use history when stdin is a tty. */
639     if (isatty(fileno(stdin))) {
640         history = 1;
641 
642         if (getenv("HOME") != NULL) {
643             historyfile = sdscatprintf(sdsempty(),"%s/.rediscli_history",getenv("HOME"));
644             linenoiseHistoryLoad(historyfile);
645         }
646     }
647 
648     cliRefreshPrompt();
649     while((line = linenoise(context ? config.prompt : "not connected> ")) != NULL) {
650         if (line[0] != '\0') {
651             argv = sdssplitargs(line,&argc);
652             if (history) linenoiseHistoryAdd(line);
653             if (historyfile) linenoiseHistorySave(historyfile);
654 
655             if (argv == NULL) {
656                 printf("Invalid argument(s)\n");
657                 free(line);
658                 continue;
659             } else if (argc > 0) {
660                 if (strcasecmp(argv[0],"quit") == 0 ||
661                     strcasecmp(argv[0],"exit") == 0)
662                 {
663                     exit(0);
664                 } else if (argc == 3 && !strcasecmp(argv[0],"connect")) {
665                     sdsfree(config.hostip);
666                     config.hostip = sdsnew(argv[1]);
667                     config.hostport = atoi(argv[2]);
668                     cliConnect(1);
669                 } else if (argc == 1 && !strcasecmp(argv[0],"clear")) {
670                     linenoiseClearScreen();
671                 } else {
672                     long long start_time = mstime(), elapsed;
673                     int repeat, skipargs = 0;
674 
675                     repeat = atoi(argv[0]);
676                     if (argc > 1 && repeat) {
677                         skipargs = 1;
678                     } else {
679                         repeat = 1;
680                     }
681 
682                     if (cliSendCommand(argc-skipargs,argv+skipargs,repeat)
683                         != REDIS_OK)
684                     {
685                         cliConnect(1);
686 
687                         /* If we still cannot send the command print error.
688                          * We'll try to reconnect the next time. */
689                         if (cliSendCommand(argc-skipargs,argv+skipargs,repeat)
690                             != REDIS_OK)
691                             cliPrintContextError();
692                     }
693                     elapsed = mstime()-start_time;
694                     if (elapsed >= 500) {
695                         printf("(%.2fs)\n",(double)elapsed/1000);
696                     }
697                 }
698             }
699             /* Free the argument vector */
700             while(argc--) sdsfree(argv[argc]);
701             zfree(argv);
702         }
703         /* linenoise() returns malloc-ed lines like readline() */
704         free(line);
705     }
706     exit(0);
707 }
708 
709 static int noninteractive(int argc, char **argv) {
710     int retval = 0;
711     if (config.stdinarg) {
712         argv = zrealloc(argv, (argc+1)*sizeof(char*));
713         argv[argc] = readArgFromStdin();
714         retval = cliSendCommand(argc+1, argv, config.repeat);
715     } else {
716         /* stdin is probably a tty, can be tested with S_ISCHR(s.st_mode) */
717         retval = cliSendCommand(argc, argv, config.repeat);
718     }
719     return retval;
720 }
721 
722 static void latencyMode(void) {
723     redisReply *reply;
724     long long start, latency, min, max, tot, count = 0;
725     double avg;
726 
727     if (!context) exit(1);
728     while(1) {
729         start = mstime();
730         reply = redisCommand(context,"PING");
731         if (reply == NULL) {
732             fprintf(stderr,"\nI/O error\n");
733             exit(1);
734         }
735         latency = mstime()-start;
736         freeReplyObject(reply);
737         count++;
738         if (count == 1) {
739             min = max = tot = latency;
740             avg = (double) latency;
741         } else {
742             if (latency < min) min = latency;
743             if (latency > max) max = latency;
744             tot += latency;
745             avg = (double) tot/count;
746         }
747         printf("\x1b[0G\x1b[2Kmin: %lld, max: %lld, avg: %.2f (%lld samples)",
748             min, max, avg, count);
749         fflush(stdout);
750         usleep(10000);
751     }
752 }
753 
754 int main(int argc, char **argv) {
755     int firstarg;
756 
757     config.hostip = sdsnew("127.0.0.1");
758     config.hostport = 6379;
759     config.hostsocket = NULL;
760     config.repeat = 1;
761     config.interval = 0;
762     config.dbnum = 0;
763     config.interactive = 0;
764     config.shutdown = 0;
765     config.monitor_mode = 0;
766     config.pubsub_mode = 0;
767     config.latency_mode = 0;
768     config.stdinarg = 0;
769     config.auth = NULL;
770     config.raw_output = !isatty(fileno(stdout)) && (getenv("FAKETTY") == NULL);
771     config.mb_delim = sdsnew("\n");
772     cliInitHelp();
773 
774     firstarg = parseOptions(argc,argv);
775     argc -= firstarg;
776     argv += firstarg;
777 
778     /* Start in latency mode if appropriate */
779     if (config.latency_mode) {
780         cliConnect(0);
781         latencyMode();
782     }
783 
784     /* Start interactive mode when no command is provided */
785     if (argc == 0) {
786         /* Note that in repl mode we don't abort on connection error.
787          * A new attempt will be performed for every command send. */
788         cliConnect(0);
789         repl();
790     }
791 
792     /* Otherwise, we have some arguments to execute */
793     if (cliConnect(0) != REDIS_OK) exit(1);
794     return noninteractive(argc,convertToSds(argc,argv));
795 }

你可能感兴趣的:(redis-cli)