SchOS/.claude/memory/schos-schinit.md
2026-08-09 16:36:54 +02:00

63 lines
3.1 KiB
Markdown

---
name: schos-schinit
description: "schinit is process 1, launchd-shaped; job manifests, the ControllingTerminal trap, and what milestones 2 and 3 are"
metadata:
node_type: memory
type: project
originSessionId: 9d8a2aef-bcf0-47a0-96c6-f706efab4a95
modified: 2026-08-09T14:36:32.587Z
---
**`cmd/schinit/` is process 1**, chosen by Nexoniarz to be "close to Apple's
launchd, the king of inits". `lib/initcode.S` execs `/bin/schinit`. The old
`cmd/init` is still in the tree but nothing runs it.
**Milestone 1 (done, 2026-08-09):** manifests, `RunAtLoad`, `KeepAlive` with
`ThrottleInterval`, reaping, `ControllingTerminal`.
**Milestone 2:** socket activation — schinit holds the listener and starts the
job on first connect. `[Socket name]` / `Path` is already parsed and stored in
`job.sock_path`, so manifests do not have to change.
**Milestone 3:** `schinitctl` (list/start/stop/reload), `WatchPaths`, per-user
agents.
**The format is sectioned `key = value`**, not plists. Decided deliberately:
the roadmap's own words are that "the point of copying launchd rather than
sysvinit is not the config format, it is *demand launching*". Keys keep
launchd's names so the model transfers. Files live in `etc/schinit/*.job` in the
repo and are installed to `/etc/schinit/` by the `etc-install` target — **not
written straight into `sysroot/`, which is gitignored**; that is why a fresh
clone has no `/etc/passwd` or `/etc/motd` (still an open gap).
**No libc in `cmd/`.** These programs link `lib/` — raw syscall stubs. No
malloc, no printf, no strlen. Static storage and hand-rolled helpers only.
**`getdents64` was exposed for this.** The kernel has had syscall 96/217 since
the Linux ABI landed, but nothing declared it in `include/user.h` or
`lib/usys.S`, so first-party programs could not list a directory at all. Step
records by `d_reclen`, never `sizeof`.
## The trap worth remembering: setsid() costs you the terminal
`setsid()` puts a job in its own session, and **a new session has no controlling
terminal**. The console delivers input only to the foreground process group it
tracks (`tty_fg_pgrp`), so the shell started, echoed every keystroke through the
line discipline, and executed nothing. It looks exactly like a hung shell and is
really a terminal that is not listening to it. See
[[schunix-tty-fg-pgrp-coupling]].
`ControllingTerminal = yes` claims it in the child: `ioctl(0, TIOCSCTTY, 0)`
then `ioctl(0, TIOCSPGRP, &pg)` where `pg = getpid()` (after `setsid()`, pgid ==
pid). **Off by default** — a daemon has no business seizing the console.
`TIOCSCTTY` is currently a no-op in the kernel; `TIOCSPGRP` does the real work.
## Do not transcribe bootstrap() from memory
The first version dropped `/dev/input/event0-1` and most of `/proc`, because it
was copied from the part of the old `cmd/init/main.c` that had been read on
screen rather than all 124 lines. `-Evdev` caught it with
`inputtest: cannot open /dev/input/event0`. **Read the whole file before
replacing it.**
Also: `build/usbstick.img` must be built by `all`. Without it QEMU refuses the
`-drive` and the entire `-Usb` run produces no output — which reads as a broken
USB driver, not a missing file.