2012-05-20 10:52:19 +00:00
#!/bin/bash
### BEGIN INIT INFO
2012-05-21 12:22:28 +00:00
# Provides: msm
2012-05-20 10:52:19 +00:00
# Required-Start: $local_fs $remote_fs
# Required-Stop: $local_fs $remote_fs
2012-05-21 12:22:28 +00:00
# Should-Start: $network
# Should-Stop: $network
2012-05-20 10:52:19 +00:00
# Default-Start: 2 3 4 5
2012-05-21 12:22:28 +00:00
# Default-Stop: 0 1 6
# Short-Description:
# Description:
2012-05-20 10:52:19 +00:00
### END INIT INFO
2012-05-20 13:02:40 +00:00
# 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.
2012-05-22 14:50:54 +00:00
### Source the configuration file
source "/etc/msm.conf"
2012-05-20 11:48:13 +00:00
2012-05-22 14:50:54 +00:00
### Config variables the user should need/want to change
2012-05-20 12:40:46 +00:00
2012-05-22 14:50:54 +00:00
# Minecraft's whitelist file
declare -r WHITELIST="white-list.txt"
# Minecraft's banned ips file
declare -r BANNED_IPS="banned-ips.txt"
# Minecraft's banned players file
declare -r BANNED_PLAYERS="banned-players.txt"
# Jar group file which contains the download target URL
declare -r JARGROUP_TARGET="target.txt"
# Jar group directory name to download new jars to, is deleted afterwards
declare -r JARGROUP_DOWNLOAD_DIR="downloads"
2012-05-20 13:02:40 +00:00
2012-05-24 00:09:05 +00:00
# The flag which indicates the world should be put in RAM
declare -r WORLD_FLAG_INRAM="inram"
# The flag which indicates the server is active
declare -r SERVER_FLAG_ACTIVE="active"
2012-05-20 10:52:19 +00:00
2012-05-24 02:57:30 +00:00
### Script State Variables
# "true" whilst the script is counting down a delay to stop the server
declare STOP_COUNTDOWN
# "true" whilst the script is counting down a delay to restart the server
declare RESTART_COUNTDOWN
2012-05-20 12:40:46 +00:00
### Utility Functions
2012-05-24 00:09:05 +00:00
# Executes the command "$2" as user "$1"
# $1: The user to execute the command as
# $2: The command to execute
2012-05-20 12:40:46 +00:00
as_user() {
2012-05-24 00:09:05 +00:00
if [ $(whoami) == $1 ]; then
bash -c "$2"
2012-05-20 12:40:46 +00:00
else
2012-05-24 00:09:05 +00:00
su - $1 -s /bin/bash -c "$2"
2012-05-20 12:40:46 +00:00
fi
}
2012-05-22 14:50:54 +00:00
# Executes the command "$1" as SERVER_USER but returns stderr instead
2012-05-21 12:22:28 +00:00
as_user_stderr() {
2012-05-21 20:49:58 +00:00
as_user "$1" > /dev/null 2>&1
2012-05-21 12:22:28 +00:00
}
2012-05-22 14:50:54 +00:00
# Echo to stderr
2012-05-21 16:59:04 +00:00
echoerr() {
echo "$@" 1>&2
}
2012-05-22 14:50:54 +00:00
# Determines whether "$1" is a valid name for a server or jar group directory
# It must only contain upper or lower case letters, digits, dashes or
# underscores.
# It must also not be one of a list of reserved names.
2012-05-21 12:22:28 +00:00
is_valid_name() {
2012-05-21 16:59:04 +00:00
local valid="^[a-zA-Z0-9\_\-]+$"
2012-05-22 16:26:20 +00:00
local invalid="^server|jargroup|start|stop|restart|all$"
2012-05-21 12:22:28 +00:00
2012-05-21 16:59:04 +00:00
if [[ "$1" =~ $valid ]]; then
if [[ "$1" =~ $invalid ]]; then
2012-05-22 16:26:20 +00:00
echoerr "Invalid name \"$1\": A name may not be any of the following reserved worlds \"start\", \"stop\", \"restart\", \"server\", \"jargroup\" or \"all\"."
2012-05-21 20:49:58 +00:00
return 1
2012-05-21 16:59:04 +00:00
else
2012-05-21 20:49:58 +00:00
return 0
2012-05-21 16:59:04 +00:00
fi
2012-05-21 12:22:28 +00:00
else
2012-05-21 16:59:04 +00:00
echoerr "Invalid name \"$1\": A name may only contain letters, numbers, dashes and unscores."
2012-05-21 20:49:58 +00:00
return 1
2012-05-21 12:22:28 +00:00
fi
}
2012-05-22 14:50:54 +00:00
# Gets the latest jar from a jar group, based upon the date and time encoded
# in the file name.
2012-05-21 12:22:28 +00:00
get_latest_file() {
local best_time=0
local best_file=""
2012-05-21 16:59:04 +00:00
for file in "$1/*"; do
2012-05-21 12:22:28 +00:00
# Remove the path, leaving just the file name
2012-05-21 16:59:04 +00:00
local name=$(basename "$file")
2012-05-21 12:22:28 +00:00
2012-05-21 20:49:58 +00:00
local date_time=$(echo $name | awk -F '-' '{print $1 "-" $2 "-" $3 " " $4 ":" $5 ":" $6}')
2012-05-21 12:22:28 +00:00
# Get the time in seconds since 1970 from file name
2012-05-21 20:49:58 +00:00
local seconds=$(date -d "$date_time" "+%s" 2> /dev/null)
2012-05-21 12:22:28 +00:00
# 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
}
2012-05-24 00:09:05 +00:00
# Returns the current time as a UNIX timestamp (in seconds since 1970)
now() {
date +%s
}
2012-05-21 12:22:28 +00:00
2012-05-24 00:09:05 +00:00
### Log Utility Functions
# Gets the UNIX timestamp for a server log line
# $1: A server log line
# returns: Time in seconds since 1970-01-01 00:00:00 UTC
log_line_get_time() {
time_string=$(echo $1 | awk '{print $1 " " $2}')
date -d "$time_string" "+%s" 2> /dev/null
2012-05-21 17:34:12 +00:00
}
2012-05-24 00:09:05 +00:00
### World Utility Functions
# Moves a world to RAM
# $1: the ID of the world to move
world_to_ram() {
if $RAMDISK_STORAGE_PATH; then
as_user ${server_user_name[${world_server_id[$1]}]} "mkdir -p ${world_ramdisk_path[$1]} && rsync -rt --exclude '$WORLD_FLAG_INRAM' ${world_path[$1]}/ ${world_ramdisk_path[$1]}"
2012-05-21 12:22:28 +00:00
fi
2012-05-20 13:02:40 +00:00
}
2012-05-24 01:29:20 +00:00
# Moves a world in RAM to disk
# $1: the ID of the world to move
world_to_disk() {
as_user ${server_user_name[${world_server_id[$1]}]} "rsync -rt --exclude '$WORLD_FLAG_INRAM' ${world_ramdisk_path[$1]}/ ${world_path[$1]}"
}
2012-05-21 14:59:23 +00:00
2012-05-24 00:09:05 +00:00
### Server Utility Functions
# Returns the ID for a server.
# An ID is given to a server when loaded into memory, and can be used to lookup
# config information for that server
server_get_id() {
local i=0
while [[ $i < $num_servers ]]; do
if [[ "${server_name[$i]}" == "$1" ]]; then
echo "$i"
return 0
2012-05-21 16:59:04 +00:00
fi
2012-05-24 00:09:05 +00:00
i=$(( $i + 1 ))
done
return 1
}
# Returns 0 if the server $1 is running and 1 if not
# $1: The name of server
server_is_running() {
local id=$(server_get_id "$1")
2012-05-24 01:29:20 +00:00
if ps ax | grep -v grep | grep "${server_screen_name[$1]} ${server_invocation[$1]}" > /dev/null
2012-05-24 00:09:05 +00:00
then
return 0
2012-05-21 20:49:58 +00:00
else
return 1
2012-05-21 15:13:24 +00:00
fi
}
2012-05-24 00:09:05 +00:00
# Creates symbolic links in the server directory (SERVER_STORAGE_PATH) for each
# of the Minecraft worlds located in the world storage directory.
# $1: The id of the server for which links should be ensured
server_ensure_links() {
local i=${server_world_offset[$1]}
local max=$(( $i + ${server_num_worlds[$1]} ))
while [[ $i < $max ]]; do
# -L checks for the path being a link rather than a file
# ! -a, since it is within double square brackets means: the negation of
# the existence of the file. In other words: true if does not exist
if [[ -L "${world_link[$i]}" || ! -a "${world_link[$i]}" ]]; then
# If there is a symbolic link in the server direcotry to this world,
# or there is not a directory in the server directory containing this world.
# Get the original file path the symbolic link is pointing to
# If there is no link, link_target will contain nothing
link_target=$(readlink "${world_link[$i]}")
if ${world_inram[$i]}; then
# If this world is marked as loaded into RAM
2012-05-24 03:22:59 +00:00
if [ "${link_target}" != "${world_ramdisk_path[$i]}" ]; then
2012-05-24 00:09:05 +00:00
# If the symbolic link does not point to the RAM version of the world
# Remove the symbolic link if it exists
2012-05-24 03:22:59 +00:00
as_user ${server_user_name[$1]} "rm -f \"${world_link[$i]}\""
2012-05-24 00:09:05 +00:00
# Create a new symbolic link pointing to the RAM version of the world
2012-05-24 03:22:59 +00:00
as_user ${server_user_name[$1]} "ln -s \"${world_ramdisk_path[$i]}\" \"${world_link[$i]}\""
2012-05-21 20:49:58 +00:00
fi
2012-05-21 15:13:24 +00:00
else
2012-05-24 00:09:05 +00:00
# Otherwise the world is not loaded into RAM, and is just on disk
2012-05-24 03:22:59 +00:00
if [ "${link_target}" != "${world_path[$i]}" ]; then
2012-05-24 00:09:05 +00:00
# If the symbolic link does not point to the disk version of the world
# Remove the symbolic link if it exists
2012-05-24 03:22:59 +00:00
as_user ${server_user_name[$1]} "rm -f \"${world_link[$i]}\""
2012-05-24 00:09:05 +00:00
# Create a new symbolic link pointing to the disk version of the world
2012-05-24 03:22:59 +00:00
as_user ${server_user_name[$1]} "ln -s \"${world_path[$i]}\" \"${world_link[$i]}\""
2012-05-24 00:09:05 +00:00
fi
2012-05-21 15:13:24 +00:00
fi
else
2012-05-24 00:09:05 +00:00
# There was no symbolic link, and there was a directory, meaning a world
# directory has been placed in SERVER_PATH, this is not allowed:
echoerr "Could not process ${world_name[$i]}. Please move all worlds to ${server_world_storage[$1]}."
2012-05-21 20:49:58 +00:00
return 1
2012-05-21 16:59:04 +00:00
fi
2012-05-24 00:09:05 +00:00
i=$(( $i + 1 ))
done
}
# Moves a servers worlds into RAM
# $1: The ID of the server
server_worlds_to_ram() {
# Only proceed if there is a ramdisk path set in config
if $RAMDISK_STORAGE_PATH; then
2012-05-24 02:12:21 +00:00
echo -n "Synchronising flagged worlds on disk to RAM... "
2012-05-24 00:09:05 +00:00
local i=${server_world_offset[$1]}
local max=$(( $i + ${server_num_worlds[$1]} ))
# For each of the servers worlds:
while [[ $i < $max ]]; do
if ${world_inram[$i]} && [ -L ${world_link[$i]} ]; then
world_to_ram $i
fi
i=$(( $i + 1 ))
done
2012-05-24 02:12:21 +00:00
echo "Done."
2012-05-21 14:59:23 +00:00
fi
}
2012-05-24 01:29:20 +00:00
# Moves a servers "in RAM" worlds back to disk
# $1: The ID of the server
server_worlds_to_disk() {
if $RAMDISK_STORAGE_PATH; then
2012-05-24 02:12:21 +00:00
echo -n "Synchronising worlds in RAM to disk... "
2012-05-24 01:29:20 +00:00
local i=${server_world_offset[$1]}
local max=$(( $i + ${server_num_worlds[$1]} ))
# For each of the servers worlds:
while [[ $i < $max ]]; do
if [ -e ${world_ramdisk_path[$i]} ]; then
world_to_ram $i
fi
done
echo "Done."
fi
}
2012-05-24 00:09:05 +00:00
# Watches the a server's log
# $1: The ID for the server
# $2: The line in the log to wait for
# $3: A UNIX timestamp (seconds since 1970) which the $2 line must be after
# returns: When the line is found
server_log_get_line() {
# Make sure there is a server log to check
as_user ${server_user_name[$1]} "touch ${server_log[$1]}"
regex="^[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2} \[.*\] ${2}"
while read line
do
line_time=$(log_line_get_time "$line")
# If the entry is old enough and matches the regular expression
if [[ $line_time -ge $3 && $line =~ $regex ]]
then
echo $line
break
fi
done < <(as_user ${server_user_name[$1]} "tail --follow --lines=100 --sleep-interval=0.1 ${server_log[$1]}")
}
# The same as server_log_get_line, but does not print the line to stdout
# when found.
server_log_wait_for_line() {
server_log_get_line $1 $2 $3 > /dev/null
}
2012-05-24 01:29:20 +00:00
# Sends as string to a server for execution
# $1: The ID of the server
# $2: The line of text to enter into the server console
server_eval() {
as_user ${server_user_name[$1]} "screen -p 0 -S ${server_screen_name[$1]} -X eval 'stuff \"$2\"\015'"
}
# The same as server_eval, but also waits for a log entry before returning
# $1: The ID of the server
# $2: A line of text to enter into the server console
# $3: The line of text in the log to wait for
# stdout: The full entry found in the logs
server_eval_and_get_line() {
time_now=$(now)
server_eval $1 "$2"
server_log_get_line $1 "$3" "$time_now"
}
# The same as server_eval_and_get_line, but does not print anything to stdout
server_eval_and_wait() {
server_eval_and_get_line $1 "$2" "$3" > /dev/null
}
# Gets the process ID for a server if running, otherwise it outputs nothing
# $1: The ID of the server
server_pid() {
ps ax | grep -v grep | grep "${server_screen_name[$1]} ${server_invocation[$1]}" | awk '{print $1}'
}
# Waits for a server to stop by polling 10 times a second
# This approach is fairyl intensive, so only use when you are expecting the
# server to stop soon
# $1: The ID of the server to wait for
server_wait_for_stop() {
local pid=$(server_pid $1)
while ps -p $pid > /dev/null; do
sleep 0.1
done
}
2012-05-24 01:44:10 +00:00
# Sets a server's active/inactive state
# $1: The ID of the server
# $2: A string containing "active" or "inactive"
server_set_active() {
case "$2" in
active)
as_user ${server_user_name[$1]} "touch ${server_flag_active[$1]}"
server_active[$1]="true"
;;
inactive)
as_user ${server_user_name[$1]} "rm -f ${server_flag_active[$1]}"
server_active[$1]="false"
;;
*)
return 1
;;
esac
}
2012-05-21 17:34:12 +00:00
### Jar Group Functions
2012-05-22 14:50:54 +00:00
# Lists the jar files grouped by jar groups.
2012-05-21 20:49:58 +00:00
jargroup_list() {
for group in $(ls -1 "$JAR_STORAGE_PATH"); do
echo "${group}:"
for jar in $(ls -1r $JAR_STORAGE_PATH/$group); do
if [[ "$jar" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}- ]]; then
echo " $jar"
fi
done
done
}
2012-05-22 14:50:54 +00:00
# Creates a new jargroup
# $1: The name for the jargroup
2012-05-21 16:59:04 +00:00
jargroup_create() {
if is_valid_name "$1"; then
2012-05-21 20:49:58 +00:00
if [[ ! -e "$JAR_STORAGE_PATH/$1" ]]; then
2012-05-21 13:48:18 +00:00
printf "Creating jar group... "
2012-05-21 12:22:28 +00:00
2012-05-21 16:59:04 +00:00
local error=$(as_user_stderr "mkdir -p '$JAR_STORAGE_PATH/$1'")
if [[ "$error" != "" ]]; then
2012-05-21 12:22:28 +00:00
echo "Failed."
echo "Reason: $error"
2012-05-21 20:49:58 +00:00
return 1
2012-05-21 12:22:28 +00:00
fi
2012-05-21 16:59:04 +00:00
error=$(as_user "echo \"$2\" > '$JAR_STORAGE_PATH/$1/$JARGROUP_TARGET'")
if [[ "$error" != "" ]]; then
2012-05-21 12:22:28 +00:00
echo "Failed."
echo "Reason: $error"
2012-05-21 20:49:58 +00:00
return 1
2012-05-21 12:22:28 +00:00
fi
echo "Done."
2012-05-21 13:28:37 +00:00
# Download the latest version now
2012-05-21 16:59:04 +00:00
jargroup_getlatest "$1"
2012-05-21 20:49:58 +00:00
else
echo "A jar group with that name already exists."
return 1
2012-05-21 12:22:28 +00:00
fi
else
2012-05-21 20:49:58 +00:00
return 1
2012-05-21 12:22:28 +00:00
fi
}
2012-05-22 14:50:54 +00:00
# Downloads the latest version for a jargroup, using the target URL for that
# group. Saves the download with the date and time encoded in the start of the
# file name, in the jar group directory in question. Removes the file if there
# is no difference between it and the current version.
# $1: The jargroup name to download the latest version for
2012-05-21 16:59:04 +00:00
jargroup_getlatest() {
2012-05-21 20:49:58 +00:00
if is_valid_name "$1"; then
if [[ -e "$JAR_STORAGE_PATH/$1" ]]; then
if [[ -e "$JAR_STORAGE_PATH/$1/$JARGROUP_TARGET" ]]; then
printf "Downloading latest version... "
2012-05-21 13:39:59 +00:00
2012-05-21 20:49:58 +00:00
# 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"
return 1
fi
2012-05-21 13:39:59 +00:00
2012-05-21 20:49:58 +00:00
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."
2012-05-21 13:39:59 +00:00
2012-05-21 20:49:58 +00:00
local num_files=$(as_user "ls -1 '$JAR_STORAGE_PATH/$1/$JARGROUP_DOWNLOAD_DIR' | wc -l")
2012-05-21 13:39:59 +00:00
2012-05-21 20:49:58 +00:00
if [[ $num_files == 1 ]]; then
# There was 1 file downloaded
2012-05-21 12:22:28 +00:00
2012-05-21 20:49:58 +00:00
local file_name=$(ls -1 "$JAR_STORAGE_PATH/$1/$JARGROUP_DOWNLOAD_DIR")
local new_name="$(date +%F-%H-%M-%S)-$file_name"
local most_recent_jar=$(get_latest_file "$JAR_STORAGE_PATH/$1")
2012-05-21 12:22:28 +00:00
2012-05-21 20:49:58 +00:00
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
2012-05-21 13:39:59 +00:00
2012-05-21 20:49:58 +00:00
[[ -e "$most_recent_jar" ]]
local was_previous=$?
2012-05-21 12:22:28 +00:00
2012-05-21 20:49:58 +00:00
as_user "mv '$JAR_STORAGE_PATH/$1/$JARGROUP_DOWNLOAD_DIR/$file_name' '$JAR_STORAGE_PATH/$1/$new_name'"
2012-05-21 12:22:28 +00:00
2012-05-21 20:49:58 +00:00
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
2012-05-21 12:22:28 +00:00
else
2012-05-21 20:49:58 +00:00
echo "Existing version \"$JAR_STORAGE_PATH/$1/$new_name\" was already up to date."
2012-05-21 12:22:28 +00:00
fi
2012-05-21 20:49:58 +00:00
elif [[ $num_files == 0 ]]; then
# No file was downloaded
echo "Failed. No files were downloaded."
2012-05-21 12:22:28 +00:00
else
2012-05-21 20:49:58 +00:00
# Multiple files were
echo "Error. URL downloads multiple files."
2012-05-21 12:22:28 +00:00
fi
2012-05-21 20:49:58 +00:00
# Clean up the temp download folder
as_user "rm -fr '$JAR_STORAGE_PATH/$1/$JARGROUP_DOWNLOAD_DIR'"
2012-05-21 12:22:28 +00:00
else
2012-05-21 20:49:58 +00:00
echo "Target URL not found, use $0 jargroup seturl <download-url>"
return 1
2012-05-21 12:22:28 +00:00
fi
2012-05-21 13:39:59 +00:00
else
2012-05-21 16:59:04 +00:00
echo "There is no jar group with the name \"$1\"."
2012-05-21 20:49:58 +00:00
return 1
2012-05-21 16:59:04 +00:00
fi
2012-05-21 20:49:58 +00:00
else
return 1
2012-05-21 13:39:59 +00:00
fi
}
2012-05-22 14:50:54 +00:00
# Deletes an existing jargroup
# $1: The name of the existing jargroup
2012-05-21 16:59:04 +00:00
jargroup_delete() {
2012-05-21 20:49:58 +00:00
if is_valid_name "$1"; then
if [[ -e "$JAR_STORAGE_PATH/$1" ]]; then
printf "Are you sure you want to delete this jar group [y/N]: "
2012-05-21 13:39:59 +00:00
2012-05-21 20:49:58 +00:00
read answer
if [[ "$answer" =~ ^y|Y|yes$ ]]; then
as_user "rm -rf $JAR_STORAGE_PATH/$1"
echo "Jar group deleted."
else
echo "Jar group was NOT deleted."
fi
2012-05-21 12:22:28 +00:00
else
2012-05-21 16:59:04 +00:00
echo "There is no jar group with the name \"$1\"."
2012-05-21 20:49:58 +00:00
return 1
2012-05-21 16:59:04 +00:00
fi
2012-05-21 20:49:58 +00:00
else
return 1
2012-05-21 12:22:28 +00:00
fi
2012-05-20 12:40:46 +00:00
}
2012-05-22 16:23:53 +00:00
# Renames an existing jargroup
# $1: The name of the existing jargroup
# $2: The new name
jargroup_rename() {
if is_valid_name "$1"; then
if [[ -e "$JAR_STORAGE_PATH/$1" ]]; then
# If the jar group name is valid,
# and there is no other jar group with the name $1
if is_valid_name "$2"; then
if [[ -e "$JAR_STORAGE_PATH/$2" ]]; then
echo "Could not be renamed, there is already a jar group with the name \"$2\"."
return 1
else
# TODO: Update any symbolic links which point to a jar in this directory
as_user "mv '$JAR_STORAGE_PATH/$1' '$JAR_STORAGE_PATH/$2'"
echo "Renamed jar group \"$1\" to \"$2\"."
fi
else
return 1
fi
else
echo "There is no jar group with the name \"$1\"."
return 1
fi
else
return 1
fi
}
2012-05-20 12:40:46 +00:00
2012-05-24 00:09:05 +00:00
### Server Functions
# Echos a list of servers in the SERVER_STORAGE_PATH
server_list() {
echo "Servers:"
for server in "$SERVER_STORAGE_PATH/*"; do
echo " $(basename $server)"
done
}
# Creates a new server
# $1: The server name to create
server_create() {
if is_valid_name "$1"; then
if [[ -e "$SERVER_STORAGE_PATH/$1" ]]; then
echo "A server with that name already exists."
return 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
return 1
fi
}
# Deletes an existing server
# $2: The server name to delete
server_delete() {
if is_valid_name "$1"; then
if [[ -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 -rf '$SERVER_STORAGE_PATH/$1'"
echo "Server deleted."
else
echo "Server was NOT deleted."
fi
else
echo "There is no server with the name \"$1\"."
return 1
fi
else
return 1
fi
}
# Renames an existing server
# $1: The server name to change
# $2: The new name for the server
server_rename() {
if is_valid_name "$1"; then
if [[ -e "$SERVER_STORAGE_PATH/$1" ]]; then
# If the server name is valid and exists
# TODO: Check that the server is not running first, return if it is
if is_valid_name "$2"; then
# If the server name is valid
if [[ -e "$SERVER_STORAGE_PATH/$2" ]]; then
# and there is not already a server with the name $2
echo "Could not be renamed, there is already a server with the name \"$2\"."
return 1
2012-05-21 20:49:58 +00:00
else
2012-05-24 00:09:05 +00:00
as_user "mv '$SERVER_STORAGE_PATH/$1' '$SERVER_STORAGE_PATH/$2'"
echo "Renamed server \"$1\" to \"$2\"."
2012-05-21 20:49:58 +00:00
fi
2012-05-24 00:09:05 +00:00
else
return 1
fi
else
echo "There is no server with the name \"$1\"."
return 1
fi
else
return 1
fi
}
# Starts a single server
2012-05-24 02:12:21 +00:00
# $1: The ID of the server
2012-05-24 00:09:05 +00:00
server_start() {
if server_is_running $1; then
echo "Server \"${server_name[$1]}\" is already running!"
else
server_ensure_links $1
server_worlds_to_ram $1
local time_now=$(now)
printf "Starting server... "
2012-05-24 01:44:10 +00:00
server_set_active $1 "active"
2012-05-24 00:09:05 +00:00
as_user ${server_user_name[$1]} "cd ${server_path[$1]} && screen -dmS ${server_screen_name[$1]} ${server_invocation[$1]}"
server_log_wait_for_line $1 "${server_confirm_start[$1]}" "$time_now"
echo "Done."
fi
}
2012-05-24 01:29:20 +00:00
# Sends the "save-all" command to a server
# $1: The ID of the server
server_save_all() {
if server_is_running $1; then
# Send the "save-all" command and wait for it to finish
printf "Forcing save... "
server_eval_and_wait $1 "save-all" "${server_confirm_save_all[$1]}"
echo "Done."
else
echo "Server \"${server_name[$1]}\" was not running."
fi
}
# Stops a single server
2012-05-24 02:12:21 +00:00
# $1: The ID of the server
2012-05-24 01:29:20 +00:00
server_stop() {
if server_is_running $1; then
server_save_all $1
printf "Stopping the server... "
2012-05-24 01:44:10 +00:00
server_set_active $1 "inactive"
2012-05-24 01:29:20 +00:00
server_eval $1 "stop"
2012-05-24 02:57:30 +00:00
STOP_COUNTDOWN[$1]="false"
RESTART_COUNTDOWN[$1]="false"
2012-05-24 01:29:20 +00:00
server_wait_for_stop $1
echo "Done."
2012-05-24 02:12:21 +00:00
# Synchronise all worlds in RAM to disk
server_worlds_to_disk $1
2012-05-24 01:29:20 +00:00
else
echo "Server \"${server_name[$1]}\" was not running."
fi
}
2012-05-24 02:12:21 +00:00
# Restarts a single server
# $1: The ID of the server
server_restart() {
# Restarts the server if it is already running
if server_is_running $1; then
server_stop $1
fi
server_start $1
}
2012-05-24 03:22:59 +00:00
# List the worlds available for a server
# $1: The ID of the server
server_worlds_list() {
for ((i=${server_world_offset[$1]}; i<${server_num_worlds[$1]}; i++)); do
if ${world_inram[$i]}; then
echo "[RAM] ${world_name[$i]}"
else
echo "[DSK] ${world_name[$i]}"
fi
done
}
2012-05-24 00:09:05 +00:00
### Main Functions
init() {
local i=0
local j=0
for server in $(ls -1 "$SERVER_STORAGE_PATH"); do
server_name[$i]="$server"
server_path[$i]="$SERVER_STORAGE_PATH/$server"
server_conf[$i]="${server_path[$i]}/$DEFAULT_SERVER_CONF"
2012-05-24 01:44:10 +00:00
server_flag_active[$i]="${server_path[$i]}/$SERVER_FLAG_ACTIVE"
2012-05-24 00:09:05 +00:00
2012-05-24 01:44:10 +00:00
if [[ -e "${server_flag_active[$i]}" ]]; then
2012-05-24 00:09:05 +00:00
server_active[$i]="true"
else
server_active[$i]="false"
fi
# Setup defaults
# Note: screen_name will at this stage have the {SERVER_NAME} tag in it
# which needs to be replaced.
2012-05-24 01:49:23 +00:00
# Invocation may also the {RAM} and {JAR} tags.
2012-05-24 00:09:05 +00:00
server_user_name[$i]="$DEFAULT_SERVER_USER"
server_screen_name[$i]="${DEFAULT_SCREEN_NAME//\{SERVER_NAME\}/${server_name[$i]}}" # Replace tags now, they cannot change
server_world_storage[$i]="${server_path[$i]}/$DEFAULT_WORLD_STORAGE_PATH"
server_log[$i]="${server_path[$i]}/$DEFAULT_LOG"
server_jar[$i]="${server_path[$i]}/$DEFAULT_JAR"
server_ram[$i]="$DEFAULT_RAM"
server_invocation[$i]="$DEFAULT_INVOCATION" # Don't replace tags yet, they may change
server_stop_delay[$i]="$DEFAULT_STOP_DELAY"
server_restart_delay[$i]="$DEFAULT_RESTART_DELAY"
server_stop_message[$i]="$DEFAULT_STOP_MESSAGE"
2012-05-24 02:57:30 +00:00
server_stop_abort[$i]="$DEFAULT_STOP_ABORT"
2012-05-24 00:09:05 +00:00
server_restart_message[$i]="$DEFAULT_RESTART_MESSAGE"
2012-05-24 02:57:30 +00:00
server_restart_abort[$i]="$DEFAULT_RESTART_ABORT"
2012-05-24 00:09:05 +00:00
server_world_backup_started[$i]="$DEFAULT_WORLD_BACKUP_STARTED"
server_world_backup_finished[$i]="$DEFAULT_WORLD_BACKUP_FINISHED"
server_complete_backup_started[$i]="$DEFAULT_COMPLETE_BACKUP_STARTED"
server_complete_backup_finished[$i]="$DEFAULT_COMPLETE_BACKUP_FINISHED"
server_confirm_save_on[$i]="$DEFAULT_CONFIRM_SAVE_ON"
server_confirm_save_off[$i]="$DEFAULT_CONFIRM_SAVE_OFF"
server_confirm_save_all[$i]="$DEFAULT_CONFIRM_SAVE_ALL"
server_confirm_start[$i]="$DEFAULT_CONFIRM_START"
# Load config overrides from server config file if present
if [[ -e "${server_conf[$i]}" ]]; then
local name
local value
while read line; do
# ignore comment lines
echo "$line" | grep "^#" >/dev/null 2>&1 && continue
# if not empty, set the property using declare
if [ ! -z "$line" ]; then
name="$(echo $line | awk -F '=' '{print $1}')"
2012-05-24 01:48:05 +00:00
value="$(echo $line | awk -F '\"' '{print $2}')"
2012-05-22 16:23:53 +00:00
fi
2012-05-24 00:09:05 +00:00
case "$name" in
SERVER_USER) server_user_name[$i]="$value";;
SCREEN_NAME) server_screen_name[$i]="$value";;
WORLD_STORAGE_DIR) server_world_storage[$i]="${server_path[$i]}/$value";;
LOG) server_log[$i]="${server_path[$i]}/$value";;
JAR) server_jar[$i]="${server_path[$i]}/$value";;
RAM) server_ram[$i]="$value";;
INVOCATION) server_invocation[$i]="$value";;
STOP_DELAY) server_stop_delay[$i]="$value";;
RESTART_DELAY) server_restart_delay[$i]="$value";;
STOP_MESSAGE) server_stop_message[$i]="$value";;
2012-05-24 02:57:30 +00:00
STOP_ABORT) server_stop_abort[$i]="$value";;
2012-05-24 00:09:05 +00:00
RESTART_MESSAGE) server_restart_message[$i]="$value";;
2012-05-24 02:57:30 +00:00
RESTART_ABORT) server_restart_abort[$i]="$value";;
2012-05-24 00:09:05 +00:00
WORLD_BACKUP_STARTED) server_world_backup_started[$i]="$value";;
WORLD_BACKUP_FINISHED) server_world_backup_finished[$i]="$value";;
COMPLETE_BACKUP_STARTED) server_complete_backup_started[$i]="$value";;
COMPLETE_BACKUP_FINISHED) server_complete_backup_finished[$i]="$value";;
CONFIRM_SAVE_ON) server_confirm_save_on[$i]="$value";;
CONFIRM_SAVE_OFF) server_confirm_save_off[$i]="$value";;
CONFIRM_SAVE_ALL) server_confrim_save_all[$i]="$value";;
CONFIRM_START) server_confrim_start[$i]="$value";;
esac
done < "${server_conf[$i]}"
fi
# Replace tags in delay messages
server_stop_message[$i]="${server_stop_message[$i]//\{DELAY\}/${server_stop_delay[$i]}}"
server_restart_message[$i]="${server_restart_message[$i]//\{DELAY\}/${server_restart_delay[$i]}}"
# Replace tags in server invocation
server_invocation[$i]="${server_invocation[$i]//\{RAM\}/${server_ram[$i]}}"
server_invocation[$i]="${server_invocation[$i]//\{JAR\}/${server_jar[$i]}}"
# Load worlds if there is a world storage directory present
server_world_offset[$i]=0
server_num_worlds[$i]=0
if [[ -e "${server_world_storage[$i]}" ]]; then
server_worlds[$i]=$(ls -1 "${server_world_storage[$i]}")
# Record the index at which worlds for this server start
server_world_offset[$i]=$j
for world in ${server_worlds[$i]}; do
world_server_id[$j]="$i"
world_name[$j]="$world"
2012-05-24 03:22:59 +00:00
world_path[$j]="${server_world_storage[$i]}/${world_name[$j]}"
world_link[$j]="${server_path[$i]}/${world_name[$j]}"
2012-05-24 01:44:10 +00:00
world_flag_inram[$j]="${world_path[$j]}/$WORLD_FLAG_INRAM"
2012-05-24 00:09:05 +00:00
if $RAMDISK_STORAGE_PATH; then
world_ramdisk_path[$j]="${RAMDISK_STORAGE_PATH}/${server_name[$i]}/${world_name[$j]}"
2012-05-21 20:49:58 +00:00
fi
2012-05-24 00:09:05 +00:00
if [[ -e "${world_path[$j]}/$WORLD_FLAG_INRAM" ]]; then
world_inram[$j]="true"
2012-05-21 20:49:58 +00:00
else
2012-05-24 00:09:05 +00:00
world_inram[$j]="false"
2012-05-21 20:49:58 +00:00
fi
2012-05-24 00:09:05 +00:00
2012-05-24 03:22:59 +00:00
### Printout
2012-05-24 00:09:05 +00:00
# echo "World server: ${server_name[${world_server_id[$j]}]}"
2012-05-24 03:22:59 +00:00
# echo "World ID: $j"
2012-05-24 00:09:05 +00:00
# echo "World name: ${world_name[$j]}"
2012-05-24 03:22:59 +00:00
# echo "World path: ${world_path[$j]}"
# echo "World link: ${world_link[$j]}"
# echo "World flag inram: ${world_flag_inram[$j]}"
# echo "World ramdisk path: ${world_ramdisk_path[$j]}"
2012-05-24 00:09:05 +00:00
# echo "World inram: ${world_inram[$j]}"
j=$(($j+1))
done
# Record the number of worlds this server has
server_num_worlds[$i]=$(( $j - ${server_world_offset[$i]} ))
fi
### Printout
# echo "Name: ${server_name[$i]}"
# echo "Path: ${server_path[$i]}"
# echo "Conf: ${server_conf[$i]}"
# echo "Active: ${server_active[$i]}"
# echo "User name: ${server_user_name[$i]}"
# echo "Screen name: ${server_screen_name[$i]}"
# echo "World storage: ${server_world_storage[$i]}"
# echo "Log: ${server_log[$i]}"
# echo "Jar: ${server_jar[$i]}"
# echo "Ram: ${server_ram[$i]}"
# echo "Invocation: ${server_invocation[$i]}"
# echo "Stop delay: ${server_stop_delay[$i]}"
# echo "Restart delay: ${server_restart_delay[$i]}"
# echo "Stop message: ${server_stop_message[$i]}"
# echo "Restart message: ${server_restart_message[$i]}"
# echo "World backup started: ${server_world_backup_started[$i]}"
# echo "World backup finished: ${server_world_backup_finished[$i]}"
# echo "World backup started: ${server_complete_backup_started[$i]}"
# echo "World backup finished: ${server_complete_backup_finished[$i]}"
# echo "Confirm save-on: ${server_confirm_save_on[$i]}"
# echo "Confirm save-off: ${server_confirm_save_off[$i]}"
# echo "Confirm save-all: ${server_confirm_save_all[$i]}"
# echo "Confirm start: ${server_confirm_start[$i]}"
# echo -n "Worlds: "
# for world in ${server_worlds[$i]}; do
# echo -n "$world, "
# done
# echo
# echo
i=$(($i+1))
done
num_servers=$i
num_worlds=$j
}
2012-05-24 02:57:30 +00:00
# Called if the script is interrupted before exiting naturally
interrupt() {
for ((i=0; $i<$num_servers; i++)); do
if [[ "${STOP_COUNTDOWN[$i]}" ]] && server_is_running $i; then
server_eval $i "say ${server_stop_abort[$i]}"
echo
echo "Broadcast the message \"${server_stop_abort[$id]}\" to players."
fi
if [[ "${RESTART_COUNTDOWN[$i]}" ]] && server_is_running $i; then
server_eval $i "say ${server_restart_abort[$i]}"
echo
echo "Broadcast the message \"${server_restart_abort[$id]}\" to players."
fi
done
exit
}
# The main function which starts the script
2012-05-24 00:09:05 +00:00
main() {
# Initialise variables that represent system state
init
2012-05-24 02:57:30 +00:00
# Trap interrupts to the script by calling the interrupt function
trap interrupt EXIT
2012-05-24 00:09:05 +00:00
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)
2012-05-21 14:52:31 +00:00
case "$2" in
2012-05-24 00:09:05 +00:00
list)
# Lists the existing servers
server_list
2012-05-21 14:52:31 +00:00
;;
2012-05-24 00:09:05 +00:00
create)
if [ -z "$3" ]; then
# If a server name is not provided
echo "Invalid command."
else
# Create a new server
server_create "$3"
fi
2012-05-21 14:52:31 +00:00
;;
2012-05-24 00:09:05 +00:00
delete)
if [ -z "$3" ]; then
# If a server name is not provided
echo "Invalid command."
else
# Delete an existing server, with confirmation
server_delete "$3"
fi
2012-05-21 14:52:31 +00:00
;;
2012-05-24 00:09:05 +00:00
rename)
if [ -z "$3" ] || [ -z "$4" ]; then
# If a server name is not provided
echo "Invalid command."
else
# Rename an existing server
server_rename "$3" "$4"
fi
2012-05-21 14:52:31 +00:00
;;
2012-05-24 00:09:05 +00:00
*)
# "server" is not a valid command
echo "Invalid command."
2012-05-21 14:52:31 +00:00
;;
2012-05-24 00:09:05 +00:00
esac
;;
jargroup)
case "$2" in
list)
# Lists the jars grouped by jargroup
jargroup_list
2012-05-21 14:52:31 +00:00
;;
2012-05-24 00:09:05 +00:00
create)
if [ -z "$3" ] || [ -z "$4" ]; then
echo "Invlaid command."
else
jargroup_create "$3" "$4"
fi
2012-05-21 14:52:31 +00:00
;;
2012-05-24 00:09:05 +00:00
delete)
if [ -z "$3" ]; then
echo "Invalid command."
else
jargroup_delete "$3"
fi
2012-05-21 14:52:31 +00:00
;;
2012-05-24 00:09:05 +00:00
rename)
if [ -z "$3" ] || [ -z "$4" ]; then
echo "Invalid command."
else
jargroup_rename $3 $4
fi
2012-05-21 14:52:31 +00:00
;;
2012-05-24 00:09:05 +00:00
changetarget)
if [ -z "$3" ] || [ -z "$4" ]; then
echo "Invalid command."
else
jargroup_settarget "$3" "$4"
fi
2012-05-21 14:52:31 +00:00
;;
2012-05-24 00:09:05 +00:00
getlatest)
if [ -z "$3" ]; then
echo "Invalid command."
else
jargroup_getlatest "$3"
fi
2012-05-21 14:52:31 +00:00
;;
2012-05-24 00:09:05 +00:00
*)
# "jargroup" is not a valid command
echo "Invalid command."
2012-05-21 14:52:31 +00:00
;;
esac
2012-05-24 00:09:05 +00:00
;;
help)
# Outputs a list of all commands
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> status \t\t\t\tShow the running/stopped status of a server"
echo -e " <server> connected \t\t\t\tList a servers connected players"
echo -e " <server> worlds list \t\t\t\tLists the worlds a server has"
2012-05-24 03:24:20 +00:00
echo -e " <server> worlds load \t\t\t\tCreates links to worlds in storage for a server"
2012-05-24 00:09:05 +00:00
echo -e " <server> worlds ram <world> \t\t\tToggles a world's \"in RAM\" status"
echo -e " <server> worlds toram \t\t\tSynchronises any RAM enabled worlds to RAM a server has"
echo -e " <server> worlds todisk \t\t\tSynchronises any \"in RAM\" worlds to disk a server has"
echo -e " <server> worlds backup \t\t\tMakes a backup of all worlds a server has"
echo -e " <server> logroll \t\t\t\tMove a server log to a gziped archive, to reduce lag"
echo -e " <server> backup \t\t\t\tMakes a backup of an entire server directory"
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 [now]\t\t\t\t\tStops all running servers"
echo -e " restart [now]\t\t\t\t\tRestarts all active servers"
;;
"")
echo "No such command see: $0 help"
;;
*)
if [[ "$1" == "all" ]] || [[ -e "$SERVER_STORAGE_PATH/$1" ]]; then
2012-05-24 01:29:20 +00:00
local id=$(server_get_id "$1")
2012-05-24 00:09:05 +00:00
case "$2" in
start)
2012-05-24 01:29:20 +00:00
server_start $id
2012-05-24 00:09:05 +00:00
;;
stop)
2012-05-24 01:29:20 +00:00
if server_is_running $id; then
if [[ $3 != "now" ]]; then
2012-05-24 02:57:30 +00:00
# Change the state of the script
STOP_COUNTDOWN[$id]="true"
2012-05-24 01:29:20 +00:00
server_eval $id "say ${server_stop_message[$id]}"
echo "Issued the warning \"${server_stop_message[$id]}\" to players."
echo -n "Shutting down... "
for ((i=${server_stop_delay[$id]}; i>0; i--)); do
tput sc # Save cursor position
echo -n "in $i seconds."
sleep 1
tput rc # Restore cursor to position of last `sc'
tput el # Clear to end of line
done
echo -e "Now."
fi
server_stop $id
else
echo "Server \"${server_name[$id]}\" was not running."
fi
2012-05-24 00:09:05 +00:00
;;
restart)
2012-05-24 02:12:21 +00:00
if server_is_running $id && [[ $3 != "now" ]]; then
2012-05-24 02:57:30 +00:00
# Change the state of the script
RESTART_COUNTDOWN[$id]="true"
2012-05-24 02:12:21 +00:00
server_eval $id "say ${server_restart_message[$id]}"
echo "Issued the warning \"${server_restart_message[$id]}\" to players."
echo -n "Restarting... "
for ((i=${server_restart_delay[$id]}; i>0; i--)); do
tput sc # Save cursor position
echo -n "in $i seconds."
sleep 1
tput rc # Restore cursor to position of last `sc'
tput el # Clear to end of line
done
echo -e "Now."
fi
server_restart $id
2012-05-24 00:09:05 +00:00
;;
2012-05-24 03:22:59 +00:00
status)
2012-05-24 00:09:05 +00:00
;;
2012-05-24 03:22:59 +00:00
connected)
2012-05-24 00:09:05 +00:00
;;
2012-05-24 03:22:59 +00:00
worlds)
case "$3" in
list)
server_worlds_list $id
;;
load)
;;
ram)
;;
toram)
;;
todisk)
;;
backup)
;;
*)
echo "Invalid command."
;;
esac
2012-05-24 00:09:05 +00:00
;;
2012-05-24 03:22:59 +00:00
logroll)
2012-05-24 00:09:05 +00:00
;;
2012-05-24 03:22:59 +00:00
backup)
2012-05-24 00:09:05 +00:00
;;
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
}
### Start point
2012-05-20 10:52:19 +00:00
2012-05-24 00:09:05 +00:00
main "$@"
exit 0