wsrep_sst_mariabackup.sh 42.3 KB
Newer Older
1
#!/bin/bash -ue
2
# Copyright (C) 2017-2021 MariaDB
3
# Copyright (C) 2013 Percona Inc
4 5 6 7 8 9 10 11 12 13 14 15 16
#
# 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
17
# MA  02110-1335  USA.
18 19

# Documentation:
20
# https://mariadb.com/kb/en/mariabackup-overview/
21 22
# Make sure to read that before proceeding!

23
. $(dirname "$0")/wsrep_sst_common
24
wsrep_check_datadir
25

26
OS="$(uname)"
27
ealgo=""
28
eformat=""
29 30 31 32 33 34
ekey=""
ekeyfile=""
encrypt=0
ecode=0
ssyslog=""
ssystag=""
35
BACKUP_PID=""
36 37 38
tcert=""
tpem=""
tkey=""
39
tmode="DISABLED"
40 41 42 43 44 45 46 47
sockopt=""
progress=""
ttime=0
totime=0
lsn=""
ecmd=""
rlimit=""
# Initially
48
stagemsg="$WSREP_SST_OPT_ROLE"
49 50 51 52 53 54
cpat=""
speciald=1
ib_home_dir=""
ib_log_dir=""
ib_undo_dir=""

55
sfmt=""
56 57 58 59
strmcmd=""
tfmt=""
tcmd=""
payload=0
60 61
pvformat="-F '%N => Rate:%r Avg:%a Elapsed:%t %e Bytes: %b %p'"
pvopts="-f -i 10 -N $WSREP_SST_OPT_ROLE"
62 63 64 65 66 67 68 69 70 71 72
STATDIR=""
uextra=0
disver=""

tmpopts=""
itmpdir=""
xtmpdir=""

scomp=""
sdecomp=""

73 74 75 76 77 78
ssl_dhparams=""

compress='none'
compress_chunk=""
compress_threads=""

79 80 81 82 83
backup_threads=""

encrypt_threads=""
encrypt_chunk=""

84 85
readonly SECRET_TAG="secret"

86 87 88 89 90
# Required for backup locks
# For backup locks it is 1 sent by joiner
# 5.6.21 PXC and later can't donate to an older joiner
sst_ver=1

91
if [ -n "$(command -v pv)" ] && pv --help | grep -qw -- '-F'; then
92
    pvopts="$pvopts $pvformat"
93 94 95 96
fi
pcmd="pv $pvopts"
declare -a RC

97 98
BACKUP_BIN="$(command -v mariabackup)"
if [ ! -x "$BACKUP_BIN" ]; then
99
    wsrep_log_error 'mariabackup binary not found in path'
100
    exit 42
101
fi
102

103
DATA="$WSREP_SST_OPT_DATA"
104 105
INFO_FILE="xtrabackup_galera_info"
IST_FILE="xtrabackup_ist"
106
MAGIC_FILE="$DATA/$INFO_FILE"
107

108 109 110
INNOAPPLYLOG="$DATA/mariabackup.prepare.log"
INNOMOVELOG="$DATA/mariabackup.move.log"
INNOBACKUPLOG="$DATA/mariabackup.backup.log"
111 112 113 114

# Setting the path for ss and ip
export PATH="/usr/sbin:/sbin:$PATH"

115 116
timeit()
{
117
    local stage="$1"
118 119 120 121
    shift
    local cmd="$@"
    local x1 x2 took extcode

122
    if [ $ttime -eq 1 ]; then
123 124 125 126 127 128 129 130
        x1=$(date +%s)
        wsrep_log_info "Evaluating $cmd"
        eval "$cmd"
        extcode=$?
        x2=$(date +%s)
        took=$(( x2-x1 ))
        wsrep_log_info "NOTE: $stage took $took seconds"
        totime=$(( totime+took ))
131
    else
132 133 134 135 136 137 138 139 140 141
        wsrep_log_info "Evaluating $cmd"
        eval "$cmd"
        extcode=$?
    fi
    return $extcode
}

get_keys()
{
    # $encrypt -eq 1 is for internal purposes only
142
    if [ $encrypt -ge 2 -o $encrypt -eq -1 ]; then
143
        return
144 145
    fi

146
    if [ $encrypt -eq 0 ]; then
147 148 149
        if [ -n "$ealgo" -o -n "$ekey" -o -n "$ekeyfile" ]; then
            wsrep_log_error "Options for encryption are specified, " \
                            "but encryption itself is disabled. SST may fail."
150 151 152 153
        fi
        return
    fi

154
    if [ $sfmt = 'tar' ]; then
155 156
        wsrep_log_info "NOTE: key-based encryption (encrypt=1) " \
                       "cannot be enabled with tar format"
157 158 159 160
        encrypt=-1
        return
    fi

161
    wsrep_log_info "Key based encryption enabled in my.cnf"
162

163
    if [ -z "$ealgo" ]; then
164 165 166 167
        wsrep_log_error "FATAL: Encryption algorithm empty from my.cnf, bailing out"
        exit 3
    fi

168
    if [ -z "$ekey" -a ! -r "$ekeyfile" ]; then
169 170
        wsrep_log_error "FATAL: Either key must be specified " \
                        "or keyfile must be readable"
171 172 173
        exit 3
    fi

174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194
    if [ "$eformat" = 'openssl' ]; then
        get_openssl
        if [ -z "$OPENSSL_BINARY" ]; then
            wsrep_log_error "If encryption using the openssl is enabled, " \
                            "then you need to install openssl"
            exit 2
        fi
        ecmd="'$OPENSSL_BINARY' enc -$ealgo"
        if "$OPENSSL_BINARY" enc -help 2>&1 | grep -qw -- '-pbkdf2'; then
            ecmd="$ecmd -pbkdf2"
        elif "$OPENSSL_BINARY" enc -help 2>&1 | grep -qw -- '-iter'; then
            ecmd="$ecmd -iter 1"
        elif "$OPENSSL_BINARY" enc -help 2>&1 | grep -qw -- '-md'; then
            ecmd="$ecmd -md sha256"
        fi
        if [ -z "$ekey" ]; then
            ecmd="$ecmd -kfile '$ekeyfile'"
        else
            ecmd="$ecmd -k '$ekey'"
        fi
    elif [ "$eformat" = 'xbcrypt' ]; then
195
        if [ -z "$(command -v xbcrypt)" ]; then
196 197 198 199 200 201 202 203 204 205 206
            wsrep_log_error "If encryption using the xbcrypt is enabled, " \
                            "then you need to install xbcrypt"
            exit 2
        fi
        wsrep_log_info "NOTE: xbcrypt-based encryption, " \
                       "supported only from Xtrabackup 2.1.4"
        if [ -z "$ekey" ]; then
            ecmd="xbcrypt --encrypt-algo='$ealgo' --encrypt-key-file='$ekeyfile'"
        else
            ecmd="xbcrypt --encrypt-algo='$ealgo' --encrypt-key='$ekey'"
        fi
207 208 209 210 211 212
        if [ -n "$encrypt_threads" ]; then
            ecmd="$ecmd --encrypt-threads=$encrypt_threads"
        fi
        if [ -n "$encrypt_chunk" ]; then
            ecmd="$ecmd --encrypt-chunk-size=$encrypt_chunk"
        fi
213
    else
214 215
        wsrep_log_error "Unknown encryption format='$eformat'"
        exit 2
216 217
    fi

218 219
    if [ "$WSREP_SST_OPT_ROLE" = 'joiner' ]; then
        ecmd="$ecmd -d"
220 221
    fi

222
    stagemsg="$stagemsg-XB-Encrypted"
223 224 225 226
}

