Merge unstable into 6.2
This commit is contained in:
commit
c83db6bf5a
75
.github/workflows/daily.yml
vendored
75
.github/workflows/daily.yml
vendored
@ -48,6 +48,25 @@ jobs:
|
||||
- name: cluster tests
|
||||
run: ./runtest-cluster
|
||||
|
||||
test-ubuntu-no-malloc-usable-size:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: make MALLOC=libc CFLAGS=-DNO_MALLOC_USABLE_SIZE
|
||||
- name: test
|
||||
run: |
|
||||
sudo apt-get install tcl8.6
|
||||
./runtest --accurate --verbose --dump-logs
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --verbose
|
||||
- name: sentinel tests
|
||||
run: ./runtest-sentinel
|
||||
- name: cluster tests
|
||||
run: ./runtest-cluster
|
||||
|
||||
test-ubuntu-32bit:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
@ -132,6 +151,22 @@ jobs:
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --valgrind --no-latency --verbose --clients 1
|
||||
|
||||
test-valgrind-no-malloc-usable-size:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
timeout-minutes: 14400
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: make valgrind CFLAGS="-DNO_MALLOC_USABLE_SIZE"
|
||||
- name: test
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install tcl8.6 valgrind -y
|
||||
./runtest --valgrind --verbose --clients 1 --dump-logs
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --valgrind --no-latency --verbose --clients 1
|
||||
|
||||
test-centos7-jemalloc:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'redis/redis'
|
||||
@ -221,3 +256,43 @@ jobs:
|
||||
MAKE=gmake ./runtest-moduleapi --verbose &&
|
||||
./runtest-sentinel &&
|
||||
./runtest-cluster
|
||||
|
||||
test-alpine-jemalloc:
|
||||
runs-on: ubuntu-latest
|
||||
container: alpine:latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: |
|
||||
apk add build-base
|
||||
make REDIS_CFLAGS='-Werror'
|
||||
- name: test
|
||||
run: |
|
||||
apk add tcl procps
|
||||
./runtest --accurate --verbose --dump-logs
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --verbose
|
||||
- name: sentinel tests
|
||||
run: ./runtest-sentinel
|
||||
- name: cluster tests
|
||||
run: ./runtest-cluster
|
||||
|
||||
test-alpine-libc-malloc:
|
||||
runs-on: ubuntu-latest
|
||||
container: alpine:latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: make
|
||||
run: |
|
||||
apk add build-base
|
||||
make REDIS_CFLAGS='-Werror' USE_JEMALLOC=no CFLAGS=-DUSE_MALLOC_USABLE_SIZE
|
||||
- name: test
|
||||
run: |
|
||||
apk add tcl procps
|
||||
./runtest --accurate --verbose --dump-logs
|
||||
- name: module api test
|
||||
run: ./runtest-moduleapi --verbose
|
||||
- name: sentinel tests
|
||||
run: ./runtest-sentinel
|
||||
- name: cluster tests
|
||||
run: ./runtest-cluster
|
||||
|
@ -21,7 +21,12 @@ NODEPS:=clean distclean
|
||||
|
||||
# Default settings
|
||||
STD=-pedantic -DREDIS_STATIC=''
|
||||
|
||||
# Use -Wno-c11-extensions on clang, either where explicitly used or on
|
||||
# platforms we can assume it's being used.
|
||||
ifneq (,$(findstring clang,$(CC)))
|
||||
STD+=-Wno-c11-extensions
|
||||
else
|
||||
ifneq (,$(findstring FreeBSD,$(uname_S)))
|
||||
STD+=-Wno-c11-extensions
|
||||
endif
|
||||
|
13
src/acl.c
13
src/acl.c
@ -1054,7 +1054,6 @@ void ACLInit(void) {
|
||||
UsersToLoad = listCreate();
|
||||
ACLLog = listCreate();
|
||||
ACLInitDefaultUser();
|
||||
server.requirepass = NULL; /* Only used for backward compatibility. */
|
||||
}
|
||||
|
||||
/* Check the username and password pair and return C_OK if they are valid,
|
||||
@ -2251,3 +2250,15 @@ void authCommand(client *c) {
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the password for the "default" ACL user. This implements supports for
|
||||
* requirepass config, so passing in NULL will set the user to be nopass. */
|
||||
void ACLUpdateDefaultUserPassword(sds password) {
|
||||
ACLSetUser(DefaultUser,"resetpass",-1);
|
||||
if (password) {
|
||||
sds aclop = sdscatlen(sdsnew(">"), password, sdslen(password));
|
||||
ACLSetUser(DefaultUser,aclop,sdslen(aclop));
|
||||
sdsfree(aclop);
|
||||
} else {
|
||||
ACLSetUser(DefaultUser,"nopass",-1);
|
||||
}
|
||||
}
|
||||
|
@ -499,7 +499,7 @@ void clusterInit(void) {
|
||||
if (saveconf) clusterSaveConfigOrDie(1);
|
||||
|
||||
/* We need a listening TCP port for our cluster messaging needs. */
|
||||
server.cfd_count = 0;
|
||||
server.cfd.count = 0;
|
||||
|
||||
/* Port sanity check II
|
||||
* The other handshake port check is triggered too late to stop
|
||||
@ -512,19 +512,11 @@ void clusterInit(void) {
|
||||
"Your Redis port number must be 55535 or less.");
|
||||
exit(1);
|
||||
}
|
||||
if (listenToPort(port+CLUSTER_PORT_INCR,
|
||||
server.cfd,&server.cfd_count) == C_ERR)
|
||||
{
|
||||
if (listenToPort(port+CLUSTER_PORT_INCR, &server.cfd) == C_ERR) {
|
||||
exit(1);
|
||||
} else {
|
||||
int j;
|
||||
|
||||
for (j = 0; j < server.cfd_count; j++) {
|
||||
if (aeCreateFileEvent(server.el, server.cfd[j], AE_READABLE,
|
||||
clusterAcceptHandler, NULL) == AE_ERR)
|
||||
serverPanic("Unrecoverable error creating Redis Cluster "
|
||||
"file event.");
|
||||
}
|
||||
if (createSocketAcceptHandler(&server.cfd, clusterAcceptHandler) != C_OK) {
|
||||
serverPanic("Unrecoverable error creating Redis Cluster socket accept handler.");
|
||||
}
|
||||
|
||||
/* The slots -> keys map is a radix tree. Initialize it here. */
|
||||
|
143
src/config.c
143
src/config.c
@ -504,8 +504,6 @@ void loadServerConfigFromString(char *config) {
|
||||
}
|
||||
} else if (!strcasecmp(argv[0],"include") && argc == 2) {
|
||||
loadServerConfig(argv[1], 0, NULL);
|
||||
} else if ((!strcasecmp(argv[0],"client-query-buffer-limit")) && argc == 2) {
|
||||
server.client_max_querybuf_len = memtoll(argv[1],NULL);
|
||||
} else if ((!strcasecmp(argv[0],"slaveof") ||
|
||||
!strcasecmp(argv[0],"replicaof")) && argc == 3) {
|
||||
slaveof_linenum = linenum;
|
||||
@ -521,26 +519,6 @@ void loadServerConfigFromString(char *config) {
|
||||
err = "Invalid master port"; goto loaderr;
|
||||
}
|
||||
server.repl_state = REPL_STATE_CONNECT;
|
||||
} else if (!strcasecmp(argv[0],"requirepass") && argc == 2) {
|
||||
if (sdslen(argv[1]) > CONFIG_AUTHPASS_MAX_LEN) {
|
||||
err = "Password is longer than CONFIG_AUTHPASS_MAX_LEN";
|
||||
goto loaderr;
|
||||
}
|
||||
/* The old "requirepass" directive just translates to setting
|
||||
* a password to the default user. The only thing we do
|
||||
* additionally is to remember the cleartext password in this
|
||||
* case, for backward compatibility with Redis <= 5. */
|
||||
ACLSetUser(DefaultUser,"resetpass",-1);
|
||||
sdsfree(server.requirepass);
|
||||
server.requirepass = NULL;
|
||||
if (sdslen(argv[1])) {
|
||||
sds aclop = sdscatlen(sdsnew(">"), argv[1], sdslen(argv[1]));
|
||||
ACLSetUser(DefaultUser,aclop,sdslen(aclop));
|
||||
sdsfree(aclop);
|
||||
server.requirepass = sdsdup(argv[1]);
|
||||
} else {
|
||||
ACLSetUser(DefaultUser,"nopass",-1);
|
||||
}
|
||||
} else if (!strcasecmp(argv[0],"list-max-ziplist-entries") && argc == 2){
|
||||
/* DEAD OPTION */
|
||||
} else if (!strcasecmp(argv[0],"list-max-ziplist-value") && argc == 2) {
|
||||
@ -750,23 +728,22 @@ void configSetCommand(client *c) {
|
||||
if (0) { /* this starts the config_set macros else-if chain. */
|
||||
|
||||
/* Special fields that can't be handled with general macros. */
|
||||
config_set_special_field("requirepass") {
|
||||
if (sdslen(o->ptr) > CONFIG_AUTHPASS_MAX_LEN) goto badfmt;
|
||||
/* The old "requirepass" directive just translates to setting
|
||||
* a password to the default user. The only thing we do
|
||||
* additionally is to remember the cleartext password in this
|
||||
* case, for backward compatibility with Redis <= 5. */
|
||||
ACLSetUser(DefaultUser,"resetpass",-1);
|
||||
sdsfree(server.requirepass);
|
||||
server.requirepass = NULL;
|
||||
if (sdslen(o->ptr)) {
|
||||
sds aclop = sdscatlen(sdsnew(">"), o->ptr, sdslen(o->ptr));
|
||||
ACLSetUser(DefaultUser,aclop,sdslen(aclop));
|
||||
sdsfree(aclop);
|
||||
server.requirepass = sdsdup(o->ptr);
|
||||
} else {
|
||||
ACLSetUser(DefaultUser,"nopass",-1);
|
||||
config_set_special_field("bind") {
|
||||
int vlen;
|
||||
sds *v = sdssplitlen(o->ptr,sdslen(o->ptr)," ",1,&vlen);
|
||||
|
||||
if (vlen < 1 || vlen > CONFIG_BINDADDR_MAX) {
|
||||
addReplyError(c, "Too many bind addresses specified.");
|
||||
sdsfreesplitres(v, vlen);
|
||||
return;
|
||||
}
|
||||
|
||||
if (changeBindAddr(v, vlen) == C_ERR) {
|
||||
addReplyError(c, "Failed to bind to specified addresses.");
|
||||
sdsfreesplitres(v, vlen);
|
||||
return;
|
||||
}
|
||||
sdsfreesplitres(v, vlen);
|
||||
} config_set_special_field("save") {
|
||||
int vlen, j;
|
||||
sds *v = sdssplitlen(o->ptr,sdslen(o->ptr)," ",1,&vlen);
|
||||
@ -876,10 +853,6 @@ void configSetCommand(client *c) {
|
||||
enableWatchdog(ll);
|
||||
else
|
||||
disableWatchdog();
|
||||
/* Memory fields.
|
||||
* config_set_memory_field(name,var) */
|
||||
} config_set_memory_field(
|
||||
"client-query-buffer-limit",server.client_max_querybuf_len) {
|
||||
/* Everything else is an error... */
|
||||
} config_set_else {
|
||||
addReplyErrorFormat(c,"Unsupported CONFIG parameter: %s",
|
||||
@ -959,7 +932,6 @@ void configGetCommand(client *c) {
|
||||
config_get_string_field("logfile",server.logfile);
|
||||
|
||||
/* Numerical values */
|
||||
config_get_numerical_field("client-query-buffer-limit",server.client_max_querybuf_len);
|
||||
config_get_numerical_field("watchdog-period",server.watchdog_period);
|
||||
|
||||
/* Everything we can't handle with macros follows. */
|
||||
@ -1046,16 +1018,6 @@ void configGetCommand(client *c) {
|
||||
sdsfree(aux);
|
||||
matches++;
|
||||
}
|
||||
if (stringmatch(pattern,"requirepass",1)) {
|
||||
addReplyBulkCString(c,"requirepass");
|
||||
sds password = server.requirepass;
|
||||
if (password) {
|
||||
addReplyBulkCBuffer(c,password,sdslen(password));
|
||||
} else {
|
||||
addReplyBulkCString(c,"");
|
||||
}
|
||||
matches++;
|
||||
}
|
||||
|
||||
if (stringmatch(pattern,"oom-score-adj-values",0)) {
|
||||
sds buf = sdsempty();
|
||||
@ -1564,26 +1526,6 @@ void rewriteConfigBindOption(struct rewriteConfigState *state) {
|
||||
rewriteConfigRewriteLine(state,option,line,force);
|
||||
}
|
||||
|
||||
/* Rewrite the requirepass option. */
|
||||
void rewriteConfigRequirepassOption(struct rewriteConfigState *state, char *option) {
|
||||
int force = 1;
|
||||
sds line;
|
||||
sds password = server.requirepass;
|
||||
|
||||
/* If there is no password set, we don't want the requirepass option
|
||||
* to be present in the configuration at all. */
|
||||
if (password == NULL) {
|
||||
rewriteConfigMarkAsProcessed(state,option);
|
||||
return;
|
||||
}
|
||||
|
||||
line = sdsnew(option);
|
||||
line = sdscatlen(line, " ", 1);
|
||||
line = sdscatsds(line, password);
|
||||
|
||||
rewriteConfigRewriteLine(state,option,line,force);
|
||||
}
|
||||
|
||||
/* Glue together the configuration lines in the current configuration
|
||||
* rewrite state into a single string, stripping multiple empty lines. */
|
||||
sds rewriteConfigGetContentFromState(struct rewriteConfigState *state) {
|
||||
@ -1740,8 +1682,6 @@ int rewriteConfig(char *path, int force_all) {
|
||||
rewriteConfigUserOption(state);
|
||||
rewriteConfigDirOption(state);
|
||||
rewriteConfigSlaveofOption(state,"replicaof");
|
||||
rewriteConfigRequirepassOption(state,"requirepass");
|
||||
rewriteConfigBytesOption(state,"client-query-buffer-limit",server.client_max_querybuf_len,PROTO_MAX_QUERYBUF_LEN);
|
||||
rewriteConfigStringOption(state,"cluster-config-file",server.cluster_configfile,CONFIG_DEFAULT_CLUSTER_CONFIG_FILE);
|
||||
rewriteConfigNotifykeyspaceeventsOption(state);
|
||||
rewriteConfigClientoutputbufferlimitOption(state);
|
||||
@ -2267,6 +2207,20 @@ static int updateHZ(long long val, long long prev, const char **err) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int updatePort(long long val, long long prev, const char **err) {
|
||||
/* Do nothing if port is unchanged */
|
||||
if (val == prev) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (changeListenPort(val, &server.ipfd, acceptTcpHandler) == C_ERR) {
|
||||
*err = "Unable to listen on this port. Check server logs.";
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int updateJemallocBgThread(int val, int prev, const char **err) {
|
||||
UNUSED(prev);
|
||||
UNUSED(err);
|
||||
@ -2368,6 +2322,18 @@ static int updateOOMScoreAdj(int val, int prev, const char **err) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int updateRequirePass(sds val, sds prev, const char **err) {
|
||||
UNUSED(prev);
|
||||
UNUSED(err);
|
||||
/* The old "requirepass" directive just translates to setting
|
||||
* a password to the default user. The only thing we do
|
||||
* additionally is to remember the cleartext password in this
|
||||
* case, for backward compatibility with Redis <= 5. */
|
||||
ACLUpdateDefaultUserPassword(val);
|
||||
return 1;
|
||||
}
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
static int updateTlsCfg(char *val, char *prev, const char **err) {
|
||||
UNUSED(val);
|
||||
@ -2393,6 +2359,27 @@ static int updateTlsCfgInt(long long val, long long prev, const char **err) {
|
||||
UNUSED(prev);
|
||||
return updateTlsCfg(NULL, NULL, err);
|
||||
}
|
||||
|
||||
static int updateTLSPort(long long val, long long prev, const char **err) {
|
||||
/* Do nothing if port is unchanged */
|
||||
if (val == prev) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Configure TLS if tls is enabled */
|
||||
if (prev == 0 && tlsConfigure(&server.tls_ctx_config) == C_ERR) {
|
||||
*err = "Unable to update TLS configuration. Check server logs.";
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (changeListenPort(val, &server.tlsfd, acceptTLSHandler) == C_ERR) {
|
||||
*err = "Unable to listen on this port. Check server logs.";
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif /* USE_OPENSSL */
|
||||
|
||||
standardConfig configs[] = {
|
||||
@ -2458,6 +2445,7 @@ standardConfig configs[] = {
|
||||
|
||||
/* SDS Configs */
|
||||
createSDSConfig("masterauth", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.masterauth, NULL, NULL, NULL),
|
||||
createSDSConfig("requirepass", NULL, MODIFIABLE_CONFIG, EMPTY_STRING_IS_NULL, server.requirepass, NULL, NULL, updateRequirePass),
|
||||
|
||||
/* Enum Configs */
|
||||
createEnumConfig("supervised", NULL, IMMUTABLE_CONFIG, supervised_mode_enum, server.supervised_mode, SUPERVISED_NONE, NULL, NULL),
|
||||
@ -2472,7 +2460,7 @@ standardConfig configs[] = {
|
||||
|
||||
/* Integer configs */
|
||||
createIntConfig("databases", NULL, IMMUTABLE_CONFIG, 1, INT_MAX, server.dbnum, 16, INTEGER_CONFIG, NULL, NULL),
|
||||
createIntConfig("port", NULL, IMMUTABLE_CONFIG, 0, 65535, server.port, 6379, INTEGER_CONFIG, NULL, NULL), /* TCP port. */
|
||||
createIntConfig("port", NULL, MODIFIABLE_CONFIG, 0, 65535, server.port, 6379, INTEGER_CONFIG, NULL, updatePort), /* TCP port. */
|
||||
createIntConfig("io-threads", NULL, IMMUTABLE_CONFIG, 1, 128, server.io_threads_num, 1, INTEGER_CONFIG, NULL, NULL), /* Single threaded by default */
|
||||
createIntConfig("auto-aof-rewrite-percentage", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.aof_rewrite_perc, 100, INTEGER_CONFIG, NULL, NULL),
|
||||
createIntConfig("cluster-replica-validity-factor", "cluster-slave-validity-factor", MODIFIABLE_CONFIG, 0, INT_MAX, server.cluster_slave_validity_factor, 10, INTEGER_CONFIG, NULL, NULL), /* Slave max data age factor. */
|
||||
@ -2534,13 +2522,14 @@ standardConfig configs[] = {
|
||||
createSizeTConfig("zset-max-ziplist-value", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.zset_max_ziplist_value, 64, MEMORY_CONFIG, NULL, NULL),
|
||||
createSizeTConfig("hll-sparse-max-bytes", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.hll_sparse_max_bytes, 3000, MEMORY_CONFIG, NULL, NULL),
|
||||
createSizeTConfig("tracking-table-max-keys", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.tracking_table_max_keys, 1000000, INTEGER_CONFIG, NULL, NULL), /* Default: 1 million keys max. */
|
||||
createSizeTConfig("client-query-buffer-limit", NULL, MODIFIABLE_CONFIG, 1024*1024, LONG_MAX, server.client_max_querybuf_len, 1024*1024*1024, MEMORY_CONFIG, NULL, NULL), /* Default: 1GB max query buffer. */
|
||||
|
||||
/* Other configs */
|
||||
createTimeTConfig("repl-backlog-ttl", NULL, MODIFIABLE_CONFIG, 0, LONG_MAX, server.repl_backlog_time_limit, 60*60, INTEGER_CONFIG, NULL, NULL), /* Default: 1 hour */
|
||||
createOffTConfig("auto-aof-rewrite-min-size", NULL, MODIFIABLE_CONFIG, 0, LLONG_MAX, server.aof_rewrite_min_size, 64*1024*1024, MEMORY_CONFIG, NULL, NULL),
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
createIntConfig("tls-port", NULL, IMMUTABLE_CONFIG, 0, 65535, server.tls_port, 0, INTEGER_CONFIG, NULL, updateTlsCfgInt), /* TCP port. */
|
||||
createIntConfig("tls-port", NULL, MODIFIABLE_CONFIG, 0, 65535, server.tls_port, 0, INTEGER_CONFIG, NULL, updateTLSPort), /* TCP port. */
|
||||
createIntConfig("tls-session-cache-size", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.tls_ctx_config.session_cache_size, 20*1024, INTEGER_CONFIG, NULL, updateTlsCfgInt),
|
||||
createIntConfig("tls-session-cache-timeout", NULL, MODIFIABLE_CONFIG, 0, INT_MAX, server.tls_ctx_config.session_cache_timeout, 300, INTEGER_CONFIG, NULL, updateTlsCfgInt),
|
||||
createBoolConfig("tls-cluster", NULL, MODIFIABLE_CONFIG, server.tls_cluster, 0, NULL, updateTlsCfgBool),
|
||||
|
17
src/config.h
17
src/config.h
@ -35,7 +35,6 @@
|
||||
#endif
|
||||
|
||||
#ifdef __linux__
|
||||
#include <linux/version.h>
|
||||
#include <features.h>
|
||||
#endif
|
||||
|
||||
@ -114,19 +113,7 @@
|
||||
|
||||
/* Define rdb_fsync_range to sync_file_range() on Linux, otherwise we use
|
||||
* the plain fsync() call. */
|
||||
#ifdef __linux__
|
||||
#if defined(__GLIBC__) && defined(__GLIBC_PREREQ)
|
||||
#if (LINUX_VERSION_CODE >= 0x020611 && __GLIBC_PREREQ(2, 6))
|
||||
#define HAVE_SYNC_FILE_RANGE 1
|
||||
#endif
|
||||
#else
|
||||
#if (LINUX_VERSION_CODE >= 0x020611)
|
||||
#define HAVE_SYNC_FILE_RANGE 1
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_SYNC_FILE_RANGE
|
||||
#if (defined(__linux__) && defined(SYNC_FILE_RANGE_WAIT_BEFORE))
|
||||
#define rdb_fsync_range(fd,off,size) sync_file_range(fd,off,size,SYNC_FILE_RANGE_WAIT_BEFORE|SYNC_FILE_RANGE_WRITE)
|
||||
#else
|
||||
#define rdb_fsync_range(fd,off,size) fsync(fd)
|
||||
@ -143,7 +130,7 @@
|
||||
#define ESOCKTNOSUPPORT 0
|
||||
#endif
|
||||
|
||||
#if ((defined __linux && defined(__GLIBC__)) || defined __APPLE__)
|
||||
#if (defined __linux || defined __APPLE__)
|
||||
#define USE_SETPROCTITLE
|
||||
#define INIT_SETPROCTITLE_REPLACEMENT
|
||||
void spt_init(int argc, char *argv[]);
|
||||
|
@ -427,7 +427,7 @@ int connGetState(connection *conn) {
|
||||
* For sockets, we always return "fd=<fdnum>" to maintain compatibility.
|
||||
*/
|
||||
const char *connGetInfo(connection *conn, char *buf, size_t buf_len) {
|
||||
snprintf(buf, buf_len-1, "fd=%i", conn->fd);
|
||||
snprintf(buf, buf_len-1, "fd=%i", conn == NULL ? -1 : conn->fd);
|
||||
return buf;
|
||||
}
|
||||
|
||||
|
8
src/db.c
8
src/db.c
@ -1315,7 +1315,7 @@ void scanDatabaseForReadyLists(redisDb *db) {
|
||||
*
|
||||
* Returns C_ERR if at least one of the DB ids are out of range, otherwise
|
||||
* C_OK is returned. */
|
||||
int dbSwapDatabases(long id1, long id2) {
|
||||
int dbSwapDatabases(int id1, int id2) {
|
||||
if (id1 < 0 || id1 >= server.dbnum ||
|
||||
id2 < 0 || id2 >= server.dbnum) return C_ERR;
|
||||
if (id1 == id2) return C_OK;
|
||||
@ -1356,7 +1356,7 @@ int dbSwapDatabases(long id1, long id2) {
|
||||
|
||||
/* SWAPDB db1 db2 */
|
||||
void swapdbCommand(client *c) {
|
||||
long id1, id2;
|
||||
int id1, id2;
|
||||
|
||||
/* Not allowed in cluster mode: we have just DB 0 there. */
|
||||
if (server.cluster_enabled) {
|
||||
@ -1365,11 +1365,11 @@ void swapdbCommand(client *c) {
|
||||
}
|
||||
|
||||
/* Get the two DBs indexes. */
|
||||
if (getLongFromObjectOrReply(c, c->argv[1], &id1,
|
||||
if (getIntFromObjectOrReply(c, c->argv[1], &id1,
|
||||
"invalid first DB index") != C_OK)
|
||||
return;
|
||||
|
||||
if (getLongFromObjectOrReply(c, c->argv[2], &id2,
|
||||
if (getIntFromObjectOrReply(c, c->argv[2], &id2,
|
||||
"invalid second DB index") != C_OK)
|
||||
return;
|
||||
|
||||
|
@ -54,6 +54,10 @@ typedef ucontext_t sigcontext_t;
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__) && defined(__arm64__)
|
||||
#include <mach/mach.h>
|
||||
#endif
|
||||
|
||||
/* Globals */
|
||||
static int bug_report_start = 0; /* True if bug report header was already logged. */
|
||||
static pthread_mutex_t bug_report_start_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
@ -60,7 +60,6 @@
|
||||
|
||||
#ifdef __linux__
|
||||
/* features.h uses the defines above to set feature specific defines. */
|
||||
#include <linux/version.h>
|
||||
#include <features.h>
|
||||
#endif
|
||||
|
||||
|
@ -84,9 +84,9 @@ lwCanvas *lwDrawSchotter(int console_cols, int squares_per_row, int squares_per_
|
||||
* rows. */
|
||||
float angle = 0;
|
||||
if (y > 1) {
|
||||
float r1 = (float)rand() / RAND_MAX / squares_per_col * y;
|
||||
float r2 = (float)rand() / RAND_MAX / squares_per_col * y;
|
||||
float r3 = (float)rand() / RAND_MAX / squares_per_col * y;
|
||||
float r1 = (float)rand() / (float) RAND_MAX / squares_per_col * y;
|
||||
float r2 = (float)rand() / (float) RAND_MAX / squares_per_col * y;
|
||||
float r3 = (float)rand() / (float) RAND_MAX / squares_per_col * y;
|
||||
if (rand() % 2) r1 = -r1;
|
||||
if (rand() % 2) r2 = -r2;
|
||||
if (rand() % 2) r3 = -r3;
|
||||
|
57
src/module.c
57
src/module.c
@ -1837,6 +1837,28 @@ unsigned long long RM_GetClientId(RedisModuleCtx *ctx) {
|
||||
return ctx->client->id;
|
||||
}
|
||||
|
||||
/* Return the ACL user name used by the client with the specified client ID.
|
||||
* Client ID can be obtained with RM_GetClientId() API. If the client does not
|
||||
* exist, NULL is returned and errno is set to ENOENT. If the client isn't
|
||||
* using an ACL user, NULL is returned and errno is set to ENOTSUP */
|
||||
RedisModuleString *RM_GetClientUserNameById(RedisModuleCtx *ctx, uint64_t id) {
|
||||
client *client = lookupClientByID(id);
|
||||
if (client == NULL) {
|
||||
errno = ENOENT;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (client->user == NULL) {
|
||||
errno = ENOTSUP;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
sds name = sdsnew(client->user->name);
|
||||
robj *str = createObject(OBJ_STRING, name);
|
||||
autoMemoryAdd(ctx, REDISMODULE_AM_STRING, str);
|
||||
return str;
|
||||
}
|
||||
|
||||
/* This is an helper for RM_GetClientInfoById() and other functions: given
|
||||
* a client, it populates the client info structure with the appropriate
|
||||
* fields depending on the version provided. If the version is not valid
|
||||
@ -3969,16 +3991,26 @@ RedisModuleCallReply *RM_Call(RedisModuleCtx *ctx, const char *cmdname, const ch
|
||||
RedisModuleCallReply *reply = NULL;
|
||||
int replicate = 0; /* Replicate this command? */
|
||||
|
||||
/* Create the client and dispatch the command. */
|
||||
/* Handle arguments. */
|
||||
va_start(ap, fmt);
|
||||
c = createClient(NULL);
|
||||
c->user = NULL; /* Root user. */
|
||||
argv = moduleCreateArgvFromUserFormat(cmdname,fmt,&argc,&flags,ap);
|
||||
replicate = flags & REDISMODULE_ARGV_REPLICATE;
|
||||
va_end(ap);
|
||||
|
||||
/* Setup our fake client for command execution. */
|
||||
c->flags |= CLIENT_MODULE;
|
||||
if (server.module_client == NULL) {
|
||||
/* This is the first RM_Call() ever. Create reusable client. */
|
||||
c = server.module_client = createClient(NULL);
|
||||
} else if (server.module_client->argv == NULL) {
|
||||
/* The reusable module client is not busy with a command. Use it. */
|
||||
c = server.module_client;
|
||||
} else {
|
||||
/* The reusable module client is busy. (It is probably used in a
|
||||
* recursive call to this module.) */
|
||||
c = createClient(NULL);
|
||||
}
|
||||
c->user = NULL; /* Root user. */
|
||||
c->flags = CLIENT_MODULE;
|
||||
|
||||
/* We do not want to allow block, the module do not expect it */
|
||||
c->flags |= CLIENT_DENY_BLOCKING;
|
||||
@ -4066,7 +4098,18 @@ RedisModuleCallReply *RM_Call(RedisModuleCtx *ctx, const char *cmdname, const ch
|
||||
|
||||
cleanup:
|
||||
if (ctx->module) ctx->module->in_call--;
|
||||
freeClient(c);
|
||||
if (c == server.module_client) {
|
||||
/* reset shared client so it can be reused */
|
||||
discardTransaction(c);
|
||||
pubsubUnsubscribeAllChannels(c,0);
|
||||
pubsubUnsubscribeAllPatterns(c,0);
|
||||
resetClient(c); /* frees the contents of argv */
|
||||
zfree(c->argv);
|
||||
c->argv = NULL;
|
||||
c->resp = 2;
|
||||
} else {
|
||||
freeClient(c); /* temporary client */
|
||||
}
|
||||
return reply;
|
||||
}
|
||||
|
||||
@ -8269,6 +8312,9 @@ void moduleInitModulesSystem(void) {
|
||||
/* Set up filter list */
|
||||
moduleCommandFilters = listCreate();
|
||||
|
||||
/* Reusable client for RM_Call() is created on first use */
|
||||
server.module_client = NULL;
|
||||
|
||||
moduleRegisterCoreAPI();
|
||||
if (pipe(server.module_blocked_pipe) == -1) {
|
||||
serverLog(LL_WARNING,
|
||||
@ -9133,6 +9179,7 @@ void moduleRegisterCoreAPI(void) {
|
||||
REGISTER_API(IsKeysPositionRequest);
|
||||
REGISTER_API(KeyAtPos);
|
||||
REGISTER_API(GetClientId);
|
||||
REGISTER_API(GetClientUserNameById);
|
||||
REGISTER_API(GetContextFlags);
|
||||
REGISTER_API(AvoidReplicaTraffic);
|
||||
REGISTER_API(PoolAlloc);
|
||||
|
@ -250,18 +250,20 @@ int pubsubUnsubscribePattern(client *c, robj *pattern, int notify) {
|
||||
/* Unsubscribe from all the channels. Return the number of channels the
|
||||
* client was subscribed to. */
|
||||
int pubsubUnsubscribeAllChannels(client *c, int notify) {
|
||||
int count = 0;
|
||||
if (dictSize(c->pubsub_channels) > 0) {
|
||||
dictIterator *di = dictGetSafeIterator(c->pubsub_channels);
|
||||
dictEntry *de;
|
||||
int count = 0;
|
||||
|
||||
while((de = dictNext(di)) != NULL) {
|
||||
robj *channel = dictGetKey(de);
|
||||
|
||||
count += pubsubUnsubscribeChannel(c,channel,notify);
|
||||
}
|
||||
dictReleaseIterator(di);
|
||||
}
|
||||
/* We were subscribed to nothing? Still reply to the client. */
|
||||
if (notify && count == 0) addReplyPubsubUnsubscribed(c,NULL);
|
||||
dictReleaseIterator(di);
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -999,7 +999,7 @@ int quicklistDelRange(quicklist *quicklist, const long start,
|
||||
* can just delete the entire node without ziplist math. */
|
||||
delete_entire_node = 1;
|
||||
del = node->count;
|
||||
} else if (entry.offset >= 0 && extent >= node->count) {
|
||||
} else if (entry.offset >= 0 && extent + entry.offset >= node->count) {
|
||||
/* If deleting more nodes after this one, calculate delete based
|
||||
* on size of current node. */
|
||||
del = node->count - entry.offset;
|
||||
@ -2238,6 +2238,17 @@ int quicklistTest(int argc, char *argv[]) {
|
||||
quicklistRelease(ql);
|
||||
}
|
||||
|
||||
TEST("delete less than fill but across nodes") {
|
||||
quicklist *ql = quicklistNew(-2, options[_i]);
|
||||
quicklistSetFill(ql, 32);
|
||||
for (int i = 0; i < 500; i++)
|
||||
quicklistPushTail(ql, genstr("hello", i + 1), 32);
|
||||
ql_verify(ql, 16, 500, 32, 20);
|
||||
quicklistDelRange(ql, 60, 10);
|
||||
ql_verify(ql, 16, 490, 32, 20);
|
||||
quicklistRelease(ql);
|
||||
}
|
||||
|
||||
TEST("delete negative 1 from 500 list") {
|
||||
quicklist *ql = quicklistNew(-2, options[_i]);
|
||||
quicklistSetFill(ql, 32);
|
||||
|
@ -819,6 +819,9 @@ static int cliConnect(int flags) {
|
||||
if (context == NULL || flags & CC_FORCE) {
|
||||
if (context != NULL) {
|
||||
redisFree(context);
|
||||
config.dbnum = 0;
|
||||
config.in_multi = 0;
|
||||
cliRefreshPrompt();
|
||||
}
|
||||
|
||||
if (config.hostsocket == NULL) {
|
||||
@ -2969,7 +2972,7 @@ static void clusterManagerOptimizeAntiAffinity(clusterManagerNodeArray *ipnodes,
|
||||
ip_count,
|
||||
&offenders,
|
||||
&offending_len);
|
||||
if (score == 0) break; // Optimal anti affinity reached
|
||||
if (score == 0 || offending_len == 0) break; // Optimal anti affinity reached
|
||||
/* We'll try to randomly swap a slave's assigned master causing
|
||||
* an affinity problem with another random slave, to see if we
|
||||
* can improve the affinity. */
|
||||
|
@ -665,6 +665,7 @@ REDISMODULE_API long long (*RedisModule_StreamTrimByID)(RedisModuleKey *key, int
|
||||
REDISMODULE_API int (*RedisModule_IsKeysPositionRequest)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
||||
REDISMODULE_API void (*RedisModule_KeyAtPos)(RedisModuleCtx *ctx, int pos) REDISMODULE_ATTR;
|
||||
REDISMODULE_API unsigned long long (*RedisModule_GetClientId)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
||||
REDISMODULE_API RedisModuleString * (*RedisModule_GetClientUserNameById)(RedisModuleCtx *ctx, uint64_t id) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_GetClientInfoById)(void *ci, uint64_t id) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_PublishMessage)(RedisModuleCtx *ctx, RedisModuleString *channel, RedisModuleString *message) REDISMODULE_ATTR;
|
||||
REDISMODULE_API int (*RedisModule_GetContextFlags)(RedisModuleCtx *ctx) REDISMODULE_ATTR;
|
||||
@ -936,6 +937,7 @@ static int RedisModule_Init(RedisModuleCtx *ctx, const char *name, int ver, int
|
||||
REDISMODULE_GET_API(IsKeysPositionRequest);
|
||||
REDISMODULE_GET_API(KeyAtPos);
|
||||
REDISMODULE_GET_API(GetClientId);
|
||||
REDISMODULE_GET_API(GetClientUserNameById);
|
||||
REDISMODULE_GET_API(GetContextFlags);
|
||||
REDISMODULE_GET_API(AvoidReplicaTraffic);
|
||||
REDISMODULE_GET_API(PoolAlloc);
|
||||
|
@ -619,7 +619,7 @@ int luaRedisGenericCommand(lua_State *lua, int raise_error) {
|
||||
"at least one of the keys mentioned in the "
|
||||
"command arguments");
|
||||
break;
|
||||
case ACL_DENIED_AUTH:
|
||||
case ACL_DENIED_CHANNEL:
|
||||
luaPushError(lua, "The user executing the script can't publish "
|
||||
"to the channel mentioned in the command");
|
||||
break;
|
||||
@ -1544,7 +1544,7 @@ void evalGenericCommand(client *c, int evalsha) {
|
||||
* return an error. */
|
||||
if (evalsha) {
|
||||
lua_pop(lua,1); /* remove the error handler from the stack. */
|
||||
addReply(c, shared.noscripterr);
|
||||
addReplyErrorObject(c, shared.noscripterr);
|
||||
return;
|
||||
}
|
||||
if (luaCreateFunction(c,lua,c->argv[1]) == NULL) {
|
||||
@ -1695,7 +1695,7 @@ void evalShaCommand(client *c) {
|
||||
* not the right length. So we return an error ASAP, this way
|
||||
* evalGenericCommand() can be implemented without string length
|
||||
* sanity check */
|
||||
addReply(c, shared.noscripterr);
|
||||
addReplyErrorObject(c, shared.noscripterr);
|
||||
return;
|
||||
}
|
||||
if (!(c->flags & CLIENT_LUA_DEBUG))
|
||||
|
186
src/server.c
186
src/server.c
@ -1070,7 +1070,7 @@ struct redisCommand redisCommandTable[] = {
|
||||
"write fast @stream",
|
||||
0,NULL,1,1,1,0,0,0},
|
||||
|
||||
{"xtrim",xtrimCommand,-2,
|
||||
{"xtrim",xtrimCommand,-4,
|
||||
"write random @stream",
|
||||
0,NULL,1,1,1,0,0,0},
|
||||
|
||||
@ -2648,12 +2648,11 @@ void initServerConfig(void) {
|
||||
server.arch_bits = (sizeof(long) == 8) ? 64 : 32;
|
||||
server.bindaddr_count = 0;
|
||||
server.unixsocketperm = CONFIG_DEFAULT_UNIX_SOCKET_PERM;
|
||||
server.ipfd_count = 0;
|
||||
server.tlsfd_count = 0;
|
||||
server.ipfd.count = 0;
|
||||
server.tlsfd.count = 0;
|
||||
server.sofd = -1;
|
||||
server.active_expire_enabled = 1;
|
||||
server.skip_checksum_validation = 0;
|
||||
server.client_max_querybuf_len = PROTO_MAX_QUERYBUF_LEN;
|
||||
server.saveparams = NULL;
|
||||
server.loading = 0;
|
||||
server.loading_rdb_used_mem = 0;
|
||||
@ -2989,6 +2988,34 @@ void checkTcpBacklogSettings(void) {
|
||||
#endif
|
||||
}
|
||||
|
||||
void closeSocketListeners(socketFds *sfd) {
|
||||
int j;
|
||||
|
||||
for (j = 0; j < sfd->count; j++) {
|
||||
if (sfd->fd[j] == -1) continue;
|
||||
|
||||
aeDeleteFileEvent(server.el, sfd->fd[j], AE_READABLE);
|
||||
close(sfd->fd[j]);
|
||||
}
|
||||
|
||||
sfd->count = 0;
|
||||
}
|
||||
|
||||
/* Create an event handler for accepting new connections in TCP or TLS domain sockets.
|
||||
* This works atomically for all socket fds */
|
||||
int createSocketAcceptHandler(socketFds *sfd, aeFileProc *accept_handler) {
|
||||
int j;
|
||||
|
||||
for (j = 0; j < sfd->count; j++) {
|
||||
if (aeCreateFileEvent(server.el, sfd->fd[j], AE_READABLE, accept_handler,NULL) == AE_ERR) {
|
||||
/* Rollback */
|
||||
for (j = j-1; j >= 0; j--) aeDeleteFileEvent(server.el, sfd->fd[j], AE_READABLE);
|
||||
return C_ERR;
|
||||
}
|
||||
}
|
||||
return C_OK;
|
||||
}
|
||||
|
||||
/* Initialize a set of file descriptors to listen to the specified 'port'
|
||||
* binding the addresses specified in the Redis server configuration.
|
||||
*
|
||||
@ -3007,7 +3034,7 @@ void checkTcpBacklogSettings(void) {
|
||||
* impossible to bind, or no bind addresses were specified in the server
|
||||
* configuration but the function is not able to bind * for at least
|
||||
* one of the IPv4 or IPv6 protocols. */
|
||||
int listenToPort(int port, int *fds, int *count) {
|
||||
int listenToPort(int port, socketFds *sfd) {
|
||||
int j;
|
||||
char **bindaddr = server.bindaddr;
|
||||
int bindaddr_count = server.bindaddr_count;
|
||||
@ -3025,12 +3052,12 @@ int listenToPort(int port, int *fds, int *count) {
|
||||
if (optional) addr++;
|
||||
if (strchr(addr,':')) {
|
||||
/* Bind IPv6 address. */
|
||||
fds[*count] = anetTcp6Server(server.neterr,port,addr,server.tcp_backlog);
|
||||
sfd->fd[sfd->count] = anetTcp6Server(server.neterr,port,addr,server.tcp_backlog);
|
||||
} else {
|
||||
/* Bind IPv4 address. */
|
||||
fds[*count] = anetTcpServer(server.neterr,port,addr,server.tcp_backlog);
|
||||
sfd->fd[sfd->count] = anetTcpServer(server.neterr,port,addr,server.tcp_backlog);
|
||||
}
|
||||
if (fds[*count] == ANET_ERR) {
|
||||
if (sfd->fd[sfd->count] == ANET_ERR) {
|
||||
serverLog(LL_WARNING,
|
||||
"Could not create server TCP listening socket %s:%d: %s",
|
||||
addr, port, server.neterr);
|
||||
@ -3040,11 +3067,14 @@ int listenToPort(int port, int *fds, int *count) {
|
||||
errno == ESOCKTNOSUPPORT || errno == EPFNOSUPPORT ||
|
||||
errno == EAFNOSUPPORT)
|
||||
continue;
|
||||
|
||||
/* Rollback successful listens before exiting */
|
||||
closeSocketListeners(sfd);
|
||||
return C_ERR;
|
||||
}
|
||||
anetNonBlock(NULL,fds[*count]);
|
||||
anetCloexec(fds[*count]);
|
||||
(*count)++;
|
||||
anetNonBlock(NULL,sfd->fd[sfd->count]);
|
||||
anetCloexec(sfd->fd[sfd->count]);
|
||||
sfd->count++;
|
||||
}
|
||||
return C_OK;
|
||||
}
|
||||
@ -3166,10 +3196,10 @@ void initServer(void) {
|
||||
|
||||
/* Open the TCP listening socket for the user commands. */
|
||||
if (server.port != 0 &&
|
||||
listenToPort(server.port,server.ipfd,&server.ipfd_count) == C_ERR)
|
||||
listenToPort(server.port,&server.ipfd) == C_ERR)
|
||||
exit(1);
|
||||
if (server.tls_port != 0 &&
|
||||
listenToPort(server.tls_port,server.tlsfd,&server.tlsfd_count) == C_ERR)
|
||||
listenToPort(server.tls_port,&server.tlsfd) == C_ERR)
|
||||
exit(1);
|
||||
|
||||
/* Open the listening Unix domain socket. */
|
||||
@ -3186,7 +3216,7 @@ void initServer(void) {
|
||||
}
|
||||
|
||||
/* Abort if there are no listening sockets at all. */
|
||||
if (server.ipfd_count == 0 && server.tlsfd_count == 0 && server.sofd < 0) {
|
||||
if (server.ipfd.count == 0 && server.tlsfd.count == 0 && server.sofd < 0) {
|
||||
serverLog(LL_WARNING, "Configured to not listen anywhere, exiting.");
|
||||
exit(1);
|
||||
}
|
||||
@ -3264,21 +3294,11 @@ void initServer(void) {
|
||||
|
||||
/* Create an event handler for accepting new connections in TCP and Unix
|
||||
* domain sockets. */
|
||||
for (j = 0; j < server.ipfd_count; j++) {
|
||||
if (aeCreateFileEvent(server.el, server.ipfd[j], AE_READABLE,
|
||||
acceptTcpHandler,NULL) == AE_ERR)
|
||||
{
|
||||
serverPanic(
|
||||
"Unrecoverable error creating server.ipfd file event.");
|
||||
}
|
||||
}
|
||||
for (j = 0; j < server.tlsfd_count; j++) {
|
||||
if (aeCreateFileEvent(server.el, server.tlsfd[j], AE_READABLE,
|
||||
acceptTLSHandler,NULL) == AE_ERR)
|
||||
{
|
||||
serverPanic(
|
||||
"Unrecoverable error creating server.tlsfd file event.");
|
||||
if (createSocketAcceptHandler(&server.ipfd, acceptTcpHandler) != C_OK) {
|
||||
serverPanic("Unrecoverable error creating TCP socket accept handler.");
|
||||
}
|
||||
if (createSocketAcceptHandler(&server.tlsfd, acceptTLSHandler) != C_OK) {
|
||||
serverPanic("Unrecoverable error creating TLS socket accept handler.");
|
||||
}
|
||||
if (server.sofd > 0 && aeCreateFileEvent(server.el,server.sofd,AE_READABLE,
|
||||
acceptUnixHandler,NULL) == AE_ERR) serverPanic("Unrecoverable error creating server.sofd file event.");
|
||||
@ -3324,6 +3344,9 @@ void initServer(void) {
|
||||
scriptingInit(1);
|
||||
slowlogInit();
|
||||
latencyMonitorInit();
|
||||
|
||||
/* Initialize ACL default password if it exists */
|
||||
ACLUpdateDefaultUserPassword(server.requirepass);
|
||||
}
|
||||
|
||||
/* Some steps in server initialization need to be done last (after modules
|
||||
@ -4180,11 +4203,11 @@ void incrementErrorCount(const char *fullerr, size_t namelen) {
|
||||
void closeListeningSockets(int unlink_unix_socket) {
|
||||
int j;
|
||||
|
||||
for (j = 0; j < server.ipfd_count; j++) close(server.ipfd[j]);
|
||||
for (j = 0; j < server.tlsfd_count; j++) close(server.tlsfd[j]);
|
||||
for (j = 0; j < server.ipfd.count; j++) close(server.ipfd.fd[j]);
|
||||
for (j = 0; j < server.tlsfd.count; j++) close(server.tlsfd.fd[j]);
|
||||
if (server.sofd != -1) close(server.sofd);
|
||||
if (server.cluster_enabled)
|
||||
for (j = 0; j < server.cfd_count; j++) close(server.cfd[j]);
|
||||
for (j = 0; j < server.cfd.count; j++) close(server.cfd.fd[j]);
|
||||
if (unlink_unix_socket && server.unixsocket) {
|
||||
serverLog(LL_NOTICE,"Removing the unix socket file.");
|
||||
unlink(server.unixsocket); /* don't care if this fails */
|
||||
@ -5534,6 +5557,105 @@ void redisAsciiArt(void) {
|
||||
zfree(buf);
|
||||
}
|
||||
|
||||
int changeBindAddr(sds *addrlist, int addrlist_len) {
|
||||
int i;
|
||||
int result = C_OK;
|
||||
|
||||
char *prev_bindaddr[CONFIG_BINDADDR_MAX];
|
||||
int prev_bindaddr_count;
|
||||
|
||||
/* Close old TCP and TLS servers */
|
||||
closeSocketListeners(&server.ipfd);
|
||||
closeSocketListeners(&server.tlsfd);
|
||||
|
||||
/* Keep previous settings */
|
||||
prev_bindaddr_count = server.bindaddr_count;
|
||||
memcpy(prev_bindaddr, server.bindaddr, sizeof(server.bindaddr));
|
||||
|
||||
/* Copy new settings */
|
||||
memset(server.bindaddr, 0, sizeof(server.bindaddr));
|
||||
for (i = 0; i < addrlist_len; i++) {
|
||||
server.bindaddr[i] = zstrdup(addrlist[i]);
|
||||
}
|
||||
server.bindaddr_count = addrlist_len;
|
||||
|
||||
/* Bind to the new port */
|
||||
if ((server.port != 0 && listenToPort(server.port, &server.ipfd) != C_OK) ||
|
||||
(server.tls_port != 0 && listenToPort(server.tls_port, &server.tlsfd) != C_OK)) {
|
||||
serverLog(LL_WARNING, "Failed to bind, trying to restore old listening sockets.");
|
||||
|
||||
/* Restore old bind addresses */
|
||||
for (i = 0; i < addrlist_len; i++) {
|
||||
zfree(server.bindaddr[i]);
|
||||
}
|
||||
memcpy(server.bindaddr, prev_bindaddr, sizeof(server.bindaddr));
|
||||
server.bindaddr_count = prev_bindaddr_count;
|
||||
|
||||
/* Re-Listen TCP and TLS */
|
||||
server.ipfd.count = 0;
|
||||
if (server.port != 0 && listenToPort(server.port, &server.ipfd) != C_OK) {
|
||||
serverPanic("Failed to restore old listening sockets.");
|
||||
}
|
||||
|
||||
server.tlsfd.count = 0;
|
||||
if (server.tls_port != 0 && listenToPort(server.tls_port, &server.tlsfd) != C_OK) {
|
||||
serverPanic("Failed to restore old listening sockets.");
|
||||
}
|
||||
|
||||
result = C_ERR;
|
||||
} else {
|
||||
/* Free old bind addresses */
|
||||
for (i = 0; i < prev_bindaddr_count; i++) {
|
||||
zfree(prev_bindaddr[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/* Create TCP and TLS event handlers */
|
||||
if (createSocketAcceptHandler(&server.ipfd, acceptTcpHandler) != C_OK) {
|
||||
serverPanic("Unrecoverable error creating TCP socket accept handler.");
|
||||
}
|
||||
if (createSocketAcceptHandler(&server.tlsfd, acceptTLSHandler) != C_OK) {
|
||||
serverPanic("Unrecoverable error creating TLS socket accept handler.");
|
||||
}
|
||||
|
||||
if (server.set_proc_title) redisSetProcTitle(NULL);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
int changeListenPort(int port, socketFds *sfd, aeFileProc *accept_handler) {
|
||||
socketFds new_sfd = {{0}};
|
||||
|
||||
/* Just close the server if port disabled */
|
||||
if (port == 0) {
|
||||
closeSocketListeners(sfd);
|
||||
if (server.set_proc_title) redisSetProcTitle(NULL);
|
||||
return C_OK;
|
||||
}
|
||||
|
||||
/* Bind to the new port */
|
||||
if (listenToPort(port, &new_sfd) != C_OK) {
|
||||
return C_ERR;
|
||||
}
|
||||
|
||||
/* Create event handlers */
|
||||
if (createSocketAcceptHandler(&new_sfd, accept_handler) != C_OK) {
|
||||
closeSocketListeners(&new_sfd);
|
||||
return C_ERR;
|
||||
}
|
||||
|
||||
/* Close old servers */
|
||||
closeSocketListeners(sfd);
|
||||
|
||||
/* Copy new descriptors */
|
||||
sfd->count = new_sfd.count;
|
||||
memcpy(sfd->fd, new_sfd.fd, sizeof(new_sfd.fd));
|
||||
|
||||
if (server.set_proc_title) redisSetProcTitle(NULL);
|
||||
|
||||
return C_OK;
|
||||
}
|
||||
|
||||
static void sigShutdownHandler(int sig) {
|
||||
char *msg;
|
||||
|
||||
@ -6122,7 +6244,7 @@ int main(int argc, char **argv) {
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (server.ipfd_count > 0 || server.tlsfd_count > 0)
|
||||
if (server.ipfd.count > 0 || server.tlsfd.count > 0)
|
||||
serverLog(LL_NOTICE,"Ready to accept connections");
|
||||
if (server.sofd > 0)
|
||||
serverLog(LL_NOTICE,"The server is now ready to accept connections at %s", server.unixsocket);
|
||||
|
22
src/server.h
22
src/server.h
@ -138,7 +138,6 @@ typedef long long ustime_t; /* microsecond time type. */
|
||||
#define STATS_METRIC_COUNT 3
|
||||
|
||||
/* Protocol and I/O related defines */
|
||||
#define PROTO_MAX_QUERYBUF_LEN (1024*1024*1024) /* 1GB max query buffer. */
|
||||
#define PROTO_IOBUF_LEN (1024*16) /* Generic I/O buffer size */
|
||||
#define PROTO_REPLY_CHUNK_BYTES (16*1024) /* 16k output buffer */
|
||||
#define PROTO_INLINE_MAX_SIZE (1024*64) /* Max size of inline reads */
|
||||
@ -1105,6 +1104,11 @@ struct malloc_stats {
|
||||
size_t allocator_resident;
|
||||
};
|
||||
|
||||
typedef struct socketFds {
|
||||
int fd[CONFIG_BINDADDR_MAX];
|
||||
int count;
|
||||
} socketFds;
|
||||
|
||||
/*-----------------------------------------------------------------------------
|
||||
* TLS Context Configuration
|
||||
*----------------------------------------------------------------------------*/
|
||||
@ -1196,6 +1200,7 @@ struct redisServer {
|
||||
to be processed. */
|
||||
pid_t child_pid; /* PID of current child */
|
||||
int child_type; /* Type of current child */
|
||||
client *module_client; /* "Fake" client to call Redis from modules */
|
||||
/* Networking */
|
||||
int port; /* TCP listening port */
|
||||
int tls_port; /* TLS listening port */
|
||||
@ -1204,13 +1209,10 @@ struct redisServer {
|
||||
int bindaddr_count; /* Number of addresses in server.bindaddr[] */
|
||||
char *unixsocket; /* UNIX socket path */
|
||||
mode_t unixsocketperm; /* UNIX socket permission */
|
||||
int ipfd[CONFIG_BINDADDR_MAX]; /* TCP socket file descriptors */
|
||||
int ipfd_count; /* Used slots in ipfd[] */
|
||||
int tlsfd[CONFIG_BINDADDR_MAX]; /* TLS socket file descriptors */
|
||||
int tlsfd_count; /* Used slots in tlsfd[] */
|
||||
socketFds ipfd; /* TCP socket file descriptors */
|
||||
socketFds tlsfd; /* TLS socket file descriptors */
|
||||
int sofd; /* Unix socket file descriptor */
|
||||
int cfd[CONFIG_BINDADDR_MAX];/* Cluster bus listening socket */
|
||||
int cfd_count; /* Used slots in cfd[] */
|
||||
socketFds cfd; /* Cluster bus listening socket */
|
||||
list *clients; /* List of active clients */
|
||||
list *clients_to_close; /* Clients to close asynchronously */
|
||||
list *clients_pending_write; /* There is to write or install handler. */
|
||||
@ -1855,7 +1857,7 @@ int getClientTypeByName(char *name);
|
||||
char *getClientTypeName(int class);
|
||||
void flushSlavesOutputBuffers(void);
|
||||
void disconnectSlaves(void);
|
||||
int listenToPort(int port, int *fds, int *count);
|
||||
int listenToPort(int port, socketFds *fds);
|
||||
void pauseClients(mstime_t duration, pause_type type);
|
||||
void unpauseClients(void);
|
||||
int areClientsPaused(void);
|
||||
@ -2106,6 +2108,7 @@ void addReplyCommandCategories(client *c, struct redisCommand *cmd);
|
||||
user *ACLCreateUnlinkedUser();
|
||||
void ACLFreeUserAndKillClients(user *u);
|
||||
void addACLLogEntry(client *c, int reason, int keypos, sds username);
|
||||
void ACLUpdateDefaultUserPassword(sds password);
|
||||
|
||||
/* Sorted sets data type */
|
||||
|
||||
@ -2183,6 +2186,9 @@ int processCommand(client *c);
|
||||
int processPendingCommandsAndResetClient(client *c);
|
||||
void setupSignalHandlers(void);
|
||||
void removeSignalHandlers(void);
|
||||
int createSocketAcceptHandler(socketFds *sfd, aeFileProc *accept_handler);
|
||||
int changeListenPort(int port, socketFds *sfd, aeFileProc *accept_handler);
|
||||
int changeBindAddr(sds *addrlist, int addrlist_len);
|
||||
struct redisCommand *lookupCommand(sds name);
|
||||
struct redisCommand *lookupCommandByCString(const char *s);
|
||||
struct redisCommand *lookupCommandOrOriginal(sds name);
|
||||
|
@ -232,7 +232,7 @@ void spt_init(int argc, char *argv[]) {
|
||||
if (!(SPT.arg0 = strdup(argv[0])))
|
||||
goto syerr;
|
||||
|
||||
#if __GLIBC__
|
||||
#if __linux__
|
||||
if (!(tmp = strdup(program_invocation_name)))
|
||||
goto syerr;
|
||||
|
||||
|
@ -3229,17 +3229,17 @@ cleanup:
|
||||
void xtrimCommand(client *c) {
|
||||
robj *o;
|
||||
|
||||
/* Argument parsing. */
|
||||
streamAddTrimArgs parsed_args;
|
||||
if (streamParseAddOrTrimArgsOrReply(c, &parsed_args, 1) < 0)
|
||||
return; /* streamParseAddOrTrimArgsOrReply already replied. */
|
||||
|
||||
/* If the key does not exist, we are ok returning zero, that is, the
|
||||
* number of elements removed from the stream. */
|
||||
if ((o = lookupKeyWriteOrReply(c,c->argv[1],shared.czero)) == NULL
|
||||
|| checkType(c,o,OBJ_STREAM)) return;
|
||||
stream *s = o->ptr;
|
||||
|
||||
/* Argument parsing. */
|
||||
streamAddTrimArgs parsed_args;
|
||||
if (streamParseAddOrTrimArgsOrReply(c, &parsed_args, 1) < 0)
|
||||
return; /* streamParseAddOrTrimArgsOrReply already replied. */
|
||||
|
||||
/* Perform the trimming. */
|
||||
int64_t deleted = streamTrim(s, &parsed_args);
|
||||
if (deleted) {
|
||||
@ -3570,7 +3570,7 @@ int streamValidateListpackIntegrity(unsigned char *lp, size_t size, int deep) {
|
||||
p = next; if (!lpValidateNext(lp, &next, size)) return 0;
|
||||
|
||||
/* deleted */
|
||||
lpGetIntegerIfValid(p, &valid_record);
|
||||
int64_t deleted_count = lpGetIntegerIfValid(p, &valid_record);
|
||||
if (!valid_record) return 0;
|
||||
p = next; if (!lpValidateNext(lp, &next, size)) return 0;
|
||||
|
||||
@ -3589,6 +3589,7 @@ int streamValidateListpackIntegrity(unsigned char *lp, size_t size, int deep) {
|
||||
if (!valid_record || zero != 0) return 0;
|
||||
p = next; if (!lpValidateNext(lp, &next, size)) return 0;
|
||||
|
||||
entry_count += deleted_count;
|
||||
while (entry_count--) {
|
||||
if (!p) return 0;
|
||||
int64_t fields = master_fields, extra_fields = 3;
|
||||
|
@ -32,6 +32,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <unistd.h>
|
||||
#include <assert.h>
|
||||
|
||||
/* This function provide us access to the original libc free(). This is useful
|
||||
* for instance to free results obtained by backtrace_symbols(). We need
|
||||
@ -49,19 +50,20 @@ void zlibc_free(void *ptr) {
|
||||
|
||||
#ifdef HAVE_MALLOC_SIZE
|
||||
#define PREFIX_SIZE (0)
|
||||
#define ASSERT_NO_SIZE_OVERFLOW(sz)
|
||||
#else
|
||||
#if defined(__sun) || defined(__sparc) || defined(__sparc__)
|
||||
#define PREFIX_SIZE (sizeof(long long))
|
||||
#else
|
||||
#define PREFIX_SIZE (sizeof(size_t))
|
||||
#endif
|
||||
#define ASSERT_NO_SIZE_OVERFLOW(sz) assert((sz) + PREFIX_SIZE > (sz))
|
||||
#endif
|
||||
|
||||
#if PREFIX_SIZE > 0
|
||||
#define ASSERT_NO_SIZE_OVERFLOW(sz) assert((sz) + PREFIX_SIZE > (sz))
|
||||
#else
|
||||
#define ASSERT_NO_SIZE_OVERFLOW(sz)
|
||||
#endif
|
||||
/* When using the libc allocator, use a minimum allocation size to match the
|
||||
* jemalloc behavior that doesn't return NULL in this case.
|
||||
*/
|
||||
#define MALLOC_MIN_SIZE(x) ((x) > 0 ? (x) : sizeof(long))
|
||||
|
||||
/* Explicitly override malloc/free etc when using tcmalloc. */
|
||||
#if defined(USE_TCMALLOC)
|
||||
@ -96,7 +98,7 @@ static void (*zmalloc_oom_handler)(size_t) = zmalloc_default_oom;
|
||||
* '*usable' is set to the usable size if non NULL. */
|
||||
void *ztrymalloc_usable(size_t size, size_t *usable) {
|
||||
ASSERT_NO_SIZE_OVERFLOW(size);
|
||||
void *ptr = malloc(size+PREFIX_SIZE);
|
||||
void *ptr = malloc(MALLOC_MIN_SIZE(size)+PREFIX_SIZE);
|
||||
|
||||
if (!ptr) return NULL;
|
||||
#ifdef HAVE_MALLOC_SIZE
|
||||
@ -156,7 +158,7 @@ void zfree_no_tcache(void *ptr) {
|
||||
* '*usable' is set to the usable size if non NULL. */
|
||||
void *ztrycalloc_usable(size_t size, size_t *usable) {
|
||||
ASSERT_NO_SIZE_OVERFLOW(size);
|
||||
void *ptr = calloc(1, size+PREFIX_SIZE);
|
||||
void *ptr = calloc(1, MALLOC_MIN_SIZE(size)+PREFIX_SIZE);
|
||||
if (ptr == NULL) return NULL;
|
||||
|
||||
#ifdef HAVE_MALLOC_SIZE
|
||||
@ -678,6 +680,7 @@ int zmalloc_test(int argc, char **argv) {
|
||||
|
||||
UNUSED(argc);
|
||||
UNUSED(argv);
|
||||
printf("Malloc prefix size: %d\n", (int) PREFIX_SIZE);
|
||||
printf("Initial used memory: %zu\n", zmalloc_used_memory());
|
||||
ptr = zmalloc(123);
|
||||
printf("Allocated 123 bytes; used: %zu\n", zmalloc_used_memory());
|
||||
|
@ -61,9 +61,19 @@
|
||||
#define zmalloc_size(p) malloc_size(p)
|
||||
#endif
|
||||
|
||||
/* On native libc implementations, we should still do our best to provide a
|
||||
* HAVE_MALLOC_SIZE capability. This can be set explicitly as well:
|
||||
*
|
||||
* NO_MALLOC_USABLE_SIZE disables it on all platforms, even if they are
|
||||
* known to support it.
|
||||
* USE_MALLOC_USABLE_SIZE forces use of malloc_usable_size() regardless
|
||||
* of platform.
|
||||
*/
|
||||
#ifndef ZMALLOC_LIB
|
||||
#define ZMALLOC_LIB "libc"
|
||||
#ifdef __GLIBC__
|
||||
#if !defined(NO_MALLOC_USABLE_SIZE) && \
|
||||
(defined(__GLIBC__) || defined(__FreeBSD__) || \
|
||||
defined(USE_MALLOC_USABLE_SIZE))
|
||||
#include <malloc.h>
|
||||
#define HAVE_MALLOC_SIZE 1
|
||||
#define zmalloc_size(p) malloc_usable_size(p)
|
||||
|
@ -186,6 +186,8 @@ proc is_alive pid {
|
||||
|
||||
proc stop_instance pid {
|
||||
catch {exec kill $pid}
|
||||
# Node might have been stopped in the test
|
||||
catch {exec kill -SIGCONT $pid}
|
||||
if {$::valgrind} {
|
||||
set max_wait 60000
|
||||
} else {
|
||||
|
@ -32,6 +32,7 @@ proc generate_types {} {
|
||||
# add some metadata to the stream
|
||||
r xgroup create stream mygroup 0
|
||||
set records [r xreadgroup GROUP mygroup Alice COUNT 2 STREAMS stream >]
|
||||
r xdel stream [lindex [lindex [lindex [lindex $records 0] 1] 1] 0]
|
||||
r xack stream mygroup [lindex [lindex [lindex [lindex $records 0] 1] 0] 0]
|
||||
|
||||
# create other non-collection types
|
||||
|
@ -1,6 +1,18 @@
|
||||
set system_name [string tolower [exec uname -s]]
|
||||
set system_supported 0
|
||||
|
||||
if {$system_name eq {linux} || $system_name eq {darwin}} {
|
||||
# We only support darwin or Linux with glibc
|
||||
if {$system_name eq {darwin}} {
|
||||
set system_supported 1
|
||||
} elseif {$system_name eq {linux}} {
|
||||
# Avoid the test on libmusl, which does not support backtrace
|
||||
set ldd [exec ldd src/redis-server]
|
||||
if {![string match {*libc.musl*} $ldd]} {
|
||||
set system_supported 1
|
||||
}
|
||||
}
|
||||
|
||||
if {$system_supported} {
|
||||
set server_path [tmpdir server.log]
|
||||
start_server [list overrides [list dir $server_path]] {
|
||||
test "Server is able to generate a stack trace on selected systems" {
|
||||
|
@ -52,6 +52,7 @@ start_server [list overrides [list "dir" $server_path] keep_persistence true] {
|
||||
}
|
||||
r xgroup create stream mygroup 0
|
||||
set records [r xreadgroup GROUP mygroup Alice COUNT 2 STREAMS stream >]
|
||||
r xdel stream [lindex [lindex [lindex [lindex $records 0] 1] 1] 0]
|
||||
r xack stream mygroup [lindex [lindex [lindex [lindex $records 0] 1] 0] 0]
|
||||
set digest [r debug digest]
|
||||
r config set sanitize-dump-payload no
|
||||
|
@ -72,6 +72,8 @@ proc kill_server config {
|
||||
# kill server and wait for the process to be totally exited
|
||||
send_data_packet $::test_server_fd server-killing $pid
|
||||
catch {exec kill $pid}
|
||||
# Node might have been stopped in the test
|
||||
catch {exec kill -SIGCONT $pid}
|
||||
if {$::valgrind} {
|
||||
set max_wait 60000
|
||||
} else {
|
||||
|
@ -75,6 +75,7 @@ set ::all_tests {
|
||||
unit/tracking
|
||||
unit/oom-score-adj
|
||||
unit/shutdown
|
||||
unit/networking
|
||||
}
|
||||
# Index to the next test to run in the ::all_tests list.
|
||||
set ::next_test 0
|
||||
|
@ -62,6 +62,19 @@ start_server {tags {"info"}} {
|
||||
assert_equal [s total_error_replies] 2
|
||||
}
|
||||
|
||||
test {errorstats: failed call NOSCRIPT error} {
|
||||
r config resetstat
|
||||
assert_equal [s total_error_replies] 0
|
||||
assert_match {} [errorstat NOSCRIPT]
|
||||
catch {r evalsha NotValidShaSUM 0} e
|
||||
assert_match {NOSCRIPT*} $e
|
||||
assert_match {*count=1*} [errorstat NOSCRIPT]
|
||||
assert_match {*calls=1,*,rejected_calls=0,failed_calls=1} [cmdstat evalsha]
|
||||
assert_equal [s total_error_replies] 1
|
||||
r config resetstat
|
||||
assert_match {} [errorstat NOSCRIPT]
|
||||
}
|
||||
|
||||
test {errorstats: failed call NOGROUP error} {
|
||||
r config resetstat
|
||||
assert_match {} [errorstat NOGROUP]
|
||||
|
@ -100,13 +100,10 @@ start_server {tags {"introspection"}} {
|
||||
supervised
|
||||
syslog-facility
|
||||
databases
|
||||
port
|
||||
tls-port
|
||||
io-threads
|
||||
logfile
|
||||
unixsocketperm
|
||||
slaveof
|
||||
bind
|
||||
requirepass
|
||||
server_cpulist
|
||||
bio_cpulist
|
||||
@ -131,6 +128,7 @@ start_server {tags {"introspection"}} {
|
||||
tls-protocols
|
||||
tls-ciphers
|
||||
tls-ciphersuites
|
||||
tls-port
|
||||
}
|
||||
}
|
||||
|
||||
@ -173,7 +171,7 @@ start_server {tags {"introspection"}} {
|
||||
# Rewrite entire configuration, restart and confirm the
|
||||
# server is able to parse it and start.
|
||||
assert_equal [r debug config-rewrite-force-all] "OK"
|
||||
restart_server 0 false false
|
||||
restart_server 0 true false
|
||||
assert_equal [r ping] "PONG"
|
||||
|
||||
# Verify no changes were introduced
|
||||
|
@ -16,6 +16,11 @@ start_server {tags {"modules"}} {
|
||||
assert { [string match "*cmdstat_module*" $info] }
|
||||
}
|
||||
|
||||
test {test RM_Call recursive} {
|
||||
set info [r test.call_generic test.call_generic info commandstats]
|
||||
assert { [string match "*cmdstat_module*" $info] }
|
||||
}
|
||||
|
||||
test {test redis version} {
|
||||
set version [s redis_version]
|
||||
assert_equal $version [r test.redisversion]
|
||||
@ -106,4 +111,8 @@ start_server {tags {"modules"}} {
|
||||
r test.log_tsctx "info" "Test message"
|
||||
verify_log_message 0 "*<misc> Test message*" 0
|
||||
}
|
||||
|
||||
test {test RM_Call CLIENT INFO} {
|
||||
assert_match "*fd=-1*" [r test.call_generic client info]
|
||||
}
|
||||
}
|
||||
|
36
tests/unit/networking.tcl
Normal file
36
tests/unit/networking.tcl
Normal file
@ -0,0 +1,36 @@
|
||||
test {CONFIG SET port number} {
|
||||
start_server {} {
|
||||
if {$::tls} { set port_cfg tls-port} else { set port_cfg port }
|
||||
|
||||
# available port
|
||||
set avail_port [find_available_port $::baseport $::portcount]
|
||||
set rd [redis [srv 0 host] [srv 0 port] 0 $::tls]
|
||||
$rd CONFIG SET $port_cfg $avail_port
|
||||
$rd close
|
||||
set rd [redis [srv 0 host] $avail_port 0 $::tls]
|
||||
$rd PING
|
||||
|
||||
# already inuse port
|
||||
catch {$rd CONFIG SET $port_cfg $::test_server_port} e
|
||||
assert_match {*Unable to listen on this port*} $e
|
||||
$rd close
|
||||
|
||||
# make sure server still listening on the previous port
|
||||
set rd [redis [srv 0 host] $avail_port 0 $::tls]
|
||||
$rd PING
|
||||
$rd close
|
||||
}
|
||||
}
|
||||
|
||||
test {CONFIG SET bind address} {
|
||||
start_server {} {
|
||||
# non-valid address
|
||||
catch {r CONFIG SET bind "some.wrong.bind.address"} e
|
||||
assert_match {*Failed to bind to specified addresses*} $e
|
||||
|
||||
# make sure server still bound to the previous address
|
||||
set rd [redis [srv 0 host] [srv 0 port] 0 $::tls]
|
||||
$rd PING
|
||||
$rd close
|
||||
}
|
||||
}
|
@ -114,5 +114,30 @@ start_server {tags {"tls"}} {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
test {TLS: switch between tcp and tls ports} {
|
||||
set srv_port [srv 0 port]
|
||||
|
||||
# TLS
|
||||
set rd [redis [srv 0 host] $srv_port 0 1]
|
||||
$rd PING
|
||||
|
||||
# TCP
|
||||
$rd CONFIG SET tls-port 0
|
||||
$rd CONFIG SET port $srv_port
|
||||
$rd close
|
||||
|
||||
set rd [redis [srv 0 host] $srv_port 0 0]
|
||||
$rd PING
|
||||
|
||||
# TLS
|
||||
$rd CONFIG SET port 0
|
||||
$rd CONFIG SET tls-port $srv_port
|
||||
$rd close
|
||||
|
||||
set rd [redis [srv 0 host] $srv_port 0 1]
|
||||
$rd PING
|
||||
$rd close
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user