Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,7 @@ getting it wrong strands the machine.

## Current state

Milestones 0 through 12 are done. oxinit boots under QEMU and runs as a
Milestones 0 through 13 are done. oxinit boots under QEMU and runs as a
container's PID 1.

| | |
Expand All @@ -221,12 +221,13 @@ container's PID 1.
| **M10** | A real distribution userspace, with dynamically linked services. |
| **M11** | `after`, `requires` and `conflicts` enforced; `start-sec` bounds activation. |
| **M12** | CI: every suite above, on every push. |
| **M13** | Coverage for seven behaviours the milestones claimed and nothing re-ran. |

One `epoll` loop multiplexes the signalfd, the timerfd, the notify socket, the
control socket, every socket unit's listening descriptor and every service
cgroup's `cgroup.events`. One thread. No async runtime.

Nothing is scheduled after M12. [ROADMAP.md](ROADMAP.md) has the breakdown,
Nothing is scheduled after M13. [ROADMAP.md](ROADMAP.md) has the breakdown,
including what each milestone was verified against and what was deferred out
of it.

Expand Down
49 changes: 48 additions & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -752,10 +752,57 @@ surprise: an emulated ARM64 boot on an x86_64 runner costs nine seconds more
than a native one. The 300-second budget picked in M9 turns out to be an order
of magnitude of headroom rather than a tight fit.

## Beyond M12
## M13 — Covering what was already claimed

**Done.**

M12 made the suites continuous. This is about what they were continuously
running, which turned out to be less than the milestones above claim.

Seven behaviours had no coverage at all. Each is asserted by a milestone that
says "Verified:", and each was — once, by hand, on the machine of whoever
wrote it.

| Claimed in | What nothing re-ran |
|------------|------------------------------------------------------|
| M2 | The restart policy firing at all. |
| M2 | The backoff doubling: 200ms, 400ms, 800ms. |
| M2 | `on-abnormal` treating a signal death as abnormal. |
| M2 / M3 | A watchdog miss taking the restart policy. |
| M1 | `%N` and `%u`, against a running machine. |
| M5 | `oxctl reload`. |
| M7 | `output = "null"` actually discarding. |

**`flaky.toml` is the whole story in one file.** It has existed since M2, its
comment describes the doubling exactly, and it was never reachable from
`default` — so nothing ever started it. A unit file that documents a test
nobody runs is worse than no unit file, because it reads as coverage.

The watchdog one is the most interesting. ARCHITECTURE argues at length that a
watchdog miss must take the restart policy where an `oxctl stop` overrides it,
and calls that asymmetry "the point of a watchdog". The only unit that missed a
watchdog declared `restart = "no"`, so only the half that changes nothing had
ever run.

**Nothing was broken.** Every one of the seven worked the first time it was
asked to. That is the honest result and it is worth saying plainly: this
milestone bought guarding, not fixing. The code was right; nothing was
watching it.

Verified on both architectures, in the container, and against the real Alpine
userspace: 38 checks per boot, 40 against the distribution image.

Deferred out of M13: the backoff *reset* after a sustained `Active` period,
which M2 also claims. Distinguishing "reset" from "never incremented" needs an
assertion about a counter's history, and substring matching over a log cannot
make one. It needs a different kind of test than this suite is.

## Beyond M13

Not planned, not designed, listed only so the questions have an answer:

- Coverage for `type = "datagram"` and `type = "seqpacket"` sockets, which
needs a fixture that speaks them; `listen-probe` only does streams.
- A `CLOCK_REALTIME` timerfd with `TFD_TIMER_CANCEL_ON_SET`, so a calendar
schedule survives the clock being stepped under it. Until then a wall-clock
firing is computed as a monotonic delay and an NTP correction moves it by
Expand Down
33 changes: 33 additions & 0 deletions crates/xtask/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,39 @@ const EXPECTED: &[(&str, &str)] = &[
"oxinit: tick-timer will start stamp in 3s",
"M8: and re-arms with interval, so it fires again",
),
(
"flaky exited with status 1, restarting in 200ms (restart 1)",
"M13: the restart policy fires, from the first failure",
),
(
"restarting in 400ms (restart 2)",
"M13: and the backoff doubles",
),
(
"restarting in 800ms (restart 3)",
"M13: and keeps doubling, which nothing had ever run",
),
(
"signalled terminated abnormally, restarting in",
"M13: on-abnormal sees a signal death as abnormal",
),
(
"oxinit: started signalled as pid",
"M13: and restarts for it",
),
(
"hangs-back missed its watchdog deadline",
"M13: a watchdog miss",
),
(
"hangs-back stopped, restarting in",
"M13: takes the restart policy, unlike a requested stop",
),
(
"oxinit-m13: specifiers.service runs as nobody",
"M13: %N and %u expand against a running machine",
),
("reloaded", "M5: oxctl reload, which no boot had ever run"),
(
"oxinit: shutting down to power off",
"M5: SIGTERM is handled",
Expand Down
6 changes: 6 additions & 0 deletions units/default.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@ wants = [
"needy",
"alpha",
"switch",
"flaky",
"signalled",
"hangs-back",
"specifiers",
"muted",
"reloader",
"quiet",
"probe",
"hangs",
Expand Down
15 changes: 15 additions & 0 deletions units/hangs-back.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Goes ready, stops pinging, and must come back.
#
# ARCHITECTURE argues this asymmetry is the entire point of a watchdog: a
# watchdog miss takes the restart policy, where an `oxctl stop` overrides it.
# `hangs` declares `restart = "no"`, so only half of that had ever run.
[unit]
description = "Hangs, and is restarted for it"

[service]
type = "notify"
exec = "/bin/notify-probe --hang-after 1"
watchdog-sec = "2s"
stop-sec = "1s"
restart = "on-failure"
restart-sec = "400ms"
9 changes: 9 additions & 0 deletions units/muted.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# `output = "null"`. The assertion is an absence: this token must not reach
# the console, and `oxlogd` must not have a file for it either.
[unit]
description = "Writes to nowhere"

[service]
type = "oneshot"
exec = "/bin/echo muted-should-never-appear"
output = "null"
8 changes: 8 additions & 0 deletions units/reloader.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# `oxctl reload`, which M5 listed and no boot has ever run.
[unit]
description = "Re-reads the unit directories"
after = ["banner"]

[service]
type = "oneshot"
exec = "/bin/oxctl reload"
10 changes: 10 additions & 0 deletions units/signalled.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Killed by a signal rather than exiting. `on-abnormal` restarts on a signal
# or a timeout and *not* on a non-zero exit, which is the whole difference
# between it and `on-failure` — and nothing had ever taken either branch.
[unit]
description = "Dies on a signal"

[service]
exec = '/bin/sh -c "/bin/kill -9 $$"'
restart = "on-abnormal"
restart-sec = "300ms"
10 changes: 10 additions & 0 deletions units/specifiers.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# `%N` and `%u`. M1 verified `%H` and `%n` and left the other two to a reader
# of the parser's unit tests — which do cover them, but never against a
# running machine where `user` is resolved and the unit has a real full name.
[unit]
description = "Expands the remaining specifiers"

[service]
type = "oneshot"
exec = "/bin/echo oxinit-m13: %N runs as %u"
user = "nobody"
Loading