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    ProcessRegistry::register(pid);
44
45    Ok(pid)
46}
47
48/// A guard that manages the lifecycle of a browser process.
49/// When dropped, it kills the process unless it was detached and NOT meant to be killed.
50pub struct ProcessGuard {
51    pub pid: u32,
52    pub auto_kill: bool,
53}
54
55impl Drop for ProcessGuard {
56    fn drop(&mut self) {
57        if self.auto_kill {
58            kill_pid(self.pid);
59        }
60        
61        // Remove from global registry as it's already handled
62        ProcessRegistry::unregister(self.pid);
63    }
64}
65
66/// Forcefully kills a process by PID.
67pub fn kill_pid(pid: u32) {
68    #[cfg(windows)]
69    {
70        let _ = Command::new("taskkill")
71            .args(["/F", "/PID", &pid.to_string()])
72            .stdout(std::process::Stdio::null())
73            .stderr(std::process::Stdio::null())
74            .status();
75    }
76    
77    #[cfg(unix)]
78    {
79        unsafe {
80            libc::kill(pid as i32, libc::SIGTERM);
81        }
82    }
83}
84
85/// Global cleanup function to be called on application exit if desired.
86pub fn cleanup_all() {
87    if let Ok(mut pids) = REGISTERED_PIDS.lock() {
88        for &pid in pids.iter() {
89            kill_pid(pid);
90        }
91        pids.clear();
92    }
93}
94
95pub struct ProcessRegistry;
96
97impl ProcessRegistry {
98    pub fn register(pid: u32) {
99        if let Ok(mut pids) = REGISTERED_PIDS.lock() {
100            pids.push(pid);
101        }
102    }
103
104    pub fn unregister(pid: u32) {
105        if let Ok(mut pids) = REGISTERED_PIDS.lock() {
106            pids.retain(|&p| p != pid);
107        }
108    }
109
110    pub fn cleanup() {
111        cleanup_all();
112    }
113}