Repository: GuillaumeGomez/sysinfo
Pull request #1135 (2023-11-11 21:11:49)

Add CPU architecture info
All in one commit of CPU architecture info


Patch:
diff --git a/.cirrus.yml b/.cirrus.yml
index ad663c4..36d17ed 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -1,10 +1,10 @@
 task:
-  name: rust 1.65 on freebsd 13
+  name: rust 1.69 on freebsd 13
   freebsd_instance:
     image: freebsd-13-1-release-amd64
   setup_script:
     - curl https://sh.rustup.rs -sSf --output rustup.sh
-    - sh rustup.sh -y --profile=minimal --default-toolchain=1.65
+    - sh rustup.sh -y --profile=minimal --default-toolchain=1.69
     - . $HOME/.cargo/env
     - rustup --version
     - rustup component add clippy
@@ -37,14 +37,14 @@ task:
     - FREEBSD_CI=1 cargo test --lib -j1 -- --ignored
 
 task:
-  name: rust 1.65 on mac m1
+  name: rust 1.69 on mac m1
   macos_instance:
     image: ghcr.io/cirruslabs/macos-monterey-base:latest
   setup_script:
     - brew update
     - brew install curl
     - curl https://sh.rustup.rs -sSf --output rustup.sh
-    - sh rustup.sh -y --profile=minimal --default-toolchain=1.65
+    - sh rustup.sh -y --profile=minimal --default-toolchain=1.69
     - source $HOME/.cargo/env
     - rustup --version
     - rustup component add clippy
diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml
index 8ef533a..4098a99 100644
--- a/.github/workflows/CI.yml
+++ b/.github/workflows/CI.yml
@@ -68,7 +68,7 @@ jobs:
           - { os: 'ubuntu-latest', target: 'x86_64-linux-android', cross: true }
           - { os: 'ubuntu-latest', target: 'i686-linux-android', cross: true }
         toolchain:
-          - "1.65.0"  # minimum supported rust version
+          - "1.69.0"  # minimum supported rust version
           - stable
           - nightly
     steps:
@@ -139,7 +139,7 @@ jobs:
           - macos-latest
           - windows-latest
         toolchain:
-          - "1.65.0"  # minimum supported rust version
+          - "1.69.0"  # minimum supported rust version
           - stable
           - nightly
     steps:
@@ -205,7 +205,7 @@ jobs:
     strategy:
       matrix:
         toolchain:
-          - "1.65.0" # minimum supported rust version
+          - "1.69.0" # minimum supported rust version
           - stable
     steps:
       - uses: actions/checkout@v2
diff --git a/Cargo.toml b/Cargo.toml
index b6f927a..74b9652 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -6,7 +6,7 @@ description = "Library to get system information such as processes, CPUs, disks,
 repository = "https://github.com/GuillaumeGomez/sysinfo"
 license = "MIT"
 readme = "README.md"
-rust-version = "1.65"
+rust-version = "1.69"
 exclude = ["/test-unknown"]
 categories = ["filesystem", "os", "api-bindings"]
 edition = "2018"
@@ -28,7 +28,13 @@ unknown-ci = []
 features = ["serde"]
 # Setting this default target to prevent `freebsd` to be the default one.
 default-target = "x86_64-unknown-linux-gnu"
-targets = ["i686-unknown-linux-gnu", "x86_64-apple-darwin", "x86_64-pc-windows-msvc", "i686-pc-windows-msvc", "x86_64-unknown-freebsd"]
+targets = [
+    "i686-unknown-linux-gnu",
+    "x86_64-apple-darwin",
+    "x86_64-pc-windows-msvc",
+    "i686-pc-windows-msvc",
+    "x86_64-unknown-freebsd",
+]
 cargo-args = ["-Zbuild-std"]
 rustdoc-args = ["--generate-link-to-definition"]
 
