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 |
|
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.