From 7ced579791849dc3937e68b095f45877918d2a91 Mon Sep 17 00:00:00 2001 From: Christopher Serr Date: Tue, 19 Mar 2024 20:47:35 +0100 Subject: [PATCH] Use `OsString` for `System` This uses `OsString` for the methods on `System`. --- examples/simple.rs | 25 ++++-- src/c_interface.rs | 27 ++++-- src/common.rs | 14 +-- src/unix/apple/system.rs | 89 +++++++++---------- src/unix/freebsd/system.rs | 38 ++++---- src/unix/freebsd/utils.rs | 8 +- src/unix/linux/cpu.rs | 6 +- src/unix/linux/process.rs | 2 +- src/unix/linux/system.rs | 172 ++++++++++++++++++------------------- src/unix/linux/utils.rs | 5 ++ src/unknown/system.rs | 17 ++-- src/windows/system.rs | 73 ++++++++++------ src/windows/utils.rs | 12 ++- tests/system.rs | 2 +- 14 files changed, 267 insertions(+), 223 deletions(-) diff --git a/examples/simple.rs b/examples/simple.rs index 15ce7be45..a6692aa8b 100644 --- a/examples/simple.rs +++ b/examples/simple.rs @@ -440,11 +440,26 @@ fn interpret_input( System OS version: {}\n\ System OS (long) version: {}\n\ System host name: {}", - System::name().unwrap_or_else(|| "".to_owned()), - System::kernel_version().unwrap_or_else(|| "".to_owned()), - System::os_version().unwrap_or_else(|| "".to_owned()), - System::long_os_version().unwrap_or_else(|| "".to_owned()), - System::host_name().unwrap_or_else(|| "".to_owned()), + System::name() + .unwrap_or_else(|| "".into()) + .as_encoded_bytes() + .as_bstr(), + System::kernel_version() + .unwrap_or_else(|| "".into()) + .as_encoded_bytes() + .as_bstr(), + System::os_version() + .unwrap_or_else(|| "".into()) + .as_encoded_bytes() + .as_bstr(), + System::long_os_version() + .unwrap_or_else(|| "".into()) + .as_encoded_bytes() + .as_bstr(), + System::host_name() + .unwrap_or_else(|| "".into()) + .as_encoded_bytes() + .as_bstr(), ); } e => { diff --git a/src/c_interface.rs b/src/c_interface.rs index 33e6649d3..48885c7cf 100644 --- a/src/c_interface.rs +++ b/src/c_interface.rs @@ -586,18 +586,21 @@ pub extern "C" fn sysinfo_cpu_frequency(system: CSystem) -> u64 { /// Equivalent of [`System::name()`][crate::System#method.name]. #[no_mangle] pub extern "C" fn sysinfo_system_name() -> RString { - let c_string = if let Some(c) = System::name().and_then(|p| CString::new(p).ok()) { - c.into_raw() as _ - } else { - std::ptr::null() - }; + let c_string = + if let Some(c) = System::name().and_then(|p| CString::new(p.as_encoded_bytes()).ok()) { + c.into_raw() as _ + } else { + std::ptr::null() + }; c_string } /// Equivalent of [`System::version()`][crate::System#method.version]. #[no_mangle] pub extern "C" fn sysinfo_system_version() -> RString { - let c_string = if let Some(c) = System::os_version().and_then(|c| CString::new(c).ok()) { + let c_string = if let Some(c) = + System::os_version().and_then(|c| CString::new(c.as_encoded_bytes()).ok()) + { c.into_raw() as _ } else { std::ptr::null() @@ -608,7 +611,9 @@ pub extern "C" fn sysinfo_system_version() -> RString { /// Equivalent of [`System::kernel_version()`][crate::System#method.kernel_version]. #[no_mangle] pub extern "C" fn sysinfo_system_kernel_version() -> RString { - let c_string = if let Some(c) = System::kernel_version().and_then(|c| CString::new(c).ok()) { + let c_string = if let Some(c) = + System::kernel_version().and_then(|c| CString::new(c.as_encoded_bytes()).ok()) + { c.into_raw() as _ } else { std::ptr::null() @@ -620,7 +625,9 @@ pub extern "C" fn sysinfo_system_kernel_version() -> RString { /// Equivalent of [`System::host_name()`][crate::System#method.host_name]. #[no_mangle] pub extern "C" fn sysinfo_system_host_name() -> RString { - let c_string = if let Some(c) = System::host_name().and_then(|c| CString::new(c).ok()) { + let c_string = if let Some(c) = + System::host_name().and_then(|c| CString::new(c.as_encoded_bytes()).ok()) + { c.into_raw() as _ } else { std::ptr::null() @@ -631,7 +638,9 @@ pub extern "C" fn sysinfo_system_host_name() -> RString { /// Equivalent of [`System::long_os_version()`][crate::System#method.long_os_version]. #[no_mangle] pub extern "C" fn sysinfo_system_long_version() -> RString { - let c_string = if let Some(c) = System::long_os_version().and_then(|c| CString::new(c).ok()) { + let c_string = if let Some(c) = + System::long_os_version().and_then(|c| CString::new(c.as_encoded_bytes()).ok()) + { c.into_raw() as _ } else { std::ptr::null() diff --git a/src/common.rs b/src/common.rs index b88a8bfbd..e7678917f 100644 --- a/src/common.rs +++ b/src/common.rs @@ -728,7 +728,7 @@ impl System { /// /// println!("OS: {:?}", System::name()); /// ``` - pub fn name() -> Option { + pub fn name() -> Option { SystemInner::name() } @@ -741,7 +741,7 @@ impl System { /// /// println!("kernel version: {:?}", System::kernel_version()); /// ``` - pub fn kernel_version() -> Option { + pub fn kernel_version() -> Option { SystemInner::kernel_version() } @@ -755,7 +755,7 @@ impl System { /// /// println!("OS version: {:?}", System::os_version()); /// ``` - pub fn os_version() -> Option { + pub fn os_version() -> Option { SystemInner::os_version() } @@ -768,7 +768,7 @@ impl System { /// /// println!("Long OS Version: {:?}", System::long_os_version()); /// ``` - pub fn long_os_version() -> Option { + pub fn long_os_version() -> Option { SystemInner::long_os_version() } @@ -786,7 +786,7 @@ impl System { /// /// println!("Distribution ID: {:?}", System::distribution_id()); /// ``` - pub fn distribution_id() -> String { + pub fn distribution_id() -> OsString { SystemInner::distribution_id() } @@ -799,7 +799,7 @@ impl System { /// /// println!("Hostname: {:?}", System::host_name()); /// ``` - pub fn host_name() -> Option { + pub fn host_name() -> Option { SystemInner::host_name() } @@ -812,7 +812,7 @@ impl System { /// /// println!("CPU Archicture: {:?}", System::cpu_arch()); /// ``` - pub fn cpu_arch() -> Option { + pub fn cpu_arch() -> Option { SystemInner::cpu_arch() } } diff --git a/src/unix/apple/system.rs b/src/unix/apple/system.rs index c58f86a91..4b3e2e670 100644 --- a/src/unix/apple/system.rs +++ b/src/unix/apple/system.rs @@ -10,8 +10,9 @@ use crate::{Cpu, CpuRefreshKind, LoadAvg, MemoryRefreshKind, Pid, Process, Proce #[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))] use std::cell::UnsafeCell; use std::collections::HashMap; -use std::ffi::CStr; +use std::ffi::{CStr, OsStr, OsString}; use std::mem; +use std::os::unix::ffi::{OsStrExt, OsStringExt}; #[cfg(all(target_os = "macos", not(feature = "apple-sandbox")))] use std::time::SystemTime; @@ -352,62 +353,56 @@ impl SystemInner { boot_time() } - pub(crate) fn name() -> Option { + pub(crate) fn name() -> Option { get_system_info(libc::KERN_OSTYPE, Some("Darwin")) } - pub(crate) fn long_os_version() -> Option { + pub(crate) fn long_os_version() -> Option { #[cfg(target_os = "macos")] - let friendly_name = match Self::os_version().unwrap_or_default() { - f_n if f_n.starts_with("14.0") => "Sonoma", - f_n if f_n.starts_with("10.16") - | f_n.starts_with("11.0") - | f_n.starts_with("11.1") - | f_n.starts_with("11.2") => + let friendly_name = match Self::os_version().unwrap_or_default().as_bytes() { + f_n if f_n.starts_with(b"14.0") => "Sonoma", + f_n if f_n.starts_with(b"10.16") + | f_n.starts_with(b"11.0") + | f_n.starts_with(b"11.1") + | f_n.starts_with(b"11.2") => { "Big Sur" } - f_n if f_n.starts_with("10.15") => "Catalina", - f_n if f_n.starts_with("10.14") => "Mojave", - f_n if f_n.starts_with("10.13") => "High Sierra", - f_n if f_n.starts_with("10.12") => "Sierra", - f_n if f_n.starts_with("10.11") => "El Capitan", - f_n if f_n.starts_with("10.10") => "Yosemite", - f_n if f_n.starts_with("10.9") => "Mavericks", - f_n if f_n.starts_with("10.8") => "Mountain Lion", - f_n if f_n.starts_with("10.7") => "Lion", - f_n if f_n.starts_with("10.6") => "Snow Leopard", - f_n if f_n.starts_with("10.5") => "Leopard", - f_n if f_n.starts_with("10.4") => "Tiger", - f_n if f_n.starts_with("10.3") => "Panther", - f_n if f_n.starts_with("10.2") => "Jaguar", - f_n if f_n.starts_with("10.1") => "Puma", - f_n if f_n.starts_with("10.0") => "Cheetah", + f_n if f_n.starts_with(b"10.15") => "Catalina", + f_n if f_n.starts_with(b"10.14") => "Mojave", + f_n if f_n.starts_with(b"10.13") => "High Sierra", + f_n if f_n.starts_with(b"10.12") => "Sierra", _ => "", }; #[cfg(target_os = "macos")] - let long_name = Some(format!( - "MacOS {} {}", - Self::os_version().unwrap_or_default(), - friendly_name - )); + let long_name = { + let mut buf = b"macOS ".to_vec(); + buf.extend(Self::os_version().unwrap_or_default().as_bytes()); + buf.push(b' '); + buf.extend(friendly_name.as_bytes()); + Some(OsString::from_vec(buf)) + }; #[cfg(target_os = "ios")] - let long_name = Some(format!("iOS {}", Self::os_version().unwrap_or_default())); + let long_name = { + let mut buf = b"iOS ".to_vec(); + buf.extend(Self::os_version().unwrap_or_default().as_bytes()); + Some(OsString::from_vec(buf)) + }; long_name } - pub(crate) fn host_name() -> Option { + pub(crate) fn host_name() -> Option { get_system_info(libc::KERN_HOSTNAME, None) } - pub(crate) fn kernel_version() -> Option { + pub(crate) fn kernel_version() -> Option { get_system_info(libc::KERN_OSRELEASE, None) } - pub(crate) fn os_version() -> Option { + pub(crate) fn os_version() -> Option { unsafe { // get the size for the buffer first let mut size = 0; @@ -427,7 +422,7 @@ impl SystemInner { buf.resize(pos, 0); } - String::from_utf8(buf).ok() + Some(OsString::from_vec(buf)) } else { // getting the system value failed None @@ -439,11 +434,11 @@ impl SystemInner { } } - pub(crate) fn distribution_id() -> String { - std::env::consts::OS.to_owned() + pub(crate) fn distribution_id() -> OsString { + std::env::consts::OS.into() } - pub(crate) fn cpu_arch() -> Option { + pub(crate) fn cpu_arch() -> Option { let mut arch_str: [u8; 32] = [0; 32]; let mut mib = [libc::CTL_HW as _, libc::HW_MACHINE as _]; @@ -453,12 +448,10 @@ impl SystemInner { arch_str.as_mut_ptr() as *mut _, &mut mib, ) { - CStr::from_bytes_until_nul(&arch_str) - .ok() - .and_then(|res| match res.to_str() { - Ok(arch) => Some(arch.to_string()), - Err(_) => None, - }) + Some( + OsStr::from_bytes(CStr::from_bytes_until_nul(&arch_str).ok()?.to_bytes()) + .to_os_string(), + ) } else { None } @@ -466,7 +459,7 @@ impl SystemInner { } } -fn get_system_info(value: c_int, default: Option<&str>) -> Option { +fn get_system_info(value: c_int, default: Option<&str>) -> Option { let mut mib: [c_int; 2] = [libc::CTL_KERN, value]; let mut size = 0; @@ -483,7 +476,7 @@ fn get_system_info(value: c_int, default: Option<&str>) -> Option { // exit early if we did not update the size if size == 0 { - default.map(|s| s.to_owned()) + default.map(|s| s.into()) } else { // set the buffer to the correct size let mut buf = vec![0_u8; size as _]; @@ -498,14 +491,14 @@ fn get_system_info(value: c_int, default: Option<&str>) -> Option { ) == -1 { // If command fails return default - default.map(|s| s.to_owned()) + default.map(|s| s.into()) } else { if let Some(pos) = buf.iter().position(|x| *x == 0) { // Shrink buffer to terminate the null bytes buf.resize(pos, 0); } - String::from_utf8(buf).ok() + Some(OsString::from_vec(buf)) } } } diff --git a/src/unix/freebsd/system.rs b/src/unix/freebsd/system.rs index 05eb9987e..e65aee8b8 100644 --- a/src/unix/freebsd/system.rs +++ b/src/unix/freebsd/system.rs @@ -6,8 +6,9 @@ use crate::{ use std::cell::UnsafeCell; use std::collections::HashMap; -use std::ffi::CStr; +use std::ffi::{CStr, OsStr, OsString}; use std::mem::MaybeUninit; +use std::os::unix::ffi::{OsStrExt, OsStringExt}; use std::path::{Path, PathBuf}; use std::ptr::NonNull; @@ -18,6 +19,7 @@ use crate::sys::utils::{ get_system_info, init_mib, }; +use bstr::ByteSlice; use libc::c_int; pub(crate) struct SystemInner { @@ -214,7 +216,7 @@ impl SystemInner { } } - pub(crate) fn name() -> Option { + pub(crate) fn name() -> Option { let mut os_type: [c_int; 2] = [0; 2]; unsafe { init_mib(b"kern.ostype\0", &mut os_type); @@ -222,17 +224,21 @@ impl SystemInner { } } - pub(crate) fn os_version() -> Option { + pub(crate) fn os_version() -> Option { let mut os_release: [c_int; 2] = [0; 2]; unsafe { init_mib(b"kern.osrelease\0", &mut os_release); // It returns something like "13.0-RELEASE". We want to keep everything until the "-". - get_system_info(&os_release, None) - .and_then(|s| s.split('-').next().map(|s| s.to_owned())) + get_system_info(&os_release, None).map(|s| { + let mut s = s.into_vec(); + let pos = s.find_byte(b'-').unwrap_or(s.len()); + s.drain(pos..); + OsString::from_vec(s) + }) } } - pub(crate) fn long_os_version() -> Option { + pub(crate) fn long_os_version() -> Option { let mut os_release: [c_int; 2] = [0; 2]; unsafe { init_mib(b"kern.osrelease\0", &mut os_release); @@ -240,7 +246,7 @@ impl SystemInner { } } - pub(crate) fn host_name() -> Option { + pub(crate) fn host_name() -> Option { let mut hostname: [c_int; 2] = [0; 2]; unsafe { init_mib(b"kern.hostname\0", &mut hostname); @@ -248,7 +254,7 @@ impl SystemInner { } } - pub(crate) fn kernel_version() -> Option { + pub(crate) fn kernel_version() -> Option { let mut kern_version: [c_int; 2] = [0; 2]; unsafe { init_mib(b"kern.version\0", &mut kern_version); @@ -256,22 +262,20 @@ impl SystemInner { } } - pub(crate) fn distribution_id() -> String { - std::env::consts::OS.to_owned() + pub(crate) fn distribution_id() -> OsString { + std::env::consts::OS.into() } - pub(crate) fn cpu_arch() -> Option { + pub(crate) fn cpu_arch() -> Option { let mut arch_str: [u8; 32] = [0; 32]; let mib = [libc::CTL_HW as _, libc::HW_MACHINE as _]; unsafe { if get_sys_value(&mib, &mut arch_str) { - CStr::from_bytes_until_nul(&arch_str) - .ok() - .and_then(|res| match res.to_str() { - Ok(arch) => Some(arch.to_string()), - Err(_) => None, - }) + Some( + OsStr::from_bytes(CStr::from_bytes_until_nul(&arch_str).ok()?.to_bytes()) + .to_os_string(), + ) } else { None } diff --git a/src/unix/freebsd/utils.rs b/src/unix/freebsd/utils.rs index 04cf8d276..e52ea3f9b 100644 --- a/src/unix/freebsd/utils.rs +++ b/src/unix/freebsd/utils.rs @@ -214,7 +214,7 @@ pub(crate) fn get_sys_value_str_by_name(name: &[u8]) -> Option { } } -pub(crate) fn get_system_info(mib: &[c_int], default: Option<&str>) -> Option { +pub(crate) fn get_system_info(mib: &[c_int], default: Option<&str>) -> Option { let mut size = 0; unsafe { @@ -230,7 +230,7 @@ pub(crate) fn get_system_info(mib: &[c_int], default: Option<&str>) -> Option = vec![0; size as _]; @@ -245,9 +245,9 @@ pub(crate) fn get_system_info(mib: &[c_int], default: Option<&str>) -> Option (vendor_id, brand), diff --git a/src/unix/linux/process.rs b/src/unix/linux/process.rs index ef324acd7..862227c8f 100644 --- a/src/unix/linux/process.rs +++ b/src/unix/linux/process.rs @@ -598,7 +598,7 @@ fn get_memory(path: &Path, entry: &mut ProcessInner, info: &SystemInfo) -> bool ); return false; } - let mut parts = buf.split(|c| *c == b' '); + let mut parts = buf.split_str(" "); entry.virtual_memory = parts .next() .map(slice_to_nb) diff --git a/src/unix/linux/system.rs b/src/unix/linux/system.rs index 4742eca67..88c54e50c 100644 --- a/src/unix/linux/system.rs +++ b/src/unix/linux/system.rs @@ -2,17 +2,20 @@ use crate::sys::cpu::{get_physical_core_count, CpusWrapper}; use crate::sys::process::{_get_process_data, compute_cpu_usage, refresh_procs, unset_updated}; -use crate::sys::utils::{get_all_utf8_data, to_u64}; +use crate::sys::utils::{get_all_data, to_u64}; use crate::{Cpu, CpuRefreshKind, LoadAvg, MemoryRefreshKind, Pid, Process, ProcessRefreshKind}; +use bstr::ByteSlice; use libc::{self, c_char, sysconf, _SC_CLK_TCK, _SC_HOST_NAME_MAX, _SC_PAGESIZE}; use std::cmp::min; use std::collections::HashMap; -use std::ffi::CStr; +use std::ffi::{CStr, OsStr, OsString}; use std::fs::File; use std::io::Read; +use std::os::unix::ffi::{OsStrExt, OsStringExt}; use std::path::Path; -use std::str::FromStr; +use std::slice; +use std::str::{self, FromStr}; use std::sync::{atomic::AtomicIsize, OnceLock}; // This whole thing is to prevent having too many files open at once. It could be problematic @@ -64,11 +67,11 @@ fn boot_time() -> u64 { f.read_to_end(&mut buf)?; Ok(buf) }) { - let line = buf.split(|c| *c == b'\n').find(|l| l.starts_with(b"btime")); + let line = buf.split_str("\n").find(|l| l.starts_with(b"btime")); if let Some(line) = line { return line - .split(|x| *x == b' ') + .split_str(" ") .filter(|s| !s.is_empty()) .nth(1) .map(to_u64) @@ -192,20 +195,20 @@ impl SystemInner { return; } let mut mem_available_found = false; - read_table("/proc/meminfo", ':', |key, value_kib| { + read_table("/proc/meminfo", b':', |key, value_kib| { let field = match key { - "MemTotal" => &mut self.mem_total, - "MemFree" => &mut self.mem_free, - "MemAvailable" => { + b"MemTotal" => &mut self.mem_total, + b"MemFree" => &mut self.mem_free, + b"MemAvailable" => { mem_available_found = true; &mut self.mem_available } - "Buffers" => &mut self.mem_buffers, - "Cached" => &mut self.mem_page_cache, - "Shmem" => &mut self.mem_shmem, - "SReclaimable" => &mut self.mem_slab_reclaimable, - "SwapTotal" => &mut self.swap_total, - "SwapFree" => &mut self.swap_free, + b"Buffers" => &mut self.mem_buffers, + b"Cached" => &mut self.mem_page_cache, + b"Shmem" => &mut self.mem_shmem, + b"SReclaimable" => &mut self.mem_slab_reclaimable, + b"SwapTotal" => &mut self.swap_total, + b"SwapFree" => &mut self.swap_free, _ => return, }; // /proc/meminfo reports KiB, though it says "kB". Convert it. @@ -352,11 +355,11 @@ impl SystemInner { } pub(crate) fn uptime() -> u64 { - let content = get_all_utf8_data("/proc/uptime", 50).unwrap_or_default(); + let content = get_all_data("/proc/uptime", 50).unwrap_or_default(); content - .split('.') + .split_str(".") .next() - .and_then(|t| t.parse().ok()) + .and_then(|t| str::from_utf8(t).ok()?.parse().ok()) .unwrap_or_default() } @@ -376,7 +379,7 @@ impl SystemInner { .trim() .split(' ') .take(3) - .map(|val| val.parse::().unwrap()) + .map(|val| val.parse::().unwrap_or_default()) .collect::>(); LoadAvg { one: loads[0], @@ -386,7 +389,7 @@ impl SystemInner { } #[cfg(not(target_os = "android"))] - pub(crate) fn name() -> Option { + pub(crate) fn name() -> Option { get_system_info_linux( InfoType::Name, Path::new("/etc/os-release"), @@ -395,26 +398,27 @@ impl SystemInner { } #[cfg(target_os = "android")] - pub(crate) fn name() -> Option { + pub(crate) fn name() -> Option { get_system_info_android(InfoType::Name) } - pub(crate) fn long_os_version() -> Option { + pub(crate) fn long_os_version() -> Option { #[cfg(target_os = "android")] - let system_name = "Android"; + let system_name = b"Android"; #[cfg(not(target_os = "android"))] - let system_name = "Linux"; + let system_name = b"Linux"; - Some(format!( - "{} {} {}", - system_name, - Self::os_version().unwrap_or_default(), - Self::name().unwrap_or_default() - )) + let mut buf = Vec::from(system_name); + buf.push(b' '); + buf.extend(Self::os_version().unwrap_or_default().as_bytes()); + buf.push(b' '); + buf.extend(Self::name().unwrap_or_default().as_bytes()); + + Some(OsString::from_vec(buf)) } - pub(crate) fn host_name() -> Option { + pub(crate) fn host_name() -> Option { unsafe { let hostname_max = sysconf(_SC_HOST_NAME_MAX); let mut buffer = vec![0_u8; hostname_max as usize]; @@ -423,7 +427,7 @@ impl SystemInner { // Shrink buffer to terminate the null bytes buffer.resize(pos, 0); } - String::from_utf8(buffer).ok() + Some(OsString::from_vec(buffer)) } else { sysinfo_debug!("gethostname failed: hostname cannot be retrieved..."); None @@ -431,7 +435,7 @@ impl SystemInner { } } - pub(crate) fn kernel_version() -> Option { + pub(crate) fn kernel_version() -> Option { let mut raw = std::mem::MaybeUninit::::zeroed(); unsafe { @@ -441,11 +445,11 @@ impl SystemInner { let release = info .release .iter() - .filter(|c| **c != 0) - .map(|c| *c as u8 as char) - .collect::(); + .map(|&c| c as _) + .filter(|c| *c != 0) + .collect(); - Some(release) + Some(OsString::from_vec(release)) } else { None } @@ -453,7 +457,7 @@ impl SystemInner { } #[cfg(not(target_os = "android"))] - pub(crate) fn os_version() -> Option { + pub(crate) fn os_version() -> Option { get_system_info_linux( InfoType::OsVersion, Path::new("/etc/os-release"), @@ -462,30 +466,30 @@ impl SystemInner { } #[cfg(target_os = "android")] - pub(crate) fn os_version() -> Option { + pub(crate) fn os_version() -> Option { get_system_info_android(InfoType::OsVersion) } #[cfg(not(target_os = "android"))] - pub(crate) fn distribution_id() -> String { + pub(crate) fn distribution_id() -> OsString { get_system_info_linux( InfoType::DistributionID, Path::new("/etc/os-release"), Path::new(""), ) - .unwrap_or_else(|| std::env::consts::OS.to_owned()) + .unwrap_or_else(|| std::env::consts::OS.to_owned().into()) } #[cfg(target_os = "android")] - pub(crate) fn distribution_id() -> String { + pub(crate) fn distribution_id() -> OsString { // Currently get_system_info_android doesn't support InfoType::DistributionID and always // returns None. This call is done anyway for consistency with non-Android implementation // and to suppress dead-code warning for DistributionID on Android. get_system_info_android(InfoType::DistributionID) - .unwrap_or_else(|| std::env::consts::OS.to_owned()) + .unwrap_or_else(|| std::env::consts::OS.to_owned().into()) } - pub(crate) fn cpu_arch() -> Option { + pub(crate) fn cpu_arch() -> Option { let mut raw = std::mem::MaybeUninit::::uninit(); unsafe { @@ -495,37 +499,35 @@ impl SystemInner { let info = raw.assume_init(); // Converting `&[i8]` to `&[u8]`. let machine: &[u8] = - std::slice::from_raw_parts(info.machine.as_ptr() as *const _, info.machine.len()); - - CStr::from_bytes_until_nul(machine) - .ok() - .and_then(|res| match res.to_str() { - Ok(arch) => Some(arch.to_string()), - Err(_) => None, - }) + slice::from_raw_parts(info.machine.as_ptr() as *const _, info.machine.len()); + + Some( + OsStr::from_bytes(CStr::from_bytes_until_nul(machine).ok()?.to_bytes()) + .to_os_string(), + ) } } } fn read_u64(filename: &str) -> Option { - get_all_utf8_data(filename, 16_635) - .ok() - .and_then(|d| u64::from_str(d.trim()).ok()) + let d = get_all_data(filename, 16_635).ok()?; + let d = str::from_utf8(d.trim()).ok()?; + u64::from_str(d).ok() } -fn read_table(filename: &str, colsep: char, mut f: F) +fn read_table(filename: &str, colsep: u8, mut f: F) where - F: FnMut(&str, u64), + F: FnMut(&[u8], u64), { - if let Ok(content) = get_all_utf8_data(filename, 16_635) { + if let Ok(content) = get_all_data(filename, 16_635) { content - .split('\n') + .split_str("\n") .flat_map(|line| { - let mut split = line.split(colsep); + let mut split = line.split_str(slice::from_ref(&colsep)); let key = split.next()?; let value = split.next()?; - let value0 = value.trim_start().split(' ').next()?; - let value0_u64 = u64::from_str(value0).ok()?; + let value0 = value.trim_start().split_str(" ").next()?; + let value0_u64 = u64::from_str(str::from_utf8(value0).ok()?).ok()?; Some((key, value0_u64)) }) .for_each(|(k, v)| f(k, v)); @@ -592,12 +594,10 @@ enum InfoType { } #[cfg(not(target_os = "android"))] -fn get_system_info_linux(info: InfoType, path: &Path, fallback_path: &Path) -> Option { - if let Ok(buf) = File::open(path).and_then(|mut f| { - let mut buf = String::new(); - f.read_to_string(&mut buf)?; - Ok(buf) - }) { +fn get_system_info_linux(info: InfoType, path: &Path, fallback_path: &Path) -> Option { + use bstr::ByteSlice; + + if let Ok(buf) = std::fs::read(path) { let info_str = match info { InfoType::Name => "NAME=", InfoType::OsVersion => "VERSION_ID=", @@ -605,8 +605,10 @@ fn get_system_info_linux(info: InfoType, path: &Path, fallback_path: &Path) -> O }; for line in buf.lines() { - if let Some(stripped) = line.strip_prefix(info_str) { - return Some(stripped.replace('"', "")); + if let Some(stripped) = line.strip_prefix(info_str.as_bytes()) { + let mut stripped = stripped.to_vec(); + stripped.retain(|&b| b != b'"'); + return Some(OsString::from_vec(stripped)); } } } @@ -615,13 +617,7 @@ fn get_system_info_linux(info: InfoType, path: &Path, fallback_path: &Path) -> O // VERSION_ID is not required in the `/etc/os-release` file // per https://www.linux.org/docs/man5/os-release.html // If this fails for some reason, fallback to None - let buf = File::open(fallback_path) - .and_then(|mut f| { - let mut buf = String::new(); - f.read_to_string(&mut buf)?; - Ok(buf) - }) - .ok()?; + let buf = std::fs::read(fallback_path).ok()?; let info_str = match info { InfoType::OsVersion => "DISTRIB_RELEASE=", @@ -632,15 +628,17 @@ fn get_system_info_linux(info: InfoType, path: &Path, fallback_path: &Path) -> O } }; for line in buf.lines() { - if let Some(stripped) = line.strip_prefix(info_str) { - return Some(stripped.replace('"', "")); + if let Some(stripped) = line.strip_prefix(info_str.as_bytes()) { + let mut stripped = stripped.to_vec(); + stripped.retain(|&b| b != b'"'); + return Some(OsString::from_vec(stripped)); } } None } #[cfg(target_os = "android")] -fn get_system_info_android(info: InfoType) -> Option { +fn get_system_info_android(info: InfoType) -> Option { // https://android.googlesource.com/platform/frameworks/base/+/refs/heads/master/core/java/android/os/Build.java#58 let name: &'static [u8] = match info { InfoType::Name => b"ro.product.model\0", @@ -662,7 +660,7 @@ fn get_system_info_android(info: InfoType) -> Option { if let Some(pos) = value_buffer.iter().position(|c| *c == 0) { value_buffer.resize(pos, 0); } - String::from_utf8(value_buffer).ok() + Some(OsString::from_vec(value_buffer)) } else { None } @@ -688,7 +686,7 @@ mod test { #[test] #[cfg(not(target_os = "android"))] fn lsb_release_fallback_not_android() { - use std::path::Path; + use std::{ffi::OsString, path::Path}; let dir = tempfile::tempdir().expect("failed to create temporary directory"); let tmp1 = dir.path().join("tmp1"); @@ -723,25 +721,25 @@ DISTRIB_DESCRIPTION="Ubuntu 20.10" // Check for the "normal" path: "/etc/os-release" assert_eq!( get_system_info_linux(InfoType::OsVersion, &tmp1, Path::new("")), - Some("20.10".to_owned()) + Some(OsString::from("20.10")) ); assert_eq!( get_system_info_linux(InfoType::Name, &tmp1, Path::new("")), - Some("Ubuntu".to_owned()) + Some(OsString::from("Ubuntu")) ); assert_eq!( get_system_info_linux(InfoType::DistributionID, &tmp1, Path::new("")), - Some("ubuntu".to_owned()) + Some(OsString::from("ubuntu")) ); // Check for the "fallback" path: "/etc/lsb-release" assert_eq!( get_system_info_linux(InfoType::OsVersion, Path::new(""), &tmp2), - Some("20.10".to_owned()) + Some(OsString::from("20.10")) ); assert_eq!( get_system_info_linux(InfoType::Name, Path::new(""), &tmp2), - Some("Ubuntu".to_owned()) + Some(OsString::from("Ubuntu")) ); assert_eq!( get_system_info_linux(InfoType::DistributionID, Path::new(""), &tmp2), diff --git a/src/unix/linux/utils.rs b/src/unix/linux/utils.rs index 9c3d07611..af94d13d9 100644 --- a/src/unix/linux/utils.rs +++ b/src/unix/linux/utils.rs @@ -14,6 +14,11 @@ pub(crate) fn get_all_data_from_file(file: &mut File, size: usize) -> io::Result Ok(buf) } +pub(crate) fn get_all_data>(file_path: P, size: usize) -> io::Result> { + let mut file = File::open(file_path.as_ref())?; + get_all_data_from_file(&mut file, size) +} + pub(crate) fn get_all_utf8_data_from_file(file: &mut File, size: usize) -> io::Result { let mut buf = String::with_capacity(size); file.rewind()?; diff --git a/src/unknown/system.rs b/src/unknown/system.rs index 19aada516..c7e1f7219 100644 --- a/src/unknown/system.rs +++ b/src/unknown/system.rs @@ -5,6 +5,7 @@ use crate::{ }; use std::collections::HashMap; +use std::ffi::OsString; pub(crate) struct SystemInner { processes_list: HashMap, @@ -112,30 +113,30 @@ impl SystemInner { } } - pub(crate) fn name() -> Option { + pub(crate) fn name() -> Option { None } - pub(crate) fn long_os_version() -> Option { + pub(crate) fn long_os_version() -> Option { None } - pub(crate) fn kernel_version() -> Option { + pub(crate) fn kernel_version() -> Option { None } - pub(crate) fn os_version() -> Option { + pub(crate) fn os_version() -> Option { None } - pub(crate) fn distribution_id() -> String { - std::env::consts::OS.to_owned() + pub(crate) fn distribution_id() -> OsString { + std::env::consts::OS.into() } - pub(crate) fn host_name() -> Option { + pub(crate) fn host_name() -> Option { None } - pub(crate) fn cpu_arch() -> Option { + pub(crate) fn cpu_arch() -> Option { None } } diff --git a/src/windows/system.rs b/src/windows/system.rs index 74d8eca75..3566707d8 100644 --- a/src/windows/system.rs +++ b/src/windows/system.rs @@ -18,6 +18,7 @@ use std::os::windows::ffi::OsStringExt; use std::ptr; use std::time::SystemTime; +use bstr::ByteSlice; use ntapi::ntexapi::SYSTEM_PROCESS_INFORMATION; use windows::core::PWSTR; use windows::Wdk::System::SystemInformation::{NtQuerySystemInformation, SystemProcessInformation}; @@ -38,7 +39,8 @@ impl SystemInner { WINDOWS_ELEVEN_BUILD_NUMBER <= Self::kernel_version() .unwrap_or_default() - .parse() + .to_str() + .and_then(|s| s.parse().ok()) .unwrap_or(0) } } @@ -421,18 +423,30 @@ impl SystemInner { get_load_average() } - pub(crate) fn name() -> Option { - Some("Windows".to_owned()) + pub(crate) fn name() -> Option { + Some("Windows".into()) } - pub(crate) fn long_os_version() -> Option { + pub(crate) fn long_os_version() -> Option { if Self::is_windows_eleven() { return get_reg_string_value( HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Windows NT\CurrentVersion", "ProductName", ) - .map(|product_name| product_name.replace("Windows 10 ", "Windows 11 ")); + .map(|product_name| { + // SAFETY: We only replace the Windows version, which is valid + // UTF-8. Since OsString is a superset of UTF-8 and we only + // modify the valid UTF-8 bytes, the OsString stays correctly + // encoded. + unsafe { + OsString::from_encoded_bytes_unchecked( + product_name + .into_encoded_bytes() + .replace("Windows 10 ", "Windows 11 "), + ) + } + }); } get_reg_string_value( HKEY_LOCAL_MACHINE, @@ -441,11 +455,11 @@ impl SystemInner { ) } - pub(crate) fn host_name() -> Option { + pub(crate) fn host_name() -> Option { get_dns_hostname() } - pub(crate) fn kernel_version() -> Option { + pub(crate) fn kernel_version() -> Option { get_reg_string_value( HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Windows NT\CurrentVersion", @@ -453,7 +467,7 @@ impl SystemInner { ) } - pub(crate) fn os_version() -> Option { + pub(crate) fn os_version() -> Option { let build_number = get_reg_string_value( HKEY_LOCAL_MACHINE, r"SOFTWARE\Microsoft\Windows NT\CurrentVersion", @@ -472,31 +486,36 @@ impl SystemInner { .unwrap_or_default(), ) }; - Some(format!("{major} ({build_number})")) + + unsafe { + let mut buf = format!("{major} (").into_bytes(); + buf.extend(build_number.into_encoded_bytes()); + buf.push(b')'); + + Some(OsString::from_encoded_bytes_unchecked(buf)) + } } - pub(crate) fn distribution_id() -> String { - std::env::consts::OS.to_owned() + pub(crate) fn distribution_id() -> OsString { + std::env::consts::OS.into() } - pub(crate) fn cpu_arch() -> Option { + pub(crate) fn cpu_arch() -> Option { unsafe { // https://docs.microsoft.com/fr-fr/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info let info = SYSTEM_INFO::default(); match info.Anonymous.Anonymous.wProcessorArchitecture { - SystemInformation::PROCESSOR_ARCHITECTURE_ALPHA => Some("alpha".to_string()), - SystemInformation::PROCESSOR_ARCHITECTURE_ALPHA64 => Some("alpha64".to_string()), - SystemInformation::PROCESSOR_ARCHITECTURE_AMD64 => Some("x86_64".to_string()), - SystemInformation::PROCESSOR_ARCHITECTURE_ARM => Some("arm".to_string()), - SystemInformation::PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64 => Some("arm".to_string()), - SystemInformation::PROCESSOR_ARCHITECTURE_ARM64 => Some("arm64".to_string()), + SystemInformation::PROCESSOR_ARCHITECTURE_ALPHA => Some("alpha".into()), + SystemInformation::PROCESSOR_ARCHITECTURE_ALPHA64 => Some("alpha64".into()), + SystemInformation::PROCESSOR_ARCHITECTURE_AMD64 => Some("x86_64".into()), + SystemInformation::PROCESSOR_ARCHITECTURE_ARM => Some("arm".into()), + SystemInformation::PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64 => Some("arm".into()), + SystemInformation::PROCESSOR_ARCHITECTURE_ARM64 => Some("arm64".into()), SystemInformation::PROCESSOR_ARCHITECTURE_IA32_ON_ARM64 - | SystemInformation::PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 => { - Some("ia32".to_string()) - } - SystemInformation::PROCESSOR_ARCHITECTURE_IA64 => Some("ia64".to_string()), - SystemInformation::PROCESSOR_ARCHITECTURE_INTEL => Some("x86".to_string()), - SystemInformation::PROCESSOR_ARCHITECTURE_MIPS => Some("mips".to_string()), - SystemInformation::PROCESSOR_ARCHITECTURE_PPC => Some("powerpc".to_string()), + | SystemInformation::PROCESSOR_ARCHITECTURE_IA32_ON_WIN64 => Some("ia32".into()), + SystemInformation::PROCESSOR_ARCHITECTURE_IA64 => Some("ia64".into()), + SystemInformation::PROCESSOR_ARCHITECTURE_INTEL => Some("x86".into()), + SystemInformation::PROCESSOR_ARCHITECTURE_MIPS => Some("mips".into()), + SystemInformation::PROCESSOR_ARCHITECTURE_PPC => Some("powerpc".into()), _ => None, } } @@ -559,7 +578,7 @@ pub(crate) fn get_process_name(process: &SYSTEM_PROCESS_INFORMATION, process_id: } } -fn get_dns_hostname() -> Option { +fn get_dns_hostname() -> Option { 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 @@ -586,7 +605,7 @@ fn get_dns_hostname() -> Option { buffer.resize(pos, 0); } - return String::from_utf16(&buffer).ok(); + return Some(OsString::from_wide(&buffer)); } } diff --git a/src/windows/utils.rs b/src/windows/utils.rs index 1510f08a1..ba6326e43 100644 --- a/src/windows/utils.rs +++ b/src/windows/utils.rs @@ -6,8 +6,8 @@ use windows::Win32::System::Registry::{ RegCloseKey, RegOpenKeyExW, RegQueryValueExW, HKEY, KEY_READ, REG_NONE, }; -use std::ffi::OsStr; -use std::os::windows::ffi::OsStrExt; +use std::ffi::{OsStr, OsString}; +use std::os::windows::ffi::{OsStrExt, OsStringExt}; use std::time::SystemTime; #[inline] @@ -85,7 +85,7 @@ impl Drop for RegKey { } } -pub(crate) fn get_reg_string_value(hkey: HKEY, path: &str, field_name: &str) -> Option { +pub(crate) fn get_reg_string_value(hkey: HKEY, path: &str, field_name: &str) -> Option { let c_path = utf16_str(path); let c_field_name = utf16_str(field_name); @@ -109,10 +109,8 @@ pub(crate) fn get_reg_string_value(hkey: HKEY, path: &str, field_name: &str) -> buf.set_len(buf_len as _); let words = std::slice::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(); - } + let zero_count = words.iter().rev().take_while(|&&c| c == 0).count(); + let s = OsString::from_wide(&words[..words.len() - zero_count]); Some(s) } } diff --git a/tests/system.rs b/tests/system.rs index 8127290b9..8f7e6409e 100644 --- a/tests/system.rs +++ b/tests/system.rs @@ -96,7 +96,7 @@ fn check_if_send_and_sync() { #[test] fn check_hostname_has_no_nuls() { if let Some(hostname) = System::host_name() { - assert!(!hostname.contains('\u{0}')) + assert!(!hostname.as_encoded_bytes().contains(&b'\0')) } }