diff --git a/src/cli_common.c b/src/cli_common.c index 7d98addfc..49cae3eab 100644 --- a/src/cli_common.c +++ b/src/cli_common.c @@ -424,3 +424,21 @@ sds cliVersion(void) { } return version; } + +/* This is a wrapper to call redisConnect or redisConnectWithTimeout. */ +redisContext *redisConnectWrapper(const char *ip, int port, const struct timeval tv) { + if (tv.tv_sec == 0 && tv.tv_usec == 0) { + return redisConnect(ip, port); + } else { + return redisConnectWithTimeout(ip, port, tv); + } +} + +/* This is a wrapper to call redisConnectUnix or redisConnectUnixWithTimeout. */ +redisContext *redisConnectUnixWrapper(const char *path, const struct timeval tv) { + if (tv.tv_sec == 0 && tv.tv_usec == 0) { + return redisConnectUnix(path); + } else { + return redisConnectUnixWithTimeout(path, tv); + } +} diff --git a/src/cli_common.h b/src/cli_common.h index 3377eaf3a..a5b8e44a2 100644 --- a/src/cli_common.h +++ b/src/cli_common.h @@ -53,4 +53,7 @@ sds escapeJsonString(sds s, const char *p, size_t len); sds cliVersion(void); +redisContext *redisConnectWrapper(const char *ip, int port, const struct timeval tv); +redisContext *redisConnectUnixWrapper(const char *path, const struct timeval tv); + #endif /* __CLICOMMON_H */ diff --git a/src/redis-cli.c b/src/redis-cli.c index ed6f59001..13275a826 100644 --- a/src/redis-cli.c +++ b/src/redis-cli.c @@ -211,6 +211,7 @@ static int createClusterManagerCommand(char *cmdname, int argc, char **argv); static redisContext *context; static struct config { cliConnInfo conn_info; + struct timeval connect_timeout; char *hostsocket; int tls; cliSSLconfig sslconfig; @@ -1648,9 +1649,10 @@ static int cliConnect(int flags) { /* Do not use hostsocket when we got redirected in cluster mode */ if (config.hostsocket == NULL || (config.cluster_mode && config.cluster_reissue_command)) { - context = redisConnect(config.conn_info.hostip,config.conn_info.hostport); + context = redisConnectWrapper(config.conn_info.hostip, config.conn_info.hostport, + config.connect_timeout); } else { - context = redisConnectUnix(config.hostsocket); + context = redisConnectUnixWrapper(config.hostsocket, config.connect_timeout); } if (!context->err && config.tls) { @@ -2593,7 +2595,8 @@ static redisReply *reconnectingRedisCommand(redisContext *c, const char *fmt, .. fflush(stdout); redisFree(c); - c = redisConnect(config.conn_info.hostip,config.conn_info.hostport); + c = redisConnectWrapper(config.conn_info.hostip, config.conn_info.hostport, + config.connect_timeout); if (!c->err && config.tls) { const char *err = NULL; if (cliSecureConnection(c, config.sslconfig, &err) == REDIS_ERR && err) { @@ -2648,6 +2651,15 @@ static int parseOptions(int argc, char **argv) { fprintf(stderr, "Invalid server port.\n"); exit(1); } + } else if (!strcmp(argv[i],"-t") && !lastarg) { + char *eptr; + double seconds = strtod(argv[++i], &eptr); + if (eptr[0] != '\0' || isnan(seconds) || seconds < 0.0) { + fprintf(stderr, "Invalid connection timeout for -t.\n"); + exit(1); + } + config.connect_timeout.tv_sec = (long long)seconds; + config.connect_timeout.tv_usec = ((long long)(seconds * 1000000)) % 1000000; } else if (!strcmp(argv[i],"-s") && !lastarg) { config.hostsocket = argv[++i]; } else if (!strcmp(argv[i],"-r") && !lastarg) { @@ -3011,6 +3023,8 @@ static void usage(int err) { "Usage: redis-cli [OPTIONS] [cmd [arg [arg ...]]]\n" " -h Server hostname (default: 127.0.0.1).\n" " -p Server port (default: 6379).\n" +" -t Server connection timeout in seconds (decimals allowed).\n" +" Default timeout is 0, meaning no limit, depending on the OS.\n" " -s Server socket (overrides hostname and port).\n" " -a Password to use when connecting to the server.\n" " You can also use the " REDIS_CLI_AUTH_ENV " environment\n" @@ -4072,7 +4086,7 @@ cleanup: static int clusterManagerNodeConnect(clusterManagerNode *node) { if (node->context) redisFree(node->context); - node->context = redisConnect(node->ip, node->port); + node->context = redisConnectWrapper(node->ip, node->port, config.connect_timeout); if (!node->context->err && config.tls) { const char *err = NULL; if (cliSecureConnection(node->context, config.sslconfig, &err) == REDIS_ERR && err) { @@ -7897,7 +7911,7 @@ static int clusterManagerCommandImport(int argc, char **argv) { char *reply_err = NULL; redisReply *src_reply = NULL; // Connect to the source node. - redisContext *src_ctx = redisConnect(src_ip, src_port); + redisContext *src_ctx = redisConnectWrapper(src_ip, src_port, config.connect_timeout); if (src_ctx->err) { success = 0; fprintf(stderr,"Could not connect to Redis at %s:%d: %s.\n", src_ip, @@ -9832,6 +9846,8 @@ int main(int argc, char **argv) { memset(&config.sslconfig, 0, sizeof(config.sslconfig)); config.conn_info.hostip = sdsnew("127.0.0.1"); config.conn_info.hostport = 6379; + config.connect_timeout.tv_sec = 0; + config.connect_timeout.tv_usec = 0; config.hostsocket = NULL; config.repeat = 1; config.interval = 0;