Fix DR-spare path reset on reinstall and tilde-quoting in remote commands
Two stacked bugs, found together when re-running the backup installer to
add an SFTP mirror silently reverted a previously-set absolute
DR_SYNC_PATH back to the script's tilde-based default, which then failed
outright:
1. services/backup.sh never read DR_SYNC_HOST/DR_SYNC_PATH back from an
existing backup.conf before prompting (every other setting in this file
does — passwords, mirrors). Accepting the prompt defaults on a rerun
silently reset both to blank/"~/docker/backup" instead of keeping what
was already configured. Fixed by reading them back the same way
DEST_*_PASSWORD already does.
2. extras/backup_kopia.sh's DR-spare sync wraps the remote path in single
quotes for its `ssh host "mkdir -p '...'"` / `"chmod 600 '.../...'"`
commands. Single-quoting a leading ~ stops the remote shell from
expanding it at all, so it looked for a literal directory named "~"
instead of the home directory — breaking the script's own DEFAULT
DR_SYNC_PATH ("~/docker/backup") for anyone who actually used it.
rsync's own transfer step has separate, correct tilde handling, which is
why the sync itself "succeeded" while the follow-up chmod couldn't find
the file. Fixed with a small _dr_remote_quote() helper that keeps a
leading ~/ outside the quotes while still safely quoting the rest of
the path.
Verified the quoting fix by parsing the exact constructed command string
in bash directly — a plain '~/docker/backup' stays literal (the bug),
~/'docker/backup' correctly expands to $HOME/docker/backup (the fix).
This commit is contained in:
+22
-2
@@ -229,6 +229,26 @@ done
|
|||||||
# yet. dr_bringup.sh on the spare only needs these two small files — the
|
# yet. dr_bringup.sh on the spare only needs these two small files — the
|
||||||
# repo data itself already lives wherever REMOTE_TYPE mirrored it (or is
|
# repo data itself already lives wherever REMOTE_TYPE mirrored it (or is
|
||||||
# local, if the spare IS that target).
|
# local, if the spare IS that target).
|
||||||
|
# Wraps a remote path for use inside a `ssh host "command '...'"` string so
|
||||||
|
# it's still safely quoted (spaces etc.) while a leading ~/ stays OUTSIDE
|
||||||
|
# the quotes — single-quoting a leading tilde stops the remote shell from
|
||||||
|
# expanding it at all, so it goes looking for a literal directory named
|
||||||
|
# "~" instead of the actual home directory. Confirmed live: this is exactly
|
||||||
|
# what broke `mkdir -p '~/docker/backup'` / `chmod 600 '~/docker/backup/...'`
|
||||||
|
# — the DEFAULT DR_SYNC_PATH — while rsync's own transfer step (which has
|
||||||
|
# its own tilde-aware remote-path handling, unrelated to shell quoting)
|
||||||
|
# succeeded against the exact same path.
|
||||||
|
_dr_remote_quote() {
|
||||||
|
local p="$1"
|
||||||
|
if [[ "$p" == "~/"* ]]; then
|
||||||
|
printf "~/'%s'" "${p#\~/}"
|
||||||
|
elif [[ "$p" == "~" ]]; then
|
||||||
|
printf '~'
|
||||||
|
else
|
||||||
|
printf "'%s'" "$p"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
if [ -n "${DR_SYNC_HOST:-}" ]; then
|
if [ -n "${DR_SYNC_HOST:-}" ]; then
|
||||||
_dr_path="${DR_SYNC_PATH:-~/docker/backup}"
|
_dr_path="${DR_SYNC_PATH:-~/docker/backup}"
|
||||||
log "Syncing backup.conf + README to spare ($DR_SYNC_HOST:$_dr_path)..."
|
log "Syncing backup.conf + README to spare ($DR_SYNC_HOST:$_dr_path)..."
|
||||||
@@ -240,9 +260,9 @@ if [ -n "${DR_SYNC_HOST:-}" ]; then
|
|||||||
# closed" while plain ssh exec and rsync's own protocol both still work
|
# closed" while plain ssh exec and rsync's own protocol both still work
|
||||||
# fine over the same connection. Confirmed live: scp failing this way
|
# fine over the same connection. Confirmed live: scp failing this way
|
||||||
# while `ssh "$DR_SYNC_HOST" true` succeeded, rsync doesn't hit it.
|
# while `ssh "$DR_SYNC_HOST" true` succeeded, rsync doesn't hit it.
|
||||||
if ssh -o BatchMode=yes -o ConnectTimeout=10 "$DR_SYNC_HOST" "mkdir -p '$_dr_path'" 2>"$_ERR" \
|
if ssh -o BatchMode=yes -o ConnectTimeout=10 "$DR_SYNC_HOST" "mkdir -p $(_dr_remote_quote "$_dr_path")" 2>"$_ERR" \
|
||||||
&& rsync -a -e 'ssh -o BatchMode=yes -o ConnectTimeout=10' "${_dr_files[@]}" "$DR_SYNC_HOST:$_dr_path/" 2>>"$_ERR" \
|
&& rsync -a -e 'ssh -o BatchMode=yes -o ConnectTimeout=10' "${_dr_files[@]}" "$DR_SYNC_HOST:$_dr_path/" 2>>"$_ERR" \
|
||||||
&& ssh -o BatchMode=yes -o ConnectTimeout=10 "$DR_SYNC_HOST" "chmod 600 '$_dr_path/backup.conf'" 2>>"$_ERR"; then
|
&& ssh -o BatchMode=yes -o ConnectTimeout=10 "$DR_SYNC_HOST" "chmod 600 $(_dr_remote_quote "$_dr_path/backup.conf")" 2>>"$_ERR"; then
|
||||||
log "OK spare sync ($DR_SYNC_HOST)"
|
log "OK spare sync ($DR_SYNC_HOST)"
|
||||||
else
|
else
|
||||||
_err_text="$(cat "$_ERR" 2>/dev/null)"
|
_err_text="$(cat "$_ERR" 2>/dev/null)"
|
||||||
|
|||||||
+6
-2
@@ -563,9 +563,13 @@ install_backup() {
|
|||||||
echo " up first if you haven't (ssh-keygen, then ssh-copy-id to the spare)."
|
echo " up first if you haven't (ssh-keygen, then ssh-copy-id to the spare)."
|
||||||
echo ""
|
echo ""
|
||||||
local DR_SYNC_HOST="" DR_SYNC_PATH=""
|
local DR_SYNC_HOST="" DR_SYNC_PATH=""
|
||||||
prompt_text " Spare box SSH destination, user@host (blank to skip):" "" DR_SYNC_HOST
|
if [ -f "$CONF_FILE" ]; then
|
||||||
|
DR_SYNC_HOST="$(grep '^DR_SYNC_HOST=' "$CONF_FILE" 2>/dev/null | sed -E "s/^DR_SYNC_HOST='(.*)'\$/\1/")"
|
||||||
|
DR_SYNC_PATH="$(grep '^DR_SYNC_PATH=' "$CONF_FILE" 2>/dev/null | sed -E "s/^DR_SYNC_PATH='(.*)'\$/\1/")"
|
||||||
|
fi
|
||||||
|
prompt_text " Spare box SSH destination, user@host (blank to skip):" "$DR_SYNC_HOST" DR_SYNC_HOST
|
||||||
if [ -n "$DR_SYNC_HOST" ]; then
|
if [ -n "$DR_SYNC_HOST" ]; then
|
||||||
prompt_text " Path for backup.conf/README on the spare:" "~/docker/backup" DR_SYNC_PATH
|
prompt_text " Path for backup.conf/README on the spare:" "${DR_SYNC_PATH:-~/docker/backup}" DR_SYNC_PATH
|
||||||
DR_SYNC_PATH="${DR_SYNC_PATH:-~/docker/backup}"
|
DR_SYNC_PATH="${DR_SYNC_PATH:-~/docker/backup}"
|
||||||
|
|
||||||
# Catch a missing/unauthorized key now, not at 2am during the first
|
# Catch a missing/unauthorized key now, not at 2am during the first
|
||||||
|
|||||||
Reference in New Issue
Block a user