summaryrefslogtreecommitdiffstats
path: root/scripts/wsrep_sst_rsync.sh
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--scripts/wsrep_sst_rsync.sh1011
1 files changed, 1011 insertions, 0 deletions
diff --git a/scripts/wsrep_sst_rsync.sh b/scripts/wsrep_sst_rsync.sh
new file mode 100644
index 00000000..a8bfc413
--- /dev/null
+++ b/scripts/wsrep_sst_rsync.sh
@@ -0,0 +1,1011 @@
+#!/usr/bin/env bash
+
+set -ue
+
+# Copyright (C) 2017-2022 MariaDB
+# Copyright (C) 2010-2022 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 transfer
+
+RSYNC_REAL_PID=0 # rsync process id
+STUNNEL_REAL_PID=0 # stunnel process id
+
+OS="$(uname)"
+[ "$OS" = 'Darwin' ] && export -n LD_LIBRARY_PATH
+
+. $(dirname "$0")/wsrep_sst_common
+wsrep_check_datadir
+
+wsrep_check_programs rsync
+
+cleanup_joiner()
+{
+ # Since this is invoked just after exit NNN
+ local estatus=$?
+ if [ $estatus -ne 0 ]; then
+ wsrep_log_error "Cleanup after exit with status: $estatus"
+ elif [ -z "${coords:-}" ]; then
+ estatus=32
+ wsrep_log_error "Failed to get current position"
+ fi
+
+ local failure=0
+
+ [ "$(pwd)" != "$OLD_PWD" ] && cd "$OLD_PWD"
+
+ 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" || :
+ [ -f "$BINLOG_TAR_FILE" ] && rm -f "$BINLOG_TAR_FILE" || :
+ else
+ wsrep_log_warning "rsync cleanup failed."
+ fi
+ fi
+
+ if [ "$WSREP_SST_OPT_ROLE" = 'joiner' ]; then
+ wsrep_cleanup_progress_file
+ fi
+
+ [ -f "$SST_PID" ] && rm -f "$SST_PID" || :
+
+ wsrep_log_info "Joiner cleanup done."
+
+ exit $estatus
+}
+
+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 -ne 0 ]; then
+ local opts='-p'
+ if [ "$OS" = 'FreeBSD' ]; then
+ # sockstat on FreeBSD requires the "-s" option
+ # to display the connection state:
+ opts='-sp'
+ # in addition, sockstat produces an additional column:
+ filter='([^[:space:]]+[[:space:]]+){5}[^[:space:]]+'
+ fi
+ port_info=$(sockstat "$opts" "$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
+ if ! ps -p $pid >/dev/null 2>&1; then
+ wsrep_log_error \
+ "rsync or stunnel daemon (PID: $pid)" \
+ "terminated unexpectedly."
+ exit 16 # EBUSY
+ fi
+ return 1
+ fi
+ 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"
+
+get_binlog
+
+if [ -n "$WSREP_SST_OPT_BINLOG" ]; then
+ binlog_dir=$(dirname "$WSREP_SST_OPT_BINLOG")
+ binlog_base=$(basename "$WSREP_SST_OPT_BINLOG")
+fi
+
+OLD_PWD="$(pwd)"
+
+DATA="$WSREP_SST_OPT_DATA"
+if [ -n "$DATA" -a "$DATA" != '.' ]; then
+ [ ! -d "$DATA" ] && mkdir -p "$DATA"
+ cd "$DATA"
+fi
+DATA_DIR="$(pwd)"
+
+cd "$OLD_PWD"
+
+BINLOG_TAR_FILE="$DATA_DIR/wsrep_sst_binlog.tar"
+
+ar_log_dir="$DATA_DIR"
+ib_log_dir="$DATA_DIR"
+ib_home_dir="$DATA_DIR"
+ib_undo_dir="$DATA_DIR"
+
+# if no command line argument and INNODB_LOG_GROUP_HOME is not set,
+# then try to get it from the my.cnf:
+if [ -z "$INNODB_LOG_GROUP_HOME" ]; then
+ INNODB_LOG_GROUP_HOME=$(parse_cnf '--mysqld' 'innodb-log-group-home-dir')
+ INNODB_LOG_GROUP_HOME=$(trim_dir "$INNODB_LOG_GROUP_HOME")
+fi
+
+if [ -n "$INNODB_LOG_GROUP_HOME" -a "$INNODB_LOG_GROUP_HOME" != '.' -a \
+ "$INNODB_LOG_GROUP_HOME" != "$DATA_DIR" ]
+then
+ # handle both relative and absolute paths:
+ cd "$DATA"
+ [ ! -d "$INNODB_LOG_GROUP_HOME" ] && mkdir -p "$INNODB_LOG_GROUP_HOME"
+ cd "$INNODB_LOG_GROUP_HOME"
+ ib_log_dir="$(pwd)"
+ cd "$OLD_PWD"
+fi
+
+# if no command line argument and INNODB_DATA_HOME_DIR environment
+# variable is not set, try to get it from the my.cnf:
+if [ -z "$INNODB_DATA_HOME_DIR" ]; then
+ INNODB_DATA_HOME_DIR=$(parse_cnf '--mysqld' 'innodb-data-home-dir')
+ INNODB_DATA_HOME_DIR=$(trim_dir "$INNODB_DATA_HOME_DIR")
+fi
+
+if [ -n "$INNODB_DATA_HOME_DIR" -a "$INNODB_DATA_HOME_DIR" != '.' -a \
+ "$INNODB_DATA_HOME_DIR" != "$DATA_DIR" ]
+then
+ # handle both relative and absolute paths:
+ cd "$DATA"
+ [ ! -d "$INNODB_DATA_HOME_DIR" ] && mkdir -p "$INNODB_DATA_HOME_DIR"
+ cd "$INNODB_DATA_HOME_DIR"
+ ib_home_dir="$(pwd)"
+ cd "$OLD_PWD"
+fi
+
+# if no command line argument and INNODB_UNDO_DIR is not set,
+# then try to get it from the my.cnf:
+if [ -z "$INNODB_UNDO_DIR" ]; then
+ INNODB_UNDO_DIR=$(parse_cnf '--mysqld' 'innodb-undo-directory')
+ INNODB_UNDO_DIR=$(trim_dir "$INNODB_UNDO_DIR")
+fi
+
+if [ -n "$INNODB_UNDO_DIR" -a "$INNODB_UNDO_DIR" != '.' -a \
+ "$INNODB_UNDO_DIR" != "$DATA_DIR" ]
+then
+ # handle both relative and absolute paths:
+ cd "$DATA"
+ [ ! -d "$INNODB_UNDO_DIR" ] && mkdir -p "$INNODB_UNDO_DIR"
+ cd "$INNODB_UNDO_DIR"
+ ib_undo_dir="$(pwd)"
+ cd "$OLD_PWD"
+fi
+
+# if no command line argument then try to get it from the my.cnf:
+if [ -z "$ARIA_LOG_DIR" ]; then
+ ARIA_LOG_DIR=$(parse_cnf '--mysqld' 'aria-log-dir-path')
+ ARIA_LOG_DIR=$(trim_dir "$ARIA_LOG_DIR")
+fi
+
+if [ -n "$ARIA_LOG_DIR" -a "$ARIA_LOG_DIR" != '.' -a \
+ "$ARIA_LOG_DIR" != "$DATA_DIR" ]
+then
+ # handle both relative and absolute paths:
+ cd "$DATA"
+ [ ! -d "$ARIA_LOG_DIR" ] && mkdir -p "$ARIA_LOG_DIR"
+ cd "$ARIA_LOG_DIR"
+ ar_log_dir="$(pwd)"
+ cd "$OLD_PWD"
+fi
+
+encgroups='--mysqld|sst'
+
+check_server_ssl_config
+
+SSTKEY="$tkey"
+SSTCERT="$tpem"
+SSTCA="$tcert"
+SSTCAP="$tcap"
+
+SSLMODE=$(parse_cnf "$encgroups" 'ssl-mode' | tr '[[:lower:]]' '[[:upper:]]')
+
+if [ -z "$SSLMODE" ]; then
+ # Implicit verification if CA is set and the SSL mode
+ # is not specified by user:
+ if [ -n "$SSTCA$SSTCAP" ]; then
+ STUNNEL_BIN=$(commandex 'stunnel')
+ if [ -n "$STUNNEL_BIN" ]; 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
+else
+ case "$SSLMODE" in
+ 'VERIFY_IDENTITY'|'VERIFY_CA'|'REQUIRED'|'DISABLED')
+ ;;
+ *)
+ wsrep_log_error "Unrecognized ssl-mode option: '$SSLMODE'"
+ exit 22 # EINVAL
+ ;;
+ esac
+fi
+
+if [ -n "$SSTKEY" -a -n "$SSTCERT" ]; then
+ verify_cert_matches_key "$SSTCERT" "$SSTKEY"
+fi
+
+CAFILE_OPT=""
+CAPATH_OPT=""
+if [ -n "$SSTCA$SSTCAP" ]; then
+ if [ -n "$SSTCA" ]; then
+ CAFILE_OPT="CAfile = $SSTCA"
+ fi
+ if [ -n "$SSTCAP" ]; then
+ CAPATH_OPT="CApath = $SSTCAP"
+ fi
+ if [ -n "$SSTCERT" ]; then
+ verify_ca_matches_cert "$SSTCERT" "$SSTCA" "$SSTCAP"
+ fi
+fi
+
+VERIFY_OPT=""
+CHECK_OPT=""
+CHECK_OPT_LOCAL=""
+if [ "${SSLMODE#VERIFY}" != "$SSLMODE" ]; then
+ if [ "$SSLMODE" = 'VERIFY_IDENTITY' ]; then
+ VERIFY_OPT='verifyPeer = yes'
+ else
+ VERIFY_OPT='verifyChain = yes'
+ fi
+ if [ -z "$SSTCA$SSTCAP" ]; then
+ wsrep_log_error "Can't have ssl-mode='$SSLMODE' without CA file or path"
+ exit 22 # EINVAL
+ fi
+ if [ -n "$WSREP_SST_OPT_REMOTE_USER" ]; then
+ CHECK_OPT="checkHost = $WSREP_SST_OPT_REMOTE_USER"
+ elif [ "$WSREP_SST_OPT_ROLE" = 'donor' ]; then
+ # 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
+fi
+
+STUNNEL=""
+if [ -n "$SSLMODE" -a "$SSLMODE" != 'DISABLED' ]; then
+ if [ -z "${STUNNEL_BIN+x}" ]; then
+ STUNNEL_BIN=$(commandex 'stunnel')
+ fi
+ if [ -n "$STUNNEL_BIN" ]; then
+ wsrep_log_info "Using stunnel for SSL encryption: CA: '$SSTCA'," \
+ "CAPATH='$SSTCAP', ssl-mode='$SSLMODE'"
+ STUNNEL="$STUNNEL_BIN $STUNNEL_CONF"
+ fi
+fi
+
+readonly SECRET_TAG='secret'
+readonly BYPASS_TAG='bypass'
+
+SST_PID="$WSREP_SST_OPT_DATA/wsrep_sst.pid"
+
+# give some time for previous SST to complete:
+check_round=0
+while check_pid "$SST_PID" 0; do
+ wsrep_log_info "Previous SST is not completed, waiting for it to exit"
+ check_round=$(( check_round + 1 ))
+ if [ $check_round -eq 20 ]; then
+ wsrep_log_error "previous SST script still running."
+ exit 114 # EALREADY
+ fi
+ sleep 1
+done
+
+trap simple_cleanup EXIT
+echo $$ > "$SST_PID"
+
+# give some time for stunnel from the previous SST to complete:
+check_round=0
+while check_pid "$STUNNEL_PID" 1 "$STUNNEL_CONF"; 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 still running."
+ exit 114 # EALREADY
+ fi
+ sleep 1
+done
+
+MODULE="${WSREP_SST_OPT_MODULE:-rsync_sst}"
+
+RSYNC_PID="$WSREP_SST_OPT_DATA/$MODULE.pid"
+RSYNC_CONF="$WSREP_SST_OPT_DATA/$MODULE.conf"
+
+# give some time for rsync from the previous SST to complete:
+check_round=0
+while check_pid "$RSYNC_PID" 1 "$RSYNC_CONF"; 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 still running."
+ exit 114 # EALREADY
+ fi
+ sleep 1
+done
+
+[ -f "$MAGIC_FILE" ] && rm -f "$MAGIC_FILE"
+[ -f "$BINLOG_TAR_FILE" ] && rm -f "$BINLOG_TAR_FILE"
+
+RC=0
+
+if [ "$WSREP_SST_OPT_ROLE" = 'donor' ]; then
+
+ if [ -n "$STUNNEL" ]; then
+ cat << EOF > "$STUNNEL_CONF"
+key = $SSTKEY
+cert = $SSTCERT
+${CAFILE_OPT}
+${CAPATH_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
+ 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"
+ do
+ # Check whether ERROR file exists.
+ if [ -f "$ERROR" ]; then
+ # Flush tables operation failed.
+ rm "$ERROR"
+ exit 255
+ fi
+ sleep 0.2
+ done
+
+ STATE=$(cat "$FLUSHED")
+ rm "$FLUSHED"
+
+ sync
+
+ wsrep_log_info "Tables flushed"
+
+ if [ -n "$WSREP_SST_OPT_BINLOG" ]; then
+ # Change the directory to binlog base (if possible):
+ cd "$DATA"
+ # Let's check the existence of the file with the index:
+ if [ -f "$WSREP_SST_OPT_BINLOG_INDEX" ]; then
+ # Let's read the binlog index:
+ max_binlogs=$(parse_cnf "$encgroups" 'sst-max-binlogs' 1)
+ if [ $max_binlogs -ge 0 ]; then
+ binlog_files=""
+ if [ $max_binlogs -gt 0 ]; then
+ binlog_files=$(tail -n $max_binlogs \
+ "$WSREP_SST_OPT_BINLOG_INDEX")
+ fi
+ else
+ binlog_files=$(cat "$WSREP_SST_OPT_BINLOG_INDEX")
+ fi
+ if [ -n "$binlog_files" ]; then
+ # Preparing binlog files for transfer:
+ wsrep_log_info "Preparing binlog files for transfer:"
+ tar_type=0
+ if tar --help | grep -qw -F -- '--transform'; then
+ tar_type=1
+ elif tar --version | grep -qw -E '^bsdtar'; then
+ tar_type=2
+ fi
+ if [ $tar_type -eq 2 ]; then
+ if [ -n "$BASH_VERSION" ]; then
+ printf '%s' "$binlog_files" >&2
+ else
+ echo "$binlog_files" >&2
+ fi
+ fi
+ if [ $tar_type -ne 0 ]; then
+ # Preparing list of the binlog file names:
+ echo "$binlog_files" | {
+ binlogs=""
+ while read bin_file || [ -n "$bin_file" ]; do
+ [ ! -f "$bin_file" ] && continue
+ if [ -n "$BASH_VERSION" ]; then
+ first="${bin_file:0:1}"
+ else
+ first=$(echo "$bin_file" | cut -c1)
+ fi
+ if [ "$first" = '-' -o "$first" = '@' ]; then
+ bin_file="./$bin_file"
+ fi
+ binlogs="$binlogs${binlogs:+ }'$bin_file'"
+ done
+ if [ -n "$binlogs" ]; then
+ if [ $tar_type -eq 1 ]; then
+ tar_options="--transform='s/^.*\///g'"
+ else
+ # bsdtar handles backslash incorrectly:
+ tar_options="-s '?^.*/??g'"
+ fi
+ eval tar -P $tar_options \
+ -cvf "'$BINLOG_TAR_FILE'" $binlogs >&2
+ fi
+ }
+ else
+ tar_options='-cvf'
+ echo "$binlog_files" | \
+ while read bin_file || [ -n "$bin_file" ]; do
+ [ ! -f "$bin_file" ] && continue
+ bin_dir=$(dirname "$bin_file")
+ bin_base=$(basename "$bin_file")
+ if [ -n "$BASH_VERSION" ]; then
+ first="${bin_base:0:1}"
+ else
+ first=$(echo "$bin_base" | cut -c1)
+ fi
+ if [ "$first" = '-' -o "$first" = '@' ]; then
+ bin_base="./$bin_base"
+ fi
+ if [ -n "$bin_dir" -a "$bin_dir" != '.' -a \
+ "$bin_dir" != "$DATA_DIR" ]
+ then
+ tar $tar_options "$BINLOG_TAR_FILE" \
+ -C "$bin_dir" "$bin_base" >&2
+ else
+ tar $tar_options "$BINLOG_TAR_FILE" \
+ "$bin_base" >&2
+ fi
+ tar_options='-rvf'
+ done
+ fi
+ fi
+ fi
+ cd "$OLD_PWD"
+ fi
+
+ # Use deltaxfer only for WAN:
+ WHOLE_FILE_OPT=""
+ if [ "${WSREP_METHOD%_wan}" = "$WSREP_METHOD" ]; then
+ WHOLE_FILE_OPT='--whole-file'
+ fi
+
+# 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 '- /.snapshot/'
+ -f '+ /wsrep_sst_binlog.tar'
+ -f '- $ib_home_dir/ib_lru_dump'
+ -f '- $ib_home_dir/ibdata*'
+ -f '- $ib_undo_dir/undo*'
+ -f '- $ib_log_dir/ib_logfile[0-9]*'
+ -f '- $ar_log_dir/aria_log_control'
+ -f '- $ar_log_dir/aria_log.*'
+ -f '+ /*/'
+ -f '- /*'"
+
+ # first, the normal directories, so that we can detect
+ # incompatible protocol:
+ 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
+
+ wsrep_log_info "Transfer of normal directories done"
+
+ # 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 '- **' "$ib_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
+
+ wsrep_log_info "Transfer of InnoDB data files done"
+
+ # second, we transfer the InnoDB log file
+ rsync ${STUNNEL:+--rsh="$STUNNEL"} \
+ --owner --group --perms --links --specials \
+ --ignore-times --inplace --dirs --delete --quiet \
+ $WHOLE_FILE_OPT -f '+ /ib_logfile0' \
+ -f '- **' "$ib_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
+
+ wsrep_log_info "Transfer of InnoDB log files done"
+
+ # third, we transfer InnoDB undo logs
+ rsync ${STUNNEL:+--rsh="$STUNNEL"} \
+ --owner --group --perms --links --specials \
+ --ignore-times --inplace --dirs --delete --quiet \
+ $WHOLE_FILE_OPT -f '+ /undo*' \
+ -f '- **' "$ib_undo_dir/" \
+ "rsync://$WSREP_SST_OPT_ADDR-undo_dir" >&2 || RC=$?
+
+ if [ $RC -ne 0 ]; then
+ wsrep_log_error "rsync innodb_undo_dir returned code $RC:"
+ exit 255 # unknown error
+ fi
+
+ wsrep_log_info "Transfer of InnoDB undo logs done"
+
+ # fourth, we transfer Aria logs
+ rsync ${STUNNEL:+--rsh="$STUNNEL"} \
+ --owner --group --perms --links --specials \
+ --ignore-times --inplace --dirs --delete --quiet \
+ $WHOLE_FILE_OPT -f '+ /aria_log_control' -f '+ /aria_log.*' \
+ -f '- **' "$ar_log_dir/" \
+ "rsync://$WSREP_SST_OPT_ADDR-aria_log" >&2 || RC=$?
+
+ if [ $RC -ne 0 ]; then
+ wsrep_log_error "rsync aria_log_dir_path returned code $RC:"
+ exit 255 # unknown error
+ fi
+
+ wsrep_log_info "Transfer of Aria logs done"
+
+ # then, we parallelize the transfer of database directories,
+ # use '.' so that path concatenation works:
+
+ backup_threads=$(parse_cnf '--mysqld|sst' 'backup-threads')
+ if [ -z "$backup_threads" ]; then
+ get_proc
+ backup_threads=$nproc
+ fi
+
+ cd "$DATA"
+
+ findopt='-L'
+ [ "$OS" = 'FreeBSD' ] && findopt="$findopt -E"
+
+ find $findopt . -maxdepth 1 -mindepth 1 -type d -not -name 'lost+found' \
+ -not -name '.zfs' -not -name .snapshot -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 \
+ -f '- $ib_home_dir/ib_lru_dump' \
+ -f '- $ib_home_dir/ibdata*' \
+ -f '- $ib_undo_dir/undo*' \
+ -f '- $ib_log_dir/ib_logfile[0-9]*' \
+ -f '- $ar_log_dir/aria_log_control' \
+ -f '- $ar_log_dir/aria_log.*' \
+ "$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
+
+ wsrep_log_info "Transfer of data done"
+
+ [ -f "$BINLOG_TAR_FILE" ] && rm "$BINLOG_TAR_FILE"
+
+ 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
+
+ wsrep_log_info "Sending continue to donor"
+ 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
+
+ if [ $WSREP_SST_OPT_BYPASS -ne 0 ]; then
+ echo "$BYPASS_TAG" >> "$MAGIC_FILE"
+ fi
+
+ rsync ${STUNNEL:+--rsh="$STUNNEL"} \
+ --archive --quiet --checksum "$MAGIC_FILE" \
+ "rsync://$WSREP_SST_OPT_ADDR" >&2 || RC=$?
+
+ rm "$MAGIC_FILE"
+
+ if [ $RC -ne 0 ]; then
+ wsrep_log_error "rsync $MAGIC_FILE returned code $RC:"
+ exit 255 # unknown error
+ fi
+
+ echo "done $STATE"
+
+ if [ -n "$STUNNEL" ]; then
+ rm "$STUNNEL_CONF"
+ [ -f "$STUNNEL_PID" ] && rm "$STUNNEL_PID"
+ fi
+
+else # joiner
+
+ check_sockets_utils
+
+ ADDR="$WSREP_SST_OPT_HOST"
+ RSYNC_PORT="$WSREP_SST_OPT_PORT"
+ RSYNC_ADDR="$WSREP_SST_OPT_HOST"
+ RSYNC_ADDR_UNESCAPED="$WSREP_SST_OPT_HOST_UNESCAPED"
+
+ 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 = $ib_log_dir
+[$MODULE-data_dir]
+ path = $ib_home_dir
+[$MODULE-undo_dir]
+ path = $ib_undo_dir
+[$MODULE-aria_log]
+ path = $ar_log_dir
+EOF
+
+ # 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?
+ RSYNC_BIN=$(commandex 'rsync')
+ if [ "${RSYNC_CONF#* }" = "$RSYNC_CONF" ]; then
+ cat << EOF > "$STUNNEL_CONF"
+key = $SSTKEY
+cert = $SSTCERT
+${CAFILE_OPT}
+${CAPATH_OPT}
+foreground = yes
+pid = $STUNNEL_PID
+debug = warning
+client = no
+${VERIFY_OPT}
+${CHECK_OPT}
+${CHECK_OPT_LOCAL}
+[rsync]
+accept = $STUNNEL_ACCEPT
+exec = $RSYNC_BIN
+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 '$RSYNC_BIN' --server --daemon --config='$RSYNC_CONF' ."
+ cat << EOF > "$STUNNEL_CONF"
+key = $SSTKEY
+cert = $SSTCERT
+${CAFILE_OPT}
+${CAPATH_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()
+ 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 [ ! -r "$MAGIC_FILE" ]; then
+ # This message should cause joiner to abort:
+ wsrep_log_info "rsync process ended without creating" \
+ "magic file ($MAGIC_FILE)"
+ exit 32
+ fi
+
+ if [ -n "$MY_SECRET" ]; then
+ # Check donor supplied secret:
+ SECRET=$(grep -m1 -E "^$SECRET_TAG[[:space:]]" "$MAGIC_FILE" || :)
+ SECRET=$(trim_string "${SECRET#$SECRET_TAG}")
+ 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
+ fi
+
+ if [ $WSREP_SST_OPT_BYPASS -eq 0 ]; then
+ if grep -m1 -qE "^$BYPASS_TAG([[:space:]]+.*)?\$" "$MAGIC_FILE"; then
+ readonly WSREP_SST_OPT_BYPASS=1
+ readonly WSREP_TRANSFER_TYPE='IST'
+ fi
+ fi
+
+ binlog_tar_present=0
+ if [ -f "$BINLOG_TAR_FILE" ]; then
+ binlog_tar_present=1
+ if [ $WSREP_SST_OPT_BYPASS -ne 0 ]; then
+ wsrep_log_warning "tar with binlogs transferred in the IST mode"
+ fi
+ fi
+
+ if [ $WSREP_SST_OPT_BYPASS -eq 0 -a -n "$WSREP_SST_OPT_BINLOG" ]; then
+ # If it is SST (not an IST) or tar with binlogs is present
+ # among the transferred files, then we need to remove the
+ # old binlogs:
+ cd "$DATA"
+ # Clean up the old binlog files and index:
+ binlog_index="$WSREP_SST_OPT_BINLOG_INDEX"
+ if [ -f "$binlog_index" ]; then
+ while read bin_file || [ -n "$bin_file" ]; do
+ rm -f "$bin_file" || :
+ done < "$binlog_index"
+ rm "$binlog_index"
+ fi
+ binlog_cd=0
+ # Change the directory to binlog base (if possible):
+ if [ -n "$binlog_dir" -a "$binlog_dir" != '.' -a \
+ "$binlog_dir" != "$DATA_DIR" -a -d "$binlog_dir" ]
+ then
+ binlog_cd=1
+ cd "$binlog_dir"
+ fi
+ # Clean up unindexed binlog files:
+ rm -f "$binlog_base".[0-9]* || :
+ [ $binlog_cd -ne 0 ] && cd "$DATA_DIR"
+ if [ $binlog_tar_present -ne 0 ]; then
+ # Create a temporary file:
+ tmpdir=$(parse_cnf '--mysqld|sst' 'tmpdir')
+ if [ -z "$tmpdir" ]; then
+ tmpfile="$(mktemp)"
+ elif [ "$OS" = 'Linux' ]; then
+ tmpfile=$(mktemp "--tmpdir=$tmpdir")
+ else
+ tmpfile=$(TMPDIR="$tmpdir"; mktemp)
+ fi
+ index_dir=$(dirname "$binlog_index");
+ if [ -n "$index_dir" -a "$index_dir" != '.' -a \
+ "$index_dir" != "$DATA_DIR" ]
+ then
+ [ ! -d "$index_dir" ] && mkdir -p "$index_dir"
+ fi
+ binlog_cd=0
+ if [ -n "$binlog_dir" -a "$binlog_dir" != '.' -a \
+ "$binlog_dir" != "$DATA_DIR" ]
+ then
+ [ ! -d "$binlog_dir" ] && mkdir -p "$binlog_dir"
+ binlog_cd=1
+ cd "$binlog_dir"
+ fi
+ # Extracting binlog files:
+ wsrep_log_info "Extracting binlog files:"
+ if tar --version | grep -qw -E '^bsdtar'; then
+ tar -tf "$BINLOG_TAR_FILE" > "$tmpfile" && \
+ tar -xvf "$BINLOG_TAR_FILE" > /dev/null || RC=$?
+ else
+ tar -xvf "$BINLOG_TAR_FILE" > "$tmpfile" && \
+ cat "$tmpfile" >&2 || RC=$?
+ fi
+ if [ $RC -ne 0 ]; then
+ wsrep_log_error "Error unpacking tar file with binlog files"
+ rm "$tmpfile"
+ exit 32
+ fi
+ # Rebuild binlog index:
+ [ $binlog_cd -ne 0 ] && cd "$DATA_DIR"
+ while read bin_file || [ -n "$bin_file" ]; do
+ echo "$binlog_dir${binlog_dir:+/}$bin_file" >> "$binlog_index"
+ done < "$tmpfile"
+ rm "$tmpfile"
+ cd "$OLD_PWD"
+ fi
+ fi
+
+ # Remove special tags from the magic file, and from the output:
+ coords=$(head -n1 "$MAGIC_FILE")
+ wsrep_log_info "Galera co-ords from recovery: $coords"
+ echo "$coords" # Output : UUID:seqno wsrep_gtid_domain_id
+fi
+
+wsrep_log_info "$WSREP_METHOD $WSREP_TRANSFER_TYPE completed on $WSREP_SST_OPT_ROLE"
+exit 0