mouse/cheesed

Diff

7e3c350629d7e44e711b8f95

PLAN.md

Mode 100644100644; object 379060b91848e73c47c854ce

@@ -235,10 +235,12 @@
 
 ## Milestones
 
-Current status: C0 through C2 are implemented in the MOUSE image. The C2 proof
-service covers restart exhaustion, failed-state reporting, administrative
-recovery, forced termination, runtime-only control, and shutdown. C3 remains
-open.
+Current status: C0 through C3 are implemented in the MOUSE image. The C3 base
+replaces the proof services with a supervised serial login and a ports-provided
+service whose persistent enablement exists only in `/etc/rc.conf`. The QEMU
+suite covers login respawn, required-service recovery, optional enablement,
+missing dependencies, dependency cycles, restart exhaustion, forced
+termination, runtime-only control, and reverse-order shutdown.
 
 ### C0: executable skeleton
 

README.md

Mode 100644100644; object 2e2af675be38931e83c65fea

@@ -1,11 +1,14 @@
 # cheesed
 
-`cheesed` is MOUSE's PID 1 and boot coordinator. Milestones C0 through C2 from
+`cheesed` is MOUSE's PID 1 and boot coordinator. Milestones C0 through C3 from
 [`PLAN.md`](PLAN.md) are implemented: the static Linux init performs early
 bootstrap, strictly translates `/etc/rc.conf` into disposable OpenRC
 runlevels, runs the boot graph, reaps children during transitions and steady
 state, delegates restart and stop policy to `supervise-daemon`, provides a
-recovery `tcsh`, and coordinates OpenRC shutdown before reboot or poweroff.
+`tcsh` recovery console when a required service fails, and coordinates OpenRC
+shutdown before reboot or poweroff. A healthy integrated boot leaves the
+console to OpenRC's supervised login service rather than starting the recovery
+shell.
 
 ## Development checks
 

src/linux.rs

Mode 100644100644; object c77400ea4607fb8b4ac580ad

@@ -82,18 +82,25 @@
     }
 
     console.write_line(STARTUP_BANNER);
-    console.log("C1 bootstrap complete; cheesed is PID 1");
-    match boot_services(&mut console) {
+    console.log("C3 bootstrap complete; cheesed is PID 1");
+    let recovery = match boot_services(&mut console) {
         Ok(Some(action)) => perform_shutdown(&mut console, None, action),
-        Ok(None) => {}
-        Err(error) => console.log(&format!("{error}; entering recovery console")),
-    }
-    let mut shell_pid = match spawn_emergency_shell(&mut console) {
-        Ok(pid) => Some(pid),
+        Ok(None) => false,
         Err(error) => {
-            console.log(&error);
-            None
+            console.log(&format!("{error}; entering recovery console"));
+            true
         }
+    };
+    let mut shell_pid = if recovery {
+        match spawn_emergency_shell(&mut console) {
+            Ok(pid) => Some(pid),
+            Err(error) => {
+                console.log(&error);
+                None
+            }
+        }
+    } else {
+        None
     };
 
     loop {
@@ -109,7 +116,7 @@
             perform_shutdown(&mut console, shell_pid, action);
         }
 
-        if shell_pid.is_none() {
+        if recovery && shell_pid.is_none() {
             console.log("emergency shell exited; restarting it in one second");
             thread::sleep(SHELL_RESTART_DELAY);
             shell_pid = Some(match spawn_emergency_shell(&mut console) {