get_transfer()
{
227
    if [ $tfmt = 'nc' ]; then
228
        wsrep_log_info "Using netcat as streamer"
229 230
        wsrep_check_programs nc
        tcmd="nc"
231
        if [ "$WSREP_SST_OPT_ROLE" = 'joiner' ]; then
232 233 234 235 236 237
            if nc -h 2>&1 | grep -q 'ncat'; then
                wsrep_log_info "Using Ncat as streamer"
                tcmd="$tcmd -l"
            elif nc -h 2>&1 | grep -qw -- '-d'; then
                wsrep_log_info "Using Debian netcat as streamer"
                tcmd="$tcmd -dl"
238
                if [ $WSREP_SST_OPT_HOST_IPv6 -eq 1 ]; then
239 240 241
                    # When host is not explicitly specified (when only the port
                    # is specified) netcat can only bind to an IPv4 address if
                    # the "-6" option is not explicitly specified:
242
                    tcmd="$tcmd -6"
243
                fi
244
            else
245 246
                wsrep_log_info "Using traditional netcat as streamer"
                tcmd="$tcmd -l -p"
247
            fi
248
            tcmd="$tcmd $SST_PORT"
249
        else
250 251 252 253 254 255 256
            # Check to see if netcat supports the '-N' flag.
            # -N Shutdown the network socket after EOF on stdin
            # If it supports the '-N' flag, then we need to use the '-N'
            # flag, otherwise the transfer will stay open after the file
            # transfer and cause the command to timeout.
            # Older versions of netcat did not need this flag and will
            # return an error if the flag is used.
257 258
            if nc -h 2>&1 | grep -qw -- '-N'; then
                tcmd="$tcmd -N"
259
                wsrep_log_info "Using nc -N"
260 261
            fi
            # netcat doesn't understand [] around IPv6 address
262
            if nc -h 2>&1 | grep -q ncat; then
263
                wsrep_log_info "Using Ncat as streamer"
264
            elif nc -h 2>&1 | grep -qw -- '-d'; then
265
                wsrep_log_info "Using Debian netcat as streamer"
266
            else
267
                wsrep_log_info "Using traditional netcat as streamer"
268
                tcmd="$tcmd -q0"
269
            fi
270
            tcmd="$tcmd $WSREP_SST_OPT_HOST_UNESCAPED $SST_PORT"
271 272 273
        fi
    else
        tfmt='socat'
274

275
        wsrep_log_info "Using socat as streamer"
276
        wsrep_check_programs socat
277

278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309
        if [ -n "$sockopt" ]; then
            sockopt=$(trim_string "$sockopt" ',')
            if [ -n "$sockopt" ]; then
                sockopt=",$sockopt"
            fi
        fi

        # Add an option for ipv6 if needed:
        if [ $WSREP_SST_OPT_HOST_IPv6 -eq 1 ]; then
            # If sockopt contains 'pf=ip6' somewhere in the middle,
            # this will not interfere with socat, but exclude the trivial
            # cases when sockopt contains 'pf=ip6' as prefix or suffix:
            if [ "$sockopt" = "${sockopt#,pf=ip6}" -a \
                 "$sockopt" = "${sockopt%,pf=ip6}" ]
            then
                sockopt=",pf=ip6$sockopt"
            fi
        fi

        if [ $encrypt -lt 2 ]; then
            if [ "$WSREP_SST_OPT_ROLE" = 'joiner' ]; then
                tcmd="socat -u TCP-LISTEN:$SST_PORT,reuseaddr$sockopt stdio"
            else
                tcmd="socat -u stdio TCP:$REMOTEIP:$SST_PORT$sockopt"
            fi
            return
        fi

        if ! socat -V | grep -q -F 'WITH_OPENSSL 1'; then
            wsrep_log_error "******** FATAL ERROR ************************************************ "
            wsrep_log_error "* Encryption requested, but socat is not OpenSSL enabled (encrypt=$encrypt) *"
            wsrep_log_error "********************************************************************* "
310 311 312
            exit 2
        fi

313 314 315 316 317 318 319 320
        local action='Decrypting'
        if [ "$WSREP_SST_OPT_ROLE" = 'joiner' ]; then
            tcmd="socat -u openssl-listen:$SST_PORT,reuseaddr"
        else
            tcmd="socat -u stdio openssl-connect:$REMOTEIP:$SST_PORT"
            action='Encrypting'
        fi

321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336
        if [ "${sockopt#*,dhparam=}" != "$sockopt" ]; then
            if [ -z "$ssl_dhparams" ]; then
                # Determine the socat version
                SOCAT_VERSION=$(socat -V 2>&1 | grep -m1 -oe '[0-9]\.[0-9][\.0-9]*')
                if [ -z "$SOCAT_VERSION" ]; then
                    wsrep_log_error "******** FATAL ERROR ******************"
                    wsrep_log_error "* Cannot determine the socat version. *"
                    wsrep_log_error "***************************************"
                    exit 2
                fi
                if ! check_for_version "$SOCAT_VERSION" '1.7.3'; then
                    # socat versions < 1.7.3 will have 512-bit dhparams (too small)
                    # so create 2048-bit dhparams and send that as a parameter:
                    check_for_dhparams
                fi
            fi
337 338 339
            if [ -n "$ssl_dhparams" ]; then
                tcmd="$tcmd,dhparam='$ssl_dhparams'"
            fi
340 341
        fi

342
        if [ $encrypt -eq 2 ]; then
343
            wsrep_log_info "Using openssl based encryption with socat: with crt and pem"
344
            if [ -z "$tpem" -o -z "$tcert" ]; then
345 346 347
                wsrep_log_error "Both PEM and CRT files required"
                exit 22
            fi
348 349 350 351 352
            if [ ! -r "$tpem" -o ! -r "$tcert" ]; then
                wsrep_log_error "Both PEM and CRT files must be readable"
                exit 22
            fi
            verify_ca_matches_cert "$tcert" "$tpem"
353
            tcmd="$tcmd,cert='$tpem',cafile='$tcert'$sockopt"
354
            stagemsg="$stagemsg-OpenSSL-Encrypted-2"
355 356
            wsrep_log_info "$action with cert=$tpem, cafile=$tcert"
        elif [ $encrypt -eq 3 -o $encrypt -eq 4 ]; then
357
            wsrep_log_info "Using openssl based encryption with socat: with key and crt"
358
            if [ -z "$tpem" -o -z "$tkey" ]; then
359 360 361
                wsrep_log_error "Both certificate and key files required"
                exit 22
            fi
362 363 364 365 366
            if [ ! -r "$tpem" -o ! -r "$tkey" ]; then
                wsrep_log_error "Both certificate and key files must be readable"
                exit 22
            fi
            verify_cert_matches_key "$tpem" "$tkey"
367
            stagemsg="$stagemsg-OpenSSL-Encrypted-3"
368
            if [ -z "$tcert" ]; then
369 370 371
                if [ $encrypt -eq 4 ]; then
                    wsrep_log_error "Peer certificate required if encrypt=4"
                    exit 22
372
                fi
373 374 375
                # no verification
                tcmd="$tcmd,cert='$tpem',key='$tkey',verify=0$sockopt"
                wsrep_log_info "$action with cert=$tpem, key=$tkey, verify=0"
376
            else
377
                # CA verification
378 379 380 381 382
                if [ ! -r "$tcert" ]; then
                    wsrep_log_error "Certificate file must be readable"
                    exit 22
                fi
                verify_ca_matches_cert "$tcert" "$tpem"
383 384 385 386 387 388
                if [ -n "$WSREP_SST_OPT_REMOTE_USER" ]; then
                    CN_option=",commonname='$WSREP_SST_OPT_REMOTE_USER'"
                elif [ $encrypt -eq 4 ]; then
                    CN_option=",commonname=''"
                elif is_local_ip "$WSREP_SST_OPT_HOST_UNESCAPED"; then
                    CN_option=',commonname=localhost'
389
                else
390
                    CN_option=",commonname='$WSREP_SST_OPT_HOST_UNESCAPED'"
391
                fi
392 393
                tcmd="$tcmd,cert='$tpem',key='$tkey',cafile='$tcert'$CN_option$sockopt"
                wsrep_log_info "$action with cert=$tpem, key=$tkey, cafile=$tcert"
394
            fi
395
        else
396 397 398 399 400 401
            wsrep_log_info "Unknown encryption mode: encrypt=$encrypt"
            exit 22
        fi

        if [ "$WSREP_SST_OPT_ROLE" = 'joiner' ]; then
            tcmd="$tcmd stdio"
402 403 404 405 406 407
        fi
    fi
}

