#!/bin/sh

# Some System V systems don't understand the #! construct.  
#   If your system does understand it, put ": " at the beginning of the line.
#
XRSH_VER='xssh version 1.0 derived from xrsh version 5.8'
XRSH_TIME='Time-stamp: <96/03/21>'
#
# Copyright 1991 by James J. Dempsey <jjd@bbn.com>
# Copyright 1997 by Charles F. F. Karney <karney@princeton.edu>
# 
# Permission to use, copy, modify, distribute, and sell this software and its
# documentation for any purpose is hereby granted without fee, provided that
# the above copyright notice appear in all copies and that both that
# copyright notice and this permission notice appear in supporting
# documentation, James J. Dempsey makes no representations about the
# suitability of this software for any purpose.  It is provided "as is"
# without express or implied warranty.
#
#
# xssh:  start an X11 client on another host using ssh or rsh (or rexec)
#
# Usage: xssh [-help] [-version] [-l username] [-conn connmethod] XCOMM  [-auth authtype] [-shsyntax shell] [-pass envlist] [-showdisplay] XCOMM  [-compress] [-noforward] [-screen #] [-debug] [-debug2] XCOMM  host [cmd [args ...]]



#
# Example usage: xssh expo.lcs.mit.edu xterm
#
# Runs the given command on another host with $DISPLAY set in the environment.
#    Handles authentication using xhost, xauth, or $XAUTHORITY (or none).
#    Xrsh is very careful not to leave any extra processes waiting
#       around on either machine for the client to exit.  In order
#       to achieve this, csh is used on the remote host and all inputs
#       and outputs are redirected to /dev/null.
#    The -debug switch allows you to see errors, but doesn't worry about
#       leaving extra processes around.
#    The -debug2 switch sets -x so that you can see every command
#       executed by the script  (very verbose)
#    The -l switch allows you to use a different login name on the
#       remote host.  -l and its argument are passed directly to rsh.
#    The -help switch prints out the usage message.
#    The -screen switch allows you to start a client on a different screen.
#    If no X command is given, it defaults to xterm.  This means that
#         'xrsh remotehost' will start an xterm on remotehost.
#    If the given command is a path that ends in "xterm", it adds the 
#        arguments "-name xterm-$hostname" where $hostname is the name
#        of the remote host.  This allows you to customize your server's
#        xrdb database to set attributes, such as the window color, based 
#        on the name of remote host.  If you dislike this behaviour, specify
#        your own -name argument to xterm on the xrsh command line.
#
#    Xrsh handles authentication based on the value of $XRSH_AUTH_TYPE or
#        the value of the -auth command line switch.  The value can
#        be xhost, xauth, environment or none.  It defaults to xhost.
#             xhost -- run xhost to enable the remote host to open window
#                      run xhost on the host where the server is running
#             xhost-xterminal -- 
#                      run xhost to enable the remote host to open windows
#                      run xhost on the host where xrsh was first run
#             xauth -- merge the auth entry from this host on the remote
#                        host using xauth via rsh
#             environment -- pass the $XAUTHORITY environment variable from the
#                       local host to the remote host so that a common
#                       NFS accessable authority file can be used everywhere
#             none -- do nothing.  Assume the user has handled authentication.
#
#    If the environment variable XRSH_RSH_ERRORS is set to the name of a file, 
#        any rsh errors will appear in that file on the remote host.
#        If that variable is unset, error messages will be thrown away unless
#        the -debug switch is given. (Note: don't use ~ in the filename
#        because it will expand to ~ on the local host, but try to put the
#        errors in that file on the remote host.)    
#
#
#  COMMON PROBLEMS:
#     * Make sure your PATH environment variable on the remote host is
#           set in your .cshrc or .bashrc so that rsh programs have
#           access to it.  (/bin/sh and /bin/ksh users have a hard time
#           time here since their shells don't execute any init files
#           under rsh.  One way to do this is to use the XRSH_ENVS_TO_PASS
#           environment variable to pass PATH to the remote shell.
#           Optionally, you can type a full path to xrsh.
#           E.g.  xrsh remote-host /usr/bin/X11/xterm
#
#     * Make sure your PATH environment variable on the remote host
#           includes the directory containing the X programs.  This is 
#           often /usr/bin/X11 or /usr/local/bin/X11.
#     * Make sure you have rsh configured to work on the remote host.
#           You can test this by typing:  rsh remote-host echo '$PATH'
#           This will prove that rsh works and show you the PATH that
#           will be used on the remote host.  If you get "Permission 
#           denied." you probably need to update your ~/.rhosts file
#           on the remote host.  See rlogin(1).
#
# Until X11R5, this command used to be called "xon".  The name was
# changed in order to be more descriptive, to have a name that better
# matches its cousin (xrlogin) and to not collide with a similar command
# written by Keith Packard distributed with X11R5.
#
# ACQUIRING XRSH:
#
# Check for new versions of xrsh via anonymous ftp to ftp.x.org under the 
# contrib subdirectory.  Always do this before reporting a bug to see if 
# the bug has already been fixed.
#
# Written by Jim Dempsey <jjd@bbn.com> with help from Stephen Gildea
#   <gildea@bbn.com> based on an idea by Dave Mankins <dm@think.com>.
# Some additional features due to dgreen@cs.ucla.edu,
#   "David B. Rosen" <rosen@park.bu.edu>, Eero Simoncelli 
#    <eero@whitechapel.media.mit.edu> and  Martin Friedmann 
#    <martin@whitechapel.media.mit.edu>
#   Originally written around 1987.
#   Last modification by jjd@bbn.com.
#   Version 5.1 of xrsh was distributed with X11R5.


