Advertisement
2lame2blame

wrench

Mar 20th, 2014
2,036
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Bash 158.37 KB | None | 0 0
  1. #!/bin/bash
  2. # wrench
  3. # v1.31
  4. #
  5. # A Steam Source Dedicated Server (srcds) control system.
  6. # For use with Valve's Source game servers: TF2, L4D, L4D2, HL2DM, CSS, CSGO, and more.
  7. #
  8. # --
  9. # Set PATH as is apropriate for your system. Mostly we need this for cron usage, otherwise possibly not needed.
  10. PATH=~/bin:/usr/local/sbin:/usr/sbin:/sbin:/usr/local/bin:/usr/bin:/bin
  11. #
  12. APPDIR=~/srcds # This is the installation directory where wrench adds new installations.
  13. SQLDBFILE=$APPDIR/wrench.db # The location of the SQLite database file.
  14. STEAMCMD_BIN=~/bin/steamcmd/steamcmd.sh
  15. SRCDS_LINUX="srcds_linux"
  16. WRENCH_MAILTO="nobody@localhost" # The default MAILTO address, mostly needed for autoupdate notifications.
  17. REPLAY_ENABLED=1 # Disable replay related features by setting this 0.
  18. REPLAYBASEDIR=/var/www/srcds-replays # The web server replay base directory.
  19. # --
  20. STOPCOUNTDOWN=10 # Number of seconds to give players shutdown warning in-game.
  21. STOPWAITFOR=7 # Number of seconds to wait for a srcds to quit normally before we violently kill it.
  22. UPDATETRIES=3 # How many times to attempt to update before we give up.
  23. DEMOSEXPIRE=91 # For auto-cleanup, how long to keep demo/sourcetv files in days.
  24. LOGSEXPIRE=91 # For auto-cleanup, how long to keep log files in days.
  25. DOWNLOADSEXPIRE=7 # For auto-cleanup, how long to keep download/cache files in days.
  26. REPLAYSEXPIRE=5 # For auto-cleanup, how long to keep replay files in days.
  27. AUTOUPDATE_SLEEP1=90 # Sleep sections between initially (first pass) discovering the need for an update, and then actually doing it.
  28. AUTOUPDATE_SLEEP2=15 # Sleep sections after an autoupdate failed postcheck, in between attempts.
  29. AUTOUPDATE_POSTCHECK_RETRIES=3 # How many autoupdate attempts to try when the postcheck fails. Remember that this xUPDATETRIES = total tries.
  30. RECOVER_LIMIT_WINDOW=3600 # One hour, in seconds.
  31. RECOVER_LIMIT_MAX=6 # If this number of crashes ocurs within the limit window, do not recover from the crash.
  32. RECOVERY_ABSOLUTE_MAX=100 # Absolute maximum number of crashes to tolerate during a single run.
  33. # --
  34. USER=$(whoami)
  35. MYNAME=$(basename $0)
  36. MYSELF=$(readlink -f $0)
  37. RUNID=$(date +%Y%m%d%H%M%S)-$$
  38. MYPID=$$
  39. MAILER=mail
  40. SQLITE=sqlite3
  41. SQLCMD="sqlite3 -batch -list -noheader $SQLDBFILE"
  42. SQLSHOWCMD="sqlite3 -batch -line -noheader $SQLDBFILE"
  43. NICECMD="nice -n 11 ionice -c 2 -n 6"
  44. AUTOUPDATE_LOCKFILE="/tmp/${MYNAME}-${USER}-autoupdate.pid"
  45. AUTOUPDATE_LOGDIR=$APPDIR/autoupdate-logs
  46. AUTOUPDATE_LOGFILE=$AUTOUPDATE_LOGDIR/$(basename $0 .sh)-${RUNID}.log
  47. MAIL_SUBJ_PREFIX="WRENCH:"
  48. # --
  49.  
  50. echoerr() {
  51.     # Print errors to stderr.
  52.     echo "$@" 1>&2;
  53. }
  54.  
  55. f_cleanuptrap() {
  56.     # Cleanup trap.
  57.     #
  58.     echoerr ""
  59.     echoerr "Caught signal."
  60.     #
  61.     # Find and kill off all branch processes which might be running.
  62.     MYPID_CHILDREN=$(pgrep -P $MYPID)
  63.     if [[ -n "$MYPID_CHILDREN" ]] ; then
  64.         # echoerr "DEBUG: Killing child processes: $(echo $MYPID_CHILDREN | tr -d "\n")"
  65.         for EACH in $MYPID_CHILDREN ; do
  66.             kill "$EACH" &> /dev/null
  67.         done
  68.         # Give these processes 3 seconds to die by SIGTERM before we KILL them.
  69.         # This does not work because wait is a bash built-in. # timeout 3 wait "$MYPID_CHILDREN" ; X_CKILL_TIMEOUT=$?
  70.         if [[ "$X_CKILL_TIMEOUT" == 124 ]] ; then
  71.             MYPID_BAD_CHILDREN=$(pgrep -P $MYPID)
  72.             # echoerr "DEBUG: Killing bad child processes: $(echo $MYPID_BAD_CHILDREN | tr -d "\n")"
  73.             for EACH in $MYPID_BAD_CHILDREN; do
  74.                 kill -s SIGKILL "$EACH" &> /dev/null
  75.             done
  76.         fi
  77.     fi
  78.     #
  79.     echoerr -n "Cleaning up: "
  80.     # Remove the update lock file, if we need to, and if it is there
  81.     if [[ "$UPDATING" == 1 ]] ; then # Cleanup during an update.
  82.         [[ -f "$LOCKFILE" ]] && rm -f "$LOCKFILE" &> /dev/null
  83.         f_lockdownmaster # Lock the master after an aborted update.
  84.     fi
  85.     if [[ "$AUTOUPDATING" == 1 ]] ; then # Cleanup during an auto-update.
  86.         [[ -f "$AUTOUPDATE_LOCKFILE" ]] && rm -f "$AUTOUPDATE_LOCKFILE" &> /dev/null
  87.         [[ -f "$AUTOUPDATE_LOGFILE" ]] && rm -f "$AUTOUPDATE_LOGFILE" &> /dev/null
  88.     fi
  89.     if [[ "$LISTINGPLAYERS" == 1 ]] ; then # Cleanup when listing players.
  90.         find /tmp -maxdepth 1 -type p -name "qstat-out-*$RUNID" -exec rm -f '{}' + &> /dev/null
  91.     fi
  92.     #
  93.     # We should restore the tty line settings, because traping while at a read prompt can cause problems for the user terminal.
  94.     # This turned out to be a bash bug: https://lists.gnu.org/archive/html/help-bash/2014-06/msg00006.html
  95.     if [[ -n "$TTY_LINE_SETTINGS" ]] ; then
  96.         # echoerr "DEBUG: Restored stty line settings."
  97.         stty "$TTY_LINE_SETTINGS"
  98.     fi
  99.     #
  100.     echoerr "Done"
  101.     echoerr ""
  102. }
  103.  
  104. f_getpid() {
  105.     # Get the current PID of an installation, if there is one, and record the server's operational status.
  106.     # Operational status (RUNSTATUS) can be one of "running", "stopped", "crashed", or "error".
  107.     # We return exit 1 on error, otherwise 0.
  108.     # f_getpid should never send stdout or stderr, unless debugging.
  109.     #
  110.     # declare -i GAMESERVPID
  111.     # We should know where to look up the PIDFILE. If we don't, that's a serious problem.
  112.     if [[ -z "$PIDFILE" ]] ; then
  113.         # echoerr "DEBUG: Call to obtain PID, but PIDFILE not set."
  114.         GAMESERVPID=""
  115.         RUNSTATUS="error"
  116.         return 1
  117.     fi
  118.     # Bad things that could possibly happen include the PID file being missing, the process being missing, or the tmux session being missing.
  119.     if [[ -r "$PIDFILE" ]] ; then
  120.         GAMESERVPID=$(cat $PIDFILE)
  121.         # Make sure the process is not crashed.
  122.         # Note that we use SRCDS_LINUX as the name of the process here for validation. This may be incompatible with mods, and non-srcds_linux platforms.
  123.         if [[ -n "$GAMESERVPID" ]] && [[ "$(ps --no-headers -p $GAMESERVPID -o comm)" == "$SRCDS_LINUX" ]] && [[ -z "$(ps --no-headers -p $GAMESERVPID -o pid)" ]] ; then
  124.             GAMESERVPID=""
  125.             RUNSTATUS="crashed"
  126.         else
  127.             RUNSTATUS="running"
  128.         fi
  129.         # We don't bother to check on the tmux session, because it is very unlikely that the process could be running without tmux as it's parent.
  130.     # It is also possible that the PIDFILE is missing, but the tmux session is running. The process may or may not also be running.
  131.     # In theory, the only time this should happen is when the server is just starting up, or if the STARTBIN/SRCDS_LINUX has failed.
  132.     elif ( tmux has-session -t $INSTALLID 2> /dev/null ) ; then
  133.         # echoerr "DEBUG: TMUX session exists, but there is no PIDFILE."
  134.         GAMESERVPID=""
  135.         RUNSTATUS="error"
  136.         return 1
  137.     # If there is no PIDFILE and no tmux session, then it's good to assume that the server is stopped.
  138.     else
  139.         GAMESERVPID=""
  140.         RUNSTATUS="stopped"
  141.     fi
  142.     #
  143.     # echoerr "DEBUG: PIDFILE=$PIDFILE"
  144.     # echoerr "DEBUG: GAMESERVPID=$GAMESERVPID"
  145.     # echoerr "DEBUG: RUNSTATUS=$RUNSTATUS"
  146. }
  147.  
  148. f_quitifrunning() {
  149.     # For certain activities, we don't want to proceed if the installation is actively running.
  150.     # We set RUNQUIT=1, and the parent func uses that to exit/return as needed.
  151.     #
  152.     f_getpid ; X_GETPID="$?"
  153.     if [[ "$RUNSTATUS" == "stopped" ]] ; then
  154.         return 0
  155.     elif [[ "$RUNSTATUS" == "running" ]] ; then
  156.         RUNQUIT=1
  157.         echoerr ""
  158.         echoerr "ERROR: Installation $INSTALLID appears to be running."
  159.         echoerr "  Unable to continue."
  160.         echoerr ""
  161.         return 1
  162.     elif [[ "$RUNSTATUS" == "crashed" ]] ; then
  163.         RUNQUIT=1
  164.         echoerr ""
  165.         echoerr "ERROR: Installation $INSTALLID appears to have crashed."
  166.         echoerr "  Recommended course of action is to remove the PID file after making sure the process is really dead."
  167.         echoerr "  You may also run the \"$MYNAME stop\" command, and the situation should get cleaned up automatically."
  168.         echoerr "  PIDFILE=$PIDFILE, PID: $GAMESERVPID"
  169.         echoerr "  Unable to continue."
  170.         echoerr ""
  171.         return 1
  172.     else
  173.         RUNQUIT=1
  174.         echoerr ""
  175.         echoerr "ERROR: Unable to get the installation's current operational status."
  176.         echoerr "  This may be a temporary error, or because the process has failed."
  177.         echoerr "  Unable to continue."
  178.         echoerr ""
  179.         return 1
  180.     fi
  181. }
  182.  
  183. f_qstat() {
  184.     # qstat replacement function. Mostly because of 0.0.0.0 addresses.
  185.     QSTAT_PARAMS="$@"
  186.     if [[ "$IPADDR" == "0.0.0.0" ]] ; then
  187.         # We need to replace 0.0.0.0 with a real local IP address.
  188.         # This has been tested on multiple hosts. I hope it works everywhere.
  189.         QSTAT_IPADDR=$(ip route get 8.8.8.8 | egrep -o "src [0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}" | cut -f 2 -d " ")
  190.         QSTAT_PARAMS="$(echo $QSTAT_PARAMS | sed -e "s/0\.0\.0\.0/$QSTAT_IPADDR/")"
  191.     fi
  192.     $QSTAT_CMD $QSTAT_PARAMS
  193. }
  194.  
  195. f_start() {
  196.     # Start an installation.
  197.     #
  198.     f_loaddbinst
  199.     if [[ "$LOADDBINSTINFO_FAIL" == 1 ]] ; then return 1 ; fi
  200.     f_mailnotify start "Server start request received."
  201.     #
  202.     # TEMP: Warn user if they are using the HLDSID var in their STARTBINARGS.
  203.     if ( echo "$DB_STARTBINARGS" | egrep '\$HLDSID' &> /dev/null) ; then
  204.         echoerr ""
  205.         echoerr "WARNING: Installation \"$IN_INSTALLID\" STARTBINARGS includes the \"HLDSID\" parameter."
  206.         echoerr "  The HLDSID parameter is deprecidated and will be removed in a future version of $MYNAME."
  207.         echoerr "  If you are using HLDSID as the \"-game\" argument, you should use \"GAMEARG\" instead."
  208.         echoerr "  Use the \"$MYNAME reconfig $IN_INSTALLID\" command to change your STARTBINARGS."
  209.         echoerr ""
  210.     fi
  211.     #
  212.     # Never allow a master type server to start.
  213.     if [[ "$INSTTYPE" == master ]] ; then
  214.         echoerr ""
  215.         echoerr "ERROR: $INSTALLID is a master type installation. Master type installations can not be started."
  216.         echoerr ""
  217.         return 1
  218.     fi
  219.     # Do not start if ALLOWSTART is not set.
  220.     if [[ ! "$ALLOWSTART" == 1 ]] ; then
  221.         echoerr ""
  222.         echoerr "ERROR: $INSTALLID configuration option ALLOWSTART is set to disallow starting."
  223.         echoerr ""
  224.         return 1
  225.     fi
  226.     # Find out if server is already running, and error out if true.
  227.     f_quitifrunning # Do not proceed if the installation is actively running.
  228.     if [[ "$RUNQUIT" == 1 ]] ; then return 1 ; fi
  229.     # FIXME: We need a way to mark installations as linked/delinked. The best place to do this would be the DB inst table.
  230.     # Validate that STARTBIN and SRCDS_LINUX is valid.
  231.     if ( [[ "$STARTBIN" == srcds_run ]] && [[ ! -x "$BINDIR/$STARTBIN" ]] ) || ( ! [[ "$STARTBIN" == srcds_run ]] && ! (type "$STARTBIN" &> /dev/null) ) ; then
  232.         echoerr ""
  233.         echoerr "ERROR: The STARTBIN, \"$STARTBIN\", is not executable or can not be found."
  234.         # echoerr "  DEBUG: PATH=$PATH"
  235.         echoerr ""
  236.         return 1
  237.     fi
  238.     if [[ ! -x "$BINDIR/$SRCDS_LINUX" ]] ; then
  239.         echoerr ""
  240.         echoerr "ERROR: The SRCDS_LINUX, \"$SRCDS_LINUX\", is not executable or can not be found."
  241.         echoerr ""
  242.         return 1
  243.     fi
  244.     # Check for an update lock.
  245.     f_lockcheck
  246.     if [[ "$LOCKFAIL" == 1 ]] ; then return 1 ; fi
  247.     # Continue if sane.
  248.     #
  249.     # NOTE: umask 002 (file:rw-rw-r-- dir:rwxrwxr-x) is needed so that replay files can be read by the web server.
  250.     # NOTE: Global user umask set in /etc/login.defs
  251.     #
  252.     if [[ "$STARTBIN" == srcds_run ]] ; then
  253.         # If the STARTBIN is srcds_run, we will only look for it in the BINDIR. PATH is not referenced.
  254.         EVALTXT_START_CMD="tmux new-session -d -s $INSTALLID \"umask 002 ; cd $BINDIR ; ./$STARTBIN $STARTBINARGS\""
  255.     else
  256.         if [[ "$STARTBIN" == "$MYNAME" ]] ; then
  257.             # If STARTBIN is wrench, we append "run" to make it run in wrench run mode.
  258.             FQP_STARTBIN="$MYSELF run"
  259.         else
  260.             # If the STARTBIN is anything else, we look for it in PATH, and set it explicitly. FQP=Fully Qualified Path.
  261.             # NOTE: tmux really likes to screw up PATH and env, so we explicitly set the path before tmux resets it's own PATH.
  262.             FQP_STARTBIN=$(type -p $STARTBIN)
  263.         fi
  264.         # echo "DEBUG: MYSELF=$MYSELF"
  265.         # echo "DEBUG: FQP_STARTBIN=$FQP_STARTBIN"
  266.         # echo "DEBUG: PATH=$PATH"
  267.         #
  268.         # Additionally, we want to export a number of vars. wrench_run requires many of these, and alternatives could make use of them.
  269.         # tmux does not accept any vars into new session environments, so we need to be clever in how we get these to the STARTBIN.
  270.         # This command is complicated. Usage of env taking input from a double-nested command subsitution bash for loop with indirect parameter expansion
  271.         #   inside of a var which will be eval'ed later makes for one clusterfuck of a command. I should be drug out to the street and shot for this.
  272.         EXPORT_VARS="IN_ARG APPDIR SQLITE SQLCMD SQLSHOWCMD SQLDBFILE STEAMCMD_BIN USER MYNAME RUNID NICECMD INSTALLID \
  273.             INSTALLDIR GAMETYPE GAMENAME INSTTYPE STARTBIN GAMEARG BINDIR BINDIRSUBD USEMASTER LOCKFILE \
  274.             PIDFILE IPADDR CLIENTPORT HOSTPORT TVPORT STEAMPORT ALLOWSTART BOOTSTART AUTOCLEANUP MAILTO \
  275.             MAILER MAIL_SUBJ_PREFIX MAILNOTIFY RECOVER_CRASH_ENABLE RECOVER_WATCHDOG_ENABLE RECOVER_WATCHDOG_TEST_INTERVAL \
  276.             RECOVER_WATCHDOG_POLL_MAX RECOVER_WATCHDOG_START_WAIT RECOVER_SLEEP"
  277.         #
  278.         # FIXME: We don't really need eval any more. We can show what tmux used as it's start command like so;
  279.         #   tmux list-panes -t "$INSTALLID" -F '#{pane_start_command}'
  280.         EVALTXT_START_CMD="tmux new-session -d -s $INSTALLID \"env $(echo $(for VAR in $EXPORT_VARS ; do echo "${VAR}='${!VAR}'" ; done) | tr '\n' ' ') $FQP_STARTBIN $STARTBINARGS\""
  281.     fi
  282.     #
  283.     # echo ""
  284.     # echo "DEBUG : The startup command shall be: "
  285.     # echo "$EVALTXT_START_CMD"
  286.     # echo ""
  287.     #
  288.     # Execute it.
  289.     eval $EVALTXT_START_CMD ; X_TMUXSTART=$?
  290.     #
  291.     # We set tmux remain-on-exit on here so that if the srcds_run/wrench_run process dies before
  292.     #   srcds_linux starts the server, we can dump the window pane contents to a file, so that we
  293.     #   review it later. Without this, tmux would close the session and we would have no idea what
  294.     #   went wrong.
  295.     #   Note that if tmux closes really fast, you might need to add a sleep 1 to the end of the new-session command.
  296.     tmux set-window-option -t $INSTALLID:0 remain-on-exit on &> /dev/null
  297.     #
  298.     echo ""
  299.     # Check the tmux exit code for errors.
  300.     if [[ $X_TMUXSTART -ne 0 ]] ; then
  301.         echoerr "ERROR: tmux failed to run or experienced an error."
  302.         echoerr "  tmux exit code $X_TMUXSTART."
  303.         echoerr "  Startup failed."
  304.         echoerr ""
  305.         return 1
  306.     fi
  307.     # Need to wait for the PIDFILE to be written, but tmux returns immediately.
  308.     # We will wait until the PIDFILE is written. Sometimes this can take many seconds.
  309.     if [[ -z "$(cat "$PIDFILE" 2> /dev/null)" ]] ; then # NOTE: We won't even hit this if the PIDFILE is written really fast (unlikely).
  310.         echo -n "Starting $INSTALLID:"
  311.         STARTWAITCOUNT=0
  312.         until [[ -n "$(cat "$PIDFILE" 2> /dev/null)" ]] ; do
  313.             # Check if the tmux pane died early. This is a sign that the STARTBIN or SRCDS_LINUX quit.
  314.             if [[ "$(tmux list-panes -t "$INSTALLID" -F '#{pane_dead}' 2> /dev/null)" == 1 ]] ; then
  315.                 STARTUP_CAPTURE_PANE_FILE="$INSTALLDIR/crash_capture-pane_$(date +%Y%m%d%H%M%S)-$$.log"
  316.                 echoerr ""
  317.                 echoerr "ERROR: The STARTBIN or SRCDS_LINUX quit before the PIDFILE was written."
  318.                 echoerr "  We will capture the tmux session output to file. Review the file for details."
  319.                 echoerr "  Capture file: $STARTUP_CAPTURE_PANE_FILE"
  320.                 echoerr ""
  321.                 tmux capture-pane "$TMUX_CAPTURE_OPTS" -t "$INSTALLID":0.0 \; save-buffer "$STARTUP_CAPTURE_PANE_FILE"
  322.                 tmux kill-session -t "$INSTALLID"
  323.                 break
  324.             fi
  325.             # Check if the session has disappeared. This should not happen if "remain-on-exit on" has been set.
  326.             if ! ( tmux has-session -t $INSTALLID 2> /dev/null ) ; then
  327.                 echoerr ""
  328.                 echoerr "ERROR: The tmux session has disappeared before the PIDFILE was written."
  329.                 echoerr ""
  330.                 break
  331.             fi
  332.             # Our timeout counter.
  333.             STARTWAITCOUNT=$(( $STARTWAITCOUNT + 1))
  334.             if [[ "$STARTWAITCOUNT" -gt 15 ]] ; then
  335.                 echoerr ""
  336.                 echoerr "ERROR: Done waiting. Startup may have failed. Check status."
  337.                 echoerr ""
  338.                 break
  339.             fi
  340.             sleep 1
  341.             echo -n "."
  342.         done
  343.         echo "Done"
  344.     fi
  345.     #
  346.     # Since the server has started, we can allow tmux to kill sessions when they die again.
  347.     tmux set-window-option -t $INSTALLID:0 remain-on-exit off &> /dev/null
  348.     #
  349.     # Now that the PIDFILE should be readable, get the PID
  350.     f_getpid
  351.     if [[ -n "$GAMESERVPID" ]] ; then
  352.         echo "Server started at PID $GAMESERVPID."
  353.     fi
  354.     # Renice the process to make sure it runs as smooth as possible.
  355.     # This requires pam_limits configuration via /etc/security/limits.conf
  356.     # Note that wrench_run now does this too, so this should only be needed when STARTBIN is srcds_run or some other tool.
  357.     # We will now test if this is configured properly, and use it if so.
  358.     if [[ -n "$GAMESERVPID" ]] && (egrep "$USER.*nice" /etc/security/limits.conf &> /dev/null) ; then
  359.         echo -n "Renicing srcds process: "
  360.         # bash -c "renice -9 $GAMESERVPID 1> /dev/null" ; X_RENICE=$? # FIXME: WTF was I doing here with a bash -c?
  361.         renice -n -9 "$GAMESERVPID" 1> /dev/null ; X_RENICE=$?
  362.         if [[ ! "$X_RENICE" = 0 ]] ; then
  363.             echoerr ""
  364.             echoerr "Raising the priority of the game server process failed."
  365.             echoerr "This might be due to pam_limits not being configured correctly."
  366.             echoerr ""
  367.         fi
  368.         echo "Done"
  369.     fi
  370.     echo "Start completed."
  371.     echo ""
  372. }
  373.  
  374. f_stop() {
  375.     # Stop a running installation.
  376.     #
  377.     f_loaddbinst
  378.     if [[ "$LOADDBINSTINFO_FAIL" == 1 ]] ; then return 1 ; fi
  379.     # FIXME: For mail notifications, we really should not notify until later, after we know we CAN stop, and maybe after we know the exit code.
  380.     f_mailnotify stop "Server stop request received."
  381.     STOPFAIL=0
  382.     f_getpid ; X_GETPID="$?"
  383.     if [[ "$RUNSTATUS" == "running" ]] ; then
  384.         echo ""
  385.         # Give users warning that the server is going down, unless we are in a rush.
  386.         if ! [[ "$STOPNOW" == yes ]] ; then
  387.             STOPCOUNT=$STOPCOUNTDOWN
  388.             echo -n "Giving players $STOPCOUNT seconds warning: "
  389.             until [[ "$STOPCOUNT" -eq 0 ]] ; do
  390.                 echo -n "."
  391.                 # Send both say and cm_csay, since sm_csay requires sourcemod.
  392.                 tmux send-keys -t "$INSTALLID" \
  393.                     "sm_csay Server is shutting down in $STOPCOUNT seconds!" C-m
  394.                 tmux send-keys -t "$INSTALLID" \
  395.                     "say Server is shutting down in $STOPCOUNT seconds!" C-m
  396.                 STOPCOUNT=$(( $STOPCOUNT - 1))
  397.                 sleep 1
  398.             done
  399.             echo "Done"
  400.         else
  401.             # Stop more or less immediately.
  402.             echo "Stopping now, without warning to players."
  403.         fi
  404.         # When countdown is done, send quit command
  405.         tmux send-keys -t "$INSTALLID" "quit" C-m
  406.         # Now verify that the process is actually gone via the PID.
  407.         if [[ $(ps --no-headers -p "$GAMESERVPID" -o pid | wc -l) -gt 0 ]] ; then
  408.             echo -n "Waiting for server to stop: "
  409.             STOPWAITCOUNT=0
  410.             while [[ $(ps --no-headers -p "$GAMESERVPID" -o pid | wc -l) -gt 0 ]] ; do
  411.                 STOPWAITCOUNT=$(( $STOPWAITCOUNT + 1))
  412.                 if [[ "$STOPWAITCOUNT" -gt "$STOPWAITFOR" ]] ; then
  413.                     echo ""
  414.                     echo "Giving up waiting after $STOPWAITFOR seconds. Stop may have failed."
  415.                     echo ""
  416.                     echo "Here's the process info:"
  417.                     ps uw -p "$GAMESERVPID"
  418.                     echo ""
  419.                     echo -n "Sending SIGQUIT to the process: "
  420.                     kill -SIGQUIT "$GAMESERVPID"
  421.                     echo "Done"
  422.                     STOPFAIL=1
  423.                     break
  424.                 fi
  425.                 sleep 1
  426.                 echo -n "."
  427.             done
  428.             echo "Done"
  429.         fi
  430.         #
  431.         # Kill the tmux session too. If "quit" causes a crash, it will just restart with a new PID.
  432.         if ( tmux has-session -t "$INSTALLID" 2> /dev/null ) ; then
  433.             echo -n "Waiting for tmux session to die: "
  434.             TMUXDIEWAITCOUNT=0
  435.             while ( tmux has-session -t "$INSTALLID" 2> /dev/null ) ; do
  436.                 TMUXDIEWAITCOUNT=$(( $TMUXDIEWAITCOUNT + 1))
  437.                 if [[ "$TMUXDIEWAITCOUNT" -gt 5 ]] ; then
  438.                     echo ""
  439.                     echo "Giving up waiting for tmux session to die."
  440.                     echo "  This may be caused by the srcds_linux process crashing upon \"quit\" being issued, which is common."
  441.                     echo -n "  I am going to kill the tmux session, which should kill everything: "
  442.                     tmux kill-session -t "$INSTALLID"
  443.                     echo "Done"
  444.                     STOPFAIL=1
  445.                     break
  446.                 fi
  447.                 sleep 1
  448.                 echo -n "."
  449.             done
  450.             echo "Done"
  451.         fi
  452.         # Remove the PID file.
  453.         rm -f "$PIDFILE" || { echoerr "ERROR: Failed to remove PIDFILE!" ; STOPFAIL=1 ; }
  454.         if [[ "$STOPFAIL" == 0 ]] ; then
  455.             echo "$INSTALLID was stopped successfully."
  456.         else
  457.             echoerr "$INSTALLID stop request encountered errors."
  458.         fi
  459.         echo ""
  460.     elif [[ "$RUNSTATUS" == "stopped" ]] ; then
  461.         echo ""
  462.         echo "Installation already stopped. Nothing to do."
  463.         echo ""
  464.     elif [[ "$RUNSTATUS" == "crashed" ]] ; then
  465.         echoerr ""
  466.         echoerr "WARNING: The installation appears to be crashed."
  467.         echoerr "  PIDFILE=\"$PIDFILE\", bogus PID is \"$GAMESERVPID\"."
  468.         echoerr "  Since you requested a stop, I will remove the old PIDFILE automatically."
  469.         echoerr ""
  470.         if [[ -f "$PIDFILE" ]] ; then
  471.             # If the PIDFILE exists, delete it.
  472.             rm -f "$PIDFILE" || echoerr "ERROR: Unable to remove PIDFILE!"
  473.         fi
  474.         if ( tmux has-session -t "$INSTALLID" 2> /dev/null ) ; then
  475.             # If the tmux session exists, kill it.
  476.             tmux kill-session -t "$INSTALLID" &> /dev/null
  477.         fi
  478.     else
  479.         echoerr ""
  480.         echoerr "ERROR: Unable to get the installation's current operational status."
  481.         echoerr "  This may be a temporary error (startup), or because the server has crashed."
  482.         echoerr "  Since you requested a stop, I will attempt to perform a forceful shutdown and cleanup."
  483.         echoerr ""
  484.         if [[ -f "$PIDFILE" ]] ; then
  485.             # If the PIDFILE exists, delete it.
  486.             rm -f "$PIDFILE" || echoerr "ERROR: Unable to remove PIDFILE!"
  487.         fi
  488.         if ( tmux has-session -t "$INSTALLID" 2> /dev/null ) ; then
  489.             # If the tmux session exists, kill it.
  490.             tmux kill-session -t "$INSTALLID" &> /dev/null
  491.         fi
  492.         echoerr "Cleanup completed."
  493.         echoerr ""
  494.     fi
  495. }
  496.  
  497. f_stopall() {
  498.     # Stop ALL running srcds installations.
  499.     #
  500.     f_listrunning
  501.     echo ""
  502.     if [[ -z "$LIST_RUNNING" ]] ; then
  503.         echo "No running installations found. Nothing to stop."
  504.         echo ""
  505.         return 0
  506.     else
  507.         echo "Stopping all running installations: $(echo $LIST_RUNNING | tr '\n' ' ')"
  508.         echo ""
  509.         for EACH in $LIST_RUNNING; do
  510.             ( IN_INSTALLID="$EACH"
  511.             echo "Stopping $EACH"
  512.             f_stop
  513.             echo "--"
  514.             echo "" )
  515.         done
  516.         echo "All running installations stopped."
  517.         echo ""
  518.     fi
  519. }
  520.  
  521. f_bootstart() {
  522.     # Start all bootstartable srcds installations.
  523.     #
  524.     f_listbootstartable
  525.     echo ""
  526.     echo "Starting all bootstartable installations: $(echo $LIST_BOOTSTARTABLE | tr '\n' ' ')"
  527.     for EACH in $LIST_BOOTSTARTABLE ; do
  528.         ( IN_INSTALLID="$EACH"
  529.         f_loaddbinst
  530.         if [[ "$LOADDBINSTINFO_FAIL" == 1 ]] ; then continue ; fi
  531.         # If the installation is already running, we must not try to start it.
  532.         f_getpid ; X_GETPID="$?"
  533.         if [[ "$RUNSTATUS" == "stopped" ]] ; then
  534.             f_start
  535.             echo "--"
  536.             continue
  537.         elif [[ "$RUNSTATUS" == "running" ]] ; then
  538.             echo ""
  539.             echo "Installation $INSTALLID is already running."
  540.             echo ""
  541.             echo "--"
  542.             continue
  543.         elif [[ "$RUNSTATUS" == "crashed" ]] ; then
  544.             echoerr ""
  545.             echoerr "WARNING: Installation $INSTALLID appears to be crashed."
  546.             echoerr "  If the host shut down before the running installations were stopped, it may have caused this situation."
  547.             echoerr "  Recommended course of action is to stop the installation to force a cleanup."
  548.             echoerr "  Skipping $INSTALLID."
  549.             echoerr ""
  550.             echoerr "--"
  551.             continue
  552.         else
  553.             echoerr ""
  554.             echoerr "WARNING: $INSTALLID is currently in error status. Unable to start."
  555.             echoerr "  Skipping $INSTALLID."
  556.             echoerr ""
  557.             echoerr "--"
  558.             continue
  559.         fi )
  560.     done
  561.     echo ""
  562.     echo "Done"
  563.     echo ""
  564. }
  565.  
  566. f_status() {
  567.     # Display the status of a particular installation. Intended mostly to display info about active/running installations.
  568.     #
  569.     f_loaddbinst
  570.     if [[ "$LOADDBINSTINFO_FAIL" == 1 ]] ; then return 1 ; fi
  571.     f_getpid ; X_GETPID="$?"
  572.     if [[ "$RUNSTATUS" == "running" ]] ; then
  573.         echo ""
  574.         echo "Showing status for $INSTALLID"
  575.         echo ""
  576.         echo "tmux session: $(tmux list-sessions | egrep "^ *$INSTALLID: ")"
  577.         echo ""
  578.         echo "Game server PID: $GAMESERVPID"
  579.         echo ""
  580.         echo "Configured IP:Hostport: $IPADDR:$HOSTPORT"
  581.         echo ""
  582.         PROCINFO_ARGS=$(ps --no-headers -p $GAMESERVPID -o args)
  583.         echo "Process command arguments (from ps): "
  584.         echo "$PROCINFO_ARGS"
  585.         echo ""
  586.         echo "Various ps stats:"
  587.         #
  588.         PROCINFO_PCPU=$(ps --no-headers -p $GAMESERVPID -o pcpu)
  589.         PROCINFO_PPRIO=$(ps --no-headers -p $GAMESERVPID -o pri | sed -e "s/ \{1,\}//g")
  590.         PROCINFO_PSR=$(ps --no-headers -p $GAMESERVPID -o psr | sed -e "s/ \{1,\}//g")
  591.         PROCINFO_EUSER=$(ps --no-headers -p $GAMESERVPID -o euser)
  592.         PROCINFO_RSS=$(ps --no-headers -p $GAMESERVPID -o rss)
  593.         PROCINFO_VSZ=$(ps --no-headers -p $GAMESERVPID -o vsz)
  594.         PROCINFO_LSTART=$(ps --no-headers -p $GAMESERVPID -o lstart)
  595.         PROCINFO_ETIME=$(ps --no-headers -p $GAMESERVPID -o etime | sed -e "s/ \{1,\}//g")
  596.         PROCINFO_CPUTIME=$(ps --no-headers -p $GAMESERVPID -o cputime)
  597.         #
  598.         ( echo "  CPU usage percent per-core:|$PROCINFO_PCPU%"
  599.         echo "  Process priority (normal=19, higher=better):|$PROCINFO_PPRIO"
  600.         echo "  Running on processor core:|$PROCINFO_PSR"
  601.         echo "  Running as effective user:|$PROCINFO_EUSER"
  602.         echo "  RSS/Real memory usage:|$PROCINFO_RSS KB"
  603.         echo "  VSZ/Virtual memory usage:|$PROCINFO_VSZ KB"
  604.         echo "  Process started at:|$PROCINFO_LSTART"
  605.         echo "  Process started elapsed-time ago:|$PROCINFO_ETIME"
  606.         echo "  Process CPU in-use time:|$PROCINFO_CPUTIME"
  607.         ) | column -t -s "|"
  608.         echo ""
  609.         echo "lsof says the following network sockets are in use by PID $GAMESERVPID:"
  610.         lsof -i -n -a -p $GAMESERVPID | column -t
  611.         echo ""
  612.         echo "qstat player list for $INSTALLID:"
  613.         f_qstat -P -a2s "$IPADDR":"$HOSTPORT"
  614.         echo ""
  615.         echo "Done"
  616.     elif [[ "$RUNSTATUS" == "stopped" ]] ; then
  617.         echo ""
  618.         echo "Installation not running."
  619.         echo ""
  620.     elif [[ "$RUNSTATUS" == "crashed" ]] ; then
  621.         echoerr ""
  622.         echoerr "WARNING: The installation appears to be crashed."
  623.         echoerr "  Recommended course of action is to stop the installation to force a cleanup."
  624.         echoerr ""
  625.     else
  626.         echoerr ""
  627.         echoerr "WARNING: Unable to determine the status of the installation."
  628.         echoerr "  This might be a temporary problem. Try again?"
  629.         echoerr ""
  630.     fi
  631. }
  632.  
  633. f_listinstalls() {
  634.     # List all valid installations. This is determined by an entry in the DB inst table, then we look for a directory in $APPDIR.
  635.     #
  636.     echo ""
  637.     LIST_INSTALLS=$($SQLCMD "select INSTALLID from inst;")
  638.     if [[ -z "$LIST_INSTALLS" ]] ; then
  639.         echo "No installations were found."
  640.         echo ""
  641.         return 0
  642.     else
  643.         echo "The following installations were found: "
  644.         echo ""
  645.         # Write out in column format.
  646.         ( echo "Install ID|Install Type|Run Status" ; echo "--|--|--"
  647.         for EACH_LISTINSTALL in $LIST_INSTALLS ; do
  648.             IN_INSTALLID="$EACH_LISTINSTALL"
  649.             f_loaddbinst
  650.             if [[ "$LOADDBINSTINFO_FAIL" == 1 ]] ; then continue ; fi
  651.             f_getpid ; X_GETPID="$?"
  652.             echo "$INSTALLID|$INSTTYPE|$RUNSTATUS"
  653.         done
  654.         ) | column -t -s "|"
  655.         echo ""
  656.     fi
  657. }
  658.  
  659. f_listplayers() {
  660.     # Show a list of all running srcds installations with their qstat player listings.
  661.     #
  662.     # qstat is required.
  663.     if [[ "$QSTAT_MISSING" == 1 ]] ; then
  664.         echoerr ""
  665.         echoerr "ERROR: qstat command not found. Quitting."
  666.         echoerr ""
  667.         exit 1
  668.     fi
  669.     f_listrunning
  670.     #
  671.     # Print notice if no running servers found
  672.     if [[ -z "$LIST_RUNNING" ]] ; then
  673.         echo ""
  674.         echo "No running servers found."
  675.         echo ""
  676.         return 0
  677.     fi
  678.     #
  679.     echo ""
  680.     echo "Listing all players on all active srcds servers: "
  681.     echo ""
  682.     #
  683.     echo "Local time is: $(date)"
  684.     echo ""
  685.     # Write each fifo so that we can read it later
  686.     # We use fifos here so that we can do the polling in parallel, making this process much faster.
  687.     LISTINGPLAYERS=1
  688.     for EACH in $LIST_RUNNING ; do
  689.         ( while IFS='|' read IPADDR HOSTPORT ; do
  690.             DB_IPADDR=$IPADDR
  691.             DB_HOSTPORT=$HOSTPORT
  692.         done < <($SQLCMD "select IPADDR,HOSTPORT from inst where INSTALLID='$EACH';")
  693.         # Normalize database parameters.
  694.             IPADDR=$DB_IPADDR
  695.             HOSTPORT=$DB_HOSTPORT
  696.         # echo "working on: $EACH $IPADDR:$HOSTPORT"
  697.         QSTATOUT=/tmp/qstat-out-$EACH-$RUNID
  698.         mkfifo $QSTATOUT
  699.         echo "$(f_qstat -P -a2s $IPADDR:$HOSTPORT)" 1> $QSTATOUT 2> /dev/null ) &
  700.     done
  701.     # sleep to make the output look a little more consistent, since qstat sleeps 1 anyway.
  702.     sleep 2
  703.     #
  704.     # Read out the results
  705.     for EACH in $LIST_RUNNING ; do
  706.         ( QSTATOUT=/tmp/qstat-out-$EACH-$RUNID
  707.         echo "qstat player list for $EACH:"
  708.         cat "$QSTATOUT"
  709.         echo ""
  710.         rm -f "$QSTATOUT" )
  711.     done
  712.     LISTINGPLAYERS=0
  713.     echo ""
  714. }
  715.  
  716. f_lockdownmaster() {
  717.     # Lock down the filesystem permissions on a master installation to prevent accidental changes.
  718.     # This also sets sane permissions, such as all files not being ugo+x. This replaces fixperms.
  719.     #
  720.     f_loaddbinst
  721.     if [[ "$LOADDBINSTINFO_FAIL" == 1 ]] ; then return 1 ; fi
  722.     echo ""
  723.     # The installation specified must be a master type install, otherwise quit.
  724.     if [[ ! "$INSTTYPE" == master ]] ; then
  725.         echoerr "ERROR: This command can only be used against a \"master\" type installation."
  726.         echoerr "  $INSTALLID is a \"$INSTTYPE\" type installation."
  727.         echoerr ""
  728.         return 1
  729.     fi
  730.     echo -n "Locking down the master installation: "
  731.     $NICECMD find "$INSTALLDIR" -type f -exec chmod ugo-x '{}' + # Remove all needless file execute perms.
  732.     for EACH in $($NICECMD find "$BINDIR" -mindepth 1 -maxdepth 1 -type f -name "srcds_*" | egrep "^$BINDIR/srcds_[amd$|i386$|i486$|i586$|i686$|run$|linux$|osx$]") ; do
  733.         # echoerr "DEBUG: Adding +x bit on $EACH"
  734.         chmod ug+x "$EACH" || LOCKMASTER_FAIL=1 # Re-add execute perms for only those files which should have them.
  735.     done
  736.     $NICECMD find "$INSTALLDIR" -type f -exec chmod ugo+r '{}' + || LOCKMASTER_FAIL=1
  737.     $NICECMD find "$INSTALLDIR" -type d -exec chmod ugo+rx '{}' + || LOCKMASTER_FAIL=1
  738.     $NICECMD find "$INSTALLDIR" -type f -exec chmod ugo-w '{}' + || LOCKMASTER_FAIL=1
  739.     $NICECMD find "$INSTALLDIR" -type d -exec chmod ugo-w '{}' + || LOCKMASTER_FAIL=1
  740.     if [[ "$LOCKMASTER_FAIL" == 1 ]] ; then
  741.         echoerr ""
  742.         echoerr "ERROR: Locking master installation \"$INSTALLDIR\" failed."
  743.         echoerr ""
  744.         return 1
  745.     fi
  746.     echo "Done"
  747.     echo ""
  748. }
  749.  
  750. f_unlockmaster() {
  751.     # Unlock the master installation for writing. The only time this should be needed is for updating or intentional modification of the master.
  752.     #
  753.     f_loaddbinst
  754.     if [[ "$LOADDBINSTINFO_FAIL" == 1 ]] ; then return 1 ; fi
  755.     echo ""
  756.     # The installation specified must be a master type install, otherwise quit.
  757.     if [[ ! "$INSTTYPE" == master ]] ; then
  758.         echoerr "ERROR: unlock-master can only be used against a \"master\" type installation."
  759.         echoerr "  $INSTALLID is a \"$INSTTYPE\" type installation."
  760.         echoerr ""
  761.         return 1
  762.     fi
  763.     echo -n "Unlocking the master installation: "
  764.     $NICECMD find "$INSTALLDIR" -type d -exec chmod ug+w '{}' + || UNLOCKMASTER_FAIL=1
  765.     $NICECMD find "$INSTALLDIR" -type f -exec chmod ug+w '{}' + || UNLOCKMASTER_FAIL=1
  766.     if [[ "$UNLOCKMASTER_FAIL" == 1 ]] ; then
  767.         echoerr ""
  768.         echoerr "ERROR: Unlocking master installation \"$INSTALLDIR\" failed."
  769.         echoerr ""
  770.         return 1
  771.     fi
  772.     echo "Done"
  773.     echo ""
  774. }
  775.  
  776. f_getlocalver() {
  777.     # Get the local application version.
  778.     #
  779.     # We must have a valid Steam.inf file to work with.
  780.     #
  781.     if [[ -z "$STEAMINF_FILE" ]] ; then
  782.         echoerr "ERROR: Call to get the local application version, but STEAMINF_FILE is invalid."
  783.         return 1
  784.     fi
  785.     LOCAL_VER=$(egrep "^PatchVersion=" "$STEAMINF_FILE" | cut -f 2 -d "=" | tr -c -d [:digit:])
  786. }
  787.  
  788. f_getup2datereqver() {
  789.     # Get the remote Required Version from Valve's UpToDate WebAPI.
  790.     #
  791.     # Without the CLIENT_APPID, we can do nothing.
  792.     if [[ -z "$CLIENT_APPID" ]] ; then
  793.         echoerr "ERROR: Call to get the remote Required Version, but CLIENT_APPID is invalid."
  794.         return 1
  795.     fi
  796.     UPTODATECHECK_URL="http://api.steampowered.com/ISteamApps/UpToDateCheck/v1?appid=${CLIENT_APPID}&version=0&format=xml"
  797.     # wget 3 tries with 3 second timeouts should be okay for most users. Default is 20 tries with a 900 second timeout, which is unreasonable.
  798.     REMOTE_VER=$(wget --timeout=3 --tries=3 -O - "$UPTODATECHECK_URL" 2> /dev/null | xmlstarlet sel -t -v "response/required_version" 2> /dev/null)
  799. }
  800.  
  801. f_update_steamcmd() {
  802.     # Update with SteamCMD/SteamPipe
  803.     # Note that we don't do any lock checking here. f_update does that.
  804.     #
  805.     echo ""
  806.     #
  807.     # If SteamCMD is not found, there is nothing we can do.
  808.     if [[ ! -x "$STEAMCMD_BIN" ]] ; then
  809.         echoerr "ERROR: Unable to find the SteamCMD installation."
  810.         echoerr ""
  811.         return 2
  812.     fi
  813.     if [[ -z "$SERVER_APPID" ]] ; then
  814.         echoerr "ERROR: Request to update with SteamCMD, but we have no APPID to work with."
  815.         echoerr ""
  816.         return 2
  817.     fi
  818.     declare -i STEAMCMD_UPDATE_COUNTER="0"
  819.     if [[ "$VERIFYUPDATE" == "1" ]] ; then
  820.         UPDATEARG="+app_update $SERVER_APPID validate"
  821.         # echoerr "DEUBG: Updating with SteamCMD validate"
  822.     else
  823.         UPDATEARG="+app_update $SERVER_APPID"
  824.         # echoerr "DEBUG: Updating with SteamCMD"
  825.     fi
  826.     # cd to the APPDIR, so steamcmd doesn't litter crap wherever $PWD happens to be.
  827.     cd $APPDIR
  828.     #
  829.     EVALTXT_STEAMCMD_UPDATE_CMD="$NICECMD $STEAMCMD_BIN +@ShutdownOnFailedCommand 1 +login \"anonymous\" +force_install_dir $INSTALLDIR/ $UPDATEARG +exit"
  830.     echo "SteamCMD running command: "
  831.     echo "  $EVALTXT_STEAMCMD_UPDATE_CMD"
  832.     echo ""
  833.     #
  834.     while true ; do
  835.         eval $EVALTXT_STEAMCMD_UPDATE_CMD ; X_UPDATE_STEAMCMD=$?
  836.         STEAMCMD_UPDATE_COUNTER=$(( $STEAMCMD_UPDATE_COUNTER + 1)) # Increment the counter
  837.         # echo "" # SteamCMD seems to add a blank line after exit anyway.
  838.         echo "SteamCMD exit code: $X_UPDATE_STEAMCMD"
  839.         if [[ "$X_UPDATE_STEAMCMD" = 0 ]] ; then
  840.             echo "SteamCMD completed successfully."
  841.             echo ""
  842.             break
  843.         else
  844.             if [[ "$STEAMCMD_UPDATE_COUNTER" -lt "$UPDATETRIES" ]] ; then
  845.                 echo "SteamCMD attempt $STEAMCMD_UPDATE_COUNTER failed, will try again."
  846.                 echo ""
  847.                 sleep 0
  848.                 continue
  849.             fi
  850.             if [[ "$STEAMCMD_UPDATE_COUNTER" -eq "$UPDATETRIES" ]] ; then
  851.                 echoerr "SteamCMD failed $STEAMCMD_UPDATE_COUNTER times. Giving up."
  852.                 echoerr ""
  853.                 return 1
  854.             fi
  855.         fi
  856.     done
  857. }
  858.  
  859. f_update() {
  860.     # Update a srcds installation.
  861.     #
  862.     f_loaddbinst
  863.     if [[ "$LOADDBINSTINFO_FAIL" == 1 ]] ; then return 1 ; fi
  864.     #
  865.     # If ALLOWUPDATE is not 1, do not allow.
  866.     if [[ ! "$ALLOWUPDATE" == 1 ]] ; then
  867.         echoerr ""
  868.         echoerr "ERROR: ALLOWUPDATE disabled. Updating this installation is not allowed."
  869.         echoerr ""
  870.         return 1
  871.     fi
  872.     # We must only update valid installation types.
  873.     case "$INSTTYPE" in
  874.         'linked') # Never update a linked install. That's what relinking is for.
  875.             echoerr ""
  876.             echoerr "ERROR: $INSTALLID is a $INSTTYPE type installation."
  877.             echoerr "  This type of installation should never be updated directly."
  878.             echoerr "  If you need to relink the installation, use the \"relink\" argument."
  879.             echoerr ""
  880.             return 1
  881.         ;;
  882.         'master') # Okay to proceed.
  883.             true
  884.             # echo "NOTE: $INSTALLID is a $INSTTYPE installation."
  885.         ;;
  886.         *)
  887.             echoerr ""
  888.             echoerr "ERROR: $INSTALLID installation type invalid. Unable to update."
  889.             echoerr ""
  890.             return 1
  891.         ;;
  892.     esac
  893.     # Lockfile pre-check and set
  894.     f_lockcheck
  895.     if [[ "$LOCKFAIL" == 1 ]] ; then return 1 ; fi
  896.     f_unlockmaster ; X_UNLOCKMASTER="$?" # Unlock the master for writing.
  897.     if [[ ! "$X_UNLOCKMASTER" == 0 ]] ; then return 1 ; fi
  898.     #
  899.     UPDATING=1
  900.     echo "$MYPID" >> $LOCKFILE ; X_UPDATE_SETLOCK="$?"
  901.     if [[ ! "$X_UPDATE_SETLOCK" == 0 ]] ; then
  902.         echoerr ""
  903.         echoerr "ERROR: Unable to write lockfile for update!"
  904.         echoerr ""
  905.         UPDATING=0
  906.         return 1
  907.     fi
  908.     #
  909.     while true ; do
  910.         # Choose and use the update tool. Previously HLDSUpdateTool was an option, but now only SteamCMD is supported.
  911.         case "$UPDATER" in
  912.             'SteamCMD')
  913.                 echo "Updater using SteamCMD."
  914.                 f_update_steamcmd ; X_UPDATER="$?"
  915.             ;;
  916.             *)
  917.                 echoerr ""
  918.                 echoerr "ERROR: Unknown UPDATER type. Unable to proceed."
  919.                 echoerr ""
  920.                 UPDATING=0
  921.                 return 1
  922.         esac
  923.         #
  924.         # Now that we ran the updater, we need to verify that it worked, and if it didn't try again or fail out.
  925.         # Remember that our updater itself may retry internally, so this may be redundant.
  926.         #   The reason this exists is because sometimes the Steam Cloud content depots are out of date when updates get released.
  927.         #   Often, just waiting a little while fixes the issue.
  928.         #
  929.         UPDATE_POSTCHECK_COUNTER=$(( $UPDATE_POSTCHECK_COUNTER + 1)) # Increment the counter
  930.         #
  931.         # If we were called via an autoupdate, we sleep between attempts. Otherwise we try again immediately.
  932.         # FIXME: Should really do this as cron vs interactive, but need a test method.
  933.         if [[ ! "$IN_ARG" == "autoupdate" ]] ; then
  934.             AUTOUPDATE_SLEEP2="0"
  935.         fi
  936.         #
  937.         if [[ "$X_UPDATER" == 2 ]] ; then
  938.             # X_UPDATER=2 is a permanant error. Retrying won't help. Give up immediately.
  939.             UPDATER_FAIL=1
  940.             break
  941.         elif [[ "$X_UPDATER" == 1 ]] ; then
  942.             # X_UPDATER=1 is a soft error. Trying again might result in success.
  943.             if [[ "$UPDATE_POSTCHECK_COUNTER" -lt "$AUTOUPDATE_POSTCHECK_RETRIES" ]] ; then
  944.                 echo "Updater try $UPDATE_POSTCHECK_COUNTER failed. Will try again in $AUTOUPDATE_SLEEP2 seconds."
  945.                 echo ""
  946.                 sleep "$AUTOUPDATE_SLEEP2"
  947.                 continue
  948.             else    
  949.                 UPDATER_FAIL=1
  950.                 echoerr "ERROR: Updater tried $UPDATE_POSTCHECK_COUNTER times already. Will not try again."
  951.                 echoerr ""
  952.                 break
  953.             fi
  954.         elif [[ "$X_UPDATER" == 0 ]] ; then
  955.             # We compare the remote and local versions. If they are out of sync, that is a problem.
  956.             f_getlocalver ; POSTCHECK_LOCAL_VER="$LOCAL_VER"
  957.             f_getup2datereqver ; POSTCHECK_REMOTE_VER="$REMOTE_VER"
  958.             if [[ "$POSTCHECK_LOCAL_VER" == "$POSTCHECK_REMOTE_VER" ]]; then
  959.                 UPDATER_FAIL=0
  960.                 echo "Post-update version check result: Success"
  961.                 echo ""
  962.                 break
  963.             else
  964.                 if [[ "$UPDATE_POSTCHECK_COUNTER" -lt "$AUTOUPDATE_POSTCHECK_RETRIES" ]] ; then
  965.                     echo "Post-updater version check failed on try $UPDATE_POSTCHECK_COUNTER. Will try again in $AUTOUPDATE_SLEEP2 seconds."
  966.                     echo ""
  967.                     sleep "$AUTOUPDATE_SLEEP2"
  968.                     continue
  969.                 else    
  970.                     UPDATER_FAIL=1
  971.                     echoerr "ERROR: Post-updater version check failed on try $UPDATE_POSTCHECK_COUNTER. Will not try again."
  972.                     echoerr "POSTCHECK_LOCAL_VER=$POSTCHECK_LOCAL_VER, POSTCHECK_REMOTE_VER=$POSTCHECK_REMOTE_VER"
  973.                     echoerr ""
  974.                     break
  975.                 fi
  976.             fi
  977.         fi
  978.         #
  979.     done
  980.     #
  981.     UPDATING=0
  982.     # Remove lockfile
  983.     rm -f $LOCKFILE ; X_UPDATE_RMLOCK="$?"
  984.     if [[ ! "$X_UPDATE_RMLOCK" == 0 ]] ; then
  985.         echoerr "ERROR: Unable to remove lockfile post-update!"
  986.         echoerr ""
  987.         return 1
  988.     fi
  989.     #
  990.     if [[ "$UPDATER_FAIL" == 1 ]] ; then
  991.         echoerr "The updater failed. Please see previous errors and warnings for clues on how to fix this."
  992.         echoerr ""
  993.         f_lockdownmaster ; X_LOCKMASTER="$?" # Lock down the master from writing when done, even if we did fail.
  994.         return 1
  995.     else
  996.         f_lockdownmaster ; X_LOCKMASTER="$?" # Lock down the master from writing when done.
  997.         # Even if there are problems, continue # if [[ ! "$X_LOCKMASTER" == 0 ]] ; then return 1 ; fi
  998.     fi
  999.     echo "Update on \"$INSTALLID\" completed."
  1000.     echo ""
  1001. }
  1002.  
  1003. f_autoupdate_warnallactive() {
  1004.     # Warn an active running srcds instances which could be impacted by resource contention.
  1005.     # FIXME: Should probably merge this with f_autoupdate.
  1006.     #
  1007.     f_listrunning
  1008.     #
  1009.     # If there are active/running srcds instances, then warn them.
  1010.     if [[ -z "$LIST_RUNNING" ]] ; then
  1011.         echo "No active srcds instances to warn of impending updates."
  1012.         echo ""
  1013.     else
  1014.         echo "Sending warnings of impending updates to all active srcds instances: "
  1015.         WARNMESG_TEXT="ATTENTION: Server maintenance in progress. This may harm game performance. Please be patient."
  1016.         for WARNTARGET in $LIST_RUNNING ; do
  1017.             # Repeat ourselves a few times.
  1018.             # Group this in a compound command with ( ) and run in the background
  1019.             ( tmux send-keys -t $WARNTARGET "say $WARNMESG_TEXT" C-m
  1020.             tmux send-keys -t $WARNTARGET "sm_csay $WARNMESG_TEXT" C-m
  1021.             sleep 2
  1022.             tmux send-keys -t $WARNTARGET "say $WARNMESG_TEXT" C-m
  1023.             tmux send-keys -t $WARNTARGET "sm_csay $WARNMESG_TEXT" C-m
  1024.             sleep 2
  1025.             tmux send-keys -t $WARNTARGET "say $WARNMESG_TEXT" C-m
  1026.             tmux send-keys -t $WARNTARGET "sm_csay $WARNMESG_TEXT" C-m
  1027.             sleep 2
  1028.             tmux send-keys -t $WARNTARGET "say $WARNMESG_TEXT" C-m
  1029.             tmux send-keys -t $WARNTARGET "sm_csay $WARNMESG_TEXT" C-m
  1030.             echo "  Sent in-game warnings to $WARNTARGET" ) &
  1031.         done
  1032.         wait # Wait for the backgrounded warnings to finish. This is a simple way of doing it in parallel.
  1033.         echo "Done warning active installations."
  1034.     fi
  1035. }
  1036.  
  1037. f_autoupdate_stopactivechildren() {
  1038.     # Shut down active children prior to updating the srcds master.
  1039.     # FIXME: Should probably merge this with f_autoupdate.
  1040.     #
  1041.     if [[ -z "$ACTIVECHILD_LIST" ]] ; then
  1042.         echo "No active child srcds instances to shut down."
  1043.         echo ""
  1044.     else
  1045.         echo "Stopping all active children: "
  1046.         SHUTDOWN_TEXT="ATTENTION: A server update has been released! The server will now restart."
  1047.         for STOPTARGET in $ACTIVECHILD_LIST ; do
  1048.             # Warn all active children of a particular master installation GAMETYPE.
  1049.             ( tmux send-keys -t $STOPTARGET "say $SHUTDOWN_TEXT" C-m
  1050.             tmux send-keys -t $STOPTARGET "sm_csay $SHUTDOWN_TEXT" C-m
  1051.             sleep 2
  1052.             tmux send-keys -t $STOPTARGET "say $SHUTDOWN_TEXT" C-m
  1053.             tmux send-keys -t $STOPTARGET "sm_csay $SHUTDOWN_TEXT" C-m
  1054.             sleep 2
  1055.             tmux send-keys -t $STOPTARGET "say $SHUTDOWN_TEXT" C-m
  1056.             tmux send-keys -t $STOPTARGET "sm_csay $SHUTDOWN_TEXT" C-m
  1057.             sleep 2
  1058.             IN_INSTALLID="$STOPTARGET" ; f_stop 1> /dev/null
  1059.             echo "  $STOPTARGET stopped" ) &
  1060.         done
  1061.         wait # Wait for the backgrounded warnings to finish. This is a simple way of doing it in parallel.
  1062.         echo "Done stopping active installations."
  1063.         echo ""
  1064.     fi
  1065. }
  1066.  
  1067. f_autoupdate() {
  1068.     # Automatically update all installations.
  1069.     # Masters get updates via SteamCMD/HLDSUpdateTool, and linked installs get stopped/relinked/started.
  1070.     #
  1071.     # Set a global MAILTO address.
  1072.     AUTOUPDATE_MAILTO=$WRENCH_MAILTO
  1073.     #
  1074.     # FIXME: cleanup trap not triggering here. I suspect it is the log redirect via "exec > >" below.
  1075.     # Update lock system for autoupdates.
  1076.     if [[ -f "$AUTOUPDATE_LOCKFILE" ]] ; then
  1077.         echo ""
  1078.         echo "$MYNAME is already running at PID \"$(cat $AUTOUPDATE_LOCKFILE)\"."
  1079.         echo "This is normal if updates are already in progress."
  1080.         echo "PID/Lockfile: $AUTOUPDATE_LOCKFILE"
  1081.         echo ""
  1082.         return 5
  1083.     fi
  1084.     #
  1085.     # Get a list of all MASTER installations.
  1086.     LIST_MASTERS=$($SQLCMD "select INSTALLID from inst where INSTTYPE='master';")
  1087.     #
  1088.     if [[ -z "$LIST_MASTERS" ]] ; then
  1089.         echo ""
  1090.         echo "No master srcds installations found to update."
  1091.         echo ""
  1092.         return 0
  1093.     fi
  1094.     AUTOUPDATING=1
  1095.     echo "$MYPID" > "$AUTOUPDATE_LOCKFILE" # Set the autoupdate lockfile.
  1096.     #
  1097.     # This is a dirty hack to tee our stdout and stderr to a logfile.
  1098.     # WARNING: This smushes stderr into stdout and can not be undone.
  1099.     if [[ ! -d "$AUTOUPDATE_LOGDIR" ]] ; then
  1100.         mkdir "$AUTOUPDATE_LOGDIR" || { echoerr "ERROR: mkdir \$AUTOUPDATE_LOGDIR failed!" ; }
  1101.     fi
  1102.     exec > >(tee $AUTOUPDATE_LOGFILE) 2>&1
  1103.     #
  1104.     echo ""
  1105.     echo "Searching for updates to srcds MASTER installations: "
  1106.     echo "Start time is: $(date), RUNID=$RUNID"
  1107.     echo ""
  1108.     #
  1109.     # Iterate through the list of MASTER installs, get the local and remote versions, and add to the update list if needed
  1110.     for EACH in $LIST_MASTERS ; do
  1111.         echo "--"
  1112.         echo "Checking: $EACH"
  1113.         IN_INSTALLID="$EACH" ; f_loaddbinst # Load the DB info for the installation.
  1114.         if [[ "$LOADDBINSTINFO_FAIL" == 1 ]] ; then continue ; fi
  1115.         echo "$EACH: Local steam.inf filepath: $STEAMINF_FILE"
  1116.         if [[ ! -r "$STEAMINF_FILE" ]] ; then
  1117.             echoerr "ERROR: Unable to read STEAMINF file. Skipping MASTER installation $EACH"
  1118.             echoerr ""
  1119.             continue
  1120.         fi
  1121.         f_getlocalver
  1122.         if [[ -z "$LOCAL_VER" ]] ; then
  1123.             echoerr "ERROR: Unable to obtain the local version. Skipping $EACH."
  1124.             echoerr ""
  1125.             continue
  1126.         fi
  1127.         echo "$EACH: Local version: $LOCAL_VER"
  1128.         f_getup2datereqver
  1129.         echo "$EACH: Remote UpToDateCheck URL is: $UPTODATECHECK_URL"
  1130.         if [[ -z "$REMOTE_VER" ]] ; then
  1131.             echoerr "ERROR: Unable to obtain the remote version. Skipping $EACH."
  1132.             echoerr ""
  1133.             continue
  1134.         fi
  1135.         echo "$EACH: Remote version: $REMOTE_VER"
  1136.         # Test that we are allowed to update this installation.
  1137.         if [[ ! "$ALLOWUPDATE" == 1 ]] ; then
  1138.             echo "NOTE: Updating this installation is not allowed. Skipping $EACH."
  1139.             echo ""
  1140.             continue
  1141.         fi
  1142.         if [[ "$LOCAL_VER" == "$REMOTE_VER" ]]; then
  1143.             echo "$EACH: Result: Up to date."
  1144.         else
  1145.             echo "$EACH: Result: Installation out of date. Added to updatelist."
  1146.             # Append to the update list.
  1147.             if [[ -z $MASTERUPDATE_LIST ]] ; then
  1148.                 MASTERUPDATE_LIST="$EACH"
  1149.             else
  1150.                 MASTERUPDATE_LIST="$MASTERUPDATE_LIST $EACH"
  1151.             fi
  1152.         fi
  1153.     done
  1154.     echo "--"
  1155.     echo ""
  1156.     if [[ -z "$MASTERUPDATE_LIST" ]] ; then
  1157.         echo "No MASTER installation requires updating at this time."
  1158.     else
  1159.         # Since we have real work to do, we will record as such, for later use.
  1160.         AUTOUPDATING_SEENUPDATES=1
  1161.         #
  1162.         # Send an update start notification. The update process itself can take awhile.
  1163.         $MAILER -s "$MAIL_SUBJ_PREFIX autoupdate $RUNID started" $AUTOUPDATE_MAILTO <<-ENDMESSAGE
  1164.    
  1165.             Date: $(date)
  1166.             Master Update List: $MASTERUPDATE_LIST
  1167.    
  1168.         ENDMESSAGE
  1169.         #
  1170.         # If any master installations need to be updated, this could affect in-game performance of all active srcds instances.
  1171.         # We need a list of ALL active srcds instances, regardless of type, for in-game maintenance warning notification messages.
  1172.         #
  1173.         f_autoupdate_warnallactive
  1174.         #
  1175.         echo ""
  1176.         echo "The following MASTER installations require updating: "
  1177.         for EACH in $MASTERUPDATE_LIST ; do echo "  $EACH" ; done
  1178.         echo ""
  1179.         #
  1180.         # Sleep for awhile. This is because it I found it common that the UpToDateCheck URL would report an update, but
  1181.         # the repository had not actually yet been updated. This would result in HLDSUpdateTool/SteamCMD being run, exit 0
  1182.         # without having downloaded any files, and then the auto-update script runs again because the update was not correctly
  1183.         # downloaded the first time. This should help prevent such failures.
  1184.         # Take into consideration the warning sleeps above. In total, we should wait 60-120 seconds?
  1185.         echo -n "Sleeping for $AUTOUPDATE_SLEEP1 seconds:"
  1186.         # sleep "$AUTOUPDATE_SLEEP1"
  1187.         AUTOUPDATE_SLEEP1_COUNTDOWN="$AUTOUPDATE_SLEEP1"
  1188.         while [[ "$AUTOUPDATE_SLEEP1_COUNTDOWN" -gt 0 ]] ; do
  1189.             echo -n "."
  1190.             AUTOUPDATE_SLEEP1_COUNTDOWN=$(( $AUTOUPDATE_SLEEP1_COUNTDOWN - 1))
  1191.             sleep 1
  1192.         done
  1193.         echo "Done"
  1194.         echo ""
  1195.         #
  1196.         # Now that we have warned all active instances, let's carry on with processing each master which needs an update.
  1197.         #
  1198.         for CURRENTMASTER in $MASTERUPDATE_LIST ; do
  1199.             # For each master, we need to relink it's children, restart each, etc.
  1200.             #
  1201.             ALLCHILD_LIST="" # All linked children for each master.
  1202.             ACTIVECHILD_LIST="" # All running/active linked children for each master.
  1203.             INACTIVECHILD_LIST="" # All non-running/inactive linked children for each master.
  1204.             GAMETYPE="" # The GAMETYPE for each master. We don't have to reset this like the _LISTs above, but whatever.
  1205.             #
  1206.             echo "Processing master $CURRENTMASTER: "
  1207.             echo ""
  1208.             IN_INSTALLID="$CURRENTMASTER" ; f_loaddbinst # Load the DB info for the installation.
  1209.             if [[ "$LOADDBINSTINFO_FAIL" == 1 ]] ; then continue ; fi
  1210.             ALLCHILD_LIST=$($SQLCMD "select INSTALLID from inst where GAMETYPE='$GAMETYPE' and INSTTYPE='linked';")
  1211.             for CHILD in $ALLCHILD_LIST ; do
  1212.                 # For each child installation, we need to know if it is or is not currently active.
  1213.                 IN_INSTALLID="$CHILD" ; f_loaddbinst ; f_getpid # Load the DB info for the installation.
  1214.                 if [[ "$LOADDBINSTINFO_FAIL" == 1 ]] ; then continue ; fi
  1215.                 if [[ "$RUNSTATUS" == "running" ]] ; then
  1216.                     if [[ -z "$ACTIVECHILD_LIST" ]] ; then
  1217.                         ACTIVECHILD_LIST="$CHILD"
  1218.                     else
  1219.                         ACTIVECHILD_LIST="$ACTIVECHILD_LIST $CHILD"
  1220.                     fi
  1221.                 else
  1222.                     # If RUNSTATUS is "crashed" or "error", the installation will be relinked, but it will not get restarted.
  1223.                     if [[ -z "$INACTIVECHILD_LIST" ]] ; then
  1224.                         INACTIVECHILD_LIST="$CHILD"
  1225.                     else
  1226.                         INACTIVECHILD_LIST="$INACTIVECHILD_LIST $CHILD"
  1227.                     fi
  1228.                 fi
  1229.             done
  1230.             if [[ -z "$ACTIVECHILD_LIST" ]] ; then
  1231.                 echo "$CURRENTMASTER has no active linked children."
  1232.                 echo ""
  1233.             else
  1234.                 echo "Active linked children of $CURRENTMASTER: "
  1235.                 for EACH in $ACTIVECHILD_LIST ; do echo "  $EACH" ; done
  1236.                 echo ""
  1237.             fi
  1238.             if [[ -z "$INACTIVECHILD_LIST" ]] ; then
  1239.                 echo "$CURRENTMASTER has no inactive linked children."
  1240.                 echo ""
  1241.             else
  1242.                 echo "Inactive linked children of $CURRENTMASTER: "
  1243.                 for EACH in $INACTIVECHILD_LIST ; do echo "  $EACH" ; done
  1244.                 echo ""
  1245.             fi
  1246.             #
  1247.             # Shut down active/running children.
  1248.             f_autoupdate_stopactivechildren
  1249.                 #
  1250.             # Update the master.
  1251.             echo "Updating master installation: $CURRENTMASTER"
  1252.             IN_INSTALLID="$CURRENTMASTER"
  1253.             f_update ; X_UPDATE_MAIN="$?"
  1254.             # Depending upon the updater success or failure, we continue or shut everything down.
  1255.             if [[ "$X_UPDATE_MAIN" == 0 ]] ; then
  1256.                 # For ACTIVECHILD_LIST, we need to relink and start.
  1257.                 # For INACTIVECHILD_LIST, we need to relink, but not start.
  1258.                 #
  1259.                 for EACH in $INACTIVECHILD_LIST ; do
  1260.                     echo "Relinking linked child installation: $EACH"
  1261.                     IN_INSTALLID="$EACH"
  1262.                     f_relink 1> /dev/null
  1263.                 done
  1264.                 for EACH in $ACTIVECHILD_LIST ; do
  1265.                     echo "Relinking and restarting linked child installation: $EACH"
  1266.                     IN_INSTALLID="$EACH"
  1267.                     f_relink 1> /dev/null
  1268.                     f_start 1> /dev/null
  1269.                 done
  1270.             else
  1271.                 echoerr "Marking the installation ALLOWUPDATE=0. This will prevent further update attempts."
  1272.                 echoerr "Manual intervention will be required to fix this."
  1273.                 $SQLCMD "update inst set ALLOWUPDATE='0' where INSTALLID='$CURRENTMASTER';"
  1274.                 # FIXME: need to mark the master as unrunnable, and children need to respect that it is in a broken state.
  1275.             fi
  1276.    
  1277.         echo ""
  1278.         done
  1279.     fi
  1280.     #
  1281.     echo ""
  1282.     echo "Finish time is: $(date), RUNID=$RUNID"
  1283.     echo ""
  1284.     #
  1285.     # FIXME: Need to close the logfile tee.
  1286.     #
  1287.     if [[ ! "$AUTOUPDATING_SEENUPDATES" = 1 ]] ; then
  1288.         # If nothing interesting happened, let's just delete the log file, since we don't care.
  1289.         rm -f "$AUTOUPDATE_LOGFILE"
  1290.     else
  1291.         # Send an email log.
  1292.         $MAILER -s "$MAIL_SUBJ_PREFIX autoupdate $RUNID finished" -a $AUTOUPDATE_LOGFILE $AUTOUPDATE_MAILTO <<-ENDMESSAGE
  1293.    
  1294.             Date: $(date)
  1295.             Master Update List: $MASTERUPDATE_LIST
  1296.    
  1297.         ENDMESSAGE
  1298.     fi
  1299.     # Remove the lock/PID file.
  1300.     rm -f "$AUTOUPDATE_LOCKFILE" &> /dev/null
  1301.     AUTOUPDATING=0
  1302. }
  1303.  
  1304. f_relink() {
  1305.     # Refresh the links on a linked/child installation to it's master installation.
  1306.     # This is basically the upgrade procedure for a linked installation.
  1307.     #
  1308.     f_loaddbinst
  1309.     if [[ "$LOADDBINSTINFO_FAIL" == 1 ]] ; then return 1 ; fi
  1310.     # Verify that we are working on a linked installation. Quit otherwise.
  1311.     if [[ ! "$INSTTYPE" == linked ]] ; then
  1312.         echoerr ""
  1313.         echoerr "ERROR: $INSTALLID is not a linked installation type. Can not relink."
  1314.         echoerr ""
  1315.         return 1
  1316.     fi
  1317.     # If ALLOWUPDATE is not 1, do not allow relinking.
  1318.     if [[ ! "$ALLOWUPDATE" == 1 ]] ; then
  1319.         echoerr ""
  1320.         echoerr "ERROR: ALLOWUPDATE not set to allow updating on this installation. Can not relink."
  1321.         echoerr ""
  1322.         return 1
  1323.     fi
  1324.     #
  1325.     f_quitifrunning # Do not proceed if the installation is actively running.
  1326.     if [[ "$RUNQUIT" == 1 ]] ; then return 1 ; fi
  1327.     #
  1328.     # Test that a master exists. This is unlikely to be a problem, but it is possible.
  1329.     if [[ -z "$USEMASTER" ]] ; then
  1330.         echoerr ""
  1331.         echoerr "ERROR: No master installation for game type \"$GAMETYPE\" found in the database. Can not relink."
  1332.         echoerr ""
  1333.         return 1
  1334.     fi
  1335.     # Verify that the master installation directory exists, and err out if there is a problem.
  1336.     if [[ ! -d "$APPDIR/$USEMASTER" ]] ; then
  1337.         echoerr ""
  1338.         echoerr "ERROR: Master installation \"$USEMASTER\" found in the database, but no directory found at: $APPDIR/$USEMASTER"
  1339.         echoerr "  Can not relink."
  1340.         echoerr ""
  1341.         return 1
  1342.     fi
  1343.     echo ""
  1344.     echo -n "Relinking $INSTALLID to master $USEMASTER: "
  1345.     $NICECMD lns -q -r "$APPDIR/$USEMASTER/"* "$INSTALLDIR" ; X_LNS=$?
  1346.     if [[ ! "$X_LNS" == 0 ]] ; then
  1347.         echoerr ""
  1348.         echoerr "ERROR: Linking $INSTALLID to $USEMASTER failed. lns exit code $X_LNS."
  1349.         echoerr ""
  1350.         return 1
  1351.     fi
  1352.     echo "Done"
  1353.     echo ""
  1354.     #
  1355.     # Silently remove the InstallRecord.blob file, which is not relevant to linked installations.
  1356.     # FIXME: I think only HLDSUpdateTool did this?
  1357.     $NICECMD rm -f "$INSTALLDIR/InstallRecord.blob" 2> /dev/null
  1358.     # FIXME: Should add a blacklist file to delete files which exist in the master but are not wanted in the linked/child install.
  1359.     #   Alternatively, an overlay filesystem would do this.
  1360.     #
  1361.     # Remove old dangling symlinks
  1362.     echo "Looking for old dangling symlinks and removing them: "
  1363.     symlinks -d -r "$INSTALLDIR"
  1364.     echo "Done"
  1365.     # NOTE: We don't delete old empty directories, because we have no way to identify them. Could use "find -type d -empty" though.
  1366.     #
  1367.     # Delete the SteamCMD temp directories from linked installs. These are the 40-character 0-9a-f directories (SHA1?).
  1368.     for EACH in $(find "$INSTALLDIR" -maxdepth 1 -mindepth 1 -type d) ; do
  1369.         ( EACHD=$(basename $EACH | egrep "^[0-9a-f]{40}$")
  1370.         if [[ -n "$EACHD" ]] ; then
  1371.             # echoerr "DEBUG: Found garbage SteamCMD tempdir in linked installation: $EACHD"
  1372.             # rm -rf "$INSTALLDIR/$EACHD"
  1373.             # The following commands are much safer than rm -rf.
  1374.             # echoerr "DEBUG: 1 $NICECMD find \"$INSTALLDIR/$EACHD\" -type l -lname \"*../$USEMASTER/*\" -exec rm -f '{}' +"
  1375.             $NICECMD find "$INSTALLDIR/$EACHD" -type l -lname "*../$USEMASTER/*" -exec rm -f '{}' +
  1376.             # echoerr "DEBUG: 2 $NICECMD find \"$INSTALLDIR/$EACHD\" -type d -empty -delete"
  1377.             $NICECMD find "$INSTALLDIR/$EACHD" -type d -empty -delete
  1378.         fi )
  1379.     done
  1380.     #
  1381.     echo ""
  1382.     echo "Relinking $INSTALLID complete."
  1383.     echo ""
  1384.  
  1385. }
  1386.  
  1387. f_delink() {
  1388.     # Delete the links from a linked/child installation to it's master installation.
  1389.     # NOTE: This should not touch other links which are not related to the master.
  1390.     #
  1391.     f_loaddbinst
  1392.     if [[ "$LOADDBINSTINFO_FAIL" == 1 ]] ; then return 1 ; fi
  1393.     # Verify that we are working on a linked installation. Quit otherwise.
  1394.     if [[ ! "$INSTTYPE" == linked ]] ; then
  1395.         echoerr ""
  1396.         echoerr "ERROR: $INSTALLID is not a linked installation type. Can not delink."
  1397.         echoerr ""
  1398.         return 1
  1399.     fi
  1400.     #
  1401.     # Test that a master exists. This is unlikely to be a problem, but it is possible.
  1402.     # Note that we do not test for a removed master, since that is a valid scenario.
  1403.     if [[ -z "$USEMASTER" ]] ; then
  1404.         echoerr ""
  1405.         echoerr "ERROR: No master installation for game type \"$GAMETYPE\" found in the database. Can not delink."
  1406.         echoerr ""
  1407.         return 1
  1408.     fi
  1409.     f_quitifrunning # Do not proceed if the installation is actively running.
  1410.     if [[ "$RUNQUIT" == 1 ]] ; then return 1 ; fi
  1411.     #
  1412.     echo ""
  1413.     echo "This will delete all symbolic links in the $INSTALLID installation directory which refer to the master $USEMASTER."
  1414.     echo "This will NOT delete symbolic links which refer to other locations."
  1415.     echo ""
  1416.     echo "Are you sure you want to do this?"
  1417.     echo ""
  1418.     echo "VERIFICATION: Delink $INSTALLID from $USEMASTER ?"
  1419.     read -e -r -p "y/N: " -i "" REPLY
  1420.     if [[ ! "$REPLY" == [yY] ]]; then
  1421.         echo ""
  1422.         echo "Quitting."
  1423.         echo ""
  1424.         return 0
  1425.     fi
  1426.     echo ""
  1427.     #
  1428.     # NOTE: This is not completely safe. We are depending upon that -lname pattern, but it's close enough
  1429.     $NICECMD find "$INSTALLDIR" -type l -lname "*../$USEMASTER/*" -exec rm -f '{}' +
  1430.     echo "Delinking completed with exit code $?"
  1431.     echo ""
  1432.     # Also offer to delete worthless empty directories that lns created during the linking process.
  1433.     echo "Would you also like to delete all empty directories in the $INSTALLID installation directory?"
  1434.     echo "This includes ALL directories which are empty of anything other than additionally nested empty directories."
  1435.     echo ""
  1436.     echo "VERIFICATION: Delete empty directories in $INSTALLID ?"
  1437.     read -e -r -p "y/N: " -i "" REPLY
  1438.     if [[ ! "$REPLY" == [yY] ]]; then
  1439.         echo ""
  1440.         echo "Quitting."
  1441.         echo ""
  1442.         return 0
  1443.     else
  1444.         find "$INSTALLDIR" -type d -empty -delete
  1445.         echo ""
  1446.         echo "Done deleting empty directories."
  1447.         echo ""
  1448.     fi
  1449.     # FIXME: We should probably mark this installation as ALLOWSTART=0 in the inst table.
  1450. }
  1451.  
  1452. f_cleandemos() {
  1453.     # Manage SourceTV demo files.
  1454.     #
  1455.     DEMOARCHIVE="$BASEDIR/demos"
  1456.     echo -n "Cleaning up SourceTV demos: "
  1457.     if [[ ! -d "$DEMOARCHIVE" ]] ; then
  1458.             echo ""
  1459.             echo "$DEMOARCHIVE does not exist. Creating it now."
  1460.             $NICECMD mkdir "$DEMOARCHIVE" && chmod 770 "$DEMOARCHIVE"
  1461.     fi
  1462.     $NICECMD find $DEMOARCHIVE -maxdepth 1 -type f -name "*auto-*.dem.bz2" -mtime +$DEMOSEXPIRE -print0 | xargs -0 -r rm -f
  1463.     $NICECMD find $BASEDIR -maxdepth 1 -type f -name "*auto-*.dem" -cmin +3 -print0 | xargs -0 -r mv $DEMOARCHIVE
  1464.     $NICECMD find $DEMOARCHIVE -maxdepth 1 -type f -name "auto-*.dem" -print0 | xargs -0 -r bzip2
  1465.     echo "Done"
  1466. }
  1467.  
  1468. f_cleanlogs() {
  1469.     # Manage logfiles.
  1470.     #
  1471.     LOGARCHIVE="$BASEDIR/logs"
  1472.     echo -n "Cleaning up logfiles: "
  1473.     if [[ ! -d "$LOGARCHIVE" ]] ; then
  1474.             echo ""
  1475.             echo "$LOGARCHIVE does not exist. Creating it now."
  1476.             $NICECMD mkdir "$LOGARCHIVE" && chmod 770 "$LOGARCHIVE"
  1477.     fi
  1478.     $NICECMD find $LOGARCHIVE -maxdepth 1 -type f -name "*.log.bz2" -mtime +$LOGSEXPIRE -print0 | xargs -0 -r rm -f
  1479.     if [[ -f $BASEDIR/console.log ]] ; then
  1480.         # Note that the timestamp on the log file is from when it is closed, not opened. Might want to change this?
  1481.         $NICECMD cp -ap -n $BASEDIR/console.log $LOGARCHIVE/console-$(date +%Y%m%d%H%M%S).log && echo "" > $BASEDIR/console.log
  1482.     fi
  1483.     $NICECMD find $LOGARCHIVE -maxdepth 1 -type f -name "*.log" -mtime +1 -print0 | xargs -0 -r bzip2
  1484.     echo "Done"
  1485. }
  1486.  
  1487. f_cleandownloads() {
  1488.     # Clean up downloads files.
  1489.     #
  1490.     if [[ -d "$BASEDIR/download/user_custom" ]] ; then
  1491.         echo -n "Cleaning up download/user_custom files: "
  1492.         $NICECMD find $BASEDIR/download/user_custom -maxdepth 2 -type f -name "????????.dat" -mtime +$DOWNLOADSEXPIRE -print0 | xargs -0 -r rm -f
  1493.         # Also delete empty directories
  1494.         $NICECMD find $BASEDIR/download/user_custom -maxdepth 1 -type d -empty -delete
  1495.         echo "Done"
  1496.     fi
  1497. }
  1498.  
  1499. f_cleanreplays() {
  1500.     # Clean up old replay files, since srcds does a very poor job of doing that itself.
  1501.     #
  1502.     # Do nothing if REPLAY_ENABLED=0.
  1503.     if [[ "$REPLAY_ENABLED" == 0 ]] ; then
  1504.         return 0
  1505.     fi
  1506.     echo -n "Cleaning up replay files: "
  1507.     if [[ -d "$BASEDIR/replay/server/blocks" ]] ; then
  1508.         $NICECMD find $BASEDIR/replay/server/blocks -maxdepth 1 -type f -name "*.dmx" -mtime +$REPLAYSEXPIRE -print0 | xargs -0 -r rm -f
  1509.         $NICECMD find $BASEDIR/replay/server/sessions -maxdepth 1 -type f -name "*.dmx" -mtime +$REPLAYSEXPIRE -print0 | xargs -0 -r rm -f
  1510.     fi
  1511.     # Clean up the public-accessible www dir
  1512.     if [[ -d "$REPLAYBASEDIR/$CLEANTARGET/replay" ]] ; then
  1513.         $NICECMD find $REPLAYBASEDIR/$CLEANTARGET/replay -maxdepth 1 -type f -name "*.dmx" -mtime +$REPLAYSEXPIRE -print0 | xargs -0 -r rm -f
  1514.         $NICECMD find $REPLAYBASEDIR/$CLEANTARGET/replay -maxdepth 1 -type f -name "*.block" -mtime +$REPLAYSEXPIRE -print0 | xargs -0 -r rm -f
  1515.     fi
  1516.     echo "Done"
  1517. }
  1518.  
  1519. f_autocleanup() {
  1520.     # Automatically clean up installations marked via the "AUTOCLEAN" column in the inst table.
  1521.     #
  1522.     # FIXME: This entire func and it's subfuncs need review. This stuff is old and came from the old srcds-control.
  1523.     # FIXME: Add a lockfile for autocleanup, just like autoupdate.
  1524.     #
  1525.     # Get the full list of runnable srcds installs. Excludes masters, then figure out which should be cleaned.
  1526.     AUTOCLEANUP_LIST=$($SQLCMD "select INSTALLID from inst where AUTOCLEANUP='1' and not INSTTYPE='master';")
  1527.     # echoerr "DEBUG: cleanup target list is: $AUTOCLEANUP_LIST"
  1528.     for EACH in $AUTOCLEANUP_LIST ; do
  1529.         # FIXME: We don't f_loaddbinst here, but probably should.
  1530.         #   If we do load the inst table, we need to move BASEDIR declarations into f_loaddbinst.
  1531.         ( CLEANTARGET=$EACH
  1532.         CLEANTARGETPATH=$APPDIR/$CLEANTARGET
  1533.         GAMETYPE=$($SQLCMD "select GAMETYPE from inst where INSTALLID='$CLEANTARGET';")
  1534.         #
  1535.         # Validate the installation directory before proceeding.
  1536.         if [[ ! -d "$CLEANTARGETPATH" ]] ; then
  1537.             echoerr ""
  1538.             echoerr "ERROR: Installation directory for \"$CLEANTARGET\" not found."
  1539.             echoerr "  Continuing on to next target."
  1540.             echoerr ""
  1541.             continue
  1542.         fi
  1543.         # echo "DEBUG: Cleanup target $CLEANTARGET GAMETYPE is $GAMETYPE"
  1544.         #
  1545.         # cd to /tmp for safety reasons.
  1546.         cd /tmp
  1547.         #
  1548.         # FIXME: BASEDIR probably needs to be in the db rather than here.
  1549.         case "$GAMETYPE" in
  1550.             'css')
  1551.                 BASEDIR=$CLEANTARGETPATH/cstrike
  1552.                 echo "Cleaning up $CLEANTARGET"
  1553.                 f_cleandemos
  1554.                 f_cleanlogs
  1555.                 f_cleandownloads
  1556.             ;;
  1557.             'hl2dm')
  1558.                 BASEDIR=$CLEANTARGETPATH/hl2mp
  1559.                 echo "Cleaning up $CLEANTARGET"
  1560.                 f_cleandemos
  1561.                 f_cleanlogs
  1562.                 f_cleandownloads
  1563.             ;;
  1564.             'l4d')
  1565.                 BASEDIR=$CLEANTARGETPATH/left4dead
  1566.                 echo "Cleaning up $CLEANTARGET"
  1567.                 f_cleandemos
  1568.                 f_cleanlogs
  1569.             ;;
  1570.             'l4d2')
  1571.                 BASEDIR=$CLEANTARGETPATH/left4dead2
  1572.                 echo "Cleaning up $CLEANTARGET"
  1573.                 f_cleandemos
  1574.                 f_cleanlogs
  1575.             ;;
  1576.             'tf2')
  1577.                 BASEDIR=$CLEANTARGETPATH/tf
  1578.                 echo "cleaning up $CLEANTARGET"
  1579.                 f_cleandemos
  1580.                 f_cleanlogs
  1581.                 f_cleandownloads
  1582.                 f_cleanreplays
  1583.             ;;
  1584.             'fof')
  1585.                 BASEDIR=$CLEANTARGETPATH/fof
  1586.                 echo "Cleaning up $CLEANTARGET"
  1587.                 f_cleandemos
  1588.                 f_cleanlogs
  1589.                 f_cleandownloads
  1590.             ;;
  1591.             # FIXME: CSGO needs to be added.
  1592.             *)
  1593.                 echo "$CLEANTARGET: GAMETYPE \"$GAMETYPE\" not supported for cleanup."
  1594.         esac
  1595.         #
  1596.         echo "Done cleaning up $CLEANTARGET."
  1597.         echo "" )
  1598.     done
  1599. }
  1600.  
  1601. f_lockcheck() {
  1602.     # Check if a lock has been placed on the installation to prevent a train wreck.
  1603.     #
  1604.     # FIXME: We also need to check the master to be sure it is not being updated.
  1605.     if [[ -f "$LOCKFILE" ]] ; then
  1606.         LOCKFAIL=1
  1607.         echoerr ""
  1608.         echoerr "ERROR: lockfile active. Check to see if some other process is working on this installation."
  1609.         echoerr "  Lockfile: $LOCKFILE"
  1610.         echoerr ""
  1611.         return 1
  1612.     fi
  1613. }
  1614.  
  1615. f_uninstall() {
  1616.     # Uninstall/remove a server instance.
  1617.     # FIXME: This entire process needs review.
  1618.     #
  1619.     echo ""
  1620.     echo "This will completely remove a srcds installation, including the database entry and installation directory."
  1621.     echo "WARNING: If you uninstall a master installation, any linked children installations will become broken."
  1622.     echo ""
  1623.     echo "  The following installations were found in the DB. If the installation you are looking "
  1624.     echo "  for isn't here, then it may have already been removed from the database or is invalid."
  1625.     echo ""
  1626.     INSTALLATIONS_LIST=$($SQLCMD "select INSTALLID from inst;")
  1627.     echo "Please choose an installation to uninstall/delete:"
  1628.     echo ""
  1629.     select UNINSTALL_SELECT in $INSTALLATIONS_LIST QUIT ; do
  1630.         if [[ -z "$UNINSTALL_SELECT" ]] ; then
  1631.             echo "Invalid entry. Try again."
  1632.             continue
  1633.         fi
  1634.         if [[ "$UNINSTALL_SELECT" == "QUIT" ]] ; then
  1635.             echo ""
  1636.             echo "Quitting."
  1637.             echo ""
  1638.             return 1
  1639.         fi
  1640.         echo "For verification, please type the exact name of the installation you just selected."
  1641.         read -e -r -p "Name: " -i "" IN_UNINSTALL
  1642.         if [[ "$UNINSTALL_SELECT" == "$IN_UNINSTALL" ]] ; then
  1643.             break
  1644.         else
  1645.             echo "Verification failed. Try again?"
  1646.             continue
  1647.         fi
  1648.     done
  1649.     echo ""
  1650.     echo "FINAL VERIFICATION: Delete ${UNINSTALL_SELECT}?"
  1651.     read -e -r -p "y/N: " -i "" IN_UNINSTALLFINAL
  1652.     if [[ ! "$IN_UNINSTALLFINAL" == [yY] ]]; then
  1653.         echo ""
  1654.         echo "Quitting."
  1655.         echo ""
  1656.         return 0
  1657.     fi
  1658.     echo ""
  1659.     #
  1660.     # FIXME: If certain tests in f_loaddb fail, we may not be able to uninstall. Need to review this.
  1661.     IN_INSTALLID="$UNINSTALL_SELECT"
  1662.     f_loaddbinst # Load the DB inst info here.
  1663.     if [[ "$LOADDBINSTINFO_FAIL" == 1 ]] ; then return 1 ; fi
  1664.     #
  1665.     f_quitifrunning # Do not proceed if the installation is actively running.
  1666.     if [[ "$RUNQUIT" == 1 ]] ; then return 1 ; fi
  1667.     #
  1668.     f_lockcheck # Make sure there isn't a lock on the installation.
  1669.     if [[ "$LOCKFAIL" == 1 ]] ; then return 1 ; fi
  1670.     #
  1671.     echo -n "Deleting database record: "
  1672.     $SQLCMD "delete from inst where INSTALLID='$UNINSTALL_SELECT';" ; X_INSTDBDELETE=$?
  1673.     if [[ ! "$X_INSTDBDELETE" == 0 ]] ; then
  1674.         echoerr ""
  1675.         echoerr "WARNING: Request to delete \"$UNINSTALL_SELECT\" from the database failed. Exit code $X_INSTDBDELETE. Continuing anyway."
  1676.     fi
  1677.     echo "Done"
  1678.     #
  1679.     # If this is a master type installation, we must unlock it before we try to delete the installation directory.
  1680.     if [[ "$INSTTYPE" == "master" ]] ; then
  1681.         f_unlockmaster ; X_UNLOCKMASTER="$?" # Unlock the master for writing.
  1682.         if [[ ! "$X_UNLOCKMASTER" == 0 ]] ; then return 1 ; fi
  1683.     fi
  1684.     #
  1685.     if [[ -d "$APPDIR/$UNINSTALL_SELECT" ]] ; then 
  1686.         echo -n "Removing installation directory: "
  1687.         rm -rf "$APPDIR/$UNINSTALL_SELECT" || ( echoerr "WARNING: Error while removing installation directory. Continuing anyway." )
  1688.         echo "Done"
  1689.     else
  1690.         echoerr "WARNING: Installation dir $APPDIR/$UNINSTALL_SELECT not found. Continuing anyway."
  1691.     fi
  1692.     #
  1693.     if [[ "$REPLAY_ENABLED" == 1 ]] && [[ -d "$REPLAYBASEDIR/$UNINSTALL_SELECT" ]] ; then
  1694.         echo -n "Removing the replay web server directory: "
  1695.         rm -rf "$REPLAYBASEDIR/$UNINSTALL_SELECT" || ( echoerr "WARNING: Error while removing replay web server directory. Continuing anyway." )
  1696.         echo "Done"
  1697.     fi
  1698.     echo ""
  1699.     echo "$UNINSTALL_SELECT uninstall completed."
  1700.     echo ""
  1701. }
  1702.  
  1703. f_deletegametype() {
  1704.     # FIXME: This is not yet used.
  1705.     # Delete a GAMETYPE from the srcinfo table.
  1706.     #
  1707.     echo ""
  1708.     echo "This process will delete a srcds game type from the database."
  1709.     echo ""
  1710.     echo "Please choose which game type you wish to delete:"
  1711.     echo ""
  1712.     GAMETYPES_LIST=$($SQLCMD "select GAMETYPE from srcinfo;")
  1713.     select DELETEGAMETYPE_SELECT in $GAMETYPES_LIST QUIT ; do
  1714.         if [[ -z "$DELETEGAMETYPE_SELECT" ]] ; then
  1715.             echo "Invalid entry. Try again."
  1716.             continue
  1717.         fi
  1718.         if [[ "$DELETEGAMETYPE_SELECT" == "QUIT" ]] ; then
  1719.             echo ""
  1720.             echo "Quitting."
  1721.             echo ""
  1722.             return 1
  1723.         fi
  1724.         echo "For verification, please type the exact name of the game type you just selected."
  1725.         read -e -r -p "Name: " -i "" IN_DELETE
  1726.         if [[ "$DELETEGAMETYPE_SELECT" == "$IN_DELETE" ]] ; then
  1727.             break
  1728.         else
  1729.             echoerr ""
  1730.             echoerr "Verification failed."
  1731.             echoerr ""
  1732.             return 1
  1733.         fi
  1734.     done
  1735.     echo ""
  1736.     # There must be no leftover dependancy upon the GAMETYPE. If there is one, fail.
  1737.     GAMETYPEDEPENDANCY_LIST=$($SQLCMD "select INSTALLID from inst where GAMETYPE='$DELETEGAMETYPE_SELECT';")
  1738.     if [[ -n "$GAMETYPEDEPENDANCY_LIST" ]] ; then
  1739.         echoerr "ERROR: Unable to delete \"$DELETEGAMETYPE_SELECT\" from the database."
  1740.         echoerr "  A game type can not be deleted so long as at least one installation of it's type remains."
  1741.         echoerr ""
  1742.         echoerr "  The following installations belong to this game type: "
  1743.         echoerr ""
  1744.         echoerr "  $(echo $GAMETYPEDEPENDANCY_LIST | tr '\n' ' ')"
  1745.         echoerr ""
  1746.         echoerr "Quitting."
  1747.         echoerr ""
  1748.         return 1
  1749.     fi
  1750.     echo "FINAL VERIFICATION: Delete ${DELETEGAMETYPE_SELECT}?"
  1751.     read -e -r -p "y/N: " -i "" IN_DELETEFINAL
  1752.     if [[ ! "$IN_DELETEFINAL" == [yY] ]]; then
  1753.         echo ""
  1754.         echo "Quitting."
  1755.         echo ""
  1756.         return 0
  1757.     fi
  1758.     #
  1759.     # Delete the GAMETYPE from the DB.
  1760.     echo ""
  1761.     echo -n "Deleting database record: "
  1762.     $SQLCMD "delete from srcinfo where GAMETYPE='$DELETEGAMETYPE_SELECT';" ; X_DELETEGAMETYPE="$?"
  1763.     if [[ ! "$X_DELETEGAMETYPE" == 0 ]] ; then
  1764.         echoerr ""
  1765.         echoerr "ERROR: Request to delete \"$DELETEGAMETYPE_SELECT\" from the database failed. Exit code $X_DELETEGAMETYPE."
  1766.         echoerr ""
  1767.         return 1
  1768.     fi
  1769.     echo "Done"
  1770.     echo ""
  1771.     echo "Game type successfully deleted."
  1772.     echo ""
  1773.     return 0
  1774. }
  1775.  
  1776. f_choosename() {
  1777.     # Name an installation. We use this on first-time installations(f_installnewlinked), and renames(f_rename).
  1778.     #
  1779.     echo ""
  1780.     echo "Valid characters are a-z, A-Z, and 0-9. No other characters allowed."
  1781.     echo "An automatically-generated prefix will be prepended to the installation name."
  1782.     echo "For example: If you enter \"MyTest1\" and the game type is tf2, the "
  1783.     echo "new installation's full name will be \"tf2-MyTest1\"."
  1784.     echo ""
  1785.     while read -e -r -p "Suffix Name: " -i "" IN_NEWSUFFIX ; do
  1786.         # Validate the input.
  1787.         if [[ -z "$IN_NEWSUFFIX" ]] || (echo "$IN_NEWSUFFIX" | egrep "[[:cntrl:]]|[[:punct:]]|[[:space:]]" &> /dev/null ) ; then
  1788.             echo ""
  1789.             echo "Invalid input detected. Try again."
  1790.         else
  1791.             # Our INSTALLID will be set as IN_INSTALLNAME
  1792.             IN_INSTALLNAME=${GAMETYPE}-${IN_NEWSUFFIX}
  1793.             #
  1794.             # Verify that there isn't an existing installation by that name.
  1795.             INSTALLID_CHECK=$($SQLCMD "select INSTALLID from inst where INSTALLID='$IN_INSTALLNAME';")
  1796.             if [[ "$INSTALLID_CHECK" == "$IN_INSTALLNAME" ]] ; then
  1797.                 echoerr ""
  1798.                 echoerr "ERROR: There is already a srcds installation named \"$IN_INSTALLNAME\" in the database."
  1799.                 echoerr "  Chose another name or remove the offending conflict."
  1800.                 echoerr ""
  1801.                 continue
  1802.             elif [[ -d "$APPDIR/$IN_INSTALLNAME" ]] ; then
  1803.                 echoerr ""
  1804.                 echoerr "ERROR: There is already a directory at \"$APPDIR/$IN_INSTALLNAME\"."
  1805.                 echoerr "  Chose another name or remove the offending conflict."
  1806.                 echoerr ""
  1807.                 continue
  1808.             fi
  1809.             #
  1810.             echo ""
  1811.             echo "Please confirm: The new installation name will be \"$IN_INSTALLNAME\"?"
  1812.             # echo -n "y/N: "
  1813.             read -e -r -p "y/N: " -i "" IN_NEWSUFFIX_CONFIRM
  1814.             if [[ "$IN_NEWSUFFIX_CONFIRM" == [yY] ]]; then
  1815.                 break
  1816.             else
  1817.                 echo ""
  1818.                 echo "Okay then, let's try again. What name do you want?"
  1819.             fi
  1820.  
  1821.         fi
  1822.     done
  1823. }
  1824.  
  1825. f_configinstall() {
  1826.     # Configure an installation. We may be installing a new install, or reconfiguring an existing one.
  1827.     # We get called by either f_installnewlinked or f_reconfig.
  1828.     #Note that master installs have their own function, f_installnewmaster, for NEW installs, but can be reconfigured.
  1829.     #
  1830.     # If configuring as new(install), then use the srcinfo INSTDEF_.
  1831.     # If configuring as a reconfig(reconfig), then use the existing info from the DB.
  1832.     if [[ "$IN_ARG" == "install" ]] ; then
  1833.         # We will use the INSTDEF_ defaults from the srcinfo table to set defaults.
  1834.         # We should have already called f_loaddbsrcinfo from f_install.
  1835.         IPADDR=$INSTDEF_IPADDR
  1836.         CLIENTPORT=$INSTDEF_CLIENTPORT
  1837.         HOSTPORT=$INSTDEF_HOSTPORT
  1838.         TVPORT=$INSTDEF_TVPORT
  1839.         STEAMPORT=$INSTDEF_STEAMPORT
  1840.         ALLOWUPDATE=$INSTDEF_ALLOWUPDATE
  1841.         ALLOWSTART=$INSTDEF_ALLOWSTART
  1842.         BOOTSTART=$INSTDEF_BOOTSTART
  1843.         AUTOCLEANUP=$INSTDEF_AUTOCLEANUP
  1844.         MAILNOTIFY=$INSTDEF_MAILNOTIFY
  1845.         MAILTO=$INSTDEF_MAILTO
  1846.         STARTBIN=$INSTDEF_STARTBIN
  1847.         EVALTXT_STARTBINARGS=$INSTDEF_STARTBINARGS # Note the difference here.
  1848.         RECOVER_CRASH_ENABLE="$INSTDEF_RECOVER_CRASH_ENABLE"
  1849.         RECOVER_WATCHDOG_ENABLE="$INSTDEF_RECOVER_WATCHDOG_ENABLE"
  1850.         RECOVER_WATCHDOG_TEST_INTERVAL="$INSTDEF_RECOVER_WATCHDOG_TEST_INTERVAL"
  1851.         RECOVER_WATCHDOG_POLL_MAX="$INSTDEF_RECOVER_WATCHDOG_POLL_MAX"
  1852.         RECOVER_WATCHDOG_START_WAIT="$INSTDEF_RECOVER_WATCHDOG_START_WAIT"
  1853.         RECOVER_SLEEP="$INSTDEF_RECOVER_SLEEP"
  1854.         INSTTYPE="linked" # We need to specify that this will be a linked install for new linked installations.
  1855.     elif [[ "$IN_ARG" == "reconfig" ]] ; then
  1856.         # f_reconfig should have already called f_loaddbinst, but we set IN_ params to defaults. This is important for master reconfigs.
  1857.         IN_IPADDR=$IPADDR
  1858.         IN_CLIENTPORT=$CLIENTPORT
  1859.         IN_HOSTPORT=$HOSTPORT
  1860.         IN_TVPORT=$TVPORT
  1861.         IN_STEAMPORT=$STEAMPORT
  1862.         IN_ALLOWUPDATE=$ALLOWUPDATE
  1863.         IN_ALLOWSTART=$ALLOWSTART
  1864.         IN_BOOTSTART=$BOOTSTART
  1865.         IN_AUTOCLEANUP=$AUTOCLEANUP
  1866.         IN_MAILNOTIFY=$MAILNOTIFY
  1867.         IN_MAILTO=$MAILTO
  1868.         IN_STARTBIN=$STARTBIN
  1869.         IN_STARTBINARGS=$EVALTXT_STARTBINARGS # Note the difference here.
  1870.         IN_RECOVER_CRASH_ENABLE="$RECOVER_CRASH_ENABLE"
  1871.         IN_RECOVER_WATCHDOG_ENABLE="$RECOVER_WATCHDOG_ENABLE"
  1872.         IN_RECOVER_WATCHDOG_TEST_INTERVAL="$RECOVER_WATCHDOG_TEST_INTERVAL"
  1873.         IN_RECOVER_WATCHDOG_POLL_MAX="$RECOVER_WATCHDOG_POLL_MAX"
  1874.         IN_RECOVER_WATCHDOG_START_WAIT="$RECOVER_WATCHDOG_START_WAIT"
  1875.         IN_RECOVER_SLEEP="$RECOVER_SLEEP"
  1876.     fi
  1877.     #
  1878.     case "$INSTTYPE" in 'linked')
  1879.         echo ""
  1880.         echo "What shall be the Installation IP Address? (-ip)"
  1881.         echo "  NOTE: To bind to all local IP addresses, use \"0.0.0.0\"."
  1882.         echo "  NOTE: The following IP addresses were found on this host: "
  1883.         for EACH in $(ip route show table local 2> /dev/null | egrep "^local " | cut -f 2 -d " " | egrep -v "^127\.0\." | sort -V | uniq) ; do
  1884.             echo "    $EACH"
  1885.         done
  1886.         echo ""
  1887.         while read -e -r -p "IPADDR: " -i "$IPADDR" IN_IPADDR ; do
  1888.             # Filter invalid characters and validate input.
  1889.             IN_IPADDR=$(echo "$IN_IPADDR" | tr -c -d '[:digit:].')
  1890.             if [[ -z "$IN_IPADDR" ]] || (echo "$IN_IPADDR" | egrep -v "^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$" &> /dev/null ) ; then
  1891.                 echo ""
  1892.                 echo "Invalid input. Try again."
  1893.             else
  1894.                 break
  1895.             fi
  1896.         done
  1897.         echo ""
  1898.     esac
  1899.     #
  1900.     case "$INSTTYPE" in 'linked')
  1901.         echo "What shall be the Installation Client Port? (+clientport)"
  1902.         while read -e -r -p "CLIENTPORT: " -i "$CLIENTPORT" IN_CLIENTPORT ; do
  1903.             # Filter invalid characters and validate input.
  1904.             IN_CLIENTPORT=$(echo "$IN_CLIENTPORT" | tr -c -d '[:digit:]')
  1905.             if [[ -z "$IN_CLIENTPORT" ]] || [[ "$IN_CLIENTPORT" -lt 1024 ]] || [[ "$IN_CLIENTPORT" -gt 49151 ]] ; then
  1906.                 echo ""
  1907.                 echo "Invalid input detected. Try again."
  1908.             else
  1909.                 break
  1910.             fi
  1911.         done
  1912.         echo ""
  1913.     esac
  1914.     #
  1915.     case "$INSTTYPE" in 'linked')
  1916.         echo "What shall be the Installation Host Port? (+hostport)"
  1917.         while read -e -r -p "HOSTPORT: " -i "$HOSTPORT" IN_HOSTPORT ; do
  1918.             # Filter invalid characters and validate input.
  1919.             IN_HOSTPORT=$(echo "$IN_HOSTPORT" | tr -c -d '[:digit:]')
  1920.             if [[ -z "$IN_HOSTPORT" ]] || [[ "$IN_HOSTPORT" -lt 1024 ]] || [[ "$IN_HOSTPORT" -gt 49151 ]] ; then
  1921.                 echo ""
  1922.                 echo "Invalid input detected. Try again."
  1923.             else
  1924.                 break
  1925.             fi
  1926.         done
  1927.         echo ""
  1928.     esac
  1929.     #
  1930.     case "$INSTTYPE" in 'linked')
  1931.         echo "What shall be the Installation TV Port? (+tv_port)"
  1932.         while read -e -r -p "TVPORT: " -i "$TVPORT" IN_TVPORT ; do
  1933.             # Filter invalid characters and validate input.
  1934.             IN_TVPORT=$(echo "$IN_TVPORT" | tr -c -d '[:digit:]')
  1935.             if [[ -z "$IN_TVPORT" ]] || [[ "$IN_TVPORT" -lt 1024 ]] || [[ "$IN_TVPORT" -gt 49151 ]] ; then
  1936.                 echo ""
  1937.                 echo "Invalid input detected. Try again."
  1938.             else
  1939.                 break
  1940.             fi
  1941.         done
  1942.         echo ""
  1943.     esac
  1944.     #
  1945.     case "$INSTTYPE" in 'linked')
  1946.         echo "What shall be the Installation Steam Port? (-steamport)"
  1947.         while read -e -r -p "STEAMPORT: " -i "$STEAMPORT" IN_STEAMPORT ; do
  1948.             # Filter invalid characters and validate input.
  1949.             IN_STEAMPORT=$(echo "$IN_STEAMPORT" | tr -c -d '[:digit:]')
  1950.             if [[ -z "$IN_STEAMPORT" ]] || [[ "$IN_STEAMPORT" -lt 1024 ]] || [[ "$IN_STEAMPORT" -gt 49151 ]] ; then
  1951.                 echo ""
  1952.                 echo "Invalid input detected. Try again."
  1953.             else
  1954.             break
  1955.             fi
  1956.         done
  1957.         echo ""
  1958.     esac
  1959.     #
  1960.     case "$INSTTYPE" in 'linked'|'master') # Allow masters to be reconfiged.
  1961.         echo "Is the installation Allowed to be Updated or Relinked? 1=yes 0=no"
  1962.         while read -e -r -p "ALLOWUPDATE: " -i "$ALLOWUPDATE" IN_ALLOWUPDATE ; do
  1963.             # Filter invalid characters and validate input.
  1964.             IN_ALLOWUPDATE=$(echo "$IN_ALLOWUPDATE" | tr -c -d '[:digit:]')
  1965.             if [[ -z "$IN_ALLOWUPDATE" ]] || (echo "$IN_ALLOWUPDATE" | egrep -v "^0$|^1$" &> /dev/null ) ; then
  1966.                 echo ""
  1967.                 echo "Invalid input detected. Try again."
  1968.             else
  1969.                 break
  1970.             fi
  1971.         done
  1972.         echo ""
  1973.     esac
  1974.     #
  1975.     case "$INSTTYPE" in 'linked')
  1976.         echo "Is the installation Allowed to Start? 1=yes 0=no"
  1977.         while read -e -r -p "ALLOWSTART: " -i "$ALLOWSTART" IN_ALLOWSTART ; do
  1978.             # Filter invalid characters and validate input.
  1979.             IN_ALLOWSTART=$(echo "$IN_ALLOWSTART" | tr -c -d '[:digit:]')
  1980.             if [[ -z "$IN_ALLOWSTART" ]] || (echo "$IN_ALLOWSTART" | egrep -v "^0$|^1$" &> /dev/null ) ; then
  1981.                 echo ""
  1982.                 echo "Invalid input detected. Try again."
  1983.             else
  1984.                 break
  1985.             fi
  1986.         done
  1987.         echo ""
  1988.     esac
  1989.     #
  1990.     case "$INSTTYPE" in 'linked')
  1991.         echo "Is the installation Allowed to start at Boot? 1=yes 0=no"
  1992.         while read -e -r -p "BOOTSTART: " -i "$BOOTSTART" IN_BOOTSTART ; do
  1993.             # Filter invalid characters and validate input.
  1994.             IN_BOOTSTART=$(echo "$IN_BOOTSTART" | tr -c -d '[:digit:]')
  1995.             if [[ -z "$IN_BOOTSTART" ]] || (echo "$IN_BOOTSTART" | egrep -v "^0$|^1$" &> /dev/null ) ; then
  1996.                 echo ""
  1997.                 echo "Invalid input detected. Try again."
  1998.             else
  1999.                 break
  2000.             fi
  2001.         done
  2002.         echo ""
  2003.     esac
  2004.     #
  2005.     case "$INSTTYPE" in 'linked')
  2006.         echo "Is the installation Allowed to Auto Cleanup? 1=yes 0=no"
  2007.         while read -e -r -p "AUTOCLEANUP: " -i "$AUTOCLEANUP" IN_AUTOCLEANUP ; do
  2008.             # Filter invalid characters and validate input.
  2009.             IN_AUTOCLEANUP=$(echo "$IN_AUTOCLEANUP" | tr -c -d '[:digit:]')
  2010.             if [[ -z "$IN_AUTOCLEANUP" ]] || (echo "$IN_AUTOCLEANUP" | egrep -v "^0$|^1$" &> /dev/null ) ; then
  2011.                 echo ""
  2012.                 echo "Invalid input detected. Try again."
  2013.             else
  2014.                 break
  2015.             fi
  2016.         done
  2017.         echo ""
  2018.     esac
  2019.     #
  2020.     case "$INSTTYPE" in 'linked')
  2021.         echo "Is the installation Allowed to send Mail Notifications? 1=yes 0=no"
  2022.         while read -e -r -p "MAILNOTIFY: " -i "$MAILNOTIFY" IN_MAILNOTIFY ; do
  2023.             # Filter invalid characters and validate input.
  2024.             IN_MAILNOTIFY=$(echo "$IN_MAILNOTIFY" | tr -c -d '[:digit:]')
  2025.             if [[ -z "$IN_MAILNOTIFY" ]] || (echo "$IN_MAILNOTIFY" | egrep -v "^0$|^1$" &> /dev/null ) ; then
  2026.                 echo ""
  2027.                 echo "Invalid input detected. Try again."
  2028.             else
  2029.                 break
  2030.             fi
  2031.         done
  2032.         echo ""
  2033.     esac
  2034.     #
  2035.     case "$INSTTYPE" in 'linked')
  2036.         echo "What is the installation's Notification Email address? (leave blank to use default)"
  2037.         while read -e -r -p "MAILTO: " -i "$MAILTO" IN_MAILTO ; do
  2038.             # Filter invalid characters and validate input.
  2039.             IN_MAILTO=$(echo "$IN_MAILTO" | tr -d '"[:blank:][:cntrl:]') # FU IDN.
  2040.             if [[ -n "$IN_MAILTO" ]] && (echo "$IN_MAILTO" | egrep -v "^.+@.+$" &> /dev/null ) ; then
  2041.                 echo ""
  2042.                 echo "Invalid input detected. Try again."
  2043.             else
  2044.                 break
  2045.             fi
  2046.         done
  2047.         echo ""
  2048.     esac
  2049.     #
  2050.     case "$INSTTYPE" in 'linked')
  2051.         echo "Should the installation automatically recover after a crash? 1=yes 0=no"
  2052.         while read -e -r -p "RECOVER_CRASH_ENABLE: " -i "$RECOVER_CRASH_ENABLE" IN_RECOVER_CRASH_ENABLE ; do
  2053.             # Filter invalid characters and validate input.
  2054.             IN_RECOVER_CRASH_ENABLE=$(echo "$IN_RECOVER_CRASH_ENABLE" | tr -c -d '[:digit:]')
  2055.             if [[ -z "$IN_RECOVER_CRASH_ENABLE" ]] || (echo "$IN_RECOVER_CRASH_ENABLE" | egrep -v "^0$|^1$" &> /dev/null ) ; then
  2056.                 echo ""
  2057.                 echo "Invalid input detected. Try again."
  2058.             else
  2059.                 break
  2060.             fi
  2061.         done
  2062.         echo ""
  2063.     esac
  2064.     #
  2065.     case "$INSTTYPE" in 'linked')
  2066.         echo "Should the watchdog process monitor the installation while running? 1=yes 0=no"
  2067.         while read -e -r -p "RECOVER_WATCHDOG_ENABLE: " -i "$RECOVER_WATCHDOG_ENABLE" IN_RECOVER_WATCHDOG_ENABLE ; do
  2068.             # Filter invalid characters and validate input.
  2069.             IN_RECOVER_WATCHDOG_ENABLE=$(echo "$IN_RECOVER_WATCHDOG_ENABLE" | tr -c -d '[:digit:]')
  2070.             if [[ -z "$IN_RECOVER_WATCHDOG_ENABLE" ]] || (echo "$IN_RECOVER_WATCHDOG_ENABLE" | egrep -v "^0$|^1$" &> /dev/null ) ; then
  2071.                 echo ""
  2072.                 echo "Invalid input detected. Try again."
  2073.             else
  2074.                 break
  2075.             fi
  2076.         done
  2077.         echo ""
  2078.     esac
  2079.     #
  2080.     case "$INSTTYPE" in 'linked')
  2081.         echo "How often should the watchdog poll the installation when running, measured in seconds?"
  2082.         while read -e -r -p "RECOVER_WATCHDOG_TEST_INTERVAL: " -i "$RECOVER_WATCHDOG_TEST_INTERVAL" IN_RECOVER_WATCHDOG_TEST_INTERVAL ; do
  2083.             # Filter invalid characters and validate input.
  2084.             IN_RECOVER_WATCHDOG_TEST_INTERVAL=$(echo "$IN_RECOVER_WATCHDOG_TEST_INTERVAL" | tr -c -d '[:digit:]')
  2085.             if [[ -z "$IN_RECOVER_WATCHDOG_TEST_INTERVAL" ]] || [[ "$IN_RECOVER_WATCHDOG_TEST_INTERVAL" -gt 600 ]] ; then
  2086.                 echo ""
  2087.                 echo "Invalid input detected. Try again."
  2088.             else
  2089.                 break
  2090.             fi
  2091.         done
  2092.         echo ""
  2093.     esac
  2094.     #
  2095.     case "$INSTTYPE" in 'linked')
  2096.         echo "How many watchdog poll failures must occur before the installation is forcefully restarted?"
  2097.         while read -e -r -p "RECOVER_WATCHDOG_POLL_MAX: " -i "$RECOVER_WATCHDOG_POLL_MAX" IN_RECOVER_WATCHDOG_POLL_MAX ; do
  2098.             # Filter invalid characters and validate input.
  2099.             IN_RECOVER_WATCHDOG_POLL_MAX=$(echo "$IN_RECOVER_WATCHDOG_POLL_MAX" | tr -c -d '[:digit:]')
  2100.             if [[ -z "$IN_RECOVER_WATCHDOG_POLL_MAX" ]] || [[ "$IN_RECOVER_WATCHDOG_POLL_MAX" -gt 99 ]] ; then
  2101.                 echo ""
  2102.                 echo "Invalid input detected. Try again."
  2103.             else
  2104.                 break
  2105.             fi
  2106.         done
  2107.         echo ""
  2108.     esac
  2109.     #
  2110.     case "$INSTTYPE" in 'linked')
  2111.         echo "How long should the watchdog wait before polling a newly started installation, in seconds?"
  2112.         while read -e -r -p "RECOVER_WATCHDOG_START_WAIT: " -i "$RECOVER_WATCHDOG_START_WAIT" IN_RECOVER_WATCHDOG_START_WAIT ; do
  2113.             # Filter invalid characters and validate input.
  2114.             IN_RECOVER_WATCHDOG_START_WAIT=$(echo "$IN_RECOVER_WATCHDOG_START_WAIT" | tr -c -d '[:digit:]')
  2115.             if [[ -z "$IN_RECOVER_WATCHDOG_START_WAIT" ]] || [[ "$IN_RECOVER_WATCHDOG_START_WAIT" -gt 300 ]] ; then
  2116.                 echo ""
  2117.                 echo "Invalid input detected. Try again."
  2118.             else
  2119.                 break
  2120.             fi
  2121.         done
  2122.         echo ""
  2123.     esac
  2124.     #
  2125.     case "$INSTTYPE" in 'linked')
  2126.         echo "How long should the installation pause after a crash, in seconds?"
  2127.         while read -e -r -p "RECOVER_SLEEP: " -i "$RECOVER_SLEEP" IN_RECOVER_SLEEP ; do
  2128.             # Filter invalid characters and validate input.
  2129.             IN_RECOVER_SLEEP=$(echo "$IN_RECOVER_SLEEP" | tr -c -d '[:digit:]')
  2130.             if [[ -z "$IN_RECOVER_SLEEP" ]] || [[ "$IN_RECOVER_SLEEP" -gt 60 ]] ; then
  2131.                 echo ""
  2132.                 echo "Invalid input detected. Try again."
  2133.             else
  2134.                 break
  2135.             fi
  2136.         done
  2137.         echo ""
  2138.     esac
  2139.     #
  2140.     case "$INSTTYPE" in 'linked')
  2141.         echo "What is the installation's Startup Binary?"
  2142.         while read -e -r -p "STARTBIN: " -i "$STARTBIN" IN_STARTBIN ; do
  2143.             # Filter invalid characters and validate input.
  2144.             IN_STARTBIN=$(echo "$IN_STARTBIN" | tr -d '[:cntrl:][:blank:]"`${}[]()<>?/\!*')
  2145.             if [[ -z "$IN_STARTBIN" ]] ; then
  2146.                 echo ""
  2147.                 echo "Invalid input detected. Try again."
  2148.             else
  2149.                 break
  2150.             fi
  2151.         done
  2152.         echo ""
  2153.     esac
  2154.     #
  2155.     case "$INSTTYPE" in 'linked')
  2156.         echo "IMPORTANT: STARTBINARGS allows and encourages you to use bash parameters/variables."
  2157.         echo "  These parameters will be expanded when you start the installation."
  2158.         echo "  For example, you can use \"+clientport \$CLIENTPORT\" and the CLIENTPORT field from the database will be used."
  2159.         echo "  You DO NOT need to escape or quote these values."
  2160.         echo ""
  2161.         echo "What is the installation's Startup Arguments?"
  2162.         while read -e -r -p "STARTBINARGS: " -i "$EVALTXT_STARTBINARGS" IN_STARTBINARGS ; do
  2163.             # Filter invalid characters and validate input.
  2164.             IN_STARTBINARGS=$(echo "$IN_STARTBINARGS" | tr -c -d '[:alnum:]$ [=-=].+_~/@')
  2165.             #if [[ -z "$IN_STARTBINARGS" ]] ; then
  2166.             #   echo ""
  2167.             #   echo "Invalid input detected. Try again."
  2168.             #else
  2169.             #   break
  2170.             #fi
  2171.             break
  2172.         done
  2173.         echo ""
  2174.     esac
  2175.     #
  2176.     # User then reviews the info and confirms.
  2177.     echo "Please confirm: Is the following information correct?"
  2178.     echo ""
  2179.     ( echo "  IP Address|:|$IN_IPADDR"
  2180.     echo "  Client Port|:|$IN_CLIENTPORT"
  2181.     echo "  Host Port|:|$IN_HOSTPORT"
  2182.     echo "  TV Port|:|$IN_TVPORT"
  2183.     echo "  Steam Port|:|$IN_STEAMPORT"
  2184.     echo "  Allow Update|:|$IN_ALLOWUPDATE"
  2185.     echo "  Allow Autostart|:|$IN_ALLOWSTART"
  2186.     echo "  Allow Bootstart|:|$IN_BOOTSTART"
  2187.     echo "  Auto-Cleanup|:|$IN_AUTOCLEANUP"
  2188.     echo "  Allow Notifications|:|$IN_MAILNOTIFY"
  2189.     echo "  Email Address|:|$IN_MAILTO"
  2190.     echo "  Recovery Crash Enable|:|$IN_RECOVER_CRASH_ENABLE"
  2191.     echo "  Recovery Watchdog Enable|:|$IN_RECOVER_WATCHDOG_ENABLE"
  2192.     echo "  Recovery Watchdog Test Interval|:|$IN_RECOVER_WATCHDOG_TEST_INTERVAL"
  2193.     echo "  Recovery Watchdog Max Tests|:|$IN_RECOVER_WATCHDOG_POLL_MAX"
  2194.     echo "  Recovery Watchdog Start Wait|:|$IN_RECOVER_WATCHDOG_START_WAIT"
  2195.     echo "  Recovery Delay|:|$IN_RECOVER_SLEEP"
  2196.     echo "  Startup Binary|:|$IN_STARTBIN"
  2197.     echo "  Startup Arguments|:|$IN_STARTBINARGS"
  2198.     ) | column -t -s "|"
  2199.     echo ""
  2200.     echo "Would you like to proceed with recording these values?"
  2201.     read -e -r -p "y/N: " -i "" IN_FINAL_CONFIG_CONFIRM
  2202.         if [[ ! "$IN_FINAL_CONFIG_CONFIRM" == [yY] ]]; then
  2203.             echo ""
  2204.             echo "Quitting."
  2205.             echo ""
  2206.             return 2
  2207.         fi
  2208.     echo ""
  2209.     #
  2210.     # If we are here for a new install, we are going to do an SQL insert. If we are here for a reconfig, we do an update.
  2211.     if [[ "$IN_ARG" == "install" ]] ; then
  2212.         echo ""
  2213.         echo -n "Registering the installation into the database: "
  2214.         $SQLCMD "insert into 'inst' (INSTALLID, GAMETYPE, INSTTYPE, IPADDR, CLIENTPORT, HOSTPORT, TVPORT, STEAMPORT, ALLOWUPDATE, ALLOWSTART, BOOTSTART, AUTOCLEANUP, MAILNOTIFY, MAILTO, STARTBIN, STARTBINARGS, RECOVER_CRASH_ENABLE, RECOVER_WATCHDOG_ENABLE, RECOVER_WATCHDOG_TEST_INTERVAL, RECOVER_WATCHDOG_POLL_MAX, RECOVER_WATCHDOG_START_WAIT, RECOVER_SLEEP) values ('$IN_INSTALLNAME', '$IN_INSTALLGAMETYPE', 'linked', '$IN_IPADDR', '$IN_CLIENTPORT', '$IN_HOSTPORT', '$IN_TVPORT', '$IN_STEAMPORT', '$IN_ALLOWUPDATE', '$IN_ALLOWSTART', '$IN_BOOTSTART', '$IN_AUTOCLEANUP', '$IN_MAILNOTIFY', '$IN_MAILTO', '$IN_STARTBIN', '$IN_STARTBINARGS', '$IN_RECOVER_CRASH_ENABLE', '$IN_RECOVER_WATCHDOG_ENABLE', '$IN_RECOVER_WATCHDOG_TEST_INTERVAL', '$IN_RECOVER_WATCHDOG_POLL_MAX', '$IN_RECOVER_WATCHDOG_START_WAIT', '$IN_RECOVER_SLEEP');"
  2215.         X_INSTALLDBINSERT=$?
  2216.         if [[ ! "$X_INSTALLDBINSERT" == 0 ]] ; then
  2217.             echoerr ""
  2218.             echoerr "ERROR: The database command returned exit code $X_INSTALLDBINSERT."
  2219.             echoerr "  Something went wrong when attempting to record the new installation."
  2220.             echoerr ""
  2221.             return 1
  2222.         else
  2223.             echo "Done"
  2224.         fi
  2225.     elif [[ "$IN_ARG" == "reconfig" ]] ; then
  2226.         echo -n "Registering the installation's new config into the database: "
  2227.         $SQLCMD "update inst set IPADDR='$IN_IPADDR', CLIENTPORT='$IN_CLIENTPORT', HOSTPORT='$IN_HOSTPORT', TVPORT='$IN_TVPORT', STEAMPORT='$IN_STEAMPORT', ALLOWUPDATE='$IN_ALLOWUPDATE', ALLOWSTART='$IN_ALLOWSTART', BOOTSTART='$IN_BOOTSTART', AUTOCLEANUP='$IN_AUTOCLEANUP', MAILNOTIFY='$IN_MAILNOTIFY', MAILTO='$IN_MAILTO', STARTBIN='$IN_STARTBIN', STARTBINARGS='$IN_STARTBINARGS', RECOVER_CRASH_ENABLE='$IN_RECOVER_CRASH_ENABLE', RECOVER_WATCHDOG_ENABLE='$IN_RECOVER_WATCHDOG_ENABLE', RECOVER_WATCHDOG_TEST_INTERVAL='$IN_RECOVER_WATCHDOG_TEST_INTERVAL', RECOVER_WATCHDOG_POLL_MAX='$IN_RECOVER_WATCHDOG_POLL_MAX', RECOVER_WATCHDOG_START_WAIT='$IN_RECOVER_WATCHDOG_START_WAIT', RECOVER_SLEEP='$IN_RECOVER_SLEEP' where INSTALLID='$IN_INSTALLID';"
  2228.         X_RECONFIGINST=$?
  2229.         if [[ ! "$X_RECONFIGINST" == 0 ]] ; then
  2230.             echoerr ""
  2231.             echoerr "ERROR: The database command returned exit code $X_RECONFIGINST."
  2232.             echoerr "  Something went wrong when attempting to record the new values."
  2233.             echoerr ""
  2234.             return 1
  2235.         else
  2236.             echo "Done"
  2237.         fi
  2238.     fi
  2239. }
  2240.  
  2241. f_installnewlinked() {
  2242.     # Create a new linked/child installation.
  2243.     #
  2244.     # We must have a master installation of the same GAMETYPE which can be used. Otherwise, it must be installed.
  2245.     if [[ -z "$USEMASTER" ]] ; then
  2246.         echo ""
  2247.         echo "No master installation for game type \"$IN_INSTALLGAMETYPE\" found in the database, so we will install one."
  2248.         f_installnewmaster
  2249.         if [[ "$NEWMASTERFAIL" == 1 ]] ; then
  2250.             echoerr ""
  2251.             echoerr "ERROR: The new master installation process failed. We can not proceed."
  2252.             echoerr ""
  2253.             return 1
  2254.         fi
  2255.     fi
  2256.     # Verify that the master installation directory exists, and err out if there is a problem.
  2257.     if [[ ! -d "$APPDIR/$USEMASTER" ]] ; then
  2258.         echoerr ""
  2259.         echoerr "ERROR: Master installation \"$USEMASTER\" found in the database, but no directory found at \"$APPDIR/$USEMASTER\"."
  2260.         echoerr "  If you uninstall the old master, a new one will automatically be installed if you try again."
  2261.         echoerr ""
  2262.         return 1
  2263.     fi
  2264.     echo "Choose a unique name/name-suffix name for the installation."
  2265.     f_choosename
  2266.     #
  2267.     # FIXME: Need an automated method of selecting IP and ports from the DB
  2268.     #
  2269.     # echo "We will now need to provide the configuration to use for this srcds installation."
  2270.     #
  2271.     # f_configinstall prompts the user for configuration input and inserts the new install into the database.
  2272.     f_configinstall ; X_CONFIGINSTALL=$?
  2273.     if [[ ! "$X_CONFIGINSTALL" == 0 ]] ; then return 1 ; fi
  2274.     #
  2275.     # Load the inst table after we have written it, since other parameters get set at the same time.
  2276.     # This is redundant, but it is also an error check, since failing here will quit the install process.
  2277.     IN_INSTALLID="$IN_INSTALLNAME"
  2278.     f_loaddbinst
  2279.     if [[ "$LOADDBINSTINFO_FAIL" == 1 ]] ; then return 1 ; fi
  2280.     #
  2281.     #
  2282.     echo ""
  2283.     echo -n "Creating new installation directory: "
  2284.     mkdir $APPDIR/$IN_INSTALLNAME ; X_INSTALLMKDIR=$?
  2285.     if [[ ! "$X_INSTALLMKDIR" == 0 ]] ; then
  2286.         echoerr ""
  2287.         echoerr "ERROR: Unable to mkdir $APPDIR/IN_INSTALLNAME, exit code $X_INSTALLMKDIR"
  2288.         echoerr ""
  2289.         return 1
  2290.     fi
  2291.     echo "Done"
  2292.     #
  2293.     # Link up the new installation to it's master.
  2294.     f_relink
  2295.     #
  2296.     # If the replay system is enabled globally, set up the directories for this server, even if won't use it.
  2297.     if [[ "$REPLAY_ENABLED" == 1 ]] && [[ -d "$REPLAYBASEDIR" ]] ; then
  2298.         echo -n "Creating public replay web dir for \"$IN_INSTALLNAME\" at \"$REPLAYDIR\": "
  2299.         mkdir -p "$REPLAYDIR"
  2300.         chmod -R 0775 "$REPLAYBASEDIR/$IN_INSTALLNAME"
  2301.         echo "Done"
  2302.     fi
  2303.     #
  2304.     echo ""
  2305.     echo "Linked installation completed."
  2306.     echo ""
  2307.     return 0
  2308. }
  2309.  
  2310. f_installnewmaster() {
  2311.     # Install a new master srcds installation.
  2312.     # This assumes that the srcinfo table row exists for this game type.
  2313.     # The GAMETYPE can be read in as IN_INSTALLGAMETYPE.
  2314.     # We should have gotten here via f_installnewlinked, so go read that for reference.
  2315.     #
  2316.     echo ""
  2317.     # Get the info we need from the srcinfo database.
  2318.     while IFS='|' read GAMETYPE GAMENAME UPDATER HLDSID SERVER_APPID ; do
  2319.         echo -n "Reading database info for game type \"$IN_INSTALLGAMETYPE\": "
  2320.         NEWMASTER_GAMETYPE=$GAMETYPE
  2321.         NEWMASTER_GAMENAME=$GAMENAME
  2322.         NEWMASTER_UPDATER=$UPDATER
  2323.         NEWMASTER_HLDSID=$HLDSID
  2324.         NEWMASTER_SERVER_APPID=$SERVER_APPID
  2325.     done < <($SQLCMD "select GAMETYPE,GAMENAME,UPDATER,HLDSID,SERVER_APPID from srcinfo where GAMETYPE='$IN_INSTALLGAMETYPE';")
  2326.     X_INSTALLMASTERGETINFO=$?
  2327.     echo "Done"
  2328.     if [[ ! "$X_INSTALLMASTERGETINFO" == 0 ]] ; then
  2329.         NEWMASTERFAIL=1
  2330.         echoerr ""
  2331.         echoerr "ERROR: The database command returned exit code $X_INSTALLMASTERGETINFO."
  2332.         echoerr "  Unable to retrieve necessary information."
  2333.         echoerr ""
  2334.         return 1
  2335.     fi
  2336.     NEWMASTER_INSTALLID="${NEWMASTER_GAMETYPE}-MASTER"
  2337.     echo ""
  2338.     ( echo "  Game Type:|$NEWMASTER_GAMETYPE"
  2339.     echo "  Game Name:|$NEWMASTER_GAMENAME"
  2340.     echo "  Updater:|$NEWMASTER_UPDATER"
  2341.     echo "  SteamCMD APPID:|$NEWMASTER_SERVER_APPID"
  2342.     echo "  New master name:|$NEWMASTER_INSTALLID"
  2343.     echo "  New directory path:|$APPDIR/$NEWMASTER_INSTALLID"
  2344.     ) | column -t -s "|"
  2345.     echo ""
  2346.     echo "Proceed with new master installation?"
  2347.     read -e -r -p "y/N: " -i "" IN_INSTALLNEWMASTER_CONFIRM
  2348.     if [[ ! "$IN_INSTALLNEWMASTER_CONFIRM" == [yY] ]]; then
  2349.         NEWMASTERFAIL=1
  2350.         echo ""
  2351.         echo "Quitting."
  2352.         echo ""
  2353.         return 0
  2354.     fi
  2355.     #
  2356.     # Insert the new inst table record. A number of fields get set null/empty/sane specifically for master installations.
  2357.     echo -n "Registering the master installation into the database: "
  2358.     $SQLCMD "insert into 'inst' (INSTALLID, GAMETYPE, INSTTYPE, IPADDR, CLIENTPORT, HOSTPORT, TVPORT, STEAMPORT, ALLOWUPDATE, ALLOWSTART, BOOTSTART, AUTOCLEANUP, MAILNOTIFY, MAILTO, STARTBIN, STARTBINARGS) values ('$NEWMASTER_INSTALLID', '$NEWMASTER_GAMETYPE', 'master', '', '', '', '', '', '1', '0', '0', '0', '', '', '', '' );"
  2359.     X_NEWMASTERDBINSERT=$?
  2360.     echo "Done"
  2361.     if [[ ! "$X_NEWMASTERDBINSERT" == 0 ]] ; then
  2362.         NEWMASTERFAIL=1
  2363.         echoerr ""
  2364.         echoerr "ERROR: The database command returned exit code $X_NEWMASTERDBINSERT."
  2365.         echoerr "  We can not proceed since we don't know if the insert was recorded correctly."
  2366.         echoerr ""
  2367.         return 1
  2368.     fi
  2369.     #
  2370.     # Make the new installation directory.
  2371.     mkdir "$APPDIR/$NEWMASTER_INSTALLID" ; X_NEWMASTER_MKDIR=$?
  2372.     if [[ ! "$X_NEWMASTER_MKDIR" == 0 ]] ; then
  2373.         NEWMASTERFAIL=1
  2374.         echoerr ""
  2375.         echoerr "ERROR: There was a problem creating the new master installation directory."
  2376.         echoerr "  Exit code: $X_NEWMASTER_MKDIR, installation directory: \"$APPDIR/NEWMASTER_INSTALLID\"."
  2377.         echoerr ""
  2378.         return 1
  2379.     fi
  2380.     #
  2381.     # Once we have the DB entry and installation directory, we just update it since there is little difference between a new install and an update.
  2382.     IN_INSTALLID="$NEWMASTER_INSTALLID"
  2383.     f_update ; X_UPDATE_MAIN="$?"
  2384.     if [[ ! "$X_UPDATE_MAIN" == 0 ]] ; then
  2385.         NEWMASTERFAIL=1
  2386.         return 1
  2387.     fi
  2388.     #
  2389.     echo ""
  2390.     echo "Master install completed."
  2391.     echo ""
  2392. }
  2393.  
  2394. f_changegametype() {
  2395.     # FIXME: this needs lots of testing.
  2396.     # FIXME: Doing a ctrl+c is breaking the terminal and requires a reset. Need to fix that.
  2397.     #
  2398.     # FIXME: Need to validate input. For example, GAMENAME "Garry's Mod" blows up the SQLite insert command.
  2399.     #
  2400.     # Configure a game type. We may be installing a new game type for a newinstall, or reconfiguring an existing one.
  2401.     #
  2402.     # If configuring as new, then use supplied defaults.
  2403.     # If configuring as a reconfig, then use the existing info from the DB.
  2404.     #
  2405.     if [[ "$GAMETYPE_ARG" == "add" ]] ; then
  2406.         echo ""
  2407.         echo "In order to configure a new game type, you must register the necessary information into the database."
  2408.         echo "If you don't have the information necessary to complete the process, ctrl+c at any time to quit."
  2409.         #
  2410.         # We will need to specify defaults for each field.
  2411.         DEFAULT_GAMETYPE=""
  2412.         DEFAULT_GAMENAME=""
  2413.         DEFAULT_UPDATER="SteamCMD"
  2414.         DEFAULT_HLDSID=""
  2415.         DEFAULT_SERVER_APPID=""
  2416.         DEFAULT_CLIENT_APPID=""
  2417.         DEFAULT_GAMEARG=""
  2418.         DEFAULT_BINDIRSUBD=""
  2419.         DEFAULT_STEAMINF=""
  2420.         DEFAULT_INSTDEF_ALLOWUPDATE="1"
  2421.         DEFAULT_INSTDEF_ALLOWSTART="1"
  2422.         DEFAULT_INSTDEF_BOOTSTART="1"
  2423.         DEFAULT_INSTDEF_AUTOCLEANUP="1"
  2424.         DEFAULT_INSTDEF_MAILNOTIFY="1"
  2425.         DEFAULT_INSTDEF_MAILTO=""
  2426.         DEFAULT_INSTDEF_RECOVER_CRASH_ENABLE="1"
  2427.         DEFAULT_INSTDEF_RECOVER_WATCHDOG_ENABLE="1"
  2428.         DEFAULT_INSTDEF_RECOVER_WATCHDOG_TEST_INTERVAL="10"
  2429.         DEFAULT_INSTDEF_RECOVER_WATCHDOG_POLL_MAX="6"
  2430.         DEFAULT_INSTDEF_RECOVER_WATCHDOG_START_WAIT="60"
  2431.         DEFAULT_INSTDEF_RECOVER_SLEEP="10"
  2432.         DEFAULT_INSTDEF_IPADDR="0.0.0.0"
  2433.         DEFAULT_INSTDEF_CLIENTPORT="27005"
  2434.         DEFAULT_INSTDEF_HOSTPORT="27015"
  2435.         DEFAULT_INSTDEF_TVPORT="27020"
  2436.         DEFAULT_INSTDEF_STEAMPORT="26005"
  2437.         DEFAULT_INSTDEF_STARTBIN="$MYNAME"
  2438.         DEFAULT_INSTDEF_STARTBINARGS='-game $GAMEARG -ip $IPADDR +clientport $CLIENTPORT +hostport $HOSTPORT +tv_port $TVPORT -steamport $STEAMPORT -pidfile $PIDFILE -strictportbind'
  2439.     elif [[ "$GAMETYPE_ARG" == "change" ]] ; then
  2440.         # We should load the existing parameters from the database.
  2441.         echo ""
  2442.         echo "This process will help you reconfigure an existing gametype configuration."
  2443.         echo ""
  2444.         echo "Please choose a srcds game type from the list below: "
  2445.         echo ""
  2446.         ( echo "  Game Type|Game Name"
  2447.         echo "  --|--"
  2448.         while IFS='|' read GAMETYPE GAMENAME ; do
  2449.                 echo "  $GAMETYPE|$GAMENAME"
  2450.         done < <($SQLCMD "select GAMETYPE,GAMENAME from srcinfo;")
  2451.         ) | column -t -s "|"
  2452.         echo ""
  2453.         GAMETYPE_LIST=$($SQLCMD "select GAMETYPE from srcinfo;")
  2454.         select IN_SELECTGAMETYPE in $GAMETYPE_LIST ; do
  2455.             IN_GAMETYPE="$IN_SELECTGAMETYPE"
  2456.             f_loaddbsrcinfo
  2457.             break
  2458.         done
  2459.         #
  2460.         if [[ "$LOADDBSRCINFO_FAIL" == 1 ]] ; then
  2461.             echoerr ""
  2462.             echoerr "ERROR: Unable to get the current configuration from the database."
  2463.             echoerr ""
  2464.             return 1
  2465.         fi
  2466.         DEFAULT_GAMETYPE="$GAMETYPE"
  2467.         DEFAULT_GAMENAME="$GAMENAME"
  2468.         DEFAULT_UPDATER="$UPDATER"
  2469.         DEFAULT_HLDSID="$HLDSID"
  2470.         DEFAULT_SERVER_APPID="$SERVER_APPID"
  2471.         DEFAULT_CLIENT_APPID="$CLIENT_APPID"
  2472.         DEFAULT_GAMEARG="$GAMEARG"
  2473.         DEFAULT_BINDIRSUBD="$BINDIRSUBD"
  2474.         DEFAULT_STEAMINF="$STEAMINF"
  2475.         DEFAULT_INSTDEF_ALLOWUPDATE="$INSTDEF_ALLOWUPDATE"
  2476.         DEFAULT_INSTDEF_ALLOWSTART="$INSTDEF_ALLOWSTART"
  2477.         DEFAULT_INSTDEF_BOOTSTART="$INSTDEF_BOOTSTART"
  2478.         DEFAULT_INSTDEF_AUTOCLEANUP="$INSTDEF_AUTOCLEANUP"
  2479.         DEFAULT_INSTDEF_MAILNOTIFY="$INSTDEF_MAILNOTIFY"
  2480.         DEFAULT_INSTDEF_MAILTO="$MAILTO"
  2481.         DEFAULT_INSTDEF_RECOVER_CRASH_ENABLE="$INSTDEF_RECOVER_CRASH_ENABLE"
  2482.         DEFAULT_INSTDEF_RECOVER_WATCHDOG_ENABLE="$INSTDEF_RECOVER_WATCHDOG_ENABLE"
  2483.         DEFAULT_INSTDEF_RECOVER_WATCHDOG_TEST_INTERVAL="$INSTDEF_RECOVER_WATCHDOG_TEST_INTERVAL"
  2484.         DEFAULT_INSTDEF_RECOVER_WATCHDOG_POLL_MAX="$INSTDEF_RECOVER_WATCHDOG_POLL_MAX"
  2485.         DEFAULT_INSTDEF_RECOVER_WATCHDOG_START_WAIT="$INSTDEF_RECOVER_WATCHDOG_START_WAIT"
  2486.         DEFAULT_INSTDEF_RECOVER_SLEEP="$INSTDEF_RECOVER_SLEEP"
  2487.         DEFAULT_INSTDEF_IPADDR="$INSTDEF_IPADDR"
  2488.         DEFAULT_INSTDEF_CLIENTPORT="$INSTDEF_CLIENTPORT"
  2489.         DEFAULT_INSTDEF_HOSTPORT="$INSTDEF_HOSTPORT"
  2490.         DEFAULT_INSTDEF_TVPORT="$INSTDEF_TVPORT"
  2491.         DEFAULT_INSTDEF_STEAMPORT="$INSTDEF_STEAMPORT"
  2492.         DEFAULT_INSTDEF_STARTBIN="$INSTDEF_STARTBIN"
  2493.         DEFAULT_INSTDEF_STARTBINARGS="$INSTDEF_STARTBINARGS"
  2494.  
  2495.         echo ""
  2496.         echo "First, let's show the existing configuration: "
  2497.         echo ""
  2498.         $SQLSHOWCMD "select * from srcinfo where GAMETYPE='$IN_GAMETYPE';"
  2499.         echo ""
  2500.         echo "Would you like to proceed in reconfiguring this gametype?"
  2501.         read -e -r -p "y/N: " -i "" IN_RECONFIG_CONFIRM
  2502.         if [[ ! "$IN_RECONFIG_CONFIRM" == [yY] ]]; then
  2503.             echo ""
  2504.             echo "Quitting."
  2505.             echo ""
  2506.             return 0
  2507.         fi
  2508.         echo ""
  2509.         echo "Okay, let's proceed."
  2510.     fi
  2511.     # --
  2512.     echo ""
  2513.     echo "WARNING: We do not validate input here at this time."
  2514.     echo "  For example, don't use an apostrophe. The GAMENAME \"Garry's Mod\" will cause an error."
  2515.     echo "  We assume you know what you are doing. You were warned."
  2516.     echo ""
  2517.     echo "What is the Game Type name? This should be a short abbreviated name, like tf2, csgo, or l4d2."
  2518.     read -e -r -p "GAMETYPE: " -i "$DEFAULT_GAMETYPE" IN_NEW_GAMETYPE
  2519.     echo ""
  2520.     #
  2521.     echo "What is the Game Name? This should be Valve's official name for the product, like \"Team Fortress 2\"."
  2522.     read -e -r -p "GAMENAME: " -i "$DEFAULT_GAMENAME" IN_NEW_GAMENAME
  2523.     echo ""
  2524.     #
  2525.     echo "What is the Updater type? Only \"SteamCMD\" is supported, so you don't really have a choice here."
  2526.     read -e -r -p "UPDATER: " -i "$DEFAULT_UPDATER" IN_NEW_UPDATER
  2527.     echo ""
  2528.     #
  2529.     echo "What is the HLDS Game Type ID? NOTE: HLDSUpdateTool is depreciated, so this is not needed. Probably leave it blank."
  2530.     read -e -r -p "HLDSID: " -i "$DEFAULT_HLDSID" IN_NEW_HLDSID
  2531.     echo ""
  2532.     #
  2533.     echo "What is the SteamPipe Server APPID, as used in updating with SteamCMD?"
  2534.     read -e -r -p "SERVER_APPID: " -i "$DEFAULT_SERVER_APPID" IN_NEW_SERVER_APPID
  2535.     echo ""
  2536.     #
  2537.     echo "What is the SteamPipe client APPID? This is the \"appID\" in the steam.inf file."
  2538.     read -e -r -p "CLIENT_APPID: " -i "$DEFAULT_CLIENT_APPID" IN_NEW_CLIENT_APPID
  2539.     echo ""
  2540.     #
  2541.     echo "What is the Game Argument? This is passed to the \"-game\" argument in the startup command line (STARTBINARGS)."
  2542.     read -e -r -p "GAMEARG: " -i "$DEFAULT_GAMEARG" IN_NEW_GAMEARG
  2543.     echo ""
  2544.     #
  2545.     echo "What is the Binary Directory Subdirectory? This is where you will find the srcds_linux/srcds_run files."
  2546.     read -e -r -p "BINDIRSUBD: " -i "$DEFAULT_BINDIRSUBD" IN_NEW_BINDIRSUBD
  2547.     echo ""
  2548.     #
  2549.     echo "What is the steam.inf file path?"
  2550.     read -e -r -p "STEAMINF: " -i "$DEFAULT_STEAMINF" IN_NEW_STEAMINF
  2551.     echo ""
  2552.     #
  2553.     echo "What should the inst default IP Address be?"
  2554.     read -e -r -p "INSTDEF_IPADDR: " -i "$DEFAULT_INSTDEF_IPADDR" IN_NEW_INSTDEF_IPADDR
  2555.     echo ""
  2556.     #
  2557.     echo "What should the inst default Client Port be?"
  2558.     read -e -r -p "INSTDEF_CLIENTPORT: " -i "$DEFAULT_INSTDEF_CLIENTPORT" IN_NEW_INSTDEF_CLIENTPORT
  2559.     echo ""
  2560.     #
  2561.     echo "What should the inst default Host Port be?"
  2562.     read -e -r -p "INSTDEF_HOSTPORT: " -i "$DEFAULT_INSTDEF_HOSTPORT" IN_NEW_INSTDEF_HOSTPORT
  2563.     echo ""
  2564.     #
  2565.     echo "What should the inst default TV Port be?"
  2566.     read -e -r -p "INSTDEF_TVPORT: " -i "$DEFAULT_INSTDEF_TVPORT" IN_NEW_INSTDEF_TVPORT
  2567.     echo ""
  2568.     #
  2569.     echo "What should the inst default Steam Port be?"
  2570.     read -e -r -p "INSTDEF_STEAMPORT: " -i "$DEFAULT_INSTDEF_STEAMPORT" IN_NEW_INSTDEF_STEAMPORT
  2571.     echo ""
  2572.     #
  2573.     echo "What should the inst default Allow Update field be?"
  2574.     read -e -r -p "INSTDEF_ALLOWUPDATE: " -i "$DEFAULT_INSTDEF_ALLOWUPDATE" IN_NEW_INSTDEF_ALLOWUPDATE
  2575.     echo ""
  2576.     #
  2577.     echo "What should the inst default Allow Start field be?"
  2578.     read -e -r -p "INSTDEF_ALLOWSTART: " -i "$DEFAULT_INSTDEF_ALLOWSTART" IN_NEW_INSTDEF_ALLOWSTART
  2579.     echo ""
  2580.     #
  2581.     echo "What should the inst default Boot Start field be?"
  2582.     read -e -r -p "INSTDEF_BOOTSTART: " -i "$DEFAULT_INSTDEF_BOOTSTART" IN_NEW_INSTDEF_BOOTSTART
  2583.     echo ""
  2584.     #
  2585.     echo "What should the inst default Auto Cleanup field be?"
  2586.     read -e -r -p "INSTDEF_AUTOCLEANUP: " -i "$DEFAULT_INSTDEF_AUTOCLEANUP" IN_NEW_INSTDEF_AUTOCLEANUP
  2587.     echo ""
  2588.     #
  2589.     echo "What should the inst default Mail Notify field be?"
  2590.     read -e -r -p "INSTDEF_MAILNOTIFY: " -i "$DEFAULT_INSTDEF_MAILNOTIFY" IN_NEW_INSTDEF_MAILNOTIFY
  2591.     echo ""
  2592.     #
  2593.     echo "What should the inst default Mailto address be?"
  2594.     read -e -r -p "INSTDEF_MAILTO: " -i "$DEFAULT_INSTDEF_MAILTO" IN_NEW_INSTDEF_MAILTO
  2595.     echo ""
  2596.     #
  2597.     echo "What should the inst default Recovery Crash Enabled field be?"
  2598.     read -e -r -p "INSTDEF_RECOVER_CRASH_ENABLE: " -i "$DEFAULT_INSTDEF_RECOVER_CRASH_ENABLE" IN_NEW_INSTDEF_RECOVER_CRASH_ENABLE
  2599.     echo ""
  2600.     #
  2601.     echo "What should the inst default Recovery Watchdog Enabled field be?"
  2602.     read -e -r -p "INSTDEF_RECOVER_WATCHDOG_ENABLE: " -i "$DEFAULT_INSTDEF_RECOVER_WATCHDOG_ENABLE" IN_NEW_INSTDEF_RECOVER_WATCHDOG_ENABLE
  2603.     echo ""
  2604.     #
  2605.     echo "What should the inst default Recovery Watchdog Test Interval field be?"
  2606.     read -e -r -p "INSTDEF_RECOVER_WATCHDOG_TEST_INTERVAL: " -i "$DEFAULT_INSTDEF_RECOVER_WATCHDOG_TEST_INTERVAL" IN_NEW_INSTDEF_RECOVER_WATCHDOG_TEST_INTERVAL
  2607.     echo ""
  2608.     #
  2609.     echo "What should the inst default Recovery Watchdog Max Poll field be?"
  2610.     read -e -r -p "INSTDEF_RECOVER_WATCHDOG_POLL_MAX: " -i "$DEFAULT_INSTDEF_RECOVER_WATCHDOG_POLL_MAX" IN_NEW_INSTDEF_RECOVER_WATCHDOG_POLL_MAX
  2611.     echo ""
  2612.     #
  2613.     echo "What should the inst default Recovery Watchdog Start Wait field be?"
  2614.     read -e -r -p "INSTDEF_RECOVER_WATCHDOG_START_WAIT: " -i "$DEFAULT_INSTDEF_RECOVER_WATCHDOG_START_WAIT" IN_NEW_INSTDEF_RECOVER_WATCHDOG_START_WAIT
  2615.     echo ""
  2616.     #
  2617.     echo "What should the inst default Recovery Sleep field be?"
  2618.     read -e -r -p "INSTDEF_RECOVER_SLEEP: " -i "$DEFAULT_INSTDEF_RECOVER_SLEEP" IN_NEW_INSTDEF_RECOVER_SLEEP
  2619.     echo ""
  2620.     #
  2621.     echo "What should the inst default Startup Binary be?"
  2622.     read -e -r -p "INSTDEF_STARTBIN: " -i "$DEFAULT_INSTDEF_STARTBIN" IN_NEW_INSTDEF_STARTBIN
  2623.     echo ""
  2624.     #
  2625.     echo "What should the inst default Startup Arguments be?"
  2626.     read -e -r -p "INSTDEF_STARTBINARGS: " -i "$DEFAULT_INSTDEF_STARTBINARGS" IN_NEW_INSTDEF_STARTBINARGS
  2627.     echo ""
  2628.     #
  2629.     echo "Let's review all of the information: "
  2630.     echo ""
  2631.     ( for EACH in GAMETYPE GAMENAME UPDATER HLDSID SERVER_APPID CLIENT_APPID GAMEARG BINDIRSUBD STEAMINF INSTDEF_IPADDR INSTDEF_CLIENTPORT INSTDEF_HOSTPORT INSTDEF_TVPORT INSTDEF_STEAMPORT INSTDEF_ALLOWUPDATE INSTDEF_ALLOWSTART INSTDEF_BOOTSTART INSTDEF_AUTOCLEANUP INSTDEF_MAILNOTIFY INSTDEF_MAILTO INSTDEF_RECOVER_CRASH_ENABLE INSTDEF_RECOVER_WATCHDOG_ENABLE INSTDEF_RECOVER_WATCHDOG_TEST_INTERVAL INSTDEF_RECOVER_WATCHDOG_POLL_MAX INSTDEF_RECOVER_WATCHDOG_START_WAIT INSTDEF_RECOVER_SLEEP INSTDEF_STARTBIN INSTDEF_STARTBINARGS ; do
  2632.         IN_NEW_EACH="IN_NEW_${EACH}"
  2633.         echo "  ${EACH}|:|${!IN_NEW_EACH}"
  2634.     done
  2635.     ) | column -t -s "|"
  2636.     #
  2637.     echo ""
  2638.     echo "It this correct, and would you like to proceed?"
  2639.     read -e -r -p "y/N: " -i "" IN_INSTALLNEWTYPE_CONFIRM
  2640.     if [[ ! "$IN_INSTALLNEWTYPE_CONFIRM" == [yY] ]]; then
  2641.         echo ""
  2642.         echo "Quitting."
  2643.         echo ""
  2644.         return 0
  2645.     fi
  2646.     echo ""
  2647.     # If we are here for a new game type, we are going to do an SQL insert. If we are here for a change, we do an update.
  2648.     if [[ "$GAMETYPE_ARG" == "add" ]] ; then
  2649.         echo ""
  2650.         echo -n "Registering the new game type into the database: "
  2651.         $SQLCMD "insert into 'srcinfo' (GAMETYPE, GAMENAME, UPDATER, HLDSID, SERVER_APPID, CLIENT_APPID, GAMEARG, BINDIRSUBD, STEAMINF, INSTDEF_IPADDR, INSTDEF_CLIENTPORT, INSTDEF_HOSTPORT, INSTDEF_TVPORT, INSTDEF_STEAMPORT, INSTDEF_ALLOWUPDATE, INSTDEF_ALLOWSTART, INSTDEF_BOOTSTART, INSTDEF_AUTOCLEANUP, INSTDEF_MAILNOTIFY, INSTDEF_MAILTO, INSTDEF_RECOVER_CRASH_ENABLE, INSTDEF_RECOVER_WATCHDOG_ENABLE, INSTDEF_RECOVER_WATCHDOG_TEST_INTERVAL, INSTDEF_RECOVER_WATCHDOG_POLL_MAX, INSTDEF_RECOVER_WATCHDOG_START_WAIT, INSTDEF_RECOVER_SLEEP, INSTDEF_STARTBIN, INSTDEF_STARTBINARGS) values ('$IN_NEW_GAMETYPE', '$IN_NEW_GAMENAME', '$IN_NEW_UPDATER', '$IN_NEW_HLDSID', '$IN_NEW_SERVER_APPID', '$IN_NEW_CLIENT_APPID', '$IN_NEW_GAMEARG', '$IN_NEW_BINDIRSUBD', '$IN_NEW_STEAMINF', '$IN_NEW_INSTDEF_IPADDR', '$IN_NEW_INSTDEF_CLIENTPORT', '$IN_NEW_INSTDEF_HOSTPORT', '$IN_NEW_INSTDEF_TVPORT', '$IN_NEW_INSTDEF_STEAMPORT', '$IN_NEW_INSTDEF_ALLOWUPDATE', '$IN_NEW_INSTDEF_ALLOWSTART', '$IN_NEW_INSTDEF_BOOTSTART', '$IN_NEW_INSTDEF_AUTOCLEANUP', '$IN_NEW_INSTDEF_MAILNOTIFY', '$IN_NEW_INSTDEF_MAILTO', '$IN_NEW_INSTDEF_RECOVER_CRASH_ENABLE', '$IN_NEW_INSTDEF_RECOVER_WATCHDOG_ENABLE', '$IN_NEW_INSTDEF_RECOVER_WATCHDOG_TEST_INTERVAL', '$IN_NEW_INSTDEF_RECOVER_WATCHDOG_POLL_MAX', '$IN_NEW_INSTDEF_RECOVER_WATCHDOG_START_WAIT', '$IN_NEW_INSTDEF_RECOVER_SLEEP', '$IN_NEW_INSTDEF_STARTBIN', '$IN_NEW_INSTDEF_STARTBINARGS');"
  2652.         X_INSTALLDBGAMETYPE=$?
  2653.         if [[ ! "$X_INSTALLDBGAMETYPE" == 0 ]] ; then
  2654.             echoerr ""
  2655.             echoerr "ERROR: The database command returned exit code $X_INSTALLDBGAMETYPE."
  2656.             echoerr "  Something went wrong when attempting to record the new installation."
  2657.             echoerr ""
  2658.             return 1
  2659.         else
  2660.             echo "Done"
  2661.         fi
  2662.     elif [[ "$GAMETYPE_ARG" == "change" ]] ; then
  2663.         echo -n "Registering the game type changed config into the database: "
  2664.         $SQLCMD "update srcinfo set GAMETYPE='$IN_NEW_GAMETYPE', GAMENAME='$IN_NEW_GAMENAME', UPDATER='$IN_NEW_UPDATER', HLDSID='$IN_NEW_HLDSID', SERVER_APPID='$IN_NEW_SERVER_APPID', CLIENT_APPID='$IN_NEW_CLIENT_APPID', GAMEARG='$IN_NEW_GAMEARG', BINDIRSUBD='$IN_NEW_BINDIRSUBD', STEAMINF='$IN_NEW_STEAMINF', INSTDEF_IPADDR='$IN_NEW_INSTDEF_IPADDR', INSTDEF_CLIENTPORT='$IN_NEW_INSTDEF_CLIENTPORT', INSTDEF_HOSTPORT='$IN_NEW_INSTDEF_HOSTPORT', INSTDEF_TVPORT='$IN_NEW_INSTDEF_TVPORT', INSTDEF_STEAMPORT='$IN_NEW_INSTDEF_STEAMPORT', INSTDEF_ALLOWUPDATE='$IN_NEW_INSTDEF_ALLOWUPDATE', INSTDEF_ALLOWSTART='$IN_NEW_INSTDEF_ALLOWSTART', INSTDEF_BOOTSTART='$IN_NEW_INSTDEF_BOOTSTART', INSTDEF_AUTOCLEANUP='$IN_NEW_INSTDEF_AUTOCLEANUP', INSTDEF_MAILNOTIFY='$IN_NEW_INSTDEF_MAILNOTIFY', INSTDEF_MAILTO='$IN_NEW_INSTDEF_MAILTO', INSTDEF_RECOVER_CRASH_ENABLE='$IN_NEW_INSTDEF_RECOVER_CRASH_ENABLE', INSTDEF_RECOVER_WATCHDOG_ENABLE='$IN_NEW_INSTDEF_RECOVER_WATCHDOG_ENABLE', INSTDEF_RECOVER_WATCHDOG_TEST_INTERVAL='$IN_NEW_INSTDEF_RECOVER_WATCHDOG_TEST_INTERVAL', INSTDEF_RECOVER_WATCHDOG_POLL_MAX='$IN_NEW_INSTDEF_RECOVER_WATCHDOG_POLL_MAX', INSTDEF_RECOVER_WATCHDOG_START_WAIT='$IN_NEW_INSTDEF_RECOVER_WATCHDOG_START_WAIT', INSTDEF_RECOVER_SLEEP='$IN_NEW_INSTDEF_RECOVER_SLEEP', INSTDEF_STARTBIN='$IN_NEW_INSTDEF_STARTBIN', INSTDEF_STARTBINARGS='$IN_NEW_INSTDEF_STARTBINARGS' where GAMETYPE='$IN_GAMETYPE';"
  2665.         X_CHANGEGAMETYPE=$?
  2666.         if [[ ! "$X_CHANGEGAMETYPE" == 0 ]] ; then
  2667.             echoerr ""
  2668.             echoerr "ERROR: The database command returned exit code $X_CHANGEGAMETYPE."
  2669.             echoerr "  Something went wrong when attempting to record the new values."
  2670.             echoerr ""
  2671.             return 1
  2672.         else
  2673.             echo "Done"
  2674.         fi
  2675.     fi
  2676.     echo ""
  2677.     echo "The game type has been successfully recorded."
  2678.     echo ""
  2679. }
  2680.  
  2681. f_install() {
  2682.     # Install a new srcds installation.
  2683.     #
  2684.     # Ignore the IN_INSTALLID, if it was provided as an argument. FIXME: This should not be possible since 'install' became a IN_PARAMS1.
  2685.     if [[ -n "$IN_INSTALLID" ]] ; then
  2686.         IN_INSTALLID=""
  2687.     fi
  2688.     echo ""
  2689.     echo "This process will assist you in installing a new srcds installation."
  2690.     echo ""
  2691.     echo "You will need to provide information about the new installation, including:"
  2692.     echo "  A unique name for the installation."
  2693.     echo "  The srcds game type."
  2694.     echo "  The IP address and service ports."
  2695.     echo ""
  2696.     while true ; do
  2697.         echo "Please choose a srcds game type from the list below: "
  2698.         echo ""
  2699.         ( echo "  Game Type|Game Name"
  2700.         echo "  --|--"
  2701.         while IFS='|' read GAMETYPE GAMENAME ; do
  2702.             echo "  $GAMETYPE|$GAMENAME"
  2703.         done < <($SQLCMD "select GAMETYPE,GAMENAME from srcinfo;")
  2704.         ) | column -t -s "|"
  2705.         echo ""
  2706.         echo "To install a new srcds game type, choose \"NEW\"."
  2707.         echo "To quit, choose \"QUIT\"."
  2708.         echo ""
  2709.         GAMETYPE_LIST=$($SQLCMD "select GAMETYPE from srcinfo;")
  2710.         select IN_INSTALLGAMETYPE in $GAMETYPE_LIST NEW QUIT ; do
  2711.             case "$IN_INSTALLGAMETYPE" in
  2712.                 'QUIT')
  2713.                     echo ""
  2714.                     echo "Quitting."
  2715.                     echo ""
  2716.                     return 0
  2717.                 ;;
  2718.                 'NEW')
  2719.                     GAMETYPE_ARG="add"
  2720.                     f_changegametype ; X_NEWINSTALL_NEWTYPE="$?"
  2721.                     if [[ "$X_NEWINSTALL_NEWTYPE" == 0 ]] ; then
  2722.                         echo "We will now restart the installation process and the new game type should be available for selection."
  2723.                         echo ""
  2724.                     else
  2725.                         echoerr "Installing the new game type failed. Unable to continue."
  2726.                         echoerr ""
  2727.                         return "$X_NEWINSTALL_NEWTYPE"
  2728.                     fi
  2729.                     break
  2730.                 ;;
  2731.                 *)
  2732.                     # Validate it.
  2733.                     if [[ -z "$IN_INSTALLGAMETYPE" ]] ; then
  2734.                         echo "Choose the number which corresponds with the game type which you wish to install."
  2735.                     else
  2736.                         IN_GAMETYPE=$IN_INSTALLGAMETYPE
  2737.                         f_loaddbsrcinfo
  2738.                         if [[ "$LOADDBSRCINFO_FAIL" == 1 ]] ; then exit 1 ; fi
  2739.                         f_installnewlinked
  2740.                         return "$?"
  2741.                     fi
  2742.             esac
  2743.         done
  2744.     done
  2745. }
  2746.  
  2747. f_tmuxattach() {
  2748.     # Attach to the tmux session for a given installation.
  2749.     # FIXME: Need to call f_getpid here and give feedback based on status.
  2750.     # FIXME: current stdout is bad.
  2751.     #
  2752.     f_loaddbinst
  2753.     if [[ "$LOADDBINSTINFO_FAIL" == 1 ]] ; then return 1 ; fi
  2754.     # If we didn't get a valid INSTALLID, fail.
  2755.     if [[ -z "$INSTALLID" ]] ; then
  2756.         return 1
  2757.     fi
  2758.     echo ""
  2759.     echo "Attaching to tmux session $INSTALLID: "
  2760.     tmux attach-session -t "$INSTALLID" ; X_TMUXATTACH=$?
  2761.     echo ""
  2762.     return 0
  2763. }
  2764.  
  2765. f_listbootstartable() {
  2766.     # Get a list of srcds installations which should be started at host boot and place them into LIST_BOOTSTARTABLE.
  2767.     #
  2768.     LIST_BOOTSTARTABLE=$($SQLCMD "select INSTALLID from inst where BOOTSTART='1' and not INSTTYPE='master';")
  2769. }
  2770.  
  2771. f_listrunnable() {
  2772.     # Get a list of possibly runnable srcds installations and place them into LIST_RUNNABLE.
  2773.     #
  2774.     LIST_RUNNABLE=$($SQLCMD "select INSTALLID from inst where INSTTYPE='linked' or INSTTYPE='standalone';")
  2775.     # LIST_RUNNABLE=$($SQLCMD "select INSTALLID from inst where ALLOWSTART='1' and not INSTTYPE='master';")
  2776. }
  2777.  
  2778. f_listrunning() {
  2779.     # Get a list of actively running srcds installation and place them into a list; LIST_RUNNING
  2780.     #
  2781.     f_listrunnable
  2782.     #
  2783.     # Look for the PID file in all runnable servers to get the list of actively running.
  2784.     for EACH in $LIST_RUNNABLE ; do
  2785.         # FIXME: Need to use RUNSTATUS here instead.
  2786.         # echoerr "DEBUG: EACH=$EACH"
  2787.         # echoerr "DEBUG: PIDFILE=$PIDFILE"
  2788.         PIDFILE=$APPDIR/$EACH/server.pid
  2789.         if [[ -f "$PIDFILE" ]] ; then
  2790.             if [[ -z "$LIST_RUNNING" ]] ; then
  2791.                 LIST_RUNNING="$EACH"
  2792.             else
  2793.                 LIST_RUNNING="$LIST_RUNNING $EACH"
  2794.             fi
  2795.         else
  2796.             continue
  2797.         fi
  2798.     done
  2799. }
  2800.  
  2801. f_loaddbsrcinfo() {
  2802.     # Load the database config for a particular game type.
  2803.     #
  2804.     # echoerr "DEBUG: Loaded src DB for $IN_GAMETYPE"
  2805.     LOADDBSRCINFO_FAIL=0
  2806.     # We must have a target srcinfo row to work with.
  2807.     if [[ -z "$IN_GAMETYPE" ]] ; then
  2808.         LOADDBSRCINFO_FAIL=1
  2809.         echoerr ""
  2810.         echoerr "ERROR: Request to load game type (srcinfo) config, but no valid input."
  2811.         echoerr ""
  2812.         return 1
  2813.     fi
  2814.     # Get the srcinfo row for the game type in question.
  2815.     while IFS='|' read GAMETYPE GAMENAME GAMEARG UPDATER HLDSID SERVER_APPID CLIENT_APPID BINDIRSUBD STEAMINF INSTDEF_ALLOWUPDATE INSTDEF_ALLOWSTART INSTDEF_BOOTSTART INSTDEF_AUTOCLEANUP INSTDEF_MAILNOTIFY INSTDEF_MAILTO INSTDEF_STARTBIN INSTDEF_STARTBINARGS INSTDEF_IPADDR INSTDEF_CLIENTPORT INSTDEF_HOSTPORT INSTDEF_TVPORT INSTDEF_STEAMPORT INSTDEF_RECOVER_CRASH_ENABLE INSTDEF_RECOVER_WATCHDOG_ENABLE INSTDEF_RECOVER_WATCHDOG_TEST_INTERVAL INSTDEF_RECOVER_WATCHDOG_POLL_MAX INSTDEF_RECOVER_WATCHDOG_START_WAIT INSTDEF_RECOVER_SLEEP ; do
  2816.         DB_GAMETYPE=$GAMETYPE
  2817.         DB_GAMENAME=$GAMENAME
  2818.         DB_GAMEARG=$GAMEARG
  2819.         DB_UPDATER=$UPDATER
  2820.         DB_HLDSID=$HLDSID
  2821.         DB_SERVER_APPID=$SERVER_APPID
  2822.         DB_CLIENT_APPID=$CLIENT_APPID
  2823.         DB_BINDIRSUBD=$BINDIRSUBD
  2824.         DB_STEAMINF=$STEAMINF
  2825.         DB_INSTDEF_ALLOWUPDATE="$INSTDEF_ALLOWUPDATE"
  2826.         DB_INSTDEF_ALLOWSTART="$INSTDEF_ALLOWSTART"
  2827.         DB_INSTDEF_BOOTSTART="$INSTDEF_BOOTSTART"
  2828.         DB_INSTDEF_AUTOCLEANUP="$INSTDEF_AUTOCLEANUP"
  2829.         DB_INSTDEF_MAILNOTIFY="$INSTDEF_MAILNOTIFY"
  2830.         DB_INSTDEF_MAILTO="$INSTDEF_MAILTO"
  2831.         DB_INSTDEF_STARTBIN="$INSTDEF_STARTBIN"
  2832.         DB_INSTDEF_STARTBINARGS="$INSTDEF_STARTBINARGS"
  2833.         DB_INSTDEF_IPADDR="$INSTDEF_IPADDR"
  2834.         DB_INSTDEF_CLIENTPORT="$INSTDEF_CLIENTPORT"
  2835.         DB_INSTDEF_HOSTPORT="$INSTDEF_HOSTPORT"
  2836.         DB_INSTDEF_TVPORT="$INSTDEF_TVPORT"
  2837.         DB_INSTDEF_STEAMPORT="$INSTDEF_STEAMPORT"
  2838.         DB_INSTDEF_RECOVER_CRASH_ENABLE="$INSTDEF_RECOVER_CRASH_ENABLE"
  2839.         DB_INSTDEF_RECOVER_WATCHDOG_ENABLE="$INSTDEF_RECOVER_WATCHDOG_ENABLE"
  2840.         DB_INSTDEF_RECOVER_WATCHDOG_TEST_INTERVAL="$INSTDEF_RECOVER_WATCHDOG_TEST_INTERVAL"
  2841.         DB_INSTDEF_RECOVER_WATCHDOG_POLL_MAX="$INSTDEF_RECOVER_WATCHDOG_POLL_MAX"
  2842.         DB_INSTDEF_RECOVER_WATCHDOG_START_WAIT="$INSTDEF_RECOVER_WATCHDOG_START_WAIT"
  2843.         DB_INSTDEF_RECOVER_SLEEP="$INSTDEF_RECOVER_SLEEP"
  2844.     done < <($SQLCMD "select GAMETYPE,GAMENAME,GAMEARG,UPDATER,HLDSID,SERVER_APPID,CLIENT_APPID,BINDIRSUBD,STEAMINF,INSTDEF_ALLOWUPDATE,INSTDEF_ALLOWSTART,INSTDEF_BOOTSTART,INSTDEF_AUTOCLEANUP,INSTDEF_MAILNOTIFY,INSTDEF_MAILTO,INSTDEF_STARTBIN,INSTDEF_STARTBINARGS,INSTDEF_IPADDR,INSTDEF_CLIENTPORT,INSTDEF_HOSTPORT,INSTDEF_TVPORT,INSTDEF_STEAMPORT,INSTDEF_RECOVER_CRASH_ENABLE,INSTDEF_RECOVER_WATCHDOG_ENABLE,INSTDEF_RECOVER_WATCHDOG_TEST_INTERVAL,INSTDEF_RECOVER_WATCHDOG_POLL_MAX,INSTDEF_RECOVER_WATCHDOG_START_WAIT,INSTDEF_RECOVER_SLEEP from srcinfo where GAMETYPE='$IN_GAMETYPE';")
  2845.     X_DBLOADCFG_SRCINFO=$?
  2846.     if [[ ! "$X_DBLOADCFG_SRCINFO" == 0 ]] ; then
  2847.         LOADDBSRCINFO_FAIL=1
  2848.         echoerr ""
  2849.         echoerr "ERROR: The database command returned exit code $X_DBLOADCFG_SRCINFO."
  2850.         echoerr "  Unable to retrieve necessary information."
  2851.         echoerr ""
  2852.         return 1
  2853.     fi
  2854.     # Verify some of the information from the database.
  2855.     # echoerr "DEBUG: IN_GAMETYPE=$IN_GAMETYPE, GAMETYPE=$GAMETYPE, DB_GAMETYPE=$DB_GAMETYPE"
  2856.     if [[ ! "$IN_GAMETYPE" == "$DB_GAMETYPE" ]] ; then
  2857.         LOADDBSRCINFO_FAIL=1
  2858.         echoerr ""
  2859.         echoerr "ERROR: No such game type found, or the database record could not be loaded."
  2860.         echoerr ""
  2861.         return 1
  2862.     fi
  2863.     # Normalize database parameters.
  2864.         GAMETYPE=$DB_GAMETYPE
  2865.         GAMENAME=$DB_GAMENAME
  2866.         GAMEARG=$DB_GAMEARG
  2867.         UPDATER=$DB_UPDATER
  2868.         HLDSID=$DB_HLDSID
  2869.         SERVER_APPID=$DB_SERVER_APPID
  2870.         CLIENT_APPID=$DB_CLIENT_APPID
  2871.         BINDIRSUBD=$DB_BINDIRSUBD
  2872.         STEAMINF=$DB_STEAMINF
  2873.         INSTDEF_ALLOWUPDATE=$DB_INSTDEF_ALLOWUPDATE
  2874.         INSTDEF_ALLOWSTART=$DB_INSTDEF_ALLOWSTART
  2875.         INSTDEF_BOOTSTART=$DB_INSTDEF_BOOTSTART
  2876.         INSTDEF_AUTOCLEANUP=$DB_INSTDEF_AUTOCLEANUP
  2877.         INSTDEF_MAILNOTIFY=$DB_INSTDEF_MAILNOTIFY
  2878.         INSTDEF_MAILTO=$DB_INSTDEF_MAILTO
  2879.         INSTDEF_STARTBIN=$DB_INSTDEF_STARTBIN
  2880.         INSTDEF_STARTBINARGS=$DB_INSTDEF_STARTBINARGS
  2881.         INSTDEF_IPADDR=$DB_INSTDEF_IPADDR
  2882.         INSTDEF_CLIENTPORT=$DB_INSTDEF_CLIENTPORT
  2883.         INSTDEF_HOSTPORT=$DB_INSTDEF_HOSTPORT
  2884.         INSTDEF_TVPORT=$DB_INSTDEF_TVPORT
  2885.         INSTDEF_STEAMPORT=$DB_INSTDEF_STEAMPORT
  2886.         INSTDEF_RECOVER_CRASH_ENABLE="$DB_INSTDEF_RECOVER_CRASH_ENABLE"
  2887.         INSTDEF_RECOVER_WATCHDOG_ENABLE="$DB_INSTDEF_RECOVER_WATCHDOG_ENABLE"
  2888.         INSTDEF_RECOVER_WATCHDOG_TEST_INTERVAL="$DB_INSTDEF_RECOVER_WATCHDOG_TEST_INTERVAL"
  2889.         INSTDEF_RECOVER_WATCHDOG_POLL_MAX="$DB_INSTDEF_RECOVER_WATCHDOG_POLL_MAX"
  2890.         INSTDEF_RECOVER_WATCHDOG_START_WAIT="$DB_INSTDEF_RECOVER_WATCHDOG_START_WAIT"
  2891.         INSTDEF_RECOVER_SLEEP="$DB_INSTDEF_RECOVER_SLEEP"
  2892.     # Set the master installation, if it exists.
  2893.     USEMASTER=$($SQLCMD "select INSTALLID from inst where INSTTYPE='master' and GAMETYPE='$GAMETYPE' limit 1;")
  2894.     #
  2895. }
  2896.  
  2897. f_loaddbinst() {
  2898.     # Load the database config for a particular installation.
  2899.     #
  2900.     # echoerr "DEBUG: Loading inst table for $IN_INSTALLID"
  2901.     LOADDBINSTINFO_FAIL=0
  2902.     # We must have a target inst row to work with.
  2903.     if [[ -z "$IN_INSTALLID" ]] ; then
  2904.         LOADDBINSTINFO_FAIL=1
  2905.         echoerr ""
  2906.         echoerr "ERROR: Request to load installation (inst) config, but no valid input."
  2907.         echoerr ""
  2908.         return 1
  2909.     fi
  2910.     DEFAULT_MAILTO=$WRENCH_MAILTO
  2911.     # Get the inst record for the working installation, and the srcinfo along with it via a join.
  2912.     while IFS='|' read INSTALLID GAMETYPE INSTTYPE IPADDR CLIENTPORT HOSTPORT TVPORT STEAMPORT GAMENAME GAMEARG UPDATER HLDSID SERVER_APPID CLIENT_APPID BINDIRSUBD STEAMINF ALLOWUPDATE ALLOWSTART BOOTSTART AUTOCLEANUP MAILNOTIFY MAILTO STARTBIN STARTBINARGS RECOVER_CRASH_ENABLE RECOVER_WATCHDOG_ENABLE RECOVER_WATCHDOG_TEST_INTERVAL RECOVER_WATCHDOG_POLL_MAX RECOVER_WATCHDOG_START_WAIT RECOVER_SLEEP INSTDEF_ALLOWUPDATE INSTDEF_ALLOWSTART INSTDEF_BOOTSTART INSTDEF_AUTOCLEANUP INSTDEF_MAILNOTIFY INSTDEF_MAILTO INSTDEF_STARTBIN INSTDEF_STARTBINARGS INSTDEF_IPADDR INSTDEF_CLIENTPORT INSTDEF_HOSTPORT INSTDEF_TVPORT INSTDEF_STEAMPORT ; do
  2913.         DB_INSTALLID=$INSTALLID
  2914.         DB_GAMETYPE=$GAMETYPE
  2915.         DB_INSTTYPE=$INSTTYPE
  2916.         DB_IPADDR=$IPADDR
  2917.         DB_CLIENTPORT=$CLIENTPORT
  2918.         DB_HOSTPORT=$HOSTPORT
  2919.         DB_TVPORT=$TVPORT
  2920.         DB_STEAMPORT=$STEAMPORT
  2921.         DB_GAMENAME=$GAMENAME
  2922.         DB_GAMEARG=$GAMEARG
  2923.         DB_UPDATER=$UPDATER
  2924.         DB_HLDSID=$HLDSID
  2925.         DB_SERVER_APPID=$SERVER_APPID
  2926.         DB_CLIENT_APPID=$CLIENT_APPID
  2927.         DB_BINDIRSUBD=$BINDIRSUBD
  2928.         DB_STEAMINF=$STEAMINF
  2929.         DB_ALLOWUPDATE=$ALLOWUPDATE
  2930.         DB_ALLOWSTART=$ALLOWSTART
  2931.         DB_BOOTSTART=$BOOTSTART
  2932.         DB_AUTOCLEANUP=$AUTOCLEANUP
  2933.         DB_MAILNOTIFY=$MAILNOTIFY
  2934.         DB_MAILTO=$MAILTO
  2935.         DB_STARTBIN=$STARTBIN
  2936.         DB_STARTBINARGS="$STARTBINARGS"
  2937.         DB_RECOVER_CRASH_ENABLE="$RECOVER_CRASH_ENABLE"
  2938.         DB_RECOVER_WATCHDOG_ENABLE="$RECOVER_WATCHDOG_ENABLE"
  2939.         DB_RECOVER_WATCHDOG_TEST_INTERVAL="$RECOVER_WATCHDOG_TEST_INTERVAL"
  2940.         DB_RECOVER_WATCHDOG_POLL_MAX="$RECOVER_WATCHDOG_POLL_MAX"
  2941.         DB_RECOVER_WATCHDOG_START_WAIT="$RECOVER_WATCHDOG_START_WAIT"
  2942.         DB_RECOVER_SLEEP="$RECOVER_SLEEP"
  2943.         DB_INSTDEF_ALLOWUPDATE="$INSTDEF_ALLOWUPDATE"
  2944.         DB_INSTDEF_ALLOWSTART="$INSTDEF_ALLOWSTART"
  2945.         DB_INSTDEF_BOOTSTART="$INSTDEF_BOOTSTART"
  2946.         DB_INSTDEF_AUTOCLEANUP="$INSTDEF_AUTOCLEANUP"
  2947.         DB_INSTDEF_MAILNOTIFY="$INSTDEF_MAILNOTIFY"
  2948.         DB_INSTDEF_MAILTO="$INSTDEF_MAILTO"
  2949.         DB_INSTDEF_STARTBIN="$INSTDEF_STARTBIN"
  2950.         DB_INSTDEF_STARTBINARGS="$INSTDEF_STARTBINARGS"
  2951.         DB_INSTDEF_IPADDR="$INSTDEF_IPADDR"
  2952.         DB_INSTDEF_CLIENTPORT="$INSTDEF_CLIENTPORT"
  2953.         DB_INSTDEF_HOSTPORT="$INSTDEF_HOSTPORT"
  2954.         DB_INSTDEF_TVPORT="$INSTDEF_TVPORT"
  2955.         DB_INSTDEF_STEAMPORT="$INSTDEF_STEAMPORT"
  2956.     done < <($SQLCMD "select inst.INSTALLID,inst.GAMETYPE,inst.INSTTYPE,inst.IPADDR,inst.CLIENTPORT,inst.HOSTPORT,inst.TVPORT,inst.STEAMPORT,srcinfo.GAMENAME,srcinfo.GAMEARG,srcinfo.UPDATER,srcinfo.HLDSID,srcinfo.SERVER_APPID,srcinfo.CLIENT_APPID,srcinfo.BINDIRSUBD,srcinfo.STEAMINF,inst.ALLOWUPDATE,inst.ALLOWSTART,inst.BOOTSTART,inst.AUTOCLEANUP,inst.MAILNOTIFY,inst.MAILTO,inst.STARTBIN,inst.STARTBINARGS,inst.RECOVER_CRASH_ENABLE,inst.RECOVER_WATCHDOG_ENABLE,inst.RECOVER_WATCHDOG_TEST_INTERVAL,inst.RECOVER_WATCHDOG_POLL_MAX,inst.RECOVER_WATCHDOG_START_WAIT,inst.RECOVER_SLEEP,srcinfo.INSTDEF_ALLOWUPDATE,srcinfo.INSTDEF_ALLOWSTART,srcinfo.INSTDEF_BOOTSTART,srcinfo.INSTDEF_AUTOCLEANUP,srcinfo.INSTDEF_MAILNOTIFY,srcinfo.INSTDEF_MAILTO,srcinfo.INSTDEF_STARTBIN,srcinfo.INSTDEF_STARTBINARGS,srcinfo.INSTDEF_IPADDR,srcinfo.INSTDEF_CLIENTPORT,srcinfo.INSTDEF_HOSTPORT,srcinfo.INSTDEF_TVPORT,srcinfo.INSTDEF_STEAMPORT from inst,srcinfo on inst.GAMETYPE=srcinfo.GAMETYPE where INSTALLID='$IN_INSTALLID';")
  2957.     X_DBLOADCFG_INST=$?
  2958.     # echoerr "DEBUG: DB inst info loaded, X_DBLOADCFG_INST=$X_DBLOADCFG_INST"
  2959.     if [[ ! "$X_DBLOADCFG_INST" == 0 ]] ; then
  2960.         LOADDBINSTINFO_FAIL=1
  2961.         echoerr ""
  2962.         echoerr "ERROR: The database command returned exit code $X_DBLOADCFG_INST."
  2963.         echoerr "  Unable to load the inst table data."
  2964.         echoerr ""
  2965.         return 1
  2966.     fi
  2967.     # Verify some of the information from the database.
  2968.     # echoerr "DEBUG: IN_INSTALLID=$IN_INSTALLID, DB_INSTALLID=$DB_INSTALLID"
  2969.     if [[ ! "$IN_INSTALLID" == "$DB_INSTALLID" ]] ; then
  2970.         LOADDBINSTINFO_FAIL=1
  2971.         echoerr ""
  2972.         echoerr "ERROR: No such installation found, or the database record could not be loaded."
  2973.         echoerr ""
  2974.         return 1
  2975.     fi
  2976.     # Check that a installation directory exists for the DB record, but only if we are not doing an install/uninstall.
  2977.     # FIXME: We should probably make this a soft erorr and let the calling function decide on what to do with it.
  2978.     if [[ ! -d "$APPDIR/$DB_INSTALLID" ]] && [[ ! "$IN_ARG" == "install" ]] && [[ ! "$IN_ARG" == "uninstall" ]]; then
  2979.         LOADDBINSTINFO_FAIL=1
  2980.         echoerr ""
  2981.         echoerr "ERROR: No installation directory for $DB_INSTALLID found at $APPDIR/$DB_INSTALLID"
  2982.         echoerr ""
  2983.         return 1
  2984.     fi
  2985.     # Normalize database parameters.
  2986.         INSTALLID=$DB_INSTALLID
  2987.         INSTALLDIR=$APPDIR/$IN_INSTALLID # Set INSTALLDIR.
  2988.         GAMETYPE=$DB_GAMETYPE
  2989.         INSTTYPE=$DB_INSTTYPE
  2990.         IPADDR=$DB_IPADDR
  2991.         CLIENTPORT=$DB_CLIENTPORT
  2992.         HOSTPORT=$DB_HOSTPORT
  2993.         TVPORT=$DB_TVPORT
  2994.         STEAMPORT=$DB_STEAMPORT
  2995.         GAMENAME=$DB_GAMENAME
  2996.         GAMEARG=$DB_GAMEARG
  2997.         UPDATER=$DB_UPDATER
  2998.         HLDSID=$DB_HLDSID
  2999.         SERVER_APPID=$DB_SERVER_APPID
  3000.         CLIENT_APPID=$DB_CLIENT_APPID
  3001.         BINDIRSUBD=$DB_BINDIRSUBD
  3002.         STEAMINF=$DB_STEAMINF
  3003.         STEAMINF_FILE=$INSTALLDIR/$DB_STEAMINF # We need the full file path, since STEAMINF is relative.
  3004.         ALLOWUPDATE=$DB_ALLOWUPDATE
  3005.         ALLOWSTART=$DB_ALLOWSTART
  3006.         BOOTSTART=$DB_BOOTSTART
  3007.         AUTOCLEANUP=$DB_AUTOCLEANUP
  3008.         MAILNOTIFY=$DB_MAILNOTIFY
  3009.         MAILTO=${DB_MAILTO:-$DEFAULT_MAILTO} # Use the default MAILTO, if the DB_MAILTO is blank.
  3010.         STARTBIN=$DB_STARTBIN
  3011.         RECOVER_CRASH_ENABLE="$DB_RECOVER_CRASH_ENABLE"
  3012.         RECOVER_WATCHDOG_ENABLE="$DB_RECOVER_WATCHDOG_ENABLE"
  3013.         RECOVER_WATCHDOG_TEST_INTERVAL="$DB_RECOVER_WATCHDOG_TEST_INTERVAL"
  3014.         RECOVER_WATCHDOG_POLL_MAX="$DB_RECOVER_WATCHDOG_POLL_MAX"
  3015.         RECOVER_WATCHDOG_START_WAIT="$DB_RECOVER_WATCHDOG_START_WAIT"
  3016.         RECOVER_SLEEP="$DB_RECOVER_SLEEP"
  3017.         INSTDEF_ALLOWUPDATE=$DB_INSTDEF_ALLOWUPDATE
  3018.         INSTDEF_ALLOWSTART=$DB_INSTDEF_ALLOWSTART
  3019.         INSTDEF_BOOTSTART=$DB_INSTDEF_BOOTSTART
  3020.         INSTDEF_AUTOCLEANUP=$DB_INSTDEF_AUTOCLEANUP
  3021.         INSTDEF_MAILNOTIFY=$DB_INSTDEF_MAILNOTIFY
  3022.         INSTDEF_MAILTO=$DB_INSTDEF_MAILTO
  3023.         INSTDEF_STARTBIN=$DB_INSTDEF_STARTBIN
  3024.         INSTDEF_STARTBINARGS=$DB_INSTDEF_STARTBINARGS
  3025.         INSTDEF_IPADDR=$DB_INSTDEF_IPADDR
  3026.         INSTDEF_CLIENTPORT=$DB_INSTDEF_CLIENTPORT
  3027.         INSTDEF_HOSTPORT=$DB_INSTDEF_HOSTPORT
  3028.         INSTDEF_TVPORT=$DB_INSTDEF_TVPORT
  3029.         INSTDEF_STEAMPORT=$DB_INSTDEF_STEAMPORT
  3030.     #
  3031.     LOCKFILE=$INSTALLDIR/lockfile.lock
  3032.     PIDFILE=$INSTALLDIR/server.pid
  3033.     # In many cases, $BINDIRSUBD is going to be null, so this prevents a double slash.
  3034.     if [[ -z "$BINDIRSUBD" ]] ; then
  3035.         BINDIR=$INSTALLDIR
  3036.     else
  3037.         BINDIR=$INSTALLDIR/$BINDIRSUBD
  3038.     fi
  3039.     if [[ "$REPLAY_ENABLED" == 1 ]] && [[ -d "$REPLAYBASEDIR" ]] ; then
  3040.         REPLAYDIR="$REPLAYBASEDIR/$INSTALLID/replay"
  3041.     else
  3042.         REPLAYDIR=""
  3043.     fi
  3044.     # Normalize database parameters
  3045.         # STARTBINARGS is saved in the SQL DB as bash code, including variables. We need to eval it to do parameter expansion.
  3046.         # This needs to be done after all other parameters/variables are set, or it won't eval correctly.
  3047.         # We filter out special bash control characters for safety reasons. Imagine if someone wrote " ; rm -rf ~" in there.
  3048.         # We could do a tr sanitation whitelist like this:  tr -c -d '[:alnum:]$ [=-=].+_~/@'
  3049.         # Or we could do a tr sanitation blacklist like this:   tr -d '[:cntrl:]`;&*!@#(){}[]|\?<>'
  3050.         # Since the whitelist is more strict, we will use that.
  3051.         DB_STARTBINARGS=$(echo "$DB_STARTBINARGS" | tr -c -d '[:alnum:]$ [=-=].+_~/@')
  3052.         EVALTXT_STARTBINARGS="$DB_STARTBINARGS"
  3053.         STARTBINARGS=$(eval echo "$DB_STARTBINARGS")
  3054.     # If this is a linked install, declare the master installation. At this time, we do not support more than one master per game type.
  3055.     if [[ "$INSTTYPE" == linked ]] ; then
  3056.         USEMASTER=$($SQLCMD "select INSTALLID from inst where INSTTYPE='master' and GAMETYPE='$GAMETYPE' limit 1;")
  3057.     fi
  3058.     #
  3059.     # Validate critical info from the DB before we proceed.
  3060.     # Note that many different functions load info, so keep this generic.
  3061.     # echoerr "DEBUG: Finished inst table load for $INSTALLID"
  3062. }
  3063.  
  3064. f_showconfig() {
  3065.     # Show the database configuration for a particular installation.
  3066.     #
  3067.     f_loaddbinst
  3068.     if [[ "$LOADDBINSTINFO_FAIL" == 1 ]] ; then return 1 ; fi
  3069.     echo ""
  3070.     echo "Showing database configuration for installation \"$IN_INSTALLID\": "
  3071.     echo ""
  3072.     $SQLSHOWCMD "select * from inst where INSTALLID='$IN_INSTALLID';"
  3073.     echo ""
  3074.     echo "NOTE: STARTBINARGS evaluates to: "
  3075.     echo "$STARTBINARGS"
  3076.     echo ""
  3077.     echo "Also showing game type configuration (minus INSTDEF_s) for \"$GAMETYPE\": "
  3078.     echo ""
  3079.     $SQLSHOWCMD "select * from srcinfo where GAMETYPE='$GAMETYPE';" | egrep -v "^ *INSTDEF_"
  3080.     echo ""
  3081. }
  3082.  
  3083. f_rename() {
  3084.     # Rename an installation.
  3085.     #
  3086.     f_loaddbinst
  3087.     if [[ "$LOADDBINSTINFO_FAIL" == 1 ]] ; then return 1 ; fi
  3088.     # Do not rename master type installations.
  3089.     if [[ "$INSTTYPE" == "master" ]] ; then
  3090.         echoerr ""
  3091.         echoerr "ERROR: Master type installations can not be renamed."
  3092.         echoerr ""
  3093.         return 1
  3094.     fi
  3095.     f_quitifrunning # Do not proceed if the installation is actively running.
  3096.     if [[ "$RUNQUIT" == 1 ]] ; then return 1 ; fi
  3097.     #
  3098.     echo ""
  3099.     echo "This process will help you rename an existing installation."
  3100.     echo ""
  3101.     echo "To what shall \"$IN_INSTALLID\" be renamed to?"
  3102.     f_choosename
  3103.     #
  3104.     # Proceed to rename the installation.
  3105.     #
  3106.     echo -n "Renaming the installation in the database: "
  3107.     $SQLCMD "update inst set INSTALLID='$IN_INSTALLNAME' where INSTALLID='$IN_INSTALLID';"
  3108.     X_RENAMEINSTDB=$?
  3109.     if [[ ! "$X_RENAMEINSTDB" == 0 ]] ; then
  3110.         echoerr ""
  3111.         echoerr "ERROR: The database command returned exit code $X_RECONFIGINST."
  3112.         echoerr "  Unable to rename the DB entry. Stopping here."
  3113.         echoerr ""
  3114.         return 1
  3115.     else
  3116.         echo "Done"
  3117.     fi
  3118.     echo -n "Renaming the installation directory: "
  3119.     if [[ -d "$APPDIR/$IN_INSTALLID" ]] ; then
  3120.         mv "$APPDIR/$IN_INSTALLID" "$APPDIR/$IN_INSTALLNAME" ; X_RENAMEINSTDIR=$?
  3121.         if [[ ! "$X_RENAMEINSTDB" == 0 ]] ; then
  3122.             echoerr ""
  3123.             echoerr "ERROR: Renaming the installation directory failed."
  3124.             echoerr ""
  3125.             return 1
  3126.         else
  3127.             echo "Done"
  3128.         fi
  3129.     else
  3130.         echoerr ""
  3131.         echoerr "ERROR: No installation directory found!"
  3132.         echoerr ""
  3133.         return 1
  3134.     fi
  3135.     # Rename the replay directory.
  3136.     if [[ "$REPLAY_ENABLED" == 1 ]] && [[ -d "$REPLAYBASEDIR/$IN_INSTALLID" ]] ; then
  3137.         echo -n "Renaming the replay webserver directory: "
  3138.         mv "$REPLAYBASEDIR/$IN_INSTALLID" "$REPLAYBASEDIR/$IN_INSTALLNAME"
  3139.         echo "Done"
  3140.     fi
  3141.     echo ""
  3142.     echo "We have completed the rename process. However, you should check your installation files, as they may need to be updated as well."
  3143.     echo "This is especially true of the replay configuration, which specifies the replay file directory and URL presented to clients."
  3144.     echo ""
  3145. }
  3146.  
  3147. f_reconfig() {
  3148.     # Configure or reconfigure a srcds installation. Data is stored in the database.
  3149.     # NOTE: Do not perform any input validation here, since we may need to reconfigure both masters and linked types.
  3150.     #
  3151.     f_loaddbinst
  3152.     if [[ "$LOADDBINSTINFO_FAIL" == 1 ]] ; then return 1 ; fi
  3153.     f_quitifrunning # Do not proceed if the installation is actively running.
  3154.     if [[ "$RUNQUIT" == 1 ]] ; then return 1 ; fi
  3155.     #
  3156.     echo ""
  3157.     echo "This process will help you reconfigure an existing installation's configuration."
  3158.     echo ""
  3159.     echo "First, let's show the existing configuration: "
  3160.     f_showconfig
  3161.     echo "Would you like to proceed in reconfiguring this installation?"
  3162.     read -e -r -p "y/N: " -i "" IN_RECONFIG_CONFIRM
  3163.     if [[ ! "$IN_RECONFIG_CONFIRM" == [yY] ]]; then
  3164.         echo ""
  3165.         echo "Quitting."
  3166.         echo ""
  3167.         return 0
  3168.     fi
  3169.     echo ""
  3170.     echo "Okay, let's proceed."
  3171.     echo ""
  3172.     f_configinstall ; X_CONFIGINSTALL=$?
  3173.     if [[ "$X_CONFIGINSTALL" == 0 ]] ; then
  3174.         echo ""
  3175.         echo "Reconfig completed."
  3176.         echo ""
  3177.     fi
  3178. }
  3179.  
  3180. f_setup() {
  3181.     # Do the first-time setup. Dependency checks, creating the APPDIR, loading the DB, and similar requirements.
  3182.     #
  3183.     echo ""
  3184.     echo "This process will help you with the basic setup needed to get $MYNAME up and running."
  3185.     echo ""
  3186.     echo "If you have not already, be sure to read the ${MYNAME}_README.txt file. It contains important information."
  3187.     #
  3188.     # Dependency checks.
  3189.     echo ""
  3190.     echo "--"
  3191.     echo ""
  3192.     echo "$MYNAME requires a number of dependencies to run. We will try to verify them."
  3193.     echo ""
  3194.     echo "VERIFICATION: Shall we continue?"
  3195.     read -e -r -p "y/N: " -i "" REPLY
  3196.     if [[ ! "$REPLY" == [yY] ]]; then
  3197.         echo ""
  3198.         echo "Quitting."
  3199.         echo ""
  3200.         return 0
  3201.     fi
  3202.     #
  3203.     DEPENDENCIES="lns $QSTAT_CMD tmux sqlite3 xmlstarlet symlinks wget lsof sed awk xargs cat echo kill egrep mail mkdir chmod find cut tr ps wc ip sort uniq rm stat whoami basename date nice renice ionice pgrep pkill"
  3204.     for EACH in $DEPENDENCIES ; do
  3205.         DEP_TARGET=$(type -p $EACH 2> /dev/null)
  3206.         # Handle shell builtins too
  3207.         if [[ -z "$DEP_TARGET" ]] && [[ "$(type $EACH)" == "$EACH is a shell builtin" ]]; then
  3208.             DEP_TARGET="shell builtin"
  3209.         fi
  3210.         if [[ -z "$DEP_TARGET" ]] ; then
  3211.             DEP_FAIL=1
  3212.             DEP_TARGET="Not found."
  3213.             DEP_STATUS="FAILURE!"
  3214.         else
  3215.             DEP_STATUS="Success"
  3216.         fi
  3217.         echo "Checking dependency \"$EACH\": Status: $DEP_STATUS : Path: $DEP_TARGET"
  3218.     done
  3219.     echo ""
  3220.     if [[ "$DEP_FAIL" == 1 ]] ; then
  3221.         echoerr "ERROR: At least one dependency check failed. You will need to fix this before you can continue."
  3222.         echoerr "  The ${MYNAME}_readme.txt file has useful information regarding fixing dependancies."
  3223.         echoerr ""
  3224.         echoerr "Quitting."
  3225.         echoerr ""
  3226.         return 1
  3227.     else
  3228.         echo "All dependencies found successfully."
  3229.     fi
  3230.     #
  3231.     # Verify SteamCMD is installed where it should be.
  3232.     echo ""
  3233.     echo "--"
  3234.     echo ""
  3235.     echo "We will now verify that SteamCMD is installed, or install it if necessary."
  3236.     echo ""
  3237.     echo "VERIFICATION: Shall we continue?"
  3238.     read -e -r -p "y/N: " -i "" REPLY
  3239.     if [[ ! "$REPLY" == [yY] ]]; then
  3240.         echo ""
  3241.         echo "Quitting."
  3242.         echo ""
  3243.         return 0
  3244.     fi
  3245.     echo ""
  3246.     if [[ -x "$STEAMCMD_BIN" ]] ; then
  3247.         echo "SteamCMD found successfully: $STEAMCMD_BIN."
  3248.     else
  3249.         STEAMCMD_URL="http://media.steampowered.com/installer/steamcmd_linux.tar.gz"
  3250.         echo "SteamCMD not found. Do you want to install SteamCMD now?"
  3251.         echo "  SteamCMD will be installed in $STEAMCMD_BIN"
  3252.         echo ""
  3253.         read -e -r -p "y/N: " -i "" REPLY
  3254.         if [[ ! "$REPLY" == [yY] ]]; then
  3255.             echo ""
  3256.             echo "SteamCMD must be installed to use $MYNAME."
  3257.             echo "Quitting."
  3258.             echo ""
  3259.             return 0
  3260.         fi
  3261.         # FIXME: This is junk. I got lazy. I'll fix it some day.
  3262.         mkdir -p ~/bin/steamcmd || { echoerr "ERROR: Unable to create steamcmd dir!" ; return 1 ; }
  3263.         wget --timeout=30 --tries=3 -O ~/bin/steamcmd/steamcmd_linux.tar.gz "$STEAMCMD_URL" &> /dev/null || { echoerr "ERROR: SteamCMD download failed. Try again later?" ; return 1 ; }
  3264.         tar -xzf ~/bin/steamcmd/steamcmd_linux.tar.gz -C ~/bin/steamcmd/ || { echoerr "ERROR: Extracting the SteamCMD archive failed." ; return 1 ; }
  3265.         #
  3266.         # Verify that SteamCMD got installed correctly.
  3267.         if [[ ! -x "$STEAMCMD_BIN" ]] ; then
  3268.             echoerr ""
  3269.             echoerr "ERROR: Post-install failure. SteamCMD install failed somehow."
  3270.             echoerr ""
  3271.             return 1
  3272.         else
  3273.             echo ""
  3274.             echo "SteamCMD successfully installed."
  3275.         fi
  3276.        
  3277.     fi
  3278.     #
  3279.     # APPDIR check and creation.
  3280.     echo ""
  3281.     echo "--"
  3282.     echo ""
  3283.     echo "Now we will check if the configured APPDIR exists, and create it if necessary."
  3284.     echo "This is where the installation directories will reside."
  3285.     echo ""
  3286.     # Test to see if APPDIR already exists.
  3287.     if [[ -d "$APPDIR" ]] ; then
  3288.         echo "WARNING: APPDIR $APPDIR already seems to exist."
  3289.         echo ""
  3290.         echo "VERIFICATION: Shall we continue anyway?"
  3291.         read -e -r -p "y/N: " -i "" REPLY
  3292.         if [[ ! "$REPLY" == [yY] ]]; then
  3293.             echo ""
  3294.             echo "Quitting."
  3295.             echo ""
  3296.             return 0
  3297.         fi
  3298.     else
  3299.         echo "VERIFICATION: APPDIR not found. Shall we create it now?"
  3300.         read -e -r -p "y/N: " -i "" REPLY
  3301.         if [[ ! "$REPLY" == [yY] ]]; then
  3302.             echo ""
  3303.             echo "Quitting."
  3304.             echo ""
  3305.             return 0
  3306.         fi
  3307.         echo -n "Creating the APPDIR directory \"$APPDIR\": "
  3308.         mkdir "$APPDIR" ; X_MKDIR_APPDIR=$?
  3309.         if [[ "$X_MKDIR_APPDIR" == 0 ]] ; then
  3310.             chmod u=wrx,g-rwx,o-wrx "$APPDIR" # Set perms.
  3311.             echo "Done"
  3312.         else
  3313.             echo "Failure."
  3314.             echo ""
  3315.             echo "APPDIR creation attempt failed. This is probably because you don't have filesystem permission to do so."
  3316.             echo "  This is not unexpected if you were trying to install into /srv."
  3317.             echo "  Please manually create the directory and try setup again."
  3318.             echo ""
  3319.             return 1
  3320.         fi
  3321.     fi
  3322.     #
  3323.     # DB installation.
  3324.     echo ""
  3325.     echo "--"
  3326.     echo ""
  3327.     echo "$MYNAME requires an SQLite database to keep track of installations and game server types."
  3328.     # Skip this step if a DB file already exists.
  3329.     if [[ -f "$SQLDBFILE" ]] ; then
  3330.         echo ""
  3331.         echo "A database file appears to already be present. Skipping this step."
  3332.     else
  3333.         echo "  Do you want download a sample DB which includes info for common srcds servers "
  3334.         echo "  (TF2, CSS, CSGO, L4D2, HL2DM), or do you want to start out with a new, blank, DB?"
  3335.         echo "  HINT: You probably want to download it."
  3336.         echo ""
  3337.         # FIXME: Does this even work any more? Need to test this because of pastebin's desire to manage files.
  3338.         select DB_OPT in DOWNLOAD NEW ; do
  3339.             if [[ "$DB_OPT" == "DOWNLOAD" ]] ; then
  3340.                 # Sample DB
  3341.                 GETDBURL="http://pastebin.com/download.php?i=eMKUsADN" # DB file source.
  3342.                 while true ; do
  3343.                     echo ""
  3344.                     echo "Downloading and installing database: "
  3345.                     wget --timeout=30 --tries=3 -O "$SQLDBFILE.tmp" "$GETDBURL" &> /dev/null || { echoerr "ERROR: DB download failed. Try again?" ; exit 1 ; }
  3346.                     $SQLITE "$SQLDBFILE" < "$SQLDBFILE.tmp" ; rm -f "$SQLDBFILE.tmp"
  3347.                     # FIXME: We need some real DB validation.
  3348.                     if [[ "$($SQLCMD '.tables')" == "inst     srcinfo" ]] ; then
  3349.                         echo "DB file downloaded and verified successfully."
  3350.                         break
  3351.                     else
  3352.                         rm -f "$SQLDBFILE"
  3353.                         echo "ERROR: DB file download or verification failed."
  3354.                         echo "  Either the file did not download, the hash test failed, or $MYNAME failed somewhere."
  3355.                         echo ""
  3356.                         echo "Do you want to try again?"
  3357.                         read -e -r -p "y/N: " -i "" IN_DLDBCONFIRM
  3358.                         if [[ "$IN_DLDBCONFIRM" == [yY] ]]; then
  3359.                             continue
  3360.                         else
  3361.                             echo ""
  3362.                             echo "Quitting."
  3363.                             echo ""
  3364.                             return 1
  3365.                         fi
  3366.                     fi
  3367.                 done
  3368.                 break
  3369.             elif [[ "$DB_OPT" == "NEW" ]] ; then
  3370.                 touch "$SQLDBFILE"
  3371.                 chmod u=wr,g-rwx,o-wrx "$SQLDBFILE"
  3372.                 echo ""
  3373.                 echo "New blank DB file created: \"$SQLDBFILE\""
  3374.                 break
  3375.             fi
  3376.         done
  3377.     fi
  3378.     #
  3379.     # Bash auto-completion script notice.
  3380.     echo ""
  3381.     echo "--"
  3382.     echo ""
  3383.     echo "$MYNAME offers a bash-autocompletion script. This enables command tab-completion of wrench arguments."
  3384.     echo "  On some system, you can simply append the contents of the bash-completion script to your ~/.bash_completion file."
  3385.     echo "  Otherwise, just append the file contents to your bash profile (.profile, .bash_profile, etc)."
  3386.     echo ""
  3387.     read -r -s -p "Press ENTER to continue." ; echo ""
  3388.     #
  3389.     # Renice notice.
  3390.     echo ""
  3391.     echo "--"
  3392.     echo ""
  3393.     echo "$MYNAME uses nice/renice to change the run priority of running installations."
  3394.     echo "  Renicing the srcds process can prevent performance problems caused by local load."
  3395.     echo "  Unfortunately, this requires user configuration in /etc/security/limits.conf."
  3396.     echo "  You will need to manually configure this if you want your srcds processes to be reniced."
  3397.     echo ""
  3398.     read -r -s -p "Press ENTER to continue." ; echo ""
  3399.     #
  3400.     # cron items notice.
  3401.     echo ""
  3402.     echo "--"
  3403.     echo ""
  3404.     echo "For the autoupdate and autocleanup features to work, you must add an entry to your cron tab."
  3405.     echo "  Examples have been listed below, where autoupdate will be run every five minutes, and autocleanup "
  3406.     echo "  will be run each day at 0500 local time: "
  3407.     echo ""
  3408.     echo '  */5 * * * *      ~/bin/wrench autoupdate 1> /dev/null'
  3409.     echo '  0 5 * * *      ~/bin/wrench autocleanup 1> /dev/null'
  3410.     echo ""
  3411.     read -r -s -p "Press ENTER to continue." ; echo ""
  3412.     #
  3413.     # Mail configuration notice.
  3414.     echo ""
  3415.     echo "--"
  3416.     echo ""
  3417.     echo "wrench sends email notifications for important events. Be sure to configure your host to send email and set MAILTO as apropriate."
  3418.     echo ""
  3419.     read -r -s -p "Press ENTER to continue." ; echo ""
  3420.     #
  3421.     # Firewall notice.
  3422.     echo ""
  3423.     echo "--"
  3424.     echo ""
  3425.     echo "If you have a firewall (and you should), be sure to allow inbound and outbound traffic for srcds."
  3426.     echo "  This script won't help you with configuring your firewall."
  3427.     echo ""
  3428.     read -r -s -p "Press ENTER to continue." ; echo ""
  3429.     #
  3430.     echo "Setup process completed!"
  3431.     echo ""
  3432.     echo "--"
  3433.     echo ""
  3434.     echo "Setup completed."
  3435.     echo ""
  3436.     echo "You should now be able to install a new srcds installation. Use the \"$MYNAME install\" command to do so."
  3437.     echo ""
  3438.     return 0
  3439. }
  3440.  
  3441. f_runwatchdog() {
  3442.     # A watchdog process used in wrench_run mode.
  3443.     # The watchdog exists to recover from when the SRCDS_LINUX process is still running, but has seized up.
  3444.     #
  3445.     # The watchdog requires certain env vars. If we don't have them, quit.
  3446.     # See EXPORT_VARS in f_start for what is being exported to wrench_run.
  3447.     for EACH in INSTALLDIR PIDFILE IPADDR HOSTPORT RECOVER_CRASH_ENABLE RECOVER_WATCHDOG_ENABLE RECOVER_WATCHDOG_TEST_INTERVAL RECOVER_WATCHDOG_POLL_MAX RECOVER_WATCHDOG_START_WAIT RECOVER_SLEEP; do
  3448.         if [[ -z "$EACH" ]] ; then # FIXME: Would like to use test -v here, but not portable.
  3449.             echoerr "$MYNAME: ERROR: environmental requirement \"$EACH\" not found."
  3450.             ENV_ERROR=1
  3451.         fi
  3452.     done
  3453.     if [[ "$ENV_ERROR" == 1 ]] ; then
  3454.         echoerr ""
  3455.         echoerr "$MYNAME watchdog: ERROR: At least one required envrionmental variable is missing."
  3456.         echoerr ""
  3457.         return 1
  3458.     fi
  3459.     #
  3460.     # qstat is required.
  3461.     if [[ "$QSTAT_MISSING" == 1 ]] ; then
  3462.         echoerr "$MYNAME watchdog: Watchdog disabled because qstat could not be found."
  3463.         return 1
  3464.     fi
  3465.     #
  3466.     declare -i WATCHDOG_FAILCOUNT=0
  3467.     # Don't do anything until the server has been up at least this long.
  3468.     echo "$MYNAME watchdog: Waiting $RECOVER_WATCHDOG_START_WAIT seconds on startup before monitoring begins."
  3469.     sleep "$RECOVER_WATCHDOG_START_WAIT"
  3470.     echo "$MYNAME watchdog: Finished startup wait, begin monitoring server."
  3471.     #
  3472.     # FIXME: It might be better to background the job inside of the watchdog function instead of outside of it.
  3473.     while true ; do
  3474.         f_getpid ; X_GETPID=$?
  3475.         #
  3476.         # Watchdog should only be running if the SRCDS_LINUX process is running. Otherwise, quit. This is a safeguard.
  3477.         if [[ ! "$RUNSTATUS" == "running" ]] ; then
  3478.             echo "$MYNAME watchdog: Watchdog can't find process to watch. Quitting."
  3479.             return 0
  3480.         fi
  3481.         #
  3482.         # FIXME: Not sure if monitoring for "DOWN" reply is apropriate here.
  3483.         if ( f_qstat -retry 1 -nh -a2s "$IPADDR":"$HOSTPORT" | egrep "^$IPADDR:$HOSTPORT no response$|^$IPADDR:$HOSTPORT       DOWN$" &> /dev/null ) ; then
  3484.             WATCHDOG_FAILCOUNT=$(( $WATCHDOG_FAILCOUNT + 1 ))
  3485.             echo "$MYNAME watchdog: Server did not respond to query. Failure count: $WATCHDOG_FAILCOUNT."
  3486.         else
  3487.             # echo "$MYNAME watchdog: Server responding. Looks good."
  3488.             WATCHDOG_FAILCOUNT=0
  3489.         fi
  3490.         # If our fail counter gets reached, we need to kill the process, because it's seized up.
  3491.         if [[ "$WATCHDOG_FAILCOUNT" == "$RECOVER_WATCHDOG_POLL_MAX" ]] ; then
  3492.             echoerr "$MYNAME watchdog: Server appears to be seized up. Killing it to cause a restart/exit."
  3493.             if [[ "$X_GETPID" == "0" ]] ; then
  3494.                 # We kill the process, since it is almost certainly not going to respond to any other signal.
  3495.                 kill -SIGKILL "$GAMESERVPID"
  3496.                 # We did our job, now quit.
  3497.                 echo "$MYNAME watchdog: Watchdog quitting post-kill."
  3498.                 return 0
  3499.             else
  3500.                 echo "$MYNAME watchdog: Unable to get PID; unable to kill process. Watchdog quitting on error."
  3501.                 return 1
  3502.             fi
  3503.         fi
  3504.         sleep "$RECOVER_WATCHDOG_TEST_INTERVAL"
  3505.     done
  3506. }
  3507.  
  3508. f_stopwatchdog() {
  3509.     # Stop the watchdog process, if it is running.
  3510.     if [[ "$WATCHDOG_RUNNING" == 1 ]] && ( ps --no-headers -p $X_WATCHDOG_PID -o pid &> /dev/null ) ; then
  3511.         echo -n "$MYNAME: Stopping watchdog: "
  3512.         # kill -TERM "$X_WATCHDOG_PID" &> /dev/null
  3513.         kill -TERM "$X_WATCHDOG_PID" &> /dev/null
  3514.         wait "$X_WATCHDOG_PID" 2> /dev/null # This wait is here to supress job termination notifications.
  3515.         WATCHDOG_RUNNING=0
  3516.         echo "Done"
  3517.     fi
  3518. }
  3519.  
  3520. f_wrenchrun() {
  3521.     # wrench run (wrench_run) mode is a replacement for srcds_run.
  3522.     #
  3523.     # $1 must be "run" to run this func. This should be impossible to occur.
  3524.     if ! [[ "$1" == "run" ]] ; then
  3525.         echoerr ""
  3526.         echoerr "ERROR: The first argument for run mode must be \"run\"."
  3527.         echoerr "  This should never happen. Something is seriously wrong."
  3528.         echoerr ""
  3529.         return 1
  3530.     else
  3531.         # Remove "run" as $1 and move everything else over.
  3532.         shift
  3533.     fi
  3534.     #
  3535.     # srcds_run compatability: Set a number of vars based on what srcds_linux probably expects.
  3536.     WRENCHRUN_PARAMS=$*
  3537.     export LD_LIBRARY_PATH="$INSTALLDIR:$INSTALLDIR/bin:$LD_LIBRARY_PATH"
  3538.     GAME="$GAMEARG" # Possibly overwritten later by -game
  3539.     umask 002 # Needed for a local web server to serve replay files.
  3540.     #
  3541.     # Set certain envrionment vars based on stdin. This is mostly for compatability with srcds_run.
  3542.     while [[ "$#" -gt 0 ]] ; do
  3543.         case "$1" in
  3544.             '-h'|'--help')
  3545.                 f_wrenchrun_commandhelper
  3546.             ;;
  3547.             '+map')
  3548.                 MAP="$2"
  3549.                 shift 2
  3550.             ;;
  3551.             '-game')
  3552.                 GAME="$2"
  3553.                 shift 2
  3554.             ;;
  3555.             '-pidfile')
  3556.                 PID_FILE="$2" ; PID_FILE_SET=1
  3557.                 shift 2
  3558.             ;;
  3559.             '-debug')
  3560.                 DEBUG=1
  3561.                 shift
  3562.             ;;
  3563.             '-ignoresigint')
  3564.                 IGNORE_INT=1
  3565.                 shift
  3566.             ;;
  3567.         esac
  3568.         shift
  3569.     done
  3570.     #
  3571.     echo ""
  3572.     echo "$MYNAME: Starting installation \"$INSTALLID\"."
  3573.     echo "$MYNAME: Startup parameters: \"$WRENCHRUN_PARAMS\""
  3574.     echo ""
  3575.     #
  3576.     # Verify that we have required envrionmental variables.
  3577.     # See EXPORT_VARS in f_start for what is being exported to wrench_run.
  3578.     for EACH in INSTALLDIR PIDFILE GAMEARG GAME RECOVER_CRASH_ENABLE RECOVER_WATCHDOG_ENABLE RECOVER_WATCHDOG_TEST_INTERVAL RECOVER_WATCHDOG_POLL_MAX RECOVER_WATCHDOG_START_WAIT RECOVER_SLEEP; do
  3579.         if [[ -z "$EACH" ]] ; then # FIXME: Would like to use test -v here, but not portable.
  3580.             echoerr "$MYNAME: ERROR: environmental requirement \"$EACH\" not found."
  3581.             ENV_ERROR=1
  3582.         fi
  3583.     done
  3584.     if [[ "$ENV_ERROR" == 1 ]] ; then
  3585.         echoerr ""
  3586.         echoerr "$MYNAME: ERROR: At least one required envrionmental variable is missing."
  3587.         echoerr ""
  3588.         return 3
  3589.     fi
  3590.     #
  3591.     # $PWD must be $INSTALLDIR; verify.
  3592.     cd $INSTALLDIR &> /dev/null
  3593.     if ! [[ "$(pwd)" == "$INSTALLDIR" ]] ; then
  3594.         echoerr ""
  3595.         echoerr "$MYNAME: ERROR: The working directory must be INSTALLDIR to run $MYNAME, but is not."
  3596.         echoerr "  INSTALLDIR=$INSTALLDIR"
  3597.         echoerr "  PWD=$(pwd)"
  3598.         echoerr ""
  3599.         return 1
  3600.     fi
  3601.     #
  3602.     # PID_FILE and PIDFILE are possibly different. PID_FILE comes from the -pidfile argument, and PIDFILE is generated automatically in wrench.
  3603.     # However, these must be the same, or else it's an error. Usually, a user should just specify "-pidfile $PIDFILE" in the STARTUPARGS.
  3604.     # This will catch a user who has removed -pidfile or broken it.
  3605.     if [[ -z "$PID_FILE_SET" ]] ; then
  3606.         echoerr ""
  3607.         echoerr "$MYNAME: ERROR: -pidfile is missing from STARTBINARGS."
  3608.         echoerr "  The -pidfile argument is required for the startup arguments to pass to $SRCDS_LINUX."
  3609.         echoerr "  Add the following to your STARTBINARGS to fix this: \"-pidfile \$PIDFILE\""
  3610.         echoerr ""
  3611.         return 1
  3612.     fi
  3613.     if ! [[ "$PIDFILE" == "$PID_FILE" ]] ; then
  3614.         echoerr ""
  3615.         echoerr "$MYNAME: ERROR: the -pidfile argument is invalid."
  3616.         echoerr "  The -pidfile argument must be \"\$PIDFILE\"."
  3617.         echoerr""
  3618.         return 1
  3619.     fi
  3620.     #
  3621.     # Error if a directory within INSTALLDIR does not match up to GAMEARG/GAME.
  3622.     if ! [[ -d "$GAME" ]] ; then
  3623.         echoerr ""
  3624.         echoerr "$MYNAME: ERROR: GAME parameter \"$GAME\" does not match a directory within the INSTALLDIR."
  3625.         echoerr "  Are you trying to start the correct game type for this installation?"
  3626.         echoerr "  GAMEARG=\"$GAMEARG\", GAME=\"$GAME\""
  3627.         echoerr ""
  3628.         return 1
  3629.     fi
  3630.     #
  3631.     # Warn if +map is not given on the startup command. Not required, but often an issue.
  3632.     if [[ -z "$MAP" ]] ; then
  3633.         echo "$MYNAME: No map specified in startup arguments."
  3634.     fi
  3635.     #
  3636.     # If our host allows us to raise the process priority when starting SRCDS_LINUX, we should do that.
  3637.     if (egrep "$USER.*nice" /etc/security/limits.conf &> /dev/null) ; then
  3638.         WRENCHRUN_NICEOPTS="nice -n -10 ionice -c 2 -n 2"
  3639.     else
  3640.         WRENCHRUN_NICEOPTS=""
  3641.     fi
  3642.     #
  3643.     # Ignore SIGINT, which is what happens when you do ctrl+c.
  3644.     if [[ "$IGNORE_INT" == 1 ]] ; then
  3645.         trap "" SIGINT
  3646.         trap f_wrenchrun_cleanuptrap SIGHUP SIGQUIT SIGABRT
  3647.     else
  3648.         trap f_wrenchrun_cleanuptrap SIGHUP SIGINT SIGQUIT SIGABRT
  3649.     fi
  3650.     # Set up the crash counter array.
  3651.     declare -A ar_CRASHES
  3652.     declare -i CRASH_COUNTER ABSOLUTE_MAX_CRASH_COUNTER
  3653.     #
  3654.     # --
  3655.     #
  3656.     # Run the srcds binary program.
  3657.     while true ; do
  3658.         # Run the watchdog if it is enabled.
  3659.         if [[ "$RECOVER_WATCHDOG_ENABLE" == 1 ]] ; then
  3660.             f_runwatchdog & X_WATCHDOG_PID="$!"
  3661.             WATCHDOG_RUNNING=1
  3662.             echo "$MYNAME watchdog: Started watchdog process at PID: $X_WATCHDOG_PID."
  3663.         else
  3664.             echo "$MYNAME watchdog: RECOVER_WATCHDOG_ENABLE disabled. Not starting watchdog."
  3665.         fi
  3666.         #
  3667.         echo "$MYNAME: Executing $SRCDS_LINUX."
  3668.         $WRENCHRUN_NICEOPTS ./$SRCDS_LINUX $WRENCHRUN_PARAMS ; X_SRCDS_LINUX=$?
  3669.         f_stopwatchdog # We must stop the watchdog as soon as SRCDS_LINUX exits, no matter why it quit.
  3670.         if [[ "$X_SRCDS_LINUX" == 0 ]] ; then
  3671.             # Exit 0 is caused by issuing "quit" on the console.
  3672.             echo "$MYNAME: $SRCDS_LINUX quit normally, exit code: $X_SRCDS_LINUX."
  3673.             f_wrenchrun_cleanup
  3674.             break
  3675.         elif [[ "$X_SRCDS_LINUX" == 129 ]] ; then
  3676.             echo "$MYNAME: $SRCDS_LINUX quit on SIGHUP(1), exit code: $X_SRCDS_LINUX."
  3677.             f_wrenchrun_cleanup
  3678.             break
  3679.         elif [[ "$X_SRCDS_LINUX" == 130 ]] ; then
  3680.             # ctrl+c on console is SIGINT.
  3681.             echo "$MYNAME: $SRCDS_LINUX quit on SIGINT(2), exit code: $X_SRCDS_LINUX."
  3682.             f_wrenchrun_cleanup
  3683.             break
  3684.         elif [[ "$X_SRCDS_LINUX" == 131 ]] ; then
  3685.             echo "$MYNAME: $SRCDS_LINUX quit on SIGQUIT(3), exit code: $X_SRCDS_LINUX."
  3686.             f_wrenchrun_cleanup
  3687.             break
  3688.         elif [[ "$X_SRCDS_LINUX" == 134 ]] ; then
  3689.             echo "$MYNAME: $SRCDS_LINUX quit on SIGABRT(6), exit code: $X_SRCDS_LINUX."
  3690.             f_wrenchrun_cleanup
  3691.             break
  3692.         elif [[ "$X_SRCDS_LINUX" == 143 ]] ; then
  3693.             echo "$MYNAME: $SRCDS_LINUX quit on SIGTERM(15), exit code: $X_SRCDS_LINUX."
  3694.             f_wrenchrun_cleanup
  3695.             break
  3696.         else
  3697.             # Anything else is a crash.
  3698.             NEW_CRASHID="$(date +%s)" # Epoch seconds.
  3699.             CRASH_CAPTURE_PANE_FILE="$INSTALLDIR/crash_capture-pane_$(date +%Y%m%d%H%M%S)-$$.log" # FIXME: Not sub-second safe.
  3700.             #
  3701.             # A SIGKILL is special. This is how we force a seized/hung process to stop. It's still a type of crash though.
  3702.             # A kill could come from the watchdog, but it could also come from elsewhere, so we don't know.
  3703.             # FIXME: We can pipe back a signal from the watchdog process, I suppose.
  3704.             if [[ "$X_SRCDS_LINUX" == 137 ]] ; then
  3705.                 echoerr "$MYNAME: $SRCDS_LINUX killed on SIGKILL(9), exit code: $X_SRCDS_LINUX."
  3706.                 f_mailnotify crash "Server quit on SIGKILL(9), exit code: $X_SRCDS_LINUX."
  3707.             else
  3708.                 echoerr "$MYNAME: $SRCDS_LINUX crashed with exit code: $X_SRCDS_LINUX"
  3709.                 f_mailnotify crash "Server crashed, exit code: $X_SRCDS_LINUX."
  3710.             fi
  3711.             #
  3712.             # Write the recent tmux window pane history to a file, so that that it can be reviewed later.
  3713.             # Note that the default tmux scrollback buffer is limited to 2000 lines. Define "history-limit" in your .tmux.conf to change this.
  3714.             echoerr "$MYNAME: Saving recent tmux window pane history to file: $CRASH_CAPTURE_PANE_FILE"
  3715.             tmux capture-pane "$TMUX_CAPTURE_OPTS" -t "$INSTALLID":0.0 \; save-buffer "$CRASH_CAPTURE_PANE_FILE"
  3716.             #
  3717.             # If crash recovery is disabled via RECOVER_CRASH_ENABLE, stop here and don't restart.
  3718.             if [[ ! "$RECOVER_CRASH_ENABLE" == 1 ]] ; then
  3719.                 echoerr "$MYNAME: Crash recovery disabled. Will not restart."
  3720.                 f_wrenchrun_cleanup
  3721.                 break
  3722.             fi
  3723.             #
  3724.             # Stop a running installation if it crashes too often.
  3725.             ar_CRASHES["$NEW_CRASHID"]="$NEW_CRASHID" # Append the current crash ID to the crashes array.
  3726.             CRASH_COUNTER=0 # FIXME: Could use ${#ar_CRASHES[@]} instead.
  3727.             for EACH in "${ar_CRASHES[@]}" ; do
  3728.                 # echoerr "DEBUG: Processing crash ID $EACH"
  3729.                 # Find out if the current crash ID is within the limit window, and count it towards the limit max if so.
  3730.                 if [[ "$(( $NEW_CRASHID - $EACH ))" -le "$RECOVER_LIMIT_WINDOW" ]] ; then
  3731.                     CRASH_COUNTER=$(( CRASH_COUNTER + 1))
  3732.                     # echoerr "DEBUG: Added $EACH to crash count."
  3733.                 else
  3734.                     # Remove old crashes from the array.
  3735.                     unset ar_CRASHES[$EACH]
  3736.                     # echoerr "DEBUG: Removed old $EACH from crash array."
  3737.                 fi
  3738.             done
  3739.                 # echoerr "DEBUG: Crash array list is: $(echo "${ar_CRASHES[@]}" | tr '\n' ' ')"
  3740.             # If we have reached the maximum number of crashes allowed in this time window, we should not recover.
  3741.             if [[ "$CRASH_COUNTER" -ge "$RECOVER_LIMIT_MAX" ]] ; then
  3742.                 echoerr "$MYNAME: Crash limit reached. $CRASH_COUNTER crashes within $RECOVER_LIMIT_WINDOW seconds. Will not restart."
  3743.                 f_wrenchrun_cleanup
  3744.                 break
  3745.             fi
  3746.             #
  3747.             # The absolute max crash protection routine below is mostly for people who disable the regular crash limiter above.
  3748.             ABSOLUTE_MAX_CRASH_COUNTER=$(( ABSOLUTE_MAX_CRASH_COUNTER + 1))
  3749.             if [[ "$ABSOLUTE_MAX_CRASH_COUNTER" -gt "$RECOVERY_ABSOLUTE_MAX" ]] ; then
  3750.                 echoerr "$MYNAME: Absolute maximum number of crashes reached. Will not restart."
  3751.                 f_wrenchrun_cleanup
  3752.                 break
  3753.             fi
  3754.             #
  3755.             echoerr "$MYNAME: Restarting in $RECOVER_SLEEP seconds. Total crashes: $ABSOLUTE_MAX_CRASH_COUNTER"
  3756.             sleep "$RECOVER_SLEEP"
  3757.             echoerr ""
  3758.             continue
  3759.         fi
  3760.     done
  3761.     #
  3762.     unset ar_CRASHES # Delete the crash counter array.
  3763.     #
  3764.     echo ""
  3765.     echo "$MYNAME: Quitting."
  3766.     echo ""
  3767.     sleep 1 # Sleep a second. This helps f_start figure out what went wrong on failed startups.
  3768.     # sleep 60 # DEBUG
  3769. }
  3770.  
  3771. f_mailnotify() {
  3772.     # Mail notifications.
  3773.     #
  3774.     # Uncomment this to disable email notifications during testing.
  3775.     # echoerr "DEBUG: Mail notifications disabled." ; MAILNOTIFY=disabled
  3776.     #
  3777.     MAIL_TOPIC="$1"
  3778.     MAIL_MESSAGE="$2"
  3779.     # We need a MAIL_TOPIC
  3780.     if [[ ! -n "$MAIL_TOPIC" ]] ; then
  3781.         echoerr ""
  3782.         echoerr "ERROR: Mail notification called, but no topic specified."
  3783.         echoerr ""
  3784.         return 1
  3785.     fi
  3786.     # Only send mail if MAILNOTIFY is set.
  3787.     if [[ "$MAILNOTIFY" == 1 ]] ; then
  3788.         $MAILER -s "$MAIL_SUBJ_PREFIX $INSTALLID $MAIL_TOPIC " $MAILTO <<-ENDMESSAGE
  3789.            
  3790.             Date: $(date)
  3791.             Server: $INSTALLID
  3792.             Action: $MAIL_TOPIC (Called via: $IN_ARG)
  3793.             Message: $MAIL_MESSAGE
  3794.  
  3795.         ENDMESSAGE
  3796.     fi
  3797. }
  3798.  
  3799. f_wrenchrun_cleanuptrap() {
  3800.     # Cleanup trap.
  3801.     #
  3802.     echo ""
  3803.     echo "$MYNAME: Cleaning up: "
  3804.     f_wrenchrun_cleanup
  3805.     echo "$MYNAME: Cleanup done."
  3806.     echo ""
  3807.     exit 91
  3808. }
  3809.  
  3810. f_wrenchrun_commandhelper() {
  3811.     # Print this if input is nonsense or requests help.
  3812.     #
  3813.     echoerr ""
  3814.     echoerr "$MYNAME is a srcds_run replacement to be used with the wrench srcds control system."
  3815.     echoerr "  $MYNAME takes stdin and passes it directly on to the $SRCDS_LINUX binary, without modification."
  3816.     echoerr "  $MYNAME specific features are controled via parameters in the startup environment, as passed to it by wrench."
  3817.     echoerr ""
  3818.     echoerr "  For full documentation on wrench and $MYNAME, see the ${MYNAME}_README.txt file."
  3819.     echoerr ""
  3820.     exit 0
  3821. }
  3822.  
  3823. f_wrenchrun_cleanup() {
  3824.     # Stop the watchdog if it is running.
  3825.     f_stopwatchdog
  3826.     #
  3827.     # If the srcds_linux process is running, we want to pass signal on to it.
  3828.     # Most of the time, SRCDS_LINUX is already going to be dead by this point. This is for trapping a signal.
  3829.     f_getpid
  3830.     if ( [[ -n "$GAMESERVPID" ]] && [[ "$(ps --no-headers -p $GAMESERVPID -o comm 2> /dev/null)" == "$SRCDS_LINUX" ]] ) ; then
  3831.         echo -n "$MYNAME: Sending SIGINT to $SRCDS_LINUX, PID: $GAMESERVPID: "
  3832.         kill -s SIGINT "$GAMESERVPID"
  3833.         echo "Done"
  3834.     fi
  3835.     #
  3836.     # Delete the PIDFILE.
  3837.     # FIXME: is this even possible?
  3838.     if ( [[ -n "$PIDFILE" ]] && [[ -f "$PIDFILE" ]] ) ; then rm -f "$PIDFILE" || echoerr "$MYNAME: ERROR: Unable to remove PIDFILE!" ; fi
  3839.     if ( [[ -n "$PID_FILE_SET" ]] && [[ -f "$PID_FILE" ]] ) ; then rm -f "$PID_FILE" || echoerr "$MYNAME: ERROR: Unable to remove PID_FILE!" ; fi
  3840. }
  3841.  
  3842. f_help() {
  3843.     # Print some basic help info.
  3844.     BEENHERE_HELP=1
  3845.     echoerr ""
  3846.     echoerr "$MYNAME is a Source Dedicated Server (srcds) management tool for Valve's srcds-based game servers."
  3847.     echoerr ""
  3848.     echoerr "For more information, including full documentation, go here: "
  3849.     echoerr "  http://forums.srcds.com/viewpost/118741"
  3850.     f_commandusage
  3851. }
  3852.  
  3853. f_commandusage() {
  3854.     # A quick print of valid commands.
  3855.     #
  3856.     echoerr ""
  3857.     echoerr "Single-argument commands:"
  3858.     for EACH in $IN_PARAMS1 ; do
  3859.         echoerr "  $MYNAME $EACH"
  3860.     done
  3861.     echoerr ""
  3862.     echoerr "Server-specific commands:"
  3863.     for EACH in $IN_PARAMS2 ; do
  3864.         echoerr "  $MYNAME $EACH MyTarget"
  3865.     done
  3866.     if [[ ! "$BEENHERE_HELP" == 1 ]] ; then
  3867.         echoerr ""
  3868.         echoerr "Try \"$MYNAME help\" for more information."
  3869.     fi
  3870.     echoerr ""
  3871. }
  3872.  
  3873. #--
  3874.  
  3875. # If we are running an interactive shell (not cron), then save the terminal settings in case we trap and need to restore them.
  3876. # This is needed due to a bug in bash prior to 2015.
  3877. if (tty &> /dev/null) ; then
  3878.     # echo "DEBUG: Saved stty line settings."
  3879.     TTY_LINE_SETTINGS="$(stty -g 2> /dev/null)"
  3880. fi
  3881.  
  3882. trap 'f_cleanuptrap ; exit 90' SIGHUP SIGQUIT SIGABRT SIGTERM
  3883. trap 'f_cleanuptrap ; trap - SIGINT ; kill -s SIGINT $$' SIGINT
  3884. # trap f_cleanuptrap SIGHUP SIGQUIT SIGABRT SIGTERM SIGINT
  3885.  
  3886. IN_ARG="$1"
  3887. IN_INSTALLID="$2"
  3888. IN_PARAMS="$@"
  3889. IN_PARAMS_NUM="$#"
  3890.  
  3891. # A list of our single-argument and dual-argument commands.
  3892. # Note that there are also number of hidden input arguments, such as "run" and "gametype", plus aliases.
  3893. #  IN_PARAMS1 = single argument commands. No $2 possible.
  3894. #  IN_PARAMS2 = dual argument commands, $2 is required.
  3895. IN_PARAMS1="install uninstall list listplayers autocleanup autoupdate bootstart stopall setup"
  3896. IN_PARAMS2="start stop stopnow restart restartnow console status showconfig reconfig rename relink delink lockdown-master unlock-master update updatenow update-validate update-validatenow gametype"
  3897.  
  3898. # If "quakestat" is insatlled instead of qstat, use quakestat instead. quakestat comes from the debian qstat package.
  3899. if ! (type qstat &> /dev/null) && (type quakestat &> /dev/null) ; then
  3900.     # echoerr "DEBUG: Using quakestat in place of qstat."
  3901.     QSTAT_CMD=quakestat
  3902. else
  3903.     QSTAT_CMD=qstat
  3904. fi
  3905.  
  3906.  
  3907.  
  3908. # For anything other than initial setup or help, do some sanity checking.
  3909. if [[ ! "$IN_ARG" =~ "setup"|"help" ]] ; then
  3910.     # Require APPDIR to be present.
  3911.     if [[ ! -d "$APPDIR" ]] ; then
  3912.         echoerr ""
  3913.         echoerr "ERROR: APPDIR \"$APPDIR\" not found!"
  3914.         echoerr "  If you want to perform setup for the first time, use the \"setup\" argument."
  3915.         echoerr "  Be sure you have read the ${MYNAME}_README.txt file first!"
  3916.         echoerr ""
  3917.         exit 1
  3918.     fi
  3919.     # Require the DB file to be present.
  3920.     if [[ ! -f "$SQLDBFILE" ]] ; then
  3921.         echoerr ""
  3922.         echoerr "ERROR: SQLDBFILE, \"$SQLDBFILE\" not found!"
  3923.         echoerr "  If you want to perform setup for the first time, use the \"setup\" argument."
  3924.         echoerr "  Be sure you have read the ${MYNAME}_README.txt file first!"
  3925.         echoerr ""
  3926.         exit 1
  3927.     fi
  3928.     # If the steam and steamcmd.sh binaries are not found, we probably can't do installs or updates.
  3929.     if [[ ! -x "$STEAMCMD_BIN" ]] ; then
  3930.         echoerr ""
  3931.         echoerr "WARNING: Unable to find the SteamCMD installation."
  3932.         STEAMCMD_MISSING=1
  3933.     fi
  3934.     # Check for qstat; needed for wrench run, status, and listplayers at least.
  3935.     if ! (type -p "$QSTAT_CMD" &> /dev/null) ; then
  3936.         echoerr ""
  3937.         echoerr "WARNING: qstat/quakestat is missing."
  3938.         QSTAT_MISSING=1
  3939.     fi
  3940. fi
  3941.  
  3942. # This script is not multi-user safe because of eval usage. Test for go+w on SQLDBFILE.
  3943. EVAL_INSECURE=1 # Disable this test by setting this to 0.
  3944. if [[ "$EVAL_INSECURE" == 1 ]] && [[ -a "$SQLDBFILE" ]] && ( stat --format %A "$SQLDBFILE" | cut -c 6,9 | egrep "^w$" &> /dev/null ) ; then
  3945.     echoerr ""
  3946.     echoerr "ERROR: The SQLDBFILE is group and/or world writable. This is insecure."
  3947.     echoerr "  $MYNAME uses eval in ways which are a security risk if the DB file is writable by other users."
  3948.     echoerr "  Remove the offending permissions, or set EVAL_INSECURE=0 in $MYNAME."
  3949.     echoerr "  SQLDBFILE: $SQLDBFILE"
  3950.     echoerr ""
  3951.     exit 1
  3952. fi
  3953.  
  3954. # tmux related prereq work
  3955. if (type tmux &> /dev/null) ; then
  3956.     # Get the tmux version.
  3957.     TMUX_VER=$(tmux -V | egrep -o "[0-9]+.*[0-9]+" 2> /dev/null)
  3958.     # tmux capture-pane supports the -J argument from version 1.8 and on. This is very helpful. Use it if possible.
  3959.     TMUX_VER_GT18_REGEX="^1\.[8-9]$|^1\.[1-9][0-9]$|^[2-9]+" # We want to match versions > 1.8
  3960.     if [[ "$TMUX_VER" =~ $TMUX_VER_GT18_REGEX ]] ; then
  3961.         TMUX_CAPTURE_OPTS="-S -32768 -J"
  3962.     else
  3963.         TMUX_CAPTURE_OPTS="-S -32768"
  3964.     fi
  3965.     # Require tmux version 1.6 or later. This is because of the epoll/libevent bug which caused redirection hangs.
  3966.     TMUX_VER_LT16_REGEX="^0\.[0-9]+|^1\.[0-5]$" # We want to match versions < 1.6
  3967.     if [[ "$TMUX_VER" =~ $TMUX_VER_LT16_REGEX ]] ; then
  3968.         echoerr ""
  3969.         echoerr "$MYNAME requires tmux version 1.6 or later."
  3970.         echoerr ""
  3971.         exit 1
  3972.     fi
  3973. fi
  3974.  
  3975. f_validate_1arg() {
  3976.     # If this is a single-argument command trying to use more arguments when it should not, print a specific error.
  3977.     if [[ "$IN_PARAMS_NUM" -gt 1 ]] ; then
  3978.         echoerr ""
  3979.         echoerr "ERROR: Action argument \"$IN_ARG\" does not accept any sub-arguments."
  3980.         echoerr ""
  3981.         exit 1
  3982.     fi
  3983. }
  3984.  
  3985. f_validate_installid() {
  3986.     # Validate arguments where not more or less than one INSTALLID target is requried.
  3987.     if [[ "$IN_PARAMS_NUM" -gt 2 ]] ; then
  3988.         echoerr ""
  3989.         echoerr "Too many arguments! Only a single argument to \"$IN_ARG\" is accepted."
  3990.         echoerr ""
  3991.         exit 1
  3992.     fi
  3993.     if [[ "$IN_PARAMS_NUM" -lt 2 ]] ; then
  3994.         echoerr ""
  3995.         echoerr "ERROR: This command requires a second argument, which should be an installation ID."
  3996.         echoerr "  What installation do you want to \"$IN_ARG\"?"
  3997.         echoerr ""
  3998.         exit 1
  3999.     fi
  4000.     # If IN_INSTALLID has any trailing slashes, due to shell autocompletion, remove it
  4001.     if (echo "$IN_INSTALLID" | egrep "+*/$" &> /dev/null) ; then
  4002.         # echo "DEBUG: Trimming trailing slash on input."
  4003.         IN_INSTALLID=$(echo "$IN_INSTALLID" | tr -d '/')
  4004.     fi
  4005.     # Test the DB to see if there is a valid installation by that name.
  4006.     if ! [[ "$($SQLCMD "select INSTALLID from inst where INSTALLID='$IN_INSTALLID';")" == "$IN_INSTALLID" ]] ; then
  4007.         echoerr ""
  4008.         echoerr "ERROR: Installation ID \"$IN_INSTALLID\" not found in the database."
  4009.         echoerr ""
  4010.         exit 1
  4011.     fi
  4012.     # If we are doing something to an install, there should be a directory there.
  4013.     if [[ ! -d "$APPDIR/$IN_INSTALLID" ]] ; then
  4014.         echoerr ""
  4015.         echoerr "ERROR: No directory for installation \"$IN_INSTALLID\" found!"
  4016.         echoerr ""
  4017.         exit 1
  4018.     fi
  4019. }
  4020.  
  4021. # --
  4022.  
  4023. case "$IN_ARG" in
  4024.     'start')
  4025.         f_validate_installid
  4026.         f_start
  4027.     ;;
  4028.     'bootstart'|'startall')
  4029.         f_validate_1arg
  4030.         f_bootstart
  4031.     ;;
  4032.     'stop')
  4033.         f_validate_installid
  4034.         f_stop
  4035.     ;;
  4036.     'stopall')
  4037.         f_validate_1arg
  4038.         f_stopall
  4039.     ;;
  4040.     'stopnow')
  4041.         f_validate_installid
  4042.         STOPNOW=yes
  4043.         f_stop
  4044.     ;;
  4045.     'restart')
  4046.         f_validate_installid
  4047.         f_stop
  4048.         sleep 1
  4049.         f_start
  4050.     ;;
  4051.     'restartnow')
  4052.         f_validate_installid
  4053.         STOPNOW=yes
  4054.         f_stop
  4055.         sleep 1
  4056.         f_start
  4057.     ;;
  4058.     'listplayers')
  4059.         f_validate_1arg
  4060.         f_listplayers
  4061.     ;;
  4062.     'list')
  4063.         f_validate_1arg
  4064.         f_listinstalls
  4065.     ;;
  4066.     'status')
  4067.         f_validate_installid
  4068.         f_status
  4069.     ;;
  4070.     'autoupdate')
  4071.         f_validate_1arg
  4072.         f_autoupdate
  4073.     ;;
  4074.     'update')
  4075.         f_validate_installid
  4076.         f_update
  4077.     ;;
  4078.     'updatenow')
  4079.         f_validate_installid
  4080.         STOPNOW=yes
  4081.         f_update
  4082.     ;;
  4083.     'update-validate'|'update-verify-all')
  4084.         f_validate_installid
  4085.         VERIFYUPDATE=1
  4086.         f_update
  4087.     ;;
  4088.     'update-validatenow'|'update-verify-all-now'|'update-validate-now')
  4089.         f_validate_installid
  4090.         VERIFYUPDATE=1
  4091.         STOPNOW=yes
  4092.         f_update
  4093.     ;;
  4094.     'relink')
  4095.         f_validate_installid
  4096.         f_relink
  4097.     ;;
  4098.     'delink')
  4099.         f_validate_installid
  4100.         f_delink
  4101.     ;;
  4102.     'console')
  4103.         f_validate_installid
  4104.         f_tmuxattach
  4105.     ;;
  4106.     'install')
  4107.         f_validate_1arg
  4108.         f_install
  4109.     ;;
  4110.     'uninstall')
  4111.         f_validate_1arg
  4112.         f_uninstall
  4113.     ;;
  4114.     'showconfig')
  4115.         f_validate_installid
  4116.         f_showconfig
  4117.     ;;
  4118.     'reconfig')
  4119.         f_validate_installid
  4120.         f_reconfig
  4121.     ;;
  4122.     'rename')
  4123.         f_validate_installid
  4124.         f_rename
  4125.     ;;
  4126.     'lockdown-master'|'lock-master')
  4127.         f_validate_installid
  4128.         f_lockdownmaster
  4129.     ;;
  4130.     'unlock-master'|'unlockdown-master')
  4131.         f_validate_installid
  4132.         f_unlockmaster
  4133.     ;;
  4134.     'autocleanup'|'autoclean')
  4135.         f_validate_1arg
  4136.         f_autocleanup
  4137.     ;;
  4138.     'gametype')
  4139.         GAMETYPE_ARG="$2"
  4140.         case "$GAMETYPE_ARG" in
  4141.             'add'|'new'|'install'|'inst')
  4142.                 GAMETYPE_ARG="add"
  4143.                 f_changegametype
  4144.             ;;
  4145.             'delete'|'del'|'rm')
  4146.                 GAMETYPE_ARG="delete"
  4147.                 f_deletegametype
  4148.             ;;
  4149.             'change'|'modify'|'alter'|'reconfig')
  4150.                 GAMETYPE_ARG="change"
  4151.                 f_changegametype
  4152.             ;;
  4153.             *)
  4154.                 echoerr ""
  4155.                 echoerr "Invalid sub-argument to gametype."
  4156.                 echoerr ""
  4157.                 exit 1
  4158.             ;;
  4159.         esac
  4160.     ;;
  4161.     'setup')
  4162.         f_validate_1arg
  4163.         f_setup
  4164.     ;;
  4165.     'run')
  4166.         # Note that "run" is special and may have many or no arguments.
  4167.         f_wrenchrun "$@"
  4168.     ;;
  4169.     '--help'|'-help'|'help'|'-h')
  4170.         f_help
  4171.     ;;
  4172.     *)
  4173.         echoerr ""
  4174.         echoerr "Invalid argument."
  4175.         f_commandusage
  4176.         exit 1
  4177. esac
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement