summaryrefslogtreecommitdiff
path: root/src/redis-cli.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/redis-cli.c')
-rw-r--r--src/redis-cli.c150
1 files changed, 99 insertions, 51 deletions
diff --git a/src/redis-cli.c b/src/redis-cli.c
index 63d31f79a..7e1fe3934 100644
--- a/src/redis-cli.c
+++ b/src/redis-cli.c
@@ -227,7 +227,7 @@ static struct config {
int scan_mode;
int intrinsic_latency_mode;
int intrinsic_latency_duration;
- char *pattern;
+ sds pattern;
char *rdb_filename;
int bigkeys;
int memkeys;
@@ -237,6 +237,7 @@ static struct config {
char *auth;
int askpass;
char *user;
+ int quoted_input; /* Force input args to be treated as quoted strings */
int output; /* output mode, see OUTPUT_* defines */
int push_output; /* Should we display spontaneous PUSH replies */
sds mb_delim;
@@ -405,15 +406,17 @@ static void parseRedisUri(const char *uri) {
if (!strncasecmp(tlsscheme, curr, strlen(tlsscheme))) {
#ifdef USE_OPENSSL
config.tls = 1;
+ curr += strlen(tlsscheme);
#else
fprintf(stderr,"rediss:// is only supported when redis-cli is compiled with OpenSSL\n");
exit(1);
#endif
- } else if (strncasecmp(scheme, curr, strlen(scheme))) {
+ } else if (!strncasecmp(scheme, curr, strlen(scheme))) {
+ curr += strlen(scheme);
+ } else {
fprintf(stderr,"Invalid URI scheme\n");
exit(1);
}
- curr += strlen(scheme);
if (curr == end) return;
/* Extract user info. */
@@ -763,6 +766,23 @@ static void freeHintsCallback(void *ptr) {
* Networking / parsing
*--------------------------------------------------------------------------- */
+/* Unquote a null-terminated string and return it as a binary-safe sds. */
+static sds unquoteCString(char *str) {
+ int count;
+ sds *unquoted = sdssplitargs(str, &count);
+ sds res = NULL;
+
+ if (unquoted && count == 1) {
+ res = unquoted[0];
+ unquoted[0] = NULL;
+ }
+
+ if (unquoted)
+ sdsfreesplitres(unquoted, count);
+
+ return res;
+}
+
/* Send AUTH command to the server */
static int cliAuth(redisContext *ctx, char *user, char *auth) {
redisReply *reply;
@@ -1533,6 +1553,8 @@ static int parseOptions(int argc, char **argv) {
config.output = OUTPUT_RAW;
} else if (!strcmp(argv[i],"--no-raw")) {
config.output = OUTPUT_STANDARD;
+ } else if (!strcmp(argv[i],"--quoted-input")) {
+ config.quoted_input = 1;
} else if (!strcmp(argv[i],"--csv")) {
config.output = OUTPUT_CSV;
} else if (!strcmp(argv[i],"--latency")) {
@@ -1557,7 +1579,15 @@ static int parseOptions(int argc, char **argv) {
} else if (!strcmp(argv[i],"--scan")) {
config.scan_mode = 1;
} else if (!strcmp(argv[i],"--pattern") && !lastarg) {
- config.pattern = argv[++i];
+ sdsfree(config.pattern);
+ config.pattern = sdsnew(argv[++i]);
+ } else if (!strcmp(argv[i],"--quoted-pattern") && !lastarg) {
+ sdsfree(config.pattern);
+ config.pattern = unquoteCString(argv[++i]);
+ if (!config.pattern) {
+ fprintf(stderr,"Invalid quoted string specified for --quoted-pattern.\n");
+ exit(1);
+ }
} else if (!strcmp(argv[i],"--intrinsic-latency") && !lastarg) {
config.intrinsic_latency_mode = 1;
config.intrinsic_latency_duration = atoi(argv[++i]);
@@ -1841,6 +1871,7 @@ static void usage(void) {
" --raw Use raw formatting for replies (default when STDOUT is\n"
" not a tty).\n"
" --no-raw Force formatted output even when STDOUT is not a tty.\n"
+" --quoted-input Force input to be handled as quoted strings.\n"
" --csv Output in CSV format.\n"
" --show-pushes <yn> Whether to print RESP3 PUSH messages. Enabled by default when\n"
" STDOUT is a tty but can be overriden with --show-pushes no.\n"
@@ -1876,6 +1907,8 @@ static void usage(void) {
" --scan List all keys using the SCAN command.\n"
" --pattern <pat> Keys pattern when using the --scan, --bigkeys or --hotkeys\n"
" options (default: *).\n"
+" --quoted-pattern <pat> Same as --pattern, but the specified string can be\n"
+" quoted, in order to pass an otherwise non binary-safe string.\n"
" --intrinsic-latency <sec> Run a test to measure intrinsic system latency.\n"
" The test will run for the specified amount of seconds.\n"
" --eval <file> Send an EVAL command using the Lua script at <file>.\n"
@@ -1901,6 +1934,7 @@ static void usage(void) {
" redis-cli get mypasswd\n"
" redis-cli -r 100 lpush mylist x\n"
" redis-cli -r 100 -i 1 info | grep used_memory_human:\n"
+" redis-cli --quoted-input set '\"null-\\x00-separated\"' value\n"
" redis-cli --eval myscript.lua key1 key2 , arg1 arg2 arg3\n"
" redis-cli --scan --pattern '*:12345*'\n"
"\n"
@@ -1930,22 +1964,28 @@ static int confirmWithYes(char *msg, int ignore_force) {
return (nread != 0 && !strcmp("yes", buf));
}
-/* Turn the plain C strings into Sds strings */
-static char **convertToSds(int count, char** args) {
- int j;
- char **sds = zmalloc(sizeof(char*)*count);
-
- for(j = 0; j < count; j++)
- sds[j] = sdsnew(args[j]);
-
- return sds;
-}
+/* Create an sds array from argv, either as-is or by dequoting every
+ * element. When quoted is non-zero, may return a NULL to indicate an
+ * invalid quoted string.
+ */
+static sds *getSdsArrayFromArgv(int argc, char **argv, int quoted) {
+ sds *res = sds_malloc(sizeof(sds) * argc);
+
+ for (int j = 0; j < argc; j++) {
+ if (quoted) {
+ sds unquoted = unquoteCString(argv[j]);
+ if (!unquoted) {
+ while (--j >= 0) sdsfree(res[j]);
+ sds_free(res);
+ return NULL;
+ }
+ res[j] = unquoted;
+ } else {
+ res[j] = sdsnew(argv[j]);
+ }
+ }
-static void freeConvertedSds(int count, char **sds) {
- int j;
- for (j = 0; j < count; j++)
- sdsfree(sds[j]);
- zfree(sds);
+ return res;
}
static int issueCommandRepeat(int argc, char **argv, long repeat) {
@@ -2178,17 +2218,19 @@ static void repl(void) {
static int noninteractive(int argc, char **argv) {
int retval = 0;
-
- argv = convertToSds(argc, argv);
+ sds *sds_args = getSdsArrayFromArgv(argc, argv, config.quoted_input);
+ if (!sds_args) {
+ printf("Invalid quoted string\n");
+ return 1;
+ }
if (config.stdinarg) {
- argv = zrealloc(argv, (argc+1)*sizeof(char*));
- argv[argc] = readArgFromStdin();
- retval = issueCommand(argc+1, argv);
- sdsfree(argv[argc]);
- } else {
- retval = issueCommand(argc, argv);
+ sds_args = sds_realloc(sds_args, (argc + 1) * sizeof(sds));
+ sds_args[argc] = readArgFromStdin();
+ argc++;
}
- freeConvertedSds(argc, argv);
+
+ retval = issueCommand(argc, sds_args);
+ sdsfreesplitres(sds_args, argc);
return retval;
}
@@ -2913,8 +2955,12 @@ static int clusterManagerGetAntiAffinityScore(clusterManagerNodeArray *ipnodes,
else types = sdsempty();
/* Master type 'm' is always set as the first character of the
* types string. */
- if (!node->replicate) types = sdscatprintf(types, "m%s", types);
- else types = sdscat(types, "s");
+ if (node->replicate) types = sdscat(types, "s");
+ else {
+ sds s = sdscatsds(sdsnew("m"), types);
+ sdsfree(types);
+ types = s;
+ }
dictReplace(related, key, types);
}
/* Now it's trivial to check, for each related group having the
@@ -7156,7 +7202,10 @@ static void getRDB(clusterManagerNode *node) {
redisFree(s); /* Close the connection ASAP as fsync() may take time. */
if (node)
node->context = NULL;
- fsync(fd);
+ if (fsync(fd) == -1) {
+ fprintf(stderr,"Fail to fsync '%s': %s\n", filename, strerror(errno));
+ exit(1);
+ }
close(fd);
if (node) {
sdsfree(filename);
@@ -7332,8 +7381,8 @@ static redisReply *sendScan(unsigned long long *it) {
redisReply *reply;
if (config.pattern)
- reply = redisCommand(context,"SCAN %llu MATCH %s",
- *it,config.pattern);
+ reply = redisCommand(context, "SCAN %llu MATCH %b",
+ *it, config.pattern, sdslen(config.pattern));
else
reply = redisCommand(context,"SCAN %llu",*it);
@@ -7368,8 +7417,14 @@ static int getDbSize(void) {
reply = redisCommand(context, "DBSIZE");
- if(reply == NULL || reply->type != REDIS_REPLY_INTEGER) {
- fprintf(stderr, "Couldn't determine DBSIZE!\n");
+ if (reply == NULL) {
+ fprintf(stderr, "\nI/O error\n");
+ exit(1);
+ } else if (reply->type == REDIS_REPLY_ERROR) {
+ fprintf(stderr, "Couldn't determine DBSIZE: %s\n", reply->str);
+ exit(1);
+ } else if (reply->type != REDIS_REPLY_INTEGER) {
+ fprintf(stderr, "Non INTEGER response from DBSIZE!\n");
exit(1);
}
@@ -7945,23 +8000,16 @@ static void scanMode(void) {
unsigned long long cur = 0;
do {
- if (config.pattern)
- reply = redisCommand(context,"SCAN %llu MATCH %s",
- cur,config.pattern);
- else
- reply = redisCommand(context,"SCAN %llu",cur);
- if (reply == NULL) {
- printf("I/O error\n");
- exit(1);
- } else if (reply->type == REDIS_REPLY_ERROR) {
- printf("ERROR: %s\n", reply->str);
- exit(1);
- } else {
- unsigned int j;
-
- cur = strtoull(reply->element[0]->str,NULL,10);
- for (j = 0; j < reply->element[1]->elements; j++)
+ reply = sendScan(&cur);
+ for (unsigned int j = 0; j < reply->element[1]->elements; j++) {
+ if (config.output == OUTPUT_STANDARD) {
+ sds out = sdscatrepr(sdsempty(), reply->element[1]->element[j]->str,
+ reply->element[1]->element[j]->len);
+ printf("%s\n", out);
+ sdsfree(out);
+ } else {
printf("%s\n", reply->element[1]->element[j]->str);
+ }
}
freeReplyObject(reply);
} while(cur != 0);