mcba@newt.phys.unsw.OZ.AU (Michael C. B. Ashley) (02/27/91)
Has anyone managed to successfully run a number of Xterminals from a DECstation using dxsession? If so, could use PLEASE send me your /etc/ttys entries and any relevant setup files? I have my Xterminals 99% working, my remaining problem is that if you log into more than one Xterminal with the same username, then when you quit dxsession from one terminal it clobbers the window manager on the both terminals. This can cause major problems in our teaching labs where we have accounts used by many people. The documentation on dxsession is insufficiently detailed for me to work out what is wrong. Thanks! Michael Ashley mcba@newt.phys.unsw.oz.au Astrophysics Department University of New South Wales
mcba@newt.phys.unsw.OZ.AU (Michael C. B. Ashley) (03/14/91)
A couple of weeks ago I asked | Has anyone managed to successfully run a number of Xterminals from a | DECstation using dxsession? If so, could use PLEASE send me your | /etc/ttys entries and any relevant setup files? | | I have my Xterminals 99% working, my remaining problem is that if you | log into more than one Xterminal with the same username, then when you | quit dxsession from one terminal it clobbers the window manager on the | both terminals. This can cause major problems in our teaching labs where | we have accounts used by many people. The documentation on dxsession is | insufficiently detailed for me to work out what is wrong. | | Thanks! I received a very helpful reply from Barry Davis <barry@ncd.com> which enabled me to solve my problems. The following setup will allow you to run a number of Xterminals from a DECstation using dxsession. Each terminal will display the DIGITAL login box, and behave just like the console. I have only tried this with NCD 15b Xterminals (great little machines!). The system is a DS5000/200 running ULTRIX 4.0 with 88 MBytes of memory and 23 Xterminals. There are no response problems and the system runs like a rocket. To begin with you add one entry in the /etc/ttys file for each Xterminal. I put the entries at the end of the file, and they should look something like: 17 "/usr/bin/login -P /usr/local/ncd/Xprompter-ncd -C /usr/local/ncd/start-ncd -e" none on secure 18 "/usr/bin/login -P /usr/local/ncd/Xprompter-ncd -C /usr/local/ncd/start-ncd -e" none on secure (Note: this shows two entries. Each entry should be on a single line.) These entries instruct init to invoke /usr/bin/login for each terminal, with its stdin and stdout connected to /usr/local/ncd/Xprompter-ncd (we shall see in a minute that this is a script which starts up Xprompter for the terminal in question (which results in a DIGITAL login box appearing)), and that when a login succeeds, the script /usr/local/ncd/start-ncd is to be run (this starts up dxsession). The "-e" switch enables an "extended protocol", whatever that is (maybe it encrypts the password for transmission?). The critical trick is that the first thing on the line ("17" and "18" in the above examples) is sent to the scripts /usr/local/ncd/Xprompter-ncd and /usr/local/ncd/start-ncd as an argument, and it is this that enables these scripts to work out which Xterminal to connect to. I am not sure what possible forms this first argument can take, just choose something from which you can work out the IP address of the Xterminal. Clearly there are special cases you should avoid (e.g., tty01, ttyp0, and :0 (the console)). You should also make an entry in /dev for each of the new "devices", e.g., # cd /dev # ln -s /dev/null 17 # ln -s /dev/null 18 or, maybe you could use the following # cd /dev # ls -l /dev/null crw-rw-rw- 1 root 3, 2 Feb 20 11:21 /dev/null # mknod /dev/17 c 3 2 # chmod 666 /dev/17 # mknod /dev/18 c 3 2 # chmod 666 /dev/18 # ls -l /dev/null* (Could someone advise me of the pros and cons of these approaches?) The only reason that I add an entry to /dev is so that finger works (otherwise it reports "can't stat device 17"). Presumably there are other reasons to do this. Can someone offer advice? Don't forget to do "kill -HUP 1" so that the entries in /etc/ttys take effect. Here are the other files that you need: ---------------------------------------------------------- /usr/local/ncd/Xprompter-ncd #!/bin/sh if [ $1 = "-e" ] then shift fi DISPLAY=`grep '^'$1 /usr/local/ncd/ncdlist | awk '{print $2}'` export DISPLAY /usr/bin/Xprompter -e $DISPLAY ---------------------------------------------------------- /usr/local/ncd/start-ncd #!/bin/sh if [ $1 = "-e" ] then shift fi DISPLAY=`grep '^'$1 /usr/local/ncd/ncdlist | awk '{print $2}'` export DISPLAY /usr/bin/dxsession $DISPLAY /usr/local/ncd/stop-ncd -display $DISPLAY ---------------------------------------------------------- /usr/local/ncd/stop-ncd #!/bin/sh for i in `/usr/bin/X11/xlswins | grep -v '^ ' | awk '{print $1}'` do /usr/bin/X11/xkill -id $i $@ done ---------------------------------------------------------- and the file /usr/local/ncd/ncdlist contains the correspondences between the entries in /etc/ttys and the Xterminal addresses. For example, 17 129.94.130.128:0 18 129.94.130.129:0 (you can use names rather than IP numbers if you want to). When you successfully login, you are running "/usr/bin/dxsession $DISPLAY" from the "/usr/local/ncd/start-ncd" script. When you logout by choosing the "Quit" option from the "Session" menu of the "Session Manager", the next line in the script is executed, which calls "/usr/local/ncd/stop-ncd", which uses xkill to get rid of all your remaining windows (otherwise the login box will not reappear). I'm a bit worried about using xkill since the manual entry for it says don't ever use it. The alternative is to let dxsession kill your windows for you (this is what happens on the console), however, dxsession does not appear to be intelligent enough to correctly guess which windows to remove, and will happily kill window managers that you may have running on other Xterminals. You persuade dxsession not to kill your windows by giving it the argument $DISPLAY (actually anything unique will do (I think)). A final problem is what to do when a terminal fails to display a login box. I have only seen this occur if a user turns the terminal off in the middle of a session, and then only rarely. Sometimes you can fix this by using the "Restart Session" button from the NCD Compose Setup main menu. Othertimes I have found that there is a dxsession process still hanging which appears to ignore "Restart Session", so you have to explicitly kill it. Rarely, the Xprompter process itself needs to be killed and restarted. To allow users to correct these problems even if they don't own the dxsession process (e.g., the terminal was hung by someone who then walked off), I have written the perl script which follows /usr/local/bin/resetxterm.pl #!/usr/local/bin/perl -w print "This program will attempt to terminate your X session and restore a DIGITAL login box. WARNING: you must run this program from a Telnet Session created from the Main Menu that appears when you use COMPOSE-F3. Also, you must only run this program from the X terminal that you wish to restart. If both of these conditions are met, type \"yes\" to continue >"; chop($answer=<STDIN>); if ($answer ne 'yes') {exit 0;} $ENV{'IFS'} = '' if $ENV{'IFS'}; $ENV{'PATH'} = "/bin:/usr/bin:/usr/ucb/:/usr/local/bin"; open(TTY, '/usr/bin/tty|') || die "can't run tty!"; $tty = substr(<TTY>,8,2); close(TTY); open(W, '/usr/ucb/w|') || die "can't run w!"; $ip = 'invalid'; while(<W>) { if ($tty eq substr($_,9,2)) { $ip = substr($_,12,14); last; } } close(W); if (substr($ip,0,8) ne '129.94.1') { die "can't find the internet address for this X-terminal sorry, I can't help!"; } print "\nyour IP address appears to be $ip\n"; open(PS, "/bin/ps -ax|grep \"dxsession $ip\"|") || die "can't run ps!"; $dxsession_pid='0'; while (<PS>) { if (/usr/) { print "the terminal is being controlled by the following process\n$_"; $dxsession_pid=substr($_,0,5); $pid=$dxsession_pid; $pid=~s/ //g; open(PS2, "/bin/ps -$pid|grep \"dxsession $ip\"|") || die "can't run ps!"; if ($dxsession_pid ne substr(<PS2>,0,5)) { print "hmm... the process appears to have vanished...\n"; last; } print "now trying to kill the process...\n"; kill 15, $dxsession_pid; sleep(5); kill 9, $dxsession_pid; last; } } close(PS); close(PS2); if ($dxsession_pid eq '0') { print "can't find the controlling dxsession for this X-terminal maybe the problem is a stuck Xprompter ...\n"; } $Xprompter_pid = '0'; open(PS3, "/bin/ps -ax|grep \"/bin/Xprompter -e $ip\"|") || die "can't run ps!"; while (<PS3>) { if (/usr/) { print "the terminal's Xprompter is being controlled by the following process\n$_"; $Xprompter_pid=substr($_,0,5); $pid=$Xprompter_pid; $pid=~s/ //g; open(PS4, "/bin/ps -$pid|grep \"/bin/Xprompter -e $ip\"|") || die "can't run ps!"; if ($Xprompter_pid ne substr(<PS4>,0,5)) { print "hmm... the process appears to have vanished...\n"; last; } print "now trying to kill the process...\n"; kill 15, $Xprompter_pid; last; } } close(PS3); close(PS4); print "OK, I've done all that I can, click Main Menu, followed by Done; the DIGITAL login box should appear shortly. If it doesn't, try the Restart Session button in the Main Menu. As a last resort, try turning the terminal off, leaving it for 30 seconds, and turning it back on. DON'T FORGET TO LOG OUT OF THIS TELNET SESSION WHEN YOU HAVE FINISHED!!!\n"; ------------------------------------------------------------------------------- This script has to run setuid to root, so I call it with the following program main(argc,argv) int argc; char **argv; { execv("/usr/local/bin/resetxterm.pl",argv); } the executable of this program is in /usr/local/bin/resetxterm with a protection mask of 4711, and owned by root. A remaining problem is that the command "w" doesn't work out the process idle time correctly and you get crazy answers like 20 hours even though you have only been logged in for half an hour. That's all folks! I hope that this has been useful to someone. If you have any suggestions on how to improve this setup, or can correct my possible misconceptions, please let me know. Michael Ashley mcba@newt.phys.unsw.oz.au Astrophysics Dept. / Uni of NSW / Sydney, Australia
mcm@rti.rti.org (Mike Mitchell) (03/15/91)
In article <1214@usage.csd.unsw.oz.au> Michael Ashley wrote about his promblems with Xterminals and dxsession. We have just received some software that turns VAXstation 2000's into Xterminals, and I've had many of the same problems. With the help of a home-grown system-call tracing facility and a friend with Ultrix 3.1 source, I've found some serious bugs in dxsession. The first bug is that it calls 'getpgrp' with the wrong number of arguments. It calls 'getpgrp' with no arguments, which is fine IF THE PROGRAM WAS COMPILED IN THE POSIX ENVIRONMENT (which it is NOT). The next bug is that it calls 'setpgrp' with the wrong number of arguments. It calls 'setpgrp' with one argument, which is WRONG for both SYSV and Berkeley forms of the call. The next problem is that dxsession doesn't try hard enough to kill off its children. It only sends a SIGHUP to the process group, when it should send a SIGHUP, wait, send a SIGTERM, wait, then send a SIGKILL. Back to the BIG problems. The original intention of calling 'getpgrp' is to find out if dxsession is in process group 0 or 1. If it dxsession is in either of these groups, a new process group is entered by calling 'setpgrp'. Later when the quit box is clicked, dxsession does a 'killpg(1, getpgrp());' and then exits. Well, the 'if' statement that checks for process group 0 or 1 does not check for an error return, so if the 'getpgrp()' fails, dxsession assumes it is already in a process group. The 'getpgrp()' will fail, because the pid to search for was NOT passed in, and the system call gets garbage from the stack. If they had said 'getpgrp(0)', the code would be correct. Now then, if the dxsession is in process group 0 or 1, the setpgrp call gets executed. It is of the form 'setpgrp(getpid())'. Under SYSV, setpgrp takes no arguments, and under Berkeley, it takes two. This has the desired effect I guess, as the process group of process id 'getpid()' will be set to some random value that was left on the stack. However, if there is more than one dxsession running on your system, it is very likely they took the same path to get to the 'setpgrp' call, so the same value is probably on the stack. The net result is that everyone is running in the same process group. This explains why when one person is logged in twice and logs out in one window, both windows get killed. To fix these problems, I put a wrapper program around the invocation of dxsession. This program forks, the child puts itself in its own process group, and then execs dxsession. The parent waits for dxsession to terminate, then it does a killpg with a SIGTERM. Five seconds later it does a killpg with a SIGKILL, then it exits. I'm including my scripts and program below. In my /etc/ttys file I have: xterm1:0 "/local/etc/startwindow xterm1::0" none on secure Init will pass to the startwindow script the 'xterm1::0' argument. The startwindow script looks like: #! /bin/sh # # `login' needs an extra argument as a tty name. Without it, getttyname # dumps core. Luckily, it passes the name in to the Xprompter, and the # Xprompter uses that as a display name. # DISPLAY=${1} export DISPLAY exec /usr/bin/login -P /usr/bin/Xprompter -C /local/etc/dxsession ${DISPLAY} The whole reason for the script is to set the environment variable for the display. Our version of dxsession is again a script, which contains the following: #! /bin/sh exec /local/etc/newpgrp /usr/bin/dxsession ${1+"$@"} And the program 'newpgrp' is the following: /* * This program forks and execs its argument list. It puts * the child in its own process group. It waits for the * child to terminate, and then sends a SIGTERM to the * process group. Five seconds later it sends a SIGKILL * to the process group. The parent exits with the * same status as the original child. * * The purpose of this program is to put an 'Xsession' program * in a process group, then kill off all of the clients when the * 'Xsession' program terminates. This is used to support the * Xterminal software on the VaxStation 2000s. */ #include <stdio.h> #include <signal.h> #include <sys/types.h> #include <sys/wait.h> main(argc, argv) int argc; char *argv[]; { int i, rc; long s; /* wait status */ if (argc > 1) { if ((i = fork()) == 0) { i = getpid(); setpgrp(i, i); /* set process group to pid */ execvp(argv[1], &argv[1]); perror(argv[1]); /* couldn't exec, tell why */ exit(127); } /* wait for child to terminate */ while((rc = wait(&s)) > 0) { /* * if the pid is the one we are waiting for, and * it is not stopped, break out of the loop. */ if (rc == i && !WIFSTOPPED(s)) break; } /* * Send a SIGTERM, and if that succeeds, send a SIGKILL */ if (killpg(i, SIGTERM) == 0) { sleep(5); (void) killpg(i, SIGKILL); } /* * if the child exited due to a signal, signal * ourselves with the same signal. Hopefully * we will exit the same way. */ if (WIFSIGNALED(s)) (void) kill(getpid(), WTERMSIG(s)); /* * Now exit with the same exit status as the child */ exit(WEXITSTATUS(s)); } /* * Here there were no arguments, so exit with an error code. */ exit(127); } ----------------------------------------------------------- Mike Mitchell mcm@rti.rti.org uunet!rti!mcm "There's laughter where I used to see a tear. (919) 541-6098 It's all done with mirrors, have no fear." -- Mike Mitchell mcm@rti.rti.org uunet!rti!mcm "There's laughter where I used to see a tear. (919) 541-6098 It's all done with mirrors, have no fear."