mirror of
https://github.com/MariaDB/server.git
synced 2025-01-30 02:30:06 +01:00
2fb4407827
This commit reduces the likelihood of getting a busy port on quick restarts with rsync SST (problem MDEV-25818) and fixes a number of other flaws in SST scripts, adds new functionality, and also synchronizes the xtrabackup-v2 script with the mariabackup script (the latter applies only to the 10.2 branch): 1) SST via rsync: rsync and stunnel does not always get the right time to complete by correctly handling SIGTERM. These utilities are now given more time to complete normally (via normal SIGTERM processing) before we move on to using "kill -9"; 2) SST via rsync: attempts to terminate an rsync or stunnel process (via "kill" utility) are only made if it did not terminated on its own; 3) SST via rsync: if a combination of stunnel and rsync is used, then we need to wait for both utilities to finish or stop, not just one of them; 4) The config file and pid file for stunnel are now deleted after successful completion of SST on the donor node; 5) The configs and pid files from rsync and stunnel should not be deleted unless these utilities succeed (or are sucessfully terminated) on the joiner node; 6) The configs and pid files now excluded from transfer via rsync; 7) Spaces in paths are now valid for config files as well (when used with SST via rsync or mariabackup / xtrabackup[-v2]); 8) SST via mariabackup: added preliminary verification of keys and certificates that are used when establishing a connection using SSL (to avoid long timeouts and improve diagnostics) - by analogy with how it is done for the xtrabackup-v2 (plus check for CA file), while that check is skipped if the user does not have openssl installed (or does not have diff utility); 9) Added backup-threads=<n> configuration option which adds "--parallel=<n>" for mariabackup / xtrabackup at backup and move-back stages; 10) Added encrypt-threads and encrypt-chunk-size configuration options for xbcrypt management (when xbcrypt is used); 11) Small optimization: checking the socat version and adding a file with parameters for 2048-bit Diffie-Hellman (if necessary) is done only if the user has not specified "dhparam=" in the "sockopt" option value; 12) SST via rsync now supports "backup-threads" configuration option (in server-related sections or in the "[sst]"); 13) Determining the number of available processors is now supported for FreeBSD + mariabackup/xtrabackup: before that we might have problems with "--compact" (rebuild indexes) or qpress on FreeBSD; 14) The check_pid() function should not raise an error state in the rare cases when the pid file was created, but it is empty, or if it is deleted right during the check, or when zero is read from the pid file; 15) Iproved templates that are used to check if a requested socket is "listening" when using the ss utility; 16) Shortened some other templates for socket state utilities; 17) Temporary files created by mariabackup / xtrabackup are moved to a separate subdirectory inside tmpdir (so they don't get mixed with other temporary files, which can make debugging more difficult); 18) 10.2 only: the script for SST via xtrabackup-v2 has been brought in full compliance with all the bugfixes made for mariabackup (as it previously contained many flaws compared to the updated script for mariabackup).
737 lines
22 KiB
Bash
737 lines
22 KiB
Bash
#!/bin/bash -ue
|
|
|
|
# Copyright (C) 2017-2021 MariaDB
|
|
# Copyright (C) 2010-2014 Codership Oy
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; version 2 of the License.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program; see the file COPYING. If not, write to the
|
|
# Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston
|
|
# MA 02110-1335 USA.
|
|
|
|
# This is a reference script for rsync-based state snapshot tansfer
|
|
|
|
RSYNC_REAL_PID=0 # rsync process id
|
|
STUNNEL_REAL_PID=0 # stunnel process id
|
|
|
|
OS="$(uname)"
|
|
[ "$OS" = 'Darwin' ] && export -n LD_LIBRARY_PATH
|
|
|
|
# Setting the path for lsof on CentOS
|
|
export PATH="/usr/sbin:/sbin:$PATH"
|
|
|
|
. $(dirname "$0")/wsrep_sst_common
|
|
wsrep_check_datadir
|
|
|
|
wsrep_check_programs rsync
|
|
|
|
cleanup_joiner()
|
|
{
|
|
local failure=0
|
|
|
|
wsrep_log_info "Joiner cleanup: rsync PID=$RSYNC_REAL_PID, stunnel PID=$STUNNEL_REAL_PID"
|
|
|
|
if [ -n "$STUNNEL" ]; then
|
|
if cleanup_pid $STUNNEL_REAL_PID "$STUNNEL_PID" "$STUNNEL_CONF"; then
|
|
if [ $RSYNC_REAL_PID -eq 0 ]; then
|
|
if [ -r "$RSYNC_PID" ]; then
|
|
RSYNC_REAL_PID=$(cat "$RSYNC_PID" 2>/dev/null)
|
|
if [ -z "$RSYNC_REAL_PID" ]; then
|
|
RSYNC_REAL_PID=0
|
|
fi
|
|
fi
|
|
fi
|
|
else
|
|
wsrep_log_warning "stunnel cleanup failed."
|
|
failure=1
|
|
fi
|
|
fi
|
|
|
|
if [ $failure -eq 0 ]; then
|
|
if cleanup_pid $RSYNC_REAL_PID "$RSYNC_PID" "$RSYNC_CONF"; then
|
|
[ -f "$MAGIC_FILE" ] && rm -f "$MAGIC_FILE"
|
|
else
|
|
wsrep_log_warning "rsync cleanup failed."
|
|
fi
|
|
fi
|
|
|
|
wsrep_log_info "Joiner cleanup done."
|
|
|
|
if [ "$WSREP_SST_OPT_ROLE" = 'joiner' ]; then
|
|
wsrep_cleanup_progress_file
|
|
fi
|
|
}
|
|
|
|
check_pid_and_port()
|
|
{
|
|
local pid_file="$1"
|
|
local pid=$2
|
|
local addr="$3"
|
|
local port="$4"
|
|
|
|
local utils='rsync|stunnel'
|
|
|
|
if ! check_port "$pid" "$port" "$utils"; then
|
|
local port_info
|
|
local busy=0
|
|
|
|
if [ $lsof_available -ne 0 ]; then
|
|
port_info=$(lsof -Pnl -i ":$port" 2>/dev/null | \
|
|
grep -F '(LISTEN)')
|
|
echo "$port_info" | \
|
|
grep -q -E "[[:space:]](\\*|\\[?::\\]?):$port[[:space:]]" && busy=1
|
|
else
|
|
local filter='([^[:space:]]+[[:space:]]+){4}[^[:space:]]+'
|
|
if [ $sockstat_available -eq 1 ]; then
|
|
port_info=$(sockstat -p "$port" 2>/dev/null | \
|
|
grep -E '[[:space:]]LISTEN' | grep -o -E "$filter")
|
|
else
|
|
port_info=$(ss -nlpH "( sport = :$port )" 2>/dev/null | \
|
|
grep -F 'users:(' | grep -o -E "$filter")
|
|
fi
|
|
echo "$port_info" | \
|
|
grep -q -E "[[:space:]](\\*|\\[?::\\]?):$port\$" && busy=1
|
|
fi
|
|
|
|
if [ $busy -eq 0 ]; then
|
|
if echo "$port_info" | grep -qw -F "[$addr]:$port" || \
|
|
echo "$port_info" | grep -qw -F -- "$addr:$port"
|
|
then
|
|
busy=1
|
|
fi
|
|
fi
|
|
|
|
if [ $busy -eq 0 ]; then
|
|
return 1
|
|
fi
|
|
|
|
if ! check_port "$pid" "$port" "$utils"; then
|
|
wsrep_log_error "rsync or stunnel daemon port '$port' " \
|
|
"has been taken by another program"
|
|
exit 16 # EBUSY
|
|
fi
|
|
fi
|
|
|
|
check_pid "$pid_file" && [ $CHECK_PID -eq $pid ]
|
|
}
|
|
|
|
STUNNEL_CONF="$WSREP_SST_OPT_DATA/stunnel.conf"
|
|
STUNNEL_PID="$WSREP_SST_OPT_DATA/stunnel.pid"
|
|
|
|
MAGIC_FILE="$WSREP_SST_OPT_DATA/rsync_sst_complete"
|
|
|
|
BINLOG_TAR_FILE="$WSREP_SST_OPT_DATA/wsrep_sst_binlog.tar"
|
|
BINLOG_N_FILES=1
|
|
|
|
get_binlog
|
|
|
|
if [ -n "$WSREP_SST_OPT_BINLOG" ]; then
|
|
BINLOG_DIRNAME=$(dirname "$WSREP_SST_OPT_BINLOG")
|
|
BINLOG_FILENAME=$(basename "$WSREP_SST_OPT_BINLOG")
|
|
fi
|
|
|
|
# if no command line argument and INNODB_LOG_GROUP_HOME is not set,
|
|
# try to get it from my.cnf:
|
|
if [ -z "$INNODB_LOG_GROUP_HOME" ]; then
|
|
INNODB_LOG_GROUP_HOME=$(parse_cnf '--mysqld' 'innodb-log-group-home-dir')
|
|
fi
|
|
|
|
OLD_PWD="$(pwd)"
|
|
|
|
WSREP_LOG_DIR="$INNODB_LOG_GROUP_HOME"
|
|
|
|
cd "$WSREP_SST_OPT_DATA"
|
|
if [ -n "$WSREP_LOG_DIR" ]; then
|
|
# handle both relative and absolute paths
|
|
[ ! -d "$WSREP_LOG_DIR" ] && mkdir -p "$WSREP_LOG_DIR"
|
|
cd "$WSREP_LOG_DIR"
|
|
fi
|
|
WSREP_LOG_DIR=$(pwd -P)
|
|
|
|
cd "$OLD_PWD"
|
|
|
|
# if no command line argument and INNODB_DATA_HOME_DIR environment variable
|
|
# is not set, try to get it from my.cnf:
|
|
if [ -z "$INNODB_DATA_HOME_DIR" ]; then
|
|
INNODB_DATA_HOME_DIR=$(parse_cnf '--mysqld' 'innodb-data-home-dir')
|
|
fi
|
|
|
|
cd "$WSREP_SST_OPT_DATA"
|
|
if [ -n "$INNODB_DATA_HOME_DIR" ]; then
|
|
# handle both relative and absolute paths
|
|
[ ! -d "$INNODB_DATA_HOME_DIR" ] && mkdir -p "$INNODB_DATA_HOME_DIR"
|
|
cd "$INNODB_DATA_HOME_DIR"
|
|
fi
|
|
INNODB_DATA_HOME_DIR=$(pwd -P)
|
|
|
|
cd "$OLD_PWD"
|
|
|
|
# if no command line argument then try to get it from my.cnf:
|
|
if [ -z "$INNODB_UNDO_DIR" ]; then
|
|
INNODB_UNDO_DIR=$(parse_cnf '--mysqld' 'innodb-undo-directory')
|
|
fi
|
|
|
|
cd "$WSREP_SST_OPT_DATA"
|
|
if [ -n "$INNODB_UNDO_DIR" ]; then
|
|
# handle both relative and absolute paths
|
|
[ ! -d "$INNODB_UNDO_DIR" ] && mkdir -p "$INNODB_UNDO_DIR"
|
|
cd "$INNODB_UNDO_DIR"
|
|
fi
|
|
INNODB_UNDO_DIR=$(pwd -P)
|
|
|
|
cd "$OLD_PWD"
|
|
|
|
# Old filter - include everything except selected
|
|
# FILTER=(--exclude '*.err' --exclude '*.pid' --exclude '*.sock' \
|
|
# --exclude '*.conf' --exclude core --exclude 'galera.*' \
|
|
# --exclude grastate.txt --exclude '*.pem' \
|
|
# --exclude '*.[0-9][0-9][0-9][0-9][0-9][0-9]' --exclude '*.index')
|
|
|
|
# New filter - exclude everything except dirs (schemas) and innodb files
|
|
FILTER="-f '- /lost+found'
|
|
-f '- /.zfs'
|
|
-f '- /.fseventsd'
|
|
-f '- /.Trashes'
|
|
-f '- /.pid'
|
|
-f '- /.conf'
|
|
-f '+ /wsrep_sst_binlog.tar'
|
|
-f '- $INNODB_DATA_HOME_DIR/ib_lru_dump'
|
|
-f '- $INNODB_DATA_HOME_DIR/ibdata*'
|
|
-f '+ $INNODB_UNDO_DIR/undo*'
|
|
-f '+ /*/'
|
|
-f '- /*'"
|
|
|
|
# old-style SSL config
|
|
SSTKEY=$(parse_cnf 'sst' 'tkey')
|
|
SSTCERT=$(parse_cnf 'sst' 'tcert')
|
|
SSTCA=$(parse_cnf 'sst' 'tca')
|
|
|
|
SST_SECTIONS="--mysqld|sst"
|
|
|
|
check_server_ssl_config()
|
|
{
|
|
SSTKEY=$(parse_cnf "$SST_SECTIONS" 'ssl-key')
|
|
SSTCERT=$(parse_cnf "$SST_SECTIONS" 'ssl-cert')
|
|
SSTCA=$(parse_cnf "$SST_SECTIONS" 'ssl-ca')
|
|
}
|
|
|
|
SSLMODE=$(parse_cnf "$SST_SECTIONS" 'ssl-mode' | tr [:lower:] [:upper:])
|
|
|
|
# no old-style SSL config in [sst], check for new one:
|
|
if [ -z "$SSTKEY" -a -z "$SSTCERT" -a -z "$SSTCA" ]
|
|
then
|
|
check_server_ssl_config
|
|
fi
|
|
|
|
if [ -z "$SSLMODE" ]; then
|
|
# Implicit verification if CA is set and the SSL mode
|
|
# is not specified by user:
|
|
if [ -n "$SSTCA" ]; then
|
|
if [ -n "$(command -v stunnel)" ]; then
|
|
SSLMODE='VERIFY_CA'
|
|
fi
|
|
# Require SSL by default if SSL key and cert are present:
|
|
elif [ -n "$SSTKEY" -a -n "$SSTCERT" ]; then
|
|
SSLMODE='REQUIRED'
|
|
fi
|
|
fi
|
|
|
|
if [ -n "$SSTCA" ]
|
|
then
|
|
CAFILE_OPT="CAfile = $SSTCA"
|
|
else
|
|
CAFILE_OPT=""
|
|
fi
|
|
|
|
VERIFY_OPT=""
|
|
CHECK_OPT=""
|
|
CHECK_OPT_LOCAL=""
|
|
if [ "${SSLMODE#VERIFY}" != "$SSLMODE" ]
|
|
then
|
|
case "$SSLMODE" in
|
|
'VERIFY_IDENTITY')
|
|
VERIFY_OPT='verifyPeer = yes'
|
|
;;
|
|
'VERIFY_CA')
|
|
VERIFY_OPT='verifyChain = yes'
|
|
if [ -n "$WSREP_SST_OPT_REMOTE_USER" ]; then
|
|
CHECK_OPT="checkHost = $WSREP_SST_OPT_REMOTE_USER"
|
|
else
|
|
# check if the address is an ip-address (v4 or v6):
|
|
if echo "$WSREP_SST_OPT_HOST_UNESCAPED" | \
|
|
grep -q -E '^([0-9]+(\.[0-9]+){3}|[0-9a-fA-F]*(\:[0-9a-fA-F]*)+)$'
|
|
then
|
|
CHECK_OPT="checkIP = $WSREP_SST_OPT_HOST_UNESCAPED"
|
|
else
|
|
CHECK_OPT="checkHost = $WSREP_SST_OPT_HOST"
|
|
fi
|
|
if is_local_ip "$WSREP_SST_OPT_HOST_UNESCAPED"; then
|
|
CHECK_OPT_LOCAL="checkHost = localhost"
|
|
fi
|
|
fi
|
|
;;
|
|
*)
|
|
wsrep_log_error "Unrecognized ssl-mode option: '$SSLMODE'"
|
|
exit 22 # EINVAL
|
|
esac
|
|
if [ -z "$CAFILE_OPT" ]; then
|
|
wsrep_log_error "Can't have ssl-mode='$SSLMODE' without CA file"
|
|
exit 22 # EINVAL
|
|
fi
|
|
fi
|
|
|
|
STUNNEL=""
|
|
if [ -n "$SSLMODE" -a "$SSLMODE" != 'DISABLED' ]; then
|
|
STUNNEL_BIN="$(command -v stunnel)"
|
|
if [ -n "$STUNNEL_BIN" ]; then
|
|
wsrep_log_info "Using stunnel for SSL encryption: CAfile: '$SSTCA', ssl-mode='$SSLMODE'"
|
|
STUNNEL="$STUNNEL_BIN $STUNNEL_CONF"
|
|
fi
|
|
fi
|
|
|
|
readonly SECRET_TAG="secret"
|
|
|
|
if [ "$WSREP_SST_OPT_ROLE" = 'donor' ]
|
|
then
|
|
|
|
[ -f "$MAGIC_FILE" ] && rm -f "$MAGIC_FILE"
|
|
[ -f "$BINLOG_TAR_FILE" ] && rm -f "$BINLOG_TAR_FILE"
|
|
[ -f "$STUNNEL_PID" ] && rm -f "$STUNNEL_PID"
|
|
|
|
if [ -n "$STUNNEL" ]
|
|
then
|
|
cat << EOF > "$STUNNEL_CONF"
|
|
key = $SSTKEY
|
|
cert = $SSTCERT
|
|
${CAFILE_OPT}
|
|
foreground = yes
|
|
pid = $STUNNEL_PID
|
|
debug = warning
|
|
client = yes
|
|
connect = $WSREP_SST_OPT_HOST_UNESCAPED:$WSREP_SST_OPT_PORT
|
|
TIMEOUTclose = 0
|
|
${VERIFY_OPT}
|
|
${CHECK_OPT}
|
|
${CHECK_OPT_LOCAL}
|
|
EOF
|
|
else
|
|
[ -f "$STUNNEL_CONF" ] && rm -f "$STUNNEL_CONF"
|
|
fi
|
|
|
|
if [ $WSREP_SST_OPT_BYPASS -eq 0 ]
|
|
then
|
|
|
|
FLUSHED="$WSREP_SST_OPT_DATA/tables_flushed"
|
|
ERROR="$WSREP_SST_OPT_DATA/sst_error"
|
|
|
|
[ -f "$FLUSHED" ] && rm -f "$FLUSHED"
|
|
[ -f "$ERROR" ] && rm -f "$ERROR"
|
|
|
|
echo "flush tables"
|
|
|
|
# Wait for :
|
|
# (a) Tables to be flushed, AND
|
|
# (b) Cluster state ID & wsrep_gtid_domain_id to be written to the file, OR
|
|
# (c) ERROR file, in case flush tables operation failed.
|
|
|
|
while [ ! -r "$FLUSHED" ] && ! grep -q -F ':' "$FLUSHED" >/dev/null 2>&1
|
|
do
|
|
# Check whether ERROR file exists.
|
|
if [ -f "$ERROR" ]
|
|
then
|
|
# Flush tables operation failed.
|
|
rm -f "$ERROR"
|
|
exit 255
|
|
fi
|
|
sleep 0.2
|
|
done
|
|
|
|
STATE=$(cat "$FLUSHED")
|
|
rm -f "$FLUSHED"
|
|
|
|
sync
|
|
|
|
if [ -n "$WSREP_SST_OPT_BINLOG" ]
|
|
then
|
|
# Prepare binlog files
|
|
cd "$BINLOG_DIRNAME"
|
|
|
|
binlog_files_full=$(tail -n $BINLOG_N_FILES "${WSREP_SST_OPT_BINLOG_INDEX%.index}.index")
|
|
|
|
binlog_files=""
|
|
for ii in $binlog_files_full
|
|
do
|
|
binlog_file=$(basename "$ii")
|
|
binlog_files="$binlog_files $binlog_file"
|
|
done
|
|
|
|
if [ -n "$binlog_files" ]
|
|
then
|
|
wsrep_log_info "Preparing binlog files for transfer:"
|
|
tar -cvf "$BINLOG_TAR_FILE" $binlog_files >&2
|
|
fi
|
|
|
|
cd "$OLD_PWD"
|
|
fi
|
|
|
|
# Use deltaxfer only for WAN
|
|
inv=$(basename "$0")
|
|
WHOLE_FILE_OPT=""
|
|
if [ "${inv%wsrep_sst_rsync_wan*}" != "$inv" ]; then
|
|
WHOLE_FILE_OPT="--whole-file"
|
|
fi
|
|
|
|
# first, the normal directories, so that we can detect incompatible protocol
|
|
RC=0
|
|
eval rsync ${STUNNEL:+"'--rsh=$STUNNEL'"} \
|
|
--owner --group --perms --links --specials \
|
|
--ignore-times --inplace --dirs --delete --quiet \
|
|
$WHOLE_FILE_OPT $FILTER "'$WSREP_SST_OPT_DATA/'" \
|
|
"'rsync://$WSREP_SST_OPT_ADDR'" >&2 || RC=$?
|
|
|
|
if [ $RC -ne 0 ]; then
|
|
wsrep_log_error "rsync returned code $RC:"
|
|
case $RC in
|
|
12) RC=71 # EPROTO
|
|
wsrep_log_error \
|
|
"rsync server on the other end has incompatible protocol. " \
|
|
"Make sure you have the same version of rsync on all nodes."
|
|
;;
|
|
22) RC=12 # ENOMEM
|
|
;;
|
|
*) RC=255 # unknown error
|
|
;;
|
|
esac
|
|
exit $RC
|
|
fi
|
|
|
|
# Transfer InnoDB data files
|
|
rsync ${STUNNEL:+--rsh="$STUNNEL"} \
|
|
--owner --group --perms --links --specials \
|
|
--ignore-times --inplace --dirs --delete --quiet \
|
|
$WHOLE_FILE_OPT -f '+ /ibdata*' -f '+ /ib_lru_dump' \
|
|
-f '- **' "$INNODB_DATA_HOME_DIR/" \
|
|
"rsync://$WSREP_SST_OPT_ADDR-data_dir" >&2 || RC=$?
|
|
|
|
if [ $RC -ne 0 ]; then
|
|
wsrep_log_error "rsync innodb_data_home_dir returned code $RC:"
|
|
exit 255 # unknown error
|
|
fi
|
|
|
|
# second, we transfer InnoDB log files
|
|
rsync ${STUNNEL:+--rsh="$STUNNEL"} \
|
|
--owner --group --perms --links --specials \
|
|
--ignore-times --inplace --dirs --delete --quiet \
|
|
$WHOLE_FILE_OPT -f '+ /ib_logfile[0-9]*' -f '+ /aria_log.*' \
|
|
-f '+ /aria_log_control' -f '- **' "$WSREP_LOG_DIR/" \
|
|
"rsync://$WSREP_SST_OPT_ADDR-log_dir" >&2 || RC=$?
|
|
|
|
if [ $RC -ne 0 ]; then
|
|
wsrep_log_error "rsync innodb_log_group_home_dir returned code $RC:"
|
|
exit 255 # unknown error
|
|
fi
|
|
|
|
# then, we parallelize the transfer of database directories,
|
|
# use '.' so that path concatenation works:
|
|
|
|
cd "$WSREP_SST_OPT_DATA"
|
|
|
|
backup_threads=$(parse_cnf "--mysqld|sst" 'backup-threads')
|
|
if [ -z "$backup_threads" ]; then
|
|
get_proc
|
|
backup_threads=$nproc
|
|
fi
|
|
|
|
find . -maxdepth 1 -mindepth 1 -type d -not -name 'lost+found' \
|
|
-not -name '.zfs' -print0 | xargs -I{} -0 -P $backup_threads \
|
|
rsync ${STUNNEL:+--rsh="$STUNNEL"} \
|
|
--owner --group --perms --links --specials \
|
|
--ignore-times --inplace --recursive --delete --quiet \
|
|
$WHOLE_FILE_OPT --exclude '*/ib_logfile*' --exclude '*/aria_log.*' \
|
|
--exclude '*/aria_log_control' "$WSREP_SST_OPT_DATA/{}/" \
|
|
"rsync://$WSREP_SST_OPT_ADDR/{}" >&2 || RC=$?
|
|
|
|
cd "$OLD_PWD"
|
|
|
|
if [ $RC -ne 0 ]; then
|
|
wsrep_log_error "find/rsync returned code $RC:"
|
|
exit 255 # unknown error
|
|
fi
|
|
|
|
else # BYPASS
|
|
|
|
wsrep_log_info "Bypassing state dump."
|
|
|
|
# Store donor's wsrep GTID (state ID) and wsrep_gtid_domain_id
|
|
# (separated by a space).
|
|
STATE="$WSREP_SST_OPT_GTID $WSREP_SST_OPT_GTID_DOMAIN_ID"
|
|
|
|
fi
|
|
|
|
echo 'continue' # now server can resume updating data
|
|
|
|
echo "$STATE" > "$MAGIC_FILE"
|
|
|
|
if [ -n "$WSREP_SST_OPT_REMOTE_PSWD" ]; then
|
|
# Let joiner know that we know its secret
|
|
echo "$SECRET_TAG $WSREP_SST_OPT_REMOTE_PSWD" >> "$MAGIC_FILE"
|
|
fi
|
|
|
|
rsync ${STUNNEL:+--rsh="$STUNNEL"} \
|
|
--archive --quiet --checksum "$MAGIC_FILE" "rsync://$WSREP_SST_OPT_ADDR"
|
|
|
|
echo "done $STATE"
|
|
|
|
if [ -n "$STUNNEL" ]; then
|
|
[ -f "$STUNNEL_CONF" ] && rm -f "$STUNNEL_CONF"
|
|
[ -f "$STUNNEL_PID" ] && rm -f "$STUNNEL_PID"
|
|
fi
|
|
|
|
elif [ "$WSREP_SST_OPT_ROLE" = 'joiner' ]
|
|
then
|
|
check_sockets_utils
|
|
|
|
# give some time for lingering stunnel from previous SST to complete
|
|
check_round=0
|
|
while check_pid "$STUNNEL_PID" 1
|
|
do
|
|
wsrep_log_info "lingering stunnel daemon found at startup, waiting for it to exit"
|
|
check_round=$(( check_round + 1 ))
|
|
if [ $check_round -eq 10 ]; then
|
|
wsrep_log_error "stunnel daemon already running."
|
|
exit 114 # EALREADY
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
MODULE="rsync_sst"
|
|
RSYNC_PID="$WSREP_SST_OPT_DATA/$MODULE.pid"
|
|
RSYNC_CONF="$WSREP_SST_OPT_DATA/$MODULE.conf"
|
|
|
|
# give some time for lingering rsync from previous SST to complete
|
|
check_round=0
|
|
while check_pid "$RSYNC_PID" 1
|
|
do
|
|
wsrep_log_info "lingering rsync daemon found at startup, waiting for it to exit"
|
|
check_round=$(( check_round + 1 ))
|
|
if [ $check_round -eq 10 ]; then
|
|
wsrep_log_error "rsync daemon already running."
|
|
exit 114 # EALREADY
|
|
fi
|
|
sleep 1
|
|
done
|
|
|
|
[ -f "$MAGIC_FILE" ] && rm -f "$MAGIC_FILE"
|
|
[ -f "$BINLOG_TAR_FILE" ] && rm -f "$BINLOG_TAR_FILE"
|
|
|
|
if [ -z "$STUNNEL" ]; then
|
|
[ -f "$STUNNEL_CONF" ] && rm -f "$STUNNEL_CONF"
|
|
fi
|
|
|
|
ADDR="$WSREP_SST_OPT_ADDR"
|
|
RSYNC_PORT="$WSREP_SST_OPT_PORT"
|
|
RSYNC_ADDR="$WSREP_SST_OPT_HOST"
|
|
RSYNC_ADDR_UNESCAPED="$WSREP_SST_OPT_HOST_UNESCAPED"
|
|
|
|
trap "exit 32" HUP PIPE
|
|
trap "exit 3" INT TERM ABRT
|
|
trap cleanup_joiner EXIT
|
|
|
|
touch "$SST_PROGRESS_FILE"
|
|
|
|
if [ -n "${MYSQL_TMP_DIR:-}" ]; then
|
|
SILENT="log file = $MYSQL_TMP_DIR/rsyncd.log"
|
|
else
|
|
SILENT=""
|
|
fi
|
|
|
|
cat << EOF > "$RSYNC_CONF"
|
|
pid file = $RSYNC_PID
|
|
use chroot = no
|
|
read only = no
|
|
timeout = 300
|
|
$SILENT
|
|
[$MODULE]
|
|
path = $WSREP_SST_OPT_DATA
|
|
exclude = .zfs
|
|
[$MODULE-log_dir]
|
|
path = $WSREP_LOG_DIR
|
|
[$MODULE-data_dir]
|
|
path = $INNODB_DATA_HOME_DIR
|
|
EOF
|
|
|
|
# rm -rf "$DATA/ib_logfile"* # we don't want old logs around
|
|
|
|
# If the IP is local, listen only on it:
|
|
if is_local_ip "$RSYNC_ADDR_UNESCAPED"
|
|
then
|
|
RSYNC_EXTRA_ARGS="--address $RSYNC_ADDR_UNESCAPED"
|
|
STUNNEL_ACCEPT="$RSYNC_ADDR_UNESCAPED:$RSYNC_PORT"
|
|
else
|
|
# Not local, possibly a NAT, listen on all interfaces:
|
|
RSYNC_EXTRA_ARGS=""
|
|
STUNNEL_ACCEPT="$RSYNC_PORT"
|
|
# Overwrite address with all:
|
|
RSYNC_ADDR="*"
|
|
fi
|
|
|
|
if [ -z "$STUNNEL" ]
|
|
then
|
|
rsync --daemon --no-detach --port "$RSYNC_PORT" --config "$RSYNC_CONF" $RSYNC_EXTRA_ARGS &
|
|
RSYNC_REAL_PID=$!
|
|
TRANSFER_REAL_PID="$RSYNC_REAL_PID"
|
|
TRANSFER_PID=$RSYNC_PID
|
|
else
|
|
# Let's check if the path to the config file contains a space?
|
|
if [ "${RSYNC_CONF#* }" = "$RSYNC_CONF" ]; then
|
|
cat << EOF > "$STUNNEL_CONF"
|
|
key = $SSTKEY
|
|
cert = $SSTCERT
|
|
${CAFILE_OPT}
|
|
foreground = yes
|
|
pid = $STUNNEL_PID
|
|
debug = warning
|
|
client = no
|
|
${VERIFY_OPT}
|
|
${CHECK_OPT}
|
|
${CHECK_OPT_LOCAL}
|
|
[rsync]
|
|
accept = $STUNNEL_ACCEPT
|
|
exec = $(command -v rsync)
|
|
execargs = rsync --server --daemon --config=$RSYNC_CONF .
|
|
EOF
|
|
else
|
|
# The path contains a space, so we will run it via
|
|
# shell with "eval" command:
|
|
export RSYNC_CMD="eval $(command -v rsync) --server --daemon --config='$RSYNC_CONF' ."
|
|
cat << EOF > "$STUNNEL_CONF"
|
|
key = $SSTKEY
|
|
cert = $SSTCERT
|
|
${CAFILE_OPT}
|
|
foreground = yes
|
|
pid = $STUNNEL_PID
|
|
debug = warning
|
|
client = no
|
|
${VERIFY_OPT}
|
|
${CHECK_OPT}
|
|
${CHECK_OPT_LOCAL}
|
|
[rsync]
|
|
accept = $STUNNEL_ACCEPT
|
|
exec = $SHELL
|
|
execargs = $SHELL -c \$RSYNC_CMD
|
|
EOF
|
|
fi
|
|
stunnel "$STUNNEL_CONF" &
|
|
STUNNEL_REAL_PID=$!
|
|
TRANSFER_REAL_PID="$STUNNEL_REAL_PID"
|
|
TRANSFER_PID=$STUNNEL_PID
|
|
fi
|
|
|
|
if [ "${SSLMODE#VERIFY}" != "$SSLMODE" ]
|
|
then # backward-incompatible behavior
|
|
CN=""
|
|
if [ -n "$SSTCERT" ]
|
|
then
|
|
# find out my Common Name
|
|
get_openssl
|
|
if [ -z "$OPENSSL_BINARY" ]; then
|
|
wsrep_log_error 'openssl not found but it is required for authentication'
|
|
exit 42
|
|
fi
|
|
CN=$("$OPENSSL_BINARY" x509 -noout -subject -in "$SSTCERT" | \
|
|
tr "," "\n" | grep -F 'CN =' | cut -d= -f2 | sed s/^\ // | \
|
|
sed s/\ %//)
|
|
fi
|
|
MY_SECRET="$(wsrep_gen_secret)"
|
|
# Add authentication data to address
|
|
ADDR="$CN:$MY_SECRET@$WSREP_SST_OPT_HOST"
|
|
else
|
|
MY_SECRET="" # for check down in recv_joiner()
|
|
ADDR="$WSREP_SST_OPT_HOST"
|
|
fi
|
|
|
|
until check_pid_and_port "$TRANSFER_PID" $TRANSFER_REAL_PID "$RSYNC_ADDR_UNESCAPED" "$RSYNC_PORT"
|
|
do
|
|
sleep 0.2
|
|
done
|
|
|
|
echo "ready $ADDR:$RSYNC_PORT/$MODULE"
|
|
|
|
MYSQLD_PID="$WSREP_SST_OPT_PARENT"
|
|
|
|
# wait for SST to complete by monitoring magic file
|
|
while [ ! -r "$MAGIC_FILE" ] && check_pid "$TRANSFER_PID" && \
|
|
ps -p $MYSQLD_PID >/dev/null 2>&1
|
|
do
|
|
sleep 1
|
|
done
|
|
|
|
if ! ps -p $MYSQLD_PID >/dev/null 2>&1
|
|
then
|
|
wsrep_log_error \
|
|
"Parent mysqld process (PID: $MYSQLD_PID) terminated unexpectedly."
|
|
kill -- -$MYSQLD_PID
|
|
sleep 1
|
|
exit 32
|
|
fi
|
|
|
|
if [ -n "$WSREP_SST_OPT_BINLOG" ]; then
|
|
if [ -f "$BINLOG_TAR_FILE" ]; then
|
|
cd "$BINLOG_DIRNAME"
|
|
|
|
binlog_index="${WSREP_SST_OPT_BINLOG_INDEX%.index}.index"
|
|
|
|
# Clean up old binlog files first
|
|
rm -f "$BINLOG_FILENAME".[0-9]*
|
|
[ -f "$binlog_index" ] && rm "$binlog_index"
|
|
|
|
wsrep_log_info "Extracting binlog files:"
|
|
tar -xvf "$BINLOG_TAR_FILE" >> _binlog_tmp_files_$!
|
|
while read bin_file; do
|
|
echo "$BINLOG_DIRNAME/$bin_file" >> "$binlog_index"
|
|
done < _binlog_tmp_files_$!
|
|
rm -f _binlog_tmp_files_$!
|
|
|
|
cd "$OLD_PWD"
|
|
fi
|
|
fi
|
|
|
|
if [ -r "$MAGIC_FILE" ]
|
|
then
|
|
# check donor supplied secret
|
|
SECRET=$(grep -F -- "$SECRET_TAG " "$MAGIC_FILE" 2>/dev/null | cut -d ' ' -f 2)
|
|
if [ "$SECRET" != "$MY_SECRET" ]; then
|
|
wsrep_log_error "Donor does not know my secret!"
|
|
wsrep_log_info "Donor:'$SECRET', my:'$MY_SECRET'"
|
|
exit 32
|
|
fi
|
|
|
|
# remove secret from magic file
|
|
grep -v -F -- "$SECRET_TAG " "$MAGIC_FILE" > "$MAGIC_FILE.new"
|
|
|
|
mv "$MAGIC_FILE.new" "$MAGIC_FILE"
|
|
# UUID:seqno & wsrep_gtid_domain_id is received here.
|
|
cat "$MAGIC_FILE" # Output : UUID:seqno wsrep_gtid_domain_id
|
|
else
|
|
# this message should cause joiner to abort
|
|
echo "rsync process ended without creating '$MAGIC_FILE'"
|
|
fi
|
|
|
|
# wsrep_cleanup_progress_file
|
|
# cleanup_joiner
|
|
else
|
|
wsrep_log_error "Unrecognized role: '$WSREP_SST_OPT_ROLE'"
|
|
exit 22 # EINVAL
|
|
fi
|
|
|
|
[ -f "$BINLOG_TAR_FILE" ] && rm -f "$BINLOG_TAR_FILE"
|
|
|
|
exit 0
|