#!/bin/sh
# Copyright 2000 Citrix Systems, Inc.  All Rights Reserved.
#
# This script will shutdown (by fair means or foul) the components of
# PRODNAME running on a single host.
#

#if DBG
#
# Perforce Id: $Id$
#
# This script is pre-processed using ctxpp, which strips out
# lines as appropriate (including these ones.)
#
# It performs the following functions:
#
# 1.  Locks the server to prevent the server allowing further logins.
# 2.  Finds all active sessions and informs them of the impending shutdown.
# 3.  Pauses for the message timeout.
# 4.  Performs a friendly logoff for all remaining sessions (incl.
#     disconnected ones)
# 5.  Pauses for the logoff timeout.
# 6.  Resets all remaining sessions - forced logoff.
# 7.  Shuts down the Frame Manager.
# 8.  Pauses to allow the license information to be transferred.
# 9.  Shuts down the ibrowser.
# 10. Releases nologin lock. (not necessary)
#
# Certain assumptions have to be made about the access requirement for the user
# executing this script.
#
# 1 9 - requires ... access
# 3 5 - requires no special access (normal user)
# 2   - requires messaging access (ctxadm or normal user with ctxsecurity)
# 4   - requires logoff access    ( ----------------- " ---------------- )
# 6   - requires (current) ctxshutdown access (???)
# 7   - ------------------- " ---------------------
# 8   - requires unix-level kill access to kill ibrowser (ctxsrvr or root)
#
#
# If this is run from within a session, we need to be able to cope
# with the hosting xterm getting killed and sending us a sighup
#
#endif

#################################  CONSTANTS  ################################

EXECUTABLE=`basename $0`

#include "@MASTER@/include/scripts/Ctx_set_paths.sh"
#include "@MASTER@/include/scripts/Ctx_getstr.sh"

CTXFM=ctxhdx
CTXBR=ctxibrow
#CTXBRLOG=ibrowser.log

#
# Defaults
#
MESSAGE_TIMEOUT=60
LOGOFF_TIMEOUT=30
BROWSER_TIMEOUT=15
QUIET=0

ADMIN_USER=ctxsrvr
ADMIN_GROUP=ctxadm

#################################  FUNCTIONS  ################################

#
# Usage message
#
usage() {
    $ECHO "Usage: $EXECUTABLE [-q] [-m secs] [-l secs] [message]"
    $ECHO "where"
    $ECHO "  -q      = operate in quiet mode"
    $ECHO "  -m secs = the grace period in seconds for messaging the user [60]"
    $ECHO "  -l secs = the grace period in seconds for interactive logoff [30]"
    $ECHO "  message = the message displayed to the user"
    $ECHO
    exit 1
}

#
# Tidy up routine to ensure the release of resources
#
tidy() {
    msg "User interruption.  Releasing resources and exiting."
    if [ is_fm_running ]; then
	msg "Frame Manager still running."
	reenable_login
    else
	msg "Frame Manager already killed cannot re-enable logins."
    fi 
    exit 2
}

#
# Informational message to the admin
#
msg() {
    if [ "$QUIET" = "0" ]; then
	$ECHO "$EXECUTABLE: $1"
    fi
}

#
# Warning message to the admin
#
warn() {
    $ECHO "$EXECUTABLE warning: $1"
}

#
# Fatal error message to the admin
#
error() {
    $ECHO "$EXECUTABLE error: $1"
    exit 1
}



#if DBG
# Big complications in here : we are trying to parse the output
# of ctxqsession. Output is lines of the form
#   server:id   <username>     state type 
# where the username column may be blank
# And during shutdown, the fm currently chooses to leave
# sessions in the 'down' state, rather than deleting them
# (to get round a problem with locks)
# So breaking at spaces is complicated - we use presence
# of a 6th column to decide how the other columns were
# laid out. The G is there to pick up any junk in case more
# columns get added later
# But we ought to add a (secret ?) option to qsession to generate
# output in an easy-to-parse (and guaranteed) format.
#endif


#
# Find the session IDs for all sessions
#
get_all_ids() {
    all_ids=`$CTXQSESSION | (
		IFS=" "
		read A
		read A
		while read A B C D E; do
		    id="$A"
		    state="$C"
		    echo "$id \c"
		done
		)`
    all_ids_count=`$ECHO "$all_ids" | $WC -w`
}

