diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-17 12:02:58 +0000 |
commit | 698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch) | |
tree | 173a775858bd501c378080a10dca74132f05bc50 /vendor/sysinfo/src/windows/process.rs | |
parent | Initial commit. (diff) | |
download | rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip |
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vendor/sysinfo/src/windows/process.rs')
-rw-r--r-- | vendor/sysinfo/src/windows/process.rs | 1019 |
1 files changed, 1019 insertions, 0 deletions
diff --git a/vendor/sysinfo/src/windows/process.rs b/vendor/sysinfo/src/windows/process.rs new file mode 100644 index 000000000..bdc35c53e --- /dev/null +++ b/vendor/sysinfo/src/windows/process.rs @@ -0,0 +1,1019 @@ +// Take a look at the license at the top of the repository in the LICENSE file. + +use crate::sys::utils::to_str; +use crate::{DiskUsage, Gid, Pid, ProcessExt, ProcessRefreshKind, ProcessStatus, Signal, Uid}; + +use std::ffi::OsString; +use std::fmt; +use std::mem::{size_of, zeroed, MaybeUninit}; +use std::ops::Deref; +use std::os::windows::ffi::OsStringExt; +use std::os::windows::process::CommandExt; +use std::path::{Path, PathBuf}; +use std::process; +use std::ptr::null_mut; +use std::str; +use std::sync::Arc; + +use libc::{c_void, memcpy}; + +use ntapi::ntpebteb::PEB; +use ntapi::ntwow64::{PEB32, PRTL_USER_PROCESS_PARAMETERS32, RTL_USER_PROCESS_PARAMETERS32}; +use once_cell::sync::Lazy; + +use ntapi::ntpsapi::{ + NtQueryInformationProcess, ProcessBasicInformation, ProcessCommandLineInformation, + ProcessWow64Information, PROCESSINFOCLASS, PROCESS_BASIC_INFORMATION, +}; +use ntapi::ntrtl::{RtlGetVersion, PRTL_USER_PROCESS_PARAMETERS, RTL_USER_PROCESS_PARAMETERS}; +use winapi::shared::basetsd::SIZE_T; +use winapi::shared::minwindef::{DWORD, FALSE, FILETIME, LPVOID, MAX_PATH, TRUE, ULONG}; +use winapi::shared::ntdef::{NT_SUCCESS, UNICODE_STRING}; +use winapi::shared::ntstatus::{ + STATUS_BUFFER_OVERFLOW, STATUS_BUFFER_TOO_SMALL, STATUS_INFO_LENGTH_MISMATCH, +}; +use winapi::shared::winerror::ERROR_INSUFFICIENT_BUFFER; +use winapi::um::errhandlingapi::GetLastError; +use winapi::um::handleapi::CloseHandle; +use winapi::um::heapapi::{GetProcessHeap, HeapAlloc, HeapFree}; +use winapi::um::memoryapi::{ReadProcessMemory, VirtualQueryEx}; +use winapi::um::processthreadsapi::{ + GetProcessTimes, GetSystemTimes, OpenProcess, OpenProcessToken, +}; +use winapi::um::psapi::{ + EnumProcessModulesEx, GetModuleBaseNameW, GetModuleFileNameExW, GetProcessMemoryInfo, + LIST_MODULES_ALL, PROCESS_MEMORY_COUNTERS, PROCESS_MEMORY_COUNTERS_EX, +}; +use winapi::um::securitybaseapi::GetTokenInformation; +use winapi::um::winbase::{GetProcessIoCounters, CREATE_NO_WINDOW}; +use winapi::um::winnt::{ + TokenUser, HANDLE, HEAP_ZERO_MEMORY, IO_COUNTERS, MEMORY_BASIC_INFORMATION, + PROCESS_QUERY_INFORMATION, PROCESS_VM_READ, RTL_OSVERSIONINFOEXW, TOKEN_QUERY, TOKEN_USER, + ULARGE_INTEGER, +}; + +impl fmt::Display for ProcessStatus { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(match *self { + ProcessStatus::Run => "Runnable", + _ => "Unknown", + }) + } +} + +fn get_process_handler(pid: Pid) -> Option<HandleWrapper> { + if pid.0 == 0 { + return None; + } + let options = PROCESS_QUERY_INFORMATION | PROCESS_VM_READ; + + unsafe { HandleWrapper::new(OpenProcess(options, FALSE, pid.0 as DWORD)) } +} + +unsafe fn get_process_user_id( + handle: &HandleWrapper, + refresh_kind: ProcessRefreshKind, +) -> Option<Uid> { + struct HeapWrap<T>(*mut T); + + impl<T> HeapWrap<T> { + unsafe fn new(size: DWORD) -> Option<Self> { + let ptr = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, size as _) as *mut T; + if ptr.is_null() { + sysinfo_debug!("HeapAlloc failed"); + None + } else { + Some(Self(ptr)) + } + } + } + + impl<T> Drop for HeapWrap<T> { + fn drop(&mut self) { + if !self.0.is_null() { + unsafe { + HeapFree(GetProcessHeap(), 0, self.0 as *mut _); + } + } + } + } + + if !refresh_kind.user() { + return None; + } + + let mut token = null_mut(); + + if OpenProcessToken(**handle, TOKEN_QUERY, &mut token) == 0 { + sysinfo_debug!("OpenProcessToken failed"); + return None; + } + + let token = HandleWrapper::new(token)?; + + let mut size = 0; + + if GetTokenInformation(*token, TokenUser, null_mut(), 0, &mut size) == 0 { + let err = GetLastError(); + if err != ERROR_INSUFFICIENT_BUFFER { + sysinfo_debug!("GetTokenInformation failed, error: {:?}", err); + return None; + } + } + + let ptu: HeapWrap<TOKEN_USER> = HeapWrap::new(size)?; + + if GetTokenInformation(*token, TokenUser, ptu.0 as *mut _, size, &mut size) == 0 { + sysinfo_debug!("GetTokenInformation failed, error: {:?}", GetLastError()); + return None; + } + + let mut name_use = 0; + let mut name = [0u16; 256]; + let mut domain_name = [0u16; 256]; + let mut size = 256; + + if winapi::um::winbase::LookupAccountSidW( + std::ptr::null_mut(), + (*ptu.0).User.Sid, + name.as_mut_ptr(), + &mut size, + domain_name.as_mut_ptr(), + &mut size, + &mut name_use, + ) == 0 + { + sysinfo_debug!( + "LookupAccountSidW failed: {:?}", + winapi::um::errhandlingapi::GetLastError(), + ); + None + } else { + Some(Uid(to_str(name.as_mut_ptr()).into_boxed_str())) + } +} + +struct HandleWrapper(HANDLE); + +impl HandleWrapper { + fn new(handle: HANDLE) -> Option<Self> { + if handle.is_null() { + None + } else { + Some(Self(handle)) + } + } +} + +impl Deref for HandleWrapper { + type Target = HANDLE; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Drop for HandleWrapper { + fn drop(&mut self) { + unsafe { + CloseHandle(self.0); + } + } +} + +#[allow(clippy::non_send_fields_in_send_ty)] +unsafe impl Send for HandleWrapper {} +unsafe impl Sync for HandleWrapper {} + +#[doc = include_str!("../../md_doc/process.md")] +pub struct Process { + name: String, + cmd: Vec<String>, + exe: PathBuf, + pid: Pid, + user_id: Option<Uid>, + environ: Vec<String>, + cwd: PathBuf, + root: PathBuf, + pub(crate) memory: u64, + pub(crate) virtual_memory: u64, + parent: Option<Pid>, + status: ProcessStatus, + handle: Option<Arc<HandleWrapper>>, + cpu_calc_values: CPUsageCalculationValues, + start_time: u64, + pub(crate) run_time: u64, + cpu_usage: f32, + pub(crate) updated: bool, + old_read_bytes: u64, + old_written_bytes: u64, + read_bytes: u64, + written_bytes: u64, +} + +struct CPUsageCalculationValues { + old_process_sys_cpu: u64, + old_process_user_cpu: u64, + old_system_sys_cpu: u64, + old_system_user_cpu: u64, +} + +impl CPUsageCalculationValues { + fn new() -> Self { + CPUsageCalculationValues { + old_process_sys_cpu: 0, + old_process_user_cpu: 0, + old_system_sys_cpu: 0, + old_system_user_cpu: 0, + } + } +} +static WINDOWS_8_1_OR_NEWER: Lazy<bool> = Lazy::new(|| unsafe { + let mut version_info: RTL_OSVERSIONINFOEXW = MaybeUninit::zeroed().assume_init(); + + version_info.dwOSVersionInfoSize = std::mem::size_of::<RTL_OSVERSIONINFOEXW>() as u32; + if !NT_SUCCESS(RtlGetVersion( + &mut version_info as *mut RTL_OSVERSIONINFOEXW as *mut _, + )) { + return true; + } + + // Windows 8.1 is 6.3 + version_info.dwMajorVersion > 6 + || version_info.dwMajorVersion == 6 && version_info.dwMinorVersion >= 3 +}); + +unsafe fn get_process_name(process_handler: &HandleWrapper, h_mod: *mut c_void) -> String { + let mut process_name = [0u16; MAX_PATH + 1]; + + GetModuleBaseNameW( + **process_handler, + h_mod as _, + process_name.as_mut_ptr(), + MAX_PATH as DWORD + 1, + ); + null_terminated_wchar_to_string(&process_name) +} + +unsafe fn get_h_mod(process_handler: &HandleWrapper, h_mod: &mut *mut c_void) -> bool { + let mut cb_needed = 0; + EnumProcessModulesEx( + **process_handler, + h_mod as *mut *mut c_void as _, + size_of::<DWORD>() as DWORD, + &mut cb_needed, + LIST_MODULES_ALL, + ) != 0 +} + +unsafe fn get_exe(process_handler: &HandleWrapper, h_mod: *mut c_void) -> PathBuf { + let mut exe_buf = [0u16; MAX_PATH + 1]; + GetModuleFileNameExW( + **process_handler, + h_mod as _, + exe_buf.as_mut_ptr(), + MAX_PATH as DWORD + 1, + ); + + PathBuf::from(null_terminated_wchar_to_string(&exe_buf)) +} + +impl Process { + pub(crate) fn new_from_pid( + pid: Pid, + now: u64, + refresh_kind: ProcessRefreshKind, + ) -> Option<Process> { + unsafe { + let process_handler = get_process_handler(pid)?; + let mut info: MaybeUninit<PROCESS_BASIC_INFORMATION> = MaybeUninit::uninit(); + if NtQueryInformationProcess( + *process_handler, + ProcessBasicInformation, + info.as_mut_ptr() as *mut _, + size_of::<PROCESS_BASIC_INFORMATION>() as _, + null_mut(), + ) != 0 + { + return None; + } + let info = info.assume_init(); + let mut h_mod = null_mut(); + + let name = if get_h_mod(&process_handler, &mut h_mod) { + get_process_name(&process_handler, h_mod) + } else { + String::new() + }; + + let exe = get_exe(&process_handler, h_mod); + let mut root = exe.clone(); + root.pop(); + let (cmd, environ, cwd) = match get_process_params(&process_handler) { + Ok(args) => args, + Err(_e) => { + sysinfo_debug!("Failed to get process parameters: {}", _e); + (Vec::new(), Vec::new(), PathBuf::new()) + } + }; + let (start_time, run_time) = get_start_and_run_time(&process_handler, now); + let parent = if info.InheritedFromUniqueProcessId as usize != 0 { + Some(Pid(info.InheritedFromUniqueProcessId as _)) + } else { + None + }; + let user_id = get_process_user_id(&process_handler, refresh_kind); + Some(Process { + handle: Some(Arc::new(process_handler)), + name, + pid, + parent, + user_id, + cmd, + environ, + exe, + cwd, + root, + status: ProcessStatus::Run, + memory: 0, + virtual_memory: 0, + cpu_usage: 0., + cpu_calc_values: CPUsageCalculationValues::new(), + start_time, + run_time, + updated: true, + old_read_bytes: 0, + old_written_bytes: 0, + read_bytes: 0, + written_bytes: 0, + }) + } + } + + pub(crate) fn new_full( + pid: Pid, + parent: Option<Pid>, + memory: u64, + virtual_memory: u64, + name: String, + now: u64, + refresh_kind: ProcessRefreshKind, + ) -> Process { + if let Some(handle) = get_process_handler(pid) { + let mut h_mod = null_mut(); + + unsafe { + let exe = if get_h_mod(&handle, &mut h_mod) { + get_exe(&handle, h_mod) + } else { + PathBuf::new() + }; + let mut root = exe.clone(); + root.pop(); + let (cmd, environ, cwd) = match get_process_params(&handle) { + Ok(args) => args, + Err(_e) => { + sysinfo_debug!("Failed to get process parameters: {}", _e); + (Vec::new(), Vec::new(), PathBuf::new()) + } + }; + let (start_time, run_time) = get_start_and_run_time(&handle, now); + let user_id = get_process_user_id(&handle, refresh_kind); + Process { + handle: Some(Arc::new(handle)), + name, + pid, + user_id, + parent, + cmd, + environ, + exe, + cwd, + root, + status: ProcessStatus::Run, + memory, + virtual_memory, + cpu_usage: 0., + cpu_calc_values: CPUsageCalculationValues::new(), + start_time, + run_time, + updated: true, + old_read_bytes: 0, + old_written_bytes: 0, + read_bytes: 0, + written_bytes: 0, + } + } + } else { + Process { + handle: None, + name, + pid, + user_id: None, + parent, + cmd: Vec::new(), + environ: Vec::new(), + exe: get_executable_path(pid), + cwd: PathBuf::new(), + root: PathBuf::new(), + status: ProcessStatus::Run, + memory, + virtual_memory, + cpu_usage: 0., + cpu_calc_values: CPUsageCalculationValues::new(), + start_time: 0, + run_time: 0, + updated: true, + old_read_bytes: 0, + old_written_bytes: 0, + read_bytes: 0, + written_bytes: 0, + } + } + } + + pub(crate) fn update( + &mut self, + refresh_kind: crate::ProcessRefreshKind, + nb_cpus: u64, + now: u64, + ) { + if refresh_kind.cpu() { + compute_cpu_usage(self, nb_cpus); + } + if refresh_kind.disk_usage() { + update_disk_usage(self); + } + self.run_time = now.saturating_sub(self.start_time()); + self.updated = true; + } + + pub(crate) fn get_handle(&self) -> Option<HANDLE> { + self.handle.as_ref().map(|h| ***h) + } +} + +impl ProcessExt for Process { + fn kill_with(&self, signal: Signal) -> Option<bool> { + super::system::convert_signal(signal)?; + let mut kill = process::Command::new("taskkill.exe"); + kill.arg("/PID").arg(self.pid.to_string()).arg("/F"); + kill.creation_flags(CREATE_NO_WINDOW); + match kill.output() { + Ok(o) => Some(o.status.success()), + Err(_) => Some(false), + } + } + + fn name(&self) -> &str { + &self.name + } + + fn cmd(&self) -> &[String] { + &self.cmd + } + + fn exe(&self) -> &Path { + self.exe.as_path() + } + + fn pid(&self) -> Pid { + self.pid + } + + fn environ(&self) -> &[String] { + &self.environ + } + + fn cwd(&self) -> &Path { + self.cwd.as_path() + } + + fn root(&self) -> &Path { + self.root.as_path() + } + + fn memory(&self) -> u64 { + self.memory + } + + fn virtual_memory(&self) -> u64 { + self.virtual_memory + } + + fn parent(&self) -> Option<Pid> { + self.parent + } + + fn status(&self) -> ProcessStatus { + self.status + } + + fn start_time(&self) -> u64 { + self.start_time + } + + fn run_time(&self) -> u64 { + self.run_time + } + + fn cpu_usage(&self) -> f32 { + self.cpu_usage + } + + fn disk_usage(&self) -> DiskUsage { + DiskUsage { + written_bytes: self.written_bytes - self.old_written_bytes, + total_written_bytes: self.written_bytes, + read_bytes: self.read_bytes - self.old_read_bytes, + total_read_bytes: self.read_bytes, + } + } + + fn user_id(&self) -> Option<&Uid> { + self.user_id.as_ref() + } + + fn group_id(&self) -> Option<Gid> { + None + } +} + +unsafe fn get_start_and_run_time(handle: &HandleWrapper, now: u64) -> (u64, u64) { + let mut fstart: FILETIME = zeroed(); + let mut x = zeroed(); + + GetProcessTimes( + **handle, + &mut fstart as *mut FILETIME, + &mut x as *mut FILETIME, + &mut x as *mut FILETIME, + &mut x as *mut FILETIME, + ); + let tmp = super::utils::filetime_to_u64(fstart); + // 11_644_473_600 is the number of seconds between the Windows epoch (1601-01-01) and + // the linux epoch (1970-01-01). + let start = tmp / 10_000_000 - 11_644_473_600; + let run_time = check_sub(now, start); + (start, run_time) +} + +#[allow(clippy::uninit_vec)] +unsafe fn ph_query_process_variable_size( + process_handle: &HandleWrapper, + process_information_class: PROCESSINFOCLASS, +) -> Option<Vec<u16>> { + let mut return_length = MaybeUninit::<ULONG>::uninit(); + + let mut status = NtQueryInformationProcess( + **process_handle, + process_information_class, + null_mut(), + 0, + return_length.as_mut_ptr() as *mut _, + ); + + if status != STATUS_BUFFER_OVERFLOW + && status != STATUS_BUFFER_TOO_SMALL + && status != STATUS_INFO_LENGTH_MISMATCH + { + return None; + } + + let mut return_length = return_length.assume_init(); + let buf_len = (return_length as usize) / 2; + let mut buffer: Vec<u16> = Vec::with_capacity(buf_len + 1); + buffer.set_len(buf_len); + + status = NtQueryInformationProcess( + **process_handle, + process_information_class, + buffer.as_mut_ptr() as *mut _, + return_length, + &mut return_length as *mut _, + ); + if !NT_SUCCESS(status) { + return None; + } + buffer.push(0); + Some(buffer) +} + +unsafe fn get_cmdline_from_buffer(buffer: *const u16) -> Vec<String> { + // Get argc and argv from the command line + let mut argc = MaybeUninit::<i32>::uninit(); + let argv_p = winapi::um::shellapi::CommandLineToArgvW(buffer, argc.as_mut_ptr()); + if argv_p.is_null() { + return Vec::new(); + } + let argc = argc.assume_init(); + let argv = std::slice::from_raw_parts(argv_p, argc as usize); + + let mut res = Vec::new(); + for arg in argv { + let len = libc::wcslen(*arg); + let str_slice = std::slice::from_raw_parts(*arg, len); + res.push(String::from_utf16_lossy(str_slice)); + } + + winapi::um::winbase::LocalFree(argv_p as *mut _); + + res +} + +unsafe fn get_region_size(handle: &HandleWrapper, ptr: LPVOID) -> Result<usize, &'static str> { + let mut meminfo = MaybeUninit::<MEMORY_BASIC_INFORMATION>::uninit(); + if VirtualQueryEx( + **handle, + ptr, + meminfo.as_mut_ptr() as *mut _, + size_of::<MEMORY_BASIC_INFORMATION>(), + ) == 0 + { + return Err("Unable to read process memory information"); + } + let meminfo = meminfo.assume_init(); + Ok((meminfo.RegionSize as isize - ptr.offset_from(meminfo.BaseAddress)) as usize) +} + +#[allow(clippy::uninit_vec)] +unsafe fn get_process_data( + handle: &HandleWrapper, + ptr: LPVOID, + size: usize, +) -> Result<Vec<u16>, &'static str> { + let mut buffer: Vec<u16> = Vec::with_capacity(size / 2 + 1); + buffer.set_len(size / 2); + if ReadProcessMemory( + **handle, + ptr as *mut _, + buffer.as_mut_ptr() as *mut _, + size, + null_mut(), + ) != TRUE + { + return Err("Unable to read process data"); + } + Ok(buffer) +} + +trait RtlUserProcessParameters { + fn get_cmdline(&self, handle: &HandleWrapper) -> Result<Vec<u16>, &'static str>; + fn get_cwd(&self, handle: &HandleWrapper) -> Result<Vec<u16>, &'static str>; + fn get_environ(&self, handle: &HandleWrapper) -> Result<Vec<u16>, &'static str>; +} + +macro_rules! impl_RtlUserProcessParameters { + ($t:ty) => { + impl RtlUserProcessParameters for $t { + fn get_cmdline(&self, handle: &HandleWrapper) -> Result<Vec<u16>, &'static str> { + let ptr = self.CommandLine.Buffer; + let size = self.CommandLine.Length; + unsafe { get_process_data(handle, ptr as _, size as _) } + } + fn get_cwd(&self, handle: &HandleWrapper) -> Result<Vec<u16>, &'static str> { + let ptr = self.CurrentDirectory.DosPath.Buffer; + let size = self.CurrentDirectory.DosPath.Length; + unsafe { get_process_data(handle, ptr as _, size as _) } + } + fn get_environ(&self, handle: &HandleWrapper) -> Result<Vec<u16>, &'static str> { + let ptr = self.Environment; + unsafe { + let size = get_region_size(handle, ptr as LPVOID)?; + get_process_data(handle, ptr as _, size as _) + } + } + } + }; +} + +impl_RtlUserProcessParameters!(RTL_USER_PROCESS_PARAMETERS32); +impl_RtlUserProcessParameters!(RTL_USER_PROCESS_PARAMETERS); + +unsafe fn get_process_params( + handle: &HandleWrapper, +) -> Result<(Vec<String>, Vec<String>, PathBuf), &'static str> { + if !cfg!(target_pointer_width = "64") { + return Err("Non 64 bit targets are not supported"); + } + + // First check if target process is running in wow64 compatibility emulator + let mut pwow32info = MaybeUninit::<LPVOID>::uninit(); + let result = NtQueryInformationProcess( + **handle, + ProcessWow64Information, + pwow32info.as_mut_ptr() as *mut _, + size_of::<LPVOID>() as u32, + null_mut(), + ); + if !NT_SUCCESS(result) { + return Err("Unable to check WOW64 information about the process"); + } + let pwow32info = pwow32info.assume_init(); + + if pwow32info.is_null() { + // target is a 64 bit process + + let mut pbasicinfo = MaybeUninit::<PROCESS_BASIC_INFORMATION>::uninit(); + let result = NtQueryInformationProcess( + **handle, + ProcessBasicInformation, + pbasicinfo.as_mut_ptr() as *mut _, + size_of::<PROCESS_BASIC_INFORMATION>() as u32, + null_mut(), + ); + if !NT_SUCCESS(result) { + return Err("Unable to get basic process information"); + } + let pinfo = pbasicinfo.assume_init(); + + let mut peb = MaybeUninit::<PEB>::uninit(); + if ReadProcessMemory( + **handle, + pinfo.PebBaseAddress as *mut _, + peb.as_mut_ptr() as *mut _, + size_of::<PEB>() as SIZE_T, + null_mut(), + ) != TRUE + { + return Err("Unable to read process PEB"); + } + + let peb = peb.assume_init(); + + let mut proc_params = MaybeUninit::<RTL_USER_PROCESS_PARAMETERS>::uninit(); + if ReadProcessMemory( + **handle, + peb.ProcessParameters as *mut PRTL_USER_PROCESS_PARAMETERS as *mut _, + proc_params.as_mut_ptr() as *mut _, + size_of::<RTL_USER_PROCESS_PARAMETERS>() as SIZE_T, + null_mut(), + ) != TRUE + { + return Err("Unable to read process parameters"); + } + + let proc_params = proc_params.assume_init(); + return Ok(( + get_cmd_line(&proc_params, handle), + get_proc_env(&proc_params, handle), + get_cwd(&proc_params, handle), + )); + } + // target is a 32 bit process in wow64 mode + + let mut peb32 = MaybeUninit::<PEB32>::uninit(); + if ReadProcessMemory( + **handle, + pwow32info, + peb32.as_mut_ptr() as *mut _, + size_of::<PEB32>() as SIZE_T, + null_mut(), + ) != TRUE + { + return Err("Unable to read PEB32"); + } + let peb32 = peb32.assume_init(); + + let mut proc_params = MaybeUninit::<RTL_USER_PROCESS_PARAMETERS32>::uninit(); + if ReadProcessMemory( + **handle, + peb32.ProcessParameters as *mut PRTL_USER_PROCESS_PARAMETERS32 as *mut _, + proc_params.as_mut_ptr() as *mut _, + size_of::<RTL_USER_PROCESS_PARAMETERS32>() as SIZE_T, + null_mut(), + ) != TRUE + { + return Err("Unable to read 32 bit process parameters"); + } + let proc_params = proc_params.assume_init(); + Ok(( + get_cmd_line(&proc_params, handle), + get_proc_env(&proc_params, handle), + get_cwd(&proc_params, handle), + )) +} + +fn get_cwd<T: RtlUserProcessParameters>(params: &T, handle: &HandleWrapper) -> PathBuf { + match params.get_cwd(handle) { + Ok(buffer) => unsafe { PathBuf::from(null_terminated_wchar_to_string(buffer.as_slice())) }, + Err(_e) => { + sysinfo_debug!("get_cwd failed to get data: {}", _e); + PathBuf::new() + } + } +} + +unsafe fn null_terminated_wchar_to_string(slice: &[u16]) -> String { + match slice.iter().position(|&x| x == 0) { + Some(pos) => OsString::from_wide(&slice[..pos]) + .to_string_lossy() + .into_owned(), + None => OsString::from_wide(slice).to_string_lossy().into_owned(), + } +} + +fn get_cmd_line_old<T: RtlUserProcessParameters>( + params: &T, + handle: &HandleWrapper, +) -> Vec<String> { + match params.get_cmdline(handle) { + Ok(buffer) => unsafe { get_cmdline_from_buffer(buffer.as_ptr()) }, + Err(_e) => { + sysinfo_debug!("get_cmd_line_old failed to get data: {}", _e); + Vec::new() + } + } +} + +#[allow(clippy::cast_ptr_alignment)] +fn get_cmd_line_new(handle: &HandleWrapper) -> Vec<String> { + unsafe { + if let Some(buffer) = ph_query_process_variable_size(handle, ProcessCommandLineInformation) + { + let buffer = (*(buffer.as_ptr() as *const UNICODE_STRING)).Buffer; + + get_cmdline_from_buffer(buffer) + } else { + vec![] + } + } +} + +fn get_cmd_line<T: RtlUserProcessParameters>(params: &T, handle: &HandleWrapper) -> Vec<String> { + if *WINDOWS_8_1_OR_NEWER { + get_cmd_line_new(handle) + } else { + get_cmd_line_old(params, handle) + } +} + +fn get_proc_env<T: RtlUserProcessParameters>(params: &T, handle: &HandleWrapper) -> Vec<String> { + match params.get_environ(handle) { + Ok(buffer) => { + let equals = "=".encode_utf16().next().unwrap(); + let raw_env = buffer; + let mut result = Vec::new(); + let mut begin = 0; + while let Some(offset) = raw_env[begin..].iter().position(|&c| c == 0) { + let end = begin + offset; + if raw_env[begin..end].iter().any(|&c| c == equals) { + result.push( + OsString::from_wide(&raw_env[begin..end]) + .to_string_lossy() + .into_owned(), + ); + begin = end + 1; + } else { + break; + } + } + result + } + Err(_e) => { + sysinfo_debug!("get_proc_env failed to get data: {}", _e); + Vec::new() + } + } +} + +pub(crate) fn get_executable_path(_pid: Pid) -> PathBuf { + /*let where_req = format!("ProcessId={}", pid); + + if let Some(ret) = run_wmi(&["process", "where", &where_req, "get", "ExecutablePath"]) { + for line in ret.lines() { + if line.is_empty() || line == "ExecutablePath" { + continue + } + return line.to_owned(); + } + }*/ + PathBuf::new() +} + +#[inline] +fn check_sub(a: u64, b: u64) -> u64 { + if a < b { + a + } else { + a - b + } +} + +/// Before changing this function, you must consider the following: +/// https://github.com/GuillaumeGomez/sysinfo/issues/459 +pub(crate) fn compute_cpu_usage(p: &mut Process, nb_cpus: u64) { + unsafe { + let mut ftime: FILETIME = zeroed(); + let mut fsys: FILETIME = zeroed(); + let mut fuser: FILETIME = zeroed(); + let mut fglobal_idle_time: FILETIME = zeroed(); + let mut fglobal_kernel_time: FILETIME = zeroed(); // notice that it includes idle time + let mut fglobal_user_time: FILETIME = zeroed(); + + if let Some(handle) = p.get_handle() { + GetProcessTimes( + handle, + &mut ftime as *mut FILETIME, + &mut ftime as *mut FILETIME, + &mut fsys as *mut FILETIME, + &mut fuser as *mut FILETIME, + ); + } + GetSystemTimes( + &mut fglobal_idle_time as *mut FILETIME, + &mut fglobal_kernel_time as *mut FILETIME, + &mut fglobal_user_time as *mut FILETIME, + ); + + let mut sys: ULARGE_INTEGER = std::mem::zeroed(); + memcpy( + &mut sys as *mut ULARGE_INTEGER as *mut c_void, + &mut fsys as *mut FILETIME as *mut c_void, + size_of::<FILETIME>(), + ); + let mut user: ULARGE_INTEGER = std::mem::zeroed(); + memcpy( + &mut user as *mut ULARGE_INTEGER as *mut c_void, + &mut fuser as *mut FILETIME as *mut c_void, + size_of::<FILETIME>(), + ); + let mut global_kernel_time: ULARGE_INTEGER = std::mem::zeroed(); + memcpy( + &mut global_kernel_time as *mut ULARGE_INTEGER as *mut c_void, + &mut fglobal_kernel_time as *mut FILETIME as *mut c_void, + size_of::<FILETIME>(), + ); + let mut global_user_time: ULARGE_INTEGER = std::mem::zeroed(); + memcpy( + &mut global_user_time as *mut ULARGE_INTEGER as *mut c_void, + &mut fglobal_user_time as *mut FILETIME as *mut c_void, + size_of::<FILETIME>(), + ); + + let sys = *sys.QuadPart(); + let user = *user.QuadPart(); + let global_kernel_time = *global_kernel_time.QuadPart(); + let global_user_time = *global_user_time.QuadPart(); + + let delta_global_kernel_time = + check_sub(global_kernel_time, p.cpu_calc_values.old_system_sys_cpu); + let delta_global_user_time = + check_sub(global_user_time, p.cpu_calc_values.old_system_user_cpu); + let delta_user_time = check_sub(user, p.cpu_calc_values.old_process_user_cpu); + let delta_sys_time = check_sub(sys, p.cpu_calc_values.old_process_sys_cpu); + + p.cpu_calc_values.old_process_user_cpu = user; + p.cpu_calc_values.old_process_sys_cpu = sys; + p.cpu_calc_values.old_system_user_cpu = global_user_time; + p.cpu_calc_values.old_system_sys_cpu = global_kernel_time; + + let denominator = delta_global_user_time.saturating_add(delta_global_kernel_time) as f32; + + if denominator < 0.00001 { + p.cpu_usage = 0.; + return; + } + + p.cpu_usage = 100.0 + * (delta_user_time.saturating_add(delta_sys_time) as f32 / denominator as f32) + * nb_cpus as f32; + } +} + +pub(crate) fn update_disk_usage(p: &mut Process) { + let mut counters = MaybeUninit::<IO_COUNTERS>::uninit(); + + if let Some(handle) = p.get_handle() { + unsafe { + let ret = GetProcessIoCounters(handle, counters.as_mut_ptr()); + if ret == 0 { + sysinfo_debug!("GetProcessIoCounters call failed on process {}", p.pid()); + } else { + let counters = counters.assume_init(); + p.old_read_bytes = p.read_bytes; + p.old_written_bytes = p.written_bytes; + p.read_bytes = counters.ReadTransferCount; + p.written_bytes = counters.WriteTransferCount; + } + } + } +} + +pub(crate) fn update_memory(p: &mut Process) { + if let Some(handle) = p.get_handle() { + unsafe { + let mut pmc: PROCESS_MEMORY_COUNTERS_EX = zeroed(); + if GetProcessMemoryInfo( + handle, + &mut pmc as *mut PROCESS_MEMORY_COUNTERS_EX as *mut c_void + as *mut PROCESS_MEMORY_COUNTERS, + size_of::<PROCESS_MEMORY_COUNTERS_EX>() as DWORD, + ) != 0 + { + p.memory = (pmc.WorkingSetSize as u64) / 1_000; + p.virtual_memory = (pmc.PrivateUsage as u64) / 1_000; + } + } + } +} |