rpi_plugin_sdk/
process.rs1use std::process::{Child, Command};
5
6#[cfg(windows)]
7#[derive(Debug)]
8pub struct ProcessTree(std::os::windows::io::OwnedHandle);
9
10#[cfg(windows)]
11impl ProcessTree {
12 pub fn prepare(command: &mut Command) {
13 use std::os::windows::process::CommandExt;
14 use windows_sys::Win32::System::Threading::{CREATE_NO_WINDOW, CREATE_SUSPENDED};
15 command.creation_flags(CREATE_NO_WINDOW | CREATE_SUSPENDED);
17 }
18 pub fn attach(child: &Child) -> Result<Self, String> {
19 use std::os::windows::io::{AsRawHandle, FromRawHandle, OwnedHandle};
20 use windows_sys::Win32::System::JobObjects::*;
21 unsafe {
25 let job = CreateJobObjectW(std::ptr::null(), std::ptr::null());
26 if job.is_null() {
27 return Err(std::io::Error::last_os_error().to_string());
28 }
29 let tree = Self(OwnedHandle::from_raw_handle(job));
30 let mut limits: JOBOBJECT_EXTENDED_LIMIT_INFORMATION = std::mem::zeroed();
31 limits.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
32 if SetInformationJobObject(
33 job,
34 JobObjectExtendedLimitInformation,
35 &limits as *const _ as *const _,
36 std::mem::size_of_val(&limits) as u32,
37 ) == 0
38 || AssignProcessToJobObject(job, child.as_raw_handle()) == 0
39 {
40 return Err(format!(
41 "protect MCP process tree: {}",
42 std::io::Error::last_os_error()
43 ));
44 }
45 resume_child(child.id())?;
46 Ok(tree)
47 }
48 }
49 pub fn kill(&self) {
50 use std::os::windows::io::AsRawHandle;
51 unsafe {
53 windows_sys::Win32::System::JobObjects::TerminateJobObject(self.0.as_raw_handle(), 1);
54 }
55 }
56}
57
58#[cfg(unix)]
59#[derive(Debug)]
60pub struct ProcessTree(i32);
61#[cfg(unix)]
62impl ProcessTree {
63 pub fn prepare(command: &mut Command) {
64 use std::os::unix::process::CommandExt;
65 command.process_group(0);
66 }
67 pub fn attach(child: &Child) -> Result<Self, String> {
68 Ok(Self(child.id() as i32))
69 }
70 pub fn kill(&self) {
71 unsafe {
74 libc::kill(-self.0, libc::SIGKILL);
75 }
76 }
77}
78
79#[cfg(windows)]
80fn resume_child(pid: u32) -> Result<(), String> {
81 use std::os::windows::io::{AsRawHandle, FromRawHandle, OwnedHandle};
82 use windows_sys::Win32::Foundation::INVALID_HANDLE_VALUE;
83 use windows_sys::Win32::System::Diagnostics::ToolHelp::*;
84 use windows_sys::Win32::System::Threading::{OpenThread, ResumeThread, THREAD_SUSPEND_RESUME};
85 unsafe {
88 let snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
89 if snapshot == INVALID_HANDLE_VALUE {
90 return Err(format!(
91 "snapshot MCP thread: {}",
92 std::io::Error::last_os_error()
93 ));
94 }
95 let snapshot = OwnedHandle::from_raw_handle(snapshot);
96 let mut entry: THREADENTRY32 = std::mem::zeroed();
97 entry.dwSize = std::mem::size_of_val(&entry) as u32;
98 let mut found = Thread32First(snapshot.as_raw_handle(), &mut entry);
99 let mut result = Err("MCP primary thread not found".into());
100 while found != 0 {
101 if entry.th32OwnerProcessID == pid {
102 let thread = OpenThread(THREAD_SUSPEND_RESUME, 0, entry.th32ThreadID);
103 if thread.is_null() {
104 result = Err(format!(
105 "open MCP thread: {}",
106 std::io::Error::last_os_error()
107 ));
108 } else {
109 let thread = OwnedHandle::from_raw_handle(thread);
110 let resumed = ResumeThread(thread.as_raw_handle());
111 result = if resumed == u32::MAX {
112 Err(format!(
113 "resume MCP thread: {}",
114 std::io::Error::last_os_error()
115 ))
116 } else {
117 Ok(())
118 };
119 }
120 break;
121 }
122 found = Thread32Next(snapshot.as_raw_handle(), &mut entry);
123 }
124 result
125 }
126}
127
128#[cfg(windows)]
131pub fn process_is_running(pid: u32) -> std::io::Result<bool> {
132 use std::os::windows::io::{AsRawHandle, FromRawHandle, OwnedHandle};
133 use windows_sys::Win32::System::Threading::{
134 GetExitCodeProcess, OpenProcess, PROCESS_QUERY_LIMITED_INFORMATION,
135 };
136 unsafe {
139 let process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, 0, pid);
140 if process.is_null() {
141 let error = std::io::Error::last_os_error();
142 return if error.raw_os_error() == Some(87) {
143 Ok(false)
144 } else {
145 Err(error)
146 };
147 }
148 let process = OwnedHandle::from_raw_handle(process);
149 let mut code = 0;
150 if GetExitCodeProcess(process.as_raw_handle(), &mut code) == 0 {
151 return Err(std::io::Error::last_os_error());
152 }
153 Ok(code == 259) }
155}
156
157#[cfg(all(test, windows))]
158mod tests {
159 #[test]
160 fn queries_live_and_missing_processes() {
161 assert!(super::process_is_running(std::process::id()).unwrap());
162 assert!(!super::process_is_running(0).unwrap());
163 }
164}