git.lucas.co / cce-cloud
cloud storage client
git clone https://git.lucas.co/cce-cloud.git

commit71d6cc0656e2c71e64a150a5059f361e2af96b17
parent22c0a36f6e
authorLucas Galante <[email protected]>
date2026-08-23 22:39
fix: reap launched apps — the daemon collected a zombie per exited app

spawn_detached handled the hard parts (process_group(0), KillMode=process
survival, spawn.log redirection) but finished with a bare cmd.spawn().ok(),
dropping the Child handle with no reaper. The daemon lives for the whole
session, so every app it ever launched stayed in the process table as a
zombie after exiting — four cce-designer <defunct> entries in one afternoon
— unreadable in /proc and reported "alive" by kill(pid, 0) probes.

Route the fully-built Command through cce_ui::process::spawn_detached, whose
whole job is spawn-then-reap; the group/log setup is untouched.

 src/main.rs | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/src/main.rs b/src/main.rs
index f300468..4c6c9c1 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -536,7 +536,11 @@ fn spawn_detached(program: &str, args: &[&str]) {
         let _ = writeln!(f, "[spawn] executing: {} {}", program, args.join(" "));
         cmd.stdout(f.try_clone().unwrap()).stderr(f);
     }
-    cmd.spawn().ok();
+    // Through the toolkit's reaping spawn, NOT a bare `cmd.spawn()`: the daemon
+    // lives for the whole session, and a dropped Child handle means every app
+    // it ever launched sits in the process table as a zombie once it exits —
+    // unreadable in /proc and reported "alive" by kill(pid, 0) probes.
+    let _ = cce_ui::process::spawn_detached(cmd);
 }