[comp.unix.questions] DISPLAY environment variable from login

bph@buengc.BU.EDU (Blair P. Houghton) (09/05/89)

When opening a new login window (pseudo-terminal in an X window running
a shell enabled to run the login(1) program), as in

	xterm -ls -e login &

(xterm(1X) from X11R2) the new window comes up and immediately
does the login(1).

Only, login(1) creates an entirely new set of environment variables,
effectively ignoring the imported environment, and DISPLAY in particular.
One can also no longer determine the DISPLAY variable by using tty(1)
to look at whether one is at ttyv0 or ttyv1 (the display-consoles),
because the terminal is a pty with the name of whichever was the next
available pseudo-terminal, and any pty can get set to either of the two
displays on these two-head GPX workstations.

The question is, although I know the window is on this screen, and the
X server knows the window is on this screen, how can I get the shell
(csh(1)) to know which screen this window is on, in order to set
DISPLAY (in $HOME/.login, for the benefit of this process' children),
other than by prompting for that information from the user?

/etc/gettytab(5) has this information in it so that getty(8) knows
where to send the display-consoles, but it's tied again to knowing that
you're using either ttyv0 or ttyv1, so that route is moot.

Any assistance will be appreciated.

Flame if you must:  you know I would. :-)

				--Blair
				  "Shell Script 101 has been cancelled
				   for the fall semester due to the
				   fact that the instructor is stumped
				   on this one."

janssen@holmes (Bill Janssen) (09/06/89)

Actually, using the tty information for this is an extremely fragile
option.  The server could have been started with a different display
number, or the screens could have been switched.  Some type of
meta-environment variable is really needed for this kind of situation
(multiple logins under the control of one "session").

Bill
--
 Bill Janssen        janssen.pa@xerox.com      (415) 494-4763
 Xerox Palo Alto Research Center
 3333 Coyote Hill Road, Palo Alto, California   94304

dce@Solbourne.COM (David Elliott) (09/06/89)

In article <4045@buengc.BU.EDU> bph@buengc.bu.edu (Blair P. Houghton) writes:
>Only, login(1) creates an entirely new set of environment variables,
>effectively ignoring the imported environment, and DISPLAY in particular.

Yep, this is a real pain.  I've seen a lot of solutions, but most
end up asking the user or assuming no more than one rlogin.  The only
one I really like is one in which you can query xterm for the
value of DISPLAY, but there are problems in doing this, so I use
the following solution:

1. Instead of using the normal rlogin, I have in my path ~/.rlogin,
   which contains the same type of links as /usr/hosts (that is,
   links for each hostname and nickname to an rlogin program) and a
   command called rlogin, which is the following shell script:

	#!/bin/sh

	case "$DISPLAY" in
	"")
		;;
	*)
		rm -f $HOME/.xdisplay
		echo "$DISPLAY" > $HOME/.xdisplay
		;;
	esac

	case "$0" in
	*rlogin)
		;;
	*)
		set "`basename $0`" ${1+"$@"}
		;;
	esac

	exec /usr/ucb/rlogin ${1+"$@"}

   This results in creating a file called ~/.xdisplay which contains the
   value of DISPLAY.

2. In my .login, I have the following block of code:

	if ("$TERM" == "xterm") then
		if ($?DISPLAY) then
			:
		else
			setenv DISPLAY `getdisplay`
		endif
		switch ("$DISPLAY")

		case selene\:*:
			alias bitmap bitmap -nodashed -geometry '1024x850+20+20'
			breaksw
		...

   That is, if my terminal type is xterm, then I want to get the DISPLAY
   variable if it isn't set already (i.e., I rlogin'ed instead of
   creating a new xterm directly on the client machine).  The last bit
   is where I set up aliases and variables for specific servers.

