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 @@
#define HI_SDS_MAX_PREALLOC (1024*1024)
#ifdef _MSC_VER
#define __attribute__(x)
typedef
long
long
ssize_t
;
#define SSIZE_MAX (LLONG_MAX >> 1)
#ifndef __clang__
#define __attribute__(x)
#endif
#endif
#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)
/* For Winsock connect(), the WSAEWOULDBLOCK error means the same thing as
* EINPROGRESS for POSIX connect(), so we do that translation to keep POSIX
* logic consistent. */
if
(
errno
==
EWOULDBLOCK
)
{
* logic consistent.
* 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
;
}
else
if
(
err
==
EIO
)
{
errno
=
EALREADY
;
}
return
ret
!=
SOCKET_ERROR
?
ret
:
-
1
;
}
...
...
@@ -205,6 +212,14 @@ int win32_getsockopt(SOCKET sockfd, int level, int optname, void *optval, sockle
}
else
{
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
);
return
ret
!=
SOCKET_ERROR
?
ret
:
-
1
;
}
...
...
@@ -245,4 +260,21 @@ int win32_poll(struct pollfd *fds, nfds_t nfds, int timeout) {
_updateErrno
(
ret
!=
SOCKET_ERROR
);
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 */
deps/hiredis/sockcompat.h
View file @
1a589818
...
...
@@ -50,6 +50,7 @@
#include <ws2tcpip.h>
#include <stddef.h>
#include <errno.h>
#include <mstcpip.h>
#ifdef _MSC_VER
typedef
long
long
ssize_t
;
...
...
@@ -71,6 +72,8 @@ ssize_t win32_send(SOCKET sockfd, const void *buf, size_t len, int flags);
typedef
ULONG
nfds_t
;
int
win32_poll
(
struct
pollfd
*
fds
,
nfds_t
nfds
,
int
timeout
);
int
win32_redisKeepAlive
(
SOCKET
sockfd
,
int
interval_ms
);
#ifndef REDIS_SOCKCOMPAT_IMPLEMENTATION
#define getaddrinfo(node, service, hints, res) win32_getaddrinfo(node, service, hints, res)
#undef gai_strerror
...
...
deps/hiredis/ssl.c
View file @
1a589818
...
...
@@ -32,6 +32,7 @@
#include "hiredis.h"
#include "async.h"
#include "net.h"
#include <assert.h>
#include <errno.h>
...
...
@@ -39,6 +40,14 @@
#ifdef _WIN32
#include <windows.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
#include <pthread.h>
#endif
...
...
@@ -219,6 +228,25 @@ redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *
const
char
*
cert_filename
,
const
char
*
private_key_filename
,
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
HCERTSTORE
win_store
=
NULL
;
PCCERT_CONTEXT
win_ctx
=
NULL
;
...
...
@@ -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_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
)
||
(
private_key_filename
!=
NULL
&&
cert_filename
==
NULL
))
{
...
...
@@ -273,6 +301,11 @@ redisSSLContext *redisCreateSSLContext(const char *cacert_filename, const char *
if
(
error
)
*
error
=
REDIS_SSL_CTX_CA_CERT_LOAD_FAILED
;
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
)
{
...
...
@@ -560,6 +593,7 @@ static void redisSSLAsyncWrite(redisAsyncContext *ac) {
}
redisContextFuncs
redisContextSSLFuncs
=
{
.
close
=
redisNetClose
,
.
free_privctx
=
redisSSLFree
,
.
async_read
=
redisSSLAsyncRead
,
.
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}
REDIS_PORT
=
${
REDIS_PORT
:-
56379
}
REDIS_SSL_PORT
=
${
REDIS_SSL_PORT
:-
56443
}
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
=
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
)
PID_FILE
=
${
tmpdir
}
/hiredis-test-redis.pid
...
...
@@ -43,20 +51,34 @@ if [ "$TEST_SSL" = "1" ]; then
fi
cleanup
()
{
set
+e
kill
$(
cat
${
PID_FILE
}
)
if
[
-n
"
${
REDIS_DOCKER
}
"
]
;
then
docker
kill
redis-test-server
else
set
+e
kill
$(
cat
${
PID_FILE
}
)
fi
rm
-rf
${
tmpdir
}
}
trap
cleanup INT TERM EXIT
# base config
cat
>
${
tmpdir
}
/redis.conf
<<
EOF
daemonize yes
pidfile
${
PID_FILE
}
port
${
REDIS_PORT
}
bind 127.0.0.1
unixsocket
${
SOCK_FILE
}
unixsocketperm 777
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
cat
>>
${
tmpdir
}
/redis.conf
<<
EOF
tls-port
${
REDIS_SSL_PORT
}
...
...
@@ -66,13 +88,25 @@ tls-key-file ${SSL_KEY}
EOF
fi
echo
${
tmpdir
}
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
echo
waiting
for
server
while
[
!
-S
"
${
SOCK_FILE
}
"
]
;
do
sleep
1
;
done
# 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
}
deps/lua/src/lua_cjson.c
View file @
1a589818
...
...
@@ -39,6 +39,7 @@
#include <assert.h>
#include <string.h>
#include <math.h>
#include <stdint.h>
#include <limits.h>
#include "lua.h"
#include "lauxlib.h"
...
...
@@ -141,13 +142,13 @@ typedef struct {
typedef
struct
{
json_token_type_t
type
;
in
t
index
;
size_
t
index
;
union
{
const
char
*
string
;
double
number
;
int
boolean
;
}
value
;
in
t
string_len
;
size_
t
string_len
;
}
json_token_t
;
static
const
char
*
char2escape
[
256
]
=
{
...
...
@@ -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
* If there are any excess pages, they won't be hit anyway.
* This gains ~5% speedup. */
if
(
len
>
SIZE_MAX
/
6
-
3
)
abort
();
/* Overflow check */
strbuf_ensure_empty_length
(
json
,
len
*
6
+
2
);
strbuf_append_char_unsafe
(
json
,
'\"'
);
...
...
@@ -706,7 +709,7 @@ static int json_encode(lua_State *l)
strbuf_t
local_encode_buf
;
strbuf_t
*
encode_buf
;
char
*
json
;
in
t
len
;
size_
t
len
;
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) {
void
mp_buf_append
(
lua_State
*
L
,
mp_buf
*
buf
,
const
unsigned
char
*
s
,
size_t
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
->
free
=
newsize
-
buf
->
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
)
{
unsigned
char
hdr
[
5
];
in
t
hdrlen
;
size_
t
hdrlen
;
if
(
len
<
32
)
{
hdr
[
0
]
=
0xa0
|
(
len
&
0xff
);
/* fix raw */
...
...
@@ -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
)
{
unsigned
char
b
[
9
];
in
t
enclen
;
size_
t
enclen
;
if
(
n
>=
0
)
{
if
(
n
<=
127
)
{
...
...
@@ -290,9 +292,9 @@ void mp_encode_int(lua_State *L, mp_buf *buf, int64_t n) {
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
];
in
t
enclen
;
size_
t
enclen
;
if
(
n
<=
15
)
{
b
[
0
]
=
0x90
|
(
n
&
0xf
);
/* fix array */
...
...
@@ -313,7 +315,7 @@ void mp_encode_array(lua_State *L, mp_buf *buf, int64_t n) {
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
];
int
enclen
;
...
...
@@ -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
;
const
char
*
s
;
mp_cur
c
;
...
...
@@ -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 */
return
luaL_error
(
L
,
"Invalid request to unpack with offset of %d and limit of %d."
,
offset
,
len
);
(
int
)
offset
,
(
int
)
len
);
else
if
(
offset
>
len
)
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
;
...
...
@@ -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.
* subtract the entire buffer size from the unprocessed size
* 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"
);
/* 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
* in place. Lua takes care of only returning
* elements above the args for us.
...
...
@@ -852,15 +855,15 @@ int mp_unpack(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 */
lua_pop
(
L
,
lua_gettop
(
L
)
-
1
);
return
mp_unpack_full
(
L
,
1
,
offset
);
}
int
mp_unpack_limit
(
lua_State
*
L
)
{
int
limit
=
luaL_checkinteger
(
L
,
2
);
int
offset
=
luaL_optinteger
(
L
,
3
,
0
);
lua_Integer
limit
=
luaL_checkinteger
(
L
,
2
);
lua_Integer
offset
=
luaL_optinteger
(
L
,
3
,
0
);
/* Variable pop because offset may not exist */
lua_pop
(
L
,
lua_gettop
(
L
)
-
1
);
...
...
deps/lua/src/strbuf.c
View file @
1a589818
...
...
@@ -26,6 +26,7 @@
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <stdint.h>
#include "strbuf.h"
...
...
@@ -38,22 +39,22 @@ static void die(const char *fmt, ...)
va_end
(
arg
);
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
;
else
size
=
len
+
1
;
/* \0 terminator */
size
=
len
+
1
;
if
(
size
<
len
)
die
(
"Overflow, len: %zu"
,
len
);
s
->
buf
=
NULL
;
s
->
size
=
size
;
s
->
length
=
0
;
s
->
increment
=
STRBUF_DEFAULT_INCREMENT
;
s
->
dynamic
=
0
;
s
->
reallocs
=
0
;
s
->
debug
=
0
;
...
...
@@ -65,7 +66,7 @@ void strbuf_init(strbuf_t *s, int len)
strbuf_ensure_null
(
s
);
}
strbuf_t
*
strbuf_new
(
in
t
len
)
strbuf_t
*
strbuf_new
(
size_
t
len
)
{
strbuf_t
*
s
;
...
...
@@ -81,20 +82,10 @@ strbuf_t *strbuf_new(int len)
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
)
{
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
);
}
}
...
...
@@ -113,7 +104,7 @@ void strbuf_free(strbuf_t *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
;
...
...
@@ -131,57 +122,62 @@ char *strbuf_free_to_string(strbuf_t *s, int *len)
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
)
die
(
"BUG: Invalid strbuf length requested"
);
/* Ensure there is room for optional NULL termination */
reqsize
=
len
+
1
;
if
(
reqsize
<
len
)
die
(
"Overflow, len: %zu"
,
len
);
/* If the user has requested to shrink the buffer, do it exactly */
if
(
s
->
size
>
reqsize
)
return
reqsize
;
newsize
=
s
->
size
;
if
(
s
->
increment
<
0
)
{
if
(
reqsize
>=
SIZE_MAX
/
2
)
{
newsize
=
reqsize
;
}
else
{
/* Exponential sizing */
while
(
newsize
<
reqsize
)
newsize
*=
-
s
->
increment
;
}
else
{
/* Linear sizing */
newsize
=
((
newsize
+
s
->
increment
-
1
)
/
s
->
increment
)
*
s
->
increment
;
newsize
*=
2
;
}
if
(
newsize
<
reqsize
)
die
(
"BUG: strbuf length would overflow, len: %zu"
,
len
);
return
newsize
;
}
/* Ensure strbuf can handle a string length bytes long (ignoring NULL
* 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
);
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
);
}
s
->
size
=
newsize
;
s
->
buf
=
realloc
(
s
->
buf
,
s
->
size
);
if
(
!
s
->
buf
)
die
(
"Out of memory
"
);
die
(
"Out of memory
, len: %zu"
,
len
);
s
->
reallocs
++
;
}
void
strbuf_append_string
(
strbuf_t
*
s
,
const
char
*
str
)
{
int
space
,
i
;
int
i
;
size_t
space
;
space
=
strbuf_empty_length
(
s
);
...
...
@@ -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:
*/
deps/lua/src/strbuf.h
View file @
1a589818
...
...
@@ -27,15 +27,13 @@
/* Size: Total bytes allocated to *buf
* Length: String length, excluding optional NULL terminator.
* Increment: Allocation increments when resizing the string buffer.
* Dynamic: True if created via strbuf_new()
*/
typedef
struct
{
char
*
buf
;
int
size
;
int
length
;
int
increment
;
size_t
size
;
size_t
length
;
int
dynamic
;
int
reallocs
;
int
debug
;
...
...
@@ -44,32 +42,26 @@ typedef struct {
#ifndef STRBUF_DEFAULT_SIZE
#define STRBUF_DEFAULT_SIZE 1023
#endif
#ifndef STRBUF_DEFAULT_INCREMENT
#define STRBUF_DEFAULT_INCREMENT -2
#endif
/* Initialise */
extern
strbuf_t
*
strbuf_new
(
int
len
);
extern
void
strbuf_init
(
strbuf_t
*
s
,
int
len
);
extern
void
strbuf_set_increment
(
strbuf_t
*
s
,
int
increment
);
extern
strbuf_t
*
strbuf_new
(
size_t
len
);
extern
void
strbuf_init
(
strbuf_t
*
s
,
size_t
len
);
/* Release */
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 */
extern
void
strbuf_resize
(
strbuf_t
*
s
,
in
t
len
);
static
in
t
strbuf_empty_length
(
strbuf_t
*
s
);
static
in
t
strbuf_length
(
strbuf_t
*
s
);
static
char
*
strbuf_string
(
strbuf_t
*
s
,
in
t
*
len
);
static
void
strbuf_ensure_empty_length
(
strbuf_t
*
s
,
in
t
len
);
extern
void
strbuf_resize
(
strbuf_t
*
s
,
size_
t
len
);
static
size_
t
strbuf_empty_length
(
strbuf_t
*
s
);
static
size_
t
strbuf_length
(
strbuf_t
*
s
);
static
char
*
strbuf_string
(
strbuf_t
*
s
,
size_
t
*
len
);
static
void
strbuf_ensure_empty_length
(
strbuf_t
*
s
,
size_
t
len
);
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 */
extern
void
strbuf_append_fmt
(
strbuf_t
*
s
,
int
len
,
const
char
*
fmt
,
...);
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
);
static
void
strbuf_append_mem
(
strbuf_t
*
s
,
const
char
*
c
,
size_t
len
);
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_ensure_null
(
strbuf_t
*
s
);
...
...
@@ -87,12 +79,12 @@ static inline int strbuf_allocated(strbuf_t *s)
/* Return bytes remaining in the string buffer
* 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
;
}
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
))
strbuf_resize
(
s
,
s
->
length
+
len
);
...
...
@@ -103,12 +95,12 @@ static inline char *strbuf_empty_ptr(strbuf_t *s)
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
;
}
static
inline
in
t
strbuf_length
(
strbuf_t
*
s
)
static
inline
size_
t
strbuf_length
(
strbuf_t
*
s
)
{
return
s
->
length
;
}
...
...
@@ -124,14 +116,14 @@ static inline void strbuf_append_char_unsafe(strbuf_t *s, const char 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
);
memcpy
(
s
->
buf
+
s
->
length
,
c
,
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
);
s
->
length
+=
len
;
...
...
@@ -142,7 +134,7 @@ static inline void strbuf_ensure_null(strbuf_t *s)
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
)
*
len
=
s
->
length
;
...
...
redis.conf
View file @
1a589818
...
...
@@ -346,6 +346,7 @@ pidfile /var/run/redis_6379.pid
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)
# warning (only very important / critical messages are logged)
# nothing (nothing is logged)
loglevel
notice
# Specify the log file name. Also the empty string can be used to force
...
...
@@ -1742,6 +1743,11 @@ aof-timestamp-enabled no
#
# 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,
# 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
...
...
sentinel.conf
View file @
1a589818
...
...
@@ -25,6 +25,7 @@ pidfile /var/run/redis-sentinel.pid
# verbose (many rarely useful info, but not a mess like the debug level)
# notice (moderately verbose, what you want in production probably)
# warning (only very important / critical messages are logged)
# nothing (nothing is logged)
loglevel
notice
# 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)
else
REDIS_CFLAGS
+=
-flto
=
auto
endif
REDIS_LDFLAGS
+=
-flto
REDIS_LDFLAGS
+=
-O3
-flto
endif
DEPENDENCY_TARGETS
=
hiredis linenoise lua hdr_histogram fpconv
NODEPS
:=
clean distclean
...
...
@@ -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
:=
\#
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'
)
ifeq
($(C11_ATOMIC),yes)
STD
+=
-std
=
c
11
STD
+=
-std
=
gnu
11
else
STD
+=
-std
=
c99
endif
...
...
src/acl.c
View file @
1a589818
...
...
@@ -1923,26 +1923,28 @@ void ACLKillPubsubClientsIfNeeded(user *new, user *original) {
if
(
c
->
user
==
original
&&
getClientType
(
c
)
==
CLIENT_TYPE_PUBSUB
)
{
/* Check for pattern violations. */
listRewind
(
c
->
pubsub_patterns
,
&
lpi
);
while
(
!
kill
&&
((
lpn
=
listNext
(
&
lpi
))
!=
NULL
))
{
o
=
lpn
->
value
;
dictIterator
*
di
=
dictGetIterator
(
c
->
pubsub_patterns
);
dictEntry
*
de
;
while
(
!
kill
&&
((
de
=
dictNext
(
di
))
!=
NULL
))
{
o
=
dictGetKey
(
de
)
;
int
res
=
ACLCheckChannelAgainstList
(
upcoming
,
o
->
ptr
,
sdslen
(
o
->
ptr
),
1
);
kill
=
(
res
==
ACL_DENIED_CHANNEL
);
}
dictReleaseIterator
(
di
);
/* Check for channel violations. */
if
(
!
kill
)
{
/* Check for global channels violation. */
dictIterator
*
di
=
dictGetIterator
(
c
->
pubsub_channels
);
dictEntry
*
de
;
di
=
dictGetIterator
(
c
->
pubsub_channels
);
while
(
!
kill
&&
((
de
=
dictNext
(
di
))
!=
NULL
))
{
o
=
dictGetKey
(
de
);
int
res
=
ACLCheckChannelAgainstList
(
upcoming
,
o
->
ptr
,
sdslen
(
o
->
ptr
),
0
);
kill
=
(
res
==
ACL_DENIED_CHANNEL
);
}
dictReleaseIterator
(
di
);
}
if
(
!
kill
)
{
/* Check for shard channels violation. */
di
=
dictGetIterator
(
c
->
pubsubshard_channels
);
while
(
!
kill
&&
((
de
=
dictNext
(
di
))
!=
NULL
))
{
...
...
@@ -1950,7 +1952,6 @@ void ACLKillPubsubClientsIfNeeded(user *new, user *original) {
int
res
=
ACLCheckChannelAgainstList
(
upcoming
,
o
->
ptr
,
sdslen
(
o
->
ptr
),
0
);
kill
=
(
res
==
ACL_DENIED_CHANNEL
);
}
dictReleaseIterator
(
di
);
}
...
...
@@ -2112,10 +2113,6 @@ int ACLAppendUserForLoading(sds *argv, int argc, int *argc_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 */
int
merged_argc
;
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) {
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
++
)
{
if
(
ACLSetUser
(
fakeuser
,
acl_args
[
j
],
sdslen
(
acl_args
[
j
]))
==
C_ERR
)
{
if
(
errno
!=
ENOENT
)
{
...
...
src/bio.c
View file @
1a589818
...
...
@@ -142,7 +142,7 @@ void bioInit(void) {
for
(
j
=
0
;
j
<
BIO_WORKER_NUM
;
j
++
)
{
void
*
arg
=
(
void
*
)(
unsigned
long
)
j
;
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
);
}
bio_threads
[
j
]
=
thread
;
...
...
src/blocked.c
View file @
1a589818
...
...
@@ -325,6 +325,9 @@ void handleClientsBlockedOnKeys(void) {
* (i.e. not from call(), module context, etc.) */
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
)
{
list
*
l
;
...
...
@@ -564,7 +567,10 @@ static void handleClientsBlockedOnKey(readyList *rl) {
listIter
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
);
robj
*
o
=
lookupKeyReadWithFlags
(
rl
->
db
,
rl
->
key
,
LOOKUP_NOEFFECTS
);
/* 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
if
(
bloking_keys_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
path
=
strchr
(
curr
,
'/'
);
if
(
*
curr
!=
'/'
)
{
host
=
path
?
path
-
1
:
end
;
if
((
port
=
strchr
(
curr
,
':'
)))
{
connInfo
->
hostport
=
atoi
(
port
+
1
);
host
=
port
-
1
;
if
(
*
curr
==
'['
)
{
curr
+=
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
);
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 {
long
long
repl_offset
;
/* Last known repl offset for this node. */
char
ip
[
NET_IP_STR_LEN
];
/* Latest known IP address of this node */
sds
hostname
;
/* The known hostname for this node */
int
port
;
/*
Latest
known
clients port (TLS or plain).
*/
int
p
port
;
/* Latest known clients
plaintext port. Only used
if the main clients port is for TLS.
*/
sds
human_nodename
;
/*
The
known
human readable nodename for this node
*/
int
tcp_
port
;
/* Latest known clients
TCP port. */
int
tls_port
;
/* Latest known clients TLS port
*/
int
cport
;
/* Latest known cluster port of this node. */
clusterLink
*
link
;
/* TCP/IP link established toward this node */
clusterLink
*
inbound_link
;
/* TCP/IP link accepted from this node */
...
...
@@ -213,6 +213,13 @@ typedef struct clusterState {
long
long
stats_pfail_nodes
;
/* Number of nodes in PFAIL status,
excluding nodes without address. */
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
;
/* Redis cluster messages header */
...
...
@@ -225,10 +232,10 @@ typedef struct {
uint32_t
ping_sent
;
uint32_t
pong_received
;
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
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
;
}
clusterMsgDataGossip
;
...
...
@@ -260,6 +267,7 @@ typedef struct {
* consistent manner. */
typedef
enum
{
CLUSTERMSG_EXT_TYPE_HOSTNAME
,
CLUSTERMSG_EXT_TYPE_HUMAN_NODENAME
,
CLUSTERMSG_EXT_TYPE_FORGOTTEN_NODE
,
CLUSTERMSG_EXT_TYPE_SHARDID
,
}
clusterMsgPingtypes
;
...
...
@@ -271,6 +279,10 @@ typedef struct {
char
hostname
[
1
];
/* The announced hostname, ends with \0. */
}
clusterMsgPingExtHostname
;
typedef
struct
{
char
human_nodename
[
1
];
/* The announced nodename, ends with \0. */
}
clusterMsgPingExtHumanNodename
;
typedef
struct
{
char
name
[
CLUSTER_NAMELEN
];
/* Node name. */
uint64_t
ttl
;
/* Remaining time to blacklist the node, in seconds. */
...
...
@@ -288,6 +300,7 @@ typedef struct {
uint16_t
unused
;
/* 16 bits of padding to make this structure 8 byte aligned. */
union
{
clusterMsgPingExtHostname
hostname
;
clusterMsgPingExtHumanNodename
human_nodename
;
clusterMsgPingExtForgottenNode
forgotten_node
;
clusterMsgPingExtShardId
shard_id
;
}
ext
[];
/* Actual extension information, formatted so that the data is 8
...
...
@@ -331,7 +344,7 @@ typedef struct {
char
sig
[
4
];
/* Signature "RCmb" (Redis Cluster message bus). */
uint32_t
totlen
;
/* Total length of this message */
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
count
;
/* Only used for some kind of messages. */
uint64_t
currentEpoch
;
/* The epoch accordingly to the sending node. */
...
...
@@ -346,7 +359,8 @@ typedef struct {
char
myip
[
NET_IP_STR_LEN
];
/* Sender IP, if not all zeroed. */
uint16_t
extensions
;
/* Number of extensions sent along with this packet. */
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
flags
;
/* Sender node flags */
unsigned
char
state
;
/* Cluster state from the POV of the sender */
...
...
@@ -422,8 +436,11 @@ void slotToChannelAdd(sds channel);
void
slotToChannelDel
(
sds
channel
);
void
clusterUpdateMyselfHostname
(
void
);
void
clusterUpdateMyselfAnnouncedPorts
(
void
);
sds
clusterGenNodesDescription
(
int
filter
,
int
use_pport
);
sds
clusterGenNodesDescription
(
client
*
c
,
int
filter
,
int
tls_primary
);
sds
genClusterInfoString
(
void
);
void
freeClusterLink
(
clusterLink
*
link
);
void
clusterUpdateMyselfHumanNodename
(
void
);
int
isValidAuxString
(
char
*
s
,
unsigned
int
length
);
int
getNodeDefaultClientPort
(
clusterNode
*
n
);
#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