# initialize some variables

default_auth_type=none   # sites might want to change this
authenv=
foundarg=
progname=`basename $0`
rshoptions=""
passenvs=                # environment variables to pass along
sshflags=
noforwardflg=
backgroundflg=
showdisplay=
noforward=
usage="usage: $progname [-help] [-version] [-l username] [-conn connmethod] [-auth authtype] [-shsyntax shell] [-pass envlist] [-showdisplay] [-compress] [-noforward] [-screen #] [-debug] [-debug2] host [cmd [args ...]]"

if [ -z "$DISPLAY" ]; then
    echo "$progname: "'$DISPLAY must be set' 1>&2 ;
    exit 1
fi

if [ -z "$XRSH_RSH_ERRORS" ]; then
    XRSH_RSH_ERRORS=/dev/null
fi

case "$SHELL" in
    */sh | */bash | */ksh ) shsyntax=sh;;
    * ) shsyntax=csh;;
esac

case "$progname" in
 *ssh* ) default_conn_method=ssh;;
 * )     default_conn_method=rsh;;    # sites might want to change this
esac

# process command line arguments
until [ "$foundarg" = "no" ]
do
    foundarg=no
    case $1 in
	-version)
	    echo "Version: ${XRSH_VER}"
            echo "Last saved: ${XRSH_TIME}"
            exit 0
	    ;;
        -debug)
            debug=t; foundarg=yes; shift;
            ;;
        -debug2)
            set -x; foundarg=yes; shift; 
            ;;
	-help)
            echo $usage; exit 0;
            ;;
        -l)
            shift; rshoptions="-l $1"; foundarg=yes; shift;
            ;;
	-auth)
            shift; XRSH_AUTH_TYPE=$1; foundarg=yes; shift;
            ;;
	-shsyntax)
            shift; shsyntax=$1; foundarg=yes; shift;
            ;;
	-pass)
            shift; XRSH_ENVS_TO_PASS=$1; foundarg=yes; shift;
            ;;
	-noforward)
	    sshflags="$sshflags -a -k"; noforward=y; XRSH_CONN_METHOD=ssh; foundarg=yes; shift;
	    ;;
	-compress)
	    sshflags="$sshflags -C"; XRSH_AUTH_TYPE=ssh; foundarg=yes; shift;
	    ;;
	-showdisplay)
	    showdisplay='echo $DISPLAY;'; foundarg=yes; shift;
	    ;;
        -screen)
            shift; 
            # this line was blatantly stolen from Keith Packard's xon
            DISPLAY=`echo $DISPLAY|sed 's/:\\([0-9][0-9]*\\)\\.[0-9]/:\1/'`.$1
            foundarg=yes;
            shift;
            ;;
	-conn)
	    shift; XRSH_CONN_METHOD=$1; foundarg=yes; shift;
	    ;;
    esac
