#!/bin/ksh -p
#
# ident "@(#)utdtsession.sh	1.81 03/04/23 SMI"
#
# Copyright 1999-2003 Sun Microsystems, Inc.  All rights reserved.
#
# Script to add an X server to the Xservers file and set it up via the
# Xconfig file to service the session ID given as argument 1
#
# The database of available display numbers is taken as 
# Xservers. If a display is mentioned as
#
#	sysname:num OR :num
#
# at the beginning of a line or the beginning of a comment, it is considered
# unavailable for use.
#
# E.g. a line '# :3 RESERVED' will prevent display 3 from being assigned
#

#exec 2>/var/tmp/utdtsession.$$       # Debug
#set -x


ME=$0

verbose=0
umask 0022

# XXX Running pkginfo can be a performance hit. Should fix th value of
# XXX BASE at install time.
BASE=`pkginfo -r SUNWuto 2>/dev/null`
SUNWUT=$BASE/SUNWut
SUNWUTBIN=$SUNWUT/bin
SUNWUTLIB=$SUNWUT/lib

# DM notification script
UTDMSESSION="$SUNWUTLIB/utdmsession"

libdir=$SUNWUTLIB
# Type can be "normal" which means dtlogin, or "special"
type="normal"
# Session type is one of the session types in the sessionTypes.props file
SESSION_TYPE="default"

XSUN="$SUNWUTLIB/utxsun"
XSUNFLAGS="-nobanner -terminate"
XINIT="$SUNWUTLIB/utxinit"

INET_SERVICES=/etc/inet/services 

#
# Set default values
#
INFODIR=/var/opt/SUNWut
ETCDIR=/etc/opt/SUNWut
TMPDIR=/tmp/SUNWut

RESCONFIG="$SUNWUTLIB/utresexec"
DEVCONFIG="$INFODIR/altdispconfig"

set -u	# All vars must be set from now on

#
# Set defaults
#
MINDISP=2		# minimum display number XXX 
MAXDISP=1000		# maximum display number XXX -> must match OWconfig
dir=/tmp/SUNWut/config/xconfig # X-Windows configuration directory
xconfiglock=$dir/config.lock	# X-Windows config lock file
sid=""			# required parameter
tid=""			# required parameter
rtid=""
termid=""
modelid=""
res=""
pdata=""
callbackCookie=""

function usage {
print "
Usage: $ME [-d dir] [-f lockfile] [-h max] [-l min]	\\
	[-p path] [-x dir] [-t token] [ -r rawtoken ] [-c sessionType] {add|delete}
Parameters:
 -c sessionType		# script must exist: /etc/opt/SUNWut/<stype>.start
 -d infoDir		# base directory for mapping databases (tokens, displays)
 -f lockfileName	# name of lock file for X-Windows config files
 -h maxDisplay		# highest display number (default=$MAXDISP)
 -l minDisplay		# lowest display number (default=$MINDISP)
 -n displaynum		# number of display
 -p path		# debugging option to specify path to support progs
 -r rawtoken		# raw token name before auth processing
 -t tokenId		# token name
 -v			# verbose
 -x dtDirectory		# X-Windows configuration directory (default=$dir)

 add			# add an X display for this token
 delete			# remove the X display for this token
 "
exit 1
}


# Function to replace the session variables
function replaceVariables {

        typeset dispfile=$1

        #
        # Read each line of the file, if it does not
	# start with one of the variables to be replaced,
	# write out as is, otherwise replace value to
	# new value
        # Continue till end of file.

        TMPR=$DISPDIR/.tmp.$$

        sed -n  '
                {
                s/^CALLBACK_COOKIE=[0-9]*/CALLBACK_COOKIE='$callbackCookie'/
		s/^INSERT_TOKEN=.*$/INSERT_TOKEN='$rtid'/
		s/^TOKEN_SET=.*$/TOKEN_SET='$tokenset'/
                p
                }' $dispfile | /bin/cat > $TMPR
	if [[ $? -ne 0 ]]
	then
	        return false
	fi

        # Move tmp file over the displays file

        /usr/bin/cp $TMPR $dispfile
	if [[ $? -ne 0 ]]
	then
	        return false
	fi

	/usr/bin/rm -f $TMPR
	return true
}

# Function to create a dispinfo file
# CreateDispinfo filename res pdata termid modelid
function CreateDispinfo {
        typeset resstr=

	umask 0022 
	if [ ! -z "$5" ]
	then
	        resstr="MODEL_ID=$5\n$resstr"
	fi
	if [ ! -z "$4" ]
	then
	        resstr="TERMINAL_ID=$4\n$resstr"
	fi
	if [ ! -z "$3" ]
	then
	        resstr="GROUP_PRIVATE_DATA=$3\n$resstr"
	fi
	if [ ! -z "$2" ]
	then
	        resstr="CURRENT_RESOLUTION=$2\n$resstr"
	fi
	print -n "$resstr" | /bin/cat > $1
# it's non-catastrophic to fail to create this file, we'll just pan/scan
}

# Function to delete a session
function deleteSession {
	typeset xserv=$1	# dtlogin's Xservers file
	typeset xconf=$2	# dtlogin's Xconfig file
	typeset token=$3	# token name
	typeset tokenfile=$4	# token file containing session ID
	typeset lockfile=$5	# X config lock file

	#
	# Get the current X display and tell the DM about the
	# session on this display going away. Do this before
	# we remove the display file.
	#
	dpy=$($libdir/utdtutil -l $lockfile -g "_TOKEN=$token\>" \
		$xserv $xconf)

	# Tell the DM about this session going away.
	$UTDMSESSION -d $dpy -z utdtsession

	#
	# Remove the display file on success -> this must be atomic
	# because the display number may be reassigned immediately
	#
	# We can remove the token file later because we hold the token
	# lock throughout
	#

	typeset rmdisplay="$SUNWUTLIB/utrmdpy "'$UT_DPY'

	$libdir/utdtutil -l $lockfile -D "_TOKEN=$token\>" -r "$rmdisplay" \
		$xserv $xconf >/dev/null

	/bin/rm -f $tokenfile
}

# Function to establish user connection authentication for an X display
function xauthDisplay {
        typeset dpy=$1
        typeset cookie_dir="/tmp/SUNWut/config/xauth"
        typeset cookie_path="$cookie_dir/cookie:$dpy"
        typeset xmkcookie="/usr/openwin/lib/mkcookie"

	# If a cookie file already exists then set back its timestamp
	# so that we can detect whether mkcookie manages to update
	# it.  We could just remove the old file but if mkcookie
	# fails we'll be better off continuing with the old file
	# than running wih no cookie at all.
	#
	/bin/touch -c -t 200110301725 $cookie_path

        if [ -x "$xmkcookie" ]
        then
                [ ! -d "$cookie_dir" ] && /bin/mkdir -p $cookie_dir
                $xmkcookie $cookie_path -auth magic-cookie 2>&1 > /dev/null
        fi

        # If we have a cookie file, even if it's an old one, enable X
	# server authentication
	#
        if [ -r $cookie_path ]
        then
		# If the cookie file didn't get rewritten (its mtime
		# has not been modified since we touch'ed it back to
		# Sebastian's birthday) then complain.
		#
		if [ X`/bin/find $cookie_path -mtime +1` != X ]
		then
			print -u2 "$ME: using stale cookie for display :$dpy"
		fi

                # Make the cookie and database the same file
                export XAUTHORITY=$cookie_path
                XSUNFLAGS="$XSUNFLAGS -auth $cookie_path"
	else
		# We have no cookie file at all.  Complain but continue
		# anyway, with no Xauthority and no cookie.
		#
		print -u2 "$ME: no cookie at all for display :$dpy"
        fi
}

