[systemd-devel] systemd-consoled: terminal hangup misconceptions

Jonathan de Boyne Pollard j.deboynepollard-newsgroups at ntlworld.com
Fri Dec 13 10:17:28 PST 2013


I'll patch the comment.  The code is up to you.  (-:

--- consoled-pty-old.c 2013-12-05 12:53:24.000000000 +0000
+++ consoled-pty.c 2013-12-05 12:53:11.000000000 +0000
@@ -40,26 +40,45 @@
 #include "sd-event.h"

 /*
- * PTY
- * A PTY object represents a single PTY connection between a master and a
- * child. The child process is fork()ed so the caller controls what program
- * will be run.
- *
- * Programs like /bin/login tend to perform a vhangup() on their TTY
- * before running the login procedure. This also causes the pty master
- * to get a EPOLLHUP event as long as no client has the TTY opened.
- * This means, we cannot use the TTY connection as reliable way to track
- * the client. Instead, we _must_ rely on the PID of the client to track
- * them.
- * However, this has the side effect that if the client forks and the
- * parent exits, we loose them and restart the client. But this seems to
- * be the expected behavior so we implement it here.
- *
- * Unfortunately, epoll always polls for EPOLLHUP so as long as the
- * vhangup() is ongoing, we will _always_ get EPOLLHUP and cannot sleep.
- * This gets worse if the client closes the TTY but doesn't exit.
- * Therefore, the fd must be edge-triggered in the epoll-set so we
- * only get the events once they change.
+A PTY object represents a single PTY connection between a master and a child.
+The library function to create a new PTY returns after fork() rather than
+exec()ing directly itself, so the caller has control of what program will be
+run.
+
+The correct way to detect that the slave-side session has terminated is to
+detect hangup in the terminal line discipline.  The line discipline hangs up a
+terminal when all open file descriptors for the terminal have been closed, i.e.
+when all open file descriptors to the slave side of a pseudo-terminal have been
+closed.  On a true remote terminal device, the serial device control
lines would
+change causing the modem to drop carrier.  On a pseudo-terminal, this simply
+becomes a hangup condition on the master side, which can be detected by polling
+a file descriptor for the master for POLLHUP.
+
+This is partly why nohup(1) closes all open file descriptors to a terminal.  If
+it didn't, the final file descriptors would never be closed and the line
+discipline would not hang up at logout (also hence vhangup() et al.).
+
+/bin/login (the util-linux one, at least, in its init_tty() function) closes
+all open file descriptors and re-opens them, which would normally trigger the
+line discipline to hang up.  So polling whilst /bin/login is in the process of
+doing this would continually return POLLHUP and we'd either never sleep (if we
+ignored POLLHUP) or erroneously think the session to be terminated (if we
+processed POLLHUP).  We would have to bodge around it by using edge-triggered
+events in epoll(), which would have consequences for how we respond to
+POLLIN and POLLOUT.
+
+HOWEVER, /bin/login is smart enough to clear the HUPCL flag in the terminal
+attributes (see tcsetattr(3)) whilst it is closing and re-opening the
+(slave-side) terminal device.  This tells the line discipline to NOT hang up
+when the last open file descriptor is closed.  So this situation never occurs
+and we don't need to employ edge-triggering bodges to work around it.
+
+One can see that this situation never occurs by observing that running
+/bin/login on a serial device connected to a modem doesn't hang up the
+connection before one even gets a chance to log on.  (-:
+
+It's also worth noting on the gripping hand that systemd-consoled doesn't
+even invoke /bin/login anyway.
  */

 static void pty_dispatch_write(Pty *pty) {


More information about the systemd-devel mailing list