diff options
author | Jonas Gunz <himself@jonasgunz.de> | 2022-01-25 11:01:32 +0100 |
---|---|---|
committer | Jonas Gunz <himself@jonasgunz.de> | 2022-01-25 11:01:32 +0100 |
commit | 9b35f4f8f9253ffb2e7aa8d7ee7526472ab191f2 (patch) | |
tree | 7bbe74ae3924f479ede973e0a9603b5c0646b2f4 /server.sh | |
parent | b0e2841205ed1f5ce8223c05e90258b30ea88879 (diff) | |
parent | 77a9baea740a49c2204893a022fd4bf25d109c07 (diff) | |
download | minecraft-server-tools-master.tar.gz |
Diffstat (limited to 'server.sh')
-rwxr-xr-x | server.sh | 348 |
1 files changed, 256 insertions, 92 deletions
@@ -4,39 +4,51 @@ 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 +source "backends/tar.sh" +source "backends/bup.sh" +source "backends/borg.sh" + +function log_debug() { + if [ $VERBOSE -ne 0 ]; then + 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 () { 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,\ + 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 } -function assert_not_running() { +function assert_running() { if ! server_running; then - echo "Server not running" + log_info "Server not running" exit 1 fi } function server_start() { - assert_running + assert_not_running 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 @@ -44,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 } @@ -52,7 +64,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 @@ -63,7 +75,7 @@ function server_stop() { RET=$? done - echo "stopped the server" + log_info "stopped the server" rm -f $PIDFILE @@ -71,7 +83,7 @@ function server_stop() { } function server_attach() { - assert_not_running + assert_running tmux -S $TMUX_SOCKET attach -t $TMUX_WINDOW exit } @@ -81,16 +93,16 @@ function server_running() { ps -p $(cat $PIDFILE) > /dev/null return fi - + false } 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 } @@ -106,132 +118,282 @@ 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[*]} + do + 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" + fi + done + + backup_backend_run "init" +} + +function same_world() { + delta=$(diff -r "$1" "$2") + if [ $? -ne 0 ]; then + return 1 + fi + if [ -z "$delta" ] ; then + return 0 + fi + return 1 +} + +# 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 + if ! server_restore "$backup_dir" 0 "$tmpdir" ; then + log_error "Failed to get latest snapshot from \"$backup_dir\"" + retcode=1 + elif ! same_world "$WORLD_NAME" "$tmpdir/$WORLD_NAME" ; then + log_error "Latest backup from \"$backup_dir\" differs from current world!" + retcode=1 + else + log_info "Backup at \"$backup_dir\" is OK" + fi + + rm -r "$tmpdir" + done + + if [ $retcode -ne 0 ] ; then + log_error "Backup integrity check: FAILED" + else + log_info "Backup integrity check: OK" + fi + return $retcode +} + +function create_backup() { + init_backup + backup_backend_run "create_backup" + test_backup_integrity +} function server_backup_safe() { - force=$1 - echo "Detected running server. Checking if players online..." + local force=$1 + 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 ] do sleep 1 done sleep 2 - echo "Done! starting backup..." + log_info "Done! starting backup..." + + create_backup - if [ $USE_BUP = "YES" ]; then - create_bup_backup - else - create_backup_archive - fi - 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 + if [ $RET -eq 0 ]; then + 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 + log_info "Running backup hook" + $BACKUP_HOOK + fi +} + +function backup_running() { + systemctl is-active --quiet mc-backup.service +} + +function fbackup_running() { + systemctl is-active --quiet mc-fbackup.service +} + +function server_backup() { + local force=$1 - if [ $USE_BUP = "YES" ]; then - create_bup_backup + if [ "$force" = "true" ]; then + if backup_running; then + log_info "A backup is running. Aborting..." + return + fi else - create_backup_archive + if fbackup_running; then + log_info "A force backup is running. Aborting..." + return + fi fi - if [ $? -eq 0 ] - then - echo Running backup hook - $BACKUP_HOOK + if server_running; then + server_backup_safe "$force" + else + server_backup_unsafe fi } -function create_bup_backup() { - BACKUP_DIR="mc-backups" - CUR_BACK_DIR="mc-backups/$CUR_YEAR" +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 +function choose_from() { + local items=("$@") + select item in "${items[@]}"; do + echo "$item" + return + done +} + +# checks if an item is in the array +function is_in() { + local item="$1" + shift + local array=("$@") - if [ ! -d "$CUR_BACK_DIR" ]; then - mkdir -p "$CUR_BACK_DIR" + # these :space: things allow checking that *exactly* this item is in array + if [[ ${array[*]} =~ (^|[[:space:]])"$item"($|[[:space:]]) ]]; then + return fi + false +} +function server_restore() { + local backup_dir + local snapshot_index + local dest="$PWD" - 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" + # 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 - bup -d "$CUR_BACK_DIR" save -n "$BACKUP_NAME" "$WORLD_NAME" + if [ "$dest" = "$PWD" ]; then + assert_not_running + fi - echo "Backup using bup to $CUR_BACK_DIR is complete" -} + if [ ${#BACKUP_DIRS[@]} -eq 0 ]; then + log_error "No backup directories found, abort" + return 1 + fi -# 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 + if [ -z $backup_dir ]; then + log_info "From where get the snapshot?" + backup_dir="$(choose_from "${BACKUP_DIRS[@]}")" + fi + if ! is_in "$backup_dir" "${BACKUP_DIRS[@]}" ; then + log_error "No valid backup directory selected, abort" return 1 - else - echo $ARCHNAME created. fi -} -function backup_running() { - systemctl is-active --quiet mc-backup.service -} -function fbackup_running() { - systemctl is-active --quiet mc-fbackup.service -} + local snapshots="$( backup_backend_run "ls \"$backup_dir\"" )" + log_debug "Snapshots found:" + log_debug "$snapshots" + if [ -z "$snapshots" ]; then + log_error "No snapshots found, abort" + return 1 + fi + # convert multiline string to bash array + snapshots=($(echo "$snapshots")) -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 + local snapshot + if [ -z $snapshot_index ]; then + log_info "Select which snapshot to restore" + snapshot=$(choose_from "${snapshots[@]}") + else + snapshot="${snapshots[snapshot_index]}" + fi + if ! is_in "$snapshot" "${snapshots[@]}" ; then + log_error "No valid snapshot selected, abort" + return 1 fi - if server_running; then - server_backup_safe "$force" - else - server_backup_unsafe + + 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 + oldworld_name="${WORLD_NAME}.old.$(date +'%F_%H-%M-%S.%N')" + log_info "Preserving old world: $(mv -n -v "$PWD/$WORLD_NAME" "$PWD/$oldworld_name")" fi - exit -} -function ls_bup() { - bup -d "mc-backups/${CUR_YEAR}" ls "mc-sad-squad/$1" + 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 + 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 + + log_debug "Snapshot restored" + + return 0 } #cd $(dirname $0) @@ -249,7 +411,9 @@ case $1 in "backup") server_backup ;; - # TODO: Add restore command + "restore") + server_restore + ;; "status") server_status ;; @@ -257,7 +421,7 @@ case $1 in server_backup "true" ;; "ls") - ls_bup $2 + server_ls_backups ;; *) echo "Usage: $0 start|stop|attach|status|backup" |