summaryrefslogtreecommitdiffstats
path: root/vendor/sysinfo/src/windows
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
commit698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch)
tree173a775858bd501c378080a10dca74132f05bc50 /vendor/sysinfo/src/windows
parentInitial commit. (diff)
downloadrustc-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')
-rw-r--r--vendor/sysinfo/src/windows/component.rs374
-rw-r--r--vendor/sysinfo/src/windows/cpu.rs548
-rw-r--r--vendor/sysinfo/src/windows/disk.rs249
-rw-r--r--vendor/sysinfo/src/windows/macros.rs16
-rw-r--r--vendor/sysinfo/src/windows/mod.rs20
-rw-r--r--vendor/sysinfo/src/windows/network.rs249
-rw-r--r--vendor/sysinfo/src/windows/process.rs1019
-rw-r--r--vendor/sysinfo/src/windows/system.rs623
-rw-r--r--vendor/sysinfo/src/windows/tools.rs55
-rw-r--r--vendor/sysinfo/src/windows/users.rs181
-rw-r--r--vendor/sysinfo/src/windows/utils.rs36
11 files changed, 3370 insertions, 0 deletions
diff --git a/vendor/sysinfo/src/windows/component.rs b/vendor/sysinfo/src/windows/component.rs
new file mode 100644
index 000000000..502594e38
--- /dev/null
+++ b/vendor/sysinfo/src/windows/component.rs
@@ -0,0 +1,374 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::ComponentExt;
+
+use std::ptr::null_mut;
+
+use winapi::shared::rpcdce::{
+ RPC_C_AUTHN_LEVEL_CALL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE,
+ RPC_C_IMP_LEVEL_IMPERSONATE,
+};
+use winapi::shared::winerror::{FAILED, SUCCEEDED, S_FALSE, S_OK};
+use winapi::shared::wtypesbase::CLSCTX_INPROC_SERVER;
+use winapi::um::combaseapi::{
+ CoCreateInstance, CoInitializeEx, CoInitializeSecurity, CoSetProxyBlanket, CoUninitialize,
+};
+use winapi::um::oaidl::VARIANT;
+use winapi::um::objidl::EOAC_NONE;
+use winapi::um::oleauto::{SysAllocString, SysFreeString, VariantClear};
+use winapi::um::wbemcli::{
+ CLSID_WbemLocator, IEnumWbemClassObject, IID_IWbemLocator, IWbemClassObject, IWbemLocator,
+ IWbemServices, WBEM_FLAG_FORWARD_ONLY, WBEM_FLAG_NONSYSTEM_ONLY, WBEM_FLAG_RETURN_IMMEDIATELY,
+};
+
+#[doc = include_str!("../../md_doc/component.md")]
+pub struct Component {
+ temperature: f32,
+ max: f32,
+ critical: Option<f32>,
+ label: String,
+ connection: Option<Connection>,
+}
+
+impl Component {
+ /// Creates a new `Component` with the given information.
+ fn new() -> Option<Component> {
+ let mut c = Connection::new()
+ .and_then(|x| x.initialize_security())
+ .and_then(|x| x.create_instance())
+ .and_then(|x| x.connect_server())
+ .and_then(|x| x.set_proxy_blanket())
+ .and_then(|x| x.exec_query())?;
+
+ c.temperature(true)
+ .map(|(temperature, critical)| Component {
+ temperature,
+ label: "Computer".to_owned(),
+ max: temperature,
+ critical,
+ connection: Some(c),
+ })
+ }
+}
+
+impl ComponentExt for Component {
+ fn temperature(&self) -> f32 {
+ self.temperature
+ }
+
+ fn max(&self) -> f32 {
+ self.max
+ }
+
+ fn critical(&self) -> Option<f32> {
+ self.critical
+ }
+
+ fn label(&self) -> &str {
+ &self.label
+ }
+
+ fn refresh(&mut self) {
+ if self.connection.is_none() {
+ self.connection = Connection::new()
+ .and_then(|x| x.initialize_security())
+ .and_then(|x| x.create_instance())
+ .and_then(|x| x.connect_server())
+ .and_then(|x| x.set_proxy_blanket());
+ }
+ self.connection = if let Some(x) = self.connection.take() {
+ x.exec_query()
+ } else {
+ None
+ };
+ if let Some(ref mut connection) = self.connection {
+ if let Some((temperature, _)) = connection.temperature(false) {
+ self.temperature = temperature;
+ if self.temperature > self.max {
+ self.max = self.temperature;
+ }
+ }
+ }
+ }
+}
+
+pub(crate) fn get_components() -> Vec<Component> {
+ match Component::new() {
+ Some(c) => vec![c],
+ None => Vec::new(),
+ }
+}
+
+struct Instance(*mut IWbemLocator);
+
+impl Drop for Instance {
+ fn drop(&mut self) {
+ if !self.0.is_null() {
+ unsafe {
+ (*self.0).Release();
+ }
+ }
+ }
+}
+
+struct ServerConnection(*mut IWbemServices);
+
+impl Drop for ServerConnection {
+ fn drop(&mut self) {
+ if !self.0.is_null() {
+ unsafe {
+ (*self.0).Release();
+ }
+ }
+ }
+}
+
+struct Enumerator(*mut IEnumWbemClassObject);
+
+impl Drop for Enumerator {
+ fn drop(&mut self) {
+ if !self.0.is_null() {
+ unsafe {
+ (*self.0).Release();
+ }
+ }
+ }
+}
+
+macro_rules! bstr {
+ ($($x:expr),*) => {{
+ let x: &[u16] = &[$($x as u16),*, 0];
+ SysAllocString(x.as_ptr())
+ }}
+}
+
+struct Connection {
+ instance: Option<Instance>,
+ server_connection: Option<ServerConnection>,
+ enumerator: Option<Enumerator>,
+ initialized: bool,
+}
+
+#[allow(clippy::non_send_fields_in_send_ty)]
+unsafe impl Send for Connection {}
+unsafe impl Sync for Connection {}
+
+impl Connection {
+ #[allow(clippy::unnecessary_wraps)]
+ fn new() -> Option<Connection> {
+ unsafe {
+ let val = CoInitializeEx(null_mut(), 0);
+ Some(Connection {
+ instance: None,
+ server_connection: None,
+ enumerator: None,
+ initialized: val == S_OK || val == S_FALSE,
+ })
+ }
+ }
+
+ fn initialize_security(self) -> Option<Connection> {
+ unsafe {
+ if FAILED(CoInitializeSecurity(
+ null_mut(),
+ -1,
+ null_mut(),
+ null_mut(),
+ RPC_C_AUTHN_LEVEL_DEFAULT,
+ RPC_C_IMP_LEVEL_IMPERSONATE,
+ null_mut(),
+ EOAC_NONE,
+ null_mut(),
+ )) {
+ None
+ } else {
+ Some(self)
+ }
+ }
+ }
+
+ fn create_instance(mut self) -> Option<Connection> {
+ let mut p_loc = null_mut();
+
+ unsafe {
+ if FAILED(CoCreateInstance(
+ &CLSID_WbemLocator as *const _,
+ null_mut(),
+ CLSCTX_INPROC_SERVER,
+ &IID_IWbemLocator as *const _,
+ &mut p_loc as *mut _ as *mut _,
+ )) {
+ None
+ } else {
+ self.instance = Some(Instance(p_loc));
+ Some(self)
+ }
+ }
+ }
+
+ fn connect_server(mut self) -> Option<Connection> {
+ let mut p_svc = null_mut();
+
+ if let Some(ref instance) = self.instance {
+ unsafe {
+ // "root\WMI"
+ let s = bstr!('r', 'o', 'o', 't', '\\', 'W', 'M', 'I');
+ let res = (*instance.0).ConnectServer(
+ s,
+ null_mut(),
+ null_mut(),
+ null_mut(),
+ 0,
+ null_mut(),
+ null_mut(),
+ &mut p_svc as *mut _,
+ );
+ SysFreeString(s);
+ if FAILED(res) {
+ return None;
+ }
+ }
+ } else {
+ return None;
+ }
+ self.server_connection = Some(ServerConnection(p_svc));
+ Some(self)
+ }
+
+ fn set_proxy_blanket(self) -> Option<Connection> {
+ if let Some(ref server_connection) = self.server_connection {
+ unsafe {
+ if FAILED(CoSetProxyBlanket(
+ server_connection.0 as *mut _,
+ RPC_C_AUTHN_WINNT,
+ RPC_C_AUTHZ_NONE,
+ null_mut(),
+ RPC_C_AUTHN_LEVEL_CALL,
+ RPC_C_IMP_LEVEL_IMPERSONATE,
+ null_mut(),
+ EOAC_NONE,
+ )) {
+ return None;
+ }
+ }
+ } else {
+ return None;
+ }
+ Some(self)
+ }
+
+ fn exec_query(mut self) -> Option<Connection> {
+ let mut p_enumerator = null_mut();
+
+ if let Some(ref server_connection) = self.server_connection {
+ unsafe {
+ // "WQL"
+ let s = bstr!('W', 'Q', 'L'); // query kind
+ // "SELECT * FROM MSAcpi_ThermalZoneTemperature"
+ let query = bstr!(
+ 'S', 'E', 'L', 'E', 'C', 'T', ' ', '*', ' ', 'F', 'R', 'O', 'M', ' ', 'M', 'S',
+ 'A', 'c', 'p', 'i', '_', 'T', 'h', 'e', 'r', 'm', 'a', 'l', 'Z', 'o', 'n', 'e',
+ 'T', 'e', 'm', 'p', 'e', 'r', 'a', 't', 'u', 'r', 'e'
+ );
+ let hres = (*server_connection.0).ExecQuery(
+ s,
+ query,
+ (WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY) as _,
+ null_mut(),
+ &mut p_enumerator as *mut _,
+ );
+ SysFreeString(s);
+ SysFreeString(query);
+ if FAILED(hres) {
+ return None;
+ }
+ }
+ } else {
+ return None;
+ }
+ self.enumerator = Some(Enumerator(p_enumerator));
+ Some(self)
+ }
+
+ fn temperature(&mut self, get_critical: bool) -> Option<(f32, Option<f32>)> {
+ use winapi::um::wbemcli::WBEM_INFINITE;
+
+ let p_enum = match self.enumerator.take() {
+ Some(x) => x,
+ None => {
+ return None;
+ }
+ };
+ let mut p_obj: *mut IWbemClassObject = null_mut();
+ let mut nb_returned = 0;
+
+ unsafe {
+ (*p_enum.0).Next(
+ WBEM_INFINITE as _, // Time out
+ 1, // One object
+ &mut p_obj as *mut _,
+ &mut nb_returned,
+ );
+
+ if nb_returned == 0 {
+ return None; // not enough rights I suppose...
+ }
+
+ (*p_obj).BeginEnumeration(WBEM_FLAG_NONSYSTEM_ONLY as _);
+
+ let mut p_val = std::mem::MaybeUninit::<VARIANT>::uninit();
+ // "CurrentTemperature"
+ let temp = bstr!(
+ 'C', 'u', 'r', 'r', 'e', 'n', 't', 'T', 'e', 'm', 'p', 'e', 'r', 'a', 't', 'u',
+ 'r', 'e'
+ );
+ let res = (*p_obj).Get(temp, 0, p_val.as_mut_ptr(), null_mut(), null_mut());
+ let mut p_val = p_val.assume_init();
+
+ SysFreeString(temp);
+ VariantClear(&mut p_val as *mut _ as *mut _);
+
+ let temp = if SUCCEEDED(res) {
+ // temperature is given in tenth of degrees Kelvin
+ (p_val.n1.decVal().Lo64 / 10) as f32 - 273.15
+ } else {
+ (*p_obj).Release();
+ return None;
+ };
+
+ let mut critical = None;
+ if get_critical {
+ // "CriticalPoint"
+ let crit = bstr!(
+ 'C', 'r', 'i', 't', 'i', 'c', 'a', 'l', 'T', 'r', 'i', 'p', 'P', 'o', 'i', 'n',
+ 't'
+ );
+ let res = (*p_obj).Get(crit, 0, &mut p_val, null_mut(), null_mut());
+
+ SysFreeString(crit);
+ VariantClear(&mut p_val as *mut _ as *mut _);
+
+ if SUCCEEDED(res) {
+ // temperature is given in tenth of degrees Kelvin
+ critical = Some((p_val.n1.decVal().Lo64 / 10) as f32 - 273.15);
+ }
+ }
+ (*p_obj).Release();
+ Some((temp, critical))
+ }
+ }
+}
+
+impl Drop for Connection {
+ fn drop(&mut self) {
+ // Those three calls are here to enforce that they get dropped in the good order.
+ self.enumerator.take();
+ self.server_connection.take();
+ self.instance.take();
+ if self.initialized {
+ unsafe {
+ CoUninitialize();
+ }
+ }
+ }
+}
diff --git a/vendor/sysinfo/src/windows/cpu.rs b/vendor/sysinfo/src/windows/cpu.rs
new file mode 100644
index 000000000..bbaa27ad7
--- /dev/null
+++ b/vendor/sysinfo/src/windows/cpu.rs
@@ -0,0 +1,548 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::sys::tools::KeyHandler;
+use crate::{CpuExt, CpuRefreshKind, LoadAvg};
+
+use std::collections::HashMap;
+use std::io::Error;
+use std::mem;
+use std::ops::DerefMut;
+use std::ptr::null_mut;
+use std::sync::Mutex;
+
+use ntapi::ntpoapi::PROCESSOR_POWER_INFORMATION;
+
+use winapi::shared::minwindef::FALSE;
+use winapi::shared::winerror::{ERROR_INSUFFICIENT_BUFFER, ERROR_SUCCESS};
+use winapi::um::handleapi::CloseHandle;
+use winapi::um::pdh::{
+ PdhAddEnglishCounterA, PdhAddEnglishCounterW, PdhCloseQuery, PdhCollectQueryData,
+ PdhCollectQueryDataEx, PdhGetFormattedCounterValue, PdhOpenQueryA, PdhRemoveCounter,
+ PDH_FMT_COUNTERVALUE, PDH_FMT_DOUBLE, PDH_HCOUNTER, PDH_HQUERY,
+};
+use winapi::um::powerbase::CallNtPowerInformation;
+use winapi::um::synchapi::CreateEventA;
+use winapi::um::sysinfoapi::GetLogicalProcessorInformationEx;
+use winapi::um::sysinfoapi::SYSTEM_INFO;
+use winapi::um::winbase::{RegisterWaitForSingleObject, INFINITE};
+use winapi::um::winnt::{
+ ProcessorInformation, RelationAll, RelationProcessorCore, BOOLEAN, HANDLE,
+ PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX, PVOID, WT_EXECUTEDEFAULT,
+};
+
+// This formula comes from linux's include/linux/sched/loadavg.h
+// https://github.com/torvalds/linux/blob/345671ea0f9258f410eb057b9ced9cefbbe5dc78/include/linux/sched/loadavg.h#L20-L23
+#[allow(clippy::excessive_precision)]
+const LOADAVG_FACTOR_1F: f64 = 0.9200444146293232478931553241;
+#[allow(clippy::excessive_precision)]
+const LOADAVG_FACTOR_5F: f64 = 0.9834714538216174894737477501;
+#[allow(clippy::excessive_precision)]
+const LOADAVG_FACTOR_15F: f64 = 0.9944598480048967508795473394;
+// The time interval in seconds between taking load counts, same as Linux
+const SAMPLING_INTERVAL: usize = 5;
+
+// maybe use a read/write lock instead?
+static LOAD_AVG: once_cell::sync::Lazy<Mutex<Option<LoadAvg>>> =
+ once_cell::sync::Lazy::new(|| unsafe { init_load_avg() });
+
+pub(crate) fn get_load_average() -> LoadAvg {
+ if let Ok(avg) = LOAD_AVG.lock() {
+ if let Some(avg) = &*avg {
+ return avg.clone();
+ }
+ }
+ LoadAvg::default()
+}
+
+unsafe extern "system" fn load_avg_callback(counter: PVOID, _: BOOLEAN) {
+ let mut display_value = mem::MaybeUninit::<PDH_FMT_COUNTERVALUE>::uninit();
+
+ if PdhGetFormattedCounterValue(
+ counter as _,
+ PDH_FMT_DOUBLE,
+ null_mut(),
+ display_value.as_mut_ptr(),
+ ) != ERROR_SUCCESS as _
+ {
+ return;
+ }
+ let display_value = display_value.assume_init();
+ if let Ok(mut avg) = LOAD_AVG.lock() {
+ if let Some(avg) = avg.deref_mut() {
+ let current_load = display_value.u.doubleValue();
+
+ avg.one = avg.one * LOADAVG_FACTOR_1F + current_load * (1.0 - LOADAVG_FACTOR_1F);
+ avg.five = avg.five * LOADAVG_FACTOR_5F + current_load * (1.0 - LOADAVG_FACTOR_5F);
+ avg.fifteen =
+ avg.fifteen * LOADAVG_FACTOR_15F + current_load * (1.0 - LOADAVG_FACTOR_15F);
+ }
+ }
+}
+
+unsafe fn init_load_avg() -> Mutex<Option<LoadAvg>> {
+ // You can see the original implementation here: https://github.com/giampaolo/psutil
+ let mut query = null_mut();
+
+ if PdhOpenQueryA(null_mut(), 0, &mut query) != ERROR_SUCCESS as _ {
+ sysinfo_debug!("init_load_avg: PdhOpenQueryA failed");
+ return Mutex::new(None);
+ }
+
+ let mut counter: PDH_HCOUNTER = mem::zeroed();
+ if PdhAddEnglishCounterA(
+ query,
+ b"\\System\\Cpu Queue Length\0".as_ptr() as _,
+ 0,
+ &mut counter,
+ ) != ERROR_SUCCESS as _
+ {
+ PdhCloseQuery(query);
+ sysinfo_debug!("init_load_avg: failed to get CPU queue length");
+ return Mutex::new(None);
+ }
+
+ let event = CreateEventA(null_mut(), FALSE, FALSE, b"LoadUpdateEvent\0".as_ptr() as _);
+ if event.is_null() {
+ PdhCloseQuery(query);
+ sysinfo_debug!("init_load_avg: failed to create event `LoadUpdateEvent`");
+ return Mutex::new(None);
+ }
+
+ if PdhCollectQueryDataEx(query, SAMPLING_INTERVAL as _, event) != ERROR_SUCCESS as _ {
+ PdhCloseQuery(query);
+ sysinfo_debug!("init_load_avg: PdhCollectQueryDataEx failed");
+ return Mutex::new(None);
+ }
+
+ let mut wait_handle = null_mut();
+ if RegisterWaitForSingleObject(
+ &mut wait_handle,
+ event,
+ Some(load_avg_callback),
+ counter as _,
+ INFINITE,
+ WT_EXECUTEDEFAULT,
+ ) == 0
+ {
+ PdhRemoveCounter(counter);
+ PdhCloseQuery(query);
+ sysinfo_debug!("init_load_avg: RegisterWaitForSingleObject failed");
+ Mutex::new(None)
+ } else {
+ Mutex::new(Some(LoadAvg::default()))
+ }
+}
+
+struct InternalQuery {
+ query: PDH_HQUERY,
+ event: HANDLE,
+ data: HashMap<String, PDH_HCOUNTER>,
+}
+
+unsafe impl Send for InternalQuery {}
+unsafe impl Sync for InternalQuery {}
+
+impl Drop for InternalQuery {
+ fn drop(&mut self) {
+ unsafe {
+ for (_, counter) in self.data.iter() {
+ PdhRemoveCounter(*counter);
+ }
+
+ if !self.event.is_null() {
+ CloseHandle(self.event);
+ }
+
+ if !self.query.is_null() {
+ PdhCloseQuery(self.query);
+ }
+ }
+ }
+}
+
+pub(crate) struct Query {
+ internal: InternalQuery,
+}
+
+impl Query {
+ pub fn new() -> Option<Query> {
+ let mut query = null_mut();
+ unsafe {
+ if PdhOpenQueryA(null_mut(), 0, &mut query) == ERROR_SUCCESS as i32 {
+ let q = InternalQuery {
+ query,
+ event: null_mut(),
+ data: HashMap::new(),
+ };
+ Some(Query { internal: q })
+ } else {
+ sysinfo_debug!("Query::new: PdhOpenQueryA failed");
+ None
+ }
+ }
+ }
+
+ #[allow(clippy::ptr_arg)]
+ pub fn get(&self, name: &String) -> Option<f32> {
+ if let Some(counter) = self.internal.data.get(name) {
+ unsafe {
+ let mut display_value = mem::MaybeUninit::<PDH_FMT_COUNTERVALUE>::uninit();
+ let counter: PDH_HCOUNTER = *counter;
+
+ let ret = PdhGetFormattedCounterValue(
+ counter,
+ PDH_FMT_DOUBLE,
+ null_mut(),
+ display_value.as_mut_ptr(),
+ ) as u32;
+ let display_value = display_value.assume_init();
+ return if ret == ERROR_SUCCESS as _ {
+ let data = *display_value.u.doubleValue();
+ Some(data as f32)
+ } else {
+ sysinfo_debug!("Query::get: PdhGetFormattedCounterValue failed");
+ Some(0.)
+ };
+ }
+ }
+ None
+ }
+
+ #[allow(clippy::ptr_arg)]
+ pub fn add_english_counter(&mut self, name: &String, getter: Vec<u16>) -> bool {
+ if self.internal.data.contains_key(name) {
+ sysinfo_debug!("Query::add_english_counter: doesn't have key `{:?}`", name);
+ return false;
+ }
+ unsafe {
+ let mut counter: PDH_HCOUNTER = std::mem::zeroed();
+ let ret = PdhAddEnglishCounterW(self.internal.query, getter.as_ptr(), 0, &mut counter);
+ if ret == ERROR_SUCCESS as _ {
+ self.internal.data.insert(name.clone(), counter);
+ } else {
+ sysinfo_debug!(
+ "Query::add_english_counter: failed to add counter '{}': {:x}...",
+ name,
+ ret,
+ );
+ return false;
+ }
+ }
+ true
+ }
+
+ pub fn refresh(&self) {
+ unsafe {
+ if PdhCollectQueryData(self.internal.query) != ERROR_SUCCESS as _ {
+ sysinfo_debug!("failed to refresh CPU data");
+ }
+ }
+ }
+}
+
+pub(crate) struct CpusWrapper {
+ global: Cpu,
+ cpus: Vec<Cpu>,
+ got_cpu_frequency: bool,
+}
+
+impl CpusWrapper {
+ pub fn new() -> Self {
+ Self {
+ global: Cpu::new_with_values("Total CPU".to_owned(), String::new(), String::new(), 0),
+ cpus: Vec::new(),
+ got_cpu_frequency: false,
+ }
+ }
+
+ pub fn global_cpu(&self) -> &Cpu {
+ &self.global
+ }
+
+ pub fn global_cpu_mut(&mut self) -> &mut Cpu {
+ &mut self.global
+ }
+
+ pub fn cpus(&self) -> &[Cpu] {
+ &self.cpus
+ }
+
+ fn init_if_needed(&mut self, refresh_kind: CpuRefreshKind) {
+ if self.cpus.is_empty() {
+ let (cpus, vendor_id, brand) = super::tools::init_cpus(refresh_kind);
+ self.cpus = cpus;
+ self.global.vendor_id = vendor_id;
+ self.global.brand = brand;
+ self.got_cpu_frequency = refresh_kind.frequency();
+ }
+ }
+
+ pub fn len(&mut self) -> usize {
+ self.init_if_needed(CpuRefreshKind::new());
+ self.cpus.len()
+ }
+
+ pub fn iter_mut(&mut self, refresh_kind: CpuRefreshKind) -> impl Iterator<Item = &mut Cpu> {
+ self.init_if_needed(refresh_kind);
+ self.cpus.iter_mut()
+ }
+
+ pub fn get_frequencies(&mut self) {
+ if self.got_cpu_frequency {
+ return;
+ }
+ let frequencies = get_frequencies(self.cpus.len());
+
+ for (cpu, frequency) in self.cpus.iter_mut().zip(frequencies) {
+ cpu.set_frequency(frequency);
+ }
+ self.got_cpu_frequency = true;
+ }
+}
+
+#[doc = include_str!("../../md_doc/cpu.md")]
+pub struct Cpu {
+ name: String,
+ cpu_usage: f32,
+ key_used: Option<KeyHandler>,
+ vendor_id: String,
+ brand: String,
+ frequency: u64,
+}
+
+impl CpuExt for Cpu {
+ fn cpu_usage(&self) -> f32 {
+ self.cpu_usage
+ }
+
+ fn name(&self) -> &str {
+ &self.name
+ }
+
+ fn frequency(&self) -> u64 {
+ self.frequency
+ }
+
+ fn vendor_id(&self) -> &str {
+ &self.vendor_id
+ }
+
+ fn brand(&self) -> &str {
+ &self.brand
+ }
+}
+
+impl Cpu {
+ pub(crate) fn new_with_values(
+ name: String,
+ vendor_id: String,
+ brand: String,
+ frequency: u64,
+ ) -> Cpu {
+ Cpu {
+ name,
+ cpu_usage: 0f32,
+ key_used: None,
+ vendor_id,
+ brand,
+ frequency,
+ }
+ }
+
+ pub(crate) fn set_cpu_usage(&mut self, value: f32) {
+ self.cpu_usage = value;
+ }
+
+ pub(crate) fn set_frequency(&mut self, value: u64) {
+ self.frequency = value;
+ }
+}
+
+fn get_vendor_id_not_great(info: &SYSTEM_INFO) -> String {
+ use winapi::um::winnt;
+ // https://docs.microsoft.com/fr-fr/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info
+ unsafe {
+ match info.u.s().wProcessorArchitecture {
+ winnt::PROCESSOR_ARCHITECTURE_INTEL => "Intel x86",
+ winnt::PROCESSOR_ARCHITECTURE_MIPS => "MIPS",
+ winnt::PROCESSOR_ARCHITECTURE_ALPHA => "RISC Alpha",
+ winnt::PROCESSOR_ARCHITECTURE_PPC => "PPC",
+ winnt::PROCESSOR_ARCHITECTURE_SHX => "SHX",
+ winnt::PROCESSOR_ARCHITECTURE_ARM => "ARM",
+ winnt::PROCESSOR_ARCHITECTURE_IA64 => "Intel Itanium-based x64",
+ winnt::PROCESSOR_ARCHITECTURE_ALPHA64 => "RISC Alpha x64",
+ winnt::PROCESSOR_ARCHITECTURE_MSIL => "MSIL",
+ winnt::PROCESSOR_ARCHITECTURE_AMD64 => "(Intel or AMD) x64",
+ winnt::PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 => "Intel Itanium-based x86",
+ winnt::PROCESSOR_ARCHITECTURE_NEUTRAL => "unknown",
+ winnt::PROCESSOR_ARCHITECTURE_ARM64 => "ARM x64",
+ winnt::PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64 => "ARM",
+ winnt::PROCESSOR_ARCHITECTURE_IA32_ON_ARM64 => "Intel Itanium-based x86",
+ _ => "unknown",
+ }
+ .to_owned()
+ }
+}
+
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+pub(crate) fn get_vendor_id_and_brand(info: &SYSTEM_INFO) -> (String, String) {
+ #[cfg(target_arch = "x86")]
+ use std::arch::x86::__cpuid;
+ #[cfg(target_arch = "x86_64")]
+ use std::arch::x86_64::__cpuid;
+
+ unsafe fn add_u32(v: &mut Vec<u8>, i: u32) {
+ let i = &i as *const u32 as *const u8;
+ v.push(*i);
+ v.push(*i.offset(1));
+ v.push(*i.offset(2));
+ v.push(*i.offset(3));
+ }
+
+ unsafe {
+ // First, we try to get the complete name.
+ let res = __cpuid(0x80000000);
+ let n_ex_ids = res.eax;
+ let brand = if n_ex_ids >= 0x80000004 {
+ let mut extdata = Vec::with_capacity(5);
+
+ for i in 0x80000000..=n_ex_ids {
+ extdata.push(__cpuid(i));
+ }
+
+ // 4 * u32 * nb_entries
+ let mut out = Vec::with_capacity(4 * std::mem::size_of::<u32>() * 3);
+ for data in extdata.iter().take(5).skip(2) {
+ add_u32(&mut out, data.eax);
+ add_u32(&mut out, data.ebx);
+ add_u32(&mut out, data.ecx);
+ add_u32(&mut out, data.edx);
+ }
+ let mut pos = 0;
+ for e in out.iter() {
+ if *e == 0 {
+ break;
+ }
+ pos += 1;
+ }
+ match std::str::from_utf8(&out[..pos]) {
+ Ok(s) => s.to_owned(),
+ _ => String::new(),
+ }
+ } else {
+ String::new()
+ };
+
+ // Failed to get full name, let's retry for the short version!
+ let res = __cpuid(0);
+ let mut x = Vec::with_capacity(3 * std::mem::size_of::<u32>());
+ add_u32(&mut x, res.ebx);
+ add_u32(&mut x, res.edx);
+ add_u32(&mut x, res.ecx);
+ let mut pos = 0;
+ for e in x.iter() {
+ if *e == 0 {
+ break;
+ }
+ pos += 1;
+ }
+ let vendor_id = match std::str::from_utf8(&x[..pos]) {
+ Ok(s) => s.to_owned(),
+ Err(_) => get_vendor_id_not_great(info),
+ };
+ (vendor_id, brand)
+ }
+}
+
+#[cfg(all(not(target_arch = "x86_64"), not(target_arch = "x86")))]
+pub(crate) fn get_vendor_id_and_brand(info: &SYSTEM_INFO) -> (String, String) {
+ (get_vendor_id_not_great(info), String::new())
+}
+
+pub(crate) fn get_key_used(p: &mut Cpu) -> &mut Option<KeyHandler> {
+ &mut p.key_used
+}
+
+// From https://stackoverflow.com/a/43813138:
+//
+// If your PC has 64 or fewer logical cpus installed, the above code will work fine. However,
+// if your PC has more than 64 logical cpus installed, use GetActiveCpuCount() or
+// GetLogicalCpuInformation() to determine the total number of logical cpus installed.
+pub(crate) fn get_frequencies(nb_cpus: usize) -> Vec<u64> {
+ let size = nb_cpus * mem::size_of::<PROCESSOR_POWER_INFORMATION>();
+ let mut infos: Vec<PROCESSOR_POWER_INFORMATION> = Vec::with_capacity(nb_cpus);
+
+ unsafe {
+ if CallNtPowerInformation(
+ ProcessorInformation,
+ null_mut(),
+ 0,
+ infos.as_mut_ptr() as _,
+ size as _,
+ ) == 0
+ {
+ infos.set_len(nb_cpus);
+ // infos.Number
+ return infos
+ .into_iter()
+ .map(|i| i.CurrentMhz as u64)
+ .collect::<Vec<_>>();
+ }
+ }
+ sysinfo_debug!("get_frequencies: CallNtPowerInformation failed");
+ vec![0; nb_cpus]
+}
+
+pub(crate) fn get_physical_core_count() -> Option<usize> {
+ // we cannot use the number of cpus here to pre calculate the buf size
+ // GetLogicalCpuInformationEx with RelationProcessorCore passed to it not only returns
+ // the logical cores but also numa nodes
+ //
+ // GetLogicalCpuInformationEx: https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getlogicalprocessorinformationex
+
+ let mut needed_size = 0;
+ unsafe {
+ GetLogicalProcessorInformationEx(RelationAll, null_mut(), &mut needed_size);
+
+ let mut buf: Vec<u8> = Vec::with_capacity(needed_size as _);
+
+ loop {
+ if GetLogicalProcessorInformationEx(
+ RelationAll,
+ buf.as_mut_ptr() as *mut _,
+ &mut needed_size,
+ ) == FALSE
+ {
+ let e = Error::last_os_error();
+ // For some reasons, the function might return a size not big enough...
+ match e.raw_os_error() {
+ Some(value) if value == ERROR_INSUFFICIENT_BUFFER as _ => {}
+ _ => {
+ sysinfo_debug!(
+ "get_physical_core_count: GetLogicalCpuInformationEx failed"
+ );
+ return None;
+ }
+ }
+ } else {
+ break;
+ }
+ buf.reserve(needed_size as usize - buf.capacity());
+ }
+
+ buf.set_len(needed_size as _);
+
+ let mut i = 0;
+ let raw_buf = buf.as_ptr();
+ let mut count = 0;
+ while i < buf.len() {
+ let p = &*(raw_buf.add(i) as PSYSTEM_LOGICAL_PROCESSOR_INFORMATION_EX);
+ i += p.Size as usize;
+ if p.Relationship == RelationProcessorCore {
+ // Only count the physical cores.
+ count += 1;
+ }
+ }
+ Some(count)
+ }
+}
diff --git a/vendor/sysinfo/src/windows/disk.rs b/vendor/sysinfo/src/windows/disk.rs
new file mode 100644
index 000000000..ae393afb2
--- /dev/null
+++ b/vendor/sysinfo/src/windows/disk.rs
@@ -0,0 +1,249 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::{DiskExt, DiskType};
+
+use std::ffi::{OsStr, OsString};
+use std::mem::size_of;
+use std::path::Path;
+
+use winapi::ctypes::c_void;
+use winapi::shared::minwindef::{DWORD, MAX_PATH};
+use winapi::um::fileapi::{
+ CreateFileW, GetDiskFreeSpaceExW, GetDriveTypeW, GetLogicalDrives, GetVolumeInformationW,
+ OPEN_EXISTING,
+};
+use winapi::um::handleapi::{CloseHandle, INVALID_HANDLE_VALUE};
+use winapi::um::ioapiset::DeviceIoControl;
+use winapi::um::winbase::{DRIVE_FIXED, DRIVE_REMOVABLE};
+use winapi::um::winioctl::{
+ DEVICE_TRIM_DESCRIPTOR, IOCTL_STORAGE_QUERY_PROPERTY, STORAGE_PROPERTY_QUERY,
+};
+use winapi::um::winnt::{FILE_SHARE_READ, FILE_SHARE_WRITE, HANDLE, ULARGE_INTEGER};
+
+#[doc = include_str!("../../md_doc/disk.md")]
+pub struct Disk {
+ type_: DiskType,
+ name: OsString,
+ file_system: Vec<u8>,
+ mount_point: Vec<u16>,
+ s_mount_point: String,
+ total_space: u64,
+ available_space: u64,
+ is_removable: bool,
+}
+
+impl DiskExt for Disk {
+ fn type_(&self) -> DiskType {
+ self.type_
+ }
+
+ fn name(&self) -> &OsStr {
+ &self.name
+ }
+
+ fn file_system(&self) -> &[u8] {
+ &self.file_system
+ }
+
+ fn mount_point(&self) -> &Path {
+ Path::new(&self.s_mount_point)
+ }
+
+ fn total_space(&self) -> u64 {
+ self.total_space
+ }
+
+ fn available_space(&self) -> u64 {
+ self.available_space
+ }
+
+ fn is_removable(&self) -> bool {
+ self.is_removable
+ }
+
+ fn refresh(&mut self) -> bool {
+ if self.total_space != 0 {
+ unsafe {
+ let mut tmp: ULARGE_INTEGER = std::mem::zeroed();
+ if GetDiskFreeSpaceExW(
+ self.mount_point.as_ptr(),
+ std::ptr::null_mut(),
+ std::ptr::null_mut(),
+ &mut tmp,
+ ) != 0
+ {
+ self.available_space = *tmp.QuadPart();
+ return true;
+ }
+ }
+ }
+ false
+ }
+}
+
+struct HandleWrapper(HANDLE);
+
+impl HandleWrapper {
+ unsafe fn new(drive_name: &[u16], open_rights: DWORD) -> Option<Self> {
+ let handle = CreateFileW(
+ drive_name.as_ptr(),
+ open_rights,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ std::ptr::null_mut(),
+ OPEN_EXISTING,
+ 0,
+ std::ptr::null_mut(),
+ );
+ if handle == INVALID_HANDLE_VALUE {
+ CloseHandle(handle);
+ None
+ } else {
+ Some(Self(handle))
+ }
+ }
+}
+
+impl Drop for HandleWrapper {
+ fn drop(&mut self) {
+ unsafe {
+ CloseHandle(self.0);
+ }
+ }
+}
+
+unsafe fn get_drive_size(mount_point: &[u16]) -> Option<(u64, u64)> {
+ let mut total_size: ULARGE_INTEGER = std::mem::zeroed();
+ let mut available_space: ULARGE_INTEGER = std::mem::zeroed();
+ if GetDiskFreeSpaceExW(
+ mount_point.as_ptr(),
+ std::ptr::null_mut(),
+ &mut total_size,
+ &mut available_space,
+ ) != 0
+ {
+ Some((
+ *total_size.QuadPart() as u64,
+ *available_space.QuadPart() as u64,
+ ))
+ } else {
+ None
+ }
+}
+
+pub(crate) unsafe fn get_disks() -> Vec<Disk> {
+ let drives = GetLogicalDrives();
+ if drives == 0 {
+ return Vec::new();
+ }
+
+ #[cfg(feature = "multithread")]
+ use rayon::iter::ParallelIterator;
+
+ crate::utils::into_iter(0..DWORD::BITS)
+ .filter_map(|x| {
+ if (drives >> x) & 1 == 0 {
+ return None;
+ }
+ let mount_point = [b'A' as u16 + x as u16, b':' as u16, b'\\' as u16, 0];
+
+ let drive_type = GetDriveTypeW(mount_point.as_ptr());
+
+ let is_removable = drive_type == DRIVE_REMOVABLE;
+
+ if drive_type != DRIVE_FIXED && drive_type != DRIVE_REMOVABLE {
+ return None;
+ }
+ let mut name = [0u16; MAX_PATH + 1];
+ let mut file_system = [0u16; 32];
+ if GetVolumeInformationW(
+ mount_point.as_ptr(),
+ name.as_mut_ptr(),
+ name.len() as DWORD,
+ std::ptr::null_mut(),
+ std::ptr::null_mut(),
+ std::ptr::null_mut(),
+ file_system.as_mut_ptr(),
+ file_system.len() as DWORD,
+ ) == 0
+ {
+ return None;
+ }
+ let mut pos = 0;
+ for x in name.iter() {
+ if *x == 0 {
+ break;
+ }
+ pos += 1;
+ }
+ let name = String::from_utf16_lossy(&name[..pos]);
+ let name = OsStr::new(&name);
+
+ pos = 0;
+ for x in file_system.iter() {
+ if *x == 0 {
+ break;
+ }
+ pos += 1;
+ }
+ let file_system: Vec<u8> = file_system[..pos].iter().map(|x| *x as u8).collect();
+
+ let drive_name = [
+ b'\\' as u16,
+ b'\\' as u16,
+ b'.' as u16,
+ b'\\' as u16,
+ b'A' as u16 + x as u16,
+ b':' as u16,
+ 0,
+ ];
+ let handle = HandleWrapper::new(&drive_name, 0)?;
+ let (total_space, available_space) = get_drive_size(&mount_point)?;
+ if total_space == 0 {
+ return None;
+ }
+ /*let mut spq_trim: STORAGE_PROPERTY_QUERY = std::mem::zeroed();
+ spq_trim.PropertyId = StorageDeviceTrimProperty;
+ spq_trim.QueryType = PropertyStandardQuery;
+ let mut dtd: DEVICE_TRIM_DESCRIPTOR = std::mem::zeroed();*/
+ let mut spq_trim = STORAGE_PROPERTY_QUERY {
+ PropertyId: 8,
+ QueryType: 0,
+ AdditionalParameters: [0],
+ };
+ let mut dtd: DEVICE_TRIM_DESCRIPTOR = std::mem::zeroed();
+
+ let mut dw_size = 0;
+ let type_ = if DeviceIoControl(
+ handle.0,
+ IOCTL_STORAGE_QUERY_PROPERTY,
+ &mut spq_trim as *mut STORAGE_PROPERTY_QUERY as *mut c_void,
+ size_of::<STORAGE_PROPERTY_QUERY>() as DWORD,
+ &mut dtd as *mut DEVICE_TRIM_DESCRIPTOR as *mut c_void,
+ size_of::<DEVICE_TRIM_DESCRIPTOR>() as DWORD,
+ &mut dw_size,
+ std::ptr::null_mut(),
+ ) == 0
+ || dw_size != size_of::<DEVICE_TRIM_DESCRIPTOR>() as DWORD
+ {
+ DiskType::Unknown(-1)
+ } else {
+ let is_ssd = dtd.TrimEnabled != 0;
+ if is_ssd {
+ DiskType::SSD
+ } else {
+ DiskType::HDD
+ }
+ };
+ Some(Disk {
+ type_,
+ name: name.to_owned(),
+ file_system: file_system.to_vec(),
+ mount_point: mount_point.to_vec(),
+ s_mount_point: String::from_utf16_lossy(&mount_point[..mount_point.len() - 1]),
+ total_space,
+ available_space,
+ is_removable,
+ })
+ })
+ .collect::<Vec<_>>()
+}
diff --git a/vendor/sysinfo/src/windows/macros.rs b/vendor/sysinfo/src/windows/macros.rs
new file mode 100644
index 000000000..b0c024c7c
--- /dev/null
+++ b/vendor/sysinfo/src/windows/macros.rs
@@ -0,0 +1,16 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+/// Allows to cast only when needed.
+#[macro_export]
+macro_rules! auto_cast {
+ ($t:expr, $cast:ty) => {{
+ #[cfg(target_pointer_width = "32")]
+ {
+ $t as $cast
+ }
+ #[cfg(not(target_pointer_width = "32"))]
+ {
+ $t
+ }
+ }};
+}
diff --git a/vendor/sysinfo/src/windows/mod.rs b/vendor/sysinfo/src/windows/mod.rs
new file mode 100644
index 000000000..fa5d66beb
--- /dev/null
+++ b/vendor/sysinfo/src/windows/mod.rs
@@ -0,0 +1,20 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+mod component;
+mod cpu;
+mod disk;
+#[macro_use]
+mod macros;
+mod network;
+mod process;
+mod system;
+mod tools;
+mod users;
+mod utils;
+
+pub use self::component::Component;
+pub use self::cpu::Cpu;
+pub use self::disk::Disk;
+pub use self::network::{NetworkData, Networks};
+pub use self::process::Process;
+pub use self::system::System;
diff --git a/vendor/sysinfo/src/windows/network.rs b/vendor/sysinfo/src/windows/network.rs
new file mode 100644
index 000000000..6a09a0490
--- /dev/null
+++ b/vendor/sysinfo/src/windows/network.rs
@@ -0,0 +1,249 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::{NetworkExt, NetworksExt, NetworksIter};
+
+use std::collections::{hash_map, HashMap};
+
+use winapi::shared::ifdef::{MediaConnectStateDisconnected, NET_LUID};
+use winapi::shared::netioapi::{
+ FreeMibTable, GetIfEntry2, GetIfTable2, MIB_IF_ROW2, PMIB_IF_TABLE2,
+};
+use winapi::shared::winerror::NO_ERROR;
+
+macro_rules! old_and_new {
+ ($ty_:expr, $name:ident, $old:ident, $new_val:expr) => {{
+ $ty_.$old = $ty_.$name;
+ $ty_.$name = $new_val;
+ }};
+}
+
+#[doc = include_str!("../../md_doc/networks.md")]
+pub struct Networks {
+ interfaces: HashMap<String, NetworkData>,
+}
+
+impl Networks {
+ pub(crate) fn new() -> Networks {
+ Networks {
+ interfaces: HashMap::new(),
+ }
+ }
+}
+
+impl NetworksExt for Networks {
+ #[allow(clippy::needless_lifetimes)]
+ fn iter<'a>(&'a self) -> NetworksIter<'a> {
+ NetworksIter::new(self.interfaces.iter())
+ }
+
+ fn refresh_networks_list(&mut self) {
+ let mut table: PMIB_IF_TABLE2 = std::ptr::null_mut();
+
+ unsafe {
+ if GetIfTable2(&mut table) != NO_ERROR {
+ return;
+ }
+
+ for (_, data) in self.interfaces.iter_mut() {
+ data.updated = false;
+ }
+
+ // In here, this is tricky: we have to filter out the software interfaces to only keep
+ // the hardware ones. To do so, we first check the connection potential speed (if 0, not
+ // interesting), then we check its state: if not open, not interesting either. And finally,
+ // we count the members of a same group: if there is more than 1, then it's software level.
+ let mut groups = HashMap::new();
+ let mut indexes = Vec::new();
+ let ptr = (*table).Table.as_ptr();
+ for i in 0..(*table).NumEntries {
+ let ptr = &*ptr.offset(i as _);
+ if (ptr.TransmitLinkSpeed == 0 && ptr.ReceiveLinkSpeed == 0)
+ || ptr.MediaConnectState == MediaConnectStateDisconnected
+ || ptr.PhysicalAddressLength == 0
+ {
+ continue;
+ }
+ let id = vec![
+ ptr.InterfaceGuid.Data2,
+ ptr.InterfaceGuid.Data3,
+ ptr.InterfaceGuid.Data4[0] as _,
+ ptr.InterfaceGuid.Data4[1] as _,
+ ptr.InterfaceGuid.Data4[2] as _,
+ ptr.InterfaceGuid.Data4[3] as _,
+ ptr.InterfaceGuid.Data4[4] as _,
+ ptr.InterfaceGuid.Data4[5] as _,
+ ptr.InterfaceGuid.Data4[6] as _,
+ ptr.InterfaceGuid.Data4[7] as _,
+ ];
+ let entry = groups.entry(id.clone()).or_insert(0);
+ *entry += 1;
+ if *entry > 1 {
+ continue;
+ }
+ indexes.push((i, id));
+ }
+ for (i, id) in indexes {
+ let ptr = &*ptr.offset(i as _);
+ if *groups.get(&id).unwrap_or(&0) > 1 {
+ continue;
+ }
+ let mut pos = 0;
+ for x in ptr.Alias.iter() {
+ if *x == 0 {
+ break;
+ }
+ pos += 1;
+ }
+ let interface_name = match String::from_utf16(&ptr.Alias[..pos]) {
+ Ok(s) => s,
+ _ => continue,
+ };
+ match self.interfaces.entry(interface_name) {
+ hash_map::Entry::Occupied(mut e) => {
+ let mut interface = e.get_mut();
+ old_and_new!(interface, current_out, old_out, ptr.OutOctets);
+ old_and_new!(interface, current_in, old_in, ptr.InOctets);
+ old_and_new!(
+ interface,
+ packets_in,
+ old_packets_in,
+ ptr.InUcastPkts.saturating_add(ptr.InNUcastPkts)
+ );
+ old_and_new!(
+ interface,
+ packets_out,
+ old_packets_out,
+ ptr.OutUcastPkts.saturating_add(ptr.OutNUcastPkts)
+ );
+ old_and_new!(interface, errors_in, old_errors_in, ptr.InErrors);
+ old_and_new!(interface, errors_out, old_errors_out, ptr.OutErrors);
+ interface.updated = true;
+ }
+ hash_map::Entry::Vacant(e) => {
+ let packets_in = ptr.InUcastPkts.saturating_add(ptr.InNUcastPkts);
+ let packets_out = ptr.OutUcastPkts.saturating_add(ptr.OutNUcastPkts);
+
+ e.insert(NetworkData {
+ id: ptr.InterfaceLuid,
+ current_out: ptr.OutOctets,
+ old_out: ptr.OutOctets,
+ current_in: ptr.InOctets,
+ old_in: ptr.InOctets,
+ packets_in,
+ old_packets_in: packets_in,
+ packets_out,
+ old_packets_out: packets_out,
+ errors_in: ptr.InErrors,
+ old_errors_in: ptr.InErrors,
+ errors_out: ptr.OutErrors,
+ old_errors_out: ptr.OutErrors,
+ updated: true,
+ });
+ }
+ }
+ }
+ FreeMibTable(table as _);
+ }
+ // Remove interfaces which are gone.
+ self.interfaces.retain(|_, d| d.updated);
+ }
+
+ fn refresh(&mut self) {
+ let entry = std::mem::MaybeUninit::<MIB_IF_ROW2>::zeroed();
+
+ unsafe {
+ let mut entry = entry.assume_init();
+ for (_, interface) in self.interfaces.iter_mut() {
+ entry.InterfaceLuid = interface.id;
+ entry.InterfaceIndex = 0; // to prevent the function to pick this one as index
+ if GetIfEntry2(&mut entry) != NO_ERROR {
+ continue;
+ }
+ old_and_new!(interface, current_out, old_out, entry.OutOctets);
+ old_and_new!(interface, current_in, old_in, entry.InOctets);
+ old_and_new!(
+ interface,
+ packets_in,
+ old_packets_in,
+ entry.InUcastPkts.saturating_add(entry.InNUcastPkts)
+ );
+ old_and_new!(
+ interface,
+ packets_out,
+ old_packets_out,
+ entry.OutUcastPkts.saturating_add(entry.OutNUcastPkts)
+ );
+ old_and_new!(interface, errors_in, old_errors_in, entry.InErrors);
+ old_and_new!(interface, errors_out, old_errors_out, entry.OutErrors);
+ }
+ }
+ }
+}
+
+#[doc = include_str!("../../md_doc/network_data.md")]
+pub struct NetworkData {
+ id: NET_LUID,
+ current_out: u64,
+ old_out: u64,
+ current_in: u64,
+ old_in: u64,
+ packets_in: u64,
+ old_packets_in: u64,
+ packets_out: u64,
+ old_packets_out: u64,
+ errors_in: u64,
+ old_errors_in: u64,
+ errors_out: u64,
+ old_errors_out: u64,
+ updated: bool,
+}
+
+impl NetworkExt for NetworkData {
+ fn received(&self) -> u64 {
+ self.current_in.saturating_sub(self.old_in)
+ }
+
+ fn total_received(&self) -> u64 {
+ self.current_in
+ }
+
+ fn transmitted(&self) -> u64 {
+ self.current_out.saturating_sub(self.old_out)
+ }
+
+ fn total_transmitted(&self) -> u64 {
+ self.current_out
+ }
+
+ fn packets_received(&self) -> u64 {
+ self.packets_in.saturating_sub(self.old_packets_in)
+ }
+
+ fn total_packets_received(&self) -> u64 {
+ self.packets_in
+ }
+
+ fn packets_transmitted(&self) -> u64 {
+ self.packets_out.saturating_sub(self.old_packets_out)
+ }
+
+ fn total_packets_transmitted(&self) -> u64 {
+ self.packets_out
+ }
+
+ fn errors_on_received(&self) -> u64 {
+ self.errors_in.saturating_sub(self.old_errors_in)
+ }
+
+ fn total_errors_on_received(&self) -> u64 {
+ self.errors_in
+ }
+
+ fn errors_on_transmitted(&self) -> u64 {
+ self.errors_out.saturating_sub(self.old_errors_out)
+ }
+
+ fn total_errors_on_transmitted(&self) -> u64 {
+ self.errors_out
+ }
+}
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;
+ }
+ }
+ }
+}
diff --git a/vendor/sysinfo/src/windows/system.rs b/vendor/sysinfo/src/windows/system.rs
new file mode 100644
index 000000000..6abd30db5
--- /dev/null
+++ b/vendor/sysinfo/src/windows/system.rs
@@ -0,0 +1,623 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::{
+ CpuRefreshKind, LoadAvg, Networks, Pid, ProcessExt, ProcessRefreshKind, RefreshKind, SystemExt,
+ User,
+};
+use winapi::um::winreg::HKEY_LOCAL_MACHINE;
+
+use crate::sys::component::{self, Component};
+use crate::sys::cpu::*;
+use crate::sys::disk::{get_disks, Disk};
+use crate::sys::process::{update_memory, Process};
+use crate::sys::tools::*;
+use crate::sys::users::get_users;
+use crate::sys::utils::get_now;
+
+use crate::utils::into_iter;
+
+use std::cell::UnsafeCell;
+use std::collections::HashMap;
+use std::ffi::OsStr;
+use std::mem::{size_of, zeroed};
+use std::os::windows::ffi::OsStrExt;
+use std::slice::from_raw_parts;
+use std::time::SystemTime;
+
+use ntapi::ntexapi::{
+ NtQuerySystemInformation, SystemProcessInformation, SYSTEM_PROCESS_INFORMATION,
+};
+use winapi::ctypes::wchar_t;
+use winapi::shared::minwindef::{DWORD, FALSE, HKEY, LPBYTE, TRUE};
+use winapi::shared::ntdef::{PVOID, ULONG};
+use winapi::shared::ntstatus::STATUS_INFO_LENGTH_MISMATCH;
+use winapi::shared::winerror;
+use winapi::um::minwinbase::STILL_ACTIVE;
+use winapi::um::processthreadsapi::GetExitCodeProcess;
+use winapi::um::psapi::{GetPerformanceInfo, PERFORMANCE_INFORMATION};
+use winapi::um::sysinfoapi::{
+ ComputerNamePhysicalDnsHostname, GetComputerNameExW, GetTickCount64, GlobalMemoryStatusEx,
+ MEMORYSTATUSEX,
+};
+use winapi::um::winnt::{HANDLE, KEY_READ};
+use winapi::um::winreg::{RegOpenKeyExW, RegQueryValueExW};
+
+declare_signals! {
+ (),
+ Signal::Kill => (),
+ _ => None,
+}
+
+#[doc = include_str!("../../md_doc/system.md")]
+pub struct System {
+ process_list: HashMap<Pid, Process>,
+ mem_total: u64,
+ mem_available: u64,
+ swap_total: u64,
+ swap_used: u64,
+ cpus: CpusWrapper,
+ components: Vec<Component>,
+ disks: Vec<Disk>,
+ query: Option<Query>,
+ networks: Networks,
+ boot_time: u64,
+ users: Vec<User>,
+}
+
+// Useful for parallel iterations.
+struct Wrap<T>(T);
+
+#[allow(clippy::non_send_fields_in_send_ty)]
+unsafe impl<T> Send for Wrap<T> {}
+unsafe impl<T> Sync for Wrap<T> {}
+
+unsafe fn boot_time() -> u64 {
+ match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
+ Ok(n) => n.as_secs().saturating_sub(GetTickCount64()) / 1000,
+ Err(_e) => {
+ sysinfo_debug!("Failed to compute boot time: {:?}", _e);
+ 0
+ }
+ }
+}
+
+impl SystemExt for System {
+ const IS_SUPPORTED: bool = true;
+ const SUPPORTED_SIGNALS: &'static [Signal] = supported_signals();
+
+ #[allow(non_snake_case)]
+ fn new_with_specifics(refreshes: RefreshKind) -> System {
+ let mut s = System {
+ process_list: HashMap::with_capacity(500),
+ mem_total: 0,
+ mem_available: 0,
+ swap_total: 0,
+ swap_used: 0,
+ cpus: CpusWrapper::new(),
+ components: Vec::new(),
+ disks: Vec::with_capacity(2),
+ query: None,
+ networks: Networks::new(),
+ boot_time: unsafe { boot_time() },
+ users: Vec::new(),
+ };
+ s.refresh_specifics(refreshes);
+ s
+ }
+
+ fn refresh_cpu_specifics(&mut self, refresh_kind: CpuRefreshKind) {
+ if self.query.is_none() {
+ self.query = Query::new();
+ if let Some(ref mut query) = self.query {
+ add_english_counter(
+ r"\Processor(_Total)\% Processor Time".to_string(),
+ query,
+ get_key_used(self.cpus.global_cpu_mut()),
+ "tot_0".to_owned(),
+ );
+ for (pos, proc_) in self.cpus.iter_mut(refresh_kind).enumerate() {
+ add_english_counter(
+ format!(r"\Processor({})\% Processor Time", pos),
+ query,
+ get_key_used(proc_),
+ format!("{}_0", pos),
+ );
+ }
+ }
+ }
+ if let Some(ref mut query) = self.query {
+ query.refresh();
+ let mut used_time = None;
+ if let Some(ref key_used) = *get_key_used(self.cpus.global_cpu_mut()) {
+ used_time = Some(
+ query
+ .get(&key_used.unique_id)
+ .expect("global_key_idle disappeared"),
+ );
+ }
+ if let Some(used_time) = used_time {
+ self.cpus.global_cpu_mut().set_cpu_usage(used_time);
+ }
+ for p in self.cpus.iter_mut(refresh_kind) {
+ let mut used_time = None;
+ if let Some(ref key_used) = *get_key_used(p) {
+ used_time = Some(
+ query
+ .get(&key_used.unique_id)
+ .expect("key_used disappeared"),
+ );
+ }
+ if let Some(used_time) = used_time {
+ p.set_cpu_usage(used_time);
+ }
+ }
+ if refresh_kind.frequency() {
+ self.cpus.get_frequencies();
+ }
+ }
+ }
+
+ fn refresh_memory(&mut self) {
+ unsafe {
+ let mut mem_info: MEMORYSTATUSEX = zeroed();
+ mem_info.dwLength = size_of::<MEMORYSTATUSEX>() as u32;
+ GlobalMemoryStatusEx(&mut mem_info);
+ self.mem_total = auto_cast!(mem_info.ullTotalPhys, u64) / 1_000;
+ self.mem_available = auto_cast!(mem_info.ullAvailPhys, u64) / 1_000;
+ let mut perf_info: PERFORMANCE_INFORMATION = zeroed();
+ if GetPerformanceInfo(&mut perf_info, size_of::<PERFORMANCE_INFORMATION>() as u32)
+ == TRUE
+ {
+ let swap_total = perf_info.PageSize.saturating_mul(
+ perf_info
+ .CommitLimit
+ .saturating_sub(perf_info.PhysicalTotal),
+ );
+ let swap_used = perf_info.PageSize.saturating_mul(
+ perf_info
+ .CommitTotal
+ .saturating_sub(perf_info.PhysicalTotal),
+ );
+ self.swap_total = (swap_total / 1000) as u64;
+ self.swap_used = (swap_used / 1000) as u64;
+ }
+ }
+ }
+
+ fn refresh_components_list(&mut self) {
+ self.components = component::get_components();
+ }
+
+ #[allow(clippy::map_entry)]
+ fn refresh_process_specifics(&mut self, pid: Pid, refresh_kind: ProcessRefreshKind) -> bool {
+ if self.process_list.contains_key(&pid) {
+ return refresh_existing_process(self, pid, refresh_kind);
+ }
+ let now = get_now();
+ if let Some(mut p) = Process::new_from_pid(pid, now, refresh_kind) {
+ p.update(refresh_kind, self.cpus.len() as u64, now);
+ p.updated = false;
+ self.process_list.insert(pid, p);
+ true
+ } else {
+ false
+ }
+ }
+
+ #[allow(clippy::cast_ptr_alignment)]
+ fn refresh_processes_specifics(&mut self, refresh_kind: ProcessRefreshKind) {
+ // Windows 10 notebook requires at least 512KiB of memory to make it in one go
+ let mut buffer_size: usize = 512 * 1024;
+ let now = get_now();
+
+ loop {
+ let mut process_information: Vec<u8> = Vec::with_capacity(buffer_size);
+ let mut cb_needed = 0;
+
+ unsafe {
+ process_information.set_len(buffer_size);
+ let ntstatus = NtQuerySystemInformation(
+ SystemProcessInformation,
+ process_information.as_mut_ptr() as PVOID,
+ buffer_size as ULONG,
+ &mut cb_needed,
+ );
+
+ if ntstatus != STATUS_INFO_LENGTH_MISMATCH {
+ if ntstatus < 0 {
+ sysinfo_debug!(
+ "Couldn't get process infos: NtQuerySystemInformation returned {}",
+ ntstatus
+ );
+ }
+
+ // Parse the data block to get process information
+ let mut process_ids = Vec::with_capacity(500);
+ let mut process_information_offset = 0;
+ loop {
+ let p = process_information
+ .as_ptr()
+ .offset(process_information_offset)
+ as *const SYSTEM_PROCESS_INFORMATION;
+ let pi = &*p;
+
+ process_ids.push(Wrap(p));
+
+ if pi.NextEntryOffset == 0 {
+ break;
+ }
+
+ process_information_offset += pi.NextEntryOffset as isize;
+ }
+ let process_list = Wrap(UnsafeCell::new(&mut self.process_list));
+ let nb_cpus = if refresh_kind.cpu() {
+ self.cpus.len() as u64
+ } else {
+ 0
+ };
+
+ #[cfg(feature = "multithread")]
+ use rayon::iter::ParallelIterator;
+
+ // TODO: instead of using parallel iterator only here, would be better to be
+ // able to run it over `process_information` directly!
+ let processes = into_iter(process_ids)
+ .filter_map(|pi| {
+ let pi = *pi.0;
+ let pid = Pid(pi.UniqueProcessId as _);
+ if let Some(proc_) = (*process_list.0.get()).get_mut(&pid) {
+ proc_.memory = (pi.WorkingSetSize as u64) / 1_000;
+ proc_.virtual_memory = (pi.VirtualSize as u64) / 1_000;
+ proc_.update(refresh_kind, nb_cpus, now);
+ return None;
+ }
+ let name = get_process_name(&pi, pid);
+ let mut p = Process::new_full(
+ pid,
+ if pi.InheritedFromUniqueProcessId as usize != 0 {
+ Some(Pid(pi.InheritedFromUniqueProcessId as _))
+ } else {
+ None
+ },
+ (pi.WorkingSetSize as u64) / 1_000,
+ (pi.VirtualSize as u64) / 1_000,
+ name,
+ now,
+ refresh_kind,
+ );
+ p.update(refresh_kind, nb_cpus, now);
+ Some(p)
+ })
+ .collect::<Vec<_>>();
+ for p in processes.into_iter() {
+ self.process_list.insert(p.pid(), p);
+ }
+ self.process_list.retain(|_, v| {
+ let x = v.updated;
+ v.updated = false;
+ x
+ });
+
+ break;
+ }
+
+ // GetNewBufferSize
+ if cb_needed == 0 {
+ buffer_size *= 2;
+ continue;
+ }
+ // allocating a few more kilo bytes just in case there are some new process
+ // kicked in since new call to NtQuerySystemInformation
+ buffer_size = (cb_needed + (1024 * 10)) as usize;
+ }
+ }
+ }
+
+ fn refresh_disks_list(&mut self) {
+ self.disks = unsafe { get_disks() };
+ }
+
+ fn refresh_users_list(&mut self) {
+ self.users = unsafe { get_users() };
+ }
+
+ fn processes(&self) -> &HashMap<Pid, Process> {
+ &self.process_list
+ }
+
+ fn process(&self, pid: Pid) -> Option<&Process> {
+ self.process_list.get(&pid)
+ }
+
+ fn global_cpu_info(&self) -> &Cpu {
+ self.cpus.global_cpu()
+ }
+
+ fn cpus(&self) -> &[Cpu] {
+ self.cpus.cpus()
+ }
+
+ fn physical_core_count(&self) -> Option<usize> {
+ get_physical_core_count()
+ }
+
+ fn total_memory(&self) -> u64 {
+ self.mem_total
+ }
+
+ fn free_memory(&self) -> u64 {
+ // MEMORYSTATUSEX doesn't report free memory
+ self.mem_available
+ }
+
+ fn available_memory(&self) -> u64 {
+ self.mem_available
+ }
+
+ fn used_memory(&self) -> u64 {
+ self.mem_total - self.mem_available
+ }
+
+ fn total_swap(&self) -> u64 {
+ self.swap_total
+ }
+
+ fn free_swap(&self) -> u64 {
+ self.swap_total - self.swap_used
+ }
+
+ fn used_swap(&self) -> u64 {
+ self.swap_used
+ }
+
+ fn components(&self) -> &[Component] {
+ &self.components
+ }
+
+ fn components_mut(&mut self) -> &mut [Component] {
+ &mut self.components
+ }
+
+ fn disks(&self) -> &[Disk] {
+ &self.disks
+ }
+
+ fn disks_mut(&mut self) -> &mut [Disk] {
+ &mut self.disks
+ }
+
+ fn users(&self) -> &[User] {
+ &self.users
+ }
+
+ fn networks(&self) -> &Networks {
+ &self.networks
+ }
+
+ fn networks_mut(&mut self) -> &mut Networks {
+ &mut self.networks
+ }
+
+ fn uptime(&self) -> u64 {
+ unsafe { GetTickCount64() / 1000 }
+ }
+
+ fn boot_time(&self) -> u64 {
+ self.boot_time
+ }
+
+ fn load_average(&self) -> LoadAvg {
+ get_load_average()
+ }
+
+ fn name(&self) -> Option<String> {
+ Some("Windows".to_owned())
+ }
+
+ fn long_os_version(&self) -> Option<String> {
+ get_reg_string_value(
+ HKEY_LOCAL_MACHINE,
+ "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
+ "ProductName",
+ )
+ }
+
+ fn host_name(&self) -> Option<String> {
+ get_dns_hostname()
+ }
+
+ fn kernel_version(&self) -> Option<String> {
+ get_reg_string_value(
+ HKEY_LOCAL_MACHINE,
+ "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
+ "CurrentBuildNumber",
+ )
+ }
+
+ fn os_version(&self) -> Option<String> {
+ let major = get_reg_value_u32(
+ HKEY_LOCAL_MACHINE,
+ "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
+ "CurrentMajorVersionNumber",
+ );
+
+ let build_number = get_reg_string_value(
+ HKEY_LOCAL_MACHINE,
+ "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion",
+ "CurrentBuildNumber",
+ );
+
+ Some(format!(
+ "{} ({})",
+ u32::from_le_bytes(major.unwrap_or_default()),
+ build_number.unwrap_or_default()
+ ))
+ }
+}
+
+impl Default for System {
+ fn default() -> System {
+ System::new()
+ }
+}
+
+fn is_proc_running(handle: HANDLE) -> bool {
+ let mut exit_code = 0;
+ unsafe {
+ let ret = GetExitCodeProcess(handle, &mut exit_code);
+ !(ret == FALSE || exit_code != STILL_ACTIVE)
+ }
+}
+
+fn refresh_existing_process(s: &mut System, pid: Pid, refresh_kind: ProcessRefreshKind) -> bool {
+ if let Some(ref mut entry) = s.process_list.get_mut(&pid) {
+ if let Some(handle) = entry.get_handle() {
+ if !is_proc_running(handle) {
+ return false;
+ }
+ } else {
+ return false;
+ }
+ update_memory(entry);
+ entry.update(refresh_kind, s.cpus.len() as u64, get_now());
+ entry.updated = false;
+ true
+ } else {
+ false
+ }
+}
+
+#[allow(clippy::size_of_in_element_count)]
+//^ needed for "name.Length as usize / std::mem::size_of::<u16>()"
+pub(crate) fn get_process_name(process: &SYSTEM_PROCESS_INFORMATION, process_id: Pid) -> String {
+ let name = &process.ImageName;
+ if name.Buffer.is_null() {
+ match process_id.0 {
+ 0 => "Idle".to_owned(),
+ 4 => "System".to_owned(),
+ _ => format!("<no name> Process {}", process_id),
+ }
+ } else {
+ unsafe {
+ let slice = std::slice::from_raw_parts(
+ name.Buffer,
+ // The length is in bytes, not the length of string
+ name.Length as usize / std::mem::size_of::<u16>(),
+ );
+
+ String::from_utf16_lossy(slice)
+ }
+ }
+}
+
+fn utf16_str<S: AsRef<OsStr> + ?Sized>(text: &S) -> Vec<u16> {
+ OsStr::new(text)
+ .encode_wide()
+ .chain(Some(0).into_iter())
+ .collect::<Vec<_>>()
+}
+
+fn get_reg_string_value(hkey: HKEY, path: &str, field_name: &str) -> Option<String> {
+ let c_path = utf16_str(path);
+ let c_field_name = utf16_str(field_name);
+
+ let mut new_hkey: HKEY = std::ptr::null_mut();
+ unsafe {
+ if RegOpenKeyExW(hkey, c_path.as_ptr(), 0, KEY_READ, &mut new_hkey) != 0 {
+ return None;
+ }
+
+ let mut buf_len: DWORD = 2048;
+ let mut buf_type: DWORD = 0;
+ let mut buf: Vec<u8> = Vec::with_capacity(buf_len as usize);
+ loop {
+ match RegQueryValueExW(
+ new_hkey,
+ c_field_name.as_ptr(),
+ std::ptr::null_mut(),
+ &mut buf_type,
+ buf.as_mut_ptr() as LPBYTE,
+ &mut buf_len,
+ ) as DWORD
+ {
+ 0 => break,
+ winerror::ERROR_MORE_DATA => {
+ buf.reserve(buf_len as _);
+ }
+ _ => return None,
+ }
+ }
+
+ buf.set_len(buf_len as _);
+
+ let words = from_raw_parts(buf.as_ptr() as *const u16, buf.len() / 2);
+ let mut s = String::from_utf16_lossy(words);
+ while s.ends_with('\u{0}') {
+ s.pop();
+ }
+ Some(s)
+ }
+}
+
+fn get_reg_value_u32(hkey: HKEY, path: &str, field_name: &str) -> Option<[u8; 4]> {
+ let c_path = utf16_str(path);
+ let c_field_name = utf16_str(field_name);
+
+ let mut new_hkey: HKEY = std::ptr::null_mut();
+ unsafe {
+ if RegOpenKeyExW(hkey, c_path.as_ptr(), 0, KEY_READ, &mut new_hkey) != 0 {
+ return None;
+ }
+
+ let mut buf_len: DWORD = 4;
+ let mut buf_type: DWORD = 0;
+ let mut buf = [0u8; 4];
+
+ match RegQueryValueExW(
+ new_hkey,
+ c_field_name.as_ptr(),
+ std::ptr::null_mut(),
+ &mut buf_type,
+ buf.as_mut_ptr() as LPBYTE,
+ &mut buf_len,
+ ) as DWORD
+ {
+ 0 => Some(buf),
+ _ => None,
+ }
+ }
+}
+
+fn get_dns_hostname() -> Option<String> {
+ let mut buffer_size = 0;
+ // Running this first to get the buffer size since the DNS name can be longer than MAX_COMPUTERNAME_LENGTH
+ // setting the `lpBuffer` to null will return the buffer size
+ // https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getcomputernameexw
+ unsafe {
+ GetComputerNameExW(
+ ComputerNamePhysicalDnsHostname,
+ std::ptr::null_mut(),
+ &mut buffer_size,
+ );
+
+ // Setting the buffer with the new length
+ let mut buffer = vec![0_u16; buffer_size as usize];
+
+ // https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/ne-sysinfoapi-computer_name_format
+ if GetComputerNameExW(
+ ComputerNamePhysicalDnsHostname,
+ buffer.as_mut_ptr() as *mut wchar_t,
+ &mut buffer_size,
+ ) == TRUE
+ {
+ if let Some(pos) = buffer.iter().position(|c| *c == 0) {
+ buffer.resize(pos, 0);
+ }
+
+ return String::from_utf16(&buffer).ok();
+ }
+ }
+
+ sysinfo_debug!("Failed to get computer hostname");
+ None
+}
diff --git a/vendor/sysinfo/src/windows/tools.rs b/vendor/sysinfo/src/windows/tools.rs
new file mode 100644
index 000000000..a0c334010
--- /dev/null
+++ b/vendor/sysinfo/src/windows/tools.rs
@@ -0,0 +1,55 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::sys::cpu::{self, Cpu, Query};
+use crate::CpuRefreshKind;
+
+use std::mem::zeroed;
+
+use winapi::um::sysinfoapi::{GetSystemInfo, SYSTEM_INFO};
+
+pub(crate) struct KeyHandler {
+ pub unique_id: String,
+}
+
+impl KeyHandler {
+ pub fn new(unique_id: String) -> KeyHandler {
+ KeyHandler { unique_id }
+ }
+}
+
+pub(crate) fn init_cpus(refresh_kind: CpuRefreshKind) -> (Vec<Cpu>, String, String) {
+ unsafe {
+ let mut sys_info: SYSTEM_INFO = zeroed();
+ GetSystemInfo(&mut sys_info);
+ let (vendor_id, brand) = cpu::get_vendor_id_and_brand(&sys_info);
+ let nb_cpus = sys_info.dwNumberOfProcessors as usize;
+ let frequencies = if refresh_kind.frequency() {
+ cpu::get_frequencies(nb_cpus)
+ } else {
+ vec![0; nb_cpus]
+ };
+ let mut ret = Vec::with_capacity(nb_cpus + 1);
+ for (nb, frequency) in frequencies.iter().enumerate() {
+ ret.push(Cpu::new_with_values(
+ format!("CPU {}", nb + 1),
+ vendor_id.clone(),
+ brand.clone(),
+ *frequency,
+ ));
+ }
+ (ret, vendor_id, brand)
+ }
+}
+
+pub(crate) fn add_english_counter(
+ s: String,
+ query: &mut Query,
+ keys: &mut Option<KeyHandler>,
+ counter_name: String,
+) {
+ let mut full = s.encode_utf16().collect::<Vec<_>>();
+ full.push(0);
+ if query.add_english_counter(&counter_name, full) {
+ *keys = Some(KeyHandler::new(counter_name));
+ }
+}
diff --git a/vendor/sysinfo/src/windows/users.rs b/vendor/sysinfo/src/windows/users.rs
new file mode 100644
index 000000000..2fef05c3f
--- /dev/null
+++ b/vendor/sysinfo/src/windows/users.rs
@@ -0,0 +1,181 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use crate::sys::utils::to_str;
+use crate::{
+ common::{Gid, Uid},
+ User,
+};
+
+use std::ptr::null_mut;
+use winapi::shared::lmcons::{MAX_PREFERRED_LENGTH, NET_API_STATUS};
+use winapi::shared::minwindef::DWORD;
+use winapi::shared::ntstatus::STATUS_SUCCESS;
+use winapi::shared::winerror::ERROR_MORE_DATA;
+use winapi::um::lmaccess::{NetUserEnum, NetUserGetLocalGroups};
+use winapi::um::lmaccess::{
+ FILTER_NORMAL_ACCOUNT, LG_INCLUDE_INDIRECT, LPLOCALGROUP_USERS_INFO_0, USER_INFO_0,
+};
+use winapi::um::lmapibuf::NetApiBufferFree;
+use winapi::um::ntlsa::{
+ LsaEnumerateLogonSessions, LsaFreeReturnBuffer, LsaGetLogonSessionData,
+ PSECURITY_LOGON_SESSION_DATA,
+};
+use winapi::um::winnt::{LPWSTR, PLUID};
+
+// FIXME: once this is mreged in winapi, it can be removed.
+#[allow(non_upper_case_globals)]
+const NERR_Success: NET_API_STATUS = 0;
+
+unsafe fn get_groups_for_user(username: LPWSTR) -> Vec<String> {
+ let mut buf: LPLOCALGROUP_USERS_INFO_0 = null_mut();
+ let mut nb_entries = 0;
+ let mut total_entries = 0;
+ let mut groups;
+
+ let status = NetUserGetLocalGroups(
+ [0u16].as_ptr(),
+ username,
+ 0,
+ LG_INCLUDE_INDIRECT,
+ &mut buf as *mut _ as _,
+ MAX_PREFERRED_LENGTH,
+ &mut nb_entries,
+ &mut total_entries,
+ );
+
+ if status == NERR_Success {
+ groups = Vec::with_capacity(nb_entries as _);
+
+ if !buf.is_null() {
+ for i in 0..nb_entries {
+ let tmp = buf.offset(i as _);
+ if tmp.is_null() {
+ break;
+ }
+ groups.push(to_str((*tmp).lgrui0_name));
+ }
+ }
+ } else {
+ groups = Vec::new();
+ sysinfo_debug!("NetUserGetLocalGroups failed with ret code {}", status);
+ }
+ if !buf.is_null() {
+ NetApiBufferFree(buf as *mut _);
+ }
+
+ groups
+}
+
+// FIXME: For now, the Uid is the user name, which is quite bad. Normally, there is `PSID` for
+// that. But when getting the `PSID` from the processes, it doesn't match the ones we have for
+// the users (`EqualSid`). Anyway, until I have time and motivation to fix this. It'll remain
+// like that...
+pub unsafe fn get_users() -> Vec<User> {
+ let mut users = Vec::new();
+ let mut buffer: *mut USER_INFO_0 = null_mut();
+ let mut nb_read = 0;
+ let mut total = 0;
+ let mut resume_handle: DWORD = 0;
+
+ loop {
+ let status = NetUserEnum(
+ null_mut(),
+ 0,
+ FILTER_NORMAL_ACCOUNT,
+ &mut buffer as *mut _ as *mut _,
+ MAX_PREFERRED_LENGTH,
+ &mut nb_read,
+ &mut total,
+ &mut resume_handle as *mut _ as *mut _,
+ );
+ if status == NERR_Success || status == ERROR_MORE_DATA {
+ let entries: &[USER_INFO_0] = std::slice::from_raw_parts(buffer, nb_read as _);
+ for entry in entries {
+ if entry.usri0_name.is_null() {
+ continue;
+ }
+ // let mut user: *mut USER_INFO_23 = null_mut();
+
+ // if NetUserGetInfo(
+ // null_mut(),
+ // entry.usri0_name,
+ // 23,
+ // &mut user as *mut _ as *mut _,
+ // ) == NERR_Success
+ // {
+ // let groups = get_groups_for_user((*user).usri23_name);
+ // users.push(User {
+ // uid: Uid(name.clone().into_boxed_str()),
+ // gid: Gid(0),
+ // name: to_str((*user).usri23_name),
+ // groups,
+ // });
+ // }
+ // if !user.is_null() {
+ // NetApiBufferFree(user as *mut _);
+ // }
+ let groups = get_groups_for_user(entry.usri0_name);
+ let name = to_str(entry.usri0_name);
+ users.push(User {
+ uid: Uid(name.clone().into_boxed_str()),
+ gid: Gid(0),
+ name,
+ groups,
+ });
+ }
+ } else {
+ sysinfo_debug!(
+ "NetUserEnum error: {}",
+ if status == winapi::shared::winerror::ERROR_ACCESS_DENIED {
+ "access denied"
+ } else if status == winapi::shared::winerror::ERROR_INVALID_LEVEL {
+ "invalid level"
+ } else {
+ "unknown error"
+ }
+ );
+ }
+ if !buffer.is_null() {
+ NetApiBufferFree(buffer as *mut _);
+ buffer = null_mut();
+ }
+ if status != ERROR_MORE_DATA {
+ break;
+ }
+ }
+
+ // First part done. Second part now!
+ let mut nb_sessions = 0;
+ let mut uids: PLUID = null_mut();
+ if LsaEnumerateLogonSessions(&mut nb_sessions, &mut uids) != STATUS_SUCCESS {
+ sysinfo_debug!("LsaEnumerateLogonSessions failed");
+ } else {
+ for offset in 0..nb_sessions {
+ let entry = uids.add(offset as _);
+ let mut data: PSECURITY_LOGON_SESSION_DATA = null_mut();
+
+ if LsaGetLogonSessionData(entry, &mut data) == STATUS_SUCCESS && !data.is_null() {
+ let data = *data;
+ if data.LogonType == winapi::um::ntlsa::Network {
+ continue;
+ }
+ let name = to_str(data.UserName.Buffer);
+ if users.iter().any(|u| u.name == name) {
+ continue;
+ }
+ users.push(User {
+ uid: Uid(name.clone().into_boxed_str()),
+ gid: Gid(0),
+ name,
+ // There is no local groups for a non-local user.
+ groups: Vec::new(),
+ });
+ }
+ if !data.is_null() {
+ LsaFreeReturnBuffer(data as *mut _);
+ }
+ }
+ }
+
+ users
+}
diff --git a/vendor/sysinfo/src/windows/utils.rs b/vendor/sysinfo/src/windows/utils.rs
new file mode 100644
index 000000000..419ee195c
--- /dev/null
+++ b/vendor/sysinfo/src/windows/utils.rs
@@ -0,0 +1,36 @@
+// Take a look at the license at the top of the repository in the LICENSE file.
+
+use winapi::shared::minwindef::FILETIME;
+use winapi::um::winnt::LPWSTR;
+
+use std::time::SystemTime;
+
+#[inline]
+pub(crate) fn filetime_to_u64(f: FILETIME) -> u64 {
+ (f.dwHighDateTime as u64) << 32 | (f.dwLowDateTime as u64)
+}
+
+#[inline]
+pub(crate) fn get_now() -> u64 {
+ SystemTime::now()
+ .duration_since(SystemTime::UNIX_EPOCH)
+ .map(|n| n.as_secs())
+ .unwrap_or(0)
+}
+
+pub(crate) unsafe fn to_str(p: LPWSTR) -> String {
+ let mut i = 0;
+
+ loop {
+ let c = *p.offset(i);
+ if c == 0 {
+ break;
+ }
+ i += 1;
+ }
+ let s = std::slice::from_raw_parts(p, i as _);
+ String::from_utf16(s).unwrap_or_else(|_e| {
+ sysinfo_debug!("Failed to convert to UTF-16 string: {}", _e);
+ String::new()
+ })
+}