use std::ffi::OsString;
use std::io::{Error, Result};
use std::marker::PhantomData;
use std::mem;
use std::os::windows::ffi::OsStringExt;
use std::path::PathBuf;
use ntapi::ntpsapi;
use winapi::ctypes::wchar_t;
use winapi::shared::minwindef::{DWORD, FILETIME, MAX_PATH};
use winapi::shared::{ntstatus, winerror};
use winapi::um::{handleapi, processthreadsapi, psapi, winbase, winnt};
use heim_common::sys::IntoTime;
use heim_common::units::{time, Time};
use super::super::process::CpuTime;
use crate::Pid;
pub trait ProcessHandlePermissions {}
#[derive(Debug)]
pub struct QueryLimitedInformation;
impl ProcessHandlePermissions for QueryLimitedInformation {}
#[derive(Debug)]
pub struct Termination;
impl ProcessHandlePermissions for Termination {}
#[derive(Debug)]
pub struct SuspendResume;
impl ProcessHandlePermissions for SuspendResume {}
#[derive(Debug)]
pub struct ProcessHandle<T> {
handle: winnt::HANDLE,
_type: PhantomData<T>,
}
impl ProcessHandle<QueryLimitedInformation> {
pub fn query_limited_info(pid: Pid) -> Result<ProcessHandle<QueryLimitedInformation>> {
let handle = unsafe {
processthreadsapi::OpenProcess(
winnt::PROCESS_QUERY_LIMITED_INFORMATION | winnt::PROCESS_VM_READ,
0,
pid,
)
};
if handle.is_null() {
Err(Error::last_os_error())
} else {
Ok(ProcessHandle {
handle,
_type: PhantomData,
})
}
}
pub fn exit_code(&self) -> Result<DWORD> {
let mut code: DWORD = 0;
let result = unsafe {
processthreadsapi::GetExitCodeProcess(self.handle, &mut code)
};
if result == 0 {
Err(Error::last_os_error())
} else {
Ok(code)
}
}
pub fn exe(&self) -> Result<PathBuf> {
let mut buffer: [wchar_t; MAX_PATH] = [0; MAX_PATH];
let mut size = MAX_PATH as DWORD;
let result = unsafe {
winbase::QueryFullProcessImageNameW(self.handle, 0, buffer.as_mut_ptr(), &mut size)
};
if result == 0 {
Err(Error::last_os_error())
} else {
Ok(OsString::from_wide(&buffer[..(size as usize)]).into())
}
}
pub fn memory(&self) -> Result<psapi::PROCESS_MEMORY_COUNTERS_EX> {
let mut counters = mem::MaybeUninit::<psapi::PROCESS_MEMORY_COUNTERS_EX>::uninit();
let result = unsafe {
psapi::GetProcessMemoryInfo(
self.handle,
counters.as_mut_ptr() as *mut psapi::PROCESS_MEMORY_COUNTERS,
mem::size_of::<psapi::PROCESS_MEMORY_COUNTERS_EX>() as DWORD,
)
};
if result == 0 {
Err(Error::last_os_error())
} else {
unsafe { Ok(counters.assume_init()) }
}
}
pub fn cpu_time(&self) -> Result<CpuTime> {
let (_, _, kernel, user) = self.process_times()?;
Ok(CpuTime {
user: user.into_time(),
kernel: kernel.into_time(),
})
}
pub fn create_time(&self) -> Result<Time> {
let (creation, _, _, _) = self.process_times()?;
let unix_epoch_delta = Time::new::<time::second>(11_644_473_600.0);
Ok(creation.into_time() - unix_epoch_delta)
}
fn process_times(&self) -> Result<(FILETIME, FILETIME, FILETIME, FILETIME)> {
let mut creation = FILETIME::default();
let mut exit = FILETIME::default();
let mut kernel = FILETIME::default();
let mut user = FILETIME::default();
let result = unsafe {
processthreadsapi::GetProcessTimes(
self.handle,
&mut creation,
&mut exit,
&mut kernel,
&mut user,
)
};
if result == 0 {
Err(Error::last_os_error())
} else {
Ok((creation, exit, kernel, user))
}
}
}
impl ProcessHandle<Termination> {
pub fn for_termination(pid: Pid) -> Result<ProcessHandle<Termination>> {
let handle = unsafe { processthreadsapi::OpenProcess(winnt::PROCESS_TERMINATE, 0, pid) };
if handle.is_null() {
Err(Error::last_os_error())
} else {
Ok(ProcessHandle {
handle,
_type: PhantomData,
})
}
}
pub fn terminate(&self) -> Result<()> {
let result = unsafe {
processthreadsapi::TerminateProcess(
self.handle,
libc::SIGTERM as u32,
)
};
if result == 0 {
let e = Error::last_os_error();
if e.raw_os_error() == Some(winerror::ERROR_ACCESS_DENIED as i32) {
Ok(())
} else {
Err(e)
}
} else {
Ok(())
}
}
}
impl ProcessHandle<SuspendResume> {
pub fn for_suspend_resume(pid: Pid) -> Result<ProcessHandle<SuspendResume>> {
let handle =
unsafe { processthreadsapi::OpenProcess(winnt::PROCESS_SUSPEND_RESUME, 0, pid) };
if handle.is_null() {
Err(Error::last_os_error())
} else {
Ok(ProcessHandle {
handle,
_type: PhantomData,
})
}
}
pub fn suspend(&self) -> Result<()> {
let result = unsafe { ntpsapi::NtSuspendProcess(self.handle) };
if result != ntstatus::STATUS_SUCCESS {
Err(Error::last_os_error())
} else {
Ok(())
}
}
pub fn resume(&self) -> Result<()> {
let result = unsafe { ntpsapi::NtResumeProcess(self.handle) };
if result != ntstatus::STATUS_SUCCESS {
Err(Error::last_os_error())
} else {
Ok(())
}
}
}
impl<T> Drop for ProcessHandle<T> {
fn drop(&mut self) {
let result = unsafe { handleapi::CloseHandle(self.handle) };
debug_assert!(result != 0);
}
}