use std::time::Duration;
use tracing::{info, warn};
use super::manager::SessionManager;
use super::record::ManagedSessionState;
#[cfg(not(test))]
const SIGTERM_GRACE_SECS: u64 = 2;
#[cfg(test)]
const SIGTERM_GRACE_SECS: u64 = 0;
impl SessionManager {
pub async fn shutdown(&self) {
let records = self.list().await;
let live: Vec<_> = records
.into_iter()
.filter(|r| {
matches!(
r.state,
ManagedSessionState::Active | ManagedSessionState::Provisioning
)
})
.collect();
if live.is_empty() {
info!("shutdown: no live managed sessions to stop");
return;
}
let pids: Vec<Option<u32>> = live
.iter()
.map(|r| {
crate::core::process::find_claude_pid_in_tmux(
&r.tmux_name,
1,
Duration::from_millis(0),
)
})
.collect();
tokio::time::sleep(Duration::from_secs(SIGTERM_GRACE_SECS)).await;
let mut stopped = 0usize;
for (record, pid) in live.iter().zip(pids) {
match self.tmux.graceful_stop(&record.tmux_name, pid) {
Ok(()) => {
stopped += 1;
}
Err(e) => {
warn!(
name = %record.tmux_name,
"shutdown: graceful_stop failed (may already be gone): {e}"
);
}
}
}
info!("shutdown: gracefully stopped {stopped} live managed session(s)");
}
pub(crate) async fn graceful_terminate_runtime(&self, tmux_name: &str) {
if !self.tmux.session_exists(tmux_name) {
return;
}
let pid =
crate::core::process::find_claude_pid_in_tmux(tmux_name, 1, Duration::from_millis(0));
self.tmux.signal_terminate(tmux_name, pid);
tokio::time::sleep(Duration::from_secs(SIGTERM_GRACE_SECS)).await;
if let Err(e) = self.tmux.kill_session(tmux_name) {
warn!(
name = %tmux_name,
"graceful_terminate_runtime: kill_session failed (may already be gone): {e}"
);
}
}
}