--- name: schunix-tty-fg-pgrp-coupling description: TIOCGPGRP and getpgrp() must agree or the shell spins forever at startup metadata: node_type: memory type: project originSessionId: d1a1ea76-f919-411c-b0ef-0e1bc5064563 modified: 2026-08-01T19:01:09.569Z --- `tcgetpgrp()` (TIOCGPGRP in `kernel/sys/sys_fs.c`) and `getpgrp()` must return the same value, or mksh hangs at startup in its standard loop: ```c while (tcgetpgrp(tty) != getpgrp()) kill(0, SIGTTIN); ``` **Why this is a trap:** both used to answer `proc->pid` independently, so they agreed *by accident*. The moment `getpgrp()` started reporting a real process group (which a process inherits from its parent, so pid != pgid), they disagreed permanently and the shell spun — RUNNING, never printing a prompt, never exiting, with no error anywhere. Fixed 2026-08-01 by adding `tty_fg_pgrp` (defined in `kernel/core/console.c`, declared in `defs.h`), initialised to 1 so the first shell — which inherits init's group — sees itself as foreground. TIOCGPGRP reads it, TIOCSPGRP writes it. Anything that changes process-group semantics must keep these coupled. **Diagnosing this class of hang:** Ctrl-P over the serial console dumps the process table (`procdump`, wired up in `console.c`). A shell shown as `run` rather than `sleep` means a busy loop in userland, not a crash — and if `exit()` is never reached, nothing is dying, so look for a spin, not a fault. See [[schunix-verify-by-driving-shell]], [[schunix-rewrite-priorities]].