done

if [ "$#" = "0" ]; then
    echo $usage 1>&2; exit 1;
fi

clienthost="$1"; shift      # The full remote host name (as full as possible)

command=$1
if [ -z "$command" ]; then  # default command to xterm if none specified
    command=xterm
else
    shift
fi

# Grab the arguments to the command here so that we don't have to worry
# about restoring them when doing IFS hacking below
xcmdargs="$@"

# Try to avoid running hostname.  
# Some shells set $HOST or $HOSTNAME automatically.
localhost=${HOSTNAME-${HOST-`hostname`}}           # The local host name

# Remove domain part from hostname.

# The following paragraph of IFS based code replaces sed and 
#    runs faster because it doesn't fork.
#       clientshort is remote host name without domain
# clientshort=`echo $clienthost | sed -e 's/\..*$//'` 
oldIFS=$IFS
IFS=.
set -- $clienthost
clientshort=$1
IFS=$oldIFS

# Find display host

# The following paragraph of IFS based code replaces sed and 
#    runs faster because it doesn't fork.
# displayhost=`echo $DISPLAY | sed 's/:.*//'`
oldIFS=$IFS
IFS=:
set -- $DISPLAY
case "$#" in
    1) displayhost=$localhost;;
    *) displayhost=$1;;
esac
IFS=$oldIFS

name=
case "$displayhost" in
    "unix"|"local"|"")
        displayhost=$localhost;;
    [0-9]*.[0-9]*.[0-9]*.[0-9]*)
	name=`/usr/X11R6/bin/canonhost $displayhost`
	test -z "$name" && name=$displayhost
	displayhost=$name
    ;;
esac

# People use the -name resource to specify host-specific resources
# such as window color.
if [ `basename "$command"` = "xterm" ]; then
    command="$command -name xterm-$clientshort -n xterm-$clientshort -title 'xterm@$clientshort'"
fi
if [ $# -ge 1 ]; then
    command="$command $xcmdargs"
fi

# rshd doesn't run on Crays; use rexecd

XRSH_AUTH_TYPE=${XRSH_AUTH_TYPE-$default_auth_type}

test "$XRSH_AUTH_TYPE" = ssh && XRSH_CONN_METHOD=ssh

if test -z "$XRSH_CONN_METHOD"; then
    case "$clienthost" in
	k*.nersc.gov) XRSH_CONN_METHOD=rexec;;
	*.nersc.gov) XRSH_CONN_METHOD=ssh;;
	*) XRSH_CONN_METHOD=$default_conn_method;;
    esac
fi

if test "$sshflags" -a "$XRSH_CONN_METHOD" != ssh; then
    echo "$progname: Incompatible connection type, -compress " 1>&2
    echo "and -noforward imply -conn ssh" 1>&2
    exit 1
fi

case "$XRSH_CONN_METHOD" in
    ssh ) rsh="ssh"
	  noforwardflg=-a; backgroundflg=-f
    ;;
    rsh ) rsh="rsh" ;;
    rexec ) rexec=/usr/X11R6/bin/rexec ;;
    * )
    echo "$progname: Connection type $XRSH_CONN_METHOD is unknown. " 1>&2
    echo '$XRSH_CONN_METHOD should be one of "rsh", "ssh", "rexec"' 1>&2
    exit 1
    ;;
esac

# Construct the new $DISPLAY for the remote client

