]>
Commit | Line | Data |
---|---|---|
923072b8 FG |
1 | // Take a look at the license at the top of the repository in the LICENSE file. |
2 | ||
3 | use crate::{ | |
4 | CpuRefreshKind, LoadAvg, Networks, Pid, ProcessExt, ProcessRefreshKind, RefreshKind, SystemExt, | |
5 | User, | |
6 | }; | |
7 | use winapi::um::winreg::HKEY_LOCAL_MACHINE; | |
8 | ||
9 | use crate::sys::component::{self, Component}; | |
10 | use crate::sys::cpu::*; | |
11 | use crate::sys::disk::{get_disks, Disk}; | |
487cf647 | 12 | use crate::sys::process::{get_start_time, update_memory, Process}; |
923072b8 FG |
13 | use crate::sys::tools::*; |
14 | use crate::sys::users::get_users; | |
15 | use crate::sys::utils::get_now; | |
16 | ||
17 | use crate::utils::into_iter; | |
18 | ||
19 | use std::cell::UnsafeCell; | |
20 | use std::collections::HashMap; | |
21 | use std::ffi::OsStr; | |
22 | use std::mem::{size_of, zeroed}; | |
23 | use std::os::windows::ffi::OsStrExt; | |
24 | use std::slice::from_raw_parts; | |
25 | use std::time::SystemTime; | |
26 | ||
27 | use ntapi::ntexapi::{ | |
28 | NtQuerySystemInformation, SystemProcessInformation, SYSTEM_PROCESS_INFORMATION, | |
29 | }; | |
30 | use winapi::ctypes::wchar_t; | |
31 | use winapi::shared::minwindef::{DWORD, FALSE, HKEY, LPBYTE, TRUE}; | |
32 | use winapi::shared::ntdef::{PVOID, ULONG}; | |
33 | use winapi::shared::ntstatus::STATUS_INFO_LENGTH_MISMATCH; | |
34 | use winapi::shared::winerror; | |
35 | use winapi::um::minwinbase::STILL_ACTIVE; | |
36 | use winapi::um::processthreadsapi::GetExitCodeProcess; | |
37 | use winapi::um::psapi::{GetPerformanceInfo, PERFORMANCE_INFORMATION}; | |
38 | use winapi::um::sysinfoapi::{ | |
39 | ComputerNamePhysicalDnsHostname, GetComputerNameExW, GetTickCount64, GlobalMemoryStatusEx, | |
40 | MEMORYSTATUSEX, | |
41 | }; | |
42 | use winapi::um::winnt::{HANDLE, KEY_READ}; | |
43 | use winapi::um::winreg::{RegOpenKeyExW, RegQueryValueExW}; | |
44 | ||
45 | declare_signals! { | |
46 | (), | |
47 | Signal::Kill => (), | |
48 | _ => None, | |
49 | } | |
50 | ||
51 | #[doc = include_str!("../../md_doc/system.md")] | |
52 | pub struct System { | |
53 | process_list: HashMap<Pid, Process>, | |
54 | mem_total: u64, | |
55 | mem_available: u64, | |
56 | swap_total: u64, | |
57 | swap_used: u64, | |
58 | cpus: CpusWrapper, | |
59 | components: Vec<Component>, | |
60 | disks: Vec<Disk>, | |
61 | query: Option<Query>, | |
62 | networks: Networks, | |
63 | boot_time: u64, | |
64 | users: Vec<User>, | |
65 | } | |
66 | ||
487cf647 FG |
67 | static WINDOWS_ELEVEN_BUILD_NUMBER: u32 = 22000; |
68 | ||
69 | impl System { | |
70 | fn is_windows_eleven(&self) -> bool { | |
71 | WINDOWS_ELEVEN_BUILD_NUMBER | |
72 | <= self | |
73 | .kernel_version() | |
74 | .unwrap_or_default() | |
75 | .parse() | |
76 | .unwrap_or(0) | |
77 | } | |
78 | } | |
79 | ||
923072b8 FG |
80 | // Useful for parallel iterations. |
81 | struct Wrap<T>(T); | |
82 | ||
83 | #[allow(clippy::non_send_fields_in_send_ty)] | |
84 | unsafe impl<T> Send for Wrap<T> {} | |
85 | unsafe impl<T> Sync for Wrap<T> {} | |
86 | ||
87 | unsafe fn boot_time() -> u64 { | |
88 | match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) { | |
487cf647 | 89 | Ok(n) => n.as_secs().saturating_sub(GetTickCount64() / 1_000), |
923072b8 FG |
90 | Err(_e) => { |
91 | sysinfo_debug!("Failed to compute boot time: {:?}", _e); | |
92 | 0 | |
93 | } | |
94 | } | |
95 | } | |
96 | ||
97 | impl SystemExt for System { | |
98 | const IS_SUPPORTED: bool = true; | |
99 | const SUPPORTED_SIGNALS: &'static [Signal] = supported_signals(); | |
100 | ||
101 | #[allow(non_snake_case)] | |
102 | fn new_with_specifics(refreshes: RefreshKind) -> System { | |
103 | let mut s = System { | |
104 | process_list: HashMap::with_capacity(500), | |
105 | mem_total: 0, | |
106 | mem_available: 0, | |
107 | swap_total: 0, | |
108 | swap_used: 0, | |
109 | cpus: CpusWrapper::new(), | |
110 | components: Vec::new(), | |
111 | disks: Vec::with_capacity(2), | |
112 | query: None, | |
113 | networks: Networks::new(), | |
114 | boot_time: unsafe { boot_time() }, | |
115 | users: Vec::new(), | |
116 | }; | |
117 | s.refresh_specifics(refreshes); | |
118 | s | |
119 | } | |
120 | ||
121 | fn refresh_cpu_specifics(&mut self, refresh_kind: CpuRefreshKind) { | |
122 | if self.query.is_none() { | |
123 | self.query = Query::new(); | |
124 | if let Some(ref mut query) = self.query { | |
125 | add_english_counter( | |
064997fb | 126 | r"\Processor(_Total)\% Processor Time".to_string(), |
923072b8 FG |
127 | query, |
128 | get_key_used(self.cpus.global_cpu_mut()), | |
129 | "tot_0".to_owned(), | |
130 | ); | |
131 | for (pos, proc_) in self.cpus.iter_mut(refresh_kind).enumerate() { | |
132 | add_english_counter( | |
487cf647 | 133 | format!(r"\Processor({pos})\% Processor Time"), |
923072b8 FG |
134 | query, |
135 | get_key_used(proc_), | |
487cf647 | 136 | format!("{pos}_0"), |
923072b8 FG |
137 | ); |
138 | } | |
139 | } | |
140 | } | |
141 | if let Some(ref mut query) = self.query { | |
142 | query.refresh(); | |
143 | let mut used_time = None; | |
144 | if let Some(ref key_used) = *get_key_used(self.cpus.global_cpu_mut()) { | |
145 | used_time = Some( | |
146 | query | |
147 | .get(&key_used.unique_id) | |
148 | .expect("global_key_idle disappeared"), | |
149 | ); | |
150 | } | |
151 | if let Some(used_time) = used_time { | |
152 | self.cpus.global_cpu_mut().set_cpu_usage(used_time); | |
153 | } | |
154 | for p in self.cpus.iter_mut(refresh_kind) { | |
155 | let mut used_time = None; | |
156 | if let Some(ref key_used) = *get_key_used(p) { | |
157 | used_time = Some( | |
158 | query | |
159 | .get(&key_used.unique_id) | |
160 | .expect("key_used disappeared"), | |
161 | ); | |
162 | } | |
163 | if let Some(used_time) = used_time { | |
164 | p.set_cpu_usage(used_time); | |
165 | } | |
166 | } | |
167 | if refresh_kind.frequency() { | |
168 | self.cpus.get_frequencies(); | |
169 | } | |
170 | } | |
171 | } | |
172 | ||
173 | fn refresh_memory(&mut self) { | |
174 | unsafe { | |
175 | let mut mem_info: MEMORYSTATUSEX = zeroed(); | |
176 | mem_info.dwLength = size_of::<MEMORYSTATUSEX>() as u32; | |
177 | GlobalMemoryStatusEx(&mut mem_info); | |
487cf647 FG |
178 | self.mem_total = mem_info.ullTotalPhys as _; |
179 | self.mem_available = mem_info.ullAvailPhys as _; | |
923072b8 FG |
180 | let mut perf_info: PERFORMANCE_INFORMATION = zeroed(); |
181 | if GetPerformanceInfo(&mut perf_info, size_of::<PERFORMANCE_INFORMATION>() as u32) | |
182 | == TRUE | |
183 | { | |
184 | let swap_total = perf_info.PageSize.saturating_mul( | |
185 | perf_info | |
186 | .CommitLimit | |
187 | .saturating_sub(perf_info.PhysicalTotal), | |
188 | ); | |
189 | let swap_used = perf_info.PageSize.saturating_mul( | |
190 | perf_info | |
191 | .CommitTotal | |
192 | .saturating_sub(perf_info.PhysicalTotal), | |
193 | ); | |
487cf647 FG |
194 | self.swap_total = swap_total as _; |
195 | self.swap_used = swap_used as _; | |
923072b8 FG |
196 | } |
197 | } | |
198 | } | |
199 | ||
200 | fn refresh_components_list(&mut self) { | |
201 | self.components = component::get_components(); | |
202 | } | |
203 | ||
204 | #[allow(clippy::map_entry)] | |
205 | fn refresh_process_specifics(&mut self, pid: Pid, refresh_kind: ProcessRefreshKind) -> bool { | |
923072b8 | 206 | let now = get_now(); |
487cf647 FG |
207 | let nb_cpus = self.cpus.len() as u64; |
208 | ||
209 | if let Some(proc_) = self.process_list.get_mut(&pid) { | |
210 | if let Some(ret) = refresh_existing_process(proc_, nb_cpus, now, refresh_kind) { | |
211 | return ret; | |
212 | } | |
213 | // We need to re-make the process because the PID owner changed. | |
214 | } | |
923072b8 | 215 | if let Some(mut p) = Process::new_from_pid(pid, now, refresh_kind) { |
487cf647 | 216 | p.update(refresh_kind, nb_cpus, now); |
923072b8 FG |
217 | p.updated = false; |
218 | self.process_list.insert(pid, p); | |
219 | true | |
220 | } else { | |
221 | false | |
222 | } | |
223 | } | |
224 | ||
225 | #[allow(clippy::cast_ptr_alignment)] | |
226 | fn refresh_processes_specifics(&mut self, refresh_kind: ProcessRefreshKind) { | |
227 | // Windows 10 notebook requires at least 512KiB of memory to make it in one go | |
228 | let mut buffer_size: usize = 512 * 1024; | |
229 | let now = get_now(); | |
230 | ||
231 | loop { | |
232 | let mut process_information: Vec<u8> = Vec::with_capacity(buffer_size); | |
233 | let mut cb_needed = 0; | |
234 | ||
235 | unsafe { | |
236 | process_information.set_len(buffer_size); | |
237 | let ntstatus = NtQuerySystemInformation( | |
238 | SystemProcessInformation, | |
239 | process_information.as_mut_ptr() as PVOID, | |
240 | buffer_size as ULONG, | |
241 | &mut cb_needed, | |
242 | ); | |
243 | ||
244 | if ntstatus != STATUS_INFO_LENGTH_MISMATCH { | |
245 | if ntstatus < 0 { | |
246 | sysinfo_debug!( | |
247 | "Couldn't get process infos: NtQuerySystemInformation returned {}", | |
248 | ntstatus | |
249 | ); | |
250 | } | |
251 | ||
252 | // Parse the data block to get process information | |
253 | let mut process_ids = Vec::with_capacity(500); | |
254 | let mut process_information_offset = 0; | |
255 | loop { | |
256 | let p = process_information | |
257 | .as_ptr() | |
258 | .offset(process_information_offset) | |
259 | as *const SYSTEM_PROCESS_INFORMATION; | |
260 | let pi = &*p; | |
261 | ||
262 | process_ids.push(Wrap(p)); | |
263 | ||
264 | if pi.NextEntryOffset == 0 { | |
265 | break; | |
266 | } | |
267 | ||
268 | process_information_offset += pi.NextEntryOffset as isize; | |
269 | } | |
270 | let process_list = Wrap(UnsafeCell::new(&mut self.process_list)); | |
271 | let nb_cpus = if refresh_kind.cpu() { | |
272 | self.cpus.len() as u64 | |
273 | } else { | |
274 | 0 | |
275 | }; | |
276 | ||
277 | #[cfg(feature = "multithread")] | |
278 | use rayon::iter::ParallelIterator; | |
279 | ||
280 | // TODO: instead of using parallel iterator only here, would be better to be | |
281 | // able to run it over `process_information` directly! | |
282 | let processes = into_iter(process_ids) | |
283 | .filter_map(|pi| { | |
284 | let pi = *pi.0; | |
285 | let pid = Pid(pi.UniqueProcessId as _); | |
286 | if let Some(proc_) = (*process_list.0.get()).get_mut(&pid) { | |
487cf647 FG |
287 | if proc_ |
288 | .get_start_time() | |
289 | .map(|start| start == proc_.start_time()) | |
290 | .unwrap_or(true) | |
291 | { | |
292 | proc_.memory = pi.WorkingSetSize as _; | |
293 | proc_.virtual_memory = pi.VirtualSize as _; | |
294 | proc_.update(refresh_kind, nb_cpus, now); | |
295 | return None; | |
296 | } | |
297 | // If the PID owner changed, we need to recompute the whole process. | |
298 | sysinfo_debug!("owner changed for PID {}", proc_.pid()); | |
923072b8 FG |
299 | } |
300 | let name = get_process_name(&pi, pid); | |
301 | let mut p = Process::new_full( | |
302 | pid, | |
303 | if pi.InheritedFromUniqueProcessId as usize != 0 { | |
304 | Some(Pid(pi.InheritedFromUniqueProcessId as _)) | |
305 | } else { | |
306 | None | |
307 | }, | |
487cf647 FG |
308 | pi.WorkingSetSize as _, |
309 | pi.VirtualSize as _, | |
923072b8 FG |
310 | name, |
311 | now, | |
312 | refresh_kind, | |
313 | ); | |
314 | p.update(refresh_kind, nb_cpus, now); | |
315 | Some(p) | |
316 | }) | |
317 | .collect::<Vec<_>>(); | |
318 | for p in processes.into_iter() { | |
319 | self.process_list.insert(p.pid(), p); | |
320 | } | |
321 | self.process_list.retain(|_, v| { | |
322 | let x = v.updated; | |
323 | v.updated = false; | |
324 | x | |
325 | }); | |
326 | ||
327 | break; | |
328 | } | |
329 | ||
330 | // GetNewBufferSize | |
331 | if cb_needed == 0 { | |
332 | buffer_size *= 2; | |
333 | continue; | |
334 | } | |
335 | // allocating a few more kilo bytes just in case there are some new process | |
336 | // kicked in since new call to NtQuerySystemInformation | |
337 | buffer_size = (cb_needed + (1024 * 10)) as usize; | |
338 | } | |
339 | } | |
340 | } | |
341 | ||
342 | fn refresh_disks_list(&mut self) { | |
343 | self.disks = unsafe { get_disks() }; | |
344 | } | |
345 | ||
346 | fn refresh_users_list(&mut self) { | |
347 | self.users = unsafe { get_users() }; | |
348 | } | |
349 | ||
350 | fn processes(&self) -> &HashMap<Pid, Process> { | |
351 | &self.process_list | |
352 | } | |
353 | ||
354 | fn process(&self, pid: Pid) -> Option<&Process> { | |
355 | self.process_list.get(&pid) | |
356 | } | |
357 | ||
358 | fn global_cpu_info(&self) -> &Cpu { | |
359 | self.cpus.global_cpu() | |
360 | } | |
361 | ||
362 | fn cpus(&self) -> &[Cpu] { | |
363 | self.cpus.cpus() | |
364 | } | |
365 | ||
366 | fn physical_core_count(&self) -> Option<usize> { | |
367 | get_physical_core_count() | |
368 | } | |
369 | ||
370 | fn total_memory(&self) -> u64 { | |
371 | self.mem_total | |
372 | } | |
373 | ||
374 | fn free_memory(&self) -> u64 { | |
375 | // MEMORYSTATUSEX doesn't report free memory | |
376 | self.mem_available | |
377 | } | |
378 | ||
379 | fn available_memory(&self) -> u64 { | |
380 | self.mem_available | |
381 | } | |
382 | ||
383 | fn used_memory(&self) -> u64 { | |
384 | self.mem_total - self.mem_available | |
385 | } | |
386 | ||
387 | fn total_swap(&self) -> u64 { | |
388 | self.swap_total | |
389 | } | |
390 | ||
391 | fn free_swap(&self) -> u64 { | |
392 | self.swap_total - self.swap_used | |
393 | } | |
394 | ||
395 | fn used_swap(&self) -> u64 { | |
396 | self.swap_used | |
397 | } | |
398 | ||
399 | fn components(&self) -> &[Component] { | |
400 | &self.components | |
401 | } | |
402 | ||
403 | fn components_mut(&mut self) -> &mut [Component] { | |
404 | &mut self.components | |
405 | } | |
406 | ||
407 | fn disks(&self) -> &[Disk] { | |
408 | &self.disks | |
409 | } | |
410 | ||
411 | fn disks_mut(&mut self) -> &mut [Disk] { | |
412 | &mut self.disks | |
413 | } | |
414 | ||
487cf647 FG |
415 | fn sort_disks_by<F>(&mut self, compare: F) |
416 | where | |
417 | F: FnMut(&Disk, &Disk) -> std::cmp::Ordering, | |
418 | { | |
419 | self.disks.sort_unstable_by(compare); | |
420 | } | |
421 | ||
923072b8 FG |
422 | fn users(&self) -> &[User] { |
423 | &self.users | |
424 | } | |
425 | ||
426 | fn networks(&self) -> &Networks { | |
427 | &self.networks | |
428 | } | |
429 | ||
430 | fn networks_mut(&mut self) -> &mut Networks { | |
431 | &mut self.networks | |
432 | } | |
433 | ||
434 | fn uptime(&self) -> u64 { | |
487cf647 | 435 | unsafe { GetTickCount64() / 1_000 } |
923072b8 FG |
436 | } |
437 | ||
438 | fn boot_time(&self) -> u64 { | |
439 | self.boot_time | |
440 | } | |
441 | ||
442 | fn load_average(&self) -> LoadAvg { | |
443 | get_load_average() | |
444 | } | |
445 | ||
446 | fn name(&self) -> Option<String> { | |
447 | Some("Windows".to_owned()) | |
448 | } | |
449 | ||
450 | fn long_os_version(&self) -> Option<String> { | |
487cf647 FG |
451 | if self.is_windows_eleven() { |
452 | return get_reg_string_value( | |
453 | HKEY_LOCAL_MACHINE, | |
454 | "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", | |
455 | "ProductName", | |
456 | ) | |
457 | .map(|product_name| product_name.replace("Windows 10 ", "Windows 11 ")); | |
458 | } | |
923072b8 FG |
459 | get_reg_string_value( |
460 | HKEY_LOCAL_MACHINE, | |
461 | "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", | |
462 | "ProductName", | |
463 | ) | |
464 | } | |
465 | ||
466 | fn host_name(&self) -> Option<String> { | |
467 | get_dns_hostname() | |
468 | } | |
469 | ||
470 | fn kernel_version(&self) -> Option<String> { | |
471 | get_reg_string_value( | |
472 | HKEY_LOCAL_MACHINE, | |
473 | "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", | |
474 | "CurrentBuildNumber", | |
475 | ) | |
476 | } | |
477 | ||
478 | fn os_version(&self) -> Option<String> { | |
923072b8 FG |
479 | let build_number = get_reg_string_value( |
480 | HKEY_LOCAL_MACHINE, | |
481 | "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", | |
482 | "CurrentBuildNumber", | |
487cf647 FG |
483 | ) |
484 | .unwrap_or_default(); | |
485 | let major = if self.is_windows_eleven() { | |
486 | 11u32 | |
487 | } else { | |
488 | u32::from_le_bytes( | |
489 | get_reg_value_u32( | |
490 | HKEY_LOCAL_MACHINE, | |
491 | "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion", | |
492 | "CurrentMajorVersionNumber", | |
493 | ) | |
494 | .unwrap_or_default(), | |
495 | ) | |
496 | }; | |
497 | Some(format!("{major} ({build_number})")) | |
498 | } | |
923072b8 | 499 | |
487cf647 FG |
500 | fn distribution_id(&self) -> String { |
501 | std::env::consts::OS.to_owned() | |
923072b8 FG |
502 | } |
503 | } | |
504 | ||
505 | impl Default for System { | |
506 | fn default() -> System { | |
507 | System::new() | |
508 | } | |
509 | } | |
510 | ||
487cf647 | 511 | pub(crate) fn is_proc_running(handle: HANDLE) -> bool { |
923072b8 FG |
512 | let mut exit_code = 0; |
513 | unsafe { | |
514 | let ret = GetExitCodeProcess(handle, &mut exit_code); | |
515 | !(ret == FALSE || exit_code != STILL_ACTIVE) | |
516 | } | |
517 | } | |
518 | ||
487cf647 FG |
519 | /// If it returns `None`, it means that the PID owner changed and that the `Process` must be |
520 | /// completely recomputed. | |
521 | fn refresh_existing_process( | |
522 | proc_: &mut Process, | |
523 | nb_cpus: u64, | |
524 | now: u64, | |
525 | refresh_kind: ProcessRefreshKind, | |
526 | ) -> Option<bool> { | |
527 | if let Some(handle) = proc_.get_handle() { | |
528 | if get_start_time(handle) != proc_.start_time() { | |
529 | sysinfo_debug!("owner changed for PID {}", proc_.pid()); | |
530 | // PID owner changed! | |
531 | return None; | |
532 | } | |
533 | if !is_proc_running(handle) { | |
534 | return Some(false); | |
923072b8 | 535 | } |
923072b8 | 536 | } else { |
487cf647 | 537 | return Some(false); |
923072b8 | 538 | } |
487cf647 FG |
539 | update_memory(proc_); |
540 | proc_.update(refresh_kind, nb_cpus, now); | |
541 | proc_.updated = false; | |
542 | Some(true) | |
923072b8 FG |
543 | } |
544 | ||
545 | #[allow(clippy::size_of_in_element_count)] | |
546 | //^ needed for "name.Length as usize / std::mem::size_of::<u16>()" | |
547 | pub(crate) fn get_process_name(process: &SYSTEM_PROCESS_INFORMATION, process_id: Pid) -> String { | |
548 | let name = &process.ImageName; | |
549 | if name.Buffer.is_null() { | |
550 | match process_id.0 { | |
551 | 0 => "Idle".to_owned(), | |
552 | 4 => "System".to_owned(), | |
487cf647 | 553 | _ => format!("<no name> Process {process_id}"), |
923072b8 FG |
554 | } |
555 | } else { | |
556 | unsafe { | |
557 | let slice = std::slice::from_raw_parts( | |
558 | name.Buffer, | |
559 | // The length is in bytes, not the length of string | |
560 | name.Length as usize / std::mem::size_of::<u16>(), | |
561 | ); | |
562 | ||
563 | String::from_utf16_lossy(slice) | |
564 | } | |
565 | } | |
566 | } | |
567 | ||
568 | fn utf16_str<S: AsRef<OsStr> + ?Sized>(text: &S) -> Vec<u16> { | |
569 | OsStr::new(text) | |
570 | .encode_wide() | |
571 | .chain(Some(0).into_iter()) | |
572 | .collect::<Vec<_>>() | |
573 | } | |
574 | ||
575 | fn get_reg_string_value(hkey: HKEY, path: &str, field_name: &str) -> Option<String> { | |
576 | let c_path = utf16_str(path); | |
577 | let c_field_name = utf16_str(field_name); | |
578 | ||
579 | let mut new_hkey: HKEY = std::ptr::null_mut(); | |
580 | unsafe { | |
581 | if RegOpenKeyExW(hkey, c_path.as_ptr(), 0, KEY_READ, &mut new_hkey) != 0 { | |
582 | return None; | |
583 | } | |
584 | ||
585 | let mut buf_len: DWORD = 2048; | |
586 | let mut buf_type: DWORD = 0; | |
587 | let mut buf: Vec<u8> = Vec::with_capacity(buf_len as usize); | |
588 | loop { | |
589 | match RegQueryValueExW( | |
590 | new_hkey, | |
591 | c_field_name.as_ptr(), | |
592 | std::ptr::null_mut(), | |
593 | &mut buf_type, | |
594 | buf.as_mut_ptr() as LPBYTE, | |
595 | &mut buf_len, | |
596 | ) as DWORD | |
597 | { | |
598 | 0 => break, | |
599 | winerror::ERROR_MORE_DATA => { | |
600 | buf.reserve(buf_len as _); | |
601 | } | |
602 | _ => return None, | |
603 | } | |
604 | } | |
605 | ||
606 | buf.set_len(buf_len as _); | |
607 | ||
608 | let words = from_raw_parts(buf.as_ptr() as *const u16, buf.len() / 2); | |
609 | let mut s = String::from_utf16_lossy(words); | |
610 | while s.ends_with('\u{0}') { | |
611 | s.pop(); | |
612 | } | |
613 | Some(s) | |
614 | } | |
615 | } | |
616 | ||
617 | fn get_reg_value_u32(hkey: HKEY, path: &str, field_name: &str) -> Option<[u8; 4]> { | |
618 | let c_path = utf16_str(path); | |
619 | let c_field_name = utf16_str(field_name); | |
620 | ||
621 | let mut new_hkey: HKEY = std::ptr::null_mut(); | |
622 | unsafe { | |
623 | if RegOpenKeyExW(hkey, c_path.as_ptr(), 0, KEY_READ, &mut new_hkey) != 0 { | |
624 | return None; | |
625 | } | |
626 | ||
627 | let mut buf_len: DWORD = 4; | |
628 | let mut buf_type: DWORD = 0; | |
629 | let mut buf = [0u8; 4]; | |
630 | ||
631 | match RegQueryValueExW( | |
632 | new_hkey, | |
633 | c_field_name.as_ptr(), | |
634 | std::ptr::null_mut(), | |
635 | &mut buf_type, | |
636 | buf.as_mut_ptr() as LPBYTE, | |
637 | &mut buf_len, | |
638 | ) as DWORD | |
639 | { | |
640 | 0 => Some(buf), | |
641 | _ => None, | |
642 | } | |
643 | } | |
644 | } | |
645 | ||
646 | fn get_dns_hostname() -> Option<String> { | |
647 | let mut buffer_size = 0; | |
648 | // Running this first to get the buffer size since the DNS name can be longer than MAX_COMPUTERNAME_LENGTH | |
649 | // setting the `lpBuffer` to null will return the buffer size | |
650 | // https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getcomputernameexw | |
651 | unsafe { | |
652 | GetComputerNameExW( | |
653 | ComputerNamePhysicalDnsHostname, | |
654 | std::ptr::null_mut(), | |
655 | &mut buffer_size, | |
656 | ); | |
657 | ||
658 | // Setting the buffer with the new length | |
659 | let mut buffer = vec![0_u16; buffer_size as usize]; | |
660 | ||
661 | // https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/ne-sysinfoapi-computer_name_format | |
662 | if GetComputerNameExW( | |
663 | ComputerNamePhysicalDnsHostname, | |
664 | buffer.as_mut_ptr() as *mut wchar_t, | |
665 | &mut buffer_size, | |
666 | ) == TRUE | |
667 | { | |
668 | if let Some(pos) = buffer.iter().position(|c| *c == 0) { | |
669 | buffer.resize(pos, 0); | |
670 | } | |
671 | ||
672 | return String::from_utf16(&buffer).ok(); | |
673 | } | |
674 | } | |
675 | ||
676 | sysinfo_debug!("Failed to get computer hostname"); | |
677 | None | |
678 | } |