1use crate::{
4 Cpu, CpuRefreshKind, LoadAvg, MemoryRefreshKind, Pid, ProcessRefreshKind, ProcessesToUpdate,
5};
6
7use crate::sys::cpu::*;
8use crate::{Process, ProcessInner};
9
10use std::collections::HashMap;
11use std::ffi::OsStr;
12use std::mem::{size_of, zeroed};
13use std::os::windows::ffi::OsStrExt;
14use std::time::{Duration, SystemTime};
15
16use windows::Win32::Foundation::{self, HANDLE, STILL_ACTIVE};
17use windows::Win32::System::Diagnostics::ToolHelp::{
18 CreateToolhelp32Snapshot, PROCESSENTRY32W, Process32FirstW, Process32NextW, TH32CS_SNAPPROCESS,
19};
20use windows::Win32::System::ProcessStatus::{K32GetPerformanceInfo, PERFORMANCE_INFORMATION};
21use windows::Win32::System::Registry::{
22 HKEY, HKEY_LOCAL_MACHINE, KEY_READ, REG_NONE, RegCloseKey, RegOpenKeyExW, RegQueryValueExW,
23};
24use windows::Win32::System::SystemInformation::{self, GetSystemInfo};
25use windows::Win32::System::SystemInformation::{
26 ComputerNamePhysicalDnsHostname, GetComputerNameExW, GetTickCount64, GlobalMemoryStatusEx,
27 MEMORYSTATUSEX, SYSTEM_INFO,
28};
29use windows::Win32::System::Threading::GetExitCodeProcess;
30use windows::core::{Owned, PCWSTR, PWSTR};
31
32declare_signals! {
33 (),
34 Signal::Kill => (),
35 _ => None,
36}
37
38#[doc = include_str!("../../md_doc/supported_signals.md")]
39pub const SUPPORTED_SIGNALS: &[crate::Signal] = supported_signals();
40#[doc = include_str!("../../md_doc/minimum_cpu_update_interval.md")]
41pub const MINIMUM_CPU_UPDATE_INTERVAL: Duration = Duration::from_millis(200);
42
43const WINDOWS_ELEVEN_BUILD_NUMBER: u32 = 22000;
44
45impl SystemInner {
46 fn is_windows_eleven() -> bool {
47 WINDOWS_ELEVEN_BUILD_NUMBER
48 <= Self::kernel_version()
49 .unwrap_or_default()
50 .parse()
51 .unwrap_or(0)
52 }
53}
54
55unsafe fn boot_time() -> u64 {
60 match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) {
61 Ok(n) => {
62 let system_time_ns = n.as_nanos();
63 let tick_count_ns = unsafe { GetTickCount64() } as u128 * 1_000_000;
65 let boot_time_sec = system_time_ns.saturating_sub(tick_count_ns) / 1_000_000_000;
67 boot_time_sec.try_into().unwrap_or(u64::MAX)
68 }
69 Err(_e) => {
70 sysinfo_debug!("Failed to compute boot time: {:?}", _e);
71 0
72 }
73 }
74}
75
76pub(crate) struct SystemInner {
77 process_list: HashMap<Pid, Process>,
78 mem_total: u64,
79 mem_available: u64,
80 swap_total: u64,
81 swap_used: u64,
82 cpus: CpusWrapper,
83 query: Option<Query>,
84}
85
86impl SystemInner {
87 pub(crate) fn new() -> Self {
88 Self {
89 process_list: HashMap::with_capacity(500),
90 mem_total: 0,
91 mem_available: 0,
92 swap_total: 0,
93 swap_used: 0,
94 cpus: CpusWrapper::new(),
95 query: None,
96 }
97 }
98
99 fn initialize_cpu_counters(&mut self, refresh_kind: CpuRefreshKind) {
100 if let Some(ref mut query) = self.query {
101 add_english_counter(
102 r"\Processor(_Total)\% Idle Time".to_string(),
103 query,
104 &mut self.cpus.global.key_used,
105 "tot_0".to_owned(),
106 );
107 for (pos, proc_) in self.cpus.iter_mut(refresh_kind).enumerate() {
108 add_english_counter(
109 format!(r"\Processor({pos})\% Idle Time"),
110 query,
111 get_key_used(proc_),
112 format!("{pos}_0"),
113 );
114 }
115 }
116 }
117
118 pub(crate) fn refresh_cpu_specifics(&mut self, refresh_kind: CpuRefreshKind) {
119 if self.query.is_none() {
120 self.query = Query::new(false);
121 self.initialize_cpu_counters(refresh_kind);
122 } else if self.cpus.global.key_used.is_none() {
123 self.query = Query::new(true);
124 self.initialize_cpu_counters(refresh_kind);
125 }
126 if let Some(ref mut query) = self.query {
127 query.refresh();
128 let mut total_idle_time = None;
129 if let Some(ref key_used) = self.cpus.global.key_used {
130 total_idle_time = Some(
131 query
132 .get(&key_used.unique_id)
133 .expect("global_key_idle disappeared"),
134 );
135 }
136 if let Some(total_idle_time) = total_idle_time {
137 self.cpus.global.set_cpu_usage(100.0 - total_idle_time);
138 }
139 for cpu in self.cpus.iter_mut(refresh_kind) {
140 let mut idle_time = None;
141 if let Some(ref key_used) = *get_key_used(cpu) {
142 idle_time = Some(
143 query
144 .get(&key_used.unique_id)
145 .expect("key_used disappeared"),
146 );
147 }
148 if let Some(idle_time) = idle_time {
149 cpu.inner.set_cpu_usage(100.0 - idle_time);
150 }
151 }
152 if refresh_kind.frequency() {
153 self.cpus.get_frequencies();
154 }
155 }
156 }
157
158 pub(crate) fn refresh_cpu_list(&mut self, refresh_kind: CpuRefreshKind) {
159 self.cpus = CpusWrapper::new();
160 self.refresh_cpu_specifics(refresh_kind);
161 }
162
163 pub(crate) fn refresh_memory_specifics(&mut self, refresh_kind: MemoryRefreshKind) {
164 unsafe {
165 if refresh_kind.ram() {
166 let mut mem_info: MEMORYSTATUSEX = zeroed();
167 mem_info.dwLength = size_of::<MEMORYSTATUSEX>() as _;
168 let _err = GlobalMemoryStatusEx(&mut mem_info);
169 self.mem_total = mem_info.ullTotalPhys as _;
170 self.mem_available = mem_info.ullAvailPhys as _;
171 }
172 if refresh_kind.swap() {
173 let mut perf_info: PERFORMANCE_INFORMATION = zeroed();
174 if K32GetPerformanceInfo(&mut perf_info, size_of::<PERFORMANCE_INFORMATION>() as _)
175 .as_bool()
176 {
177 let page_size = perf_info.PageSize as u64;
178 let physical_total = perf_info.PhysicalTotal as u64;
179 let commit_limit = perf_info.CommitLimit as u64;
180 let commit_total = perf_info.CommitTotal as u64;
181 self.swap_total =
182 page_size.saturating_mul(commit_limit.saturating_sub(physical_total));
183 self.swap_used =
184 page_size.saturating_mul(commit_total.saturating_sub(physical_total));
185 }
186 }
187 }
188 }
189
190 pub(crate) fn cgroup_limits(&self) -> Option<crate::CGroupLimits> {
191 None
192 }
193
194 #[allow(clippy::cast_ptr_alignment)]
195 pub(crate) fn refresh_processes_specifics(
196 &mut self,
197 processes_to_update: ProcessesToUpdate<'_>,
198 refresh_kind: ProcessRefreshKind,
199 ) -> usize {
200 #[inline(always)]
201 fn real_filter(e: Pid, filter: &[Pid]) -> bool {
202 filter.contains(&e)
203 }
204
205 #[inline(always)]
206 fn empty_filter(_e: Pid, _filter: &[Pid]) -> bool {
207 true
208 }
209
210 #[allow(clippy::type_complexity)]
211 let (filter_array, filter_callback): (
212 &[Pid],
213 &(dyn Fn(Pid, &[Pid]) -> bool + Sync + Send),
214 ) = match processes_to_update {
215 ProcessesToUpdate::All => (&[], &empty_filter),
216 ProcessesToUpdate::Some(pids) => {
217 if pids.is_empty() {
218 return 0;
219 }
220 (pids, &real_filter)
221 }
222 };
223
224 let now = get_now();
225
226 let nb_cpus = if refresh_kind.cpu() {
227 self.cpus.len() as u64
228 } else {
229 0
230 };
231
232 let snapshot = match unsafe { CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0) } {
235 Ok(handle) => handle,
236 Err(_err) => {
237 sysinfo_debug!(
238 "Error capturing process snapshot: CreateToolhelp32Snapshot returned {}",
239 _err
240 );
241 return 0;
242 }
243 };
244
245 let snapshot = unsafe { Owned::new(snapshot) };
247
248 let mut process_entry = PROCESSENTRY32W {
252 dwSize: size_of::<PROCESSENTRY32W>() as u32,
253 ..Default::default()
254 };
255
256 let mut num_procs = 0; let process_list = &mut self.process_list;
258
259 unsafe {
261 if let Err(_error) = Process32FirstW(*snapshot, &mut process_entry) {
262 sysinfo_debug!("Process32FirstW has failed: {_error:?}");
263 return 0;
264 }
265 }
266
267 loop {
270 let proc_id = Pid::from_u32(process_entry.th32ProcessID);
271
272 if filter_callback(proc_id, filter_array) {
273 if let Some(p) = process_list.get_mut(&proc_id) {
275 let p = &mut p.inner;
277 p.update(refresh_kind, nb_cpus, now, false);
278
279 let parent = if process_entry.th32ParentProcessID == 0 {
281 None
282 } else {
283 Some(Pid::from_u32(process_entry.th32ParentProcessID))
284 };
285
286 p.parent = parent;
287 } else {
288 let mut p = ProcessInner::from_process_entry(&process_entry, now);
290 p.update(refresh_kind, nb_cpus, now, false);
291 process_list.insert(proc_id, Process { inner: p });
292 }
293
294 num_procs += 1;
295 }
296
297 if unsafe { Process32NextW(*snapshot, &mut process_entry).is_err() } {
299 break;
300 }
301 }
302
303 num_procs
304 }
305
306 pub(crate) fn processes(&self) -> &HashMap<Pid, Process> {
307 &self.process_list
308 }
309
310 pub(crate) fn processes_mut(&mut self) -> &mut HashMap<Pid, Process> {
311 &mut self.process_list
312 }
313
314 pub(crate) fn process(&self, pid: Pid) -> Option<&Process> {
315 self.process_list.get(&pid)
316 }
317
318 pub(crate) fn global_cpu_usage(&self) -> f32 {
319 self.cpus.global_cpu_usage()
320 }
321
322 pub(crate) fn cpus(&self) -> &[Cpu] {
323 self.cpus.cpus()
324 }
325
326 pub(crate) fn total_memory(&self) -> u64 {
327 self.mem_total
328 }
329
330 pub(crate) fn free_memory(&self) -> u64 {
331 self.mem_available
333 }
334
335 pub(crate) fn available_memory(&self) -> u64 {
336 self.mem_available
337 }
338
339 pub(crate) fn used_memory(&self) -> u64 {
340 self.mem_total - self.mem_available
341 }
342
343 pub(crate) fn total_swap(&self) -> u64 {
344 self.swap_total
345 }
346
347 pub(crate) fn free_swap(&self) -> u64 {
348 self.swap_total - self.swap_used
349 }
350
351 pub(crate) fn used_swap(&self) -> u64 {
352 self.swap_used
353 }
354
355 pub(crate) fn uptime() -> u64 {
356 unsafe { GetTickCount64() / 1_000 }
357 }
358
359 pub(crate) fn boot_time() -> u64 {
360 unsafe { boot_time() }
361 }
362
363 pub(crate) fn load_average() -> LoadAvg {
364 get_load_average()
365 }
366
367 pub(crate) fn name() -> Option<String> {
368 Some("Windows".to_owned())
369 }
370
371 pub(crate) fn long_os_version() -> Option<String> {
372 if Self::is_windows_eleven() {
373 return get_reg_string_value(
374 HKEY_LOCAL_MACHINE,
375 r"SOFTWARE\Microsoft\Windows NT\CurrentVersion",
376 "ProductName",
377 )
378 .map(|product_name| product_name.replace("Windows 10 ", "Windows 11 "));
379 }
380 get_reg_string_value(
381 HKEY_LOCAL_MACHINE,
382 r"SOFTWARE\Microsoft\Windows NT\CurrentVersion",
383 "ProductName",
384 )
385 }
386
387 pub(crate) fn host_name() -> Option<String> {
388 get_dns_hostname()
389 }
390
391 pub(crate) fn kernel_version() -> Option<String> {
392 get_reg_string_value(
393 HKEY_LOCAL_MACHINE,
394 r"SOFTWARE\Microsoft\Windows NT\CurrentVersion",
395 "CurrentBuildNumber",
396 )
397 }
398
399 pub(crate) fn os_version() -> Option<String> {
400 let build_number = get_reg_string_value(
401 HKEY_LOCAL_MACHINE,
402 r"SOFTWARE\Microsoft\Windows NT\CurrentVersion",
403 "CurrentBuildNumber",
404 )
405 .unwrap_or_default();
406 let major = if Self::is_windows_eleven() {
407 11u32
408 } else {
409 u32::from_le_bytes(
410 get_reg_value_u32(
411 HKEY_LOCAL_MACHINE,
412 r"SOFTWARE\Microsoft\Windows NT\CurrentVersion",
413 "CurrentMajorVersionNumber",
414 )
415 .unwrap_or_default(),
416 )
417 };
418 Some(format!("{major} ({build_number})"))
419 }
420
421 pub(crate) fn distribution_id() -> String {
422 std::env::consts::OS.to_owned()
423 }
424
425 pub(crate) fn distribution_id_like() -> Vec<String> {
426 Vec::new()
427 }
428
429 pub(crate) fn kernel_name() -> Option<&'static str> {
430 Some("Windows")
431 }
432
433 pub(crate) fn cpu_arch() -> Option<String> {
434 unsafe {
435 let mut info = SYSTEM_INFO::default();
437 GetSystemInfo(&mut info);
438 match info.Anonymous.Anonymous.wProcessorArchitecture {
439 SystemInformation::PROCESSOR_ARCHITECTURE_ALPHA => Some("alpha".to_string()),
440 SystemInformation::PROCESSOR_ARCHITECTURE_ALPHA64 => Some("alpha64".to_string()),
441 SystemInformation::PROCESSOR_ARCHITECTURE_AMD64 => Some("x86_64".to_string()),
442 SystemInformation::PROCESSOR_ARCHITECTURE_ARM => Some("arm".to_string()),
443 SystemInformation::PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64 => Some("arm".to_string()),
444 SystemInformation::PROCESSOR_ARCHITECTURE_ARM64 => Some("arm64".to_string()),
445 SystemInformation::PROCESSOR_ARCHITECTURE_IA32_ON_ARM64
446 | SystemInformation::PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 => {
447 Some("ia32".to_string())
448 }
449 SystemInformation::PROCESSOR_ARCHITECTURE_IA64 => Some("ia64".to_string()),
450 SystemInformation::PROCESSOR_ARCHITECTURE_INTEL => Some("x86".to_string()),
451 SystemInformation::PROCESSOR_ARCHITECTURE_MIPS => Some("mips".to_string()),
452 SystemInformation::PROCESSOR_ARCHITECTURE_PPC => Some("powerpc".to_string()),
453 _ => None,
454 }
455 }
456 }
457
458 pub(crate) fn physical_core_count() -> Option<usize> {
459 get_physical_core_count()
460 }
461
462 pub(crate) fn open_files_limit() -> Option<usize> {
463 Some(8192)
475 }
476}
477
478pub(crate) fn is_proc_running(handle: HANDLE) -> bool {
479 let mut exit_code = 0;
480 unsafe { GetExitCodeProcess(handle, &mut exit_code) }.is_ok()
481 && exit_code == STILL_ACTIVE.0 as u32
482}
483
484fn get_dns_hostname() -> Option<String> {
485 let mut buffer_size = 0;
486 unsafe {
490 let _err = GetComputerNameExW(ComputerNamePhysicalDnsHostname, None, &mut buffer_size);
491
492 let mut buffer = vec![0_u16; buffer_size as usize];
494
495 if GetComputerNameExW(
497 ComputerNamePhysicalDnsHostname,
498 Some(PWSTR::from_raw(buffer.as_mut_ptr())),
499 &mut buffer_size,
500 )
501 .is_ok()
502 {
503 if let Some(pos) = buffer.iter().position(|c| *c == 0) {
504 buffer.resize(pos, 0);
505 }
506
507 return String::from_utf16(&buffer).ok();
508 }
509 }
510
511 sysinfo_debug!("Failed to get computer hostname");
512 None
513}
514
515fn add_english_counter(
516 s: String,
517 query: &mut super::cpu::Query,
518 keys: &mut Option<KeyHandler>,
519 counter_name: String,
520) {
521 let mut full = s.encode_utf16().collect::<Vec<_>>();
522 full.push(0);
523 if query.add_english_counter(&counter_name, full) {
524 *keys = Some(KeyHandler::new(counter_name));
525 }
526}
527
528fn get_now() -> u64 {
529 SystemTime::now()
530 .duration_since(SystemTime::UNIX_EPOCH)
531 .map(|n| n.as_secs())
532 .unwrap_or(0)
533}
534
535fn utf16_str<S: AsRef<OsStr> + ?Sized>(text: &S) -> Vec<u16> {
536 OsStr::new(text)
537 .encode_wide()
538 .chain(Some(0))
539 .collect::<Vec<_>>()
540}
541
542struct RegKey(HKEY);
543
544impl RegKey {
545 unsafe fn open(hkey: HKEY, path: &[u16]) -> Option<Self> {
546 let mut new_hkey = Default::default();
547 if unsafe {
548 RegOpenKeyExW(
549 hkey,
550 PCWSTR::from_raw(path.as_ptr()),
551 Some(0),
552 KEY_READ,
553 &mut new_hkey,
554 )
555 }
556 .is_err()
557 {
558 return None;
559 }
560 Some(Self(new_hkey))
561 }
562
563 unsafe fn get_value(
564 &self,
565 field_name: &[u16],
566 buf: &mut [u8],
567 buf_len: &mut u32,
568 ) -> windows::core::Result<()> {
569 let mut buf_type = REG_NONE;
570
571 unsafe {
572 RegQueryValueExW(
573 self.0,
574 PCWSTR::from_raw(field_name.as_ptr()),
575 None,
576 Some(&mut buf_type),
577 Some(buf.as_mut_ptr()),
578 Some(buf_len),
579 )
580 }
581 .ok()
582 }
583}
584
585impl Drop for RegKey {
586 fn drop(&mut self) {
587 let _err = unsafe { RegCloseKey(self.0) };
588 }
589}
590
591pub(crate) fn get_reg_string_value(hkey: HKEY, path: &str, field_name: &str) -> Option<String> {
592 let c_path = utf16_str(path);
593 let c_field_name = utf16_str(field_name);
594
595 unsafe {
596 let new_key = RegKey::open(hkey, &c_path)?;
597 let mut buf_len: u32 = 2048;
598 let mut buf: Vec<u8> = Vec::with_capacity(buf_len as usize);
599
600 loop {
601 match new_key.get_value(&c_field_name, &mut buf, &mut buf_len) {
602 Ok(()) => break,
603 Err(err) if err.code() == Foundation::ERROR_MORE_DATA.to_hresult() => {
604 buf.set_len(buf.capacity());
606 buf.reserve(buf_len as _);
607 }
608 _ => return None,
609 }
610 }
611
612 buf.set_len(buf_len as _);
613
614 let words = std::slice::from_raw_parts(buf.as_ptr() as *const u16, buf.len() / 2);
615 let mut s = String::from_utf16_lossy(words);
616 while s.ends_with('\u{0}') {
617 s.pop();
618 }
619 Some(s)
620 }
621}
622
623pub(crate) fn get_reg_value_u32(hkey: HKEY, path: &str, field_name: &str) -> Option<[u8; 4]> {
624 let c_path = utf16_str(path);
625 let c_field_name = utf16_str(field_name);
626
627 unsafe {
628 let new_key = RegKey::open(hkey, &c_path)?;
629 let mut buf_len: u32 = 4;
630 let mut buf = [0u8; 4];
631
632 new_key
633 .get_value(&c_field_name, &mut buf, &mut buf_len)
634 .map(|_| buf)
635 .ok()
636 }
637}