SchOS/.claude/memory/schos-schinit.md
Nexoniarz a1c965da1e include: one folder per subsystem, and includes that name it
Sixty-five headers sat side by side in include/, so `#include "irq.h"`
said nothing about where irq.h was or what it belonged to, and errno.h,
stat.h and fcntl.h at the top shadowed the system's for any host tool
built with -I./include. They are in folders now -- arch/ core/ fs/
dev/ gfx/ net/ uapi/ (and ldls/ for the Linux driver layer) -- and
every include names the folder, as Linux's do: "core/proc.h".

Done mechanically and checked the hard way: the kernel built from this
tree disassembles identically to the one built from the tree before
it. The full image boots, and userland is the same set of programs.
Paths in docs and comments follow; docs/guides/layout.md has a table
of what is where, and tools/fix-includes.py rewrites old-style
includes from the folders in the tree, for code written before this.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1giNvrHTrPMPw7V6x1kQ4
2026-10-23 15:05:12 +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/uapi/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.