Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
ruanhaishen
redis
Commits
1a589818
Unverified
Commit
1a589818
authored
Jul 10, 2023
by
Oran Agra
Committed by
GitHub
Jul 10, 2023
Browse files
Release Redis 7.2 RC3
parents
a51eb05b
c7b3ce90
Changes
153
Expand all
Hide whitespace changes
Inline
Side-by-side
deps/hiredis/sds.h
View file @
1a589818
...
@@ -35,9 +35,11 @@
...
@@ -35,9 +35,11 @@
#define HI_SDS_MAX_PREALLOC (1024*1024)
#define HI_SDS_MAX_PREALLOC (1024*1024)
#ifdef _MSC_VER
#ifdef _MSC_VER
#define __attribute__(x)
typedef
long
long
ssize_t
;
typedef
long
long
ssize_t
;
#define SSIZE_MAX (LLONG_MAX >> 1)
#define SSIZE_MAX (LLONG_MAX >> 1)
#ifndef __clang__
#define __attribute__(x)
#endif
#endif
#endif
#include <sys/types.h>
#include <sys/types.h>
...
...
deps/hiredis/sockcompat.c
View file @
1a589818
...
@@ -180,10 +180,17 @@ int win32_connect(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen)
...
@@ -180,10 +180,17 @@ int win32_connect(SOCKET sockfd, const struct sockaddr *addr, socklen_t addrlen)
/* For Winsock connect(), the WSAEWOULDBLOCK error means the same thing as
/* For Winsock connect(), the WSAEWOULDBLOCK error means the same thing as
* EINPROGRESS for POSIX connect(), so we do that translation to keep POSIX
* EINPROGRESS for POSIX connect(), so we do that translation to keep POSIX
* logic consistent. */
* logic consistent.
if
(
errno
==
EWOULDBLOCK
)
{
* Additionally, WSAALREADY is can be reported as WSAEINVAL to and this is
* translated to EIO. Convert appropriately
*/
int
err
=
errno
;
if
(
err
==
EWOULDBLOCK
)
{
errno
=
EINPROGRESS
;
errno
=
EINPROGRESS
;
}
}
else
if
(
err
==
EIO
)
{
errno
=
EALREADY
;
}
return
ret
!=
SOCKET_ERROR
?
ret
:
-
1
;
return
ret
!=
SOCKET_ERROR
?
ret
:
-
1
;
}
}
...
@@ -205,6 +212,14 @@ int win32_getsockopt(SOCKET sockfd, int level, int optname, void *optval, sockle
...
@@ -205,6 +212,14 @@ int win32_getsockopt(SOCKET sockfd, int level, int optname, void *optval, sockle
}
else
{
}
else
{
ret
=
getsockopt
(
sockfd
,
level
,
optname
,
(
char
*
)
optval
,
optlen
);
ret
=
getsockopt
(
sockfd
,
level
,
optname
,
(
char
*
)
optval
,
optlen
);
}
}
if
(
ret
!=
SOCKET_ERROR
&&
level
==
SOL_SOCKET
&&
optname
==
SO_ERROR
)
{
/* translate SO_ERROR codes, if non-zero */
int
err
=
*
(
int
*
)
optval
;
if
(
err
!=
0
)
{
err
=
_wsaErrorToErrno
(
err
);
*
(
int
*
)
optval
=
err
;
}
}
_updateErrno
(
ret
!=
SOCKET_ERROR
);
_updateErrno
(
ret
!=
SOCKET_ERROR
);
return
ret
!=
SOCKET_ERROR
?
ret
:
-
1
;
return
ret
!=
SOCKET_ERROR
?
ret
:
-
1
;
}
}
...
@@ -245,4 +260,21 @@ int win32_poll(struct pollfd *fds, nfds_t nfds, int timeout) {
...
@@ -245,4 +260,21 @@ int win32_poll(struct pollfd *fds, nfds_t nfds, int timeout) {
_updateErrno
(
ret
!=
SOCKET_ERROR
);
_updateErrno
(
ret
!=
SOCKET_ERROR
);
return
ret
!=
SOCKET_ERROR
?
ret
:
-
1
;
return
ret
!=
SOCKET_ERROR
?
ret
:
-
1
;
}
}
int
win32_redisKeepAlive
(
SOCKET
sockfd
,
int
interval_ms
)
{
struct
tcp_keepalive
cfg
;
DWORD
bytes_in
;
int
res
;
cfg
.
onoff
=
1
;
cfg
.
keepaliveinterval
=
interval_ms
;
cfg
.
keepalivetime
=
interval_ms
;
res
=
WSAIoctl
(
sockfd
,
SIO_KEEPALIVE_VALS
,
&
cfg
,
sizeof
(
struct
tcp_keepalive
),
NULL
,
0
,
&
bytes_in
,
NULL
,
NULL
);
return
res
==
0
?
0
:
_wsaErrorToErrno
(
res
);
}
#endif
/* _WIN32 */
#endif
/* _WIN32 */
deps/hiredis/sockcompat.h
View file @
1a589818
...
@@ -50,6 +50,7 @@
...
@@ -50,6 +50,7 @@
#include <ws2tcpip.h>
#include <ws2tcpip.h>
#include <stddef.h>
#include <stddef.h>
#include <errno.h>
#include <errno.h>
#include <mstcpip.h>
#ifdef _MSC_VER
#ifdef _MSC_VER
typedef
long
long
ssize_t
;
typedef
long
long
ssize_t
;
...
@@ -71,6 +72,8 @@ ssize_t win32_send(SOCKET sockfd, const void *buf, size_t len, int flags);
...
@@ -71,6 +72,8 @@ ssize_t win32_send(SOCKET sockfd, const void *buf, size_t len, int flags);
typedef
ULONG
nfds_t
;
typedef
ULONG
nfds_t
;
int
win32_poll
(
struct
pollfd
*
fds
,
nfds_t
nfds
,
int
timeout
);
int
win32_poll
(
struct
pollfd
*
fds
,
nfds_t
nfds
,
int
timeout
);
int
win32_redisKeepAlive
(
SOCKET
sockfd
,
int
interval_ms
);
#ifndef REDIS_SOCKCOMPAT_IMPLEMENTATION
#ifndef REDIS_SOCKCOMPAT_IMPLEMENTATION
#define getaddrinfo(node, service, hints, res) win32_getaddrinfo(node, service, hints, res)
#define getaddrinfo(node, service, hints, res) win32_getaddrinfo(node, service, hints, res)
#undef gai_strerror
#undef gai_strerror
...
...
deps/hiredis/ssl.c
View file @
1a589818
...
@@ -32,6 +32,7 @@
...
@@ -32,6 +32,7 @@
#include "hiredis.h"
#include "hiredis.h"
#include "async.h"
#include "async.h"
#include "net.h"
#include <assert.h>
#include <assert.h>
#include <errno.h>
#include <errno.h>
...
@@ -39,6 +40,14 @@
...
@@ -39,6 +40,14 @@
#ifdef _WIN32
#ifdef _WIN32
#include <windows.h>
#include <windows.h>
#include <wincrypt.h>
#include <wincrypt.h>
#ifdef OPENSSL_IS_BORINGSSL
#undef X509_NAME
#undef X509_EXTENSIONS
#undef PKCS7_ISSUER_AND_SERIAL
#undef PKCS7_SIGNER_INFO
#undef OCSP_REQUEST
#undef OCSP_RESPONSE
#endif
#else
#else
#include <pthread.h>
#include <pthread.h>
#endif
#endif
...
@@ -219,6 +228,25 @@ redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *
...
@@ -219,6 +228,25 @@ redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *
const
char
*
cert_filename
,
const
char
*
private_key_filename
,
const
char
*
cert_filename
,
const
char
*
private_key_filename
,
const
char
*
server_name
,
redisSSLContextError
*
error
)
const
char
*
server_name
,
redisSSLContextError
*
error
)
{
{
redisSSLOptions
options
=
{
.
cacert_filename
=
cacert_filename
,
.
capath
=
capath
,
.
cert_filename
=
cert_filename
,
.
private_key_filename
=
private_key_filename
,
.
server_name
=
server_name
,
.
verify_mode
=
REDIS_SSL_VERIFY_PEER
,
};
return
redisCreateSSLContextWithOptions
(
&
options
,
error
);
}
redisSSLContext
*
redisCreateSSLContextWithOptions
(
redisSSLOptions
*
options
,
redisSSLContextError
*
error
)
{
const
char
*
cacert_filename
=
options
->
cacert_filename
;
const
char
*
capath
=
options
->
capath
;
const
char
*
cert_filename
=
options
->
cert_filename
;
const
char
*
private_key_filename
=
options
->
private_key_filename
;
const
char
*
server_name
=
options
->
server_name
;
#ifdef _WIN32
#ifdef _WIN32
HCERTSTORE
win_store
=
NULL
;
HCERTSTORE
win_store
=
NULL
;
PCCERT_CONTEXT
win_ctx
=
NULL
;
PCCERT_CONTEXT
win_ctx
=
NULL
;
...
@@ -235,7 +263,7 @@ redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *
...
@@ -235,7 +263,7 @@ redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *
}
}
SSL_CTX_set_options
(
ctx
->
ssl_ctx
,
SSL_OP_NO_SSLv2
|
SSL_OP_NO_SSLv3
);
SSL_CTX_set_options
(
ctx
->
ssl_ctx
,
SSL_OP_NO_SSLv2
|
SSL_OP_NO_SSLv3
);
SSL_CTX_set_verify
(
ctx
->
ssl_ctx
,
SSL_VERIFY_PEER
,
NULL
);
SSL_CTX_set_verify
(
ctx
->
ssl_ctx
,
options
->
verify_mode
,
NULL
);
if
((
cert_filename
!=
NULL
&&
private_key_filename
==
NULL
)
||
if
((
cert_filename
!=
NULL
&&
private_key_filename
==
NULL
)
||
(
private_key_filename
!=
NULL
&&
cert_filename
==
NULL
))
{
(
private_key_filename
!=
NULL
&&
cert_filename
==
NULL
))
{
...
@@ -273,6 +301,11 @@ redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *
...
@@ -273,6 +301,11 @@ redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *
if
(
error
)
*
error
=
REDIS_SSL_CTX_CA_CERT_LOAD_FAILED
;
if
(
error
)
*
error
=
REDIS_SSL_CTX_CA_CERT_LOAD_FAILED
;
goto
error
;
goto
error
;
}
}
}
else
{
if
(
!
SSL_CTX_set_default_verify_paths
(
ctx
->
ssl_ctx
))
{
if
(
error
)
*
error
=
REDIS_SSL_CTX_CLIENT_DEFAULT_CERT_FAILED
;
goto
error
;
}
}
}
if
(
cert_filename
)
{
if
(
cert_filename
)
{
...
@@ -560,6 +593,7 @@ static void redisSSLAsyncWrite(redisAsyncContext *ac) {
...
@@ -560,6 +593,7 @@ static void redisSSLAsyncWrite(redisAsyncContext *ac) {
}
}
redisContextFuncs
redisContextSSLFuncs
=
{
redisContextFuncs
redisContextSSLFuncs
=
{
.
close
=
redisNetClose
,
.
free_privctx
=
redisSSLFree
,
.
free_privctx
=
redisSSLFree
,
.
async_read
=
redisSSLAsyncRead
,
.
async_read
=
redisSSLAsyncRead
,
.
async_write
=
redisSSLAsyncWrite
,
.
async_write
=
redisSSLAsyncWrite
,
...
...
deps/hiredis/test.c
View file @
1a589818
This diff is collapsed.
Click to expand it.
deps/hiredis/test.sh
View file @
1a589818
...
@@ -4,9 +4,17 @@ REDIS_SERVER=${REDIS_SERVER:-redis-server}
...
@@ -4,9 +4,17 @@ REDIS_SERVER=${REDIS_SERVER:-redis-server}
REDIS_PORT
=
${
REDIS_PORT
:-
56379
}
REDIS_PORT
=
${
REDIS_PORT
:-
56379
}
REDIS_SSL_PORT
=
${
REDIS_SSL_PORT
:-
56443
}
REDIS_SSL_PORT
=
${
REDIS_SSL_PORT
:-
56443
}
TEST_SSL
=
${
TEST_SSL
:-
0
}
TEST_SSL
=
${
TEST_SSL
:-
0
}
SKIPS_AS_FAILS
=
${
SKIPS_AS_FAILS
-
:0
}
SKIPS_AS_FAILS
=
${
SKIPS_AS_FAILS
:-
0
}
ENABLE_DEBUG_CMD
=
SSL_TEST_ARGS
=
SSL_TEST_ARGS
=
SKIPS_ARG
=
SKIPS_ARG
=
${
SKIPS_ARG
:-}
REDIS_DOCKER
=
${
REDIS_DOCKER
:-}
# We need to enable the DEBUG command for redis-server >= 7.0.0
REDIS_MAJOR_VERSION
=
"
$(
redis-server
--version
|awk
-F
'[^0-9]+'
'{ print $2 }'
)
"
if
[
"
$REDIS_MAJOR_VERSION
"
-gt
"6"
]
;
then
ENABLE_DEBUG_CMD
=
"enable-debug-command local"
fi
tmpdir
=
$(
mktemp
-d
)
tmpdir
=
$(
mktemp
-d
)
PID_FILE
=
${
tmpdir
}
/hiredis-test-redis.pid
PID_FILE
=
${
tmpdir
}
/hiredis-test-redis.pid
...
@@ -43,20 +51,34 @@ if [ "$TEST_SSL" = "1" ]; then
...
@@ -43,20 +51,34 @@ if [ "$TEST_SSL" = "1" ]; then
fi
fi
cleanup
()
{
cleanup
()
{
set
+e
if
[
-n
"
${
REDIS_DOCKER
}
"
]
;
then
kill
$(
cat
${
PID_FILE
}
)
docker
kill
redis-test-server
else
set
+e
kill
$(
cat
${
PID_FILE
}
)
fi
rm
-rf
${
tmpdir
}
rm
-rf
${
tmpdir
}
}
}
trap
cleanup INT TERM EXIT
trap
cleanup INT TERM EXIT
# base config
cat
>
${
tmpdir
}
/redis.conf
<<
EOF
cat
>
${
tmpdir
}
/redis.conf
<<
EOF
daemonize yes
pidfile
${
PID_FILE
}
pidfile
${
PID_FILE
}
port
${
REDIS_PORT
}
port
${
REDIS_PORT
}
bind 127.0.0.1
unixsocket
${
SOCK_FILE
}
unixsocket
${
SOCK_FILE
}
unixsocketperm 777
EOF
EOF
# if not running in docker add these:
if
[
!
-n
"
${
REDIS_DOCKER
}
"
]
;
then
cat
>>
${
tmpdir
}
/redis.conf
<<
EOF
daemonize yes
${
ENABLE_DEBUG_CMD
}
bind 127.0.0.1
EOF
fi
# if doing ssl, add these
if
[
"
$TEST_SSL
"
=
"1"
]
;
then
if
[
"
$TEST_SSL
"
=
"1"
]
;
then
cat
>>
${
tmpdir
}
/redis.conf
<<
EOF
cat
>>
${
tmpdir
}
/redis.conf
<<
EOF
tls-port
${
REDIS_SSL_PORT
}
tls-port
${
REDIS_SSL_PORT
}
...
@@ -66,13 +88,25 @@ tls-key-file ${SSL_KEY}
...
@@ -66,13 +88,25 @@ tls-key-file ${SSL_KEY}
EOF
EOF
fi
fi
echo
${
tmpdir
}
cat
${
tmpdir
}
/redis.conf
cat
${
tmpdir
}
/redis.conf
${
REDIS_SERVER
}
${
tmpdir
}
/redis.conf
if
[
-n
"
${
REDIS_DOCKER
}
"
]
;
then
chmod
a+wx
${
tmpdir
}
chmod
a+r
${
tmpdir
}
/
*
docker run
-d
--rm
--name
redis-test-server
\
-p
${
REDIS_PORT
}
:
${
REDIS_PORT
}
\
-p
${
REDIS_SSL_PORT
}
:
${
REDIS_SSL_PORT
}
\
-v
${
tmpdir
}
:
${
tmpdir
}
\
${
REDIS_DOCKER
}
\
redis-server
${
tmpdir
}
/redis.conf
else
${
REDIS_SERVER
}
${
tmpdir
}
/redis.conf
fi
# Wait until we detect the unix socket
# Wait until we detect the unix socket
echo
waiting
for
server
while
[
!
-S
"
${
SOCK_FILE
}
"
]
;
do
sleep
1
;
done
while
[
!
-S
"
${
SOCK_FILE
}
"
]
;
do
sleep
1
;
done
# Treat skips as failures if directed
# Treat skips as failures if directed
[
"
$SKIPS_AS_FAILS
"
=
1
]
&&
SKIPS_ARG
=
"--skips-as-fails"
[
"
$SKIPS_AS_FAILS
"
=
1
]
&&
SKIPS_ARG
=
"
${
SKIPS_ARG
}
--skips-as-fails"
${
TEST_PREFIX
:-}
./hiredis-test
-h
127.0.0.1
-p
${
REDIS_PORT
}
-s
${
SOCK_FILE
}
${
SSL_TEST_ARGS
}
${
SKIPS_ARG
}
${
TEST_PREFIX
:-}
./hiredis-test
-h
127.0.0.1
-p
${
REDIS_PORT
}
-s
${
SOCK_FILE
}
${
SSL_TEST_ARGS
}
${
SKIPS_ARG
}
deps/lua/src/lua_cjson.c
View file @
1a589818
...
@@ -39,6 +39,7 @@
...
@@ -39,6 +39,7 @@
#include <assert.h>
#include <assert.h>
#include <string.h>
#include <string.h>
#include <math.h>
#include <math.h>
#include <stdint.h>
#include <limits.h>
#include <limits.h>
#include "lua.h"
#include "lua.h"
#include "lauxlib.h"
#include "lauxlib.h"
...
@@ -141,13 +142,13 @@ typedef struct {
...
@@ -141,13 +142,13 @@ typedef struct {
typedef
struct
{
typedef
struct
{
json_token_type_t
type
;
json_token_type_t
type
;
in
t
index
;
size_
t
index
;
union
{
union
{
const
char
*
string
;
const
char
*
string
;
double
number
;
double
number
;
int
boolean
;
int
boolean
;
}
value
;
}
value
;
in
t
string_len
;
size_
t
string_len
;
}
json_token_t
;
}
json_token_t
;
static
const
char
*
char2escape
[
256
]
=
{
static
const
char
*
char2escape
[
256
]
=
{
...
@@ -473,6 +474,8 @@ static void json_append_string(lua_State *l, strbuf_t *json, int lindex)
...
@@ -473,6 +474,8 @@ static void json_append_string(lua_State *l, strbuf_t *json, int lindex)
* This buffer is reused constantly for small strings
* This buffer is reused constantly for small strings
* If there are any excess pages, they won't be hit anyway.
* If there are any excess pages, they won't be hit anyway.
* This gains ~5% speedup. */
* This gains ~5% speedup. */
if
(
len
>
SIZE_MAX
/
6
-
3
)
abort
();
/* Overflow check */
strbuf_ensure_empty_length
(
json
,
len
*
6
+
2
);
strbuf_ensure_empty_length
(
json
,
len
*
6
+
2
);
strbuf_append_char_unsafe
(
json
,
'\"'
);
strbuf_append_char_unsafe
(
json
,
'\"'
);
...
@@ -706,7 +709,7 @@ static int json_encode(lua_State *l)
...
@@ -706,7 +709,7 @@ static int json_encode(lua_State *l)
strbuf_t
local_encode_buf
;
strbuf_t
local_encode_buf
;
strbuf_t
*
encode_buf
;
strbuf_t
*
encode_buf
;
char
*
json
;
char
*
json
;
in
t
len
;
size_
t
len
;
luaL_argcheck
(
l
,
lua_gettop
(
l
)
==
1
,
1
,
"expected 1 argument"
);
luaL_argcheck
(
l
,
lua_gettop
(
l
)
==
1
,
1
,
"expected 1 argument"
);
...
...
deps/lua/src/lua_cmsgpack.c
View file @
1a589818
...
@@ -117,7 +117,9 @@ mp_buf *mp_buf_new(lua_State *L) {
...
@@ -117,7 +117,9 @@ mp_buf *mp_buf_new(lua_State *L) {
void
mp_buf_append
(
lua_State
*
L
,
mp_buf
*
buf
,
const
unsigned
char
*
s
,
size_t
len
)
{
void
mp_buf_append
(
lua_State
*
L
,
mp_buf
*
buf
,
const
unsigned
char
*
s
,
size_t
len
)
{
if
(
buf
->
free
<
len
)
{
if
(
buf
->
free
<
len
)
{
size_t
newsize
=
(
buf
->
len
+
len
)
*
2
;
size_t
newsize
=
buf
->
len
+
len
;
if
(
newsize
<
buf
->
len
||
newsize
>=
SIZE_MAX
/
2
)
abort
();
newsize
*=
2
;
buf
->
b
=
(
unsigned
char
*
)
mp_realloc
(
L
,
buf
->
b
,
buf
->
len
+
buf
->
free
,
newsize
);
buf
->
b
=
(
unsigned
char
*
)
mp_realloc
(
L
,
buf
->
b
,
buf
->
len
+
buf
->
free
,
newsize
);
buf
->
free
=
newsize
-
buf
->
len
;
buf
->
free
=
newsize
-
buf
->
len
;
...
@@ -173,7 +175,7 @@ void mp_cur_init(mp_cur *cursor, const unsigned char *s, size_t len) {
...
@@ -173,7 +175,7 @@ void mp_cur_init(mp_cur *cursor, const unsigned char *s, size_t len) {
void
mp_encode_bytes
(
lua_State
*
L
,
mp_buf
*
buf
,
const
unsigned
char
*
s
,
size_t
len
)
{
void
mp_encode_bytes
(
lua_State
*
L
,
mp_buf
*
buf
,
const
unsigned
char
*
s
,
size_t
len
)
{
unsigned
char
hdr
[
5
];
unsigned
char
hdr
[
5
];
in
t
hdrlen
;
size_
t
hdrlen
;
if
(
len
<
32
)
{
if
(
len
<
32
)
{
hdr
[
0
]
=
0xa0
|
(
len
&
0xff
);
/* fix raw */
hdr
[
0
]
=
0xa0
|
(
len
&
0xff
);
/* fix raw */
...
@@ -220,7 +222,7 @@ void mp_encode_double(lua_State *L, mp_buf *buf, double d) {
...
@@ -220,7 +222,7 @@ void mp_encode_double(lua_State *L, mp_buf *buf, double d) {
void
mp_encode_int
(
lua_State
*
L
,
mp_buf
*
buf
,
int64_t
n
)
{
void
mp_encode_int
(
lua_State
*
L
,
mp_buf
*
buf
,
int64_t
n
)
{
unsigned
char
b
[
9
];
unsigned
char
b
[
9
];
in
t
enclen
;
size_
t
enclen
;
if
(
n
>=
0
)
{
if
(
n
>=
0
)
{
if
(
n
<=
127
)
{
if
(
n
<=
127
)
{
...
@@ -290,9 +292,9 @@ void mp_encode_int(lua_State *L, mp_buf *buf, int64_t n) {
...
@@ -290,9 +292,9 @@ void mp_encode_int(lua_State *L, mp_buf *buf, int64_t n) {
mp_buf_append
(
L
,
buf
,
b
,
enclen
);
mp_buf_append
(
L
,
buf
,
b
,
enclen
);
}
}
void
mp_encode_array
(
lua_State
*
L
,
mp_buf
*
buf
,
int64_t
n
)
{
void
mp_encode_array
(
lua_State
*
L
,
mp_buf
*
buf
,
u
int64_t
n
)
{
unsigned
char
b
[
5
];
unsigned
char
b
[
5
];
in
t
enclen
;
size_
t
enclen
;
if
(
n
<=
15
)
{
if
(
n
<=
15
)
{
b
[
0
]
=
0x90
|
(
n
&
0xf
);
/* fix array */
b
[
0
]
=
0x90
|
(
n
&
0xf
);
/* fix array */
...
@@ -313,7 +315,7 @@ void mp_encode_array(lua_State *L, mp_buf *buf, int64_t n) {
...
@@ -313,7 +315,7 @@ void mp_encode_array(lua_State *L, mp_buf *buf, int64_t n) {
mp_buf_append
(
L
,
buf
,
b
,
enclen
);
mp_buf_append
(
L
,
buf
,
b
,
enclen
);
}
}
void
mp_encode_map
(
lua_State
*
L
,
mp_buf
*
buf
,
int64_t
n
)
{
void
mp_encode_map
(
lua_State
*
L
,
mp_buf
*
buf
,
u
int64_t
n
)
{
unsigned
char
b
[
5
];
unsigned
char
b
[
5
];
int
enclen
;
int
enclen
;
...
@@ -791,7 +793,7 @@ void mp_decode_to_lua_type(lua_State *L, mp_cur *c) {
...
@@ -791,7 +793,7 @@ void mp_decode_to_lua_type(lua_State *L, mp_cur *c) {
}
}
}
}
int
mp_unpack_full
(
lua_State
*
L
,
int
limit
,
int
offset
)
{
int
mp_unpack_full
(
lua_State
*
L
,
lua_Integer
limit
,
lua_Integer
offset
)
{
size_t
len
;
size_t
len
;
const
char
*
s
;
const
char
*
s
;
mp_cur
c
;
mp_cur
c
;
...
@@ -803,10 +805,10 @@ int mp_unpack_full(lua_State *L, int limit, int offset) {
...
@@ -803,10 +805,10 @@ int mp_unpack_full(lua_State *L, int limit, int offset) {
if
(
offset
<
0
||
limit
<
0
)
/* requesting negative off or lim is invalid */
if
(
offset
<
0
||
limit
<
0
)
/* requesting negative off or lim is invalid */
return
luaL_error
(
L
,
return
luaL_error
(
L
,
"Invalid request to unpack with offset of %d and limit of %d."
,
"Invalid request to unpack with offset of %d and limit of %d."
,
offset
,
len
);
(
int
)
offset
,
(
int
)
len
);
else
if
(
offset
>
len
)
else
if
(
offset
>
len
)
return
luaL_error
(
L
,
return
luaL_error
(
L
,
"Start offset %d greater than input length %d."
,
offset
,
len
);
"Start offset %d greater than input length %d."
,
(
int
)
offset
,
(
int
)
len
);
if
(
decode_all
)
limit
=
INT_MAX
;
if
(
decode_all
)
limit
=
INT_MAX
;
...
@@ -828,12 +830,13 @@ int mp_unpack_full(lua_State *L, int limit, int offset) {
...
@@ -828,12 +830,13 @@ int mp_unpack_full(lua_State *L, int limit, int offset) {
/* c->left is the remaining size of the input buffer.
/* c->left is the remaining size of the input buffer.
* subtract the entire buffer size from the unprocessed size
* subtract the entire buffer size from the unprocessed size
* to get our next start offset */
* to get our next start offset */
int
offset
=
len
-
c
.
left
;
size_t
new_offset
=
len
-
c
.
left
;
if
(
new_offset
>
LONG_MAX
)
abort
();
luaL_checkstack
(
L
,
1
,
"in function mp_unpack_full"
);
luaL_checkstack
(
L
,
1
,
"in function mp_unpack_full"
);
/* Return offset -1 when we have have processed the entire buffer. */
/* Return offset -1 when we have have processed the entire buffer. */
lua_pushinteger
(
L
,
c
.
left
==
0
?
-
1
:
offset
);
lua_pushinteger
(
L
,
c
.
left
==
0
?
-
1
:
(
lua_Integer
)
new_
offset
);
/* Results are returned with the arg elements still
/* Results are returned with the arg elements still
* in place. Lua takes care of only returning
* in place. Lua takes care of only returning
* elements above the args for us.
* elements above the args for us.
...
@@ -852,15 +855,15 @@ int mp_unpack(lua_State *L) {
...
@@ -852,15 +855,15 @@ int mp_unpack(lua_State *L) {
}
}
int
mp_unpack_one
(
lua_State
*
L
)
{
int
mp_unpack_one
(
lua_State
*
L
)
{
int
offset
=
luaL_optinteger
(
L
,
2
,
0
);
lua_Integer
offset
=
luaL_optinteger
(
L
,
2
,
0
);
/* Variable pop because offset may not exist */
/* Variable pop because offset may not exist */
lua_pop
(
L
,
lua_gettop
(
L
)
-
1
);
lua_pop
(
L
,
lua_gettop
(
L
)
-
1
);
return
mp_unpack_full
(
L
,
1
,
offset
);
return
mp_unpack_full
(
L
,
1
,
offset
);
}
}
int
mp_unpack_limit
(
lua_State
*
L
)
{
int
mp_unpack_limit
(
lua_State
*
L
)
{
int
limit
=
luaL_checkinteger
(
L
,
2
);
lua_Integer
limit
=
luaL_checkinteger
(
L
,
2
);
int
offset
=
luaL_optinteger
(
L
,
3
,
0
);
lua_Integer
offset
=
luaL_optinteger
(
L
,
3
,
0
);
/* Variable pop because offset may not exist */
/* Variable pop because offset may not exist */
lua_pop
(
L
,
lua_gettop
(
L
)
-
1
);
lua_pop
(
L
,
lua_gettop
(
L
)
-
1
);
...
...
deps/lua/src/strbuf.c
View file @
1a589818
...
@@ -26,6 +26,7 @@
...
@@ -26,6 +26,7 @@
#include <stdlib.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdarg.h>
#include <string.h>
#include <string.h>
#include <stdint.h>
#include "strbuf.h"
#include "strbuf.h"
...
@@ -38,22 +39,22 @@ static void die(const char *fmt, ...)
...
@@ -38,22 +39,22 @@ static void die(const char *fmt, ...)
va_end
(
arg
);
va_end
(
arg
);
fprintf
(
stderr
,
"
\n
"
);
fprintf
(
stderr
,
"
\n
"
);
exit
(
-
1
);
abort
(
);
}
}
void
strbuf_init
(
strbuf_t
*
s
,
in
t
len
)
void
strbuf_init
(
strbuf_t
*
s
,
size_
t
len
)
{
{
in
t
size
;
size_
t
size
;
if
(
len
<=
0
)
if
(
!
len
)
size
=
STRBUF_DEFAULT_SIZE
;
size
=
STRBUF_DEFAULT_SIZE
;
else
else
size
=
len
+
1
;
/* \0 terminator */
size
=
len
+
1
;
if
(
size
<
len
)
die
(
"Overflow, len: %zu"
,
len
);
s
->
buf
=
NULL
;
s
->
buf
=
NULL
;
s
->
size
=
size
;
s
->
size
=
size
;
s
->
length
=
0
;
s
->
length
=
0
;
s
->
increment
=
STRBUF_DEFAULT_INCREMENT
;
s
->
dynamic
=
0
;
s
->
dynamic
=
0
;
s
->
reallocs
=
0
;
s
->
reallocs
=
0
;
s
->
debug
=
0
;
s
->
debug
=
0
;
...
@@ -65,7 +66,7 @@ void strbuf_init(strbuf_t *s, int len)
...
@@ -65,7 +66,7 @@ void strbuf_init(strbuf_t *s, int len)
strbuf_ensure_null
(
s
);
strbuf_ensure_null
(
s
);
}
}
strbuf_t
*
strbuf_new
(
in
t
len
)
strbuf_t
*
strbuf_new
(
size_
t
len
)
{
{
strbuf_t
*
s
;
strbuf_t
*
s
;
...
@@ -81,20 +82,10 @@ strbuf_t *strbuf_new(int len)
...
@@ -81,20 +82,10 @@ strbuf_t *strbuf_new(int len)
return
s
;
return
s
;
}
}
void
strbuf_set_increment
(
strbuf_t
*
s
,
int
increment
)
{
/* Increment > 0: Linear buffer growth rate
* Increment < -1: Exponential buffer growth rate */
if
(
increment
==
0
||
increment
==
-
1
)
die
(
"BUG: Invalid string increment"
);
s
->
increment
=
increment
;
}
static
inline
void
debug_stats
(
strbuf_t
*
s
)
static
inline
void
debug_stats
(
strbuf_t
*
s
)
{
{
if
(
s
->
debug
)
{
if
(
s
->
debug
)
{
fprintf
(
stderr
,
"strbuf(%lx) reallocs: %d, length: %d, size: %d
\n
"
,
fprintf
(
stderr
,
"strbuf(%lx) reallocs: %d, length: %
z
d, size: %
z
d
\n
"
,
(
long
)
s
,
s
->
reallocs
,
s
->
length
,
s
->
size
);
(
long
)
s
,
s
->
reallocs
,
s
->
length
,
s
->
size
);
}
}
}
}
...
@@ -113,7 +104,7 @@ void strbuf_free(strbuf_t *s)
...
@@ -113,7 +104,7 @@ void strbuf_free(strbuf_t *s)
free
(
s
);
free
(
s
);
}
}
char
*
strbuf_free_to_string
(
strbuf_t
*
s
,
in
t
*
len
)
char
*
strbuf_free_to_string
(
strbuf_t
*
s
,
size_
t
*
len
)
{
{
char
*
buf
;
char
*
buf
;
...
@@ -131,57 +122,62 @@ char *strbuf_free_to_string(strbuf_t *s, int *len)
...
@@ -131,57 +122,62 @@ char *strbuf_free_to_string(strbuf_t *s, int *len)
return
buf
;
return
buf
;
}
}
static
in
t
calculate_new_size
(
strbuf_t
*
s
,
in
t
len
)
static
size_
t
calculate_new_size
(
strbuf_t
*
s
,
size_
t
len
)
{
{
in
t
reqsize
,
newsize
;
size_
t
reqsize
,
newsize
;
if
(
len
<=
0
)
if
(
len
<=
0
)
die
(
"BUG: Invalid strbuf length requested"
);
die
(
"BUG: Invalid strbuf length requested"
);
/* Ensure there is room for optional NULL termination */
/* Ensure there is room for optional NULL termination */
reqsize
=
len
+
1
;
reqsize
=
len
+
1
;
if
(
reqsize
<
len
)
die
(
"Overflow, len: %zu"
,
len
);
/* If the user has requested to shrink the buffer, do it exactly */
/* If the user has requested to shrink the buffer, do it exactly */
if
(
s
->
size
>
reqsize
)
if
(
s
->
size
>
reqsize
)
return
reqsize
;
return
reqsize
;
newsize
=
s
->
size
;
newsize
=
s
->
size
;
if
(
s
->
increment
<
0
)
{
if
(
reqsize
>=
SIZE_MAX
/
2
)
{
newsize
=
reqsize
;
}
else
{
/* Exponential sizing */
/* Exponential sizing */
while
(
newsize
<
reqsize
)
while
(
newsize
<
reqsize
)
newsize
*=
-
s
->
increment
;
newsize
*=
2
;
}
else
{
/* Linear sizing */
newsize
=
((
newsize
+
s
->
increment
-
1
)
/
s
->
increment
)
*
s
->
increment
;
}
}
if
(
newsize
<
reqsize
)
die
(
"BUG: strbuf length would overflow, len: %zu"
,
len
);
return
newsize
;
return
newsize
;
}
}
/* Ensure strbuf can handle a string length bytes long (ignoring NULL
/* Ensure strbuf can handle a string length bytes long (ignoring NULL
* optional termination). */
* optional termination). */
void
strbuf_resize
(
strbuf_t
*
s
,
in
t
len
)
void
strbuf_resize
(
strbuf_t
*
s
,
size_
t
len
)
{
{
in
t
newsize
;
size_
t
newsize
;
newsize
=
calculate_new_size
(
s
,
len
);
newsize
=
calculate_new_size
(
s
,
len
);
if
(
s
->
debug
>
1
)
{
if
(
s
->
debug
>
1
)
{
fprintf
(
stderr
,
"strbuf(%lx) resize: %d => %d
\n
"
,
fprintf
(
stderr
,
"strbuf(%lx) resize: %
z
d => %
z
d
\n
"
,
(
long
)
s
,
s
->
size
,
newsize
);
(
long
)
s
,
s
->
size
,
newsize
);
}
}
s
->
size
=
newsize
;
s
->
size
=
newsize
;
s
->
buf
=
realloc
(
s
->
buf
,
s
->
size
);
s
->
buf
=
realloc
(
s
->
buf
,
s
->
size
);
if
(
!
s
->
buf
)
if
(
!
s
->
buf
)
die
(
"Out of memory
"
);
die
(
"Out of memory
, len: %zu"
,
len
);
s
->
reallocs
++
;
s
->
reallocs
++
;
}
}
void
strbuf_append_string
(
strbuf_t
*
s
,
const
char
*
str
)
void
strbuf_append_string
(
strbuf_t
*
s
,
const
char
*
str
)
{
{
int
space
,
i
;
int
i
;
size_t
space
;
space
=
strbuf_empty_length
(
s
);
space
=
strbuf_empty_length
(
s
);
...
@@ -197,55 +193,6 @@ void strbuf_append_string(strbuf_t *s, const char *str)
...
@@ -197,55 +193,6 @@ void strbuf_append_string(strbuf_t *s, const char *str)
}
}
}
}
/* strbuf_append_fmt() should only be used when an upper bound
* is known for the output string. */
void
strbuf_append_fmt
(
strbuf_t
*
s
,
int
len
,
const
char
*
fmt
,
...)
{
va_list
arg
;
int
fmt_len
;
strbuf_ensure_empty_length
(
s
,
len
);
va_start
(
arg
,
fmt
);
fmt_len
=
vsnprintf
(
s
->
buf
+
s
->
length
,
len
,
fmt
,
arg
);
va_end
(
arg
);
if
(
fmt_len
<
0
)
die
(
"BUG: Unable to convert number"
);
/* This should never happen.. */
s
->
length
+=
fmt_len
;
}
/* strbuf_append_fmt_retry() can be used when the there is no known
* upper bound for the output string. */
void
strbuf_append_fmt_retry
(
strbuf_t
*
s
,
const
char
*
fmt
,
...)
{
va_list
arg
;
int
fmt_len
,
try
;
int
empty_len
;
/* If the first attempt to append fails, resize the buffer appropriately
* and try again */
for
(
try
=
0
;
;
try
++
)
{
va_start
(
arg
,
fmt
);
/* Append the new formatted string */
/* fmt_len is the length of the string required, excluding the
* trailing NULL */
empty_len
=
strbuf_empty_length
(
s
);
/* Add 1 since there is also space to store the terminating NULL. */
fmt_len
=
vsnprintf
(
s
->
buf
+
s
->
length
,
empty_len
+
1
,
fmt
,
arg
);
va_end
(
arg
);
if
(
fmt_len
<=
empty_len
)
break
;
/* SUCCESS */
if
(
try
>
0
)
die
(
"BUG: length of formatted string changed"
);
strbuf_resize
(
s
,
s
->
length
+
fmt_len
);
}
s
->
length
+=
fmt_len
;
}
/* vi:ai et sw=4 ts=4:
/* vi:ai et sw=4 ts=4:
*/
*/
deps/lua/src/strbuf.h
View file @
1a589818
...
@@ -27,15 +27,13 @@
...
@@ -27,15 +27,13 @@
/* Size: Total bytes allocated to *buf
/* Size: Total bytes allocated to *buf
* Length: String length, excluding optional NULL terminator.
* Length: String length, excluding optional NULL terminator.
* Increment: Allocation increments when resizing the string buffer.
* Dynamic: True if created via strbuf_new()
* Dynamic: True if created via strbuf_new()
*/
*/
typedef
struct
{
typedef
struct
{
char
*
buf
;
char
*
buf
;
int
size
;
size_t
size
;
int
length
;
size_t
length
;
int
increment
;
int
dynamic
;
int
dynamic
;
int
reallocs
;
int
reallocs
;
int
debug
;
int
debug
;
...
@@ -44,32 +42,26 @@ typedef struct {
...
@@ -44,32 +42,26 @@ typedef struct {
#ifndef STRBUF_DEFAULT_SIZE
#ifndef STRBUF_DEFAULT_SIZE
#define STRBUF_DEFAULT_SIZE 1023
#define STRBUF_DEFAULT_SIZE 1023
#endif
#endif
#ifndef STRBUF_DEFAULT_INCREMENT
#define STRBUF_DEFAULT_INCREMENT -2
#endif
/* Initialise */
/* Initialise */
extern
strbuf_t
*
strbuf_new
(
int
len
);
extern
strbuf_t
*
strbuf_new
(
size_t
len
);
extern
void
strbuf_init
(
strbuf_t
*
s
,
int
len
);
extern
void
strbuf_init
(
strbuf_t
*
s
,
size_t
len
);
extern
void
strbuf_set_increment
(
strbuf_t
*
s
,
int
increment
);
/* Release */
/* Release */
extern
void
strbuf_free
(
strbuf_t
*
s
);
extern
void
strbuf_free
(
strbuf_t
*
s
);
extern
char
*
strbuf_free_to_string
(
strbuf_t
*
s
,
in
t
*
len
);
extern
char
*
strbuf_free_to_string
(
strbuf_t
*
s
,
size_
t
*
len
);
/* Management */
/* Management */
extern
void
strbuf_resize
(
strbuf_t
*
s
,
in
t
len
);
extern
void
strbuf_resize
(
strbuf_t
*
s
,
size_
t
len
);
static
in
t
strbuf_empty_length
(
strbuf_t
*
s
);
static
size_
t
strbuf_empty_length
(
strbuf_t
*
s
);
static
in
t
strbuf_length
(
strbuf_t
*
s
);
static
size_
t
strbuf_length
(
strbuf_t
*
s
);
static
char
*
strbuf_string
(
strbuf_t
*
s
,
in
t
*
len
);
static
char
*
strbuf_string
(
strbuf_t
*
s
,
size_
t
*
len
);
static
void
strbuf_ensure_empty_length
(
strbuf_t
*
s
,
in
t
len
);
static
void
strbuf_ensure_empty_length
(
strbuf_t
*
s
,
size_
t
len
);
static
char
*
strbuf_empty_ptr
(
strbuf_t
*
s
);
static
char
*
strbuf_empty_ptr
(
strbuf_t
*
s
);
static
void
strbuf_extend_length
(
strbuf_t
*
s
,
in
t
len
);
static
void
strbuf_extend_length
(
strbuf_t
*
s
,
size_
t
len
);
/* Update */
/* Update */
extern
void
strbuf_append_fmt
(
strbuf_t
*
s
,
int
len
,
const
char
*
fmt
,
...);
static
void
strbuf_append_mem
(
strbuf_t
*
s
,
const
char
*
c
,
size_t
len
);
extern
void
strbuf_append_fmt_retry
(
strbuf_t
*
s
,
const
char
*
format
,
...);
static
void
strbuf_append_mem
(
strbuf_t
*
s
,
const
char
*
c
,
int
len
);
extern
void
strbuf_append_string
(
strbuf_t
*
s
,
const
char
*
str
);
extern
void
strbuf_append_string
(
strbuf_t
*
s
,
const
char
*
str
);
static
void
strbuf_append_char
(
strbuf_t
*
s
,
const
char
c
);
static
void
strbuf_append_char
(
strbuf_t
*
s
,
const
char
c
);
static
void
strbuf_ensure_null
(
strbuf_t
*
s
);
static
void
strbuf_ensure_null
(
strbuf_t
*
s
);
...
@@ -87,12 +79,12 @@ static inline int strbuf_allocated(strbuf_t *s)
...
@@ -87,12 +79,12 @@ static inline int strbuf_allocated(strbuf_t *s)
/* Return bytes remaining in the string buffer
/* Return bytes remaining in the string buffer
* Ensure there is space for a NULL terminator. */
* Ensure there is space for a NULL terminator. */
static
inline
in
t
strbuf_empty_length
(
strbuf_t
*
s
)
static
inline
size_
t
strbuf_empty_length
(
strbuf_t
*
s
)
{
{
return
s
->
size
-
s
->
length
-
1
;
return
s
->
size
-
s
->
length
-
1
;
}
}
static
inline
void
strbuf_ensure_empty_length
(
strbuf_t
*
s
,
in
t
len
)
static
inline
void
strbuf_ensure_empty_length
(
strbuf_t
*
s
,
size_
t
len
)
{
{
if
(
len
>
strbuf_empty_length
(
s
))
if
(
len
>
strbuf_empty_length
(
s
))
strbuf_resize
(
s
,
s
->
length
+
len
);
strbuf_resize
(
s
,
s
->
length
+
len
);
...
@@ -103,12 +95,12 @@ static inline char *strbuf_empty_ptr(strbuf_t *s)
...
@@ -103,12 +95,12 @@ static inline char *strbuf_empty_ptr(strbuf_t *s)
return
s
->
buf
+
s
->
length
;
return
s
->
buf
+
s
->
length
;
}
}
static
inline
void
strbuf_extend_length
(
strbuf_t
*
s
,
in
t
len
)
static
inline
void
strbuf_extend_length
(
strbuf_t
*
s
,
size_
t
len
)
{
{
s
->
length
+=
len
;
s
->
length
+=
len
;
}
}
static
inline
in
t
strbuf_length
(
strbuf_t
*
s
)
static
inline
size_
t
strbuf_length
(
strbuf_t
*
s
)
{
{
return
s
->
length
;
return
s
->
length
;
}
}
...
@@ -124,14 +116,14 @@ static inline void strbuf_append_char_unsafe(strbuf_t *s, const char c)
...
@@ -124,14 +116,14 @@ static inline void strbuf_append_char_unsafe(strbuf_t *s, const char c)
s
->
buf
[
s
->
length
++
]
=
c
;
s
->
buf
[
s
->
length
++
]
=
c
;
}
}
static
inline
void
strbuf_append_mem
(
strbuf_t
*
s
,
const
char
*
c
,
in
t
len
)
static
inline
void
strbuf_append_mem
(
strbuf_t
*
s
,
const
char
*
c
,
size_
t
len
)
{
{
strbuf_ensure_empty_length
(
s
,
len
);
strbuf_ensure_empty_length
(
s
,
len
);
memcpy
(
s
->
buf
+
s
->
length
,
c
,
len
);
memcpy
(
s
->
buf
+
s
->
length
,
c
,
len
);
s
->
length
+=
len
;
s
->
length
+=
len
;
}
}
static
inline
void
strbuf_append_mem_unsafe
(
strbuf_t
*
s
,
const
char
*
c
,
in
t
len
)
static
inline
void
strbuf_append_mem_unsafe
(
strbuf_t
*
s
,
const
char
*
c
,
size_
t
len
)
{
{
memcpy
(
s
->
buf
+
s
->
length
,
c
,
len
);
memcpy
(
s
->
buf
+
s
->
length
,
c
,
len
);
s
->
length
+=
len
;
s
->
length
+=
len
;
...
@@ -142,7 +134,7 @@ static inline void strbuf_ensure_null(strbuf_t *s)
...
@@ -142,7 +134,7 @@ static inline void strbuf_ensure_null(strbuf_t *s)
s
->
buf
[
s
->
length
]
=
0
;
s
->
buf
[
s
->
length
]
=
0
;
}
}
static
inline
char
*
strbuf_string
(
strbuf_t
*
s
,
in
t
*
len
)
static
inline
char
*
strbuf_string
(
strbuf_t
*
s
,
size_
t
*
len
)
{
{
if
(
len
)
if
(
len
)
*
len
=
s
->
length
;
*
len
=
s
->
length
;
...
...
redis.conf
View file @
1a589818
...
@@ -346,6 +346,7 @@ pidfile /var/run/redis_6379.pid
...
@@ -346,6 +346,7 @@ pidfile /var/run/redis_6379.pid
# verbose (many rarely useful info, but not a mess like the debug level)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)
# notice (moderately verbose, what you want in production probably)
# warning (only very important / critical messages are logged)
# warning (only very important / critical messages are logged)
# nothing (nothing is logged)
loglevel
notice
loglevel
notice
# Specify the log file name. Also the empty string can be used to force
# Specify the log file name. Also the empty string can be used to force
...
@@ -1742,6 +1743,11 @@ aof-timestamp-enabled no
...
@@ -1742,6 +1743,11 @@ aof-timestamp-enabled no
#
#
# cluster-announce-hostname ""
# cluster-announce-hostname ""
# Clusters can configure an optional nodename to be used in addition to the node ID for
# debugging and admin information. This name is broadcasted between nodes, so will be used
# in addition to the node ID when reporting cross node events such as node failures.
# cluster-announce-human-nodename ""
# Clusters can advertise how clients should connect to them using either their IP address,
# Clusters can advertise how clients should connect to them using either their IP address,
# a user defined hostname, or by declaring they have no endpoint. Which endpoint is
# a user defined hostname, or by declaring they have no endpoint. Which endpoint is
# shown as the preferred endpoint is set by using the cluster-preferred-endpoint-type
# shown as the preferred endpoint is set by using the cluster-preferred-endpoint-type
...
...
sentinel.conf
View file @
1a589818
...
@@ -25,6 +25,7 @@ pidfile /var/run/redis-sentinel.pid
...
@@ -25,6 +25,7 @@ pidfile /var/run/redis-sentinel.pid
# verbose (many rarely useful info, but not a mess like the debug level)
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)
# notice (moderately verbose, what you want in production probably)
# warning (only very important / critical messages are logged)
# warning (only very important / critical messages are logged)
# nothing (nothing is logged)
loglevel
notice
loglevel
notice
# Specify the log file name. Also the empty string can be used to force
# Specify the log file name. Also the empty string can be used to force
...
...
src/Makefile
View file @
1a589818
...
@@ -23,7 +23,7 @@ ifeq ($(OPTIMIZATION),-O3)
...
@@ -23,7 +23,7 @@ ifeq ($(OPTIMIZATION),-O3)
else
else
REDIS_CFLAGS
+=
-flto
=
auto
REDIS_CFLAGS
+=
-flto
=
auto
endif
endif
REDIS_LDFLAGS
+=
-flto
REDIS_LDFLAGS
+=
-O3
-flto
endif
endif
DEPENDENCY_TARGETS
=
hiredis linenoise lua hdr_histogram fpconv
DEPENDENCY_TARGETS
=
hiredis linenoise lua hdr_histogram fpconv
NODEPS
:=
clean distclean
NODEPS
:=
clean distclean
...
@@ -47,10 +47,10 @@ OPT=$(OPTIMIZATION)
...
@@ -47,10 +47,10 @@ OPT=$(OPTIMIZATION)
# NUMBER_SIGN_CHAR is a workaround to support both GNU Make 4.3 and older versions.
# NUMBER_SIGN_CHAR is a workaround to support both GNU Make 4.3 and older versions.
NUMBER_SIGN_CHAR
:=
\#
NUMBER_SIGN_CHAR
:=
\#
C11_ATOMIC
:=
$(
shell
sh
-c
'echo "
$(NUMBER_SIGN_CHAR)
include <stdatomic.h>" > foo.c; \
C11_ATOMIC
:=
$(
shell
sh
-c
'echo "
$(NUMBER_SIGN_CHAR)
include <stdatomic.h>" > foo.c; \
$(CC)
-std=
c
11 -c foo.c -o foo.o > /dev/null 2>&1; \
$(CC)
-std=
gnu
11 -c foo.c -o foo.o > /dev/null 2>&1; \
if [ -f foo.o ]; then echo "yes"; rm foo.o; fi; rm foo.c'
)
if [ -f foo.o ]; then echo "yes"; rm foo.o; fi; rm foo.c'
)
ifeq
($(C11_ATOMIC),yes)
ifeq
($(C11_ATOMIC),yes)
STD
+=
-std
=
c
11
STD
+=
-std
=
gnu
11
else
else
STD
+=
-std
=
c99
STD
+=
-std
=
c99
endif
endif
...
...
src/acl.c
View file @
1a589818
...
@@ -1923,26 +1923,28 @@ void ACLKillPubsubClientsIfNeeded(user *new, user *original) {
...
@@ -1923,26 +1923,28 @@ void ACLKillPubsubClientsIfNeeded(user *new, user *original) {
if
(
c
->
user
==
original
&&
getClientType
(
c
)
==
CLIENT_TYPE_PUBSUB
)
{
if
(
c
->
user
==
original
&&
getClientType
(
c
)
==
CLIENT_TYPE_PUBSUB
)
{
/* Check for pattern violations. */
/* Check for pattern violations. */
listRewind
(
c
->
pubsub_patterns
,
&
lpi
);
dictIterator
*
di
=
dictGetIterator
(
c
->
pubsub_patterns
);
while
(
!
kill
&&
((
lpn
=
listNext
(
&
lpi
))
!=
NULL
))
{
dictEntry
*
de
;
while
(
!
kill
&&
((
de
=
dictNext
(
di
))
!=
NULL
))
{
o
=
lpn
->
value
;
o
=
dictGetKey
(
de
)
;
int
res
=
ACLCheckChannelAgainstList
(
upcoming
,
o
->
ptr
,
sdslen
(
o
->
ptr
),
1
);
int
res
=
ACLCheckChannelAgainstList
(
upcoming
,
o
->
ptr
,
sdslen
(
o
->
ptr
),
1
);
kill
=
(
res
==
ACL_DENIED_CHANNEL
);
kill
=
(
res
==
ACL_DENIED_CHANNEL
);
}
}
dictReleaseIterator
(
di
);
/* Check for channel violations. */
/* Check for channel violations. */
if
(
!
kill
)
{
if
(
!
kill
)
{
/* Check for global channels violation. */
/* Check for global channels violation. */
dictIterator
*
di
=
dictGetIterator
(
c
->
pubsub_channels
);
di
=
dictGetIterator
(
c
->
pubsub_channels
);
dictEntry
*
de
;
while
(
!
kill
&&
((
de
=
dictNext
(
di
))
!=
NULL
))
{
while
(
!
kill
&&
((
de
=
dictNext
(
di
))
!=
NULL
))
{
o
=
dictGetKey
(
de
);
o
=
dictGetKey
(
de
);
int
res
=
ACLCheckChannelAgainstList
(
upcoming
,
o
->
ptr
,
sdslen
(
o
->
ptr
),
0
);
int
res
=
ACLCheckChannelAgainstList
(
upcoming
,
o
->
ptr
,
sdslen
(
o
->
ptr
),
0
);
kill
=
(
res
==
ACL_DENIED_CHANNEL
);
kill
=
(
res
==
ACL_DENIED_CHANNEL
);
}
}
dictReleaseIterator
(
di
);
dictReleaseIterator
(
di
);
}
if
(
!
kill
)
{
/* Check for shard channels violation. */
/* Check for shard channels violation. */
di
=
dictGetIterator
(
c
->
pubsubshard_channels
);
di
=
dictGetIterator
(
c
->
pubsubshard_channels
);
while
(
!
kill
&&
((
de
=
dictNext
(
di
))
!=
NULL
))
{
while
(
!
kill
&&
((
de
=
dictNext
(
di
))
!=
NULL
))
{
...
@@ -1950,7 +1952,6 @@ void ACLKillPubsubClientsIfNeeded(user *new, user *original) {
...
@@ -1950,7 +1952,6 @@ void ACLKillPubsubClientsIfNeeded(user *new, user *original) {
int
res
=
ACLCheckChannelAgainstList
(
upcoming
,
o
->
ptr
,
sdslen
(
o
->
ptr
),
0
);
int
res
=
ACLCheckChannelAgainstList
(
upcoming
,
o
->
ptr
,
sdslen
(
o
->
ptr
),
0
);
kill
=
(
res
==
ACL_DENIED_CHANNEL
);
kill
=
(
res
==
ACL_DENIED_CHANNEL
);
}
}
dictReleaseIterator
(
di
);
dictReleaseIterator
(
di
);
}
}
...
@@ -2112,10 +2113,6 @@ int ACLAppendUserForLoading(sds *argv, int argc, int *argc_err) {
...
@@ -2112,10 +2113,6 @@ int ACLAppendUserForLoading(sds *argv, int argc, int *argc_err) {
return
C_ERR
;
return
C_ERR
;
}
}
/* Try to apply the user rules in a fake user to see if they
* are actually valid. */
user
*
fakeuser
=
ACLCreateUnlinkedUser
();
/* Merged selectors before trying to process */
/* Merged selectors before trying to process */
int
merged_argc
;
int
merged_argc
;
sds
*
acl_args
=
ACLMergeSelectorArguments
(
argv
+
2
,
argc
-
2
,
&
merged_argc
,
argc_err
);
sds
*
acl_args
=
ACLMergeSelectorArguments
(
argv
+
2
,
argc
-
2
,
&
merged_argc
,
argc_err
);
...
@@ -2124,6 +2121,10 @@ int ACLAppendUserForLoading(sds *argv, int argc, int *argc_err) {
...
@@ -2124,6 +2121,10 @@ int ACLAppendUserForLoading(sds *argv, int argc, int *argc_err) {
return
C_ERR
;
return
C_ERR
;
}
}
/* Try to apply the user rules in a fake user to see if they
* are actually valid. */
user
*
fakeuser
=
ACLCreateUnlinkedUser
();
for
(
int
j
=
0
;
j
<
merged_argc
;
j
++
)
{
for
(
int
j
=
0
;
j
<
merged_argc
;
j
++
)
{
if
(
ACLSetUser
(
fakeuser
,
acl_args
[
j
],
sdslen
(
acl_args
[
j
]))
==
C_ERR
)
{
if
(
ACLSetUser
(
fakeuser
,
acl_args
[
j
],
sdslen
(
acl_args
[
j
]))
==
C_ERR
)
{
if
(
errno
!=
ENOENT
)
{
if
(
errno
!=
ENOENT
)
{
...
...
src/bio.c
View file @
1a589818
...
@@ -142,7 +142,7 @@ void bioInit(void) {
...
@@ -142,7 +142,7 @@ void bioInit(void) {
for
(
j
=
0
;
j
<
BIO_WORKER_NUM
;
j
++
)
{
for
(
j
=
0
;
j
<
BIO_WORKER_NUM
;
j
++
)
{
void
*
arg
=
(
void
*
)(
unsigned
long
)
j
;
void
*
arg
=
(
void
*
)(
unsigned
long
)
j
;
if
(
pthread_create
(
&
thread
,
&
attr
,
bioProcessBackgroundJobs
,
arg
)
!=
0
)
{
if
(
pthread_create
(
&
thread
,
&
attr
,
bioProcessBackgroundJobs
,
arg
)
!=
0
)
{
serverLog
(
LL_WARNING
,
"Fatal: Can't initialize Background Jobs.
"
);
serverLog
(
LL_WARNING
,
"Fatal: Can't initialize Background Jobs.
Error message: %s"
,
strerror
(
errno
)
);
exit
(
1
);
exit
(
1
);
}
}
bio_threads
[
j
]
=
thread
;
bio_threads
[
j
]
=
thread
;
...
...
src/blocked.c
View file @
1a589818
...
@@ -325,6 +325,9 @@ void handleClientsBlockedOnKeys(void) {
...
@@ -325,6 +325,9 @@ void handleClientsBlockedOnKeys(void) {
* (i.e. not from call(), module context, etc.) */
* (i.e. not from call(), module context, etc.) */
serverAssert
(
server
.
also_propagate
.
numops
==
0
);
serverAssert
(
server
.
also_propagate
.
numops
==
0
);
/* If a command being unblocked causes another command to get unblocked,
* like a BLMOVE would do, then the new unblocked command will get processed
* right away rather than wait for later. */
while
(
listLength
(
server
.
ready_keys
)
!=
0
)
{
while
(
listLength
(
server
.
ready_keys
)
!=
0
)
{
list
*
l
;
list
*
l
;
...
@@ -564,7 +567,10 @@ static void handleClientsBlockedOnKey(readyList *rl) {
...
@@ -564,7 +567,10 @@ static void handleClientsBlockedOnKey(readyList *rl) {
listIter
li
;
listIter
li
;
listRewind
(
clients
,
&
li
);
listRewind
(
clients
,
&
li
);
while
((
ln
=
listNext
(
&
li
)))
{
/* Avoid processing more than the initial count so that we're not stuck
* in an endless loop in case the reprocessing of the command blocks again. */
long
count
=
listLength
(
clients
);
while
((
ln
=
listNext
(
&
li
))
&&
count
--
)
{
client
*
receiver
=
listNodeValue
(
ln
);
client
*
receiver
=
listNodeValue
(
ln
);
robj
*
o
=
lookupKeyReadWithFlags
(
rl
->
db
,
rl
->
key
,
LOOKUP_NOEFFECTS
);
robj
*
o
=
lookupKeyReadWithFlags
(
rl
->
db
,
rl
->
key
,
LOOKUP_NOEFFECTS
);
/* 1. In case new key was added/touched we need to verify it satisfy the
/* 1. In case new key was added/touched we need to verify it satisfy the
...
@@ -728,3 +734,30 @@ void totalNumberOfBlockingKeys(unsigned long *blocking_keys, unsigned long *blok
...
@@ -728,3 +734,30 @@ void totalNumberOfBlockingKeys(unsigned long *blocking_keys, unsigned long *blok
if
(
bloking_keys_on_nokey
)
if
(
bloking_keys_on_nokey
)
*
bloking_keys_on_nokey
=
bkeys_on_nokey
;
*
bloking_keys_on_nokey
=
bkeys_on_nokey
;
}
}
void
blockedBeforeSleep
(
void
)
{
/* Handle precise timeouts of blocked clients. */
handleBlockedClientsTimeout
();
/* Unblock all the clients blocked for synchronous replication
* in WAIT or WAITAOF. */
if
(
listLength
(
server
.
clients_waiting_acks
))
processClientsWaitingReplicas
();
/* Try to process blocked clients every once in while.
*
* Example: A module calls RM_SignalKeyAsReady from within a timer callback
* (So we don't visit processCommand() at all).
*
* This may unblock clients, so must be done before processUnblockedClients */
handleClientsBlockedOnKeys
();
/* Check if there are clients unblocked by modules that implement
* blocking commands. */
if
(
moduleCount
())
moduleHandleBlockedClients
();
/* Try to process pending commands for clients that were just unblocked. */
if
(
listLength
(
server
.
unblocked_clients
))
processUnblockedClients
();
}
src/cli_common.c
View file @
1a589818
...
@@ -352,9 +352,19 @@ void parseRedisUri(const char *uri, const char* tool_name, cliConnInfo *connInfo
...
@@ -352,9 +352,19 @@ void parseRedisUri(const char *uri, const char* tool_name, cliConnInfo *connInfo
path
=
strchr
(
curr
,
'/'
);
path
=
strchr
(
curr
,
'/'
);
if
(
*
curr
!=
'/'
)
{
if
(
*
curr
!=
'/'
)
{
host
=
path
?
path
-
1
:
end
;
host
=
path
?
path
-
1
:
end
;
if
((
port
=
strchr
(
curr
,
':'
)))
{
if
(
*
curr
==
'['
)
{
connInfo
->
hostport
=
atoi
(
port
+
1
);
curr
+=
1
;
host
=
port
-
1
;
if
((
port
=
strchr
(
curr
,
']'
)))
{
if
(
*
(
port
+
1
)
==
':'
)
{
connInfo
->
hostport
=
atoi
(
port
+
2
);
}
host
=
port
-
1
;
}
}
else
{
if
((
port
=
strchr
(
curr
,
':'
)))
{
connInfo
->
hostport
=
atoi
(
port
+
1
);
host
=
port
-
1
;
}
}
}
sdsfree
(
connInfo
->
hostip
);
sdsfree
(
connInfo
->
hostip
);
connInfo
->
hostip
=
sdsnewlen
(
curr
,
host
-
curr
+
1
);
connInfo
->
hostip
=
sdsnewlen
(
curr
,
host
-
curr
+
1
);
...
...
src/cluster.c
View file @
1a589818
This diff is collapsed.
Click to expand it.
src/cluster.h
View file @
1a589818
...
@@ -141,9 +141,9 @@ typedef struct clusterNode {
...
@@ -141,9 +141,9 @@ typedef struct clusterNode {
long
long
repl_offset
;
/* Last known repl offset for this node. */
long
long
repl_offset
;
/* Last known repl offset for this node. */
char
ip
[
NET_IP_STR_LEN
];
/* Latest known IP address of this node */
char
ip
[
NET_IP_STR_LEN
];
/* Latest known IP address of this node */
sds
hostname
;
/* The known hostname for this node */
sds
hostname
;
/* The known hostname for this node */
int
port
;
/*
Latest
known
clients port (TLS or plain).
*/
sds
human_nodename
;
/*
The
known
human readable nodename for this node
*/
int
p
port
;
/* Latest known clients
plaintext port. Only used
int
tcp_
port
;
/* Latest known clients
TCP port. */
if the main clients port is for TLS.
*/
int
tls_port
;
/* Latest known clients TLS port
*/
int
cport
;
/* Latest known cluster port of this node. */
int
cport
;
/* Latest known cluster port of this node. */
clusterLink
*
link
;
/* TCP/IP link established toward this node */
clusterLink
*
link
;
/* TCP/IP link established toward this node */
clusterLink
*
inbound_link
;
/* TCP/IP link accepted from this node */
clusterLink
*
inbound_link
;
/* TCP/IP link accepted from this node */
...
@@ -213,6 +213,13 @@ typedef struct clusterState {
...
@@ -213,6 +213,13 @@ typedef struct clusterState {
long
long
stats_pfail_nodes
;
/* Number of nodes in PFAIL status,
long
long
stats_pfail_nodes
;
/* Number of nodes in PFAIL status,
excluding nodes without address. */
excluding nodes without address. */
unsigned
long
long
stat_cluster_links_buffer_limit_exceeded
;
/* Total number of cluster links freed due to exceeding buffer limit */
unsigned
long
long
stat_cluster_links_buffer_limit_exceeded
;
/* Total number of cluster links freed due to exceeding buffer limit */
/* Bit map for slots that are no longer claimed by the owner in cluster PING
* messages. During slot migration, the owner will stop claiming the slot after
* the ownership transfer. Set the bit corresponding to the slot when a node
* stops claiming the slot. This prevents spreading incorrect information (that
* source still owns the slot) using UPDATE messages. */
unsigned
char
owner_not_claiming_slot
[
CLUSTER_SLOTS
/
8
];
}
clusterState
;
}
clusterState
;
/* Redis cluster messages header */
/* Redis cluster messages header */
...
@@ -225,10 +232,10 @@ typedef struct {
...
@@ -225,10 +232,10 @@ typedef struct {
uint32_t
ping_sent
;
uint32_t
ping_sent
;
uint32_t
pong_received
;
uint32_t
pong_received
;
char
ip
[
NET_IP_STR_LEN
];
/* IP address last time it was seen */
char
ip
[
NET_IP_STR_LEN
];
/* IP address last time it was seen */
uint16_t
port
;
/*
base
port last time it was seen */
uint16_t
port
;
/*
primary
port last time it was seen */
uint16_t
cport
;
/* cluster port last time it was seen */
uint16_t
cport
;
/* cluster port last time it was seen */
uint16_t
flags
;
/* node->flags copy */
uint16_t
flags
;
/* node->flags copy */
uint16_t
pport
;
/*
plaintext-port, when base port is TLS
*/
uint16_t
pport
;
/*
secondary port last time it was seen
*/
uint16_t
notused1
;
uint16_t
notused1
;
}
clusterMsgDataGossip
;
}
clusterMsgDataGossip
;
...
@@ -260,6 +267,7 @@ typedef struct {
...
@@ -260,6 +267,7 @@ typedef struct {
* consistent manner. */
* consistent manner. */
typedef
enum
{
typedef
enum
{
CLUSTERMSG_EXT_TYPE_HOSTNAME
,
CLUSTERMSG_EXT_TYPE_HOSTNAME
,
CLUSTERMSG_EXT_TYPE_HUMAN_NODENAME
,
CLUSTERMSG_EXT_TYPE_FORGOTTEN_NODE
,
CLUSTERMSG_EXT_TYPE_FORGOTTEN_NODE
,
CLUSTERMSG_EXT_TYPE_SHARDID
,
CLUSTERMSG_EXT_TYPE_SHARDID
,
}
clusterMsgPingtypes
;
}
clusterMsgPingtypes
;
...
@@ -271,6 +279,10 @@ typedef struct {
...
@@ -271,6 +279,10 @@ typedef struct {
char
hostname
[
1
];
/* The announced hostname, ends with \0. */
char
hostname
[
1
];
/* The announced hostname, ends with \0. */
}
clusterMsgPingExtHostname
;
}
clusterMsgPingExtHostname
;
typedef
struct
{
char
human_nodename
[
1
];
/* The announced nodename, ends with \0. */
}
clusterMsgPingExtHumanNodename
;
typedef
struct
{
typedef
struct
{
char
name
[
CLUSTER_NAMELEN
];
/* Node name. */
char
name
[
CLUSTER_NAMELEN
];
/* Node name. */
uint64_t
ttl
;
/* Remaining time to blacklist the node, in seconds. */
uint64_t
ttl
;
/* Remaining time to blacklist the node, in seconds. */
...
@@ -288,6 +300,7 @@ typedef struct {
...
@@ -288,6 +300,7 @@ typedef struct {
uint16_t
unused
;
/* 16 bits of padding to make this structure 8 byte aligned. */
uint16_t
unused
;
/* 16 bits of padding to make this structure 8 byte aligned. */
union
{
union
{
clusterMsgPingExtHostname
hostname
;
clusterMsgPingExtHostname
hostname
;
clusterMsgPingExtHumanNodename
human_nodename
;
clusterMsgPingExtForgottenNode
forgotten_node
;
clusterMsgPingExtForgottenNode
forgotten_node
;
clusterMsgPingExtShardId
shard_id
;
clusterMsgPingExtShardId
shard_id
;
}
ext
[];
/* Actual extension information, formatted so that the data is 8
}
ext
[];
/* Actual extension information, formatted so that the data is 8
...
@@ -331,7 +344,7 @@ typedef struct {
...
@@ -331,7 +344,7 @@ typedef struct {
char
sig
[
4
];
/* Signature "RCmb" (Redis Cluster message bus). */
char
sig
[
4
];
/* Signature "RCmb" (Redis Cluster message bus). */
uint32_t
totlen
;
/* Total length of this message */
uint32_t
totlen
;
/* Total length of this message */
uint16_t
ver
;
/* Protocol version, currently set to 1. */
uint16_t
ver
;
/* Protocol version, currently set to 1. */
uint16_t
port
;
/*
TCP base
port number. */
uint16_t
port
;
/*
Primary
port number
(TCP or TLS)
. */
uint16_t
type
;
/* Message type */
uint16_t
type
;
/* Message type */
uint16_t
count
;
/* Only used for some kind of messages. */
uint16_t
count
;
/* Only used for some kind of messages. */
uint64_t
currentEpoch
;
/* The epoch accordingly to the sending node. */
uint64_t
currentEpoch
;
/* The epoch accordingly to the sending node. */
...
@@ -346,7 +359,8 @@ typedef struct {
...
@@ -346,7 +359,8 @@ typedef struct {
char
myip
[
NET_IP_STR_LEN
];
/* Sender IP, if not all zeroed. */
char
myip
[
NET_IP_STR_LEN
];
/* Sender IP, if not all zeroed. */
uint16_t
extensions
;
/* Number of extensions sent along with this packet. */
uint16_t
extensions
;
/* Number of extensions sent along with this packet. */
char
notused1
[
30
];
/* 30 bytes reserved for future usage. */
char
notused1
[
30
];
/* 30 bytes reserved for future usage. */
uint16_t
pport
;
/* Sender TCP plaintext port, if base port is TLS */
uint16_t
pport
;
/* Secondary port number: if primary port is TCP port, this is
TLS port, and if primary port is TLS port, this is TCP port.*/
uint16_t
cport
;
/* Sender TCP cluster bus port */
uint16_t
cport
;
/* Sender TCP cluster bus port */
uint16_t
flags
;
/* Sender node flags */
uint16_t
flags
;
/* Sender node flags */
unsigned
char
state
;
/* Cluster state from the POV of the sender */
unsigned
char
state
;
/* Cluster state from the POV of the sender */
...
@@ -422,8 +436,11 @@ void slotToChannelAdd(sds channel);
...
@@ -422,8 +436,11 @@ void slotToChannelAdd(sds channel);
void
slotToChannelDel
(
sds
channel
);
void
slotToChannelDel
(
sds
channel
);
void
clusterUpdateMyselfHostname
(
void
);
void
clusterUpdateMyselfHostname
(
void
);
void
clusterUpdateMyselfAnnouncedPorts
(
void
);
void
clusterUpdateMyselfAnnouncedPorts
(
void
);
sds
clusterGenNodesDescription
(
int
filter
,
int
use_pport
);
sds
clusterGenNodesDescription
(
client
*
c
,
int
filter
,
int
tls_primary
);
sds
genClusterInfoString
(
void
);
sds
genClusterInfoString
(
void
);
void
freeClusterLink
(
clusterLink
*
link
);
void
freeClusterLink
(
clusterLink
*
link
);
void
clusterUpdateMyselfHumanNodename
(
void
);
int
isValidAuxString
(
char
*
s
,
unsigned
int
length
);
int
getNodeDefaultClientPort
(
clusterNode
*
n
);
#endif
/* __CLUSTER_H */
#endif
/* __CLUSTER_H */
src/commands.def
View file @
1a589818
This diff is collapsed.
Click to expand it.
Prev
1
2
3
4
5
6
7
8
Next
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment