#!/bin/ksh -p
#
# ident "@(#)utdtsession.sh	1.98 04/12/17 SMI"
#
# Copyright 1999-2004 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#
# 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
unset LD_LIBRARY_PATH
PATH=/bin:/usr/bin
export PATH

# support package relocation by using basedir symlink to point to SUNWuto location
BASE=/etc/opt/SUNWut/basedir
SUNWUT=$BASE
SUNWUTBIN=$SUNWUT/bin
SUNWUTLIB=$SUNWUT/lib
XMGR=/etc/opt/SUNWut/xmgr

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

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

XSUN="$SUNWUTLIB/utxsun"
XFLAGS="-terminate"
XINIT="$SUNWUTLIB/utxinit"
CP=cp
RM=rm
MV=mv
LN=ln

#
# 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
#
dir=/tmp/SUNWut/config/xconfig # X-Windows configuration directory
sid=""			# required parameter
tid=""			# required parameter
rtid=""
termid=""
modelid=""
res=""
pdata=""
callbackCookie=""

function usage {
print "
Usage: $ME [-d dir] \\
	[-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)
 -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 | cat > $TMPR
	if [[ $? -ne 0 ]]
	then
	        return 1
	fi

        # Move tmp file over the displays file

        $CP $TMPR $dispfile
	if [[ $? -ne 0 ]]
	then
	        return 1
	fi

	$RM -f $TMPR
	return 0
}

# 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" | cat > $1
# it's non-catastrophic to fail to create this file, we'll just pan/scan
}

#
# If utresexec exists then give it the opportunity to
# modify the DTU's monitor timing and/or dimensions.
#
function configureResolution {
	typeset cterminal_id=$1
	typeset csid=$2
	typeset ctid=$3
	typeset cpurge=$4
	typeset dpyfile=$5

	newres=""
	if [ -x "$RESCONFIG" ]
	then
		#
		# Wipe any stale resolution cache files for this token
		#
		if [ -n "$cpurge" ]
		then
			"$RESCONFIG" -k -t "$ctid"
		fi
		#
		# Use the 'print' builtin to send the SID through
		# stdin, it must never be exposed on a command line
		#
		newres=` print $csid | "$RESCONFIG" -c "$cterminal_id" -t "$ctid" `
	elif [ -x "$DEVCONFIG" ]
	then
		newres=`$DEVCONFIG $dpyfile`
	fi

	if [ -n "$newres" ]
	then
		print $newres
	fi
}

# Function to delete a session
function deleteSession {
	typeset token=$1	# token name
	typeset tokenfile=$2	# token file containing session ID

	#
	# 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=$($XMGR/lookup-dpy $dir $token)

	# 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'

	$XMGR/remove-dpy $dir $token "$rmdisplay" >/dev/null

	$RM -f $tokenfile
}

# Function to generate a fresh X cookie into a given file
function xmkcookie {
	typeset authfile=$1
	typeset dpy=$2
	typeset cookie_dir=$3 

	if [ -x /usr/openwin/lib/mkcookie ] ; then
		#
		# set HOME so mkcookie has a place to store .Xauthority
		#
		export HOME="$cookie_dir"

		/usr/openwin/lib/mkcookie $authfile -auth magic-cookie

		# after mkcookie runs we don't need HOME anymore
		unset HOME

	elif [ -x /usr/bin/mcookie ] ; then
		#
		# Use 'xauth source -' and feed it the 'add' command on
		# stdin.  This avoids exposure of the cookie itself on
		# the command line, where it would be visible to anyone
		# running 'ps'.
		#
		(print -n "add :$dpy . " ; /usr/bin/mcookie) | \
		    /usr/X11R6/bin/xauth -q -f $authfile source -
	fi
}

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

	# If a cookie file already exists then set back its timestamp
	# so that we can detect whether xmkcookie manages to update
	# it.  We could just remove the old file but if xmkcookie
	# fails we'll be better off continuing with the old file
	# than running with no cookie at all.
	#
	[ ! -d "$cookie_dir" ] && mkdir -p $cookie_dir
	touch -c -t 200110301725 $cookie_path
	xmkcookie $cookie_path $dpy $cookie_dir 2>&1 >/dev/null

        # 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`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
                XFLAGS="$XFLAGS -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:n:p:r:t:vx: name
