summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOran Agra <oran@redislabs.com>2021-03-02 08:14:39 +0200
committerGitHub <noreply@github.com>2021-03-02 08:14:39 +0200
commit92bde124caaedef0e1572765854b2deb9aa22a35 (patch)
tree33e7d37c5ebd78f8203d65e0249ca826deb0d6ec
parent445aa844b946a8f1bc21ac8554b44adb1ecb4018 (diff)
parent7018ad69a22c992765ae3f3497ac5684edac6a81 (diff)
downloadredis-92bde124caaedef0e1572765854b2deb9aa22a35.tar.gz
Merge Redis 6.2.16.2.1
Redis 6.2.1
-rw-r--r--.github/workflows/daily.yml75
-rw-r--r--00-RELEASENOTES27
-rw-r--r--src/Makefile5
-rw-r--r--src/acl.c13
-rw-r--r--src/cluster.c18
-rw-r--r--src/config.c143
-rw-r--r--src/config.h17
-rw-r--r--src/connection.c2
-rw-r--r--src/db.c8
-rw-r--r--src/debug.c4
-rw-r--r--src/fmacros.h1
-rw-r--r--src/lolwut5.c6
-rw-r--r--src/module.c57
-rw-r--r--src/pubsub.c14
-rw-r--r--src/quicklist.c13
-rw-r--r--src/redis-cli.c5
-rw-r--r--src/redismodule.h2
-rw-r--r--src/scripting.c6
-rw-r--r--src/server.c186
-rw-r--r--src/server.h22
-rw-r--r--src/setproctitle.c2
-rw-r--r--src/t_stream.c13
-rw-r--r--src/version.h4
-rw-r--r--src/zmalloc.c17
-rw-r--r--src/zmalloc.h12
-rw-r--r--tests/instances.tcl2
-rw-r--r--tests/integration/corrupt-dump-fuzzer.tcl1
-rw-r--r--tests/integration/logging.tcl14
-rw-r--r--tests/integration/rdb.tcl1
-rw-r--r--tests/support/server.tcl2
-rw-r--r--tests/test_helper.tcl1
-rw-r--r--tests/unit/info.tcl13
-rw-r--r--tests/unit/introspection.tcl6
-rw-r--r--tests/unit/moduleapi/misc.tcl9
-rw-r--r--tests/unit/networking.tcl36
-rw-r--r--tests/unit/tls.tcl25
36 files changed, 589 insertions, 193 deletions
diff --git a/.github/workflows/daily.yml b/.github/workflows/daily.yml
index 9d7eb65c9..59d236d9a 100644
--- a/.github/workflows/daily.yml
+++ b/.github/workflows/daily.yml
@@ -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
diff --git a/00-RELEASENOTES b/00-RELEASENOTES
index 36ef7156f..d94c60465 100644
--- a/00-RELEASENOTES
+++ b/00-RELEASENOTES
@@ -12,6 +12,33 @@ SECURITY: There are security fixes in the release.
--------------------------------------------------------------------------------
================================================================================
+Redis 6.2.1 Released Mon Mar 1 17:51:36 IST 2021
+================================================================================
+
+Upgrade urgency: LOW.
+
+Here is a comprehensive list of changes in this release compared to 6.2.0,
+each one includes the PR number that added it, so you can get more details
+at https://github.com/redis/redis/pull/<number>
+
+Bug fixes:
+* Fix sanitize-dump-payload for stream with deleted records (#8568)
+* Prevent client-query-buffer-limit config from being set to lower than 1mb (#8557)
+
+Improvements:
+* Make port, tls-port and bind config options modifiable at runtime (#8510)
+
+Platform and deployment-related changes:
+* Fix compilation error on non-glibc systems if jemalloc is not used (#8533)
+* Improved memory consumption and memory usage tracking on FreeBSD (#8545)
+* Fix compilation on ARM64 MacOS with jemalloc (#8458)
+
+Modules:
+* New Module API for getting user name of a client (#8508)
+* Optimize RM_Call by utilizing a shared reusable client (#8516)
+* Fix crash running CLIENT INFO via RM_Call (#8560)
+
+================================================================================
Redis 6.2.0 GA Released Tue Feb 22 14:00:00 IST 2021
================================================================================
diff --git a/src/Makefile b/src/Makefile
index 3bc9f11c0..ecd69295f 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -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
diff --git a/src/acl.c b/src/acl.c
index e120b36fc..445409ecd 100644
--- a/src/acl.c
+++ b/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);
+ }
+}
diff --git a/src/cluster.c b/src/cluster.c
index efe2f652d..4605e3ea9 100644
--- a/src/cluster.c
+++ b/src/cluster.c
@@ -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. */
diff --git a/src/config.c b/src/config.c
index 9081d0312..1d6ce6b8c 100644
--- a/src/config.c
+++ b/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),
diff --git a/src/config.h b/src/config.h
index 1d4cb5835..56c1ab6ae 100644
--- a/src/config.h
+++ b/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[]);
diff --git a/src/connection.c b/src/connection.c
index 3e2021fb5..a59463220 100644
--- a/src/connection.c
+++ b/src/connection.c
@@ -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;
}
diff --git a/src/db.c b/src/db.c
index ed522401f..57705f003 100644
--- a/src/db.c
+++ b/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;
diff --git a/src/debug.c b/src/debug.c
index a725e5a30..e7fec293a 100644
--- a/src/debug.c
+++ b/src/debug.c
@@ -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;
diff --git a/src/fmacros.h b/src/fmacros.h
index 089dc8de7..a97d21a47 100644
--- a/src/fmacros.h
+++ b/src/fmacros.h
@@ -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
diff --git a/src/lolwut5.c b/src/lolwut5.c
index d64e0bb27..d864888ba 100644
--- a/src/lolwut5.c
+++ b/src/lolwut5.c
@@ -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;
diff --git a/src/module.c b/src/module.c
index fe0cd8345..274210590 100644
--- a/src/module.c
+++ b/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);
diff --git a/src/pubsub.c b/src/pubsub.c
index a7b370d5d..5f7335bbe 100644
--- a/src/pubsub.c
+++ b/src/pubsub.c
@@ -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) {
- dictIterator *di = dictGetSafeIterator(c->pubsub_channels);
- dictEntry *de;
int count = 0;
+ if (dictSize(c->pubsub_channels) > 0) {
+ dictIterator *di = dictGetSafeIterator(c->pubsub_channels);
+ dictEntry *de;
- while((de = dictNext(di)) != NULL) {
- robj *channel = dictGetKey(de);
+ while((de = dictNext(di)) != NULL) {
+ robj *channel = dictGetKey(de);
- count += pubsubUnsubscribeChannel(c,channel,notify);
+ 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;
}
diff --git a/src/quicklist.c b/src/quicklist.c
index c8517414c..7b7aa7839 100644
--- a/src/quicklist.c
+++ b/src/quicklist.c
@@ -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);
diff --git a/src/redis-cli.c b/src/redis-cli.c
index ef1d49e3e..63d31f79a 100644
--- a/src/redis-cli.c
+++ b/src/redis-cli.c
@@ -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. */
diff --git a/src/redismodule.h b/src/redismodule.h
index 0c2801bea..ea271b82b 100644
--- a/src/redismodule.h
+++ b/src/redismodule.h
@@ -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);
diff --git a/src/scripting.c b/src/scripting.c
index e23d49e8b..6830e7a70 100644
--- a/src/scripting.c
+++ b/src/scripting.c
@@ -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))
diff --git a/src/server.c b/src/server.c
index b22af980b..4784431ba 100644
--- a/src/server.c
+++ b/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.");
- }
+ if (createSocketAcceptHandler(&server.ipfd, acceptTcpHandler) != C_OK) {
+ serverPanic("Unrecoverable error creating TCP socket accept handler.");
}
- 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.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);
diff --git a/src/server.h b/src/server.h
index 55f718111..e241bad70 100644
--- a/src/server.h
+++ b/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);
diff --git a/src/setproctitle.c b/src/setproctitle.c
index 1c91570eb..019402348 100644
--- a/src/setproctitle.c
+++ b/src/setproctitle.c
@@ -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;
diff --git a/src/t_stream.c b/src/t_stream.c
index d9db97c3c..18138bec5 100644
--- a/src/t_stream.c
+++ b/src/t_stream.c
@@ -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;
diff --git a/src/version.h b/src/version.h
index 62ddcfd59..64fdbe89b 100644
--- a/src/version.h
+++ b/src/version.h
@@ -1,2 +1,2 @@
-#define REDIS_VERSION "6.2.0"
-#define REDIS_VERSION_NUM 0x00060200
+#define REDIS_VERSION "6.2.1"
+#define REDIS_VERSION_NUM 0x00060201
diff --git a/src/zmalloc.c b/src/zmalloc.c
index c8d6c825f..1dc662e57 100644
--- a/src/zmalloc.c
+++ b/src/zmalloc.c
@@ -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,20 +50,21 @@ 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
-#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)
#define malloc(size) tc_malloc(size)
@@ -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());
diff --git a/src/zmalloc.h b/src/zmalloc.h
index 64bc9fc76..d44c7b389 100644
--- a/src/zmalloc.h
+++ b/src/zmalloc.h
@@ -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)
diff --git a/tests/instances.tcl b/tests/instances.tcl
index 793bce80d..255d9740f 100644
--- a/tests/instances.tcl
+++ b/tests/instances.tcl
@@ -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 {
diff --git a/tests/integration/corrupt-dump-fuzzer.tcl b/tests/integration/corrupt-dump-fuzzer.tcl
index 2506205a5..4fb503b8e 100644
--- a/tests/integration/corrupt-dump-fuzzer.tcl
+++ b/tests/integration/corrupt-dump-fuzzer.tcl
@@ -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
diff --git a/tests/integration/logging.tcl b/tests/integration/logging.tcl
index fd9034644..fec14ff2b 100644
--- a/tests/integration/logging.tcl
+++ b/tests/integration/logging.tcl
@@ -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" {
diff --git a/tests/integration/rdb.tcl b/tests/integration/rdb.tcl
index 7df1e2f74..9e1c2651a 100644
--- a/tests/integration/rdb.tcl
+++ b/tests/integration/rdb.tcl
@@ -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
diff --git a/tests/support/server.tcl b/tests/support/server.tcl
index 4fbb99920..3ff923d7e 100644
--- a/tests/support/server.tcl
+++ b/tests/support/server.tcl
@@ -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 {
diff --git a/tests/test_helper.tcl b/tests/test_helper.tcl
index eb14b5ee9..3c7d46d57 100644
--- a/tests/test_helper.tcl
+++ b/tests/test_helper.tcl
@@ -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
diff --git a/tests/unit/info.tcl b/tests/unit/info.tcl
index 08171fff9..0602e7147 100644
--- a/tests/unit/info.tcl
+++ b/tests/unit/info.tcl
@@ -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]
diff --git a/tests/unit/introspection.tcl b/tests/unit/introspection.tcl
index ba28341ff..698ed7789 100644
--- a/tests/unit/introspection.tcl
+++ b/tests/unit/introspection.tcl
@@ -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
diff --git a/tests/unit/moduleapi/misc.tcl b/tests/unit/moduleapi/misc.tcl
index d60ccca8f..a6a7a78f9 100644
--- a/tests/unit/moduleapi/misc.tcl
+++ b/tests/unit/moduleapi/misc.tcl
@@ -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]
+ }
}
diff --git a/tests/unit/networking.tcl b/tests/unit/networking.tcl
new file mode 100644
index 000000000..19feee8c3
--- /dev/null
+++ b/tests/unit/networking.tcl
@@ -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
+ }
+} \ No newline at end of file
diff --git a/tests/unit/tls.tcl b/tests/unit/tls.tcl
index a6c2f3c2c..14e06fcdf 100644
--- a/tests/unit/tls.tcl
+++ b/tests/unit/tls.tcl
@@ -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
+ }
}
}