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

David Herrmann dh.herrmann at gmail.com
Tue Dec 17 08:22:11 PST 2013


On Fri, Dec 13, 2013 at 7:17 PM, Jonathan de Boyne Pollard
<j.deboynepollard-newsgroups at ntlworld.com> wrote:
> 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
> +
> +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.

I don't know which code is responsible for it, but without the
EPOLLET/EPOLLHUP workaround, spawning /bin/login on the pty will
result in HUP approx 75% of the time. Hence, I will keep that
behavior. I think vhangup is the culprit, but I'm not sure.

Furthermore, systemd-logind also keeps sessions open until the
session-spawner exits. I like mirroring that behavior here. What are
the downsides of doing this?


>   */
>  static void pty_dispatch_write(Pty *pty) {
> _______________________________________________
> systemd-devel mailing list
> systemd-devel at lists.freedesktop.org
> http://lists.freedesktop.org/mailman/listinfo/systemd-devel

More information about the systemd-devel mailing list