3. In my personal bin directory, I have getdisplay:

	#!/bin/sh

	PATH=/bin:/usr/bin

	if [ -f "$HOME/.xdisplay"  ]
	then
		DISPLAY=`cat $HOME/.xdisplay`
		rm -f $HOME/.xdisplay
	else
		DISPLAY="unknown:0"
	fi

	echo "$DISPLAY"

   In other words, if there's a .xdisplay file, use it to set DISPLAY.
   Note that .xdisplay is removed after it is used to avoid the possibility
   of rlogins clashing (it's better to have no DISPLAY than to have it
   be wrong).

This solution has stood me well over the last few months.  It's not
perfect, especially since the way we have our network setup, half the
machines I can login to can't find my home directory, but until there
exists an rlogin-type function that can send across required variables,
this should do fine.
-- 
David Elliott		dce@Solbourne.COM
			...!{uunet,boulder,nbires,sun}!stan!dce

"We don't do this because we love you or like you...we don't even know you!"

schaefer@ogccse.ogc.edu (Barton E. Schaefer) (09/07/89)

In article <2273@marvin.Solbourne.COM> dce@Solbourne.com (David Elliott) writes:
} In article <4045@buengc.BU.EDU> bph@buengc.bu.edu (Blair P. Houghton) writes:
} >Only, login(1) creates an entirely new set of environment variables,
} >effectively ignoring the imported environment, and DISPLAY in particular.
} 
} Yep, this is a real pain.  I've seen a lot of solutions, but most
} end up asking the user or assuming no more than one rlogin.

Most rlogins (except those on certain unusually brain-damaged Xenix and SysV
implementations) pass the value of $TERM to the remote login process, so
that the terminal type can be set correctly without asking the user.

So, you create a front-end script on the local machine that looks like

    #! /bin/sh -
    TERM="$TERM.$DISPLAY" ; export TERM
    exec /path/to/real/rlogin ${@+"$@"}

and then in .login on the remote machine you do

    # While $term has a .suffix
    while ("$term" != "$term:r")
	# If $display already contains something
	if ($?display)
	    # Append the .suffix to $display
	    set display="$display.$term:e"
	else
	    # Initialize $display as the suffix
	    set display="$term:e"
	endif
	# Strip off one suffix from $term
	set term="$term:r"
    end
    # If we built a $display, export it
    if ($?display) setenv DISPLAY "$display"

You Bourne shell people can probably figure out how to do the same thing
with "basename" and/or "sed".

If the value of $DISPLAY is particularly long, this may run into problems
with the lenght of the string that rlogin will pass as $TERM.  However,
I've used this trick to pass other information across rlogins with no
toruble whatsoever.
-- 
Bart Schaefer           "And if you believe that, you'll believe anything."
                                                            -- DangerMouse
CSNET / Internet                schaefer@cse.ogc.edu
UUCP                            ...{sequent,tektronix,verdix}!ogccse!schaefer

dce@Solbourne.COM (David Elliott) (09/07/89)

In article <4599@ogccse.ogc.edu> schaefer@ogccse.UUCP (Barton E. Schaefer) writes:
>So, you create a front-end script on the local machine that looks like
>
>    #! /bin/sh -
>    TERM="$TERM.$DISPLAY" ; export TERM
>    exec /path/to/real/rlogin ${@+"$@"}
...
>If the value of $DISPLAY is particularly long, this may run into problems
>with the lenght of the string that rlogin will pass as $TERM.  However,

The solution outlined here is quite reasonable in many cases, and I
used it for about a day.

There are two problems with it, though.

One is outlined above, that being the problem of long values of
DISPLAY.  This is not really a problem in practice, since the
array used to store the data is 64 characters.  Assuming xterm
for the terminal type and 6 characters to pass the screen size,
you are left with 52 characters for $DISPLAY.  Given that there
still exist programs that hardcode the number 32 for the maximum
hostname length (the number in 4.3BSD is defined by MAXHOSTNAMELEN,
which is 64), it's not too likely that you'll run into this problem.

The other problem I outlined in my posting.  Using the above scheme,
if you rlogin to a machine for which you have no home directory,
and thus no .login/.profile that will rebuild the variables, you
end up setting your TERM variable by hand.

Either method will work fine, though.

