use anyhow::bail;
#[cfg(target_os = "windows")]
use windows::Win32::{
Foundation::{CloseHandle, HANDLE},
System::Threading::{
OpenProcess, PROCESS_QUERY_INFORMATION, PROCESS_TERMINATE, TerminateProcess,
},
};
use crate::collection::processes::Pid;
#[cfg(target_os = "windows")]
struct Process(HANDLE);
#[cfg(target_os = "windows")]
impl Process {
fn open(pid: u32) -> anyhow::Result<Process> {
match unsafe { OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_TERMINATE, false, pid) } {
Ok(process) => Ok(Process(process)),
Err(_) => bail!("process may have already been terminated."),
}
}
fn kill(self) -> anyhow::Result<()> {
let result = unsafe { TerminateProcess(self.0, 1) };
if result.is_err() {
bail!("process may have already been terminated.");
}
Ok(())
}
}
#[cfg(target_os = "windows")]
impl Drop for Process {
fn drop(&mut self) {
unsafe {
let _ = CloseHandle(self.0);
}
}
}
#[cfg(target_os = "windows")]
pub fn kill_process_given_pid(pid: Pid) -> anyhow::Result<()> {
let process = Process::open(pid as u32)?;
process.kill()?;
Ok(())
}
#[cfg(unix)]
pub fn kill_process_given_pid(pid: Pid, signal: usize) -> anyhow::Result<()> {
let output = unsafe { libc::kill(pid, signal as i32) };
if output != 0 {
let err_code = std::io::Error::last_os_error().raw_os_error();
let err = match err_code {
Some(libc::ESRCH) => "the target process did not exist.",
Some(libc::EPERM) => {
"the calling process does not have the permissions to terminate the target process(es)."
}
Some(libc::EINVAL) => "an invalid signal was specified.",
_ => "Unknown error occurred.",
};
if let Some(err_code) = err_code {
bail!(format!("Error code {err_code} - {err}"))
} else {
bail!(format!("Error code unknown - {err}"))
};
}
Ok(())
}