get_footprint()
{
408
    pushd "$WSREP_SST_OPT_DATA" 1>/dev/null
409
    payload=$(find . -regex '.*\.ibd$\|.*\.MYI$\|.*\.MYD$\|.*ibdata1$' -type f -print0 | du --files0-from=- --block-size=1 -c -s | awk 'END { print $1 }')
410
    if [ "$compress" != 'none' ]; then
411 412 413 414 415
        # QuickLZ has around 50% compression ratio
        # When compression/compaction used, the progress is only an approximate.
        payload=$(( payload*1/2 ))
    fi
    popd 1>/dev/null
416
    pcmd="$pcmd -s $payload"
417 418 419 420 421
    adjust_progress
}

adjust_progress()
{
422
    if [ -z "$(command -v pv)" ]; then
423 424 425 426 427 428 429 430
        wsrep_log_error "pv not found in path: $PATH"
        wsrep_log_error "Disabling all progress/rate-limiting"
        pcmd=""
        rlimit=""
        progress=""
        return
    fi

431 432
    if [ -n "$progress" -a "$progress" != '1' ]; then
        if [ -e "$progress" ]; then
433
            pcmd="$pcmd 2>>'$progress'"
434
        else
435
            pcmd="$pcmd 2>'$progress'"
436
        fi
437
    elif [ -z "$progress" -a -n "$rlimit" ]; then
438 439
            # When rlimit is non-zero
            pcmd="pv -q"
440
    fi
441

442
    if [ -n "$rlimit" -a "$WSREP_SST_OPT_ROLE" = 'donor' ]; then
443
        wsrep_log_info "Rate-limiting SST to $rlimit"
444
        pcmd="$pcmd -L \$rlimit"
445 446 447
    fi
}

448 449
encgroups='--mysqld|sst|xtrabackup'

450 451
check_server_ssl_config()
{
452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475
    # backward-compatible behavior:
    tcert=$(parse_cnf 'sst' 'tca')
    tpem=$(parse_cnf 'sst' 'tcert')
    tkey=$(parse_cnf 'sst' 'tkey')
    # reading new ssl configuration options:
    local tcert2=$(parse_cnf "$encgroups" 'ssl-ca')
    local tpem2=$(parse_cnf "$encgroups" 'ssl-cert')
    local tkey2=$(parse_cnf "$encgroups" 'ssl-key')
    # if there are no old options, then we take new ones:
    if [ -z "$tcert" -a -z "$tpem" -a -z "$tkey" ]; then
        tcert="$tcert2"
        tpem="$tpem2"
        tkey="$tkey2"
    # checking for presence of the new-style SSL configuration:
    elif [ -n "$tcert2" -o -n "$tpem2" -o -n "$tkey2" ]; then
        if [ "$tcert" != "$tcert2" -o \
             "$tpem"  != "$tpem2"  -o \
             "$tkey"  != "$tkey2" ]
        then
            wsrep_log_info "new ssl configuration options (ssl-ca, ssl-cert " \
                           "and ssl-key) are ignored by SST due to presence " \
                           "of the tca, tcert and/or tkey in the [sst] section"
        fi
    fi
476 477
}

