Monorepo for Tangled tangled.org
11

Configure Feed

Select the types of activity you want to include in your feed.

shuttle: wait for nix-daemon pids to be gone before waiting for the restarted one

Signed-off-by: dawn <dawn@tangled.org>

author
dawn
date (Jun 27, 2026, 3:19 PM +0300) commit 9824c1d4 parent c7c025ca change-id xknzlswq
+60 -25
+60 -25
shuttle/src/nix_config.rs
··· 1 1 use crate::command::{self, Spec}; 2 2 use crate::protocol::v1; 3 3 use anyhow::{Context, Result}; 4 + use nix::sys::signal::{self, Signal}; 5 + use nix::unistd::Pid; 4 6 use serde::{Deserialize, Serialize}; 5 7 use std::collections::HashSet; 6 8 use std::fmt::Write as _; ··· 160 162 const NIX_DAEMON_SOCKET: &str = "/nix/var/nix/daemon-socket/socket"; 161 163 162 164 async fn restart_nix_daemon() { 163 - let systemd = Path::new(SYSTEMCTL_EXECUTABLE).exists(); 164 - let spec = if systemd { 165 - Spec::new(SYSTEMCTL_EXECUTABLE) 165 + if Path::new(SYSTEMCTL_EXECUTABLE).exists() { 166 + let spec = Spec::new(SYSTEMCTL_EXECUTABLE) 166 167 .args(["try-restart", "nix-daemon.service"]) 167 - .timeout(Duration::from_secs(5)) 168 - } else { 169 - // on non-systemd we can just kill the daemon and it should restart 170 - Spec::new("pkill") 171 - .args(["-f", "nix-daemon"]) 172 - .timeout(Duration::from_secs(5)) 168 + .timeout(Duration::from_secs(5)); 169 + match command::run_capture(spec).await { 170 + Ok(output) if output.success() => {} 171 + Ok(output) => warn!( 172 + exit_code = output.exit.exit_code, 173 + error = ?output.exit.error, 174 + output = %output.combined_lossy(), 175 + "nix-daemon restart failed" 176 + ), 177 + Err(error) => warn!(%error, "nix-daemon restart failed"), 178 + } 179 + return; 180 + } 181 + 182 + let old_pids = nix_daemon_pids(); 183 + if old_pids.is_empty() { 184 + info!("no nix-daemon running, skipping restart"); 185 + return; 186 + } 187 + for pid in &old_pids { 188 + if let Err(error) = signal::kill(*pid, Signal::SIGTERM) { 189 + warn!(%error, pid = pid.as_raw(), "failed to signal nix-daemon"); 190 + } 191 + } 192 + 193 + // wait for nix daemon to be gone, kill is not sync 194 + wait_pids_gone(&old_pids, Duration::from_secs(5)).await; 195 + wait_for_nix_daemon_socket(Duration::from_secs(5)).await; 196 + } 197 + 198 + fn nix_daemon_pids() -> Vec<Pid> { 199 + let self_pid = std::process::id(); 200 + let Ok(entries) = fs::read_dir("/proc") else { 201 + return Vec::new(); 173 202 }; 203 + entries 204 + .flatten() 205 + .filter_map(|entry| { 206 + let pid = entry.file_name().to_str()?.parse::<i32>().ok()?; 207 + if pid as u32 == self_pid { 208 + return None; 209 + } 210 + let comm = fs::read_to_string(entry.path().join("comm")).ok()?; 211 + (comm.trim() == "nix-daemon").then(|| Pid::from_raw(pid)) 212 + }) 213 + .collect() 214 + } 174 215 175 - match command::run_capture(spec).await { 176 - Ok(output) if output.success() => { 177 - if !systemd { 178 - // init has to respawn the daemon before any step needs it 179 - wait_for_nix_daemon_socket(Duration::from_secs(5)).await; 180 - } 216 + async fn wait_pids_gone(pids: &[Pid], timeout: Duration) { 217 + let deadline = tokio::time::Instant::now() + timeout; 218 + loop { 219 + // signal 0 only checks for existence; Err (ESRCH) means it's gone 220 + if !pids.iter().any(|pid| signal::kill(*pid, None).is_ok()) { 221 + return; 181 222 } 182 - // pkill exits 1 when nothing matched, ie. no daemon to restart 183 - Ok(output) if !systemd && output.exit.exit_code == 1 => { 184 - info!("no nix-daemon running, skipping restart") 223 + if tokio::time::Instant::now() >= deadline { 224 + warn!("old nix-daemon did not exit before restart timeout"); 225 + return; 185 226 } 186 - Ok(output) => warn!( 187 - exit_code = output.exit.exit_code, 188 - error = ?output.exit.error, 189 - output = %output.combined_lossy(), 190 - "nix-daemon restart failed" 191 - ), 192 - Err(error) => warn!(%error, "nix-daemon restart failed"), 227 + tokio::time::sleep(Duration::from_millis(10)).await; 193 228 } 194 229 } 195 230