Skip to main content

xcelerate_core/stealth/
process.rs

1use std::process::Command;
2use std::sync::Mutex;
3use once_cell::sync::Lazy;
4use crate::error::{XcelerateResult, XcelerateError};
5
6#[cfg(windows)]
7use std::os::windows::process::CommandExt;
8
9#[cfg(unix)]
10use std::os::unix::process::CommandExt;
11
12/// Global registry of active browser PIDs for emergency cleanup.
13pub static REGISTERED_PIDS: Lazy<Mutex<Vec<u32>>> = Lazy::new(|| Mutex::new(Vec::new()));
14
15/// Spawns a detached process that can outlive the parent.
16/// 
17/// Returns the PID of the spawned process.
18pub fn spawn_detached(mut cmd: Command) -> XcelerateResult<u32> {
19    #[cfg(windows)]
20    {
21        // CREATE_NEW_PROCESS_GROUP = 0x00000200
22        // DETACHED_PROCESS = 0x00000008
23        cmd.creation_flags(0x00000200 | 0x00000008);
24    }
25
26    #[cfg(unix)]
27    {
28        unsafe {
29            cmd.pre_exec(|| {
30                // Create a new session to detach from the parent terminal
31                if libc::setsid() == -1 {
32                    return Err(std::io::Error::last_os_error());
33                }
34                Ok(())
35            });
36        }
37    }
38
39    let child = cmd.spawn().map_err(|e| XcelerateError::NotFound(format!("Failed to spawn detached process: {}", e)))?;
40    let pid = child.id();
41    
42    // Register the PID for global cleanup
43    if let Ok(mut pids) = REGISTERED_PIDS.lock() {
44        pids.push(pid);
45    }
46
47    Ok(pid)
48}
49
50/// A guard that manages the lifecycle of a browser process.
51/// When dropped, it kills the process unless it was detached and NOT meant to be killed.
52pub struct ProcessGuard {
53    pub pid: u32,
54    pub auto_kill: bool,
55}
56
57impl Drop for ProcessGuard {
58    fn drop(&mut self) {
59        if self.auto_kill {
60            kill_pid(self.pid);
61        }
62        
63        // Remove from global registry as it's already handled
64        if let Ok(mut pids) = REGISTERED_PIDS.lock() {
65            pids.retain(|&p| p != self.pid);
66        }
67    }
68}
69
70/// Forcefully kills a process by PID.
71pub fn kill_pid(pid: u32) {
72    #[cfg(windows)]
73    {
74        let _ = Command::new("taskkill")
75            .args(["/F", "/PID", &pid.to_string()])
76            .stdout(std::process::Stdio::null())
77            .stderr(std::process::Stdio::null())
78            .status();
79    }
80    
81    #[cfg(unix)]
82    {
83        unsafe {
84            libc::kill(pid as i32, libc::SIGTERM);
85        }
86    }
87}
88
89/// Global cleanup function to be called on application exit if desired.
90pub fn cleanup_all() {
91    if let Ok(mut pids) = REGISTERED_PIDS.lock() {
92        for &pid in pids.iter() {
93            kill_pid(pid);
94        }
95        pids.clear();
96    }
97}
98
99pub struct ProcessRegistry;
100
101impl ProcessRegistry {
102    pub fn register(pid: u32) {
103        if let Ok(mut pids) = REGISTERED_PIDS.lock() {
104            pids.push(pid);
105        }
106    }
107
108    pub fn unregister(pid: u32) {
109        if let Ok(mut pids) = REGISTERED_PIDS.lock() {
110            pids.retain(|&p| p != pid);
111        }
112    }
113
114    pub fn cleanup() {
115        cleanup_all();
116    }
117}