platform-info-0.2.0/.cargo/config000064400000000000000000000001020072674642500147620ustar 00000000000000[target.x86_64-unknown-redox] linker = "x86_64-unknown-redox-gcc" platform-info-0.2.0/.cargo_vcs_info.json0000644000000001120000000000100136020ustar { "git": { "sha1": "b072d823a9cea317e951d812005d400b9dc6ad38" } } platform-info-0.2.0/.gitattributes000064400000000000000000000000140072674642500153160ustar 00000000000000* text=auto platform-info-0.2.0/.github/dependabot.yml000064400000000000000000000002210072674642500166130ustar 00000000000000version: 2 updates: - package-ecosystem: cargo directory: "/" schedule: interval: daily time: "07:00" open-pull-requests-limit: 10 platform-info-0.2.0/.github/workflows/ci.yml000064400000000000000000000104760072674642500171530ustar 00000000000000on: [push, pull_request] name: Basic CI jobs: check: name: cargo check runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, macOS-latest, windows-latest] steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - uses: actions-rs/cargo@v1 with: command: check test: name: cargo test runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, macOS-latest, windows-latest] steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - uses: actions-rs/cargo@v1 with: command: test fmt: name: cargo fmt --all -- --check runs-on: ubuntu-latest steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - run: rustup component add rustfmt - uses: actions-rs/cargo@v1 with: command: fmt args: --all -- --check clippy: name: cargo clippy -- -D warnings runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, macOS-latest, windows-latest] steps: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: stable override: true - run: rustup component add clippy - uses: actions-rs/cargo@v1 with: command: clippy args: -- -D warnings grcov: name: Code coverage runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, macOS-latest, windows-latest] toolchain: - nightly cargo_flags: - "--all-features" steps: - name: Initialize workflow variables id: vars shell: bash run: | # * CODECOV_FLAGS CODECOV_FLAGS=$( echo "${{ matrix.os }}" | sed 's/[^[:alnum:]]/_/g' ) - name: Checkout source code uses: actions/checkout@v2 - name: Install Rust uses: actions-rs/toolchain@v1 with: profile: minimal toolchain: ${{ matrix.toolchain }} override: true - name: Install grcov uses: actions-rs/install@v0.1 with: crate: grcov version: latest use-tool-cache: true - name: Test uses: actions-rs/cargo@v1 with: command: test args: --all --no-fail-fast ${{ matrix.cargo_flags }} env: CARGO_INCREMENTAL: "0" RUSTFLAGS: '-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort -Cdebug-assertions=off' RUSTDOCFLAGS: '-Zprofile -Ccodegen-units=1 -Copt-level=0 -Clink-dead-code -Coverflow-checks=off -Zpanic_abort_tests -Cpanic=abort -Cdebug-assertions=off' - name: Generate coverage data id: grcov shell: bash # uses: actions-rs/grcov@v0.1 run: | ## Generate coverage data COVERAGE_REPORT_DIR="target/debug" COVERAGE_REPORT_FILE="${COVERAGE_REPORT_DIR}/lcov.info" mkdir -p "${COVERAGE_REPORT_DIR}" # display coverage files grcov . --output-type files --ignore build.rs --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()" | sort --unique # generate coverage report grcov . --output-type lcov --output-path "${COVERAGE_REPORT_FILE}" --branch --ignore build.rs --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()" echo ::set-output name=report::${COVERAGE_REPORT_FILE} - name: Upload coverage results (to Codecov.io) uses: codecov/codecov-action@v1 # if: steps.vars.outputs.HAS_CODECOV_TOKEN with: # token: ${{ secrets.CODECOV_TOKEN }} file: ${{ steps.coverage.outputs.report }} ## flags: IntegrationTests, UnitTests, ${{ steps.vars.outputs.CODECOV_FLAGS }} flags: ${{ steps.vars.outputs.CODECOV_FLAGS }} name: codecov-umbrella fail_ci_if_error: false platform-info-0.2.0/.github/workflows/main.yml000064400000000000000000000000250072674642500174710ustar 00000000000000# Empty to enable it platform-info-0.2.0/.gitignore000064400000000000000000000000500072674642500144130ustar 00000000000000/.vscode/ /target **/*.rs.bk Cargo.lock platform-info-0.2.0/.travis/redox-toolchain.sh000064400000000000000000000004260072674642500174530ustar 00000000000000#!/bin/bash rustup target add x86_64-unknown-redox sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys AA12E97F0881517F sudo add-apt-repository 'deb https://static.redox-os.org/toolchain/apt /' sudo apt-get update -qq sudo apt-get install -y x86-64-unknown-redox-gcc platform-info-0.2.0/.travis.yml000064400000000000000000000115240072674642500145440ustar 00000000000000# Based on the "trust" template v0.1.2 # https://github.com/japaric/trust/tree/v0.1.2 dist: trusty language: rust services: docker sudo: required addons: apt: packages: - libssl-dev after_success: | if [ "$TARGET" = x86_64-unknown-linux-gnu -a "$TRAVIS_RUST_VERSION" = stable ]; then bash <(curl https://raw.githubusercontent.com/xd009642/tarpaulin/master/travis-install.sh) cargo tarpaulin --out Xml bash <(curl -s https://codecov.io/bash) fi # TODO Rust builds on stable by default, this can be # overridden on a case by case basis down below. env: global: # TODO Update this to match the name of your project. - CRATE_NAME=platform-info matrix: # TODO These are all the build jobs. Adjust as necessary. Comment out what you # don't need include: # Android - env: TARGET=aarch64-linux-android DISABLE_TESTS=1 - env: TARGET=arm-linux-androideabi DISABLE_TESTS=1 - env: TARGET=armv7-linux-androideabi DISABLE_TESTS=1 - env: TARGET=i686-linux-android DISABLE_TESTS=1 - env: TARGET=x86_64-linux-android DISABLE_TESTS=1 # iOS - env: TARGET=aarch64-apple-ios DISABLE_TESTS=1 os: osx - env: TARGET=armv7-apple-ios DISABLE_TESTS=1 os: osx - env: TARGET=armv7s-apple-ios DISABLE_TESTS=1 os: osx - env: TARGET=i386-apple-ios DISABLE_TESTS=1 os: osx - env: TARGET=x86_64-apple-ios DISABLE_TESTS=1 os: osx # Linux - env: TARGET=aarch64-unknown-linux-gnu - env: TARGET=arm-unknown-linux-gnueabi - env: TARGET=armv7-unknown-linux-gnueabihf - env: TARGET=i686-unknown-linux-gnu - env: TARGET=i686-unknown-linux-musl - env: TARGET=mips-unknown-linux-gnu - env: TARGET=mips64-unknown-linux-gnuabi64 - env: TARGET=mips64el-unknown-linux-gnuabi64 - env: TARGET=mipsel-unknown-linux-gnu - env: TARGET=powerpc-unknown-linux-gnu - env: TARGET=powerpc64-unknown-linux-gnu - env: TARGET=powerpc64le-unknown-linux-gnu - env: TARGET=s390x-unknown-linux-gnu DISABLE_TESTS=1 - env: TARGET=x86_64-unknown-linux-gnu - env: TARGET=x86_64-unknown-linux-musl # OSX - env: TARGET=i686-apple-darwin os: osx - env: TARGET=x86_64-apple-darwin os: osx # *BSD - env: TARGET=i686-unknown-freebsd DISABLE_TESTS=1 - env: TARGET=x86_64-unknown-freebsd DISABLE_TESTS=1 - env: TARGET=x86_64-unknown-netbsd DISABLE_TESTS=1 # Windows - env: TARGET=x86_64-pc-windows-gnu # Redox - env: TARGET=x86_64-unknown-redox CC=x86_64-unknown-redox-gcc REDOX=1 DISABLE_TESTS=1 # Bare metal # These targets don't support std and as such are likely not suitable for # most crates. # - env: TARGET=thumbv6m-none-eabi # - env: TARGET=thumbv7em-none-eabi # - env: TARGET=thumbv7em-none-eabihf # - env: TARGET=thumbv7m-none-eabi # Testing other channels - env: TARGET=x86_64-unknown-linux-gnu rust: nightly - env: TARGET=x86_64-apple-darwin os: osx rust: nightly before_install: - set -e - rustup self update - if [ $REDOX ]; then bash .travis/redox-toolchain.sh; fi install: - sh ci/install.sh - source ~/.cargo/env || true script: - bash ci/script.sh after_script: set +e before_deploy: - sh ci/before_deploy.sh deploy: # TODO update `api_key.secure` # - Create a `public_repo` GitHub token. Go to: https://github.com/settings/tokens/new # - Encrypt it: `travis encrypt 0123456789012345678901234567890123456789 # - Paste the output down here api_key: secure: "MuEDd+Gr6R8QYtfW2hiA84iJN85x3mP1PXa3Cu21paX9rYAtOte6LdxHfl7J22qQa2t/+Mu7aI1JF5g0apCPrWHizjhcS626PFfJYh2YKJnR8vD9ExjhSAYep29VPcOQan3FzklleG9skX5dxwUweMCXXOycsBlnDr3NJsnLgIk0EkWhpveIt/AefODuSFefPasUkxo9XenjXqYzBqI3OdOBbWoGLBTL9DtZ95Zxoi00pHTsL8sTg6ucatxnL6RToIiUXI8+hr+DsHcr5eFX5OMml0i2+rEgh5ZHANJZII3TwJulMj5YwRLMvNz7R7RKZVr2eTxItG2sy44L9F/QSDQ4LQhLg5tQVmG1wzrAgbPJXoEoj8aeLVBMCfrqCXoGMkTfQwsLiVqnCrg3tzcBju3/vZ/fz0hmKSxFCTb+P+zt86dEXXDd+zTHmG7scvcEVLsALdKKDtH4F7pb0kj1Sam6CwSMKraJpF0I9bi/jDGVtmiB6sMs8RCK27dqzGgZWA0uI86iRedE2DmkJMHtIdbPix8897sHZUzuvgLnMcI2nEtIM6dyQGRRw0SYysO1KxfJDWerd6l2q/DuSLDXM6VC4rhjkQTuHtjtULtkg9ObhEW2BC6nwKho3Yj2MKiJalpN0+E0Rx5Ii1mG4Dhqd5azypc+m8nkDkIJaA385Ig=" file_glob: true file: $CRATE_NAME-$TRAVIS_TAG-$TARGET.* on: # TODO Here you can pick which targets will generate binary releases # In this example, there are some targets that are tested using the stable # and nightly channels. This condition makes sure there is only one release # for such targets and that's generated using the stable channel condition: $TRAVIS_RUST_VERSION = stable tags: true provider: releases skip_cleanup: true cache: cargo before_cache: # Travis can't cache files that are not readable by "others" - chmod -R a+r $HOME/.cargo branches: only: # release tags - /^v\d+\.\d+\.\d+.*$/ - master notifications: email: on_success: never platform-info-0.2.0/Cargo.toml0000644000000020640000000000100116100ustar # 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" name = "platform-info" version = "0.2.0" authors = ["Alex Lyon "] description = "A simple cross-platform interface to get info about a system" homepage = "https://github.com/uutils/platform-info" readme = "README.md" keywords = ["platform", "info", "system"] categories = ["os"] license = "MIT" repository = "https://github.com/uutils/platform-info" [dependencies.libc] version = "0.2" [dependencies.winapi] version = "0.3" features = ["libloaderapi", "processthreadsapi", "sysinfoapi", "winbase", "winver"] [badges.appveyor] repository = "uutils/platform-info" platform-info-0.2.0/Cargo.toml.orig000064400000000000000000000011310072674642500153130ustar 00000000000000[package] name = "platform-info" version = "0.2.0" authors = ["Alex Lyon "] edition = "2018" description = "A simple cross-platform interface to get info about a system" homepage = "https://github.com/uutils/platform-info" repository = "https://github.com/uutils/platform-info" readme = "README.md" keywords = ["platform", "info", "system"] categories = ["os"] license = "MIT" [badges] appveyor = { repository = "uutils/platform-info" } [dependencies] libc = "0.2" winapi = { version = "0.3", features = ["libloaderapi", "processthreadsapi", "sysinfoapi", "winbase", "winver"] } platform-info-0.2.0/LICENSE000064400000000000000000000020350072674642500134350ustar 00000000000000Copyright (c) Jordi Boggiano 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. platform-info-0.2.0/README.md000064400000000000000000000017760072674642500137220ustar 00000000000000platform-info ============= [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![Build status](https://ci.appveyor.com/api/projects/status/2wogy3wkenwreeq0/branch/master?svg=true)](https://ci.appveyor.com/project/Arcterus/platform-info/branch/master) [![CodeCov](https://codecov.io/gh/uutils/platform-info/branch/master/graph/badge.svg)](https://codecov.io/gh/uutils/platform-info) A simple cross-platform way to get information about the currently running system. Example ------- This simple example: ``` use platform_info::*; fn main() { let uname = PlatformInfo::new().unwrap(); println!("{}", uname.sysname()); println!("{}", uname.nodename()); println!("{}", uname.release()); println!("{}", uname.version()); println!("{}", uname.machine()); } ``` should return something like: ``` Linux hostname 5.10.0-8-amd64 #1 SMP Debian 5.10.46-4 (2021-08-03) x86_64 ``` License ------- `platform-info` is licensed under the MIT License - see the LICENSE file for details. platform-info-0.2.0/appveyor.yml000064400000000000000000000054150072674642500150250ustar 00000000000000# Based on the "trust" template v0.1.2 # https://github.com/japaric/trust/tree/v0.1.2 environment: global: # TODO This is the Rust channel that build jobs will use by default but can be # overridden on a case by case basis down below RUST_VERSION: stable # TODO Update this to match the name of your project. CRATE_NAME: platform-info # TODO These are all the build jobs. Adjust as necessary. Comment out what you # don't need matrix: # MinGW - TARGET: i686-pc-windows-gnu - TARGET: x86_64-pc-windows-gnu # MSVC - TARGET: i686-pc-windows-msvc - TARGET: x86_64-pc-windows-msvc # Testing other channels - TARGET: x86_64-pc-windows-gnu RUST_VERSION: nightly - TARGET: x86_64-pc-windows-msvc RUST_VERSION: nightly install: - ps: >- If ($Env:TARGET -eq 'x86_64-pc-windows-gnu') { $Env:PATH += ';C:\msys64\mingw64\bin' } ElseIf ($Env:TARGET -eq 'i686-pc-windows-gnu') { $Env:PATH += ';C:\msys64\mingw32\bin' } - curl -sSf -o rustup-init.exe https://win.rustup.rs/ - rustup-init.exe -y --default-host %TARGET% --default-toolchain %RUST_VERSION% - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin - rustup component add rustfmt-preview - rustc -Vv - cargo -V # TODO This is the "test phase", tweak it as you see fit test_script: # we don't run the "test phase" when doing deploys - if [%APPVEYOR_REPO_TAG%]==[false] ( cargo fmt --all && cargo build --target %TARGET% && cargo build --target %TARGET% --release && cargo test --target %TARGET% && cargo test --target %TARGET% --release ) before_deploy: # TODO Update this to build the artifacts that matter to you - cargo rustc --target %TARGET% --release -- -C lto - ps: ci\before_deploy.ps1 deploy: artifact: /.*\.zip/ # TODO update `auth_token.secure` # - Create a `public_repo` GitHub token. Go to: https://github.com/settings/tokens/new # - Encrypt it. Go to https://ci.appveyor.com/tools/encrypt # - Paste the output down here auth_token: secure: 8Nx9bhoOgCXhU0sqfXb/1kSbl9JpnGXHyvu6umBQLBQ2XKIjDEYzpKBkESEu7jUp description: '' on: # TODO Here you can pick which targets will generate binary releases # In this example, there are some targets that are tested using the stable # and nightly channels. This condition makes sure there is only one release # for such targets and that's generated using the stable channel RUST_VERSION: stable appveyor_repo_tag: true provider: GitHub cache: - C:\Users\appveyor\.cargo\registry - target branches: only: # Release tags - /^v\d+\.\d+\.\d+.*$/ - master notifications: - provider: Email on_build_success: false # Building is done in the test phase, so we disable Appveyor's build phase. build: false platform-info-0.2.0/src/lib.rs000064400000000000000000000033250072674642500143360ustar 00000000000000// This file is part of the uutils coreutils package. // // (c) Alex Lyon // // For the full copyright and license information, please view the LICENSE file // that was distributed with this source code. // /*! This crate provides the ability to retrieve various information specific to your current platform without having to use platform-specific methods to so. Currently, only information pertinent to a utility like [`uname`](https://github.com/uutils/coreutils/blob/master/src/uname/uname.rs) is provided; however, in the future, more functionality may become available. # Usage This crate is available on [crate.io](https://crates.io/crates/platform-info), so using it in your project is as simple as adding `platform-info` to your project's `Cargo.toml`, like so: ```toml [dependencies] platform-info = "0.1" ``` To see specific usage details, I recommend looking at the `uname` utility linked above as it makes use of every feature. */ pub use self::sys::*; use std::borrow::Cow; #[cfg(unix)] #[path = "unix.rs"] mod sys; #[cfg(windows)] #[path = "windows.rs"] mod sys; #[cfg(not(any(unix, windows)))] #[path = "unknown.rs"] mod sys; /// `Uname` is meant for types that can provide information relevant to `uname`. pub trait Uname { /// The name of this implementation of the operating system. fn sysname(&self) -> Cow; /// The node name (network node hostname) of this machine. fn nodename(&self) -> Cow; /// The current release level of the operating system. fn release(&self) -> Cow; /// The current version level of the current release. fn version(&self) -> Cow; /// The name of the current system's hardware. fn machine(&self) -> Cow; } platform-info-0.2.0/src/unix.rs000064400000000000000000000030600072674642500145470ustar 00000000000000// This file is part of the uutils coreutils package. // // (c) Jian Zeng // (c) Alex Lyon // // For the full copyright and license information, please view the LICENSE file // that was distributed with this source code. // extern crate libc; use self::libc::{uname, utsname}; use super::Uname; use std::borrow::Cow; use std::ffi::CStr; use std::io; use std::mem; macro_rules! cstr2cow { ($v:expr) => { unsafe { CStr::from_ptr($v.as_ref().as_ptr()).to_string_lossy() } }; } /// `PlatformInfo` handles retrieving information for the current platform (a Unix-like operating /// in this case). pub struct PlatformInfo { inner: utsname, } impl PlatformInfo { /// Creates a new instance of `PlatformInfo`. This function *should* never fail. pub fn new() -> io::Result { unsafe { #[allow(deprecated)] let mut uts: utsname = mem::uninitialized(); if uname(&mut uts) != -1 { Ok(Self { inner: uts }) } else { Err(io::Error::last_os_error()) } } } } impl Uname for PlatformInfo { fn sysname(&self) -> Cow { cstr2cow!(self.inner.sysname) } fn nodename(&self) -> Cow { cstr2cow!(self.inner.nodename) } fn release(&self) -> Cow { cstr2cow!(self.inner.release) } fn version(&self) -> Cow { cstr2cow!(self.inner.version) } fn machine(&self) -> Cow { cstr2cow!(self.inner.machine) } } platform-info-0.2.0/src/unknown.rs000064400000000000000000000022060072674642500152640ustar 00000000000000// This file is part of the uutils coreutils package. // // (c) Ingvar Stepanyan // // For the full copyright and license information, please view the LICENSE file // that was distributed with this source code. // use super::Uname; use std::borrow::Cow; pub struct PlatformInfo(()); impl PlatformInfo { pub fn new() -> std::io::Result { Ok(Self(())) } } impl Uname for PlatformInfo { fn sysname(&self) -> Cow { Cow::Borrowed("unknown") } fn nodename(&self) -> Cow { Cow::Borrowed("unknown") } fn release(&self) -> Cow { Cow::Borrowed("unknown") } fn version(&self) -> Cow { Cow::Borrowed("unknown") } fn machine(&self) -> Cow { Cow::Borrowed("unknown") } } #[test] fn test_unknown() { let platform_info = PlatformInfo::new().unwrap(); assert_eq!(platform_info.sysname(), "unknown"); assert_eq!(platform_info.nodename(), "unknown"); assert_eq!(platform_info.release(), "unknown"); assert_eq!(platform_info.version(), "unknown"); assert_eq!(platform_info.machine(), "unknown"); } platform-info-0.2.0/src/windows.rs000064400000000000000000000337670072674642500152770ustar 00000000000000// This file is part of the uutils coreutils package. // // (c) Alex Lyon // // For the full copyright and license information, please view the LICENSE file // that was distributed with this source code. // extern crate winapi; use self::winapi::shared::minwindef::*; use self::winapi::shared::ntdef::NTSTATUS; use self::winapi::shared::ntstatus::*; use self::winapi::um::libloaderapi::*; use self::winapi::um::sysinfoapi::*; use self::winapi::um::winbase::*; use self::winapi::um::winnt::*; use self::winapi::um::winver::*; use super::Uname; use std::borrow::Cow; use std::ffi::{CStr, OsStr, OsString}; use std::io; use std::iter; use std::mem; use std::os::windows::ffi::{OsStrExt, OsStringExt}; use std::path::PathBuf; use std::ptr; #[allow(unused_variables)] #[allow(non_snake_case)] #[repr(C)] struct VS_FIXEDFILEINFO { dwSignature: DWORD, dwStrucVersion: DWORD, dwFileVersionMS: DWORD, dwFileVersionLS: DWORD, dwProductVersionMS: DWORD, dwProductVersionLS: DWORD, dwFileFlagsMask: DWORD, dwFileFlags: DWORD, dwFileOS: DWORD, dwFileType: DWORD, dwFileSubtype: DWORD, dwFileDateMS: DWORD, dwFileDateLS: DWORD, } /// `PlatformInfo` handles retrieving information for the current platform (Windows in this case). pub struct PlatformInfo { sysinfo: SYSTEM_INFO, nodename: String, release: String, version: String, } impl PlatformInfo { /// Creates a new instance of `PlatformInfo`. Because of the way the information is retrieved, /// it is possible for this function to fail. pub fn new() -> io::Result { unsafe { #[allow(deprecated)] let mut sysinfo = mem::uninitialized(); GetNativeSystemInfo(&mut sysinfo); let (version, release) = Self::version_info()?; let nodename = Self::computer_name()?; Ok(Self { sysinfo, nodename, version, release, }) } } fn computer_name() -> io::Result { let mut size = 0; unsafe { // NOTE: shouldn't need to check the error because, on error, the required size will be // stored in the size variable // XXX: verify that ComputerNameDnsHostname is the best option GetComputerNameExW(ComputerNameDnsHostname, ptr::null_mut(), &mut size); } let mut data = Vec::with_capacity(size as usize); unsafe { // we subtract one from the size because the returned size includes the null terminator data.set_len(size as usize - 1); if GetComputerNameExW(ComputerNameDnsHostname, data.as_mut_ptr(), &mut size) != 0 { Ok(String::from_utf16_lossy(&data)) } else { // XXX: should this error or just return localhost? Err(io::Error::last_os_error()) } } } // NOTE: the only reason any of this has to be done is Microsoft deprecated GetVersionEx() and // it is now basically useless for us on Windows 8.1 and Windows 10 unsafe fn version_info() -> io::Result<(String, String)> { let dll_wide: Vec = OsStr::new("ntdll.dll") .encode_wide() .chain(iter::once(0)) .collect(); let module = GetModuleHandleW(dll_wide.as_ptr()); if !module.is_null() { let funcname = CStr::from_bytes_with_nul_unchecked(b"RtlGetVersion\0"); let func = GetProcAddress(module, funcname.as_ptr()); if !func.is_null() { let func: extern "stdcall" fn(*mut RTL_OSVERSIONINFOEXW) -> NTSTATUS = mem::transmute(func as *const ()); let mut osinfo: RTL_OSVERSIONINFOEXW = mem::zeroed(); osinfo.dwOSVersionInfoSize = mem::size_of::() as _; if func(&mut osinfo) == STATUS_SUCCESS { let version = String::from_utf16_lossy( osinfo.szCSDVersion.split(|&v| v == 0).next().unwrap(), ); let release = Self::determine_release( osinfo.dwMajorVersion, osinfo.dwMinorVersion, osinfo.wProductType, osinfo.wSuiteMask, ); return Ok((version, release)); } } } // as a last resort, try to get the relevant info by loading the version info from a system // file (specifically Kernel32.dll) Self::version_info_from_file() } fn version_info_from_file() -> io::Result<(String, String)> { use self::winapi::um::sysinfoapi; let pathbuf = Self::get_kernel32_path()?; let file_info = Self::get_file_version_info(pathbuf)?; let (major, minor) = Self::query_version_info(file_info)?; #[allow(deprecated)] let mut info: OSVERSIONINFOEXW = unsafe { mem::uninitialized() }; info.wSuiteMask = VER_SUITE_WH_SERVER as WORD; info.wProductType = VER_NT_WORKSTATION; let mask = unsafe { sysinfoapi::VerSetConditionMask(0, VER_SUITENAME, VER_EQUAL) }; let suite_mask = if unsafe { VerifyVersionInfoW(&mut info, VER_SUITENAME, mask) } != 0 { VER_SUITE_WH_SERVER as USHORT } else { 0 }; let mask = unsafe { sysinfoapi::VerSetConditionMask(0, VER_PRODUCT_TYPE, VER_EQUAL) }; let product_type = if unsafe { VerifyVersionInfoW(&mut info, VER_PRODUCT_TYPE, mask) } != 0 { VER_NT_WORKSTATION } else { 0 }; Ok(( String::new(), Self::determine_release(major, minor, product_type, suite_mask), )) } fn get_kernel32_path() -> io::Result { let file = OsStr::new("Kernel32.dll"); // the "- 1" is to account for the path separator let buf_capacity = MAX_PATH - file.len() - 1; let mut buffer = Vec::with_capacity(buf_capacity); let buf_size = unsafe { GetSystemDirectoryW(buffer.as_mut_ptr(), buf_capacity as UINT) }; if buf_size >= buf_capacity as UINT || buf_size == 0 { Err(io::Error::last_os_error()) } else { unsafe { buffer.set_len(buf_size as usize); } let mut pathbuf = PathBuf::from(OsString::from_wide(&buffer)); pathbuf.push(file); Ok(pathbuf) } } fn get_file_version_info(path: PathBuf) -> io::Result> { let path_wide: Vec<_> = path .as_os_str() .encode_wide() .chain(iter::once(0)) .collect(); let fver_size = unsafe { GetFileVersionInfoSizeW(path_wide.as_ptr(), ptr::null_mut()) }; if fver_size == 0 { return Err(io::Error::last_os_error()); } let mut buffer = Vec::with_capacity(fver_size as usize); if unsafe { GetFileVersionInfoW( path_wide.as_ptr(), 0, fver_size, buffer.as_mut_ptr() as *mut _, ) } == 0 { Err(io::Error::last_os_error()) } else { unsafe { buffer.set_len(fver_size as usize); } Ok(buffer) } } fn query_version_info(buffer: Vec) -> io::Result<(ULONG, ULONG)> { let mut block_size = 0; #[allow(deprecated)] let mut block = unsafe { mem::uninitialized() }; let sub_block: Vec<_> = OsStr::new("\\") .encode_wide() .chain(iter::once(0)) .collect(); if unsafe { VerQueryValueW( buffer.as_ptr() as *const _, sub_block.as_ptr(), &mut block, &mut block_size, ) == 0 && block_size < mem::size_of::() as UINT } { return Err(io::Error::last_os_error()); } let info = unsafe { &*(block as *const VS_FIXEDFILEINFO) }; Ok(( HIWORD(info.dwProductVersionMS) as _, LOWORD(info.dwProductVersionMS) as _, )) } fn determine_release( major: ULONG, minor: ULONG, product_type: UCHAR, suite_mask: USHORT, ) -> String { let mut name = match major { 5 => match minor { 0 => "Windows 2000", 1 => "Windows XP", 2 if product_type == VER_NT_WORKSTATION => "Windows XP Professional x64 Edition", 2 if suite_mask as UINT == VER_SUITE_WH_SERVER => "Windows Home Server", 2 => "Windows Server 2003", _ => "", }, 6 => match minor { 0 if product_type == VER_NT_WORKSTATION => "Windows Vista", 0 => "Windows Server 2008", 1 if product_type != VER_NT_WORKSTATION => "Windows Server 2008 R2", 1 => "Windows 7", 2 if product_type != VER_NT_WORKSTATION => "Windows Server 2012", 2 => "Windows 8", 3 if product_type != VER_NT_WORKSTATION => "Windows Server 2012 R2", 3 => "Windows 8.1", _ => "", }, 10 => match minor { 0 if product_type != VER_NT_WORKSTATION => "Windows Server 2016", _ => "", }, _ => "", }; // we're doing this down here so we don't have to copy this into multiple branches if name.is_empty() { name = if product_type == VER_NT_WORKSTATION { "Windows" } else { "Windows Server" }; } format!("{} {}.{}", name, major, minor) } } impl Uname for PlatformInfo { fn sysname(&self) -> Cow { // TODO: report if using MinGW instead of MSVC // XXX: if Rust ever works on Windows CE and winapi has the VER_PLATFORM_WIN32_CE // constant, we should probably check for that Cow::from("WindowsNT") } fn nodename(&self) -> Cow { Cow::from(self.nodename.as_str()) } // FIXME: definitely wrong fn release(&self) -> Cow { Cow::from(self.release.as_str()) } // FIXME: this is prob wrong fn version(&self) -> Cow { Cow::from(self.version.as_str()) } fn machine(&self) -> Cow { let arch = unsafe { self.sysinfo.u.s().wProcessorArchitecture }; let arch_str = match arch { PROCESSOR_ARCHITECTURE_AMD64 => "x86_64", PROCESSOR_ARCHITECTURE_INTEL => match self.sysinfo.wProcessorLevel { 4 => "i486", 5 => "i586", 6 => "i686", _ => "i386", }, PROCESSOR_ARCHITECTURE_IA64 => "ia64", // FIXME: not sure if this is wrong because I think uname usually returns stuff like // armv7l on Linux, but can't find a way to figure that out on Windows PROCESSOR_ARCHITECTURE_ARM => "arm", // XXX: I believe this is correct for GNU compat, but differs from LLVM? Like the ARM // branch above, I'm not really sure about this one either PROCESSOR_ARCHITECTURE_ARM64 => "aarch64", PROCESSOR_ARCHITECTURE_MIPS => "mips", PROCESSOR_ARCHITECTURE_PPC => "powerpc", PROCESSOR_ARCHITECTURE_ALPHA | PROCESSOR_ARCHITECTURE_ALPHA64 => "alpha", // FIXME: I don't know anything about this architecture, so this may be incorrect PROCESSOR_ARCHITECTURE_SHX => "sh", _ => "unknown", }; Cow::from(arch_str) } } #[cfg(test)] fn is_wow64() -> bool { use self::winapi::um::processthreadsapi::*; let mut result = FALSE; let dll_wide: Vec = OsStr::new("Kernel32.dll") .encode_wide() .chain(iter::once(0)) .collect(); unsafe { let module = GetModuleHandleW(dll_wide.as_ptr()); if !module.is_null() { let funcname = CStr::from_bytes_with_nul_unchecked(b"IsWow64Process\0"); let func = GetProcAddress(module, funcname.as_ptr()); if !func.is_null() { let func: extern "stdcall" fn(HANDLE, *mut BOOL) -> BOOL = mem::transmute(func as *const ()); // we don't bother checking for errors as we assume that means that we are not using // WoW64 func(GetCurrentProcess(), &mut result); } } } result == TRUE } #[test] fn test_sysname() { assert_eq!(PlatformInfo::new().unwrap().sysname(), "WindowsNT"); } #[test] fn test_machine() { let target = if cfg!(target_arch = "x86_64") || (cfg!(target_arch = "x86") && is_wow64()) { vec!["x86_64"] } else if cfg!(target_arch = "x86") { vec!["i386", "i486", "i586", "i686"] } else if cfg!(target_arch = "arm") { vec!["arm"] } else if cfg!(target_arch = "aarch64") { // NOTE: keeping both of these until the correct behavior is sorted out vec!["arm64", "aarch64"] } else if cfg!(target_arch = "powerpc") { vec!["powerpc"] } else if cfg!(target_arch = "mips") { vec!["mips"] } else { // NOTE: the other architecture are currently not valid targets for Rust (in fact, I am // almost certain some of these are not even valid targets for the Windows build) vec!["unknown"] }; let info = PlatformInfo::new().unwrap(); println!("{}", info.machine()); assert!(target.contains(&&*info.machine())); } // TODO: figure out a way to test these /* #[test] fn test_nodename() { let info = PlatformInfo::new().unwrap(); panic!("{}", info.nodename()); } #[test] fn test_version() { let info = PlatformInfo::new().unwrap(); panic!("{}", info.version()); } #[test] fn test_release() { let info = PlatformInfo::new().unwrap(); panic!("{}", info.release()); } */ platform-info-0.2.0/tests/integration_test.rs000064400000000000000000000012710072674642500175230ustar 00000000000000use platform_info::*; #[test] fn platform() -> Result<(), String> { let uname = match PlatformInfo::new() { Ok(info) => info, Err(error) => panic!("{}", error), }; println!("sysname = {}", uname.sysname()); println!("nodename = {}", uname.nodename()); println!("release = {}", uname.release()); println!("version = {}", uname.version()); println!("machine = {}", uname.machine()); assert!(!uname.sysname().is_empty()); assert!(!uname.nodename().is_empty()); assert!(!uname.release().is_empty()); #[cfg(not(windows))] // empty on windows assert!(!uname.version().is_empty()); assert!(!uname.machine().is_empty()); Ok(()) }