do
	case $name in
	(c)	SESSION_TYPE="$OPTARG";
		type=special;
		;;
	(d)	INFODIR="$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
DBDIR=$INFODIR/tokens
DISPDIR=$INFODIR/displays
INSERTDIR=$INFODIR/itokens
CTOKENDIR=$INFODIR/ctokens
RESDIR=$INFODIR/dispinfo
Xconfig=$dir/Xconfig


# 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=$(awk -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
#

# Replace only the first "." with a "/". Other "."s in the token-id can
# be valid characters. Fix for bug id 4877641.

tif=$DBDIR/$(print $tid | sed -n 's/\./\//p')
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 $tid $tif
	$XMGR/notify

	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=$(head -1 $tif)

	# get the existing session type
	OLD_SESSION_TYPE=""
	dpy=$($XMGR/lookup-dpy $dir $tid)

	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
			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 $tid $tif
	else
		#
		# handle custom DTU monitor timing
		#
		newres=""
		purge=""
		newres=$(configureResolution "$termid" "$sid" "$tid" "$purge" "$DISPDIR/$dpy")

		if [ -n "$newres" ]
		then
			res="$newres"
		fi	

		# Atomic update of current resolution and other unit/display data
		#

		CreateDispinfo "$RESDIR/$dpy" "$res" "$pdata" "$termid" "$modelid"

		# 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.

		$XMGR/notify
		Exit 0
	fi
fi

#
# Add a session
#

# 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" | cat > $TMPD
if [[ $? -ne 0 ]]
then
    print -u2 "Error: couldn't create display file"
    Exit 2
fi

#
# Handle custom DTU monitor timing
#
newres=""
purge="yes"
newres=$(configureResolution "$termid" "$sid" "$tid" "$purge" "$TMPD")

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 | 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; $MV $TMPD $DISPDIR/"'$UT_DPY'

CreateDispinfo "$TMPR" "$res" "$pdata" "$termid" "$modelid"
typeset mvdisplay="$mvdisplay; $MV $TMPR $RESDIR/"'$UT_DPY'

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

dpy=$($XMGR/add-dpy $dir $tid $type "$mvdisplay")

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
	$RM -f $TMPD $TMPR
	Exit 2
fi

# save mapping of token to session
umask 0177 
print $sid | 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.
$RM -f $CTOKENDIR/$tid
$LN $DISPDIR/$dpy $CTOKENDIR/$tid

# Link $INSERTDIR/$rtid to $DISPDIR/$dpy if $rtid is defined.
if [ -n "$rtid" ]
then
	print "INSERT_TOKEN=$rtid" | 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.

	$RM -f $INSERTDIR/$rtid
	$LN $DISPDIR/$dpy $INSERTDIR/$rtid
fi

WEBGUI_GROUP=$(${SUNWUTLIB}/utadmingid)
chgrp $WEBGUI_GROUP $DISPDIR/$dpy
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 UT_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).
	#
	[ -x /bin/priocntl ] && /bin/priocntl -s -c TS -p 0 $$

	#
	# Look for the magic comment that tells us whether to start
	# an X session for this session type.  Ugh.  We should do this
	# differently, perhaps by building this into the name of the
	# script (as for nph- CGIs) instead of looking in the body of
	# the file, but for now we're stuck with this hack.
	# 
	if [ -n "`sed -n '/^## utdtsession no-xinit/p' \
	    $ETCDIR/$SESSION_TYPE.start`" ]
	then
		($ETCDIR/$SESSION_TYPE.start $tid $options) &
	else
		xauthDisplay $dpy
		($XINIT $ETCDIR/$SESSION_TYPE.start $tid $options --\
		    $XSUN ":$dpy" $XFLAGS) &
	fi
	# We do not keep track of child.
	$XMGR/notify
	Exit 0
fi

$XMGR/notify $dpy

Exit 0
