aboutsummaryrefslogtreecommitdiffstats
path: root/src/pty.c
diff options
context:
space:
mode:
authoruser <user@clank>2026-08-14 21:32:25 +0200
committeruser <user@clank>2026-08-14 21:32:25 +0200
commit7556fa84d354150de5c961df57e230c1e74bb7af (patch)
treed9b54466b862ec7ab417ced72af1d25df6d3347c /src/pty.c
parentsl0ppty: contrib themes, a way to look at them, and a handover (diff)
downloadslosh-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.c10
1 files changed, 9 insertions, 1 deletions
diff --git a/src/pty.c b/src/pty.c
index 163e877..09d7ea6 100644
--- a/src/pty.c
+++ b/src/pty.c
@@ -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;
}