#
# Find the session IDs for all active sessions
#
get_active_ids() {
    active_ids=`$CTXQSESSION | (
		IFS=" "
		read A
		read A
		while read A B C D E; do
		    id="$A"
		    state="$C"
		    if [ "$state" = "active" ]; then
			$ECHO "$id \c"
		    fi
		done
		)`
    active_ids_count=`$ECHO "$active_ids" | $WC -w`
}

is_fm_running() {
   pid=`$PS -ef | $GREP -v grep | $GREP $CTXFM | $CUT -c9-14`
   if [ "$pid" = "" ]; then
	return 0;
   else
	return 1;
   fi
}

#
# Disable further logins to the Frame Manager
#
disable_login() {
    $CTXHALT -N
    if [ "$?" = "0" ]; then
	msg "Locked server against further logins."
    else
	warn "Unable to lock server against further logins. ($?)"
    fi
}

#
# Re-enable logins to the Frame Manager
#
reenable_login() {
    $CTXHALT -L
    if [ "$?" = "0" ]; then
	msg "Logins re-enabled for server."
    else
	warn "Unable to re-enable logins for server."
    fi
}

#################################  MAIN EXEC  ################################


#
# Ensure we trap SIGINT (ctrl-c)
#
trap tidy INT

# If this script is invoked from within a session, it
# will be sent a SIGHUP, which we need to ignore. This does
# mean that closing the xterm will not cancel a shutdown.
# Also, we dont need stdin, and closing it now may save
# complications.

trap '' HUP
exec 0< /dev/null

#
# Read command-line switches
#
while :; do
    case $1 in
	"-?" | "-h" | "-help")  usage;;
	"-q")     QUIET=1; shift;;
 	"-m")     if [ "$2" != "" ]; then
		      MESSAGE_TIMEOUT=$2
		      shift 2
		  else
                      usage
		  fi;;
	"-l")     if [ "$2" != "" ]; then
		      LOGOFF_TIMEOUT=$2
		      shift 2
		  else
		      usage
		  fi;;
	-*)       usage;;
	*)       break;;
    esac
done

# anything left is the message
#

MSG="$*"
if [ "$MSG" = "" ]; then
    MSG="`$GETSTR CTX_SHUTDOWN_DEFAULT_MSG \"$MESSAGE_TIMEOUT\"`"
fi

#
# Okay, let's rock and roll
#
msg "Locking the server from accepting further logins."
disable_login

get_active_ids

msg "Active sessions: $active_ids"

#
# First send a message to all active sessions warning about the auto-logoff.
# 
if [ "$active_ids" != "" ]; then
    msg "Found some active sessions.  Informing them of shutdown."

    for session in $active_ids; do
        session=`echo $session | cut -f 2 -d:`
	$CTXMSG $session "$MSG"
	if [ "$?" = "0" ]; then
	    msg "Told session '$session'"
	else
	    warn "cannot send message to session '$session'"
	fi
    done

    #
    # Now wait for the preset period.
    #
    msg "Waiting for $MESSAGE_TIMEOUT seconds..."
    sleep $MESSAGE_TIMEOUT
    msg " done."
fi

#
# Now auto-logoff the sessions that are still around
#
get_all_ids

if [ "$all_ids" != "" ]; then
    msg "Found some outstanding sessions.  Auto-logging-off $all_ids."

    for session in $all_ids; do
        session=`echo $session | cut -f 2 -d:`
	$CTXLOGOFF $session
	if [ "$?" = "0" ]; then
	    msg "Auto-logoff sent to session '$session'."
	else
	    warn "cannot logoff session '$session'"
	fi
    done

    #
    # Now wait for the preset grace period.
    #
    msg "Waiting for $LOGOFF_TIMEOUT seconds..."
    sleep $LOGOFF_TIMEOUT
    msg " done."
fi

#
# Finally reset any surviving sessions and kill the FM
#
msg "Killing those remaining and killing Citrix Virtual Apps & Desktops server."

# This command will reset all sessions and kill the FM
sudo /sbin/service ctxhdx stop
if [ "$?" = "0" ]; then
    msg "Shut down the Citrix Virtual Apps & Desktops server."
else
    warn "problem shutting down the Citrix Virtual Apps & Desktops server. ($?)"
fi

#
# Do a quick sanity check.
# Not much we can do about anything but inform the admin.
#
get_all_ids

if [ "$all_ids" = "" ]; then
    msg "All sessions successfully shutdown."
else
    msg "Sessions '$all_ids' are still ALIVE!!"
fi

exit
