os_info-3.7.0/.cargo_vcs_info.json0000644000000001450000000000100124770ustar { "git": { "sha1": "755ae488dfc5d34a424a998bdb5c947e82bc2976" }, "path_in_vcs": "os_info" }os_info-3.7.0/Cargo.lock0000644000000077250000000000100104650ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "ansi_term" version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d52a9bb7ec0cf484c551830a7ce27bd20d67eac647e1befb56b0be4ee39a55d2" dependencies = [ "winapi", ] [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "ctor" version = "0.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdffe87e1d521a10f9696f833fe502293ea446d7f256c06128293a4119bdf4cb" dependencies = [ "quote", "syn", ] [[package]] name = "diff" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" [[package]] name = "doc-comment" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" [[package]] name = "log" version = "0.4.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e" dependencies = [ "cfg-if", ] [[package]] name = "os_info" version = "3.7.0" dependencies = [ "doc-comment", "log", "pretty_assertions", "serde", "winapi", ] [[package]] name = "output_vt100" version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "628223faebab4e3e40667ee0b2336d34a5b960ff60ea743ddfdbcf7770bcfb66" dependencies = [ "winapi", ] [[package]] name = "pretty_assertions" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c89f989ac94207d048d92db058e4f6ec7342b0971fc58d1271ca148b799b3563" dependencies = [ "ansi_term", "ctor", "diff", "output_vt100", ] [[package]] name = "proc-macro2" version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c278e965f1d8cf32d6e0e96de3d3e79712178ae67986d9cf9151f51e95aac89b" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" dependencies = [ "proc-macro2", ] [[package]] name = "serde" version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc855a42c7967b7c369eb5860f7164ef1f6f81c20c7cc1141f2a604e18723b03" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f2122636b9fe3b81f1cb25099fcf2d3f542cdb1d45940d56c713158884a05da" dependencies = [ "proc-macro2", "quote", "syn", ] [[package]] name = "syn" version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "unicode-ident" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "15c61ba63f9235225a22310255a29b806b907c9b8c964bcbd0a2c70f3f2deea7" [[package]] name = "winapi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" dependencies = [ "winapi-i686-pc-windows-gnu", "winapi-x86_64-pc-windows-gnu", ] [[package]] name = "winapi-i686-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" [[package]] name = "winapi-x86_64-pc-windows-gnu" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" os_info-3.7.0/Cargo.toml0000644000000027330000000000100105020ustar # 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.7.0" authors = [ "Jan Schulte ", "Stanislav Tkach ", ] 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" [dependencies.log] version = "0.4.5" [dependencies.serde] version = "1" features = ["derive"] optional = true [dev-dependencies.doc-comment] version = "0.3.1" [dev-dependencies.pretty_assertions] version = "1" [features] default = ["serde"] [target."cfg(windows)".dependencies.winapi] version = "0.3.8" features = [ "minwindef", "ntdef", "ntstatus", "sysinfoapi", "winnt", "winuser", "libloaderapi", "processthreadsapi", "winerror", "winreg", ] os_info-3.7.0/Cargo.toml.orig000064400000000000000000000016221046102023000141570ustar 00000000000000[package] name = "os_info" version = "3.7.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" [features] default = ["serde"] [dependencies] log = "0.4.5" serde = { version = "1", features = ["derive"], optional = true } [target.'cfg(windows)'.dependencies] winapi = { version = "0.3.8", features = ["minwindef", "ntdef", "ntstatus", "sysinfoapi", "winnt", "winuser", "libloaderapi", "processthreadsapi", "winerror", "winreg"] } [dev-dependencies] pretty_assertions = "1" doc-comment = "0.3.1" os_info-3.7.0/LICENSE000064400000000000000000000020721046102023000122750ustar 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.7.0/README.md000064400000000000000000000062311046102023000125500ustar 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.41+-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: - Alpaquita Linux - Alpine Linux - Amazon Linux AMI - Android - Arch Linux - Artix Linux - CentOS - Debian - DragonFly BSD - Emscripten - EndeavourOS - Fedora - FreeBSD - Garuda Linux - Gentoo Linux - HardenedBSD - Illumos - Linux - macOS (Mac OS X or OS X) - Manjaro - Mariner - MidnightBSD - Mint - NetBSD - NixOS - OpenBSD - openSUSE - Oracle Linux - Pop!_OS - Raspberry Pi OS - Red Hat Linux - Red Hat Enterprise Linux - Redox - Solus - SUSE Linux Enterprise Server - Ubuntu - Unknown - 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.7.0/examples/print_version.rs000064400000000000000000000006701046102023000163570ustar 00000000000000fn main() { 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()); } os_info-3.7.0/src/android/mod.rs000064400000000000000000000007041046102023000146240ustar 00000000000000use log::trace; use crate::{Bitness, Info, Type}; pub fn current_platform() -> Info { trace!("android::current_platform is called"); let info = Info::with_type(Type::Android); 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::Android, version.os_type()); } } os_info-3.7.0/src/architecture.rs000064400000000000000000000013231046102023000151050ustar 00000000000000use std::process::Command; use log::error; pub fn get() -> Option { Command::new("uname") .arg("-m") .output() .map_err(|e| { error!("Cannot invoke 'uname` to get architecture type: {:?}", e); }) .ok() .and_then(|out| { if out.status.success() { Some(String::from_utf8_lossy(&out.stdout).trim_end().to_owned()) } else { log::error!("'uname' invocation error: {:?}", out); None } }) } #[cfg(test)] mod tests { use super::*; #[test] fn uname_nonempty() { let val = get().expect("uname failed"); assert!(!val.is_empty()); } } os_info-3.7.0/src/bitness.rs000064400000000000000000000071321046102023000140760ustar 00000000000000// spell-checker:ignore getconf use std::fmt::{self, Display, Formatter}; #[cfg(any( target_os = "dragonfly", target_os = "freebsd", target_os = "illumos", target_os = "linux", target_os = "macos", target_os = "netbsd", target_os = "openbsd" ))] 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 = "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(all( test, any( target_os = "dragonfly", target_os = "freebsd", target_os = "linux", target_os = "macos", target_os = "netbsd", target_os = "openbsd" ) ))] 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.7.0/src/dragonfly/mod.rs000064400000000000000000000013201046102023000151640ustar 00000000000000use std::process::Command; use log::trace; use crate::{bitness, uname::uname, Bitness, Info, Type, Version}; pub fn current_platform() -> Info { trace!("dragonfly::current_platform is called"); let version = uname() .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.7.0/src/emscripten/mod.rs000064400000000000000000000007671046102023000153660ustar 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.7.0/src/freebsd/mod.rs000064400000000000000000000026441046102023000146230ustar 00000000000000use std::process::Command; use std::str; use log::{error, trace}; use crate::{bitness, uname::uname, Info, Type, Version}; pub fn current_platform() -> Info { trace!("freebsd::current_platform is called"); let version = uname() .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 { let os = Command::new("uname") .arg("-s") .output() .expect("Failed to get OS"); match str::from_utf8(&os.stdout) { Ok("FreeBSD\n") => { let check_hardening = Command::new("sysctl") .arg("hardening.version") .output() .expect("Failed to check if is hardened"); match str::from_utf8(&check_hardening.stderr) { Ok("0\n") => Type::HardenedBSD, Ok(_) => Type::FreeBSD, Err(_) => Type::FreeBSD, } } Ok("MidnightBSD\n") => Type::MidnightBSD, Ok(_) => Type::Unknown, Err(_) => 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.7.0/src/illumos/mod.rs000064400000000000000000000027401046102023000146720ustar 00000000000000use std::process::Command; use std::str; use log::{error, trace}; use crate::{bitness, uname::uname, Info, Type, Version}; pub fn current_platform() -> Info { trace!("illumos::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 { Command::new("uname") .arg("-v") .output() .map_err(|e| { error!("Failed to invoke 'uname': {:?}", e); }) .ok() .and_then(|out| { if out.status.success() { Some(String::from_utf8_lossy(&out.stdout).trim_end().to_owned()) } else { log::error!("'uname' invocation error: {:?}", out); None } }) } fn get_os() -> Type { let os = Command::new("uname") .arg("-o") .output() .expect("Failed to get OS"); match str::from_utf8(&os.stdout) { Ok("illumos\n") => Type::Illumos, Ok(_) => Type::Unknown, Err(_) => 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.7.0/src/info.rs000064400000000000000000000226111046102023000133610ustar 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::Redox, Type::Alpaquita, Type::Alpine, Type::Amazon, Type::Android, Type::Arch, Type::Artix, Type::CentOS, Type::Debian, Type::Emscripten, Type::EndeavourOS, Type::Fedora, Type::Gentoo, Type::Linux, Type::Macos, Type::Manjaro, Type::Mariner, Type::NixOS, Type::OpenCloudOS, Type::openEuler, Type::openSUSE, Type::OracleLinux, Type::Pop, Type::Redhat, Type::RedHatEnterprise, Type::Redox, Type::Solus, Type::SUSE, Type::Ubuntu, 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::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.7.0/src/lib.rs000064400000000000000000000045771046102023000132070ustar 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 = "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 = "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 = "redox")] #[path = "redox/mod.rs"] mod imp; #[cfg(windows)] #[path = "windows/mod.rs"] mod imp; #[cfg(not(any( target_os = "android", target_os = "dragonfly", target_os = "emscripten", target_os = "freebsd", target_os = "illumos", target_os = "linux", target_os = "macos", target_os = "netbsd", target_os = "openbsd", 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" ))] mod architecture; mod bitness; mod info; #[cfg(not(windows))] mod matcher; mod os_type; #[cfg(any( target_os = "dragonfly", target_os = "freebsd", target_os = "illumos", target_os = "netbsd", target_os = "openbsd" ))] 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.7.0/src/linux/file_release.rs000064400000000000000000000443461046102023000162150ustar 00000000000000// spell-checker:ignore sles 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 { 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" => Alma "alpaquita" => Some(Type::Alpaquita), "alpine" => Some(Type::Alpine), "amzn" => Some(Type::Amazon), //"antergos" => Antergos //"aosc" => AOSC "arch" => Some(Type::Arch), "archarm" => Some(Type::Arch), "artix" => Some(Type::Artix), "centos" => Some(Type::CentOS), //"clear-linux-os" => ClearLinuxOS //"clearos" => ClearOS //"coreos" //"cumulus-linux" => Cumulus "debian" => Some(Type::Debian), //"devuan" => Devuan //"elementary" => Elementary "fedora" => Some(Type::Fedora), //"gentoo" => Gentoo //"ios_xr" => ios_xr //"kali" => Kali //"mageia" => Mageia //"manjaro" => Manjaro "linuxmint" => Some(Type::Mint), "mariner" => Some(Type::Mariner), //"nexus" => Nexus "nixos" => Some(Type::NixOS), "opencloudos" => Some(Type::OpenCloudOS), "openEuler" => Some(Type::openEuler), "ol" => Some(Type::OracleLinux), "opensuse" => Some(Type::openSUSE), "opensuse-leap" => Some(Type::openSUSE), //"rancheros" => RancherOS //"raspbian" => Raspbian // note XBian also uses "raspbian" "rhel" => Some(Type::RedHatEnterprise), //"rocky" => Rocky //"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), //"virtuozzo" => Virtuozzo //"void" => Void //"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 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 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 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 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 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 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 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 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 release_info_debug() { dbg!("{:?}", &DISTRIBUTIONS[0]); } } os_info-3.7.0/src/linux/lsb_release.rs000064400000000000000000000402641046102023000160510ustar 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::Custom(v.to_owned()), None => Version::Unknown, }; let os_type = match release.distribution.as_ref().map(String::as_ref) { Some("Alpaquita") => Type::Alpaquita, Some("Amazon") | Some("AmazonAMI") => Type::Amazon, Some("Arch") => Type::Arch, Some("Artix") => Type::Artix, Some("CentOS") => Type::CentOS, Some("Debian") => Type::Debian, Some("EndeavourOS") => Type::EndeavourOS, Some("Fedora") | Some("Fedora Linux") => Type::Fedora, Some("Garuda") => Type::Garuda, Some("Gentoo") => Type::Gentoo, Some("Linuxmint") => Type::Mint, Some("MaboxLinux") => Type::Mabox, Some("ManjaroLinux") => Type::Manjaro, Some("Mariner") => Type::Mariner, Some("NixOS") => Type::NixOS, 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, _ => Type::Linux, }; 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 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 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 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 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 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); } 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 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 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" } // 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 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\ " } } os_info-3.7.0/src/linux/mod.rs000064400000000000000000000027341046102023000143500ustar 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::Alpaquita | Type::Alpine | Type::Amazon | Type::Arch | Type::Artix | Type::CentOS | Type::Debian | Type::EndeavourOS | Type::Fedora | Type::Garuda | Type::Gentoo | Type::Linux | Type::Mabox | Type::Manjaro | Type::Mariner | Type::NixOS | Type::OpenCloudOS | Type::openEuler | Type::openSUSE | Type::OracleLinux | Type::Pop | Type::Raspbian | Type::Redhat | Type::RedHatEnterprise | Type::Solus | Type::SUSE | Type::Ubuntu | Type::Mint => (), os_type => { panic!("Unexpected OS type: {}", os_type); } } } } os_info-3.7.0/src/linux/tests/Alpaquita/etc/os-release000064400000000000000000000003461046102023000210400ustar 00000000000000NAME="BellSoft Alpaquita Linux Stream" ID=alpaquita ID_LIKE=alpine VERSION_ID=23 PRETTY_NAME="BellSoft Alpaquita Linux Stream 23 (musl)" HOME_URL="https://bell-sw.com/" BUG_REPORT_URL="https://bell-sw.com/support/" LIBC_TYPE=musl os_info-3.7.0/src/linux/tests/Alpine/etc/alpine-release000064400000000000000000000000061046102023000211470ustar 00000000000000A.B.C os_info-3.7.0/src/linux/tests/Alpine_3_12/etc/os-release000064400000000000000000000002441046102023000210500ustar 00000000000000NAME="Alpine Linux" ID=alpine VERSION_ID=3.12.0 PRETTY_NAME="Alpine Linux v3.12" HOME_URL="https://alpinelinux.org/" BUG_REPORT_URL="https://bugs.alpinelinux.org/" os_info-3.7.0/src/linux/tests/Amazon_1/etc/os-release000064400000000000000000000003651046102023000205650ustar 00000000000000NAME="Amazon Linux AMI" VERSION="2018.03" ID="amzn" ID_LIKE="rhel fedora" VERSION_ID="2018.03" PRETTY_NAME="Amazon Linux AMI 2018.03" ANSI_COLOR="0;33" CPE_NAME="cpe:/o:amazon:linux:2018.03:ga" HOME_URL="http://aws.amazon.com/amazon-linux-ami/" os_info-3.7.0/src/linux/tests/Amazon_2/etc/os-release000064400000000000000000000003241046102023000205610ustar 00000000000000NAME="Amazon Linux" VERSION="2" ID="amzn" ID_LIKE="centos rhel fedora" VERSION_ID="2" PRETTY_NAME="Amazon Linux 2" ANSI_COLOR="0;33" CPE_NAME="cpe:2.3:o:amazon:amazon_linux:2" HOME_URL="https://amazonlinux.com/" os_info-3.7.0/src/linux/tests/Arch/etc/os-release000064400000000000000000000005431046102023000177730ustar 00000000000000NAME="Arch Linux" PRETTY_NAME="Arch Linux" ID=arch BUILD_ID=rolling ANSI_COLOR="38;2;23;147;209" HOME_URL="https://archlinux.org/" DOCUMENTATION_URL="https://wiki.archlinux.org/" SUPPORT_URL="https://bbs.archlinux.org/" BUG_REPORT_URL="https://bugs.archlinux.org/" PRIVACY_POLICY_URL="https://terms.archlinux.org/docs/privacy-policy/" LOGO=archlinux-logo os_info-3.7.0/src/linux/tests/ArchARM/etc/os-release000064400000000000000000000005231046102023000203310ustar 00000000000000NAME="Arch Linux ARM" PRETTY_NAME="Arch Linux ARM" ID=archarm ID_LIKE=arch BUILD_ID=rolling ANSI_COLOR="38;2;23;147;209" HOME_URL="https://archlinuxarm.org/" DOCUMENTATION_URL="https://archlinuxarm.org/wiki" SUPPORT_URL="https://archlinuxarm.org/forum" BUG_REPORT_URL="https://github.com/archlinuxarm/PKGBUILDs/issues" LOGO=archlinux-logo os_info-3.7.0/src/linux/tests/Artix/etc/os-release000064400000000000000000000004301046102023000202000ustar 00000000000000NAME="Artix Linux" PRETTY_NAME="Artix Linux" ID=artix BUILD_ID=rolling ANSI_COLOR="38;2;23;147;209" HOME_URL="https://artixlinux.org/" DOCUMENTATION_URL="https://wiki.artixlinux.org/" SUPPORT_URL="https://forum.artixlinux.org/" BUG_REPORT_URL="https://gitea.artixlinux.org/artix" os_info-3.7.0/src/linux/tests/CentOS/etc/centos-release000064400000000000000000000000301046102023000211120ustar 00000000000000Centos Linux release XX os_info-3.7.0/src/linux/tests/CentOS_7/etc/os-release000064400000000000000000000006101046102023000204720ustar 00000000000000NAME="CentOS Linux" VERSION="7 (Core)" ID="centos" ID_LIKE="rhel fedora" VERSION_ID="7" PRETTY_NAME="CentOS Linux 7 (Core)" ANSI_COLOR="0;31" CPE_NAME="cpe:/o:centos:centos:7" HOME_URL="https://www.centos.org/" BUG_REPORT_URL="https://bugs.centos.org/" CENTOS_MANTISBT_PROJECT="CentOS-7" CENTOS_MANTISBT_PROJECT_VERSION="7" REDHAT_SUPPORT_PRODUCT="centos" REDHAT_SUPPORT_PRODUCT_VERSION="7" os_info-3.7.0/src/linux/tests/CentOS_Stream/etc/os-release000064400000000000000000000005571046102023000215710ustar 00000000000000NAME="CentOS Stream" VERSION="8" ID="centos" ID_LIKE="rhel fedora" VERSION_ID="8" PLATFORM_ID="platform:el8" PRETTY_NAME="CentOS Stream 8" ANSI_COLOR="0;31" CPE_NAME="cpe:/o:centos:centos:8" HOME_URL="https://centos.org/" BUG_REPORT_URL="https://bugzilla.redhat.com/" REDHAT_SUPPORT_PRODUCT="Red Hat Enterprise Linux 8" REDHAT_SUPPORT_PRODUCT_VERSION="CentOS Stream" os_info-3.7.0/src/linux/tests/CentOS_Unknown/etc/centos-release000064400000000000000000000000151046102023000226340ustar 00000000000000Centos Linux os_info-3.7.0/src/linux/tests/Debian_11/etc/os-release000064400000000000000000000004131046102023000205750ustar 00000000000000PRETTY_NAME="Debian GNU/Linux 11 (bullseye)" NAME="Debian GNU/Linux" VERSION_ID="11" VERSION="11 (bullseye)" VERSION_CODENAME=bullseye ID=debian HOME_URL="https://www.debian.org/" SUPPORT_URL="https://www.debian.org/support" BUG_REPORT_URL="https://bugs.debian.org/" os_info-3.7.0/src/linux/tests/Fedora/etc/fedora-release000064400000000000000000000000371046102023000211330ustar 00000000000000Fedora release 26 (Twenty Six) os_info-3.7.0/src/linux/tests/Fedora_32/etc/os-release000064400000000000000000000013421046102023000206200ustar 00000000000000NAME=Fedora VERSION="32 (Cloud Edition)" ID=fedora VERSION_ID=32 VERSION_CODENAME="" PLATFORM_ID="platform:f32" PRETTY_NAME="Fedora 32 (Cloud Edition)" ANSI_COLOR="0;34" LOGO=fedora-logo-icon CPE_NAME="cpe:/o:fedoraproject:fedora:32" HOME_URL="https://fedoraproject.org/" DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora/f32/system-administrators-guide/" SUPPORT_URL="https://fedoraproject.org/wiki/Communicating_and_getting_help" BUG_REPORT_URL="https://bugzilla.redhat.com/" REDHAT_BUGZILLA_PRODUCT="Fedora" REDHAT_BUGZILLA_PRODUCT_VERSION=32 REDHAT_SUPPORT_PRODUCT="Fedora" REDHAT_SUPPORT_PRODUCT_VERSION=32 PRIVACY_POLICY_URL="https://fedoraproject.org/wiki/Legal:PrivacyPolicy" VARIANT="Cloud Edition" VARIANT_ID=cloud os_info-3.7.0/src/linux/tests/Fedora_35/etc/os-release000064400000000000000000000013661046102023000206310ustar 00000000000000NAME="Fedora Linux" VERSION="35 (Workstation Edition)" ID=fedora VERSION_ID=35 VERSION_CODENAME="" PLATFORM_ID="platform:f35" PRETTY_NAME="Fedora Linux 35 (Workstation Edition)" ANSI_COLOR="0;38;2;60;110;180" LOGO=fedora-logo-icon CPE_NAME="cpe:/o:fedoraproject:fedora:35" HOME_URL="https://fedoraproject.org/" DOCUMENTATION_URL="https://docs.fedoraproject.org/en-US/fedora/f35/system-administrators-guide/" SUPPORT_URL="https://ask.fedoraproject.org/" BUG_REPORT_URL="https://bugzilla.redhat.com/" REDHAT_BUGZILLA_PRODUCT="Fedora" REDHAT_BUGZILLA_PRODUCT_VERSION=35 REDHAT_SUPPORT_PRODUCT="Fedora" REDHAT_SUPPORT_PRODUCT_VERSION=35 PRIVACY_POLICY_URL="https://fedoraproject.org/wiki/Legal:PrivacyPolicy" VARIANT="Workstation Edition" VARIANT_ID=workstation os_info-3.7.0/src/linux/tests/Fedora_Unknown/etc/fedora-release000064400000000000000000000000211046102023000226430ustar 00000000000000Fedora (Foo Bar) os_info-3.7.0/src/linux/tests/Mariner/etc/mariner-release000064400000000000000000000000661046102023000215270ustar 00000000000000CBL-Mariner 2.0.20220210 MARINER_BUILD_NUMBER=0d11c66 os_info-3.7.0/src/linux/tests/Mariner_Unknown/etc/mariner-release000064400000000000000000000000141046102023000232370ustar 00000000000000CBL-Mariner os_info-3.7.0/src/linux/tests/Mint/etc/os-release000064400000000000000000000005621046102023000200260ustar 00000000000000NAME="Linux Mint" VERSION="20 (Ulyana)" ID=linuxmint ID_LIKE=ubuntu PRETTY_NAME="Linux Mint 20" VERSION_ID="20" HOME_URL="https://www.linuxmint.com/" SUPPORT_URL="https://forums.linuxmint.com/" BUG_REPORT_URL="http://linuxmint-troubleshooting-guide.readthedocs.io/en/latest/" PRIVACY_POLICY_URL="https://www.linuxmint.com/" VERSION_CODENAME=ulyana UBUNTU_CODENAME=focal os_info-3.7.0/src/linux/tests/NixOS/etc/os-release000064400000000000000000000005561046102023000201220ustar 00000000000000NAME=NixOS ID=nixos VERSION="21.05pre275822.916ee862e87 (Okapi)" VERSION_CODENAME=okapi VERSION_ID="21.05pre275822.916ee862e87" PRETTY_NAME="NixOS 21.05 (Okapi)" LOGO="nix-snowflake" HOME_URL="https://nixos.org/" DOCUMENTATION_URL="https://nixos.org/learn.html" SUPPORT_URL="https://nixos.org/community.html" BUG_REPORT_URL="https://github.com/NixOS/nixpkgs/issues" os_info-3.7.0/src/linux/tests/OpenCloudOS/etc/os-release000064400000000000000000000003261046102023000212470ustar 00000000000000NAME="OpenCloudOS" VERSION="8.6" ID="opencloudos" ID_LIKE="rhel fedora centos" VERSION_ID="8.6" PLATFORM_ID="platform:oc8" PRETTY_NAME="OpenCloudOS 8.6" ANSI_COLOR="0;31" CPE_NAME="cpe:/o:opencloudos:opencloudos:8"os_info-3.7.0/src/linux/tests/OracleLinux/etc/os-release000064400000000000000000000007371046102023000213500ustar 00000000000000NAME="Oracle Linux Server" VERSION="8.1" ID="ol" ID_LIKE="fedora" VARIANT="Server" VARIANT_ID="server" VERSION_ID="8.1" PLATFORM_ID="platform:el8" PRETTY_NAME="Oracle Linux Server 8.1" ANSI_COLOR="0;31" CPE_NAME="cpe:/o:oracle:linux:8:1:server" HOME_URL="https://linux.oracle.com/" BUG_REPORT_URL="https://bugzilla.oracle.com/" ORACLE_BUGZILLA_PRODUCT="Oracle Linux 8" ORACLE_BUGZILLA_PRODUCT_VERSION=8.1 ORACLE_SUPPORT_PRODUCT="Oracle Linux" ORACLE_SUPPORT_PRODUCT_VERSION=8.1 os_info-3.7.0/src/linux/tests/OracleLinux/etc/redhat-release000064400000000000000000000000271046102023000221660ustar 00000000000000Redhat Linux release XXos_info-3.7.0/src/linux/tests/RedHatEnterprise/etc/redhat-release000064400000000000000000000000301046102023000231430ustar 00000000000000Redhat Linux release XX os_info-3.7.0/src/linux/tests/RedHatEnterprise_7/etc/os-release000064400000000000000000000010241046102023000225470ustar 00000000000000NAME="Red Hat Enterprise Linux Server" VERSION="7.9 (Maipo)" ID="rhel" ID_LIKE="fedora" VARIANT="Server" VARIANT_ID="server" VERSION_ID="7.9" PRETTY_NAME="Red Hat Enterprise Linux Server 7.9 (Maipo)" ANSI_COLOR="0;31" CPE_NAME="cpe:/o:redhat:enterprise_linux:7.9:GA:server" HOME_URL="https://www.redhat.com/" BUG_REPORT_URL="https://bugzilla.redhat.com/" REDHAT_BUGZILLA_PRODUCT="Red Hat Enterprise Linux 7" REDHAT_BUGZILLA_PRODUCT_VERSION=7.9 REDHAT_SUPPORT_PRODUCT="Red Hat Enterprise Linux" REDHAT_SUPPORT_PRODUCT_VERSION="7.9" os_info-3.7.0/src/linux/tests/RedHatEnterprise_8/etc/os-release000064400000000000000000000007651046102023000225630ustar 00000000000000NAME="Red Hat Enterprise Linux" VERSION="8.2 (Ootpa)" ID="rhel" ID_LIKE="fedora" VERSION_ID="8.2" PLATFORM_ID="platform:el8" PRETTY_NAME="Red Hat Enterprise Linux 8.2 (Ootpa)" ANSI_COLOR="0;31" CPE_NAME="cpe:/o:redhat:enterprise_linux:8.2:GA" HOME_URL="https://www.redhat.com/" BUG_REPORT_URL="https://bugzilla.redhat.com/" REDHAT_BUGZILLA_PRODUCT="Red Hat Enterprise Linux 8" REDHAT_BUGZILLA_PRODUCT_VERSION=8.2 REDHAT_SUPPORT_PRODUCT="Red Hat Enterprise Linux" REDHAT_SUPPORT_PRODUCT_VERSION="8.2" os_info-3.7.0/src/linux/tests/RedHatEnterprise_Unknown/etc/redhat-release000064400000000000000000000000001046102023000246570ustar 00000000000000os_info-3.7.0/src/linux/tests/SUSE_12/etc/os-release000064400000000000000000000002401046102023000201710ustar 00000000000000NAME="SLES" VERSION="12-SP5" VERSION_ID="12.5" PRETTY_NAME="SUSE Linux Enterprise Server 12 SP5" ID="sles" ANSI_COLOR="0;32" CPE_NAME="cpe:/o:suse:sles:12:sp5" os_info-3.7.0/src/linux/tests/SUSE_15/etc/os-release000064400000000000000000000002571046102023000202040ustar 00000000000000NAME="SLES" VERSION="15-SP2" VERSION_ID="15.2" PRETTY_NAME="SUSE Linux Enterprise Server 15 SP2" ID="sles" ID_LIKE="suse" ANSI_COLOR="0;32" CPE_NAME="cpe:/o:suse:sles:15:sp2" os_info-3.7.0/src/linux/tests/Ubuntu/etc/os-release000064400000000000000000000005721046102023000204020ustar 00000000000000NAME="Ubuntu" VERSION="18.10 (Cosmic Cuttlefish)" ID=ubuntu ID_LIKE=debian PRETTY_NAME="Ubuntu 18.10" VERSION_ID="18.10" HOME_URL="https://www.ubuntu.com/" SUPPORT_URL="https://help.ubuntu.com/" BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/" PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy" VERSION_CODENAME=cosmic UBUNTU_CODENAME=cosmic os_info-3.7.0/src/linux/tests/none_invalid_os_release/etc/os-release000064400000000000000000000001021046102023000237530ustar 00000000000000ID="Lorem ipsum dolor sit amet" VERSION_ID="It's all greek to me" os_info-3.7.0/src/linux/tests/openEuler/etc/os-release000064400000000000000000000001721046102023000210520ustar 00000000000000NAME="openEuler" VERSION="22.03 LTS" ID="openEuler" VERSION_ID="22.03" PRETTY_NAME="openEuler 22.03 LTS" ANSI_COLOR="0;31"os_info-3.7.0/src/macos/mod.rs000064400000000000000000000051051046102023000143060ustar 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 info = Info { os_type: Type::Macos, version: version(), bitness: bitness::get(), architecture: architecture::get(), ..Default::default() }; trace!("Returning {:?}", info); info } fn version() -> Version { match product_version() { None => Version::Unknown, Some(val) => Version::from_string(val), } } fn product_version() -> Option { 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.7.0/src/matcher.rs000064400000000000000000000103651046102023000140540ustar 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.7.0/src/netbsd/mod.rs000064400000000000000000000014001046102023000144550ustar 00000000000000use std::process::Command; use log::{error, trace}; use crate::{architecture, bitness, uname::uname, Info, Type, Version}; pub fn current_platform() -> Info { trace!("netbsd::current_platform is called"); let version = uname() .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.7.0/src/openbsd/mod.rs000064400000000000000000000014031046102023000146330ustar 00000000000000use std::process::Command; use log::{error, trace}; use crate::{architecture, bitness, uname::uname, Info, Type, Version}; pub fn current_platform() -> Info { trace!("openbsd::current_platform is called"); let version = uname() .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.7.0/src/os_type.rs000064400000000000000000000146561046102023000141220ustar 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 { /// Alpaquita Linux (). Alpaquita, /// Alpine Linux (). Alpine, /// Amazon Linux AMI (). Amazon, /// Android (). Android, /// Arch Linux (). Arch, /// Artix Linux (). Artix, /// CentOS (). CentOS, /// Debian (). Debian, /// DragonFly BSD (). DragonFly, /// 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, /// 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, /// OpenBSD (). OpenBSD, /// OpenCloudOS (). OpenCloudOS, /// openEuler (). openEuler, /// openSUSE (). openSUSE, /// Oracle Linux (). OracleLinux, /// Pop!_OS () Pop, /// Raspberry Pi OS (). Raspbian, /// Red Hat Linux (). Redhat, /// Red Hat Enterprise Linux (). RedHatEnterprise, /// Redox (). Redox, /// Solus (). Solus, /// SUSE Linux Enterprise Server (). SUSE, /// Ubuntu (). Ubuntu, /// 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::Amazon => write!(f, "Amazon Linux AMI"), Type::Arch => write!(f, "Arch Linux"), Type::Artix => write!(f, "Artix Linux"), Type::DragonFly => write!(f, "DragonFly BSD"), Type::Garuda => write!(f, "Garuda Linux"), Type::Gentoo => write!(f, "Gentoo Linux"), Type::Illumos => write!(f, "illumos"), Type::Macos => write!(f, "Mac OS"), Type::MidnightBSD => write!(f, "Midnight BSD"), Type::Mint => write!(f, "Linux Mint"), 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::SUSE => write!(f, "SUSE Linux Enterprise Server"), _ => 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::Alpaquita, "Alpaquita Linux"), (Type::Alpine, "Alpine Linux"), (Type::Amazon, "Amazon Linux AMI"), (Type::Android, "Android"), (Type::Arch, "Arch Linux"), (Type::Artix, "Artix Linux"), (Type::CentOS, "CentOS"), (Type::Debian, "Debian"), (Type::DragonFly, "DragonFly BSD"), (Type::Emscripten, "Emscripten"), (Type::EndeavourOS, "EndeavourOS"), (Type::Fedora, "Fedora"), (Type::Garuda, "Garuda Linux"), (Type::Gentoo, "Gentoo Linux"), (Type::FreeBSD, "FreeBSD"), (Type::Linux, "Linux"), (Type::Macos, "Mac OS"), (Type::Manjaro, "Manjaro"), (Type::Mint, "Linux Mint"), (Type::NetBSD, "NetBSD"), (Type::NixOS, "NixOS"), (Type::OpenBSD, "OpenBSD"), (Type::openSUSE, "openSUSE"), (Type::OracleLinux, "OracleLinux"), (Type::Pop, "Pop!_OS"), (Type::Raspbian, "Raspberry Pi OS"), (Type::Redhat, "Red Hat Linux"), (Type::RedHatEnterprise, "Red Hat Enterprise Linux"), (Type::Redox, "Redox"), (Type::Solus, "Solus"), (Type::SUSE, "SUSE Linux Enterprise Server"), (Type::Ubuntu, "Ubuntu"), (Type::Unknown, "Unknown"), (Type::Windows, "Windows"), ]; for (t, expected) in &data { assert_eq!(&t.to_string(), expected); } } } os_info-3.7.0/src/redox/mod.rs000064400000000000000000000023051046102023000143240ustar 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.7.0/src/uname.rs000064400000000000000000000013011046102023000135240ustar 00000000000000use std::process::Command; use log::error; pub fn uname() -> Option { Command::new("uname") .arg("-r") .output() .map_err(|e| { error!("Failed to invoke 'uname': {:?}", e); }) .ok() .and_then(|out| { if out.status.success() { Some(String::from_utf8_lossy(&out.stdout).trim_end().to_owned()) } else { log::error!("'uname' invocation error: {:?}", out); None } }) } #[cfg(test)] mod tests { use super::*; #[test] fn uname_nonempty() { let val = uname().expect("uname failed"); assert!(!val.is_empty()); } } os_info-3.7.0/src/unknown/mod.rs000064400000000000000000000005621046102023000147050ustar 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.7.0/src/version.rs000064400000000000000000000103111046102023000141050ustar 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.7.0/src/windows/mod.rs000064400000000000000000000007611046102023000147010ustar 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.7.0/src/windows/winapi.rs000064400000000000000000000264751046102023000154230ustar 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, os::windows::ffi::{OsStrExt, OsStringExt}, ptr, }; use winapi::{ shared::{ minwindef::{DWORD, FARPROC, LPBYTE}, ntdef::{LPCSTR, NTSTATUS}, ntstatus::STATUS_SUCCESS, winerror::ERROR_SUCCESS, }, um::{ libloaderapi::{GetModuleHandleA, GetProcAddress}, sysinfoapi::{GetSystemInfo, SYSTEM_INFO}, winnt::{ KEY_READ, PROCESSOR_ARCHITECTURE_AMD64, REG_SZ, VER_NT_WORKSTATION, VER_SUITE_WH_SERVER, WCHAR, }, winreg::{RegOpenKeyExW, RegQueryValueExW, HKEY_LOCAL_MACHINE, LSTATUS}, winuser::{GetSystemMetrics, SM_SERVERR2}, }, }; use crate::{Bitness, Info, Type, Version}; #[cfg(target_arch = "x86")] type OSVERSIONINFOEX = winapi::um::winnt::OSVERSIONINFOEXA; #[cfg(not(target_arch = "x86"))] type OSVERSIONINFOEX = winapi::um::winnt::OSVERSIONINFOEXW; pub fn get() -> Info { let (version, edition) = version(); Info { os_type: Type::Windows, version, edition, bitness: bitness(), ..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)), ), } } #[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 winapi::{ shared::{ minwindef::{BOOL, FALSE, PBOOL}, ntdef::HANDLE, }, um::processthreadsapi::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, PBOOL) -> 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 DWORD; if unsafe { rtl_get_version(&mut info) } == STATUS_SUCCESS { Some(info) } else { None } } fn product_name(info: &OSVERSIONINFOEX) -> Option { const REG_SUCCESS: LSTATUS = ERROR_SUCCESS as LSTATUS; let sub_key = to_wide("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"); let mut key = ptr::null_mut(); if unsafe { RegOpenKeyExW(HKEY_LOCAL_MACHINE, sub_key.as_ptr(), 0, KEY_READ, &mut key) } != REG_SUCCESS || key.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: DWORD = 0; let mut data_size: DWORD = 0; if unsafe { RegQueryValueExW( key, name.as_ptr(), ptr::null_mut(), &mut data_type, ptr::null_mut(), &mut data_size, ) } != REG_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, name.as_ptr(), ptr::null_mut(), ptr::null_mut(), data.as_mut_ptr() as LPBYTE, &mut data_size, ) } != REG_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, ) { // 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 && unsafe { info.u.s().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() as LPCSTR) }; if handle.is_null() { log::error!( "GetModuleHandleA({}) failed", String::from_utf8_lossy(module) ); return None; } unsafe { Some(GetProcAddress(handle, proc.as_ptr() as LPCSTR)) } } #[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; 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_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); } } } os_info-3.7.0/tests/basic.rs000064400000000000000000000000611046102023000140550ustar 00000000000000#[test] fn get() { let _ = os_info::get(); } os_info-3.7.0/tests/md_doc.rs000064400000000000000000000001101046102023000142140ustar 00000000000000// spell-checker:ignore doctest doc_comment::doctest!("../README.md");