#!/bin/bash ### BEGIN INIT INFO # Provides: msm # 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: # Description: ### END INIT INFO # See http://www.debian.org/doc/debian-policy/ch-opersys.html#s-sysvinit for # more information on debain init.d scripts, which may help you understand # this script. source "msm.config" WHITELIST="white-list.txt" BANNED_IPS="banned-ips.txt" BANNED_PLAYERS="banned-players.txt" JARGROUP_TARGET="target.txt" JARGROUP_DOWNLOAD_DIR="downloads" ### Utility Functions # Esecutes the command "$1" as SERVER_USER as_user() { if [ $(whoami) == $SERVER_USER ] ; then bash -c "$1" else su - $SERVER_USER -s /bin/bash -c "$1" fi } as_user_stderr() { as_user "$1" 2>&1 1>/dev/null } echoerr() { echo "$@" 1>&2 } is_valid_name() { local valid="^[a-zA-Z0-9\_\-]+$" local invalid="^server|jargroup|start|stop|restart$" if [[ "$1" =~ $valid ]]; then if [[ "$1" =~ $invalid ]]; then echoerr "Invalid name \"$1\": A name may not be any of the following reserved worlds \"start\", \"stop\", \"restart\", \"server\" or \"jargroup\"." exit 1 else exit 0 fi else echoerr "Invalid name \"$1\": A name may only contain letters, numbers, dashes and unscores." exit 1 fi } get_latest_file() { local best_time=0 local best_file="" for file in "$1/*"; do # Remove the path, leaving just the file name local name=$(basename "$file") # Get the time in seconds since 1970 from file name local seconds=$(date -d "${name:0:10}" "+%s" 2> /dev/null) # If that is newer than the current best, override variables if [[ $seconds > $best_time ]]; then best_time=$seconds best_file=$file fi done echo $best_file } ### Manager Functions manager_create_server() { if is_valid_name "$1"; then if [[ -e "$SERVER_STORAGE_PATH/$1" ]]; then echo "A server with that name already exists." exit 1 else printf "Creating server directory... " as_user "mkdir -p '$SERVER_STORAGE_PATH/$1'" as_user "touch '$SERVER_STORAGE_PATH/$1/$WHITELIST'" as_user "touch '$SERVER_STORAGE_PATH/$1/$BANNED_IPS'" as_user "touch '$SERVER_STORAGE_PATH/$1/$BANNED_PLAYERS'" echo "Done." fi else exit 1 fi } manager_delete_server() { if is_valid_name "$1" && [[ -e "$SERVER_STORAGE_PATH/$1" ]]; then printf "Are you sure you want to delete this server and its worlds (note: backups are preserved) [y/N]: " read answer if [[ "$answer" =~ ^y|Y|yes$ ]]; then # TODO: stop the server if running first as_user "rm -r '$SERVER_STORAGE_PATH/$1'" echo "Server deleted." else echo "Server was NOT deleted." fi else if [[ ! -e "$SERVER_STORAGE_PATH/$1" ]]; then echo "There is no server with the name \"$1\"." fi exit 1 fi } manager_rename_server() { if is_valid_name "$1" && [[ -e "$SERVER_STORAGE_PATH/$1" ]]; then # If the server name is valid, # and there is no other server with the name $1 # TODO: Check that the server is not running first, exit if it is if is_valid_name "$2"; then if [[ -e "$SERVER_STORAGE_PATH/$2" ]]; then echo "Could not be renamed, there is already a server with the name \"$2\"." exit 1 else as_user "mv '$SERVER_STORAGE_PATH/$1' '$SERVER_STORAGE_PATH/$2'" echo "Renamed server \"$1\" to \"$2\"." fi else echo "Invalid name: The new server name may only contain letters, numbers, a dash or an underscore." fi else if [[ -e "$SERVER_STORAGE_PATH/$1" ]]; then echo "There is no server with the name \"$1\"." fi exit 1 fi } jargroup_create() { if is_valid_name "$1"; then if [[ -e "$JAR_STORAGE_PATH/$1" ]]; then echo "A jar group with that name already exists." exit 2 else printf "Creating jar group... " local error=$(as_user_stderr "mkdir -p '$JAR_STORAGE_PATH/$1'") if [[ "$error" != "" ]]; then echo "Failed." echo "Reason: $error" exit 1 fi error=$(as_user "echo \"$2\" > '$JAR_STORAGE_PATH/$1/$JARGROUP_TARGET'") if [[ "$error" != "" ]]; then echo "Failed." echo "Reason: $error" exit 1 fi echo "Done." # Download the latest version now jargroup_getlatest "$1" fi else exit 1 fi } jargroup_getlatest() { if is_valid_name "$1" && [[ -e "$JAR_STORAGE_PATH/$1" ]]; then if [[ -e "$JAR_STORAGE_PATH/$1/$JARGROUP_TARGET" ]]; then printf "Downloading latest version... " # Try and make local error=$(as_user_stderr "mkdir -p '$JAR_STORAGE_PATH/$1/$JARGROUP_DOWNLOAD_DIR'") if [[ $error != "" ]]; then echo "Failed." echo "Reason: $error" exit 1 fi as_user "wget --quiet --input-file='$JAR_STORAGE_PATH/$1/$JARGROUP_TARGET' --directory-prefix='$JAR_STORAGE_PATH/$1/$JARGROUP_DOWNLOAD_DIR' --no-check-certificate" echo "Done." local num_files=$(as_user "ls -1 '$JAR_STORAGE_PATH/$1/$JARGROUP_DOWNLOAD_DIR' | wc -l") if [[ $num_files == 1 ]]; then # There was 1 file downloaded local file_name=$(ls -1 "$JAR_STORAGE_PATH/$1/$JARGROUP_DOWNLOAD_DIR") local new_name="$(date +%F)-$file_name" local most_recent_jar=$(get_latest_file "$JAR_STORAGE_PATH/$1") if [[ ! -e "$most_recent_jar" ]] || ! diff "$most_recent_jar" "$JAR_STORAGE_PATH/$1/$JARGROUP_DOWNLOAD_DIR/$file_name"; then # There is not a previous version to do a comparison against, or # The previous version is different: # Add it to the group [[ -e "$most_recent_jar" ]] local was_previous=$? as_user "mv '$JAR_STORAGE_PATH/$1/$JARGROUP_DOWNLOAD_DIR/$file_name' '$JAR_STORAGE_PATH/$1/$new_name'" if [[ $was_previous == 0 ]]; then echo "Downloaded version was different to previous latest. Saved as \"$1/$new_name\"." else echo "Saved as \"$JAR_STORAGE_PATH/$1/$new_name\"." fi else echo "Existing version \"$JAR_STORAGE_PATH/$1/$new_name\" was already up to date." fi elif [[ $num_files == 0 ]]; then # No file was downloaded echo "Failed. No files were downloaded." else # Multiple files were echo "Error. URL downloads multiple files." fi # Clean up the temp download folder as_user "rm -r '$JAR_STORAGE_PATH/$1/$JARGROUP_DOWNLOAD_DIR'" else echo "Target URL not found." fi else if [[ -e "$JAR_STORAGE_PATH/$1" ]]; then echo "There is no jar group with the name \"$1\"." fi fi } jargroup_delete() { if is_valid_name "$1" && [[ -e "$JAR_STORAGE_PATH/$1" ]]; then printf "Are you sure you want to delete this jar group [y/N]: " read answer if [[ "$answer" =~ ^y|Y|yes$ ]]; then as_user "rm -r $JAR_STORAGE_PATH/$1" echo "Jar group deleted." else echo "Jar group was NOT deleted." fi else if [[ -e "$JAR_STORAGE_PATH/$1" ]]; then echo "There is no jar group with the name \"$1\"." fi fi } ### Script Options case "$1" in start) # Required start option, for debian init.d scripts ;; stop) # Required stop option, for debian init.d scripts ;; restart) # Required restart option, for debian init.d scripts ;; server) case "$2" in list) ;; create) if [ -z "$3" ]; then # If a server name is not provided echo "Invalid command." else # Create a new server manager_create_server "$3" fi ;; delete) if [ -z "$3" ]; then # If a server name is not provided echo "Invalid command." else # Delete an existing server, with confirmation manager_delete_server "$3" fi ;; rename) if [ -z "$3" ] || [ -z "$4" ]; then # If a server name is not provided echo "Invalid command." else # Rename an existing server manager_rename_server "$3" "$4" fi ;; *) echo "Invalid command." ;; esac ;; jargroup) case "$2" in list) ;; create) jargroup_create "$4" "$5" ;; delete) jargroup_delete "$4" ;; rename) ;; changetarget) jargroup_settarget "$4" "$5" ;; getlatest) jargroup_getlatest "$4" ;; *) echo "Invalid command." ;; esac ;; help) echo -e "Usage: $0 command:" echo -e echo -e "--Setup Commands------------------------------------------------" echo -e " server list \t\t\t\t\tList servers" echo -e " server create <name> \t\t\t\tCreates a new Minecraft server" echo -e " server delete <name> \t\t\t\tDeletes an existing Minecraft server" echo -e " server rename <name> <new-name> \t\tRenames an existing Minecraft server" echo -e echo -e "--Server Mangement Commands-------------------------------------" echo -e " <server> start \t\t\t\tStarts a server" echo -e " <server> stop [now] \t\t\t\tStops a server after warning players, or right now" echo -e " <server> restart [now] \t\t\tRestarts a server after warning players, or right now" echo -e " <server> worlds \t\t\t\tLists the worlds a server has" echo -e " <server> worlds load \t\t\t\tCreates links to wolrds in storage for a server" echo -e " <server> worlds todisk \t\t\tSynchronises any \"in RAM\" worlds to disk a server has" echo -e " <server> worlds toram \t\t\tSynchronises any RAM enabled worlds to RAM a server has" echo -e " <server> worlds backup \t\t\tMakes a backup of all worlds a server has" echo -e " <server> worlds ram <world> \t\t\tToggles a world's \"in RAM\" status" echo -e " <server> backup \t\t\t\tMakes a backup of an entire server directory" echo -e " <server> logroll \t\t\t\tMove a server log to a gziped archive, to reduce lag" echo -e " <server> connected \t\t\t\tList a servers connected players" echo -e " <server> status \t\t\t\tShow the running/stopped status of a server" echo -e echo -e "--Server Pass Through Commands----------------------------------" echo -e " <server> wl on|off <player> \t\t\tEnabled/disable server whitelist check" echo -e " <server> wl add|remove <player> \t\tAdd/remove a player to/from a server's whitelist" echo -e " <server> wl list \t\t\t\tList the players whitelisted for a server" echo -e " <server> bl player add|remove <player> \tBan/pardon a player from/for a server" echo -e " <server> bl ip add|remove <ip address> \tBan/pardon an IP address from/for a server" echo -e " <server> bl list \t\t\t\tLists the banned players and IP address for a server" echo -e " <server> op add|remove <player> \t\tAdd/remove operator status for a player on a server" echo -e " <server> gm survival|creative <player> \tChange the game mode for a player on a server" echo -e " <server> kick <player> \t\t\tForcibly disconnect a player from a server" echo -e " <server> say <message> \t\t\tBroadcast a (pink) message to all players on a server" echo -e " <server> time set|add <number> \t\tSet/increment time on a server (0-24000)" echo -e " <server> toggledownfall \t\t\tToggles rain and snow on a server" echo -e " <server> save on|off \t\t\t\tEnable/disable writing world changes to file" echo -e " <server> save all \t\t\t\tForce the writing of all non-saved world changes to file" echo -e " <server> cmd <command> \t\t\tSend a command string to the server and return" echo -e " <server> cmdlog <command> \t\t\tSame as 'cmd' but shows log output afterwards (Ctrl+C to exit)" echo -e echo -e "--Jar Commands--------------------------------------------------" echo -e " jargroup list \t\t\t\tList the stored jar files." echo -e " jargroup create <name> <download-url> \tCreate a new jar group, with a URL for new downloads" echo -e " jargroup delete <name> \t\t\tDelete a jar group" echo -e " jargroup rename <name> <new-name> \t\tRename a jar group" echo -e " jargroup changeurl <name> <download-url> \tChange the download URL for a jar group" echo -e " jargroup getlatest <name> \t\t\tDownload the latest jar file for a jar group" echo -e echo -e "--Global Commands-----------------------------------------------" echo -e " start \t\t\t\t\tStarts all active servers" echo -e " stop \t\t\t\t\t\tStops all running servers" echo -e " restart \t\t\t\t\tRestarts all active servers" ;; "") echo "No such command see: $0 help" ;; *) # TODO: Check first if $1 is an existing server name if is_valid_name "$1" && [[ -e "$SERVER_STORAGE_PATH/$1" ]]; then case "$2" in start) ;; stop) ;; restart) ;; worlds) ;; backup) ;; logroll) ;; connected) ;; status) ;; whitelist|wl) ;; blacklist|bl) ;; operator|op) ;; gamemode|gm) ;; kick) ;; say) ;; "time") ;; toggledownfall) ;; save) ;; cmd) ;; cmdlog) ;; esac else echo "No server with that name." fi ;; esac exit 0