From 4c7ffe10e2968ceadf3b27479b016fd9920d8a6e Mon Sep 17 00:00:00 2001 From: Rolv Apneseth Date: Sat, 28 Sep 2024 18:49:45 +0100 Subject: [PATCH] fix: rebase `8.0.0` branch with the latest changes from `main` (#179) * Fix Armv7 build (#158) * Add armv7 build target * Fix mismatched types for pointer width 32 * Use `sh` instead of `python` for `which` assertion * Replace mach with mach2 (#157) * feat: add support for RPM `ndb` databases (#159) * feat: add support for RPM `ndb` databases * chore: always try sqlite before librpm * chore: remove `rpm` feature * fix: librpm call not lazily evaluated * Add `librpm` dependency note to README (#160) * Update the changelog * Bump version to 7.1.0 * Fix i686 build (#162) closes #161 * Add Sonoma to macos_version_to_name (#163) * BREAKING CHANGE: Change BatteryReadout::health return value to u8 There's no particular reason why one should allocate 64 bits for a value that can only be <= 100. As a bonus, ceil() the return value before finally casting to u8. * Update BatteryReadout::health function signature for other operating systems besides Linux. * Added general detection for wayland compositors (#164) * Upgrade dependencies to their latest versions * Update changelog Make some formatting changes as well. * Add missing generic argument sqlite's changed a bit between releases. * Bump version to 7.2.0 * Add missing second generic argument to sqlite read() call * Remove unneeded argument to unistd::gethostname function call * Remove unused variable * Refactor obsolete find_ifa function * Bump version to 7.2.1 * Bump vergen version * Use gitcl feature of vergen This depends on the git binary, more ubiquituous than the libgit2 bindings, so it should technically work on every platform we support. * Refactor the old vergen interface * Add new entry to the changelog * Update version to 7.2.2 * Replace flatten() calls with map_while(Result::ok) * Fix Readouts struct's network field type Closes: #168 * Improve CI workflow (#169) * Replace discontinued actions-rs * Split cargo fmt and clippy into their own CI job * Faster package count on Alpine Linux (#170) * Bump version to 7.2.3 * added macos 15 version name (#171) https://www.apple.com/newsroom/2024/06/macos-sequoia-takes-productivity-and-intelligence-on-mac-to-new-heights/ * Removed panic if local gpu db is not able to be read (#173) * Add support for the Nix package manager (#172) Added support for the Nix package manager using Nix' SQLite database. * Bump version and update changelog * linux: Safely exit when homebrew is not installed * Improve linuxbrew keepme safeguard * Remove unused import * Bump version to 7.3.1 * Allow disk_space function to accept a path argument (#156) BREAKING CHANGE: allow disk_space function to accept a path argument - Bump version and update changelog - Change disk_space path argument to be of type &Path and check path exists in shared::disk_space - Add missing import for openwrt --------- Co-authored-by: Adrian Groh <50576978+Gobidev@users.noreply.github.com> Co-authored-by: Silas Groh Co-authored-by: grtcdr Co-authored-by: Rex Ng Co-authored-by: Absolpega <106615943+Absolpega@users.noreply.github.com> Co-authored-by: grtcdr Co-authored-by: Matthias Baer Co-authored-by: Rex Ng Co-authored-by: coolGi <57488297+coolGi2007@users.noreply.github.com> --- .github/workflows/libmacchina.yml | 69 ++++++++++--------- CHANGELOG.md | 71 +++++++++++++++++-- Cargo.toml | 43 ++++++------ README.md | 6 ++ build.rs | 20 ++---- src/android/mod.rs | 4 +- src/dirs.rs | 8 ++- src/extra.rs | 5 +- src/freebsd/mod.rs | 4 +- src/lib.rs | 6 +- src/linux/mod.rs | 110 +++++++++++++++++++++++------- src/linux/pci_devices.rs | 2 +- src/macos/mach_ffi.rs | 12 ++-- src/macos/mod.rs | 12 ++-- src/netbsd/mod.rs | 5 +- src/openwrt/mod.rs | 44 ++++++------ src/shared/mod.rs | 9 ++- src/traits.rs | 60 ++++++++-------- src/windows/mod.rs | 6 +- src/winman.rs | 71 ++++++++----------- 20 files changed, 347 insertions(+), 220 deletions(-) diff --git a/.github/workflows/libmacchina.yml b/.github/workflows/libmacchina.yml index d4de7ebd..3c3166a5 100644 --- a/.github/workflows/libmacchina.yml +++ b/.github/workflows/libmacchina.yml @@ -3,9 +3,31 @@ on: [push, pull_request] name: CI jobs: + lint: + runs-on: ubuntu-latest + name: Lint + env: + RUSTFLAGS: "-Dwarnings" + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Bootstrap + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + + - name: Formatting + run: cargo fmt --all -- --check + + - name: Clippy + run: cargo clippy --all-targets --all-features + checks: name: ${{ matrix.name }} (${{ matrix.target }}) runs-on: ${{ matrix.os }} + env: + PROGRAM: ${{ matrix.cross && 'cross' || 'cargo' }} strategy: fail-fast: false matrix: @@ -17,6 +39,7 @@ jobs: - x86_64-unknown-freebsd - aarch64-linux-android - aarch64-unknown-linux-gnu + - armv7-unknown-linux-gnueabihf include: - os: ubuntu-latest @@ -62,44 +85,28 @@ jobs: test: true cargo_args: --features "openwrt" + - os: ubuntu-latest + name: Linux ARMv7 + target: armv7-unknown-linux-gnueabihf + cross: true + test: true + steps: - name: Checkout - uses: actions/checkout@v2 + uses: actions/checkout@v4 - name: Bootstrap - uses: actions-rs/toolchain@v1 - with: - toolchain: stable - components: rustfmt, clippy - target: ${{ matrix.target }} - - - name: Formatting - uses: actions-rs/cargo@v1 + uses: dtolnay/rust-toolchain@stable with: - command: fmt - args: -- --check - use-cross: ${{ matrix.cross }} - continue-on-error: false + targets: ${{ matrix.target }} - - name: Lints - uses: actions-rs/cargo@v1 - with: - command: clippy - args: --target=${{ matrix.target }} -- --no-deps -D clippy::all - use-cross: ${{ matrix.cross }} - continue-on-error: false + - name: Install cross + run: cargo install cross + if: ${{ matrix.cross }} - name: Build - uses: actions-rs/cargo@v1 - with: - command: build - args: --target=${{ matrix.target }} ${{ matrix.cargo_args }} - use-cross: ${{ matrix.cross }} + run: ${{ env.PROGRAM }} build --target=${{ matrix.target }} ${{ matrix.cargo_args }} - - name: Test - uses: actions-rs/cargo@v1 - with: - command: test - args: --target=${{ matrix.target }} ${{ matrix.cargo_args }} - use-cross: ${{ matrix.cross }} + - name: Test + run: ${{ env.PROGRAM }} test --target=${{ matrix.target }} ${{ matrix.cargo_args }} if: ${{ matrix.test }} diff --git a/CHANGELOG.md b/CHANGELOG.md index 8276e583..aec76046 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,63 @@ - Rolv Apneseth: - BREAKING CHANGE: Allow disk_space function to accept a path argument (#156) +## `7.3.1` + +- Fix overflow affecting linux::count_homebrew() implementation + +## `7.3.0` + +coolGi2007: +- Add support for the Nix package manager +- Bump `sqlite` dependency to version `0.36.0` +- Don't panic if the `pci.ids` database could not be found + +Rex Ng: +- Recognize latest version of macOS + +## `7.2.3` + +- Fix `Readouts` struct `network` field type + +Matthias Baer: +- Improve CI workflow (#169) + +Adrian Groh: +- Faster package count on Alpine Linux (#170) + +## `7.2.2` + +- Update `vergen` dependency and correct its invocation. + +## `7.2.1` + +- Fix some build errors + +## `7.2.0` + +Adrian Groh: +- Fix build errors affecting i686 (#162) + +Rex Ng: +- Recognize macOS Sonoma (#163) + +Absolpega: +- Add general detection for Wayland (#164) + +- Change the return value of BatteryReadout::health from `u64` to `u8` +- Upgrade dependencies to their latest versions + +## `7.1.0` + +Silas Groh: + - Add support for `ndb` databases + +Adrian Groh: + - Replace `mach` dependency with `mach2` + - Replace `python` command with `sh` in `extra::which` unit tests + - Add armv7 to the list of build targets in the CI pipeline + - Fix compilation issues for armv7 build target + ## `7.0.0` - Rolv Apneseth: @@ -18,15 +75,19 @@ ## `6.4.0` -- Adrian Groh: +Adrian Groh: - Use the correct kernel parameters when initializing FreeBSD `KernelReadout` (#148) - Implement uptime readout for FreeBSD systems (#138) - Use `MemAvailable` to calculate used memory (#134) - Prioritize detecting window managers with xprop (#133) -- Rolv Apneseth: Implement GPU readout for Linux systems (#140) -- Matthias Baer: Use a singleton for `COMLibrary` (#143) -- Xarblu: Change Flatpak package-counting method (#125) -- Kian-Meng Ang: Fix a typo in the documentation + +Rolv Apneseth: Implement GPU readout for Linux systems (#140) + +Matthias Baer: Use a singleton for `COMLibrary` (#143) + +Xarblu: Change Flatpak package-counting method (#125) + +Kian-Meng Ang: Fix a typo in the documentation ## `6.3.5` diff --git a/Cargo.toml b/Cargo.toml index 79dbc87a..4dfc503e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,37 +12,40 @@ build = "build.rs" [dependencies] cfg-if = "1.0.0" -libc = "0.2.131" -home = "0.5.3" -pciid-parser = "0.6.2" +libc = "0.2.148" +home = "0.5.5" +pciid-parser = "0.6.3" [build-dependencies.vergen] -version = "7.3.2" +version = "8.2.6" optional = true default-features = false -features = ["build","cargo","git","rustc"] +features = ["build","cargo","git","gitcl","rustc"] [target.'cfg(target_os = "linux")'.dependencies] -dirs = "4.0" -walkdir = "2.3.2" +dirs = "5.0.1" +walkdir = "2.4.0" os-release = "0.1" -regex = "1.6.0" +regex = "1.9.2" +rpm-pkg-count = { version = "0.2.1", features = ["runtime"] } +nix = { version = "0.26.2", features = ["socket"], default-features = false } +wayland-sys = { version = "0.31.1", features = ["dlopen", "client"] } [target.'cfg(target_os = "netbsd")'.dependencies] -nix = { version = "0.24.1", default-features = false, features = ["hostname"] } -regex = "1.6.0" +nix = { version = "0.26.2", default-features = false, features = ["hostname"] } +regex = "1.9.2" [target.'cfg(target_os = "macos")'.dependencies] core-foundation = "0.9.3" -core-graphics = "0.22.3" +core-graphics = "0.23.1" core-video-sys = "0.1.4" -mach = "0.3.2" +mach2 = "0.4.1" [target.'cfg(target_family = "unix")'.dependencies] -num_cpus = "1.13.1" +num_cpus = "1.16.0" [target.'cfg(target_os = "windows")'.dependencies] -local-ip-address = "0.4.4" +local-ip-address = "0.5.6" wmi = "0.12.0" winreg = "0.10.1" windows = { version = "0.39.0", features = [ @@ -53,22 +56,22 @@ windows = { version = "0.39.0", features = [ ]} [target.'cfg(not(target_os = "windows"))'.dependencies] -if-addrs = "0.6.7" +if-addrs = "0.10.2" [target.'cfg(any(target_os="freebsd", target_os = "linux"))'.dependencies] -sqlite = "0.27.0" +sqlite = "0.36.0" [target.'cfg(any(target_os="freebsd", target_os = "netbsd"))'.dependencies] -x11rb = "0.10.1" +x11rb = "0.12.0" [target.'cfg(any(target_os = "linux", target_os = "netbsd", target_os = "android"))'.dependencies] -itertools = "0.10.3" +itertools = "0.11.0" [target.'cfg(not(any(target_os = "netbsd", target_os = "windows")))'.dependencies] -sysctl = "0.4.6" +sysctl = "0.5.4" [target.'cfg(any(target_os = "linux", target_os = "netbsd"))'.build-dependencies] -pkg-config = { version = "0.3.25", optional = true} +pkg-config = { version = "0.3.27", optional = true} [features] openwrt = [] diff --git a/README.md b/README.md index b266c776..465bc34c 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,12 @@ Add the following to your project's _Cargo.toml_ file: libmacchina = "7" ``` +### Notes + +On distributions like openSUSE that use the `ndb` RPM database format, `librpm` +(which is usually provided by the `rpm-devel` package) is required for the RPM +package count readout to work. + ### Examples ```rust diff --git a/build.rs b/build.rs index 56b4037d..ef531e4b 100644 --- a/build.rs +++ b/build.rs @@ -1,16 +1,4 @@ -use std::env; - -#[cfg(feature = "version")] -fn commit_hash() { - use vergen::{Config, ShaKind}; - - let mut config = Config::default(); - *config.git_mut().sha_kind_mut() = ShaKind::Short; - - if let Err(e) = vergen::vergen(config) { - eprintln!("{}", e); - } -} +use std::{env, error::Error}; fn build_macos() { println!("cargo:rustc-link-lib=framework=Foundation"); @@ -20,7 +8,7 @@ fn build_macos() { println!("cargo:rustc-link-lib=framework=DisplayServices"); } -fn main() { +fn main() -> Result<(), Box> { match env::var("CARGO_CFG_TARGET_OS").as_ref().map(|x| &**x) { Ok("macos") => build_macos(), Ok("netbsd") => {} @@ -28,5 +16,7 @@ fn main() { } #[cfg(feature = "version")] - commit_hash() + vergen::EmitBuilder::builder().emit()?; + + Ok(()) } diff --git a/src/android/mod.rs b/src/android/mod.rs index 83b1ce3f..aa4c7f0e 100644 --- a/src/android/mod.rs +++ b/src/android/mod.rs @@ -79,7 +79,7 @@ impl BatteryReadout for AndroidBatteryReadout { } } - fn health(&self) -> Result { + fn health(&self) -> Result { Err(ReadoutError::NotImplemented) } } @@ -216,7 +216,7 @@ impl GeneralReadout for AndroidGeneralReadout { if let Ok(content) = file { let reader = BufReader::new(content); - for line in reader.lines().flatten() { + for line in reader.lines().map_while(Result::ok) { if line.starts_with("Hardware") { hardware = Some(get_value_from_line(line, "Hardware")); break; // If "Hardware" information is present, the rest is not needed. diff --git a/src/dirs.rs b/src/dirs.rs index 011b15d4..e9e80303 100644 --- a/src/dirs.rs +++ b/src/dirs.rs @@ -14,7 +14,9 @@ where /// Returns the value of PKG_DBDIR if exists or a default if not. pub fn pkgdb_dir() -> Option { if let Ok(lines) = read_lines("/etc/mk.conf") { - let line = lines.flatten().find(|l| l.starts_with("PKG_DBDIR")); + let line = lines + .map_while(Result::ok) + .find(|l| l.starts_with("PKG_DBDIR")); if let Some(pkg_dbdir) = line { if let Some(value) = pkg_dbdir.split('=').nth(1) { @@ -29,7 +31,9 @@ pub fn pkgdb_dir() -> Option { /// Returns the value of LOCALBASE if exists or a default if not. pub fn localbase_dir() -> Option { if let Ok(lines) = read_lines("/etc/mk.conf") { - let line = lines.flatten().find(|l| l.starts_with("LOCALBASE")); + let line = lines + .map_while(Result::ok) + .find(|l| l.starts_with("LOCALBASE")); if let Some(pkg_dbdir) = line { if let Some(value) = pkg_dbdir.split('=').nth(1) { diff --git a/src/extra.rs b/src/extra.rs index 99c3b18c..6974d777 100644 --- a/src/extra.rs +++ b/src/extra.rs @@ -73,7 +73,8 @@ Returns the entries of a given `Path`. pub fn get_entries(path: &Path) -> Option> { if let Ok(dir) = std::fs::read_dir(path) { let mut entries: Vec = Vec::new(); - dir.flatten().for_each(|x| entries.push(x.path())); + dir.map_while(Result::ok) + .for_each(|x| entries.push(x.path())); return Some(entries); } @@ -118,7 +119,7 @@ mod tests { #[test] #[cfg(not(feature = "openwrt"))] fn test_which() { - assert!(which("python")); + assert!(which("sh")); assert!(!which("not_a_real_command")); } } diff --git a/src/freebsd/mod.rs b/src/freebsd/mod.rs index 2708a72a..5ac976d7 100644 --- a/src/freebsd/mod.rs +++ b/src/freebsd/mod.rs @@ -80,7 +80,7 @@ impl BatteryReadout for FreeBSDBatteryReadout { Err(ReadoutError::MetricNotAvailable) } - fn health(&self) -> Result { + fn health(&self) -> Result { Err(ReadoutError::NotImplemented) } } @@ -401,7 +401,7 @@ impl FreeBSDPackageReadout { let statement = con.prepare("SELECT COUNT(*) FROM packages"); if let Ok(mut s) = statement { if s.next().is_ok() { - return match s.read::>(0) { + return match s.read::, _>(0) { Ok(Some(count)) => Some(count as usize), _ => None, }; diff --git a/src/lib.rs b/src/lib.rs index b8f60e0f..df9d5591 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -93,15 +93,15 @@ pub struct Readouts { pub general: GeneralReadout, pub product: ProductReadout, pub packages: PackageReadout, - pub network: PackageReadout, + pub network: NetworkReadout, } #[cfg(feature = "version")] pub fn version() -> &'static str { if let Some(git_sha) = option_env!("VERGEN_GIT_SHA_SHORT") { - return Box::leak(format!("{} ({})", env!("CARGO_PKG_VERSION"), git_sha).into_boxed_str()); + Box::leak(format!("{} ({})", env!("CARGO_PKG_VERSION"), git_sha).into_boxed_str()) } else { - return env!("CARGO_PKG_VERSION"); + env!("CARGO_PKG_VERSION") } } diff --git a/src/linux/mod.rs b/src/linux/mod.rs index a07cd4cc..173c07e5 100644 --- a/src/linux/mod.rs +++ b/src/linux/mod.rs @@ -11,6 +11,7 @@ use crate::traits::*; use itertools::Itertools; use pciid_parser::Database; use regex::Regex; +use std::ffi::OsStr; use std::fs; use std::fs::read_dir; use std::fs::File; @@ -117,7 +118,7 @@ impl BatteryReadout for LinuxBatteryReadout { Err(ReadoutError::Other("No batteries detected.".to_string())) } - fn health(&self) -> Result { + fn health(&self) -> Result { if let Some(entries) = get_entries(Path::new("/sys/class/power_supply")) { let dirs: Vec = entries .into_iter() @@ -134,20 +135,20 @@ impl BatteryReadout for LinuxBatteryReadout { if let Some(battery) = dirs.first() { let energy_full = extra::pop_newline(fs::read_to_string(battery.join("energy_full"))?) - .parse::(); + .parse::(); let energy_full_design = extra::pop_newline(fs::read_to_string(battery.join("energy_full_design"))?) - .parse::(); + .parse::(); match (energy_full, energy_full_design) { (Ok(mut ef), Ok(efd)) => { if ef > efd { ef = efd; - return Ok(((ef as f64 / efd as f64) * 100_f64) as u64); + return Ok((ef as f32 / efd as f32 * 100_f32).ceil() as u8); } - return Ok(((ef as f64 / efd as f64) * 100_f64) as u64); + return Ok((ef as f32 / efd as f32 * 100_f32).ceil() as u8); } _ => { return Err(ReadoutError::Other( @@ -391,7 +392,7 @@ impl GeneralReadout for LinuxGeneralReadout { match file { Ok(content) => { let reader = BufReader::new(content); - for line in reader.lines().flatten() { + for line in reader.lines().map_while(Result::ok) { if line.to_uppercase().starts_with("PPID") { let s_mem_kb: String = line.chars().filter(|c| c.is_ascii_digit()).collect(); @@ -478,7 +479,7 @@ impl GeneralReadout for LinuxGeneralReadout { use std::io::{BufRead, BufReader}; if let Ok(content) = File::open("/proc/cpuinfo") { let reader = BufReader::new(content); - for line in reader.lines().flatten() { + for line in reader.lines().map_while(Result::ok) { if line.to_lowercase().starts_with("cpu cores") { return Ok(line .split(':') @@ -550,7 +551,7 @@ impl GeneralReadout for LinuxGeneralReadout { fn gpus(&self) -> Result, ReadoutError> { let db = match Database::read() { Ok(db) => db, - _ => panic!("Could not read pci.ids file"), + _ => return Err(ReadoutError::MetricNotAvailable), }; let devices = get_pci_devices()?; @@ -726,6 +727,10 @@ impl PackageReadout for LinuxPackageReadout { packages.push((PackageManager::Homebrew, c)); } + if let Some(c) = LinuxPackageReadout::count_nix() { + packages.push((PackageManager::Nix, c)); + } + packages } } @@ -736,25 +741,30 @@ impl LinuxPackageReadout { fn count_rpm() -> Option { // Return the number of installed packages using sqlite (~1ms) // as directly calling rpm or dnf is too expensive (~500ms) - let db = "/var/lib/rpm/rpmdb.sqlite"; - if !Path::new(db).is_file() { - return None; - } + let count_sqlite = 'sqlite: { + let db = "/var/lib/rpm/rpmdb.sqlite"; + if !Path::new(db).is_file() { + break 'sqlite None; + } - let connection = sqlite::open(db); - if let Ok(con) = connection { - let statement = con.prepare("SELECT COUNT(*) FROM Installtid"); - if let Ok(mut s) = statement { - if s.next().is_ok() { - return match s.read::>(0) { - Ok(Some(count)) => Some(count as usize), - _ => None, - }; + let connection = sqlite::open(db); + if let Ok(con) = connection { + let statement = con.prepare("SELECT COUNT(*) FROM Installtid"); + if let Ok(mut s) = statement { + if s.next().is_ok() { + break 'sqlite match s.read::, _>(0) { + Ok(Some(count)) => Some(count as usize), + _ => None, + }; + } } } - } - None + None + }; + + // If counting with sqlite failed, try using librpm instead + count_sqlite.or_else(|| unsafe { rpm_pkg_count::count() }.map(|count| count as usize)) } /// Returns the number of installed packages for systems @@ -816,14 +826,25 @@ impl LinuxPackageReadout { /// Returns the number of installed packages for systems /// that have `homebrew` installed. fn count_homebrew(home: &Path) -> Option { + let keepme = OsStr::new(".keepme"); let mut base = home.join(".linuxbrew"); + if !base.is_dir() { base = PathBuf::from("/home/linuxbrew/.linuxbrew"); } + if !base.is_dir() { + return None; + } + match read_dir(base.join("Cellar")) { - // subtract 1 as ${base}/Cellar contains a ".keepme" file - Ok(dir) => Some(dir.count() - 1), + Ok(dir) => Some( + dir.filter(|entry| match entry { + Err(_) => false, + Ok(file) => file.file_name() != keepme, + }) + .count(), + ), Err(_) => None, } } @@ -850,6 +871,12 @@ impl LinuxPackageReadout { /// Returns the number of installed packages for systems /// that utilize `apk` as their package manager. fn count_apk() -> Option { + // faster method for alpine: count empty lines in /lib/apk/db/installed + if let Ok(content) = fs::read_to_string(Path::new("/lib/apk/db/installed")) { + return Some(content.lines().filter(|l| l.is_empty()).count()); + } + + // fallback to command invocation if !extra::which("apk") { return None; } @@ -923,4 +950,37 @@ impl LinuxPackageReadout { None } + + /// Returns the number of installed packages for systems + /// that utilize `nix` as their package manager. + fn count_nix() -> Option { + return 'sqlite: { + let db = "/nix/var/nix/db/db.sqlite"; + if !Path::new(db).is_file() { + break 'sqlite None; + } + + let connection = sqlite::Connection::open_with_flags( + // The nix store is immutable, so we need to inform sqlite about it + "file:".to_owned() + db + "?immutable=1", + sqlite::OpenFlags::new().with_read_only().with_uri(), + ); + + if let Ok(con) = connection { + let statement = + con.prepare("SELECT COUNT(path) FROM ValidPaths WHERE sigs IS NOT NULL"); + + if let Ok(mut s) = statement { + if s.next().is_ok() { + break 'sqlite match s.read::, _>(0) { + Ok(Some(count)) => Some(count as usize), + _ => None, + }; + } + } + } + + None + }; + } } diff --git a/src/linux/pci_devices.rs b/src/linux/pci_devices.rs index 76cad3d2..8fc7b724 100644 --- a/src/linux/pci_devices.rs +++ b/src/linux/pci_devices.rs @@ -100,7 +100,7 @@ pub fn get_pci_devices() -> Result, io::Error> { let devices_dir = read_dir("/sys/bus/pci/devices/")?; let mut devices = vec![]; - for device_entry in devices_dir.flatten() { + for device_entry in devices_dir.map_while(Result::ok) { devices.push(PciDevice::new(device_entry.path())); } diff --git a/src/macos/mach_ffi.rs b/src/macos/mach_ffi.rs index 39099936..b54ee1ff 100644 --- a/src/macos/mach_ffi.rs +++ b/src/macos/mach_ffi.rs @@ -1,11 +1,11 @@ #![allow(non_camel_case_types, non_upper_case_globals, dead_code, unused)] -use mach::boolean; -use mach::kern_return; -use mach::kern_return::kern_return_t; -use mach::mach_types::{host_name_port_t, host_t}; -use mach::message::mach_msg_type_number_t; -use mach::vm_types::{integer_t, natural_t}; +use mach2::boolean; +use mach2::kern_return; +use mach2::kern_return::kern_return_t; +use mach2::mach_types::{host_name_port_t, host_t}; +use mach2::message::mach_msg_type_number_t; +use mach2::vm_types::{integer_t, natural_t}; use core_foundation::array::CFArrayRef; use core_foundation::base::{mach_port_t, CFAllocatorRef, CFRelease, CFTypeRef, TCFTypeRef}; diff --git a/src/macos/mod.rs b/src/macos/mod.rs index e2146adf..b0e91705 100644 --- a/src/macos/mod.rs +++ b/src/macos/mod.rs @@ -17,7 +17,7 @@ use core_video_sys::{ kCVTimeIsIndefinite, CVDisplayLinkCreateWithCGDisplay, CVDisplayLinkGetNominalOutputVideoRefreshPeriod, CVDisplayLinkRef, CVDisplayLinkRelease, }; -use mach::kern_return::KERN_SUCCESS; +use mach2::kern_return::KERN_SUCCESS; use std::ffi::CString; use std::fs::DirEntry; use std::path::Path; @@ -94,7 +94,7 @@ impl BatteryReadout for MacOSBatteryReadout { ))) } - fn health(&self) -> Result { + fn health(&self) -> Result { Err(ReadoutError::NotImplemented) } } @@ -493,9 +493,9 @@ impl MemoryReadout for MacOSMemoryReadout { impl MacOSMemoryReadout { fn mach_vm_stats() -> Result { - use mach::kern_return::KERN_SUCCESS; - use mach::message::mach_msg_type_number_t; - use mach::vm_types::integer_t; + use mach2::kern_return::KERN_SUCCESS; + use mach2::message::mach_msg_type_number_t; + use mach2::vm_types::integer_t; use mach_ffi::*; const HOST_VM_INFO_COUNT: mach_msg_type_number_t = @@ -691,6 +691,8 @@ fn macos_version_to_name(version: &NSOperatingSystemVersion) -> &'static str { (11, _) | (10, 16) => "Big Sur", (12, _) => "Monterey", (13, _) => "Ventura", + (14, _) => "Sonoma", + (15, _) => "Sequoia", _ => "Unknown", } } diff --git a/src/netbsd/mod.rs b/src/netbsd/mod.rs index f531d9db..29019e34 100644 --- a/src/netbsd/mod.rs +++ b/src/netbsd/mod.rs @@ -82,7 +82,7 @@ impl BatteryReadout for NetBSDBatteryReadout { Err(ReadoutError::Other("envstat is not installed".to_owned())) } - fn health(&self) -> Result { + fn health(&self) -> Result { Err(ReadoutError::NotImplemented) } } @@ -178,8 +178,7 @@ impl GeneralReadout for NetBSDGeneralReadout { } fn hostname(&self) -> Result { - let mut buf = [0u8; 64]; - let hostname_cstr = unistd::gethostname(&mut buf); + let hostname_cstr = unistd::gethostname(); match hostname_cstr { Ok(hostname_cstr) => { let hostname = hostname_cstr.to_str().unwrap_or("Unknown"); diff --git a/src/openwrt/mod.rs b/src/openwrt/mod.rs index 30c9b6d0..90b717fe 100644 --- a/src/openwrt/mod.rs +++ b/src/openwrt/mod.rs @@ -42,7 +42,7 @@ impl BatteryReadout for OpenWrtBatteryReadout { Err(ReadoutError::NotImplemented) } - fn health(&self) -> Result { + fn health(&self) -> Result { Err(ReadoutError::NotImplemented) } } @@ -93,11 +93,11 @@ impl GeneralReadout for OpenWrtGeneralReadout { let file = fs::File::open("/proc/cpuinfo"); if let Ok(content) = file { let reader = BufReader::new(content); - for line in reader.lines().into_iter().flatten() { + for line in reader.lines().map_while(Result::ok) { if line.starts_with("machine") { return Ok(line .replace("machine", "") - .replace(":", "") + .replace(':', "") .trim() .to_string()); } @@ -155,11 +155,11 @@ impl GeneralReadout for OpenWrtGeneralReadout { let file = fs::File::open("/proc/cpuinfo"); if let Ok(content) = file { let reader = BufReader::new(content); - for line in reader.lines().into_iter().flatten() { + for line in reader.lines().map_while(Result::ok) { if line.starts_with("cpu model") { return Ok(line .replace("cpu model", "") - .replace(":", "") + .replace(':', "") .trim() .to_string()); } @@ -187,11 +187,11 @@ impl GeneralReadout for OpenWrtGeneralReadout { let f_load = 1f64 / (1 << libc::SI_LOAD_SHIFT) as f64; let cpu_usage = info.loads[0] as f64 * f_load; let cpu_usage_u = (cpu_usage / num_cpus::get() as f64 * 100.0).round() as usize; - return Ok(cpu_usage_u as usize); + Ok(cpu_usage_u as usize) } else { - return Err(ReadoutError::Other(String::from( + Err(ReadoutError::Other(String::from( "sysinfo struct returned an error.", - ))); + ))) } } @@ -200,11 +200,11 @@ impl GeneralReadout for OpenWrtGeneralReadout { let info_ptr: *mut sysinfo = &mut info; let ret = unsafe { sysinfo(info_ptr) }; if ret != -1 { - return Ok(info.uptime as usize); + Ok(info.uptime as usize) } else { - return Err(ReadoutError::Other(String::from( + Err(ReadoutError::Other(String::from( "sysinfo struct returned an error.", - ))); + ))) } } @@ -233,11 +233,11 @@ impl MemoryReadout for OpenWrtMemoryReadout { let info_ptr: *mut sysinfo = &mut info; let ret = unsafe { sysinfo(info_ptr) }; if ret != -1 { - return Ok(info.totalram as u64 * info.mem_unit as u64 / 1024); + Ok(info.totalram as u64 * info.mem_unit as u64 / 1024) } else { - return Err(ReadoutError::Other(String::from( + Err(ReadoutError::Other(String::from( "sysinfo struct returned an error.", - ))); + ))) } } @@ -246,11 +246,11 @@ impl MemoryReadout for OpenWrtMemoryReadout { let info_ptr: *mut sysinfo = &mut info; let ret = unsafe { sysinfo(info_ptr) }; if ret != -1 { - return Ok(info.freeram as u64 * info.mem_unit as u64 / 1024); + Ok(info.freeram as u64 * info.mem_unit as u64 / 1024) } else { - return Err(ReadoutError::Other(String::from( + Err(ReadoutError::Other(String::from( "sysinfo struct returned an error.", - ))); + ))) } } @@ -259,11 +259,11 @@ impl MemoryReadout for OpenWrtMemoryReadout { let info_ptr: *mut sysinfo = &mut info; let ret = unsafe { sysinfo(info_ptr) }; if ret != -1 { - return Ok(info.bufferram as u64 * info.mem_unit as u64 / 1024); + Ok(info.bufferram as u64 * info.mem_unit as u64 / 1024) } else { - return Err(ReadoutError::Other(format!( - "Failed to get system statistics" - ))); + Err(ReadoutError::Other(String::from( + "Failed to get system statistics", + ))) } } @@ -316,7 +316,7 @@ impl OpenWrtPackageReadout { let file = fs::File::open("/usr/lib/opkg/status"); if let Ok(content) = file { let reader = BufReader::new(content); - for line in reader.lines().flatten() { + for line in reader.lines().map_while(Result::ok) { if line.starts_with("Package:") { count += 1 } diff --git a/src/shared/mod.rs b/src/shared/mod.rs index 485a6cbb..184e7a0f 100644 --- a/src/shared/mod.rs +++ b/src/shared/mod.rs @@ -202,7 +202,7 @@ pub(crate) fn cpu_model_name() -> String { match file { Ok(content) => { let reader = BufReader::new(content); - for line in reader.lines().flatten() { + for line in reader.lines().map_while(Result::ok) { if line.starts_with("model name") { return line .replace("model name", "") @@ -266,12 +266,15 @@ pub(crate) fn disk_space(path: &Path) -> Result<(u64, u64), ReadoutError> { let stats: libc::statfs = unsafe { s.assume_init() }; - let disk_size = stats.f_blocks * stats.f_bsize as UInt; + let disk_size = stats.f_blocks as UInt * stats.f_bsize as UInt; let free = stats.f_bavail as UInt * stats.f_bsize as UInt; let used_byte = disk_size - free; let disk_size_byte = disk_size; + #[cfg(target_pointer_width = "32")] + return Ok((used_byte.into(), disk_size_byte.into())); + #[cfg(target_pointer_width = "64")] return Ok((used_byte, disk_size_byte)); } @@ -288,7 +291,7 @@ pub(crate) fn get_meminfo_value(value: &str) -> u64 { match file { Ok(content) => { let reader = BufReader::new(content); - for line in reader.lines().flatten() { + for line in reader.lines().map_while(Result::ok) { if line.starts_with(value) { let s_mem_kb: String = line.chars().filter(|c| c.is_ascii_digit()).collect(); return s_mem_kb.parse::().unwrap_or(0); diff --git a/src/traits.rs b/src/traits.rs index 29f6c7cf..5ed9313c 100644 --- a/src/traits.rs +++ b/src/traits.rs @@ -24,17 +24,17 @@ pub enum ReadoutError { Warning(String), } -impl ToString for ReadoutError { - fn to_string(&self) -> String { +impl std::fmt::Display for ReadoutError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { ReadoutError::MetricNotAvailable => { - String::from("Metric is not available on this system.") + write!(f, "Metric is not available on this system.") } ReadoutError::NotImplemented => { - String::from("This metric is not available on this platform or is not yet implemented by libmacchina.") + write!(f, "This metric is not available on this platform or is not yet implemented by libmacchina.") } - ReadoutError::Other(s) => s.clone(), - ReadoutError::Warning(s) => s.clone(), + ReadoutError::Other(s) => write!(f, "{}", s), + ReadoutError::Warning(s) => write!(f, "{}", s), } } } @@ -76,7 +76,7 @@ impl BatteryReadout for MacOSBatteryReadout { Ok(BatteryState::Charging) //always charging. } - fn health(&self) -> Result{ + fn health(&self) -> Result{ //check the battery health... Ok(100) //totally healtyh } @@ -97,7 +97,7 @@ pub trait BatteryReadout { fn status(&self) -> Result; /// This function is used for querying the current battery's health in percentage. - fn health(&self) -> Result; + fn health(&self) -> Result; } /** @@ -658,28 +658,30 @@ pub enum PackageManager { Android, Pkg, Scoop, + Nix, } -impl ToString for PackageManager { - fn to_string(&self) -> String { - String::from(match self { - PackageManager::Homebrew => "Homebrew", - PackageManager::MacPorts => "MacPorts", - PackageManager::Pacman => "pacman", - PackageManager::Portage => "portage", - PackageManager::Dpkg => "dpkg", - PackageManager::Opkg => "opkg", - PackageManager::Xbps => "xbps", - PackageManager::Pkgsrc => "pkgsrc", - PackageManager::Apk => "apk", - PackageManager::Eopkg => "eopkg", - PackageManager::Rpm => "rpm", - PackageManager::Cargo => "cargo", - PackageManager::Flatpak => "flatpak", - PackageManager::Snap => "snap", - PackageManager::Android => "Android", - PackageManager::Pkg => "pkg", - PackageManager::Scoop => "Scoop", - }) +impl std::fmt::Display for PackageManager { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + PackageManager::Homebrew => write!(f, "Homebrew"), + PackageManager::MacPorts => write!(f, "MacPorts"), + PackageManager::Pacman => write!(f, "pacman"), + PackageManager::Portage => write!(f, "portage"), + PackageManager::Dpkg => write!(f, "dpkg"), + PackageManager::Opkg => write!(f, "opkg"), + PackageManager::Xbps => write!(f, "xbps"), + PackageManager::Pkgsrc => write!(f, "pkgsrc"), + PackageManager::Apk => write!(f, "apk"), + PackageManager::Eopkg => write!(f, "eopkg"), + PackageManager::Rpm => write!(f, "rpm"), + PackageManager::Cargo => write!(f, "cargo"), + PackageManager::Flatpak => write!(f, "flatpak"), + PackageManager::Snap => write!(f, "snap"), + PackageManager::Android => write!(f, "Android"), + PackageManager::Pkg => write!(f, "pkg"), + PackageManager::Scoop => write!(f, "Scoop"), + PackageManager::Nix => write!(f, "nix"), + } } } diff --git a/src/windows/mod.rs b/src/windows/mod.rs index 6de5c16f..1bd04269 100644 --- a/src/windows/mod.rs +++ b/src/windows/mod.rs @@ -53,7 +53,7 @@ impl BatteryReadout for WindowsBatteryReadout { } } - fn health(&self) -> Result { + fn health(&self) -> Result { Err(ReadoutError::NotImplemented) } } @@ -455,9 +455,9 @@ impl NetworkReadout for WindowsNetworkReadout { fn logical_address(&self, interface: Option<&str>) -> Result { match interface { - Some(it) => { + Some(interface) => { if let Ok(addresses) = local_ip_address::list_afinet_netifas() { - if let Some((_, ip)) = local_ip_address::find_ifa(addresses, it) { + if let Some((_, ip)) = addresses.iter().find(|(name, _)| name == interface) { return Ok(ip.to_string()); } } diff --git a/src/winman.rs b/src/winman.rs index 1e157abb..f64241c2 100644 --- a/src/winman.rs +++ b/src/winman.rs @@ -7,56 +7,45 @@ use crate::traits::ReadoutError; use std::process::{Command, Stdio}; #[cfg(target_os = "linux")] -/// Detects if the host is using Sway. -pub fn is_running_sway() -> bool { - if let Ok(socket) = std::env::var("SWAYSOCK") { - if std::path::PathBuf::from(socket).exists() { - return true; - } - } - - false -} +use wayland_sys::{client::*, ffi_dispatch}; #[cfg(target_os = "linux")] -/// Detects if the host is using Wayfire. -pub fn is_running_wayfire() -> bool { - if let Ok(config) = std::env::var("WAYFIRE_CONFIG_FILE") { - if std::path::PathBuf::from(config).exists() { - return true; - } - } +use nix::sys::socket::{sockopt, GetSockOpt}; - false -} +#[cfg(target_os = "linux")] +use std::os::fd::AsRawFd; #[cfg(target_os = "linux")] -/// Detects if the host is using Qtile. -pub fn is_running_qtile() -> bool { - if let Some(cache) = dirs::cache_dir() { - if let Ok(display) = std::env::var("WAYLAND_DISPLAY") { - let socket = cache.join("qtile").join(format!("qtilesocket.{display}")); - - if socket.exists() { - return true; - } - } +pub fn detect_wayland_window_manager() -> Result { + if !is_lib_available() { + return Err(ReadoutError::MetricNotAvailable); } - false -} + let display_ptr = unsafe { + ffi_dispatch!( + wayland_client_handle(), + wl_display_connect, + ::std::ptr::null() + ) + }; -#[cfg(target_os = "linux")] -pub fn detect_wayland_window_manager() -> Result { - if is_running_sway() { - Ok(String::from("Sway")) - } else if is_running_qtile() { - Ok(String::from("Qtile")) - } else if is_running_wayfire() { - Ok(String::from("Wayfire")) - } else { - Err(ReadoutError::Other(String::from("Unknown window manager."))) + if display_ptr.is_null() { + return Err(ReadoutError::MetricNotAvailable); } + + let display_fd = + unsafe { ffi_dispatch!(wayland_client_handle(), wl_display_get_fd, display_ptr) } + .as_raw_fd(); + + let pid = sockopt::PeerCredentials + .get(display_fd) + .map_err(|_| ReadoutError::MetricNotAvailable)? + .pid(); + + Ok(extra::pop_newline(std::fs::read_to_string(format!( + "/proc/{}/comm", + pid + ))?)) } pub fn detect_xorg_window_manager() -> Result {