Last active
January 4, 2016 22:09
-
-
Save Korko/8686056 to your computer and use it in GitHub Desktop.
Minecraft management script : sudo /etc/init.d/minecraft Legendary start => will start /home/minecraft/Legendary/minecraft.jar
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/minecraft | |
# version 2015-01-20 (YYYY-MM-DD) | |
### BEGIN INIT INFO | |
# Provides: minecraft | |
# 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 | |
# Description: Starts the Minecraft server | |
### END INIT INFO | |
# minecraft-init-script - An initscript to start Minecraft or CraftBukkit | |
# Copyright (C) 2011-2014 Super Jamie <[email protected]> | |
# https://github.com/superjamie/minecraft-init-script | |
# | |
# This program is free software: you can redistribute it and/or modify | |
# it under the terms of the GNU General Public License as published by | |
# the Free Software Foundation, either version 3 of the License, or | |
# (at your option) any later version. | |
# | |
# This program is distributed in the hope that it will be useful, | |
# but WITHOUT ANY WARRANTY; without even the implied warranty of | |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
# GNU General Public License for more details. | |
# | |
# You should have received a copy of the GNU General Public License | |
# along with this program. If not, see <http://www.gnu.org/licenses/>. | |
# Source the function library | |
## CentOS/Fedora | |
if [ -f /etc/rc.d/init.d/functions ] | |
then | |
. /etc/rc.d/init.d/functions | |
fi | |
## Ubuntu | |
if [ -f /lib/lsb/init-functions ] | |
then | |
. /lib/lsb/init-functions | |
fi | |
mc_list() { | |
echo " List of running servers" | |
ps -ef | grep -v grep | grep -i screen | awk '{print "* "$12}' | |
} | |
if [ $1 == "list" -a $# -eq 1 ] | |
then | |
mc_list | |
exit $? | |
fi | |
## Settings for minecraft-init-script | |
# Name of Screen session | |
SCRNAME=$1 | |
# Username of non-root user who will run the server | |
USERNAME="minecraft" | |
# Path of server binary and world | |
MCPATH="/home/minecraft/$SCRNAME" | |
# Number of CPU cores to thread across if using multithreaded garbage collection | |
CPU_COUNT="4" | |
# Where backups go | |
BACKUPPATH="/home/minecraft/backups/$SCRNAME" | |
# How many days worth of backups to keep (default 7) | |
BACKUPS_TO_KEEP="7" | |
if [ -f $MCPATH/server_config ] | |
then | |
. $MCPATH/server_config | |
fi | |
# Nice looking name of service for script to report back to users | |
SERVERNAME=${SERVERNAME:-$SCRNAME} | |
# Filename of server binary | |
SERVICE=${SERVICE:-"minecraft_server.jar"} | |
# Memory allocated (min/max) | |
MAXHEAP=${MAXHEAP:-1024} | |
MINHEAP=${MINHEAP:-1024} | |
## The Java command to run the server, uncomment one INVOCATION line only! | |
# Nothing special, just start the server with 1Gb RAM | |
# INVOCATION="java -Xms1024M -Xmx1024M -Djava.net.preferIPv4Stack=true -jar $SERVICE nogui" | |
# This is what I run my server with, tune your RAM usage accordingly | |
# Tested fastest GC - Default parallel new gen collector, plus parallel old gen collector | |
INVOCATION="java -Xms${MINHEAP}M -Xmx${MAXHEAP}M -Djava.net.preferIPv4Stack=true -XX:MaxPermSize=256M -XX:UseSSE=3 -XX:-DisableExplicitGC -XX:+UseParallelOldGC -XX:ParallelGCThreads=$CPU_COUNT -XX:+AggressiveOpts -jar $SERVICE nogui" | |
# I removed these "performance" commands as I don't see any difference with them | |
# -XX:+UseFastAccessorMethods -XX:+UseAdaptiveGCBoundary | |
# I've had a suggestion from a Java tuning engineer to use these | |
# -XX:+AggressiveOpts -XX:+UseCompressedStrings | |
# Add HugePage support if you have it configured on the OS | |
# You should be using HugePages if you give more than 4Gb on the Java invocation line | |
# -XX:+UseLargePages | |
## End of settings file | |
## --------------------------------------------------------- | |
### ### ### ### ### ### ### ### ### ### ### ### ### ### | |
### You shouldn't need to edit anything below here! ### | |
### ### ### ### ### ### ### ### ### ### ### ### ### ### | |
# Find the world name from the existing server file | |
WORLDNAME="$(grep -E 'level-name' $MCPATH/server.properties | sed -e s/.*level-name=//)" | |
# Find the port number from the existing server file | |
SERVERPORT="$(grep -E 'server-port' $MCPATH/server.properties | sed -e s/.*server-port=//)" | |
## Runs all commands as the non-root user | |
as_user() { | |
ME="$(whoami)" | |
if [ "$ME" == "$USERNAME" ] | |
then | |
bash -c "$1" | |
else | |
su - "$USERNAME" -c "$1" | |
fi | |
} | |
## Check if the server is running or not, and get the Java Process ID if it is | |
server_running() { | |
# Get the PID of the running Screen session: | |
# ps, remove grep, look for screen, look for the screen session $SCRNAME to differentiate between multiple servers, awk out everything but the pid | |
SCREENPID="" | |
SCREENPID="$(ps -ef | grep -v grep | grep -i screen | grep $SCRNAME | awk '{print $2}')" | |
# if the screen session with $SCRNAME is not running, then the server is not running, so we return 1 and exit the function | |
if [ -z "$SCREENPID" ] | |
then | |
return 1 | |
fi | |
# PID="$(ps -ef | grep -v grep | grep -i screen | grep $SCRNAME | awk '{print $2}' | xargs ps -f --ppid | grep $SERVICE | awk '{print $2}')" | |
# use the screen session pid to get the parent pid, which is the actual pid of the java process, check that process is actually $SERVICE | |
JAVAPID="$(ps -f --ppid $SCREENPID | grep $SERVICE | awk '{print $2}')" | |
# if the java process is not running, then the server is not running, so we return 1 to exit the function | |
if [ -z "$JAVAPID" ] | |
then | |
return 1 | |
fi | |
# if we haven't failed those two tests, we have a running server, so we return success | |
return 0 | |
} | |
## Start the server executable as a service | |
mc_start() { | |
if server_running | |
then | |
echo " * [ERROR] $SERVERNAME was already running (pid $JAVAPID). Not starting!" | |
return 1 | |
else | |
echo " * $SERVERNAME was not already running. Starting..." | |
echo " * Using map named \"$WORLDNAME\"..." | |
as_user "cd \"$MCPATH\" && screen -c /dev/null -dmS $SCRNAME $INVOCATION" | |
echo " * Checking $SERVERNAME is running..." | |
# start a counter and check for 15 seconds if the server is running or not | |
COUNT=0 | |
while [ $COUNT -lt 15 ]; do | |
if server_running | |
then | |
echo " * [OK] $SERVERNAME is now running (pid $JAVAPID)." | |
return 0 | |
else | |
let COUNT=COUNT+1 | |
sleep 1 | |
fi | |
done | |
# if we've reached here, the server hasn't started in 15 seconds | |
echo " * [ERROR] Could not start $SERVERNAME." | |
return 1 | |
fi | |
} | |
## Stop the executable | |
mc_stop() { | |
if server_running | |
then | |
echo " * $SERVERNAME is running (pid $JAVAPID). Commencing shutdown..." | |
echo " * Notifying users of shutdown..." | |
as_user "screen -p 0 -S $SCRNAME -X eval 'stuff \"say SERVER SHUTTING DOWN IN 10 SECONDS. Saving map...\"\015'" | |
echo -n " * Saving map named \"$WORLDNAME\" to disk" | |
as_user "screen -p 0 -S $SCRNAME -X eval 'stuff \"save-all\"\015'" | |
# start a counter and check for 20 seconds for when the world has saved | |
COUNT=0 | |
while [ $COUNT -lt 10 ]; do | |
echo -n "." | |
sleep 2 | |
if [ -f "$MCPATH/logs/latest.log" ] | |
then | |
if [[ "$(tail -n 20 $MCPATH/logs/latest.log | grep -E 'Save complete|Saved the world' | wc -l)" -gt 0 ]]; then | |
COUNT=99 | |
fi | |
fi | |
let COUNT=COUNT+1 | |
done | |
echo "" | |
echo -n " * Stopping $SERVERNAME" | |
as_user "screen -p 0 -S $SCRNAME -X eval 'stuff \"stop\"\015'" | |
# start a counter and check for 15 seconds if the server is running or not | |
COUNT=0 | |
while [ $COUNT -lt 15 ]; do | |
if server_running | |
then | |
echo -n "." | |
let COUNT=COUNT+1 | |
sleep 1 | |
else | |
echo "" | |
echo " * [OK] $SERVERNAME is shut down." | |
return 0 | |
fi | |
done | |
echo "" | |
# if we've reached here, the server hasn't stopped in 15 seconds | |
echo " * [ERROR] $SERVERNAME is still running (pid $JAVAPID). Could not be shutdown!" | |
return 1 | |
else | |
echo " * [OK] $SERVERNAME is shut down." | |
fi | |
} | |
## Set the server read-only, save the map, and have Linux sync filesystem buffers to disk | |
mc_saveoff() { | |
if server_running | |
then | |
echo " * $SERVERNAME is running. Commencing save..." | |
echo " * Notifying users of save..." | |
as_user "screen -p 0 -S $SCRNAME -X eval 'stuff \"say SERVER BACKUP STARTING. Server going read-only...\"\015'" | |
echo " * Setting server read-only..." | |
as_user "screen -p 0 -S $SCRNAME -X eval 'stuff \"save-off\"\015'" | |
echo " * Saving map named \"$WORLDNAME\" to disk..." | |
as_user "screen -p 0 -S $SCRNAME -X eval 'stuff \"save-all\"\015'" | |
sync | |
sleep 10 | |
echo " * [OK] Map saved." | |
else | |
echo " * [INFO] $SERVERNAME was not running. Not suspending saves." | |
fi | |
} | |
## Set the server read-write | |
mc_saveon() { | |
if server_running | |
then | |
echo " * $SERVERNAME is running. Re-enabling saves..." | |
as_user "screen -p 0 -S $SCRNAME -X eval 'stuff \"say SERVER BACKUP ENDED. Server going read-write...\"\015'" | |
as_user "screen -p 0 -S $SCRNAME -X eval 'stuff \"save-on\"\015'" | |
else | |
echo " * [INFO] $SERVERNAME was not running. Not resuming saves." | |
fi | |
} | |
## Checks for update, exits if update not required, updates if the server is not running | |
mc_update() { | |
echo " * Checking latest $SERVERNAME version against existing server..." | |
## Get MC last server file name and compare with the local one | |
if [ -f "$MCPATH/lastVersion.sh" ] | |
then | |
SERVER_URL=$(bash "$MCPATH/lastVersion.sh" get) | |
else | |
SERVER_URL=$(wget -q -O - "https://minecraft.net/download" | grep -o "\"https.*minecraft_server.[0-9]*.[0-9]*.[0-9]*.jar\"" | tr -d "\"") | |
fi | |
SERVER_FILE=$(basename "$SERVER_URL") | |
SERVER_FILE=${SERVER_FILE%%\?*} | |
if [ -f "$MCPATH/$SERVER_FILE" ] | |
then | |
echo " * You are already running the latest version of $SERVERNAME!" | |
return 0; # keep this exit in as we don't need to do anything | |
else | |
echo " * Downloading latest $SERVERNAME executable..." | |
as_user "cd \"$MCPATH\" && curl -# -L -o \"$SERVER_FILE\" \"$SERVER_URL\"" | |
fi | |
RESTART=server_running | |
if $RESTART | |
then | |
echo " * $SERVERNAME is running (pid $JAVAPID). Shutting down for update..." | |
mc_stop | |
fi | |
mc_backupmap | |
if server_running | |
then | |
echo " * [ERROR] $SERVICE is still running (pid $JAVAPID). Cannot update!" | |
return 1 | |
else | |
if [ -f "$MCPATH/lastVersion.sh" ] | |
then | |
as_user "cd \"$MCPATH\" && bash \"lastVersion.sh\" update $SERVER_FILE" | |
else | |
echo " * Removing the old executable" | |
as_user "reallink -f \"$MCPATH/$SERVICE\" | xargs rm" | |
echo " * Linking to the new version" | |
as_user "rm \"$MCPATH/$SERVICE\" && ln -s \"$MCPATH/$SERVER_FILE\" \"$MCPATH/$SERVICE\"" | |
fi | |
echo " * [OK] $SERVERNAME successfully updated." | |
fi | |
if $RESTART | |
then | |
echo " * Restarting $SERVERNAME" | |
mc_start | |
fi | |
} | |
## Check and see if a worldname was given to the backup command. Use the default world, or check the optional world exists and exit if it doesn't. | |
mc_checkbackup() { | |
if server_running | |
then | |
if [ -n "$1" ] | |
then | |
WORLDNAME="$1" | |
if [ -d "$MCPATH/$WORLDNAME" ] | |
then | |
echo " * Found world named \"$MCPATH/$WORLDNAME\"" | |
else | |
echo " * Could not find world named \"$MCPATH/$WORLDNAME\"" | |
return 1 | |
fi | |
fi | |
else | |
echo " * [ERROR] $SERVERNAME is not running." | |
return 1 | |
fi | |
} | |
## Backs up map by rsyncing current world to backup location, creates tar.gz with datestamp | |
mc_backupmap() { | |
echo " * Backing up $SERVERNAME map named \"$WORLDNAME\"..." | |
echo " * Syncing \"$MCPATH/$WORLDNAME\" to \"$BACKUPPATH/$WORLDNAME\"" | |
as_user "mkdir -p \"$BACKUPPATH/$WORLDNAME\"" | |
as_user "rsync --checksum --group --human-readable --copy-links --owner --perms --recursive --times --update --delete \"$MCPATH/$WORLDNAME\" \"$BACKUPPATH\"" | |
# if the nether exists, back it up | |
WORLDNETHER="$WORLDNAME""_nether" | |
if [ -d "$MCPATH/$WORLDNETHER" ] | |
then | |
echo " * Syncing \"$MCPATH/$WORLDNETHER\" to \"$BACKUPPATH/$WORLDNETHER\"" | |
as_user "rsync --checksum --group --human-readable --copy-links --owner --perms --recursive --times --update --delete \"$MCPATH/$WORLDNETHER\" \"$BACKUPPATH\"" | |
else | |
echo " * \"$MCPATH/$WORLDNETHER\" doesn't exist, skipping." | |
fi | |
# if the end exists, back it up | |
WORLDTHEEND="$WORLDNAME""_the_end" | |
if [ -d "$MCPATH/$WORLDTHEEND" ] | |
then | |
echo " * Syncing \"$MCPATH/$WORLDTHEEND\" to \"$BACKUPPATH/$WORLDTHEEND\"" | |
as_user "rsync --checksum --group --human-readable --copy-links --owner --perms --recursive --times --update --delete \"$MCPATH/$WORLDTHEEND\" \"$BACKUPPATH\"" | |
else | |
echo " * \"$MCPATH/$WORLDTHEEND\" doesn't exist, skipping." | |
fi | |
sleep 10 | |
echo " * Creating compressed backup..." | |
NOW="$(date +%Y-%m-%d.%H-%M-%S)" | |
# Create a compressed backup file and background it so we can get back to restarting the server | |
# You can tell when the compression is done as it makes an md5sum file of the backup | |
as_user "cd "$BACKUPPATH" && tar cfz \""$WORLDNAME"_backup_"$NOW".tar.gz\" \"$WORLDNAME\" && md5sum \""$WORLDNAME"_backup_"$NOW".tar.gz\" > \""$WORLDNAME"_backup_"$NOW".tar.gz.md5\" &" | |
echo " * [OK] Backed up map \"$WORLDNAME\"." | |
# if we backed up the nether, create a backup of that too | |
if [ -d "$BACKUPPATH/$WORLDNETHER" ] | |
then | |
as_user "cd "$BACKUPPATH" && tar cfz \""$WORLDNETHER"_backup_"$NOW".tar.gz\" \"$WORLDNETHER\" && md5sum \""$WORLDNETHER"_backup_"$NOW".tar.gz\" > \""$WORLDNETHER"_backup_"$NOW".tar.gz.md5\" &" | |
echo " * [OK] Backed up map \"$WORLDNETHER\"." | |
fi | |
# if we backed up the end, create a backup of that too | |
if [ -d "$BACKUPPATH/$WORLDTHEEND" ] | |
then | |
as_user "cd "$BACKUPPATH" && tar cfz \""$WORLDTHEEND"_backup_"$NOW".tar.gz\" \"$WORLDTHEEND\" && md5sum \""$WORLDTHEEND"_backup_"$NOW".tar.gz\" > \""$WORLDTHEEND"_backup_"$NOW".tar.gz.md5\" &" | |
echo " * [OK] Backed up map \"$WORLDTHEEND\"." | |
fi | |
# we can safely background the above commands and get back to restarting the server | |
} | |
## Removes any backups older than $BACKUPS_TO_KEEP days, designed to be called by daily cron job | |
mc_removeoldbackups() { | |
echo " * Removing backups older than $BACKUPS_TO_KEEP days..." | |
as_user "cd \"$BACKUPPATH\" && find . -name \"*backup*\" -type f -mtime +$BACKUPS_TO_KEEP | xargs rm -fv" | |
echo " * Removed old backups." | |
} | |
## Rotates logfile to server.1 through server.7, designed to be called by daily cron job | |
mc_logrotate() { | |
# Define a function to copy the old logfile to the new | |
mc_copylog() { | |
as_user "/bin/cp $logfile $MCPATH/$LOGNEW" | |
} | |
# Server logfiles in chronological order | |
LOGLIST="$(ls -r $MCPATH/server.log* | grep -v lck)" | |
# How many logs to keep | |
COUNT="6" | |
# Look at all the logfiles | |
for logfile in $LOGLIST; do | |
LOGTMP="$(basename $logfile | cut -d '.' -f 3)" | |
# If we're working with server.log then append .1 | |
if [ -z "$LOGTMP" ] | |
then | |
LOGNEW="server.log.1" | |
mc_copylog | |
# Otherwise, check if the file number is under $COUNT | |
elif [ "$LOGTMP" -gt "$COUNT" ] | |
then | |
# If so, delete it | |
as_user "rm -f $logfile" | |
else | |
# Otherwise, add one to the number | |
LOGBASE="$(basename $logfile | cut -d '.' -f 1-2)" | |
LOGNEW="$LOGBASE.$(($LOGTMP+1))" | |
mc_copylog | |
fi | |
done | |
# Blank the existing logfile to renew it | |
as_user "echo -n \"\" > $MCPATH/server.log" | |
} | |
## Check if server is running and display PID | |
mc_status() { | |
if server_running | |
then | |
echo " * $SERVERNAME status: Running (pid $JAVAPID)." | |
else | |
echo " * $SERVERNAME status: Not running." | |
return 1 | |
fi | |
} | |
## Display some extra environment informaton | |
mc_info() { | |
if server_running | |
then | |
RSS="$(ps --pid $JAVAPID --format rss | grep -v RSS)" | |
echo " - Java Path : $(readlink -f $(which java))" | |
echo " - Start Command : $INVOCATION" | |
echo " - Server Path : $MCPATH" | |
echo " - World Name : $WORLDNAME" | |
echo " - Process ID : $JAVAPID" | |
echo " - Screen Session : $SCRNAME" | |
echo " - Memory Usage : $((RSS/1024)) Mb ($RSS kb)" | |
# Check for HugePages support in kernel, display statistics if HugePages are available, otherwise skip | |
if [ -n "$(grep HugePages_Total /proc/meminfo | awk '{print $2}')" -a "$(grep HugePages_Total /proc/meminfo | awk '{print $2}')" ] | |
then | |
HP_SIZE="$(grep Hugepagesize /proc/meminfo | awk '{print $2}')" | |
HP_TOTAL="$(grep HugePages_Total /proc/meminfo | awk '{print $2}')" | |
HP_FREE="$(grep HugePages_Free /proc/meminfo | awk '{print $2}')" | |
HP_RSVD="$(grep HugePages_Rsvd /proc/meminfo | awk '{print $2}')" | |
HP_USED="$((HP_TOTAL-HP_FREE+HP_RSVD))" | |
TOTALMEM="$((RSS+(HP_USED*HP_SIZE)))" | |
echo " - HugePage Usage : $((HP_USED*(HP_SIZE/1024))) Mb ($HP_USED HugePages)" | |
echo " - Total Memory Usage : $((TOTALMEM/1024)) Mb ($TOTALMEM kb)" | |
fi | |
echo " - Active Connections : " | |
netstat --inet -tna | grep -E "Proto|$SERVERPORT" | |
else | |
echo " * $SERVERNAME is not running. Unable to give info." | |
return 1 | |
fi | |
} | |
## Connect to the active Screen session, disconnect with Ctrl+a then d | |
mc_console() { | |
if server_running | |
then | |
chmod g+rwx $(tty) | |
as_user "screen -S $SCRNAME -dr" | |
else | |
echo " * [ERROR] $SERVERNAME was not running! Unable to console." | |
return 1 | |
fi | |
} | |
## Broadcasts a message | |
mc_say() { | |
say_string="${@:-1}" | |
if [[ -z "$say_string" ]] | |
then | |
echo " * You need to enter your message. Usage; \"minecraft say message\"" | |
elif server_running | |
then | |
echo " * Broadcasting \"$say_string\"" | |
as_user "screen -p 0 -S $SCRNAME -X eval 'stuff \"say $say_string\"\015'" | |
else | |
echo " * [ERROR] $SERVERNAME was not running!" | |
return 1 | |
fi | |
} | |
usage() { | |
echo "Usage: $0 <minecraftDirectoryName> {start|stop|restart|backup (worldname)|save-on|save-off|update|status|info|console|say}" | |
return 1 | |
} | |
# Don't know why, the tty is alway write only for group | |
chmod g+rw $(tty) | |
## These are the parameters passed to the script | |
case "$2" in | |
start) | |
mc_start | |
;; | |
stop) | |
mc_stop | |
;; | |
restart) | |
mc_stop && sleep 1 && mc_start | |
;; | |
update) | |
mc_update | |
;; | |
backup) | |
mc_checkbackup "$3" && mc_saveoff && mc_backupmap && mc_saveon | |
;; | |
status) | |
mc_status | |
;; | |
info) | |
mc_info | |
;; | |
console) | |
mc_console | |
;; | |
# These are intended for cron usage, not regular users. | |
removeoldbackups) | |
mc_removeoldbackups | |
;; | |
logrotate) | |
mc_logrotate | |
;; | |
save-off) | |
mc_saveoff | |
;; | |
save-on) | |
mc_saveon | |
;; | |
say) | |
mc_say "$3" | |
;; | |
*) | |
usage | |
;; | |
esac | |
exit $? |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment