From cd32e794b00c04a5b969fd0292c7764e32e25863 Mon Sep 17 00:00:00 2001 From: TheMightyV Date: Sun, 2 Jan 2022 19:52:03 +0100 Subject: converted spaces to tabs, moved tar and bup backends to backend directory --- backends/bup.sh | 23 +++++++++++++++++++ backends/tar.sh | 14 ++++++++++++ server.sh | 71 ++++++++++++++------------------------------------------- serverconf.sh | 5 ++-- 4 files changed, 56 insertions(+), 57 deletions(-) create mode 100644 backends/bup.sh create mode 100644 backends/tar.sh diff --git a/backends/bup.sh b/backends/bup.sh new file mode 100644 index 0000000..83e4e98 --- /dev/null +++ b/backends/bup.sh @@ -0,0 +1,23 @@ +function create_bup_backup() { + BACKUP_DIR="mc-backups" + CUR_BACK_DIR="mc-backups/$CUR_YEAR" + + if [ ! -d "$CUR_BACK_DIR" ]; then + mkdir -p "$CUR_BACK_DIR" + fi + + bup -d "$CUR_BACK_DIR" index "$WORLD_NAME" + status=$? + if [ $status -eq 1 ]; then + bup -d "$CUR_BACK_DIR" init + bup -d "$CUR_BACK_DIR" index "$WORLD_NAME" + fi + + bup -d "$CUR_BACK_DIR" save -n "$BACKUP_NAME" "$WORLD_NAME" + + echo "Backup using bup to $CUR_BACK_DIR is complete" +} + +function ls_bup() { + bup -d "mc-backups/${CUR_YEAR}" ls "mc-sad-squad/$1" +} diff --git a/backends/tar.sh b/backends/tar.sh new file mode 100644 index 0000000..4d7ff36 --- /dev/null +++ b/backends/tar.sh @@ -0,0 +1,14 @@ +# TODO: Make default .tar with optional bup +function tar_create_backup() { + ARCHNAME="backup/$WORLD_NAME-backup_`date +%d-%m-%y-%T`.tar.gz" + tar -czf "$ARCHNAME" "./$WORLD_NAME" + + if [ ! $? -eq 0 ] + then + echo "TAR failed. No Backup created." + rm $ARCHNAME #remove (probably faulty) archive + return 1 + else + echo $ARCHNAME created. + fi +} diff --git a/server.sh b/server.sh index 7e453a7..0bed9af 100755 --- a/server.sh +++ b/server.sh @@ -8,6 +8,9 @@ else exit 1 fi +source "backends/tar.sh" +source "backends/bup.sh" + function backup_hook_example { bup -d $CUR_BACK_DIR ls -l $BACKUP_NAME/latest/var/minecraft } @@ -81,7 +84,7 @@ function server_running() { ps -p $(cat $PIDFILE) > /dev/null return fi - + false } @@ -119,7 +122,7 @@ function server_backup_safe() { send_cmd "save-off" send_cmd "save-all flush" echo "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 ] do @@ -133,7 +136,7 @@ function server_backup_safe() { else create_backup_archive fi - + local RET=$? echo "Re-enabling auto-save" @@ -162,42 +165,6 @@ function server_backup_unsafe() { fi } -function create_bup_backup() { - BACKUP_DIR="mc-backups" - CUR_BACK_DIR="mc-backups/$CUR_YEAR" - - if [ ! -d "$CUR_BACK_DIR" ]; then - mkdir -p "$CUR_BACK_DIR" - fi - - - bup -d "$CUR_BACK_DIR" index "$WORLD_NAME" - status=$? - if [ $status -eq 1 ]; then - bup -d "$CUR_BACK_DIR" init - bup -d "$CUR_BACK_DIR" index "$WORLD_NAME" - fi - - bup -d "$CUR_BACK_DIR" save -n "$BACKUP_NAME" "$WORLD_NAME" - - echo "Backup using bup to $CUR_BACK_DIR is complete" -} - -# TODO: Make default .tar with optional bup -function create_backup_archive() { - ARCHNAME="backup/$WORLD_NAME-backup_`date +%d-%m-%y-%T`.tar.gz" - tar -czf "$ARCHNAME" "./$WORLD_NAME" - - if [ ! $? -eq 0 ] - then - echo "TAR failed. No Backup created." - rm $ARCHNAME #remove (probably faulty) archive - return 1 - else - echo $ARCHNAME created. - fi -} - function backup_running() { systemctl is-active --quiet mc-backup.service } @@ -210,30 +177,26 @@ function server_backup() { force=$1 if [ "$force" = "true" ]; then - if backup_running; then - echo "A backup is running. Aborting..." - return - fi - else - if fbackup_running; then - echo "A force backup is running. Aborting..." - return - fi + if backup_running; then + echo "A backup is running. Aborting..." + return + fi + else + if fbackup_running; then + echo "A force backup is running. Aborting..." + return + fi fi - if server_running; then + if server_running; then server_backup_safe "$force" - else + else server_backup_unsafe fi exit } -function ls_bup() { - bup -d "mc-backups/${CUR_YEAR}" ls "mc-sad-squad/$1" -} - #cd $(dirname $0) case $1 in diff --git a/serverconf.sh b/serverconf.sh index cb88dc4..25ba16e 100644 --- a/serverconf.sh +++ b/serverconf.sh @@ -1,10 +1,10 @@ # serverconf.sh -# configuration file for server.sh minecraft server +# configuration file for server.sh minecraft server # management script #CONFIG JRE_JAVA="java" -JVM_ARGS="-Xms4096M -Xmx6144M" +JVM_ARGS="-Xms4096M -Xmx6144M" JAR="fabric-server-launch.jar" JAR_ARGS="-nogui" @@ -20,4 +20,3 @@ USE_BUP="NO" #Constants CUR_YEAR=`date +"%Y"` - -- cgit v1.2.3 From 57f56df53bb63f23b5f4447046a8f9f725162ad9 Mon Sep 17 00:00:00 2001 From: TheMightyV Date: Sun, 2 Jan 2022 19:58:23 +0100 Subject: multiple backup destinations --- backends/bup.sh | 70 ++++++++++++++++++++++++++++++++++++++++++++------------- backends/tar.sh | 49 ++++++++++++++++++++++++++++++++++++---- server.sh | 50 ++++++++++++++++++++++++++++++++--------- serverconf.sh | 2 ++ 4 files changed, 141 insertions(+), 30 deletions(-) diff --git a/backends/bup.sh b/backends/bup.sh index 83e4e98..e51aaf1 100644 --- a/backends/bup.sh +++ b/backends/bup.sh @@ -1,23 +1,63 @@ -function create_bup_backup() { - BACKUP_DIR="mc-backups" - CUR_BACK_DIR="mc-backups/$CUR_YEAR" - - if [ ! -d "$CUR_BACK_DIR" ]; then - mkdir -p "$CUR_BACK_DIR" - fi +# use first not-remote backup directory as bup's local repository +# defaults to ".bup" +function bup_local() { + for BACKUP_DIR in ${BACKUP_DIRS[*]} + do + if [[ $BACKUP_DIR != *:* ]]; then + echo "$BACKUP_DIR" + return + fi + done + echo ".bup" +} - bup -d "$CUR_BACK_DIR" index "$WORLD_NAME" +function bup_init() { + bup -d "$(bup_local)" index "$WORLD_NAME" status=$? - if [ $status -eq 1 ]; then - bup -d "$CUR_BACK_DIR" init - bup -d "$CUR_BACK_DIR" index "$WORLD_NAME" + if [ $status -ne 0 ]; then + echo "bup: no local repo found, creating..." + bup -d "$(bup_local)" init -r "$(bup_local)" + echo "bup: created local repo at $(bup_local)" fi +} + +function bup_create_backup() { + echo "bup: backup started" + + bup -d "$(bup_local)" index "$WORLD_NAME" - bup -d "$CUR_BACK_DIR" save -n "$BACKUP_NAME" "$WORLD_NAME" + # 0 if saved to at least one non-local repository + RETCODE=1 + for BACKUP_DIR in ${BACKUP_DIRS[*]} + do + 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" + # 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" + else + echo "bup: failed to make remote at \"$BACKUP_DIR\", moving on" + fi + else + if [ "$BACKUP_DIR" = "$(bup_local)" ]; then + RETCODE=0 + fi + fi + done - echo "Backup using bup to $CUR_BACK_DIR is complete" + echo "bup: backup finished" + return $RETCODE } -function ls_bup() { - bup -d "mc-backups/${CUR_YEAR}" ls "mc-sad-squad/$1" +function bup_ls() { + 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 } diff --git a/backends/tar.sh b/backends/tar.sh index 4d7ff36..c6012e3 100644 --- a/backends/tar.sh +++ b/backends/tar.sh @@ -1,14 +1,55 @@ +function tar_init() { + # nothing to do for tar? + : +} + # TODO: Make default .tar with optional bup function tar_create_backup() { - ARCHNAME="backup/$WORLD_NAME-backup_`date +%d-%m-%y-%T`.tar.gz" + echo "tar: backing up..." + + # save world to a temporary archive + ARCHNAME="/tmp/${BACKUP_NAME}_`date +%d-%m-%y-%T`.tar.gz" tar -czf "$ARCHNAME" "./$WORLD_NAME" if [ ! $? -eq 0 ] then - echo "TAR failed. No Backup created." - rm $ARCHNAME #remove (probably faulty) archive + echo "tar: failed to save the world" + rm "$ARCHNAME" #remove (probably faulty) archive return 1 else - echo $ARCHNAME created. + echo "tar: world saved to $ARCHNAME, pushing it to backup directories..." fi + + RETCODE=2 + for BACKUP_DIR in ${BACKUP_DIRS[*]} + do + 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" + else + RETCODE=0 + fi + done + + rm "$ARCHNAME" + + echo "tar: backup finished" + + return $RETCODE +} + +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 + done } diff --git a/server.sh b/server.sh index 0bed9af..9ed7edf 100755 --- a/server.sh +++ b/server.sh @@ -109,6 +109,34 @@ function players_online() { [ `tail -n 3 "$LOGFILE" | grep -c "There are 0"` -lt 1 ] } +function init_backup() { + 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\"" + else + mkdir -p "$BACKUP_DIR" + fi + done + + if [ $USE_BUP = "YES" ]; then + bup_init + else + tar_init + fi +} + +function create_backup() { + init_backup + + if [ $USE_BUP = "YES" ]; then + bup_create_backup + else + tar_create_backup + fi +} function server_backup_safe() { force=$1 @@ -131,11 +159,7 @@ function server_backup_safe() { sleep 2 echo "Done! starting backup..." - if [ $USE_BUP = "YES" ]; then - create_bup_backup - else - create_backup_archive - fi + create_backup local RET=$? @@ -152,11 +176,7 @@ function server_backup_safe() { function server_backup_unsafe() { echo "No running server detected. Running Backup" - if [ $USE_BUP = "YES" ]; then - create_bup_backup - else - create_backup_archive - fi + create_backup if [ $? -eq 0 ] then @@ -197,6 +217,14 @@ function server_backup() { exit } +function ls_backups() { + if [ $USE_BUP = "YES" ]; then + bup_ls + else + tar_ls + fi +} + #cd $(dirname $0) case $1 in @@ -220,7 +248,7 @@ case $1 in server_backup "true" ;; "ls") - ls_bup $2 + ls_backups ;; *) echo "Usage: $0 start|stop|attach|status|backup" diff --git a/serverconf.sh b/serverconf.sh index 25ba16e..8625bf3 100644 --- a/serverconf.sh +++ b/serverconf.sh @@ -20,3 +20,5 @@ USE_BUP="NO" #Constants CUR_YEAR=`date +"%Y"` + +BACKUP_DIRS=(".bak/$CUR_YEAR" "user@backupserver:/path/to/backup/$CUR_YEAR") -- cgit v1.2.3 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 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ server.sh | 13 ++++++++--- serverconf.sh | 10 ++++++++- 3 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 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 +} diff --git a/server.sh b/server.sh index 9ed7edf..ee16af7 100755 --- a/server.sh +++ b/server.sh @@ -10,6 +10,7 @@ fi source "backends/tar.sh" source "backends/bup.sh" +source "backends/borg.sh" function backup_hook_example { bup -d $CUR_BACK_DIR ls -l $BACKUP_NAME/latest/var/minecraft @@ -121,8 +122,10 @@ function init_backup() { fi done - if [ $USE_BUP = "YES" ]; then + if [ $BACKUP_BACKEND = "bup" ]; then bup_init + elif [ $BACKUP_BACKEND = "borg" ]; then + borg_init else tar_init fi @@ -131,8 +134,10 @@ function init_backup() { function create_backup() { init_backup - if [ $USE_BUP = "YES" ]; then + if [ $BACKUP_BACKEND = "bup" ]; then bup_create_backup + elif [ $BACKUP_BACKEND = "borg" ]; then + borg_create_backup else tar_create_backup fi @@ -218,8 +223,10 @@ function server_backup() { } function ls_backups() { - if [ $USE_BUP = "YES" ]; then + if [ $BACKUP_BACKEND = "bup" ]; then bup_ls + elif [ $BACKUP_BACKEND = "borg" ]; then + borg_ls else tar_ls fi diff --git a/serverconf.sh b/serverconf.sh index 8625bf3..a73a65d 100644 --- a/serverconf.sh +++ b/serverconf.sh @@ -16,9 +16,17 @@ WORLD_NAME="lfja" BACKUP_NAME="${WORLD_NAME}_backup" LOGFILE="logs/latest.log" PIDFILE="server-screen.pid" -USE_BUP="NO" +# if not bup or borg, uses tar by default +#BACKUP_BACKEND="tar" +#BACKUP_BACKEND="bup" +BACKUP_BACKEND="borg" #Constants CUR_YEAR=`date +"%Y"` BACKUP_DIRS=(".bak/$CUR_YEAR" "user@backupserver:/path/to/backup/$CUR_YEAR") + +# borg repository could be password protected +# 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" -- 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(-) 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 023a18b1d150fc9c09bf6ac135663f7ddce3b757 Mon Sep 17 00:00:00 2001 From: TheMightyV Date: Sun, 2 Jan 2022 20:15:48 +0100 Subject: fallback world name to server.properties --- serverconf.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/serverconf.sh b/serverconf.sh index a73a65d..3748541 100644 --- a/serverconf.sh +++ b/serverconf.sh @@ -12,6 +12,10 @@ TMUX_WINDOW="minecraft" TMUX_SOCKET="mc_tmux_socket" WORLD_NAME="lfja" +if [ -f "server.properties" ]; then + WORLD_NAME=$(grep level-name server.properties | cut -d= -f2) + echo "Getting world name from server.properties: $WORLD_NAME" +fi BACKUP_NAME="${WORLD_NAME}_backup" LOGFILE="logs/latest.log" -- 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(-) 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(-) 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 fc9f81108360e528f8adfccf8051c79e089df2c3 Mon Sep 17 00:00:00 2001 From: TheMightyV Date: Tue, 4 Jan 2022 20:03:55 +0100 Subject: added tests --- tests.sh | 118 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100755 tests.sh diff --git a/tests.sh b/tests.sh new file mode 100755 index 0000000..f5c4120 --- /dev/null +++ b/tests.sh @@ -0,0 +1,118 @@ +#!/bin/bash + +source server.sh + +# TODO: testing remote directories +BACKUP_DIRS=(".bak") +mkdir ".bak" + +function test_backend() { + BACKUP_BACKEND="$1" + + # doing tests inside /tmp/ + DIR=$(mktemp -d) + cd "$DIR" + local status=$? + if [ $status -ne 0 ] ; then + echo "Failed to make a temporary directory" + return 1 + fi + function cleanup() { + rm -r "$DIR" + } + + # make a "world" with some volatile data + function make_world() { + mkdir -p "$DIR/$1/DIM0" + date > "$1/data0" + shuf -e {1..100} | tr '\n' ' ' > "$DIR/$1/DIM0/data1" + } + + # make two versions of a world and back up both + make_world "$WORLD_NAME" + local old_world="${WORLD_NAME}.orig0" + cp -r "$DIR/$WORLD_NAME" "$DIR/$old_world" + if ! server_backup ; then + cleanup + exit + fi + + # backup time in archive's name is specified up to seconds, so subsequent backups without some delay will have the same name and previous backup be overwritten + if [ $BACKUP_BACKEND = "tar" ]; then + sleep 1 + fi + + make_world "$WORLD_NAME" + local new_world="${WORLD_NAME}.orig1" + cp -r "$DIR/$WORLD_NAME" "$DIR/$new_world" + if ! server_backup ; then + cleanup + exit + fi + + function same_world() { + delta=$(diff -r "$DIR/$1" "$DIR/$2") + if [ -z "$delta" ] ; then + return 0 + fi + return 1 + } + + # corrupting current (new) world + find "$DIR/$WORLD_NAME" -type f -exec shred {} \; + if same_world "$WORLD_NAME" "$new_world" ; then + echo "Failed to corrupt new world" + cleanup + exit + fi + + + # restore new backup + server_restore "${BACKUP_DIRS[0]}" 0 + # must be: new backup == new world + if ! same_world "$WORLD_NAME" "$new_world" ; then + echo "${BACKUP_BACKEND}: new backup != new world" + cleanup + exit + fi + # must be: new backup != old world + if same_world "$WORLD_NAME" "$old_world" ; then + echo "${BACKUP_BACKEND}: new backup == old world" + cleanup + exit + fi + + + # restore old backup + if [ $BACKUP_BACKEND = "bup" ]; then + # bup's 0th option is "latest", which links to 1st option, this is not present in tar and borg + server_restore "${BACKUP_DIRS[0]}" 2 + else + server_restore "${BACKUP_DIRS[0]}" 1 + fi + # must be: old backup == old world + if ! same_world "$WORLD_NAME" "$old_world" ; then + echo "${BACKUP_BACKEND}: old backup != old world" + cleanup + exit + fi + # must be: old backup != new world + if same_world "$WORLD_NAME" "$new_world" ; then + echo "${BACKUP_BACKEND}: old backup == new world" + cleanup + exit + fi + + cleanup +} + +echo "Testing tar backend" +test_backend "tar" + +echo "Testing bup backend" +test_backend "bup" + +echo "Testing borg backend" +test_backend "borg" + +echo "All tests passed" -- 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(-) 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(-) 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(-) 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 66f9d91a93148d607e791fc98d14b38c584ea0ea Mon Sep 17 00:00:00 2001 From: TheMightyV Date: Tue, 4 Jan 2022 23:06:29 +0100 Subject: removed unnecessary code from tests --- tests.sh | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/tests.sh b/tests.sh index f5c4120..b12a32a 100755 --- a/tests.sh +++ b/tests.sh @@ -1,10 +1,11 @@ #!/bin/bash +# these tests will create a "old" and "new/current" worlds, make backups of then and check if they actually correspond to what they supposed to +# if tests succeed, you'll see the last line "All tests passed" + source server.sh -# TODO: testing remote directories -BACKUP_DIRS=(".bak") -mkdir ".bak" +WORLD_NAME="test_world" function test_backend() { BACKUP_BACKEND="$1" @@ -20,6 +21,8 @@ function test_backend() { function cleanup() { rm -r "$DIR" } + # TODO: testing remote directories + BACKUP_DIRS=( "$PWD/.bak" ) # make a "world" with some volatile data function make_world() { @@ -50,17 +53,10 @@ function test_backend() { exit fi - function same_world() { - delta=$(diff -r "$DIR/$1" "$DIR/$2") - if [ -z "$delta" ] ; then - return 0 - fi - return 1 - } # corrupting current (new) world find "$DIR/$WORLD_NAME" -type f -exec shred {} \; - if same_world "$WORLD_NAME" "$new_world" ; then + if same_world "$DIR/$WORLD_NAME" "$DIR/$new_world" ; then echo "Failed to corrupt new world" cleanup exit @@ -70,13 +66,13 @@ function test_backend() { # restore new backup server_restore "${BACKUP_DIRS[0]}" 0 # must be: new backup == new world - if ! same_world "$WORLD_NAME" "$new_world" ; then + if ! same_world "$DIR/$WORLD_NAME" "$DIR/$new_world" ; then echo "${BACKUP_BACKEND}: new backup != new world" cleanup exit fi # must be: new backup != old world - if same_world "$WORLD_NAME" "$old_world" ; then + if same_world "$DIR/$WORLD_NAME" "$DIR/$old_world" ; then echo "${BACKUP_BACKEND}: new backup == old world" cleanup exit @@ -91,13 +87,13 @@ function test_backend() { server_restore "${BACKUP_DIRS[0]}" 1 fi # must be: old backup == old world - if ! same_world "$WORLD_NAME" "$old_world" ; then + if ! same_world "$DIR/$WORLD_NAME" "$DIR/$old_world" ; then echo "${BACKUP_BACKEND}: old backup != old world" cleanup exit fi # must be: old backup != new world - if same_world "$WORLD_NAME" "$new_world" ; then + if same_world "$DIR/$WORLD_NAME" "$DIR/$new_world" ; then echo "${BACKUP_BACKEND}: old backup == new world" cleanup exit -- cgit v1.2.3 From 9a80e613c6838b5c73d2e18237c6547fa8ec3dfc Mon Sep 17 00:00:00 2001 From: TheMightyV Date: Wed, 5 Jan 2022 19:48:16 +0100 Subject: flipped asserts to match "canon" assert behaviour added "server not running" check in server_restore --- server.sh | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/server.sh b/server.sh index af7eb1e..2f4a0ae 100755 --- a/server.sh +++ b/server.sh @@ -26,7 +26,7 @@ function send_cmd () { tmux -S $TMUX_SOCKET send -t $TMUX_WINDOW "$1" enter } -function assert_running() { +function assert_not_running() { if server_running; then echo "It seems a server is already running. If this is not the case,\ manually attach to the running screen and close it." @@ -34,7 +34,7 @@ function assert_running() { fi } -function assert_not_running() { +function assert_running() { if ! server_running; then echo "Server not running" exit 1 @@ -42,7 +42,7 @@ function assert_not_running() { } function server_start() { - assert_running + assert_not_running if [ ! -f "eula.txt" ] then @@ -62,7 +62,7 @@ function server_stop() { # Allow success even if server is not running #trap "exit 0" EXIT - assert_not_running + assert_running send_cmd "stop" local RET=1 @@ -81,7 +81,7 @@ function server_stop() { } function server_attach() { - assert_not_running + assert_running tmux -S $TMUX_SOCKET attach -t $TMUX_WINDOW exit } @@ -302,6 +302,8 @@ function is_in() { } function server_restore() { + assert_not_running + local backup_dir local snapshot_index local dest="$PWD" @@ -363,7 +365,6 @@ function server_restore() { 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 -- cgit v1.2.3 From 1ba766d277fc35435ca80b9fcfa28c57468f7bce Mon Sep 17 00:00:00 2001 From: TheMightyV Date: Wed, 5 Jan 2022 19:58:46 +0100 Subject: tests always verbose tests block spacing tests show list of backups fixed bad variable in bup_ls_all --- backends/bup.sh | 2 +- tests.sh | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/backends/bup.sh b/backends/bup.sh index cae36fe..d1250de 100644 --- a/backends/bup.sh +++ b/backends/bup.sh @@ -66,7 +66,7 @@ function bup_ls_dir() { function bup_ls_all() { for backup_dir in ${BACKUP_DIRS[*]} do - echo "bup: backups in \"$BACKUP_DIR\":" + echo "bup: backups in \"$backup_dir\":" bup -d "$(bup_local)" ls -r "$backup_dir" --human-readable -l "$BACKUP_NAME" done } diff --git a/tests.sh b/tests.sh index b12a32a..fc6a692 100755 --- a/tests.sh +++ b/tests.sh @@ -5,6 +5,8 @@ source server.sh +VERBOSE=1 + WORLD_NAME="test_world" function test_backend() { @@ -53,6 +55,8 @@ function test_backend() { exit fi + ls_backups + # corrupting current (new) world find "$DIR/$WORLD_NAME" -type f -exec shred {} \; @@ -102,13 +106,13 @@ function test_backend() { cleanup } -echo "Testing tar backend" +printf "\n\n\nTesting tar backend\n" test_backend "tar" -echo "Testing bup backend" +printf "\n\n\nTesting bup backend\n" test_backend "bup" -echo "Testing borg backend" +printf "\n\n\nTesting borg backend\n" test_backend "borg" -echo "All tests passed" +printf "\n\n\nAll tests passed\n" -- 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(-) 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 From d184a0c42497e3c9c7b2949092933fbca783b21d Mon Sep 17 00:00:00 2001 From: TheMightyV Date: Wed, 5 Jan 2022 22:11:57 +0100 Subject: removed default password --- serverconf.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/serverconf.sh b/serverconf.sh index a8f02a6..c9ef3c8 100644 --- a/serverconf.sh +++ b/serverconf.sh @@ -33,7 +33,7 @@ CUR_YEAR=`date +"%Y"` # IMPORTANT: local paths must be absolute! BACKUP_DIRS=( "$PWD/.bak/$CUR_YEAR" "user@backupserver:/path/to/backup/$CUR_YEAR" ) -# borg repositories are password protected by default +# borg repository could be pasword-protected # 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 3257c178d20dff95bdb6177cd8ad6b85204e0165 Mon Sep 17 00:00:00 2001 From: TheMightyV Date: Sat, 8 Jan 2022 12:07:00 +0100 Subject: require server-off for restoration in PWD only --- server.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/server.sh b/server.sh index 2b58798..d9c06b7 100755 --- a/server.sh +++ b/server.sh @@ -302,8 +302,6 @@ function is_in() { } function server_restore() { - assert_not_running - local backup_dir local snapshot_index local dest="$PWD" @@ -317,6 +315,10 @@ function server_restore() { dest="$3" fi + if [ "$dest" = "$PWD" ]; then + assert_not_running + fi + if [ ${#BACKUP_DIRS[@]} -eq 0 ]; then log_error "No backup directories found, abort" return 1 -- cgit v1.2.3 From 5481c591d4b12eebda23d12dc09caa56a7727ff2 Mon Sep 17 00:00:00 2001 From: TheMightyV Date: Sat, 8 Jan 2022 12:20:21 +0100 Subject: backup test mode --- server.sh | 10 ++++++++++ serverconf.sh | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/server.sh b/server.sh index d9c06b7..d8caa16 100755 --- a/server.sh +++ b/server.sh @@ -162,9 +162,19 @@ function same_world() { # checking if latest snapshots are the same as the current world function test_backup_integrity() { + if [ $BACKUP_CHECK_MODE = 0 ]; then + log_info "Backup integrity check: skipped" + return 0 + fi + local retcode=0 for backup_dir in ${BACKUP_DIRS[*]} do + if [[ "$backup_dir" == *:* ]] && [ $BACKUP_CHECK_MODE = 1 ]; then + log_info "Skipping check of remote backup at $backup_dir" + continue + fi + local tmpdir=$(mktemp -d); # restore most recent backup to a temporary dir diff --git a/serverconf.sh b/serverconf.sh index c9ef3c8..5531969 100644 --- a/serverconf.sh +++ b/serverconf.sh @@ -37,3 +37,8 @@ BACKUP_DIRS=( "$PWD/.bak/$CUR_YEAR" "user@backupserver:/path/to/backup/$CUR_YEAR # 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" + +# 0 - don't check backups after creation +# 1 - check only local backups +# 2 - check local and remote backups (may take a while if world is large and connection is slow) +BACKUP_CHECK_MODE=1 -- cgit v1.2.3 From 5210769da03df76174939a9bdfa68a99257fb5a2 Mon Sep 17 00:00:00 2001 From: TheMightyV Date: Sat, 15 Jan 2022 23:38:35 +0100 Subject: Updated Readme --- Readme.md | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 55 insertions(+), 10 deletions(-) diff --git a/Readme.md b/Readme.md index 757e394..304521d 100644 --- a/Readme.md +++ b/Readme.md @@ -1,18 +1,31 @@ # minecraft-server-tools -My minecraft server management script with safe online Backup. +My minecraft server management script with safe online backup. + +Supports backing up using several backends to multiple local and remote directories. + +## Dependencies + +- [tmux](https://github.com/tmux/tmux) + +Either of backup backends: +- tar +- [bup](https://github.com/bup/bup) +- [borgbackup](https://github.com/borgbackup/borg) + +All of them are available on the Debian repository. ## Configuration -Config-variables are located at the top of `server.sh` +Config variables are located at `serverconf.sh`. ## Usage -`./server.sh start|stop|attach|status|backup` +`./server.sh start|stop|attach|status|backup|fbackup|restore|ls` ### start -Creates a `screen` session and starts a minecraft server within. +Creates a `tmux` session and starts a minecraft server within. Fails, if a session is already running with the same sessionname. ### stop @@ -21,26 +34,54 @@ Sends `stop` command to running server instance to safely shut down. ### attach -attaches to `screen` session. Exit with `CTRL + A d` +Attaches to tmux session with a server. Detach with `CTRL + A d`. ### status -lists active screen sessions with `SCREEN_SESSIONNAME`. +Shows if the server is running. ### backup -Backs up the world as a `tar.gz` archive in `./backup/`. -If a running server is detected, +Creates a backup of the current world: + +- If a running server is detected, the world is flushed to disk and autosave is disabled temporarily to prevent chunk corruption. -The command specified in `$BACKUP_HOOK` is +- Initializes backup directories if needed. + +- Backs up the world **if there are players on the server**. +The backup has `$BACKUP_NAME_` prefix. + +- Performs tests: backup is pulled from each backup directory and is compared to the current world. +This behaviour is controlled with `$BACKUP_CHECK_MODE`. + +- The command specified in `$BACKUP_HOOK` is executed on every successful backup. `$ARCHNAME` contains the relative path to the archive. This can be used to further process the created backup. +This is recommended for automated backups. + +**Warning** If all players leave just before a backup, progress is not saved. + +### fbackup + +Does the same as `backup`, but does not check for presence of players. + +This is not recommended for automated use except for deduplicating backup backends (bup and borgbackup). + +### restore + +Restores a backup from selected directory. +Old world is preserved with a current timestamp. + +### ls + +Lists backups in all directories. + ## Start automatically Create user and group `minecraft` with home in `/var/minecraft`. -Populate the directory with server.sh and a server jar. +Populate the directory with `server.sh`, `serverconf.sh`, `backends` and a server jar. Place `minecraft.service` in `/etc/systemd/system/` and run `systemctl start minecraft` to start once or `systemctl enable minecraft` to enable autostarting. @@ -62,3 +103,7 @@ The scripts are provided as-is at no warranty. They are in no way idiot-proof. Improvements are welcome. + +## TODO +- Allow non-forced backup to be run one time with no players on the server. +- Reach similar automated behaviour without depending on systemd? -- cgit v1.2.3 From 77a9baea740a49c2204893a022fd4bf25d109c07 Mon Sep 17 00:00:00 2001 From: Jonas Gunz Date: Tue, 25 Jan 2022 11:01:04 +0100 Subject: Relicense MIT --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a03b259 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 Jonas Gunz and Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. -- cgit v1.2.3