-- 
David Elliott		dce@Solbourne.COM
			...!{uunet,boulder,nbires,sun}!stan!dce

"We don't do this because we love you or like you...we don't even know you!"

bph@buengc.BU.EDU (Blair P. Houghton) (09/08/89)

In article <2284@marvin.Solbourne.COM> dce@Solbourne.com (David Elliott) writes:
>In article <4599@ogccse.ogc.edu> schaefer@ogccse.UUCP (Barton E. Schaefer) writes:
>>So, you create a front-end script on the local machine that looks like
>
>The solution outlined here is quite reasonable in many cases, and I
>used it for about a day.

Very nice, guys.  I appreciate the help greatly.

However, me and a couple of other guys came up with an even better
hack (well, they found it, I kludged it).  There's an _undocumented_
flag '-p' to the login(1) in Ultrix 3.1 (nee 3.0) that preserves the
environment across the login-procedure.  The only trick is to elide
every other shell and environment variable without losing DISPLAY.

Don't let that "undocumented is unsupported" stuff fool you.  If it
works in this version, it works in this version.  There isn't even
a guarantee that login(1) will exist in the next rev. of the OS.

Here's the script.  I use it about twenty times a day.

				--Blair
				  "It woiks!!"

P.S.  One comment I left out of the code:  I couldn't get 'unsetenv *'
to work.  It should.  It don't.  Hence the eval of the sed script
output.  Hackitty-hack-hack... :-)

---------------------------->8  Clip 'n' bolog! 8<--------------------
#!/bin/sh
#
# This is a shell archive.  To extract its contents,
# execute this file with /bin/sh to create the files:
# bolog
#
# This shell archive created: Tue Sep  5 04:19:34 EST 1989
#
echo "extracting file bolog"
sed -e 's/^X//' <<\*EOF > bolog
X#! /bin/csh -f
X
X#
X#  Bolog -- the C-shell script that keeps your DISPLAY in scope
X#	through a login(1) execution.
X#
X#  (C) 1989 Blair P. Houghton, All Rights Reserved
X#  Distribute freely, but keep my name attached.
X#
X#  ULTRIX belongs to DEC, and UMAX belongs to Encore.  This
X#	script was tested under Ultrix 3.1.
X#
X
X#
X#  This loop will work, but only by fixing it to echo the "set"
X#	commands to a file, then sourcing the file, since the
X#	loop actually is a subshell...All we really intend
X#	to propagate is the DISPLAY variable, anyway...
X#
X#foreach i ( $* )
X#    set $i=`printenv $i`
X#end
X
X#
X#  Protect DISPLAY from total unsetenv
X#
X
Xset DISPLAY=`printenv DISPLAY`
X
X#
X#  Wipe out all environment variables; requires cut(1),
X#	which comes with Ultrix, but not Umax but is in
X#	PD archives, so may be in a ...local/bin directory.
X#
X
Xeval `printenv | cut -f1 -d"=" | sed -e '1,$s/^/unsetenv /'`
X
X#
X#  Put DISPLAY where it belongs, since it's impossible
X#	to inform the shell after the login to do so.
X#
X
Xsetenv DISPLAY $DISPLAY
X
X#
X#  Wipe out all shell variables, which may also get passed
X#	and not superseded.
X#
X
Xunset *
X
X#
X#  Paths have also been unset.  Replace this process with the login.
X#	When login is done (i.e., via logout), control will
X#	be returned to the parent of this process.  If this
X#	process is run as argument to the -e flag of xterm(1X),
X#	then the xterm is the parent, and will immediately exit
X#	when this process returns.
X#
X#  NOTE:  this uses an undocumented, and therefore
X#	unsupported, flag ('-p') to the login(1)
X#	command.  I owe this knowledge to Peter Morreale
X#	of NCAR, and Guy Cardwell of UCI. --Blair
X#
X
Xexec /bin/login -p
*EOF
if [ `wc -c <bolog` -ne     1660 ]
then
	echo "lengths do not match -- Bad Copy of bolog"
fi
echo "Done."