VAF@score.stanford.edu (Vince Fuller) (03/18/88)
I am attempting to write an application that will talk to a child process via a PTY. I want to make this communication as completely half duplex as possible, e.g. my program sends a command string to the PTY then reads the response and processes it. Because I do not have control over the format of the output from the child process and it is safe to assume that it is synchronous (i.e. it reads its input, generates some output, then reads some more input), I want to be able to read output from the PTY until the child blocks for input. Is there any way for me to detect that the child process has done this (blocked for input)? My groveling about in the UNIX manual pages for pty(4), tty(4), et. al. hasn't gotten me anywhere. Alternatively, if it is not possible to do what I want, can someone suggest an alternative method to write this application? 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 ------- -------
edw@IUS1.CS.CMU.EDU (Eddie Wyatt) (03/18/88)
In article <12472@brl-adm.ARPA>, VAF@score.stanford.edu (Vince Fuller) writes: > I want to > be able to read output from the PTY until the child blocks for input. Is there > any way for me to detect that the child process has done this (blocked for > input)? man 2 wait x x x x x x x -- Eddie Wyatt e-mail: edw@ius1.cs.cmu.edu
ka@june.cs.washington.edu (Kenneth Almquist) (03/21/88)
> 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