#!/bin/bash # Initial version from Minecraft wiki # Heavily modified, customized and expanded by masa # version x.x.x 2018-10-13 (YYYY-MM-DD) export LANG=en_US.UTF-8 export LC_ALL=en_US.UTF-8 as_user() { ME=`whoami` if [ ${ME} == ${USERNAME} ] ; then bash -c "${1}" else su - ${USERNAME} -c "${1}" fi } mc_start() { L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" # Check that the server is not already running if pgrep -u ${USERNAME} -f ${SERVICE} > /dev/null then echo "${L_PREFIX} [WARNING] mc_start(): ${SERVICE} is already running" | tee -a ${EVENT_LOG_FILE} else echo "${L_PREFIX} [INFO] mc_start(): Starting ${SERVICE}..." | tee -a ${EVENT_LOG_FILE} if [ "${USE_TEMP_LOCATION}" = "true" ] then # Copy the save files rom the actual game directory to the run-time location inside /tmp copy_save_files_from_game_dir_to_tmp local RET=$? if [ $RET -ne 0 ]; then echo "${L_TIMESTAMP} [ERROR] mc_start(): Failed to copy over the save files!" | tee -a ${EVENT_LOG_FILE} exit 1 fi fi # Start the server process as_user "cd ${INSTANCE_PATH} && screen -dmS ${SCREEN_SESSION} ${INVOCATION}" sleep ${POST_START_DELAY} # Verify that the server process was started successfully, by checking a few times in a delayed loop COUNTER=0 while [ $COUNTER -lt ${START_GRACE_DELAY} ] do if pgrep -u ${USERNAME} -f ${SERVICE} > /dev/null then L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" echo "${L_PREFIX} [INFO] mc_start(): ${SERVICE} is now running" | tee -a ${EVENT_LOG_FILE} # Create the state file to indicate that the server is/should be running as_user "touch ${STATE_FILE}" # Disable automatic saving? if [ ${START_TO_DISABLE_AUTOSAVE_DELAY} -ge 0 ] then sleep ${START_TO_DISABLE_AUTOSAVE_DELAY} mc_saveoff fi break fi sleep 1 ((COUNTER += 1)) done # If the loop counter hit the max value, the process was not started successfully if [ ${COUNTER} -ge ${START_GRACE_DELAY} ] then L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" echo "${L_PREFIX} [ERROR] mc_start(): Could not start ${SERVICE}" | tee -a ${EVENT_LOG_FILE} fi fi } mc_stop() { L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" # Check that the process is running if pgrep -u ${USERNAME} -f ${SERVICE} > /dev/null then echo "${L_PREFIX} [INFO] mc_stop(): Stopping ${SERVICE}" | tee -a ${EVENT_LOG_FILE} if [ ${SAVEALL_STOP_INTERVAL} -ge 0 ]; then if [ "${1}" = "broadcast" ]; then as_user "screen -p 0 -S ${SCREEN_SESSION} -X eval 'stuff \"say §dSERVER SHUTTING DOWN NOW. Saving the map...\"\015'" fi as_user "screen -p 0 -S ${SCREEN_SESSION} -X eval 'stuff \"save-all\"\015'" sleep ${SAVEALL_STOP_INTERVAL} else if [ "${1}" = "broadcast" ]; then as_user "screen -p 0 -S ${SCREEN_SESSION} -X eval 'stuff \"say §dSERVER SHUTTING DOWN NOW!!\"\015'" sleep 3 fi fi as_user "screen -p 0 -S ${SCREEN_SESSION} -X eval 'stuff \"stop\"\015'" sleep ${POST_STOP_DELAY} # Check if the process was successfully stopped COUNTER=0 while [ ${COUNTER} -lt ${STOP_GRACE_DELAY} ]; do if ! pgrep -u ${USERNAME} -f ${SERVICE} > /dev/null then # Remove the state file to indicate that the server has been stopped gracefully as_user "rm ${STATE_FILE} > /dev/null 2>&1" L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" echo "${L_PREFIX} [INFO] mc_stop(): ${SERVICE} has been stopped" | tee -a ${EVENT_LOG_FILE} if [ "${USE_TEMP_LOCATION}" = "true" ] then # Copy the save files from the temp directory (in RAM) into the actual server directory copy_save_files_from_tmp_to_game_dir local RET=$? # If copying the save file succeeded, and this is not a restart, then remove the temp files if [ $RET -eq 0 ] && [ "$1" != "true" ] then remove_save_files_from_tmp_location fi fi break fi sleep 1 ((COUNTER += 1)) done if [ ${COUNTER} -ge ${STOP_GRACE_DELAY} ] then L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" echo "${L_PREFIX} [ERROR] mc_stop(): ${SERVICE} could not be stopped" | tee -a ${EVENT_LOG_FILE} fi else echo "${L_PREFIX} [WARNING] mc_stop(): ${SERVICE} was not running" | tee -a ${EVENT_LOG_FILE} fi } mc_restart() { # Use the arguments as override delay values, if they exist if [ -n "${1}" ] then RESTART_FIRST_WARNING=${1} if [ -n "${2}" ] then RESTART_LAST_WARNING=${2} fi fi # Check if the server process is running, and broadcast warning messages before stopping the server if pgrep -u ${USERNAME} -f ${SERVICE} > /dev/null then if [ ${RESTART_FIRST_WARNING} -gt ${RESTART_LAST_WARNING} ] then as_user "screen -p 0 -S ${SCREEN_SESSION} -X eval 'stuff \"say §dSERVER RESTARTING IN ${RESTART_FIRST_WARNING} SECONDS\"\015'" ((DELAY = ${RESTART_FIRST_WARNING} - ${RESTART_LAST_WARNING})) sleep ${DELAY} fi as_user "screen -p 0 -S $SCREEN_SESSION -X eval 'stuff \"say §dSERVER RESTARTING IN ${RESTART_LAST_WARNING} SECONDS\"\015'" sleep ${RESTART_LAST_WARNING} L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" echo "${L_PREFIX} [INFO] mc_restart(): Stopping ${SERVICE}" | tee -a ${EVENT_LOG_FILE} mc_stop "true" if ! pgrep -u ${USERNAME} -f ${SERVICE} > /dev/null then L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" echo "${L_PREFIX} [INFO] mc_restart(): ${SERVICE} has been stopped" | tee -a ${EVENT_LOG_FILE} fi fi # (Re-)Start the server if ! pgrep -u ${USERNAME} -f ${SERVICE} > /dev/null then L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" echo "${L_PREFIX} [INFO] mc_restart(): (Re-)starting ${SERVICE}..." | tee -a ${EVENT_LOG_FILE} mc_start L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" if pgrep -u ${USERNAME} -f ${SERVICE} > /dev/null then echo "${L_PREFIX} [INFO] mc_restart(): ${SERVICE} is now running" | tee -a ${EVENT_LOG_FILE} else echo "${L_PREFIX} [ERROR] mc_restart(): Could not start ${SERVICE}" | tee -a ${EVENT_LOG_FILE} fi else echo "${L_PREFIX} [ERROR] mc_restart(): ${SERVICE} could not be stopped" | tee -a ${EVENT_LOG_FILE} fi } mc_restart_if_up() { # Use the arguments as override delay values, if they exist if [ -n "${1}" ] then RESTART_FIRST_WARNING=${1} if [ -n "${2}" ] then RESTART_LAST_WARNING=${2} fi fi # First check if the server is supposed to be running if [ -f "${STATE_FILE}" ] then # Check if the server process is running, and broadcast warning messages before stopping the server if pgrep -u ${USERNAME} -f ${SERVICE} > /dev/null then if [ ${RESTART_FIRST_WARNING} -gt ${RESTART_LAST_WARNING} ] then as_user "screen -p 0 -S ${SCREEN_SESSION} -X eval 'stuff \"say §dSERVER RESTARTING IN ${RESTART_FIRST_WARNING} SECONDS\"\015'" ((DELAY = ${RESTART_FIRST_WARNING} - ${RESTART_LAST_WARNING})) sleep ${DELAY} fi as_user "screen -p 0 -S $SCREEN_SESSION -X eval 'stuff \"say §dSERVER RESTARTING IN ${RESTART_LAST_WARNING} SECONDS\"\015'" sleep ${RESTART_LAST_WARNING} L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" echo "${L_PREFIX} [INFO] mc_restart_if_up(): Stopping ${SERVICE}" | tee -a ${EVENT_LOG_FILE} mc_stop "true" fi # The server was running and was successfully stopped, or at least is supposed to be running. Now we start it up again. if ! pgrep -u ${USERNAME} -f ${SERVICE} > /dev/null then L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" echo "${L_PREFIX} [INFO] mc_restart_if_up(): ${SERVICE} has been stopped" | tee -a ${EVENT_LOG_FILE} echo "${L_PREFIX} [INFO] mc_restart_if_up(): Restarting ${SERVICE}..." | tee -a ${EVENT_LOG_FILE} mc_start L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" if pgrep -u ${USERNAME} -f ${SERVICE} > /dev/null then echo "${L_PREFIX} [INFO] mc_restart_if_up(): ${SERVICE} is now running" | tee -a ${EVENT_LOG_FILE} else echo "${L_PREFIX} [ERROR] mc_restart_if_up(): Could not start ${SERVICE}" | tee -a ${EVENT_LOG_FILE} fi else echo "${L_PREFIX} [ERROR] mc_restart_if_up(): ${SERVICE} could not be stopped" | tee -a ${EVENT_LOG_FILE} fi # else # L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` # L_PREFIX="${L_TIMESTAMP}" # echo "${L_PREFIX} [WARNING] mc_restart_if_up(): ${SERVICE} was not running" | tee -a ${EVENT_LOG_FILE} fi } mc_saveoff() { if pgrep -u ${USERNAME} -f ${SERVICE} > /dev/null then if [ "${1}" = "broadcast" ]; then as_user "screen -p 0 -S ${SCREEN_SESSION} -X eval 'stuff \"say §dServer going read-only...\"\015'" fi as_user "screen -p 0 -S ${SCREEN_SESSION} -X eval 'stuff \"save-off\"\015'" fi } mc_saveon() { if pgrep -u ${USERNAME} -f ${SERVICE} > /dev/null then if [ "${1}" = "broadcast" ]; then as_user "screen -p 0 -S ${SCREEN_SESSION} -X eval 'stuff \"say §dServer going read-write...\"\015'" fi as_user "screen -p 0 -S ${SCREEN_SESSION} -X eval 'stuff \"save-on\"\015'" fi } mc_saveall() { if pgrep -u ${USERNAME} -f ${SERVICE} > /dev/null then if [ "${1}" = "broadcast" ]; then as_user "screen -p 0 -S ${SCREEN_SESSION} -X eval 'stuff \"say §dSaving the map...\"\015'" fi as_user "screen -p 0 -S ${SCREEN_SESSION} -X eval 'stuff \"save-all\"\015'" sleep ${POST_SAVEALL_DELAY} fi } mc_sync() { if [ "${USE_TEMP_LOCATION}" = "true" ]; then # Check if the server process is running if pgrep -u ${USERNAME} -f ${SERVICE} > /dev/null then mc_say "§dSyncing world data to disk, server going read-only" mc_saveoff if [ ${SAVEALL_BACKUP_INTERVAL} -ge 0 ]; then mc_saveall sleep ${SAVEALL_BACKUP_INTERVAL} else sleep 2 fi # Copy the save files from the temp directory (in RAM) into the actual server directory copy_save_files_from_tmp_to_game_dir mc_say "§dSyncing world data to disk finished, server going read-write" mc_saveon # Server not running, but the temp path exists elif [ -e "${WORLD_PATH_TMP}" ]; then # Copy the save files from the temp directory (in RAM) into the actual server directory copy_save_files_from_tmp_to_game_dir fi fi } copy_save_files_from_game_dir_to_tmp() { L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` if [ -z "${WORLD_PATH_TMP}" ] || [ -z "${WORLD_PATH_REAL}" ] || [ -z "${WORLD_PATH_SYMLINK}" ] then echo "Empty real or temp world path, or world path symlink name!" exit 1 fi # Make the necessary temporary directories if [ ! -d "${WORLD_PATH_TMP}" ] then echo "${L_TIMESTAMP} [INFO] copy_save_files_from_game_dir_to_tmp(): Creating the temporary save directory '${WORLD_PATH_TMP}'" | tee -a ${EVENT_LOG_FILE} as_user "mkdir -p ${WORLD_PATH_TMP} > /dev/null 2>&1" as_user "chmod 1777 ${WORLD_PATH_TMP}" fi # The symlink does not point to the temp directory # (which means that it likely points to the real directory on disk, see remove_save_files_from_tmp_location()) if [ -h "${WORLD_PATH_SYMLINK}" ] && [ `readlink -f ${WORLD_PATH_SYMLINK}` != "${WORLD_PATH_TMP}" ] then as_user "rm ${WORLD_PATH_SYMLINK} > /dev/null 2>&1" fi # No symlink present if [ ! -e "${WORLD_PATH_SYMLINK}" ] then echo "${L_TIMESTAMP} [INFO] copy_save_files_from_game_dir_to_tmp(): (Re-)Creating a symlink for the world directory to ${WORLD_PATH_TMP}" | tee -a ${EVENT_LOG_FILE} as_user "ln -s ${WORLD_PATH_TMP} ${WORLD_PATH_SYMLINK} > /dev/null 2>&1" fi echo "${L_TIMESTAMP} [INFO] copy_save_files_from_game_dir_to_tmp(): Going to copy the save files to the temp directory..." | tee -a ${EVENT_LOG_FILE} # Copy the save files from the "real save path" on disk to the temporary location (in RAM). # Note: -u to only copy newer files! as_user "rsync -au ${WORLD_PATH_REAL}/ ${WORLD_PATH_TMP} > /dev/null 2>&1" #as_user "rsync -auhv ${WORLD_PATH_REAL}/ ${WORLD_PATH_TMP}" local RET=$? L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` if [ $RET -ne 0 ]; then echo "${L_TIMESTAMP} [ERROR] copy_save_files_from_game_dir_to_tmp(): Failed to copy over the save files" | tee -a ${EVENT_LOG_FILE} return 1 else echo "${L_TIMESTAMP} [INFO] copy_save_files_from_game_dir_to_tmp(): Done copying files" | tee -a ${EVENT_LOG_FILE} fi } copy_save_files_from_tmp_to_game_dir() { L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` echo "${L_TIMESTAMP} [INFO] copy_save_files_from_tmp_to_game_dir(): Going to copy the save files from the temp directory to the instance directory" | tee -a ${EVENT_LOG_FILE} if [ -z "${WORLD_PATH_TMP}" ] || [ -z "${WORLD_PATH_REAL}" ] then echo "EMPTY PATH" exit 1 fi if [ ! -e "${WORLD_PATH_TMP}" ] then echo "${L_TIMESTAMP} [INFO] copy_save_files_from_tmp_to_game_dir: temp directory '${WORLD_PATH_TMP}' does not exist, nothing to do" return 0 fi # Sync any changed files from the temp location to the real save path (the -u flag only copies newer files) as_user "rsync -au ${WORLD_PATH_TMP}/ ${WORLD_PATH_REAL} > /dev/null" #as_user "rsync -auhv ${WORLD_PATH_TMP}/ ${WORLD_PATH_REAL}" local RET=$? L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` if [ $RET -ne 0 ]; then echo "${L_TIMESTAMP} [INFO] copy_save_files_from_tmp_to_game_dir(): Error copying save files!" | tee -a ${EVENT_LOG_FILE} return 1 else echo "${L_TIMESTAMP} [INFO] copy_save_files_from_tmp_to_game_dir(): Done copying files" | tee -a ${EVENT_LOG_FILE} fi } remove_save_files_from_tmp_location() { if [ -n ${INSTANCE_PATH_TMP} ] then L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` echo "${L_TIMESTAMP} [INFO] remove_save_files_from_tmp_location(): Removing the save files from the temp directory..." | tee -a ${EVENT_LOG_FILE} as_user "rm -r --preserve-root ${INSTANCE_PATH_TMP} > /dev/null" L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` echo "${L_TIMESTAMP} [INFO] remove_save_files_from_tmp_location(): Done removing files" | tee -a ${EVENT_LOG_FILE} # World path is a symlink #if [ -h "${WORLD_PATH_SYMLINK}" ] #then # echo "${L_TIMESTAMP} [INFO] remove_save_files_from_tmp_location(): Changing the symlink to point to the real world" | tee -a ${EVENT_LOG_FILE} # as_user "rm ${WORLD_PATH_SYMLINK} > /dev/null 2>&1" # as_user "cd ${INSTANCE_PATH}" # local WORLDNAME=`basename ${WORLD_PATH_REAL}` # as_user "ln -s ${WORLDNAME} ${WORLD_PATH_SYMLINK} > /dev/null 2>&1" #fi fi } mc_backup() { TIMESTAMP_TAR=`date '+%Y-%m-%d_%H.%M.%S'` TIMESTAMP_GIT=`date '+%Y-%m-%d %H:%M'` if [ "${USE_TEMP_LOCATION}" = "true" ] && [ -e "${WORLD_PATH_TMP}" ] then # Copy the save files from the temp directory (in RAM) into the actual server directory copy_save_files_from_tmp_to_game_dir local RET=$? if [ $RET -ne 0 ]; then L_PREFIX=`date "+%Y-%m-%d %H:%M:%S"` echo "${L_PREFIX} [INFO] mc_backup(): Failed to copy save files from temp to game dir for instance '${INSTANCE}'" | tee -a ${EVENT_LOG_FILE} echo "${L_PREFIX} [INFO] mc_backup(): Failed to copy save files from temp to game dir for instance '${INSTANCE}'" | tee -a ${BACKUP_LOG_FILE} > /dev/null return 1 fi fi if [ "${BACKUP_USING_TAR}" = "true" ] then L_PREFIX=`date "+%Y-%m-%d %H:%M:%S"` if [ "${BACKUP_WORLD_ONLY}" = "true" ] then echo "${L_PREFIX} [INFO] mc_backup(): Starting a backup of the world of '${INSTANCE}' using tar" | tee -a ${EVENT_LOG_FILE} echo "${L_PREFIX} [INFO] mc_backup(): Starting a backup of the world of '${INSTANCE}' using tar" | tee -a ${BACKUP_LOG_FILE} > /dev/null as_user "cd ${INSTANCE_PATH} && tar --exclude '.git' -czpf ${BACKUP_PATH_TAR}/${BACKUP_PREFIX}_world_${TIMESTAMP_TAR}.tar.gz ${WORLD_NAME} | tee -a ${BACKUP_LOG_FILE} > /dev/null 2>&1" else echo "${L_PREFIX} [INFO] mc_backup(): Starting a backup of the server '${INSTANCE}' using tar" | tee -a ${EVENT_LOG_FILE} echo "${L_PREFIX} [INFO] mc_backup(): Starting a backup of the server '${INSTANCE}' using tar" | tee -a ${BACKUP_LOG_FILE} > /dev/null as_user "cd `dirname ${INSTANCE_PATH}` && tar --exclude '.git' -czpf ${BACKUP_PATH_TAR}/${BACKUP_PREFIX}_${TIMESTAMP_TAR}.tar.gz `basename ${INSTANCE_PATH}` | tee -a ${BACKUP_LOG_FILE} > /dev/null 2>&1" fi fi if [ "${BACKUP_USING_GIT}" = "true" ] then # Do we have an additional commit message? if [ -n "${1}" ] then MSG=" ${1}" else MSG="" fi if [ "${2}" = "true" ] then local GIT_PRM_ALLOW_EMPTY="--allow-empty" fi L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" if [ "${BACKUP_WORLD_ONLY}" = "true" ] then echo "${L_PREFIX} [INFO] mc_backup(): Starting a backup of the world of '${INSTANCE}' using git" | tee -a ${EVENT_LOG_FILE} echo "${L_PREFIX} [INFO] mc_backup(): Starting a backup of the world of '${INSTANCE}' using git" | tee -a ${BACKUP_LOG_FILE} > /dev/null as_user "cd ${WORLD_PATH_REAL} && git add -A . | tee -a ${BACKUP_LOG_FILE} && git commit ${GIT_PRM_ALLOW_EMPTY} -m \"${TIMESTAMP_GIT}${MSG}\" | tee -a ${BACKUP_LOG_FILE}" else echo "${L_PREFIX} [INFO] mc_backup(): Starting a backup of the server '${INSTANCE}' using git" | tee -a ${EVENT_LOG_FILE} echo "${L_PREFIX} [INFO] mc_backup(): Starting a backup of the server '${INSTANCE}' using git" | tee -a ${BACKUP_LOG_FILE} > /dev/null as_user "cd ${INSTANCE_PATH} && git add -A . | tee -a ${BACKUP_LOG_FILE} && git commit ${GIT_PRM_ALLOW_EMPTY} -m \"${TIMESTAMP_GIT}${MSG}\" | tee -a ${BACKUP_LOG_FILE}" fi fi if [ "${BACKUP_USING_RESTIC}" = "true" ] then L_PREFIX=`date "+%Y-%m-%d %H:%M:%S"` if [ "${BACKUP_WORLD_ONLY}" = "true" ] then echo "${L_PREFIX} [INFO] mc_backup(): Starting a backup of the world of '${INSTANCE}' using restic" | tee -a ${EVENT_LOG_FILE} echo "${L_PREFIX} [INFO] mc_backup(): Starting a backup of the world of '${INSTANCE}' using restic" | tee -a ${BACKUP_LOG_FILE} > /dev/null as_user "restic -r ${BACKUP_RESTIC_REPO} backup \"${INSTANCE_PATH}/${WORLD_NAME}\" --verbose --exclude \".git\" | tee -a ${BACKUP_LOG_FILE}" else echo "${L_PREFIX} [INFO] mc_backup(): Starting a backup of the server '${INSTANCE}' using restic" | tee -a ${EVENT_LOG_FILE} echo "${L_PREFIX} [INFO] mc_backup(): Starting a backup of the server '${INSTANCE}' using restic" | tee -a ${BACKUP_LOG_FILE} > /dev/null as_user "restic -r ${BACKUP_RESTIC_REPO} backup \"${INSTANCE_PATH}\" --verbose --exclude \".git\" | tee -a ${BACKUP_LOG_FILE}" fi fi if [ "${BACKUP_SERVICE}" = "true" ] then L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" echo "${L_PREFIX} [INFO] mc_backup(): Backing up service ${SERVICE}" | tee -a ${EVENT_LOG_FILE} > /dev/null as_user "cd ${INSTANCE_PATH} && cp -pn ${SERVICE} ${BACKUP_PATH}/${BACKUP_PREFIX}_${TIMESTAMP_TAR}.jar > /dev/null 2>&1" fi L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" echo "${L_PREFIX} [INFO] mc_backup(): Backup of server instance '${INSTANCE}' complete" | tee -a ${EVENT_LOG_FILE} > /dev/null echo "${L_PREFIX} [INFO] mc_backup(): Backup of server instance '${INSTANCE}' complete" | tee -a ${BACKUP_LOG_FILE} > /dev/null } mc_backups_list() { if [ "${BACKUP_USING_RESTIC}" = "true" ] then L_PREFIX=`date "+%Y-%m-%d %H:%M:%S"` echo "${L_PREFIX} [INFO] mc_backups_list(): Listing latest backups of '${INSTANCE}' made with restic" | tee -a ${EVENT_LOG_FILE} as_user "restic -r ${BACKUP_RESTIC_REPO} snapshots | grep -E '[0-9]{4}-[0-9]{2}-[0-9]{2}' | cut -d' ' -f1-4 | tail -n 15" fi } mc_backup_wrapper() { if [ ${START_TO_DISABLE_AUTOSAVE_DELAY} -ge 0 ] then mc_say "§dSERVER BACKUP STARTING" if [ ${SAVEALL_BACKUP_INTERVAL} -ge 0 ]; then mc_saveall sleep ${SAVEALL_BACKUP_INTERVAL} else sleep 2 fi mc_backup "${1}" mc_say "§dSERVER BACKUP FINISHED" else mc_say "§dSERVER BACKUP STARTING, server going read-only" mc_saveoff if [ ${SAVEALL_BACKUP_INTERVAL} -ge 0 ]; then mc_saveall sleep ${SAVEALL_BACKUP_INTERVAL} else sleep 5 fi mc_backup "${1}" mc_say "§dSERVER BACKUP FINISHED, server going read-write" mc_saveon fi } mc_startifcrashed_basic() { L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" # If the state file is present, then the server is supposed to be running if [ -f "${STATE_FILE}" ] then # If the state file was present, but the process is not found, we assume that the server has crashed if ! pgrep -u ${USERNAME} -f ${SERVICE} > /dev/null then echo "${L_PREFIX} [WARNING] mc_startifcrashed_basic(): ${SERVICE} is not running (crashed?)" | tee -a ${EVENT_LOG_FILE} echo "${L_PREFIX} [INFO] mc_startifcrashed_basic(): Attempting to start ${SERVICE}..." | tee -a ${EVENT_LOG_FILE} mc_start L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" if pgrep -u ${USERNAME} -f ${SERVICE} > /dev/null then echo "${L_PREFIX} [INFO] mc_startifcrashed_basic(): ${SERVICE} is now running" | tee -a ${EVENT_LOG_FILE} else echo "${L_PREFIX} [ERROR] mc_startifcrashed_basic(): Could not start ${SERVICE}" | tee -a ${EVENT_LOG_FILE} fi else echo "${L_PREFIX} [INFO] mc_startifcrashed_basic(): ${SERVICE} is running" | tee -a ${EVENT_LOG_FILE} fi else echo "${L_PREFIX} [INFO] mc_startifcrashed_basic(): ${SERVICE} has been stopped gracefully" | tee -a ${EVENT_LOG_FILE} fi } mc_startifcrashed_ping() { L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" # If the state file is present, then the server is supposed to be running if [ -f "${STATE_FILE}" ] then # If the state file was present, and the process is found, we try to ping the server if pgrep -u ${USERNAME} -f ${SERVICE} > /dev/null then echo "${L_PREFIX} [INFO] mc_startifcrashed_ping(): ${SERVICE} is running, pinging it..." | tee -a ${EVENT_LOG_FILE} # We try the ping a maximum of PING_RETRY_COUNT times, to try and avoid false positive failures and restarts COUNTER=0 while [ ${COUNTER} -lt ${PING_RETRY_COUNT} ]; do if ${PING_INVOCATION} | grep ': OK' > /dev/null then L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" echo "${L_PREFIX} [INFO] mc_startifcrashed_ping(): ${SERVICE} responded to ping" | tee -a ${EVENT_LOG_FILE} break fi sleep ${PING_RETRY_DELAY} done # If the ping failed every time, then we assume that the server has crashed and we will kill it and start it up again if [ ${COUNTER} -ge ${PING_RETRY_COUNT} ] then L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" echo "${L_PREFIX} [WARNING] mc_startifcrashed_ping(): ${SERVICE} doesn't respond to ping (crashed?)" | tee -a ${EVENT_LOG_FILE} echo "${L_PREFIX} [INFO] mc_startifcrashed_ping(): Killing and (re-)starting ${SERVICE}" | tee -a ${EVENT_LOG_FILE} mc_kill sleep ${KILL_TO_START_DELAY} mc_start L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" if pgrep -u ${USERNAME} -f ${SERVICE} > /dev/null then echo "${L_PREFIX} [INFO] mc_startifcrashed_ping(): ${SERVICE} is now running" | tee -a ${EVENT_LOG_FILE} else echo "${L_PREFIX} [ERROR] mc_startifcrashed_ping(): Could not start ${SERVICE}" | tee -a ${EVENT_LOG_FILE} fi fi else # Process not found (crashed?), we try to start the server echo "${L_PREFIX} [WARNING] mc_startifcrashed_ping(): ${SERVICE} not running (crashed?)" | tee -a ${EVENT_LOG_FILE} echo "${L_PREFIX} [INFO] mc_startifcrashed_ping(): Attempting to start ${SERVICE}" | tee -a ${EVENT_LOG_FILE} mc_start L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" if pgrep -u ${USERNAME} -f ${SERVICE} > /dev/null then echo "${L_PREFIX} [INFO] mc_startifcrashed_ping(): ${SERVICE} is now running" | tee -a ${EVENT_LOG_FILE} else echo "${L_PREFIX} [ERROR] mc_startifcrashed_ping(): Could not start ${SERVICE}" | tee -a ${EVENT_LOG_FILE} fi fi else # No state file present, the server is supposed to be down echo "${L_PREFIX} [INFO] mc_check(): ${SERVICE} has been stopped gracefully" | tee -a ${EVENT_LOG_FILE} fi } mc_kill() { L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" if pgrep -u ${USERNAME} -f ${SERVICE} > /dev/null then echo "${L_PREFIX} [INFO] mc_kill(): Killing minecraft server instance '${INSTANCE}'..." | tee -a ${EVENT_LOG_FILE} as_user "pkill -SIGKILL -u ${USERNAME} -f ${SERVICE}" sleep ${POST_KILL_DELAY} COUNTER=0 while [ ${COUNTER} -lt ${KILL_GRACE_DELAY} ]; do if ! pgrep -u ${USERNAME} -f ${SERVICE} > /dev/null then L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" echo "${L_PREFIX} [INFO] mc_kill(): Server instance '${INSTANCE}' has been killed" | tee -a ${EVENT_LOG_FILE} # FIXME: Could this be done cleaner? screen -wipe if [ "${USE_TEMP_LOCATION}" = "true" ] then # Copy the save files from the temp directory (in RAM) into the actual server directory copy_save_files_from_tmp_to_game_dir local RET=$? if [ $RET -eq 0 ]; then remove_save_files_from_tmp_location fi fi break fi sleep 1 ((COUNTER += 1)) done if [ ${COUNTER} -ge ${KILL_GRACE_DELAY} ] then echo "${L_PREFIX} [ERROR] mc_kill(): Unable to kill minecraft server '${INSTANCE}'" | tee -a ${EVENT_LOG_FILE} fi else echo "${L_PREFIX} [WARNING] mc_kill(): Minecraft server instance '${INSTANCE}' was not running" | tee -a ${EVENT_LOG_FILE} fi } mc_download_server() { # Only do something if we have a version argument if [ -n "${1}" ] then VERSION="${1}" # If the requested server version doesn't yet exist, we attempt to download it if [ ! -f "${SERVER_FILES_PATH}/${SERVER_FILE_PREFIX}${VERSION}.jar" ] then MC_SERVER_URL="https://s3.amazonaws.com/Minecraft.Download/versions/${VERSION}/minecraft_server.${VERSION}.jar" as_user "wget -q -O \"${SERVER_FILES_PATH}/${SERVER_FILE_PREFIX}${VERSION}.jar\" ${MC_SERVER_URL}" # If wget returns 0 if [ "$?" -eq "0" ] then L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" echo "${L_PREFIX} [INFO] mc_download_server(): Downloaded ${MC_SERVER_URL} to ${SERVER_FILES_PATH}/${SERVER_FILE_PREFIX}${VERSION}.jar" | tee -a ${EVENT_LOG_FILE} else # Download failed echo "${L_PREFIX} [ERROR] mc_download_server(): Download of ${MC_SERVER_URL} failed" | tee -a ${EVENT_LOG_FILE} # Remove the empty file just created as_user "rm ${SERVER_FILES_PATH}/${SERVER_FILE_PREFIX}${VERSION}.jar" exit 1 fi fi fi } mc_update() { CURRENT_VERSION=`readlink -f "${INSTANCE_PATH}/${SERVICE}" | sed "s/.*\/${SERVER_FILE_PREFIX}\(.*\)\.jar/\1/"` # Any version change via this script on this server instance is forbidden if [ "${ALLOW_UPDATES}" = 'false' ] && [ "${ALLOW_DOWNGRADES}" = 'false' ] then L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" echo "${L_PREFIX} [ERROR] mc_update(): Any updates or downgrades of '${SERVICE}' are not allowed (@ ${CURRENT_VERSION})" | tee -a ${EVENT_LOG_FILE} return fi if [ -z "${1}" ] then # No version specified, assume we want the latest stable release VERSIONS_URL="https://launchermeta.mojang.com/mc/game/version_manifest.json" # Old URL: VERSIONS_URL="https://s3.amazonaws.com/Minecraft.Download/versions/versions.json" as_user "wget -q -O ${VERSIONS_JSON} ${VERSIONS_URL}" # Get the latest[release] value VERSION=`tr '\r\n' ' ' < ${VERSIONS_JSON} | sed "s/ //g" | sed 's/.*\("latest[^}]\+}\).*/\1/' | sed 's/.*"release":"\([^"]\+\)"}/\1/'` else # Use the version given as an argument VERSION="${1}" fi echo "Target version: ${VERSION}, Current version: ${CURRENT_VERSION}" #exit # If the requested version is different from the current version if [ "${CURRENT_VERSION}" != "${VERSION}" ] then # Check that the requested version change is permitted # FIXME This won't work correctly with alpha, beta etc. prefixed versions if [[ "${VERSION}" < "${CURRENT_VERSION}" ]] && [ "${ALLOW_DOWNGRADES}" != 'true' ] then L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" echo "${L_PREFIX} [ERROR] mc_update(): Downgrades of '${SERVICE}' are not allowed (${CURRENT_VERSION} -> ${VERSION})" | tee -a ${EVENT_LOG_FILE} return fi # FIXME This won't work correctly with alpha, beta etc. prefixed versions if [[ "${VERSION}" > "${CURRENT_VERSION}" ]] && [ "${ALLOW_UPDATES}" != 'true' ] then L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" echo "${L_PREFIX} [ERROR] mc_update(): Updates of '${SERVICE}' are not allowed (${CURRENT_VERSION} -> ${VERSION})" | tee -a ${EVENT_LOG_FILE} return fi # If the server is currently running, broadcast a message before starting the update process if pgrep -u ${USERNAME} -f ${SERVICE} > /dev/null then RUNNING="true" L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" echo "${L_PREFIX} [INFO] mc_update(): ${SERVICE} is running, stopping it and starting the update" | tee -a ${EVENT_LOG_FILE} mc_say "§d=== SERVER UPDATE STARTING ===" sleep 5 mc_stop "broadcast" else RUNNING="false" L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" echo "${L_PREFIX} [INFO] mc_update(): ${SERVICE} is stopped, starting the update" | tee -a ${EVENT_LOG_FILE} fi mc_backup "Backup before updating to version ${VERSION} (@ ${CURRENT_VERSION})" "true" mc_download_server ${VERSION} # If the requested server file exists, update the symlink if [ -f "${SERVER_FILES_PATH}/${SERVER_FILE_PREFIX}${VERSION}.jar" ] then as_user "ln -fs \"${SERVER_FILES_PATH}/${SERVER_FILE_PREFIX}${VERSION}.jar\" \"${INSTANCE_PATH}/${SERVICE}\"" if [ "${RUNNING}" = "true" ] then L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" echo "${L_PREFIX} [INFO] mc_update(): Updated '${SERVICE}' to version '${VERSION}', starting ${SERVICE}..." | tee -a ${EVENT_LOG_FILE} mc_start else L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" echo "${L_PREFIX} [INFO] mc_update(): Updated '${SERVICE}' to version '${VERSION}'" | tee -a ${EVENT_LOG_FILE} fi else # Download failed L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" echo "${L_PREFIX} [ERROR] mc_update(): Update of '${SERVICE}' to version '${VERSION}' failed" | tee -a ${EVENT_LOG_FILE} fi else L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" echo "${L_PREFIX} [WARNING] mc_update(): '${SERVICE}' is already at version '${VERSION}', not doing anything" | tee -a ${EVENT_LOG_FILE} fi } mc_say() { L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" if [ -n "${1}" ] then MSG="${1}" if pgrep -u ${USERNAME} -f ${SERVICE} > /dev/null then # echo "${L_PREFIX} [INFO] mc_say(): ${SERVICE} is running, saying '${MSG}'" | tee -a ${EVENT_LOG_FILE} as_user "screen -p 0 -S ${SCREEN_SESSION} -X eval 'stuff \"say ${MSG}\"\015'" # else # echo "${L_PREFIX} [WARNING] mc_say(): ${SERVICE} is not running" | tee -a ${EVENT_LOG_FILE} fi else echo "${L_PREFIX} [ERROR] mc_say(): You must specify a message" | tee -a ${EVENT_LOG_FILE} fi } mc_command() { L_TIMESTAMP=`date "+%Y-%m-%d %H:%M:%S"` L_PREFIX="${L_TIMESTAMP}" if [ "${1}" ] then command="${1}" if pgrep -u ${USERNAME} -f ${SERVICE} > /dev/null then echo "${L_PREFIX} [INFO] mc_command(): ${SERVICE} is running, executing command '${command}'" | tee -a ${EVENT_LOG_FILE} as_user "screen -p 0 -S ${SCREEN_SESSION} -X eval 'stuff \"${command}\"\015'" fi else echo "${L_PREFIX} [ERROR] mc_command(): You must specify a server command" | tee -a ${EVENT_LOG_FILE} fi }