tcrm_task/tasks/process/action/
pause.rs

1/// Pause a process by process ID (Unix).
2///
3/// Sends SIGSTOP signal to the specified process to pause its execution.
4///
5/// # Arguments
6///
7/// * `pid` - The process ID to pause
8///
9/// # Returns
10///
11/// - `Ok(())` if the signal was sent successfully
12/// - `Err(std::io::Error)` if pausing failed
13///
14/// # Example
15/// ```rust,no_run
16/// use tcrm_task::tasks::process::action::pause::pause_process;
17/// let pid = 1234;
18/// pause_process(pid).unwrap();
19/// ```
20#[cfg(unix)]
21pub fn pause_process(pid: u32) -> Result<(), std::io::Error> {
22    use nix::errno::Errno;
23    use nix::sys::signal::{Signal, kill};
24    use nix::unistd::Pid;
25
26    // Check for invalid pid
27    if pid == 0 {
28        return Err(std::io::Error::new(
29            std::io::ErrorKind::InvalidInput,
30            "Invalid PID: 0",
31        ));
32    }
33
34    // Convert u32 to i32 safely, checking for overflow
35    let pid_i32 = match i32::try_from(pid) {
36        Ok(p) => p,
37        Err(_) => {
38            return Err(std::io::Error::new(
39                std::io::ErrorKind::InvalidInput,
40                format!("PID {} is too large for this system", pid),
41            ));
42        }
43    };
44
45    match kill(Pid::from_raw(pid_i32), Signal::SIGSTOP) {
46        Ok(_) => Ok(()),
47        Err(e) => match e {
48            Errno::ESRCH => Err(std::io::Error::new(
49                std::io::ErrorKind::NotFound,
50                format!("Process with PID {} does not exist", pid),
51            )),
52            Errno::EPERM => Err(std::io::Error::new(
53                std::io::ErrorKind::PermissionDenied,
54                format!("Permission denied to pause PID {}", pid),
55            )),
56            _ => Err(std::io::Error::new(
57                std::io::ErrorKind::Other,
58                format!("Failed to send SIGSTOP to PID {}: {}", pid, e),
59            )),
60        },
61    }
62}
63
64/// Pause a process by process ID (Windows).
65///
66/// Suspends all threads in the specified process using NtSuspendProcess.
67///
68/// # Arguments
69///
70/// * `pid` - The process ID to pause
71///
72/// # Returns
73///
74/// - `Ok(())` if the process was suspended successfully
75/// - `Err(std::io::Error)` if pausing failed
76///
77/// # Example
78/// ```rust,no_run
79/// use tcrm_task::tasks::process::action::pause::pause_process;
80/// let pid = 1234;
81/// pause_process(pid).unwrap();
82/// ```
83#[cfg(windows)]
84pub fn pause_process(pid: u32) -> Result<(), std::io::Error> {
85    use windows::Win32::Foundation::CloseHandle;
86    use windows::Win32::System::Diagnostics::ToolHelp::{
87        CreateToolhelp32Snapshot, TH32CS_SNAPTHREAD, THREADENTRY32, Thread32First, Thread32Next,
88    };
89    use windows::Win32::System::Threading::{OpenThread, SuspendThread, THREAD_SUSPEND_RESUME};
90
91    unsafe {
92        let snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0).map_err(|e| {
93            std::io::Error::new(
94                std::io::ErrorKind::Other,
95                format!("Failed to create thread snapshot: {:?}", e),
96            )
97        })?;
98
99        let mut thread_entry = THREADENTRY32 {
100            dwSize: std::mem::size_of::<THREADENTRY32>() as u32,
101            ..Default::default()
102        };
103
104        let mut suspended_count = 0;
105
106        if Thread32First(snapshot, &mut thread_entry).is_ok() {
107            loop {
108                if thread_entry.th32OwnerProcessID == pid {
109                    let thread_handle =
110                        OpenThread(THREAD_SUSPEND_RESUME, false, thread_entry.th32ThreadID);
111                    if let Ok(handle) = thread_handle {
112                        SuspendThread(handle);
113                        let _ = CloseHandle(handle);
114                        suspended_count += 1;
115                    }
116                }
117
118                if Thread32Next(snapshot, &mut thread_entry).is_err() {
119                    break;
120                }
121            }
122        }
123
124        let _ = CloseHandle(snapshot);
125
126        if suspended_count == 0 {
127            Err(std::io::Error::new(
128                std::io::ErrorKind::NotFound,
129                format!("No threads found for process with PID {}", pid),
130            ))
131        } else {
132            Ok(())
133        }
134    }
135}
136
137/// Process pausing is not available on this platform.
138#[cfg(not(any(unix, windows)))]
139pub fn pause_process(_pid: u32) -> Result<(), std::io::Error> {
140    Err(std::io::Error::new(
141        std::io::ErrorKind::Other,
142        "Process pausing not supported on this platform",
143    ))
144}