Skip to main content

vs_core/service/
cleanup.rs

1//! Session cleanup services for shell integration state.
2
3use std::fs;
4
5use crate::{App, CoreError};
6
7impl App {
8    /// Removes the session tools file for the current session.
9    ///
10    /// Called by the shell EXIT trap via `vs __cleanup-session` so that
11    /// the home directory is resolved at runtime rather than hardcoded
12    /// in the activation script.
13    pub fn cleanup_session(&self) -> Result<(), CoreError> {
14        let session_file = self.session_file()?;
15        if session_file.exists() {
16            fs::remove_file(&session_file)?;
17        }
18        Ok(())
19    }
20
21    /// Removes session files for processes that no longer exist.
22    ///
23    /// Called during activation for shells that lack an EXIT trap
24    /// (nushell, clink) via `vs __cleanup-stale-sessions`.
25    pub fn cleanup_stale_sessions(&self) -> Result<(), CoreError> {
26        let sessions_dir = self.home().join("sessions");
27        if !sessions_dir.exists() {
28            return Ok(());
29        }
30        for entry in fs::read_dir(&sessions_dir)? {
31            let entry = entry?;
32            let path = entry.path();
33            if path.extension().and_then(|e| e.to_str()) != Some("toml") {
34                continue;
35            }
36            // Skip our own session file.
37            if let Some(session_id) = &self.session_id {
38                if path.file_stem().and_then(|s| s.to_str()) == Some(session_id.as_str()) {
39                    continue;
40                }
41            }
42            let stem = match path.file_stem().and_then(|s| s.to_str()) {
43                Some(s) => s,
44                None => continue,
45            };
46            // Try to interpret the filename as a PID.
47            if let Ok(pid) = stem.parse::<u32>() {
48                if !process_alive(pid) {
49                    let _ = fs::remove_file(&path);
50                }
51            }
52        }
53        Ok(())
54    }
55}
56
57/// Checks whether a process with the given PID is still running.
58fn process_alive(pid: u32) -> bool {
59    #[cfg(unix)]
60    {
61        // `kill -0` checks process existence without sending a signal.
62        std::process::Command::new("kill")
63            .args(["-0", &pid.to_string()])
64            .stdout(std::process::Stdio::null())
65            .stderr(std::process::Stdio::null())
66            .status()
67            .map(|s| s.success())
68            .unwrap_or(false)
69    }
70    #[cfg(not(unix))]
71    {
72        // Cannot reliably check on non-unix; assume alive.
73        let _ = pid;
74        true
75    }
76}