Skip to content

Instantly share code, notes, and snippets.

@travis-g
Last active January 5, 2016 17:47
Show Gist options
  • Save travis-g/a002a0f269d379d18348 to your computer and use it in GitHub Desktop.
Save travis-g/a002a0f269d379d18348 to your computer and use it in GitHub Desktop.
Minecraft flexible server daemon
#!/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