redis源码笔记 - redis-cli.c

时间:2021-03-23 21:59:22

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

redis源码笔记 - redis-cli.c
  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源码笔记 - redis-cli.c