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 <noreply@anthropic.com>
This commit is contained in:
Nexoniarz 2026-09-22 09:21:06 +02:00
commit 0b39f8bad1

View file

@ -478,6 +478,13 @@ fork(void)
// POSIX: the child inherits process group, session and all four ids. // POSIX: the child inherits process group, session and all four ids.
np->pgid = myproc()->pgid; np->pgid = myproc()->pgid;
np->sid = myproc()->sid; 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->uid = myproc()->uid; np->euid = myproc()->euid; np->suid = myproc()->suid;
np->gid = myproc()->gid; np->egid = myproc()->egid; np->sgid = myproc()->sgid; np->gid = myproc()->gid; np->egid = myproc()->egid; np->sgid = myproc()->sgid;