os_info-3.13.0/.cargo_vcs_info.json0000644000000001450000000000100125540ustar { "git": { "sha1": "ac803e4783d063ee16f70989b95d0bc9999b0b47" }, "path_in_vcs": "os_info" }os_info-3.13.0/Cargo.lock0000644000000210320000000000100105250ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "android_system_properties" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ "libc", ] [[package]] name = "bitflags" version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" [[package]] name = "block2" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" dependencies = [ "objc2", ] [[package]] name = "cfg-if" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "diff" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "dispatch2" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" dependencies = [ "bitflags", "objc2", ] [[package]] name = "doc-comment" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "libc" version = "0.2.177" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" [[package]] name = "log" version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" [[package]] name = "nix" version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ "bitflags", "cfg-if", "cfg_aliases", "libc", ] [[package]] name = "objc2" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05" dependencies = [ "objc2-encode", ] [[package]] name = "objc2-cloud-kit" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" dependencies = [ "bitflags", "objc2", "objc2-foundation", ] [[package]] name = "objc2-core-data" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" dependencies = [ "objc2", "objc2-foundation", ] [[package]] name = "objc2-core-foundation" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ "bitflags", "dispatch2", "objc2", ] [[package]] name = "objc2-core-graphics" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" dependencies = [ "bitflags", "dispatch2", "objc2", "objc2-core-foundation", "objc2-io-surface", ] [[package]] name = "objc2-core-image" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" dependencies = [ "objc2", "objc2-foundation", ] [[package]] name = "objc2-core-location" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" dependencies = [ "objc2", "objc2-foundation", ] [[package]] name = "objc2-core-text" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" dependencies = [ "bitflags", "objc2", "objc2-core-foundation", "objc2-core-graphics", ] [[package]] name = "objc2-encode" version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" [[package]] name = "objc2-foundation" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ "bitflags", "block2", "libc", "objc2", "objc2-core-foundation", ] [[package]] name = "objc2-io-surface" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" dependencies = [ "bitflags", "objc2", "objc2-core-foundation", ] [[package]] name = "objc2-quartz-core" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" dependencies = [ "bitflags", "objc2", "objc2-core-foundation", "objc2-foundation", ] [[package]] name = "objc2-ui-kit" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" dependencies = [ "bitflags", "block2", "objc2", "objc2-cloud-kit", "objc2-core-data", "objc2-core-foundation", "objc2-core-graphics", "objc2-core-image", "objc2-core-location", "objc2-core-text", "objc2-foundation", "objc2-quartz-core", "objc2-user-notifications", ] [[package]] name = "objc2-user-notifications" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" dependencies = [ "objc2", "objc2-foundation", ] [[package]] name = "os_info" version = "3.13.0" dependencies = [ "android_system_properties", "doc-comment", "log", "nix", "objc2", "objc2-foundation", "objc2-ui-kit", "pretty_assertions", "serde", "windows-sys", ] [[package]] name = "pretty_assertions" version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" dependencies = [ "diff", "yansi", ] [[package]] name = "proc-macro2" version = "1.0.93" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.38" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" dependencies = [ "proc-macro2", ] [[package]] name = "serde" version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "syn" version = "2.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36147f1a48ae0ec2b5b3bc5b537d267457555a10dc06f3dbc8cb11ba3006d3b1" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "unicode-ident" version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a210d160f08b701c8721ba1c726c11662f877ea6b7094007e1ca9a1041945034" [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-sys" version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ "windows-link", ] [[package]] name = "yansi" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" os_info-3.13.0/Cargo.toml0000644000000050220000000000100105510ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2018" rust-version = "1.60" name = "os_info" version = "3.13.0" authors = [ "Jan Schulte ", "Stanislav Tkach ", ] build = false include = [ "Cargo.toml", "LICENSE", "src/**/*.rs", "README.md", ] autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Detect the operating system type and version." homepage = "https://github.com/stanislav-tkach/os_info" documentation = "https://docs.rs/os_info" readme = "README.md" keywords = [ "os", "os_type", "os_version", "os_info", ] categories = ["os"] license = "MIT" repository = "https://github.com/stanislav-tkach/os_info" [features] default = ["serde"] [lib] name = "os_info" path = "src/lib.rs" [dependencies.log] version = "0.4" [dependencies.serde] version = "1" features = ["derive"] optional = true [dev-dependencies.doc-comment] version = "0.3" [dev-dependencies.pretty_assertions] version = "1" [target.'cfg(any(target_os = "aix", target_os = "dragonfly", target_os = "freebsd", target_os = "illumos", target_os = "linux", target_os = "macos", target_os = "netbsd", target_os = "openbsd", target_os = "cygwin"))'.dependencies.nix] version = "0.30" features = ["feature"] [target.'cfg(target_os = "android")'.dependencies.android_system_properties] version = "0.1" [target.'cfg(target_os = "ios")'.dependencies.objc2] version = "0.6" [target.'cfg(target_os = "ios")'.dependencies.objc2-foundation] version = "0.3" features = ["NSString"] [target.'cfg(target_os = "ios")'.dependencies.objc2-ui-kit] version = "0.3" [target.'cfg(target_os = "macos")'.dependencies.objc2-foundation] version = "0.3" features = [ "NSData", "NSError", "NSEnumerator", "NSString", ] [target."cfg(windows)".dependencies.windows-sys] version = "0.61" features = [ "Win32_Foundation", "Win32_System_LibraryLoader", "Win32_System_Registry", "Win32_System_SystemInformation", "Win32_System_SystemServices", "Win32_System_Threading", "Win32_UI_WindowsAndMessaging", ] os_info-3.13.0/Cargo.toml.orig000064400000000000000000000034041046102023000142340ustar 00000000000000[package] name = "os_info" version = "3.13.0" authors = ["Jan Schulte ", "Stanislav Tkach "] description = "Detect the operating system type and version." documentation = "https://docs.rs/os_info" homepage = "https://github.com/stanislav-tkach/os_info" repository = "https://github.com/stanislav-tkach/os_info" readme = "README.md" keywords = ["os", "os_type", "os_version", "os_info"] categories = ["os"] license = "MIT" edition = "2018" rust-version = "1.60" include = [ "Cargo.toml", "LICENSE", "src/**/*.rs", "README.md", ] [features] default = ["serde"] [dependencies] log.workspace = true serde = { version = "1", features = ["derive"], optional = true } [target.'cfg(target_os = "android")'.dependencies] android_system_properties = "0.1" [target.'cfg(target_os = "ios")'.dependencies] objc2 = "0.6" objc2-foundation = { version = "0.3", features = ["NSString"] } objc2-ui-kit = "0.3" [target.'cfg(target_os = "macos")'.dependencies] objc2-foundation = { version = "0.3", features = [ "NSData", "NSError", "NSEnumerator", "NSString", ] } [target.'cfg(windows)'.dependencies] windows-sys = { version = "0.61", features = [ "Win32_Foundation", "Win32_System_LibraryLoader", "Win32_System_Registry", "Win32_System_SystemInformation", "Win32_System_SystemServices", "Win32_System_Threading", "Win32_UI_WindowsAndMessaging", ] } [target.'cfg(any(target_os = "aix", target_os = "dragonfly", target_os = "freebsd", target_os = "illumos", target_os = "linux", target_os = "macos", target_os = "netbsd", target_os = "openbsd", target_os = "cygwin"))'.dependencies] nix = { version = "0.30", features = ["feature"] } [dev-dependencies] pretty_assertions = "1" doc-comment = "0.3" os_info-3.13.0/LICENSE000064400000000000000000000020721046102023000123520ustar 00000000000000The MIT License (MIT) Copyright (c) 2017 Stanislav Tkach Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. os_info-3.13.0/README.md000064400000000000000000000065151046102023000126320ustar 00000000000000# os_info **Status:** [![CI](https://github.com/stanislav-tkach/os_info/workflows/CI/badge.svg)](https://github.com/stanislav-tkach/os_info/actions) [![Coverage](https://codecov.io/gh/stanislav-tkach/os_info/branch/master/graph/badge.svg)](https://codecov.io/gh/stanislav-tkach/os_info) [![Dependency status](https://deps.rs/repo/github/stanislav-tkach/os_info/status.svg)](https://deps.rs/repo/github/stanislav-tkach/os_info) **Project info:** [![Docs.rs](https://docs.rs/os_info/badge.svg)](https://docs.rs/os_info) [![Latest version](https://img.shields.io/crates/v/os_info.svg)](https://crates.io/crates/os_info) [![License](https://img.shields.io/github/license/stanislav-tkach/os_info.svg)](https://github.com/stanislav-tkach/os_info/blob/master/LICENSE) **Project details:** [![LoC](https://tokei.rs/b1/github/stanislav-tkach/os_info)](https://github.com/stanislav-tkach/os_info) ![Rust 1.60+ required](https://img.shields.io/badge/rust-1.60+-blue.svg?label=Required%20Rust) ## Overview This project consists of two parts: the library that can be used to detect the operating system type (including version and bitness) and the command line tool that uses the library. ### Library (`os_info`) #### `os_info` usage To use this crate, add `os_info` as a dependency to your project's Cargo.toml: ```toml [dependencies] os_info = "3" ``` This project has `serde` as an optional dependency, so if you don't need it, then you can speed up compilation disabling it: ```toml [dependencies] os_info = { version = "3", default-features = false } ``` #### Example ```rust let info = os_info::get(); // Print full information: println!("OS information: {info}"); // Print information separately: println!("Type: {}", info.os_type()); println!("Version: {}", info.version()); println!("Bitness: {}", info.bitness()); println!("Architecture: {}", info.architecture()); ``` ### Command line tool (`os_info_cli`) A simple wrapper around the `os_info` library. #### Installation This tool can be installed using the following cargo command: ```console cargo install os_info_cli ``` #### `os_info_cli` usage Despite being named `os_info_cli` during installation, it is actually named `os_info`. You can use the `--help` flag to see available options: ```console os_info --help ``` ## Supported operating systems Right now, the following operating system types can be returned: - AIX - AlmaLinux - Alpaquita Linux - Alpine Linux - Amazon Linux AMI - Android - AOSC OS - Arch Linux - Artix Linux - CachyOS - CentOS - Debian - DragonFly BSD - Elementary OS - Emscripten - EndeavourOS - Fedora - FreeBSD - Garuda Linux - Gentoo Linux - HardenedBSD - illumos - iOS - Kali Linux - Linux - Mabox - macOS (Mac OS X or OS X) - Manjaro - Mariner - MidnightBSD - Mint - NetBSD - NixOS - Nobara Linux - OpenBSD - OpenCloudOS - openEuler (EulerOS) - openSUSE - Oracle Linux - Pop!_OS - Raspberry Pi OS - Red Hat Linux - Red Hat Enterprise Linux - Redox - Rocky Linux - Solus - SUSE Linux Enterprise Server - Ubuntu - Ultramarine Linux - Unknown - Void Linux - Windows If you need support for more OS types, I am looking forward to your Pull Request. ## License `os_info` is licensed under the MIT license. See [LICENSE] for the details. [lsb_release]: http://refspecs.linuxbase.org/LSB_2.0.1/LSB-PDA/LSB-PDA/lsbrelease.html [LICENSE]: https://github.com/stanislav-tkach/os_info/blob/master/LICENSE os_info-3.13.0/src/aix/mod.rs000064400000000000000000000020271046102023000140420ustar 00000000000000use std::{process::Command, str}; use log::{error, trace}; use crate::{ bitness, uname::{uname, UnameField}, Info, Type, Version, }; pub fn current_platform() -> Info { trace!("aix::current_platform is called"); let version = get_version() .map(Version::from_string) .unwrap_or_else(|| Version::Unknown); let info = Info { os_type: get_os(), version, bitness: bitness::get(), ..Default::default() }; trace!("Returning {:?}", info); info } fn get_version() -> Option { let major = uname(UnameField::Version)?; let minor = uname(UnameField::Release).unwrap_or(String::from("0")); Some(format!("{}.{}", major, minor)) } fn get_os() -> Type { match uname(UnameField::Sysname).as_deref() { Some("AIX") => Type::AIX, _ => Type::Unknown, } } #[cfg(test)] mod tests { use super::*; #[test] fn os_type() { let version = current_platform(); assert_eq!(Type::AIX, version.os_type()); } } os_info-3.13.0/src/android/mod.rs000064400000000000000000000020071046102023000146770ustar 00000000000000use log::trace; use crate::{Bitness, Info, Type, Version}; use android_system_properties::AndroidSystemProperties; pub fn current_platform() -> Info { trace!("android::current_platform is called"); let bitness = match std::env::consts::ARCH { "x86" | "arm" => Bitness::X32, "x86_64" | "aarch64" => Bitness::X64, _ => Bitness::Unknown, }; let info = Info { os_type: Type::Android, version: version(), bitness, ..Default::default() }; trace!("Returning {:?}", info); info } fn version() -> Version { let android_system_properties = AndroidSystemProperties::new(); match android_system_properties.get("ro.build.version.release") { Some(v) => Version::from_string(v), None => Version::Unknown, } } #[cfg(test)] mod tests { use super::*; use pretty_assertions::assert_eq; #[test] fn os_type() { let version = current_platform(); assert_eq!(Type::Android, version.os_type()); } } os_info-3.13.0/src/architecture.rs000064400000000000000000000004451046102023000151660ustar 00000000000000use crate::uname::{uname, UnameField}; pub fn get() -> Option { uname(UnameField::Machine) } #[cfg(test)] mod tests { use super::*; #[test] fn uname_nonempty() { let val = get().expect("architecture::get() failed"); assert!(!val.is_empty()); } } os_info-3.13.0/src/bitness.rs000064400000000000000000000100361046102023000141500ustar 00000000000000// spell-checker:ignore getconf use std::fmt::{self, Display, Formatter}; #[cfg(any( target_os = "aix", target_os = "dragonfly", target_os = "freebsd", target_os = "illumos", target_os = "linux", target_os = "macos", target_os = "netbsd", target_os = "openbsd", target_os = "cygwin" ))] use std::process::{Command, Output}; /// Operating system architecture in terms of how many bits compose the basic values it can deal with. #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[non_exhaustive] pub enum Bitness { /// Unknown bitness (unable to determine). Unknown, /// 32-bit. X32, /// 64-bit. X64, } impl Display for Bitness { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match *self { Bitness::Unknown => write!(f, "unknown bitness"), Bitness::X32 => write!(f, "32-bit"), Bitness::X64 => write!(f, "64-bit"), } } } #[cfg(any( target_os = "dragonfly", target_os = "freebsd", target_os = "linux", target_os = "cygwin", target_os = "macos", ))] pub fn get() -> Bitness { match &Command::new("getconf").arg("LONG_BIT").output() { Ok(Output { stdout, .. }) if stdout == b"32\n" => Bitness::X32, Ok(Output { stdout, .. }) if stdout == b"64\n" => Bitness::X64, _ => Bitness::Unknown, } } #[cfg(target_os = "netbsd")] pub fn get() -> Bitness { match &Command::new("sysctl") .arg("-n") .arg("hw.machine_arch") .output() { Ok(Output { stdout, .. }) if stdout == b"amd64\n" => Bitness::X64, Ok(Output { stdout, .. }) if stdout == b"x86_64\n" => Bitness::X64, Ok(Output { stdout, .. }) if stdout == b"i386\n" => Bitness::X32, Ok(Output { stdout, .. }) if stdout == b"aarch64\n" => Bitness::X64, Ok(Output { stdout, .. }) if stdout == b"earmv7hf\n" => Bitness::X32, Ok(Output { stdout, .. }) if stdout == b"sparc64\n" => Bitness::X64, _ => Bitness::Unknown, } } #[cfg(target_os = "openbsd")] pub fn get() -> Bitness { match &Command::new("sysctl").arg("-n").arg("hw.machine").output() { Ok(Output { stdout, .. }) if stdout == b"amd64\n" => Bitness::X64, Ok(Output { stdout, .. }) if stdout == b"x86_64\n" => Bitness::X64, Ok(Output { stdout, .. }) if stdout == b"i386\n" => Bitness::X32, Ok(Output { stdout, .. }) if stdout == b"aarch64\n" => Bitness::X64, Ok(Output { stdout, .. }) if stdout == b"earmv7hf\n" => Bitness::X32, Ok(Output { stdout, .. }) if stdout == b"sparc64\n" => Bitness::X64, _ => Bitness::Unknown, } } #[cfg(target_os = "illumos")] pub fn get() -> Bitness { match &Command::new("isainfo").arg("-b").output() { Ok(Output { stdout, .. }) if stdout == b"64\n" => Bitness::X64, Ok(Output { stdout, .. }) if stdout == b"32\n" => Bitness::X32, _ => Bitness::Unknown, } } #[cfg(target_os = "aix")] pub fn get() -> Bitness { match &Command::new("prtconf").arg("-c").output() { Ok(Output { stdout, .. }) if stdout == b"CPU Type: 64-bit\n" => Bitness::X64, Ok(Output { stdout, .. }) if stdout == b"CPU Type: 32-bit\n" => Bitness::X32, _ => Bitness::Unknown, } } #[cfg(all( test, any( target_os = "aix", target_os = "dragonfly", target_os = "freebsd", target_os = "linux", target_os = "macos", target_os = "netbsd", target_os = "openbsd", target_os = "cygwin" ) ))] mod tests { use super::*; use pretty_assertions::assert_ne; #[test] fn get_bitness() { let b = get(); assert_ne!(b, Bitness::Unknown); } #[test] fn display() { let data = [ (Bitness::Unknown, "unknown bitness"), (Bitness::X32, "32-bit"), (Bitness::X64, "64-bit"), ]; for (bitness, expected) in &data { assert_eq!(&bitness.to_string(), expected); } } } os_info-3.13.0/src/cygwin/mod.rs000064400000000000000000000014601046102023000145610ustar 00000000000000use std::process::Command; use log::{error, trace}; use crate::{ architecture, bitness, uname::{uname, UnameField}, Info, Type, Version, }; pub fn current_platform() -> Info { trace!("cygwin::current_platform is called"); let version = uname(UnameField::Release) .map(Version::from_string) .unwrap_or_else(|| Version::Unknown); let info = Info { os_type: Type::Cygwin, version, bitness: bitness::get(), architecture: architecture::get(), ..Default::default() }; trace!("Returning {:?}", info); info } #[cfg(test)] mod tests { use super::*; use pretty_assertions::assert_eq; #[test] fn os_type() { let version = current_platform(); assert_eq!(Type::Cygwin, version.os_type()); } } os_info-3.13.0/src/dragonfly/mod.rs000064400000000000000000000014001046102023000152400ustar 00000000000000use std::process::Command; use log::trace; use crate::{ bitness, uname::{uname, UnameField}, Bitness, Info, Type, Version, }; pub fn current_platform() -> Info { trace!("dragonfly::current_platform is called"); let version = uname(UnameField::Release) .map(Version::from_string) .unwrap_or_else(|| Version::Unknown); let info = Info { os_type: Type::DragonFly, version, bitness: bitness::get(), ..Default::default() }; trace!("Returning {:?}", info); info } #[cfg(test)] mod tests { use super::*; use pretty_assertions::assert_eq; #[test] fn os_type() { let version = current_platform(); assert_eq!(Type::DragonFly, version.os_type()); } } os_info-3.13.0/src/emscripten/mod.rs000064400000000000000000000007671046102023000154430ustar 00000000000000use log::trace; use crate::{Bitness, Info, Type}; // TODO: Somehow get the real OS version? pub fn current_platform() -> Info { trace!("emscripten::current_platform is called"); let info = Info::with_type(Type::Emscripten); trace!("Returning {:?}", info); info } #[cfg(test)] mod tests { use super::*; use pretty_assertions::assert_eq; #[test] fn os_type() { let version = current_platform(); assert_eq!(Type::Emscripten, version.os_type()); } } os_info-3.13.0/src/freebsd/mod.rs000064400000000000000000000027661046102023000147050ustar 00000000000000use std::process::Command; use std::str; use log::{error, trace}; use crate::{ bitness, uname::{uname, UnameField}, Info, Type, Version, }; pub fn current_platform() -> Info { trace!("freebsd::current_platform is called"); let version = uname(UnameField::Release) .map(Version::from_string) .unwrap_or_else(|| Version::Unknown); let info = Info { os_type: get_os(), version, bitness: bitness::get(), ..Default::default() }; trace!("Returning {:?}", info); info } fn get_os() -> Type { match uname(UnameField::Sysname).as_deref() { Some("MidnightBSD") => Type::MidnightBSD, Some("FreeBSD") => { let check_hardening = match Command::new("/sbin/sysctl") .arg("hardening.version") .output() { Ok(o) => o, Err(e) => { error!("Failed to invoke '/sbin/sysctl': {:?}", e); return Type::FreeBSD; } }; match str::from_utf8(&check_hardening.stderr) { Ok("0\n") => Type::HardenedBSD, Ok(_) => Type::FreeBSD, Err(_) => Type::FreeBSD, } } _ => Type::Unknown, } } #[cfg(test)] mod tests { use super::*; use pretty_assertions::assert_eq; #[test] fn os_type() { let version = current_platform(); assert_eq!(Type::FreeBSD, version.os_type()); } } os_info-3.13.0/src/illumos/mod.rs000064400000000000000000000016421046102023000147470ustar 00000000000000use std::process::Command; use std::str; use log::{error, trace}; use crate::{ bitness, uname::{uname, UnameField}, Info, Type, Version, }; pub fn current_platform() -> Info { trace!("illumos::current_platform is called"); let version = uname(UnameField::Version) .map(Version::from_string) .unwrap_or_else(|| Version::Unknown); let info = Info { os_type: get_os(), version, bitness: bitness::get(), ..Default::default() }; trace!("Returning {:?}", info); info } fn get_os() -> Type { match uname(UnameField::OperatingSystem).as_deref() { Some("illumos") => Type::Illumos, _ => Type::Unknown, } } #[cfg(test)] mod tests { use super::*; use pretty_assertions::assert_eq; #[test] fn os_type() { let version = current_platform(); assert_eq!(Type::Illumos, version.os_type()); } } os_info-3.13.0/src/info.rs000064400000000000000000000235711046102023000134440ustar 00000000000000// spell-checker:ignore itertools, iproduct, bitnesses use std::fmt::{self, Display, Formatter}; use super::{Bitness, Type, Version}; /// Holds information about operating system (type, version, etc.). /// /// The best way to get string representation of the operation system information is to use its /// `Display` implementation. /// /// # Examples /// /// ``` /// use os_info; /// /// let info = os_info::get(); /// println!("OS information: {info}"); /// ``` #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Info { /// Operating system type. See `Type` for details. pub(crate) os_type: Type, /// Operating system version. See `Version` for details. pub(crate) version: Version, /// Operating system edition. pub(crate) edition: Option, /// Operating system codename. pub(crate) codename: Option, /// Operating system architecture in terms of how many bits compose the basic values it can deal /// with. See `Bitness` for details. pub(crate) bitness: Bitness, /// Processor architecture. pub(crate) architecture: Option, } impl Info { /// Constructs a new `Info` instance with unknown type, version and bitness. /// /// # Examples /// /// ``` /// use os_info::{Info, Type, Version, Bitness}; /// /// let info = Info::unknown(); /// assert_eq!(Type::Unknown, info.os_type()); /// assert_eq!(&Version::Unknown, info.version()); /// assert_eq!(None, info.edition()); /// assert_eq!(None, info.codename()); /// assert_eq!(Bitness::Unknown, info.bitness()); /// assert_eq!(None, info.architecture()); /// ``` pub fn unknown() -> Self { Self { os_type: Type::Unknown, version: Version::Unknown, edition: None, codename: None, bitness: Bitness::Unknown, architecture: None, } } /// Constructs a new `Info` instance with the specified operating system type. /// /// # Examples /// /// ``` /// use os_info::{Info, Type, Version, Bitness}; /// /// let os_type = Type::Linux; /// let info = Info::with_type(os_type); /// assert_eq!(os_type, info.os_type()); /// assert_eq!(&Version::Unknown, info.version()); /// assert_eq!(None, info.edition()); /// assert_eq!(None, info.codename()); /// assert_eq!(Bitness::Unknown, info.bitness()); /// assert_eq!(None, info.architecture()); /// ``` pub fn with_type(os_type: Type) -> Self { Self { os_type, ..Default::default() } } /// Returns operating system type. See `Type` for details. /// /// # Examples /// /// ``` /// use os_info::{Info, Type}; /// /// let info = Info::unknown(); /// assert_eq!(Type::Unknown, info.os_type()); /// ``` pub fn os_type(&self) -> Type { self.os_type } /// Returns operating system version. See `Version` for details. /// /// # Examples /// /// ``` /// use os_info::{Info, Version}; /// /// let info = Info::unknown(); /// assert_eq!(&Version::Unknown, info.version()); /// ``` pub fn version(&self) -> &Version { &self.version } /// Returns optional operation system edition. /// /// # Examples /// /// ``` /// use os_info::Info; /// /// let info = Info::unknown(); /// assert_eq!(None, info.edition()); pub fn edition(&self) -> Option<&str> { self.edition.as_ref().map(String::as_ref) } /// Returns optional operation system 'codename'. /// /// # Examples /// /// ``` /// use os_info::Info; /// /// let info = Info::unknown(); /// assert_eq!(None, info.codename()); pub fn codename(&self) -> Option<&str> { self.codename.as_ref().map(String::as_ref) } /// Returns operating system bitness. See `Bitness` for details. /// /// # Examples /// /// ``` /// use os_info::{Info, Bitness}; /// /// let info = Info::unknown(); /// assert_eq!(Bitness::Unknown, info.bitness()); /// ``` pub fn bitness(&self) -> Bitness { self.bitness } /// Returns operating system architecture. /// /// # Examples /// /// ``` /// use os_info::Info; /// /// let info = Info::unknown(); /// assert_eq!(None, info.architecture()); pub fn architecture(&self) -> Option<&str> { self.architecture.as_ref().map(String::as_ref) } } impl Default for Info { fn default() -> Self { Self::unknown() } } impl Display for Info { fn fmt(&self, f: &mut Formatter) -> fmt::Result { write!(f, "{}", self.os_type)?; if self.version != Version::Unknown { write!(f, " {}", self.version)?; } if let Some(ref edition) = self.edition { write!(f, " ({edition})")?; } if let Some(ref codename) = self.codename { write!(f, " ({codename})")?; } write!(f, " [{}]", self.bitness) } } #[cfg(test)] mod tests { use super::*; use pretty_assertions::assert_eq; #[test] fn unknown() { let info = Info::unknown(); assert_eq!(Type::Unknown, info.os_type()); assert_eq!(&Version::Unknown, info.version()); assert_eq!(None, info.edition()); assert_eq!(None, info.codename()); assert_eq!(Bitness::Unknown, info.bitness()); assert_eq!(None, info.architecture()); } #[test] fn with_type() { let types = [ Type::AIX, Type::Redox, Type::Alpaquita, Type::Alpine, Type::ALTLinux, Type::Amazon, Type::Android, Type::AOSC, Type::Arch, Type::Artix, Type::Bluefin, Type::CachyOS, Type::CentOS, Type::Debian, Type::Emscripten, Type::EndeavourOS, Type::Fedora, Type::Gentoo, Type::Linux, Type::Macos, Type::Manjaro, Type::Mariner, Type::NixOS, Type::Nobara, Type::Uos, Type::OpenCloudOS, Type::openEuler, Type::openSUSE, Type::OracleLinux, Type::Pop, Type::Redhat, Type::RedHatEnterprise, Type::Redox, Type::Solus, Type::SUSE, Type::Ubuntu, Type::Ultramarine, Type::Void, Type::Mint, Type::Unknown, Type::Windows, ]; for t in &types { let info = Info::with_type(*t); assert_eq!(t, &info.os_type()); } } #[test] fn default() { assert_eq!(Info::default(), Info::unknown()); } #[test] fn display() { let data = [ // All unknown. (Info::unknown(), "Unknown [unknown bitness]"), // Type. ( Info { os_type: Type::Redox, ..Default::default() }, "Redox [unknown bitness]", ), // Type and version. ( Info { os_type: Type::Linux, version: Version::Semantic(2, 3, 4), ..Default::default() }, "Linux 2.3.4 [unknown bitness]", ), ( Info { os_type: Type::AOSC, version: Version::Semantic(12, 1, 3), ..Default::default() }, "AOSC OS 12.1.3 [unknown bitness]", ), ( Info { os_type: Type::Arch, version: Version::Rolling(None), ..Default::default() }, "Arch Linux Rolling Release [unknown bitness]", ), ( Info { os_type: Type::Artix, version: Version::Rolling(None), ..Default::default() }, "Artix Linux Rolling Release [unknown bitness]", ), ( Info { os_type: Type::Manjaro, version: Version::Rolling(Some("2020.05.24".to_owned())), ..Default::default() }, "Manjaro Rolling Release (2020.05.24) [unknown bitness]", ), ( Info { os_type: Type::Windows, version: Version::Custom("Special Version".to_owned()), ..Default::default() }, "Windows Special Version [unknown bitness]", ), // Bitness. ( Info { bitness: Bitness::X32, ..Default::default() }, "Unknown [32-bit]", ), ( Info { bitness: Bitness::X64, ..Default::default() }, "Unknown [64-bit]", ), // All info. ( Info { os_type: Type::Macos, version: Version::Semantic(10, 2, 0), edition: Some("edition".to_owned()), codename: Some("codename".to_owned()), bitness: Bitness::X64, architecture: Some("architecture".to_owned()), }, "Mac OS 10.2.0 (edition) (codename) [64-bit]", ), ]; for (info, expected) in &data { assert_eq!(expected, &info.to_string()); } } } os_info-3.13.0/src/ios/mod.rs000064400000000000000000000023421046102023000140530ustar 00000000000000use log::trace; use objc2::{msg_send, rc::Retained, ClassType}; use objc2_foundation::NSString; use objc2_ui_kit::UIDevice; use crate::{Bitness, Info, Type, Version}; pub fn current_platform() -> Info { trace!("ios::current_platform is called"); let bitness = match std::env::consts::ARCH { "x86" | "arm" => Bitness::X32, "x86_64" | "aarch64" => Bitness::X64, _ => Bitness::Unknown, }; let info = Info { os_type: Type::Ios, version: version(), bitness, ..Default::default() }; trace!("Returning {:?}", info); info } fn version() -> Version { match system_version().map(|ns| ns.to_string()) { Some(v) => Version::from_string(v), None => Version::Unknown, } } #[allow(unsafe_code)] fn system_version() -> Option> { let device: Retained = unsafe { msg_send![UIDevice::class(), currentDevice] }; let ver: Retained = unsafe { msg_send![&device, systemVersion] }; Some(ver) } #[cfg(test)] mod tests { use super::*; use pretty_assertions::assert_eq; #[test] fn os_type() { let version = current_platform(); assert_eq!(Type::Ios, version.os_type()); } } os_info-3.13.0/src/lib.rs000064400000000000000000000053731046102023000132570ustar 00000000000000//! `os_info` //! //! Provides interfaces for getting information about the current operating system, such as type, //! version, edition and bitness. #![deny( missing_debug_implementations, missing_docs, unsafe_code, missing_doc_code_examples )] #[cfg(target_os = "aix")] #[path = "aix/mod.rs"] mod imp; #[cfg(target_os = "android")] #[path = "android/mod.rs"] mod imp; #[cfg(target_os = "dragonfly")] #[path = "dragonfly/mod.rs"] mod imp; #[cfg(target_os = "emscripten")] #[path = "emscripten/mod.rs"] mod imp; #[cfg(target_os = "freebsd")] #[path = "freebsd/mod.rs"] mod imp; #[cfg(target_os = "illumos")] #[path = "illumos/mod.rs"] mod imp; #[cfg(target_os = "ios")] #[path = "ios/mod.rs"] mod imp; #[cfg(target_os = "linux")] #[path = "linux/mod.rs"] mod imp; #[cfg(target_os = "macos")] #[path = "macos/mod.rs"] mod imp; #[cfg(target_os = "netbsd")] #[path = "netbsd/mod.rs"] mod imp; #[cfg(target_os = "openbsd")] #[path = "openbsd/mod.rs"] mod imp; #[cfg(target_os = "cygwin")] #[path = "cygwin/mod.rs"] mod imp; #[cfg(target_os = "redox")] #[path = "redox/mod.rs"] mod imp; #[cfg(windows)] #[path = "windows/mod.rs"] mod imp; #[cfg(not(any( target_os = "aix", target_os = "android", target_os = "dragonfly", target_os = "emscripten", target_os = "freebsd", target_os = "illumos", target_os = "ios", target_os = "linux", target_os = "macos", target_os = "netbsd", target_os = "openbsd", target_os = "cygwin", target_os = "redox", target_os = "windows" )))] #[path = "unknown/mod.rs"] mod imp; #[cfg(any( target_os = "linux", target_os = "macos", target_os = "netbsd", target_os = "openbsd", target_os = "cygwin" ))] mod architecture; mod bitness; mod info; #[cfg(not(windows))] mod matcher; mod os_type; #[cfg(any( target_os = "aix", target_os = "dragonfly", target_os = "freebsd", target_os = "illumos", target_os = "linux", target_os = "macos", target_os = "netbsd", target_os = "openbsd", target_os = "cygwin" ))] mod uname; mod version; pub use crate::{bitness::Bitness, info::Info, os_type::Type, version::Version}; /// Returns information about the current operating system (type, version, edition, etc.). /// /// # Examples /// /// ``` /// use os_info; /// /// let info = os_info::get(); /// /// // Print full information: /// println!("OS information: {info}"); /// /// // Print information separately: /// println!("Type: {}", info.os_type()); /// println!("Version: {}", info.version()); /// println!("Edition: {:?}", info.edition()); /// println!("Codename: {:?}", info.codename()); /// println!("Bitness: {}", info.bitness()); /// println!("Architecture: {:?}", info.architecture()); /// ``` pub fn get() -> Info { imp::current_platform() } os_info-3.13.0/src/linux/file_release.rs000064400000000000000000000573121046102023000162670ustar 00000000000000// spell-checker:ignore sles, AOSCOS use std::{fmt, fs::File, io::Read, path::Path}; use log::{trace, warn}; use crate::{matcher::Matcher, Bitness, Info, Type, Version}; pub fn get() -> Option { retrieve(&DISTRIBUTIONS, "/") } fn retrieve(distributions: &[ReleaseInfo], root: &str) -> Option { for release_info in distributions { let path = Path::new(root).join(release_info.path); if !path.exists() { trace!("Path '{}' doesn't exist", release_info.path); continue; } let mut file = match File::open(&path) { Ok(val) => val, Err(e) => { warn!("Unable to open {:?} file: {:?}", &path, e); continue; } }; let mut file_content = String::new(); if let Err(e) = file.read_to_string(&mut file_content) { warn!("Unable to read {:?} file: {:?}", &path, e); continue; } let os_type = (release_info.os_type)(&file_content); // If os_type is indeterminate, try the next release_info if os_type.is_none() { continue; } let version = (release_info.version)(&file_content); return Some(Info { // Unwrap is OK here because of the `os_type.is_none()` check above. os_type: os_type.unwrap(), version: version.unwrap_or(Version::Unknown), bitness: Bitness::Unknown, ..Default::default() }); } // Failed to determine os info None } /// Struct containing information on how to parse distribution info from a release file. #[derive(Clone)] struct ReleaseInfo<'a> { /// Relative path to the release file this struct corresponds to from root. path: &'a str, /// A closure that determines the os type from the release file contents. os_type: for<'b> fn(&'b str) -> Option, /// A closure that determines the os version from the release file contents. version: for<'b> fn(&'b str) -> Option, } impl fmt::Debug for ReleaseInfo<'_> { fn fmt<'a>(&'a self, f: &mut fmt::Formatter<'_>) -> std::fmt::Result { f.debug_struct("ReleaseInfo") .field("path", &self.path) .field("os_type", &(self.os_type as fn(&'a str) -> Option)) .field("version", &(self.version as fn(&'a str) -> Option)) .finish() } } /// List of all supported distributions and the information on how to parse their version from the /// release file. static DISTRIBUTIONS: [ReleaseInfo; 6] = [ // Keep this first; most modern distributions have this file. ReleaseInfo { path: "etc/os-release", os_type: |release| { Matcher::KeyValue { key: "ID" } .find(release) .and_then(|id| match id.as_str() { // os-release information collected from // https://github.com/chef/os_release "almalinux" => Some(Type::AlmaLinux), "alpaquita" => Some(Type::Alpaquita), "alpine" => Some(Type::Alpine), "altlinux" => Some(Type::ALTLinux), "amzn" => Some(Type::Amazon), //"antergos" => Antergos "aosc" => Some(Type::AOSC), "arch" => Some(Type::Arch), "archarm" => Some(Type::Arch), "artix" => Some(Type::Artix), "bluefin" => Some(Type::Bluefin), "cachyos" => Some(Type::CachyOS), "centos" => Some(Type::CentOS), //"clear-linux-os" => ClearLinuxOS //"clearos" => ClearOS //"coreos" //"cumulus-linux" => Cumulus "debian" => { // Check if it's actually Raspberry Pi OS if std::path::Path::new("/etc/rpi-issue").exists() { Some(Type::Raspbian) } else { Some(Type::Debian) } } //"devuan" => Devuan "elementary" => Some(Type::Elementary), "fedora" => Some(Type::Fedora), //"gentoo" => Gentoo //"ios_xr" => ios_xr "kali" => Some(Type::Kali), //"mageia" => Mageia //"manjaro" => Manjaro "manjaro-arm" => Some(Type::Manjaro), "linuxmint" => Some(Type::Mint), "mariner" => Some(Type::Mariner), //"nexus" => Nexus "nixos" => Some(Type::NixOS), "nobara" => Some(Type::Nobara), "Uos" => Some(Type::Uos), "opencloudos" => Some(Type::OpenCloudOS), "openEuler" => Some(Type::openEuler), "ol" => Some(Type::OracleLinux), "opensuse" => Some(Type::openSUSE), "opensuse-leap" => Some(Type::openSUSE), "opensuse-microos" => Some(Type::openSUSE), "opensuse-tumbleweed" => Some(Type::openSUSE), //"rancheros" => RancherOS //"raspbian" => Raspbian // note XBian also uses "raspbian" "rhel" => Some(Type::RedHatEnterprise), "rocky" => Some(Type::RockyLinux), //"sabayon" => Sabayon //"scientific" => Scientific //"slackware" => Slackware "sled" => Some(Type::SUSE), // SUSE desktop "sles" => Some(Type::SUSE), "sles_sap" => Some(Type::SUSE), // SUSE SAP "ubuntu" => Some(Type::Ubuntu), "ultramarine" => Some(Type::Ultramarine), //"virtuozzo" => Virtuozzo "void" => Some(Type::Void), "zorin" => Some(Type::Zorin), //"XCP-ng" => xcp-ng //"xenenterprise" => xcp-ng //"xenserver" => xcp-ng _ => None, }) }, version: |release| { Matcher::KeyValue { key: "VERSION_ID" } .find(release) .map(Version::from_string) }, }, // Older distributions must have their specific release file parsed. ReleaseInfo { path: "etc/mariner-release", os_type: |_| Some(Type::Mariner), version: |release| { Matcher::PrefixedVersion { prefix: "CBL-Mariner", } .find(release) .map(Version::from_string) }, }, ReleaseInfo { path: "etc/centos-release", os_type: |_| Some(Type::CentOS), version: |release| { Matcher::PrefixedVersion { prefix: "release" } .find(release) .map(Version::from_string) }, }, ReleaseInfo { path: "etc/fedora-release", os_type: |_| Some(Type::Fedora), version: |release| { Matcher::PrefixedVersion { prefix: "release" } .find(release) .map(Version::from_string) }, }, ReleaseInfo { path: "etc/alpine-release", os_type: |_| Some(Type::Alpine), version: |release| Matcher::AllTrimmed.find(release).map(Version::from_string), }, ReleaseInfo { path: "etc/redhat-release", os_type: |_| Some(Type::RedHatEnterprise), version: |release| { Matcher::PrefixedVersion { prefix: "release" } .find(release) .map(Version::from_string) }, }, ]; #[cfg(test)] mod tests { use super::*; use pretty_assertions::assert_eq; #[test] fn almalinux_9_0_release() { let root = "src/linux/tests/AlmaLinux-9.0"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::AlmaLinux); assert_eq!(info.version, Version::Semantic(9, 0, 0)); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn alpaquita_os_release() { let root = "src/linux/tests/Alpaquita"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::Alpaquita); assert_eq!(info.version, Version::Semantic(23, 0, 0)); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn alpine_3_12_os_release() { let root = "src/linux/tests/Alpine_3_12"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::Alpine); assert_eq!(info.version, Version::Semantic(3, 12, 0)); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn alpine_release() { let root = "src/linux/tests/Alpine"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::Alpine); assert_eq!(info.version, Version::Custom("A.B.C".to_owned())); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn alt_p11_os_release() { let root = "src/linux/tests/ALTLinux_p11"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::ALTLinux); assert_eq!(info.version, Version::Semantic(11, 0, 0)); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn amazon_1_os_release() { let root = "src/linux/tests/Amazon_1"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::Amazon); assert_eq!(info.version, Version::Semantic(2018, 3, 0)); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn amazon_2_os_release() { let root = "src/linux/tests/Amazon_2"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::Amazon); assert_eq!(info.version, Version::Semantic(2, 0, 0)); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn aosc_os_release() { let root = "src/linux/tests/AOSCOS"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::AOSC); assert_eq!(info.version, Version::Semantic(12, 1, 3)); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn arch_os_release() { let root = "src/linux/tests/Arch"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::Arch); assert_eq!(info.version, Version::Unknown); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn archarm_os_release() { let root = "src/linux/tests/ArchARM"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::Arch); assert_eq!(info.version, Version::Unknown); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn artix_os_release() { let root = "src/linux/tests/Artix"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::Artix); assert_eq!(info.version, Version::Unknown); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn bluefin_os_release() { let root = "src/linux/tests/Bluefin"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::Bluefin); assert_eq!(info.version, Version::Semantic(41, 0, 0)); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn centos_7_os_release() { let root = "src/linux/tests/CentOS_7"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::CentOS); assert_eq!(info.version, Version::Semantic(7, 0, 0)); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn centos_stream_os_release() { let root = "src/linux/tests/CentOS_Stream"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::CentOS); assert_eq!(info.version, Version::Semantic(8, 0, 0)); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn centos_release() { let root = "src/linux/tests/CentOS"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::CentOS); assert_eq!(info.version, Version::Custom("XX".to_owned())); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn centos_release_unknown() { let root = "src/linux/tests/CentOS_Unknown"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::CentOS); assert_eq!(info.version, Version::Unknown); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn debian_11_os_release() { let root = "src/linux/tests/Debian_11"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::Debian); assert_eq!(info.version, Version::Semantic(11, 0, 0)); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn fedora_32_os_release() { let root = "src/linux/tests/Fedora_32"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::Fedora); assert_eq!(info.version, Version::Semantic(32, 0, 0)); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn fedora_35_os_release() { let root = "src/linux/tests/Fedora_35"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::Fedora); assert_eq!(info.version, Version::Semantic(35, 0, 0)); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn fedora_release() { let root = "src/linux/tests/Fedora"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::Fedora); assert_eq!(info.version, Version::Semantic(26, 0, 0)); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn fedora_release_unknown() { let root = "src/linux/tests/Fedora_Unknown"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::Fedora); assert_eq!(info.version, Version::Unknown); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn kali_2023_2_os_release() { let root = "src/linux/tests/Kali_2023_2"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::Kali); assert_eq!(info.version, Version::Semantic(2023, 2, 0)); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn manjaro_arm_release() { let root = "src/linux/tests/ManjaroArm"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::Manjaro); assert_eq!(info.version, Version::Unknown); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn mariner_release() { let root = "src/linux/tests/Mariner"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::Mariner); assert_eq!(info.version, Version::Semantic(2, 0, 20220210)); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn mariner_release_unknown() { let root = "src/linux/tests/Mariner_Unknown"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::Mariner); assert_eq!(info.version, Version::Unknown); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn mint_os_release() { let root = "src/linux/tests/Mint"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::Mint); assert_eq!(info.version, Version::Semantic(20, 0, 0)); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn nixos_os_release() { let root = "src/linux/tests/NixOS"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::NixOS); assert_eq!( info.version, Version::Custom("21.05pre275822.916ee862e87".to_string()) ); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn nobara_os_release() { let root = "src/linux/tests/Nobara"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::Nobara); assert_eq!(info.version, Version::Semantic(39, 0, 0)); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn uos_os_release() { let root = "src/linux/tests/Uos"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::Uos); assert_eq!(info.version, Version::Semantic(20, 0, 0)); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn none_invalid_os_release() { let root = "src/linux/tests/none_invalid_os_release"; let info = retrieve(&DISTRIBUTIONS, root); assert_eq!(info, None); } #[test] fn none_no_release() { let root = "src/linux/tests/none_no_release"; let info = retrieve(&DISTRIBUTIONS, root); assert_eq!(info, None); } #[test] fn none_no_path() { let root = "src/linux/tests/none_no_path"; let info = retrieve(&DISTRIBUTIONS, root); assert_eq!(info, None); } #[test] fn opencloudos_os_release() { let root = "src/linux/tests/OpenCloudOS"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::OpenCloudOS); assert_eq!(info.version, Version::Semantic(8, 6, 0)); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn openeuler_os_release() { let root = "src/linux/tests/openEuler"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::openEuler); assert_eq!(info.version, Version::Semantic(22, 3, 0)); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn opensuse_tumbleweed_os_release() { let root = "src/linux/tests/openSUSE_Tumbleweed"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::openSUSE); assert_eq!(info.version, Version::Semantic(20230816, 0, 0)); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn oracle_linux_os_release() { let root = "src/linux/tests/OracleLinux"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::OracleLinux); assert_eq!(info.version, Version::Semantic(8, 1, 0)); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn rhel_8_os_release() { let root = "src/linux/tests/RedHatEnterprise_8"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::RedHatEnterprise); assert_eq!(info.version, Version::Semantic(8, 2, 0)); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn rhel_7_os_release() { let root = "src/linux/tests/RedHatEnterprise_7"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::RedHatEnterprise); assert_eq!(info.version, Version::Semantic(7, 9, 0)); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn redhat_release() { let root = "src/linux/tests/RedHatEnterprise"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::RedHatEnterprise); assert_eq!(info.version, Version::Custom("XX".to_owned())); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn redhat_release_unknown() { let root = "src/linux/tests/RedHatEnterprise_Unknown"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::RedHatEnterprise); assert_eq!(info.version, Version::Unknown); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn rocky_9_2_release() { let root = "src/linux/tests/RockyLinux-9.2"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::RockyLinux); assert_eq!(info.version, Version::Semantic(9, 2, 0)); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn suse_12_os_release() { let root = "src/linux/tests/SUSE_12"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::SUSE); assert_eq!(info.version, Version::Semantic(12, 5, 0)); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn suse_15_os_release() { let root = "src/linux/tests/SUSE_15"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::SUSE); assert_eq!(info.version, Version::Semantic(15, 2, 0)); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn ubuntu_os_release() { let root = "src/linux/tests/Ubuntu"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::Ubuntu); assert_eq!(info.version, Version::Semantic(18, 10, 0)); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn ultramarine_os_release() { let root = "src/linux/tests/Ultramarine"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::Ultramarine); assert_eq!(info.version, Version::Semantic(39, 0, 0)); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn void_os_release() { let root = "src/linux/tests/Void"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::Void); assert_eq!(info.version, Version::Unknown); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn cachy_os_release() { let root = "src/linux/tests/CachyOS"; let info = retrieve(&DISTRIBUTIONS, root).unwrap(); assert_eq!(info.os_type(), Type::CachyOS); assert_eq!(info.version, Version::Unknown); assert_eq!(info.edition, None); assert_eq!(info.codename, None); } #[test] fn release_info_debug() { dbg!("{:?}", &DISTRIBUTIONS[0]); } } os_info-3.13.0/src/linux/lsb_release.rs000064400000000000000000000526261046102023000161330ustar 00000000000000// spell-checker:ignore codename, noarch, rhel, ootpa, maipo use std::process::Command; use log::{debug, trace}; use crate::{matcher::Matcher, Info, Type, Version}; pub fn get() -> Option { let release = retrieve()?; let version = match release.version.as_deref() { Some("rolling") => Version::Rolling(None), Some(v) => Version::from_string(v.to_owned()), None => Version::Unknown, }; let os_type = match release.distribution.as_ref().map(String::as_ref) { Some("Alpaquita") => Type::Alpaquita, Some("ALT Linux") => Type::ALTLinux, Some("Amazon") | Some("AmazonAMI") => Type::Amazon, Some("AOSC") => Type::AOSC, Some("Arch") => Type::Arch, Some("Artix") => Type::Artix, Some("Bluefin") => Type::Bluefin, Some("cachyos") => Type::CachyOS, Some("CentOS") => Type::CentOS, Some("Debian") => { // Check if it's actually Raspberry Pi OS if std::path::Path::new("/etc/rpi-issue").exists() { Type::Raspbian } else { Type::Debian } } Some("Elementary") => Type::Elementary, Some("EndeavourOS") => Type::EndeavourOS, Some("Fedora") | Some("Fedora Linux") => Type::Fedora, Some("Garuda") => Type::Garuda, Some("Gentoo") => Type::Gentoo, Some("Kali") => Type::Kali, Some("Linuxmint") => Type::Mint, Some("MaboxLinux") => Type::Mabox, Some("ManjaroLinux") | Some("Manjaro-ARM") => Type::Manjaro, Some("Mariner") => Type::Mariner, Some("NixOS") => Type::NixOS, Some("NobaraLinux") => Type::Nobara, Some("Uos") => Type::Uos, Some("OpenCloudOS") => Type::OpenCloudOS, Some("openEuler") => Type::openEuler, Some("openSUSE") => Type::openSUSE, Some("OracleServer") => Type::OracleLinux, Some("Pop") => Type::Pop, Some("Raspbian") => Type::Raspbian, Some("RedHatEnterprise") | Some("RedHatEnterpriseServer") => Type::RedHatEnterprise, Some("Solus") => Type::Solus, Some("SUSE") => Type::SUSE, Some("Ubuntu") => Type::Ubuntu, Some("UltramarineLinux") => Type::Ultramarine, Some("VoidLinux") => Type::Void, Some("Zorin") => Type::Zorin, // Return None here so file_release::get is then used. _ => return None, }; Some(Info { os_type, version, codename: release.codename, ..Default::default() }) } struct LsbRelease { pub distribution: Option, pub version: Option, pub codename: Option, } fn retrieve() -> Option { match Command::new("lsb_release").arg("-a").output() { Ok(output) => { trace!("lsb_release command returned {:?}", output); Some(parse(&String::from_utf8_lossy(&output.stdout))) } Err(e) => { debug!("lsb_release command failed with {:?}", e); None } } } fn parse(output: &str) -> LsbRelease { trace!("Trying to parse {:?}", output); let distribution = Matcher::PrefixedWord { prefix: "Distributor ID:", } .find(output); let codename = Matcher::PrefixedWord { prefix: "Codename:", } .find(output) .filter(|c| c != "n/a"); let version = Matcher::PrefixedVersion { prefix: "Release:" }.find(output); trace!( "Parsed as '{:?}' distribution and '{:?}' version", distribution, version ); LsbRelease { distribution, version, codename, } } #[cfg(test)] mod tests { use super::*; use pretty_assertions::assert_eq; #[test] fn debian() { let parse_results = parse(file()); assert_eq!(parse_results.distribution, Some("Debian".to_string())); assert_eq!(parse_results.version, Some("7.8".to_string())); assert_eq!(parse_results.codename, Some("wheezy".to_string())); } #[test] fn alpaquita() { let parse_results = parse(alpaquita_file()); assert_eq!(parse_results.distribution, Some("Alpaquita".to_string())); assert_eq!(parse_results.version, Some("23".to_string())); assert_eq!(parse_results.codename, None); } #[test] fn altlinux() { let parse_results = parse(alt_file()); assert_eq!(parse_results.distribution, Some("ALT".to_string())); assert_eq!(parse_results.version, Some("n/a".to_string())); assert_eq!(parse_results.codename, Some("Salvia".to_string())); } #[test] fn aosc() { let parse_results = parse(aosc_file()); assert_eq!(parse_results.distribution, Some("AOSC".to_string())); assert_eq!(parse_results.version, Some("12.3.1".to_string())); assert_eq!(parse_results.codename, Some("localhost".to_string())); } #[test] fn arch() { let parse_results = parse(arch_file()); assert_eq!(parse_results.distribution, Some("Arch".to_string())); assert_eq!(parse_results.version, Some("rolling".to_string())); assert_eq!(parse_results.codename, None); } #[test] fn artix() { let parse_results = parse(artix_file()); assert_eq!(parse_results.distribution, Some("Artix".to_string())); assert_eq!(parse_results.version, Some("rolling".to_string())); assert_eq!(parse_results.codename, None); } #[test] fn fedora() { let parse_results = parse(fedora_file()); assert_eq!(parse_results.distribution, Some("Fedora".to_string())); assert_eq!(parse_results.version, Some("26".to_string())); assert_eq!(parse_results.codename, Some("TwentySix".to_string())); } #[test] fn kali_2023_2() { let parse_results = parse(kali_2023_2_file()); assert_eq!(parse_results.distribution, Some("Kali".to_string())); assert_eq!(parse_results.version, Some("2023.2".to_string())); assert_eq!(parse_results.codename, Some("kali-rolling".to_string())); } #[test] fn ubuntu() { let parse_results = parse(ubuntu_file()); assert_eq!(parse_results.distribution, Some("Ubuntu".to_string())); assert_eq!(parse_results.version, Some("16.04".to_string())); assert_eq!(parse_results.codename, Some("xenial".to_string())); } #[test] fn mint() { let parse_results = parse(mint_file()); assert_eq!(parse_results.distribution, Some("Linuxmint".to_string())); assert_eq!(parse_results.version, Some("20".to_string())); assert_eq!(parse_results.codename, Some("ulyana".to_string())); } #[test] fn nixos() { let parse_results = parse(nixos_file()); assert_eq!(parse_results.distribution, Some("NixOS".to_string())); assert_eq!( parse_results.version, Some("21.05pre275822.916ee862e87".to_string()) ); assert_eq!(parse_results.codename, Some("okapi".to_string())); } #[test] fn nobara() { let parse_results = parse(nobara_file()); assert_eq!(parse_results.distribution, Some("NobaraLinux".to_string())); assert_eq!(parse_results.version, Some("39".to_string())); assert_eq!(parse_results.codename, None); } #[test] fn uos() { let parse_results = parse(uos_file()); assert_eq!(parse_results.distribution, Some("uos".to_string())); assert_eq!(parse_results.version, Some("20".to_string())); assert_eq!(parse_results.codename, Some("eagle".to_string())); } #[test] fn amazon1() { let parse_results = parse(amazon1_file()); assert_eq!(parse_results.distribution, Some("AmazonAMI".to_string())); assert_eq!(parse_results.version, Some("2018.03".to_string())); assert_eq!(parse_results.codename, None); } #[test] fn amazon2() { let parse_results = parse(amazon2_file()); assert_eq!(parse_results.distribution, Some("Amazon".to_string())); assert_eq!(parse_results.version, Some("2".to_string())); assert_eq!(parse_results.codename, Some("Karoo".to_string())); } #[test] fn redhat_enterprise_8() { let parse_results = parse(rhel8_file()); assert_eq!( parse_results.distribution, Some("RedHatEnterprise".to_string()) ); assert_eq!(parse_results.version, Some("8.1".to_string())); assert_eq!(parse_results.codename, Some("Ootpa".to_string())); } #[test] fn redhat_enterprise_7() { let parse_results = parse(rhel7_file()); assert_eq!( parse_results.distribution, Some("RedHatEnterpriseServer".to_string()) ); assert_eq!(parse_results.version, Some("7.7".to_string())); assert_eq!(parse_results.codename, Some("Maipo".to_string())); } #[test] fn redhat_enterprise_6() { let parse_results = parse(rhel6_file()); assert_eq!( parse_results.distribution, Some("RedHatEnterpriseServer".to_string()) ); assert_eq!(parse_results.version, Some("6.10".to_string())); assert_eq!(parse_results.codename, Some("Santiago".to_string())); } #[test] fn suse_enterprise_15_1() { let parse_results = parse(suse_enterprise15_1_file()); assert_eq!(parse_results.distribution, Some("SUSE".to_string())); assert_eq!(parse_results.version, Some("15.1".to_string())); assert_eq!(parse_results.codename, None); } #[test] fn suse_enterprise_12_5() { let parse_results = parse(suse_enterprise12_5_file()); assert_eq!(parse_results.distribution, Some("SUSE".to_string())); assert_eq!(parse_results.version, Some("12.5".to_string())); assert_eq!(parse_results.codename, None); } #[test] fn open_suse_15_1() { let parse_results = parse(open_suse_15_1_file()); assert_eq!(parse_results.distribution, Some("openSUSE".to_string())); assert_eq!(parse_results.version, Some("15.1".to_string())); assert_eq!(parse_results.codename, None); } #[test] fn oracle_linux_7_5() { let parse_results = parse(oracle_server_linux_7_5_file()); assert_eq!(parse_results.distribution, Some("OracleServer".to_string())); assert_eq!(parse_results.version, Some("7.5".to_string())); assert_eq!(parse_results.codename, None); } #[test] fn oracle_linux_8_1() { let parse_results = parse(oracle_server_linux_8_1_file()); assert_eq!(parse_results.distribution, Some("OracleServer".to_string())); assert_eq!(parse_results.version, Some("8.1".to_string())); assert_eq!(parse_results.codename, None); } #[test] fn pop_os_20_04_lts() { let parse_results = parse(pop_os_20_04_lts_file()); assert_eq!(parse_results.distribution, Some("Pop".to_string())); assert_eq!(parse_results.version, Some("20.04".to_string())); assert_eq!(parse_results.codename, Some("focal".to_string())); } #[test] fn solus_4_1() { let parse_results = parse(solus_4_1_file()); assert_eq!(parse_results.distribution, Some("Solus".to_string())); assert_eq!(parse_results.version, Some("4.1".to_string())); assert_eq!(parse_results.codename, Some("fortitude".to_string())); } #[test] fn manjaro() { let parse_results = parse(manjaro_19_0_2_file()); assert_eq!(parse_results.distribution, Some("ManjaroLinux".to_string())); assert_eq!(parse_results.version, Some("19.0.2".to_string())); assert_eq!(parse_results.codename, None); } #[test] fn manjaro_arm() { let parse_results = parse(manjaro_arm_24_04()); assert_eq!(parse_results.distribution, Some("Manjaro-ARM".to_string())); assert_eq!(parse_results.version, Some("24.04".to_string())); assert_eq!(parse_results.codename, None); } #[test] fn mariner() { let parse_results = parse(mariner_file()); assert_eq!(parse_results.distribution, Some("Mariner".to_string())); assert_eq!(parse_results.version, Some("2.0.20220210".to_string())); assert_eq!(parse_results.codename, Some("Mariner".to_string())); } #[test] fn endeavouros() { let parse_results = parse(endeavouros_file()); assert_eq!(parse_results.distribution, Some("EndeavourOS".to_string())); assert_eq!(parse_results.version, Some("rolling".to_string())); assert_eq!(parse_results.codename, None); } #[test] fn ultramarine() { let parse_results = parse(ultramarine_file()); assert_eq!( parse_results.distribution, Some("UltramarineLinux".to_string()) ); assert_eq!(parse_results.version, Some("39".to_string())); assert_eq!(parse_results.codename, Some("kuma".to_string())); } #[test] fn void_linux() { let parse_results = parse(void_file()); assert_eq!(parse_results.distribution, Some("Void".to_string())); assert_eq!(parse_results.version, Some("rolling".to_string())); } #[test] fn raspbian() { let parse_results = parse(raspberry_os_file()); assert_eq!(parse_results.distribution, Some("Raspbian".to_string())); assert_eq!(parse_results.version, Some("10".to_string())); assert_eq!(parse_results.codename, None); } #[test] fn cachyos() { let parse_results = parse(cachyos_file()); assert_eq!(parse_results.distribution, Some("cachyos".to_string())); assert_eq!(parse_results.version, Some("rolling".to_string())); } fn file() -> &'static str { "\nDistributor ID: Debian\n\ Description: Debian GNU/Linux 7.8 (wheezy)\n\ Release: 7.8\n\ Codename: wheezy\n\ " } fn alpaquita_file() -> &'static str { "\nDistributor ID: Alpaquita\n\ Description: BellSoft Alpaquita Linux Stream 23 (musl)\n\ Release: 23\n\ Codename: n/a" } fn alt_file() -> &'static str { "\nDistributor ID: ALT\n\ Description: ALT p11 Starterkit (Salvia)\n\ Release: n/a\n\ Codename: Salvia" } fn aosc_file() -> &'static str { "\nDistributor ID: AOSC OS\n\ Description: AOSC OS\n\ Release: 12.3.1\n\ Codename: localhost" } fn arch_file() -> &'static str { "\nLSB Version: 1.4\n\ Distributor ID: Arch\n\ Description: Arch Linux\n\ Release: rolling\n\ Codename: n/a" } fn artix_file() -> &'static str { "\nLSB Version: n/a\n\ Distributor ID: Artix\n\ Description: Artix Linux\n\ Release: rolling\n\ Codename: n/a" } fn fedora_file() -> &'static str { "\nLSB Version: :core-4.1-amd64:core-4.1-noarch:cxx-4.1-amd64:cxx-4.1-noarch\n\ Distributor ID: Fedora\n\ Description: Fedora release 26 (Twenty Six)\n\ Release: 26\n\ Codename: TwentySix\n\ " } fn kali_2023_2_file() -> &'static str { "\nDistributor ID: Kali\n\ Description: Kali GNU/Linux Rolling\n\ Release: 2023.2\n\ Codename: kali-rolling\n\ " } fn ubuntu_file() -> &'static str { "Distributor ID: Ubuntu\n\ Description: Ubuntu 16.04.5 LTS\n\ Release: 16.04\n\ Codename: xenial" } fn mint_file() -> &'static str { "Distributor ID: Linuxmint\n\ Description: Linux Mint 20\n\ Release: 20\n\ Codename: ulyana" } fn nixos_file() -> &'static str { "Distributor ID: NixOS\n\ Description: NixOS 21.05 (Okapi)\n\ Release: 21.05pre275822.916ee862e87\n\ Codename: okapi" } fn nobara_file() -> &'static str { "LSB Version: n/a\n\ Distributor ID: NobaraLinux\n\ Description: Nobara Linux 39 (KDE Plasma)\n\ Release: 39\n\ Codename: n/a\n\ " } fn uos_file() -> &'static str { "Distributor ID: uos\n\ Description: UnionTech OS 20\n\ Release: 20\n\ Codename: eagle\n\ " } // Amazon Linux 1 uses a separate Distributor ID and Release format from Amazon Linux 2 fn amazon1_file() -> &'static str { "LSB Version: :base-4.0-amd64:base-4.0-noarch:core-4.0-amd64:core-4.0-noarch\n\ Distributor ID: AmazonAMI\n\ Description: Amazon Linux AMI release 2018.03\n\ Release: 2018.03\n\ Codename: n/a\n\ " } // Amazon Linux 2 uses a separate Distributor ID and Release format from Amazon Linux 1 fn amazon2_file() -> &'static str { "LSB Version: :core-4.1-amd64:core-4.1-noarch\n\ Distributor ID: Amazon\n\ Description: Amazon Linux release 2 (Karoo)\n\ Release: 2\n\ Codename: Karoo\n\ " } fn rhel8_file() -> &'static str { "LSB Version: :core-4.1-amd64:core-4.1-noarch\n\ Distributor ID: RedHatEnterprise\n\ Description: Red Hat Enterprise Linux release 8.1 (Ootpa)\n\ Release: 8.1\n\ Codename: Ootpa\n\ " } fn rhel7_file() -> &'static str { "LSB Version: :core-4.1-amd64:core-4.1-noarch\n\ Distributor ID: RedHatEnterpriseServer\n\ Description: Red Hat Enterprise Linux Server release 7.7 (Maipo)\n\ Release: 7.7\n\ Codename: Maipo\n\ " } fn rhel6_file() -> &'static str { "LSB Version: :base-4.0-amd64:base-4.0-noarch:core-4.0-amd64:core-4.0-noarch:graphics-4.0-amd64:graphics-4.0-noarch:printing-4.0-amd64:printing-4.0-noarch\n\ Distributor ID: RedHatEnterpriseServer\n\ Description: Red Hat Enterprise Linux Server release 6.10 (Santiago)\n\ Release: 6.10\n\ Codename: Santiago\n\ " } fn suse_enterprise15_1_file() -> &'static str { "LSB Version: n/a\n\ Distributor ID: SUSE\n\ Description: SUSE Linux Enterprise Server 15 SP1\n\ Release: 15.1\n\ Codename: n/a\n\ " } fn suse_enterprise12_5_file() -> &'static str { "LSB Version: n/a\n\ Distributor ID: SUSE\n\ Description: SUSE Linux Enterprise Server 12 SP5\n\ Release: 12.5\n\ Codename: n/a\n\ " } fn raspberry_os_file() -> &'static str { "LSB Version: n/a\n\ Distributor ID: Raspbian\n\ Description: Raspbian GNU/Linux 10 (buster)\n\ Release: 10\n\ Codename: n/a\n\ " } fn open_suse_15_1_file() -> &'static str { "LSB Version: n/a\n\ Distributor ID: openSUSE\n\ Description: openSUSE Leap 15.1\n\ Release: 15.1\n\ Codename: n/a\n\ " } fn oracle_server_linux_7_5_file() -> &'static str { "LSB Version: :core-4.1-amd64:core-4.1-noarch\n\ Distributor ID: OracleServer\n\ Description: Oracle Linux Server release 7.5\n\ Release: 7.5\n\ Codename: n/a\n\ " } fn oracle_server_linux_8_1_file() -> &'static str { "LSB Version: :core-4.1-amd64:core-4.1-noarch\n\ Distributor ID: OracleServer\n\ Description: Oracle Linux Server release 8.1\n\ Release: 8.1\n\ Codename: n/a\n\ " } fn pop_os_20_04_lts_file() -> &'static str { "No LSB modules are available.\n\ Distributor ID: Pop\n\ Description: Pop!_OS 20.04 LTS\n\ Release: 20.04\n\ Codename: focal\n\ " } fn solus_4_1_file() -> &'static str { "LSB Version: 1.4\n\ Distributor ID: Solus\n\ Description: Solus\n\ Release: 4.1\n\ Codename: fortitude\n\ " } fn manjaro_19_0_2_file() -> &'static str { "LSB Version: n/a\n\ Distributor ID: ManjaroLinux\n\ Description: Manjaro Linux\n\ Release: 19.0.2\n\ Codename: n/a\n\ " } fn manjaro_arm_24_04() -> &'static str { "LSB Version: n/a\n\ Distributor ID: Manjaro-ARM\n\ Description: Manjaro ARM Linux\n\ Release: 24.04\n\ Codename: n/a\n\ " } fn mariner_file() -> &'static str { "LSB Version: n/a\n\ Distributor ID: Mariner\n\ Description: CBL-Mariner 2.0.20220210\n\ Release: 2.0.20220210\n\ Codename: Mariner\n\ " } fn endeavouros_file() -> &'static str { "LSB Version: 1.4\n\ Distributor ID: EndeavourOS\n\ Description: EndeavourOS Linux\n\ Release: rolling\n\ Codename: n/a\n\ " } fn ultramarine_file() -> &'static str { "LSB Version: n/a\n\ Distributor ID: UltramarineLinux\n\ Description: Ultramarine Linux 39 (Kuma)\n\ Release: 39\n\ Codename: kuma\n\ " } fn void_file() -> &'static str { "LSB Version: n/a\n\ Distributor ID: Void\n\ Description: Void Linux\n\ Release: rolling\n\ Codename: n/a\n\ " } fn cachyos_file() -> &'static str { "Distributor ID: cachyos\n\ Description: CachyOS\n\ Release: rolling\n\ Codename: n/a\n\ " } } os_info-3.13.0/src/linux/mod.rs000064400000000000000000000035051046102023000144220ustar 00000000000000mod file_release; mod lsb_release; use log::trace; use crate::{architecture, bitness, Info, Type}; pub fn current_platform() -> Info { trace!("linux::current_platform is called"); let mut info = lsb_release::get() .or_else(file_release::get) .unwrap_or_else(|| Info::with_type(Type::Linux)); info.bitness = bitness::get(); info.architecture = architecture::get(); trace!("Returning {:?}", info); info } #[cfg(test)] mod tests { use super::*; #[test] fn os_type() { let version = current_platform(); match version.os_type() { Type::AlmaLinux | Type::Alpaquita | Type::Alpine | Type::ALTLinux | Type::Amazon | Type::AOSC | Type::Arch | Type::Artix | Type::Bluefin | Type::CachyOS | Type::CentOS | Type::Debian | Type::Elementary | Type::EndeavourOS | Type::Fedora | Type::Garuda | Type::Gentoo | Type::Kali | Type::Linux | Type::Mabox | Type::Manjaro | Type::Mariner | Type::NixOS | Type::Nobara | Type::Uos | Type::OpenCloudOS | Type::openEuler | Type::openSUSE | Type::OracleLinux | Type::Pop | Type::Raspbian | Type::Redhat | Type::RedHatEnterprise | Type::RockyLinux | Type::Solus | Type::SUSE | Type::Ubuntu | Type::Ultramarine | Type::Void | Type::Zorin | Type::Mint => (), os_type => { panic!("Unexpected OS type: {}", os_type); } } } } os_info-3.13.0/src/macos/mod.rs000064400000000000000000000114101046102023000143570ustar 00000000000000use std::process::Command; use log::{trace, warn}; use crate::{architecture, bitness, matcher::Matcher, Info, Type, Version}; pub fn current_platform() -> Info { trace!("macos::current_platform is called"); let architecture = architecture::get(); let bits = architecture .as_deref() .map(|arch| match arch { "arm64" | "x86_64" => bitness::Bitness::X64, "i386" => bitness::Bitness::X32, _ => bitness::get(), }) .unwrap_or_else(bitness::get); let info = Info { os_type: Type::Macos, version: version(), bitness: bits, architecture, ..Default::default() }; trace!("Returning {:?}", info); info } fn version() -> Version { match product_version() { None => Version::Unknown, Some(val) => Version::from_string(val), } } #[allow(unsafe_code)] fn product_version_from_file() -> Option { use objc2_foundation::NSData; use objc2_foundation::NSDictionary; use objc2_foundation::NSPropertyListFormat; use objc2_foundation::NSPropertyListMutabilityOptions; use objc2_foundation::NSPropertyListSerialization; use objc2_foundation::{ns_string, NSString}; let buffer = std::fs::read("/System/Library/CoreServices/SystemVersion.plist"); if let Err(ref e) = buffer { warn!("Failed to read SystemVersion.plist: {e:?}"); } let buffer = buffer.ok()?; let data = NSData::with_bytes(&buffer); let mut format = NSPropertyListFormat::OpenStepFormat; // SAFETY: Necessary to call native API, exit-code is checked. let result = unsafe { NSPropertyListSerialization::propertyListWithData_options_format_error( &data, NSPropertyListMutabilityOptions(0), &mut format, ) }; if let Err(ref e) = result { warn!("Failed to parse SystemVersion.plist: {e:?}"); return None; } trace!("Parsed SystemVersion.plist with format: {format:?}"); let obj = result.ok()?; let dict = obj.downcast_ref::(); if dict.is_none() { warn!("Failed to downcast to NSDictionary"); return None; } let product_version = dict?.objectForKey(ns_string!("ProductVersion")); if product_version.is_none() { warn!("Failed to get ProductVersion from NSDictionary"); return None; } let product_version = product_version? .downcast_ref::() .map(NSString::to_string); if product_version.is_none() { warn!("Failed to downcast ProductVersion to NSString"); return None; } product_version } fn product_version() -> Option { if let Some(version) = product_version_from_file() { trace!("ProductVersion from SystemVersion.plist: {version:?}"); return Some(version); } match Command::new("sw_vers").output() { Ok(val) => { let output = String::from_utf8_lossy(&val.stdout); trace!("sw_vers command returned {:?}", output); parse(&output) } Err(e) => { warn!("sw_vers command failed with {:?}", e); None } } } fn parse(sw_vers_output: &str) -> Option { Matcher::PrefixedVersion { prefix: "ProductVersion:", } .find(sw_vers_output) } #[cfg(test)] mod tests { use super::*; use pretty_assertions::{assert_eq, assert_ne}; #[test] fn os_type() { let version = current_platform(); assert_eq!(Type::Macos, version.os_type()); } #[test] fn os_version() { let version = version(); assert_ne!(Version::Unknown, version); } #[test] fn string_product_version() { let version = product_version(); assert!(version.is_some()); } #[test] fn parse_version() { let parse_output = parse(sw_vers_output()); assert_eq!(parse_output, Some("10.10.5".to_string())); } fn sw_vers_output() -> &'static str { "ProductName: Mac OS X\n\ ProductVersion: 10.10.5\n\ BuildVersion: 14F27" } #[test] fn parse_beta_version() { let parse_output = parse(sw_vers_output_beta()); assert_eq!(parse_output, Some("10.15".to_string())); } fn sw_vers_output_beta() -> &'static str { "ProductName: Mac OS X\n\ ProductVersion: 10.15\n\ BuildVersion: 19A546d" } #[test] fn parse_double_digit_patch_version() { let parse_output = parse(sw_vers_output_double_digit_patch_version()); assert_eq!(parse_output, Some("10.15.21".to_string())); } fn sw_vers_output_double_digit_patch_version() -> &'static str { "ProductName: Mac OS X\n\ ProductVersion: 10.15.21\n\ BuildVersion: ABCD123" } } os_info-3.13.0/src/matcher.rs000064400000000000000000000103651046102023000141310ustar 00000000000000/// An implementation to match on simple strings. #[derive(Debug, Clone)] #[allow(dead_code)] pub enum Matcher { /// Considers the entire string (trimmed) to be the match. AllTrimmed, /// After finding the `prefix` followed by one or more spaces, returns the following word. PrefixedWord { prefix: &'static str }, /// Similar to `PrefixedWord`, but only if the word is a valid version. PrefixedVersion { prefix: &'static str }, /// Takes a set of lines (separated by `\n`) and searches for the value in a key/value pair /// separated by the `=` character. For example `VERSION_ID="8.1"`. KeyValue { key: &'static str }, } impl Matcher { /// Find the match on the input `string`. pub fn find(&self, string: &str) -> Option { match *self { Self::AllTrimmed => Some(string.trim().to_string()), Self::PrefixedWord { prefix } => find_prefixed_word(string, prefix).map(str::to_owned), Self::PrefixedVersion { prefix } => find_prefixed_word(string, prefix) .filter(|&v| is_valid_version(v)) .map(str::to_owned), Self::KeyValue { key } => find_by_key(string, key).map(str::to_owned), } } } fn find_by_key<'a>(string: &'a str, key: &str) -> Option<&'a str> { let key = [key, "="].concat(); for line in string.lines() { if line.starts_with(&key) { return Some(line[key.len()..].trim_matches(|c: char| c == '"' || c.is_whitespace())); } } None } fn find_prefixed_word<'a>(string: &'a str, prefix: &str) -> Option<&'a str> { if let Some(prefix_start) = string.find(prefix) { // Ignore prefix and leading whitespace let string = &string[prefix_start + prefix.len()..].trim_start(); // Find where the word boundary ends let word_end = string .find(|c: char| c.is_whitespace()) .unwrap_or(string.len()); let string = &string[..word_end]; Some(string) } else { None } } fn is_valid_version(word: &str) -> bool { !word.starts_with('.') && !word.ends_with('.') } #[cfg(test)] mod tests { use super::*; use pretty_assertions::assert_eq; #[test] fn trimmed() { let data = [ ("", Some("")), ("test", Some("test")), (" test", Some("test")), ("test ", Some("test")), (" test ", Some("test")), ]; let matcher = Matcher::AllTrimmed; for (input, expected) in &data { let result = matcher.find(input); assert_eq!(result.as_deref(), *expected); } } #[test] fn prefixed_word() { let data = [ ("", None), ("test", Some("")), ("test1", Some("1")), ("test 1", Some("1")), (" test 1", Some("1")), ("test 1.2.3", Some("1.2.3")), (" test 1.2.3", Some("1.2.3")), ]; let matcher = Matcher::PrefixedWord { prefix: "test" }; for (input, expected) in &data { let result = matcher.find(input); assert_eq!(result.as_deref(), *expected); } } #[test] fn prefixed_version() { let data = [ ("", None), ("test", Some("")), ("test 1", Some("1")), ("test .1", None), ("test 1.", None), ("test .1.", None), (" test 1", Some("1")), ("test 1.2.3", Some("1.2.3")), (" test 1.2.3", Some("1.2.3")), ]; let matcher = Matcher::PrefixedVersion { prefix: "test" }; for (input, expected) in &data { let result = matcher.find(input); assert_eq!(result.as_deref(), *expected); } } #[test] fn key_value() { let data = [ ("", None), ("key", None), ("key=value", Some("value")), ("key=1", Some("1")), ("key=\"1\"", Some("1")), ("key=\"CentOS Linux\"", Some("CentOS Linux")), ]; let matcher = Matcher::KeyValue { key: "key" }; for (input, expected) in &data { let result = matcher.find(input); assert_eq!(result.as_deref(), *expected); } } } os_info-3.13.0/src/netbsd/mod.rs000064400000000000000000000014601046102023000145400ustar 00000000000000use std::process::Command; use log::{error, trace}; use crate::{ architecture, bitness, uname::{uname, UnameField}, Info, Type, Version, }; pub fn current_platform() -> Info { trace!("netbsd::current_platform is called"); let version = uname(UnameField::Sysname) .map(Version::from_string) .unwrap_or_else(|| Version::Unknown); let info = Info { os_type: Type::NetBSD, version, bitness: bitness::get(), architecture: architecture::get(), ..Default::default() }; trace!("Returning {:?}", info); info } #[cfg(test)] mod tests { use super::*; use pretty_assertions::assert_eq; #[test] fn os_type() { let version = current_platform(); assert_eq!(Type::NetBSD, version.os_type()); } } os_info-3.13.0/src/openbsd/mod.rs000064400000000000000000000014631046102023000147160ustar 00000000000000use std::process::Command; use log::{error, trace}; use crate::{ architecture, bitness, uname::{uname, UnameField}, Info, Type, Version, }; pub fn current_platform() -> Info { trace!("openbsd::current_platform is called"); let version = uname(UnameField::Release) .map(Version::from_string) .unwrap_or_else(|| Version::Unknown); let info = Info { os_type: Type::OpenBSD, version, bitness: bitness::get(), architecture: architecture::get(), ..Default::default() }; trace!("Returning {:?}", info); info } #[cfg(test)] mod tests { use super::*; use pretty_assertions::assert_eq; #[test] fn os_type() { let version = current_platform(); assert_eq!(Type::OpenBSD, version.os_type()); } } os_info-3.13.0/src/os_type.rs000064400000000000000000000225601046102023000141700ustar 00000000000000use std::fmt::{self, Display, Formatter}; /// A list of supported operating system types. #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] #[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[allow(non_camel_case_types, clippy::upper_case_acronyms)] #[non_exhaustive] pub enum Type { /// IBM AIX (). AIX, /// AlmaLinux (). AlmaLinux, /// Alpaquita Linux (). Alpaquita, /// Alpine Linux (). Alpine, /// ALT Linux (https://en.wikipedia.org/wiki/ALT_Linux). ALTLinux, /// Amazon Linux AMI (). Amazon, /// Android (). Android, /// AOSC OS (). AOSC, /// Arch Linux (). Arch, /// Artix Linux (). Artix, /// Bluefin (). Bluefin, /// CachyOS (). CachyOS, /// CentOS (). CentOS, /// Cygwin (). Cygwin, /// Debian (). Debian, /// DragonFly BSD (). DragonFly, /// Elementary OS (). Elementary, /// Emscripten (). Emscripten, /// EndeavourOS (). EndeavourOS, /// Fedora (). Fedora, /// FreeBSD (). FreeBSD, /// Garuda Linux () Garuda, /// Gentoo Linux (). Gentoo, /// HardenedBSD (https://hardenedbsd.org/). HardenedBSD, /// Illumos (https://en.wikipedia.org/wiki/Illumos). Illumos, /// iOS (). Ios, /// Kali Linux (https://en.wikipedia.org/wiki/Kali_Linux). Kali, /// Linux based operating system (). Linux, /// Mabox (). Mabox, /// Mac OS X/OS X/macOS (). Macos, /// Manjaro (). Manjaro, /// Mariner (). Mariner, /// MidnightBSD (). MidnightBSD, /// Mint (). Mint, /// NetBSD (). NetBSD, /// NixOS (). NixOS, /// Nobara (). Nobara, /// OpenBSD (). OpenBSD, /// OpenCloudOS (). OpenCloudOS, /// openEuler (). openEuler, /// openSUSE (). openSUSE, /// Oracle Linux (). OracleLinux, /// Pop!_OS () Pop, /// Raspberry Pi OS (). #[cfg_attr(feature = "serde", serde(alias = "RaspberryPiOS"))] Raspbian, /// Red Hat Linux (). Redhat, /// Red Hat Enterprise Linux (). RedHatEnterprise, /// Redox (). Redox, /// Rocky Linux (). RockyLinux, /// Solus (). Solus, /// SUSE Linux Enterprise Server (). SUSE, /// Ubuntu (). Ubuntu, /// Ultramarine (). Ultramarine, /// Uos (). Uos, /// Void Linux (). Void, /// Zorin OS (). Zorin, /// Unknown operating system. Unknown, /// Windows (). Windows, } impl Default for Type { fn default() -> Self { Type::Unknown } } impl Display for Type { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match *self { Type::Alpaquita => write!(f, "Alpaquita Linux"), Type::Alpine => write!(f, "Alpine Linux"), Type::AlmaLinux => write!(f, "AlmaLinux"), Type::ALTLinux => write!(f, "ALT Linux"), Type::Amazon => write!(f, "Amazon Linux AMI"), Type::AOSC => write!(f, "AOSC OS"), Type::Arch => write!(f, "Arch Linux"), Type::Bluefin => write!(f, "Bluefin"), Type::CachyOS => write!(f, "CachyOS Linux"), Type::Artix => write!(f, "Artix Linux"), Type::DragonFly => write!(f, "DragonFly BSD"), Type::Elementary => write!(f, "Elementary OS"), Type::Garuda => write!(f, "Garuda Linux"), Type::Gentoo => write!(f, "Gentoo Linux"), Type::Illumos => write!(f, "illumos"), Type::Ios => write!(f, "iOS"), Type::Kali => write!(f, "Kali Linux"), Type::Macos => write!(f, "Mac OS"), Type::MidnightBSD => write!(f, "Midnight BSD"), Type::Mint => write!(f, "Linux Mint"), Type::Nobara => write!(f, "Nobara Linux"), Type::openEuler => write!(f, "EulerOS"), Type::OracleLinux => write!(f, "Oracle Linux"), Type::Pop => write!(f, "Pop!_OS"), Type::Raspbian => write!(f, "Raspberry Pi OS"), Type::Redhat => write!(f, "Red Hat Linux"), Type::RedHatEnterprise => write!(f, "Red Hat Enterprise Linux"), Type::RockyLinux => write!(f, "Rocky Linux"), Type::SUSE => write!(f, "SUSE Linux Enterprise Server"), Type::Ultramarine => write!(f, "Ultramarine Linux"), Type::Uos => write!(f, "UOS"), Type::Void => write!(f, "Void Linux"), Type::Zorin => write!(f, "Zorin OS"), _ => write!(f, "{self:?}"), } } } #[cfg(test)] mod tests { use super::*; use pretty_assertions::assert_eq; #[test] fn default() { assert_eq!(Type::Unknown, Type::default()); } #[test] fn display() { let data = [ (Type::AIX, "AIX"), (Type::AlmaLinux, "AlmaLinux"), (Type::Alpaquita, "Alpaquita Linux"), (Type::Alpine, "Alpine Linux"), (Type::ALTLinux, "ALT Linux"), (Type::Amazon, "Amazon Linux AMI"), (Type::Android, "Android"), (Type::AOSC, "AOSC OS"), (Type::Arch, "Arch Linux"), (Type::Artix, "Artix Linux"), (Type::Bluefin, "Bluefin"), (Type::CachyOS, "CachyOS Linux"), (Type::CentOS, "CentOS"), (Type::Cygwin, "Cygwin"), (Type::Debian, "Debian"), (Type::DragonFly, "DragonFly BSD"), (Type::Elementary, "Elementary OS"), (Type::Emscripten, "Emscripten"), (Type::EndeavourOS, "EndeavourOS"), (Type::Fedora, "Fedora"), (Type::FreeBSD, "FreeBSD"), (Type::Garuda, "Garuda Linux"), (Type::Gentoo, "Gentoo Linux"), (Type::HardenedBSD, "HardenedBSD"), (Type::Illumos, "illumos"), (Type::Ios, "iOS"), (Type::Kali, "Kali Linux"), (Type::Linux, "Linux"), (Type::Mabox, "Mabox"), (Type::Macos, "Mac OS"), (Type::Manjaro, "Manjaro"), (Type::Mariner, "Mariner"), (Type::MidnightBSD, "Midnight BSD"), (Type::Mint, "Linux Mint"), (Type::NetBSD, "NetBSD"), (Type::NixOS, "NixOS"), (Type::Nobara, "Nobara Linux"), (Type::OpenCloudOS, "OpenCloudOS"), (Type::OpenBSD, "OpenBSD"), (Type::openEuler, "EulerOS"), (Type::openSUSE, "openSUSE"), (Type::OracleLinux, "Oracle Linux"), (Type::Pop, "Pop!_OS"), (Type::Raspbian, "Raspberry Pi OS"), (Type::Redhat, "Red Hat Linux"), (Type::RedHatEnterprise, "Red Hat Enterprise Linux"), (Type::Redox, "Redox"), (Type::RockyLinux, "Rocky Linux"), (Type::Solus, "Solus"), (Type::SUSE, "SUSE Linux Enterprise Server"), (Type::Ubuntu, "Ubuntu"), (Type::Ultramarine, "Ultramarine Linux"), (Type::Unknown, "Unknown"), (Type::Uos, "UOS"), (Type::Void, "Void Linux"), (Type::Zorin, "Zorin OS"), (Type::Windows, "Windows"), ]; for (t, expected) in &data { assert_eq!(&t.to_string(), expected); } } } os_info-3.13.0/src/redox/mod.rs000064400000000000000000000023051046102023000144010ustar 00000000000000// spell-checker:ignore uname use std::{fs::File, io::Read}; use log::{error, trace}; use crate::{Bitness, Info, Type, Version}; const UNAME_FILE: &str = "sys:uname"; pub fn current_platform() -> Info { trace!("redox::current_platform is called"); let version = get_version() .map(Version::from_string) .unwrap_or_else(|| Version::Unknown); let info = Info { os_type: Type::Redox, version, bitness: Bitness::Unknown, ..Default::default() }; trace!("Returning {:?}", info); info } fn get_version() -> Option { let mut file = match File::open(UNAME_FILE) { Ok(file) => file, Err(e) => { error!("Unable to open {} file: {:?}", UNAME_FILE, e); return None; } }; let mut version = String::new(); if let Err(e) = file.read_to_string(&mut version) { error!("Unable to read {} file: {:?}", UNAME_FILE, e); return None; } Some(version) } #[cfg(test)] mod tests { use super::*; use pretty_assertions::assert_eq; #[test] fn os_type() { let version = current_platform(); assert_eq!(Type::Redox, version.os_type()); } } os_info-3.13.0/src/uname.rs000064400000000000000000000047241046102023000136150ustar 00000000000000use std::process::Command; use log::error; use nix::sys::utsname::uname as nix_uname; #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[allow(dead_code)] pub enum UnameField { Sysname, Release, Version, Machine, Nodename, OperatingSystem, } impl UnameField { fn cli_arg_name(&self) -> &'static str { match self { UnameField::Sysname => "-s", UnameField::Release => "-r", UnameField::Version => "-v", UnameField::Machine => "-m", UnameField::Nodename => "-n", UnameField::OperatingSystem => "-o", } } fn supports_uname_syscall(&self) -> bool { self != &UnameField::OperatingSystem } fn get_from_syscall(&self) -> Option { if !self.supports_uname_syscall() { return None; } let utsname = match nix_uname() { Ok(utsname) => utsname, Err(e) => { log::error!("Failed to invoke native uname: {e:?}"); return None; } }; let val_os = match self { UnameField::Sysname => utsname.sysname(), UnameField::Release => utsname.release(), UnameField::Version => utsname.version(), UnameField::Machine => utsname.machine(), UnameField::Nodename => utsname.nodename(), UnameField::OperatingSystem => return None, }; let val = val_os.to_str(); if val.is_none() { error!("Failed to convert uname value to string: {val_os:?}"); return None; } val.map(String::from) } } pub fn uname(field: UnameField) -> Option { field .get_from_syscall() .or_else(|| uname_cli(field.cli_arg_name())) } fn uname_cli(arg: &str) -> Option { Command::new("uname") .arg(arg) .output() .map_err(|e| { error!("Failed to invoke 'uname {}': {:?}", arg, e); }) .ok() .and_then(|out| { if out.status.success() { Some(String::from_utf8_lossy(&out.stdout).trim_end().to_owned()) } else { error!("'uname {}' failed with status: {:?}", arg, out.status); None } }) } #[cfg(test)] mod tests { use super::*; #[test] fn uname_nonempty() { let val = uname(UnameField::Sysname).expect("uname failed"); assert!(!val.is_empty()); } } os_info-3.13.0/src/unknown/mod.rs000064400000000000000000000005621046102023000147620ustar 00000000000000use log::trace; use crate::{Info, Type}; pub fn current_platform() -> Info { trace!("unknown::current_platform is called"); Info::unknown() } #[cfg(test)] mod tests { use super::*; use pretty_assertions::assert_eq; #[test] fn os_type() { let version = current_platform(); assert_eq!(Type::Unknown, version.os_type()); } } os_info-3.13.0/src/version.rs000064400000000000000000000103111046102023000141620ustar 00000000000000use std::fmt::{self, Display, Formatter}; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; /// Operating system version. #[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] pub enum Version { /// Unknown version. Unknown, /// Semantic version (major.minor.patch). Semantic(u64, u64, u64), /// Rolling version. Optionally contains the release date in the string format. Rolling(Option), /// Custom version format. Custom(String), } impl Version { /// Constructs `VersionType` from the given string. /// /// Returns `VersionType::Unknown` if the string is empty. If it can be parsed as a semantic /// version, then `VersionType::Semantic`, otherwise `VersionType::Custom`. /// /// # Examples /// /// ``` /// use os_info::Version; /// /// let v = Version::from_string("custom"); /// assert_eq!(Version::Custom("custom".to_owned()), v); /// /// let v = Version::from_string("1.2.3"); /// assert_eq!(Version::Semantic(1, 2, 3), v); /// ``` pub fn from_string + AsRef>(s: S) -> Self { if s.as_ref().is_empty() { Self::Unknown } else if let Some((major, minor, patch)) = parse_version(s.as_ref()) { Self::Semantic(major, minor, patch) } else { Self::Custom(s.into()) } } } impl Default for Version { fn default() -> Self { Version::Unknown } } impl Display for Version { fn fmt(&self, f: &mut Formatter) -> fmt::Result { match *self { Self::Unknown => f.write_str("Unknown"), Self::Semantic(major, minor, patch) => write!(f, "{major}.{minor}.{patch}"), Self::Rolling(ref date) => { let date = match date { Some(date) => format!(" ({date})"), None => "".to_owned(), }; write!(f, "Rolling Release{date}") } Self::Custom(ref version) => write!(f, "{version}"), } } } fn parse_version(s: &str) -> Option<(u64, u64, u64)> { let mut iter = s.trim().split_terminator('.').fuse(); let major = iter.next().and_then(|s| s.parse().ok())?; let minor = iter.next().unwrap_or("0").parse().ok()?; let patch = iter.next().unwrap_or("0").parse().ok()?; if iter.next().is_some() { return None; } Some((major, minor, patch)) } #[cfg(test)] mod tests { use super::*; use pretty_assertions::assert_eq; #[test] fn parse_semantic_version() { let data = [ ("", None), ("version", None), ("1", Some((1, 0, 0))), ("1.", Some((1, 0, 0))), ("1.2", Some((1, 2, 0))), ("1.2.", Some((1, 2, 0))), ("1.2.3", Some((1, 2, 3))), ("1.2.3.", Some((1, 2, 3))), ("1.2.3. ", Some((1, 2, 3))), (" 1.2.3.", Some((1, 2, 3))), (" 1.2.3. ", Some((1, 2, 3))), ("1.2.3.4", None), ("1.2.3.4.5.6.7.8.9", None), ]; for (s, expected) in &data { let result = parse_version(s); assert_eq!(expected, &result); } } #[test] fn from_string() { let custom_version = "some version"; let data = [ ("", Version::Unknown), ("1.2.3", Version::Semantic(1, 2, 3)), (custom_version, Version::Custom(custom_version.to_owned())), ]; for (s, expected) in &data { let version = Version::from_string(*s); assert_eq!(expected, &version); } } #[test] fn default() { assert_eq!(Version::Unknown, Version::default()); } #[test] fn display() { let data = [ (Version::Unknown, "Unknown"), (Version::Semantic(1, 5, 0), "1.5.0"), (Version::Rolling(None), "Rolling Release"), ( Version::Rolling(Some("date".to_owned())), "Rolling Release (date)", ), ]; for (version, expected) in &data { assert_eq!(expected, &version.to_string()); } } } os_info-3.13.0/src/windows/mod.rs000064400000000000000000000007611046102023000147560ustar 00000000000000mod winapi; use log::trace; use crate::Info; pub fn current_platform() -> Info { trace!("windows::current_platform is called"); let info = winapi::get(); trace!("Returning {:?}", info); info } #[cfg(test)] mod tests { use super::*; use crate::Type; use pretty_assertions::assert_eq; #[test] fn os_type() { let version = current_platform(); assert_eq!(Type::Windows, version.os_type()); assert!(version.edition().is_some()); } } os_info-3.13.0/src/windows/winapi.rs000064400000000000000000000327371046102023000154760ustar 00000000000000// spell-checker:ignore dword, minwindef, ntdef, ntdll, ntstatus, osversioninfoex, osversioninfoexa // spell-checker:ignore osversioninfoexw, serverr, sysinfoapi, winnt, winuser, pbool, libloaderapi // spell-checker:ignore lpcstr, processthreadsapi, farproc, lstatus, wchar, lpbyte, hkey, winerror // spell-checker:ignore osstr, winreg #![allow(unsafe_code)] use std::{ ffi::{OsStr, OsString}, mem::{self, MaybeUninit}, os::windows::ffi::{OsStrExt, OsStringExt}, ptr, }; use windows_sys::Win32::{ Foundation::{ERROR_SUCCESS, FARPROC, NTSTATUS, STATUS_SUCCESS}, System::{ LibraryLoader::{GetModuleHandleA, GetProcAddress}, Registry::{ RegCloseKey, RegOpenKeyExW, RegQueryValueExW, HKEY, HKEY_LOCAL_MACHINE, KEY_READ, REG_SZ, }, SystemInformation::{ GetNativeSystemInfo, GetSystemInfo, PROCESSOR_ARCHITECTURE_AMD64, PROCESSOR_ARCHITECTURE_ARM, PROCESSOR_ARCHITECTURE_IA64, PROCESSOR_ARCHITECTURE_INTEL, SYSTEM_INFO, }, SystemServices::{VER_NT_WORKSTATION, VER_SUITE_WH_SERVER}, }, UI::WindowsAndMessaging::{GetSystemMetrics, SM_SERVERR2}, }; use crate::{Bitness, Info, Type, Version}; #[cfg(target_arch = "x86")] type OSVERSIONINFOEX = windows_sys::Win32::System::SystemInformation::OSVERSIONINFOEXA; #[cfg(not(target_arch = "x86"))] type OSVERSIONINFOEX = windows_sys::Win32::System::SystemInformation::OSVERSIONINFOEXW; struct HKeyWrapper(HKEY); impl Drop for HKeyWrapper { fn drop(&mut self) { if !self.0.is_null() { unsafe { RegCloseKey(self.0) }; } } } pub fn get() -> Info { let (version, edition) = version(); let native_system_info = native_system_info(); Info { os_type: Type::Windows, version, edition, bitness: bitness(), architecture: architecture(native_system_info), ..Default::default() } } fn version() -> (Version, Option) { match version_info() { None => (Version::Unknown, None), Some(v) => ( Version::Semantic( v.dwMajorVersion as u64, v.dwMinorVersion as u64, v.dwBuildNumber as u64, ), product_name(&v).or_else(|| edition(&v)), ), } } // According to https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info // there is a variant for AMD64 CPUs, but it's not defined in generated bindings. const PROCESSOR_ARCHITECTURE_ARM64: u16 = 12; fn native_system_info() -> SYSTEM_INFO { let mut system_info: MaybeUninit = MaybeUninit::zeroed(); unsafe { GetNativeSystemInfo(system_info.as_mut_ptr()); }; unsafe { system_info.assume_init() } } fn architecture(system_info: SYSTEM_INFO) -> Option { let cpu_architecture = unsafe { system_info.Anonymous.Anonymous.wProcessorArchitecture }; match cpu_architecture { PROCESSOR_ARCHITECTURE_AMD64 => Some("x86_64"), PROCESSOR_ARCHITECTURE_IA64 => Some("ia64"), PROCESSOR_ARCHITECTURE_ARM => Some("arm"), PROCESSOR_ARCHITECTURE_ARM64 => Some("aarch64"), PROCESSOR_ARCHITECTURE_INTEL => Some("i386"), _ => None, } .map(str::to_string) } #[cfg(target_pointer_width = "64")] fn bitness() -> Bitness { // x64 program can only run on x64 Windows. Bitness::X64 } #[cfg(target_pointer_width = "32")] fn bitness() -> Bitness { use windows_sys::core::BOOL; use windows_sys::Win32::Foundation::{FALSE, HANDLE}; use windows_sys::Win32::System::Threading::GetCurrentProcess; // IsWow64Process is not available on all supported versions of Windows. Use GetModuleHandle to // get a handle to the DLL that contains the function and GetProcAddress to get a pointer to the // function if available. let is_wow_64 = match get_proc_address(b"kernel32\0", b"IsWow64Process\0") { None => return Bitness::Unknown, Some(val) => val, }; type IsWow64 = unsafe extern "system" fn(HANDLE, *mut BOOL) -> BOOL; let is_wow_64: IsWow64 = unsafe { mem::transmute(is_wow_64) }; let mut result = FALSE; if unsafe { is_wow_64(GetCurrentProcess(), &mut result) } == 0 { log::error!("IsWow64Process failed"); return Bitness::Unknown; } if result == FALSE { Bitness::X32 } else { Bitness::X64 } } // Calls the Win32 API function RtlGetVersion to get the OS version information: // https://msdn.microsoft.com/en-us/library/mt723418(v=vs.85).aspx fn version_info() -> Option { let rtl_get_version = match get_proc_address(b"ntdll\0", b"RtlGetVersion\0") { None => return None, Some(val) => val, }; type RtlGetVersion = unsafe extern "system" fn(&mut OSVERSIONINFOEX) -> NTSTATUS; let rtl_get_version: RtlGetVersion = unsafe { mem::transmute(rtl_get_version) }; let mut info: OSVERSIONINFOEX = unsafe { mem::zeroed() }; info.dwOSVersionInfoSize = mem::size_of::() as u32; if unsafe { rtl_get_version(&mut info) } == STATUS_SUCCESS { Some(info) } else { None } } fn product_name(info: &OSVERSIONINFOEX) -> Option { let sub_key = to_wide("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"); let mut key = HKeyWrapper(ptr::null_mut()); if unsafe { RegOpenKeyExW( HKEY_LOCAL_MACHINE, sub_key.as_ptr(), 0, KEY_READ, &mut key.0, ) } != ERROR_SUCCESS || key.0.is_null() { log::error!("RegOpenKeyExW(HKEY_LOCAL_MACHINE, ...) failed"); return None; } let is_win_11 = info.dwMajorVersion == 10 && info.dwBuildNumber >= 22000; // Get size of the data. let name = to_wide(if is_win_11 { "EditionID" } else { "ProductName" }); let mut data_type = 0; let mut data_size = 0; if unsafe { RegQueryValueExW( key.0, name.as_ptr(), ptr::null_mut(), &mut data_type, ptr::null_mut(), &mut data_size, ) } != ERROR_SUCCESS || data_type != REG_SZ || data_size == 0 || data_size % 2 != 0 { log::error!("RegQueryValueExW failed"); return None; } // Get the data. let mut data = vec![0u16; data_size as usize / 2]; if unsafe { RegQueryValueExW( key.0, name.as_ptr(), ptr::null_mut(), ptr::null_mut(), data.as_mut_ptr().cast(), &mut data_size, ) } != ERROR_SUCCESS || data_size as usize != data.len() * 2 { return None; } // If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type, the string may not have been // stored with the proper terminating null characters. match data.last() { Some(0) => { data.pop(); } _ => {} } let value = OsString::from_wide(data.as_slice()) .to_string_lossy() .into_owned(); if is_win_11 { Some(format!("Windows 11 {}", value)) } else { Some(value) } } fn to_wide(value: &str) -> Vec { OsStr::new(value).encode_wide().chain(Some(0)).collect() } // Examines data in the OSVERSIONINFOEX structure to determine the Windows edition: // https://msdn.microsoft.com/en-us/library/windows/desktop/ms724833(v=vs.85).aspx fn edition(version_info: &OSVERSIONINFOEX) -> Option { match ( version_info.dwMajorVersion, version_info.dwMinorVersion, version_info.wProductType as u32, ) { // Windows 10. (10, 0, VER_NT_WORKSTATION) => { if version_info.dwBuildNumber >= 22000 { Some("Windows 11") } else { Some("Windows 10") } } (10, 0, _) => Some("Windows Server 2016"), // Windows Vista, 7, 8 and 8.1. (6, 3, VER_NT_WORKSTATION) => Some("Windows 8.1"), (6, 3, _) => Some("Windows Server 2012 R2"), (6, 2, VER_NT_WORKSTATION) => Some("Windows 8"), (6, 2, _) => Some("Windows Server 2012"), (6, 1, VER_NT_WORKSTATION) => Some("Windows 7"), (6, 1, _) => Some("Windows Server 2008 R2"), (6, 0, VER_NT_WORKSTATION) => Some("Windows Vista"), (6, 0, _) => Some("Windows Server 2008"), // Windows 2000, Home Server, 2003 Server, 2003 R2 Server, XP and XP Professional x64. (5, 1, _) => Some("Windows XP"), (5, 0, _) => Some("Windows 2000"), (5, 2, _) if unsafe { GetSystemMetrics(SM_SERVERR2) } == 0 => { let mut info: SYSTEM_INFO = unsafe { mem::zeroed() }; unsafe { GetSystemInfo(&mut info) }; if Into::::into(version_info.wSuiteMask) & VER_SUITE_WH_SERVER == VER_SUITE_WH_SERVER { Some("Windows Home Server") } else if version_info.wProductType == VER_NT_WORKSTATION as u8 && unsafe { info.Anonymous.Anonymous.wProcessorArchitecture } == PROCESSOR_ARCHITECTURE_AMD64 { Some("Windows XP Professional x64 Edition") } else { Some("Windows Server 2003") } } _ => None, } .map(str::to_string) } fn get_proc_address(module: &[u8], proc: &[u8]) -> Option { assert!( *module.last().expect("Empty module name") == 0, "Module name should be zero-terminated" ); assert!( *proc.last().expect("Empty procedure name") == 0, "Procedure name should be zero-terminated" ); let handle = unsafe { GetModuleHandleA(module.as_ptr()) }; if handle.is_null() { log::error!( "GetModuleHandleA({}) failed", String::from_utf8_lossy(module) ); return None; } unsafe { Some(GetProcAddress(handle, proc.as_ptr())) } } #[cfg(test)] mod tests { use super::*; use pretty_assertions::{assert_eq, assert_ne}; #[test] fn version() { let info = get(); assert_eq!(Type::Windows, info.os_type()); } #[test] fn get_version_info() { let version = version_info(); assert!(version.is_some()); } #[test] fn get_edition() { let test_data = [ (10, 0, 0, "Windows Server 2016"), (6, 3, VER_NT_WORKSTATION, "Windows 8.1"), (6, 3, 0, "Windows Server 2012 R2"), (6, 2, VER_NT_WORKSTATION, "Windows 8"), (6, 2, 0, "Windows Server 2012"), (6, 1, VER_NT_WORKSTATION, "Windows 7"), (6, 1, 0, "Windows Server 2008 R2"), (6, 0, VER_NT_WORKSTATION, "Windows Vista"), (6, 0, 0, "Windows Server 2008"), (5, 1, 0, "Windows XP"), (5, 1, 1, "Windows XP"), (5, 1, 100, "Windows XP"), (5, 0, 0, "Windows 2000"), (5, 0, 1, "Windows 2000"), (5, 0, 100, "Windows 2000"), ]; let mut info = version_info().unwrap(); for &(major, minor, product_type, expected_edition) in &test_data { info.dwMajorVersion = major; info.dwMinorVersion = minor; info.wProductType = product_type as u8; let edition = edition(&info).unwrap(); assert_eq!(edition, expected_edition); } } #[test] fn get_bitness() { let b = bitness(); assert_ne!(b, Bitness::Unknown); } #[test] #[should_panic(expected = "Empty module name")] fn empty_module_name() { get_proc_address(b"", b"RtlGetVersion\0"); } #[test] #[should_panic(expected = "Module name should be zero-terminated")] fn non_zero_terminated_module_name() { get_proc_address(b"ntdll", b"RtlGetVersion\0"); } #[test] #[should_panic(expected = "Empty procedure name")] fn empty_proc_name() { get_proc_address(b"ntdll\0", b""); } #[test] #[should_panic(expected = "Procedure name should be zero-terminated")] fn non_zero_terminated_proc_name() { get_proc_address(b"ntdll\0", b"RtlGetVersion"); } #[test] fn proc_address() { let address = get_proc_address(b"ntdll\0", b"RtlGetVersion\0"); assert!(address.is_some()); } #[test] fn get_architecture() { let cpu_types: [(u16, Option); 6] = [ (PROCESSOR_ARCHITECTURE_AMD64, Some("x86_64".to_owned())), (PROCESSOR_ARCHITECTURE_ARM, Some("arm".to_owned())), (PROCESSOR_ARCHITECTURE_ARM64, Some("aarch64".to_owned())), (PROCESSOR_ARCHITECTURE_IA64, Some("ia64".to_owned())), (PROCESSOR_ARCHITECTURE_INTEL, Some("i386".to_owned())), (0xffff, None), ]; let mut native_info = native_system_info(); for cpu_type in cpu_types { native_info.Anonymous.Anonymous.wProcessorArchitecture = cpu_type.0; assert_eq!(architecture(native_info), cpu_type.1); } } #[test] fn get_product_name() { let version = version_info().expect("version_info() failed"); let edition = product_name(&version).expect("edition() failed"); assert!(!edition.is_empty()); } #[test] fn to_wide_str() { let data = [ ("", [0x0000].as_ref()), ("U", &[0x0055, 0x0000]), ("你好", &[0x4F60, 0x597D, 0x0000]), ]; for (s, expected) in &data { let wide = to_wide(s); assert_eq!(&wide, expected); } } }