aboutsummaryrefslogtreecommitdiff
path: root/server.sh
diff options
context:
space:
mode:
Diffstat (limited to 'server.sh')
-rwxr-xr-xserver.sh348
1 files changed, 256 insertions, 92 deletions
diff --git a/server.sh b/server.sh
index 7e453a7..d8caa16 100755
--- a/server.sh
+++ b/server.sh
@@ -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"