Last active
January 5, 2016 17:47
-
-
Save travis-g/a002a0f269d379d18348 to your computer and use it in GitHub Desktop.
Minecraft flexible server daemon
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
# /etc/init.d/minecraftd | |
VERSION=1.0.0 | |
### BEGIN INIT INFO | |
# Provides: minecraftd | |
# Required-Start: $local_fs $remote_fs | |
# Required-Stop: $local_fs $remote_fs | |
# Should-Start: $network | |
# Should-Stop: $network | |
# Default-Start: 2 3 4 5 | |
# Default-Stop: 0 1 6 | |
# Short-Description: Minecraft server daemon | |
# Description: Starts and controls the minecraft server daemon | |
### END INIT INFO | |
### INSTALLATION AS A SERVICE | |
# 1. Copy the script into /etc/init.d/ and run `chmod a+x /etc/init.d/minecraftd`. | |
# 2. Run `update-rc.d minecraftd defaults` before then running `insserv minecraftd`. | |
# 3. Restart the server device/box. On restart, the minecraftd process should start automatically. | |
# Note: if installed as a service, most commands should be run as super user (root). | |
# Note: if started as a service, reattach the screen session with `sudo screen -R | |
### SETTINGS START | |
USERNAME="user" | |
SERVERPATH="/home/$USERNAME/server" | |
SERVICE="forge-universal.jar" | |
BACKUPPATH="/home/$USERNAME/backup" | |
CRASHLOGPATH="/home/$USERNAME/crashes" | |
JAVA="/usr/bin/java" | |
MEMORY_OPTS="-Xmx768M -Xms500M" | |
JAVA_OPTS="" | |
INVOCATION="${JAVA} ${MEMORY_OPTS} ${JAVA_OPTS} -jar $SERVICE nogui" | |
BACKUPARCHIVEPATH="$BACKUPPATH/archive" | |
BACKUPDIR=$(date +%b_%Y) | |
PORT=$(grep server-port $SERVERPATH/server.properties | cut -d '=' -f2) | |
if [ -z "$PORT" ]; then | |
PORT=25565 | |
fi | |
is_running() { | |
# return false if java.pid does not exist | |
if [ ! -e $SERVERPATH/java.pid ]; then | |
return 1 | |
fi | |
pid=$(cat $SERVERPATH/java.pid) | |
# return false if PID string is empty | |
if [ -z $pid ]; then | |
return 1 | |
fi | |
ps -eo "%p" | grep "^\\s*$pid\\s*\$" > /dev/null | |
return $? | |
} | |
mc_start() { | |
if is_running; then | |
echo "$SERVICE was already running!" | |
else | |
echo "$SERVICE was not running... starting." | |
cd $SERVERPATH | |
# begin a new detached screen session and invoke the server process | |
screen -d -m -S mc$PORT $INVOCATION & | |
# wait for screen and the server instance to appear as processes | |
for (( i=0; i < 10; i++ )); do | |
screenpid=$(ps -eo '%p %a' | grep -v grep | grep -i screen | grep mc$PORT | awk '{print $1}') | |
javapid=$(ps -eo '%P %p' | grep "^\\s*$screenpid " | awk '{print $2}') | |
if [[ -n "$screenpid" && -n "$javapid" ]]; then | |
break | |
fi | |
sleep 1 | |
done | |
# record the running processes' PIDs as files | |
if [[ -n "$screenpid" && -n "$javapid" ]]; then | |
echo "$SERVICE is now running." | |
echo "$javapid" > $SERVERPATH/java.pid | |
echo "$screenpid.mc$PORT" > $SERVERPATH/screen.name | |
else | |
echo "Could not start $SERVICE." | |
fi | |
fi | |
} | |
mc_stop() { | |
if is_running; then | |
echo "$SERVICE is running... stopping." | |
# notify server users and save the server cleanly: | |
mc_exec "say §4SERVER §4SHUTTING §4DOWN §4IN §410 §4SECONDS. Saving map..." | |
mc_exec "save-all" | |
sleep 10 | |
mc_exec "stop" | |
# wait for the server processes to stop: | |
for (( i=0; i < 20; i++ )); do | |
is_running || break | |
sleep 1 | |
done | |
else | |
echo "$SERVICE was not running." | |
fi | |
# kill processes if they are still running: | |
if is_running; then | |
echo "$SERVICE could not be shut down cleanly... still running." | |
mc_kill | |
else | |
echo "$SERVICE is shut down." | |
fi | |
# remove the recorded PIDs | |
rm $SERVERPATH/java.pid | |
rm $SERVERPATH/screen.name | |
} | |
mc_saveoff() { | |
if is_running; then | |
# begin the backup process: | |
echo "$SERVICE is running... suspending saves" | |
mc_exec "say §9SERVER §9BACKUP §9STARTING. Server going readonly..." | |
mc_exec "save-off" | |
mc_exec "save-all" | |
# force dump of all loaded memory to the disk: | |
sync | |
sleep 10 | |
else | |
echo "$SERVICE was not running. Not suspending saves." | |
fi | |
} | |
mc_saveon() { | |
if is_running; then | |
# resume read-write status: | |
echo "$SERVICE is running... re-enabling saves" | |
mc_exec "save-on" | |
mc_exec "say §9SERVER §9BACKUP §9ENDED. Server going read-write..." | |
else | |
echo "$SERVICE was not running. Not resuming saves." | |
fi | |
} | |
mc_kill() { | |
pid=$(cat $SERVERPATH/java.pid) | |
# attempt to have java terminate the process (semi-clean exit): | |
echo "terminating process with pid $pid" | |
kill $pid | |
for (( i=0; i < 10; i++ )); do | |
is_running || break | |
sleep 1 | |
done | |
# force-kill the still-running java process: | |
if is_running; then | |
echo "$SERVICE could not be terminated, killing..." | |
kill -SIGKILL $pid | |
echo "$SERVICE killed" | |
else | |
echo "$SERVICE terminated" | |
fi | |
} | |
mc_backup() { | |
echo "Backing up minecraft world" | |
# check for an existing backup directory: | |
[ -d "$BACKUPPATH/$BACKUPDIR" ] || mkdir -p "$BACKUPPATH/$BACKUPDIR" | |
# store server image as an incremental backup using rdiff-backup: | |
rdiff-backup $SERVERPATH "$BACKUPPATH/$BACKUPDIR" | |
echo "Backup complete" | |
} | |
mc_thinoutbackup() { | |
if (($(date +%H) == 0)) && (($(date +%M) < 15)); then | |
archivedate=$(date --date="7 days ago") | |
echo "Thinning backups created $archivedate out" | |
archivedateunix=$(date --date="$archivedate" +%s) | |
archivesourcedir=$BACKUPPATH/$(date --date="$archivedate" +%b_%Y) | |
archivesource=$archivesourcedir/rdiff-backup-data/increments.$(date --date="$archivedate" +%Y-%m-%dT%H):0*.dir | |
archivesource=$(echo $archivesource) | |
archivedest=$BACKUPARCHIVEPATH/$(date --date="$archivedate" +%b_%Y) | |
if [[ ! -f $archivesource ]]; then | |
echo "Nothing to be done" | |
else | |
tempdir=$(mktemp -d) | |
if [[ ! $tempdir =~ ^/tmp ]]; then | |
echo "invalid tmp dir $tempdir" | |
else | |
rdiff-backup $archivesource $tempdir | |
rdiff-backup --current-time $archivedateunix $tempdir $archivedest | |
rm -R "$tempdir" | |
rdiff-backup --remove-older-than 7D --force $archivesourcedir | |
echo "done" | |
fi | |
fi | |
fi | |
} | |
mc_exec() { | |
# run a server command while the screen session is detached: | |
if is_running; then | |
screen -p 0 -S $(cat $SERVERPATH/screen.name) -X stuff "$@$(printf \\r)" | |
else | |
echo "$SERVICE was not running. Not executing command." | |
fi | |
} | |
mc_dumpcrashlogs() { | |
if is_running; then | |
cp $SERVERPATH/crash-reports/* $CRASHLOGPATH | |
mv $SERVERPATH/crash-reports/* $SERVERPATH/crash-reports.archive/ | |
fi | |
} | |
case "$1" in | |
start) | |
mc_start | |
;; | |
stop) | |
mc_stop | |
;; | |
restart) | |
mc_stop | |
mc_start | |
;; | |
backup) | |
mc_saveoff | |
mc_backup | |
mc_saveon | |
mc_thinoutbackup | |
;; | |
exec) | |
shift | |
mc_exec "$@" | |
;; | |
dumpcrashlogs) | |
mc_dumpcrashlogs | |
;; | |
status) | |
if is_running | |
then | |
echo "$SERVICE is running." | |
else | |
echo "$SERVICE is not running." | |
fi | |
;; | |
*) | |
echo "Usage: $(readlink -f $0) {start|stop|restart|backup|exec|dumpcrashlogs|status}" | |
exit 1 | |
;; | |
esac | |
exit 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment