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
63 lines
3.1 KiB
Markdown
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/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.
|