diff --git a/hive-priv/src/main.rs b/hive-priv/src/main.rs index ba96273d..3c435c27 100644 --- a/hive-priv/src/main.rs +++ b/hive-priv/src/main.rs @@ -288,76 +288,7 @@ async fn exec(req: PrivRequest, writer: &mut OwnedWriteHalf) -> Result<(String, Ok((String::new(), String::new())) } - PrivRequest::ReloadGatewayNginx => { - // Query the nginx unit's ActiveState inside the gateway container. - // Requires root: --machine= transport enters the container namespace - // via the machine bus, which is forbidden for unprivileged users. - let state_out = Command::new("systemctl") - .args([ - "--machine=hive-gateway", - "show", - "--property=ActiveState", - "--value", - "nginx", - ]) - .output() - .await - .context("query nginx ActiveState in hive-gateway")?; - let state = String::from_utf8_lossy(&state_out.stdout).trim().to_owned(); - // State-aware action: reload when running; reset+start after - // start-limit failure; plain start when inactive or unknown. - match state.as_str() { - "active" => { - let out = Command::new("systemctl") - .args(["--machine=hive-gateway", "reload", "nginx"]) - .output() - .await - .context("reload nginx in hive-gateway")?; - if !out.status.success() { - bail!( - "gateway nginx reload failed ({}): {}", - out.status, - String::from_utf8_lossy(&out.stderr).trim() - ); - } - } - "failed" => { - // Clear start-limit hit so the next start can proceed. - let _ = Command::new("systemctl") - .args(["--machine=hive-gateway", "reset-failed", "nginx"]) - .status() - .await; - let out = Command::new("systemctl") - .args(["--machine=hive-gateway", "start", "nginx"]) - .output() - .await - .context("start nginx after reset-failed in hive-gateway")?; - if !out.status.success() { - bail!( - "gateway nginx start (after reset-failed) failed ({}): {}", - out.status, - String::from_utf8_lossy(&out.stderr).trim() - ); - } - } - _ => { - // inactive, activating, deactivating, unknown — just start. - let out = Command::new("systemctl") - .args(["--machine=hive-gateway", "start", "nginx"]) - .output() - .await - .context("start nginx in hive-gateway")?; - if !out.status.success() { - bail!( - "gateway nginx start failed (state={state}) ({}): {}", - out.status, - String::from_utf8_lossy(&out.stderr).trim() - ); - } - } - } - Ok((String::new(), String::new())) - } + PrivRequest::ReloadGatewayNginx => sync_gateway_nginx().await, PrivRequest::ChownSocketDir { ref agent_name, @@ -629,6 +560,92 @@ async fn read_container_journal( Ok((stdout, stderr)) } +/// Synchronise the nginx unit inside the `hive-gateway` container. +/// +/// Queries `ActiveState` via `systemctl --machine=hive-gateway` (requires +/// root — machine-bus transport enters the container namespace), then +/// dispatches: +/// - `active` → `systemctl reload nginx` (SIGHUP, zero-downtime) +/// - `failed` → `systemctl reset-failed nginx` + `systemctl start nginx` +/// - otherwise → `systemctl start nginx` +/// +/// Returns `(String::new(), String::new())` on success so it fits the +/// `exec` return type directly. +async fn sync_gateway_nginx() -> Result<(String, String)> { + let state_out = Command::new("systemctl") + .args([ + "--machine=hive-gateway", + "show", + "--property=ActiveState", + "--value", + "nginx", + ]) + .output() + .await + .context("query nginx ActiveState in hive-gateway")?; + if !state_out.status.success() { + tracing::warn!( + exit_code = ?state_out.status.code(), + stderr = %String::from_utf8_lossy(&state_out.stderr).trim(), + "systemctl show ActiveState exited non-zero — gateway container may be down" + ); + } + let state = String::from_utf8_lossy(&state_out.stdout).trim().to_owned(); + // State-aware dispatch: reload when running; reset+start after + // start-limit failure; plain start when inactive or unknown. + match state.as_str() { + "active" => { + let out = Command::new("systemctl") + .args(["--machine=hive-gateway", "reload", "nginx"]) + .output() + .await + .context("reload nginx in hive-gateway")?; + if !out.status.success() { + bail!( + "gateway nginx reload failed ({}): {}", + out.status, + String::from_utf8_lossy(&out.stderr).trim() + ); + } + } + "failed" => { + // Clear start-limit so the next start can proceed. + let _ = Command::new("systemctl") + .args(["--machine=hive-gateway", "reset-failed", "nginx"]) + .status() + .await; + let out = Command::new("systemctl") + .args(["--machine=hive-gateway", "start", "nginx"]) + .output() + .await + .context("start nginx after reset-failed in hive-gateway")?; + if !out.status.success() { + bail!( + "gateway nginx start (after reset-failed) failed ({}): {}", + out.status, + String::from_utf8_lossy(&out.stderr).trim() + ); + } + } + _ => { + // inactive, activating, deactivating, unknown — just start. + let out = Command::new("systemctl") + .args(["--machine=hive-gateway", "start", "nginx"]) + .output() + .await + .context("start nginx in hive-gateway")?; + if !out.status.success() { + bail!( + "gateway nginx start failed (state={state:?}) ({}): {}", + out.status, + String::from_utf8_lossy(&out.stderr).trim() + ); + } + } + } + Ok((String::new(), String::new())) +} + /// Return the system container name for a logical agent name. /// All agents (including the manager) use the `h-` prefix. fn container_system_name(name: &str) -> String {