Click to See Complete Forum and Search --> : Sys-V Game Daemon Init Script


andysimmons
04-10-2004, 04:40 AM
Wasn't sure if this should go in "How I did it", but this forum said to share your code here, so here it is.

This is a really useful and versatile init script I wrote for running game daemons. All the documentation is in the comments, so just read those if you're curious. BTW, ignore the apparently random dots in the comment borders - I had to put them there to keep the vbcode from going nuts on the formatting.

Feedback, especially criticism, is more than welcome.

#!/bin/sh
# chkconfig: - 85 15
# description: _____ startup script
#
#===============================.================= ==============#
# System-V Game Daemon Init Script #
# Author: Andy Simmons (andy@gamingfederation.com) #
# Date: 04/09/04 #
# #
# Init script primarily for game servers. Each game #
# daemon is run from a screen owned by root, but the #
# daemon itself is run as a normal user, in a chroot jail. #
# #
# Requires: screen, chrootuid #
#===============================.================= ==============#
#
#===============================.================= ==============#
# The actual game startup scripts follow this basic format: #
#===============================.================= ==============#
#................................................. ..............#
# #
# #!/bin/bash #
# DIR="/path/to/game/dir/" #
# #
# if [[ $(whoami) != "root" ]]; then #
# HOME="/home/$(whoami)" #
# fi #
# #
#................................................. ..............#
# #
# You need that stuff above since chrootuid doesn't facilitate #
# login shells, and therefore legit environment variables. #
# #
# Then, any necessary prep work (clearing cache, zipping old #
# logs, etc.) would go here. #
# #
# The next part runs the server, continually restarting after #
# crashes, until server is terminated normally. #
# #
#................................................. ..............#
# #
# cd $DIR #
# #
# while ls /path/to/lockfile 2>/dev/null; do #
# exec server_binary arg1 arg2 etc. #
# sleep 2 #
# done #
# #
#................................................. ..............#
#===============================.================= ==============#


NAME="game_name"
DESC="Description of Server"
LOCKDIR=/var/lock/subsys
PATH=/bin:/usr/bin:/sbin:/usr/sbin
DIR="/the/actual/start/script's/dir/"
CMD="actual_start_script_name"
RUNAS="some_user"
JAIL="/path/to/jail"

#===============================.================= ======#
# Routine: status #
# Checks to see if this service is already running #
#===============================.================= ======#
status() {

if [[ $(screen -ls |grep $NAME) ]]; then
RETVAL=1
else
RETVAL=0
fi

return $RETVAL

} # === end of status() === #


#===============================.================= ======#
# Routine: start #
# Starts the server as user $RUNAS, on a screen #
# only available to the root user. #
#===============================.================= ======#
start() {

status
if [[ $? == 1 ]]; then
echo "Service is already running."

else
echo -n "Starting $DESC ... "
touch $LOCKDIR/$NAME
screen -dmS $NAME chrootuid $JAIL $RUNAS $DIR/$CMD
echo "done."
fi

} # === end of start() === #


#===============================.================= ======#
# Routine: stop #
# Uh, this would be what stops the server. #
#===============================.================= ======#
stop() {

echo -n "Stopping $DESC ... "
rm -f $LOCKDIR/$NAME 2>/dev/null

status
if [[ $? == 1 ]]; then
kill $(screen -ls |grep $NAME |awk -F. '{print $1}' |awk '{print $1}')
fi

kill -9 $(ps -aux |grep $DIR |grep -v grep |awk '{print $2}')

echo done.

} # === end of stop() === #


#=================================.=============== ======#
# Routine: stuff #
# Sends a string of text to the input buffer of the #
# screen that invoked our game daemon. This allows #
# us to send console commands to every server. #
# #
# Note #1: This assumes it's receiving the script's #
# args, so it drops the first argument. Just nuke #
# the pipe to sed on the echo line to stop that. #
# #
# Note #2: Yes, I am aware that screen has it's own #
# stuff function. However, it doesn't work with #
# local variables, and this method does. #
#=================================.=============== ======#
stuff() {

## Pick a random register to minimize chance of ##
## screwing up another daemon's stuff. ##
## ##
## The only reason to worry about this in the ##
## first place would be a cron conflict. ##

AVAIL_REGISTERS=(a b c d e f g h i j k l m n o p)
index=$RANDOM

let "index %= ${#AVAIL_REGISTERS[@]}"
register=${AVAIL_REGISTERS[$index]}

echo $@ |sed -e "s/$1 //" > /tmp/.$NAME'_buffer'
screen -S $NAME -X readreg $register /tmp/.$NAME'_buffer'
screen -S $NAME -X paste $register
rm -f /tmp/.$NAME'_buffer' 2>/dev/null

} # === end of stuff() === #


#===============================.================= ======#
# Let's see how we were called... #
#===============================.================= ======#

case "$1" in
start)
start
;;

stop)
stop
;;

restart)
stop
start
;;

status)
status
if [[ $? == 1 ]]; then
echo $DESC is UP.

elif [[ -e $LOCKDIR/$NAME ]]; then
echo $DESC is DOWN, unlocking...
rm -f $LOCKDIR/$NAME 2>/dev/null

else
echo $DESC is DOWN.
fi
;;

stuff)
stuff $@
;;

*)
echo "Usage: $0 {start|stop|restart|status|stuff}"
exit 1
;;

esac

exit

andysimmons
04-14-2004, 07:41 AM
I just edited that post to fix the stop() funciotn. It didn't work for BFV/BF1942 servers using the BVSM/BFSM for server management.