@@ -43,40 +49,40 @@ once_cell = "1.18"
 [target.'cfg(windows)'.dependencies]
 ntapi = "0.4"
 windows = { version = "0.51", features = [
-  "Wdk_System_SystemInformation",
-  "Wdk_System_SystemServices",
-  "Wdk_System_Threading",
-  "Win32_Foundation",
-  "Win32_NetworkManagement_IpHelper",
-  "Win32_NetworkManagement_NetManagement",
-  "Win32_NetworkManagement_Ndis",
-  "Win32_Networking_WinSock",
-  "Win32_Security",
-  "Win32_Security_Authentication_Identity",
-  "Win32_Security_Authorization",
-  "Win32_Storage_FileSystem",
-  "Win32_System_Com",
-  "Win32_System_Diagnostics_Debug",
-  "Win32_System_IO",
-  "Win32_System_Ioctl",
-  "Win32_System_LibraryLoader",
-  "Win32_System_Kernel",
-  "Win32_System_Memory",
-  "Win32_System_Ole",
-  "Win32_System_Performance",
-  "Win32_System_Power",
-  "Win32_System_ProcessStatus",
-  "Win32_System_Registry",
-  "Win32_System_RemoteDesktop",
-  "Win32_System_Rpc",
-  "Win32_System_SystemInformation",
-  "Win32_System_SystemServices",
-  "Win32_System_Threading",
-  "Win32_System_Variant",
-  "Win32_System_WindowsProgramming",
-  "Win32_System_Wmi",
-  "Win32_UI_Shell",
-]}
+    "Wdk_System_SystemInformation",
+    "Wdk_System_SystemServices",
+    "Wdk_System_Threading",
+    "Win32_Foundation",
+    "Win32_NetworkManagement_IpHelper",
+    "Win32_NetworkManagement_NetManagement",
+    "Win32_NetworkManagement_Ndis",
+    "Win32_Networking_WinSock",
+    "Win32_Security",
+    "Win32_Security_Authentication_Identity",
+    "Win32_Security_Authorization",
+    "Win32_Storage_FileSystem",
+    "Win32_System_Com",
+    "Win32_System_Diagnostics_Debug",
+    "Win32_System_IO",
+    "Win32_System_Ioctl",
+    "Win32_System_LibraryLoader",
+    "Win32_System_Kernel",
+    "Win32_System_Memory",
+    "Win32_System_Ole",
+    "Win32_System_Performance",
+    "Win32_System_Power",
+    "Win32_System_ProcessStatus",
+    "Win32_System_Registry",
+    "Win32_System_RemoteDesktop",
+    "Win32_System_Rpc",
+    "Win32_System_SystemInformation",
+    "Win32_System_SystemServices",
+    "Win32_System_Threading",
+    "Win32_System_Variant",
+    "Win32_System_WindowsProgramming",
+    "Win32_System_Wmi",
+    "Win32_UI_Shell",
+] }
 
 [target.'cfg(not(any(target_os = "unknown", target_arch = "wasm32")))'.dependencies]
 libc = "^0.2.150"
diff --git a/src/common.rs b/src/common.rs
index b7f708f..0e26933 100644
--- a/src/common.rs
+++ b/src/common.rs
@@ -701,6 +701,18 @@ impl System {
     pub fn host_name(&self) -> Option<String> {
         self.inner.host_name()
     }
+
+    /// Returns the CPU architecture (eg. x86, amd64, aarch64, ...)
+    ///
+    /// ```no_run
+    /// use sysinfo::System;
+    ///
+    /// let s = System::new();
+    /// println!("CPU Archicture: {:?}", s.cpu_arch());
+    /// ```
+    pub fn cpu_arch(&self) -> Option<String> {
+        self.inner.cpu_arch()
+    }
 }
 
 /// Struct containing information of a process.
diff --git a/src/lib.rs b/src/lib.rs
index 9ca6058..540980a 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -520,4 +520,10 @@ mod test {
             .iter()
             .any(|(_, process)| !process.cmd().is_empty()));
     }
+
+    #[test]
+    fn check_cpu_arch() {
+        let s = System::new();
+        assert_eq!(s.cpu_arch().is_some(), IS_SUPPORTED);
+    }
 }