#
# Parse and validate command line options
#
if (( $# == 0 ))
then
	usage
fi

while getopts :c:d:f:h:l:n:p:r:t:vx: name
do
	case $name in
	(c)	SESSION_TYPE="$OPTARG";
		type=special;
		;;
	(d)	INFODIR="$OPTARG";;
	(f)	xconfiglock="$OPTARG";;
	(h)	MAXDISP="$OPTARG";;
	(l)	MINDISP="$OPTARG";;
	(n)	dpyparm="$OPTARG";;
	(p)	libdir="$OPTARG";;
	(r)	rtid="$OPTARG";;
	(t)	tid="$OPTARG";;
	(v)	verbose=$(($verbose + 1))
		exec 1>&- 2>&-
		exec 1>/tmp/utdt.$$ 2>&1
		;;
	(x)	dir="$OPTARG";;
	(?)	print -u2 Invalid option "'$OPTARG'";
		usage;;
	(:)	print -u2 Required option "'$OPTARG'" is missing;
		usage;;
	esac
done
shift $(expr $OPTIND - 1)

case $1 in
(add)
	action=add
	shift
	;;
(delete)
	action=delete
	sid=dont_care
	shift
	;;
(*)
	print -u2 "Invalid or missing action; {add|delete} required"
	usage
	;;
esac

# It is possible to pass arguments to type=special sessions run by XINIT
if [[ "${1:-}" == "--" ]]
then
	shift
	options="${@:-}"
else
	options=""
fi
# command line argument parsing is complete


# The following vars can be affected by command line arguments
libfile=$libdir/xdisplayutil
Xservers=$dir/Xservers
Xconfig=$dir/Xconfig
DBDIR=$INFODIR/tokens
DISPDIR=$INFODIR/displays
INSERTDIR=$INFODIR/itokens
CTOKENDIR=$INFODIR/ctokens
RESDIR=$INFODIR/dispinfo


# Get common support functions 
. $libfile


#
# Read parameters from stdin if they have not been fully specified already
#
# Passing parameters through the environment exposes them to world read.
# Do not export sid.
#
if [[ -z "$sid" || ( -z "$tid" && -z "$dpyparm" ) ]]
then
	while read expression
	do
		case $expression in
		("#"*)		# allow comment
			;;
		(CORONA_SESSION=*)
			if [ -z "$sid" ]
			then
				sid=${expression#CORONA_SESSION=};
			fi
			;;
		(TOKEN_IDENTITY=*)
			if [ -z "$tid" ]
			then
				tid=${expression#TOKEN_IDENTITY=};
			fi
			;;
		(INSERT_TOKEN=*)
			if [ -z "$rtid" ]
			then
				rtid=${expression#INSERT_TOKEN=};
			fi
			;;
		(CURRENT_RESOLUTION=*)
			res=${expression#CURRENT_RESOLUTION=};
			;;
		(GROUP_PRIVATE_DATA=*)
			pdata=${expression#GROUP_PRIVATE_DATA=};
			;;
		(TERMINAL_ID=*)
			termid=${expression#TERMINAL_ID=};
			if [ ! -z "$termid" ]
			then
                		export TERMINAL_ID=$termid
			fi
			;;
		(MODEL_ID=*)
			modelid=${expression#MODEL_ID=};
			if [ ! -z "$modelid" ]
			then
                		export MODEL_ID=$modelid
			fi
			;;
		(TOKEN_SET=*)
			tokenset=${expression#TOKEN_SET=};
			;;
                (CALLBACK_COOKIE=*)
                        callbackCookie=${expression#CALLBACK_COOKIE=};
                        ;;
		(*)	break	# quit on garbage or blank line
			;;
		esac
	done
fi

# Validate parameters
error=false
if [ -z "$sid" ]
then
	print -u2 'Missing session specification (-s option)'
	error=true
fi

# If we know the display (only possible for a delete), derive the token
if [[ -z "$tid" && -n "$dpyparm" ]]
then
	tid=$(nawk -F[=] '$1 == "TOKEN" {print $2}' ${DISPDIR}/${dpyparm})
	if [[ -z "$tid" ]]
	then
		# If we can't get the token from the display file, try
		# to get it from Xconfig
		tid=$(sed -n "s/^Dtlogin\.\*_${dpyparm}\.environment:.*\<SUN_SUNRAY_TOKEN=\([^ 	]*\).*/\1/p" $Xconfig)
	fi
	if [[ -z "$tid" ]]
	then
	    	print -u2 "Can't delete session for display ${dpyparm}: can't determine token."
		exit 2
	fi
fi

if [ -z "$tid" ]
then
	print -u2 'Missing token specification'
	error=true
fi
if [ "$error" = "true" ]
then
	usage
fi

#
# Check for existing session
#

tif=$DBDIR/$(print $tid | tr '.' '/')
tiflock=$tif.lock

if [[ $verbose -gt 1 ]]; then print tif=$tif; fi
umask 0077
mkdir -p $(dirname $tif)/.
mkdir -p $(dirname $tiflock)/.
umask 0022
mkdir -p $DISPDIR/.
umask 0022
mkdir -p $RESDIR/.
umask 0022

startlocks
trap endlocks 0 HUP INT TERM	# must be outside lock()

lock $tiflock $libdir/lockfile

if [ "$action" = "delete" ]
then
	deleteSession $Xservers $Xconfig $tid $tif $xconfiglock
	pkill -HUP -P 1 -u 0 dtlogin

	exit 0
fi

if [ -f $tif ]
then
	# A session is already configured.
	# If the session is compatible, then let it stand, else remove it
	# so that a new session can be created.

	# get the existing session ID
	oldsid=$(/usr/bin/head -1 $tif)

	# get the existing session type
	OLD_SESSION_TYPE=""
	dpy=$($libdir/utdtutil -l $xconfiglock -g "_TOKEN=$tid\>" \
		$Xservers $Xconfig)
	if [[ -n "$dpy" && -f $DISPDIR/$dpy ]]
	then
		# replaceVariables updates the displays file and modifies
		# the time on the display file for use by the group manager
		# to decide which server owned this token last.
	        if ! replaceVariables $DISPDIR/$dpy
		then
			print -u2 "Error: couldn't replace display file"
			exit 2
		fi

		if [ -f $TMPDIR/session_proc/$dpy ]
		then
			/bin/touch $TMPDIR/session_proc/$dpy
		fi

		OLD_SESSION_TYPE=$(grep '^SESSION_TYPE=' $DISPDIR/$dpy)
		OLD_SESSION_TYPE=${OLD_SESSION_TYPE#SESSION_TYPE=}
	fi
		
	if [[ "$sid" != "$oldsid" || "$SESSION_TYPE" != "$OLD_SESSION_TYPE" ]]
	then
		#
		# The token is already in use with session $oldsid.
		# delete the old dtsession and create a new one.
		#
		deleteSession $Xservers $Xconfig $tid $tif $xconfiglock
	else
		#
		# If utresexec exists then give it the opportunity to
		# modify the DTU's monitor timing and/or dimensions.  If
		# utresexec does not exist then for backwards compatibility
		# give altdispconfig a shot at the same task.
		#
		newres=""
		if [ -x "$RESCONFIG" ]
		then
			#
			# Use the 'print' builtin to send the SID through
			# stdin, it must never be exposed on a command line
			#
			newres=` print $sid | \
				"$RESCONFIG" -c "$TERMINAL_ID" -t "$tid" `
		elif [ -x "$DEVCONFIG" ]
		then
			newres=`$DEVCONFIG $DISPDIR/$dpy`
		fi
		if [ -n "$newres" ]
		then
			res="$newres"
		fi	

		# Atomic update of current resolution if it is available
		#
		# NB: we remove the file if the string goes away.
		# (Instead, could leave it as the last-known value)

		if [ ! -z "$res" ]  || [ ! -z "$pdata" ]
		then
		    CreateDispinfo "$RESDIR/$dpy" "$res" "$pdata" "$termid" "$modelid"
		else
		    rm -f $RESDIR/$dpy 
		fi

		# XXX It should be that since the configuration did not
		# change, we should not need to bother dtlogin.  However,
		# during testing, there was at least one case where the
		# kill was needed.  So, the kill remains.

		pkill -HUP -P 1 -u 0 dtlogin
		exit 0
	fi
fi

#
# Add a session
#

xcdesc="Dtlogin.*_%d.environment: SUN_SUNRAY_TOKEN=$tid CORONA_TOKEN=$tid"
if [ "$type" = normal ]
then
	xsdesc=":%d SunRay local@none $SUNWUTLIB/utxsun :%d -nobanner"
else
	xsdesc="# :%d RESERVED"
fi

# save mapping of token to session
# into a temporary "displays" file and move into place on success
umask 0177 
TMPD=$DISPDIR/.tmp.$$
TMPR=$RESDIR/.tmp.$$

print "SESSION=$sid\nTOKEN=$tid\nSESSION_TYPE=$SESSION_TYPE\nTOKEN_SET=$tokenset\nCALLBACK_COOKIE=$callbackCookie" | /bin/cat > $TMPD
if [[ $? -ne 0 ]]
then
    print -u2 "Error: couldn't create display file"
    exit 2
fi

#
# If utresexec exists then give it the opportunity to
# modify the DTU's monitor timing and/or dimensions.  If
# utresexec does not exist then for backwards compatibility
# give altdispconfig a shot at the same task.
#
newres=""
if [ -x "$RESCONFIG" ]
then
	#
	# Wipe any stale resolution cache files for this token
	#
	"$RESCONFIG" -k -t "$tid"
	#
	# Use the 'print' builtin to send the SID through
	# stdin, it must never be exposed on a command line
	#
	newres=` print $sid | "$RESCONFIG" -c "$TERMINAL_ID" -t "$tid" `
elif [ -x "$DEVCONFIG" ]
then
	newres=`$DEVCONFIG $DISPDIR/$dpy`
fi
if [ -n "$newres" ]
then
	res="$newres"
fi	

#
# Build the command to be run after display number is allocated but
# before updating the X configuration files
#
typeset mvdisplay='/bin/echo DISPLAY=$UT_DPY | /bin/cat >> '"$TMPD"

#
# The following command isn't required because appending to a file
# which is less than one block can't fail.  In order to make this work
# utdtutil would have to change to detect failure of the precmd, and
# abort the rest of the operation.
#
# typeset mvdisplay='$mvdisplay; if [ $? != 0 ]; then
#				 echo "Display file creation failed" 2>&1;
#				 exit 2;
#				 fi'

typeset mvdisplay="$mvdisplay; /bin/mv $TMPD $DISPDIR/"'$UT_DPY'

if [ ! -z "$res" ] || [ ! -z "$pdata" ]
    then
    CreateDispinfo "$TMPR" "$res" "$pdata" "$termid" "$modelid"
    typeset mvdisplay="$mvdisplay; /bin/mv $TMPR $RESDIR/"'$UT_DPY'
fi

#
# Allow everyone to read Xsessions and Xconfig file
#
umask 0022

dpy=$($libdir/utdtutil -l $xconfiglock -s "$xsdesc" -c "$xcdesc" \
	-i "$INET_SERVICES" -F $MINDISP -L $MAXDISP \
	-R "$mvdisplay" $Xservers $Xconfig)

if [ $? -ne 0 -o -z "$dpy" -o "$dpy" -lt 0 ]
then
	print -u2 "Error: No display numbers available"
	# no need to bother dtlogin, no change could be made
	/bin/rm -f $TMPD $TMPR
	exit 2
fi

# save mapping of token to session
umask 0177 
print $sid | /bin/cat > $tif
if [[ $? -ne 0 ]]
then
        print -u2 "Error: Can't create token file"
	rm -f $TMPD $TMPR
	rm -f $tif
	exit 2
fi

# Link $CTOKENDIR/$tid to $DISPDIR/$dpy.
/bin/rm -f $CTOKENDIR/$tid
/bin/ln $DISPDIR/$dpy $CTOKENDIR/$tid

# Link $INSERTDIR/$rtid to $DISPDIR/$dpy if $rtid is defined.
if [ -n "$rtid" ]
then
	print "INSERT_TOKEN=$rtid" | /bin/cat >> $DISPDIR/$dpy
# This should't be able to fail, since dpy file is already created and
# less than one block long append should succeed.  Messy to write
# cleanup code here and unnecessary.

	/bin/rm -f $INSERTDIR/$rtid
	/bin/ln $DISPDIR/$dpy $INSERTDIR/$rtid
fi

WEBGUI_GROUP=$(${SUNWUTLIB}/utadmingid)
/bin/chgrp $WEBGUI_GROUP $DISPDIR/$dpy
/bin/chmod g+r $DISPDIR/$dpy
umask 0022

# 4852961 Export the raw token as an environment variable so
#         that start scripts can get access to it. Since the
#         command line argument list to the start scripts is
#         not well defined, we can't arbitrarily add a new
#         command line option to pass to the start scripts
#         since this may conflict with other command line
#         options being passed in to this script.
if [ -n "$rtid" ]
then
    export RAW_TOKEN_ID="$rtid"
fi

# Tell the DM and smartcard subsystem about this session.
$UTDMSESSION -c $dpy -z utdtsession

if [ "$type" != normal ]
then
	#
	# Fix our priority back to normal
	# (raised in /etc/init.d/utsvc).
	#
	/bin/priocntl -s -c TS -p 0 $$

	if /usr/xpg4/bin/grep -q '^## utdtsession no-xinit' \
	    $ETCDIR/$SESSION_TYPE.start
	then
		($ETCDIR/$SESSION_TYPE.start $tid $options) &
	else
		xauthDisplay $dpy
		($XINIT $ETCDIR/$SESSION_TYPE.start $tid $options --\
		    $XSUN ":$dpy" $XSUNFLAGS) &
	fi
	# We do not keep track of child.
fi

pkill -HUP -P 1 -u 0 dtlogin

exit 0