478 479
read_cnf()
{
480 481
    sfmt=$(parse_cnf sst streamfmt 'mbstream')
    tfmt=$(parse_cnf sst transferfmt 'socat')
482

483 484
    encrypt=$(parse_cnf "$encgroups" 'encrypt' 0)
    tmode=$(parse_cnf "$encgroups" 'ssl-mode' 'DISABLED' | tr [:lower:] [:upper:])
485 486 487

    if [ $encrypt -eq 0 -o $encrypt -ge 2 ]
    then
488 489
        if [ "$tmode" != 'DISABLED' -o $encrypt -ge 2 ]; then
            check_server_ssl_config
490
        fi
491
        if [ "$tmode" != 'DISABLED' ]; then
492 493 494 495 496 497 498 499 500 501
            if [ 0 -eq $encrypt -a -n "$tpem" -a -n "$tkey" ]
            then
                encrypt=3 # enable cert/key SSL encyption

                # avoid CA verification if not set explicitly:
                # nodes may happen to have different CA if self-generated
                # zeroing up tcert does the trick
                [ "${tmode#VERIFY}" != "$tmode" ] || tcert=""
            fi
        fi
502 503 504 505
    elif [ $encrypt -eq 1 ]; then
        ealgo=$(parse_cnf "$encgroups" 'encrypt-algo')
        eformat=$(parse_cnf "$encgroups" 'encrypt-format' 'openssl')
        ekey=$(parse_cnf "$encgroups" 'encrypt-key')
506 507 508 509 510
        # The keyfile should be read only when the key
        # is not specified or empty:
        if [ -z "$ekey" ]; then
            ekeyfile=$(parse_cnf "$encgroups" 'encrypt-key-file')
        fi
511 512
    fi

513 514
    wsrep_log_info "SSL configuration: CA='$tcert', CERT='$tpem'," \
                   "KEY='$tkey', MODE='$tmode', encrypt='$encrypt'"
515

516 517 518
    sockopt=$(parse_cnf sst sockopt "")
    progress=$(parse_cnf sst progress "")
    ttime=$(parse_cnf sst time 0)
519
    cpat='.*\.pem$\|.*galera\.cache$\|.*sst_in_progress$\|.*\.sst$\|.*gvwstate\.dat$\|.*grastate\.dat$\|.*\.err$\|.*\.log$\|.*RPM_UPGRADE_MARKER$\|.*RPM_UPGRADE_HISTORY$'
520 521
    [ "$OS" = 'FreeBSD' ] && cpat=$(echo "$cpat" | sed 's/\\|/|/g')
    cpat=$(parse_cnf sst cpat "$cpat")
522 523 524 525 526 527 528 529 530
    scomp=$(parse_cnf sst compressor "")
    sdecomp=$(parse_cnf sst decompressor "")

    rlimit=$(parse_cnf sst rlimit "")
    uextra=$(parse_cnf sst use-extra 0)
    speciald=$(parse_cnf sst sst-special-dirs 1)
    iopts=$(parse_cnf sst inno-backup-opts "")
    iapts=$(parse_cnf sst inno-apply-opts "")
    impts=$(parse_cnf sst inno-move-opts "")
531
    stimeout=$(parse_cnf sst sst-initial-timeout 300)
532 533
    ssyslog=$(parse_cnf sst sst-syslog 0)
    ssystag=$(parse_cnf mysqld_safe syslog-tag "${SST_SYSLOG_TAG:-}")
534
    ssystag="$ssystag-"
535
    sstlogarchive=$(parse_cnf sst sst-log-archive 1)
536
    sstlogarchivedir=$(parse_cnf sst sst-log-archive-dir '/tmp/sst_log_archive')
537

538
    if [ $speciald -eq 0 ]; then
539 540
        wsrep_log_error "sst-special-dirs equal to 0 is not supported, falling back to 1"
        speciald=1
541
    fi
542

543
    if [ $ssyslog -ne -1 ]; then
544
        ssyslog=$(in_config 'mysqld_safe' 'syslog')
545
    fi
546 547 548 549 550 551 552 553

    if [ "$WSREP_SST_OPT_ROLE" = 'donor' ]; then
        compress=$(parse_cnf "$encgroups" 'compress' 'none')
        if [ "$compress" != 'none' ]; then
            compress_chunk=$(parse_cnf "$encgroups" 'compress-chunk-size')
            compress_threads=$(parse_cnf "$encgroups" 'compress-threads')
        fi
    fi
554 555 556 557 558 559 560

    backup_threads=$(parse_cnf "$encgroups" 'backup-threads')

    if [ "$eformat" = 'xbcrypt' ]; then
        encrypt_threads=$(parse_cnf "$encgroups" 'encrypt-threads')
        encrypt_chunk=$(parse_cnf "$encgroups" 'encrypt-chunk-size')
    fi
561 562 563 564
}

get_stream()
{
565
    if [ "$sfmt" = 'mbstream' -o "$sfmt" = 'xbstream' ]; then
566
        sfmt='mbstream'
567 568
        STREAM_BIN="$(command -v mbstream)"
        if [ -z "$STREAM_BIN" ]; then
569 570 571
            wsrep_log_error "Streaming with $sfmt, but $sfmt not found in path"
            exit 42
        fi
572
        if [ "$WSREP_SST_OPT_ROLE" = 'joiner' ]; then
573
            strmcmd="'$STREAM_BIN' -x"
574
        else
575
            strmcmd="'$STREAM_BIN' -c '$INFO_FILE'"
576 577
        fi
    else
578 579 580
        sfmt='tar'
        if [ "$WSREP_SST_OPT_ROLE" = 'joiner' ]; then
            strmcmd='tar xfi -'
581
        else
582
            strmcmd="tar cf - '$INFO_FILE'"
583 584
        fi
    fi
585
    wsrep_log_info "Streaming with $sfmt"
586 587 588 589 590
}

sig_joiner_cleanup()
{
    wsrep_log_error "Removing $MAGIC_FILE file due to signal"
591
    [ -f "$MAGIC_FILE" ] && rm -f "$MAGIC_FILE"
592 593
}

594
cleanup_at_exit()
595 596 597
{
    # Since this is invoked just after exit NNN
    local estatus=$?
598
    if [ $estatus -ne 0 ]; then
599 600 601
        wsrep_log_error "Cleanup after exit with status:$estatus"
    fi

602 603 604 605 606 607 608 609 610
    if [ "$WSREP_SST_OPT_ROLE" = 'joiner' ]; then
        wsrep_log_info "Removing the sst_in_progress file"
        wsrep_cleanup_progress_file
    else
        if [ -n "$BACKUP_PID" ]; then
            if check_pid "$BACKUP_PID" 1; then
                wsrep_log_error "mariabackup process is still running. Killing..."
                cleanup_pid $CHECK_PID "$BACKUP_PID"
            fi
611
        fi
612
        [ -f "$DATA/$IST_FILE" ] && rm -f "$DATA/$IST_FILE"
613
    fi
614

615
    if [ -n "$progress" -a -p "$progress" ]; then
616
        wsrep_log_info "Cleaning up fifo file $progress"
617
        rm -f "$progress" || true
618 619 620 621
    fi

    wsrep_log_info "Cleaning up temporary directories"

622 623 624 625 626 627 628 629
    if [ "$WSREP_SST_OPT_ROLE" = 'joiner' ]; then
        if [ -n "$STATDIR" ]; then
           [ -d "$STATDIR" ] && rm -rf "$STATDIR"
        fi
    else
        [ -n "$xtmpdir" -a -d "$xtmpdir" ] && rm -rf "$xtmpdir" || true
        [ -n "$itmpdir" -a -d "$itmpdir" ] && rm -rf "$itmpdir" || true
    fi
630

631
    # Final cleanup
632 633 634 635
    pgid=$(ps -o pgid= $$ | grep -o '[0-9]*')

    # This means no setsid done in mysqld.
    # We don't want to kill mysqld here otherwise.
636
    if [ $$ -eq $pgid ]; then
637
        # This means a signal was delivered to the process.
638
        # So, more cleanup.
639
        if [ $estatus -ge 128 ]; then
640
            kill -KILL -- -$$ || true
641 642 643 644 645 646 647 648
        fi
    fi

    exit $estatus
}

setup_ports()
{
649
    SST_PORT="$WSREP_SST_OPT_PORT"
650 651 652 653
    if [ "$WSREP_SST_OPT_ROLE" = 'donor' ]; then
        REMOTEIP="$WSREP_SST_OPT_HOST"
        lsn="$WSREP_SST_OPT_LSN"
        sst_ver="$WSREP_SST_OPT_SST_VER"
654 655 656
    fi
}

657 658 659 660
#
# Waits ~30 seconds for socat or nc to open the port and
# then reports ready, regardless of timeout.
#
661 662
wait_for_listen()
{
663 664 665
    local PORT="$1"
    local ADDR="$2"
    local MODULE="$3"
666 667
    for i in {1..150}; do
        if check_port "" "$PORT" 'socat|nc'; then
668
            break
669
        fi
670 671
        sleep 0.2
    done
672
    echo "ready $ADDR/$MODULE//$sst_ver"
673 674 675 676 677
}

