#!/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