ralph/commands/daemon/
stop.rs1use crate::config::Resolved;
18use crate::lock::PidLiveness;
19use anyhow::{Context, Result, bail};
20use std::fs;
21use std::time::Duration;
22
23use super::{
24 DAEMON_LOCK_DIR, DAEMON_STATE_FILE, daemon_pid_liveness, get_daemon_state,
25 manual_daemon_cleanup_instructions,
26};
27
28pub fn stop(resolved: &Resolved) -> Result<()> {
30 let cache_dir = resolved.repo_root.join(".ralph/cache");
31
32 let state = match get_daemon_state(&cache_dir)? {
34 Some(state) => state,
35 None => {
36 println!("Daemon is not running");
37 return Ok(());
38 }
39 };
40
41 match daemon_pid_liveness(state.pid) {
42 PidLiveness::NotRunning => {
43 println!("Daemon is not running (removing stale state file)");
44 let state_path = cache_dir.join(DAEMON_STATE_FILE);
45 if let Err(e) = fs::remove_file(&state_path) {
46 log::debug!(
47 "Failed to remove stale daemon state file {}: {}",
48 state_path.display(),
49 e
50 );
51 }
52 let lock_path = cache_dir.join(DAEMON_LOCK_DIR);
53 if let Err(e) = fs::remove_dir_all(&lock_path) {
54 log::debug!(
55 "Failed to remove stale daemon lock dir {}: {}",
56 lock_path.display(),
57 e
58 );
59 }
60 return Ok(());
61 }
62 PidLiveness::Indeterminate => {
63 bail!(
64 "Daemon PID {} liveness is indeterminate; preserving state/lock to avoid concurrent supervisors. \
65 {}",
66 state.pid,
67 manual_daemon_cleanup_instructions(&cache_dir)
68 );
69 }
70 PidLiveness::Running => {}
71 }
72
73 crate::signal::create_stop_signal(&cache_dir).context("Failed to create stop signal")?;
75 println!("Stop signal sent to daemon (PID: {})", state.pid);
76
77 println!("Waiting for daemon to stop...");
79 for _ in 0..100 {
80 std::thread::sleep(Duration::from_millis(100));
81 if matches!(daemon_pid_liveness(state.pid), PidLiveness::NotRunning) {
82 println!("Daemon stopped successfully");
83 let state_path = cache_dir.join(DAEMON_STATE_FILE);
84 if let Err(e) = fs::remove_file(&state_path) {
85 log::debug!(
86 "Failed to remove daemon state file after stop {}: {}",
87 state_path.display(),
88 e
89 );
90 }
91 return Ok(());
92 }
93 }
94
95 bail!(
97 "Daemon did not stop within 10 seconds. PID: {}. You may need to kill it manually with `kill -9 {}`",
98 state.pid,
99 state.pid
100 );
101}