check_extra()
{
    local use_socket=1
678 679 680 681 682
    if [ $uextra -eq 1 ]; then
        local thread_handling=$(parse_cnf '--mysqld' 'thread-handling')
        if [ "$thread_handling" = 'pool-of-threads' ]; then
            local eport=$(parse_cnf '--mysqld' 'extra-port')
            if [ -n "$eport" ]; then
683 684
                # mariabackup works only locally.
                # Hence, setting host to 127.0.0.1 unconditionally:
685
                wsrep_log_info "SST through extra_port $eport"
686
                INNOEXTRA="$INNOEXTRA --host=127.0.0.1 --port=$eport"
687
                use_socket=0
688
            else
689 690 691
                wsrep_log_error "Extra port $eport null, failing"
                exit 1
            fi
692
        else
693 694 695
            wsrep_log_info "Thread pool not set, ignore the option use_extra"
        fi
    fi
696
    if [ $use_socket -eq 1 -a -n "$WSREP_SST_OPT_SOCKET" ]; then
697
        INNOEXTRA="$INNOEXTRA --socket='$WSREP_SST_OPT_SOCKET'"
698 699 700 701 702
    fi
}

recv_joiner()
{
703 704
    local dir="$1"
    local msg="$2"
705 706
    local tmt=$3
    local checkf=$4
707
    local wait=$5
708

709
    if [ ! -d "$dir" ]; then
710 711 712 713
        # This indicates that IST is in progress
        return
    fi

714 715
    local ltcmd="$tcmd"
    if [ $tmt -gt 0 ]; then
716
        if [ -n "$(command -v timeout)" ]; then
717 718 719 720 721
            if timeout --help | grep -qw -- '-k'; then
                ltcmd="timeout -k $(( tmt+10 )) $tmt $tcmd"
            else
                ltcmd="timeout -s9 $tmt $tcmd"
            fi
722 723 724
        fi
    fi

725 726 727
    pushd "$dir" 1>/dev/null
    set +e

728 729 730 731 732 733
    if [ $wait -ne 0 ]; then
        wait_for_listen "$SST_PORT" "$ADDR" "$MODULE" &
    fi

    timeit "$msg" "$ltcmd | $strmcmd; RC=( "\${PIPESTATUS[@]}" )"

734
    set -e
735
    popd 1>/dev/null
736

737
    if [ ${RC[0]} -eq 124 ]; then
738
        wsrep_log_error "Possible timeout in receiving first data from " \
739
                        "donor in gtid stage: exit codes: ${RC[@]}"
740 741 742
        exit 32
    fi

743 744
    for ecode in "${RC[@]}"; do
        if [ $ecode -ne 0 ]; then
745 746 747 748 749 750
            wsrep_log_error "Error while getting data from donor node: " \
                            "exit codes: ${RC[@]}"
            exit 32
        fi
    done

751 752 753 754
    if [ $checkf -eq 1 ]; then
        if [ ! -r "$MAGIC_FILE" ]; then
            # this message should cause joiner to abort
            wsrep_log_error "receiving process ended without creating " \
755
                            "'$MAGIC_FILE'"
756
            wsrep_log_info "Contents of datadir"
757
            wsrep_log_info $(ls -l "$dir/"*)
758 759 760 761
            exit 32
        fi

        # check donor supplied secret
762
        SECRET=$(grep -F -- "$SECRET_TAG " "$MAGIC_FILE" 2>/dev/null | cut -d ' ' -f 2)
763 764 765 766 767 768
        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

769 770
        # remove secret from the magic file
        grep -v -F -- "$SECRET_TAG " "$MAGIC_FILE" > "$MAGIC_FILE.new"
771
        mv "$MAGIC_FILE.new" "$MAGIC_FILE"
772 773 774 775 776
    fi
}

send_donor()
{
777 778
    local dir="$1"
    local msg="$2"
779

780
    pushd "$dir" 1>/dev/null
781 782 783
    set +e
    timeit "$msg" "$strmcmd | $tcmd; RC=( "\${PIPESTATUS[@]}" )"
    set -e
784
    popd 1>/dev/null
785

786 787
    for ecode in "${RC[@]}"; do
        if [ $ecode -ne 0 ]; then
788
            wsrep_log_error "Error while sending data to joiner node: " \
789 790 791 792 793 794
                            "exit codes: ${RC[@]}"
            exit 32
        fi
    done
}

795 796 797 798 799
monitor_process()
{
    local sst_stream_pid=$1

    while true ; do
800
        if ! ps -p "$WSREP_SST_OPT_PARENT" >/dev/null 2>&1; then
801
            wsrep_log_error "Parent mysqld process (PID: $WSREP_SST_OPT_PARENT) terminated unexpectedly."
802
            kill -- -"$WSREP_SST_OPT_PARENT"
803 804
            exit 32
        fi
805
        if ! ps -p "$sst_stream_pid" >/dev/null 2>&1; then
806 807 808 809 810 811
            break
        fi
        sleep 0.1
    done
}

812
[ -f "$MAGIC_FILE" ] && rm -f "$MAGIC_FILE"
813

814
if [ "$WSREP_SST_OPT_ROLE" != 'joiner' -a "$WSREP_SST_OPT_ROLE" != 'donor' ]; then
815
    wsrep_log_error "Invalid role '$WSREP_SST_OPT_ROLE'"
816 817 818 819 820 821
    exit 22
fi

read_cnf
setup_ports

822
if "$BACKUP_BIN" --help 2>/dev/null | grep -qw -- '--version-check'; then
823
    disver='--no-version-check'
824 825
fi

826
# if no command line argument and INNODB_DATA_HOME_DIR environment variable
827
# is not set, try to get it from my.cnf:
828
if [ -z "$INNODB_DATA_HOME_DIR" ]; then
829
    INNODB_DATA_HOME_DIR=$(parse_cnf '--mysqld' 'innodb-data-home-dir')
830 831
fi

832 833
OLD_PWD="$(pwd)"

834
cd "$WSREP_SST_OPT_DATA"
835 836
if [ -n "$INNODB_DATA_HOME_DIR" ]; then
    # handle both relative and absolute paths
837 838
    [ ! -d "$INNODB_DATA_HOME_DIR" ] && mkdir -p "$INNODB_DATA_HOME_DIR"
    cd "$INNODB_DATA_HOME_DIR"
839
fi
840
INNODB_DATA_HOME_DIR=$(pwd -P)
841

842 843
cd "$OLD_PWD"

844
if [ $ssyslog -eq 1 ]; then
845
    if [ -n "$(command -v logger)" ]; then
846
        wsrep_log_info "Logging all stderr of SST/mariabackup to syslog"
847 848 849 850 851

        exec 2> >(logger -p daemon.err -t ${ssystag}wsrep-sst-$WSREP_SST_OPT_ROLE)

        wsrep_log_error()
        {
852
            logger -p daemon.err -t ${ssystag}wsrep-sst-$WSREP_SST_OPT_ROLE "$@"
853 854 855 856
        }

        wsrep_log_info()
        {
857
            logger -p daemon.info -t ${ssystag}wsrep-sst-$WSREP_SST_OPT_ROLE "$@"
858
        }
859 860
    else
        wsrep_log_error "logger not in path: $PATH. Ignoring"
861
    fi
862 863 864
    INNOAPPLY="2>&1 | logger -p daemon.err -t ${ssystag}innobackupex-apply"
    INNOMOVE="2>&1 | logger -p daemon.err -t ${ssystag}innobackupex-move"
    INNOBACKUP="2> >(logger -p daemon.err -t ${ssystag}innobackupex-backup)"
