12.2 The Graceful Sequence
12.2.1 Step 1: Enter the shutdown state #
peinit sets an internal flag. While it is set, no new service starts,
and control commands other than status, list and operation-status
are rejected as invalid for the current state.
Timer triggers are not disarmed. A calendar timer that fires during the shutdown window is still classified and acted on: for a service in Inactive, Completed or Failed — precisely the states step 3 classifies as not participating — that means creating a start operation and starting the service. The shutdown plan was fixed when the shutdown began, so a service started this way is in no wave, is not waited for, and is not reached by the global-timeout sweep.
12.2.2 Step 2: Suspend Critical failure semantics #
A Critical service failing during shutdown is recorded but does not trigger a reboot. The system is already going down, and rebooting from here would loop.
12.2.3 Step 3: Classify #
Completed services — Oneshots with RemainAfterExit — have no process
and are transitioned to Inactive, releasing the dependency
relationships they were holding so their dependents can be stopped
cleanly.
The rest are classified for stop eligibility:
- Active and Reloading are graceful-stop eligible and enter the waves.
- Stopping services are already on a stop path. They join the waves for ordering and timeout purposes, but do not receive another SIGTERM.
- Starting services are not eligible. peinit cancels the startup,
SIGKILLs the service cgroup if one exists, and transitions them to
Failed with cause
ShutdownWavewhile post-kill verification is pending. A cgroup still populated after the post-kill timeout takes the service to Abandoned with causeProcessUnkillableand the cgroup is leaked. A Starting service whose job never forked skips the check and goes straight to Failed. - Inactive, Failed, Skipped, Backoff and Abandoned do not participate. Abandoned cgroups stay leaked and shutdown continues.
12.2.4 Step 4: Stop in reverse dependency order #
peinit builds the reverse dependency graph over hard dependencies and stops in waves:
- Each eligible service receives SIGTERM. Already-stopping ones do not.
- Each has
StopTimeoutto exit. - On expiry, SIGKILL to the service's entire cgroup.
- No service is stopped until everything depending on it has stopped.
A service that sent STOPPING=1 does not receive a SIGTERM at all: it
has already said it is shutting down, and peinit goes straight to the
stop deadline.
12.2.4.1 Timing an already-stopping service #
peinit does not reset an already-Stopping service's clock, either when shutdown begins or when its wave becomes eligible. It uses the timing evidence retained from the stop path that put the service in Stopping:
- If a Stop operation, or a Restart executing its stop leg, is in flight, that operation's retained timing governs.
- Otherwise peinit uses service-level evidence, which carries the cause
that initiated the transition —
ExplicitStop,ShutdownWave,ConflictEvictionorBindsToPropagation.
peinit requires the evidence to be present, to belong to a service actually in Stopping, to carry a cause matching the service's current cause, to name one of those four causes, and not to describe a deadline earlier than its own start. Evidence that is missing, stale or ambiguous fails closed rather than being guessed at — and it fails the whole shutdown, not just that participant, so a shutdown command with one such service is refused, and one discovered on a later wave ends the runtime loop.
An operation whose lifetime expires while it is still Pending — a later-wave stop waiting for its dependencies — fails that operation and its waiters. It does not authorise signalling the service before its wave is eligible. Shutdown owns the signalling; the operation object is an observation of it.
12.2.5 Step 5: Global timeout #
| Key | Default | Meaning |
|---|---|---|
Machine\System\Boot\ShutdownTimeout | 90 | Seconds for the whole sequence. |
Machine\System\Boot\PostKillTimeout | 5 | Seconds for a cgroup to drain after SIGKILL. |
On expiry, every remaining participant's cgroup is killed, anything that does not drain within the post-kill timeout is marked Abandoned with its cgroup leaked, and shutdown continues regardless.
12.2.6 Step 6: Save the random seed #
After every service has stopped and before any unmount, peinit writes a
fresh seed to /var/state/peinit/random-seed for the next boot: 512
bytes from the kernel CSPRNG on this machine, never copied from an
image, protected so that only SYSTEM-equivalent authority can read or
replace it.
The write is crash-conscious: a temporary file on the same filesystem, written, flushed, atomically renamed over the old seed, and the containing directory flushed. A failure is recorded and shutdown continues.
The forced and Critical-reboot paths skip it, along with the unmount step, and go directly to sync and the final action.
12.2.7 Step 7: Unmount #
- Snapshot the mount table first, from
/proc/self/mountinfo. - Attempt to unmount every remaining non-root mount in the namespace — not only what peinit mounted, so the Phase 1 set is covered by construction.
- Process in descending path depth, so children go before parents.
- A mount point already gone is a successful no-op.
- On failure, attempt a read-only remount. If that fails too, record it and continue.
- The root is never unmounted, but is remounted read-only at the end. A failure there is recorded and does not stop step 8.
12.2.8 Step 8: Sync and the final action #
This step is irreversible. Cleanup failures retained from steps 6 and 7 affect diagnostics only and never block it.
sync(). Called on all three paths — graceful, forced and Critical reboot.reboot(2)withRB_POWER_OFF,RB_AUTOBOOTorRB_HALT_SYSTEM.- If
reboot(2)returns, the final action failed. peinit enters a minimal failed-shutdown state, keeps PID 1 alive, records the failure, and retries the same action no more than once a second. It does not restart services and does not enter recovery mode — finalisation has begun and there is nothing to go back to.
12.2.9 Shutdown during boot #
A shutdown requested while Phase 2 is still running takes effect immediately, through the same classification: Starting services are SIGKILLed, services that reached Active are stopped gracefully, and the boot is abandoned.