Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Final output on slave pty is lost if it was not closed in parent. Why?

I wrote and maintain a program rlwrap that uses a pseudo-terminal to communicate with a child process. Pseudo-terminals (ptys) are found in all Unix(-like) systems, but they behave slightly differently on different platforms.

Case in point: In rlwrap, the parent process keeps the slave pty open to keep tabs on the child's terminal settings (on Linux and FreeBSD one can use the master for that, but not in Solaris, for example)

On FreeBSD (8.2) (but not Linux) this leads to the loss of the child's final output. For example:

#include <stdio.h>  /* save as test.c and compile with gcc -o test test.c -lutil */  #define BUFSIZE 255  int main(void) {   int master, slave;   char buf[BUFSIZE];   int nread;    openpty(&master, &slave, NULL, NULL, NULL);    if (fork()) {       /* parent:                                                      */     close(slave);     /* leave this out and lose slave's final words ... WHY?         */     do {       nread = read(master, buf, BUFSIZE);       write(STDOUT_FILENO, buf, nread); /* echo child's output to stdout              */     } while (nread > 0);        } else {             /* child:                                                      */     login_tty(slave);  /* this makes child a session leader and slave a controlling   */                        /* terminal for it, then dup()s std{in,out,err} to slave       */      printf("Feeling OK :-)\n");     sleep(1);     printf("Feeling unwell ... Arghhh!\n"); /* this line may get lost                 */   }   return 0; } 

The parent process will echo the child's output, as expected, but when I omit the close(slave) (keeping it open like in rlwrap):

  • on FreeBSD, the parent doesn't see the final output line, it reads an EOF instead. (If anything, I would have expected the opposite - that keeping the slave end open would prevent the output from being lost)
  • On Linux, on the other hand, an EOF is never seen, not even after the child has died (whether we close the slave or not)

Is this behaviour documented somewhere? Is there a rationale for it? Can I circumvent it without closing the slave in the parent process?

I found out that not making the slave a controlling terminal - replacing the login_tty call with a few simple dup() calls - will cure the problem. This is no solution for rlwrap however: quite a few commands need a controlling terminal (/dev/tty) to talk to, so rlwrap has to provide one for them.

like image 861
Hans Lub Avatar asked May 04 '14 15:05

Hans Lub


1 Answers

I think there is unique separate behaviour for the Pty.

  1. The system terminates if the last data is written
  2. The system terminates if the child exits (broken pipe?)

The code relies on the pipe existing long enough to send the data through, but the child exiting may cause the virtual channel to be deleted before the data is received.

This would be unique to Pty, and not exist for real terminal.

like image 145
mksteve Avatar answered Oct 04 '22 14:10

mksteve