#! /bin/bash
#
# Determines the number of the virtual terminal that hosts the X-server. 
# If there are multiple server running the one with biggest user-time is 
# choosen. If there is no X running at all, nothing is printed.
# must be run as root.
# If no X-server or wrong usage it returns 1, else 0.
#
# Author: Christian Zoz <zoz@suse.de>
#         Stefan Seyfried <seife@suse.de>
#
# Example usage:
#	# switch to X
#	chvt `wttyhx`
#
# The name of the X server binary. Change this if you are using e.g. Xaccel
 
usage() {
	echo "usage: $0 [-h] [-q] [-t]"
	echo "  -a: print all users that run a X server and the display numbers"
	echo "  -h: print a help message"
	echo "  -q: be quiet, dont print any output"
	echo "  -t: print a table of pids and vts of all running X server"
	echo "  -v: print tty, user, display and version of X"
	echo "  -3: also return 1 if XF86version is 3"
	echo "  -4: also return 1 if XF86version is 4"
	echo "-a, -q, -t and -v are mutually exclusive"
	exit 1
}

#getusername() {
#	OLD_IFS="$IFS"
#	IFS=:
#	while read NAME PASS ID REST; do
#		test "$1" = "$ID" && break
#	done < /etc/passwd
#	IFS="$OLD_IFS"
#	echo "$NAME"
#}

getusername() {
        local _IFS DUMMY
        _IFS="$IFS"
        IFS=:
        read USERNAME DUMMY < <(getent passwd $1)
        echo $USERNAME
        IFS="${_IFS}"
}

getuserfromwho() {
	while read NAME DISP REST; do
		test "$1" = "$DISP" && break
	done < <(who)
	echo "${NAME:--}"
}

ACTION=tty
LEAVE=0
while [ $# -gt 0 ] ; do
	case "$1" in
		-a) ACTION=alluser ;;
		-h) 
			echo "  ${0##*/} determines the number of the virtual terminal that"
			echo "  hosts the X-server. If there are multiple server running that"
			echo "  with biggest user-time is choosen. If there is no X running at"
			echo "  all, nothing is printed."
			echo "  Additionally ${0##*/} can provide the user that owns the X"
			echo "  server and the major version of the server."
			echo "  If no X-server or wrong usage it returns 1, else 0"
			usage
			exit 
			;;
		-q) ACTION=""      ;;
		-t) ACTION=table   ;;
		-v) ACTION=verbose ;;
		-3) LEAVE=3        ;;
		-4) LEAVE=4        ;;
		*) usage           ;;
	esac
	shift
done


declare -a XPIDS XTTYS BINARY VERSION UTIME
declare -a USER_WHO UID_WHO USER_W UID_W USER_REAL UID_REAL
declare -a DISP_PROC UID_PROC EUID_PROC SUID_PROC FSUID_PROC
declare -i n=0 m=0 uid_real uid

# Lets look for all running X server, their owner and their tty
# this will only work, if the X server is called by a "/usr/bin/X"-symlink.
# this is common, so we don't need to search the whole PID-space
pushd /proc &>/dev/null
for PID in `pidof X`; do
	# At first search all XFree86 processes and get their versions and utime
	BIN=`readlink $PID/exe`
	case "$BIN" in
		/usr/X11R6/bin/XFree86) VERSION[$n]=4 ;;
		/usr/X11R6/bin/Xorg)    VERSION[$n]=4 ;;
		/usr/X11R6/bin/X)       VERSION[$n]=3 ;;
		*)                      continue      ;;
	esac
	XPIDS[$n]=$PID
	BINARY[$n]=${BIN##*/}
	read pid comm state ppid pgrp session tty_nr tty_pgrp flags min_flt \
	     cmin_flt maj_flt cmaj_flt utime stime cutime cstime prio nice rest \
	     < $PID/stat
	UTIME[$n]=$utime
	# Then look for the tty that hosts X
	for FD in $PID/fd/*; do
		FDL=`readlink $FD`
		test "${FDL%[0-9]*}" = "/dev/tty" && break
	done
	XTTYS[$n]=${FDL#/dev/tty}
	# Get the display number from the commandline
	DISP=`tr '\0' '\40' < $PID/cmdline | sed 's#^.*:\([0-9]\+\).*$#\1\n#'`
	DISP_PROC[$n]=$DISP
	# Finally get the user that owns the X server. We need different
	# methods to get the real X user, because it depends on the version of
	# the server and on how it was started. So we finally look for user
	# id at several places and then take the user with the highest uid.
	while read USER D R; do
		test "$D" = "$DISP" && break
	done < <(w -hn | sed -n 's#^\([^ ]*\).* :\([0-9]\+\).*$#\1 \2#p')
	USER_W[$n]=$USER; UID_W[$n]=`id -u $USER 2>/dev/null`
	while read TAG U0 U1 U2 U3 R; do
		if [ "$TAG" = "Uid:" ] ; then
                        UID_PROC[$n]=$U0
                        EUID_PROC[$n]=$U1
                        SUID_PROC[$n]=$U2
                        FSUID_PROC[$n]=$U3
		fi
	done < $PID/status
	USER_WHO[$n]=`getuserfromwho ":$DISP"`
        UID_WHO[$n]=`id -u ${USER_WHO[$n]} 2>/dev/null`
	USER_REAL[$n]="root"; UID_REAL[$n]=0
	for uid in ${UID_PROC[$n]} ${EUID_PROC[$n]} ${SUID_PROC[$n]} \
                    ${FSUID_PROC[$n]} ${UID_WHO[$n]} ${UID_W[$n]}; do
		test $uid -gt ${UID_REAL[$n]} && UID_REAL[$n]=$uid
	done
        USER_REAL[$n]=`getusername ${UID_REAL[$n]}`
	: $((n++))
done
popd &>/dev/null

test $n -lt 1 && exit 1

# search the X server with biggest user-time
utime=0
while [ "$n" -ge 0 ] ; do
	if [ "${UTIME[$n]:-0}" -gt "$utime" ] ; then
		utime=${UTIME[$n]}
		m=$n
	fi
	: $((n--))
done

case "$ACTION" in
	table)
		OLDIFS=$IFS
		IFS=$'\t'
		echo "pids:           ${XPIDS[*]}"
		echo "vt-proc:        ${XTTYS[*]}"
		echo "disp-proc:      ${DISP_PROC[*]}"
		# echo "uid-proc:       ${UID_PROC[*]}"
		# echo "euid-proc:      ${EUID_PROC[*]}"
		# echo "suid-proc:      ${SUID_PROC[*]}"
		# echo "fsuid-proc:     ${FSUID_PROC[*]}"
		# echo "user-who:       ${USER_WHO[*]}"
		# echo "user-w:         ${USER_W[*]}"
		echo "user:           ${USER_REAL[*]}"
		echo "utime:          ${UTIME[*]}"
		echo "binary:         ${BINARY[*]}"
		echo "version:        ${VERSION[*]}"
		IFS=$OLDIFS
		;;
	tty)
		echo "${XTTYS[$m]}"
		;;
	verbose)
		echo "${XTTYS[$m]} ${USER_REAL[$m]} :${DISP_PROC[$m]} ${VERSION[$m]}"
		;;
	alluser)
		n=0
		while [ -n "${USER_REAL[$n]}" ] ; do
			echo -e "${USER_REAL[$n]}\t:${DISP_PROC[$n]}"
			: $((n++))
		done
		;;
esac

if [ "$VERSION" -eq "$LEAVE" ] ; then
	exit 1
fi
