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
efb6495a
Unverified
Commit
efb6495a
authored
Oct 16, 2019
by
Salvatore Sanfilippo
Committed by
GitHub
Oct 16, 2019
Browse files
Merge pull request #6236 from yossigo/poc/conns
Abstract Connections I/O API & TLS Support
parents
b8e02f2b
8e29b0b2
Changes
56
Show whitespace changes
Inline
Side-by-side
src/redis-cli.c
View file @
efb6495a
...
@@ -47,6 +47,10 @@
...
@@ -47,6 +47,10 @@
#include <math.h>
#include <math.h>
#include <hiredis.h>
#include <hiredis.h>
#ifdef USE_OPENSSL
#include <openssl/ssl.h>
#include <hiredis_ssl.h>
#endif
#include <sds.h>
/* use sds.h from hiredis, so that only one set of sds functions will be present in the binary */
#include <sds.h>
/* use sds.h from hiredis, so that only one set of sds functions will be present in the binary */
#include "dict.h"
#include "dict.h"
#include "adlist.h"
#include "adlist.h"
...
@@ -188,6 +192,12 @@ static struct config {
...
@@ -188,6 +192,12 @@ static struct config {
char
*
hostip
;
char
*
hostip
;
int
hostport
;
int
hostport
;
char
*
hostsocket
;
char
*
hostsocket
;
int
tls
;
char
*
sni
;
char
*
cacert
;
char
*
cacertdir
;
char
*
cert
;
char
*
key
;
long
repeat
;
long
repeat
;
long
interval
;
long
interval
;
int
dbnum
;
int
dbnum
;
...
@@ -758,6 +768,71 @@ static int cliSelect(void) {
...
@@ -758,6 +768,71 @@ static int cliSelect(void) {
return
REDIS_ERR
;
return
REDIS_ERR
;
}
}
/* Wrapper around redisSecureConnection to avoid hiredis_ssl dependencies if
* not building with TLS support.
*/
static
int
cliSecureConnection
(
redisContext
*
c
,
const
char
**
err
)
{
#ifdef USE_OPENSSL
static
SSL_CTX
*
ssl_ctx
=
NULL
;
if
(
!
ssl_ctx
)
{
ssl_ctx
=
SSL_CTX_new
(
SSLv23_client_method
());
if
(
!
ssl_ctx
)
{
*
err
=
"Failed to create SSL_CTX"
;
goto
error
;
}
SSL_CTX_set_options
(
ssl_ctx
,
SSL_OP_NO_SSLv2
|
SSL_OP_NO_SSLv3
);
SSL_CTX_set_verify
(
ssl_ctx
,
SSL_VERIFY_PEER
,
NULL
);
if
(
config
.
cacert
||
config
.
cacertdir
)
{
if
(
!
SSL_CTX_load_verify_locations
(
ssl_ctx
,
config
.
cacert
,
config
.
cacertdir
))
{
*
err
=
"Invalid CA Certificate File/Directory"
;
goto
error
;
}
}
else
{
if
(
!
SSL_CTX_set_default_verify_paths
(
ssl_ctx
))
{
*
err
=
"Failed to use default CA paths"
;
goto
error
;
}
}
if
(
config
.
cert
&&
!
SSL_CTX_use_certificate_chain_file
(
ssl_ctx
,
config
.
cert
))
{
*
err
=
"Invalid client certificate"
;
goto
error
;
}
if
(
config
.
key
&&
!
SSL_CTX_use_PrivateKey_file
(
ssl_ctx
,
config
.
key
,
SSL_FILETYPE_PEM
))
{
*
err
=
"Invalid private key"
;
goto
error
;
}
}
SSL
*
ssl
=
SSL_new
(
ssl_ctx
);
if
(
!
ssl
)
{
*
err
=
"Failed to create SSL object"
;
return
REDIS_ERR
;
}
if
(
config
.
sni
&&
!
SSL_set_tlsext_host_name
(
ssl
,
config
.
sni
))
{
*
err
=
"Failed to configure SNI"
;
SSL_free
(
ssl
);
return
REDIS_ERR
;
}
return
redisInitiateSSL
(
c
,
ssl
);
error:
SSL_CTX_free
(
ssl_ctx
);
ssl_ctx
=
NULL
;
return
REDIS_ERR
;
#else
(
void
)
c
;
(
void
)
err
;
return
REDIS_OK
;
#endif
}
/* Select RESP3 mode if redis-cli was started with the -3 option. */
/* Select RESP3 mode if redis-cli was started with the -3 option. */
static
int
cliSwitchProto
(
void
)
{
static
int
cliSwitchProto
(
void
)
{
redisReply
*
reply
;
redisReply
*
reply
;
...
@@ -789,6 +864,16 @@ static int cliConnect(int flags) {
...
@@ -789,6 +864,16 @@ static int cliConnect(int flags) {
context
=
redisConnectUnix
(
config
.
hostsocket
);
context
=
redisConnectUnix
(
config
.
hostsocket
);
}
}
if
(
!
context
->
err
&&
config
.
tls
)
{
const
char
*
err
=
NULL
;
if
(
cliSecureConnection
(
context
,
&
err
)
==
REDIS_ERR
&&
err
)
{
fprintf
(
stderr
,
"Could not negotiate a TLS connection: %s
\n
"
,
err
);
context
=
NULL
;
redisFree
(
context
);
return
REDIS_ERR
;
}
}
if
(
context
->
err
)
{
if
(
context
->
err
)
{
if
(
!
(
flags
&
CC_QUIET
))
{
if
(
!
(
flags
&
CC_QUIET
))
{
fprintf
(
stderr
,
"Could not connect to Redis at "
);
fprintf
(
stderr
,
"Could not connect to Redis at "
);
...
@@ -804,6 +889,7 @@ static int cliConnect(int flags) {
...
@@ -804,6 +889,7 @@ static int cliConnect(int flags) {
return
REDIS_ERR
;
return
REDIS_ERR
;
}
}
/* Set aggressive KEEP_ALIVE socket option in the Redis context socket
/* Set aggressive KEEP_ALIVE socket option in the Redis context socket
* in order to prevent timeouts caused by the execution of long
* in order to prevent timeouts caused by the execution of long
* commands. At the same time this improves the detection of real
* commands. At the same time this improves the detection of real
...
@@ -1305,6 +1391,13 @@ static redisReply *reconnectingRedisCommand(redisContext *c, const char *fmt, ..
...
@@ -1305,6 +1391,13 @@ static redisReply *reconnectingRedisCommand(redisContext *c, const char *fmt, ..
redisFree
(
c
);
redisFree
(
c
);
c
=
redisConnect
(
config
.
hostip
,
config
.
hostport
);
c
=
redisConnect
(
config
.
hostip
,
config
.
hostport
);
if
(
!
c
->
err
&&
config
.
tls
)
{
const
char
*
err
=
NULL
;
if
(
cliSecureConnection
(
c
,
&
err
)
==
REDIS_ERR
&&
err
)
{
fprintf
(
stderr
,
"TLS Error: %s
\n
"
,
err
);
exit
(
1
);
}
}
usleep
(
1000000
);
usleep
(
1000000
);
}
}
...
@@ -1498,6 +1591,20 @@ static int parseOptions(int argc, char **argv) {
...
@@ -1498,6 +1591,20 @@ static int parseOptions(int argc, char **argv) {
}
else
if
(
!
strcmp
(
argv
[
i
],
"--cluster-search-multiple-owners"
))
{
}
else
if
(
!
strcmp
(
argv
[
i
],
"--cluster-search-multiple-owners"
))
{
config
.
cluster_manager_command
.
flags
|=
config
.
cluster_manager_command
.
flags
|=
CLUSTER_MANAGER_CMD_FLAG_CHECK_OWNERS
;
CLUSTER_MANAGER_CMD_FLAG_CHECK_OWNERS
;
#ifdef USE_OPENSSL
}
else
if
(
!
strcmp
(
argv
[
i
],
"--tls"
))
{
config
.
tls
=
1
;
}
else
if
(
!
strcmp
(
argv
[
i
],
"--sni"
))
{
config
.
sni
=
argv
[
++
i
];
}
else
if
(
!
strcmp
(
argv
[
i
],
"--cacertdir"
))
{
config
.
cacertdir
=
argv
[
++
i
];
}
else
if
(
!
strcmp
(
argv
[
i
],
"--cacert"
))
{
config
.
cacert
=
argv
[
++
i
];
}
else
if
(
!
strcmp
(
argv
[
i
],
"--cert"
))
{
config
.
cert
=
argv
[
++
i
];
}
else
if
(
!
strcmp
(
argv
[
i
],
"--key"
))
{
config
.
key
=
argv
[
++
i
];
#endif
}
else
if
(
!
strcmp
(
argv
[
i
],
"-v"
)
||
!
strcmp
(
argv
[
i
],
"--version"
))
{
}
else
if
(
!
strcmp
(
argv
[
i
],
"-v"
)
||
!
strcmp
(
argv
[
i
],
"--version"
))
{
sds
version
=
cliVersion
();
sds
version
=
cliVersion
();
printf
(
"redis-cli %s
\n
"
,
version
);
printf
(
"redis-cli %s
\n
"
,
version
);
...
@@ -1591,6 +1698,15 @@ static void usage(void) {
...
@@ -1591,6 +1698,15 @@ static void usage(void) {
" -x Read last argument from STDIN.
\n
"
" -x Read last argument from STDIN.
\n
"
" -d <delimiter> Multi-bulk delimiter in for raw formatting (default:
\\
n).
\n
"
" -d <delimiter> Multi-bulk delimiter in for raw formatting (default:
\\
n).
\n
"
" -c Enable cluster mode (follow -ASK and -MOVED redirections).
\n
"
" -c Enable cluster mode (follow -ASK and -MOVED redirections).
\n
"
#ifdef USE_OPENSSL
" --tls Establish a secure TLS connection.
\n
"
" --cacert CA Certificate file to verify with.
\n
"
" --cacertdir Directory where trusted CA certificates are stored.
\n
"
" If neither cacert nor cacertdir are specified, the default
\n
"
" system-wide trusted root certs configuration will apply.
\n
"
" --cert Client certificate to authenticate with.
\n
"
" --key Private key file to authenticate with.
\n
"
#endif
" --raw Use raw formatting for replies (default when STDOUT is
\n
"
" --raw Use raw formatting for replies (default when STDOUT is
\n
"
" not a tty).
\n
"
" not a tty).
\n
"
" --no-raw Force formatted output even when STDOUT is not a tty.
\n
"
" --no-raw Force formatted output even when STDOUT is not a tty.
\n
"
...
@@ -1615,7 +1731,9 @@ static void usage(void) {
...
@@ -1615,7 +1731,9 @@ static void usage(void) {
" --pipe Transfer raw Redis protocol from stdin to server.
\n
"
" --pipe Transfer raw Redis protocol from stdin to server.
\n
"
" --pipe-timeout <n> In --pipe mode, abort with error if after sending all data.
\n
"
" --pipe-timeout <n> In --pipe mode, abort with error if after sending all data.
\n
"
" no reply is received within <n> seconds.
\n
"
" no reply is received within <n> seconds.
\n
"
" Default timeout: %d. Use 0 to wait forever.
\n
"
" Default timeout: %d. Use 0 to wait forever.
\n
"
,
REDIS_CLI_DEFAULT_PIPE_TIMEOUT
);
fprintf
(
stderr
,
" --bigkeys Sample Redis keys looking for keys with many elements (complexity).
\n
"
" --bigkeys Sample Redis keys looking for keys with many elements (complexity).
\n
"
" --memkeys Sample Redis keys looking for keys consuming a lot of memory.
\n
"
" --memkeys Sample Redis keys looking for keys consuming a lot of memory.
\n
"
" --memkeys-samples <n> Sample Redis keys looking for keys consuming a lot of memory.
\n
"
" --memkeys-samples <n> Sample Redis keys looking for keys consuming a lot of memory.
\n
"
...
@@ -1638,8 +1756,7 @@ static void usage(void) {
...
@@ -1638,8 +1756,7 @@ static void usage(void) {
" line interface.
\n
"
" line interface.
\n
"
" --help Output this help and exit.
\n
"
" --help Output this help and exit.
\n
"
" --version Output version and exit.
\n
"
" --version Output version and exit.
\n
"
"
\n
"
,
"
\n
"
);
REDIS_CLI_DEFAULT_PIPE_TIMEOUT
);
/* Using another fprintf call to avoid -Woverlength-strings compile warning */
/* Using another fprintf call to avoid -Woverlength-strings compile warning */
fprintf
(
stderr
,
fprintf
(
stderr
,
"Cluster Manager Commands:
\n
"
"Cluster Manager Commands:
\n
"
...
@@ -2407,6 +2524,15 @@ cleanup:
...
@@ -2407,6 +2524,15 @@ cleanup:
static
int
clusterManagerNodeConnect
(
clusterManagerNode
*
node
)
{
static
int
clusterManagerNodeConnect
(
clusterManagerNode
*
node
)
{
if
(
node
->
context
)
redisFree
(
node
->
context
);
if
(
node
->
context
)
redisFree
(
node
->
context
);
node
->
context
=
redisConnect
(
node
->
ip
,
node
->
port
);
node
->
context
=
redisConnect
(
node
->
ip
,
node
->
port
);
if
(
!
node
->
context
->
err
&&
config
.
tls
)
{
const
char
*
err
=
NULL
;
if
(
cliSecureConnection
(
node
->
context
,
&
err
)
==
REDIS_ERR
&&
err
)
{
fprintf
(
stderr
,
"TLS Error: %s
\n
"
,
err
);
redisFree
(
node
->
context
);
node
->
context
=
NULL
;
return
0
;
}
}
if
(
node
->
context
->
err
)
{
if
(
node
->
context
->
err
)
{
fprintf
(
stderr
,
"Could not connect to Redis at "
);
fprintf
(
stderr
,
"Could not connect to Redis at "
);
fprintf
(
stderr
,
"%s:%d: %s
\n
"
,
node
->
ip
,
node
->
port
,
fprintf
(
stderr
,
"%s:%d: %s
\n
"
,
node
->
ip
,
node
->
port
,
...
...
src/replication.c
View file @
efb6495a
...
@@ -39,7 +39,7 @@
...
@@ -39,7 +39,7 @@
#include <sys/stat.h>
#include <sys/stat.h>
void
replicationDiscardCachedMaster
(
void
);
void
replicationDiscardCachedMaster
(
void
);
void
replicationResurrectCachedMaster
(
int
newfd
);
void
replicationResurrectCachedMaster
(
connection
*
conn
);
void
replicationSendAck
(
void
);
void
replicationSendAck
(
void
);
void
putSlaveOnline
(
client
*
slave
);
void
putSlaveOnline
(
client
*
slave
);
int
cancelReplicationHandshake
(
void
);
int
cancelReplicationHandshake
(
void
);
...
@@ -57,7 +57,7 @@ char *replicationGetSlaveName(client *c) {
...
@@ -57,7 +57,7 @@ char *replicationGetSlaveName(client *c) {
ip
[
0
]
=
'\0'
;
ip
[
0
]
=
'\0'
;
buf
[
0
]
=
'\0'
;
buf
[
0
]
=
'\0'
;
if
(
c
->
slave_ip
[
0
]
!=
'\0'
||
if
(
c
->
slave_ip
[
0
]
!=
'\0'
||
anet
PeerToString
(
c
->
fd
,
ip
,
sizeof
(
ip
),
NULL
)
!=
-
1
)
conn
PeerToString
(
c
->
conn
,
ip
,
sizeof
(
ip
),
NULL
)
!=
-
1
)
{
{
/* Note that the 'ip' buffer is always larger than 'c->slave_ip' */
/* Note that the 'ip' buffer is always larger than 'c->slave_ip' */
if
(
c
->
slave_ip
[
0
]
!=
'\0'
)
memcpy
(
ip
,
c
->
slave_ip
,
sizeof
(
c
->
slave_ip
));
if
(
c
->
slave_ip
[
0
]
!=
'\0'
)
memcpy
(
ip
,
c
->
slave_ip
,
sizeof
(
c
->
slave_ip
));
...
@@ -432,7 +432,7 @@ int replicationSetupSlaveForFullResync(client *slave, long long offset) {
...
@@ -432,7 +432,7 @@ int replicationSetupSlaveForFullResync(client *slave, long long offset) {
if
(
!
(
slave
->
flags
&
CLIENT_PRE_PSYNC
))
{
if
(
!
(
slave
->
flags
&
CLIENT_PRE_PSYNC
))
{
buflen
=
snprintf
(
buf
,
sizeof
(
buf
),
"+FULLRESYNC %s %lld
\r\n
"
,
buflen
=
snprintf
(
buf
,
sizeof
(
buf
),
"+FULLRESYNC %s %lld
\r\n
"
,
server
.
replid
,
offset
);
server
.
replid
,
offset
);
if
(
w
rite
(
slave
->
fd
,
buf
,
buflen
)
!=
buflen
)
{
if
(
connW
rite
(
slave
->
conn
,
buf
,
buflen
)
!=
buflen
)
{
freeClientAsync
(
slave
);
freeClientAsync
(
slave
);
return
C_ERR
;
return
C_ERR
;
}
}
...
@@ -519,7 +519,7 @@ int masterTryPartialResynchronization(client *c) {
...
@@ -519,7 +519,7 @@ int masterTryPartialResynchronization(client *c) {
}
else
{
}
else
{
buflen
=
snprintf
(
buf
,
sizeof
(
buf
),
"+CONTINUE
\r\n
"
);
buflen
=
snprintf
(
buf
,
sizeof
(
buf
),
"+CONTINUE
\r\n
"
);
}
}
if
(
w
rite
(
c
->
fd
,
buf
,
buflen
)
!=
buflen
)
{
if
(
connW
rite
(
c
->
conn
,
buf
,
buflen
)
!=
buflen
)
{
freeClientAsync
(
c
);
freeClientAsync
(
c
);
return
C_OK
;
return
C_OK
;
}
}
...
@@ -685,7 +685,7 @@ void syncCommand(client *c) {
...
@@ -685,7 +685,7 @@ void syncCommand(client *c) {
* paths will change the state if we handle the slave differently. */
* paths will change the state if we handle the slave differently. */
c
->
replstate
=
SLAVE_STATE_WAIT_BGSAVE_START
;
c
->
replstate
=
SLAVE_STATE_WAIT_BGSAVE_START
;
if
(
server
.
repl_disable_tcp_nodelay
)
if
(
server
.
repl_disable_tcp_nodelay
)
anet
DisableTcpNoDelay
(
NULL
,
c
->
fd
);
/* Non critical if it fails. */
conn
DisableTcpNoDelay
(
c
->
conn
);
/* Non critical if it fails. */
c
->
repldbfd
=
-
1
;
c
->
repldbfd
=
-
1
;
c
->
flags
|=
CLIENT_SLAVE
;
c
->
flags
|=
CLIENT_SLAVE
;
listAddNodeTail
(
server
.
slaves
,
c
);
listAddNodeTail
(
server
.
slaves
,
c
);
...
@@ -862,8 +862,7 @@ void putSlaveOnline(client *slave) {
...
@@ -862,8 +862,7 @@ void putSlaveOnline(client *slave) {
slave
->
replstate
=
SLAVE_STATE_ONLINE
;
slave
->
replstate
=
SLAVE_STATE_ONLINE
;
slave
->
repl_put_online_on_ack
=
0
;
slave
->
repl_put_online_on_ack
=
0
;
slave
->
repl_ack_time
=
server
.
unixtime
;
/* Prevent false timeout. */
slave
->
repl_ack_time
=
server
.
unixtime
;
/* Prevent false timeout. */
if
(
aeCreateFileEvent
(
server
.
el
,
slave
->
fd
,
AE_WRITABLE
,
if
(
connSetWriteHandler
(
slave
->
conn
,
sendReplyToClient
)
==
C_ERR
)
{
sendReplyToClient
,
slave
)
==
AE_ERR
)
{
serverLog
(
LL_WARNING
,
"Unable to register writable event for replica bulk transfer: %s"
,
strerror
(
errno
));
serverLog
(
LL_WARNING
,
"Unable to register writable event for replica bulk transfer: %s"
,
strerror
(
errno
));
freeClient
(
slave
);
freeClient
(
slave
);
return
;
return
;
...
@@ -873,10 +872,8 @@ void putSlaveOnline(client *slave) {
...
@@ -873,10 +872,8 @@ void putSlaveOnline(client *slave) {
replicationGetSlaveName
(
slave
));
replicationGetSlaveName
(
slave
));
}
}
void
sendBulkToSlave
(
aeEventLoop
*
el
,
int
fd
,
void
*
privdata
,
int
mask
)
{
void
sendBulkToSlave
(
connection
*
conn
)
{
client
*
slave
=
privdata
;
client
*
slave
=
connGetPrivateData
(
conn
);
UNUSED
(
el
);
UNUSED
(
mask
);
char
buf
[
PROTO_IOBUF_LEN
];
char
buf
[
PROTO_IOBUF_LEN
];
ssize_t
nwritten
,
buflen
;
ssize_t
nwritten
,
buflen
;
...
@@ -884,10 +881,10 @@ void sendBulkToSlave(aeEventLoop *el, int fd, void *privdata, int mask) {
...
@@ -884,10 +881,10 @@ void sendBulkToSlave(aeEventLoop *el, int fd, void *privdata, int mask) {
* replication process. Currently the preamble is just the bulk count of
* replication process. Currently the preamble is just the bulk count of
* the file in the form "$<length>\r\n". */
* the file in the form "$<length>\r\n". */
if
(
slave
->
replpreamble
)
{
if
(
slave
->
replpreamble
)
{
nwritten
=
w
rite
(
fd
,
slave
->
replpreamble
,
sdslen
(
slave
->
replpreamble
));
nwritten
=
connW
rite
(
conn
,
slave
->
replpreamble
,
sdslen
(
slave
->
replpreamble
));
if
(
nwritten
==
-
1
)
{
if
(
nwritten
==
-
1
)
{
serverLog
(
LL_VERBOSE
,
"Write error sending RDB preamble to replica: %s"
,
serverLog
(
LL_VERBOSE
,
"Write error sending RDB preamble to replica: %s"
,
strerror
(
errno
));
connGetLastError
(
conn
));
freeClient
(
slave
);
freeClient
(
slave
);
return
;
return
;
}
}
...
@@ -911,10 +908,10 @@ void sendBulkToSlave(aeEventLoop *el, int fd, void *privdata, int mask) {
...
@@ -911,10 +908,10 @@ void sendBulkToSlave(aeEventLoop *el, int fd, void *privdata, int mask) {
freeClient
(
slave
);
freeClient
(
slave
);
return
;
return
;
}
}
if
((
nwritten
=
w
rite
(
fd
,
buf
,
buflen
))
==
-
1
)
{
if
((
nwritten
=
connW
rite
(
conn
,
buf
,
buflen
))
==
-
1
)
{
if
(
errno
!=
EAGAIN
)
{
if
(
connGetState
(
conn
)
!=
CONN_STATE_CONNECTED
)
{
serverLog
(
LL_WARNING
,
"Write error sending DB to replica: %s"
,
serverLog
(
LL_WARNING
,
"Write error sending DB to replica: %s"
,
strerror
(
errno
));
connGetLastError
(
conn
));
freeClient
(
slave
);
freeClient
(
slave
);
}
}
return
;
return
;
...
@@ -924,11 +921,157 @@ void sendBulkToSlave(aeEventLoop *el, int fd, void *privdata, int mask) {
...
@@ -924,11 +921,157 @@ void sendBulkToSlave(aeEventLoop *el, int fd, void *privdata, int mask) {
if
(
slave
->
repldboff
==
slave
->
repldbsize
)
{
if
(
slave
->
repldboff
==
slave
->
repldbsize
)
{
close
(
slave
->
repldbfd
);
close
(
slave
->
repldbfd
);
slave
->
repldbfd
=
-
1
;
slave
->
repldbfd
=
-
1
;
aeDeleteFileEvent
(
server
.
el
,
slave
->
fd
,
AE_WRITABLE
);
connSetWriteHandler
(
slave
->
conn
,
NULL
);
putSlaveOnline
(
slave
);
putSlaveOnline
(
slave
);
}
}
}
}
/* Remove one write handler from the list of connections waiting to be writable
* during rdb pipe transfer. */
void
rdbPipeWriteHandlerConnRemoved
(
struct
connection
*
conn
)
{
if
(
!
connHasWriteHandler
(
conn
))
return
;
connSetWriteHandler
(
conn
,
NULL
);
server
.
rdb_pipe_numconns_writing
--
;
/* if there are no more writes for now for this conn, or write error: */
if
(
server
.
rdb_pipe_numconns_writing
==
0
)
{
if
(
aeCreateFileEvent
(
server
.
el
,
server
.
rdb_pipe_read
,
AE_READABLE
,
rdbPipeReadHandler
,
NULL
)
==
AE_ERR
)
{
serverPanic
(
"Unrecoverable error creating server.rdb_pipe_read file event."
);
}
}
}
/* Called in diskless master during transfer of data from the rdb pipe, when
* the replica becomes writable again. */
void
rdbPipeWriteHandler
(
struct
connection
*
conn
)
{
serverAssert
(
server
.
rdb_pipe_bufflen
>
0
);
client
*
slave
=
connGetPrivateData
(
conn
);
int
nwritten
;
if
((
nwritten
=
connWrite
(
conn
,
server
.
rdb_pipe_buff
+
slave
->
repldboff
,
server
.
rdb_pipe_bufflen
-
slave
->
repldboff
))
==
-
1
)
{
if
(
connGetState
(
conn
)
==
CONN_STATE_CONNECTED
)
return
;
/* equivalent to EAGAIN */
serverLog
(
LL_WARNING
,
"Write error sending DB to replica: %s"
,
connGetLastError
(
conn
));
freeClient
(
slave
);
return
;
}
else
{
slave
->
repldboff
+=
nwritten
;
server
.
stat_net_output_bytes
+=
nwritten
;
if
(
slave
->
repldboff
<
server
.
rdb_pipe_bufflen
)
return
;
/* more data to write.. */
}
rdbPipeWriteHandlerConnRemoved
(
conn
);
}
/* When the the pipe serving diskless rdb transfer is drained (write end was
* closed), we can clean up all the temporary variables, and cleanup after the
* fork child. */
void
RdbPipeCleanup
()
{
close
(
server
.
rdb_pipe_read
);
zfree
(
server
.
rdb_pipe_conns
);
server
.
rdb_pipe_conns
=
NULL
;
server
.
rdb_pipe_numconns
=
0
;
server
.
rdb_pipe_numconns_writing
=
0
;
zfree
(
server
.
rdb_pipe_buff
);
server
.
rdb_pipe_buff
=
NULL
;
server
.
rdb_pipe_bufflen
=
0
;
/* Since we're avoiding to detect the child exited as long as the pipe is
* not drained, so now is the time to check. */
checkChildrenDone
();
}
/* Called in diskless master, when there's data to read from the child's rdb pipe */
void
rdbPipeReadHandler
(
struct
aeEventLoop
*
eventLoop
,
int
fd
,
void
*
clientData
,
int
mask
)
{
UNUSED
(
mask
);
UNUSED
(
clientData
);
UNUSED
(
eventLoop
);
int
i
;
if
(
!
server
.
rdb_pipe_buff
)
server
.
rdb_pipe_buff
=
zmalloc
(
PROTO_IOBUF_LEN
);
serverAssert
(
server
.
rdb_pipe_numconns_writing
==
0
);
while
(
1
)
{
server
.
rdb_pipe_bufflen
=
read
(
fd
,
server
.
rdb_pipe_buff
,
PROTO_IOBUF_LEN
);
if
(
server
.
rdb_pipe_bufflen
<
0
)
{
if
(
errno
==
EAGAIN
||
errno
==
EWOULDBLOCK
)
return
;
serverLog
(
LL_WARNING
,
"Diskless rdb transfer, read error sending DB to replicas: %s"
,
strerror
(
errno
));
for
(
i
=
0
;
i
<
server
.
rdb_pipe_numconns
;
i
++
)
{
connection
*
conn
=
server
.
rdb_pipe_conns
[
i
];
if
(
!
conn
)
continue
;
client
*
slave
=
connGetPrivateData
(
conn
);
freeClient
(
slave
);
server
.
rdb_pipe_conns
[
i
]
=
NULL
;
}
killRDBChild
();
return
;
}
if
(
server
.
rdb_pipe_bufflen
==
0
)
{
/* EOF - write end was closed. */
int
stillUp
=
0
;
aeDeleteFileEvent
(
server
.
el
,
server
.
rdb_pipe_read
,
AE_READABLE
);
for
(
i
=
0
;
i
<
server
.
rdb_pipe_numconns
;
i
++
)
{
connection
*
conn
=
server
.
rdb_pipe_conns
[
i
];
if
(
!
conn
)
continue
;
stillUp
++
;
}
serverLog
(
LL_WARNING
,
"Diskless rdb transfer, done reading from pipe, %d replicas still up."
,
stillUp
);
RdbPipeCleanup
();
return
;
}
int
stillAlive
=
0
;
for
(
i
=
0
;
i
<
server
.
rdb_pipe_numconns
;
i
++
)
{
int
nwritten
;
connection
*
conn
=
server
.
rdb_pipe_conns
[
i
];
if
(
!
conn
)
continue
;
client
*
slave
=
connGetPrivateData
(
conn
);
if
((
nwritten
=
connWrite
(
conn
,
server
.
rdb_pipe_buff
,
server
.
rdb_pipe_bufflen
))
==
-
1
)
{
if
(
connGetState
(
conn
)
!=
CONN_STATE_CONNECTED
)
{
serverLog
(
LL_WARNING
,
"Diskless rdb transfer, write error sending DB to replica: %s"
,
connGetLastError
(
conn
));
freeClient
(
slave
);
server
.
rdb_pipe_conns
[
i
]
=
NULL
;
continue
;
}
/* An error and still in connected state, is equivalent to EAGAIN */
slave
->
repldboff
=
0
;
}
else
{
slave
->
repldboff
=
nwritten
;
server
.
stat_net_output_bytes
+=
nwritten
;
}
/* If we were unable to write all the data to one of the replicas,
* setup write handler (and disable pipe read handler, below) */
if
(
nwritten
!=
server
.
rdb_pipe_bufflen
)
{
server
.
rdb_pipe_numconns_writing
++
;
connSetWriteHandler
(
conn
,
rdbPipeWriteHandler
);
}
stillAlive
++
;
}
if
(
stillAlive
==
0
)
{
serverLog
(
LL_WARNING
,
"Diskless rdb transfer, last replica dropped, killing fork child."
);
killRDBChild
();
RdbPipeCleanup
();
}
/* Remove the pipe read handler if at least one write handler was set. */
if
(
server
.
rdb_pipe_numconns_writing
||
stillAlive
==
0
)
{
aeDeleteFileEvent
(
server
.
el
,
server
.
rdb_pipe_read
,
AE_READABLE
);
break
;
}
}
}
/* This function is called at the end of every background saving,
/* This function is called at the end of every background saving,
* or when the replication RDB transfer strategy is modified from
* or when the replication RDB transfer strategy is modified from
* disk to socket or the other way around.
* disk to socket or the other way around.
...
@@ -1015,8 +1158,8 @@ void updateSlavesWaitingBgsave(int bgsaveerr, int type) {
...
@@ -1015,8 +1158,8 @@ void updateSlavesWaitingBgsave(int bgsaveerr, int type) {
slave
->
replpreamble
=
sdscatprintf
(
sdsempty
(),
"$%lld
\r\n
"
,
slave
->
replpreamble
=
sdscatprintf
(
sdsempty
(),
"$%lld
\r\n
"
,
(
unsigned
long
long
)
slave
->
repldbsize
);
(
unsigned
long
long
)
slave
->
repldbsize
);
aeDeleteFileEvent
(
server
.
el
,
slave
->
fd
,
AE_WRITABLE
);
connSetWriteHandler
(
slave
->
conn
,
NULL
);
if
(
aeCreateFileEvent
(
server
.
el
,
slave
->
fd
,
AE_WRITABLE
,
sendBulkToSlave
,
slave
)
==
AE
_ERR
)
{
if
(
connSetWriteHandler
(
slave
->
conn
,
sendBulkToSlave
)
==
C
_ERR
)
{
freeClient
(
slave
);
freeClient
(
slave
);
continue
;
continue
;
}
}
...
@@ -1084,9 +1227,8 @@ void replicationSendNewlineToMaster(void) {
...
@@ -1084,9 +1227,8 @@ void replicationSendNewlineToMaster(void) {
static
time_t
newline_sent
;
static
time_t
newline_sent
;
if
(
time
(
NULL
)
!=
newline_sent
)
{
if
(
time
(
NULL
)
!=
newline_sent
)
{
newline_sent
=
time
(
NULL
);
newline_sent
=
time
(
NULL
);
if
(
write
(
server
.
repl_transfer_s
,
"
\n
"
,
1
)
==
-
1
)
{
/* Pinging back in this stage is best-effort. */
/* Pinging back in this stage is best-effort. */
}
if
(
server
.
repl_transfer_s
)
connWrite
(
server
.
repl_transfer_s
,
"
\n
"
,
1
);
}
}
}
}
...
@@ -1100,8 +1242,10 @@ void replicationEmptyDbCallback(void *privdata) {
...
@@ -1100,8 +1242,10 @@ void replicationEmptyDbCallback(void *privdata) {
/* Once we have a link with the master and the synchroniziation was
/* Once we have a link with the master and the synchroniziation was
* performed, this function materializes the master client we store
* performed, this function materializes the master client we store
* at server.master, starting from the specified file descriptor. */
* at server.master, starting from the specified file descriptor. */
void
replicationCreateMasterClient
(
int
fd
,
int
dbid
)
{
void
replicationCreateMasterClient
(
connection
*
conn
,
int
dbid
)
{
server
.
master
=
createClient
(
fd
);
server
.
master
=
createClient
(
conn
);
if
(
conn
)
connSetReadHandler
(
server
.
master
->
conn
,
readQueryFromClient
);
server
.
master
->
flags
|=
CLIENT_MASTER
;
server
.
master
->
flags
|=
CLIENT_MASTER
;
server
.
master
->
authenticated
=
1
;
server
.
master
->
authenticated
=
1
;
server
.
master
->
reploff
=
server
.
master_initial_offset
;
server
.
master
->
reploff
=
server
.
master_initial_offset
;
...
@@ -1196,7 +1340,7 @@ void disklessLoadRestoreBackups(redisDb *backup, int restore, int empty_db_flags
...
@@ -1196,7 +1340,7 @@ void disklessLoadRestoreBackups(redisDb *backup, int restore, int empty_db_flags
/* Asynchronously read the SYNC payload we receive from a master */
/* Asynchronously read the SYNC payload we receive from a master */
#define REPL_MAX_WRITTEN_BEFORE_FSYNC (1024*1024*8)
/* 8 MB */
#define REPL_MAX_WRITTEN_BEFORE_FSYNC (1024*1024*8)
/* 8 MB */
void
readSyncBulkPayload
(
aeEventLoop
*
el
,
int
fd
,
void
*
privdata
,
int
mask
)
{
void
readSyncBulkPayload
(
connection
*
conn
)
{
char
buf
[
4096
];
char
buf
[
4096
];
ssize_t
nread
,
readlen
,
nwritten
;
ssize_t
nread
,
readlen
,
nwritten
;
int
use_diskless_load
;
int
use_diskless_load
;
...
@@ -1204,9 +1348,6 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
...
@@ -1204,9 +1348,6 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
int
empty_db_flags
=
server
.
repl_slave_lazy_flush
?
EMPTYDB_ASYNC
:
int
empty_db_flags
=
server
.
repl_slave_lazy_flush
?
EMPTYDB_ASYNC
:
EMPTYDB_NO_FLAGS
;
EMPTYDB_NO_FLAGS
;
off_t
left
;
off_t
left
;
UNUSED
(
el
);
UNUSED
(
privdata
);
UNUSED
(
mask
);
/* Static vars used to hold the EOF mark, and the last bytes received
/* Static vars used to hold the EOF mark, and the last bytes received
* form the server: when they match, we reached the end of the transfer. */
* form the server: when they match, we reached the end of the transfer. */
...
@@ -1217,7 +1358,7 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
...
@@ -1217,7 +1358,7 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
/* If repl_transfer_size == -1 we still have to read the bulk length
/* If repl_transfer_size == -1 we still have to read the bulk length
* from the master reply. */
* from the master reply. */
if
(
server
.
repl_transfer_size
==
-
1
)
{
if
(
server
.
repl_transfer_size
==
-
1
)
{
if
(
s
yncReadLine
(
fd
,
buf
,
1024
,
server
.
repl_syncio_timeout
*
1000
)
==
-
1
)
{
if
(
connS
yncReadLine
(
conn
,
buf
,
1024
,
server
.
repl_syncio_timeout
*
1000
)
==
-
1
)
{
serverLog
(
LL_WARNING
,
serverLog
(
LL_WARNING
,
"I/O error reading bulk count from MASTER: %s"
,
"I/O error reading bulk count from MASTER: %s"
,
strerror
(
errno
));
strerror
(
errno
));
...
@@ -1282,7 +1423,7 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
...
@@ -1282,7 +1423,7 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
readlen
=
(
left
<
(
signed
)
sizeof
(
buf
))
?
left
:
(
signed
)
sizeof
(
buf
);
readlen
=
(
left
<
(
signed
)
sizeof
(
buf
))
?
left
:
(
signed
)
sizeof
(
buf
);
}
}
nread
=
read
(
fd
,
buf
,
readlen
);
nread
=
connRead
(
conn
,
buf
,
readlen
);
if
(
nread
<=
0
)
{
if
(
nread
<=
0
)
{
serverLog
(
LL_WARNING
,
"I/O error trying to sync with MASTER: %s"
,
serverLog
(
LL_WARNING
,
"I/O error trying to sync with MASTER: %s"
,
(
nread
==
-
1
)
?
strerror
(
errno
)
:
"connection lost"
);
(
nread
==
-
1
)
?
strerror
(
errno
)
:
"connection lost"
);
...
@@ -1390,17 +1531,17 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
...
@@ -1390,17 +1531,17 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
* handler, otherwise it will get called recursively since
* handler, otherwise it will get called recursively since
* rdbLoad() will call the event loop to process events from time to
* rdbLoad() will call the event loop to process events from time to
* time for non blocking loading. */
* time for non blocking loading. */
aeDeleteFileEvent
(
server
.
el
,
server
.
repl_transfer_s
,
AE_READABLE
);
connSetReadHandler
(
conn
,
NULL
);
serverLog
(
LL_NOTICE
,
"MASTER <-> REPLICA sync: Loading DB in memory"
);
serverLog
(
LL_NOTICE
,
"MASTER <-> REPLICA sync: Loading DB in memory"
);
rdbSaveInfo
rsi
=
RDB_SAVE_INFO_INIT
;
rdbSaveInfo
rsi
=
RDB_SAVE_INFO_INIT
;
if
(
use_diskless_load
)
{
if
(
use_diskless_load
)
{
rio
rdb
;
rio
rdb
;
rioInitWith
Fd
(
&
rdb
,
fd
,
server
.
repl_transfer_size
);
rioInitWith
Conn
(
&
rdb
,
conn
,
server
.
repl_transfer_size
);
/* Put the socket in blocking mode to simplify RDB transfer.
/* Put the socket in blocking mode to simplify RDB transfer.
* We'll restore it when the RDB is received. */
* We'll restore it when the RDB is received. */
anet
Block
(
NULL
,
fd
);
conn
Block
(
conn
);
anet
RecvTimeout
(
NULL
,
fd
,
server
.
repl_timeout
*
1000
);
conn
RecvTimeout
(
conn
,
server
.
repl_timeout
*
1000
);
startLoading
(
server
.
repl_transfer_size
);
startLoading
(
server
.
repl_transfer_size
);
if
(
rdbLoadRio
(
&
rdb
,
&
rsi
,
0
)
!=
C_OK
)
{
if
(
rdbLoadRio
(
&
rdb
,
&
rsi
,
0
)
!=
C_OK
)
{
...
@@ -1410,7 +1551,7 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
...
@@ -1410,7 +1551,7 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
"Failed trying to load the MASTER synchronization DB "
"Failed trying to load the MASTER synchronization DB "
"from socket"
);
"from socket"
);
cancelReplicationHandshake
();
cancelReplicationHandshake
();
rioFree
Fd
(
&
rdb
,
NULL
);
rioFree
Conn
(
&
rdb
,
NULL
);
if
(
server
.
repl_diskless_load
==
REPL_DISKLESS_LOAD_SWAPDB
)
{
if
(
server
.
repl_diskless_load
==
REPL_DISKLESS_LOAD_SWAPDB
)
{
/* Restore the backed up databases. */
/* Restore the backed up databases. */
disklessLoadRestoreBackups
(
diskless_load_backup
,
1
,
disklessLoadRestoreBackups
(
diskless_load_backup
,
1
,
...
@@ -1443,16 +1584,16 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
...
@@ -1443,16 +1584,16 @@ void readSyncBulkPayload(aeEventLoop *el, int fd, void *privdata, int mask) {
{
{
serverLog
(
LL_WARNING
,
"Replication stream EOF marker is broken"
);
serverLog
(
LL_WARNING
,
"Replication stream EOF marker is broken"
);
cancelReplicationHandshake
();
cancelReplicationHandshake
();
rioFree
Fd
(
&
rdb
,
NULL
);
rioFree
Conn
(
&
rdb
,
NULL
);
return
;
return
;
}
}
}
}
/* Cleanup and restore the socket to the original state to continue
/* Cleanup and restore the socket to the original state to continue
* with the normal replication. */
* with the normal replication. */
rioFree
Fd
(
&
rdb
,
NULL
);
rioFree
Conn
(
&
rdb
,
NULL
);
anet
NonBlock
(
NULL
,
fd
);
conn
NonBlock
(
conn
);
anet
RecvTimeout
(
NULL
,
fd
,
0
);
conn
RecvTimeout
(
conn
,
0
);
}
else
{
}
else
{
/* Ensure background save doesn't overwrite synced data */
/* Ensure background save doesn't overwrite synced data */
if
(
server
.
rdb_child_pid
!=
-
1
)
{
if
(
server
.
rdb_child_pid
!=
-
1
)
{
...
@@ -1529,7 +1670,7 @@ error:
...
@@ -1529,7 +1670,7 @@ error:
#define SYNC_CMD_READ (1<<0)
#define SYNC_CMD_READ (1<<0)
#define SYNC_CMD_WRITE (1<<1)
#define SYNC_CMD_WRITE (1<<1)
#define SYNC_CMD_FULL (SYNC_CMD_READ|SYNC_CMD_WRITE)
#define SYNC_CMD_FULL (SYNC_CMD_READ|SYNC_CMD_WRITE)
char
*
sendSynchronousCommand
(
int
flags
,
int
fd
,
...)
{
char
*
sendSynchronousCommand
(
int
flags
,
connection
*
conn
,
...)
{
/* Create the command to send to the master, we use redis binary
/* Create the command to send to the master, we use redis binary
* protocol to make sure correct arguments are sent. This function
* protocol to make sure correct arguments are sent. This function
...
@@ -1540,7 +1681,7 @@ char *sendSynchronousCommand(int flags, int fd, ...) {
...
@@ -1540,7 +1681,7 @@ char *sendSynchronousCommand(int flags, int fd, ...) {
sds
cmd
=
sdsempty
();
sds
cmd
=
sdsempty
();
sds
cmdargs
=
sdsempty
();
sds
cmdargs
=
sdsempty
();
size_t
argslen
=
0
;
size_t
argslen
=
0
;
va_start
(
ap
,
fd
);
va_start
(
ap
,
conn
);
while
(
1
)
{
while
(
1
)
{
arg
=
va_arg
(
ap
,
char
*
);
arg
=
va_arg
(
ap
,
char
*
);
...
@@ -1557,12 +1698,12 @@ char *sendSynchronousCommand(int flags, int fd, ...) {
...
@@ -1557,12 +1698,12 @@ char *sendSynchronousCommand(int flags, int fd, ...) {
sdsfree
(
cmdargs
);
sdsfree
(
cmdargs
);
/* Transfer command to the server. */
/* Transfer command to the server. */
if
(
s
yncWrite
(
fd
,
cmd
,
sdslen
(
cmd
),
server
.
repl_syncio_timeout
*
1000
)
if
(
connS
yncWrite
(
conn
,
cmd
,
sdslen
(
cmd
),
server
.
repl_syncio_timeout
*
1000
)
==
-
1
)
==
-
1
)
{
{
sdsfree
(
cmd
);
sdsfree
(
cmd
);
return
sdscatprintf
(
sdsempty
(),
"-Writing to master: %s"
,
return
sdscatprintf
(
sdsempty
(),
"-Writing to master: %s"
,
strerror
(
errno
));
connGetLastError
(
conn
));
}
}
sdsfree
(
cmd
);
sdsfree
(
cmd
);
}
}
...
@@ -1571,7 +1712,7 @@ char *sendSynchronousCommand(int flags, int fd, ...) {
...
@@ -1571,7 +1712,7 @@ char *sendSynchronousCommand(int flags, int fd, ...) {
if
(
flags
&
SYNC_CMD_READ
)
{
if
(
flags
&
SYNC_CMD_READ
)
{
char
buf
[
256
];
char
buf
[
256
];
if
(
s
yncReadLine
(
fd
,
buf
,
sizeof
(
buf
),
server
.
repl_syncio_timeout
*
1000
)
if
(
connS
yncReadLine
(
conn
,
buf
,
sizeof
(
buf
),
server
.
repl_syncio_timeout
*
1000
)
==
-
1
)
==
-
1
)
{
{
return
sdscatprintf
(
sdsempty
(),
"-Reading from master: %s"
,
return
sdscatprintf
(
sdsempty
(),
"-Reading from master: %s"
,
...
@@ -1637,7 +1778,7 @@ char *sendSynchronousCommand(int flags, int fd, ...) {
...
@@ -1637,7 +1778,7 @@ char *sendSynchronousCommand(int flags, int fd, ...) {
#define PSYNC_FULLRESYNC 3
#define PSYNC_FULLRESYNC 3
#define PSYNC_NOT_SUPPORTED 4
#define PSYNC_NOT_SUPPORTED 4
#define PSYNC_TRY_LATER 5
#define PSYNC_TRY_LATER 5
int
slaveTryPartialResynchronization
(
int
fd
,
int
read_reply
)
{
int
slaveTryPartialResynchronization
(
connection
*
conn
,
int
read_reply
)
{
char
*
psync_replid
;
char
*
psync_replid
;
char
psync_offset
[
32
];
char
psync_offset
[
32
];
sds
reply
;
sds
reply
;
...
@@ -1662,18 +1803,18 @@ int slaveTryPartialResynchronization(int fd, int read_reply) {
...
@@ -1662,18 +1803,18 @@ int slaveTryPartialResynchronization(int fd, int read_reply) {
}
}
/* Issue the PSYNC command */
/* Issue the PSYNC command */
reply
=
sendSynchronousCommand
(
SYNC_CMD_WRITE
,
fd
,
"PSYNC"
,
psync_replid
,
psync_offset
,
NULL
);
reply
=
sendSynchronousCommand
(
SYNC_CMD_WRITE
,
conn
,
"PSYNC"
,
psync_replid
,
psync_offset
,
NULL
);
if
(
reply
!=
NULL
)
{
if
(
reply
!=
NULL
)
{
serverLog
(
LL_WARNING
,
"Unable to send PSYNC to master: %s"
,
reply
);
serverLog
(
LL_WARNING
,
"Unable to send PSYNC to master: %s"
,
reply
);
sdsfree
(
reply
);
sdsfree
(
reply
);
aeDeleteFileEvent
(
server
.
el
,
fd
,
AE_READABLE
);
connSetReadHandler
(
conn
,
NULL
);
return
PSYNC_WRITE_ERROR
;
return
PSYNC_WRITE_ERROR
;
}
}
return
PSYNC_WAIT_REPLY
;
return
PSYNC_WAIT_REPLY
;
}
}
/* Reading half */
/* Reading half */
reply
=
sendSynchronousCommand
(
SYNC_CMD_READ
,
fd
,
NULL
);
reply
=
sendSynchronousCommand
(
SYNC_CMD_READ
,
conn
,
NULL
);
if
(
sdslen
(
reply
)
==
0
)
{
if
(
sdslen
(
reply
)
==
0
)
{
/* The master may send empty newlines after it receives PSYNC
/* The master may send empty newlines after it receives PSYNC
* and before to reply, just to keep the connection alive. */
* and before to reply, just to keep the connection alive. */
...
@@ -1681,7 +1822,7 @@ int slaveTryPartialResynchronization(int fd, int read_reply) {
...
@@ -1681,7 +1822,7 @@ int slaveTryPartialResynchronization(int fd, int read_reply) {
return
PSYNC_WAIT_REPLY
;
return
PSYNC_WAIT_REPLY
;
}
}
aeDeleteFileEvent
(
server
.
el
,
fd
,
AE_READABLE
);
connSetReadHandler
(
conn
,
NULL
);
if
(
!
strncmp
(
reply
,
"+FULLRESYNC"
,
11
))
{
if
(
!
strncmp
(
reply
,
"+FULLRESYNC"
,
11
))
{
char
*
replid
=
NULL
,
*
offset
=
NULL
;
char
*
replid
=
NULL
,
*
offset
=
NULL
;
...
@@ -1755,7 +1896,7 @@ int slaveTryPartialResynchronization(int fd, int read_reply) {
...
@@ -1755,7 +1896,7 @@ int slaveTryPartialResynchronization(int fd, int read_reply) {
/* Setup the replication to continue. */
/* Setup the replication to continue. */
sdsfree
(
reply
);
sdsfree
(
reply
);
replicationResurrectCachedMaster
(
fd
);
replicationResurrectCachedMaster
(
conn
);
/* If this instance was restarted and we read the metadata to
/* If this instance was restarted and we read the metadata to
* PSYNC from the persistence file, our replication backlog could
* PSYNC from the persistence file, our replication backlog could
...
@@ -1797,29 +1938,23 @@ int slaveTryPartialResynchronization(int fd, int read_reply) {
...
@@ -1797,29 +1938,23 @@ int slaveTryPartialResynchronization(int fd, int read_reply) {
/* This handler fires when the non blocking connect was able to
/* This handler fires when the non blocking connect was able to
* establish a connection with the master. */
* establish a connection with the master. */
void
syncWithMaster
(
aeEventLoop
*
el
,
int
fd
,
void
*
privdata
,
int
mask
)
{
void
syncWithMaster
(
connection
*
conn
)
{
char
tmpfile
[
256
],
*
err
=
NULL
;
char
tmpfile
[
256
],
*
err
=
NULL
;
int
dfd
=
-
1
,
maxtries
=
5
;
int
dfd
=
-
1
,
maxtries
=
5
;
int
sockerr
=
0
,
psync_result
;
int
psync_result
;
socklen_t
errlen
=
sizeof
(
sockerr
);
UNUSED
(
el
);
UNUSED
(
privdata
);
UNUSED
(
mask
);
/* If this event fired after the user turned the instance into a master
/* If this event fired after the user turned the instance into a master
* with SLAVEOF NO ONE we must just return ASAP. */
* with SLAVEOF NO ONE we must just return ASAP. */
if
(
server
.
repl_state
==
REPL_STATE_NONE
)
{
if
(
server
.
repl_state
==
REPL_STATE_NONE
)
{
close
(
fd
);
c
onnC
lose
(
conn
);
return
;
return
;
}
}
/* Check for errors in the socket: after a non blocking connect() we
/* Check for errors in the socket: after a non blocking connect() we
* may find that the socket is in error state. */
* may find that the socket is in error state. */
if
(
getsockopt
(
fd
,
SOL_SOCKET
,
SO_ERROR
,
&
sockerr
,
&
errlen
)
==
-
1
)
if
(
connGetState
(
conn
)
!=
CONN_STATE_CONNECTED
)
{
sockerr
=
errno
;
if
(
sockerr
)
{
serverLog
(
LL_WARNING
,
"Error condition on socket for SYNC: %s"
,
serverLog
(
LL_WARNING
,
"Error condition on socket for SYNC: %s"
,
strerror
(
sockerr
));
connGetLastError
(
conn
));
goto
error
;
goto
error
;
}
}
...
@@ -1828,18 +1963,19 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
...
@@ -1828,18 +1963,19 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
serverLog
(
LL_NOTICE
,
"Non blocking connect for SYNC fired the event."
);
serverLog
(
LL_NOTICE
,
"Non blocking connect for SYNC fired the event."
);
/* Delete the writable event so that the readable event remains
/* Delete the writable event so that the readable event remains
* registered and we can wait for the PONG reply. */
* registered and we can wait for the PONG reply. */
aeDeleteFileEvent
(
server
.
el
,
fd
,
AE_WRITABLE
);
connSetReadHandler
(
conn
,
syncWithMaster
);
connSetWriteHandler
(
conn
,
NULL
);
server
.
repl_state
=
REPL_STATE_RECEIVE_PONG
;
server
.
repl_state
=
REPL_STATE_RECEIVE_PONG
;
/* Send the PING, don't check for errors at all, we have the timeout
/* Send the PING, don't check for errors at all, we have the timeout
* that will take care about this. */
* that will take care about this. */
err
=
sendSynchronousCommand
(
SYNC_CMD_WRITE
,
fd
,
"PING"
,
NULL
);
err
=
sendSynchronousCommand
(
SYNC_CMD_WRITE
,
conn
,
"PING"
,
NULL
);
if
(
err
)
goto
write_error
;
if
(
err
)
goto
write_error
;
return
;
return
;
}
}
/* Receive the PONG command. */
/* Receive the PONG command. */
if
(
server
.
repl_state
==
REPL_STATE_RECEIVE_PONG
)
{
if
(
server
.
repl_state
==
REPL_STATE_RECEIVE_PONG
)
{
err
=
sendSynchronousCommand
(
SYNC_CMD_READ
,
fd
,
NULL
);
err
=
sendSynchronousCommand
(
SYNC_CMD_READ
,
conn
,
NULL
);
/* We accept only two replies as valid, a positive +PONG reply
/* We accept only two replies as valid, a positive +PONG reply
* (we just check for "+") or an authentication error.
* (we just check for "+") or an authentication error.
...
@@ -1864,13 +2000,13 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
...
@@ -1864,13 +2000,13 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
/* AUTH with the master if required. */
/* AUTH with the master if required. */
if
(
server
.
repl_state
==
REPL_STATE_SEND_AUTH
)
{
if
(
server
.
repl_state
==
REPL_STATE_SEND_AUTH
)
{
if
(
server
.
masteruser
&&
server
.
masterauth
)
{
if
(
server
.
masteruser
&&
server
.
masterauth
)
{
err
=
sendSynchronousCommand
(
SYNC_CMD_WRITE
,
fd
,
"AUTH"
,
err
=
sendSynchronousCommand
(
SYNC_CMD_WRITE
,
conn
,
"AUTH"
,
server
.
masteruser
,
server
.
masterauth
,
NULL
);
server
.
masteruser
,
server
.
masterauth
,
NULL
);
if
(
err
)
goto
write_error
;
if
(
err
)
goto
write_error
;
server
.
repl_state
=
REPL_STATE_RECEIVE_AUTH
;
server
.
repl_state
=
REPL_STATE_RECEIVE_AUTH
;
return
;
return
;
}
else
if
(
server
.
masterauth
)
{
}
else
if
(
server
.
masterauth
)
{
err
=
sendSynchronousCommand
(
SYNC_CMD_WRITE
,
fd
,
"AUTH"
,
server
.
masterauth
,
NULL
);
err
=
sendSynchronousCommand
(
SYNC_CMD_WRITE
,
conn
,
"AUTH"
,
server
.
masterauth
,
NULL
);
if
(
err
)
goto
write_error
;
if
(
err
)
goto
write_error
;
server
.
repl_state
=
REPL_STATE_RECEIVE_AUTH
;
server
.
repl_state
=
REPL_STATE_RECEIVE_AUTH
;
return
;
return
;
...
@@ -1881,7 +2017,7 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
...
@@ -1881,7 +2017,7 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
/* Receive AUTH reply. */
/* Receive AUTH reply. */
if
(
server
.
repl_state
==
REPL_STATE_RECEIVE_AUTH
)
{
if
(
server
.
repl_state
==
REPL_STATE_RECEIVE_AUTH
)
{
err
=
sendSynchronousCommand
(
SYNC_CMD_READ
,
fd
,
NULL
);
err
=
sendSynchronousCommand
(
SYNC_CMD_READ
,
conn
,
NULL
);
if
(
err
[
0
]
==
'-'
)
{
if
(
err
[
0
]
==
'-'
)
{
serverLog
(
LL_WARNING
,
"Unable to AUTH to MASTER: %s"
,
err
);
serverLog
(
LL_WARNING
,
"Unable to AUTH to MASTER: %s"
,
err
);
sdsfree
(
err
);
sdsfree
(
err
);
...
@@ -1894,11 +2030,14 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
...
@@ -1894,11 +2030,14 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
/* Set the slave port, so that Master's INFO command can list the
/* Set the slave port, so that Master's INFO command can list the
* slave listening port correctly. */
* slave listening port correctly. */
if
(
server
.
repl_state
==
REPL_STATE_SEND_PORT
)
{
if
(
server
.
repl_state
==
REPL_STATE_SEND_PORT
)
{
sds
port
=
sdsfromlonglong
(
server
.
slave_announce_port
?
int
port
;
server
.
slave_announce_port
:
server
.
port
);
if
(
server
.
slave_announce_port
)
port
=
server
.
slave_announce_port
;
err
=
sendSynchronousCommand
(
SYNC_CMD_WRITE
,
fd
,
"REPLCONF"
,
else
if
(
server
.
tls_replication
&&
server
.
tls_port
)
port
=
server
.
tls_port
;
"listening-port"
,
port
,
NULL
);
else
port
=
server
.
port
;
sdsfree
(
port
);
sds
portstr
=
sdsfromlonglong
(
port
);
err
=
sendSynchronousCommand
(
SYNC_CMD_WRITE
,
conn
,
"REPLCONF"
,
"listening-port"
,
portstr
,
NULL
);
sdsfree
(
portstr
);
if
(
err
)
goto
write_error
;
if
(
err
)
goto
write_error
;
sdsfree
(
err
);
sdsfree
(
err
);
server
.
repl_state
=
REPL_STATE_RECEIVE_PORT
;
server
.
repl_state
=
REPL_STATE_RECEIVE_PORT
;
...
@@ -1907,7 +2046,7 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
...
@@ -1907,7 +2046,7 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
/* Receive REPLCONF listening-port reply. */
/* Receive REPLCONF listening-port reply. */
if
(
server
.
repl_state
==
REPL_STATE_RECEIVE_PORT
)
{
if
(
server
.
repl_state
==
REPL_STATE_RECEIVE_PORT
)
{
err
=
sendSynchronousCommand
(
SYNC_CMD_READ
,
fd
,
NULL
);
err
=
sendSynchronousCommand
(
SYNC_CMD_READ
,
conn
,
NULL
);
/* Ignore the error if any, not all the Redis versions support
/* Ignore the error if any, not all the Redis versions support
* REPLCONF listening-port. */
* REPLCONF listening-port. */
if
(
err
[
0
]
==
'-'
)
{
if
(
err
[
0
]
==
'-'
)
{
...
@@ -1928,7 +2067,7 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
...
@@ -1928,7 +2067,7 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
/* Set the slave ip, so that Master's INFO command can list the
/* Set the slave ip, so that Master's INFO command can list the
* slave IP address port correctly in case of port forwarding or NAT. */
* slave IP address port correctly in case of port forwarding or NAT. */
if
(
server
.
repl_state
==
REPL_STATE_SEND_IP
)
{
if
(
server
.
repl_state
==
REPL_STATE_SEND_IP
)
{
err
=
sendSynchronousCommand
(
SYNC_CMD_WRITE
,
fd
,
"REPLCONF"
,
err
=
sendSynchronousCommand
(
SYNC_CMD_WRITE
,
conn
,
"REPLCONF"
,
"ip-address"
,
server
.
slave_announce_ip
,
NULL
);
"ip-address"
,
server
.
slave_announce_ip
,
NULL
);
if
(
err
)
goto
write_error
;
if
(
err
)
goto
write_error
;
sdsfree
(
err
);
sdsfree
(
err
);
...
@@ -1938,7 +2077,7 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
...
@@ -1938,7 +2077,7 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
/* Receive REPLCONF ip-address reply. */
/* Receive REPLCONF ip-address reply. */
if
(
server
.
repl_state
==
REPL_STATE_RECEIVE_IP
)
{
if
(
server
.
repl_state
==
REPL_STATE_RECEIVE_IP
)
{
err
=
sendSynchronousCommand
(
SYNC_CMD_READ
,
fd
,
NULL
);
err
=
sendSynchronousCommand
(
SYNC_CMD_READ
,
conn
,
NULL
);
/* Ignore the error if any, not all the Redis versions support
/* Ignore the error if any, not all the Redis versions support
* REPLCONF listening-port. */
* REPLCONF listening-port. */
if
(
err
[
0
]
==
'-'
)
{
if
(
err
[
0
]
==
'-'
)
{
...
@@ -1956,7 +2095,7 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
...
@@ -1956,7 +2095,7 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
*
*
* The master will ignore capabilities it does not understand. */
* The master will ignore capabilities it does not understand. */
if
(
server
.
repl_state
==
REPL_STATE_SEND_CAPA
)
{
if
(
server
.
repl_state
==
REPL_STATE_SEND_CAPA
)
{
err
=
sendSynchronousCommand
(
SYNC_CMD_WRITE
,
fd
,
"REPLCONF"
,
err
=
sendSynchronousCommand
(
SYNC_CMD_WRITE
,
conn
,
"REPLCONF"
,
"capa"
,
"eof"
,
"capa"
,
"psync2"
,
NULL
);
"capa"
,
"eof"
,
"capa"
,
"psync2"
,
NULL
);
if
(
err
)
goto
write_error
;
if
(
err
)
goto
write_error
;
sdsfree
(
err
);
sdsfree
(
err
);
...
@@ -1966,7 +2105,7 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
...
@@ -1966,7 +2105,7 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
/* Receive CAPA reply. */
/* Receive CAPA reply. */
if
(
server
.
repl_state
==
REPL_STATE_RECEIVE_CAPA
)
{
if
(
server
.
repl_state
==
REPL_STATE_RECEIVE_CAPA
)
{
err
=
sendSynchronousCommand
(
SYNC_CMD_READ
,
fd
,
NULL
);
err
=
sendSynchronousCommand
(
SYNC_CMD_READ
,
conn
,
NULL
);
/* Ignore the error if any, not all the Redis versions support
/* Ignore the error if any, not all the Redis versions support
* REPLCONF capa. */
* REPLCONF capa. */
if
(
err
[
0
]
==
'-'
)
{
if
(
err
[
0
]
==
'-'
)
{
...
@@ -1983,7 +2122,7 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
...
@@ -1983,7 +2122,7 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
* and the global offset, to try a partial resync at the next
* and the global offset, to try a partial resync at the next
* reconnection attempt. */
* reconnection attempt. */
if
(
server
.
repl_state
==
REPL_STATE_SEND_PSYNC
)
{
if
(
server
.
repl_state
==
REPL_STATE_SEND_PSYNC
)
{
if
(
slaveTryPartialResynchronization
(
fd
,
0
)
==
PSYNC_WRITE_ERROR
)
{
if
(
slaveTryPartialResynchronization
(
conn
,
0
)
==
PSYNC_WRITE_ERROR
)
{
err
=
sdsnew
(
"Write error sending the PSYNC command."
);
err
=
sdsnew
(
"Write error sending the PSYNC command."
);
goto
write_error
;
goto
write_error
;
}
}
...
@@ -1999,7 +2138,7 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
...
@@ -1999,7 +2138,7 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
goto
error
;
goto
error
;
}
}
psync_result
=
slaveTryPartialResynchronization
(
fd
,
1
);
psync_result
=
slaveTryPartialResynchronization
(
conn
,
1
);
if
(
psync_result
==
PSYNC_WAIT_REPLY
)
return
;
/* Try again later... */
if
(
psync_result
==
PSYNC_WAIT_REPLY
)
return
;
/* Try again later... */
/* If the master is in an transient error, we should try to PSYNC
/* If the master is in an transient error, we should try to PSYNC
...
@@ -2028,7 +2167,7 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
...
@@ -2028,7 +2167,7 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
* already populated. */
* already populated. */
if
(
psync_result
==
PSYNC_NOT_SUPPORTED
)
{
if
(
psync_result
==
PSYNC_NOT_SUPPORTED
)
{
serverLog
(
LL_NOTICE
,
"Retrying with SYNC..."
);
serverLog
(
LL_NOTICE
,
"Retrying with SYNC..."
);
if
(
s
yncWrite
(
fd
,
"SYNC
\r\n
"
,
6
,
server
.
repl_syncio_timeout
*
1000
)
==
-
1
)
{
if
(
connS
yncWrite
(
conn
,
"SYNC
\r\n
"
,
6
,
server
.
repl_syncio_timeout
*
1000
)
==
-
1
)
{
serverLog
(
LL_WARNING
,
"I/O error writing to MASTER: %s"
,
serverLog
(
LL_WARNING
,
"I/O error writing to MASTER: %s"
,
strerror
(
errno
));
strerror
(
errno
));
goto
error
;
goto
error
;
...
@@ -2053,12 +2192,13 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
...
@@ -2053,12 +2192,13 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
}
}
/* Setup the non blocking download of the bulk file. */
/* Setup the non blocking download of the bulk file. */
if
(
aeCreateFileEvent
(
server
.
el
,
fd
,
AE_READABLE
,
readSyncBulkPayload
,
NULL
)
if
(
connSetReadHandler
(
conn
,
readSyncBulkPayload
)
==
AE
_ERR
)
==
C
_ERR
)
{
{
char
conninfo
[
CONN_INFO_LEN
];
serverLog
(
LL_WARNING
,
serverLog
(
LL_WARNING
,
"Can't create readable event for SYNC: %s (
fd=%d
)"
,
"Can't create readable event for SYNC: %s (
%s
)"
,
strerror
(
errno
),
fd
);
strerror
(
errno
),
connGetInfo
(
conn
,
conninfo
,
sizeof
(
conninfo
))
);
goto
error
;
goto
error
;
}
}
...
@@ -2070,16 +2210,15 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
...
@@ -2070,16 +2210,15 @@ void syncWithMaster(aeEventLoop *el, int fd, void *privdata, int mask) {
return
;
return
;
error:
error:
aeDeleteFileEvent
(
server
.
el
,
fd
,
AE_READABLE
|
AE_WRITABLE
);
if
(
dfd
!=
-
1
)
close
(
dfd
);
if
(
dfd
!=
-
1
)
close
(
dfd
);
close
(
fd
);
connClose
(
conn
);
server
.
repl_transfer_s
=
NULL
;
if
(
server
.
repl_transfer_fd
!=
-
1
)
if
(
server
.
repl_transfer_fd
!=
-
1
)
close
(
server
.
repl_transfer_fd
);
close
(
server
.
repl_transfer_fd
);
if
(
server
.
repl_transfer_tmpfile
)
if
(
server
.
repl_transfer_tmpfile
)
zfree
(
server
.
repl_transfer_tmpfile
);
zfree
(
server
.
repl_transfer_tmpfile
);
server
.
repl_transfer_tmpfile
=
NULL
;
server
.
repl_transfer_tmpfile
=
NULL
;
server
.
repl_transfer_fd
=
-
1
;
server
.
repl_transfer_fd
=
-
1
;
server
.
repl_transfer_s
=
-
1
;
server
.
repl_state
=
REPL_STATE_CONNECT
;
server
.
repl_state
=
REPL_STATE_CONNECT
;
return
;
return
;
...
@@ -2090,26 +2229,18 @@ write_error: /* Handle sendSynchronousCommand(SYNC_CMD_WRITE) errors. */
...
@@ -2090,26 +2229,18 @@ write_error: /* Handle sendSynchronousCommand(SYNC_CMD_WRITE) errors. */
}
}
int
connectWithMaster
(
void
)
{
int
connectWithMaster
(
void
)
{
int
fd
;
server
.
repl_transfer_s
=
server
.
tls_replication
?
connCreateTLS
()
:
connCreateSocket
();
if
(
connConnect
(
server
.
repl_transfer_s
,
server
.
masterhost
,
server
.
masterport
,
fd
=
anetTcpNonBlockBestEffortBindConnect
(
NULL
,
NET_FIRST_BIND_ADDR
,
syncWithMaster
)
==
C_ERR
)
{
server
.
masterhost
,
server
.
masterport
,
NET_FIRST_BIND_ADDR
);
if
(
fd
==
-
1
)
{
serverLog
(
LL_WARNING
,
"Unable to connect to MASTER: %s"
,
serverLog
(
LL_WARNING
,
"Unable to connect to MASTER: %s"
,
strerror
(
errno
));
connGetLastError
(
server
.
repl_transfer_s
));
connClose
(
server
.
repl_transfer_s
);
server
.
repl_transfer_s
=
NULL
;
return
C_ERR
;
return
C_ERR
;
}
}
if
(
aeCreateFileEvent
(
server
.
el
,
fd
,
AE_READABLE
|
AE_WRITABLE
,
syncWithMaster
,
NULL
)
==
AE_ERR
)
{
close
(
fd
);
serverLog
(
LL_WARNING
,
"Can't create readable event for SYNC"
);
return
C_ERR
;
}
server
.
repl_transfer_lastio
=
server
.
unixtime
;
server
.
repl_transfer_lastio
=
server
.
unixtime
;
server
.
repl_transfer_s
=
fd
;
server
.
repl_state
=
REPL_STATE_CONNECTING
;
server
.
repl_state
=
REPL_STATE_CONNECTING
;
return
C_OK
;
return
C_OK
;
}
}
...
@@ -2119,11 +2250,8 @@ int connectWithMaster(void) {
...
@@ -2119,11 +2250,8 @@ int connectWithMaster(void) {
* Never call this function directly, use cancelReplicationHandshake() instead.
* Never call this function directly, use cancelReplicationHandshake() instead.
*/
*/
void
undoConnectWithMaster
(
void
)
{
void
undoConnectWithMaster
(
void
)
{
int
fd
=
server
.
repl_transfer_s
;
connClose
(
server
.
repl_transfer_s
);
server
.
repl_transfer_s
=
NULL
;
aeDeleteFileEvent
(
server
.
el
,
fd
,
AE_READABLE
|
AE_WRITABLE
);
close
(
fd
);
server
.
repl_transfer_s
=
-
1
;
}
}
/* Abort the async download of the bulk dataset while SYNC-ing with master.
/* Abort the async download of the bulk dataset while SYNC-ing with master.
...
@@ -2311,7 +2439,7 @@ void roleCommand(client *c) {
...
@@ -2311,7 +2439,7 @@ void roleCommand(client *c) {
char
ip
[
NET_IP_STR_LEN
],
*
slaveip
=
slave
->
slave_ip
;
char
ip
[
NET_IP_STR_LEN
],
*
slaveip
=
slave
->
slave_ip
;
if
(
slaveip
[
0
]
==
'\0'
)
{
if
(
slaveip
[
0
]
==
'\0'
)
{
if
(
anet
PeerToString
(
slave
->
fd
,
ip
,
sizeof
(
ip
),
NULL
)
==
-
1
)
if
(
conn
PeerToString
(
slave
->
conn
,
ip
,
sizeof
(
ip
),
NULL
)
==
-
1
)
continue
;
continue
;
slaveip
=
ip
;
slaveip
=
ip
;
}
}
...
@@ -2433,7 +2561,7 @@ void replicationCacheMasterUsingMyself(void) {
...
@@ -2433,7 +2561,7 @@ void replicationCacheMasterUsingMyself(void) {
/* The master client we create can be set to any DBID, because
/* The master client we create can be set to any DBID, because
* the new master will start its replication stream with SELECT. */
* the new master will start its replication stream with SELECT. */
server
.
master_initial_offset
=
server
.
master_repl_offset
;
server
.
master_initial_offset
=
server
.
master_repl_offset
;
replicationCreateMasterClient
(
-
1
,
-
1
);
replicationCreateMasterClient
(
NULL
,
-
1
);
/* Use our own ID / offset. */
/* Use our own ID / offset. */
memcpy
(
server
.
master
->
replid
,
server
.
replid
,
sizeof
(
server
.
replid
));
memcpy
(
server
.
master
->
replid
,
server
.
replid
,
sizeof
(
server
.
replid
));
...
@@ -2462,10 +2590,11 @@ void replicationDiscardCachedMaster(void) {
...
@@ -2462,10 +2590,11 @@ void replicationDiscardCachedMaster(void) {
* This function is called when successfully setup a partial resynchronization
* This function is called when successfully setup a partial resynchronization
* so the stream of data that we'll receive will start from were this
* so the stream of data that we'll receive will start from were this
* master left. */
* master left. */
void
replicationResurrectCachedMaster
(
int
newfd
)
{
void
replicationResurrectCachedMaster
(
connection
*
conn
)
{
server
.
master
=
server
.
cached_master
;
server
.
master
=
server
.
cached_master
;
server
.
cached_master
=
NULL
;
server
.
cached_master
=
NULL
;
server
.
master
->
fd
=
newfd
;
server
.
master
->
conn
=
conn
;
connSetPrivateData
(
server
.
master
->
conn
,
server
.
master
);
server
.
master
->
flags
&=
~
(
CLIENT_CLOSE_AFTER_REPLY
|
CLIENT_CLOSE_ASAP
);
server
.
master
->
flags
&=
~
(
CLIENT_CLOSE_AFTER_REPLY
|
CLIENT_CLOSE_ASAP
);
server
.
master
->
authenticated
=
1
;
server
.
master
->
authenticated
=
1
;
server
.
master
->
lastinteraction
=
server
.
unixtime
;
server
.
master
->
lastinteraction
=
server
.
unixtime
;
...
@@ -2474,8 +2603,7 @@ void replicationResurrectCachedMaster(int newfd) {
...
@@ -2474,8 +2603,7 @@ void replicationResurrectCachedMaster(int newfd) {
/* Re-add to the list of clients. */
/* Re-add to the list of clients. */
linkClient
(
server
.
master
);
linkClient
(
server
.
master
);
if
(
aeCreateFileEvent
(
server
.
el
,
newfd
,
AE_READABLE
,
if
(
connSetReadHandler
(
server
.
master
->
conn
,
readQueryFromClient
))
{
readQueryFromClient
,
server
.
master
))
{
serverLog
(
LL_WARNING
,
"Error resurrecting the cached master, impossible to add the readable handler: %s"
,
strerror
(
errno
));
serverLog
(
LL_WARNING
,
"Error resurrecting the cached master, impossible to add the readable handler: %s"
,
strerror
(
errno
));
freeClientAsync
(
server
.
master
);
/* Close ASAP. */
freeClientAsync
(
server
.
master
);
/* Close ASAP. */
}
}
...
@@ -2483,8 +2611,7 @@ void replicationResurrectCachedMaster(int newfd) {
...
@@ -2483,8 +2611,7 @@ void replicationResurrectCachedMaster(int newfd) {
/* We may also need to install the write handler as well if there is
/* We may also need to install the write handler as well if there is
* pending data in the write buffers. */
* pending data in the write buffers. */
if
(
clientHasPendingReplies
(
server
.
master
))
{
if
(
clientHasPendingReplies
(
server
.
master
))
{
if
(
aeCreateFileEvent
(
server
.
el
,
newfd
,
AE_WRITABLE
,
if
(
connSetWriteHandler
(
server
.
master
->
conn
,
sendReplyToClient
))
{
sendReplyToClient
,
server
.
master
))
{
serverLog
(
LL_WARNING
,
"Error resurrecting the cached master, impossible to add the writable handler: %s"
,
strerror
(
errno
));
serverLog
(
LL_WARNING
,
"Error resurrecting the cached master, impossible to add the writable handler: %s"
,
strerror
(
errno
));
freeClientAsync
(
server
.
master
);
/* Close ASAP. */
freeClientAsync
(
server
.
master
);
/* Close ASAP. */
}
}
...
@@ -2854,9 +2981,7 @@ void replicationCron(void) {
...
@@ -2854,9 +2981,7 @@ void replicationCron(void) {
server
.
rdb_child_type
!=
RDB_CHILD_TYPE_SOCKET
));
server
.
rdb_child_type
!=
RDB_CHILD_TYPE_SOCKET
));
if
(
is_presync
)
{
if
(
is_presync
)
{
if
(
write
(
slave
->
fd
,
"
\n
"
,
1
)
==
-
1
)
{
connWrite
(
slave
->
conn
,
"
\n
"
,
1
);
/* Don't worry about socket errors, it's just a ping. */
}
}
}
}
}
...
...
src/rio.c
View file @
efb6495a
...
@@ -159,13 +159,13 @@ void rioInitWithFile(rio *r, FILE *fp) {
...
@@ -159,13 +159,13 @@ void rioInitWithFile(rio *r, FILE *fp) {
r
->
io
.
file
.
autosync
=
0
;
r
->
io
.
file
.
autosync
=
0
;
}
}
/* -------------------
File descriptor
implementation -------------------
/* -------------------
Connection
implementation -------------------
* We use this RIO implemetnation when reading an RDB file directly from
* We use this RIO implemetnation when reading an RDB file directly from
* the
socket
to the memory via rdbLoadRio(), thus this implementation
* the
connection
to the memory via rdbLoadRio(), thus this implementation
* only implements reading from a
file descriptor
that is, normally,
* only implements reading from a
connection
that is, normally,
* just a socket. */
* just a socket. */
static
size_t
rio
Fd
Write
(
rio
*
r
,
const
void
*
buf
,
size_t
len
)
{
static
size_t
rio
Conn
Write
(
rio
*
r
,
const
void
*
buf
,
size_t
len
)
{
UNUSED
(
r
);
UNUSED
(
r
);
UNUSED
(
buf
);
UNUSED
(
buf
);
UNUSED
(
len
);
UNUSED
(
len
);
...
@@ -173,72 +173,72 @@ static size_t rioFdWrite(rio *r, const void *buf, size_t len) {
...
@@ -173,72 +173,72 @@ static size_t rioFdWrite(rio *r, const void *buf, size_t len) {
}
}
/* Returns 1 or 0 for success/failure. */
/* Returns 1 or 0 for success/failure. */
static
size_t
rio
Fd
Read
(
rio
*
r
,
void
*
buf
,
size_t
len
)
{
static
size_t
rio
Conn
Read
(
rio
*
r
,
void
*
buf
,
size_t
len
)
{
size_t
avail
=
sdslen
(
r
->
io
.
fd
.
buf
)
-
r
->
io
.
fd
.
pos
;
size_t
avail
=
sdslen
(
r
->
io
.
conn
.
buf
)
-
r
->
io
.
conn
.
pos
;
/* If the buffer is too small for the entire request: realloc. */
/* If the buffer is too small for the entire request: realloc. */
if
(
sdslen
(
r
->
io
.
fd
.
buf
)
+
sdsavail
(
r
->
io
.
fd
.
buf
)
<
len
)
if
(
sdslen
(
r
->
io
.
conn
.
buf
)
+
sdsavail
(
r
->
io
.
conn
.
buf
)
<
len
)
r
->
io
.
fd
.
buf
=
sdsMakeRoomFor
(
r
->
io
.
fd
.
buf
,
len
-
sdslen
(
r
->
io
.
fd
.
buf
));
r
->
io
.
conn
.
buf
=
sdsMakeRoomFor
(
r
->
io
.
conn
.
buf
,
len
-
sdslen
(
r
->
io
.
conn
.
buf
));
/* If the remaining unused buffer is not large enough: memmove so that we
/* If the remaining unused buffer is not large enough: memmove so that we
* can read the rest. */
* can read the rest. */
if
(
len
>
avail
&&
sdsavail
(
r
->
io
.
fd
.
buf
)
<
len
-
avail
)
{
if
(
len
>
avail
&&
sdsavail
(
r
->
io
.
conn
.
buf
)
<
len
-
avail
)
{
sdsrange
(
r
->
io
.
fd
.
buf
,
r
->
io
.
fd
.
pos
,
-
1
);
sdsrange
(
r
->
io
.
conn
.
buf
,
r
->
io
.
conn
.
pos
,
-
1
);
r
->
io
.
fd
.
pos
=
0
;
r
->
io
.
conn
.
pos
=
0
;
}
}
/* If we don't already have all the data in the sds, read more */
/* If we don't already have all the data in the sds, read more */
while
(
len
>
sdslen
(
r
->
io
.
fd
.
buf
)
-
r
->
io
.
fd
.
pos
)
{
while
(
len
>
sdslen
(
r
->
io
.
conn
.
buf
)
-
r
->
io
.
conn
.
pos
)
{
size_t
buffered
=
sdslen
(
r
->
io
.
fd
.
buf
)
-
r
->
io
.
fd
.
pos
;
size_t
buffered
=
sdslen
(
r
->
io
.
conn
.
buf
)
-
r
->
io
.
conn
.
pos
;
size_t
toread
=
len
-
buffered
;
size_t
toread
=
len
-
buffered
;
/* Read either what's missing, or PROTO_IOBUF_LEN, the bigger of
/* Read either what's missing, or PROTO_IOBUF_LEN, the bigger of
* the two. */
* the two. */
if
(
toread
<
PROTO_IOBUF_LEN
)
toread
=
PROTO_IOBUF_LEN
;
if
(
toread
<
PROTO_IOBUF_LEN
)
toread
=
PROTO_IOBUF_LEN
;
if
(
toread
>
sdsavail
(
r
->
io
.
fd
.
buf
))
toread
=
sdsavail
(
r
->
io
.
fd
.
buf
);
if
(
toread
>
sdsavail
(
r
->
io
.
conn
.
buf
))
toread
=
sdsavail
(
r
->
io
.
conn
.
buf
);
if
(
r
->
io
.
fd
.
read_limit
!=
0
&&
if
(
r
->
io
.
conn
.
read_limit
!=
0
&&
r
->
io
.
fd
.
read_so_far
+
buffered
+
toread
>
r
->
io
.
fd
.
read_limit
)
r
->
io
.
conn
.
read_so_far
+
buffered
+
toread
>
r
->
io
.
conn
.
read_limit
)
{
{
if
(
r
->
io
.
fd
.
read_limit
>=
r
->
io
.
fd
.
read_so_far
-
buffered
)
if
(
r
->
io
.
conn
.
read_limit
>=
r
->
io
.
conn
.
read_so_far
-
buffered
)
toread
=
r
->
io
.
fd
.
read_limit
-
r
->
io
.
fd
.
read_so_far
-
buffered
;
toread
=
r
->
io
.
conn
.
read_limit
-
r
->
io
.
conn
.
read_so_far
-
buffered
;
else
{
else
{
errno
=
EOVERFLOW
;
errno
=
EOVERFLOW
;
return
0
;
return
0
;
}
}
}
}
int
retval
=
r
ead
(
r
->
io
.
fd
.
fd
,
int
retval
=
connR
ead
(
r
->
io
.
conn
.
conn
,
(
char
*
)
r
->
io
.
fd
.
buf
+
sdslen
(
r
->
io
.
fd
.
buf
),
(
char
*
)
r
->
io
.
conn
.
buf
+
sdslen
(
r
->
io
.
conn
.
buf
),
toread
);
toread
);
if
(
retval
<=
0
)
{
if
(
retval
<=
0
)
{
if
(
errno
==
EWOULDBLOCK
)
errno
=
ETIMEDOUT
;
if
(
errno
==
EWOULDBLOCK
)
errno
=
ETIMEDOUT
;
return
0
;
return
0
;
}
}
sdsIncrLen
(
r
->
io
.
fd
.
buf
,
retval
);
sdsIncrLen
(
r
->
io
.
conn
.
buf
,
retval
);
}
}
memcpy
(
buf
,
(
char
*
)
r
->
io
.
fd
.
buf
+
r
->
io
.
fd
.
pos
,
len
);
memcpy
(
buf
,
(
char
*
)
r
->
io
.
conn
.
buf
+
r
->
io
.
conn
.
pos
,
len
);
r
->
io
.
fd
.
read_so_far
+=
len
;
r
->
io
.
conn
.
read_so_far
+=
len
;
r
->
io
.
fd
.
pos
+=
len
;
r
->
io
.
conn
.
pos
+=
len
;
return
len
;
return
len
;
}
}
/* Returns read/write position in file. */
/* Returns read/write position in file. */
static
off_t
rio
Fd
Tell
(
rio
*
r
)
{
static
off_t
rio
Conn
Tell
(
rio
*
r
)
{
return
r
->
io
.
fd
.
read_so_far
;
return
r
->
io
.
conn
.
read_so_far
;
}
}
/* Flushes any buffer to target device if applicable. Returns 1 on success
/* Flushes any buffer to target device if applicable. Returns 1 on success
* and 0 on failures. */
* and 0 on failures. */
static
int
rio
Fd
Flush
(
rio
*
r
)
{
static
int
rio
Conn
Flush
(
rio
*
r
)
{
/* Our flush is implemented by the write method, that recognizes a
/* Our flush is implemented by the write method, that recognizes a
* buffer set to NULL with a count of zero as a flush request. */
* buffer set to NULL with a count of zero as a flush request. */
return
rio
Fd
Write
(
r
,
NULL
,
0
);
return
rio
Conn
Write
(
r
,
NULL
,
0
);
}
}
static
const
rio
rio
Fd
IO
=
{
static
const
rio
rio
Conn
IO
=
{
rio
Fd
Read
,
rio
Conn
Read
,
rio
Fd
Write
,
rio
Conn
Write
,
rio
Fd
Tell
,
rio
Conn
Tell
,
rio
Fd
Flush
,
rio
Conn
Flush
,
NULL
,
/* update_checksum */
NULL
,
/* update_checksum */
0
,
/* current checksum */
0
,
/* current checksum */
0
,
/* flags */
0
,
/* flags */
...
@@ -249,108 +249,90 @@ static const rio rioFdIO = {
...
@@ -249,108 +249,90 @@ static const rio rioFdIO = {
/* Create an RIO that implements a buffered read from an fd
/* Create an RIO that implements a buffered read from an fd
* read_limit argument stops buffering when the reaching the limit. */
* read_limit argument stops buffering when the reaching the limit. */
void
rioInitWith
Fd
(
rio
*
r
,
int
fd
,
size_t
read_limit
)
{
void
rioInitWith
Conn
(
rio
*
r
,
connection
*
conn
,
size_t
read_limit
)
{
*
r
=
rio
Fd
IO
;
*
r
=
rio
Conn
IO
;
r
->
io
.
fd
.
fd
=
fd
;
r
->
io
.
conn
.
conn
=
conn
;
r
->
io
.
fd
.
pos
=
0
;
r
->
io
.
conn
.
pos
=
0
;
r
->
io
.
fd
.
read_limit
=
read_limit
;
r
->
io
.
conn
.
read_limit
=
read_limit
;
r
->
io
.
fd
.
read_so_far
=
0
;
r
->
io
.
conn
.
read_so_far
=
0
;
r
->
io
.
fd
.
buf
=
sdsnewlen
(
NULL
,
PROTO_IOBUF_LEN
);
r
->
io
.
conn
.
buf
=
sdsnewlen
(
NULL
,
PROTO_IOBUF_LEN
);
sdsclear
(
r
->
io
.
fd
.
buf
);
sdsclear
(
r
->
io
.
conn
.
buf
);
}
}
/* Release the RIO tream. Optionally returns the unread buffered data
/* Release the RIO tream. Optionally returns the unread buffered data
* when the SDS pointer 'remaining' is passed. */
* when the SDS pointer 'remaining' is passed. */
void
rioFree
Fd
(
rio
*
r
,
sds
*
remaining
)
{
void
rioFree
Conn
(
rio
*
r
,
sds
*
remaining
)
{
if
(
remaining
&&
(
size_t
)
r
->
io
.
fd
.
pos
<
sdslen
(
r
->
io
.
fd
.
buf
))
{
if
(
remaining
&&
(
size_t
)
r
->
io
.
conn
.
pos
<
sdslen
(
r
->
io
.
conn
.
buf
))
{
if
(
r
->
io
.
fd
.
pos
>
0
)
sdsrange
(
r
->
io
.
fd
.
buf
,
r
->
io
.
fd
.
pos
,
-
1
);
if
(
r
->
io
.
conn
.
pos
>
0
)
sdsrange
(
r
->
io
.
conn
.
buf
,
r
->
io
.
conn
.
pos
,
-
1
);
*
remaining
=
r
->
io
.
fd
.
buf
;
*
remaining
=
r
->
io
.
conn
.
buf
;
}
else
{
}
else
{
sdsfree
(
r
->
io
.
fd
.
buf
);
sdsfree
(
r
->
io
.
conn
.
buf
);
if
(
remaining
)
*
remaining
=
NULL
;
if
(
remaining
)
*
remaining
=
NULL
;
}
}
r
->
io
.
fd
.
buf
=
NULL
;
r
->
io
.
conn
.
buf
=
NULL
;
}
}
/* ------------------- File descriptor
s set
implementation ------------------
/* ------------------- File descriptor implementation ------------------
* This target is used to write the RDB file to
N different replicas via
* This target is used to write the RDB file to
pipe, when the master just
*
sockets, when the master just
streams the data to the replicas without
* streams the data to the replicas without
creating an RDB on-disk image
*
creating an RDB on-disk image
(diskless replication option).
* (diskless replication option).
* It only implements writes. */
* It only implements writes. */
/* Returns 1 or 0 for success/failure.
/* Returns 1 or 0 for success/failure.
* The function returns success as long as we are able to correctly write
* to at least one file descriptor.
*
*
* When buf is NULL and len is 0, the function performs a flush operation
* When buf is NULL and len is 0, the function performs a flush operation
* if there is some pending buffer, so this function is also used in order
* if there is some pending buffer, so this function is also used in order
* to implement rioFd
set
Flush(). */
* to implement rioFdFlush(). */
static
size_t
rioFd
set
Write
(
rio
*
r
,
const
void
*
buf
,
size_t
len
)
{
static
size_t
rioFdWrite
(
rio
*
r
,
const
void
*
buf
,
size_t
len
)
{
ssize_t
retval
;
ssize_t
retval
;
int
j
;
unsigned
char
*
p
=
(
unsigned
char
*
)
buf
;
unsigned
char
*
p
=
(
unsigned
char
*
)
buf
;
int
doflush
=
(
buf
==
NULL
&&
len
==
0
);
int
doflush
=
(
buf
==
NULL
&&
len
==
0
);
/* To start we always append to our buffer. If it gets larger than
/* For small writes, we rather keep the data in user-space buffer, and flush
* a given size, we actually write to the sockets. */
* it only when it grows. however for larger writes, we prefer to flush
if
(
len
)
{
* any pre-existing buffer, and write the new one directly without reallocs
r
->
io
.
fdset
.
buf
=
sdscatlen
(
r
->
io
.
fdset
.
buf
,
buf
,
len
);
* and memory copying. */
len
=
0
;
/* Prevent entering the while below if we don't flush. */
if
(
len
>
PROTO_IOBUF_LEN
)
{
if
(
sdslen
(
r
->
io
.
fdset
.
buf
)
>
PROTO_IOBUF_LEN
)
doflush
=
1
;
/* First, flush any pre-existing buffered data. */
if
(
sdslen
(
r
->
io
.
fd
.
buf
))
{
if
(
rioFdWrite
(
r
,
NULL
,
0
)
==
0
)
return
0
;
}
}
/* Write the new data, keeping 'p' and 'len' from the input. */
if
(
doflush
)
{
}
else
{
p
=
(
unsigned
char
*
)
r
->
io
.
fdset
.
buf
;
if
(
len
)
{
len
=
sdslen
(
r
->
io
.
fdset
.
buf
);
r
->
io
.
fd
.
buf
=
sdscatlen
(
r
->
io
.
fd
.
buf
,
buf
,
len
);
if
(
sdslen
(
r
->
io
.
fd
.
buf
)
>
PROTO_IOBUF_LEN
)
doflush
=
1
;
if
(
!
doflush
)
return
1
;
}
}
/* Flusing the buffered data. set 'p' and 'len' accordintly. */
/* Write in little chunchs so that when there are big writes we
p
=
(
unsigned
char
*
)
r
->
io
.
fd
.
buf
;
* parallelize while the kernel is sending data in background to
len
=
sdslen
(
r
->
io
.
fd
.
buf
);
* the TCP socket. */
while
(
len
)
{
size_t
count
=
len
<
1024
?
len
:
1024
;
int
broken
=
0
;
for
(
j
=
0
;
j
<
r
->
io
.
fdset
.
numfds
;
j
++
)
{
if
(
r
->
io
.
fdset
.
state
[
j
]
!=
0
)
{
/* Skip FDs alraedy in error. */
broken
++
;
continue
;
}
}
/* Make sure to write 'count' bytes to the socket regardless
* of short writes. */
size_t
nwritten
=
0
;
size_t
nwritten
=
0
;
while
(
nwritten
!=
count
)
{
while
(
nwritten
!=
len
)
{
retval
=
write
(
r
->
io
.
fd
set
.
fds
[
j
]
,
p
+
nwritten
,
count
-
nwritten
);
retval
=
write
(
r
->
io
.
fd
.
fd
,
p
+
nwritten
,
len
-
nwritten
);
if
(
retval
<=
0
)
{
if
(
retval
<=
0
)
{
/* With blocking
sockets
, which is the sole user of this
/* With blocking
io
, which is the sole user of this
* rio target, EWOULDBLOCK is returned only because of
* rio target, EWOULDBLOCK is returned only because of
* the SO_SNDTIMEO socket option, so we translate the error
* the SO_SNDTIMEO socket option, so we translate the error
* into one more recognizable by the user. */
* into one more recognizable by the user. */
if
(
retval
==
-
1
&&
errno
==
EWOULDBLOCK
)
errno
=
ETIMEDOUT
;
if
(
retval
==
-
1
&&
errno
==
EWOULDBLOCK
)
errno
=
ETIMEDOUT
;
break
;
return
0
;
/* error. */
}
}
nwritten
+=
retval
;
nwritten
+=
retval
;
}
}
if
(
nwritten
!=
count
)
{
r
->
io
.
fd
.
pos
+=
len
;
/* Mark this FD as broken. */
sdsclear
(
r
->
io
.
fd
.
buf
);
r
->
io
.
fdset
.
state
[
j
]
=
errno
;
if
(
r
->
io
.
fdset
.
state
[
j
]
==
0
)
r
->
io
.
fdset
.
state
[
j
]
=
EIO
;
}
}
if
(
broken
==
r
->
io
.
fdset
.
numfds
)
return
0
;
/* All the FDs in error. */
p
+=
count
;
len
-=
count
;
r
->
io
.
fdset
.
pos
+=
count
;
}
if
(
doflush
)
sdsclear
(
r
->
io
.
fdset
.
buf
);
return
1
;
return
1
;
}
}
/* Returns 1 or 0 for success/failure. */
/* Returns 1 or 0 for success/failure. */
static
size_t
rioFd
set
Read
(
rio
*
r
,
void
*
buf
,
size_t
len
)
{
static
size_t
rioFdRead
(
rio
*
r
,
void
*
buf
,
size_t
len
)
{
UNUSED
(
r
);
UNUSED
(
r
);
UNUSED
(
buf
);
UNUSED
(
buf
);
UNUSED
(
len
);
UNUSED
(
len
);
...
@@ -358,23 +340,23 @@ static size_t rioFdsetRead(rio *r, void *buf, size_t len) {
...
@@ -358,23 +340,23 @@ static size_t rioFdsetRead(rio *r, void *buf, size_t len) {
}
}
/* Returns read/write position in file. */
/* Returns read/write position in file. */
static
off_t
rioFd
set
Tell
(
rio
*
r
)
{
static
off_t
rioFdTell
(
rio
*
r
)
{
return
r
->
io
.
fd
set
.
pos
;
return
r
->
io
.
fd
.
pos
;
}
}
/* Flushes any buffer to target device if applicable. Returns 1 on success
/* Flushes any buffer to target device if applicable. Returns 1 on success
* and 0 on failures. */
* and 0 on failures. */
static
int
rioFd
set
Flush
(
rio
*
r
)
{
static
int
rioFdFlush
(
rio
*
r
)
{
/* Our flush is implemented by the write method, that recognizes a
/* Our flush is implemented by the write method, that recognizes a
* buffer set to NULL with a count of zero as a flush request. */
* buffer set to NULL with a count of zero as a flush request. */
return
rioFd
set
Write
(
r
,
NULL
,
0
);
return
rioFdWrite
(
r
,
NULL
,
0
);
}
}
static
const
rio
rioFd
set
IO
=
{
static
const
rio
rioFdIO
=
{
rioFd
set
Read
,
rioFdRead
,
rioFd
set
Write
,
rioFdWrite
,
rioFd
set
Tell
,
rioFdTell
,
rioFd
set
Flush
,
rioFdFlush
,
NULL
,
/* update_checksum */
NULL
,
/* update_checksum */
0
,
/* current checksum */
0
,
/* current checksum */
0
,
/* flags */
0
,
/* flags */
...
@@ -383,24 +365,16 @@ static const rio rioFdsetIO = {
...
@@ -383,24 +365,16 @@ static const rio rioFdsetIO = {
{
{
NULL
,
0
}
}
/* union for io-specific vars */
{
{
NULL
,
0
}
}
/* union for io-specific vars */
};
};
void
rioInitWithFdset
(
rio
*
r
,
int
*
fds
,
int
numfds
)
{
void
rioInitWithFd
(
rio
*
r
,
int
fd
)
{
int
j
;
*
r
=
rioFdIO
;
r
->
io
.
fd
.
fd
=
fd
;
*
r
=
rioFdsetIO
;
r
->
io
.
fd
.
pos
=
0
;
r
->
io
.
fdset
.
fds
=
zmalloc
(
sizeof
(
int
)
*
numfds
);
r
->
io
.
fd
.
buf
=
sdsempty
();
r
->
io
.
fdset
.
state
=
zmalloc
(
sizeof
(
int
)
*
numfds
);
memcpy
(
r
->
io
.
fdset
.
fds
,
fds
,
sizeof
(
int
)
*
numfds
);
for
(
j
=
0
;
j
<
numfds
;
j
++
)
r
->
io
.
fdset
.
state
[
j
]
=
0
;
r
->
io
.
fdset
.
numfds
=
numfds
;
r
->
io
.
fdset
.
pos
=
0
;
r
->
io
.
fdset
.
buf
=
sdsempty
();
}
}
/* release the rio stream. */
/* release the rio stream. */
void
rioFreeFdset
(
rio
*
r
)
{
void
rioFreeFd
(
rio
*
r
)
{
zfree
(
r
->
io
.
fdset
.
fds
);
sdsfree
(
r
->
io
.
fd
.
buf
);
zfree
(
r
->
io
.
fdset
.
state
);
sdsfree
(
r
->
io
.
fdset
.
buf
);
}
}
/* ---------------------------- Generic functions ---------------------------- */
/* ---------------------------- Generic functions ---------------------------- */
...
...
src/rio.h
View file @
efb6495a
...
@@ -35,6 +35,7 @@
...
@@ -35,6 +35,7 @@
#include <stdio.h>
#include <stdio.h>
#include <stdint.h>
#include <stdint.h>
#include "sds.h"
#include "sds.h"
#include "connection.h"
#define RIO_FLAG_READ_ERROR (1<<0)
#define RIO_FLAG_READ_ERROR (1<<0)
#define RIO_FLAG_WRITE_ERROR (1<<1)
#define RIO_FLAG_WRITE_ERROR (1<<1)
...
@@ -76,22 +77,20 @@ struct _rio {
...
@@ -76,22 +77,20 @@ struct _rio {
off_t
buffered
;
/* Bytes written since last fsync. */
off_t
buffered
;
/* Bytes written since last fsync. */
off_t
autosync
;
/* fsync after 'autosync' bytes written. */
off_t
autosync
;
/* fsync after 'autosync' bytes written. */
}
file
;
}
file
;
/*
file descriptor
*/
/*
Connection object (used to read from socket)
*/
struct
{
struct
{
int
fd
;
/* File descriptor.
*/
connection
*
conn
;
/* Connection
*/
off_t
pos
;
/* pos in buf that was returned */
off_t
pos
;
/* pos in buf that was returned */
sds
buf
;
/* buffered data */
sds
buf
;
/* buffered data */
size_t
read_limit
;
/* don't allow to buffer/read more than that */
size_t
read_limit
;
/* don't allow to buffer/read more than that */
size_t
read_so_far
;
/* amount of data read from the rio (not buffered) */
size_t
read_so_far
;
/* amount of data read from the rio (not buffered) */
}
fd
;
}
conn
;
/*
Multiple
FD
s
target (used to write to
N sockets
). */
/* FD target (used to write to
pipe
). */
struct
{
struct
{
int
*
fds
;
/* File descriptors. */
int
fd
;
/* File descriptor. */
int
*
state
;
/* Error state of each fd. 0 (if ok) or errno. */
int
numfds
;
off_t
pos
;
off_t
pos
;
sds
buf
;
sds
buf
;
}
fd
set
;
}
fd
;
}
io
;
}
io
;
};
};
...
@@ -159,11 +158,11 @@ static inline void rioClearErrors(rio *r) {
...
@@ -159,11 +158,11 @@ static inline void rioClearErrors(rio *r) {
void
rioInitWithFile
(
rio
*
r
,
FILE
*
fp
);
void
rioInitWithFile
(
rio
*
r
,
FILE
*
fp
);
void
rioInitWithBuffer
(
rio
*
r
,
sds
s
);
void
rioInitWithBuffer
(
rio
*
r
,
sds
s
);
void
rioInitWith
Fd
(
rio
*
r
,
int
fd
,
size_t
read_limit
);
void
rioInitWith
Conn
(
rio
*
r
,
connection
*
conn
,
size_t
read_limit
);
void
rioInitWithFd
set
(
rio
*
r
,
int
*
fd
s
,
int
numfds
);
void
rioInitWithFd
(
rio
*
r
,
int
fd
);
void
rioFreeFd
set
(
rio
*
r
);
void
rioFreeFd
(
rio
*
r
);
void
rioFree
Fd
(
rio
*
r
,
sds
*
out_remainingBufferedData
);
void
rioFree
Conn
(
rio
*
r
,
sds
*
out_remainingBufferedData
);
size_t
rioWriteBulkCount
(
rio
*
r
,
char
prefix
,
long
count
);
size_t
rioWriteBulkCount
(
rio
*
r
,
char
prefix
,
long
count
);
size_t
rioWriteBulkString
(
rio
*
r
,
const
char
*
buf
,
size_t
len
);
size_t
rioWriteBulkString
(
rio
*
r
,
const
char
*
buf
,
size_t
len
);
...
...
src/scripting.c
View file @
efb6495a
...
@@ -61,7 +61,7 @@ sds ldbCatStackValue(sds s, lua_State *lua, int idx);
...
@@ -61,7 +61,7 @@ sds ldbCatStackValue(sds s, lua_State *lua, int idx);
#define LDB_BREAKPOINTS_MAX 64
/* Max number of breakpoints. */
#define LDB_BREAKPOINTS_MAX 64
/* Max number of breakpoints. */
#define LDB_MAX_LEN_DEFAULT 256
/* Default len limit for replies / var dumps. */
#define LDB_MAX_LEN_DEFAULT 256
/* Default len limit for replies / var dumps. */
struct
ldbState
{
struct
ldbState
{
int
fd
;
/* Socket
of the debugging client. */
connection
*
conn
;
/* Connection
of the debugging client. */
int
active
;
/* Are we debugging EVAL right now? */
int
active
;
/* Are we debugging EVAL right now? */
int
forked
;
/* Is this a fork()ed debugging session? */
int
forked
;
/* Is this a fork()ed debugging session? */
list
*
logs
;
/* List of messages to send to the client. */
list
*
logs
;
/* List of messages to send to the client. */
...
@@ -1243,7 +1243,7 @@ void scriptingInit(int setup) {
...
@@ -1243,7 +1243,7 @@ void scriptingInit(int setup) {
* Note: there is no need to create it again when this function is called
* Note: there is no need to create it again when this function is called
* by scriptingReset(). */
* by scriptingReset(). */
if
(
server
.
lua_client
==
NULL
)
{
if
(
server
.
lua_client
==
NULL
)
{
server
.
lua_client
=
createClient
(
-
1
);
server
.
lua_client
=
createClient
(
NULL
);
server
.
lua_client
->
flags
|=
CLIENT_LUA
;
server
.
lua_client
->
flags
|=
CLIENT_LUA
;
}
}
...
@@ -1734,7 +1734,7 @@ NULL
...
@@ -1734,7 +1734,7 @@ NULL
/* Initialize Lua debugger data structures. */
/* Initialize Lua debugger data structures. */
void
ldbInit
(
void
)
{
void
ldbInit
(
void
)
{
ldb
.
fd
=
-
1
;
ldb
.
conn
=
NULL
;
ldb
.
active
=
0
;
ldb
.
active
=
0
;
ldb
.
logs
=
listCreate
();
ldb
.
logs
=
listCreate
();
listSetFreeMethod
(
ldb
.
logs
,(
void
(
*
)(
void
*
))
sdsfree
);
listSetFreeMethod
(
ldb
.
logs
,(
void
(
*
)(
void
*
))
sdsfree
);
...
@@ -1756,7 +1756,7 @@ void ldbFlushLog(list *log) {
...
@@ -1756,7 +1756,7 @@ void ldbFlushLog(list *log) {
void
ldbEnable
(
client
*
c
)
{
void
ldbEnable
(
client
*
c
)
{
c
->
flags
|=
CLIENT_LUA_DEBUG
;
c
->
flags
|=
CLIENT_LUA_DEBUG
;
ldbFlushLog
(
ldb
.
logs
);
ldbFlushLog
(
ldb
.
logs
);
ldb
.
fd
=
c
->
fd
;
ldb
.
conn
=
c
->
conn
;
ldb
.
step
=
1
;
ldb
.
step
=
1
;
ldb
.
bpcount
=
0
;
ldb
.
bpcount
=
0
;
ldb
.
luabp
=
0
;
ldb
.
luabp
=
0
;
...
@@ -1811,7 +1811,7 @@ void ldbSendLogs(void) {
...
@@ -1811,7 +1811,7 @@ void ldbSendLogs(void) {
proto
=
sdscatlen
(
proto
,
"
\r\n
"
,
2
);
proto
=
sdscatlen
(
proto
,
"
\r\n
"
,
2
);
listDelNode
(
ldb
.
logs
,
ln
);
listDelNode
(
ldb
.
logs
,
ln
);
}
}
if
(
w
rite
(
ldb
.
fd
,
proto
,
sdslen
(
proto
))
==
-
1
)
{
if
(
connW
rite
(
ldb
.
conn
,
proto
,
sdslen
(
proto
))
==
-
1
)
{
/* Avoid warning. We don't check the return value of write()
/* Avoid warning. We don't check the return value of write()
* since the next read() will catch the I/O error and will
* since the next read() will catch the I/O error and will
* close the debugging session. */
* close the debugging session. */
...
@@ -1863,8 +1863,8 @@ int ldbStartSession(client *c) {
...
@@ -1863,8 +1863,8 @@ int ldbStartSession(client *c) {
}
}
/* Setup our debugging session. */
/* Setup our debugging session. */
anet
Block
(
NULL
,
ldb
.
fd
);
conn
Block
(
ldb
.
conn
);
anet
SendTimeout
(
NULL
,
ldb
.
fd
,
5000
);
conn
SendTimeout
(
ldb
.
conn
,
5000
);
ldb
.
active
=
1
;
ldb
.
active
=
1
;
/* First argument of EVAL is the script itself. We split it into different
/* First argument of EVAL is the script itself. We split it into different
...
@@ -1891,7 +1891,7 @@ void ldbEndSession(client *c) {
...
@@ -1891,7 +1891,7 @@ void ldbEndSession(client *c) {
/* If it's a fork()ed session, we just exit. */
/* If it's a fork()ed session, we just exit. */
if
(
ldb
.
forked
)
{
if
(
ldb
.
forked
)
{
writeToClient
(
c
->
fd
,
c
,
0
);
writeToClient
(
c
,
0
);
serverLog
(
LL_WARNING
,
"Lua debugging session child exiting"
);
serverLog
(
LL_WARNING
,
"Lua debugging session child exiting"
);
exitFromChild
(
0
);
exitFromChild
(
0
);
}
else
{
}
else
{
...
@@ -1900,8 +1900,8 @@ void ldbEndSession(client *c) {
...
@@ -1900,8 +1900,8 @@ void ldbEndSession(client *c) {
}
}
/* Otherwise let's restore client's state. */
/* Otherwise let's restore client's state. */
anet
NonBlock
(
NULL
,
ldb
.
fd
);
conn
NonBlock
(
ldb
.
conn
);
anet
SendTimeout
(
NULL
,
ldb
.
fd
,
0
);
conn
SendTimeout
(
ldb
.
conn
,
0
);
/* Close the client connectin after sending the final EVAL reply
/* Close the client connectin after sending the final EVAL reply
* in order to signal the end of the debugging session. */
* in order to signal the end of the debugging session. */
...
@@ -2538,7 +2538,7 @@ int ldbRepl(lua_State *lua) {
...
@@ -2538,7 +2538,7 @@ int ldbRepl(lua_State *lua) {
while
(
1
)
{
while
(
1
)
{
while
((
argv
=
ldbReplParseCommand
(
&
argc
))
==
NULL
)
{
while
((
argv
=
ldbReplParseCommand
(
&
argc
))
==
NULL
)
{
char
buf
[
1024
];
char
buf
[
1024
];
int
nread
=
r
ead
(
ldb
.
fd
,
buf
,
sizeof
(
buf
));
int
nread
=
connR
ead
(
ldb
.
conn
,
buf
,
sizeof
(
buf
));
if
(
nread
<=
0
)
{
if
(
nread
<=
0
)
{
/* Make sure the script runs without user input since the
/* Make sure the script runs without user input since the
* client is no longer connected. */
* client is no longer connected. */
...
...
src/sentinel.c
View file @
efb6495a
...
@@ -30,6 +30,10 @@
...
@@ -30,6 +30,10 @@
#include "server.h"
#include "server.h"
#include "hiredis.h"
#include "hiredis.h"
#ifdef USE_OPENSSL
#include "openssl/ssl.h"
#include "hiredis_ssl.h"
#endif
#include "async.h"
#include "async.h"
#include <ctype.h>
#include <ctype.h>
...
@@ -40,6 +44,10 @@
...
@@ -40,6 +44,10 @@
extern
char
**
environ
;
extern
char
**
environ
;
#ifdef USE_OPENSSL
extern
SSL_CTX
*
redis_tls_ctx
;
#endif
#define REDIS_SENTINEL_PORT 26379
#define REDIS_SENTINEL_PORT 26379
/* ======================== Sentinel global state =========================== */
/* ======================== Sentinel global state =========================== */
...
@@ -1995,6 +2003,19 @@ void sentinelSetClientName(sentinelRedisInstance *ri, redisAsyncContext *c, char
...
@@ -1995,6 +2003,19 @@ void sentinelSetClientName(sentinelRedisInstance *ri, redisAsyncContext *c, char
}
}
}
}
static
int
instanceLinkNegotiateTLS
(
redisAsyncContext
*
context
)
{
#ifndef USE_OPENSSL
(
void
)
context
;
#else
if
(
!
redis_tls_ctx
)
return
C_ERR
;
SSL
*
ssl
=
SSL_new
(
redis_tls_ctx
);
if
(
!
ssl
)
return
C_ERR
;
if
(
redisInitiateSSL
(
&
context
->
c
,
ssl
)
==
REDIS_ERR
)
return
C_ERR
;
#endif
return
C_OK
;
}
/* Create the async connections for the instance link if the link
/* Create the async connections for the instance link if the link
* is disconnected. Note that link->disconnected is true even if just
* is disconnected. Note that link->disconnected is true even if just
* one of the two links (commands and pub/sub) is missing. */
* one of the two links (commands and pub/sub) is missing. */
...
@@ -2010,7 +2031,11 @@ void sentinelReconnectInstance(sentinelRedisInstance *ri) {
...
@@ -2010,7 +2031,11 @@ void sentinelReconnectInstance(sentinelRedisInstance *ri) {
/* Commands connection. */
/* Commands connection. */
if
(
link
->
cc
==
NULL
)
{
if
(
link
->
cc
==
NULL
)
{
link
->
cc
=
redisAsyncConnectBind
(
ri
->
addr
->
ip
,
ri
->
addr
->
port
,
NET_FIRST_BIND_ADDR
);
link
->
cc
=
redisAsyncConnectBind
(
ri
->
addr
->
ip
,
ri
->
addr
->
port
,
NET_FIRST_BIND_ADDR
);
if
(
link
->
cc
->
err
)
{
if
(
!
link
->
cc
->
err
&&
server
.
tls_replication
&&
(
instanceLinkNegotiateTLS
(
link
->
cc
)
==
C_ERR
))
{
sentinelEvent
(
LL_DEBUG
,
"-cmd-link-reconnection"
,
ri
,
"%@ #Failed to initialize TLS"
);
instanceLinkCloseConnection
(
link
,
link
->
cc
);
}
else
if
(
link
->
cc
->
err
)
{
sentinelEvent
(
LL_DEBUG
,
"-cmd-link-reconnection"
,
ri
,
"%@ #%s"
,
sentinelEvent
(
LL_DEBUG
,
"-cmd-link-reconnection"
,
ri
,
"%@ #%s"
,
link
->
cc
->
errstr
);
link
->
cc
->
errstr
);
instanceLinkCloseConnection
(
link
,
link
->
cc
);
instanceLinkCloseConnection
(
link
,
link
->
cc
);
...
@@ -2033,7 +2058,10 @@ void sentinelReconnectInstance(sentinelRedisInstance *ri) {
...
@@ -2033,7 +2058,10 @@ void sentinelReconnectInstance(sentinelRedisInstance *ri) {
/* Pub / Sub */
/* Pub / Sub */
if
((
ri
->
flags
&
(
SRI_MASTER
|
SRI_SLAVE
))
&&
link
->
pc
==
NULL
)
{
if
((
ri
->
flags
&
(
SRI_MASTER
|
SRI_SLAVE
))
&&
link
->
pc
==
NULL
)
{
link
->
pc
=
redisAsyncConnectBind
(
ri
->
addr
->
ip
,
ri
->
addr
->
port
,
NET_FIRST_BIND_ADDR
);
link
->
pc
=
redisAsyncConnectBind
(
ri
->
addr
->
ip
,
ri
->
addr
->
port
,
NET_FIRST_BIND_ADDR
);
if
(
link
->
pc
->
err
)
{
if
(
!
link
->
pc
->
err
&&
server
.
tls_replication
&&
(
instanceLinkNegotiateTLS
(
link
->
pc
)
==
C_ERR
))
{
sentinelEvent
(
LL_DEBUG
,
"-pubsub-link-reconnection"
,
ri
,
"%@ #Failed to initialize TLS"
);
}
else
if
(
link
->
pc
->
err
)
{
sentinelEvent
(
LL_DEBUG
,
"-pubsub-link-reconnection"
,
ri
,
"%@ #%s"
,
sentinelEvent
(
LL_DEBUG
,
"-pubsub-link-reconnection"
,
ri
,
"%@ #%s"
,
link
->
pc
->
errstr
);
link
->
pc
->
errstr
);
instanceLinkCloseConnection
(
link
,
link
->
pc
);
instanceLinkCloseConnection
(
link
,
link
->
pc
);
...
@@ -2584,8 +2612,9 @@ int sentinelSendHello(sentinelRedisInstance *ri) {
...
@@ -2584,8 +2612,9 @@ int sentinelSendHello(sentinelRedisInstance *ri) {
return
C_ERR
;
return
C_ERR
;
announce_ip
=
ip
;
announce_ip
=
ip
;
}
}
announce_port
=
sentinel
.
announce_port
?
if
(
sentinel
.
announce_port
)
announce_port
=
sentinel
.
announce_port
;
sentinel
.
announce_port
:
server
.
port
;
else
if
(
server
.
tls_replication
&&
server
.
tls_port
)
announce_port
=
server
.
tls_port
;
else
announce_port
=
server
.
port
;
/* Format and send the Hello message. */
/* Format and send the Hello message. */
snprintf
(
payload
,
sizeof
(
payload
),
snprintf
(
payload
,
sizeof
(
payload
),
...
...
src/server.c
View file @
efb6495a
...
@@ -1752,6 +1752,62 @@ void updateCachedTime(void) {
...
@@ -1752,6 +1752,62 @@ void updateCachedTime(void) {
server
.
daylight_active
=
tm
.
tm_isdst
;
server
.
daylight_active
=
tm
.
tm_isdst
;
}
}
void
checkChildrenDone
(
void
)
{
int
statloc
;
pid_t
pid
;
/* If we have a diskless rdb child (note that we support only one concurrent
* child), we want to avoid collecting it's exit status and acting on it
* as long as we didn't finish to drain the pipe, since then we're at risk
* of starting a new fork and a new pipe before we're done with the previous
* one. */
if
(
server
.
rdb_child_pid
!=
-
1
&&
server
.
rdb_pipe_conns
)
return
;
if
((
pid
=
wait3
(
&
statloc
,
WNOHANG
,
NULL
))
!=
0
)
{
int
exitcode
=
WEXITSTATUS
(
statloc
);
int
bysignal
=
0
;
if
(
WIFSIGNALED
(
statloc
))
bysignal
=
WTERMSIG
(
statloc
);
/* sigKillChildHandler catches the signal and calls exit(), but we
* must make sure not to flag lastbgsave_status, etc incorrectly.
* We could directly terminate the child process via SIGUSR1
* without handling it, but in this case Valgrind will log an
* annoying error. */
if
(
exitcode
==
SERVER_CHILD_NOERROR_RETVAL
)
{
bysignal
=
SIGUSR1
;
exitcode
=
1
;
}
if
(
pid
==
-
1
)
{
serverLog
(
LL_WARNING
,
"wait3() returned an error: %s. "
"rdb_child_pid = %d, aof_child_pid = %d, module_child_pid = %d"
,
strerror
(
errno
),
(
int
)
server
.
rdb_child_pid
,
(
int
)
server
.
aof_child_pid
,
(
int
)
server
.
module_child_pid
);
}
else
if
(
pid
==
server
.
rdb_child_pid
)
{
backgroundSaveDoneHandler
(
exitcode
,
bysignal
);
if
(
!
bysignal
&&
exitcode
==
0
)
receiveChildInfo
();
}
else
if
(
pid
==
server
.
aof_child_pid
)
{
backgroundRewriteDoneHandler
(
exitcode
,
bysignal
);
if
(
!
bysignal
&&
exitcode
==
0
)
receiveChildInfo
();
}
else
if
(
pid
==
server
.
module_child_pid
)
{
ModuleForkDoneHandler
(
exitcode
,
bysignal
);
if
(
!
bysignal
&&
exitcode
==
0
)
receiveChildInfo
();
}
else
{
if
(
!
ldbRemoveChild
(
pid
))
{
serverLog
(
LL_WARNING
,
"Warning, detected child with unmatched pid: %ld"
,
(
long
)
pid
);
}
}
updateDictResizePolicy
();
closeChildInfoPipe
();
}
}
/* This is our timer interrupt, called server.hz times per second.
/* This is our timer interrupt, called server.hz times per second.
* Here is where we do a number of things that need to be done asynchronously.
* Here is where we do a number of things that need to be done asynchronously.
* For instance:
* For instance:
...
@@ -1903,51 +1959,7 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
...
@@ -1903,51 +1959,7 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
/* Check if a background saving or AOF rewrite in progress terminated. */
/* Check if a background saving or AOF rewrite in progress terminated. */
if
(
hasActiveChildProcess
()
||
ldbPendingChildren
())
if
(
hasActiveChildProcess
()
||
ldbPendingChildren
())
{
{
int
statloc
;
checkChildrenDone
();
pid_t
pid
;
if
((
pid
=
wait3
(
&
statloc
,
WNOHANG
,
NULL
))
!=
0
)
{
int
exitcode
=
WEXITSTATUS
(
statloc
);
int
bysignal
=
0
;
if
(
WIFSIGNALED
(
statloc
))
bysignal
=
WTERMSIG
(
statloc
);
/* sigKillChildHandler catches the signal and calls exit(), but we
* must make sure not to flag lastbgsave_status, etc incorrectly.
* We could directly terminate the child process via SIGUSR1
* without handling it, but in this case Valgrind will log an
* annoying error. */
if
(
exitcode
==
SERVER_CHILD_NOERROR_RETVAL
)
{
bysignal
=
SIGUSR1
;
exitcode
=
1
;
}
if
(
pid
==
-
1
)
{
serverLog
(
LL_WARNING
,
"wait3() returned an error: %s. "
"rdb_child_pid = %d, aof_child_pid = %d, module_child_pid = %d"
,
strerror
(
errno
),
(
int
)
server
.
rdb_child_pid
,
(
int
)
server
.
aof_child_pid
,
(
int
)
server
.
module_child_pid
);
}
else
if
(
pid
==
server
.
rdb_child_pid
)
{
backgroundSaveDoneHandler
(
exitcode
,
bysignal
);
if
(
!
bysignal
&&
exitcode
==
0
)
receiveChildInfo
();
}
else
if
(
pid
==
server
.
aof_child_pid
)
{
backgroundRewriteDoneHandler
(
exitcode
,
bysignal
);
if
(
!
bysignal
&&
exitcode
==
0
)
receiveChildInfo
();
}
else
if
(
pid
==
server
.
module_child_pid
)
{
ModuleForkDoneHandler
(
exitcode
,
bysignal
);
if
(
!
bysignal
&&
exitcode
==
0
)
receiveChildInfo
();
}
else
{
if
(
!
ldbRemoveChild
(
pid
))
{
serverLog
(
LL_WARNING
,
"Warning, detected child with unmatched pid: %ld"
,
(
long
)
pid
);
}
}
updateDictResizePolicy
();
closeChildInfoPipe
();
}
}
else
{
}
else
{
/* If there is not a background saving/rewrite in progress check if
/* If there is not a background saving/rewrite in progress check if
* we have to save/rewrite now. */
* we have to save/rewrite now. */
...
@@ -2054,6 +2066,11 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
...
@@ -2054,6 +2066,11 @@ int serverCron(struct aeEventLoop *eventLoop, long long id, void *clientData) {
void
beforeSleep
(
struct
aeEventLoop
*
eventLoop
)
{
void
beforeSleep
(
struct
aeEventLoop
*
eventLoop
)
{
UNUSED
(
eventLoop
);
UNUSED
(
eventLoop
);
/* Handle TLS pending data. (must be done before flushAppendOnlyFile) */
tlsProcessPendingData
();
/* If tls still has pending unread data don't sleep at all. */
aeSetDontWait
(
server
.
el
,
tlsHasPendingData
());
/* Call the Redis Cluster before sleep function. Note that this function
/* Call the Redis Cluster before sleep function. Note that this function
* may change the state of Redis Cluster (from ok to fail or vice versa),
* may change the state of Redis Cluster (from ok to fail or vice versa),
* so it's a good idea to call it before serving the unblocked clients
* so it's a good idea to call it before serving the unblocked clients
...
@@ -2247,11 +2264,13 @@ void initServerConfig(void) {
...
@@ -2247,11 +2264,13 @@ void initServerConfig(void) {
server
.
dynamic_hz
=
CONFIG_DEFAULT_DYNAMIC_HZ
;
server
.
dynamic_hz
=
CONFIG_DEFAULT_DYNAMIC_HZ
;
server
.
arch_bits
=
(
sizeof
(
long
)
==
8
)
?
64
:
32
;
server
.
arch_bits
=
(
sizeof
(
long
)
==
8
)
?
64
:
32
;
server
.
port
=
CONFIG_DEFAULT_SERVER_PORT
;
server
.
port
=
CONFIG_DEFAULT_SERVER_PORT
;
server
.
tls_port
=
CONFIG_DEFAULT_SERVER_TLS_PORT
;
server
.
tcp_backlog
=
CONFIG_DEFAULT_TCP_BACKLOG
;
server
.
tcp_backlog
=
CONFIG_DEFAULT_TCP_BACKLOG
;
server
.
bindaddr_count
=
0
;
server
.
bindaddr_count
=
0
;
server
.
unixsocket
=
NULL
;
server
.
unixsocket
=
NULL
;
server
.
unixsocketperm
=
CONFIG_DEFAULT_UNIX_SOCKET_PERM
;
server
.
unixsocketperm
=
CONFIG_DEFAULT_UNIX_SOCKET_PERM
;
server
.
ipfd_count
=
0
;
server
.
ipfd_count
=
0
;
server
.
tlsfd_count
=
0
;
server
.
sofd
=
-
1
;
server
.
sofd
=
-
1
;
server
.
protected_mode
=
CONFIG_DEFAULT_PROTECTED_MODE
;
server
.
protected_mode
=
CONFIG_DEFAULT_PROTECTED_MODE
;
server
.
gopher_enabled
=
CONFIG_DEFAULT_GOPHER_ENABLED
;
server
.
gopher_enabled
=
CONFIG_DEFAULT_GOPHER_ENABLED
;
...
@@ -2286,6 +2305,7 @@ void initServerConfig(void) {
...
@@ -2286,6 +2305,7 @@ void initServerConfig(void) {
server
.
aof_rewrite_min_size
=
AOF_REWRITE_MIN_SIZE
;
server
.
aof_rewrite_min_size
=
AOF_REWRITE_MIN_SIZE
;
server
.
aof_rewrite_base_size
=
0
;
server
.
aof_rewrite_base_size
=
0
;
server
.
aof_rewrite_scheduled
=
0
;
server
.
aof_rewrite_scheduled
=
0
;
server
.
aof_flush_sleep
=
0
;
server
.
aof_last_fsync
=
time
(
NULL
);
server
.
aof_last_fsync
=
time
(
NULL
);
server
.
aof_rewrite_time_last
=
-
1
;
server
.
aof_rewrite_time_last
=
-
1
;
server
.
aof_rewrite_time_start
=
-
1
;
server
.
aof_rewrite_time_start
=
-
1
;
...
@@ -2297,6 +2317,7 @@ void initServerConfig(void) {
...
@@ -2297,6 +2317,7 @@ void initServerConfig(void) {
server
.
aof_rewrite_incremental_fsync
=
CONFIG_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC
;
server
.
aof_rewrite_incremental_fsync
=
CONFIG_DEFAULT_AOF_REWRITE_INCREMENTAL_FSYNC
;
server
.
rdb_save_incremental_fsync
=
CONFIG_DEFAULT_RDB_SAVE_INCREMENTAL_FSYNC
;
server
.
rdb_save_incremental_fsync
=
CONFIG_DEFAULT_RDB_SAVE_INCREMENTAL_FSYNC
;
server
.
rdb_key_save_delay
=
CONFIG_DEFAULT_RDB_KEY_SAVE_DELAY
;
server
.
rdb_key_save_delay
=
CONFIG_DEFAULT_RDB_KEY_SAVE_DELAY
;
server
.
key_load_delay
=
CONFIG_DEFAULT_KEY_LOAD_DELAY
;
server
.
aof_load_truncated
=
CONFIG_DEFAULT_AOF_LOAD_TRUNCATED
;
server
.
aof_load_truncated
=
CONFIG_DEFAULT_AOF_LOAD_TRUNCATED
;
server
.
aof_use_rdb_preamble
=
CONFIG_DEFAULT_AOF_USE_RDB_PREAMBLE
;
server
.
aof_use_rdb_preamble
=
CONFIG_DEFAULT_AOF_USE_RDB_PREAMBLE
;
server
.
pidfile
=
NULL
;
server
.
pidfile
=
NULL
;
...
@@ -2368,7 +2389,7 @@ void initServerConfig(void) {
...
@@ -2368,7 +2389,7 @@ void initServerConfig(void) {
server
.
repl_state
=
REPL_STATE_NONE
;
server
.
repl_state
=
REPL_STATE_NONE
;
server
.
repl_transfer_tmpfile
=
NULL
;
server
.
repl_transfer_tmpfile
=
NULL
;
server
.
repl_transfer_fd
=
-
1
;
server
.
repl_transfer_fd
=
-
1
;
server
.
repl_transfer_s
=
-
1
;
server
.
repl_transfer_s
=
NULL
;
server
.
repl_syncio_timeout
=
CONFIG_REPL_SYNCIO_TIMEOUT
;
server
.
repl_syncio_timeout
=
CONFIG_REPL_SYNCIO_TIMEOUT
;
server
.
repl_serve_stale_data
=
CONFIG_DEFAULT_SLAVE_SERVE_STALE_DATA
;
server
.
repl_serve_stale_data
=
CONFIG_DEFAULT_SLAVE_SERVE_STALE_DATA
;
server
.
repl_slave_ro
=
CONFIG_DEFAULT_SLAVE_READ_ONLY
;
server
.
repl_slave_ro
=
CONFIG_DEFAULT_SLAVE_READ_ONLY
;
...
@@ -2765,6 +2786,11 @@ void initServer(void) {
...
@@ -2765,6 +2786,11 @@ void initServer(void) {
server
.
clients_paused
=
0
;
server
.
clients_paused
=
0
;
server
.
system_memory_size
=
zmalloc_get_memory_size
();
server
.
system_memory_size
=
zmalloc_get_memory_size
();
if
(
server
.
tls_port
&&
tlsConfigure
(
&
server
.
tls_ctx_config
)
==
C_ERR
)
{
serverLog
(
LL_WARNING
,
"Failed to configure TLS. Check logs for more info."
);
exit
(
1
);
}
createSharedObjects
();
createSharedObjects
();
adjustOpenFilesLimit
();
adjustOpenFilesLimit
();
server
.
el
=
aeCreateEventLoop
(
server
.
maxclients
+
CONFIG_FDSET_INCR
);
server
.
el
=
aeCreateEventLoop
(
server
.
maxclients
+
CONFIG_FDSET_INCR
);
...
@@ -2780,6 +2806,9 @@ void initServer(void) {
...
@@ -2780,6 +2806,9 @@ void initServer(void) {
if
(
server
.
port
!=
0
&&
if
(
server
.
port
!=
0
&&
listenToPort
(
server
.
port
,
server
.
ipfd
,
&
server
.
ipfd_count
)
==
C_ERR
)
listenToPort
(
server
.
port
,
server
.
ipfd
,
&
server
.
ipfd_count
)
==
C_ERR
)
exit
(
1
);
exit
(
1
);
if
(
server
.
tls_port
!=
0
&&
listenToPort
(
server
.
tls_port
,
server
.
tlsfd
,
&
server
.
tlsfd_count
)
==
C_ERR
)
exit
(
1
);
/* Open the listening Unix domain socket. */
/* Open the listening Unix domain socket. */
if
(
server
.
unixsocket
!=
NULL
)
{
if
(
server
.
unixsocket
!=
NULL
)
{
...
@@ -2794,7 +2823,7 @@ void initServer(void) {
...
@@ -2794,7 +2823,7 @@ void initServer(void) {
}
}
/* Abort if there are no listening sockets at all. */
/* Abort if there are no listening sockets at all. */
if
(
server
.
ipfd_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."
);
serverLog
(
LL_WARNING
,
"Configured to not listen anywhere, exiting."
);
exit
(
1
);
exit
(
1
);
}
}
...
@@ -2820,6 +2849,11 @@ void initServer(void) {
...
@@ -2820,6 +2849,11 @@ void initServer(void) {
server
.
aof_child_pid
=
-
1
;
server
.
aof_child_pid
=
-
1
;
server
.
module_child_pid
=
-
1
;
server
.
module_child_pid
=
-
1
;
server
.
rdb_child_type
=
RDB_CHILD_TYPE_NONE
;
server
.
rdb_child_type
=
RDB_CHILD_TYPE_NONE
;
server
.
rdb_pipe_conns
=
NULL
;
server
.
rdb_pipe_numconns
=
0
;
server
.
rdb_pipe_numconns_writing
=
0
;
server
.
rdb_pipe_buff
=
NULL
;
server
.
rdb_pipe_bufflen
=
0
;
server
.
rdb_bgsave_scheduled
=
0
;
server
.
rdb_bgsave_scheduled
=
0
;
server
.
child_info_pipe
[
0
]
=
-
1
;
server
.
child_info_pipe
[
0
]
=
-
1
;
server
.
child_info_pipe
[
1
]
=
-
1
;
server
.
child_info_pipe
[
1
]
=
-
1
;
...
@@ -2866,6 +2900,14 @@ void initServer(void) {
...
@@ -2866,6 +2900,14 @@ void initServer(void) {
"Unrecoverable error creating server.ipfd file event."
);
"Unrecoverable error creating server.ipfd file event."
);
}
}
}
}
for
(
j
=
0
;
j
<
server
.
tlsfd_count
;
j
++
)
{
if
(
aeCreateFileEvent
(
server
.
el
,
server
.
tlsfd
[
j
],
AE_READABLE
,
acceptTLSHandler
,
NULL
)
==
AE_ERR
)
{
serverPanic
(
"Unrecoverable error creating server.tlsfd file event."
);
}
}
if
(
server
.
sofd
>
0
&&
aeCreateFileEvent
(
server
.
el
,
server
.
sofd
,
AE_READABLE
,
if
(
server
.
sofd
>
0
&&
aeCreateFileEvent
(
server
.
el
,
server
.
sofd
,
AE_READABLE
,
acceptUnixHandler
,
NULL
)
==
AE_ERR
)
serverPanic
(
"Unrecoverable error creating server.sofd file event."
);
acceptUnixHandler
,
NULL
)
==
AE_ERR
)
serverPanic
(
"Unrecoverable error creating server.sofd file event."
);
...
@@ -3570,6 +3612,7 @@ void closeListeningSockets(int unlink_unix_socket) {
...
@@ -3570,6 +3612,7 @@ void closeListeningSockets(int unlink_unix_socket) {
int
j
;
int
j
;
for
(
j
=
0
;
j
<
server
.
ipfd_count
;
j
++
)
close
(
server
.
ipfd
[
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
]);
if
(
server
.
sofd
!=
-
1
)
close
(
server
.
sofd
);
if
(
server
.
sofd
!=
-
1
)
close
(
server
.
sofd
);
if
(
server
.
cluster_enabled
)
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
[
j
]);
...
@@ -3940,7 +3983,7 @@ sds genRedisInfoString(char *section) {
...
@@ -3940,7 +3983,7 @@ sds genRedisInfoString(char *section) {
#endif
#endif
(
int64_t
)
getpid
(),
(
int64_t
)
getpid
(),
server
.
runid
,
server
.
runid
,
server
.
port
,
server
.
port
?
server
.
port
:
server
.
tls_port
,
(
int64_t
)
uptime
,
(
int64_t
)
uptime
,
(
int64_t
)(
uptime
/
(
3600
*
24
)),
(
int64_t
)(
uptime
/
(
3600
*
24
)),
server
.
hz
,
server
.
hz
,
...
@@ -4324,7 +4367,7 @@ sds genRedisInfoString(char *section) {
...
@@ -4324,7 +4367,7 @@ sds genRedisInfoString(char *section) {
long
lag
=
0
;
long
lag
=
0
;
if
(
slaveip
[
0
]
==
'\0'
)
{
if
(
slaveip
[
0
]
==
'\0'
)
{
if
(
anet
PeerToString
(
slave
->
fd
,
ip
,
sizeof
(
ip
),
&
port
)
==
-
1
)
if
(
conn
PeerToString
(
slave
->
conn
,
ip
,
sizeof
(
ip
),
&
port
)
==
-
1
)
continue
;
continue
;
slaveip
=
ip
;
slaveip
=
ip
;
}
}
...
@@ -4578,7 +4621,7 @@ void redisAsciiArt(void) {
...
@@ -4578,7 +4621,7 @@ void redisAsciiArt(void) {
if
(
!
show_logo
)
{
if
(
!
show_logo
)
{
serverLog
(
LL_NOTICE
,
serverLog
(
LL_NOTICE
,
"Running mode=%s, port=%d."
,
"Running mode=%s, port=%d."
,
mode
,
server
.
port
mode
,
server
.
port
?
server
.
port
:
server
.
tls_port
);
);
}
else
{
}
else
{
snprintf
(
buf
,
1024
*
16
,
ascii_logo
,
snprintf
(
buf
,
1024
*
16
,
ascii_logo
,
...
@@ -4586,7 +4629,7 @@ void redisAsciiArt(void) {
...
@@ -4586,7 +4629,7 @@ void redisAsciiArt(void) {
redisGitSHA1
(),
redisGitSHA1
(),
strtol
(
redisGitDirty
(),
NULL
,
10
)
>
0
,
strtol
(
redisGitDirty
(),
NULL
,
10
)
>
0
,
(
sizeof
(
long
)
==
8
)
?
"64"
:
"32"
,
(
sizeof
(
long
)
==
8
)
?
"64"
:
"32"
,
mode
,
server
.
port
,
mode
,
server
.
port
?
server
.
port
:
server
.
tls_port
,
(
long
)
getpid
()
(
long
)
getpid
()
);
);
serverLogRaw
(
LL_NOTICE
|
LL_RAW
,
buf
);
serverLogRaw
(
LL_NOTICE
|
LL_RAW
,
buf
);
...
@@ -4769,7 +4812,7 @@ void redisSetProcTitle(char *title) {
...
@@ -4769,7 +4812,7 @@ void redisSetProcTitle(char *title) {
setproctitle
(
"%s %s:%d%s"
,
setproctitle
(
"%s %s:%d%s"
,
title
,
title
,
server
.
bindaddr_count
?
server
.
bindaddr
[
0
]
:
"*"
,
server
.
bindaddr_count
?
server
.
bindaddr
[
0
]
:
"*"
,
server
.
port
,
server
.
port
?
server
.
port
:
server
.
tls_port
,
server_mode
);
server_mode
);
#else
#else
UNUSED
(
title
);
UNUSED
(
title
);
...
@@ -4920,6 +4963,7 @@ int main(int argc, char **argv) {
...
@@ -4920,6 +4963,7 @@ int main(int argc, char **argv) {
ACLInit
();
/* The ACL subsystem must be initialized ASAP because the
ACLInit
();
/* The ACL subsystem must be initialized ASAP because the
basic networking code and client creation depends on it. */
basic networking code and client creation depends on it. */
moduleInitModulesSystem
();
moduleInitModulesSystem
();
tlsInit
();
/* Store the executable path and arguments in a safe place in order
/* Store the executable path and arguments in a safe place in order
* to be able to restart the server later. */
* to be able to restart the server later. */
...
@@ -5053,7 +5097,7 @@ int main(int argc, char **argv) {
...
@@ -5053,7 +5097,7 @@ int main(int argc, char **argv) {
exit
(
1
);
exit
(
1
);
}
}
}
}
if
(
server
.
ipfd_count
>
0
)
if
(
server
.
ipfd_count
>
0
||
server
.
tlsfd_count
>
0
)
serverLog
(
LL_NOTICE
,
"Ready to accept connections"
);
serverLog
(
LL_NOTICE
,
"Ready to accept connections"
);
if
(
server
.
sofd
>
0
)
if
(
server
.
sofd
>
0
)
serverLog
(
LL_NOTICE
,
"The server is now ready to accept connections at %s"
,
server
.
unixsocket
);
serverLog
(
LL_NOTICE
,
"The server is now ready to accept connections at %s"
,
server
.
unixsocket
);
...
...
src/server.h
View file @
efb6495a
...
@@ -66,6 +66,7 @@ typedef long long mstime_t; /* millisecond time type. */
...
@@ -66,6 +66,7 @@ typedef long long mstime_t; /* millisecond time type. */
#include "quicklist.h"
/* Lists are encoded as linked lists of
#include "quicklist.h"
/* Lists are encoded as linked lists of
N-elements flat arrays */
N-elements flat arrays */
#include "rax.h"
/* Radix tree */
#include "rax.h"
/* Radix tree */
#include "connection.h"
/* Connection abstraction */
/* Following includes allow test functions to be called from Redis main() */
/* Following includes allow test functions to be called from Redis main() */
#include "zipmap.h"
#include "zipmap.h"
...
@@ -84,6 +85,7 @@ typedef long long mstime_t; /* millisecond time type. */
...
@@ -84,6 +85,7 @@ typedef long long mstime_t; /* millisecond time type. */
#define CONFIG_MAX_HZ 500
#define CONFIG_MAX_HZ 500
#define MAX_CLIENTS_PER_CLOCK_TICK 200
/* HZ is adapted based on that. */
#define MAX_CLIENTS_PER_CLOCK_TICK 200
/* HZ is adapted based on that. */
#define CONFIG_DEFAULT_SERVER_PORT 6379
/* TCP port. */
#define CONFIG_DEFAULT_SERVER_PORT 6379
/* TCP port. */
#define CONFIG_DEFAULT_SERVER_TLS_PORT 0
/* TCP port. */
#define CONFIG_DEFAULT_TCP_BACKLOG 511
/* TCP listen backlog. */
#define CONFIG_DEFAULT_TCP_BACKLOG 511
/* TCP listen backlog. */
#define CONFIG_DEFAULT_CLIENT_TIMEOUT 0
/* Default client timeout: infinite */
#define CONFIG_DEFAULT_CLIENT_TIMEOUT 0
/* Default client timeout: infinite */
#define CONFIG_DEFAULT_DBNUM 16
#define CONFIG_DEFAULT_DBNUM 16
...
@@ -133,6 +135,7 @@ typedef long long mstime_t; /* millisecond time type. */
...
@@ -133,6 +135,7 @@ typedef long long mstime_t; /* millisecond time type. */
#define CONFIG_DEFAULT_REPL_DISKLESS_SYNC 0
#define CONFIG_DEFAULT_REPL_DISKLESS_SYNC 0
#define CONFIG_DEFAULT_REPL_DISKLESS_SYNC_DELAY 5
#define CONFIG_DEFAULT_REPL_DISKLESS_SYNC_DELAY 5
#define CONFIG_DEFAULT_RDB_KEY_SAVE_DELAY 0
#define CONFIG_DEFAULT_RDB_KEY_SAVE_DELAY 0
#define CONFIG_DEFAULT_KEY_LOAD_DELAY 0
#define CONFIG_DEFAULT_SLAVE_SERVE_STALE_DATA 1
#define CONFIG_DEFAULT_SLAVE_SERVE_STALE_DATA 1
#define CONFIG_DEFAULT_SLAVE_READ_ONLY 1
#define CONFIG_DEFAULT_SLAVE_READ_ONLY 1
#define CONFIG_DEFAULT_SLAVE_IGNORE_MAXMEMORY 1
#define CONFIG_DEFAULT_SLAVE_IGNORE_MAXMEMORY 1
...
@@ -826,7 +829,7 @@ typedef struct user {
...
@@ -826,7 +829,7 @@ typedef struct user {
* Clients are taken in a linked list. */
* Clients are taken in a linked list. */
typedef
struct
client
{
typedef
struct
client
{
uint64_t
id
;
/* Client incremental unique ID. */
uint64_t
id
;
/* Client incremental unique ID. */
int
fd
;
/* Client socket. */
connection
*
conn
;
int
resp
;
/* RESP protocol version. Can be 2 or 3. */
int
resp
;
/* RESP protocol version. Can be 2 or 3. */
redisDb
*
db
;
/* Pointer to currently SELECTed DB. */
redisDb
*
db
;
/* Pointer to currently SELECTed DB. */
robj
*
name
;
/* As set by CLIENT SETNAME. */
robj
*
name
;
/* As set by CLIENT SETNAME. */
...
@@ -1034,6 +1037,22 @@ struct malloc_stats {
...
@@ -1034,6 +1037,22 @@ struct malloc_stats {
size_t
allocator_resident
;
size_t
allocator_resident
;
};
};
/*-----------------------------------------------------------------------------
* TLS Context Configuration
*----------------------------------------------------------------------------*/
typedef
struct
redisTLSContextConfig
{
char
*
cert_file
;
char
*
key_file
;
char
*
dh_params_file
;
char
*
ca_cert_file
;
char
*
ca_cert_dir
;
char
*
protocols
;
char
*
ciphers
;
char
*
ciphersuites
;
int
prefer_server_ciphers
;
}
redisTLSContextConfig
;
/*-----------------------------------------------------------------------------
/*-----------------------------------------------------------------------------
* Global server state
* Global server state
*----------------------------------------------------------------------------*/
*----------------------------------------------------------------------------*/
...
@@ -1088,6 +1107,7 @@ struct redisServer {
...
@@ -1088,6 +1107,7 @@ struct redisServer {
pid_t
module_child_pid
;
/* PID of module child */
pid_t
module_child_pid
;
/* PID of module child */
/* Networking */
/* Networking */
int
port
;
/* TCP listening port */
int
port
;
/* TCP listening port */
int
tls_port
;
/* TLS listening port */
int
tcp_backlog
;
/* TCP listen() backlog */
int
tcp_backlog
;
/* TCP listen() backlog */
char
*
bindaddr
[
CONFIG_BINDADDR_MAX
];
/* Addresses we should bind to */
char
*
bindaddr
[
CONFIG_BINDADDR_MAX
];
/* Addresses we should bind to */
int
bindaddr_count
;
/* Number of addresses in server.bindaddr[] */
int
bindaddr_count
;
/* Number of addresses in server.bindaddr[] */
...
@@ -1095,6 +1115,8 @@ struct redisServer {
...
@@ -1095,6 +1115,8 @@ struct redisServer {
mode_t
unixsocketperm
;
/* UNIX socket permission */
mode_t
unixsocketperm
;
/* UNIX socket permission */
int
ipfd
[
CONFIG_BINDADDR_MAX
];
/* TCP socket file descriptors */
int
ipfd
[
CONFIG_BINDADDR_MAX
];
/* TCP socket file descriptors */
int
ipfd_count
;
/* Used slots in ipfd[] */
int
ipfd_count
;
/* Used slots in ipfd[] */
int
tlsfd
[
CONFIG_BINDADDR_MAX
];
/* TLS socket file descriptors */
int
tlsfd_count
;
/* Used slots in tlsfd[] */
int
sofd
;
/* Unix socket file descriptor */
int
sofd
;
/* Unix socket file descriptor */
int
cfd
[
CONFIG_BINDADDR_MAX
];
/* Cluster bus listening socket */
int
cfd
[
CONFIG_BINDADDR_MAX
];
/* Cluster bus listening socket */
int
cfd_count
;
/* Used slots in cfd[] */
int
cfd_count
;
/* Used slots in cfd[] */
...
@@ -1198,6 +1220,7 @@ struct redisServer {
...
@@ -1198,6 +1220,7 @@ struct redisServer {
off_t
aof_rewrite_base_size
;
/* AOF size on latest startup or rewrite. */
off_t
aof_rewrite_base_size
;
/* AOF size on latest startup or rewrite. */
off_t
aof_current_size
;
/* AOF current size. */
off_t
aof_current_size
;
/* AOF current size. */
off_t
aof_fsync_offset
;
/* AOF offset which is already synced to disk. */
off_t
aof_fsync_offset
;
/* AOF offset which is already synced to disk. */
int
aof_flush_sleep
;
/* Micros to sleep before flush. (used by tests) */
int
aof_rewrite_scheduled
;
/* Rewrite once BGSAVE terminates. */
int
aof_rewrite_scheduled
;
/* Rewrite once BGSAVE terminates. */
pid_t
aof_child_pid
;
/* PID if rewriting process */
pid_t
aof_child_pid
;
/* PID if rewriting process */
list
*
aof_rewrite_buf_blocks
;
/* Hold changes during an AOF rewrite. */
list
*
aof_rewrite_buf_blocks
;
/* Hold changes during an AOF rewrite. */
...
@@ -1243,10 +1266,17 @@ struct redisServer {
...
@@ -1243,10 +1266,17 @@ struct redisServer {
int
rdb_child_type
;
/* Type of save by active child. */
int
rdb_child_type
;
/* Type of save by active child. */
int
lastbgsave_status
;
/* C_OK or C_ERR */
int
lastbgsave_status
;
/* C_OK or C_ERR */
int
stop_writes_on_bgsave_err
;
/* Don't allow writes if can't BGSAVE */
int
stop_writes_on_bgsave_err
;
/* Don't allow writes if can't BGSAVE */
int
rdb_pipe_write_result_to_parent
;
/* RDB pipes used to return the state */
int
rdb_pipe_write
;
/* RDB pipes used to transfer the rdb */
int
rdb_pipe_read_result_from_child
;
/* of each slave in diskless SYNC. */
int
rdb_pipe_read
;
/* data to the parent process in diskless repl. */
connection
**
rdb_pipe_conns
;
/* Connections which are currently the */
int
rdb_pipe_numconns
;
/* target of diskless rdb fork child. */
int
rdb_pipe_numconns_writing
;
/* Number of rdb conns with pending writes. */
char
*
rdb_pipe_buff
;
/* In diskless replication, this buffer holds data */
int
rdb_pipe_bufflen
;
/* that was read from the the rdb pipe. */
int
rdb_key_save_delay
;
/* Delay in microseconds between keys while
int
rdb_key_save_delay
;
/* Delay in microseconds between keys while
* writing the RDB. (for testings) */
* writing the RDB. (for testings) */
int
key_load_delay
;
/* Delay in microseconds between keys while
* loading aof or rdb. (for testings) */
/* Pipe and data structures for child -> parent info sharing. */
/* Pipe and data structures for child -> parent info sharing. */
int
child_info_pipe
[
2
];
/* Pipe used to write the child_info_data. */
int
child_info_pipe
[
2
];
/* Pipe used to write the child_info_data. */
struct
{
struct
{
...
@@ -1299,7 +1329,7 @@ struct redisServer {
...
@@ -1299,7 +1329,7 @@ struct redisServer {
off_t
repl_transfer_size
;
/* Size of RDB to read from master during sync. */
off_t
repl_transfer_size
;
/* Size of RDB to read from master during sync. */
off_t
repl_transfer_read
;
/* Amount of RDB read from master during sync. */
off_t
repl_transfer_read
;
/* Amount of RDB read from master during sync. */
off_t
repl_transfer_last_fsync_off
;
/* Offset when we fsync-ed last time. */
off_t
repl_transfer_last_fsync_off
;
/* Offset when we fsync-ed last time. */
int
repl_transfer_s
;
/* Slave -> Master SYNC
socket
*/
connection
*
repl_transfer_s
;
/* Slave -> Master SYNC
connection
*/
int
repl_transfer_fd
;
/* Slave -> Master SYNC temp file descriptor */
int
repl_transfer_fd
;
/* Slave -> Master SYNC temp file descriptor */
char
*
repl_transfer_tmpfile
;
/* Slave-> master SYNC temp file name */
char
*
repl_transfer_tmpfile
;
/* Slave-> master SYNC temp file name */
time_t
repl_transfer_lastio
;
/* Unix time of the latest read, for timeout */
time_t
repl_transfer_lastio
;
/* Unix time of the latest read, for timeout */
...
@@ -1423,6 +1453,11 @@ struct redisServer {
...
@@ -1423,6 +1453,11 @@ struct redisServer {
int
watchdog_period
;
/* Software watchdog period in ms. 0 = off */
int
watchdog_period
;
/* Software watchdog period in ms. 0 = off */
/* System hardware info */
/* System hardware info */
size_t
system_memory_size
;
/* Total memory in system as reported by OS */
size_t
system_memory_size
;
/* Total memory in system as reported by OS */
/* TLS Configuration */
int
tls_cluster
;
int
tls_replication
;
int
tls_auth_clients
;
redisTLSContextConfig
tls_ctx_config
;
};
};
typedef
struct
pubsubPattern
{
typedef
struct
pubsubPattern
{
...
@@ -1570,12 +1605,12 @@ size_t redisPopcount(void *s, long count);
...
@@ -1570,12 +1605,12 @@ size_t redisPopcount(void *s, long count);
void
redisSetProcTitle
(
char
*
title
);
void
redisSetProcTitle
(
char
*
title
);
/* networking.c -- Networking and Client related operations */
/* networking.c -- Networking and Client related operations */
client
*
createClient
(
int
fd
);
client
*
createClient
(
connection
*
conn
);
void
closeTimedoutClients
(
void
);
void
closeTimedoutClients
(
void
);
void
freeClient
(
client
*
c
);
void
freeClient
(
client
*
c
);
void
freeClientAsync
(
client
*
c
);
void
freeClientAsync
(
client
*
c
);
void
resetClient
(
client
*
c
);
void
resetClient
(
client
*
c
);
void
sendReplyToClient
(
aeEventLoop
*
el
,
int
fd
,
void
*
privdata
,
int
mask
);
void
sendReplyToClient
(
connection
*
conn
);
void
*
addReplyDeferredLen
(
client
*
c
);
void
*
addReplyDeferredLen
(
client
*
c
);
void
setDeferredArrayLen
(
client
*
c
,
void
*
node
,
long
length
);
void
setDeferredArrayLen
(
client
*
c
,
void
*
node
,
long
length
);
void
setDeferredMapLen
(
client
*
c
,
void
*
node
,
long
length
);
void
setDeferredMapLen
(
client
*
c
,
void
*
node
,
long
length
);
...
@@ -1587,8 +1622,9 @@ void processInputBufferAndReplicate(client *c);
...
@@ -1587,8 +1622,9 @@ void processInputBufferAndReplicate(client *c);
void
processGopherRequest
(
client
*
c
);
void
processGopherRequest
(
client
*
c
);
void
acceptHandler
(
aeEventLoop
*
el
,
int
fd
,
void
*
privdata
,
int
mask
);
void
acceptHandler
(
aeEventLoop
*
el
,
int
fd
,
void
*
privdata
,
int
mask
);
void
acceptTcpHandler
(
aeEventLoop
*
el
,
int
fd
,
void
*
privdata
,
int
mask
);
void
acceptTcpHandler
(
aeEventLoop
*
el
,
int
fd
,
void
*
privdata
,
int
mask
);
void
acceptTLSHandler
(
aeEventLoop
*
el
,
int
fd
,
void
*
privdata
,
int
mask
);
void
acceptUnixHandler
(
aeEventLoop
*
el
,
int
fd
,
void
*
privdata
,
int
mask
);
void
acceptUnixHandler
(
aeEventLoop
*
el
,
int
fd
,
void
*
privdata
,
int
mask
);
void
readQueryFromClient
(
aeEventLoop
*
el
,
int
fd
,
void
*
privdata
,
int
mask
);
void
readQueryFromClient
(
connection
*
conn
);
void
addReplyNull
(
client
*
c
);
void
addReplyNull
(
client
*
c
);
void
addReplyNullArray
(
client
*
c
);
void
addReplyNullArray
(
client
*
c
);
void
addReplyBool
(
client
*
c
,
int
b
);
void
addReplyBool
(
client
*
c
,
int
b
);
...
@@ -1646,7 +1682,7 @@ int handleClientsWithPendingReadsUsingThreads(void);
...
@@ -1646,7 +1682,7 @@ int handleClientsWithPendingReadsUsingThreads(void);
int
stopThreadedIOIfNeeded
(
void
);
int
stopThreadedIOIfNeeded
(
void
);
int
clientHasPendingReplies
(
client
*
c
);
int
clientHasPendingReplies
(
client
*
c
);
void
unlinkClient
(
client
*
c
);
void
unlinkClient
(
client
*
c
);
int
writeToClient
(
int
fd
,
client
*
c
,
int
handler_installed
);
int
writeToClient
(
client
*
c
,
int
handler_installed
);
void
linkClient
(
client
*
c
);
void
linkClient
(
client
*
c
);
void
protectClient
(
client
*
c
);
void
protectClient
(
client
*
c
);
void
unprotectClient
(
client
*
c
);
void
unprotectClient
(
client
*
c
);
...
@@ -1782,6 +1818,8 @@ void clearReplicationId2(void);
...
@@ -1782,6 +1818,8 @@ void clearReplicationId2(void);
void
chopReplicationBacklog
(
void
);
void
chopReplicationBacklog
(
void
);
void
replicationCacheMasterUsingMyself
(
void
);
void
replicationCacheMasterUsingMyself
(
void
);
void
feedReplicationBacklog
(
void
*
ptr
,
size_t
len
);
void
feedReplicationBacklog
(
void
*
ptr
,
size_t
len
);
void
rdbPipeReadHandler
(
struct
aeEventLoop
*
eventLoop
,
int
fd
,
void
*
clientData
,
int
mask
);
void
rdbPipeWriteHandlerConnRemoved
(
struct
connection
*
conn
);
/* Generic persistence functions */
/* Generic persistence functions */
void
startLoadingFile
(
FILE
*
fp
,
char
*
filename
);
void
startLoadingFile
(
FILE
*
fp
,
char
*
filename
);
...
@@ -1954,6 +1992,7 @@ unsigned int LRU_CLOCK(void);
...
@@ -1954,6 +1992,7 @@ unsigned int LRU_CLOCK(void);
const
char
*
evictPolicyToString
(
void
);
const
char
*
evictPolicyToString
(
void
);
struct
redisMemOverhead
*
getMemoryOverheadData
(
void
);
struct
redisMemOverhead
*
getMemoryOverheadData
(
void
);
void
freeMemoryOverheadData
(
struct
redisMemOverhead
*
mh
);
void
freeMemoryOverheadData
(
struct
redisMemOverhead
*
mh
);
void
checkChildrenDone
(
void
);
#define RESTART_SERVER_NONE 0
#define RESTART_SERVER_NONE 0
#define RESTART_SERVER_GRACEFULLY (1<<0)
/* Do proper shutdown. */
#define RESTART_SERVER_GRACEFULLY (1<<0)
/* Do proper shutdown. */
...
@@ -2369,6 +2408,10 @@ void mixDigest(unsigned char *digest, void *ptr, size_t len);
...
@@ -2369,6 +2408,10 @@ void mixDigest(unsigned char *digest, void *ptr, size_t len);
void
xorDigest
(
unsigned
char
*
digest
,
void
*
ptr
,
size_t
len
);
void
xorDigest
(
unsigned
char
*
digest
,
void
*
ptr
,
size_t
len
);
int
populateCommandTableParseFlags
(
struct
redisCommand
*
c
,
char
*
strflags
);
int
populateCommandTableParseFlags
(
struct
redisCommand
*
c
,
char
*
strflags
);
/* TLS stuff */
void
tlsInit
(
void
);
int
tlsConfigure
(
redisTLSContextConfig
*
ctx_config
);
#define redisDebug(fmt, ...) \
#define redisDebug(fmt, ...) \
printf("DEBUG %s:%d > " fmt "\n", __FILE__, __LINE__, __VA_ARGS__)
printf("DEBUG %s:%d > " fmt "\n", __FILE__, __LINE__, __VA_ARGS__)
#define redisDebugMark() \
#define redisDebugMark() \
...
...
src/tls.c
0 → 100644
View file @
efb6495a
/*
* Copyright (c) 2019, Redis Labs
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Redis nor the names of its contributors may be used
* to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*/
#include "server.h"
#include "connhelpers.h"
#include "adlist.h"
#ifdef USE_OPENSSL
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/rand.h>
#define REDIS_TLS_PROTO_TLSv1 (1<<0)
#define REDIS_TLS_PROTO_TLSv1_1 (1<<1)
#define REDIS_TLS_PROTO_TLSv1_2 (1<<2)
#define REDIS_TLS_PROTO_TLSv1_3 (1<<3)
/* Use safe defaults */
#ifdef TLS1_3_VERSION
#define REDIS_TLS_PROTO_DEFAULT (REDIS_TLS_PROTO_TLSv1_2|REDIS_TLS_PROTO_TLSv1_3)
#else
#define REDIS_TLS_PROTO_DEFAULT (REDIS_TLS_PROTO_TLSv1_2)
#endif
extern
ConnectionType
CT_Socket
;
SSL_CTX
*
redis_tls_ctx
;
static
int
parseProtocolsConfig
(
const
char
*
str
)
{
int
i
,
count
=
0
;
int
protocols
=
0
;
if
(
!
str
)
return
REDIS_TLS_PROTO_DEFAULT
;
sds
*
tokens
=
sdssplitlen
(
str
,
strlen
(
str
),
" "
,
1
,
&
count
);
if
(
!
tokens
)
{
serverLog
(
LL_WARNING
,
"Invalid tls-protocols configuration string"
);
return
-
1
;
}
for
(
i
=
0
;
i
<
count
;
i
++
)
{
if
(
!
strcasecmp
(
tokens
[
i
],
"tlsv1"
))
protocols
|=
REDIS_TLS_PROTO_TLSv1
;
else
if
(
!
strcasecmp
(
tokens
[
i
],
"tlsv1.1"
))
protocols
|=
REDIS_TLS_PROTO_TLSv1_1
;
else
if
(
!
strcasecmp
(
tokens
[
i
],
"tlsv1.2"
))
protocols
|=
REDIS_TLS_PROTO_TLSv1_2
;
else
if
(
!
strcasecmp
(
tokens
[
i
],
"tlsv1.3"
))
{
#ifdef TLS1_3_VERSION
protocols
|=
REDIS_TLS_PROTO_TLSv1_3
;
#else
serverLog
(
LL_WARNING
,
"TLSv1.3 is specified in tls-protocols but not supported by OpenSSL."
);
protocols
=
-
1
;
break
;
#endif
}
else
{
serverLog
(
LL_WARNING
,
"Invalid tls-protocols specified. "
"Use a combination of 'TLSv1', 'TLSv1.1', 'TLSv1.2' and 'TLSv1.3'."
);
protocols
=
-
1
;
break
;
}
}
sdsfreesplitres
(
tokens
,
count
);
return
protocols
;
}
/* list of connections with pending data already read from the socket, but not
* served to the reader yet. */
static
list
*
pending_list
=
NULL
;
void
tlsInit
(
void
)
{
ERR_load_crypto_strings
();
SSL_load_error_strings
();
SSL_library_init
();
if
(
!
RAND_poll
())
{
serverLog
(
LL_WARNING
,
"OpenSSL: Failed to seed random number generator."
);
}
pending_list
=
listCreate
();
/* Server configuration */
server
.
tls_auth_clients
=
1
;
/* Secure by default */
}
/* Attempt to configure/reconfigure TLS. This operation is atomic and will
* leave the SSL_CTX unchanged if fails.
*/
int
tlsConfigure
(
redisTLSContextConfig
*
ctx_config
)
{
char
errbuf
[
256
];
SSL_CTX
*
ctx
=
NULL
;
if
(
!
ctx_config
->
cert_file
)
{
serverLog
(
LL_WARNING
,
"No tls-cert-file configured!"
);
goto
error
;
}
if
(
!
ctx_config
->
key_file
)
{
serverLog
(
LL_WARNING
,
"No tls-key-file configured!"
);
goto
error
;
}
if
(
!
ctx_config
->
ca_cert_file
&&
!
ctx_config
->
ca_cert_dir
)
{
serverLog
(
LL_WARNING
,
"Either tls-ca-cert-file or tls-ca-cert-dir must be configured!"
);
goto
error
;
}
ctx
=
SSL_CTX_new
(
SSLv23_method
());
SSL_CTX_set_options
(
ctx
,
SSL_OP_NO_SSLv2
|
SSL_OP_NO_SSLv3
);
SSL_CTX_set_options
(
ctx
,
SSL_OP_SINGLE_DH_USE
);
#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
SSL_CTX_set_options
(
ctx
,
SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
);
#endif
int
protocols
=
parseProtocolsConfig
(
ctx_config
->
protocols
);
if
(
protocols
==
-
1
)
goto
error
;
if
(
!
(
protocols
&
REDIS_TLS_PROTO_TLSv1
))
SSL_CTX_set_options
(
ctx
,
SSL_OP_NO_TLSv1
);
if
(
!
(
protocols
&
REDIS_TLS_PROTO_TLSv1_1
))
SSL_CTX_set_options
(
ctx
,
SSL_OP_NO_TLSv1_1
);
#ifdef SSL_OP_NO_TLSv1_2
if
(
!
(
protocols
&
REDIS_TLS_PROTO_TLSv1_2
))
SSL_CTX_set_options
(
ctx
,
SSL_OP_NO_TLSv1_2
);
#endif
#ifdef SSL_OP_NO_TLSv1_3
if
(
!
(
protocols
&
REDIS_TLS_PROTO_TLSv1_3
))
SSL_CTX_set_options
(
ctx
,
SSL_OP_NO_TLSv1_3
);
#endif
#ifdef SSL_OP_NO_COMPRESSION
SSL_CTX_set_options
(
ctx
,
SSL_OP_NO_COMPRESSION
);
#endif
#ifdef SSL_OP_NO_CLIENT_RENEGOTIATION
SSL_CTX_set_options
(
ssl
->
ctx
,
SSL_OP_NO_CLIENT_RENEGOTIATION
);
#endif
if
(
ctx_config
->
prefer_server_ciphers
)
SSL_CTX_set_options
(
ctx
,
SSL_OP_CIPHER_SERVER_PREFERENCE
);
SSL_CTX_set_mode
(
ctx
,
SSL_MODE_ENABLE_PARTIAL_WRITE
|
SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER
);
SSL_CTX_set_verify
(
ctx
,
SSL_VERIFY_PEER
|
SSL_VERIFY_FAIL_IF_NO_PEER_CERT
,
NULL
);
SSL_CTX_set_ecdh_auto
(
ctx
,
1
);
if
(
SSL_CTX_use_certificate_file
(
ctx
,
ctx_config
->
cert_file
,
SSL_FILETYPE_PEM
)
<=
0
)
{
ERR_error_string_n
(
ERR_get_error
(),
errbuf
,
sizeof
(
errbuf
));
serverLog
(
LL_WARNING
,
"Failed to load certificate: %s: %s"
,
ctx_config
->
cert_file
,
errbuf
);
goto
error
;
}
if
(
SSL_CTX_use_PrivateKey_file
(
ctx
,
ctx_config
->
key_file
,
SSL_FILETYPE_PEM
)
<=
0
)
{
ERR_error_string_n
(
ERR_get_error
(),
errbuf
,
sizeof
(
errbuf
));
serverLog
(
LL_WARNING
,
"Failed to load private key: %s: %s"
,
ctx_config
->
key_file
,
errbuf
);
goto
error
;
}
if
(
SSL_CTX_load_verify_locations
(
ctx
,
ctx_config
->
ca_cert_file
,
ctx_config
->
ca_cert_dir
)
<=
0
)
{
ERR_error_string_n
(
ERR_get_error
(),
errbuf
,
sizeof
(
errbuf
));
serverLog
(
LL_WARNING
,
"Failed to configure CA certificate(s) file/directory: %s"
,
errbuf
);
goto
error
;
}
if
(
ctx_config
->
dh_params_file
)
{
FILE
*
dhfile
=
fopen
(
ctx_config
->
dh_params_file
,
"r"
);
DH
*
dh
=
NULL
;
if
(
!
dhfile
)
{
serverLog
(
LL_WARNING
,
"Failed to load %s: %s"
,
ctx_config
->
dh_params_file
,
strerror
(
errno
));
goto
error
;
}
dh
=
PEM_read_DHparams
(
dhfile
,
NULL
,
NULL
,
NULL
);
fclose
(
dhfile
);
if
(
!
dh
)
{
serverLog
(
LL_WARNING
,
"%s: failed to read DH params."
,
ctx_config
->
dh_params_file
);
goto
error
;
}
if
(
SSL_CTX_set_tmp_dh
(
ctx
,
dh
)
<=
0
)
{
ERR_error_string_n
(
ERR_get_error
(),
errbuf
,
sizeof
(
errbuf
));
serverLog
(
LL_WARNING
,
"Failed to load DH params file: %s: %s"
,
ctx_config
->
dh_params_file
,
errbuf
);
DH_free
(
dh
);
goto
error
;
}
DH_free
(
dh
);
}
if
(
ctx_config
->
ciphers
&&
!
SSL_CTX_set_cipher_list
(
ctx
,
ctx_config
->
ciphers
))
{
serverLog
(
LL_WARNING
,
"Failed to configure ciphers: %s"
,
ctx_config
->
ciphers
);
goto
error
;
}
#ifdef TLS1_3_VERSION
if
(
ctx_config
->
ciphersuites
&&
!
SSL_CTX_set_ciphersuites
(
ctx
,
ctx_config
->
ciphersuites
))
{
serverLog
(
LL_WARNING
,
"Failed to configure ciphersuites: %s"
,
ctx_config
->
ciphersuites
);
goto
error
;
}
#endif
SSL_CTX_free
(
redis_tls_ctx
);
redis_tls_ctx
=
ctx
;
return
C_OK
;
error:
if
(
ctx
)
SSL_CTX_free
(
ctx
);
return
C_ERR
;
}
#ifdef TLS_DEBUGGING
#define TLSCONN_DEBUG(fmt, ...) \
serverLog(LL_DEBUG, "TLSCONN: " fmt, __VA_ARGS__)
#else
#define TLSCONN_DEBUG(fmt, ...)
#endif
ConnectionType
CT_TLS
;
/* Normal socket connections have a simple events/handler correlation.
*
* With TLS connections we need to handle cases where during a logical read
* or write operation, the SSL library asks to block for the opposite
* socket operation.
*
* When this happens, we need to do two things:
* 1. Make sure we register for the even.
* 2. Make sure we know which handler needs to execute when the
* event fires. That is, if we notify the caller of a write operation
* that it blocks, and SSL asks for a read, we need to trigger the
* write handler again on the next read event.
*
*/
typedef
enum
{
WANT_READ
=
1
,
WANT_WRITE
}
WantIOType
;
#define TLS_CONN_FLAG_READ_WANT_WRITE (1<<0)
#define TLS_CONN_FLAG_WRITE_WANT_READ (1<<1)
#define TLS_CONN_FLAG_FD_SET (1<<2)
typedef
struct
tls_connection
{
connection
c
;
int
flags
;
SSL
*
ssl
;
char
*
ssl_error
;
listNode
*
pending_list_node
;
}
tls_connection
;
connection
*
connCreateTLS
(
void
)
{
tls_connection
*
conn
=
zcalloc
(
sizeof
(
tls_connection
));
conn
->
c
.
type
=
&
CT_TLS
;
conn
->
c
.
fd
=
-
1
;
conn
->
ssl
=
SSL_new
(
redis_tls_ctx
);
return
(
connection
*
)
conn
;
}
connection
*
connCreateAcceptedTLS
(
int
fd
,
int
require_auth
)
{
tls_connection
*
conn
=
(
tls_connection
*
)
connCreateTLS
();
conn
->
c
.
fd
=
fd
;
conn
->
c
.
state
=
CONN_STATE_ACCEPTING
;
if
(
!
require_auth
)
{
/* We still verify certificates if provided, but don't require them.
*/
SSL_set_verify
(
conn
->
ssl
,
SSL_VERIFY_PEER
,
NULL
);
}
SSL_set_fd
(
conn
->
ssl
,
conn
->
c
.
fd
);
SSL_set_accept_state
(
conn
->
ssl
);
return
(
connection
*
)
conn
;
}
static
void
tlsEventHandler
(
struct
aeEventLoop
*
el
,
int
fd
,
void
*
clientData
,
int
mask
);
/* Process the return code received from OpenSSL>
* Update the want parameter with expected I/O.
* Update the connection's error state if a real error has occured.
* Returns an SSL error code, or 0 if no further handling is required.
*/
static
int
handleSSLReturnCode
(
tls_connection
*
conn
,
int
ret_value
,
WantIOType
*
want
)
{
if
(
ret_value
<=
0
)
{
int
ssl_err
=
SSL_get_error
(
conn
->
ssl
,
ret_value
);
switch
(
ssl_err
)
{
case
SSL_ERROR_WANT_WRITE
:
*
want
=
WANT_WRITE
;
return
0
;
case
SSL_ERROR_WANT_READ
:
*
want
=
WANT_READ
;
return
0
;
case
SSL_ERROR_SYSCALL
:
conn
->
c
.
last_errno
=
errno
;
if
(
conn
->
ssl_error
)
zfree
(
conn
->
ssl_error
);
conn
->
ssl_error
=
errno
?
zstrdup
(
strerror
(
errno
))
:
NULL
;
break
;
default:
/* Error! */
conn
->
c
.
last_errno
=
0
;
if
(
conn
->
ssl_error
)
zfree
(
conn
->
ssl_error
);
conn
->
ssl_error
=
zmalloc
(
512
);
ERR_error_string_n
(
ERR_get_error
(),
conn
->
ssl_error
,
512
);
break
;
}
return
ssl_err
;
}
return
0
;
}
void
registerSSLEvent
(
tls_connection
*
conn
,
WantIOType
want
)
{
int
mask
=
aeGetFileEvents
(
server
.
el
,
conn
->
c
.
fd
);
switch
(
want
)
{
case
WANT_READ
:
if
(
mask
&
AE_WRITABLE
)
aeDeleteFileEvent
(
server
.
el
,
conn
->
c
.
fd
,
AE_WRITABLE
);
if
(
!
(
mask
&
AE_READABLE
))
aeCreateFileEvent
(
server
.
el
,
conn
->
c
.
fd
,
AE_READABLE
,
tlsEventHandler
,
conn
);
break
;
case
WANT_WRITE
:
if
(
mask
&
AE_READABLE
)
aeDeleteFileEvent
(
server
.
el
,
conn
->
c
.
fd
,
AE_READABLE
);
if
(
!
(
mask
&
AE_WRITABLE
))
aeCreateFileEvent
(
server
.
el
,
conn
->
c
.
fd
,
AE_WRITABLE
,
tlsEventHandler
,
conn
);
break
;
default:
serverAssert
(
0
);
break
;
}
}
void
updateSSLEvent
(
tls_connection
*
conn
)
{
int
mask
=
aeGetFileEvents
(
server
.
el
,
conn
->
c
.
fd
);
int
need_read
=
conn
->
c
.
read_handler
||
(
conn
->
flags
&
TLS_CONN_FLAG_WRITE_WANT_READ
);
int
need_write
=
conn
->
c
.
write_handler
||
(
conn
->
flags
&
TLS_CONN_FLAG_READ_WANT_WRITE
);
if
(
need_read
&&
!
(
mask
&
AE_READABLE
))
aeCreateFileEvent
(
server
.
el
,
conn
->
c
.
fd
,
AE_READABLE
,
tlsEventHandler
,
conn
);
if
(
!
need_read
&&
(
mask
&
AE_READABLE
))
aeDeleteFileEvent
(
server
.
el
,
conn
->
c
.
fd
,
AE_READABLE
);
if
(
need_write
&&
!
(
mask
&
AE_WRITABLE
))
aeCreateFileEvent
(
server
.
el
,
conn
->
c
.
fd
,
AE_WRITABLE
,
tlsEventHandler
,
conn
);
if
(
!
need_write
&&
(
mask
&
AE_WRITABLE
))
aeDeleteFileEvent
(
server
.
el
,
conn
->
c
.
fd
,
AE_WRITABLE
);
}
static
void
tlsHandleEvent
(
tls_connection
*
conn
,
int
mask
)
{
int
ret
;
TLSCONN_DEBUG
(
"tlsEventHandler(): fd=%d, state=%d, mask=%d, r=%d, w=%d, flags=%d"
,
fd
,
conn
->
c
.
state
,
mask
,
conn
->
c
.
read_handler
!=
NULL
,
conn
->
c
.
write_handler
!=
NULL
,
conn
->
flags
);
ERR_clear_error
();
switch
(
conn
->
c
.
state
)
{
case
CONN_STATE_CONNECTING
:
if
(
connGetSocketError
((
connection
*
)
conn
))
{
conn
->
c
.
last_errno
=
errno
;
conn
->
c
.
state
=
CONN_STATE_ERROR
;
}
else
{
if
(
!
(
conn
->
flags
&
TLS_CONN_FLAG_FD_SET
))
{
SSL_set_fd
(
conn
->
ssl
,
conn
->
c
.
fd
);
conn
->
flags
|=
TLS_CONN_FLAG_FD_SET
;
}
ret
=
SSL_connect
(
conn
->
ssl
);
if
(
ret
<=
0
)
{
WantIOType
want
=
0
;
if
(
!
handleSSLReturnCode
(
conn
,
ret
,
&
want
))
{
registerSSLEvent
(
conn
,
want
);
/* Avoid hitting UpdateSSLEvent, which knows nothing
* of what SSL_connect() wants and instead looks at our
* R/W handlers.
*/
return
;
}
/* If not handled, it's an error */
conn
->
c
.
state
=
CONN_STATE_ERROR
;
}
else
{
conn
->
c
.
state
=
CONN_STATE_CONNECTED
;
}
}
if
(
!
callHandler
((
connection
*
)
conn
,
conn
->
c
.
conn_handler
))
return
;
conn
->
c
.
conn_handler
=
NULL
;
break
;
case
CONN_STATE_ACCEPTING
:
ret
=
SSL_accept
(
conn
->
ssl
);
if
(
ret
<=
0
)
{
WantIOType
want
=
0
;
if
(
!
handleSSLReturnCode
(
conn
,
ret
,
&
want
))
{
/* Avoid hitting UpdateSSLEvent, which knows nothing
* of what SSL_connect() wants and instead looks at our
* R/W handlers.
*/
registerSSLEvent
(
conn
,
want
);
return
;
}
/* If not handled, it's an error */
conn
->
c
.
state
=
CONN_STATE_ERROR
;
}
else
{
conn
->
c
.
state
=
CONN_STATE_CONNECTED
;
}
if
(
!
callHandler
((
connection
*
)
conn
,
conn
->
c
.
conn_handler
))
return
;
conn
->
c
.
conn_handler
=
NULL
;
break
;
case
CONN_STATE_CONNECTED
:
{
int
call_read
=
((
mask
&
AE_READABLE
)
&&
conn
->
c
.
read_handler
)
||
((
mask
&
AE_WRITABLE
)
&&
(
conn
->
flags
&
TLS_CONN_FLAG_READ_WANT_WRITE
));
int
call_write
=
((
mask
&
AE_WRITABLE
)
&&
conn
->
c
.
write_handler
)
||
((
mask
&
AE_READABLE
)
&&
(
conn
->
flags
&
TLS_CONN_FLAG_WRITE_WANT_READ
));
/* Normally we execute the readable event first, and the writable
* event laster. This is useful as sometimes we may be able
* to serve the reply of a query immediately after processing the
* query.
*
* However if WRITE_BARRIER is set in the mask, our application is
* asking us to do the reverse: never fire the writable event
* after the readable. In such a case, we invert the calls.
* This is useful when, for instance, we want to do things
* in the beforeSleep() hook, like fsynching a file to disk,
* before replying to a client. */
int
invert
=
conn
->
c
.
flags
&
CONN_FLAG_WRITE_BARRIER
;
if
(
!
invert
&&
call_read
)
{
conn
->
flags
&=
~
TLS_CONN_FLAG_READ_WANT_WRITE
;
if
(
!
callHandler
((
connection
*
)
conn
,
conn
->
c
.
read_handler
))
return
;
}
/* Fire the writable event. */
if
(
call_write
)
{
conn
->
flags
&=
~
TLS_CONN_FLAG_WRITE_WANT_READ
;
if
(
!
callHandler
((
connection
*
)
conn
,
conn
->
c
.
write_handler
))
return
;
}
/* If we have to invert the call, fire the readable event now
* after the writable one. */
if
(
invert
&&
call_read
)
{
conn
->
flags
&=
~
TLS_CONN_FLAG_READ_WANT_WRITE
;
if
(
!
callHandler
((
connection
*
)
conn
,
conn
->
c
.
read_handler
))
return
;
}
/* If SSL has pending that, already read from the socket, we're at
* risk of not calling the read handler again, make sure to add it
* to a list of pending connection that should be handled anyway. */
if
((
mask
&
AE_READABLE
))
{
if
(
SSL_pending
(
conn
->
ssl
)
>
0
)
{
if
(
!
conn
->
pending_list_node
)
{
listAddNodeTail
(
pending_list
,
conn
);
conn
->
pending_list_node
=
listLast
(
pending_list
);
}
}
else
if
(
conn
->
pending_list_node
)
{
listDelNode
(
pending_list
,
conn
->
pending_list_node
);
conn
->
pending_list_node
=
NULL
;
}
}
break
;
}
default:
break
;
}
updateSSLEvent
(
conn
);
}
static
void
tlsEventHandler
(
struct
aeEventLoop
*
el
,
int
fd
,
void
*
clientData
,
int
mask
)
{
UNUSED
(
el
);
UNUSED
(
fd
);
tls_connection
*
conn
=
clientData
;
tlsHandleEvent
(
conn
,
mask
);
}
static
void
connTLSClose
(
connection
*
conn_
)
{
tls_connection
*
conn
=
(
tls_connection
*
)
conn_
;
if
(
conn
->
ssl
)
{
SSL_free
(
conn
->
ssl
);
conn
->
ssl
=
NULL
;
}
if
(
conn
->
ssl_error
)
{
zfree
(
conn
->
ssl_error
);
conn
->
ssl_error
=
NULL
;
}
if
(
conn
->
pending_list_node
)
{
listDelNode
(
pending_list
,
conn
->
pending_list_node
);
conn
->
pending_list_node
=
NULL
;
}
CT_Socket
.
close
(
conn_
);
}
static
int
connTLSAccept
(
connection
*
_conn
,
ConnectionCallbackFunc
accept_handler
)
{
tls_connection
*
conn
=
(
tls_connection
*
)
_conn
;
int
ret
;
if
(
conn
->
c
.
state
!=
CONN_STATE_ACCEPTING
)
return
C_ERR
;
ERR_clear_error
();
/* Try to accept */
conn
->
c
.
conn_handler
=
accept_handler
;
ret
=
SSL_accept
(
conn
->
ssl
);
if
(
ret
<=
0
)
{
WantIOType
want
=
0
;
if
(
!
handleSSLReturnCode
(
conn
,
ret
,
&
want
))
{
registerSSLEvent
(
conn
,
want
);
/* We'll fire back */
return
C_OK
;
}
else
{
conn
->
c
.
state
=
CONN_STATE_ERROR
;
return
C_ERR
;
}
}
conn
->
c
.
state
=
CONN_STATE_CONNECTED
;
if
(
!
callHandler
((
connection
*
)
conn
,
conn
->
c
.
conn_handler
))
return
C_OK
;
conn
->
c
.
conn_handler
=
NULL
;
return
C_OK
;
}
static
int
connTLSConnect
(
connection
*
conn_
,
const
char
*
addr
,
int
port
,
const
char
*
src_addr
,
ConnectionCallbackFunc
connect_handler
)
{
tls_connection
*
conn
=
(
tls_connection
*
)
conn_
;
if
(
conn
->
c
.
state
!=
CONN_STATE_NONE
)
return
C_ERR
;
ERR_clear_error
();
/* Initiate Socket connection first */
if
(
CT_Socket
.
connect
(
conn_
,
addr
,
port
,
src_addr
,
connect_handler
)
==
C_ERR
)
return
C_ERR
;
/* Return now, once the socket is connected we'll initiate
* TLS connection from the event handler.
*/
return
C_OK
;
}
static
int
connTLSWrite
(
connection
*
conn_
,
const
void
*
data
,
size_t
data_len
)
{
tls_connection
*
conn
=
(
tls_connection
*
)
conn_
;
int
ret
,
ssl_err
;
if
(
conn
->
c
.
state
!=
CONN_STATE_CONNECTED
)
return
-
1
;
ERR_clear_error
();
ret
=
SSL_write
(
conn
->
ssl
,
data
,
data_len
);
if
(
ret
<=
0
)
{
WantIOType
want
=
0
;
if
(
!
(
ssl_err
=
handleSSLReturnCode
(
conn
,
ret
,
&
want
)))
{
if
(
want
==
WANT_READ
)
conn
->
flags
|=
TLS_CONN_FLAG_WRITE_WANT_READ
;
updateSSLEvent
(
conn
);
errno
=
EAGAIN
;
return
-
1
;
}
else
{
if
(
ssl_err
==
SSL_ERROR_ZERO_RETURN
||
((
ssl_err
==
SSL_ERROR_SYSCALL
&&
!
errno
)))
{
conn
->
c
.
state
=
CONN_STATE_CLOSED
;
return
0
;
}
else
{
conn
->
c
.
state
=
CONN_STATE_ERROR
;
return
-
1
;
}
}
}
return
ret
;
}
static
int
connTLSRead
(
connection
*
conn_
,
void
*
buf
,
size_t
buf_len
)
{
tls_connection
*
conn
=
(
tls_connection
*
)
conn_
;
int
ret
;
int
ssl_err
;
if
(
conn
->
c
.
state
!=
CONN_STATE_CONNECTED
)
return
-
1
;
ERR_clear_error
();
ret
=
SSL_read
(
conn
->
ssl
,
buf
,
buf_len
);
if
(
ret
<=
0
)
{
WantIOType
want
=
0
;
if
(
!
(
ssl_err
=
handleSSLReturnCode
(
conn
,
ret
,
&
want
)))
{
if
(
want
==
WANT_WRITE
)
conn
->
flags
|=
TLS_CONN_FLAG_READ_WANT_WRITE
;
updateSSLEvent
(
conn
);
errno
=
EAGAIN
;
return
-
1
;
}
else
{
if
(
ssl_err
==
SSL_ERROR_ZERO_RETURN
||
((
ssl_err
==
SSL_ERROR_SYSCALL
)
&&
!
errno
))
{
conn
->
c
.
state
=
CONN_STATE_CLOSED
;
return
0
;
}
else
{
conn
->
c
.
state
=
CONN_STATE_ERROR
;
return
-
1
;
}
}
}
return
ret
;
}
static
const
char
*
connTLSGetLastError
(
connection
*
conn_
)
{
tls_connection
*
conn
=
(
tls_connection
*
)
conn_
;
if
(
conn
->
ssl_error
)
return
conn
->
ssl_error
;
return
NULL
;
}
int
connTLSSetWriteHandler
(
connection
*
conn
,
ConnectionCallbackFunc
func
,
int
barrier
)
{
conn
->
write_handler
=
func
;
if
(
barrier
)
conn
->
flags
|=
CONN_FLAG_WRITE_BARRIER
;
else
conn
->
flags
&=
~
CONN_FLAG_WRITE_BARRIER
;
updateSSLEvent
((
tls_connection
*
)
conn
);
return
C_OK
;
}
int
connTLSSetReadHandler
(
connection
*
conn
,
ConnectionCallbackFunc
func
)
{
conn
->
read_handler
=
func
;
updateSSLEvent
((
tls_connection
*
)
conn
);
return
C_OK
;
}
static
void
setBlockingTimeout
(
tls_connection
*
conn
,
long
long
timeout
)
{
anetBlock
(
NULL
,
conn
->
c
.
fd
);
anetSendTimeout
(
NULL
,
conn
->
c
.
fd
,
timeout
);
anetRecvTimeout
(
NULL
,
conn
->
c
.
fd
,
timeout
);
}
static
void
unsetBlockingTimeout
(
tls_connection
*
conn
)
{
anetNonBlock
(
NULL
,
conn
->
c
.
fd
);
anetSendTimeout
(
NULL
,
conn
->
c
.
fd
,
0
);
anetRecvTimeout
(
NULL
,
conn
->
c
.
fd
,
0
);
}
static
int
connTLSBlockingConnect
(
connection
*
conn_
,
const
char
*
addr
,
int
port
,
long
long
timeout
)
{
tls_connection
*
conn
=
(
tls_connection
*
)
conn_
;
int
ret
;
if
(
conn
->
c
.
state
!=
CONN_STATE_NONE
)
return
C_ERR
;
/* Initiate socket blocking connect first */
if
(
CT_Socket
.
blocking_connect
(
conn_
,
addr
,
port
,
timeout
)
==
C_ERR
)
return
C_ERR
;
/* Initiate TLS connection now. We set up a send/recv timeout on the socket,
* which means the specified timeout will not be enforced accurately. */
SSL_set_fd
(
conn
->
ssl
,
conn
->
c
.
fd
);
setBlockingTimeout
(
conn
,
timeout
);
if
((
ret
=
SSL_connect
(
conn
->
ssl
))
<=
0
)
{
conn
->
c
.
state
=
CONN_STATE_ERROR
;
return
C_ERR
;
}
unsetBlockingTimeout
(
conn
);
conn
->
c
.
state
=
CONN_STATE_CONNECTED
;
return
C_OK
;
}
static
ssize_t
connTLSSyncWrite
(
connection
*
conn_
,
char
*
ptr
,
ssize_t
size
,
long
long
timeout
)
{
tls_connection
*
conn
=
(
tls_connection
*
)
conn_
;
setBlockingTimeout
(
conn
,
timeout
);
SSL_clear_mode
(
conn
->
ssl
,
SSL_MODE_ENABLE_PARTIAL_WRITE
);
int
ret
=
SSL_write
(
conn
->
ssl
,
ptr
,
size
);
SSL_set_mode
(
conn
->
ssl
,
SSL_MODE_ENABLE_PARTIAL_WRITE
);
unsetBlockingTimeout
(
conn
);
return
ret
;
}
static
ssize_t
connTLSSyncRead
(
connection
*
conn_
,
char
*
ptr
,
ssize_t
size
,
long
long
timeout
)
{
tls_connection
*
conn
=
(
tls_connection
*
)
conn_
;
setBlockingTimeout
(
conn
,
timeout
);
int
ret
=
SSL_read
(
conn
->
ssl
,
ptr
,
size
);
unsetBlockingTimeout
(
conn
);
return
ret
;
}
static
ssize_t
connTLSSyncReadLine
(
connection
*
conn_
,
char
*
ptr
,
ssize_t
size
,
long
long
timeout
)
{
tls_connection
*
conn
=
(
tls_connection
*
)
conn_
;
ssize_t
nread
=
0
;
setBlockingTimeout
(
conn
,
timeout
);
size
--
;
while
(
size
)
{
char
c
;
if
(
SSL_read
(
conn
->
ssl
,
&
c
,
1
)
<=
0
)
{
nread
=
-
1
;
goto
exit
;
}
if
(
c
==
'\n'
)
{
*
ptr
=
'\0'
;
if
(
nread
&&
*
(
ptr
-
1
)
==
'\r'
)
*
(
ptr
-
1
)
=
'\0'
;
goto
exit
;
}
else
{
*
ptr
++
=
c
;
*
ptr
=
'\0'
;
nread
++
;
}
size
--
;
}
exit:
unsetBlockingTimeout
(
conn
);
return
nread
;
}
ConnectionType
CT_TLS
=
{
.
ae_handler
=
tlsEventHandler
,
.
accept
=
connTLSAccept
,
.
connect
=
connTLSConnect
,
.
blocking_connect
=
connTLSBlockingConnect
,
.
read
=
connTLSRead
,
.
write
=
connTLSWrite
,
.
close
=
connTLSClose
,
.
set_write_handler
=
connTLSSetWriteHandler
,
.
set_read_handler
=
connTLSSetReadHandler
,
.
get_last_error
=
connTLSGetLastError
,
.
sync_write
=
connTLSSyncWrite
,
.
sync_read
=
connTLSSyncRead
,
.
sync_readline
=
connTLSSyncReadLine
,
};
int
tlsHasPendingData
()
{
if
(
!
pending_list
)
return
0
;
return
listLength
(
pending_list
)
>
0
;
}
void
tlsProcessPendingData
()
{
listIter
li
;
listNode
*
ln
;
listRewind
(
pending_list
,
&
li
);
while
((
ln
=
listNext
(
&
li
)))
{
tls_connection
*
conn
=
listNodeValue
(
ln
);
tlsHandleEvent
(
conn
,
AE_READABLE
);
}
}
#else
/* USE_OPENSSL */
void
tlsInit
(
void
)
{
}
int
tlsConfigure
(
redisTLSContextConfig
*
ctx_config
)
{
UNUSED
(
ctx_config
);
return
C_OK
;
}
connection
*
connCreateTLS
(
void
)
{
return
NULL
;
}
connection
*
connCreateAcceptedTLS
(
int
fd
,
int
require_auth
)
{
UNUSED
(
fd
);
UNUSED
(
require_auth
);
return
NULL
;
}
int
tlsHasPendingData
()
{
return
0
;
}
void
tlsProcessPendingData
()
{
}
#endif
tests/cluster/run.tcl
View file @
efb6495a
...
@@ -8,6 +8,7 @@ source ../instances.tcl
...
@@ -8,6 +8,7 @@ source ../instances.tcl
source ../../support/cluster.tcl
;
# Redis Cluster client.
source ../../support/cluster.tcl
;
# Redis Cluster client.
set ::instances_count 20
;
# How many instances we use at max.
set ::instances_count 20
;
# How many instances we use at max.
set ::tlsdir
"../../tls"
proc main
{}
{
proc main
{}
{
parse_options
parse_options
...
...
tests/cluster/tests/04-resharding.tcl
View file @
efb6495a
...
@@ -4,6 +4,7 @@
...
@@ -4,6 +4,7 @@
# are preseved across iterations.
# are preseved across iterations.
source
"../tests/includes/init-tests.tcl"
source
"../tests/includes/init-tests.tcl"
source
"../../../tests/support/cli.tcl"
test
"Create a 5 nodes cluster"
{
test
"Create a 5 nodes cluster"
{
create_cluster 5 5
create_cluster 5 5
...
@@ -79,6 +80,7 @@ test "Cluster consistency during live resharding" {
...
@@ -79,6 +80,7 @@ test "Cluster consistency during live resharding" {
--cluster-to $target
\
--cluster-to $target
\
--cluster-slots 100
\
--cluster-slots 100
\
--cluster-yes
\
--cluster-yes
\
{*}
[
rediscli_tls_config
"../../../tests"
]
\
|
[
info nameofexecutable
]
\
|
[
info nameofexecutable
]
\
../tests/helpers/onlydots.tcl
\
../tests/helpers/onlydots.tcl
\
&
]
0
]
&
]
0
]
...
...
tests/cluster/tests/12-replica-migration-2.tcl
View file @
efb6495a
...
@@ -5,6 +5,7 @@
...
@@ -5,6 +5,7 @@
# other masters have slaves.
# other masters have slaves.
source
"../tests/includes/init-tests.tcl"
source
"../tests/includes/init-tests.tcl"
source
"../../../tests/support/cli.tcl"
# Create a cluster with 5 master and 15 slaves, to make sure there are no
# Create a cluster with 5 master and 15 slaves, to make sure there are no
# empty masters and make rebalancing simpler to handle during the test.
# empty masters and make rebalancing simpler to handle during the test.
...
@@ -33,7 +34,9 @@ test "Resharding all the master #0 slots away from it" {
...
@@ -33,7 +34,9 @@ test "Resharding all the master #0 slots away from it" {
set output
[
exec
\
set output
[
exec
\
../../../src/redis-cli --cluster rebalance
\
../../../src/redis-cli --cluster rebalance
\
127.0.0.1:
[
get_instance_attrib redis 0 port
]
\
127.0.0.1:
[
get_instance_attrib redis 0 port
]
\
{*}
[
rediscli_tls_config
"../../../tests"
]
\
--cluster-weight $
{
master0_id
}
=0 >@ stdout
]
--cluster-weight $
{
master0_id
}
=0 >@ stdout
]
}
}
test
"Master #0 should lose its replicas"
{
test
"Master #0 should lose its replicas"
{
...
@@ -51,6 +54,7 @@ test "Resharding back some slot to master #0" {
...
@@ -51,6 +54,7 @@ test "Resharding back some slot to master #0" {
set output
[
exec
\
set output
[
exec
\
../../../src/redis-cli --cluster rebalance
\
../../../src/redis-cli --cluster rebalance
\
127.0.0.1:
[
get_instance_attrib redis 0 port
]
\
127.0.0.1:
[
get_instance_attrib redis 0 port
]
\
{*}
[
rediscli_tls_config
"../../../tests"
]
\
--cluster-weight $
{
master0_id
}
=.01
\
--cluster-weight $
{
master0_id
}
=.01
\
--cluster-use-empty-masters >@ stdout
]
--cluster-use-empty-masters >@ stdout
]
}
}
...
...
tests/helpers/bg_block_op.tcl
View file @
efb6495a
source tests/support/redis.tcl
source tests/support/redis.tcl
source tests/support/util.tcl
source tests/support/util.tcl
set ::tlsdir
"tests/tls"
# This function sometimes writes sometimes blocking-reads from lists/sorted
# This function sometimes writes sometimes blocking-reads from lists/sorted
# sets. There are multiple processes like this executing at the same time
# sets. There are multiple processes like this executing at the same time
# so that we have some chance to trap some corner condition if there is
# so that we have some chance to trap some corner condition if there is
...
@@ -8,8 +10,8 @@ source tests/support/util.tcl
...
@@ -8,8 +10,8 @@ source tests/support/util.tcl
# space to just a few elements, and balance the operations so that it is
# space to just a few elements, and balance the operations so that it is
# unlikely that lists and zsets just get more data without ever causing
# unlikely that lists and zsets just get more data without ever causing
# blocking.
# blocking.
proc bg_block_op
{
host port db ops
}
{
proc bg_block_op
{
host port db ops
tls
}
{
set r
[
redis $host $port
]
set r
[
redis $host $port
0 $tls
]
$r select $db
$r select $db
for
{
set j 0
}
{
$j
< $ops
}
{
incr j
}
{
for
{
set j 0
}
{
$j
< $ops
}
{
incr j
}
{
...
@@ -49,4 +51,4 @@ proc bg_block_op {host port db ops} {
...
@@ -49,4 +51,4 @@ proc bg_block_op {host port db ops} {
}
}
}
}
bg_block_op
[
lindex $argv 0
]
[
lindex $argv 1
]
[
lindex $argv 2
]
[
lindex $argv 3
]
bg_block_op
[
lindex $argv 0
]
[
lindex $argv 1
]
[
lindex $argv 2
]
[
lindex $argv 3
]
[
lindex $argv 4
]
tests/helpers/bg_complex_data.tcl
View file @
efb6495a
source tests/support/redis.tcl
source tests/support/redis.tcl
source tests/support/util.tcl
source tests/support/util.tcl
proc bg_complex_data
{
host port db ops
}
{
set ::tlsdir
"tests/tls"
set r
[
redis $host $port
]
proc bg_complex_data
{
host port db ops tls
}
{
set r
[
redis $host $port 0 $tls
]
$r select $db
$r select $db
createComplexDataset $r $ops
createComplexDataset $r $ops
}
}
bg_complex_data
[
lindex $argv 0
]
[
lindex $argv 1
]
[
lindex $argv 2
]
[
lindex $argv 3
]
bg_complex_data
[
lindex $argv 0
]
[
lindex $argv 1
]
[
lindex $argv 2
]
[
lindex $argv 3
]
[
lindex $argv 4
]
tests/helpers/gen_write_load.tcl
View file @
efb6495a
source tests/support/redis.tcl
source tests/support/redis.tcl
proc gen_write_load
{
host port seconds
}
{
set ::tlsdir
"tests/tls"
proc gen_write_load
{
host port seconds tls
}
{
set start_time
[
clock seconds
]
set start_time
[
clock seconds
]
set r
[
redis $host $port
1
]
set r
[
redis $host $port
0 $tls
]
$r select 9
$r select 9
while 1
{
while 1
{
$r set
[
expr rand
()]
[
expr rand
()]
$r set
[
expr rand
()]
[
expr rand
()]
...
@@ -12,4 +14,4 @@ proc gen_write_load {host port seconds} {
...
@@ -12,4 +14,4 @@ proc gen_write_load {host port seconds} {
}
}
}
}
gen_write_load
[
lindex $argv 0
]
[
lindex $argv 1
]
[
lindex $argv 2
]
gen_write_load
[
lindex $argv 0
]
[
lindex $argv 1
]
[
lindex $argv 2
]
[
lindex $argv 3
]
tests/instances.tcl
View file @
efb6495a
...
@@ -17,6 +17,7 @@ source ../support/test.tcl
...
@@ -17,6 +17,7 @@ source ../support/test.tcl
set ::verbose 0
set ::verbose 0
set ::valgrind 0
set ::valgrind 0
set ::tls 0
set ::pause_on_error 0
set ::pause_on_error 0
set ::simulate_error 0
set ::simulate_error 0
set ::failed 0
set ::failed 0
...
@@ -69,7 +70,19 @@ proc spawn_instance {type base_port count {conf {}}} {
...
@@ -69,7 +70,19 @@ proc spawn_instance {type base_port count {conf {}}} {
# Write the instance config file.
# Write the instance config file.
set cfgfile
[
file join $dirname $type.conf
]
set cfgfile
[
file join $dirname $type.conf
]
set cfg
[
open $cfgfile w
]
set cfg
[
open $cfgfile w
]
if
{
$::tls
}
{
puts $cfg
"tls-port
$port
"
puts $cfg
"tls-replication yes"
puts $cfg
"tls-cluster yes"
puts $cfg
"port 0"
puts $cfg
[
format
"tls-cert-file %s/../../tls/redis.crt"
[
pwd
]]
puts $cfg
[
format
"tls-key-file %s/../../tls/redis.key"
[
pwd
]]
puts $cfg
[
format
"tls-dh-params-file %s/../../tls/redis.dh"
[
pwd
]]
puts $cfg
[
format
"tls-ca-cert-file %s/../../tls/ca.crt"
[
pwd
]]
puts $cfg
"loglevel debug"
}
else
{
puts $cfg
"port
$port
"
puts $cfg
"port
$port
"
}
puts $cfg
"dir ./
$dirname
"
puts $cfg
"dir ./
$dirname
"
puts $cfg
"logfile log.txt"
puts $cfg
"logfile log.txt"
# Add additional config files
# Add additional config files
...
@@ -88,7 +101,7 @@ proc spawn_instance {type base_port count {conf {}}} {
...
@@ -88,7 +101,7 @@ proc spawn_instance {type base_port count {conf {}}} {
}
}
# Push the instance into the right list
# Push the instance into the right list
set link
[
redis 127.0.0.1 $port
]
set link
[
redis 127.0.0.1 $port
0 $::tls
]
$link reconnect 1
$link reconnect 1
lappend ::$
{
type
}
_instances
[
list
\
lappend ::$
{
type
}
_instances
[
list
\
pid $pid
\
pid $pid
\
...
@@ -148,6 +161,13 @@ proc parse_options {} {
...
@@ -148,6 +161,13 @@ proc parse_options {} {
set ::simulate_error 1
set ::simulate_error 1
}
elseif
{
$opt
eq
{
--valgrind
}}
{
}
elseif
{
$opt
eq
{
--valgrind
}}
{
set ::valgrind 1
set ::valgrind 1
}
elseif
{
$opt
eq
{
--tls
}}
{
package require tls 1.6
::tls::init
\
-cafile
"
$::tlsdir
/ca.crt"
\
-certfile
"
$::tlsdir
/redis.crt"
\
-keyfile
"
$::tlsdir
/redis.key"
set ::tls 1
}
elseif
{
$opt
eq
"--help"
}
{
}
elseif
{
$opt
eq
"--help"
}
{
puts
"Hello, I'm sentinel.tcl and I run Sentinel unit tests."
puts
"Hello, I'm sentinel.tcl and I run Sentinel unit tests."
puts
"
\n
Options:"
puts
"
\n
Options:"
...
@@ -492,7 +512,7 @@ proc restart_instance {type id} {
...
@@ -492,7 +512,7 @@ proc restart_instance {type id} {
}
}
# Connect with it with a fresh link
# Connect with it with a fresh link
set link
[
redis 127.0.0.1 $port
]
set link
[
redis 127.0.0.1 $port
0 $::tls
]
$link reconnect 1
$link reconnect 1
set_instance_attrib $type $id link $link
set_instance_attrib $type $id link $link
...
...
tests/integration/aof-race.tcl
View file @
efb6495a
...
@@ -13,8 +13,9 @@ tags {"aof"} {
...
@@ -13,8 +13,9 @@ tags {"aof"} {
# cleaned after a child responsible for an AOF rewrite exited. This buffer
# cleaned after a child responsible for an AOF rewrite exited. This buffer
# was subsequently appended to the new AOF, resulting in duplicate commands.
# was subsequently appended to the new AOF, resulting in duplicate commands.
start_server_aof
[
list dir $server_path
]
{
start_server_aof
[
list dir $server_path
]
{
set client
[
redis
[
srv host
]
[
srv port
]]
set client
[
redis
[
srv host
]
[
srv port
]
0 $::tls
]
set bench
[
open
"|src/redis-benchmark -q -p
[
srv port
]
-c 20 -n 20000 incr foo"
"r+"
]
set bench
[
open
"|src/redis-benchmark -q -s
[
srv unixsocket
]
-c 20 -n 20000 incr foo"
"r+"
]
after 100
after 100
# Benchmark should be running by now: start background rewrite
# Benchmark should be running by now: start background rewrite
...
@@ -29,7 +30,7 @@ tags {"aof"} {
...
@@ -29,7 +30,7 @@ tags {"aof"} {
# Restart server to replay AOF
# Restart server to replay AOF
start_server_aof
[
list dir $server_path
]
{
start_server_aof
[
list dir $server_path
]
{
set client
[
redis
[
srv host
]
[
srv port
]]
set client
[
redis
[
srv host
]
[
srv port
]
0 $::tls
]
assert_equal 20000
[
$client
get foo
]
assert_equal 20000
[
$client
get foo
]
}
}
}
}
tests/integration/aof.tcl
View file @
efb6495a
...
@@ -52,7 +52,7 @@ tags {"aof"} {
...
@@ -52,7 +52,7 @@ tags {"aof"} {
assert_equal 1
[
is_alive $srv
]
assert_equal 1
[
is_alive $srv
]
}
}
set client
[
redis
[
dict get $srv host
]
[
dict get $srv port
]]
set client
[
redis
[
dict get $srv host
]
[
dict get $srv port
]
0 $::tls
]
test
"Truncated AOF loaded: we expect foo to be equal to 5"
{
test
"Truncated AOF loaded: we expect foo to be equal to 5"
{
assert
{[
$client
get foo
]
eq
"5"
}
assert
{[
$client
get foo
]
eq
"5"
}
...
@@ -69,7 +69,7 @@ tags {"aof"} {
...
@@ -69,7 +69,7 @@ tags {"aof"} {
assert_equal 1
[
is_alive $srv
]
assert_equal 1
[
is_alive $srv
]
}
}
set client
[
redis
[
dict get $srv host
]
[
dict get $srv port
]]
set client
[
redis
[
dict get $srv host
]
[
dict get $srv port
]
0 $::tls
]
test
"Truncated AOF loaded: we expect foo to be equal to 6 now"
{
test
"Truncated AOF loaded: we expect foo to be equal to 6 now"
{
assert
{[
$client
get foo
]
eq
"6"
}
assert
{[
$client
get foo
]
eq
"6"
}
...
@@ -170,7 +170,7 @@ tags {"aof"} {
...
@@ -170,7 +170,7 @@ tags {"aof"} {
}
}
test
"Fixed AOF: Keyspace should contain values that were parseable"
{
test
"Fixed AOF: Keyspace should contain values that were parseable"
{
set client
[
redis
[
dict get $srv host
]
[
dict get $srv port
]]
set client
[
redis
[
dict get $srv host
]
[
dict get $srv port
]
0 $::tls
]
wait_for_condition 50 100
{
wait_for_condition 50 100
{
[
catch
{
$client
ping
}
e
]
== 0
[
catch
{
$client
ping
}
e
]
== 0
}
else
{
}
else
{
...
@@ -194,7 +194,7 @@ tags {"aof"} {
...
@@ -194,7 +194,7 @@ tags {"aof"} {
}
}
test
"AOF+SPOP: Set should have 1 member"
{
test
"AOF+SPOP: Set should have 1 member"
{
set client
[
redis
[
dict get $srv host
]
[
dict get $srv port
]]
set client
[
redis
[
dict get $srv host
]
[
dict get $srv port
]
0 $::tls
]
wait_for_condition 50 100
{
wait_for_condition 50 100
{
[
catch
{
$client
ping
}
e
]
== 0
[
catch
{
$client
ping
}
e
]
== 0
}
else
{
}
else
{
...
@@ -218,7 +218,7 @@ tags {"aof"} {
...
@@ -218,7 +218,7 @@ tags {"aof"} {
}
}
test
"AOF+SPOP: Set should have 1 member"
{
test
"AOF+SPOP: Set should have 1 member"
{
set client
[
redis
[
dict get $srv host
]
[
dict get $srv port
]]
set client
[
redis
[
dict get $srv host
]
[
dict get $srv port
]
0 $::tls
]
wait_for_condition 50 100
{
wait_for_condition 50 100
{
[
catch
{
$client
ping
}
e
]
== 0
[
catch
{
$client
ping
}
e
]
== 0
}
else
{
}
else
{
...
@@ -241,7 +241,7 @@ tags {"aof"} {
...
@@ -241,7 +241,7 @@ tags {"aof"} {
}
}
test
"AOF+EXPIRE: List should be empty"
{
test
"AOF+EXPIRE: List should be empty"
{
set client
[
redis
[
dict get $srv host
]
[
dict get $srv port
]]
set client
[
redis
[
dict get $srv host
]
[
dict get $srv port
]
0 $::tls
]
wait_for_condition 50 100
{
wait_for_condition 50 100
{
[
catch
{
$client
ping
}
e
]
== 0
[
catch
{
$client
ping
}
e
]
== 0
}
else
{
}
else
{
...
@@ -257,4 +257,35 @@ tags {"aof"} {
...
@@ -257,4 +257,35 @@ tags {"aof"} {
r expire x -1
r expire x -1
}
}
}
}
start_server
{
overrides
{
appendonly
{
yes
}
appendfilename
{
appendonly.aof
}
appendfsync always
}}
{
test
{
AOF fsync always barrier issue
}
{
set rd
[
redis_deferring_client
]
# Set a sleep when aof is flushed, so that we have a chance to look
# at the aof size and detect if the response of an incr command
# arrives before the data was written
(
and hopefully fsynced
)
# We create a big reply, which will hopefully not have room in the
# socket buffers, and will install a write handler, then we sleep
# a big and issue the incr command, hoping that the last portion of
# the output buffer write, and the processing of the incr will happen
# in the same event loop cycle.
# Since the socket buffers and timing are unpredictable, we fuzz this
# test with slightly different sizes and sleeps a few times.
for
{
set i 0
}
{
$i
< 10
}
{
incr i
}
{
r debug aof-flush-sleep 0
r del x
r setrange x
[
expr
{
int
(
rand
()
*5000000
)
+10000000
}]
x
r debug aof-flush-sleep 500000
set aof
[
file join
[
lindex
[
r config get dir
]
1
]
appendonly.aof
]
set size1
[
file size $aof
]
$rd get x
after
[
expr
{
int
(
rand
()
*30
)}]
$rd incr new_value
$rd read
$rd read
set size2
[
file size $aof
]
assert
{
$size1
!= $size2
}
}
}
}
}
}
tests/integration/block-repl.tcl
View file @
efb6495a
...
@@ -2,9 +2,9 @@
...
@@ -2,9 +2,9 @@
# Unlike stream operations such operations are
"pop"
style, so they consume
# Unlike stream operations such operations are
"pop"
style, so they consume
# the list or sorted set, and must be replicated correctly.
# the list or sorted set, and must be replicated correctly.
proc start_bg_block_op
{
host port db ops
}
{
proc start_bg_block_op
{
host port db ops
tls
}
{
set tclsh
[
info nameofexecutable
]
set tclsh
[
info nameofexecutable
]
exec $tclsh tests/helpers/bg_block_op.tcl $host $port $db $ops &
exec $tclsh tests/helpers/bg_block_op.tcl $host $port $db $ops
$tls
&
}
}
proc stop_bg_block_op
{
handle
}
{
proc stop_bg_block_op
{
handle
}
{
...
@@ -18,9 +18,9 @@ start_server {tags {"repl"}} {
...
@@ -18,9 +18,9 @@ start_server {tags {"repl"}} {
set master_port
[
srv -1 port
]
set master_port
[
srv -1 port
]
set slave
[
srv 0 client
]
set slave
[
srv 0 client
]
set load_handle0
[
start_bg_block_op $master_host $master_port 9 100000
]
set load_handle0
[
start_bg_block_op $master_host $master_port 9 100000
$::tls
]
set load_handle1
[
start_bg_block_op $master_host $master_port 9 100000
]
set load_handle1
[
start_bg_block_op $master_host $master_port 9 100000
$::tls
]
set load_handle2
[
start_bg_block_op $master_host $master_port 9 100000
]
set load_handle2
[
start_bg_block_op $master_host $master_port 9 100000
$::tls
]
test
{
First server should have role slave after SLAVEOF
}
{
test
{
First server should have role slave after SLAVEOF
}
{
$slave slaveof $master_host $master_port
$slave slaveof $master_host $master_port
...
...
tests/integration/psync2-reg.tcl
View file @
efb6495a
...
@@ -18,6 +18,7 @@ start_server {} {
...
@@ -18,6 +18,7 @@ start_server {} {
set R
(
$j
)
[
srv
[
expr 0-$j
]
client
]
set R
(
$j
)
[
srv
[
expr 0-$j
]
client
]
set R_host
(
$j
)
[
srv
[
expr 0-$j
]
host
]
set R_host
(
$j
)
[
srv
[
expr 0-$j
]
host
]
set R_port
(
$j
)
[
srv
[
expr 0-$j
]
port
]
set R_port
(
$j
)
[
srv
[
expr 0-$j
]
port
]
set R_unixsocket
(
$j
)
[
srv
[
expr 0-$j
]
unixsocket
]
if
{
$debug
_msg
}
{
puts
"Log file:
[
srv
[
expr 0-$j
]
stdout
]
"
}
if
{
$debug
_msg
}
{
puts
"Log file:
[
srv
[
expr 0-$j
]
stdout
]
"
}
}
}
...
@@ -36,7 +37,7 @@ start_server {} {
...
@@ -36,7 +37,7 @@ start_server {} {
}
}
set cycle_start_time
[
clock milliseconds
]
set cycle_start_time
[
clock milliseconds
]
set bench_pid
[
exec src/redis-benchmark -
p
$R_
por
t
(
0
)
-n 10000000 -r 1000 incr __rand_int__ > /dev/null &
]
set bench_pid
[
exec src/redis-benchmark -
s
$R_
unixsocke
t
(
0
)
-n 10000000 -r 1000 incr __rand_int__ > /dev/null &
]
while 1
{
while 1
{
set elapsed
[
expr
{[
clock milliseconds
]
-$cycle_start_time
}]
set elapsed
[
expr
{[
clock milliseconds
]
-$cycle_start_time
}]
if
{
$elapsed
> $duration*1000
}
break
if
{
$elapsed
> $duration*1000
}
break
...
...
Prev
1
2
3
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