这份代码是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 }