SchOS/etc/schinit
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Nexoniarz bc39b73969 debugger: D5, the network transport -- and the plan closes
schdbgd bridges TCP to the debugger, which is the one way in that does not
need you to be sitting at the machine. It is a bridge and nothing else: a line
in becomes a line written to /dev/dbgro and the answer comes back, so `nc` is a
complete client and there is no clever client to be broken on the day you need
this.

    # schdbgd &
    schdbgd: debugger on port 4444, loopback only
    $ nc 127.0.0.1 4444
    dma
    arena  0xffff80003da41000-0xffff80003ea41000  16384 KB total, ...

A kernel debugger plus a TCP socket is a remote root hole, so three things were
decided rather than discovered:

  - It does not start by itself. schdbgd.job ships Disabled. SchOS has no
    kernel command line, so the boot flag the plan asked for is the job
    manifest, which is the same thing in this system's own idiom.
  - It binds loopback; -a names an address and says so in a paragraph.
  - It cannot reach a view that changes state, enforced in the kernel.

That last one is /dev/dbgro: /dev/dbg on minor 1. The minor is the one piece of
per-open state a character device already sees, so which node you opened is the
permission -- nothing to latch, nothing to remember between the write and the
read, no way across. Which views are refused comes from dbg_register_rw() on
the registration itself, because a central list of dangerous views goes stale
in the direction where a new view is quietly treated as safe. Today that is
`watch <addr>`; `watch` with no argument is still the listing and still
answered.

Also: socket() now accepts IPPROTO_TCP with SOCK_STREAM and IPPROTO_UDP with
SOCK_DGRAM. They name the default rather than departing from it, and toybox's
nc writes them out -- so TCP was "Protocol not supported" on a system whose
whole network is TCP.

docs/plan/debugger.md retires into docs/architecture/debugging.md; 16 files
repointed. Every plan in docs/plan/ that was open is now closed, so the next
thing is Gate 0 itself: the survey, with the instrument built.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VQ7Qx8E1q1M1bbCq8QLHS2
2026-09-14 15:33:53 +02:00
..
console-shell.job tty: six virtual terminals, and the plumbing that makes them terminals 2026-09-13 00:08:00 +02:00
README hybrid: the driver is killed, and the driver comes back 2026-09-14 00:05:43 +02:00
schdbgd.job debugger: D5, the network transport -- and the plan closes 2026-09-14 15:33:53 +02:00
tty2-shell.job tty: six virtual terminals, and the plumbing that makes them terminals 2026-09-13 00:08:00 +02:00
vgpud.job hybrid: the driver is killed, and the driver comes back 2026-09-14 00:05:43 +02:00

Job manifests for schinit, SchOS process 1.

Every *.job file in this directory is loaded at boot. Format is sectioned
key = value; # starts a comment, keys are case-insensitive.

  Label             a reverse-DNS name, as launchd uses. Identity, not a path.
  Program           the binary to execute
  Args              argv, space separated. argv[0] is conventionally the
                    basename, so `Args = sh` for `/bin/sh`.
  RunAtLoad         start it when schinit loads
  KeepAlive         restart it when it exits
  Disabled          load it but do not start it
  ThrottleInterval  seconds between respawns (default 1)
  StandardIn        \
  StandardOut        > paths to open as fd 0, 1, 2
  StandardError     /

  [Socket <name>]
  Path              a unix socket schinit will hold on the job's behalf and
                    start the job on first connect

A KeepAlive job that exits with status 78 (sysexits.h's EX_CONFIG) is not
restarted, and schinit says which job stopped and that it stopped on purpose.
This is for a program that has looked at the machine and found nothing here it
can run against -- a driver whose device is absent, say -- which would otherwise
report the same thing every ThrottleInterval for the rest of the uptime. A job
that was killed or crashed carries a signal in its status, never matches, and
comes back exactly as KeepAlive promises.

Socket sections are parsed but not yet acted on -- that is milestone 2, and
demand launching is the whole reason this is shaped after launchd rather than
sysvinit. The key exists now so manifests do not have to change later.

Unknown keys and unknown sections are ignored rather than fatal: a manifest
written for a newer schinit must not stop this one booting the machine.