From 1797330e2eb05a2488eeb50024469d2b1f885f70 Mon Sep 17 00:00:00 2001 From: Binbin Date: Fri, 11 Mar 2022 00:20:01 +0800 Subject: [PATCH] Initialize help when using redis-cli help or redis-cli ? (#10382) The following usage will output an empty newline: ``` > redis-cli help set empty line ``` The reason is that in interactive mode, we have called `cliInitHelp`, which initializes help. When using `redis-cli help xxx` or `redis-cli help ? xxx`, we can't match the command due to empty `helpEntries`, so we output an empty newline. In this commit, we will call `cliInitHelp` to init the help. Note that in this case, we need to call `cliInitHelp` (COMMAND DOCS) every time, which i think is acceptable. So now the output will look like: ``` [redis]# src/redis-cli help get GET key summary: Get the value of a key since: 1.0.0 group: string [redis]# ``` Fixes #10378 This PR also fix a redis-cli crash when using `--ldb --eval`: ``` [root]# src/redis-cli --ldb --eval test.lua test 1 Lua debugging session started, please use: quit -- End the session. restart -- Restart the script in debug mode again. help -- Show Lua script debugging commands. * Stopped at 1, stop reason = step over -> 1 local num = redis.call('GET', KEYS[1]); redis-cli: redis-cli.c:718: cliCountCommands: Assertion `commandTable->element[i]->type == 1' failed. Aborted ``` Because in ldb mode, `COMMAND DOCS` or `COMMAND` will return an array, only with one element, and the type is `REDIS_REPLY_STATUS`, the result is ` Unknown Redis Lua debugger command or wrong number of arguments`. So if we are in the ldb mode, and init the Redis HELP, we will get the wrong response and crash the redis-cli. In ldb mode we don't initialize HELP, help is only initialized after the lua debugging session ends. It was broken in #10043 --- src/redis-cli.c | 49 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/src/redis-cli.c b/src/redis-cli.c index 809f0ce78..a7f38be06 100644 --- a/src/redis-cli.c +++ b/src/redis-cli.c @@ -282,7 +282,7 @@ static void usage(int err); static void slaveMode(void); char *redisGitSHA1(void); char *redisGitDirty(void); -static int cliConnect(int force); +static int cliConnect(int flags); static char *getInfoField(char *info, char *field); static long getLongInfoField(char *info, char *field); @@ -800,7 +800,12 @@ static void cliInitHelp(void) { redisReply *commandTable; dict *groups; - if (cliConnect(CC_QUIET) == REDIS_ERR) return; + if (cliConnect(CC_QUIET) == REDIS_ERR) { + /* Can not connect to the server, but we still want to provide + * help, generate it only from the old help.h data instead. */ + cliOldInitHelp(); + return; + } commandTable = redisCommand(context, "COMMAND DOCS"); if (commandTable == NULL || commandTable->type == REDIS_REPLY_ERROR) { /* New COMMAND DOCS subcommand not supported - generate help from old help.h data instead. */ @@ -810,7 +815,7 @@ static void cliInitHelp(void) { return; }; if (commandTable->type != REDIS_REPLY_MAP && commandTable->type != REDIS_REPLY_ARRAY) return; - + /* Scan the array reported by COMMAND DOCS and fill in the entries */ helpEntriesLen = cliCountCommands(commandTable); helpEntries = zmalloc(sizeof(helpEntry)*helpEntriesLen); @@ -870,6 +875,12 @@ static void cliOutputHelp(int argc, char **argv) { group = argv[0]+1; } + if (helpEntries == NULL) { + /* Initialize the help using the results of the COMMAND command. + * In case we are using redis-cli help XXX, we need to init it. */ + cliInitHelp(); + } + assert(argc > 0); for (i = 0; i < helpEntriesLen; i++) { entry = &helpEntries[i]; @@ -1713,12 +1724,6 @@ static int cliSendCommand(int argc, char **argv, long repeat) { size_t *argvlen; int j, output_raw; - if (!config.eval_ldb && /* In debugging mode, let's pass "help" to Redis. */ - (!strcasecmp(command,"help") || !strcasecmp(command,"?"))) { - cliOutputHelp(--argc, ++argv); - return REDIS_OK; - } - if (context == NULL) return REDIS_ERR; output_raw = 0; @@ -2420,6 +2425,17 @@ static int confirmWithYes(char *msg, int ignore_force) { } static int issueCommandRepeat(int argc, char **argv, long repeat) { + /* In Lua debugging mode, we want to pass the "help" to Redis to get + * it's own HELP message, rather than handle it by the CLI, see ldbRepl. + * + * For the normal Redis HELP, we can process it without a connection. */ + if (!config.eval_ldb && + (!strcasecmp(argv[0],"help") || !strcasecmp(argv[0],"?"))) + { + cliOutputHelp(--argc, ++argv); + return REDIS_OK; + } + while (1) { if (config.cluster_reissue_command || context == NULL || context->err == REDIS_ERR_IO || context->err == REDIS_ERR_EOF) @@ -2439,6 +2455,8 @@ static int issueCommandRepeat(int argc, char **argv, long repeat) { } if (cliSendCommand(argc,argv,repeat) != REDIS_OK) { cliPrintContextError(); + redisFree(context); + context = NULL; return REDIS_ERR; } @@ -2576,8 +2594,13 @@ static void repl(void) { int argc; sds *argv; - /* Initialize the help using the results of the COMMAND command. */ - cliInitHelp(); + /* There is no need to initialize redis HELP when we are in lua debugger mode. + * It has its own HELP and commands (COMMAND or COMMAND DOCS will fail and got nothing). + * We will initialize the redis HELP after the Lua debugging session ended.*/ + if (!config.eval_ldb) { + /* Initialize the help using the results of the COMMAND command. */ + cliInitHelp(); + } config.interactive = 1; linenoiseSetMultiLine(1); @@ -2679,6 +2702,7 @@ static void repl(void) { printf("\n(Lua debugging session ended%s)\n\n", config.eval_ldb_sync ? "" : " -- dataset changes rolled back"); + cliInitHelp(); } elapsed = mstime()-start_time; @@ -9005,10 +9029,11 @@ int main(int argc, char **argv) { } /* Otherwise, we have some arguments to execute */ - if (cliConnect(0) != REDIS_OK) exit(1); if (config.eval) { + if (cliConnect(0) != REDIS_OK) exit(1); return evalMode(argc,argv); } else { + cliConnect(CC_QUIET); return noninteractive(argc,argv); } }