feat(packaging): pre-spawn cleanup wrapper for clean restarts
Symptom: each app restart that didn't go through Stop daemon left
an orphan mycelium running as root, claiming the TUN \"mycelium\",
UDP/9650 (multicast discovery) and TCP/8990 (JSON-RPC, hardcoded
in 0.6.1 — no flag). Subsequent starts panicked with EBUSY or
\"Address in use\" on whichever port the orphan held.
We can't SIGKILL the orphan from user-space (root process). Move
the cleanup into an elevated context that runs in the same pkexec
authentication as the daemon spawn:
/usr/bin/mycellium-bootstrap (new shell script in the .deb)
pkill -9 -x mycelium
ip link del mycelium / mycel0
exec /usr/bin/mycelium \"\$@\"
The polkit policy now annotates this exact path with
auth_admin_keep so a single password prompt covers every
subsequent restart in the user's session.
Sidecar: when /usr/bin/mycellium-bootstrap exists (production
install) we hand pkexec that path instead of the bare daemon.
\`pnpm tauri dev\` falls back to the unwrapped binary path.
This commit is contained in:
@@ -121,6 +121,17 @@ impl SidecarHandle {
|
||||
}
|
||||
|
||||
let bin = locate_sidecar(app)?;
|
||||
// In a `.deb` install, our pre-install script ships
|
||||
// /usr/bin/mycellium-bootstrap that pkill+ip-link-del cleans
|
||||
// any orphan state before exec-ing the real binary. Polkit
|
||||
// is configured to auth_admin_keep that exact path, so
|
||||
// subsequent starts are silent.
|
||||
// Falls back to the bare binary in `tauri dev` where the
|
||||
// bootstrap script isn't installed system-wide.
|
||||
let elevation_target = match bootstrap_path() {
|
||||
Some(p) => p,
|
||||
None => bin.clone(),
|
||||
};
|
||||
// Three ports: HTTP API (loopback), TCP listen, QUIC (UDP) listen.
|
||||
// mycelium defaults to 9651 for both peer-listen ports, which
|
||||
// collides if another instance (or a leftover from a previous test)
|
||||
@@ -176,11 +187,12 @@ impl SidecarHandle {
|
||||
|
||||
info!(
|
||||
?bin,
|
||||
elevation_target = %elevation_target.display(),
|
||||
api_port, tcp_port, quic_port, metrics_port,
|
||||
"spawning mycelium sidecar via pkexec"
|
||||
);
|
||||
|
||||
let mut cmd = elevation::elevated(&bin, &args);
|
||||
let mut cmd = elevation::elevated(&elevation_target, &args);
|
||||
cmd.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.kill_on_drop(true);
|
||||
@@ -299,6 +311,15 @@ impl SidecarHandle {
|
||||
}
|
||||
}
|
||||
|
||||
/// Path of the privileged wrapper script shipped in our `.deb`. When
|
||||
/// present, we invoke it instead of the mycelium binary directly so
|
||||
/// the elevated context can clean up any orphan TUN / processes from
|
||||
/// a previous crash before `exec /usr/bin/mycelium`.
|
||||
fn bootstrap_path() -> Option<PathBuf> {
|
||||
let p = PathBuf::from("/usr/bin/mycellium-bootstrap");
|
||||
p.exists().then_some(p)
|
||||
}
|
||||
|
||||
/// Mycelium logs the assigned overlay IPv6 once at startup like:
|
||||
/// `INFO mycelium: Node overlay IP: 43d:956e:7877:d933:eecc:b305:21ff:77f9`
|
||||
/// We don't pull a regex crate just for this — a hand-rolled parser is
|
||||
|
||||
Reference in New Issue
Block a user