From fa944d12132d606dc97547a57017039f32da8751 Mon Sep 17 00:00:00 2001 From: atlas Date: Wed, 8 Jul 2026 22:47:36 +0200 Subject: [PATCH] fix(#2284): replace nixos-container kill with machinectl kill SIGKILL MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit nixos-container has no kill verb. The KillContainer priv request was calling nixos-container kill which always fails. Replace with: machinectl kill --signal=SIGKILL which sends SIGKILL to all processes in the container — the correct semantics for a forced shutdown (called after graceful stop has been attempted). Add a machinectl_run helper alongside container_run so callers stay consistent. --- hive-priv/src/main.rs | 34 +++++++++++++++++++++++++++++++++- hive-sh4re/src/priv_proto.rs | 3 ++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/hive-priv/src/main.rs b/hive-priv/src/main.rs index e57bbe23..cdb3f9df 100644 --- a/hive-priv/src/main.rs +++ b/hive-priv/src/main.rs @@ -172,7 +172,11 @@ async fn exec(req: PrivRequest, writer: &mut OwnedWriteHalf) -> Result<(String, PrivRequest::KillContainer { ref name } => { validate_container_name(name)?; - container_run(&["kill", &container_system_name(name)]).await + // nixos-container has no kill verb. Use machinectl to send SIGKILL + // to all processes in the container — the right semantics for a + // forced shutdown after a graceful stop has already been attempted. + let machine = container_system_name(name); + machinectl_run(&["kill", &machine, "--signal=SIGKILL"]).await } PrivRequest::UpdateContainer { ref name, stream } => { @@ -1026,6 +1030,34 @@ async fn container_run(args: &[&str]) -> Result<(String, String)> { Ok((stdout, stderr)) } +/// Invoke `machinectl` with the given args, log output to journald. +/// Used for operations that nixos-container doesn't expose (e.g. sending +/// signals to running containers). +async fn machinectl_run(args: &[&str]) -> Result<(String, String)> { + let out = Command::new("machinectl") + .args(args) + .output() + .await + .context("invoke machinectl")?; + let stdout = String::from_utf8_lossy(&out.stdout).into_owned(); + let stderr = String::from_utf8_lossy(&out.stderr).into_owned(); + for line in stdout.lines() { + tracing::info!(target: "machinectl", "{line}"); + } + for line in stderr.lines() { + tracing::warn!(target: "machinectl", "{line}"); + } + if !out.status.success() { + bail!( + "machinectl {} failed ({}): {}", + args.join(" "), + out.status, + stderr.trim() + ); + } + Ok((stdout, stderr)) +} + /// Invoke `nixos-container` with the given args and forward output lines /// to the caller as `PrivEvent::Line` messages in real time, logging each /// line to journald as it arrives. Returns `(String::new(), String::new())` diff --git a/hive-sh4re/src/priv_proto.rs b/hive-sh4re/src/priv_proto.rs index 49fb99f0..618f312c 100644 --- a/hive-sh4re/src/priv_proto.rs +++ b/hive-sh4re/src/priv_proto.rs @@ -230,7 +230,8 @@ pub enum PrivRequest { /// `nixos-container stop ` StopContainer { name: String }, - /// `nixos-container kill ` + /// `machinectl kill --signal=SIGKILL` — force-kills all processes + /// in the container. nixos-container has no kill verb. KillContainer { name: String }, /// `nixos-container update --flake `