865
else
866 867 868
    if [ $sstlogarchive -eq 1 ]
    then
        ARCHIVETIMESTAMP=$(date "+%Y.%m.%d-%H.%M.%S.%N")
869

870 871 872 873
        if [ -n "$sstlogarchivedir" ]; then
            if [ ! -d "$sstlogarchivedir" ]; then
                mkdir -p "$sstlogarchivedir"
            fi
874 875
        fi

876
        if [ -e "$INNOAPPLYLOG" ]
877
        then
878 879 880 881 882 883 884 885 886 887
            if [ -n "$sstlogarchivedir" ]
            then
                newfile=$(basename "$INNOAPPLYLOG")
                newfile="$sstlogarchivedir/$newfile.$ARCHIVETIMESTAMP"
            else
                newfile="$INNOAPPLYLOG.$ARCHIVETIMESTAMP"
            fi
            wsrep_log_info "Moving '$INNOAPPLYLOG' to '$newfile'"
            mv "$INNOAPPLYLOG" "$newfile"
            gzip "$newfile"
888 889
        fi

890
        if [ -e "$INNOMOVELOG" ]
891
        then
892 893 894 895 896 897 898 899 900 901
            if [ -n "$sstlogarchivedir" ]
            then
                newfile=$(basename "$INNOMOVELOG")
                newfile="$sstlogarchivedir/$newfile.$ARCHIVETIMESTAMP"
            else
                newfile="$INNOMOVELOG.$ARCHIVETIMESTAMP"
            fi
            wsrep_log_info "Moving '$INNOMOVELOG' to '$newfile'"
            mv "$INNOMOVELOG" "$newfile"
            gzip "$newfile"
902 903
        fi

904
        if [ -e "$INNOBACKUPLOG" ]
905
        then
906 907 908 909 910 911 912 913 914 915
            if [ -n "$sstlogarchivedir" ]
            then
                newfile=$(basename "$INNOBACKUPLOG")
                newfile="$sstlogarchivedir/$newfile.$ARCHIVETIMESTAMP"
            else
                newfile="$INNOBACKUPLOG.$ARCHIVETIMESTAMP"
            fi
            wsrep_log_info "Moving '$INNOBACKUPLOG' to '$newfile'"
            mv "$INNOBACKUPLOG" "$newfile"
            gzip "$newfile"
916 917
        fi
    fi
918 919
    INNOAPPLY="> '$INNOAPPLYLOG' 2>&1"
    INNOMOVE="> '$INNOMOVELOG' 2>&1"
920
    INNOBACKUP="2> '$INNOBACKUPLOG'"
921 922
fi

923 924 925 926 927 928
setup_commands()
{
    local mysqld_args=""
    if [ -n "$WSREP_SST_OPT_MYSQLD" ]; then
        mysqld_args="--mysqld-args $WSREP_SST_OPT_MYSQLD"
    fi
929 930 931
    INNOAPPLY="$BACKUP_BIN --prepare $disver $iapts $INNOEXTRA --target-dir='$DATA' --datadir='$DATA' $mysqld_args $INNOAPPLY"
    INNOMOVE="$BACKUP_BIN $WSREP_SST_OPT_CONF --move-back $disver $impts --force-non-empty-directories --target-dir='$DATA' --datadir='${TDATA:-$DATA}' $INNOMOVE"
    INNOBACKUP="$BACKUP_BIN $WSREP_SST_OPT_CONF --backup $disver $iopts $tmpopts $INNOEXTRA --galera-info --stream=$sfmt --target-dir='$itmpdir' --datadir='$DATA' $mysqld_args $INNOBACKUP"
932 933
}

934 935 936
get_stream
get_transfer

937
if [ "$WSREP_SST_OPT_ROLE" = 'donor' ]
938
then
939
    trap cleanup_at_exit EXIT
940 941 942

    if [ $WSREP_SST_OPT_BYPASS -eq 0 ]
    then
943
        if [ -z "$sst_ver" ]; then
944 945 946 947 948
            wsrep_log_error "Upgrade joiner to 5.6.21 or higher for backup locks support"
            wsrep_log_error "The joiner is not supported for this version of donor"
            exit 93
        fi

949 950 951
        tmpdir=$(parse_cnf "$encgroups" 'tmpdir')
        if [ -z "$tmpdir" ]; then
            xtmpdir="$(mktemp -d)"
952
        elif [ "$OS" = 'Linux' ]; then
953
            xtmpdir=$(mktemp '-d' "--tmpdir=$tmpdir")
954 955
        else
            xtmpdir=$(TMPDIR="$tmpdir"; mktemp '-d')
956 957
        fi

958 959 960
        wsrep_log_info "Using '$xtmpdir' as mariabackup temporary directory"
        tmpopts="--tmpdir='$xtmpdir'"

961
        itmpdir="$(mktemp -d)"
962
        wsrep_log_info "Using '$itmpdir' as mariabackup working directory"
963

964
        usrst=0
965
        if [ -n "$WSREP_SST_OPT_USER" ]; then
966
           INNOEXTRA="$INNOEXTRA --user='$WSREP_SST_OPT_USER'"
967 968 969
           usrst=1
        fi

970 971 972
        if [ -n "$WSREP_SST_OPT_PSWD" ]; then
            export MYSQL_PWD="$WSREP_SST_OPT_PSWD"
        elif [ $usrst -eq 1 ]; then
973 974
            # Empty password, used for testing, debugging etc.
            unset MYSQL_PWD
975 976 977 978 979 980 981 982
        fi

        check_extra

        wsrep_log_info "Streaming GTID file before SST"

        # Store donor's wsrep GTID (state ID) and wsrep_gtid_domain_id
        # (separated by a space).
983 984 985 986 987 988
        echo "$WSREP_SST_OPT_GTID $WSREP_SST_OPT_GTID_DOMAIN_ID" > "$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
989 990 991

        ttcmd="$tcmd"

992 993 994 995 996 997 998
        if [ -n "$scomp" ]; then
            tcmd="$scomp | $tcmd"
        fi

        get_keys
        if [ $encrypt -eq 1 ]; then
            tcmd="$ecmd | $tcmd"
999 1000
        fi

1001
        send_donor "$DATA" "$stagemsg-gtid"
1002

1003
        # Restore the transport commmand to its original state
1004
        tcmd="$ttcmd"
1005 1006

        if [ -n "$progress" ]; then
1007 1008
            get_footprint
            tcmd="$pcmd | $tcmd"
1009
        elif [ -n "$rlimit" ]; then
1010 1011 1012 1013 1014 1015 1016
            adjust_progress
            tcmd="$pcmd | $tcmd"
        fi

        wsrep_log_info "Sleeping before data transfer for SST"
        sleep 10

1017
        wsrep_log_info "Streaming the backup to joiner at $REMOTEIP:$SST_PORT"
1018

1019
        # Add compression to the head of the stream (if specified)
1020
        if [ -n "$scomp" ]; then
1021 1022 1023
            tcmd="$scomp | $tcmd"
        fi

1024 1025 1026 1027 1028
        # Add encryption to the head of the stream (if specified)
        if [ $encrypt -eq 1 ]; then
            tcmd="$ecmd | $tcmd"
        fi

1029
        iopts="--databases-exclude='lost+found' $iopts"
