162 lines
3.7 KiB
Bash
162 lines
3.7 KiB
Bash
#!/bin/bash
|
|
|
|
set -euo pipefail
|
|
|
|
if [ "${DEBUG:-false}" == "true" ]; then
|
|
set -x
|
|
fi
|
|
|
|
: "${RCON_HOST:=localhost}"
|
|
: "${RCON_PORT:=25575}"
|
|
: "${RCON_PASSWORD:=minecraft}"
|
|
|
|
export RCON_HOST
|
|
export RCON_PORT
|
|
export RCON_PASSWORD
|
|
|
|
###############
|
|
## common ##
|
|
## functions ##
|
|
###############
|
|
|
|
is_elem_in_array() {
|
|
# $1 = element
|
|
# All remaining arguments are array to search for the element in
|
|
if [ "$#" -lt 2 ]; then
|
|
log INTERNALERROR "Wrong number of arguments passed to is_elem_in_array function"
|
|
return 2
|
|
fi
|
|
local element="${1}"
|
|
shift
|
|
local e
|
|
for e; do
|
|
if [ "${element}" == "${e}" ]; then
|
|
return 0
|
|
fi
|
|
done
|
|
return 1
|
|
}
|
|
|
|
log() {
|
|
if [ "$#" -lt 1 ]; then
|
|
log INTERNALERROR "Wrong number of arguments passed to log function"
|
|
return 2
|
|
fi
|
|
local level="${1}"
|
|
shift
|
|
local valid_levels=(
|
|
"INFO"
|
|
"WARN"
|
|
"ERROR"
|
|
"INTERNALERROR"
|
|
)
|
|
if ! is_elem_in_array "${level}" "${valid_levels[@]}"; then
|
|
log INTERNALERROR "Log level ${level} is not a valid level."
|
|
return 2
|
|
fi
|
|
(
|
|
# If any arguments are passed besides log level
|
|
if [ "$#" -ge 1 ]; then
|
|
# then use them as log message(s)
|
|
<<<"${*}" cat -
|
|
else
|
|
# otherwise read log messages from standard input
|
|
cat -
|
|
fi
|
|
if [ "${level}" == "INTERNALERROR" ]; then
|
|
echo "Please report this: https://github.com/itzg/docker-mc-backup/issues"
|
|
fi
|
|
) | awk -v level="${level}" '{ printf("%s %s %s\n", strftime("%FT%T%z"), level, $0); fflush(); }'
|
|
} >&2
|
|
|
|
retry() {
|
|
if [ "$#" -lt 3 ]; then
|
|
log INTERNALERROR "Wrong number of arguments passed to retry function"
|
|
return 1
|
|
fi
|
|
|
|
# How many times should we retry?
|
|
# Value smaller than zero means infinitely
|
|
local retries="${1}"
|
|
# Time to sleep between retries
|
|
local interval="${2}"
|
|
readonly retries interval
|
|
shift 2
|
|
|
|
if (( retries < 0 )); then
|
|
local retries_msg="infinite"
|
|
else
|
|
local retries_msg="${retries}"
|
|
fi
|
|
|
|
local i=-1 # -1 since we will increment it before printing
|
|
while (( retries >= ++i )) || [ "${retries_msg}" != "${retries}" ]; do
|
|
# Send SIGINT after 5 minutes. If it doesn't shut down in 30 seconds, kill it.
|
|
if output="$(timeout --signal=SIGINT --kill-after=30s 5m "${@}" 2>&1 | tr '\n' '\t')"; then
|
|
log INFO "Command executed successfully ${*}"
|
|
return 0
|
|
else
|
|
log ERROR "Unable to execute ${*} - try ${i}/${retries_msg}. Retrying in ${interval}"
|
|
if [ -n "${output}" ]; then
|
|
log ERROR "Failure reason: ${output}"
|
|
fi
|
|
fi
|
|
# shellcheck disable=SC2086
|
|
sleep ${interval}
|
|
done
|
|
return 2
|
|
}
|
|
|
|
is_function() {
|
|
if [ "${#}" -ne 1 ]; then
|
|
log INTERNALERROR "is_function expects 1 argument, received ${#}"
|
|
fi
|
|
name="${1}"
|
|
[ "$(type -t "${name}")" == "function" ]
|
|
}
|
|
|
|
call_if_function_exists() {
|
|
if [ "${#}" -lt 1 ]; then
|
|
log INTERNALERROR "call_if_function_exists expects at least 1 argument, received ${#}"
|
|
return 2
|
|
fi
|
|
function_name="${1}"
|
|
if is_function "${function_name}"; then
|
|
eval "${@}"
|
|
else
|
|
log INTERNALERROR "${function_name} is not a valid function!"
|
|
return 2
|
|
fi
|
|
}
|
|
|
|
##########
|
|
## main ##
|
|
##########
|
|
|
|
|
|
log INFO "waiting for rcon readiness..."
|
|
# 20 times, 10 second delay
|
|
retry 20 10s rcon-cli save-on
|
|
|
|
|
|
if retry 5 10s rcon-cli save-off; then
|
|
# No matter what we were doing, from now on if the script crashes
|
|
# or gets shut down, we want to make sure saving is on
|
|
trap 'retry 5 5s rcon-cli save-on' EXIT
|
|
|
|
retry 5 10s rcon-cli save-all
|
|
retry 5 10s sync
|
|
|
|
rcb backup
|
|
|
|
retry 20 10s rcon-cli save-on
|
|
# Remove our exit trap now
|
|
trap EXIT
|
|
else
|
|
log ERROR "Unable to turn saving off. Is the server running?"
|
|
exit 1
|
|
fi
|
|
|
|
if (( PRUNE_BACKUPS_DAYS > 0 )); then
|
|
rcb cleanup
|
|
fi |