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

3.1 KiB

name description metadata
schos-schinit schinit is process 1, launchd-shaped; job manifests, the ControllingTerminal trap, and what milestones 2 and 3 are
node_type type originSessionId modified
memory project 9d8a2aef-bcf0-47a0-96c6-f706efab4a95 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.