# The following paragraph of IFS based code replaces sed and 
#    runs faster because it doesn't fork.  It also handles the arp stuff.
# newdisplay="`echo $DISPLAY | sed \"s/^[^:]*:/${localhost}:/\"`"
oldIFS=$IFS
IFS=:
set -- $DISPLAY
if [ $# = 2 ]; then 
    shift
fi
case "$clienthost" in
    # If the remote host is the localhost, then don't put the hostname
    #  in $DISPLAY and let X find the fastest display path
    # But don't do this unless server is 0
    "$displayhost")
	case "$@" in
	    0 | 0.* ) newdisplay=:$@ ;;
	    * )  newdisplay="${displayhost}:$@";;
	esac
        ;;
    *.*)
	case "$displayhost" in
	    *.*) newdisplay="${displayhost}:$@";;
	    *)
		test -z "$name" && name=`/usr/X11R6/bin/canonhost $displayhost`
		test -z "$name" && name=$displayhost
		newdisplay="${name}:$@"
		;;
	esac
	;;
    # A host in the local domain
    *)
        newdisplay="${displayhost}:$@"
        ;;
    # THIS IS IGNORED
    # if a full internet domain name is used, set $DISPLAY
    # using IP address so remote host will be guaranteed return path
    *.*)
        if [ -f /usr/etc/arp ]; then
            arp=/usr/etc/arp
        elif [ -f /etc/arp ] ; then
            arp=/etc/arp
        else
            arp=arp
        fi
        address=`$arp $displayhost |tr ')' '(' | awk  -F'(' '{print $2}'`
        if [ "$address" ]; then
            newdisplay="$address:$@"
        else
            newdisplay="${displayhost}:$@"
        fi
        ;; 
    # A host in the local domain
    *)
        newdisplay="${displayhost}:$@"
        ;;
esac

# Pass along ORIG_DISPLAY if defined, else set ORIG_DISPLAY to newdisplay.

if test -z "$ORIG_DISPLAY" -a "$XRSH_AUTH_TYPE" = ssh; then
    if test "$name"; then
        ORIG_DISPLAY="${name}:$@"
    else
	case "$displayhost" in
	    *.*) ORIG_DISPLAY="${displayhost}:$@";;
	    *)
		name=`/usr/X11R6/bin/canonhost $displayhost`
		test -z "$name" && name=$displayhost
		ORIG_DISPLAY="${name}:$@"
		;;
	esac
    fi
fi

IFS=$oldIFS

# process env variables to pass early so that we can send them 
# in the xauth commands as well as the actual X client rsh command.
# This is necessary for brain-dead shells like ksh which don't run
# any init files to set the path properly.  The user can instead
# send PATH as an XRSH_ENVS_TO_PASS.
for variable in $XRSH_ENVS_TO_PASS
do
    if eval [ \""\${$variable-Xyzzy}"\" != "Xyzzy" ]; then
        eval passenvs="\"$variable='\$$variable' $passenvs"\"
    fi
done

if test "$ORIG_DISPLAY" -a "$ORIG_DISPLAY" != "$DISPLAY"; then
    passenvs="ORIG_DISPLAY=$ORIG_DISPLAY $passenvs"
fi

if test "$XRSH_AUTH_TYPE" != ssh; then
    passenvs="DISPLAY=$newdisplay $passenvs"
    test "$XRSH_CONN_METHOD" = ssh && sshflags="$sshflags -x"
fi

# We use this funny way to pass arguments to ssh, to deal with the
# spaces in the keyword-value pairs.

if test "$XRSH_CONN_METHOD" = ssh; then
    set -- -o "FallBackToRsh no" -o "BatchMode yes" -q $sshflags
else
    set none; shift
fi

# The use of ${1+"$@"} below (instead of "$@") gets around a bug with
# OSF/1's /bin/sh where "$@" with a empty argument list passed a single
# null argument (instead of no arguments).

# Use $XRSH_AUTH_TYPE to determine whether to run xhost, xauth, 
# propagate $XAUTHORITY to the remote host, or do nothing
case $XRSH_AUTH_TYPE in
  none | ssh)
      ;;
  xauth)
      if [ "$debug" ]; then
# ACTION
          xauth extract - $newdisplay | $rsh $noforwardflg ${1+"$@"} $clienthost $rshoptions "exec env $passenvs xauth merge -"
      else
# ACTION
	  xauth extract - $newdisplay | $rsh $noforwardflg ${1+"$@"} $clienthost $rshoptions "exec env $passenvs xauth merge -" >/dev/null 2>&1
      fi
      ;;
  environment)
      passenvs="XAUTHORITY=$XAUTHORITY $passenvs"
      ;;
  xhost)      
# Surely this test should be "$locahost" = "$displayhost"
#      if [ "$newdisplay" != "$DISPLAY" ]; then
      if [ "$localhost" = "$displayhost" ]; then
      # If run on the same host as server, allow remote host access X server.
          if [ "$debug" ]; then