1030 1031

        if [ ${FORCE_FTWRL:-0} -eq 1 ]; then
1032
            wsrep_log_info "Forcing FTWRL due to environment variable FORCE_FTWRL equal to $FORCE_FTWRL"
1033
            iopts="--no-backup-locks $iopts"
1034 1035 1036 1037 1038
        fi

        # if compression is enabled for backup files, then add the
        # appropriate options to the mariabackup command line:
        if [ "$compress" != 'none' ]; then
1039
            iopts="--compress${compress:+=$compress} $iopts"
1040
            if [ -n "$compress_threads" ]; then
1041
                iopts="--compress-threads=$compress_threads $iopts"
1042 1043
            fi
            if [ -n "$compress_chunk" ]; then
1044
                iopts="--compress-chunk-size=$compress_chunk $iopts"
1045 1046 1047
            fi
        fi

1048 1049 1050 1051
        if [ -n "$backup_threads" ]; then
            iopts="--parallel=$backup_threads $iopts"
        fi

1052
        setup_commands
1053
        set +e
1054
        timeit "$stagemsg-SST" "$INNOBACKUP | $tcmd; RC=( "\${PIPESTATUS[@]}" )"
1055 1056 1057
        set -e

        if [ ${RC[0]} -ne 0 ]; then
1058 1059
            wsrep_log_error "mariabackup finished with error: ${RC[0]}. " \
                            "Check syslog or '$INNOBACKUPLOG' for details"
1060
            exit 22
