platform-info-2.0.2/.cargo/config000064400000000000000000000001021046102023000147340ustar 00000000000000[target.x86_64-unknown-redox] linker = "x86_64-unknown-redox-gcc" platform-info-2.0.2/.cargo_vcs_info.json0000644000000001360000000000100136120ustar { "git": { "sha1": "841bdd9614ee56847238ad0407b7a66031d5eb56" }, "path_in_vcs": "" }platform-info-2.0.2/.codecov.yml000064400000000000000000000006241046102023000146270ustar 00000000000000# CodeCov configuration # ref: #comment: false ## disable PR coverage commentary posts coverage: status: # `informational: true` == show status but prevent blocking/failure of CI project: default: informational: true changes: default: informational: true patch: default: informational: true platform-info-2.0.2/.gitattributes000064400000000000000000000003771046102023000153040ustar 00000000000000# v2022.06.23 [rivy] # default; use LF EOLs for text files * text=auto eol=lf # CRLF required; force required CRLF EOLs for WinOS BAT/CMD and MSVC SLN files *.[bB][aA][tT] text eol=crlf *.[cC][mM][dD] text eol=crlf *.[sS][lL][nN] text eol=crlf platform-info-2.0.2/.github/workflows/ci.yml000064400000000000000000000207131046102023000171200ustar 00000000000000# spell-checker:ignore (rust) clippy rustdoc rustfmt rustup RUSTC RUSTFLAGS Zpanic Cpanic RUSTDOCFLAGS Ccodegen Coverflow profraw # spell-checker:ignore (abbrevs/acronyms) MSVC (bash) alnum esac (jargon) maint (utils) codecov grcov lcov markdownlint sccache (vars) tempfile () ntempfile uutils # spell-checker:ignore (people) dtolnay ; Swatinem ; DavidAnson on: [push, pull_request] name: CI env: PROJECT_NAME: platform-info PROJECT_DESC: 'Cross-platform host machine info' PROJECT_AUTH: 'uutils' # * `rust` caching configuration RUSTC_WRAPPER: 'sccache' SCCACHE_GHA_ENABLED: 'true' # terminate execution of the current workflow group when there is a newer changeset detected # * the group is defined by "WORKFLOW_NAME-REF", where REF can be a branch, tag, or pull-request reference # * workflows executing for the default branch are excluded from termination concurrency: group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: ${{ github.ref != format('refs/heads/{0}', github.event.repository.default_branch) }} jobs: style_format: name: Style/format runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@stable with: components: rustfmt - uses: Swatinem/rust-cache@v2 - uses: mozilla-actions/sccache-action@v0.0.3 - run: cargo fmt --all -- --check style_lint: name: Style/lint runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@stable with: components: clippy - uses: Swatinem/rust-cache@v2 - uses: mozilla-actions/sccache-action@v0.0.3 - uses: DavidAnson/markdownlint-cli2-action@v11 with: command: fix globs: | *.md docs/src/**/*.md src/**/*.md - run: cargo clippy --all-targets -- -D warnings style_spellcheck: name: Style/spellcheck runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 - uses: streetsidesoftware/cspell-action@v2 ## ref: with: incremental_files_only: false verbose: true test: name: Build/Test runs-on: ${{ matrix.os }} strategy: fail-fast: false matrix: os: [ubuntu-latest, macos-latest, windows-latest] steps: - uses: actions/checkout@v3 - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 - uses: mozilla-actions/sccache-action@v0.0.3 - name: Build/Test Info shell: bash run: | ## Build/Test Info # repo/commit info echo "## repo/commit" echo GITHUB_REPOSITORY=${GITHUB_REPOSITORY} echo GITHUB_REF=${GITHUB_REF} echo GITHUB_SHA=${GITHUB_SHA} # environment echo "## environment" echo "CI='${CI}'" echo "PWD='${PWD}'" # tooling info display echo "## tooling" which gcc >/dev/null 2>&1 && (gcc --version | head -1) || true rustup -V 2>/dev/null rustup show active-toolchain cargo -V rustc -V # dependencies echo "## crate dependency list" # * note: for applications, use `--locked` for `cargo fetch`` and `cargo tree`) and commit *Cargo.lock* cargo fetch --quiet cargo tree --edges=no-dev --prefix=none --workspace | grep -vE "\(.*[/\\]${GITHUB_REPOSITORY##*/}\)" | sort --unique - run: cargo test --all-features -- --test-threads 1 env: RUST_BACKTRACE: '1' coverage: name: Code Coverage runs-on: ${{ matrix.job.os }} strategy: fail-fast: true matrix: job: - { os: ubuntu-latest } - { os: macos-latest } - { os: windows-latest } steps: - uses: actions/checkout@v3 - name: Initialize/setup workflow id: vars shell: bash run: | ## VARs and paths setup outputs() { step_id="${{ github.action }}"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; } # toolchain TOOLCHAIN="nightly" ## default to "nightly" toolchain (required for certain required unstable compiler flags) ## !maint: refactor when stable channel has needed support # * specify gnu-type TOOLCHAIN for windows; `grcov` requires gnu-style code coverage data files case ${{ matrix.job.os }} in windows-*) TOOLCHAIN="$TOOLCHAIN-x86_64-pc-windows-msvc" ;; esac; # * use requested TOOLCHAIN if specified if [ -n "${{ matrix.job.toolchain }}" ]; then TOOLCHAIN="${{ matrix.job.toolchain }}" ; fi outputs TOOLCHAIN # target-specific options # * CARGO_FEATURES_OPTION CARGO_FEATURES_OPTION='--all-features' ; ## default to '--all-features' for code coverage if [ -n "${{ matrix.job.features }}" ]; then CARGO_FEATURES_OPTION='--features=${{ matrix.job.features }}' ; fi outputs CARGO_FEATURES_OPTION # * CODECOV_FLAGS CODECOV_FLAGS=$( echo "${{ matrix.job.os }}" | sed 's/[^[:alnum:]]/_/g' ) outputs CODECOV_FLAGS # * code coverage COVERAGE_REPORT_DIR="target/debug/coverage" PROFILES_DIR="${COVERAGE_REPORT_DIR}/profiles" mkdir -p "${COVERAGE_REPORT_DIR}" "${PROFILES_DIR}" outputs COVERAGE_REPORT_DIR PROFILES_DIR LLVM_PROFILE_FILE="${PROFILES_DIR}/profile-%p-%m.profraw" outputs LLVM_PROFILE_FILE - uses: dtolnay/rust-toolchain@master with: toolchain: ${{ steps.vars.outputs.TOOLCHAIN }} components: llvm-tools - uses: Swatinem/rust-cache@v2 - uses: mozilla-actions/sccache-action@v0.0.3 - run: cargo install grcov - name: Test shell: bash env: CARGO_INCREMENTAL: '0' LLVM_PROFILE_FILE: ${{ steps.vars.outputs.LLVM_PROFILE_FILE }} RUSTC_WRAPPER: '' RUSTFLAGS: '-C instrument-coverage' RUSTDOCFLAGS: '' run: | ## Test cargo test ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --no-fail-fast -- --test-threads 1 - name: Test examples shell: bash env: CARGO_INCREMENTAL: '0' LLVM_PROFILE_FILE: ${{ steps.vars.outputs.LLVM_PROFILE_FILE }} RUSTC_WRAPPER: '' RUSTFLAGS: '-C instrument-coverage' RUSTDOCFLAGS: '' run: | ## Test examples cargo test ${{ steps.vars.outputs.CARGO_FEATURES_OPTION }} --examples --no-fail-fast -- --test-threads 1 - name: Generate coverage data (via `grcov`) id: coverage shell: bash run: | ## Generate coverage data outputs() { step_id="${{ github.action }}"; for var in "$@" ; do echo steps.${step_id}.outputs.${var}="${!var}"; echo "${var}=${!var}" >> $GITHUB_OUTPUT; done; } # COVERAGE_REPORT_DIR='${{ steps.vars.outputs.COVERAGE_REPORT_DIR }}' LLVM_PROFILE_FILE='${{ steps.vars.outputs.LLVM_PROFILE_FILE }}' PROFILES_DIR='${{ steps.vars.outputs.PROFILES_DIR }}' BINARY_PATH='target/debug' COVERAGE_REPORT_FILE="${COVERAGE_REPORT_DIR}/lcov.info" # display coverage files grcov . --binary-path "${BINARY_PATH}" --source-dir . --output-type files --ignore build.rs --ignore "examples/*" --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()" | sort --unique # generate coverage report grcov . --binary-path "${BINARY_PATH}" --source-dir . --output-type lcov --output-path "${COVERAGE_REPORT_FILE}" --ignore build.rs --ignore "examples/*" --ignore "vendor/*" --ignore "/*" --ignore "[a-zA-Z]:/*" --excl-br-line "^\s*((debug_)?assert(_eq|_ne)?!|#\[derive\()" report=${COVERAGE_REPORT_FILE} outputs report - name: Upload coverage results (to Codecov.io) uses: codecov/codecov-action@v3 # 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-2.0.2/.gitignore000064400000000000000000000000501046102023000143650ustar 00000000000000/.vscode/ /target **/*.rs.bk Cargo.lock platform-info-2.0.2/.mailmap000064400000000000000000000012041046102023000140200ustar 00000000000000## spell-checker:disable Alex Lyon Ingvar Stepanyan Sylvestre Ledru # dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> dependabot[bot] <27856297+dependabot-preview[bot]@users.noreply.github.com> dependabot[bot] # renovate[bot] <29139614+renovate[bot]@users.noreply.github.com> platform-info-2.0.2/.vscode/cSpell.json000064400000000000000000000016671046102023000160720ustar 00000000000000// `cspell` settings { // version of the setting file "version": "0.2", // spelling language "language": "en", // custom dictionaries "dictionaries": ["acronyms+names", "jargon", "people", "shell", "workspace"], "dictionaryDefinitions": [ { "name": "acronyms+names", "path": "./cspell.dictionaries/acronyms+names.wordlist.txt" }, { "name": "jargon", "path": "./cspell.dictionaries/jargon.wordlist.txt" }, { "name": "people", "path": "./cspell.dictionaries/people.wordlist.txt" }, { "name": "shell", "path": "./cspell.dictionaries/shell.wordlist.txt" }, { "name": "workspace", "path": "./cspell.dictionaries/workspace.wordlist.txt" } ], // files to ignore (globs supported) "ignorePaths": [ "Cargo.lock", "target/**", "tests/**/fixtures/**", "vendor/**" ], // words to ignore (even if they are in the flagWords) "ignoreWords": [], // words to always consider correct "words": [] } platform-info-2.0.2/.vscode/cspell.dictionaries/acronyms+names.wordlist.txt000064400000000000000000000012531046102023000252430ustar 00000000000000# * abbreviations / acronyms AIX ASLR # address space layout randomization AST # abstract syntax tree CICD # continuous integration/deployment CPU CPUs DevOps Ext3 FIFO FIFOs FQDN # fully qualified domain name GID # group ID GIDs GNU GNUEABI GNUEABIhf JFS MSRV # minimum supported rust version MSVC NixOS POSIX POSIXLY RISC RISCV RNG # random number generator RNGs ReiserFS Solaris UID # user ID UIDs UUID # universally unique identifier WASI WASM XFS aarch flac impls lzma # * names BusyBox BusyTest Codacy Cygwin Deno EditorConfig FreeBSD Gmail GNU Illumos Irix libfuzzer MS-DOS MSDOS MacOS MinGW Minix NetBSD Novell OpenBSD POSIX PowerPC SELinux SkyPack Solaris SysV Xenix Yargs platform-info-2.0.2/.vscode/cspell.dictionaries/jargon.wordlist.txt000064400000000000000000000025401046102023000235710ustar 00000000000000AFAICT alloc arity autogenerate autogenerated autogenerates bitmask bitwise bytewise canonicalization canonicalize canonicalizing codepoint codepoints codegen colorizable colorize coprime consts conv cyclomatic dedup deduplication demangle denoland deque dequeue dev devs discoverability duplicative dsync endianness enqueue errored executable executables exponentiate eval falsey fileio filesystem filesystems flamegraph fullblock getfacl gibi gibibytes glob globbing hardcode hardcoded hardcoding hardfloat hardlink hardlinks hasher hashsums infile iflag iflags kibi kibibytes libacl lcase lossily lstat mebi mebibytes mergeable microbenchmark microbenchmarks microbenchmarking multibyte multicall noatime nocache nocreat noctty noerror nofollow nolinks nonblock nonportable nonprinting notrunc noxfer ofile oflag oflags peekable performant precompiled precompute preload prepend prepended primality pseudoprime pseudoprimes quantiles readonly reparse seedable semver semiprime semiprimes setfacl shortcode shortcodes siginfo sigusr subcommand subexpression submodule sync symlink symlinks syscall syscalls tokenize toolchain truthy ucase unbuffered udeps unescape unintuitive unprefixed unportable unsync urand whitespace wordlist wordlists # * abbreviations consts deps dev maint proc procs # * constants xffff # * variables delim errno progname retval subdir val vals platform-info-2.0.2/.vscode/cspell.dictionaries/people.wordlist.txt000064400000000000000000000044301046102023000235750ustar 00000000000000Akira Hayakawa Akira Hayakawa Alan Andrade Alan Andrade Aleksander Bielawski Aleksander Bielawski Alex Lyon Alex Lyon Alexander Batischev Alexander Batischev Alexander Fomin Alexander Fomin Anthony Deschamps Anthony Deschamps Árni Dagur Árni Dagur Ben Eills Ben Eills Ben Hirsch Ben Hirsch Benoit Benedetti Benoit Benedetti Boden Garman Boden Garman Chirag B Jadwani Chirag Jadwani Derek Chiang Derek Chiang Dorota Kapturkiewicz Dorota Kapturkiewicz Evgeniy Klyuchikov Evgeniy Klyuchikov Fangxu Hu Fangxu Hu Gil Cottle Gil Cottle Haitao Li Haitao Li Inokentiy Babushkin Inokentiy Babushkin Jan Scheer * jhscheer Jan Scheer jhscheer Jeremiah Peschka Jeremiah Peschka Jian Zeng Jian Zeng Jimmy Lu Jimmy Lu Joao Oliveira Joao Oliveira Jordi Boggiano Jordi Boggiano Jordy Dickinson Jordy Dickinson Joseph Crail Joseph Crail Joshua S Miller Joshua Miller Konstantin Pospelov Konstantin Pospelov Maciej Dziardziel Maciej Dziardziel Martin Kysel Martin Kysel Michael Debertol Michael Debertol Michael Gehring Michael Gehring Mitchell Mebane Mitchell Mebane Morten Olsen Lysgaard Morten Olsen Lysgaard Nicholas Juszczak Nicholas Juszczak Nick Platt Nick Platt Orvar Segerström Orvar Segerström Peter Atashian Peter Atashian Robert Swinford Robert Swinford Rolf Morel Rolf Morel Roman Gafiyatullin Roman Gafiyatullin Roy Ivy III * rivy Roy Ivy III rivy Sergey "Shnatsel" Davidoff Sergey Shnatsel Davidoff Sergey Shnatsel Davidoff Sokovikov Evgeniy Sokovikov Evgeniy Sunrin SHIMURA Sunrin SHIMURA Sylvestre Ledru Sylvestre Ledru T Jameson Little Jameson Little Thomas Queiroz Thomas Queiroz Tobias Bohumir Schottdorf Tobias Bohumir Schottdorf Virgile Andreani Virgile Andreani Vsevolod Velichko Vsevolod Velichko Wiktor Kuropatwa Wiktor Kuropatwa Yury Krivopalov Yury Krivopalov KokaKiwi Mahkoh Smigle00 Smigle00 Smigle anonymousknight kwantam nicoo gmnsii platform-info-2.0.2/.vscode/cspell.dictionaries/shell.wordlist.txt000064400000000000000000000014551046102023000234240ustar 00000000000000# * Mac clonefile # * POSIX TMPDIR adduser csh globstar inotify localtime mksh mountinfo mountpoint mtab nullglob passwd pipefail popd ptmx pushd setarch sh sudo sudoedit tcsh tzselect urandom wtmp zsh # * Windows APPDATA COMSPEC HKCU HKLM HOMEDRIVE HOMEPATH LOCALAPPDATA PATHEXT PATHEXT SYSTEMROOT USERDOMAIN USERNAME USERPROFILE procmon # * `git` gitattributes gitignore # * `make` (`gmake`) CURDIR GNUMAKEFLAGS GNUMakefile LIBPATTERNS MAKECMDGOALS MAKEFILES MAKEFLAGS MAKELEVEL MAKESHELL SHELLSTATUS VPATH abspath addprefix addsuffix endef findstring firstword ifeq ifneq lastword notdir patsubst # * `npm` preversion # * utilities cachegrind chglog codespell commitlint dprint dtrace gcov gmake grcov grep markdownlint rerast rollup sed selinuxenabled sestatus vdir wslpath xargs # * directories sbin platform-info-2.0.2/.vscode/cspell.dictionaries/workspace.wordlist.txt000064400000000000000000000057721046102023000243210ustar 00000000000000# * cargo cdylib rlib # * crates advapi advapi32-sys aho-corasick backtrace blake2b_simd bstr bytecount byteorder chacha chrono conv corasick crossterm exacl filetime formatteriteminfo fsext fundu getopts getrandom globset indicatif itertools lscolors mdbook memchr multifilereader onig ouroboros peekreader quickcheck rand_chacha ringbuffer rlimit rstest smallvec tempdir tempfile termion termios termsize termwidth textwrap thiserror ureq walkdir winapi xattr # * rust/rustc RUSTDOCFLAGS RUSTFLAGS clippy rustc rustfmt rustup # bitor # BitOr trait function bitxor # BitXor trait function concat fract powi println repr rfind struct structs substr splitn trunc uninit # * uutils basenc chcon chgrp chmod chown chroot cksum csplit dircolors hashsum hostid logname mkdir mkfifo mknod mktemp nohup nproc numfmt pathchk printenv printf readlink realpath relpath rmdir runcon shuf sprintf stdbuf stty tsort uname unexpand whoami # * vars/errno errno EACCES EBADF EBUSY EEXIST EINVAL ENODATA ENOENT ENOSYS ENOTEMPTY EOPNOTSUPP EPERM EROFS # * vars/fcntl F_GETFL GETFL fcntl vmsplice # * vars/libc COMFOLLOW FILENO FTSENT HOSTSIZE IDSIZE IFBLK IFCHR IFDIR IFIFO IFLNK IFMT IFREG IFSOCK IRGRP IROTH IRUSR ISGID ISUID ISVTX IWGRP IWOTH IWUSR IXGRP IXOTH IXUSR LINESIZE NAMESIZE RTLD_NEXT RTLD SIGINT SIGKILL SIGTERM SYS_fdatasync SYS_syncfs USERSIZE accpath addrinfo addrlen blocksize canonname chroot dlsym execvp fdatasync freeaddrinfo getaddrinfo getegid geteuid getgid getgrgid getgrnam getgrouplist getgroups getpwent getpwnam getpwuid getuid inode inodes isatty lchown pathlen setgid setgroups settime setuid socktype statfs statp statvfs strcmp strerror strlen syncfs umask waitpid wcslen # * vars/nix iovec unistd # * vars/signals SIGPIPE # * vars/std CString pathbuf # * vars/stat bavail bfree bsize ffree frsize fsid fstat fstype namelen # unix::fs::MetadataExt atime # access time blksize # blocksize for file system I/O blocks # number of blocks allocated to file ctime # creation time dev # ID of device containing the file gid # group ID of file owner ino # inode number mode # permissions mtime # modification time nlink # number of hard links to file rdev # device ID if file is a character/block special file size # total size of file in bytes uid # user ID of file owner nsec # nanosecond measurement scale # freebsd::MetadataExt iosize # * vars/time Timespec isdst nanos nsec nsecs strftime strptime subsec usec usecs utcoff # * vars/utmpx endutxent getutxent getutxid getutxline pututxline setutxent utmp utmpx utmpxname # * vars/winapi DWORD SYSTEMTIME LPVOID LPWSTR ULONG ULONGLONG UNLEN WCHAR WSADATA errhandlingapi fileapi handleapi lmcons minwinbase minwindef processthreadsapi synchapi sysinfoapi winbase winerror winnt winsock # * vars/selinux freecon getfilecon lgetfilecon lsetfilecon setfilecon # * vars/uucore optflag optflagmulti optflagopt optmulti optopt # * uutils ccmd coreopts coreutils keepenv libc libstdbuf musl tmpd ucmd ucommand utmpx uucore uucore_procs uudoc uumain uutil uutils # * function names getcwd platform-info-2.0.2/Cargo.lock0000644000000042750000000000100115750ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 3 [[package]] name = "aho-corasick" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43f6cb1bf222025340178f382c426f13757b2960e89779dfcb319c32542a5a41" dependencies = [ "memchr", ] [[package]] name = "libc" version = "0.2.147" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4668fb0ea861c1df094127ac5f1da3409a82116a4ba74fca2e58ef927159bb3" [[package]] name = "memchr" version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d" [[package]] name = "platform-info" version = "2.0.2" dependencies = [ "libc", "regex", "winapi", ] [[package]] name = "regex" version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2eae68fc220f7cf2532e4494aded17545fce192d59cd996e0fe7887f4ceb575" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83d3daa6976cffb758ec878f108ba0e062a45b2d6ca3a2cca965338855476caf" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5ea92a5b6195c6ef2a0295ea818b312502c6fc94dde986c5553242e18fd4ce2" [[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" platform-info-2.0.2/Cargo.toml0000644000000022360000000000100116130ustar # 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 = "2.0.2" 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" [dev-dependencies.regex] version = "1" [target."cfg(not(target_os = \"windows\"))".dependencies.libc] version = "0.2" [target."cfg(target_os = \"windows\")".dependencies.winapi] version = "0.3" features = [ "libloaderapi", "processthreadsapi", "sysinfoapi", "winbase", "winver", ] platform-info-2.0.2/Cargo.toml.orig000064400000000000000000000014121046102023000152670ustar 00000000000000[package] name = "platform-info" version = "2.0.2" 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" # spell-checker:ignore (crates) libc winapi (features) libloaderapi processthreadsapi sysinfoapi winbase winver [target.'cfg(not(target_os = "windows"))'.dependencies] libc = "0.2" [target.'cfg(target_os = "windows")'.dependencies] winapi = { version = "0.3", features = ["libloaderapi", "processthreadsapi", "sysinfoapi", "winbase", "winver"] } [dev-dependencies] regex = "1" platform-info-2.0.2/LICENSE000064400000000000000000000021131046102023000134040ustar 00000000000000Copyright (c) Jordi Boggiano Copyright (c) UUTILs maintainers MIT License 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-2.0.2/README.md000064400000000000000000000027601046102023000136660ustar 00000000000000# platform-info [![Crates.io](https://img.shields.io/crates/v/platform-info.svg)](https://crates.io/crates/platform-info) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![CodeCov](https://codecov.io/gh/uutils/platform-info/branch/main/graph/badge.svg)](https://codecov.io/gh/uutils/platform-info/tree/main) A simple cross-platform way to get information about the currently running system. ## Examples This simple example: ```rust // examples/ex.rs // * use `cargo run --example ex` to execute this example // spell-checker:ignore (API) nodename osname sysname use platform_info::*; fn main() { let info = PlatformInfo::new().expect("Unable to determine platform info"); // println!("info={:#?}", info); println!("{}", info.sysname().to_string_lossy()); println!("{}", info.nodename().to_string_lossy()); println!("{}", info.release().to_string_lossy()); println!("{}", info.version().to_string_lossy()); println!("{}", info.machine().to_string_lossy()); println!("{}", info.osname().to_string_lossy()); } ``` should display something like: ```text Linux hostname 5.10.0-8-amd64 #1 SMP Debian 5.10.46-4 (2021-08-03) x86_64 GNU/Linux ``` > Using `cargo run --example ex` will build and execute this [example code](examples/ex.rs). Other examples can be found in the [examples](examples) directory. ## License `platform-info` is licensed under the [MIT License](LICENSE). platform-info-2.0.2/examples/arch.rs000064400000000000000000000012311046102023000155000ustar 00000000000000// examples/arch.rs // * use `cargo run --example arch` to execute this example // spell-checker:ignore (API) nodename osname sysname use platform_info::*; fn main() { let info = PlatformInfo::new().unwrap_or_else(|err| { eprintln!("Unable to determine platform info: {}", err); std::process::exit(1); }); println!( "{}", match info.machine().to_os_string().into_string() { Ok(s) => s, Err(os_s) => { let s = os_s.to_string_lossy(); eprintln!("machine = [{}]'{:?}' => '{}'", os_s.len(), os_s, s); String::from(s) } } ); } platform-info-2.0.2/examples/ex.rs000064400000000000000000000012361046102023000152040ustar 00000000000000// examples/ex.rs // * use `cargo run --example ex` to execute this example // spell-checker:ignore (API) nodename osname sysname use platform_info::*; fn main() { let info = PlatformInfo::new().expect("Unable to determine platform info"); // println!("info={:#?}", info); // note: info *may* contain extra platform-specific fields println!("{}", info.sysname().to_string_lossy()); println!("{}", info.nodename().to_string_lossy()); println!("{}", info.release().to_string_lossy()); println!("{}", info.version().to_string_lossy()); println!("{}", info.machine().to_string_lossy()); println!("{}", info.osname().to_string_lossy()); } platform-info-2.0.2/renovate.json000064400000000000000000000001531046102023000151170ustar 00000000000000{ "$schema": "https://docs.renovatebot.com/renovate-schema.json", "extends": [ "config:base" ] } platform-info-2.0.2/src/lib.rs000064400000000000000000000103011046102023000143000ustar 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. // spell-checker:ignore (abbrev/names) MSVC POSIX (names) rivy (rust) rustdoc RUSTDOCFLAGS // Documentation // See or . // Use `cargo doc --no-deps --open --target=i686-pc-windows-msvc` to view WinOS documentation for this crate. // Use `cargo doc --no-deps --open --target=i686-unknown-linux-gnu` to view POSIX documentation for this crate. // * note: `cargo rustdoc` is equivalent to `cargo doc --no-deps` and is what `docs.rs` uses to generate documentation. // * ref: @@ // Enable documentation warnings for missing documentation (for public items) and broken intra-doc links. // * note: CI documentation linting has all warnings escalated to errors (using `RUSTDOCFLAGS="--deny warnings" cargo doc`) #![warn(missing_docs)] #![warn(rustdoc::broken_intra_doc_links)] // #![doc = include_str!("../README.md")] // ToDO: [2023-05-28; rivy] DRY by instead including README.md as crate documentation /*! [![Crates.io](https://img.shields.io/crates/v/platform-info.svg)](https://crates.io/crates/platform-info) [![License](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/uutils/platform-info/tree/main/LICENSE) [![CodeCov](https://codecov.io/gh/uutils/platform-info/branch/main/graph/badge.svg)](https://codecov.io/gh/uutils/platform-info/tree/main) This crate provides the ability to retrieve information specific to your current platform via a cross-platform uname-type API ([`UNameAPI`]). Additional platform-specific information may be supplied within [`PlatformInfo`]. # Usage This crate is available on [crate.io](https://crates.io/crates/platform-info). So, to use it in your project, just add the following to your project's `Cargo.toml` [dependencies](https://doc.rust-lang.org/cargo/reference/specifying-dependencies.html): ```toml [dependencies] platform-info = "2" ``` # Examples ```rust */ #![doc = include_str!("../examples/ex.rs")] /*! ``` Other examples can be found in the [`examples` directory](https://github.com/uutils/platform-info/tree/main/examples) of this crate and in the [uutils/coreutils](https://github.com/uutils/coreutils) implementation of [`uname`](https://github.com/uutils/coreutils/blob/main/src/uu/uname/src/uname.rs). */ // spell-checker:ignore (API) nodename osname sysname // spell-checker:ignore (uutils) coreutils uutils #![warn(unused_results)] // enable warnings for unused results use std::ffi::OsStr; mod lib_impl; //=== // PlatformInfo // Handles initial retrieval and holds cached information for the current platform. pub use lib_impl::PlatformInfo; #[cfg(unix)] pub use lib_impl::UTSName; #[cfg(windows)] pub use lib_impl::{WinApiSystemInfo, WinOsVersionInfo}; // PlatformInfoError /// The common error type for [`PlatformInfoAPI`]. pub use lib_impl::BoxedThreadSafeStdError as PlatformInfoError; // PlatformInfoAPI /// Defines the full API for [`PlatformInfo`]. // * includes `UNameAPI` pub trait PlatformInfoAPI: UNameAPI { /// Creates a new instance of [`PlatformInfo`]. ///
On some platforms, it is possible for this function to fail. fn new() -> Result where Self: Sized; } // UNameAPI /// Defines a trait API providing `uname` (aka "Unix name") style platform information. // ref: @@ pub trait UNameAPI { /// The name of this implementation of the operating system. fn sysname(&self) -> &OsStr; /// The node name (network node hostname) of this machine. fn nodename(&self) -> &OsStr; /// The current release level of the operating system. fn release(&self) -> &OsStr; /// The current version level of the current release. fn version(&self) -> &OsStr; /// The name of the current system's hardware. fn machine(&self) -> &OsStr; /// The name of the current OS. fn osname(&self) -> &OsStr; } platform-info-2.0.2/src/lib_impl.rs000064400000000000000000000035621046102023000153340ustar 00000000000000// "plumbing" setup and connections for `lib.rs` #![warn(unused_results)] // enable warnings for unused results #[cfg(target_os = "windows")] use std::path::Path; #[cfg(target_os = "windows")] use std::path::PathBuf; //=== types /// Standard thread-safe error type pub type ThreadSafeStdError = dyn std::error::Error + Send + Sync; /// Standard thread-safe error type (boxed to allow translation for any std::error::Error type) pub type BoxedThreadSafeStdError = Box; /// A slice of a path string /// (akin to [`str`]; aka/equivalent to [`Path`]). #[cfg(target_os = "windows")] type PathStr = Path; /// An owned, mutable path string /// (akin to [`String`]; aka/equivalent to [`PathBuf`]). #[cfg(target_os = "windows")] type PathString = PathBuf; //=== platform-specific const // HOST_OS_NAME * ref: [`uname` info](https://en.wikipedia.org/wiki/Uname) const HOST_OS_NAME: &str = if cfg!(all( target_os = "linux", any(target_env = "gnu", target_env = "") )) { "GNU/Linux" } else if cfg!(all( target_os = "linux", not(any(target_env = "gnu", target_env = "")) )) { "Linux" } else if cfg!(target_os = "android") { "Android" } else if cfg!(target_os = "windows") { "MS/Windows" // prior art == `busybox` } else if cfg!(target_os = "freebsd") { "FreeBSD" } else if cfg!(target_os = "netbsd") { "NetBSD" } else if cfg!(target_os = "openbsd") { "OpenBSD" } else if cfg!(target_vendor = "apple") { "Darwin" } else if cfg!(target_os = "fuchsia") { "Fuchsia" } else if cfg!(target_os = "redox") { "Redox" } else if cfg!(target_os = "illumos") { "illumos" } else { "unknown" }; //=== platform-specific module code #[cfg(unix)] #[path = "platform/unix.rs"] mod target; #[cfg(windows)] #[path = "platform/windows.rs"] mod target; #[cfg(not(any(unix, windows)))] #[path = "platform/unknown.rs"] mod target; pub use target::*; platform-info-2.0.2/src/platform/unix.rs000064400000000000000000000163121046102023000163510ustar 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. // spell-checker:ignore (API) domainname nodename osname sysname // spell-checker:ignore (libc) libc utsname // spell-checker:ignore (jargon) hasher // spell-checker:ignore (names) Jian Zeng * anonymousknight96 // spell-checker:ignore (rust) uninit // spell-checker:ignore (uutils) coreutils uutils // spell-checker:ignore (VSCode) endregion // refs: // [Byte-to/from-String Conversions](https://nicholasbishop.github.io/rust-conversions) @@ #![warn(unused_results)] // enable warnings for unused results use std::ffi::{OsStr, OsString}; use std::fmt; use std::fmt::{Debug, Formatter}; use crate::{PlatformInfoAPI, PlatformInfoError, UNameAPI}; use unix_safe::*; // PlatformInfo /// Handles initial retrieval and holds cached information for the current platform (a Unix-like OS in this case). #[derive(Clone, Debug, PartialEq, Eq)] pub struct PlatformInfo { /// Contains the cached results of the `utsname()` system call. // ref: pub utsname: UTSName, /* aka "Unix Time-sharing System Name"; ref: */ // * private-use fields sysname: OsString, nodename: OsString, release: OsString, version: OsString, machine: OsString, osname: OsString, } impl PlatformInfoAPI for PlatformInfo { // * note: this function *should* never fail fn new() -> Result { let utsname = UTSName(utsname()?); Ok(Self { utsname, sysname: oss_from_cstr(&utsname.0.sysname), nodename: oss_from_cstr(&utsname.0.nodename), release: oss_from_cstr(&utsname.0.release), version: oss_from_cstr(&utsname.0.version), machine: oss_from_cstr(&utsname.0.machine), osname: OsString::from(crate::lib_impl::HOST_OS_NAME), }) } } impl UNameAPI for PlatformInfo { fn sysname(&self) -> &OsStr { &self.sysname } fn nodename(&self) -> &OsStr { &self.nodename } fn release(&self) -> &OsStr { &self.release } fn version(&self) -> &OsStr { &self.version } fn machine(&self) -> &OsStr { &self.machine } fn osname(&self) -> &OsStr { &self.osname } } //=== // UTSName /// Contains information about the current computer system. /// /// Wraps [`libc::utsname`]. // ref: /* pub struct utsname { pub sysname: [::c_char; 65], pub nodename: [::c_char; 65], pub release: [::c_char; 65], pub version: [::c_char; 65], pub machine: [::c_char; 65], pub domainname: [::c_char; 65] } */ // aka "Unix Time-sharing System Name"; ref: #[derive(Clone, Copy /* , Debug, PartialEq, Eq */)] pub struct UTSName(libc::utsname); impl Debug for UTSName { fn fmt(&self, f: &mut Formatter) -> fmt::Result { let mut debug_struct = &mut f.debug_struct("UTSName"); debug_struct = debug_struct .field("sysname", &oss_from_cstr(&self.0.sysname)) .field("nodename", &oss_from_cstr(&self.0.nodename)) .field("release", &oss_from_cstr(&self.0.release)) .field("version", &oss_from_cstr(&self.0.version)) .field("machine", &oss_from_cstr(&self.0.machine)); // The domainname field is not part of the POSIX standard but a GNU extension. Therefor // BSD-like platforms and illumos are missing the domainname field. #[cfg(not(any( target_os = "illumos", target_os = "macos", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd" )))] { debug_struct = debug_struct.field("domainname", &oss_from_cstr(&self.0.domainname)); } debug_struct.finish() } } impl PartialEq for UTSName { fn eq(&self, other: &Self) -> bool { let mut equal = true; // avoid 'unused-mut' and 'clippy::let-and-return' warnings on MacOS equal = equal && ( self.0.sysname, self.0.nodename, self.0.release, self.0.version, self.0.machine, ) == ( other.0.sysname, other.0.nodename, other.0.release, other.0.version, other.0.machine, ); // The domainname field is not part of the POSIX standard but a GNU extension. Therefor // BSD-like platforms and illumos are missing the domainname field. #[cfg(not(any( target_os = "illumos", target_os = "macos", target_os = "dragonfly", target_os = "freebsd", target_os = "openbsd", target_os = "netbsd" )))] { equal = equal && (self.0.domainname == other.0.domainname) } equal } } impl Eq for UTSName {} //=== //#region unsafe code mod unix_safe { use std::convert::TryFrom; use std::ffi::{CStr, OsStr, OsString}; use std::io; use std::mem::MaybeUninit; use std::os::unix::ffi::OsStrExt; // oss_from_str() /// *Returns* an OsString created from a libc::c_char slice. pub fn oss_from_cstr(slice: &[libc::c_char]) -> OsString { assert!(slice.len() < usize::try_from(isize::MAX).unwrap()); assert!(slice.iter().position(|&c| c == 0 /* NUL */).unwrap() < slice.len()); OsString::from(OsStr::from_bytes( unsafe { CStr::from_ptr(slice.as_ptr()) }.to_bytes(), )) } // utsname() /// *Returns* a `libc::utsname` structure containing `uname`-like OS system information. pub fn utsname() -> Result { // ref: // ref: let mut uts = MaybeUninit::::uninit(); let result = unsafe { libc::uname(uts.as_mut_ptr()) }; if result != -1 { // SAFETY: `libc::uname()` succeeded => `uts` was initialized Ok(unsafe { uts.assume_init() }) } else { Err(io::Error::last_os_error()) } } } //#endregion (unsafe code) //=== Tests #[test] fn test_osname() { let info = PlatformInfo::new().unwrap(); let osname = info.osname().to_string_lossy(); assert!(osname.starts_with(crate::lib_impl::HOST_OS_NAME)); } #[test] fn structure_clone() { let info = PlatformInfo::new().unwrap(); println!("{:?}", info); #[allow(clippy::redundant_clone)] // ignore `clippy::redundant_clone` warning for direct testing let info_copy = info.clone(); assert_eq!(info_copy, info); } platform-info-2.0.2/src/platform/unknown.rs000064400000000000000000000040231046102023000170610ustar 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. // spell-checker:ignore (API) nodename osname sysname // spell-checker:ignore (names) Ingvar Stepanyan * me@rreverser.com // spell-checker:ignore (uutils) coreutils uutils #![warn(unused_results)] // enable warnings for unused results use std::ffi::{OsStr, OsString}; use crate::{PlatformInfoAPI, PlatformInfoError, UNameAPI}; // PlatformInfo /// Handles initial retrieval and holds cached information for the current platform ("unknown" in this case). #[derive(Clone, Debug, PartialEq, Eq)] pub struct PlatformInfo { unknown: OsString, } impl PlatformInfoAPI for PlatformInfo { fn new() -> Result { Ok(Self { unknown: OsString::from(crate::lib_impl::HOST_OS_NAME), }) } } impl UNameAPI for PlatformInfo { fn sysname(&self) -> &OsStr { &self.unknown } fn nodename(&self) -> &OsStr { &self.unknown } fn release(&self) -> &OsStr { &self.unknown } fn version(&self) -> &OsStr { &self.unknown } fn machine(&self) -> &OsStr { &self.unknown } fn osname(&self) -> &OsStr { &self.unknown } } #[test] fn test_unknown() { let platform_info = PlatformInfo::new().unwrap(); assert_eq!(platform_info.sysname().to_string_lossy(), "unknown"); assert_eq!(platform_info.nodename().to_string_lossy(), "unknown"); assert_eq!(platform_info.release().to_string_lossy(), "unknown"); assert_eq!(platform_info.version().to_string_lossy(), "unknown"); assert_eq!(platform_info.machine().to_string_lossy(), "unknown"); assert_eq!(platform_info.osname().to_string_lossy(), "unknown"); } #[test] fn structure_clone() { let info = PlatformInfo::new().unwrap(); println!("{:?}", info); let info_copy = info.clone(); assert_eq!(info_copy, info); } platform-info-2.0.2/src/platform/windows.rs000064400000000000000000000715411046102023000170650ustar 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. // Note: there no standardization of values for platform info (or `uname`), so mimic some current practices // busybox-v1.35.0 * `busybox uname -a` => "Windows_NT HOSTNAME 10.0 19044 x86_64 MS/Windows" // python-v3.8.3 => `uname_result(system='Windows', node='HOSTNAME', release='10', version='10.0.19044', machine='AMD64')` // refs/research: // [rust ~ std::ffi](https://doc.rust-lang.org/std/ffi) // [rust ~ std::os::windows::ffi](https://doc.rust-lang.org/std/os/windows/ffi) // [WTF-8/WTF-16](https://simonsapin.github.io/wtf-8/#ill-formed-utf-16) @@ // [UCS-2/UTF-8/UTF-16](https://unascribed.com/b/2019-08-02-the-tragedy-of-ucs2.html) @@ // [Byte-to/from-String Conversions](https://nicholasbishop.github.io/rust-conversions) @@ // [NT Version Info](https://en.wikipedia.org/wiki/Windows_NT) @@ // [NT Version Info (summary)](https://simple.wikipedia.org/wiki/Windows_NT) @@ // [NT Version Info (detailed)](https://en.wikipedia.org/wiki/Comparison_of_Microsoft_Windows_versions#Windows_NT) @@ // spell-checker:ignore (abbrev/acronyms) MSVC POSIX SuperH // spell-checker:ignore (API) sysname osname nodename // spell-checker:ignore (jargon) armv aarch hasher mmbr // spell-checker:ignore (people) Roy Ivy III * rivy // spell-checker:ignore (rust) repr stdcall uninit // spell-checker:ignore (uutils) coreutils uutils // spell-checker:ignore (WinAPI) ctypes CWSTR DWORDLONG dwStrucVersion FARPROC FIXEDFILEINFO HIWORD HMODULE libloaderapi LOWORD LPCSTR LPCVOID LPCWSTR lpdw LPDWORD lplp LPOSVERSIONINFOEXW LPSYSTEM lptstr LPVOID LPWSTR minwindef ntdef ntstatus OSVERSIONINFOEXW processthreadsapi PUINT SMALLBUSINESS SUITENAME sysinfo sysinfoapi sysinfoapi TCHAR TCHARs ULONGLONG VERSIONINFO WCHAR WCHARs winapi winbase winver WSTR wstring // spell-checker:ignore (WinOS) ntdll #![warn(unused_results)] // enable warnings for unused results use std::convert::TryFrom; use std::ffi::{OsStr, OsString}; use std::fmt; use std::fmt::{Debug, Formatter}; use std::io; use std::os::windows::ffi::OsStringExt; use winapi::shared::minwindef::*; use winapi::um::sysinfoapi::*; use winapi::um::winnt::*; use crate::{PlatformInfoAPI, PlatformInfoError, UNameAPI}; use super::PathStr; use super::PathString; type WinOSError = crate::lib_impl::BoxedThreadSafeStdError; mod windows_safe; use windows_safe::*; //=== // PlatformInfo /// Handles initial retrieval and holds cached information for the current platform (Windows/WinOS in this case). #[derive(Clone, Debug, PartialEq, Eq)] pub struct PlatformInfo { /// Cached computer name. pub computer_name: OsString, /// Wraps a cached [`WinApiSystemInfo`]. pub system_info: WinApiSystemInfo, /// Wraps a cached [`WinOsVersionInfo`]. pub version_info: WinOsVersionInfo, // * private-use fields sysname: OsString, nodename: OsString, release: OsString, version: OsString, machine: OsString, osname: OsString, } impl PlatformInfoAPI for PlatformInfo { // * note: due to the method of information retrieval, this *may* fail fn new() -> Result { let computer_name = WinOsGetComputerName()?; let system_info = WinApiSystemInfo(WinAPI_GetNativeSystemInfo()); let version_info = os_version_info()?; let sysname = determine_sysname(); let nodename = computer_name.clone(); let release = version_info.release.clone(); let version = version_info.version.clone(); let machine = determine_machine(&system_info); let osname = determine_osname(&version_info); Ok(Self { computer_name, system_info, version_info, /* private use */ sysname, nodename, release, version, machine, osname, }) } } impl UNameAPI for PlatformInfo { fn sysname(&self) -> &OsStr { &self.sysname } fn nodename(&self) -> &OsStr { &self.nodename } fn release(&self) -> &OsStr { &self.release } fn version(&self) -> &OsStr { &self.version } fn machine(&self) -> &OsStr { &self.machine } fn osname(&self) -> &OsStr { &self.osname } } //=== // WinApiSystemInfo /// Contains information about the current computer system. /// /// Wraps [SYSTEM_INFO](https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info). #[derive(Clone, Copy /* , Debug, PartialEq, Eq *//* note: implemented elsewhere */)] pub struct WinApiSystemInfo( // ref: @@ SYSTEM_INFO, ); // WinOsVersionInfo /// Contains WinOS version information as [OsString]'s; for more info, see [NT Version Info (detailed)](https://en.wikipedia.org/wiki/Comparison_of_Microsoft_Windows_versions#Windows_NT). #[derive(Clone, Debug, PartialEq, Eq)] pub struct WinOsVersionInfo { // ref: [NT Version Info (detailed)](https://en.wikipedia.org/wiki/Comparison_of_Microsoft_Windows_versions#Windows_NT) @@ /// "Friendly" OS name (eg, "Windows 10") pub os_name: OsString, /// General/main OS version (eg, "10.0") pub release: OsString, /// Specific OS version (eg, "19045") pub version: OsString, } //=== pub mod util { use std::ffi::CString; use std::ffi::OsStr; use std::os::windows::ffi::OsStrExt; use winapi::um::winnt::*; /// WinOS wide-character string buffer ///
Note: `WCHAR` (aka `TCHAR`) == `wchar_t` == `u16` #[allow(clippy::upper_case_acronyms)] pub type WSTR = Vec; /// NUL-terminated WinOS wide-character string buffer ///
Note: `WCHAR` (aka `TCHAR`) == `wchar_t` == `u16` #[allow(clippy::upper_case_acronyms)] pub type CWSTR = Vec; // to_c_string() /// Convert the leading non-NUL content of any string (which is cheaply convertible to an OsStr) into a CString, without error. /// /// Any non-Unicode sequences are replaced with [U+FFFD (REPLACEMENT CHARACTER)](https://en.wikipedia.org/wiki/Specials_(Unicode_block)). pub fn to_c_string>(os_str: S) -> CString { let nul = '\0'; let s = os_str.as_ref().to_string_lossy(); let leading_s = s.split(nul).next().unwrap_or(""); // string slice of leading non-NUL characters let maybe_c_string = CString::new(leading_s); assert!(maybe_c_string.is_ok()); //* failure here == algorithmic/logic error => panic maybe_c_string.unwrap() } /// Convert the leading non-NUL content of any string (which is cheaply convertible to an OsStr) into a CWSTR, without error. pub fn to_c_wstring>(os_str: S) -> CWSTR { let nul: WCHAR = 0; let mut wstring: WSTR = os_str.as_ref().encode_wide().collect(); wstring.push(nul); let maybe_index_first_nul = wstring.iter().position(|&i| i == nul); assert!(maybe_index_first_nul.is_some()); //* failure here == algorithmic/logic error => panic let index_first_nul = maybe_index_first_nul.unwrap(); assert!(index_first_nul < wstring.len()); //* failure here == algorithmic/logic error => panic CWSTR::from(&wstring[..(index_first_nul + 1)]) } } //=== // MmbrVersion /// Contains a version specification as major, minor, build, and revision DWORDs (ie, from *major*.*minor*.*build*.*release* version style). #[derive(Clone, Debug, PartialEq, Eq)] struct MmbrVersion { major: DWORD, minor: DWORD, build: DWORD, release: DWORD, } // WinApiFileVersionInfo /// Contains file version info (`VS_VERSIONINFO`) wrapped as a byte vector (`data`). /// /// Wraps [VS_VERSIONINFO](https://learn.microsoft.com/en-us/windows/win32/menurc/vs-versioninfo). #[derive(Clone, Debug, PartialEq, Eq)] pub struct WinApiFileVersionInfo { data: Vec, } //=== impl Debug for WinApiSystemInfo { fn fmt(&self, f: &mut Formatter) -> fmt::Result { f.debug_struct("WinApiSystemInfo") .field("wProcessorArchitecture", &self.wProcessorArchitecture()) .field("dwPageSize", &self.0.dwPageSize) .field( "lpMinimumApplicationAddress", &self.0.lpMinimumApplicationAddress, ) .field( "lpMaximumApplicationAddress", &self.0.lpMaximumApplicationAddress, ) .field("dwActiveProcessorMask", &self.0.dwActiveProcessorMask) .field("dwNumberOfProcessors", &self.0.dwNumberOfProcessors) .field("dwProcessorType", &self.0.dwProcessorType) .field("dwAllocationGranularity", &self.0.dwAllocationGranularity) .field("wAllocationGranularity", &self.0.wProcessorLevel) .field("wAllocationRevision", &self.0.wProcessorRevision) .finish() } } impl PartialEq for WinApiSystemInfo { fn eq(&self, other: &Self) -> bool { ( self.wProcessorArchitecture(), self.0.dwPageSize, self.0.lpMinimumApplicationAddress, self.0.lpMaximumApplicationAddress, self.0.dwActiveProcessorMask, self.0.dwNumberOfProcessors, self.0.dwProcessorType, self.0.dwAllocationGranularity, self.0.wProcessorLevel, self.0.wProcessorRevision, ) == ( other.wProcessorArchitecture(), other.0.dwPageSize, other.0.lpMinimumApplicationAddress, other.0.lpMaximumApplicationAddress, other.0.dwActiveProcessorMask, other.0.dwNumberOfProcessors, other.0.dwProcessorType, other.0.dwAllocationGranularity, other.0.wProcessorLevel, other.0.wProcessorRevision, ) } } impl Eq for WinApiSystemInfo {} //=== // WinOSGetComputerName /// *Returns* a NetBIOS or DNS name associated with the local computer. #[allow(non_snake_case)] fn WinOsGetComputerName() -> Result { //## NameType ~ using "ComputerNameDnsHostname" vs "ComputerNamePhysicalDnsHostname" // * "ComputerNamePhysicalDnsHostname" *may* have a different (more specific) name when in a DNS cluster // * `uname -n` may show the more specific cluster name (see https://clusterlabs.org/pacemaker/doc/deprecated/en-US/Pacemaker/1.1/html/Clusters_from_Scratch/_short_node_names.html) // * under Linux/Wine, they are *exactly* the same ([from Wine patches msgs](https://www.winehq.org/pipermail/wine-patches/2002-November/004080.html)) // * probably want the more specific in-cluster name, but, functionally, any difference will be very rare // ref: [COMPUTER_NAME_FORMAT](https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/ne-sysinfoapi-computer_name_format) @@ let name_type = ComputerNamePhysicalDnsHostname; // or ComputerNameDnsHostname let mut size: DWORD = 0; let _ = WinAPI_GetComputerNameExW(name_type, None, &mut size); let mut data = vec![0; usize::try_from(size)?]; let result = WinAPI_GetComputerNameExW(name_type, &mut data, &mut size); if result == FALSE { return Err(Box::new(io::Error::last_os_error())); } Ok(OsString::from_wide(&data[..usize::try_from(size)?])) } // WinOsGetFileVersionInfo /// *Returns* the file version information block for the specified file (`file_path`). #[allow(non_snake_case)] fn WinOsGetFileVersionInfo>( file_path: P, ) -> Result { let file_version_size = WinAPI_GetFileVersionInfoSizeW(&file_path); if file_version_size == 0 { return Err(Box::new(io::Error::last_os_error())); } let mut data: Vec = vec![0; usize::try_from(file_version_size)?]; let result = WinAPI_GetFileVersionInfoW(&file_path, &mut data); if result == FALSE { return Err(Box::new(io::Error::last_os_error())); } Ok(WinApiFileVersionInfo { data }) } // WinOSGetSystemDirectory /// *Returns* a resolved path to the Windows System Directory (aka `%SystemRoot%`). #[allow(non_snake_case)] fn WinOsGetSystemDirectory() -> Result { let required_capacity: UINT = WinAPI_GetSystemDirectoryW(None); let mut data = vec![0; usize::try_from(required_capacity)?]; let result = WinAPI_GetSystemDirectoryW(&mut data); if result == 0 { return Err(Box::new(io::Error::last_os_error())); } let path = PathString::from(OsString::from_wide(&data[..usize::try_from(result)?])); Ok(path) } //=== // os_version_info /// *Returns* OS version info (as [`WinOsVersionInfo`]) using a DLL procedure call, with fallback version info from a /// known system file. /// /// This call and fallback recipe is necessary because Microsoft deprecated the previously used `GetVersionEx()`, making /// it useless for Windows 8.1 and later windows versions. // ref: @@ // ref: @@ fn os_version_info() -> Result { match os_version_info_from_dll() { Ok(os_info) => Ok(os_info), Err(_) => { // as a last resort, try to get the relevant info by loading the version info from a system file // Note: this file version may be just the current "base" version and not the actual most up-to-date version info // * eg: kernel32.dll (or ntdll.dll) version => "10.0.19041.2130" _vs_ `cmd /c ver` => "10.0.19044.2364" return version_info_from_file("" /* use default file */); // .or. `return version_info_from_file::<_, &str>(None /* use default file */);` } } } // os_version_info_from_dll /// *Returns* version info (as [`WinOsVersionInfo`]) obtained via `NTDLL/RtlGetVersion()`. fn os_version_info_from_dll() -> Result { let os_info = NTDLL_RtlGetVersion()?; Ok(WinOsVersionInfo { os_name: winos_name( os_info.dwMajorVersion, os_info.dwMinorVersion, os_info.dwBuildNumber, os_info.wProductType, os_info.wSuiteMask.into(), ) .into(), release: format!("{}.{}", os_info.dwMajorVersion, os_info.dwMinorVersion).into(), version: format!("{}", os_info.dwBuildNumber).into(), }) } // version_info_from_file /// *Returns* version info (as [`WinOsVersionInfo`]) obtained from `file_path`. /// /// `file_path` ~ if empty or `None`, default to the full path of "kernel32.dll" (a known, omnipresent, system file) fn version_info_from_file(file_path: I) -> Result where I: Into>, P: AsRef, { let file_path: PathString = match file_path.into() { Some(ref p) if !p.as_ref().as_os_str().is_empty() => p.as_ref().into(), _ => WinOsGetSystemDirectory()?.join("kernel32.dll"), }; let file_info = WinOsGetFileVersionInfo(file_path)?; let v = mmbr_from_file_version(file_info)?; let mut info = create_OSVERSIONINFOEXW()?; info.wSuiteMask = WORD::try_from(VER_SUITE_WH_SERVER)?; info.wProductType = VER_NT_WORKSTATION; let mask = WinAPI_VerSetConditionMask(0, VER_SUITENAME, VER_EQUAL); let suite_mask = if WinAPI_VerifyVersionInfoW(&info, VER_SUITENAME, mask) != FALSE { VER_SUITE_WH_SERVER } else { 0 }; let mask = WinAPI_VerSetConditionMask(0, VER_PRODUCT_TYPE, VER_EQUAL); let product_type = if WinAPI_VerifyVersionInfoW(&info, VER_PRODUCT_TYPE, mask) != FALSE { VER_NT_WORKSTATION } else { 0 }; Ok(WinOsVersionInfo { os_name: winos_name(v.major, v.minor, v.build, product_type, suite_mask).into(), release: format!("{}.{}", v.major, v.minor).into(), version: format!("{}", v.build).into(), }) } // mmbr_from_file_version /// *Returns* version (as an [`MmbrVersion`]) copied from a view (aka slice) into the supplied `file_version_info`. fn mmbr_from_file_version( file_version_info: WinApiFileVersionInfo, ) -> Result { let info = WinOsFileVersionInfoQuery_root(&file_version_info)?; Ok(MmbrVersion { major: DWORD::try_from(HIWORD(info.dwProductVersionMS))?, minor: DWORD::try_from(LOWORD(info.dwProductVersionMS))?, build: DWORD::try_from(HIWORD(info.dwProductVersionLS))?, release: DWORD::try_from(LOWORD(info.dwProductVersionLS))?, }) } // winos_name /// *Returns* "friendly" WinOS name. fn winos_name( major: DWORD, minor: DWORD, build: DWORD, product_type: BYTE, suite_mask: DWORD, ) -> String { // [NT Version Info (detailed)](https://en.wikipedia.org/wiki/Comparison_of_Microsoft_Windows_versions#Windows_NT) @@ let default_name = if product_type == VER_NT_WORKSTATION { format!("{} {}.{}", "Windows", major, minor) } else { format!("{} {}.{}", "Windows Server", major, minor) }; let 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 == VER_SUITE_WH_SERVER => "Windows Home Server", 2 => "Windows Server 2003", _ => &default_name, }, 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", _ => &default_name, }, 10 => match minor { 0 if product_type == VER_NT_WORKSTATION && (build >= 22000) => "Windows 11", 0 if product_type != VER_NT_WORKSTATION && (14000..17000).contains(&build) => { "Windows Server 2016" } 0 if product_type != VER_NT_WORKSTATION && (17000..19000).contains(&build) => { "Windows Server 2019" } 0 if product_type != VER_NT_WORKSTATION && (build >= 20000) => "Windows Server 2022", _ => "Windows 10", }, _ => &default_name, }; name.to_string() } //=== fn determine_machine(system_info: &WinApiSystemInfo) -> OsString { let arch = system_info.wProcessorArchitecture(); // ref: [SYSTEM_INFO structure](https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info) @@ // ref: [LLVM Triples](https://llvm.org/doxygen/classllvm_1_1Triple.html) @@ // ref: [SuperH](https://en.wikipedia.org/wiki/SuperH) @@ // ref: [OldNewThing ~ SuperH](https://devblogs.microsoft.com/oldnewthing/20190805-00/?p=102749) @@ let arch_str = match arch { PROCESSOR_ARCHITECTURE_AMD64 => "x86_64", PROCESSOR_ARCHITECTURE_INTEL => match system_info.0.wProcessorLevel { 4 => "i486", 5 => "i586", 6 => "i686", _ => "i386", }, PROCESSOR_ARCHITECTURE_IA64 => "ia64", PROCESSOR_ARCHITECTURE_ARM => "arm", // `arm` may be under-specified compared to GNU implementations PROCESSOR_ARCHITECTURE_ARM64 => "aarch64", // alternatively, `arm64` may be more correct PROCESSOR_ARCHITECTURE_MIPS => "mips", PROCESSOR_ARCHITECTURE_PPC => "powerpc", PROCESSOR_ARCHITECTURE_ALPHA | PROCESSOR_ARCHITECTURE_ALPHA64 => "alpha", PROCESSOR_ARCHITECTURE_SHX => "superh", // a "SuperH" processor _ => "unknown", }; OsString::from(arch_str) } fn determine_osname(version_info: &WinOsVersionInfo) -> OsString { let mut osname = OsString::from(crate::lib_impl::HOST_OS_NAME); osname.extend([ OsString::from(" ("), version_info.os_name.clone(), OsString::from(")"), ]); osname } fn determine_sysname() -> OsString { // As of 2023-02, possible Windows kernels == [ "Windows_9x", "Windows_NT" ] // * "Windows_9x" hit end-of-service-life on 2006-07-11 (ref: [Windows_9x](https://en.wikipedia.org/wiki/Windows_9x) @@ ) OsString::from("Windows_NT") // compatible with `busybox` and MS (from std::env::var("OS")) } //=== Tests #[test] fn test_sysname() { let info = PlatformInfo::new().unwrap(); let sysname = info.sysname().to_os_string(); let expected = std::env::var_os("OS").unwrap_or_else(|| OsString::from("Windows_NT")); println!("sysname=[{}]'{:#?}'", sysname.len(), sysname); assert_eq!(sysname, expected); } #[test] #[allow(non_snake_case)] fn test_nodename_no_trailing_NUL() { let info = PlatformInfo::new().unwrap(); let nodename = info.nodename().to_string_lossy(); let trimmed = nodename.trim().trim_end_matches(|c| c == '\0'); println!("nodename=[{}]'{}'", nodename.len(), nodename); assert_eq!(nodename, trimmed); } #[test] fn test_machine() { let is_wow64 = KERNEL32_IsWow64Process(WinAPI_GetCurrentProcess()).unwrap_or_else(|err| { println!("ERR: IsWow64Process(): {:#?}", err); false }); 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"] }; println!("target={:#?}", target); let info = PlatformInfo::new().unwrap(); let machine = info.machine().to_string_lossy(); println!("machine=[{}]'{}'", machine.len(), machine); assert!(target.contains(&&machine[..])); } #[test] fn test_osname() { let info = PlatformInfo::new().unwrap(); let osname = info.osname().to_string_lossy(); println!("osname=[{}]'{}'", osname.len(), osname); assert!(osname.starts_with(crate::lib_impl::HOST_OS_NAME)); } #[test] fn test_version_vs_version() { let version_via_dll = os_version_info_from_dll().unwrap(); let version_via_file = version_info_from_file::<_, &str>(None).unwrap(); assert!(version_via_file == version_info_from_file("").unwrap()); println!("version (via dll) = '{:#?}'", version_via_dll); println!("version (via known file) = '{:#?}'", version_via_file); assert_eq!(version_via_dll.os_name, version_via_file.os_name); assert_eq!(version_via_dll.release, version_via_file.release); // the "version" portions may differ, but should have only slight variation // * assume that "version" is convertible to u32 + "version" from file is always earlier/smaller and may differ only below the thousands digit // * ref: [NT Version Info (detailed)](https://en.wikipedia.org/wiki/Comparison_of_Microsoft_Windows_versions#Windows_NT) @@ let version_via_dll_n = version_via_dll .version .to_string_lossy() .parse::() .unwrap(); let version_via_file_n = version_via_file .version .to_string_lossy() .parse::() .unwrap(); assert!(version_via_dll_n.checked_sub(version_via_file_n) < Some(1000)); } #[test] fn test_known_winos_names() { // ref: [NT Version Info (detailed)](https://en.wikipedia.org/wiki/Comparison_of_Microsoft_Windows_versions#Windows_NT) @@ assert_eq!( winos_name(3, 1, 528, VER_NT_WORKSTATION, VER_SUITE_PERSONAL), "Windows 3.1" ); assert_eq!( winos_name(3, 5, 807, VER_NT_WORKSTATION, VER_SUITE_PERSONAL), "Windows 3.5" ); assert_eq!( winos_name(3, 51, 1057, VER_NT_WORKSTATION, VER_SUITE_PERSONAL), "Windows 3.51" ); assert_eq!( winos_name(4, 0, 1381, VER_NT_WORKSTATION, VER_SUITE_PERSONAL), "Windows 4.0" ); assert_eq!( winos_name(5, 0, 2195, VER_NT_WORKSTATION, VER_SUITE_PERSONAL), "Windows 2000" ); assert_eq!( winos_name(5, 1, 2600, VER_NT_WORKSTATION, VER_SUITE_PERSONAL), "Windows XP" ); assert_eq!( winos_name(5, 2, 3790, VER_NT_WORKSTATION, VER_SUITE_PERSONAL), "Windows XP Professional x64 Edition" ); assert_eq!( winos_name(5, 2, 3790, VER_NT_SERVER, VER_SUITE_WH_SERVER), "Windows Home Server" ); assert_eq!( winos_name(5, 2, 3790, VER_NT_SERVER, VER_SUITE_SMALLBUSINESS), "Windows Server 2003" ); assert_eq!( winos_name(5, 2, 3790, VER_NT_SERVER, VER_SUITE_SMALLBUSINESS), "Windows Server 2003" ); assert_eq!( winos_name(6, 0, 6000, VER_NT_WORKSTATION, VER_SUITE_PERSONAL), "Windows Vista" ); assert_eq!( winos_name(6, 0, 6001, VER_NT_SERVER, VER_SUITE_SMALLBUSINESS), "Windows Server 2008" ); assert_eq!( winos_name(6, 1, 7600, VER_NT_WORKSTATION, VER_SUITE_PERSONAL), "Windows 7" ); assert_eq!( winos_name(6, 1, 7600, VER_NT_SERVER, VER_SUITE_SMALLBUSINESS), "Windows Server 2008 R2" ); assert_eq!( winos_name(6, 2, 9200, VER_NT_SERVER, VER_SUITE_SMALLBUSINESS), "Windows Server 2012" ); assert_eq!( winos_name(6, 2, 9200, VER_NT_WORKSTATION, VER_SUITE_PERSONAL), "Windows 8" ); assert_eq!( winos_name(6, 3, 9600, VER_NT_WORKSTATION, VER_SUITE_PERSONAL), "Windows 8.1" ); assert_eq!( winos_name(6, 3, 9600, VER_NT_SERVER, VER_SUITE_SMALLBUSINESS), "Windows Server 2012 R2" ); assert_eq!( winos_name(10, 0, 10240, VER_NT_WORKSTATION, VER_SUITE_PERSONAL), "Windows 10" ); assert_eq!( winos_name(10, 0, 17134, VER_NT_WORKSTATION, VER_SUITE_PERSONAL), "Windows 10" ); assert_eq!( winos_name(10, 0, 19141, VER_NT_WORKSTATION, VER_SUITE_PERSONAL), "Windows 10" ); assert_eq!( winos_name(10, 0, 19145, VER_NT_WORKSTATION, VER_SUITE_PERSONAL), "Windows 10" ); assert_eq!( winos_name(10, 0, 14393, VER_NT_SERVER, VER_SUITE_SMALLBUSINESS), "Windows Server 2016" ); assert_eq!( winos_name(10, 0, 17763, VER_NT_SERVER, VER_SUITE_SMALLBUSINESS), "Windows Server 2019" ); assert_eq!( winos_name(10, 0, 20348, VER_NT_SERVER, VER_SUITE_SMALLBUSINESS), "Windows Server 2022" ); assert_eq!( winos_name(10, 0, 22000, VER_NT_WORKSTATION, VER_SUITE_PERSONAL), "Windows 11" ); assert_eq!( winos_name(10, 0, 22621, VER_NT_WORKSTATION, VER_SUITE_PERSONAL), "Windows 11" ); // test unmatched versions (triggers `_` matches and returns a `default_name`) assert_eq!( winos_name(5, 9, 3790, VER_NT_WORKSTATION, VER_SUITE_PERSONAL), "Windows 5.9" ); assert_eq!( winos_name(5, 9, 3790, VER_NT_SERVER, VER_SUITE_SMALLBUSINESS), "Windows Server 5.9" ); assert_eq!( winos_name(6, 9, 9600, VER_NT_WORKSTATION, VER_SUITE_PERSONAL), "Windows 6.9" ); assert_eq!( winos_name(6, 9, 9600, VER_NT_SERVER, VER_SUITE_SMALLBUSINESS), "Windows Server 6.9" ); } #[test] fn structure_clone() { let info = PlatformInfo::new().unwrap(); println!("{:?}", info); #[allow(clippy::redundant_clone)] // ignore `clippy::redundant_clone` warning for direct testing let info_copy = info.clone(); assert_eq!(info_copy, info); let mmbr = MmbrVersion { major: 1, minor: 2, build: 3, release: 4, }; println!("{:?}", mmbr); #[allow(clippy::redundant_clone)] // ignore `clippy::redundant_clone` warning for direct testing let mmbr_copy = mmbr.clone(); assert_eq!(mmbr_copy, mmbr); let fvi = WinApiFileVersionInfo { data: vec![1, 2, 3, 4], }; println!("{:?}", fvi); #[allow(clippy::redundant_clone)] // ignore `clippy::redundant_clone` warning for direct testing let fvi_copy = fvi.clone(); assert_eq!(fvi_copy, fvi); } platform-info-2.0.2/src/platform/windows_safe.rs000064400000000000000000000711471046102023000200650ustar 00000000000000// spell-checker:ignore (abbrev) MSVC // spell-checker:ignore (API) sysname osname nodename // spell-checker:ignore (jargon) armv aarch // spell-checker:ignore (rust) nonminimal repr stdcall uninit // spell-checker:ignore (uutils) coreutils uutils // spell-checker:ignore (vars) mmbr mmrb // spell-checker:ignore (VSCode) endregion // spell-checker:ignore (WinAPI) ctypes CWSTR DWORDLONG dwStrucVersion FARPROC FIXEDFILEINFO HIWORD HMODULE libloaderapi LOWORD LPCSTR LPCVOID LPCWSTR lpdw LPDWORD lplp LPOSVERSIONINFOEXW LPSYSTEM lptstr LPVOID LPWSTR minwindef ntdef ntstatus OSVERSIONINFOEXW processthreadsapi PUINT SMALLBUSINESS SUITENAME sysinfo sysinfoapi sysinfoapi TCHAR TCHARs ULONGLONG WCHAR WCHARs winapi winbase winver WSTR wstring // spell-checker:ignore (WinOS) ntdll #![warn(unused_results)] // enable warnings for unused results use std::convert::TryFrom; use std::io; use std::mem::{self, MaybeUninit}; use std::ptr; use winapi::shared::minwindef::*; use winapi::shared::ntdef::NTSTATUS; use winapi::shared::ntstatus::*; use winapi::um::libloaderapi::*; use winapi::um::processthreadsapi::GetCurrentProcess; use winapi::um::sysinfoapi; use winapi::um::sysinfoapi::*; use winapi::um::winbase::*; use winapi::um::winnt::*; use winapi::um::winver::*; use super::util::{to_c_string, to_c_wstring, CWSTR}; use super::{WinApiFileVersionInfo, WinApiSystemInfo}; use super::PathStr; use super::WinOSError; //=== // VS_FIXEDFILEINFO /// WinAPI structure which contains version information for a file. /// /// Implements [`VS_FIXEDFILEINFO`]. // ref: [`VS_FIXEDFILEINFO`](https://learn.microsoft.com/en-us/windows/win32/api/verrsrc/ns-verrsrc-vs_fixedfileinfo) @@ #[allow(non_snake_case)] #[allow(unused_variables)] #[derive(Clone, Copy, Debug, PartialEq, Eq)] #[repr(C)] pub struct VS_FIXEDFILEINFO { pub dwSignature: DWORD, pub dwStrucVersion: DWORD, pub dwFileVersionMS: DWORD, pub dwFileVersionLS: DWORD, pub dwProductVersionMS: DWORD, pub dwProductVersionLS: DWORD, pub dwFileFlagsMask: DWORD, pub dwFileFlags: DWORD, pub dwFileOS: DWORD, pub dwFileType: DWORD, pub dwFileSubtype: DWORD, pub dwFileDateMS: DWORD, pub dwFileDateLS: DWORD, } //=== //#region unsafe code impl WinApiSystemInfo { #[allow(non_snake_case)] /// Returns `wProcessorArchitecture` extracted from the [`SYSTEM_INFO`](https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info) structure. ///
Refer to [`SYSTEM_INFO`](https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info) for more information. pub fn wProcessorArchitecture(&self) -> WORD { unsafe { self.0.u.s().wProcessorArchitecture } } } // create_OSVERSIONINFOEXW /// *Returns* an owned, mutable [`OSVERSIONINFOEXW`] structure (fully initialized). // ref: [`OSVERSIONINFOEXW`](https://learn.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-osversioninfoexw) @@ #[allow(non_snake_case)] pub fn create_OSVERSIONINFOEXW( ) -> Result { let os_info_size = DWORD::try_from(mem::size_of::())?; let mut os_info: OSVERSIONINFOEXW = unsafe { mem::zeroed() }; os_info.dwOSVersionInfoSize = os_info_size; Ok(os_info) } // NOTE: WinAPI_... functions are thin-wrapper translations of the underlying WinOS API functions into safe functions // WinAPI_FreeLibrary /// Frees the loaded dynamic-link library (DLL) module, decrementing its reference count. /// When the reference count reaches zero, the module is unloaded from the address space of the calling process and the /// handle is no longer valid. /// /// *Returns* BOOL ~ `FALSE` (aka zero) for fn *failure*; o/w non-`FALSE` (aka non-zero) for fn *success*. /// /// Wraps WinOS [`Kernel32/FreeLibrary(...)`](https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-freelibrary). #[allow(non_snake_case)] pub fn WinAPI_FreeLibrary(module: HMODULE /* from `hModule: HMODULE` */) -> BOOL { // FreeLibrary // pub unsafe fn FreeLibrary(hLibModule: HMODULE) -> BOOL // ref: @@ // * *returns* BOOL ~ `FALSE` (aka zero) for fn *failure*; o/w non-`FALSE` (aka non-zero) for fn *success* unsafe { FreeLibrary(module) } } // WinAPI_GetComputerNameExW /// Retrieves a NetBIOS or DNS name associated with the local computer; stored into WCHAR vector (`buffer`). /// /// * `name_type` ~ (in) specify requested type of computer name (as [COMPUTER_NAME_FORMAT](https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/ne-sysinfoapi-computer_name_format)) /// * `buffer` ~ (out) /// - for non-`FALSE` return, contains the requested computer name as WSTR (of length `size`) /// - for `FALSE` return, unchanged /// * `size` ~ (out) /// - for non-`FALSE` return, contains the number of TCHARs (aka WCHARs) copied to the destination buffer, *not including* the terminating null character /// - for `FALSE` return, contains the buffer size required for the result, *including* the terminating null character /// /// *Returns* BOOL ~ `FALSE` (aka zero) for fn *failure*; o/w non-`FALSE` (aka non-zero) for fn *success*. /// ///### Notes /// /// Vector capacity cannot be rigorously set because capacity is set as a minimum and may be "rounded up" by the /// implementation. So, for the supplied `buffer`, `buffer.len()`, *not* `buffer.capacity()`, is used as the measure of /// usable buffer size. /// /// Supplying a zero-length `buffer` (or alternatively, `None`) as input will return a value specifying the actual /// required buffer size for the system path. /// /// Wraps WinOS [`Kernel32/GetComputerNameExW(...)`](https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getcomputernameexw). #[allow(non_snake_case)] pub fn WinAPI_GetComputerNameExW<'a, T>( name_type: COMPUTER_NAME_FORMAT, buffer: T, /* from `lpBuffer: LPWSTR` */ size: &mut DWORD, /* from `nSize: LPDWORD` */ ) -> BOOL where T: Into>>, { // pub unsafe fn GetComputerNameExW(NameType: COMPUTER_NAME_FORMAT, lpBuffer: LPWSTR, nSize: LPDWORD) -> BOOL // ref: @@ // * `name_type` ~ (in) [COMPUTER_NAME_FORMAT](https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/ne-sysinfoapi-computer_name_format) @@ // * `nSize` ~ (in) specifies the size of the destination buffer (*lpBuffer) in TCHARs (aka WCHARs) // * `nSize` ~ (out) on *fn failure* (aka `FALSE` return), receives the buffer size required for the result, *including* the terminating null character // * `nSize` ~ (out) on *fn success*, receives the number of TCHARs (aka WCHARs) copied to the destination buffer, *not including* the terminating null character // * *returns* BOOL ~ `FALSE` (aka zero) for fn *failure*; o/w non-`FALSE` (aka non-zero) for fn *success* let maybe_buffer = buffer.into(); let (buffer_ptr, length) = match maybe_buffer { Some(buf) => (buf.as_mut_ptr(), DWORD::try_from(buf.len()).unwrap_or(0)), None => (ptr::null_mut(), 0), }; *size = length; let result = unsafe { GetComputerNameExW(name_type, buffer_ptr, size) }; assert!((result == FALSE) || (*size <= length)); // safety sanity check; panics on out-of-bounds memory writes (buffer overrun) result } // WinAPI_GetCurrentProcess /// *Returns* a pseudo handle for the current process. /// /// Wraps WinOS [`Kernel32/GetCurrentProcess()`](https://learn.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getcurrentprocess). #[allow(dead_code)] // * fn is used by test(s) #[allow(non_snake_case)] pub fn WinAPI_GetCurrentProcess() -> HANDLE { // GetCurrentProcess // pub unsafe fn GetCurrentProcess() -> HANDLE // ref: @@ unsafe { GetCurrentProcess() } } // WinAPI_GetFileVersionInfoSizeW /// Determines whether the operating system can retrieve version information for a specified file (`file_path`). /// If version information is available, GetFileVersionInfoSize returns the size, in bytes, of that information. /// /// *Returns* DWORD ~ zero for fn *failure*; o/w size of the file version information, in *bytes*, for fn *success*. /// /// Wraps WinOS [`Version/GetFileVersionInfoSizeW(...)`](https://learn.microsoft.com/en-us/windows/win32/api/winver/nf-winver-getfileversioninfosizew). #[allow(non_snake_case)] pub fn WinAPI_GetFileVersionInfoSizeW>( file_path: P, /* used to generate `lptstrFilename: LPCWSTR` */ // lpdwHandle: *mut DWORD, /* ignored/not-needed */ ) -> DWORD { // GetFileVersionInfoSizeW // pub unsafe fn GetFileVersionInfoSizeW(lptstrFilename: LPCWSTR, lpdwHandle: *mut DWORD) -> DWORD // ref: @@ // * returns DWORD ~ on *failure*, 0 // * returns DWORD ~ on *success*, size of the file version information, in *bytes* let file_path_cws: CWSTR = to_c_wstring(file_path.as_ref()); unsafe { GetFileVersionInfoSizeW(file_path_cws.as_ptr(), ptr::null_mut() /* ignored */) } } // WinAPI_GetFileVersionInfoW /// Retrieves version information for the specified file (`file_path`); stored into BYTE vector (`data`). /// /// *Returns* BOOL ~ `FALSE` (aka zero) for fn *failure*; o/w non-`FALSE` (aka non-zero) for fn *success*. /// /// Wraps WinOS [`Version/GetFileVersionInfoW(...)`](https://learn.microsoft.com/en-us/windows/win32/api/winver/nf-winver-getfileversioninfow). #[allow(non_snake_case)] pub fn WinAPI_GetFileVersionInfoW>( file_path: P, /* used to generate `lptstrFilename: LPCWSTR` */ // dwHandle: DWORD, /* ignored/not-needed */ // dwLen: DWORD, /* not-needed */ data: &mut Vec, /* from `lpData: *mut winapi::ctypes::c_void` */ ) -> BOOL { // GetFileVersionInfoW // pub unsafe fn GetFileVersionInfoW(lptstrFilename: LPCWSTR, dwHandle: DWORD, dwLen: DWORD, lpData: *mut c_void) -> BOOL // ref: @@ // * handle/dwHandle == *ignored* // * length/dwLen == maximum size (in bytes) of buffer at data_ptr/lpData // * *returns* BOOL ~ `FALSE` (aka zero) for fn *failure*, o/w non-`FALSE` (aka non-zero) for fn *success* let file_path_cws: CWSTR = to_c_wstring(file_path.as_ref()); unsafe { GetFileVersionInfoW( file_path_cws.as_ptr(), 0, /* ignored */ DWORD::try_from(data.capacity()).unwrap(), data.as_mut_ptr() as *mut _, ) } } // WinAPI_GetNativeSystemInfo /// *Returns* information (as `SYSTEM_INFO`) about the current system to an application running under WOW64. /// /// If the function is called from a 64-bit application, it is equivalent to the GetSystemInfo function. /// If the function is called from an x86 or x64 application running on a 64-bit system that does not have an Intel64 or // x64 processor (such as ARM64), it will return information as if the system is x86 only if x86 emulation is supported /// (or x64 if x64 emulation is also supported). /// /// Wraps WinOS [`Kernel32/GetNativeSystemInfo(...)`](https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getnativesysteminfo). #[allow(non_snake_case)] pub fn WinAPI_GetNativeSystemInfo() -> SYSTEM_INFO { // GetNativeSystemInfo // pub unsafe fn GetNativeSystemInfo(lpSystemInfo: LPSYSTEM_INFO) // ref: @@ let mut sysinfo = MaybeUninit::::uninit(); unsafe { GetNativeSystemInfo(sysinfo.as_mut_ptr()); // SAFETY: `GetNativeSystemInfo()` always succeeds => `sysinfo` was initialized sysinfo.assume_init() } } // WinAPI_GetProcAddress /// *Returns* the address of an exported function/procedure or variable (`symbol_name`) from the specified library (`module`). /// /// Wraps WinOS [`Kernel32/GetProcAddress(...)`](https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getprocaddress). #[allow(non_snake_case)] pub fn WinAPI_GetProcAddress>( module: HMODULE, /* from `hModule: HMODULE` */ symbol_name: P, /* used to generate `lpProcName: LPCSTR` */ ) -> FARPROC { // GetProcAddress // pub unsafe fn GetProcAddress(hModule: HMODULE, lpProcName: LPCSTR) -> FARPROC // ref: @@ let symbol_name_cs = to_c_string(symbol_name.as_ref()); unsafe { GetProcAddress(module, symbol_name_cs.as_ptr()) } } // WinAPI_GetSystemDirectoryW /// Retrieves the path of the system directory; stored into a WCHAR vector (`buffer`). /// /// * `buffer` /// - for non-zero return (*success*) with adequate buffer size, `buffer` will contain the requested WinOS System Directory path as a WSTR /// - for zero (*failure*) or non-zero (*success*) return with inadequate buffer size, `buffer` will be unchanged /// /// *Returns* UINT /// - zero for fn *failure* /// - fn *success* with adequate buffer size, contains the number of WCHARs (aka TCHARs) copied to the destination buffer, *not including* the terminating null character /// - fn *success* with inadequate buffer size, contains the buffer size required for the requested path, *including* the terminating null character /// ///### Notes /// /// Vector capacity cannot be rigorously set because capacity is set as a minimum and may be "rounded up" by the /// implementation. So, for the supplied `buffer`, `buffer.len()`, *not* `buffer.capacity()`, is used as the measure of /// usable buffer size. /// /// Supplying a zero-length `buffer` (or alternatively, `None`) as input will return a value specifying the actual /// required buffer size for the system path. /// /// Wraps WinOS [`Kernel32/GetSystemDirectoryW(...)`](https://learn.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsystemdirectoryw). #[allow(non_snake_case)] pub fn WinAPI_GetSystemDirectoryW<'a, T>( buffer: T, /* from `lpBuffer: LPWSTR` */ // uSize: UINT, /* not needed */ ) -> UINT where T: Into>>, { // GetSystemDirectoryW // pub unsafe fn GetSystemDirectoryW(lpBuffer: LPWSTR, uSize: UINT) -> UINT // ref: @@ // * `uSize` ~ (in) specifies the maximum size of the destination buffer (*lpBuffer) in TCHARs (aka WCHARs) // * returns UINT ~ on fn *failure*, 0 // * returns UINT ~ on fn *success* and uSize <= length(System Directory string), size of required destination buffer (in TCHARs [aka WCHARs]), *including* the terminating null character // * returns UINT ~ on fn *success* and uSize > length(System Directory string), the number of TCHARs (aka WCHARs) copied to the destination buffer, *not including* the terminating null character let (buffer_ptr, length) = match buffer.into() { Some(buf) => (buf.as_mut_ptr(), UINT::try_from(buf.len()).unwrap_or(0)), None => (ptr::null_mut(), 0), }; unsafe { GetSystemDirectoryW(buffer_ptr, length) } } // WinAPI_LoadLibrary /// *Returns* a module handle for the specified module (`module_name`), loading the library, if needed, and increasing /// the per-process reference count. /// /// * `module_name` ~ requested library/module
/// Note: full path specification of `module_name` has increased safety. If only base name and extension is specified, /// modules with colliding names may cause the return of a random module handle. /// /// This function is multithread-safe. /// But this function cannot be called in certain contexts, such as recursively, by any code path called by a `DllMain` /// function, or within a "module initializer" (such as a C++ constructor for a global variable). /// /// Wraps WinOS [`Kernel32/LoadLibraryW(...)`](https://learn.microsoft.com/en-us/windows/win32/api/libloaderapi/nf-libloaderapi-getmodulehandlew). // ref: // ref: #[allow(non_snake_case)] pub fn WinAPI_LoadLibrary>( module_name: P, /* used to generate `lpFileName: LPCWSTR` */ ) -> HMODULE { // LoadLibraryW // pub unsafe fn LoadLibraryW(lpFileName: LPCWSTR) -> HMODULE // ref: @@ let module_name_cws: CWSTR = to_c_wstring(module_name.as_ref()); unsafe { LoadLibraryW(module_name_cws.as_ptr()) } } // WinAPI_VerifyVersionInfoW /// Compares a set of operating system version requirements (`version_info`, `type_mask`, and `condition_mask`) to the /// corresponding values for the currently running version of the system. /// /// *Returns* BOOL ~ `FALSE` (aka zero) if resource is non-existent or requirements are not met; o/w non-`FALSE` (aka non-zero) /// /// Wraps WinOS [`Kernel32/VerifyVersionInfoW(...)`](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-verifyversioninfow). #[allow(non_snake_case)] pub fn WinAPI_VerifyVersionInfoW( version_info: &OSVERSIONINFOEXW, /* from `lpVersionInformation: LPOSVERSIONINFOEXW` */ type_mask: DWORD, /* from `dwTypeMask: DWORD` */ condition_mask: DWORDLONG, /* from `dwlConditionMask: DWORDLONG` */ ) -> BOOL { // VerifyVersionInfoW // pub unsafe fn VerifyVersionInfoW(lpVersionInformation: LPOSVERSIONINFOEXW, dwTypeMask: DWORD, dwlConditionMask: DWORDLONG) -> BOOL // ref: @@ // version_info/lpVersionInformation ~ pointer to (const) version info requirements for comparison // type_mask ~ mask indicating members of version_info to be tested // condition_mask ~ type of comparison for each version_info member to be compared // * returns BOOL ~ `FALSE` (aka zero) for non-existent resource or invalid requirements; o/w non-`FALSE` (aka non-zero) let version_info_ptr: *const OSVERSIONINFOEXW = version_info; unsafe { VerifyVersionInfoW( version_info_ptr as *mut OSVERSIONINFOEXW, // version_info_ptr *is* `*const OSVERSIONINFOEXW` but misdefined by function declaration type_mask, condition_mask, ) } } // WinAPI_VerQueryValueW /// Retrieves specified version information (`query`) as a view (`info_view`) into the specified version-information /// resource (`version_info`). /// /// *Returns* BOOL ~ `FALSE` (aka zero) for fn *failure*; o/w non-zero for fn *success*. /// /// Wraps WinOS [`Version/VerQueryValueW(...)`](https://learn.microsoft.com/en-us/windows/win32/api/winver/nf-winver-verqueryvaluew). #[allow(non_snake_case)] pub fn WinAPI_VerQueryValueW<'a, S: AsRef>( version_info: &'a Vec, /* from `pBlock: LPCVOID` */ query: S, /* from `lpSubBlock: LPCWSTR` */ info_view: &'a mut LPVOID, /* from `lplpBuffer: &mut LPVOID` */ info_view_length: &'a mut UINT, /* from `puLen: PUINT` */ ) -> BOOL { // VerQueryValueW // pub unsafe fn VerQueryValueW(pBlock: LPCVOID, lpSubBlock: LPCWSTR, lplpBuffer: &mut LPVOID, puLen: PUINT) -> BOOL // ref: @@ // version_info_ptr/pBlock ~ pointer to (const) version info // info_view/lplpBuffer ~ pointer into version info supplied by version_info_ptr (no new allocations) // info_view_length/puLen ~ pointer to size (in characters [TCHARs/WCHARs] for "version info values", in bytes for translation array or root block) // * returns BOOL ~ `FALSE` (aka zero) for invalid/non-existent resource; o/w non-`FALSE` (aka non-zero) let version_info_ptr = version_info.as_ptr() as LPCVOID; unsafe { VerQueryValueW( version_info_ptr, to_c_wstring(query.as_ref()).as_ptr(), info_view, info_view_length, ) } } // WinAPI_VerSetConditionMask /// Sets the bits of a 64-bit value to indicate the comparison operator to use for a specified operating system version /// attribute (ie, specific member of version_info [[`OSVERSIONINFOEXW`]]). This function is used, possibly repeatedly, to build the /// `condition_mask` parameter of the [WinAPI_VerifyVersionInfoW] function. /// /// * condition_mask ~ baseline value for type of comparisons for each member of version info /// * type_mask ~ mask indicating the member of version info whose comparison operator is being set /// * condition ~ comparison type /// /// *Returns* ULONGLONG ~ updated condition_mask /// /// Wraps WinOS [`Kernel32/VerSetConditionMask(...)`](https://learn.microsoft.com/en-us/windows/win32/api/winnt/nf-winnt-versetconditionmask). #[allow(non_snake_case)] pub fn WinAPI_VerSetConditionMask( condition_mask: ULONGLONG, type_mask: DWORD, condition: BYTE, ) -> ULONGLONG { // VerSetConditionMask // pub unsafe fn VerSetConditionMask(ConditionMask: ULONGLONG, TypeMask: DWORD, Condition: BYTE) -> ULONGLONG // ref: @@ // condition_mask ~ baseline value for type of comparisons for each member of version info (ie, `OSVERSIONINFOEXW`) // type_mask ~ mask indicating the member of version info whose comparison operator is being set // condition ~ comparison type // * returns ULONGLONG ~ updated condition_mask unsafe { sysinfoapi::VerSetConditionMask(condition_mask, type_mask, condition) } } // WinOsFileVersionInfoQuery_root /// *Returns* the "root" version information *as view of the internal [`VS_FIXEDFILEINFO`] structure* within the /// specified version-information resource (`version_info`). /// /// Uses WinOS [`Version/WinAPI_VerQueryValueW(...)`](https://learn.microsoft.com/en-us/windows/win32/api/winver/nf-winver-verqueryvaluew). #[allow(non_snake_case)] pub fn WinOsFileVersionInfoQuery_root( version_info: &WinApiFileVersionInfo, ) -> Result<&VS_FIXEDFILEINFO, WinOSError> { // NOTE: this function could be expanded to cover root, translation, and information queries by using an enum for a return value // VerQueryValueW // pub unsafe fn VerQueryValueW(pBlock: LPCVOID, lpSubBlock: LPCWSTR, lplpBuffer: &mut LPVOID, puLen: PUINT) -> BOOL // ref: @@ // version_info_ptr/pBlock ~ pointer to (const) version info // info_view/lplpBuffer ~ pointer into version info supplied by version_info_ptr (no new allocations) // info_view_length/puLen ~ pointer to size (in characters [TCHARs/WCHARs] for "version info values", in bytes for translation array or root block) // * returns BOOL ~ `FALSE` (aka zero) for invalid/non-existent resource; o/w non-`FALSE` (aka non-zero) for *fn success* let version_info_data = &version_info.data; let mut data_view = ptr::null_mut(); // view into the `version_info_data` block let mut data_view_size = 0; let query = "\\"; // "root" query ~ requests the VS_FIXEDFILEINFO structure from within the supplied `version_info` let fixed_file_info_size = UINT::try_from(mem::size_of::())?; // expected returned data_view_size if WinAPI_VerQueryValueW( version_info_data, query, &mut data_view, &mut data_view_size, ) == 0 || (data_view_size != fixed_file_info_size) { return Err(Box::new(io::Error::last_os_error())); } assert!(version_info_data.len() >= usize::try_from(data_view_size)?); assert!(data_view_size == fixed_file_info_size); assert!(!data_view.is_null()); // * lifetime of block/info is the same as input argument version_info Ok(unsafe { &*(data_view as *const VS_FIXEDFILEINFO) }) } // KERNEL32_IsWow64Process /// *Returns* an assertion of whether the specified `process` is running under WOW64 on an Intel64 or x64 processor. /// /// Wraps [`Kernel32/IsWow64Process`](https://learn.microsoft.com/en-us/windows/win32/api/wow64apiset/nf-wow64apiset-iswow64process). #[allow(dead_code)] // * fn is used by test(s) #[allow(non_snake_case)] pub fn KERNEL32_IsWow64Process(process: HANDLE) -> Result { // kernel32.dll/IsWow64Process // extern "stdcall" fn(HANDLE, *mut BOOL) -> BOOL // ref: @@ let module_file = "kernel32.dll"; let symbol_name = "IsWow64Process"; let module_path = super::WinOsGetSystemDirectory()?.join(module_file); // let func = super::WinOsGetModuleProcAddress(module_path, procedure); // loads module "permanently" (for the life of current process) let module = WinAPI_LoadLibrary(module_path); let func = WinAPI_GetProcAddress(module, symbol_name); if func.is_null() { return Err(Box::from(format!( "Unable to find DLL procedure '{}' within '{}'", symbol_name, module_file ))); } let func: extern "stdcall" fn(HANDLE, *mut BOOL) -> BOOL = unsafe { mem::transmute(func as *const ()) }; let mut is_wow64: BOOL = FALSE; let result: BOOL = func(process, &mut is_wow64); let _ = WinAPI_FreeLibrary(module); // FreeLibrary() failure/success can be safely ignored Ok((result != FALSE/* func() succeeded` */) && (is_wow64 != FALSE)) } // NTDLL_RtlGetVersion /// *Returns* version information about the currently running operating system. /// /// Wraps [`NTDLL/RtlGetVersion`](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/nf-wdm-rtlgetversion). #[allow(non_snake_case)] pub fn NTDLL_RtlGetVersion() -> Result { // ntdll.dll/RtlGetVersion // extern "stdcall" fn(*mut RTL_OSVERSIONINFOEXW) -> NTSTATUS // ref: @@ // ref: [`RTL_OSVERSIONINFOEXW`](https://learn.microsoft.com/en-us/windows-hardware/drivers/ddi/wdm/ns-wdm-_osversioninfoexw) @@ // note: OSVERSIONINFOEXW == RTL_OSVERSIONINFOEXW ; ref: @@ let module_file = "ntdll.dll"; let symbol_name = "RtlGetVersion"; let module_path = super::WinOsGetSystemDirectory()?.join(module_file); // let func = super::WinOsGetModuleProcAddress(module_path, procedure); // loads module "permanently" (for the life of current process) let module = WinAPI_LoadLibrary(module_path); let func = WinAPI_GetProcAddress(module, symbol_name); if func.is_null() { return Err(Box::from(format!( "Unable to find DLL procedure '{}' within '{}'", symbol_name, module_file ))); } let func: extern "stdcall" fn(*mut RTL_OSVERSIONINFOEXW) -> NTSTATUS = unsafe { mem::transmute(func as *const ()) }; let mut os_version_info = match create_OSVERSIONINFOEXW() { Ok(value) => value, Err(_) => return Err(Box::from("Unable to create OSVERSIONINFOEXW".to_string())), }; let result: NTSTATUS = func(&mut os_version_info); let _ = WinAPI_FreeLibrary(module); // FreeLibrary() failure/success can be safely ignored if result == STATUS_SUCCESS { Ok(os_version_info) } else { Err(Box::from(format!( "RtlGetVersion() failed (result/status: {})", result ))) } } //#endregion (unsafe code) //=== Tests #[test] fn structure_clone() { let ffi = VS_FIXEDFILEINFO { dwSignature: 0, dwStrucVersion: 0, dwFileVersionMS: 0, dwFileVersionLS: 0, dwProductVersionMS: 0, dwProductVersionLS: 0, dwFileFlagsMask: 0, dwFileFlags: 0, dwFileOS: 0, dwFileType: 0, dwFileSubtype: 0, dwFileDateMS: 0, dwFileDateLS: 0, }; println!("{:?}", ffi); #[allow(clippy::clone_on_copy)] // ignore `clippy::clone_on_copy` warning for direct testing let ffi_clone = ffi.clone(); assert_eq!(ffi_clone, ffi); } platform-info-2.0.2/tests/integration_test.rs000064400000000000000000000035641046102023000175040ustar 00000000000000// spell-checker:ignore (API) nodename osname sysname use platform_info::*; #[test] fn platform() -> Result<(), String> { let info = PlatformInfo::new().unwrap(); let sysname = info.sysname().to_string_lossy(); let nodename = info.nodename().to_string_lossy(); let release = info.release().to_string_lossy(); let version = info.version().to_string_lossy(); let machine = info.machine().to_string_lossy(); let osname = info.osname().to_string_lossy(); assert!(!sysname.is_empty()); assert!(!nodename.is_empty()); assert!(!release.is_empty()); assert!(!version.is_empty()); assert!(!machine.is_empty()); assert!(!osname.is_empty()); // assert!(false); Ok(()) } #[test] fn platform_no_invisible_contents() -> Result<(), String> { let info = PlatformInfo::new().unwrap(); let sysname = info.sysname().to_string_lossy(); let nodename = info.nodename().to_string_lossy(); let release = info.release().to_string_lossy(); let version = info.version().to_string_lossy(); let machine = info.machine().to_string_lossy(); let osname = info.osname().to_string_lossy(); let s = format!("sysname='{sysname}';nodename='{nodename}';release='{release}';version='{version}';machine='{machine}';osname='{osname}'"); println!("s = [{}]\"{}\"", s.len(), s); // let re = regex::Regex::new("[^[[:print:]]]").unwrap(); // matches invisible (and emojis) let re = regex::Regex::new("[^[[:print:]]\\p{Other_Symbol}]").unwrap(); // matches invisible only (not emojis) assert_eq!(re.find(&s), None); Ok(()) } #[test] fn platform_clone() -> Result<(), String> { let info = PlatformInfo::new().unwrap(); #[allow(clippy::redundant_clone)] // ignore `clippy::redundant_clone` warning for direct testing let info_copy = info.clone(); println!("{:?}", info); assert_eq!(info_copy, info); Ok(()) }