# ACTION
              xhost +$clienthost
          else
# ACTION
              xhost +$clienthost > /dev/null 2>&1
          fi
      else
          # If run on a different host as the server, use rsh to allow
          #    access to the host in DISPLAY.
          xhostcmd="xhost +$clienthost"
          if [ "$debug" ]; then
              # send $passenvs in case the user has a brain dead remote shell
              # and needs to be able to pass the PATH env var (e.g. ksh)
              echo "Executing \"xhost +$clienthost\" on $displayhost with DISPLAY=$newdisplay"
# ACTION
              $rsh -n $noforwardflg ${1+"$@"} "$displayhost" "exec env $passenvs $xhostcmd"
          else
# ACTION
              $rsh -n $noforwardflg ${1+"$@"} "$displayhost"  "exec env $passenvs $xhostcmd > /dev/null" > /dev/null 2>&1
          fi
      fi
      ;;
  xhost-xterminal)
      # If run on an X terminal, rsh to the XDMCP host to run xhost
      # if this is the first time, we are on the XDMCP host and 
      # we can just run xhost.
      if [ "$XHOST" = "" ]; then
          passenvs="XHOST=$localhost $passenvs"
          if [ "$debug" ]; then
# ACTION
              xhost +$clienthost
          else
# ACTION
              xhost +$clienthost > /dev/null 2>&1 
          fi
      else
          passenvs="XHOST=$XHOSST $passenvs"
          xhostcmd="xhost +$clienthost"
	  # send $passenvs in case the user has a brain dead remote shell
          # and needs to be able to pass the PATH env var (e.g. ksh)
          if [ "$debug" ]; then
              echo "Executing \"xhost +$clienthost\" on $XHOST with DISPLAY=$newdisplay"
# ACTION
              $rsh -n $noforwardflg ${1+"$@"} "$XHOST" "exec env $passenvs $xhostcmd"
          else
# ACTION
              $rsh -n $noforwardflg ${1+"$@"} "$XHOST" "exec env $passenvs $xhostcmd > /dev/null" > /dev/null < /dev/null 2>&1
          fi
      fi
      ;;
  *)
      echo "$progname: "'$XRSH_AUTH_TYPE must be set to "xhost", ' 1>&2
      echo '"xhost-xterminal", "xauth", "ssh", "environment", "none", or left unset' 1>&2
      exit 1
      ;;
esac

test "$passenvs" && passenvs="env $passenvs"
test "$XRSH_AUTH_TYPE" != ssh && showdisplay=

# Do the real work using rsh.
# Don't use -n to rsh because SG IRIX doesn't support it.  
# Use < /dev/null instead.
if [ "$debug" ]; then
    echo "Executing \"$showdisplay $passenvs $command\" on $clienthost"
# ACTION
    $rsh -n ${1+"$@"} $clienthost $rshoptions "$showdisplay $passenvs $command"
else

# This test determines whether we need the ssh channel left open.  This is
# done by redirecting only one of stdout or stderr.  (Since with csh it
# isn't easy to redirect stderr alone, we instead redirect stdout.)

    if test "$XRSH_AUTH_TYPE" = ssh -o 	"$XRSH_CONN_METHOD" = ssh -a -z "$noforward" -a 	  "$SSH_AUTHENTICATION_SOCKET$SSH_AUTHENTICATION_FD" ; then



        case "$shsyntax" in
	sh ) output="2>> $XRSH_RSH_ERRORS";;
	* )  output=">> $XRSH_RSH_ERRORS" ;;
	esac

# ACTION
	$rsh -n $backgroundflg ${1+"$@"} $clienthost $rshoptions "$showdisplay exec $passenvs $command < /dev/null $output"

# allow time for output to appear
	stat=$?; test "$showdisplay" && sleep 3; exit $stat
    else

        case "$shsyntax" in
	sh ) output=">> $XRSH_RSH_ERRORS 2>&1" ;;
	* ) output=">>& $XRSH_RSH_ERRORS" ;;
	esac

# ACTION
	exec $rsh -n $backgroundflg ${1+"$@"} $clienthost $rshoptions "exec $passenvs $command < /dev/null $output &"
    fi

fi