1061
        elif [ ${RC[$(( ${#RC[@]}-1 ))]} -eq 1 ]; then
1062 1063
            wsrep_log_error "$tcmd finished with error: ${RC[1]}"
            exit 22
1064 1065
        fi

1066
        # mariabackup implicitly writes PID to fixed location in $xtmpdir
1067
        BACKUP_PID="$xtmpdir/xtrabackup_pid"
1068 1069 1070 1071 1072 1073 1074 1075

    else # BYPASS FOR IST

        wsrep_log_info "Bypassing the SST for IST"
        echo "continue" # now server can resume updating data

        # Store donor's wsrep GTID (state ID) and wsrep_gtid_domain_id
        # (separated by a space).
1076 1077 1078 1079 1080 1081 1082
        echo "$WSREP_SST_OPT_GTID $WSREP_SST_OPT_GTID_DOMAIN_ID" > "$MAGIC_FILE"
        echo "1" > "$DATA/$IST_FILE"

        if [ -n "$scomp" ]; then
            tcmd="$scomp | $tcmd"
        fi

1083
        get_keys
1084 1085
        if [ $encrypt -eq 1 ]; then
            tcmd="$ecmd | $tcmd"
1086 1087
        fi

1088
        strmcmd="$strmcmd '$IST_FILE'"
1089

1090
        send_donor "$DATA" "$stagemsg-IST"
1091 1092 1093

    fi

1094
    echo "done $WSREP_SST_OPT_GTID"
1095 1096
    wsrep_log_info "Total time on donor: $totime seconds"

1097
elif [ "$WSREP_SST_OPT_ROLE" = 'joiner' ]
1098
then
1099 1100
    [ -e "$SST_PROGRESS_FILE" ] && wsrep_log_info "Stale sst_in_progress file: $SST_PROGRESS_FILE"
    [ -n "$SST_PROGRESS_FILE" ] && touch "$SST_PROGRESS_FILE"
1101

1102
    ib_home_dir="$INNODB_DATA_HOME_DIR"
1103

1104
    # if no command line argument and INNODB_LOG_GROUP_HOME is not set,
1105
    # try to get it from my.cnf:
1106 1107
    if [ -z "$INNODB_LOG_GROUP_HOME" ]; then
        INNODB_LOG_GROUP_HOME=$(parse_cnf '--mysqld' 'innodb-log-group-home-dir')
1108 1109
    fi

1110
    ib_log_dir="$INNODB_LOG_GROUP_HOME"
1111

1112 1113 1114
    # 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')
1115
    fi
1116

1117 1118
    ib_undo_dir="$INNODB_UNDO_DIR"

1119 1120 1121 1122
    if [ -n "$backup_threads" ]; then
        impts="--parallel=$backup_threads $impts"
    fi

1123
    stagemsg='Joiner-Recv'
1124 1125 1126 1127 1128 1129

    sencrypted=1
    nthreads=1

    MODULE="xtrabackup_sst"

1130
    [ -f "$DATA/$IST_FILE" ] && rm -f "$DATA/$IST_FILE"
1131 1132

    # May need xtrabackup_checkpoints later on
1133 1134 1135
    [ -f "$DATA/xtrabackup_binary"      ] && rm -f "$DATA/xtrabackup_binary"
    [ -f "$DATA/xtrabackup_galera_info" ] && rm -f "$DATA/xtrabackup_galera_info"
    [ -f "$DATA/ib_logfile0"            ] && rm -f "$DATA/ib_logfile0"
1136

1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150
    ADDR="$WSREP_SST_OPT_ADDR"

    if [ "${tmode#VERIFY}" != "$tmode" ]
    then # backward-incompatible behavior
        CN=""
        if [ -n "$tpem" ]
        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 "$tpem" | \
1151
                 tr "," "\n" | grep -F 'CN =' | cut -d= -f2 | sed s/^\ // | \
1152
                 sed s/\ %//)
1153
        fi
1154
        MY_SECRET="$(wsrep_gen_secret)"
1155 1156 1157 1158
        # Add authentication data to address
        ADDR="$CN:$MY_SECRET@$ADDR"
    else
        MY_SECRET="" # for check down in recv_joiner()
1159 1160 1161
    fi

    trap sig_joiner_cleanup HUP PIPE INT TERM
1162
    trap cleanup_at_exit EXIT
1163

1164
    if [ -n "$progress" ]; then
1165
        adjust_progress
1166
        tcmd="$tcmd | $pcmd"
1167 1168 1169
    fi

    get_keys
1170
    if [ $encrypt -eq 1 -a $sencrypted -eq 1 ]; then
1171 1172 1173 1174 1175
        strmcmd="$ecmd | $strmcmd"
    fi

    if [ -n "$sdecomp" ]; then
        strmcmd="$sdecomp | $strmcmd"
1176 1177
    fi

1178 1179 1180
    check_sockets_utils

    STATDIR="$(mktemp -d)"
1181
    MAGIC_FILE="$STATDIR/$INFO_FILE"
1182

1183 1184
    recv_joiner "$STATDIR" "$stagemsg-gtid" $stimeout 1 1

1185
    if ! ps -p "$WSREP_SST_OPT_PARENT" >/dev/null 2>&1
1186
    then
1187
        wsrep_log_error "Parent mysqld process (PID: $WSREP_SST_OPT_PARENT) terminated unexpectedly."
1188 1189 1190
        exit 32
    fi

1191
    if [ ! -r "$STATDIR/$IST_FILE" ]
1192 1193
    then

1194
        if [ -d "$DATA/.sst" ]; then
1195
            wsrep_log_info "WARNING: Stale temporary SST directory: '$DATA/.sst' from previous state transfer. Removing"
1196
            rm -rf "$DATA/.sst"
1197
        fi
1198
        mkdir -p "$DATA/.sst"
1199
        (recv_joiner "$DATA/.sst" "$stagemsg-SST" 0 0 0) &
1200 1201 1202 1203
        jpid=$!
        wsrep_log_info "Proceeding with SST"

        wsrep_log_info "Cleaning the existing datadir and innodb-data/log directories"
1204
        if [ "$OS" = 'FreeBSD' ]; then
1205 1206 1207 1208
            find -E ${ib_home_dir:+"$ib_home_dir"} \
                    ${ib_undo_dir:+"$ib_undo_dir"} \
                    ${ib_log_dir:+"$ib_log_dir"} \
                    "$DATA" -mindepth 1 -prune -regex "$cpat" -o -exec rm -rfv {} 1>&2 \+
1209
        else
1210 1211 1212 1213
            find ${ib_home_dir:+"$ib_home_dir"} \
                 ${ib_undo_dir:+"$ib_undo_dir"} \
                 ${ib_log_dir:+"$ib_log_dir"} \
                 "$DATA" -mindepth 1 -prune -regex "$cpat" -o -exec rm -rfv {} 1>&2 \+
1214
        fi
1215

1216 1217 1218 1219 1220 1221 1222 1223 1224 1225
        get_binlog

        if [ -n "$WSREP_SST_OPT_BINLOG" ]; then
            binlog_dir=$(dirname "$WSREP_SST_OPT_BINLOG")
            cd "$binlog_dir"
            wsrep_log_info "Cleaning the binlog directory $binlog_dir as well"
            rm -fv "$WSREP_SST_OPT_BINLOG".[0-9]* 1>&2 \+ || true
            binlog_index="${WSREP_SST_OPT_BINLOG_INDEX%.index}.index"
            [ -f "$binlog_index" ] && rm -fv "$binlog_index" 1>&2 \+ || true
            cd "$OLD_PWD"
1226 1227
        fi

1228 1229
        TDATA="$DATA"
        DATA="$DATA/.sst"
1230

1231
        MAGIC_FILE="$DATA/$INFO_FILE"
1232
        wsrep_log_info "Waiting for SST streaming to complete!"
1233
        monitor_process $jpid
1234

1235
        if [ ! -s "$DATA/xtrabackup_checkpoints" ]; then
1236
            wsrep_log_error "xtrabackup_checkpoints missing, failed mariabackup/SST on donor"
1237 1238 1239
            exit 2
        fi

1240
        # Compact backups are not supported by mariabackup
1241
        if grep -q -F 'compact = 1' "$DATA/xtrabackup_checkpoints"; then
1242
            wsrep_log_info "Index compaction detected"
1243 1244
            wsrel_log_error "Compact backups are not supported by mariabackup"
            exit 2
1245 1246
        fi

1247 1248
        qpfiles=$(find "$DATA" -maxdepth 1 -type f -name '*.qp' -print -quit)
        if [ -n "$qpfiles" ]; then
1249 1250
            wsrep_log_info "Compressed qpress files found"

1251 1252
            if [ -z "$(command -v qpress)" ]; then
                wsrep_log_error "qpress utility not found in the path"
1253 1254 1255
                exit 22
            fi

1256 1257 1258 1259
            get_proc

            dcmd="xargs -n 2 qpress -dT$nproc"

1260
            if [ -n "$progress" ] && pv --help | grep -qw -- '--line-mode'; then
1261
                count=$(find "$DATA" -type f -name '*.qp' | wc -l)
1262
                count=$(( count*2 ))
1263 1264 1265
                pvopts="-f -s $count -l -N Decompression"
                if pv --help | grep -qw -- '-F'; then
                    pvopts="$pvopts -F '%N => Rate:%r Elapsed:%t %e Progress: [%b/$count]'"
1266 1267 1268
                fi
                pcmd="pv $pvopts"
                adjust_progress
1269
                dcmd="$pcmd | $dcmd"
1270 1271
            fi

1272
            # Decompress the qpress files
1273
            wsrep_log_info "Decompression with $nproc threads"
1274
            timeit "Joiner-Decompression" "find '$DATA' -type f -name '*.qp' -printf '%p\n%h\n' | $dcmd"
1275 1276
            extcode=$?

1277
            if [ $extcode -eq 0 ]; then
1278
                wsrep_log_info "Removing qpress files after decompression"
1279
                find "$DATA" -type f -name '*.qp' -delete
1280
                if [ $? -ne 0 ]; then
1281 1282 1283 1284 1285 1286 1287 1288
                    wsrep_log_error "Something went wrong with deletion of qpress files. Investigate"
                fi
            else
                wsrep_log_error "Decompression failed. Exit code: $extcode"
                exit 22
            fi
        fi

1289
        if  [ -n "$WSREP_SST_OPT_BINLOG" ]; then
1290

1291 1292
            BINLOG_DIRNAME=$(dirname "$WSREP_SST_OPT_BINLOG")
            BINLOG_FILENAME=$(basename "$WSREP_SST_OPT_BINLOG")
1293

1294
            # To avoid comparing data directory and BINLOG_DIRNAME
1295
            mv "$DATA/$BINLOG_FILENAME".* "$BINLOG_DIRNAME/" 2>/dev/null || true
1296

1297 1298 1299
            cd "$BINLOG_DIRNAME"
            for bfile in $(ls -1 "$BINLOG_FILENAME".[0-9]*); do
                echo "$BINLOG_DIRNAME/$bfile" >> "${WSREP_SST_OPT_BINLOG_INDEX%.index}.index"
1300
            done
1301
            cd "$OLD_PWD"
1302 1303 1304

        fi

1305
        wsrep_log_info "Preparing the backup at $DATA"
1306
        setup_commands
1307
        timeit "mariabackup prepare stage" "$INNOAPPLY"
1308

1309
        if [ $? -ne 0 ]; then
1310
            wsrep_log_error "mariabackup apply finished with errors. Check syslog or '$INNOAPPLYLOG' for details"
1311 1312 1313
            exit 22
        fi

1314
        MAGIC_FILE="$TDATA/$INFO_FILE"
1315

1316
        wsrep_log_info "Moving the backup to $TDATA"
1317 1318
        timeit "mariabackup move stage" "$INNOMOVE"
        if [ $? -eq 0 ]; then
1319
            wsrep_log_info "Move successful, removing $DATA"
1320 1321
            rm -rf "$DATA"
            DATA="$TDATA"
1322
        else
1323
            wsrep_log_error "Move failed, keeping '$DATA' for further diagnosis"
1324
            wsrep_log_error "Check syslog or '$INNOMOVELOG' for details"
1325 1326 1327
            exit 22
        fi

1328
    else
1329

1330
        wsrep_log_info "'$IST_FILE' received from donor: Running IST"
1331

1332 1333
    fi

1334
    if [ ! -r "$MAGIC_FILE" ]; then
1335
        wsrep_log_error "SST magic file '$MAGIC_FILE' not found/readable"
1336 1337
        exit 2
    fi
1338 1339 1340

    coords=$(cat "$MAGIC_FILE")
    wsrep_log_info "Galera co-ords from recovery: $coords"
1341
    cat "$MAGIC_FILE" # Output : UUID:seqno wsrep_gtid_domain_id
1342

1343 1344 1345 1346
    wsrep_log_info "Total time on joiner: $totime seconds"
fi

exit 0