Xterminals and dxsession (summary - long)

Mike Mitchell mcm at rti.rti.org
Fri Mar 15 04:35:09 AEST 1991


In article <1214 at 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 at 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 at 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."



More information about the Comp.unix.ultrix mailing list