diff options
| author | user <user@clank> | 2026-08-14 21:32:25 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-08-14 21:32:25 +0200 |
| commit | 7556fa84d354150de5c961df57e230c1e74bb7af (patch) | |
| tree | d9b54466b862ec7ab417ced72af1d25df6d3347c /src/pty.c | |
| parent | sl0ppty: contrib themes, a way to look at them, and a handover (diff) | |
| download | slosh-7556fa84d354150de5c961df57e230c1e74bb7af.tar.gz slosh-7556fa84d354150de5c961df57e230c1e74bb7af.tar.xz slosh-7556fa84d354150de5c961df57e230c1e74bb7af.zip | |
sl0ppty: let a child's exit status survive long enough to be read
The server set `signal(SIGCHLD, SIG_IGN)`, which is not the same as having
no handler. SIG_IGN asks the kernel to reap children itself and throw their
statuses away, so `waitpid` can never answer: every pane's program exited
with "no idea".
Nothing read a status yet, so nothing was visibly wrong -- and the headless
driver, which is what the whole test suite drives, never set it at all. A
suite that is green either way is a suite that would never have caught this.
SIG_DFL is what was actually wanted: SIGCHLD's default action is to be
discarded, so nothing is interrupted and no handler is needed, but the child
waits as a zombie until somebody asks how it went.
That makes statuses readable and zombies possible, so pty_close now collects
the child it just hung up rather than glancing once with WNOHANG -- bounded
to a few hundred microseconds, because a process that outlives a SIGHUP is a
larger problem than the process-table entry it leaves behind.
Diffstat (limited to 'src/pty.c')
| -rw-r--r-- | src/pty.c | 10 |
1 files changed, 9 insertions, 1 deletions
@@ -10,6 +10,7 @@ #include <string.h> #include <sys/ioctl.h> #include <sys/wait.h> +#include <time.h> #include <termios.h> #include <unistd.h> @@ -80,7 +81,14 @@ void pty_close(pty_t *p) { p->fd = -1; if (p->pid > 0) { kill(p->pid, SIGHUP); - waitpid(p->pid, NULL, WNOHANG); + /* Collect it, so a session that opens and closes panes all day does not + * accumulate zombies. Bounded and short: almost everything is gone by the + * first pass, and a program that outlives a SIGHUP is a bigger problem + * than the process-table entry it leaves behind. */ + for (int i = 0; i < 5; i++) { + if (waitpid(p->pid, NULL, WNOHANG) != 0) break; + nanosleep(&(struct timespec){0, 200000}, NULL); + } } p->pid = -1; } |