diff --git a/src/unix/apple/system.rs b/src/unix/apple/system.rs
index cf4275c..2c10cd1 100644
--- a/src/unix/apple/system.rs
+++ b/src/unix/apple/system.rs
@@ -405,6 +405,10 @@ impl SystemInner {
     pub(crate) fn distribution_id(&self) -> String {
         std::env::consts::OS.to_owned()
     }
+
+    pub(crate) fn cpu_arch(&self) -> Option<String> {
+        get_cpu_arch()
+    }
 }
 
 fn get_system_info(value: c_int, default: Option<&str>) -> Option<String> {
@@ -451,3 +455,26 @@ fn get_system_info(value: c_int, default: Option<&str>) -> Option<String> {
         }
     }
 }
+
+pub(crate) fn get_cpu_arch() -> Option<String> {
+    use std::ffi::CStr;
+    let mut arch_str: [u8; 32] = [0; 32];
+
+    unsafe {
+        let mut mib = [libc::CTL_HW as _, libc::HW_MACHINE as _];
+        if get_sys_value(
+            mem::size_of::<[u8; 32]>(),
+            arch_str.as_mut_ptr() as *mut _,
+            &mut mib,
+        ) {
+            CStr::from_bytes_until_nul(&arch_str)
+                .map(|res| match res.to_str() {
+                    Ok(arch) => Some(arch.to_string()),
+                    Err(_) => None,
+                })
+                .unwrap_or_else(|_| None)
+        } else {
+            None
+        }
+    }
+}
diff --git a/src/unix/freebsd/cpu.rs b/src/unix/freebsd/cpu.rs
index bb5152c..f2c7847 100644
--- a/src/unix/freebsd/cpu.rs
+++ b/src/unix/freebsd/cpu.rs
@@ -67,6 +67,7 @@ impl CpusWrapper {
             // We get the CPU vendor ID in here.
             let vendor_id =
                 get_sys_value_str_by_name(b"hw.model\0").unwrap_or_else(|| "<unknown>".to_owned());
+
             for pos in 0..self.nb_cpus {
                 if refresh_kind.frequency() {
                     unsafe {
diff --git a/src/unix/freebsd/system.rs b/src/unix/freebsd/system.rs
index 1d0c25e..47a0e74 100644
--- a/src/unix/freebsd/system.rs
+++ b/src/unix/freebsd/system.rs
@@ -226,6 +226,9 @@ impl SystemInner {
     pub(crate) fn distribution_id(&self) -> String {
         std::env::consts::OS.to_owned()
     }
+    pub(crate) fn cpu_arch(&self) -> Option<String> {
+        get_cpu_arch()
+    }
 }
 
 impl SystemInner {
@@ -595,3 +598,22 @@ impl Drop for SystemInfo {
         }
     }
 }
+
+pub(crate) fn get_cpu_arch() -> Option<String> {
+    use std::ffi::CStr;
+    let mut arch_str: [u8; 32] = [0; 32];
+
+    unsafe {
+        let mib = [libc::CTL_HW as _, libc::HW_MACHINE as _];
+        if get_sys_value(&mib, &mut arch_str) {
+            CStr::from_bytes_until_nul(&arch_str)
+                .map(|res| match res.to_str() {
+                    Ok(arch) => Some(arch.to_string()),
+                    Err(_) => None,
+                })
+                .unwrap_or_else(|_| None)
+        } else {
+            None
+        }
+    }
+}
diff --git a/src/unix/linux/system.rs b/src/unix/linux/system.rs
index 62168e5..8bf5b8f 100644
--- a/src/unix/linux/system.rs
+++ b/src/unix/linux/system.rs
@@ -480,6 +480,9 @@ impl SystemInner {
         get_system_info_android(InfoType::DistributionID)
             .unwrap_or_else(|| std::env::consts::OS.to_owned())
     }
+    pub(crate) fn cpu_arch(&self) -> Option<String> {
+        get_cpu_arch()
+    }
 }
 
 fn read_u64(filename: &str) -> Option<u64> {
@@ -640,6 +643,27 @@ fn get_system_info_android(info: InfoType) -> Option<String> {
     }
 }
 
+fn get_cpu_arch() -> Option<String> {
+    let mut raw = std::mem::MaybeUninit::<libc::utsname>::zeroed();
+
+    unsafe {
+        if libc::uname(raw.as_mut_ptr()) == 0 {
+            let info = raw.assume_init();
+
+            let machine = info
+                .machine
+                .iter()
+                .filter(|c| **c != 0)
+                .map(|c| *c as u8 as char)
+                .collect::<String>();
+
+            Some(machine)
+        } else {
+            None
+        }
+    }
+}
+
 #[cfg(test)]
 mod test {
     #[cfg(target_os = "android")]
diff --git a/src/unknown/system.rs b/src/unknown/system.rs
index 1201aa4..70b027d 100644
--- a/src/unknown/system.rs
+++ b/src/unknown/system.rs
@@ -128,4 +128,7 @@ impl SystemInner {
     pub(crate) fn host_name(&self) -> Option<String> {
         None
     }
+    pub(crate) fn cpu_arch(&self) -> Option<String> {
+        None
+    }
 }
diff --git a/src/windows/system.rs b/src/windows/system.rs
index 26388dd..efbd869 100644
--- a/src/windows/system.rs
+++ b/src/windows/system.rs
@@ -22,9 +22,10 @@ use windows::Wdk::System::SystemInformation::{NtQuerySystemInformation, SystemPr
 use windows::Win32::Foundation::{HANDLE, STATUS_INFO_LENGTH_MISMATCH, STILL_ACTIVE};
 use windows::Win32::System::ProcessStatus::{K32GetPerformanceInfo, PERFORMANCE_INFORMATION};
 use windows::Win32::System::Registry::HKEY_LOCAL_MACHINE;
+use windows::Win32::System::SystemInformation;
 use windows::Win32::System::SystemInformation::{
     ComputerNamePhysicalDnsHostname, GetComputerNameExW, GetTickCount64, GlobalMemoryStatusEx,
-    MEMORYSTATUSEX,
+    MEMORYSTATUSEX, SYSTEM_INFO,
 };
 use windows::Win32::System::Threading::GetExitCodeProcess;
 
@@ -449,6 +450,9 @@ impl SystemInner {
     pub(crate) fn distribution_id(&self) -> String {
         std::env::consts::OS.to_owned()
     }
+    pub(crate) fn cpu_arch(&self) -> Option<String> {
+        get_cpu_arch()
+    }
 }
 
 pub(crate) fn is_proc_running(handle: HANDLE) -> bool {
@@ -540,3 +544,18 @@ fn get_dns_hostname() -> Option<String> {
     sysinfo_debug!("Failed to get computer hostname");
     None
 }
+
+fn get_cpu_arch() -> Option<String> {
+    // https://docs.microsoft.com/fr-fr/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info
+    unsafe {
+        let info = SYSTEM_INFO::default();
+        match info.Anonymous.Anonymous.wProcessorArchitecture {
+            SystemInformation::PROCESSOR_ARCHITECTURE_INTEL => Some("x86".to_string()),
+            SystemInformation::PROCESSOR_ARCHITECTURE_ARM => Some("arm".to_string()),
+            SystemInformation::PROCESSOR_ARCHITECTURE_AMD64 => Some("x86_64".to_string()),
+            SystemInformation::PROCESSOR_ARCHITECTURE_ARM64 => Some("arm64".to_string()),
+            SystemInformation::PROCESSOR_ARCHITECTURE_ARM32_ON_WIN64 => Some("arm".to_string()),
+            _ => None,
+        }
+    }
+}


Test patch:
diff --git a/tests/cpu.rs b/tests/cpu.rs
index 95c0bff..f1dc8c0 100644
--- a/tests/cpu.rs
+++ b/tests/cpu.rs
@@ -24,6 +24,7 @@ fn test_cpu() {
         assert!(s.cpus().iter().any(|c| !c.brand().is_empty()));
     }
     assert!(s.cpus().iter().any(|c| !c.vendor_id().is_empty()));
+    assert!(s.cpu_arch().is_some());
 }
 
 #[test]