From 0b39f8bad16d61bf133200435a50511a37ca9f33 Mon Sep 17 00:00:00 2001 From: Nexoniarz Date: Tue, 22 Sep 2026 09:21:06 +0200 Subject: [PATCH] proc: the controlling terminal crosses fork() `ps` listed the shell and nothing the shell had started -- background jobs included, which is how it was noticed. p->ctty was set by TIOCSCTTY and never inherited, so every child of a shell had ctty 0. `ps` with no arguments shows only processes on *this* terminal, so a job running on your terminal was invisible because the only process that had ever claimed one was the shell itself. The controlling terminal is a property of the session, not of the process that happened to call the ioctl. It crosses fork() with pgid and sid, three lines above where it now sits. # sleep 60 & ; sleep 61 & # ps PID TTY TIME CMD 14 1:11 00:00:00 sleep 9 1:11 00:00:00 zsh 15 1:11 00:00:00 sleep 17 1:11 00:00:00 ps Co-Authored-By: Claude Opus 5 --- kernel/proc/proc.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/kernel/proc/proc.c b/kernel/proc/proc.c index 3d91ad6..0a19da2 100644 --- a/kernel/proc/proc.c +++ b/kernel/proc/proc.c @@ -478,6 +478,13 @@ fork(void) // POSIX: the child inherits process group, session and all four ids. np->pgid = myproc()->pgid; np->sid = myproc()->sid; + // The controlling terminal is a property of the *session*, so it crosses + // fork() with the session id rather than being re-established. It was set + // only by TIOCSCTTY and never inherited, so every child of a shell had + // ctty 0 -- which is why plain `ps` listed the shell and nothing it had + // started, background jobs included. A job that is running on your terminal + // is on your terminal whoever forked it. + np->ctty = myproc()->ctty; np->uid = myproc()->uid; np->euid = myproc()->euid; np->suid = myproc()->suid; np->gid = myproc()->gid; np->egid = myproc()->egid; np->sgid = myproc()->sgid;