From 4baf9150016177582dd56a1bf3c09a3cad5aa050 Mon Sep 17 00:00:00 2001 From: TheMightyV Date: Sun, 2 Jan 2022 20:05:55 +0100 Subject: more generic backend selection, added borgbackup backend --- backends/borg.sh | 66 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 backends/borg.sh (limited to 'backends/borg.sh') diff --git a/backends/borg.sh b/backends/borg.sh new file mode 100644 index 0000000..d3d9c63 --- /dev/null +++ b/backends/borg.sh @@ -0,0 +1,66 @@ +function borg_init() { + export BORG_PASSCOMMAND="$BACKUP_PASSCOMMAND" + for BACKUP_DIR in ${BACKUP_DIRS[*]} + do + # borg will check if repo exists + borg init --encryption=repokey-blake2 "$BACKUP_DIR" + done +} + +function borg_create_backup() { + export BORG_PASSCOMMAND="$BACKUP_PASSCOMMAND" + RETCODE=255 + for BACKUP_DIR in ${BACKUP_DIRS[*]} + do + export BORG_REPO="$BACKUP_DIR" + + trap 'echo $( date ) Backup interrupted >&2; exit 2' INT TERM + + echo "borg: starting backup to \"$BACKUP_DIR\"" + + borg create \ + "${BACKUP_DIR}::${BACKUP_NAME}_{hostname}_{now}" \ + "$WORLD_NAME" \ + --filter AME \ + --compression lz4 \ + --exclude-caches \ + + backup_exit=$? + + echo "borg: pruning repository at \"$BACKUP_DIR\"" + + borg prune \ + --prefix '{hostname}-' \ + --keep-minutely 2 \ + --keep-hourly 24 \ + --keep-daily 7 \ + --keep-weekly 4 \ + --keep-monthly 6 \ + "$BACKUP_DIR" + + prune_exit=$? + + # use highest exit code as global exit code + global_exit=$(( backup_exit > prune_exit ? backup_exit : prune_exit )) + RETCODE=$(( global_exit > RETCODE ? global_exit : RETCODE )) + + if [ ${global_exit} -eq 0 ]; then + echo "borg: backup and prune finished successfully" + elif [ ${global_exit} -eq 1 ]; then + echo "borg: backup and/or prune finished with warnings" + else + echo "borg: backup and/or prune finished with errors" + fi + #exit ${global_exit} + done + return $RETCODE +} + +function borg_ls() { + export BORG_PASSCOMMAND="$BACKUP_PASSCOMMAND" + for BACKUP_DIR in ${BACKUP_DIRS[*]} + do + echo "borg: backups in \"$BACKUP_DIR\":" + borg list "$BACKUP_DIR" + done +} -- cgit v1.2.3 From de4fc74d50d536f524aa4c65e270123bfe8ef893 Mon Sep 17 00:00:00 2001 From: TheMightyV Date: Sun, 2 Jan 2022 20:12:27 +0100 Subject: added backup restoration --- backends/borg.sh | 16 +++++++++++++++- backends/bup.sh | 11 +++++++++++ backends/tar.sh | 34 ++++++++++++++++++++++++++-------- server.sh | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 105 insertions(+), 10 deletions(-) (limited to 'backends/borg.sh') diff --git a/backends/borg.sh b/backends/borg.sh index d3d9c63..999067f 100644 --- a/backends/borg.sh +++ b/backends/borg.sh @@ -19,7 +19,7 @@ function borg_create_backup() { echo "borg: starting backup to \"$BACKUP_DIR\"" borg create \ - "${BACKUP_DIR}::${BACKUP_NAME}_{hostname}_{now}" \ + "${BACKUP_DIR}::${BACKUP_NAME}_$(date +'%F_%H-%M-%S')" \ "$WORLD_NAME" \ --filter AME \ --compression lz4 \ @@ -56,6 +56,11 @@ function borg_create_backup() { return $RETCODE } +function borg_ls_remote() { + export BORG_PASSCOMMAND="$BACKUP_PASSCOMMAND" + borg list "$1" | cut -d' ' -f1 +} + function borg_ls() { export BORG_PASSCOMMAND="$BACKUP_PASSCOMMAND" for BACKUP_DIR in ${BACKUP_DIRS[*]} @@ -64,3 +69,12 @@ function borg_ls() { borg list "$BACKUP_DIR" done } + +function borg_restore() { + export BORG_PASSCOMMAND="$BACKUP_PASSCOMMAND" + REMOTE="$1" + SNAPSHOT="$2" + + export BORG_REPO="$REMOTE" + borg extract "${REMOTE}::${SNAPSHOT}" +} diff --git a/backends/bup.sh b/backends/bup.sh index e51aaf1..ab27825 100644 --- a/backends/bup.sh +++ b/backends/bup.sh @@ -54,6 +54,10 @@ function bup_create_backup() { return $RETCODE } +function bup_ls_remote() { + bup -d "$(bup_local)" ls -r "$BACKUP_DIR" "$BACKUP_NAME" +} + function bup_ls() { for BACKUP_DIR in ${BACKUP_DIRS[*]} do @@ -61,3 +65,10 @@ function bup_ls() { bup -d "$(bup_local)" ls -r "$BACKUP_DIR" --human-readable -l "$BACKUP_NAME" done } + +function bup_restore() { + REMOTE="$1" + SNAPSHOT="$2" + + bup -d "$(bup_local)" restore -r "$REMOTE" "$BACKUP_NAME/$SNAPSHOT/$PWD/." +} diff --git a/backends/tar.sh b/backends/tar.sh index c6012e3..b556798 100644 --- a/backends/tar.sh +++ b/backends/tar.sh @@ -8,7 +8,7 @@ function tar_create_backup() { echo "tar: backing up..." # save world to a temporary archive - ARCHNAME="/tmp/${BACKUP_NAME}_`date +%d-%m-%y-%T`.tar.gz" + ARCHNAME="/tmp/${BACKUP_NAME}_`date +%FT%H%M%S%z`.tar.gz" tar -czf "$ARCHNAME" "./$WORLD_NAME" if [ ! $? -eq 0 ] @@ -40,16 +40,34 @@ function tar_create_backup() { return $RETCODE } +function tar_ls_remote() { + BACKUP_DIR="$1" + if [[ $BACKUP_DIR == *:* ]]; then + REMOTE="$(echo "$BACKUP_DIR" | cut -d: -f1)" + REMOTE_DIR="$(echo "$BACKUP_DIR" | cut -d: -f2)" + ssh "$REMOTE" "ls -1 $REMOTE_DIR" | grep "tar.gz" + else + ls -1 "$BACKUP_DIR" | grep "tar.gz" + fi +} + function tar_ls() { for BACKUP_DIR in ${BACKUP_DIRS[*]} do echo "Backups in $BACKUP_DIR:" - if [[ $BACKUP_DIR == *:* ]]; then - REMOTE="$(echo "$BACKUP_DIR" | cut -d: -f1)" - REMOTE_DIR="$(echo "$BACKUP_DIR" | cut -d: -f2)" - ssh "$REMOTE" "ls -1sh $REMOTE_DIR" | grep "tar.gz" - else - ls -1sh "$BACKUP_DIR" | grep "tar.gz" - fi + tar_ls_remote "$BACKUP_DIR" done } + +function tar_restore() { + REMOTE="$1" + SNAPSHOT="$2" + + scp "$REMOTE/$SNAPSHOT" "/tmp/" + if [ ! $? -eq 0 ]; then + echo "Failed to get archive from \"$REMOTE/$SNAPSHOT\"" + return 1 + fi + + tar -xzf "/tmp/$SNAPSHOT" +} diff --git a/server.sh b/server.sh index ee16af7..d144053 100755 --- a/server.sh +++ b/server.sh @@ -232,6 +232,56 @@ function ls_backups() { fi } +# TODO: replace dmenu with terminal-only option +function server_restore() { + REMOTES=$( IFS=$'\n'; echo "${BACKUP_DIRS[*]}" ) + REMOTE=$(echo "$REMOTES" | dmenu -l 30 -p "Select a remote to get snapshot from") + if [[ ! "$REMOTES" == *"${REMOTE}"* ]] || [ ! "$REMOTE" ] ; then + echo "No remote selected, abort" + return 1 + fi + + SNAPSHOTS=$( + if [ $BACKUP_BACKEND = "bup" ]; then + bup_ls_remote "$REMOTE" + elif [ $BACKUP_BACKEND = "borg" ]; then + borg_ls_remote "$REMOTE" + else + tar_ls_remote "$REMOTE" + fi + ) + SNAPSHOT=$(echo "$SNAPSHOTS" | dmenu -l 30 -p "Select a snapshot to recover") + if [[ ! "$SNAPSHOTS" == *"${SNAPSHOT}"* ]] || [ ! "$SNAPSHOT" ] ; then + echo "No snapshot selected, abort" + return 1 + fi + + echo "Restoring snapshot \"$SNAPSHOT\" from remote \"$REMOTE\"" + + OLDWORLD_NAME="" + if [[ -d "$WORLD_NAME" ]]; then + echo "bup: Saving old world, just in case" + OLDWORLD_NAME="${WORLD_NAME}.old.$(date +'%F_%H-%M-%S')" + mv -v "$WORLD_NAME" "$OLDWORLD_NAME" + fi + + if [ $BACKUP_BACKEND = "bup" ]; then + bup_restore "$REMOTE" "$SNAPSHOT" + elif [ $BACKUP_BACKEND = "borg" ]; then + borg_restore "$REMOTE" "$SNAPSHOT" + else + tar_restore "$REMOTE" "$SNAPSHOT" + fi + + if [ ! $? -eq 0 ]; then + echo "Restore failed, reverting old world back" + rm -r "$WORLD_NAME" + mv -v "$OLDWORLD_NAME" "$WORLD_NAME" + else + echo "Restore finished" + fi +} + #cd $(dirname $0) case $1 in @@ -247,7 +297,9 @@ case $1 in "backup") server_backup ;; - # TODO: Add restore command + "restore") + server_restore + ;; "status") server_status ;; -- cgit v1.2.3 From 8974233a537d2890d75d398bdbdf5ca73152b23b Mon Sep 17 00:00:00 2001 From: TheMightyV Date: Sun, 2 Jan 2022 21:18:02 +0100 Subject: moved from dmenu to bash select, minor cosmetics --- backends/borg.sh | 7 ++++--- backends/bup.sh | 5 +++-- backends/tar.sh | 5 +++-- server.sh | 61 +++++++++++++++++++++++++++++++++++--------------------- 4 files changed, 48 insertions(+), 30 deletions(-) (limited to 'backends/borg.sh') diff --git a/backends/borg.sh b/backends/borg.sh index 999067f..f5930c8 100644 --- a/backends/borg.sh +++ b/backends/borg.sh @@ -56,17 +56,18 @@ function borg_create_backup() { return $RETCODE } -function borg_ls_remote() { +# server_restore relies on output format of this function +function borg_ls_dir() { export BORG_PASSCOMMAND="$BACKUP_PASSCOMMAND" borg list "$1" | cut -d' ' -f1 } -function borg_ls() { +function borg_ls_all() { export BORG_PASSCOMMAND="$BACKUP_PASSCOMMAND" for BACKUP_DIR in ${BACKUP_DIRS[*]} do echo "borg: backups in \"$BACKUP_DIR\":" - borg list "$BACKUP_DIR" + borg list "$BACKUP_DIR" | cut -d' ' -f1 done } diff --git a/backends/bup.sh b/backends/bup.sh index ab27825..a5ddb14 100644 --- a/backends/bup.sh +++ b/backends/bup.sh @@ -54,11 +54,12 @@ function bup_create_backup() { return $RETCODE } -function bup_ls_remote() { +# server_restore relies on output format of this function +function bup_ls_dir() { bup -d "$(bup_local)" ls -r "$BACKUP_DIR" "$BACKUP_NAME" } -function bup_ls() { +function bup_ls_all() { for BACKUP_DIR in ${BACKUP_DIRS[*]} do echo "bup: backups in \"$BACKUP_DIR\":" diff --git a/backends/tar.sh b/backends/tar.sh index b556798..1265992 100644 --- a/backends/tar.sh +++ b/backends/tar.sh @@ -40,7 +40,8 @@ function tar_create_backup() { return $RETCODE } -function tar_ls_remote() { +# server_restore relies on output format of this function +function tar_ls_dir() { BACKUP_DIR="$1" if [[ $BACKUP_DIR == *:* ]]; then REMOTE="$(echo "$BACKUP_DIR" | cut -d: -f1)" @@ -51,7 +52,7 @@ function tar_ls_remote() { fi } -function tar_ls() { +function tar_ls_all() { for BACKUP_DIR in ${BACKUP_DIRS[*]} do echo "Backups in $BACKUP_DIR:" diff --git a/server.sh b/server.sh index d144053..46156af 100755 --- a/server.sh +++ b/server.sh @@ -224,53 +224,68 @@ function server_backup() { function ls_backups() { if [ $BACKUP_BACKEND = "bup" ]; then - bup_ls + bup_ls_all elif [ $BACKUP_BACKEND = "borg" ]; then - borg_ls + borg_ls_all else - tar_ls + tar_ls_all fi } -# TODO: replace dmenu with terminal-only option +function choose_from() { + local items=("$@") + select item in "${items[@]}"; do + echo "$item" + return + done + echo "" +} + function server_restore() { - REMOTES=$( IFS=$'\n'; echo "${BACKUP_DIRS[*]}" ) - REMOTE=$(echo "$REMOTES" | dmenu -l 30 -p "Select a remote to get snapshot from") - if [[ ! "$REMOTES" == *"${REMOTE}"* ]] || [ ! "$REMOTE" ] ; then - echo "No remote selected, abort" + echo "Select from where get the snapshot" + BACKUP_DIR=$(choose_from "${BACKUP_DIRS[@]}") + if [[ ! ${BACKUP_DIRS[*]} =~ (^|[[:space:]])"$BACKUP_DIR"($|[[:space:]]) ]]; then + echo "No valid backup directory selected, abort" return 1 fi SNAPSHOTS=$( - if [ $BACKUP_BACKEND = "bup" ]; then - bup_ls_remote "$REMOTE" - elif [ $BACKUP_BACKEND = "borg" ]; then - borg_ls_remote "$REMOTE" - else - tar_ls_remote "$REMOTE" - fi + if [ $BACKUP_BACKEND = "bup" ]; then + bup_ls_dir "$BACKUP_DIR" + elif [ $BACKUP_BACKEND = "borg" ]; then + borg_ls_dir "$BACKUP_DIR" + else + tar_ls_dir "$BACKUP_DIR" + fi ) - SNAPSHOT=$(echo "$SNAPSHOTS" | dmenu -l 30 -p "Select a snapshot to recover") - if [[ ! "$SNAPSHOTS" == *"${SNAPSHOT}"* ]] || [ ! "$SNAPSHOT" ] ; then - echo "No snapshot selected, abort" + if [ -z "$SNAPSHOTS" ]; then + echo "No snapshots found, abort" + return 1 + fi + # convert multiline string to bash array + SNAPSHOTS=($(echo "$SNAPSHOTS")) + echo "Select a snapshot to restore" + SNAPSHOT=$(choose_from "${SNAPSHOTS[@]}") + if [[ ! ${SNAPSHOTS[*]} =~ (^|[[:space:]])"$SNAPSHOT"($|[[:space:]]) ]]; then + echo "No valid snapshot selected, abort" return 1 fi - echo "Restoring snapshot \"$SNAPSHOT\" from remote \"$REMOTE\"" + echo "Restoring snapshot \"$SNAPSHOT\" from \"$BACKUP_DIR\"" OLDWORLD_NAME="" if [[ -d "$WORLD_NAME" ]]; then - echo "bup: Saving old world, just in case" + echo "Saving old world, just in case" OLDWORLD_NAME="${WORLD_NAME}.old.$(date +'%F_%H-%M-%S')" mv -v "$WORLD_NAME" "$OLDWORLD_NAME" fi if [ $BACKUP_BACKEND = "bup" ]; then - bup_restore "$REMOTE" "$SNAPSHOT" + bup_restore "$BACKUP_DIR" "$SNAPSHOT" elif [ $BACKUP_BACKEND = "borg" ]; then - borg_restore "$REMOTE" "$SNAPSHOT" + borg_restore "$BACKUP_DIR" "$SNAPSHOT" else - tar_restore "$REMOTE" "$SNAPSHOT" + tar_restore "$BACKUP_DIR" "$SNAPSHOT" fi if [ ! $? -eq 0 ]; then -- cgit v1.2.3 From c1bd65a0a37cbed71e3dddfb8d3244c54e3a802c Mon Sep 17 00:00:00 2001 From: TheMightyV Date: Tue, 4 Jan 2022 20:00:50 +0100 Subject: local-ized and lower-cased local variables, descending sorting of snapshots, minor readability changes --- backends/borg.sh | 44 ++++++++++---------- backends/bup.sh | 54 ++++++++++++------------ backends/tar.sh | 70 ++++++++++++++++--------------- server.sh | 122 ++++++++++++++++++++++++++++++++++++------------------- 4 files changed, 169 insertions(+), 121 deletions(-) (limited to 'backends/borg.sh') diff --git a/backends/borg.sh b/backends/borg.sh index f5930c8..0b28619 100644 --- a/backends/borg.sh +++ b/backends/borg.sh @@ -1,33 +1,33 @@ function borg_init() { export BORG_PASSCOMMAND="$BACKUP_PASSCOMMAND" - for BACKUP_DIR in ${BACKUP_DIRS[*]} + for backup_dir in ${BACKUP_DIRS[*]} do # borg will check if repo exists - borg init --encryption=repokey-blake2 "$BACKUP_DIR" + borg init --encryption=repokey-blake2 "$backup_dir" done } function borg_create_backup() { export BORG_PASSCOMMAND="$BACKUP_PASSCOMMAND" - RETCODE=255 - for BACKUP_DIR in ${BACKUP_DIRS[*]} + local retcode=255 + for backup_dir in ${BACKUP_DIRS[*]} do - export BORG_REPO="$BACKUP_DIR" + export BORG_REPO="$backup_dir" trap 'echo $( date ) Backup interrupted >&2; exit 2' INT TERM - echo "borg: starting backup to \"$BACKUP_DIR\"" + echo "borg: starting backup to \"$backup_dir\"" borg create \ - "${BACKUP_DIR}::${BACKUP_NAME}_$(date +'%F_%H-%M-%S')" \ + "${backup_dir}::${BACKUP_NAME}_$(date +'%F_%H-%M-%S')" \ "$WORLD_NAME" \ --filter AME \ --compression lz4 \ --exclude-caches \ - backup_exit=$? + local backup_exit=$? - echo "borg: pruning repository at \"$BACKUP_DIR\"" + echo "borg: pruning repository at \"$backup_dir\"" borg prune \ --prefix '{hostname}-' \ @@ -36,13 +36,13 @@ function borg_create_backup() { --keep-daily 7 \ --keep-weekly 4 \ --keep-monthly 6 \ - "$BACKUP_DIR" + "$backup_dir" - prune_exit=$? + local prune_exit=$? # use highest exit code as global exit code - global_exit=$(( backup_exit > prune_exit ? backup_exit : prune_exit )) - RETCODE=$(( global_exit > RETCODE ? global_exit : RETCODE )) + local global_exit=$(( backup_exit > prune_exit ? backup_exit : prune_exit )) + retcode=$(( global_exit > retcode ? global_exit : retcode )) if [ ${global_exit} -eq 0 ]; then echo "borg: backup and prune finished successfully" @@ -53,29 +53,29 @@ function borg_create_backup() { fi #exit ${global_exit} done - return $RETCODE + return $retcode } # server_restore relies on output format of this function function borg_ls_dir() { export BORG_PASSCOMMAND="$BACKUP_PASSCOMMAND" - borg list "$1" | cut -d' ' -f1 + borg list "$1" | cut -d' ' -f1 | sort -r } function borg_ls_all() { export BORG_PASSCOMMAND="$BACKUP_PASSCOMMAND" - for BACKUP_DIR in ${BACKUP_DIRS[*]} + for backup_dir in ${BACKUP_DIRS[*]} do - echo "borg: backups in \"$BACKUP_DIR\":" - borg list "$BACKUP_DIR" | cut -d' ' -f1 + echo "borg: backups in \"$backup_dir\":" + borg list "$backup_dir" | cut -d' ' -f1 done } function borg_restore() { export BORG_PASSCOMMAND="$BACKUP_PASSCOMMAND" - REMOTE="$1" - SNAPSHOT="$2" + local remote="$1" + local snapshot="$2" - export BORG_REPO="$REMOTE" - borg extract "${REMOTE}::${SNAPSHOT}" + export BORG_REPO="$remote" + borg extract "${remote}::${snapshot}" } diff --git a/backends/bup.sh b/backends/bup.sh index a5ddb14..b57bd86 100644 --- a/backends/bup.sh +++ b/backends/bup.sh @@ -1,19 +1,19 @@ # use first not-remote backup directory as bup's local repository # defaults to ".bup" function bup_local() { - for BACKUP_DIR in ${BACKUP_DIRS[*]} + for backup_dir in ${BACKUP_DIRS[*]} do - if [[ $BACKUP_DIR != *:* ]]; then - echo "$BACKUP_DIR" + if [[ $backup_dir != *:* ]]; then + echo "$backup_dir" return fi done - echo ".bup" + echo ".bup" } function bup_init() { bup -d "$(bup_local)" index "$WORLD_NAME" - status=$? + local status=$? if [ $status -ne 0 ]; then echo "bup: no local repo found, creating..." bup -d "$(bup_local)" init -r "$(bup_local)" @@ -27,49 +27,51 @@ function bup_create_backup() { bup -d "$(bup_local)" index "$WORLD_NAME" # 0 if saved to at least one non-local repository - RETCODE=1 - for BACKUP_DIR in ${BACKUP_DIRS[*]} + # TODO make more strict? + local retcode=1 + for backup_dir in ${BACKUP_DIRS[*]} do - echo "bup: backing up to \"$BACKUP_DIR\"" + echo "bup: backing up to \"$backup_dir\"" # try to save to remote - bup -d "$(bup_local)" save -r "$BACKUP_DIR" -n "$BACKUP_NAME" "$WORLD_NAME" + bup -d "$(bup_local)" save -r "$backup_dir" -n "$BACKUP_NAME" "$WORLD_NAME" + local status=$? # if failed - reinit remote and try again - if [ ! $? -eq 0 ]; then - echo "bup: failed backing up to \"$BACKUP_DIR\", reinitializing remote..." - bup -d "$(bup_local)" init -r "$BACKUP_DIR" - if [ ! $? -eq 0 ]; then - echo "bup: created remote at \"$BACKUP_DIR\"" - bup -d "$(bup_local)" save -r "$BACKUP_DIR" -n "$BACKUP_NAME" "$WORLD_NAME" + if [ $status -ne 0 ]; then + echo "bup: failed backing up to \"$backup_dir\", reinitializing remote..." + bup -d "$(bup_local)" init -r "$backup_dir" + if [ $? -ne 0 ]; then + echo "bup: created remote at \"$backup_dir\"" + bup -d "$(bup_local)" save -r "$backup_dir" -n "$BACKUP_NAME" "$WORLD_NAME" else - echo "bup: failed to make remote at \"$BACKUP_DIR\", moving on" + echo "bup: failed to make remote at \"$backup_dir\", moving on" fi else - if [ "$BACKUP_DIR" = "$(bup_local)" ]; then - RETCODE=0 + if [ ! "$backup_dir" = "$(bup_local)" ]; then + retcode=0 fi fi done echo "bup: backup finished" - return $RETCODE + return $retcode } # server_restore relies on output format of this function function bup_ls_dir() { - bup -d "$(bup_local)" ls -r "$BACKUP_DIR" "$BACKUP_NAME" + local backup_dir="$1" + bup -d "$(bup_local)" ls -r "$backup_dir" "$BACKUP_NAME" | sort -r } function bup_ls_all() { - for BACKUP_DIR in ${BACKUP_DIRS[*]} + for backup_dir in ${BACKUP_DIRS[*]} do echo "bup: backups in \"$BACKUP_DIR\":" - bup -d "$(bup_local)" ls -r "$BACKUP_DIR" --human-readable -l "$BACKUP_NAME" + bup -d "$(bup_local)" ls -r "$backup_dir" --human-readable -l "$BACKUP_NAME" done } function bup_restore() { - REMOTE="$1" - SNAPSHOT="$2" - - bup -d "$(bup_local)" restore -r "$REMOTE" "$BACKUP_NAME/$SNAPSHOT/$PWD/." + local remote="$1" + local snapshot="$2" + bup -d "$(bup_local)" restore -r "$remote" "$BACKUP_NAME/$snapshot/$PWD/." } diff --git a/backends/tar.sh b/backends/tar.sh index 1265992..f7ea86e 100644 --- a/backends/tar.sh +++ b/backends/tar.sh @@ -7,68 +7,74 @@ function tar_init() { function tar_create_backup() { echo "tar: backing up..." - # save world to a temporary archive - ARCHNAME="/tmp/${BACKUP_NAME}_`date +%FT%H%M%S%z`.tar.gz" - tar -czf "$ARCHNAME" "./$WORLD_NAME" + local status - if [ ! $? -eq 0 ] - then + # save world to a temporary archive + local archname="/tmp/${BACKUP_NAME}_`date +%FT%H%M%S%z`.tar.gz" + tar -czf "$archname" "./$WORLD_NAME" + status=$? + if [ $status -ne 0 ]; then echo "tar: failed to save the world" - rm "$ARCHNAME" #remove (probably faulty) archive + rm "$archname" #remove (probably faulty) archive return 1 - else - echo "tar: world saved to $ARCHNAME, pushing it to backup directories..." fi + echo "tar: world saved to $archname, pushing it to backup directories..." - RETCODE=2 - for BACKUP_DIR in ${BACKUP_DIRS[*]} + # 0 if could save to at least one backup dir + # TODO: make more strict? + local retcode=1 + for backup_dir in ${BACKUP_DIRS[*]} do - echo "tar: pushing to \"$BACKUP_DIR\"" + echo "tar: pushing to \"$backup_dir\"" # scp acts as cp for local destination directories - scp "$ARCHNAME" "$BACKUP_DIR/" - if [ ! $? -eq 0 ]; then - echo "tar: failed pushing to \"$BACKUP_DIR\", moving on" + scp "$archname" "$backup_dir/" + status=$? + if [ $status -ne 0 ]; then + echo "tar: failed pushing to \"$backup_dir\", moving on" else - RETCODE=0 + retcode=0 fi done - rm "$ARCHNAME" + rm "$archname" echo "tar: backup finished" - return $RETCODE + return $retcode } # server_restore relies on output format of this function function tar_ls_dir() { - BACKUP_DIR="$1" - if [[ $BACKUP_DIR == *:* ]]; then - REMOTE="$(echo "$BACKUP_DIR" | cut -d: -f1)" - REMOTE_DIR="$(echo "$BACKUP_DIR" | cut -d: -f2)" - ssh "$REMOTE" "ls -1 $REMOTE_DIR" | grep "tar.gz" + local backup_dir="$1" + + if [[ "$backup_dir" == *:* ]]; then + local remote="$(echo "$backup_dir" | cut -d: -f1)" + local remote_dir="$(echo "$backup_dir" | cut -d: -f2)" + ssh "$remote" "ls -1 $remote_dir" | grep "tar.gz" | sort -r else - ls -1 "$BACKUP_DIR" | grep "tar.gz" + ls -1 "$backup_dir" | grep "tar.gz" | sort -r fi } function tar_ls_all() { - for BACKUP_DIR in ${BACKUP_DIRS[*]} + for backup_dir in ${BACKUP_DIRS[*]} do - echo "Backups in $BACKUP_DIR:" - tar_ls_remote "$BACKUP_DIR" + echo "tar: backups in ${backup_dir}:" + tar_ls_remote "$backup_dir" done } function tar_restore() { - REMOTE="$1" - SNAPSHOT="$2" + local remote="$1" + local snapshot="$2" + local status - scp "$REMOTE/$SNAPSHOT" "/tmp/" - if [ ! $? -eq 0 ]; then - echo "Failed to get archive from \"$REMOTE/$SNAPSHOT\"" + scp "$remote/$snapshot" "/tmp/" + status=$? + if [ $status -ne 0 ]; then + echo "tar: failed to get archive from \"$remote/$snapshot\"" return 1 fi - tar -xzf "/tmp/$SNAPSHOT" + tar -xzf "/tmp/$snapshot" } diff --git a/server.sh b/server.sh index 46156af..ad1a7d8 100755 --- a/server.sh +++ b/server.sh @@ -111,14 +111,14 @@ function players_online() { } function init_backup() { - for BACKUP_DIR in ${BACKUP_DIRS[*]} + for backup_dir in ${BACKUP_DIRS[*]} do - if [[ $BACKUP_DIR == *:* ]]; then - REMOTE="$(echo "$BACKUP_DIR" | cut -d: -f1)" - REMOTE_DIR="$(echo "$BACKUP_DIR" | cut -d: -f2)" - ssh "$REMOTE" "mkdir -p \"$REMOTE_DIR\"" + if [[ $backup_dir == *:* ]]; then + local remote="$(echo "$backup_dir" | cut -d: -f1)" + local remote_dir="$(echo "$backup_dir" | cut -d: -f2)" + ssh "$remote" "mkdir -p \"$remote_dir\"" else - mkdir -p "$BACKUP_DIR" + mkdir -p "$backup_dir" fi done @@ -144,7 +144,7 @@ function create_backup() { } function server_backup_safe() { - force=$1 + local force=$1 echo "Detected running server. Checking if players online..." if [ "$force" != "true" ] && ! players_online; then echo "Players are not online. Not backing up." @@ -171,8 +171,7 @@ function server_backup_safe() { echo "Re-enabling auto-save" send_cmd "save-on" - if [ $RET -eq 0 ] - then + if [ $RET -eq 0 ]; then echo Running backup hook $BACKUP_HOOK fi @@ -182,9 +181,9 @@ function server_backup_unsafe() { echo "No running server detected. Running Backup" create_backup + local status=$? - if [ $? -eq 0 ] - then + if [ $status -eq 0 ]; then echo Running backup hook $BACKUP_HOOK fi @@ -199,7 +198,7 @@ function fbackup_running() { } function server_backup() { - force=$1 + local force=$1 if [ "$force" = "true" ]; then if backup_running; then @@ -218,8 +217,6 @@ function server_backup() { else server_backup_unsafe fi - - exit } function ls_backups() { @@ -232,6 +229,7 @@ function ls_backups() { fi } +# creates a selection dialog function choose_from() { local items=("$@") select item in "${items[@]}"; do @@ -241,60 +239,102 @@ function choose_from() { echo "" } +# checks if an item is in the array +function is_in() { + local item="$1" + shift + local array=("$@") + + # these :space: things allow checking that *exactly* this item is in array + if [[ ${array[*]} =~ (^|[[:space:]])"$item"($|[[:space:]]) ]]; then + return + fi + false +} + function server_restore() { - echo "Select from where get the snapshot" - BACKUP_DIR=$(choose_from "${BACKUP_DIRS[@]}") - if [[ ! ${BACKUP_DIRS[*]} =~ (^|[[:space:]])"$BACKUP_DIR"($|[[:space:]]) ]]; then + local backup_dir + local snapshot_index + local dest + + if [ $# -ge 2 ]; then + backup_dir="$1" + snapshot_index=$2 + fi + + if [ $# -eq 3 ]; then + dest="$3" + fi + + if [ ${#BACKUP_DIRS[@]} -eq 0 ]; then + echo "No backup directories found, abort" + return 1 + fi + if [ -z $backup_dir ]; then + echo "From where get the snapshot?" + backup_dir="$(choose_from "${BACKUP_DIRS[@]}")" + else + backup_dir=${BACKUP_DIRS[backup_dir_index]} + fi + if ! is_in "$backup_dir" "${BACKUP_DIRS[@]}" ; then echo "No valid backup directory selected, abort" return 1 fi - SNAPSHOTS=$( + local snapshots=$( if [ $BACKUP_BACKEND = "bup" ]; then - bup_ls_dir "$BACKUP_DIR" + bup_ls_dir "$backup_dir" elif [ $BACKUP_BACKEND = "borg" ]; then - borg_ls_dir "$BACKUP_DIR" + borg_ls_dir "$backup_dir" else - tar_ls_dir "$BACKUP_DIR" + tar_ls_dir "$backup_dir" fi ) - if [ -z "$SNAPSHOTS" ]; then + if [ -z "$snapshots" ]; then echo "No snapshots found, abort" return 1 fi # convert multiline string to bash array - SNAPSHOTS=($(echo "$SNAPSHOTS")) - echo "Select a snapshot to restore" - SNAPSHOT=$(choose_from "${SNAPSHOTS[@]}") - if [[ ! ${SNAPSHOTS[*]} =~ (^|[[:space:]])"$SNAPSHOT"($|[[:space:]]) ]]; then + snapshots=($(echo "$snapshots")) + + local snapshot + if [ -z $snapshot_index ]; then + echo "Select which snapshot to restore" + snapshot=$(choose_from "${snapshots[@]}") + else + snapshot="${snapshots[snapshot_index]}" + fi + if ! is_in "$snapshot" "${snapshots[@]}" ; then echo "No valid snapshot selected, abort" return 1 fi - echo "Restoring snapshot \"$SNAPSHOT\" from \"$BACKUP_DIR\"" + echo "Restoring snapshot \"$snapshot\" from \"$backup_dir\"" - OLDWORLD_NAME="" + local oldworld_name="" if [[ -d "$WORLD_NAME" ]]; then - echo "Saving old world, just in case" - OLDWORLD_NAME="${WORLD_NAME}.old.$(date +'%F_%H-%M-%S')" - mv -v "$WORLD_NAME" "$OLDWORLD_NAME" + echo -n "Preserving old world: " + oldworld_name="${WORLD_NAME}.old.$(date +'%F_%H-%M-%S')" + mv -v "$PWD/$WORLD_NAME" "$PWD/$oldworld_name" fi if [ $BACKUP_BACKEND = "bup" ]; then - bup_restore "$BACKUP_DIR" "$SNAPSHOT" + bup_restore "$backup_dir" "$snapshot" elif [ $BACKUP_BACKEND = "borg" ]; then - borg_restore "$BACKUP_DIR" "$SNAPSHOT" + borg_restore "$backup_dir" "$snapshot" else - tar_restore "$BACKUP_DIR" "$SNAPSHOT" + tar_restore "$backup_dir" "$snapshot" fi - - if [ ! $? -eq 0 ]; then - echo "Restore failed, reverting old world back" - rm -r "$WORLD_NAME" - mv -v "$OLDWORLD_NAME" "$WORLD_NAME" - else - echo "Restore finished" + local status=$? + if [ $status -ne 0 ]; then + echo "Failed to restore snapshot, putting old world back where it was:" + rm -rv "$PWD/$WORLD_NAME" + mv -v "$PWD/$oldworld_name" "$PWD/$WORLD_NAME" + return 1 fi + echo "Snapshot restored" + + return 0 } #cd $(dirname $0) -- cgit v1.2.3 From c94132808d2a3499f19f86424bfdedf9e0c39312 Mon Sep 17 00:00:00 2001 From: TheMightyV Date: Tue, 4 Jan 2022 22:17:50 +0100 Subject: testing backups hid some messages behind VERBOSE option unified date formats of tars --- backends/borg.sh | 7 +++--- backends/bup.sh | 18 ++++++++------ backends/tar.sh | 17 +++++++------ server.sh | 75 +++++++++++++++++++++++++++++++++++++++++++++++++------- serverconf.sh | 15 +++++++----- 5 files changed, 98 insertions(+), 34 deletions(-) (limited to 'backends/borg.sh') diff --git a/backends/borg.sh b/backends/borg.sh index 0b28619..5d7b2c7 100644 --- a/backends/borg.sh +++ b/backends/borg.sh @@ -16,7 +16,7 @@ function borg_create_backup() { trap 'echo $( date ) Backup interrupted >&2; exit 2' INT TERM - echo "borg: starting backup to \"$backup_dir\"" + echo_debug "borg: starting backup to \"$backup_dir\"" borg create \ "${backup_dir}::${BACKUP_NAME}_$(date +'%F_%H-%M-%S')" \ @@ -27,7 +27,7 @@ function borg_create_backup() { local backup_exit=$? - echo "borg: pruning repository at \"$backup_dir\"" + echo_debug "borg: pruning repository at \"$backup_dir\"" borg prune \ --prefix '{hostname}-' \ @@ -45,7 +45,7 @@ function borg_create_backup() { retcode=$(( global_exit > retcode ? global_exit : retcode )) if [ ${global_exit} -eq 0 ]; then - echo "borg: backup and prune finished successfully" + echo_debug "borg: backup and prune finished successfully" elif [ ${global_exit} -eq 1 ]; then echo "borg: backup and/or prune finished with warnings" else @@ -75,6 +75,7 @@ function borg_restore() { export BORG_PASSCOMMAND="$BACKUP_PASSCOMMAND" local remote="$1" local snapshot="$2" + local dest="$3" export BORG_REPO="$remote" borg extract "${remote}::${snapshot}" diff --git a/backends/bup.sh b/backends/bup.sh index b57bd86..50c2b86 100644 --- a/backends/bup.sh +++ b/backends/bup.sh @@ -15,14 +15,14 @@ function bup_init() { bup -d "$(bup_local)" index "$WORLD_NAME" local status=$? if [ $status -ne 0 ]; then - echo "bup: no local repo found, creating..." + echo_debug "bup: no local repo found, creating..." bup -d "$(bup_local)" init -r "$(bup_local)" - echo "bup: created local repo at $(bup_local)" + echo_debug "bup: created local repo at $(bup_local)" fi } function bup_create_backup() { - echo "bup: backup started" + echo_debug "bup: backup started" bup -d "$(bup_local)" index "$WORLD_NAME" @@ -31,16 +31,17 @@ function bup_create_backup() { local retcode=1 for backup_dir in ${BACKUP_DIRS[*]} do - echo "bup: backing up to \"$backup_dir\"" + echo_debug "bup: backing up to \"$backup_dir\"" # try to save to remote bup -d "$(bup_local)" save -r "$backup_dir" -n "$BACKUP_NAME" "$WORLD_NAME" local status=$? # if failed - reinit remote and try again if [ $status -ne 0 ]; then - echo "bup: failed backing up to \"$backup_dir\", reinitializing remote..." + echo_debug "bup: failed backing up to \"$backup_dir\", reinitializing remote..." bup -d "$(bup_local)" init -r "$backup_dir" - if [ $? -ne 0 ]; then - echo "bup: created remote at \"$backup_dir\"" + status=$? + if [ $status -ne 0 ]; then + echo_debug "bup: created remote at \"$backup_dir\"" bup -d "$(bup_local)" save -r "$backup_dir" -n "$BACKUP_NAME" "$WORLD_NAME" else echo "bup: failed to make remote at \"$backup_dir\", moving on" @@ -73,5 +74,6 @@ function bup_ls_all() { function bup_restore() { local remote="$1" local snapshot="$2" - bup -d "$(bup_local)" restore -r "$remote" "$BACKUP_NAME/$snapshot/$PWD/." + local dest="$3" + bup -d "$(bup_local)" restore -r "$remote" --outdir "$dest" "$BACKUP_NAME/$snapshot/$PWD/." } diff --git a/backends/tar.sh b/backends/tar.sh index f7ea86e..00018c6 100644 --- a/backends/tar.sh +++ b/backends/tar.sh @@ -5,12 +5,12 @@ function tar_init() { # TODO: Make default .tar with optional bup function tar_create_backup() { - echo "tar: backing up..." + echo_debug "tar: backing up..." local status # save world to a temporary archive - local archname="/tmp/${BACKUP_NAME}_`date +%FT%H%M%S%z`.tar.gz" + local archname="/tmp/${BACKUP_NAME}_`date +%F_%H-%M-%S`.tar.gz" tar -czf "$archname" "./$WORLD_NAME" status=$? if [ $status -ne 0 ]; then @@ -18,14 +18,14 @@ function tar_create_backup() { rm "$archname" #remove (probably faulty) archive return 1 fi - echo "tar: world saved to $archname, pushing it to backup directories..." + echo_debug "tar: world saved to $archname, pushing it to backup directories..." # 0 if could save to at least one backup dir # TODO: make more strict? local retcode=1 for backup_dir in ${BACKUP_DIRS[*]} do - echo "tar: pushing to \"$backup_dir\"" + echo_debug "tar: pushing to \"$backup_dir\"" # scp acts as cp for local destination directories scp "$archname" "$backup_dir/" status=$? @@ -51,22 +51,23 @@ function tar_ls_dir() { local remote="$(echo "$backup_dir" | cut -d: -f1)" local remote_dir="$(echo "$backup_dir" | cut -d: -f2)" ssh "$remote" "ls -1 $remote_dir" | grep "tar.gz" | sort -r - else + else ls -1 "$backup_dir" | grep "tar.gz" | sort -r - fi + fi } function tar_ls_all() { for backup_dir in ${BACKUP_DIRS[*]} do echo "tar: backups in ${backup_dir}:" - tar_ls_remote "$backup_dir" + tar_ls_dir "$backup_dir" done } function tar_restore() { local remote="$1" local snapshot="$2" + local dest="$3" local status scp "$remote/$snapshot" "/tmp/" @@ -76,5 +77,5 @@ function tar_restore() { return 1 fi - tar -xzf "/tmp/$snapshot" + tar -xzf "/tmp/$snapshot" -C "$dest" } diff --git a/server.sh b/server.sh index ad1a7d8..1f4f8e1 100755 --- a/server.sh +++ b/server.sh @@ -12,6 +12,12 @@ source "backends/tar.sh" source "backends/bup.sh" source "backends/borg.sh" +function echo_debug() { + if [ $VERBOSE -ne 0 ]; then + echo "$1" + fi +} + function backup_hook_example { bup -d $CUR_BACK_DIR ls -l $BACKUP_NAME/latest/var/minecraft } @@ -111,6 +117,7 @@ function players_online() { } function init_backup() { + # even though bup and borg are smart, they will not create a path for a repo for backup_dir in ${BACKUP_DIRS[*]} do if [[ $backup_dir == *:* ]]; then @@ -131,6 +138,42 @@ function init_backup() { fi } +function same_world() { + delta=$(diff -r "$1" "$2") + if [ -z "$delta" ] ; then + return 0 + fi + return 1 +} + +# checking if latest snapshots are the same as the current world +function test_backup_integrity() { + local retcode=0 + for backup_dir in ${BACKUP_DIRS[*]} + do + local serverdir="$PWD" + local tmpdir=$(mktemp -d); + + # restore most recent backup to a temporary dir + if ! server_restore "$serverdir/$backup_dir" 0 "$tmpdir" ; then + echo "Failed to get latest snapshot from \"$backup_dir\"" + retcode=1 + elif ! same_world "$WORLD_NAME" "$tmpdir/$WORLD_NAME" ; then + echo "Latest backup from \"$backup_dir\" differs from current world!" + retcode=1 + fi + + rm -r "$tmpdir" + done + + if [ $retcode -ne 0 ] ; then + echo "Backup integrity check: FAILED" + return 1 + fi + echo "Backup integrity check: OK" + return 0 +} + function create_backup() { init_backup @@ -141,6 +184,8 @@ function create_backup() { else tar_create_backup fi + + test_backup_integrity } function server_backup_safe() { @@ -255,13 +300,13 @@ function is_in() { function server_restore() { local backup_dir local snapshot_index - local dest + local dest="$PWD" + # parameters are only used for testing backups, so thorough checks are not needed if [ $# -ge 2 ]; then backup_dir="$1" snapshot_index=$2 fi - if [ $# -eq 3 ]; then dest="$3" fi @@ -270,6 +315,8 @@ function server_restore() { echo "No backup directories found, abort" return 1 fi + + if [ -z $backup_dir ]; then echo "From where get the snapshot?" backup_dir="$(choose_from "${BACKUP_DIRS[@]}")" @@ -281,6 +328,7 @@ function server_restore() { return 1 fi + local snapshots=$( if [ $BACKUP_BACKEND = "bup" ]; then bup_ls_dir "$backup_dir" @@ -297,6 +345,7 @@ function server_restore() { # convert multiline string to bash array snapshots=($(echo "$snapshots")) + local snapshot if [ -z $snapshot_index ]; then echo "Select which snapshot to restore" @@ -309,29 +358,37 @@ function server_restore() { return 1 fi - echo "Restoring snapshot \"$snapshot\" from \"$backup_dir\"" - local oldworld_name="" - if [[ -d "$WORLD_NAME" ]]; then + echo_debug "Restoring snapshot \"$snapshot\" from \"$backup_dir\"" + + + # if we restore to PWD, we will overwrite the current world, which might be harmful + local oldworld_name + if [ "$dest" = "$PWD" ] && [[ -d "$WORLD_NAME" ]]; then echo -n "Preserving old world: " oldworld_name="${WORLD_NAME}.old.$(date +'%F_%H-%M-%S')" mv -v "$PWD/$WORLD_NAME" "$PWD/$oldworld_name" fi + if [ $BACKUP_BACKEND = "bup" ]; then - bup_restore "$backup_dir" "$snapshot" + bup_restore "$backup_dir" "$snapshot" "$dest" elif [ $BACKUP_BACKEND = "borg" ]; then - borg_restore "$backup_dir" "$snapshot" + borg_restore "$backup_dir" "$snapshot" "$dest" else - tar_restore "$backup_dir" "$snapshot" + tar_restore "$backup_dir" "$snapshot" "$dest" fi local status=$? - if [ $status -ne 0 ]; then + + + # if we preseved the current world, but failed to restore the snapshot + if [ ! -z ${oldworld_name+x} ] && [ $status -ne 0 ]; then echo "Failed to restore snapshot, putting old world back where it was:" rm -rv "$PWD/$WORLD_NAME" mv -v "$PWD/$oldworld_name" "$PWD/$WORLD_NAME" return 1 fi + echo "Snapshot restored" return 0 diff --git a/serverconf.sh b/serverconf.sh index 3748541..a8f02a6 100644 --- a/serverconf.sh +++ b/serverconf.sh @@ -2,6 +2,8 @@ # configuration file for server.sh minecraft server # management script +VERBOSE=0 + #CONFIG JRE_JAVA="java" JVM_ARGS="-Xms4096M -Xmx6144M" @@ -21,16 +23,17 @@ BACKUP_NAME="${WORLD_NAME}_backup" LOGFILE="logs/latest.log" PIDFILE="server-screen.pid" # if not bup or borg, uses tar by default -#BACKUP_BACKEND="tar" +BACKUP_BACKEND="tar" #BACKUP_BACKEND="bup" -BACKUP_BACKEND="borg" +#BACKUP_BACKEND="borg" #Constants CUR_YEAR=`date +"%Y"` -BACKUP_DIRS=(".bak/$CUR_YEAR" "user@backupserver:/path/to/backup/$CUR_YEAR") +# IMPORTANT: local paths must be absolute! +BACKUP_DIRS=( "$PWD/.bak/$CUR_YEAR" "user@backupserver:/path/to/backup/$CUR_YEAR" ) -# borg repository could be password protected +# borg repositories are password protected by default # to avoid having to manually type password, borg can run a command that should echo a password -#BACKUP_PASSCOMMAND="echo superstrongpassword" -#BACKUP_PASSCOMMAND="pass passwordname" +BACKUP_PASSCOMMAND="echo superstrongpassword" +#BACKUP_PASSCOMMAND="pass passwordname -- cgit v1.2.3 From cba2d63889fa3ae640d213ab2c15f99d3d941967 Mon Sep 17 00:00:00 2001 From: TheMightyV Date: Tue, 4 Jan 2022 22:41:10 +0100 Subject: optional password protection of borg repo fixed borg backup check added nanoseconds to old world backup to prevent collision during tests --- backends/borg.sh | 12 +++++++++++- server.sh | 9 ++++++--- 2 files changed, 17 insertions(+), 4 deletions(-) (limited to 'backends/borg.sh') diff --git a/backends/borg.sh b/backends/borg.sh index 5d7b2c7..b8854a1 100644 --- a/backends/borg.sh +++ b/backends/borg.sh @@ -1,9 +1,17 @@ function borg_init() { + local encryption + if [ -z "$BACKUP_PASSCOMMAND" ] ; then + echo "borg: no password given, repository is not protected" + encryption="none" + else + encryption="repokey-blake2" + fi + export BORG_PASSCOMMAND="$BACKUP_PASSCOMMAND" for backup_dir in ${BACKUP_DIRS[*]} do # borg will check if repo exists - borg init --encryption=repokey-blake2 "$backup_dir" + borg init --encryption="$encryption" "$backup_dir" done } @@ -78,5 +86,7 @@ function borg_restore() { local dest="$3" export BORG_REPO="$remote" + cd "$dest" borg extract "${remote}::${snapshot}" + cd - > /dev/null } diff --git a/server.sh b/server.sh index 1f4f8e1..d68a203 100755 --- a/server.sh +++ b/server.sh @@ -140,6 +140,9 @@ function init_backup() { function same_world() { delta=$(diff -r "$1" "$2") + if [ $? -ne 0 ]; then + return 1 + fi if [ -z "$delta" ] ; then return 0 fi @@ -366,8 +369,8 @@ function server_restore() { local oldworld_name if [ "$dest" = "$PWD" ] && [[ -d "$WORLD_NAME" ]]; then echo -n "Preserving old world: " - oldworld_name="${WORLD_NAME}.old.$(date +'%F_%H-%M-%S')" - mv -v "$PWD/$WORLD_NAME" "$PWD/$oldworld_name" + oldworld_name="${WORLD_NAME}.old.$(date +'%F_%H-%M-%S.%N')" + mv -n -v "$PWD/$WORLD_NAME" "$PWD/$oldworld_name" fi @@ -389,7 +392,7 @@ function server_restore() { return 1 fi - echo "Snapshot restored" + echo_debug "Snapshot restored" return 0 } -- cgit v1.2.3 From 8a980539a3fc71834d97394d6a457a194bce9f80 Mon Sep 17 00:00:00 2001 From: TheMightyV Date: Tue, 4 Jan 2022 22:59:56 +0100 Subject: fixed wrong hanling of provided backup_dir in server_restore made more outputs with VERBOSE=0 --- backends/borg.sh | 2 +- backends/bup.sh | 2 +- backends/tar.sh | 2 +- server.sh | 13 ++++++------- 4 files changed, 9 insertions(+), 10 deletions(-) (limited to 'backends/borg.sh') diff --git a/backends/borg.sh b/backends/borg.sh index b8854a1..b0351a0 100644 --- a/backends/borg.sh +++ b/backends/borg.sh @@ -24,7 +24,7 @@ function borg_create_backup() { trap 'echo $( date ) Backup interrupted >&2; exit 2' INT TERM - echo_debug "borg: starting backup to \"$backup_dir\"" + echo "borg: backing up to \"$backup_dir\"" borg create \ "${backup_dir}::${BACKUP_NAME}_$(date +'%F_%H-%M-%S')" \ diff --git a/backends/bup.sh b/backends/bup.sh index 50c2b86..cae36fe 100644 --- a/backends/bup.sh +++ b/backends/bup.sh @@ -31,7 +31,7 @@ function bup_create_backup() { local retcode=1 for backup_dir in ${BACKUP_DIRS[*]} do - echo_debug "bup: backing up to \"$backup_dir\"" + echo "bup: backing up to \"$backup_dir\"" # try to save to remote bup -d "$(bup_local)" save -r "$backup_dir" -n "$BACKUP_NAME" "$WORLD_NAME" local status=$? diff --git a/backends/tar.sh b/backends/tar.sh index 00018c6..6f7a3d6 100644 --- a/backends/tar.sh +++ b/backends/tar.sh @@ -25,7 +25,7 @@ function tar_create_backup() { local retcode=1 for backup_dir in ${BACKUP_DIRS[*]} do - echo_debug "tar: pushing to \"$backup_dir\"" + echo "tar: backing up to \"$backup_dir\"" # scp acts as cp for local destination directories scp "$archname" "$backup_dir/" status=$? diff --git a/server.sh b/server.sh index d68a203..af7eb1e 100755 --- a/server.sh +++ b/server.sh @@ -154,16 +154,17 @@ function test_backup_integrity() { local retcode=0 for backup_dir in ${BACKUP_DIRS[*]} do - local serverdir="$PWD" local tmpdir=$(mktemp -d); # restore most recent backup to a temporary dir - if ! server_restore "$serverdir/$backup_dir" 0 "$tmpdir" ; then + if ! server_restore "$backup_dir" 0 "$tmpdir" ; then echo "Failed to get latest snapshot from \"$backup_dir\"" retcode=1 elif ! same_world "$WORLD_NAME" "$tmpdir/$WORLD_NAME" ; then echo "Latest backup from \"$backup_dir\" differs from current world!" retcode=1 + else + echo "Backup at \"$backup_dir\" is OK" fi rm -r "$tmpdir" @@ -171,10 +172,10 @@ function test_backup_integrity() { if [ $retcode -ne 0 ] ; then echo "Backup integrity check: FAILED" - return 1 + else + echo "Backup integrity check: OK" fi - echo "Backup integrity check: OK" - return 0 + return $retcode } function create_backup() { @@ -323,8 +324,6 @@ function server_restore() { if [ -z $backup_dir ]; then echo "From where get the snapshot?" backup_dir="$(choose_from "${BACKUP_DIRS[@]}")" - else - backup_dir=${BACKUP_DIRS[backup_dir_index]} fi if ! is_in "$backup_dir" "${BACKUP_DIRS[@]}" ; then echo "No valid backup directory selected, abort" -- cgit v1.2.3 From ae5db1b37ae8554dff72b0da42ccbbbbc64baf71 Mon Sep 17 00:00:00 2001 From: TheMightyV Date: Wed, 5 Jan 2022 22:04:10 +0100 Subject: split echos to debug/info/error levels unified ls_all backend functions unified backend function calls ls_dir -> ls in backends fixed accidental echo -> log_info replacement --- backends/borg.sh | 28 ++++------- backends/bup.sh | 28 ++++------- backends/tar.sh | 24 +++------ server.sh | 147 +++++++++++++++++++++++++------------------------------ tests.sh | 18 ++++--- 5 files changed, 106 insertions(+), 139 deletions(-) (limited to 'backends/borg.sh') diff --git a/backends/borg.sh b/backends/borg.sh index b0351a0..ec39bc8 100644 --- a/backends/borg.sh +++ b/backends/borg.sh @@ -1,7 +1,7 @@ function borg_init() { local encryption if [ -z "$BACKUP_PASSCOMMAND" ] ; then - echo "borg: no password given, repository is not protected" + log_info "borg: no password given, repository is not protected" encryption="none" else encryption="repokey-blake2" @@ -10,9 +10,10 @@ function borg_init() { export BORG_PASSCOMMAND="$BACKUP_PASSCOMMAND" for backup_dir in ${BACKUP_DIRS[*]} do + log_debug "Initializing repo at $backup_dir " # borg will check if repo exists borg init --encryption="$encryption" "$backup_dir" - done + done } function borg_create_backup() { @@ -22,9 +23,9 @@ function borg_create_backup() { do export BORG_REPO="$backup_dir" - trap 'echo $( date ) Backup interrupted >&2; exit 2' INT TERM + trap 'echo [WARNING] $( date ) Backup interrupted >&2; exit 2' INT TERM - echo "borg: backing up to \"$backup_dir\"" + log_info "borg: backing up to \"$backup_dir\"" borg create \ "${backup_dir}::${BACKUP_NAME}_$(date +'%F_%H-%M-%S')" \ @@ -35,7 +36,7 @@ function borg_create_backup() { local backup_exit=$? - echo_debug "borg: pruning repository at \"$backup_dir\"" + log_debug "borg: pruning repository at \"$backup_dir\"" borg prune \ --prefix '{hostname}-' \ @@ -53,11 +54,11 @@ function borg_create_backup() { retcode=$(( global_exit > retcode ? global_exit : retcode )) if [ ${global_exit} -eq 0 ]; then - echo_debug "borg: backup and prune finished successfully" + log_debug "borg: backup and prune finished successfully" elif [ ${global_exit} -eq 1 ]; then - echo "borg: backup and/or prune finished with warnings" + log_info "borg: backup and/or prune finished with warnings" else - echo "borg: backup and/or prune finished with errors" + log_error "borg: backup and/or prune finished with errors" fi #exit ${global_exit} done @@ -65,20 +66,11 @@ function borg_create_backup() { } # server_restore relies on output format of this function -function borg_ls_dir() { +function borg_ls() { export BORG_PASSCOMMAND="$BACKUP_PASSCOMMAND" borg list "$1" | cut -d' ' -f1 | sort -r } -function borg_ls_all() { - export BORG_PASSCOMMAND="$BACKUP_PASSCOMMAND" - for backup_dir in ${BACKUP_DIRS[*]} - do - echo "borg: backups in \"$backup_dir\":" - borg list "$backup_dir" | cut -d' ' -f1 - done -} - function borg_restore() { export BORG_PASSCOMMAND="$BACKUP_PASSCOMMAND" local remote="$1" diff --git a/backends/bup.sh b/backends/bup.sh index d1250de..6aa92b6 100644 --- a/backends/bup.sh +++ b/backends/bup.sh @@ -15,14 +15,14 @@ function bup_init() { bup -d "$(bup_local)" index "$WORLD_NAME" local status=$? if [ $status -ne 0 ]; then - echo_debug "bup: no local repo found, creating..." + log_debug "bup: no local repo found, creating..." bup -d "$(bup_local)" init -r "$(bup_local)" - echo_debug "bup: created local repo at $(bup_local)" + log_debug "bup: created local repo at $(bup_local)" fi } function bup_create_backup() { - echo_debug "bup: backup started" + log_debug "bup: backup started" bup -d "$(bup_local)" index "$WORLD_NAME" @@ -31,20 +31,20 @@ function bup_create_backup() { local retcode=1 for backup_dir in ${BACKUP_DIRS[*]} do - echo "bup: backing up to \"$backup_dir\"" + log_info "bup: backing up to \"$backup_dir\"" # try to save to remote bup -d "$(bup_local)" save -r "$backup_dir" -n "$BACKUP_NAME" "$WORLD_NAME" local status=$? # if failed - reinit remote and try again if [ $status -ne 0 ]; then - echo_debug "bup: failed backing up to \"$backup_dir\", reinitializing remote..." + log_debug "bup: failed backing up to \"$backup_dir\", reinitializing remote..." bup -d "$(bup_local)" init -r "$backup_dir" status=$? - if [ $status -ne 0 ]; then - echo_debug "bup: created remote at \"$backup_dir\"" + if [ $status -eq 0 ]; then + log_debug "bup: created remote at \"$backup_dir\"" bup -d "$(bup_local)" save -r "$backup_dir" -n "$BACKUP_NAME" "$WORLD_NAME" else - echo "bup: failed to make remote at \"$backup_dir\", moving on" + log_error "bup: failed to make remote at \"$backup_dir\", moving on" fi else if [ ! "$backup_dir" = "$(bup_local)" ]; then @@ -53,24 +53,16 @@ function bup_create_backup() { fi done - echo "bup: backup finished" + log_debug "bup: backup finished" return $retcode } # server_restore relies on output format of this function -function bup_ls_dir() { +function bup_ls() { local backup_dir="$1" bup -d "$(bup_local)" ls -r "$backup_dir" "$BACKUP_NAME" | sort -r } -function bup_ls_all() { - for backup_dir in ${BACKUP_DIRS[*]} - do - echo "bup: backups in \"$backup_dir\":" - bup -d "$(bup_local)" ls -r "$backup_dir" --human-readable -l "$BACKUP_NAME" - done -} - function bup_restore() { local remote="$1" local snapshot="$2" diff --git a/backends/tar.sh b/backends/tar.sh index 6f7a3d6..6748ac9 100644 --- a/backends/tar.sh +++ b/backends/tar.sh @@ -5,7 +5,7 @@ function tar_init() { # TODO: Make default .tar with optional bup function tar_create_backup() { - echo_debug "tar: backing up..." + log_debug "tar: backing up..." local status @@ -14,23 +14,23 @@ function tar_create_backup() { tar -czf "$archname" "./$WORLD_NAME" status=$? if [ $status -ne 0 ]; then - echo "tar: failed to save the world" + log_error "tar: failed to save the world" rm "$archname" #remove (probably faulty) archive return 1 fi - echo_debug "tar: world saved to $archname, pushing it to backup directories..." + log_debug "tar: world saved to $archname, pushing it to backup directories..." # 0 if could save to at least one backup dir # TODO: make more strict? local retcode=1 for backup_dir in ${BACKUP_DIRS[*]} do - echo "tar: backing up to \"$backup_dir\"" + log_info "tar: backing up to \"$backup_dir\"" # scp acts as cp for local destination directories scp "$archname" "$backup_dir/" status=$? if [ $status -ne 0 ]; then - echo "tar: failed pushing to \"$backup_dir\", moving on" + log_error "tar: failed pushing to \"$backup_dir\", moving on" else retcode=0 fi @@ -38,13 +38,13 @@ function tar_create_backup() { rm "$archname" - echo "tar: backup finished" + log_debug "tar: backup finished" return $retcode } # server_restore relies on output format of this function -function tar_ls_dir() { +function tar_ls() { local backup_dir="$1" if [[ "$backup_dir" == *:* ]]; then @@ -56,14 +56,6 @@ function tar_ls_dir() { fi } -function tar_ls_all() { - for backup_dir in ${BACKUP_DIRS[*]} - do - echo "tar: backups in ${backup_dir}:" - tar_ls_dir "$backup_dir" - done -} - function tar_restore() { local remote="$1" local snapshot="$2" @@ -73,7 +65,7 @@ function tar_restore() { scp "$remote/$snapshot" "/tmp/" status=$? if [ $status -ne 0 ]; then - echo "tar: failed to get archive from \"$remote/$snapshot\"" + log_error "tar: failed to get archive from \"$remote/$snapshot\"" return 1 fi diff --git a/server.sh b/server.sh index 2f4a0ae..2b58798 100755 --- a/server.sh +++ b/server.sh @@ -4,7 +4,7 @@ if [ -e "serverconf.sh" ] then source "serverconf.sh" else - echo No configuration found in PWD. Exiting. + log_error "No configuration found in PWD. Exiting." exit 1 fi @@ -12,14 +12,16 @@ source "backends/tar.sh" source "backends/bup.sh" source "backends/borg.sh" -function echo_debug() { +function log_debug() { if [ $VERBOSE -ne 0 ]; then - echo "$1" + printf "[DEBUG] $1\n" >&2 fi } +log_info() { printf "[INFO] $1\n" ; } +log_error() { printf "[ERROR] $1\n" >&2 ; } function backup_hook_example { - bup -d $CUR_BACK_DIR ls -l $BACKUP_NAME/latest/var/minecraft + bup -d $CUR_BACK_DIR ls -l "$BACKUP_NAME/latest/var/minecraft" } function send_cmd () { @@ -28,7 +30,7 @@ function send_cmd () { function assert_not_running() { if server_running; then - echo "It seems a server is already running. If this is not the case,\ + log_info "It seems a server is already running. If this is not the case,\ manually attach to the running screen and close it." exit 1 fi @@ -36,7 +38,7 @@ function assert_not_running() { function assert_running() { if ! server_running; then - echo "Server not running" + log_info "Server not running" exit 1 fi } @@ -46,7 +48,7 @@ function server_start() { if [ ! -f "eula.txt" ] then - echo "eula.txt not found. Creating and accepting EULA." + log_info "eula.txt not found. Creating and accepting EULA." echo "eula=true" > "eula.txt" fi @@ -54,7 +56,7 @@ function server_start() { $JRE_JAVA $JVM_ARGS -jar $JAR $JAR_ARGS pid=`tmux -S $TMUX_SOCKET list-panes -t $TMUX_WINDOW -F "#{pane_pid}"` echo $pid > $PIDFILE - echo Started with PID $pid + log_info "Started with PID $pid" exit } @@ -73,7 +75,7 @@ function server_stop() { RET=$? done - echo "stopped the server" + log_info "stopped the server" rm -f $PIDFILE @@ -98,9 +100,9 @@ function server_running() { function server_status() { if server_running then - echo "Server is running" + log_info "Server is running" else - echo "Server is not running" + log_info "Server is not running" fi exit } @@ -116,6 +118,21 @@ function players_online() { [ `tail -n 3 "$LOGFILE" | grep -c "There are 0"` -lt 1 ] } +function backup_backend_run() { + local cmd + # could do ${BACKUP_BACKEND}_$1 though + if [ $BACKUP_BACKEND = "bup" ]; then + cmd="bup" + elif [ $BACKUP_BACKEND = "borg" ]; then + cmd="borg" + else + cmd="tar" + fi + cmd="${cmd}_$1" + log_debug "Backup backend command: \"$cmd\"" + eval "$cmd" +} + function init_backup() { # even though bup and borg are smart, they will not create a path for a repo for backup_dir in ${BACKUP_DIRS[*]} @@ -129,13 +146,7 @@ function init_backup() { fi done - if [ $BACKUP_BACKEND = "bup" ]; then - bup_init - elif [ $BACKUP_BACKEND = "borg" ]; then - borg_init - else - tar_init - fi + backup_backend_run "init" } function same_world() { @@ -158,52 +169,44 @@ function test_backup_integrity() { # restore most recent backup to a temporary dir if ! server_restore "$backup_dir" 0 "$tmpdir" ; then - echo "Failed to get latest snapshot from \"$backup_dir\"" + log_error "Failed to get latest snapshot from \"$backup_dir\"" retcode=1 elif ! same_world "$WORLD_NAME" "$tmpdir/$WORLD_NAME" ; then - echo "Latest backup from \"$backup_dir\" differs from current world!" + log_error "Latest backup from \"$backup_dir\" differs from current world!" retcode=1 else - echo "Backup at \"$backup_dir\" is OK" + log_info "Backup at \"$backup_dir\" is OK" fi rm -r "$tmpdir" done if [ $retcode -ne 0 ] ; then - echo "Backup integrity check: FAILED" + log_error "Backup integrity check: FAILED" else - echo "Backup integrity check: OK" + log_info "Backup integrity check: OK" fi return $retcode } function create_backup() { init_backup - - if [ $BACKUP_BACKEND = "bup" ]; then - bup_create_backup - elif [ $BACKUP_BACKEND = "borg" ]; then - borg_create_backup - else - tar_create_backup - fi - + backup_backend_run "create_backup" test_backup_integrity } function server_backup_safe() { local force=$1 - echo "Detected running server. Checking if players online..." + log_info "Detected running server. Checking if players online..." if [ "$force" != "true" ] && ! players_online; then - echo "Players are not online. Not backing up." + log_info "Players are not online. Not backing up." return fi - echo "Disabling autosave" + log_info "Disabling autosave" send_cmd "save-off" send_cmd "save-all flush" - echo "Waiting for save... If froze, run /save-on to re-enable autosave!!" + log_info "Waiting for save... If froze, run /save-on to re-enable autosave!!" sleep 1 while [ $(tail -n 3 "$LOGFILE" | grep -c "Saved the game") -lt 1 ] @@ -211,29 +214,29 @@ function server_backup_safe() { sleep 1 done sleep 2 - echo "Done! starting backup..." + log_info "Done! starting backup..." create_backup local RET=$? - echo "Re-enabling auto-save" + log_info "Re-enabling auto-save" send_cmd "save-on" if [ $RET -eq 0 ]; then - echo Running backup hook + log_info "Running backup hook" $BACKUP_HOOK fi } function server_backup_unsafe() { - echo "No running server detected. Running Backup" + log_info "No running server detected. Running Backup" create_backup local status=$? if [ $status -eq 0 ]; then - echo Running backup hook + log_info "Running backup hook" $BACKUP_HOOK fi } @@ -251,12 +254,12 @@ function server_backup() { if [ "$force" = "true" ]; then if backup_running; then - echo "A backup is running. Aborting..." + log_info "A backup is running. Aborting..." return fi else if fbackup_running; then - echo "A force backup is running. Aborting..." + log_info "A force backup is running. Aborting..." return fi fi @@ -268,14 +271,12 @@ function server_backup() { fi } -function ls_backups() { - if [ $BACKUP_BACKEND = "bup" ]; then - bup_ls_all - elif [ $BACKUP_BACKEND = "borg" ]; then - borg_ls_all - else - tar_ls_all - fi +function server_ls_backups() { + for backup_dir in ${BACKUP_DIRS[*]} + do + log_info "backups in ${backup_dir}:" + backup_backend_run "ls \"$backup_dir\"" + done } # creates a selection dialog @@ -285,7 +286,6 @@ function choose_from() { echo "$item" return done - echo "" } # checks if an item is in the array @@ -318,32 +318,26 @@ function server_restore() { fi if [ ${#BACKUP_DIRS[@]} -eq 0 ]; then - echo "No backup directories found, abort" + log_error "No backup directories found, abort" return 1 fi if [ -z $backup_dir ]; then - echo "From where get the snapshot?" + log_info "From where get the snapshot?" backup_dir="$(choose_from "${BACKUP_DIRS[@]}")" fi if ! is_in "$backup_dir" "${BACKUP_DIRS[@]}" ; then - echo "No valid backup directory selected, abort" + log_error "No valid backup directory selected, abort" return 1 fi - local snapshots=$( - if [ $BACKUP_BACKEND = "bup" ]; then - bup_ls_dir "$backup_dir" - elif [ $BACKUP_BACKEND = "borg" ]; then - borg_ls_dir "$backup_dir" - else - tar_ls_dir "$backup_dir" - fi - ) + local snapshots="$( backup_backend_run "ls \"$backup_dir\"" )" + log_debug "Snapshots found:" + log_debug "$snapshots" if [ -z "$snapshots" ]; then - echo "No snapshots found, abort" + log_error "No snapshots found, abort" return 1 fi # convert multiline string to bash array @@ -352,47 +346,40 @@ function server_restore() { local snapshot if [ -z $snapshot_index ]; then - echo "Select which snapshot to restore" + log_info "Select which snapshot to restore" snapshot=$(choose_from "${snapshots[@]}") else snapshot="${snapshots[snapshot_index]}" fi if ! is_in "$snapshot" "${snapshots[@]}" ; then - echo "No valid snapshot selected, abort" + log_error "No valid snapshot selected, abort" return 1 fi - echo_debug "Restoring snapshot \"$snapshot\" from \"$backup_dir\"" + log_debug "Restoring snapshot \"$snapshot\" from \"$backup_dir\"" # if we restore to PWD, we will overwrite the current world, which might be harmful local oldworld_name if [ "$dest" = "$PWD" ] && [[ -d "$WORLD_NAME" ]]; then - echo -n "Preserving old world: " oldworld_name="${WORLD_NAME}.old.$(date +'%F_%H-%M-%S.%N')" - mv -n -v "$PWD/$WORLD_NAME" "$PWD/$oldworld_name" + log_info "Preserving old world: $(mv -n -v "$PWD/$WORLD_NAME" "$PWD/$oldworld_name")" fi - if [ $BACKUP_BACKEND = "bup" ]; then - bup_restore "$backup_dir" "$snapshot" "$dest" - elif [ $BACKUP_BACKEND = "borg" ]; then - borg_restore "$backup_dir" "$snapshot" "$dest" - else - tar_restore "$backup_dir" "$snapshot" "$dest" - fi + backup_backend_run "restore \"$backup_dir\" \"$snapshot\" \"$dest\"" local status=$? # if we preseved the current world, but failed to restore the snapshot if [ ! -z ${oldworld_name+x} ] && [ $status -ne 0 ]; then - echo "Failed to restore snapshot, putting old world back where it was:" + log_error "Failed to restore snapshot, putting old world back where it was:" rm -rv "$PWD/$WORLD_NAME" mv -v "$PWD/$oldworld_name" "$PWD/$WORLD_NAME" return 1 fi - echo_debug "Snapshot restored" + log_debug "Snapshot restored" return 0 } @@ -422,7 +409,7 @@ case $1 in server_backup "true" ;; "ls") - ls_backups + server_ls_backups ;; *) echo "Usage: $0 start|stop|attach|status|backup" diff --git a/tests.sh b/tests.sh index fc6a692..38ce83b 100755 --- a/tests.sh +++ b/tests.sh @@ -55,13 +55,17 @@ function test_backend() { exit fi - ls_backups - + local backups="$(server_ls_backups)" + if [ -z "$backups" ]; then + log_error "Found no backups" + cleanup + exit + fi # corrupting current (new) world find "$DIR/$WORLD_NAME" -type f -exec shred {} \; if same_world "$DIR/$WORLD_NAME" "$DIR/$new_world" ; then - echo "Failed to corrupt new world" + log_error "Failed to corrupt new world" cleanup exit fi @@ -71,13 +75,13 @@ function test_backend() { server_restore "${BACKUP_DIRS[0]}" 0 # must be: new backup == new world if ! same_world "$DIR/$WORLD_NAME" "$DIR/$new_world" ; then - echo "${BACKUP_BACKEND}: new backup != new world" + log_error "${BACKUP_BACKEND}: new backup != new world" cleanup exit fi # must be: new backup != old world if same_world "$DIR/$WORLD_NAME" "$DIR/$old_world" ; then - echo "${BACKUP_BACKEND}: new backup == old world" + log_error "${BACKUP_BACKEND}: new backup == old world" cleanup exit fi @@ -92,13 +96,13 @@ function test_backend() { fi # must be: old backup == old world if ! same_world "$DIR/$WORLD_NAME" "$DIR/$old_world" ; then - echo "${BACKUP_BACKEND}: old backup != old world" + log_error "${BACKUP_BACKEND}: old backup != old world" cleanup exit fi # must be: old backup != new world if same_world "$DIR/$WORLD_NAME" "$DIR/$new_world" ; then - echo "${BACKUP_BACKEND}: old backup == new world" + log_error "${BACKUP_BACKEND}: old backup == new world" cleanup exit fi -- cgit v1.2.3