Obscure UNIX question

Kenneth Almquist ka at june.cs.washington.edu
Mon Mar 21 11:36:27 AEST 1988


> In summary, what I want to do is two step loop in the parent process:
> 
>     1) Send command to child process by writing on master end of PTY.
>     2) Read and process child's output by reading from master end of PTY
>        until child is finished processing the command. The child is
>        considered to be "finished" when it blocks for input while reading
>        from the slave end of PTY - this is the state that I need to be
>        able to detect.
>
> 	Thanks,
> 	Vince Fuller, Stanford Networking Systems

A real challenge, thanks.  Here's what you do.

1)  Set the process group for the pty to the process group of the master
    processes (see getpgrp(2)) using the TIOCSPGRP ioctl (see tty(4)).

2)  Clear the LTOSTOP tty mode in the pty (see the TIOCLBIC ioctl near
    the end of tty(4)).

3)  Place the child process in a separate process group (see setpgrp(2)).
    The pid of the child is a good number to use as the processes group.

4)  Arrange to catch SIGCHLD (see sigvec(2); the purpose will be explained
    below.)

5)  Each time you go through the loop described above:

    a)  Set the process group of the pty to the process group of the
	child process using TIOCSPGRP.

    b)  Send a SIGCONT signal to the child process.

    c)  Write the command to the pty.

    d)  Read the first character of the response.

    e)  Set the process group of the pty to the process group of the master.

    f)  Send the child process a SIGSTOP signal followed by a SIGCONT
	signal (see kill(2)).  The reason for this is explained below.

    g)  Continue reading from the pty until a SIGCHLD signal is received.

    h)  Read any data that is still buffered inside the pty.


This needs a bit of explanation.  The basic idea is to take advantage
of the job control stuff which allows the user to be informed when a
background process tries to read from the terminal.  Step (c) allows
us to wait for the child process to read the input before we put it in
the background again.  Step (f) is necessary because the child might
have finished writing all its output and invoked read before the parent
process performed step (e).  Step (f) uses an undocumented feature of
the tty code to make the child process notice that step (e) has been
performed.  There is no danger that the SIGSTOP signal will be received
before the SIGCONT signal because the number of the SIGSTOP signal is
less than the SIGCONT signal, and another undocumented feature of main-
stream UNIXes is that they deliver lower numbered signals first.

I left out some details of steps (g) and (h).  When the child process
tries to read the pty, it will be stopped, and a SIGCHLD signal will be
sent to the parent process.  (This latter fact is not explicitly docu-
mented.)  If you want the code to be reliable, you have to code it in
a way that avoids race conditions, which is not easy given the Berkeley
signal mechanism.  It requires two flags, one to indicate when the signal
has been received and the other to indicate when a longjmp is safe:

	while (signal_received == 0 && setjmp(sigjump) == 0) {
		jump_ok = 1;
		Call select to wait for input from the pty.
		jump_ok = 0;
		if (input available)
			Read it.
	}
	while (select indicates more data present)
		Read it.

This code assumes that once data is placed in the output queue of the
pty, it will only be removed by the master process.  The code for the
signal handler for SIGCHLD is:

	onsigchld() {
		signal_received = 1;
		if (jump_ok)
			longjmp(sigjump, 1);
	}

Hope this helps (and I hope it's not *too* long winded :-))
				Kenneth Almquist



More information about the Comp.unix.wizards mailing list