2009-03-22 05:30:00 -04:00
|
|
|
/* Redis CLI (command line interface)
|
|
|
|
*
|
2010-02-19 05:23:57 -05:00
|
|
|
* Copyright (c) 2009-2010, Salvatore Sanfilippo <antirez at gmail dot com>
|
2009-03-22 05:30:00 -04:00
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions are met:
|
|
|
|
*
|
|
|
|
* * Redistributions of source code must retain the above copyright notice,
|
|
|
|
* this list of conditions and the following disclaimer.
|
|
|
|
* * Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
* * Neither the name of Redis nor the names of its contributors may be used
|
|
|
|
* to endorse or promote products derived from this software without
|
|
|
|
* specific prior written permission.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
|
|
* POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
2009-03-27 17:00:27 -04:00
|
|
|
#include "fmacros.h"
|
2010-07-06 13:17:09 -04:00
|
|
|
#include "version.h"
|
2009-03-27 17:00:27 -04:00
|
|
|
|
2009-03-22 05:30:00 -04:00
|
|
|
#include <stdio.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <unistd.h>
|
2010-04-26 12:39:39 -04:00
|
|
|
#include <ctype.h>
|
2010-08-24 12:39:34 -04:00
|
|
|
#include <errno.h>
|
2010-08-25 08:48:50 -04:00
|
|
|
#include <sys/stat.h>
|
2010-11-02 13:08:30 -04:00
|
|
|
#include <sys/time.h>
|
2009-03-22 05:30:00 -04:00
|
|
|
|
2010-11-03 11:09:38 -04:00
|
|
|
#include "hiredis.h"
|
2009-03-22 05:30:00 -04:00
|
|
|
#include "sds.h"
|
|
|
|
#include "zmalloc.h"
|
2010-03-23 10:25:32 -04:00
|
|
|
#include "linenoise.h"
|
2009-03-22 05:30:00 -04:00
|
|
|
|
|
|
|
#define REDIS_NOTUSED(V) ((void) V)
|
|
|
|
|
2010-11-03 11:09:38 -04:00
|
|
|
static redisContext *context;
|
2009-03-22 05:30:00 -04:00
|
|
|
static struct config {
|
|
|
|
char *hostip;
|
|
|
|
int hostport;
|
2010-08-01 17:06:00 -04:00
|
|
|
char *hostsocket;
|
2009-11-02 19:35:39 -05:00
|
|
|
long repeat;
|
2009-11-11 23:12:09 -05:00
|
|
|
int dbnum;
|
2010-08-25 04:05:50 -04:00
|
|
|
int interactive;
|
2010-05-14 10:38:09 -04:00
|
|
|
int shutdown;
|
2010-04-27 10:07:31 -04:00
|
|
|
int monitor_mode;
|
|
|
|
int pubsub_mode;
|
2010-08-04 08:15:52 -04:00
|
|
|
int raw_output; /* output mode per command */
|
2010-08-04 11:16:05 -04:00
|
|
|
int tty; /* flag for default output format */
|
2010-09-09 10:38:10 -04:00
|
|
|
int stdinarg; /* get last arg from stdin. (-x option) */
|
2010-08-04 11:46:56 -04:00
|
|
|
char mb_sep;
|
2010-03-17 21:51:09 -04:00
|
|
|
char *auth;
|
2010-07-07 12:44:53 -04:00
|
|
|
char *historyfile;
|
2009-03-22 05:30:00 -04:00
|
|
|
} config;
|
|
|
|
|
2010-01-15 16:42:29 -05:00
|
|
|
static void usage();
|
2010-11-08 10:26:02 -05:00
|
|
|
char *redisGitSHA1(void);
|
2009-03-24 08:37:32 -04:00
|
|
|
|
2010-11-02 13:08:30 -04:00
|
|
|
/*------------------------------------------------------------------------------
|
|
|
|
* Utility functions
|
|
|
|
*--------------------------------------------------------------------------- */
|
|
|
|
|
|
|
|
static long long mstime(void) {
|
|
|
|
struct timeval tv;
|
|
|
|
long long mst;
|
|
|
|
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
mst = ((long)tv.tv_sec)*1000;
|
|
|
|
mst += tv.tv_usec/1000;
|
|
|
|
return mst;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*------------------------------------------------------------------------------
|
|
|
|
* Networking / parsing
|
|
|
|
*--------------------------------------------------------------------------- */
|
|
|
|
|
2010-11-03 11:09:38 -04:00
|
|
|
/* Send AUTH command to the server */
|
|
|
|
static int cliAuth() {
|
|
|
|
redisReply *reply;
|
|
|
|
if (config.auth == NULL) return REDIS_OK;
|
|
|
|
|
|
|
|
reply = redisCommand(context,"AUTH %s",config.auth);
|
|
|
|
if (reply != NULL) {
|
|
|
|
freeReplyObject(reply);
|
|
|
|
return REDIS_OK;
|
|
|
|
}
|
|
|
|
return REDIS_ERR;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Send SELECT dbnum to the server */
|
|
|
|
static int cliSelect() {
|
|
|
|
redisReply *reply;
|
|
|
|
char dbnum[16];
|
|
|
|
if (config.dbnum == 0) return REDIS_OK;
|
|
|
|
|
|
|
|
snprintf(dbnum,sizeof(dbnum),"%d",config.dbnum);
|
|
|
|
reply = redisCommand(context,"SELECT %s",dbnum);
|
|
|
|
if (reply != NULL) {
|
|
|
|
freeReplyObject(reply);
|
|
|
|
return REDIS_OK;
|
|
|
|
}
|
|
|
|
return REDIS_ERR;
|
|
|
|
}
|
|
|
|
|
2010-08-24 12:39:34 -04:00
|
|
|
/* Connect to the client. If force is not zero the connection is performed
|
|
|
|
* even if there is already a connected socket. */
|
|
|
|
static int cliConnect(int force) {
|
2010-11-03 11:09:38 -04:00
|
|
|
if (context == NULL || force) {
|
|
|
|
if (context != NULL)
|
|
|
|
redisFree(context);
|
2009-03-22 05:30:00 -04:00
|
|
|
|
2010-08-01 17:06:00 -04:00
|
|
|
if (config.hostsocket == NULL) {
|
2010-11-03 11:09:38 -04:00
|
|
|
context = redisConnect(config.hostip,config.hostport);
|
2010-08-01 17:06:00 -04:00
|
|
|
} else {
|
2010-11-03 11:09:38 -04:00
|
|
|
context = redisConnectUnix(config.hostsocket);
|
2010-08-01 17:06:00 -04:00
|
|
|
}
|
2010-11-03 11:09:38 -04:00
|
|
|
|
|
|
|
if (context->err) {
|
2010-08-01 17:06:00 -04:00
|
|
|
fprintf(stderr,"Could not connect to Redis at ");
|
|
|
|
if (config.hostsocket == NULL)
|
2010-11-03 11:09:38 -04:00
|
|
|
fprintf(stderr,"%s:%d: %s\n",config.hostip,config.hostport,context->errstr);
|
2010-08-01 17:06:00 -04:00
|
|
|
else
|
2010-11-03 11:09:38 -04:00
|
|
|
fprintf(stderr,"%s: %s\n",config.hostsocket,context->errstr);
|
|
|
|
redisFree(context);
|
|
|
|
context = NULL;
|
|
|
|
return REDIS_ERR;
|
2010-03-02 14:24:21 -05:00
|
|
|
}
|
2009-03-22 05:30:00 -04:00
|
|
|
|
2010-11-03 11:09:38 -04:00
|
|
|
/* Do AUTH and select the right DB. */
|
|
|
|
if (cliAuth() != REDIS_OK)
|
|
|
|
return REDIS_ERR;
|
|
|
|
if (cliSelect() != REDIS_OK)
|
|
|
|
return REDIS_ERR;
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|
2010-11-03 11:09:38 -04:00
|
|
|
return REDIS_OK;
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|
|
|
|
|
2010-11-03 11:09:38 -04:00
|
|
|
static void cliPrintContextErrorAndExit() {
|
|
|
|
if (context == NULL) return;
|
|
|
|
fprintf(stderr,"Error: %s\n",context->errstr);
|
|
|
|
exit(1);
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|
|
|
|
|
2010-11-03 11:09:38 -04:00
|
|
|
static sds cliFormatReply(redisReply *r, char *prefix) {
|
|
|
|
sds out = sdsempty();
|
|
|
|
switch (r->type) {
|
|
|
|
case REDIS_REPLY_ERROR:
|
|
|
|
if (config.tty) out = sdscat(out,"(error) ");
|
|
|
|
out = sdscatprintf(out,"%s\n", r->str);
|
|
|
|
break;
|
|
|
|
case REDIS_REPLY_STATUS:
|
|
|
|
out = sdscat(out,r->str);
|
|
|
|
out = sdscat(out,"\n");
|
|
|
|
break;
|
|
|
|
case REDIS_REPLY_INTEGER:
|
|
|
|
if (config.tty) out = sdscat(out,"(integer) ");
|
|
|
|
out = sdscatprintf(out,"%lld\n",r->integer);
|
|
|
|
break;
|
|
|
|
case REDIS_REPLY_STRING:
|
|
|
|
if (config.raw_output || !config.tty) {
|
|
|
|
out = sdscatlen(out,r->str,r->len);
|
|
|
|
} else {
|
|
|
|
/* If you are producing output for the standard output we want
|
|
|
|
* a more interesting output with quoted characters and so forth */
|
|
|
|
out = sdscatrepr(out,r->str,r->len);
|
|
|
|
out = sdscat(out,"\n");
|
2010-04-26 12:54:55 -04:00
|
|
|
}
|
2010-11-03 11:09:38 -04:00
|
|
|
break;
|
|
|
|
case REDIS_REPLY_NIL:
|
|
|
|
out = sdscat(out,"(nil)\n");
|
|
|
|
break;
|
|
|
|
case REDIS_REPLY_ARRAY:
|
|
|
|
if (r->elements == 0) {
|
|
|
|
out = sdscat(out,"(empty list or set)\n");
|
2010-08-24 12:39:34 -04:00
|
|
|
} else {
|
2010-11-03 12:03:54 -04:00
|
|
|
unsigned int i, idxlen = 0;
|
|
|
|
char _prefixlen[16];
|
|
|
|
char _prefixfmt[16];
|
|
|
|
sds _prefix;
|
2010-11-03 11:09:38 -04:00
|
|
|
sds tmp;
|
|
|
|
|
2010-11-03 12:03:54 -04:00
|
|
|
/* Calculate chars needed to represent the largest index */
|
|
|
|
i = r->elements;
|
|
|
|
do {
|
|
|
|
idxlen++;
|
|
|
|
i /= 10;
|
|
|
|
} while(i);
|
|
|
|
|
|
|
|
/* Prefix for nested multi bulks should grow with idxlen+2 spaces */
|
|
|
|
memset(_prefixlen,' ',idxlen+2);
|
|
|
|
_prefixlen[idxlen+2] = '\0';
|
|
|
|
_prefix = sdscat(sdsnew(prefix),_prefixlen);
|
|
|
|
|
|
|
|
/* Setup prefix format for every entry */
|
|
|
|
snprintf(_prefixfmt,sizeof(_prefixfmt),"%%s%%%dd) ",idxlen);
|
|
|
|
|
2010-11-03 11:09:38 -04:00
|
|
|
for (i = 0; i < r->elements; i++) {
|
2010-11-03 12:03:54 -04:00
|
|
|
/* Don't use the prefix for the first element, as the parent
|
|
|
|
* caller already prepended the index number. */
|
|
|
|
out = sdscatprintf(out,_prefixfmt,i == 0 ? "" : prefix,i+1);
|
|
|
|
|
|
|
|
/* Format the multi bulk entry */
|
|
|
|
tmp = cliFormatReply(r->element[i],_prefix);
|
2010-11-03 11:09:38 -04:00
|
|
|
out = sdscatlen(out,tmp,sdslen(tmp));
|
|
|
|
sdsfree(tmp);
|
|
|
|
}
|
2010-11-03 12:03:54 -04:00
|
|
|
sdsfree(_prefix);
|
2010-08-24 12:39:34 -04:00
|
|
|
}
|
2010-11-03 11:09:38 -04:00
|
|
|
break;
|
2009-03-24 08:37:32 -04:00
|
|
|
default:
|
2010-11-03 11:09:38 -04:00
|
|
|
fprintf(stderr,"Unknown reply type: %d\n", r->type);
|
|
|
|
exit(1);
|
2009-03-24 08:37:32 -04:00
|
|
|
}
|
2010-11-03 11:09:38 -04:00
|
|
|
return out;
|
2009-03-24 08:37:32 -04:00
|
|
|
}
|
|
|
|
|
2010-11-03 11:09:38 -04:00
|
|
|
static int cliReadReply() {
|
|
|
|
redisReply *reply;
|
|
|
|
sds out;
|
|
|
|
|
|
|
|
if (redisGetReply(context,(void**)&reply) != REDIS_OK) {
|
|
|
|
if (config.shutdown)
|
|
|
|
return REDIS_OK;
|
|
|
|
if (config.interactive) {
|
|
|
|
/* Filter cases where we should reconnect */
|
|
|
|
if (context->err == REDIS_ERR_IO && errno == ECONNRESET)
|
|
|
|
return REDIS_ERR;
|
|
|
|
if (context->err == REDIS_ERR_EOF)
|
|
|
|
return REDIS_ERR;
|
|
|
|
}
|
|
|
|
cliPrintContextErrorAndExit();
|
|
|
|
return REDIS_ERR; /* avoid compiler warning */
|
2009-11-11 23:12:09 -05:00
|
|
|
}
|
2010-11-03 11:09:38 -04:00
|
|
|
|
|
|
|
out = cliFormatReply(reply,"");
|
|
|
|
freeReplyObject(reply);
|
|
|
|
fwrite(out,sdslen(out),1,stdout);
|
|
|
|
sdsfree(out);
|
|
|
|
return REDIS_OK;
|
2009-11-11 23:12:09 -05:00
|
|
|
}
|
|
|
|
|
2010-08-30 09:57:03 -04:00
|
|
|
static void showInteractiveHelp(void) {
|
|
|
|
printf(
|
|
|
|
"\n"
|
|
|
|
"Welcome to redis-cli " REDIS_VERSION "!\n"
|
|
|
|
"Just type any valid Redis command to see a pretty printed output.\n"
|
|
|
|
"\n"
|
|
|
|
"It is possible to quote strings, like in:\n"
|
|
|
|
" set \"my key\" \"some string \\xff\\n\"\n"
|
|
|
|
"\n"
|
|
|
|
"You can find a list of valid Redis commands at\n"
|
|
|
|
" http://code.google.com/p/redis/wiki/CommandReference\n"
|
|
|
|
"\n"
|
|
|
|
"Note: redis-cli supports line editing, use up/down arrows for history."
|
|
|
|
"\n\n");
|
|
|
|
}
|
|
|
|
|
2010-03-23 09:54:49 -04:00
|
|
|
static int cliSendCommand(int argc, char **argv, int repeat) {
|
2010-05-26 12:18:37 -04:00
|
|
|
char *command = argv[0];
|
2010-11-03 11:09:38 -04:00
|
|
|
size_t *argvlen;
|
|
|
|
int j;
|
2009-03-22 05:30:00 -04:00
|
|
|
|
2010-05-26 12:18:37 -04:00
|
|
|
config.raw_output = !strcasecmp(command,"info");
|
2010-08-30 09:57:03 -04:00
|
|
|
if (!strcasecmp(command,"help")) {
|
|
|
|
showInteractiveHelp();
|
2010-11-03 11:09:38 -04:00
|
|
|
return REDIS_OK;
|
2010-08-30 09:57:03 -04:00
|
|
|
}
|
2010-05-26 12:18:37 -04:00
|
|
|
if (!strcasecmp(command,"shutdown")) config.shutdown = 1;
|
|
|
|
if (!strcasecmp(command,"monitor")) config.monitor_mode = 1;
|
|
|
|
if (!strcasecmp(command,"subscribe") ||
|
|
|
|
!strcasecmp(command,"psubscribe")) config.pubsub_mode = 1;
|
2009-03-22 05:30:00 -04:00
|
|
|
|
2010-11-03 11:09:38 -04:00
|
|
|
/* Setup argument length */
|
|
|
|
argvlen = malloc(argc*sizeof(size_t));
|
|
|
|
for (j = 0; j < argc; j++)
|
|
|
|
argvlen[j] = sdslen(argv[j]);
|
2010-05-26 12:22:05 -04:00
|
|
|
|
2010-03-23 09:54:49 -04:00
|
|
|
while(repeat--) {
|
2010-11-03 11:09:38 -04:00
|
|
|
redisAppendCommandArgv(context,argc,(const char**)argv,argvlen);
|
2010-04-27 10:07:31 -04:00
|
|
|
while (config.monitor_mode) {
|
2010-11-03 11:09:38 -04:00
|
|
|
if (cliReadReply() != REDIS_OK) exit(1);
|
2010-11-08 10:14:15 -05:00
|
|
|
fflush(stdout);
|
2010-01-20 13:38:59 -05:00
|
|
|
}
|
|
|
|
|
2010-04-27 10:07:31 -04:00
|
|
|
if (config.pubsub_mode) {
|
2010-11-03 11:09:38 -04:00
|
|
|
printf("Reading messages... (press Ctrl-C to quit)\n");
|
2010-04-27 10:07:31 -04:00
|
|
|
while (1) {
|
2010-11-03 11:09:38 -04:00
|
|
|
if (cliReadReply() != REDIS_OK) exit(1);
|
2010-04-27 10:07:31 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2010-11-03 11:09:38 -04:00
|
|
|
if (cliReadReply() != REDIS_OK)
|
|
|
|
return REDIS_ERR;
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|
2010-11-03 11:09:38 -04:00
|
|
|
return REDIS_OK;
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|
|
|
|
|
2010-11-02 13:08:30 -04:00
|
|
|
/*------------------------------------------------------------------------------
|
|
|
|
* User interface
|
|
|
|
*--------------------------------------------------------------------------- */
|
|
|
|
|
2009-03-22 05:30:00 -04:00
|
|
|
static int parseOptions(int argc, char **argv) {
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 1; i < argc; i++) {
|
|
|
|
int lastarg = i==argc-1;
|
2010-03-02 10:14:14 -05:00
|
|
|
|
2009-03-22 05:30:00 -04:00
|
|
|
if (!strcmp(argv[i],"-h") && !lastarg) {
|
2010-11-03 11:09:38 -04:00
|
|
|
config.hostip = argv[i+1];
|
2009-03-22 05:30:00 -04:00
|
|
|
i++;
|
2010-01-15 16:42:29 -05:00
|
|
|
} else if (!strcmp(argv[i],"-h") && lastarg) {
|
|
|
|
usage();
|
2010-09-09 10:38:10 -04:00
|
|
|
} else if (!strcmp(argv[i],"-x")) {
|
|
|
|
config.stdinarg = 1;
|
2009-03-22 05:30:00 -04:00
|
|
|
} else if (!strcmp(argv[i],"-p") && !lastarg) {
|
|
|
|
config.hostport = atoi(argv[i+1]);
|
|
|
|
i++;
|
2010-08-01 17:06:00 -04:00
|
|
|
} else if (!strcmp(argv[i],"-s") && !lastarg) {
|
|
|
|
config.hostsocket = argv[i+1];
|
|
|
|
i++;
|
2009-11-02 19:35:39 -05:00
|
|
|
} else if (!strcmp(argv[i],"-r") && !lastarg) {
|
|
|
|
config.repeat = strtoll(argv[i+1],NULL,10);
|
|
|
|
i++;
|
2009-11-11 23:12:09 -05:00
|
|
|
} else if (!strcmp(argv[i],"-n") && !lastarg) {
|
|
|
|
config.dbnum = atoi(argv[i+1]);
|
|
|
|
i++;
|
2010-03-17 09:41:02 -04:00
|
|
|
} else if (!strcmp(argv[i],"-a") && !lastarg) {
|
2010-03-17 21:51:09 -04:00
|
|
|
config.auth = argv[i+1];
|
2010-03-17 09:41:02 -04:00
|
|
|
i++;
|
2010-03-02 10:14:14 -05:00
|
|
|
} else if (!strcmp(argv[i],"-i")) {
|
2010-08-04 12:36:03 -04:00
|
|
|
fprintf(stderr,
|
|
|
|
"Starting interactive mode using -i is deprecated. Interactive mode is started\n"
|
|
|
|
"by default when redis-cli is executed without a command to execute.\n"
|
|
|
|
);
|
2010-05-26 12:18:37 -04:00
|
|
|
} else if (!strcmp(argv[i],"-c")) {
|
2010-08-25 08:48:50 -04:00
|
|
|
fprintf(stderr,
|
|
|
|
"Reading last argument from standard input using -c is deprecated.\n"
|
|
|
|
"When standard input is connected to a pipe or regular file, it is\n"
|
|
|
|
"automatically used as last argument.\n"
|
|
|
|
);
|
2010-07-06 13:17:09 -04:00
|
|
|
} else if (!strcmp(argv[i],"-v")) {
|
2010-11-08 10:26:02 -05:00
|
|
|
printf("redis-cli shipped with Redis version %s (%s)\n", REDIS_VERSION, redisGitSHA1());
|
2010-07-06 13:17:09 -04:00
|
|
|
exit(0);
|
2009-03-22 05:30:00 -04:00
|
|
|
} else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return i;
|
|
|
|
}
|
|
|
|
|
|
|
|
static sds readArgFromStdin(void) {
|
|
|
|
char buf[1024];
|
|
|
|
sds arg = sdsempty();
|
|
|
|
|
|
|
|
while(1) {
|
|
|
|
int nread = read(fileno(stdin),buf,1024);
|
|
|
|
|
|
|
|
if (nread == 0) break;
|
|
|
|
else if (nread == -1) {
|
|
|
|
perror("Reading from standard input");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
arg = sdscatlen(arg,buf,nread);
|
|
|
|
}
|
|
|
|
return arg;
|
|
|
|
}
|
|
|
|
|
2010-01-15 16:42:29 -05:00
|
|
|
static void usage() {
|
2010-08-01 17:06:00 -04:00
|
|
|
fprintf(stderr, "usage: redis-cli [-iv] [-h host] [-p port] [-s /path/to/socket] [-a authpw] [-r repeat_times] [-n db_num] cmd arg1 arg2 arg3 ... argN\n");
|
2010-09-09 10:38:10 -04:00
|
|
|
fprintf(stderr, "usage: echo \"argN\" | redis-cli -x [options] cmd arg1 arg2 ... arg(N-1)\n\n");
|
|
|
|
fprintf(stderr, "example: cat /etc/passwd | redis-cli -x set my_passwd\n");
|
2010-01-15 16:42:29 -05:00
|
|
|
fprintf(stderr, "example: redis-cli get my_passwd\n");
|
|
|
|
fprintf(stderr, "example: redis-cli -r 100 lpush mylist x\n");
|
2010-03-04 10:36:30 -05:00
|
|
|
fprintf(stderr, "\nRun in interactive mode: redis-cli -i or just don't pass any command\n");
|
2010-01-15 16:42:29 -05:00
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
|
2010-03-02 10:14:14 -05:00
|
|
|
/* Turn the plain C strings into Sds strings */
|
|
|
|
static char **convertToSds(int count, char** args) {
|
|
|
|
int j;
|
2010-05-26 12:18:37 -04:00
|
|
|
char **sds = zmalloc(sizeof(char*)*count);
|
2010-03-02 10:14:14 -05:00
|
|
|
|
|
|
|
for(j = 0; j < count; j++)
|
|
|
|
sds[j] = sdsnew(args[j]);
|
|
|
|
|
|
|
|
return sds;
|
|
|
|
}
|
|
|
|
|
2010-04-26 12:39:39 -04:00
|
|
|
#define LINE_BUFLEN 4096
|
2010-03-02 10:14:14 -05:00
|
|
|
static void repl() {
|
2010-04-26 12:39:39 -04:00
|
|
|
int argc, j;
|
2010-08-05 05:36:39 -04:00
|
|
|
char *line;
|
|
|
|
sds *argv;
|
2010-03-02 10:14:14 -05:00
|
|
|
|
2010-08-25 04:05:50 -04:00
|
|
|
config.interactive = 1;
|
2010-03-24 06:58:38 -04:00
|
|
|
while((line = linenoise("redis> ")) != NULL) {
|
2010-03-23 10:25:32 -04:00
|
|
|
if (line[0] != '\0') {
|
2010-08-05 05:36:39 -04:00
|
|
|
argv = sdssplitargs(line,&argc);
|
2010-04-26 12:39:39 -04:00
|
|
|
linenoiseHistoryAdd(line);
|
2010-07-07 12:44:53 -04:00
|
|
|
if (config.historyfile) linenoiseHistorySave(config.historyfile);
|
2010-08-04 09:29:28 -04:00
|
|
|
if (argv == NULL) {
|
|
|
|
printf("Invalid argument(s)\n");
|
|
|
|
continue;
|
|
|
|
} else if (argc > 0) {
|
2010-04-26 12:39:39 -04:00
|
|
|
if (strcasecmp(argv[0],"quit") == 0 ||
|
|
|
|
strcasecmp(argv[0],"exit") == 0)
|
2010-08-24 12:39:34 -04:00
|
|
|
{
|
|
|
|
exit(0);
|
|
|
|
} else {
|
2010-11-02 13:08:30 -04:00
|
|
|
long long start_time = mstime(), elapsed;
|
2010-08-24 12:39:34 -04:00
|
|
|
|
2010-11-03 11:09:38 -04:00
|
|
|
if (cliSendCommand(argc,argv,1) != REDIS_OK) {
|
|
|
|
printf("Reconnecting... ");
|
|
|
|
fflush(stdout);
|
|
|
|
if (cliConnect(1) != REDIS_OK) exit(1);
|
|
|
|
printf("OK\n");
|
|
|
|
|
|
|
|
/* If we still cannot send the command,
|
|
|
|
* print error and abort. */
|
|
|
|
if (cliSendCommand(argc,argv,1) != REDIS_OK)
|
|
|
|
cliPrintContextErrorAndExit();
|
2010-08-24 12:39:34 -04:00
|
|
|
}
|
2010-11-02 13:08:30 -04:00
|
|
|
elapsed = mstime()-start_time;
|
2010-11-03 12:07:10 -04:00
|
|
|
if (elapsed >= 500) {
|
|
|
|
printf("(%.2fs)\n",(double)elapsed/1000);
|
|
|
|
}
|
2010-08-24 12:39:34 -04:00
|
|
|
}
|
2010-04-26 12:39:39 -04:00
|
|
|
}
|
|
|
|
/* Free the argument vector */
|
|
|
|
for (j = 0; j < argc; j++)
|
|
|
|
sdsfree(argv[j]);
|
2010-04-27 12:06:52 -04:00
|
|
|
zfree(argv);
|
2010-03-02 10:14:14 -05:00
|
|
|
}
|
2010-04-26 12:39:39 -04:00
|
|
|
/* linenoise() returns malloc-ed lines like readline() */
|
2010-03-23 10:25:32 -04:00
|
|
|
free(line);
|
2010-03-02 10:14:14 -05:00
|
|
|
}
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
|
2010-08-25 08:48:50 -04:00
|
|
|
static int noninteractive(int argc, char **argv) {
|
|
|
|
int retval = 0;
|
2010-09-09 10:38:10 -04:00
|
|
|
if (config.stdinarg) {
|
2010-08-25 08:48:50 -04:00
|
|
|
argv = zrealloc(argv, (argc+1)*sizeof(char*));
|
|
|
|
argv[argc] = readArgFromStdin();
|
|
|
|
retval = cliSendCommand(argc+1, argv, config.repeat);
|
|
|
|
} else {
|
|
|
|
/* stdin is probably a tty, can be tested with S_ISCHR(s.st_mode) */
|
|
|
|
retval = cliSendCommand(argc, argv, config.repeat);
|
|
|
|
}
|
|
|
|
return retval;
|
|
|
|
}
|
|
|
|
|
2009-03-22 05:30:00 -04:00
|
|
|
int main(int argc, char **argv) {
|
2010-03-02 10:14:14 -05:00
|
|
|
int firstarg;
|
2009-03-22 05:30:00 -04:00
|
|
|
|
|
|
|
config.hostip = "127.0.0.1";
|
|
|
|
config.hostport = 6379;
|
2010-08-01 17:06:00 -04:00
|
|
|
config.hostsocket = NULL;
|
2009-11-02 19:35:39 -05:00
|
|
|
config.repeat = 1;
|
2009-11-11 23:12:09 -05:00
|
|
|
config.dbnum = 0;
|
2010-08-25 04:05:50 -04:00
|
|
|
config.interactive = 0;
|
2010-05-14 10:38:09 -04:00
|
|
|
config.shutdown = 0;
|
2010-04-27 10:07:31 -04:00
|
|
|
config.monitor_mode = 0;
|
|
|
|
config.pubsub_mode = 0;
|
2010-04-29 12:07:35 -04:00
|
|
|
config.raw_output = 0;
|
2010-09-09 10:38:10 -04:00
|
|
|
config.stdinarg = 0;
|
2010-03-17 21:51:09 -04:00
|
|
|
config.auth = NULL;
|
2010-07-07 12:44:53 -04:00
|
|
|
config.historyfile = NULL;
|
2010-08-04 12:16:39 -04:00
|
|
|
config.tty = isatty(fileno(stdout)) || (getenv("FAKETTY") != NULL);
|
2010-08-04 11:46:56 -04:00
|
|
|
config.mb_sep = '\n';
|
2010-07-07 12:44:53 -04:00
|
|
|
|
|
|
|
if (getenv("HOME") != NULL) {
|
|
|
|
config.historyfile = malloc(256);
|
|
|
|
snprintf(config.historyfile,256,"%s/.rediscli_history",getenv("HOME"));
|
|
|
|
linenoiseHistoryLoad(config.historyfile);
|
|
|
|
}
|
2009-03-22 05:30:00 -04:00
|
|
|
|
|
|
|
firstarg = parseOptions(argc,argv);
|
|
|
|
argc -= firstarg;
|
|
|
|
argv += firstarg;
|
|
|
|
|
2010-11-03 11:09:38 -04:00
|
|
|
/* Try to connect */
|
|
|
|
if (cliConnect(0) != REDIS_OK) exit(1);
|
2010-03-23 09:54:49 -04:00
|
|
|
|
2010-08-04 12:36:03 -04:00
|
|
|
/* Start interactive mode when no command is provided */
|
|
|
|
if (argc == 0) repl();
|
2010-08-25 08:48:50 -04:00
|
|
|
/* Otherwise, we have some arguments to execute */
|
|
|
|
return noninteractive(argc,convertToSds(argc,argv));
|
2009-03-22 05:30:00 -04:00
|
|
|
}
|