mirror of
https://github.com/velopack/velopack.git
synced 2025-10-25 15:19:22 +00:00
Use IsWow64Process2 to determine windows process architecture
This commit is contained in:
12
src/Rust/Cargo.lock
generated
12
src/Rust/Cargo.lock
generated
@@ -994,6 +994,17 @@ dependencies = [
|
|||||||
"vcpkg",
|
"vcpkg",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "os_info"
|
||||||
|
version = "3.8.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092"
|
||||||
|
dependencies = [
|
||||||
|
"log",
|
||||||
|
"serde",
|
||||||
|
"windows-sys 0.52.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.3.1"
|
version = "2.3.1"
|
||||||
@@ -1586,6 +1597,7 @@ dependencies = [
|
|||||||
"native-tls",
|
"native-tls",
|
||||||
"normpath",
|
"normpath",
|
||||||
"ntest",
|
"ntest",
|
||||||
|
"os_info",
|
||||||
"pretty-bytes-rust",
|
"pretty-bytes-rust",
|
||||||
"pretty_assertions",
|
"pretty_assertions",
|
||||||
"rand",
|
"rand",
|
||||||
|
|||||||
@@ -78,6 +78,7 @@ url = "2.5"
|
|||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0"
|
serde_json = "1.0"
|
||||||
time = "0.3"
|
time = "0.3"
|
||||||
|
os_info = "3.8"
|
||||||
|
|
||||||
[target.'cfg(unix)'.dependencies]
|
[target.'cfg(unix)'.dependencies]
|
||||||
native-dialog = "0.7"
|
native-dialog = "0.7"
|
||||||
@@ -101,6 +102,7 @@ windows = { version = "0.52", default-features = false, features = [
|
|||||||
"Win32_UI",
|
"Win32_UI",
|
||||||
"Win32_UI_Shell",
|
"Win32_UI_Shell",
|
||||||
"Win32_System_Threading",
|
"Win32_System_Threading",
|
||||||
|
"Win32_System_SystemInformation",
|
||||||
"Win32_System_Variant",
|
"Win32_System_Variant",
|
||||||
"Win32_Storage_EnhancedStorage",
|
"Win32_Storage_EnhancedStorage",
|
||||||
"Win32_Storage_FileSystem",
|
"Win32_Storage_FileSystem",
|
||||||
@@ -123,13 +125,6 @@ windows-sys = { version = "0.52", default-features = false, features = [
|
|||||||
"Wdk",
|
"Wdk",
|
||||||
"Wdk_System",
|
"Wdk_System",
|
||||||
"Wdk_System_Threading",
|
"Wdk_System_Threading",
|
||||||
# below are just for os_info
|
|
||||||
"Win32_UI_WindowsAndMessaging",
|
|
||||||
"Win32_System_Diagnostics_Debug",
|
|
||||||
"Win32_System_LibraryLoader",
|
|
||||||
"Win32_System_SystemInformation",
|
|
||||||
"Win32_System_Registry",
|
|
||||||
"Win32_System_SystemServices",
|
|
||||||
] }
|
] }
|
||||||
normpath = "1.0.1"
|
normpath = "1.0.1"
|
||||||
webview2-com = "0.28.0"
|
webview2-com = "0.28.0"
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
use crate::{dialogs, shared, shared::bundle, windows};
|
use crate::{
|
||||||
|
dialogs,
|
||||||
|
shared::{self, bundle, runtime_arch::RuntimeArch},
|
||||||
|
windows,
|
||||||
|
};
|
||||||
use anyhow::{anyhow, bail, Result};
|
use anyhow::{anyhow, bail, Result};
|
||||||
use memmap2::Mmap;
|
use memmap2::Mmap;
|
||||||
use pretty_bytes_rust::pretty_bytes;
|
use pretty_bytes_rust::pretty_bytes;
|
||||||
@@ -11,8 +15,9 @@ use std::{
|
|||||||
use winsafe::{self as w, co};
|
use winsafe::{self as w, co};
|
||||||
|
|
||||||
pub fn install(debug_pkg: Option<&PathBuf>, install_to: Option<&PathBuf>) -> Result<()> {
|
pub fn install(debug_pkg: Option<&PathBuf>, install_to: Option<&PathBuf>) -> Result<()> {
|
||||||
let osinfo = windows::os_info::get();
|
let osinfo = os_info::get();
|
||||||
info!("OS: {}, Arch={}", osinfo, osinfo.architecture().unwrap_or("unknown"));
|
let osarch = RuntimeArch::from_current_system();
|
||||||
|
info!("OS: {osinfo}, Arch={osarch:#?}");
|
||||||
|
|
||||||
if !w::IsWindows7OrGreater()? {
|
if !w::IsWindows7OrGreater()? {
|
||||||
bail!("This installer requires Windows 7 or later and cannot run.");
|
bail!("This installer requires Windows 7 or later and cannot run.");
|
||||||
@@ -121,7 +126,7 @@ pub fn install(debug_pkg: Option<&PathBuf>, install_to: Option<&PathBuf>) -> Res
|
|||||||
let splash_bytes = pkg.get_splash_bytes();
|
let splash_bytes = pkg.get_splash_bytes();
|
||||||
windows::splash::show_splash_dialog(app.title.to_owned(), splash_bytes)
|
windows::splash::show_splash_dialog(app.title.to_owned(), splash_bytes)
|
||||||
};
|
};
|
||||||
|
|
||||||
let install_result = install_impl(&pkg, &root_path, &tx);
|
let install_result = install_impl(&pkg, &root_path, &tx);
|
||||||
let _ = tx.send(windows::splash::MSG_CLOSE);
|
let _ = tx.send(windows::splash::MSG_CLOSE);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
pub mod bundle;
|
pub mod bundle;
|
||||||
pub mod download;
|
pub mod download;
|
||||||
pub mod macho;
|
pub mod macho;
|
||||||
|
pub mod runtime_arch;
|
||||||
|
|
||||||
mod dialogs_const;
|
mod dialogs_const;
|
||||||
mod dialogs_common;
|
mod dialogs_common;
|
||||||
|
|||||||
102
src/Rust/src/shared/runtime_arch.rs
Normal file
102
src/Rust/src/shared/runtime_arch.rs
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
#[derive(PartialEq, Debug, Clone, strum::IntoStaticStr)]
|
||||||
|
pub enum RuntimeArch {
|
||||||
|
X86,
|
||||||
|
X64,
|
||||||
|
Arm64,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RuntimeArch {
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
fn from_u32(value: u32) -> Option<Self> {
|
||||||
|
match value {
|
||||||
|
0x014c => Some(RuntimeArch::X86),
|
||||||
|
0x8664 => Some(RuntimeArch::X64),
|
||||||
|
0xAA64 => Some(RuntimeArch::Arm64),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_str(arch_str: &str) -> Option<Self> {
|
||||||
|
match arch_str.to_lowercase().as_str() {
|
||||||
|
"x86" => Some(RuntimeArch::X86),
|
||||||
|
"i386" => Some(RuntimeArch::X86),
|
||||||
|
"x64" => Some(RuntimeArch::X64),
|
||||||
|
"x86_64" => Some(RuntimeArch::X64),
|
||||||
|
"arm64" => Some(RuntimeArch::Arm64),
|
||||||
|
"aarch64" => Some(RuntimeArch::Arm64),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
pub fn from_current_system() -> Option<Self> {
|
||||||
|
return check_arch_windows();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(target_os = "windows"))]
|
||||||
|
pub fn from_current_system() -> Option<Self> {
|
||||||
|
use os_info;
|
||||||
|
let info = os_info::get();
|
||||||
|
let machine = info.architecture();
|
||||||
|
if machine.is_none() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let machine = machine.unwrap();
|
||||||
|
if machine.is_empty() {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Self::from_str(machine)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
fn check_arch_windows() -> Option<RuntimeArch> {
|
||||||
|
use windows::Win32::Foundation::{FALSE, TRUE};
|
||||||
|
use windows::Win32::System::SystemInformation::IMAGE_FILE_MACHINE;
|
||||||
|
use windows::Win32::System::Threading::{GetCurrentProcess, IsWow64Process, IsWow64Process2};
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let handle = GetCurrentProcess();
|
||||||
|
|
||||||
|
let mut process_machine: IMAGE_FILE_MACHINE = Default::default();
|
||||||
|
let mut native_machine: IMAGE_FILE_MACHINE = Default::default();
|
||||||
|
if let Ok(()) = IsWow64Process2(handle, &mut process_machine, Some(&mut native_machine)) {
|
||||||
|
return RuntimeArch::from_u32(native_machine.0.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut iswow64 = FALSE;
|
||||||
|
if let Ok(()) = IsWow64Process(handle, &mut iswow64) {
|
||||||
|
if iswow64 == TRUE {
|
||||||
|
return Some(RuntimeArch::X64);
|
||||||
|
} else {
|
||||||
|
return Some(RuntimeArch::X86);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(target_arch = "x86_64")]
|
||||||
|
return Some(RuntimeArch::X64);
|
||||||
|
|
||||||
|
#[cfg(not(target_arch = "x86_64"))]
|
||||||
|
return Some(RuntimeArch::X86);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
#[cfg(target_os = "windows")]
|
||||||
|
fn test_current_architecture() {
|
||||||
|
let arch = check_arch_windows();
|
||||||
|
assert!(arch.is_some());
|
||||||
|
let arch = arch.unwrap();
|
||||||
|
assert!(arch == RuntimeArch::X64);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_cpu_arch_from_str() {
|
||||||
|
assert_eq!(RuntimeArch::from_str("x86"), Some(RuntimeArch::X86));
|
||||||
|
assert_eq!(RuntimeArch::from_str("x64"), Some(RuntimeArch::X64));
|
||||||
|
assert_eq!(RuntimeArch::from_str("arm64"), Some(RuntimeArch::Arm64));
|
||||||
|
assert_eq!(RuntimeArch::from_str("foo"), None);
|
||||||
|
assert_eq!(RuntimeArch::from_str("X86"), Some(RuntimeArch::X86));
|
||||||
|
assert_eq!(RuntimeArch::from_str("X64"), Some(RuntimeArch::X64));
|
||||||
|
assert_eq!(RuntimeArch::from_str("ARM64"), Some(RuntimeArch::Arm64));
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
pub mod locksmith;
|
pub mod locksmith;
|
||||||
pub mod mitigate;
|
pub mod mitigate;
|
||||||
pub mod os_info;
|
|
||||||
pub mod prerequisite;
|
pub mod prerequisite;
|
||||||
pub mod runtimes;
|
pub mod runtimes;
|
||||||
pub mod splash;
|
pub mod splash;
|
||||||
|
|||||||
@@ -1,820 +0,0 @@
|
|||||||
// spell-checker:ignore dword, minwindef, ntdef, ntdll, ntstatus, osversioninfoex, osversioninfoexa
|
|
||||||
// spell-checker:ignore osversioninfoexw, serverr, sysinfoapi, winnt, winuser, pbool, libloaderapi
|
|
||||||
// spell-checker:ignore lpcstr, processthreadsapi, farproc, lstatus, wchar, lpbyte, hkey, winerror
|
|
||||||
// spell-checker:ignore osstr, winreg
|
|
||||||
|
|
||||||
#![allow(unsafe_code)]
|
|
||||||
|
|
||||||
use std::fmt::{self, Display, Formatter};
|
|
||||||
use std::{
|
|
||||||
ffi::{OsStr, OsString},
|
|
||||||
mem::{self, MaybeUninit},
|
|
||||||
os::windows::ffi::{OsStrExt, OsStringExt},
|
|
||||||
ptr,
|
|
||||||
};
|
|
||||||
|
|
||||||
use windows_sys::Win32::{
|
|
||||||
Foundation::{ERROR_SUCCESS, FARPROC, NTSTATUS, STATUS_SUCCESS},
|
|
||||||
System::{
|
|
||||||
SystemInformation::{
|
|
||||||
PROCESSOR_ARCHITECTURE_AMD64, PROCESSOR_ARCHITECTURE_ARM, PROCESSOR_ARCHITECTURE_IA64,
|
|
||||||
PROCESSOR_ARCHITECTURE_INTEL,
|
|
||||||
},
|
|
||||||
LibraryLoader::{GetModuleHandleA, GetProcAddress},
|
|
||||||
Registry::{RegOpenKeyExW, RegQueryValueExW, HKEY_LOCAL_MACHINE, KEY_READ, REG_SZ},
|
|
||||||
SystemInformation::{GetNativeSystemInfo, GetSystemInfo, SYSTEM_INFO},
|
|
||||||
SystemServices::{VER_NT_WORKSTATION, VER_SUITE_WH_SERVER},
|
|
||||||
},
|
|
||||||
UI::WindowsAndMessaging::{GetSystemMetrics, SM_SERVERR2},
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
||||||
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
|
|
||||||
pub struct Info {
|
|
||||||
/// Operating system type. See `Type` for details.
|
|
||||||
pub(crate) os_type: Type,
|
|
||||||
/// Operating system version. See `Version` for details.
|
|
||||||
pub(crate) version: Version,
|
|
||||||
/// Operating system edition.
|
|
||||||
pub(crate) edition: Option<String>,
|
|
||||||
/// Operating system codename.
|
|
||||||
pub(crate) codename: Option<String>,
|
|
||||||
/// Operating system architecture in terms of how many bits compose the basic values it can deal
|
|
||||||
/// with. See `Bitness` for details.
|
|
||||||
pub(crate) bitness: Bitness,
|
|
||||||
/// Processor architecture.
|
|
||||||
pub(crate) architecture: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Info {
|
|
||||||
/// Constructs a new `Info` instance with unknown type, version and bitness.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use os_info::{Info, Type, Version, Bitness};
|
|
||||||
///
|
|
||||||
/// let info = Info::unknown();
|
|
||||||
/// assert_eq!(Type::Unknown, info.os_type());
|
|
||||||
/// assert_eq!(&Version::Unknown, info.version());
|
|
||||||
/// assert_eq!(None, info.edition());
|
|
||||||
/// assert_eq!(None, info.codename());
|
|
||||||
/// assert_eq!(Bitness::Unknown, info.bitness());
|
|
||||||
/// assert_eq!(None, info.architecture());
|
|
||||||
/// ```
|
|
||||||
pub fn unknown() -> Self {
|
|
||||||
Self {
|
|
||||||
os_type: Type::Unknown,
|
|
||||||
version: Version::Unknown,
|
|
||||||
edition: None,
|
|
||||||
codename: None,
|
|
||||||
bitness: Bitness::Unknown,
|
|
||||||
architecture: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs a new `Info` instance with the specified operating system type.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use os_info::{Info, Type, Version, Bitness};
|
|
||||||
///
|
|
||||||
/// let os_type = Type::Linux;
|
|
||||||
/// let info = Info::with_type(os_type);
|
|
||||||
/// assert_eq!(os_type, info.os_type());
|
|
||||||
/// assert_eq!(&Version::Unknown, info.version());
|
|
||||||
/// assert_eq!(None, info.edition());
|
|
||||||
/// assert_eq!(None, info.codename());
|
|
||||||
/// assert_eq!(Bitness::Unknown, info.bitness());
|
|
||||||
/// assert_eq!(None, info.architecture());
|
|
||||||
/// ```
|
|
||||||
pub fn with_type(os_type: Type) -> Self {
|
|
||||||
Self {
|
|
||||||
os_type,
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns operating system type. See `Type` for details.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use os_info::{Info, Type};
|
|
||||||
///
|
|
||||||
/// let info = Info::unknown();
|
|
||||||
/// assert_eq!(Type::Unknown, info.os_type());
|
|
||||||
/// ```
|
|
||||||
pub fn os_type(&self) -> Type {
|
|
||||||
self.os_type
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns operating system version. See `Version` for details.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use os_info::{Info, Version};
|
|
||||||
///
|
|
||||||
/// let info = Info::unknown();
|
|
||||||
/// assert_eq!(&Version::Unknown, info.version());
|
|
||||||
/// ```
|
|
||||||
pub fn version(&self) -> &Version {
|
|
||||||
&self.version
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns optional operation system edition.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use os_info::Info;
|
|
||||||
///
|
|
||||||
/// let info = Info::unknown();
|
|
||||||
/// assert_eq!(None, info.edition());
|
|
||||||
pub fn edition(&self) -> Option<&str> {
|
|
||||||
self.edition.as_ref().map(String::as_ref)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns optional operation system 'codename'.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use os_info::Info;
|
|
||||||
///
|
|
||||||
/// let info = Info::unknown();
|
|
||||||
/// assert_eq!(None, info.codename());
|
|
||||||
pub fn codename(&self) -> Option<&str> {
|
|
||||||
self.codename.as_ref().map(String::as_ref)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns operating system bitness. See `Bitness` for details.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use os_info::{Info, Bitness};
|
|
||||||
///
|
|
||||||
/// let info = Info::unknown();
|
|
||||||
/// assert_eq!(Bitness::Unknown, info.bitness());
|
|
||||||
/// ```
|
|
||||||
pub fn bitness(&self) -> Bitness {
|
|
||||||
self.bitness
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns operating system architecture.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use os_info::Info;
|
|
||||||
///
|
|
||||||
/// let info = Info::unknown();
|
|
||||||
/// assert_eq!(None, info.architecture());
|
|
||||||
pub fn architecture(&self) -> Option<&str> {
|
|
||||||
self.architecture.as_ref().map(String::as_ref)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Info {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self::unknown()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Info {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
write!(f, "{}", self.os_type)?;
|
|
||||||
if self.version != Version::Unknown {
|
|
||||||
write!(f, " {}", self.version)?;
|
|
||||||
}
|
|
||||||
if let Some(ref edition) = self.edition {
|
|
||||||
write!(f, " ({edition})")?;
|
|
||||||
}
|
|
||||||
if let Some(ref codename) = self.codename {
|
|
||||||
write!(f, " ({codename})")?;
|
|
||||||
}
|
|
||||||
write!(f, " [{}]", self.bitness)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
||||||
#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
|
|
||||||
#[non_exhaustive]
|
|
||||||
pub enum Type {
|
|
||||||
/// IBM AIX (<https://en.wikipedia.org/wiki/IBM_AIX>).
|
|
||||||
AIX,
|
|
||||||
/// Alpaquita Linux (<https://bell-sw.com/alpaquita-linux/>).
|
|
||||||
Alpaquita,
|
|
||||||
/// Alpine Linux (<https://en.wikipedia.org/wiki/Alpine_Linux>).
|
|
||||||
Alpine,
|
|
||||||
/// Amazon Linux AMI (<https://en.wikipedia.org/wiki/Amazon_Machine_Image#Amazon_Linux_AMI>).
|
|
||||||
Amazon,
|
|
||||||
/// Android (<https://en.wikipedia.org/wiki/Android_(operating_system)>).
|
|
||||||
Android,
|
|
||||||
/// Arch Linux (<https://en.wikipedia.org/wiki/Arch_Linux>).
|
|
||||||
Arch,
|
|
||||||
/// Artix Linux (<https://en.wikipedia.org/wiki/Artix_Linux>).
|
|
||||||
Artix,
|
|
||||||
/// CentOS (<https://en.wikipedia.org/wiki/CentOS>).
|
|
||||||
CentOS,
|
|
||||||
/// Debian (<https://en.wikipedia.org/wiki/Debian>).
|
|
||||||
Debian,
|
|
||||||
/// DragonFly BSD (<https://en.wikipedia.org/wiki/DragonFly_BSD>).
|
|
||||||
DragonFly,
|
|
||||||
/// Emscripten (<https://en.wikipedia.org/wiki/Emscripten>).
|
|
||||||
Emscripten,
|
|
||||||
/// EndeavourOS (<https://en.wikipedia.org/wiki/EndeavourOS>).
|
|
||||||
EndeavourOS,
|
|
||||||
/// Fedora (<https://en.wikipedia.org/wiki/Fedora_(operating_system)>).
|
|
||||||
Fedora,
|
|
||||||
/// FreeBSD (<https://en.wikipedia.org/wiki/FreeBSD>).
|
|
||||||
FreeBSD,
|
|
||||||
/// Garuda Linux (<https://en.wikipedia.org/wiki/Garuda_Linux>)
|
|
||||||
Garuda,
|
|
||||||
/// Gentoo Linux (<https://en.wikipedia.org/wiki/Gentoo_Linux>).
|
|
||||||
Gentoo,
|
|
||||||
/// HardenedBSD (https://hardenedbsd.org/).
|
|
||||||
HardenedBSD,
|
|
||||||
/// Illumos (https://en.wikipedia.org/wiki/Illumos).
|
|
||||||
Illumos,
|
|
||||||
/// Kali Linux (https://en.wikipedia.org/wiki/Kali_Linux).
|
|
||||||
Kali,
|
|
||||||
/// Linux based operating system (<https://en.wikipedia.org/wiki/Linux>).
|
|
||||||
Linux,
|
|
||||||
/// Mabox (<https://maboxlinux.org/>).
|
|
||||||
Mabox,
|
|
||||||
/// Mac OS X/OS X/macOS (<https://en.wikipedia.org/wiki/MacOS>).
|
|
||||||
Macos,
|
|
||||||
/// Manjaro (<https://en.wikipedia.org/wiki/Manjaro>).
|
|
||||||
Manjaro,
|
|
||||||
/// Mariner (<https://en.wikipedia.org/wiki/CBL-Mariner>).
|
|
||||||
Mariner,
|
|
||||||
/// MidnightBSD (<https://en.wikipedia.org/wiki/MidnightBSD>).
|
|
||||||
MidnightBSD,
|
|
||||||
/// Mint (<https://en.wikipedia.org/wiki/Linux_Mint>).
|
|
||||||
Mint,
|
|
||||||
/// NetBSD (<https://en.wikipedia.org/wiki/NetBSD>).
|
|
||||||
NetBSD,
|
|
||||||
/// NixOS (<https://en.wikipedia.org/wiki/NixOS>).
|
|
||||||
NixOS,
|
|
||||||
/// OpenBSD (<https://en.wikipedia.org/wiki/OpenBSD>).
|
|
||||||
OpenBSD,
|
|
||||||
/// OpenCloudOS (<https://www.opencloudos.org>).
|
|
||||||
OpenCloudOS,
|
|
||||||
/// openEuler (<https://en.wikipedia.org/wiki/EulerOS>).
|
|
||||||
openEuler,
|
|
||||||
/// openSUSE (<https://en.wikipedia.org/wiki/OpenSUSE>).
|
|
||||||
openSUSE,
|
|
||||||
/// Oracle Linux (<https://en.wikipedia.org/wiki/Oracle_Linux>).
|
|
||||||
OracleLinux,
|
|
||||||
/// Pop!_OS (<https://en.wikipedia.org/wiki/Pop!_OS>)
|
|
||||||
Pop,
|
|
||||||
/// Raspberry Pi OS (<https://en.wikipedia.org/wiki/Raspberry_Pi_OS>).
|
|
||||||
Raspbian,
|
|
||||||
/// Red Hat Linux (<https://en.wikipedia.org/wiki/Red_Hat_Linux>).
|
|
||||||
Redhat,
|
|
||||||
/// Red Hat Enterprise Linux (<https://en.wikipedia.org/wiki/Red_Hat_Enterprise_Linux>).
|
|
||||||
RedHatEnterprise,
|
|
||||||
/// Redox (<https://en.wikipedia.org/wiki/Redox_(operating_system)>).
|
|
||||||
Redox,
|
|
||||||
/// Solus (<https://en.wikipedia.org/wiki/Solus_(operating_system)>).
|
|
||||||
Solus,
|
|
||||||
/// SUSE Linux Enterprise Server (<https://en.wikipedia.org/wiki/SUSE_Linux_Enterprise>).
|
|
||||||
SUSE,
|
|
||||||
/// Ubuntu (<https://en.wikipedia.org/wiki/Ubuntu_(operating_system)>).
|
|
||||||
Ubuntu,
|
|
||||||
/// Unknown operating system.
|
|
||||||
Unknown,
|
|
||||||
/// Windows (<https://en.wikipedia.org/wiki/Microsoft_Windows>).
|
|
||||||
Windows,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Type {
|
|
||||||
fn default() -> Self {
|
|
||||||
Type::Unknown
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Type {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
match *self {
|
|
||||||
Type::Alpaquita => write!(f, "Alpaquita Linux"),
|
|
||||||
Type::Alpine => write!(f, "Alpine Linux"),
|
|
||||||
Type::Amazon => write!(f, "Amazon Linux AMI"),
|
|
||||||
Type::Arch => write!(f, "Arch Linux"),
|
|
||||||
Type::Artix => write!(f, "Artix Linux"),
|
|
||||||
Type::DragonFly => write!(f, "DragonFly BSD"),
|
|
||||||
Type::Garuda => write!(f, "Garuda Linux"),
|
|
||||||
Type::Gentoo => write!(f, "Gentoo Linux"),
|
|
||||||
Type::Illumos => write!(f, "illumos"),
|
|
||||||
Type::Kali => write!(f, "Kali Linux"),
|
|
||||||
Type::Macos => write!(f, "Mac OS"),
|
|
||||||
Type::MidnightBSD => write!(f, "Midnight BSD"),
|
|
||||||
Type::Mint => write!(f, "Linux Mint"),
|
|
||||||
Type::Pop => write!(f, "Pop!_OS"),
|
|
||||||
Type::Raspbian => write!(f, "Raspberry Pi OS"),
|
|
||||||
Type::Redhat => write!(f, "Red Hat Linux"),
|
|
||||||
Type::RedHatEnterprise => write!(f, "Red Hat Enterprise Linux"),
|
|
||||||
Type::SUSE => write!(f, "SUSE Linux Enterprise Server"),
|
|
||||||
_ => write!(f, "{self:?}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
||||||
pub enum Bitness {
|
|
||||||
/// Unknown bitness (unable to determine).
|
|
||||||
Unknown,
|
|
||||||
/// 32-bit.
|
|
||||||
X32,
|
|
||||||
/// 64-bit.
|
|
||||||
X64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Bitness {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
match *self {
|
|
||||||
Bitness::Unknown => write!(f, "unknown bitness"),
|
|
||||||
Bitness::X32 => write!(f, "32-bit"),
|
|
||||||
Bitness::X64 => write!(f, "64-bit"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Operating system version.
|
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
|
||||||
pub enum Version {
|
|
||||||
/// Unknown version.
|
|
||||||
Unknown,
|
|
||||||
/// Semantic version (major.minor.patch).
|
|
||||||
Semantic(u64, u64, u64),
|
|
||||||
/// Rolling version. Optionally contains the release date in the string format.
|
|
||||||
Rolling(Option<String>),
|
|
||||||
/// Custom version format.
|
|
||||||
Custom(String),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Version {
|
|
||||||
/// Constructs `VersionType` from the given string.
|
|
||||||
///
|
|
||||||
/// Returns `VersionType::Unknown` if the string is empty. If it can be parsed as a semantic
|
|
||||||
/// version, then `VersionType::Semantic`, otherwise `VersionType::Custom`.
|
|
||||||
///
|
|
||||||
/// # Examples
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use os_info::Version;
|
|
||||||
///
|
|
||||||
/// let v = Version::from_string("custom");
|
|
||||||
/// assert_eq!(Version::Custom("custom".to_owned()), v);
|
|
||||||
///
|
|
||||||
/// let v = Version::from_string("1.2.3");
|
|
||||||
/// assert_eq!(Version::Semantic(1, 2, 3), v);
|
|
||||||
/// ```
|
|
||||||
pub fn from_string<S: Into<String> + AsRef<str>>(s: S) -> Self {
|
|
||||||
if s.as_ref().is_empty() {
|
|
||||||
Self::Unknown
|
|
||||||
} else if let Some((major, minor, patch)) = parse_version(s.as_ref()) {
|
|
||||||
Self::Semantic(major, minor, patch)
|
|
||||||
} else {
|
|
||||||
Self::Custom(s.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Version {
|
|
||||||
fn default() -> Self {
|
|
||||||
Version::Unknown
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Display for Version {
|
|
||||||
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
|
|
||||||
match *self {
|
|
||||||
Self::Unknown => f.write_str("Unknown"),
|
|
||||||
Self::Semantic(major, minor, patch) => write!(f, "{major}.{minor}.{patch}"),
|
|
||||||
Self::Rolling(ref date) => {
|
|
||||||
let date = match date {
|
|
||||||
Some(date) => format!(" ({date})"),
|
|
||||||
None => "".to_owned(),
|
|
||||||
};
|
|
||||||
write!(f, "Rolling Release{date}")
|
|
||||||
}
|
|
||||||
Self::Custom(ref version) => write!(f, "{version}"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn parse_version(s: &str) -> Option<(u64, u64, u64)> {
|
|
||||||
let mut iter = s.trim().split_terminator('.').fuse();
|
|
||||||
|
|
||||||
let major = iter.next().and_then(|s| s.parse().ok())?;
|
|
||||||
let minor = iter.next().unwrap_or("0").parse().ok()?;
|
|
||||||
let patch = iter.next().unwrap_or("0").parse().ok()?;
|
|
||||||
|
|
||||||
if iter.next().is_some() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some((major, minor, patch))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_arch = "x86")]
|
|
||||||
type OSVERSIONINFOEX = windows_sys::Win32::System::SystemInformation::OSVERSIONINFOEXA;
|
|
||||||
|
|
||||||
#[cfg(not(target_arch = "x86"))]
|
|
||||||
type OSVERSIONINFOEX = windows_sys::Win32::System::SystemInformation::OSVERSIONINFOEXW;
|
|
||||||
|
|
||||||
pub fn get() -> Info {
|
|
||||||
let (version, edition) = version();
|
|
||||||
let native_system_info = native_system_info();
|
|
||||||
|
|
||||||
Info {
|
|
||||||
os_type: Type::Windows,
|
|
||||||
version,
|
|
||||||
edition,
|
|
||||||
bitness: bitness(),
|
|
||||||
architecture: architecture(native_system_info),
|
|
||||||
..Default::default()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn version() -> (Version, Option<String>) {
|
|
||||||
match version_info() {
|
|
||||||
None => (Version::Unknown, None),
|
|
||||||
Some(v) => (
|
|
||||||
Version::Semantic(
|
|
||||||
v.dwMajorVersion as u64,
|
|
||||||
v.dwMinorVersion as u64,
|
|
||||||
v.dwBuildNumber as u64,
|
|
||||||
),
|
|
||||||
product_name(&v).or_else(|| edition(&v)),
|
|
||||||
),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// According to https://learn.microsoft.com/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info
|
|
||||||
// there is a variant for AMD64 CPUs, but it's not defined in generated bindings.
|
|
||||||
const PROCESSOR_ARCHITECTURE_ARM64: u16 = 12;
|
|
||||||
|
|
||||||
fn native_system_info() -> SYSTEM_INFO {
|
|
||||||
let mut system_info: MaybeUninit<SYSTEM_INFO> = MaybeUninit::zeroed();
|
|
||||||
unsafe {
|
|
||||||
GetNativeSystemInfo(system_info.as_mut_ptr());
|
|
||||||
};
|
|
||||||
|
|
||||||
unsafe { system_info.assume_init() }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn architecture(system_info: SYSTEM_INFO) -> Option<String> {
|
|
||||||
let cpu_architecture = unsafe { system_info.Anonymous.Anonymous.wProcessorArchitecture };
|
|
||||||
|
|
||||||
match cpu_architecture {
|
|
||||||
PROCESSOR_ARCHITECTURE_AMD64 => Some("x86_64"),
|
|
||||||
PROCESSOR_ARCHITECTURE_IA64 => Some("ia64"),
|
|
||||||
PROCESSOR_ARCHITECTURE_ARM => Some("arm"),
|
|
||||||
PROCESSOR_ARCHITECTURE_ARM64 => Some("aarch64"),
|
|
||||||
PROCESSOR_ARCHITECTURE_INTEL => Some("i386"),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
.map(str::to_string)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "64")]
|
|
||||||
fn bitness() -> Bitness {
|
|
||||||
// x64 program can only run on x64 Windows.
|
|
||||||
Bitness::X64
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "32")]
|
|
||||||
fn bitness() -> Bitness {
|
|
||||||
use windows_sys::Win32::Foundation::{BOOL, FALSE, HANDLE};
|
|
||||||
use windows_sys::Win32::System::Threading::GetCurrentProcess;
|
|
||||||
|
|
||||||
// IsWow64Process is not available on all supported versions of Windows. Use GetModuleHandle to
|
|
||||||
// get a handle to the DLL that contains the function and GetProcAddress to get a pointer to the
|
|
||||||
// function if available.
|
|
||||||
let is_wow_64 = match get_proc_address(b"kernel32\0", b"IsWow64Process\0") {
|
|
||||||
None => return Bitness::Unknown,
|
|
||||||
Some(val) => val,
|
|
||||||
};
|
|
||||||
|
|
||||||
type IsWow64 = unsafe extern "system" fn(HANDLE, *mut BOOL) -> BOOL;
|
|
||||||
let is_wow_64: IsWow64 = unsafe { mem::transmute(is_wow_64) };
|
|
||||||
|
|
||||||
let mut result = FALSE;
|
|
||||||
if unsafe { is_wow_64(GetCurrentProcess(), &mut result) } == 0 {
|
|
||||||
log::error!("IsWow64Process failed");
|
|
||||||
return Bitness::Unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
if result == FALSE {
|
|
||||||
Bitness::X32
|
|
||||||
} else {
|
|
||||||
Bitness::X64
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calls the Win32 API function RtlGetVersion to get the OS version information:
|
|
||||||
// https://msdn.microsoft.com/library/mt723418(v=vs.85).aspx
|
|
||||||
fn version_info() -> Option<OSVERSIONINFOEX> {
|
|
||||||
let rtl_get_version = match get_proc_address(b"ntdll\0", b"RtlGetVersion\0") {
|
|
||||||
None => return None,
|
|
||||||
Some(val) => val,
|
|
||||||
};
|
|
||||||
|
|
||||||
type RtlGetVersion = unsafe extern "system" fn(&mut OSVERSIONINFOEX) -> NTSTATUS;
|
|
||||||
let rtl_get_version: RtlGetVersion = unsafe { mem::transmute(rtl_get_version) };
|
|
||||||
|
|
||||||
let mut info: OSVERSIONINFOEX = unsafe { mem::zeroed() };
|
|
||||||
info.dwOSVersionInfoSize = mem::size_of::<OSVERSIONINFOEX>() as u32;
|
|
||||||
|
|
||||||
if unsafe { rtl_get_version(&mut info) } == STATUS_SUCCESS {
|
|
||||||
Some(info)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn product_name(info: &OSVERSIONINFOEX) -> Option<String> {
|
|
||||||
let sub_key = to_wide("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion");
|
|
||||||
let mut key = Default::default();
|
|
||||||
if unsafe { RegOpenKeyExW(HKEY_LOCAL_MACHINE, sub_key.as_ptr(), 0, KEY_READ, &mut key) }
|
|
||||||
!= ERROR_SUCCESS
|
|
||||||
|| key == 0
|
|
||||||
{
|
|
||||||
log::error!("RegOpenKeyExW(HKEY_LOCAL_MACHINE, ...) failed");
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let is_win_11 = info.dwMajorVersion == 10 && info.dwBuildNumber >= 22000;
|
|
||||||
|
|
||||||
// Get size of the data.
|
|
||||||
let name = to_wide(if is_win_11 {
|
|
||||||
"EditionID"
|
|
||||||
} else {
|
|
||||||
"ProductName"
|
|
||||||
});
|
|
||||||
let mut data_type = 0;
|
|
||||||
let mut data_size = 0;
|
|
||||||
if unsafe {
|
|
||||||
RegQueryValueExW(
|
|
||||||
key,
|
|
||||||
name.as_ptr(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
&mut data_type,
|
|
||||||
ptr::null_mut(),
|
|
||||||
&mut data_size,
|
|
||||||
)
|
|
||||||
} != ERROR_SUCCESS
|
|
||||||
|| data_type != REG_SZ
|
|
||||||
|| data_size == 0
|
|
||||||
|| data_size % 2 != 0
|
|
||||||
{
|
|
||||||
log::error!("RegQueryValueExW failed");
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the data.
|
|
||||||
let mut data = vec![0u16; data_size as usize / 2];
|
|
||||||
if unsafe {
|
|
||||||
RegQueryValueExW(
|
|
||||||
key,
|
|
||||||
name.as_ptr(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
ptr::null_mut(),
|
|
||||||
data.as_mut_ptr().cast(),
|
|
||||||
&mut data_size,
|
|
||||||
)
|
|
||||||
} != ERROR_SUCCESS
|
|
||||||
|| data_size as usize != data.len() * 2
|
|
||||||
{
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type, the string may not have been
|
|
||||||
// stored with the proper terminating null characters.
|
|
||||||
match data.last() {
|
|
||||||
Some(0) => {
|
|
||||||
data.pop();
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
|
|
||||||
let value = OsString::from_wide(data.as_slice())
|
|
||||||
.to_string_lossy()
|
|
||||||
.into_owned();
|
|
||||||
|
|
||||||
if is_win_11 {
|
|
||||||
Some(format!("Windows 11 {}", value))
|
|
||||||
} else {
|
|
||||||
Some(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn to_wide(value: &str) -> Vec<u16> {
|
|
||||||
OsStr::new(value).encode_wide().chain(Some(0)).collect()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Examines data in the OSVERSIONINFOEX structure to determine the Windows edition:
|
|
||||||
// https://msdn.microsoft.com/library/windows/desktop/ms724833(v=vs.85).aspx
|
|
||||||
fn edition(version_info: &OSVERSIONINFOEX) -> Option<String> {
|
|
||||||
match (
|
|
||||||
version_info.dwMajorVersion,
|
|
||||||
version_info.dwMinorVersion,
|
|
||||||
version_info.wProductType as u32,
|
|
||||||
) {
|
|
||||||
// Windows 10.
|
|
||||||
(10, 0, VER_NT_WORKSTATION) => {
|
|
||||||
if version_info.dwBuildNumber >= 22000 {
|
|
||||||
Some("Windows 11")
|
|
||||||
} else {
|
|
||||||
Some("Windows 10")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
(10, 0, _) => Some("Windows Server 2016"),
|
|
||||||
// Windows Vista, 7, 8 and 8.1.
|
|
||||||
(6, 3, VER_NT_WORKSTATION) => Some("Windows 8.1"),
|
|
||||||
(6, 3, _) => Some("Windows Server 2012 R2"),
|
|
||||||
(6, 2, VER_NT_WORKSTATION) => Some("Windows 8"),
|
|
||||||
(6, 2, _) => Some("Windows Server 2012"),
|
|
||||||
(6, 1, VER_NT_WORKSTATION) => Some("Windows 7"),
|
|
||||||
(6, 1, _) => Some("Windows Server 2008 R2"),
|
|
||||||
(6, 0, VER_NT_WORKSTATION) => Some("Windows Vista"),
|
|
||||||
(6, 0, _) => Some("Windows Server 2008"),
|
|
||||||
// Windows 2000, Home Server, 2003 Server, 2003 R2 Server, XP and XP Professional x64.
|
|
||||||
(5, 1, _) => Some("Windows XP"),
|
|
||||||
(5, 0, _) => Some("Windows 2000"),
|
|
||||||
(5, 2, _) if unsafe { GetSystemMetrics(SM_SERVERR2) } == 0 => {
|
|
||||||
let mut info: SYSTEM_INFO = unsafe { mem::zeroed() };
|
|
||||||
unsafe { GetSystemInfo(&mut info) };
|
|
||||||
|
|
||||||
if Into::<u32>::into(version_info.wSuiteMask) & VER_SUITE_WH_SERVER
|
|
||||||
== VER_SUITE_WH_SERVER
|
|
||||||
{
|
|
||||||
Some("Windows Home Server")
|
|
||||||
} else if version_info.wProductType == VER_NT_WORKSTATION as u8
|
|
||||||
&& unsafe { info.Anonymous.Anonymous.wProcessorArchitecture }
|
|
||||||
== PROCESSOR_ARCHITECTURE_AMD64
|
|
||||||
{
|
|
||||||
Some("Windows XP Professional x64 Edition")
|
|
||||||
} else {
|
|
||||||
Some("Windows Server 2003")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
.map(str::to_string)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn get_proc_address(module: &[u8], proc: &[u8]) -> Option<FARPROC> {
|
|
||||||
assert!(
|
|
||||||
*module.last().expect("Empty module name") == 0,
|
|
||||||
"Module name should be zero-terminated"
|
|
||||||
);
|
|
||||||
assert!(
|
|
||||||
*proc.last().expect("Empty procedure name") == 0,
|
|
||||||
"Procedure name should be zero-terminated"
|
|
||||||
);
|
|
||||||
|
|
||||||
let handle = unsafe { GetModuleHandleA(module.as_ptr()) };
|
|
||||||
if handle == 0 {
|
|
||||||
log::error!(
|
|
||||||
"GetModuleHandleA({}) failed",
|
|
||||||
String::from_utf8_lossy(module)
|
|
||||||
);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsafe { Some(GetProcAddress(handle, proc.as_ptr())) }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use super::*;
|
|
||||||
use pretty_assertions::{assert_eq, assert_ne};
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn version() {
|
|
||||||
let info = get();
|
|
||||||
assert_eq!(Type::Windows, info.os_type());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_version_info() {
|
|
||||||
let version = version_info();
|
|
||||||
assert!(version.is_some());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_edition() {
|
|
||||||
let test_data = [
|
|
||||||
(10, 0, 0, "Windows Server 2016"),
|
|
||||||
(6, 3, VER_NT_WORKSTATION, "Windows 8.1"),
|
|
||||||
(6, 3, 0, "Windows Server 2012 R2"),
|
|
||||||
(6, 2, VER_NT_WORKSTATION, "Windows 8"),
|
|
||||||
(6, 2, 0, "Windows Server 2012"),
|
|
||||||
(6, 1, VER_NT_WORKSTATION, "Windows 7"),
|
|
||||||
(6, 1, 0, "Windows Server 2008 R2"),
|
|
||||||
(6, 0, VER_NT_WORKSTATION, "Windows Vista"),
|
|
||||||
(6, 0, 0, "Windows Server 2008"),
|
|
||||||
(5, 1, 0, "Windows XP"),
|
|
||||||
(5, 1, 1, "Windows XP"),
|
|
||||||
(5, 1, 100, "Windows XP"),
|
|
||||||
(5, 0, 0, "Windows 2000"),
|
|
||||||
(5, 0, 1, "Windows 2000"),
|
|
||||||
(5, 0, 100, "Windows 2000"),
|
|
||||||
];
|
|
||||||
|
|
||||||
let mut info = version_info().unwrap();
|
|
||||||
|
|
||||||
for &(major, minor, product_type, expected_edition) in &test_data {
|
|
||||||
info.dwMajorVersion = major;
|
|
||||||
info.dwMinorVersion = minor;
|
|
||||||
info.wProductType = product_type as u8;
|
|
||||||
|
|
||||||
let edition = edition(&info).unwrap();
|
|
||||||
assert_eq!(edition, expected_edition);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_bitness() {
|
|
||||||
let b = bitness();
|
|
||||||
assert_ne!(b, Bitness::Unknown);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic(expected = "Empty module name")]
|
|
||||||
fn empty_module_name() {
|
|
||||||
get_proc_address(b"", b"RtlGetVersion\0");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic(expected = "Module name should be zero-terminated")]
|
|
||||||
fn non_zero_terminated_module_name() {
|
|
||||||
get_proc_address(b"ntdll", b"RtlGetVersion\0");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic(expected = "Empty procedure name")]
|
|
||||||
fn empty_proc_name() {
|
|
||||||
get_proc_address(b"ntdll\0", b"");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
#[should_panic(expected = "Procedure name should be zero-terminated")]
|
|
||||||
fn non_zero_terminated_proc_name() {
|
|
||||||
get_proc_address(b"ntdll\0", b"RtlGetVersion");
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn proc_address() {
|
|
||||||
let address = get_proc_address(b"ntdll\0", b"RtlGetVersion\0");
|
|
||||||
assert!(address.is_some());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_architecture() {
|
|
||||||
let cpu_types: [(u16, Option<String>); 6] = [
|
|
||||||
(PROCESSOR_ARCHITECTURE_AMD64, Some("x86_64".to_owned())),
|
|
||||||
(PROCESSOR_ARCHITECTURE_ARM, Some("arm".to_owned())),
|
|
||||||
(PROCESSOR_ARCHITECTURE_ARM64, Some("aarch64".to_owned())),
|
|
||||||
(PROCESSOR_ARCHITECTURE_IA64, Some("ia64".to_owned())),
|
|
||||||
(PROCESSOR_ARCHITECTURE_INTEL, Some("i386".to_owned())),
|
|
||||||
(0xffff, None),
|
|
||||||
];
|
|
||||||
|
|
||||||
let mut native_info = native_system_info();
|
|
||||||
|
|
||||||
for cpu_type in cpu_types {
|
|
||||||
native_info.Anonymous.Anonymous.wProcessorArchitecture = cpu_type.0;
|
|
||||||
assert_eq!(architecture(native_info), cpu_type.1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn get_product_name() {
|
|
||||||
let version = version_info().expect("version_info() failed");
|
|
||||||
let edition = product_name(&version).expect("edition() failed");
|
|
||||||
assert!(!edition.is_empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn to_wide_str() {
|
|
||||||
let data = [
|
|
||||||
("", [0x0000].as_ref()),
|
|
||||||
("U", &[0x0055, 0x0000]),
|
|
||||||
("你好", &[0x4F60, 0x597D, 0x0000]),
|
|
||||||
];
|
|
||||||
|
|
||||||
for (s, expected) in &data {
|
|
||||||
let wide = to_wide(s);
|
|
||||||
assert_eq!(&wide, expected);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
use crate::shared as util;
|
use crate::shared as util;
|
||||||
use crate::shared::download;
|
use crate::shared::download;
|
||||||
|
use crate::shared::runtime_arch::RuntimeArch;
|
||||||
use anyhow::{anyhow, bail, Result};
|
use anyhow::{anyhow, bail, Result};
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use std::process::Command as Process;
|
use std::process::Command as Process;
|
||||||
@@ -56,54 +57,6 @@ lazy_static! {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(PartialEq, Debug, Clone, strum::IntoStaticStr)]
|
|
||||||
pub enum RuntimeArch {
|
|
||||||
X86,
|
|
||||||
X64,
|
|
||||||
Arm64,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RuntimeArch {
|
|
||||||
pub fn from_str(arch_str: &str) -> Option<Self> {
|
|
||||||
match arch_str.to_lowercase().as_str() {
|
|
||||||
"x86" => Some(RuntimeArch::X86),
|
|
||||||
"x64" => Some(RuntimeArch::X64),
|
|
||||||
"arm64" => Some(RuntimeArch::Arm64),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn from_current_system() -> Option<Self> {
|
|
||||||
let info = super::os_info::get();
|
|
||||||
let machine = info.architecture();
|
|
||||||
if machine.is_none() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
let mut machine = machine.unwrap();
|
|
||||||
if machine.is_empty() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
if machine == "x86_64" {
|
|
||||||
machine = "x64";
|
|
||||||
} else if machine == "i386" {
|
|
||||||
machine = "x86";
|
|
||||||
} else if machine == "aarch64" {
|
|
||||||
machine = "arm64";
|
|
||||||
}
|
|
||||||
Self::from_str(machine)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_cpu_arch_from_str() {
|
|
||||||
assert_eq!(RuntimeArch::from_str("x86"), Some(RuntimeArch::X86));
|
|
||||||
assert_eq!(RuntimeArch::from_str("x64"), Some(RuntimeArch::X64));
|
|
||||||
assert_eq!(RuntimeArch::from_str("arm64"), Some(RuntimeArch::Arm64));
|
|
||||||
assert_eq!(RuntimeArch::from_str("foo"), None);
|
|
||||||
assert_eq!(RuntimeArch::from_str("X86"), Some(RuntimeArch::X86));
|
|
||||||
assert_eq!(RuntimeArch::from_str("X64"), Some(RuntimeArch::X64));
|
|
||||||
assert_eq!(RuntimeArch::from_str("ARM64"), Some(RuntimeArch::Arm64));
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialEq, Clone, strum::IntoStaticStr)]
|
#[derive(Debug, PartialEq, Clone, strum::IntoStaticStr)]
|
||||||
pub enum RuntimeInstallResult {
|
pub enum RuntimeInstallResult {
|
||||||
InstallSuccess,
|
InstallSuccess,
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
use crate::shared;
|
use crate::shared::{self, runtime_arch::RuntimeArch};
|
||||||
use anyhow::{anyhow, bail, Result};
|
use anyhow::{anyhow, bail, Result};
|
||||||
use normpath::PathExt;
|
use normpath::PathExt;
|
||||||
use std::{
|
use std::{
|
||||||
@@ -301,37 +301,31 @@ where
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_cpu_architecture_supported(architecture: &str) -> Result<bool> {
|
pub fn is_cpu_architecture_supported(architecture: &str) -> Result<bool> {
|
||||||
let info = super::os_info::get();
|
let machine = RuntimeArch::from_current_system();
|
||||||
let machine = info.architecture();
|
|
||||||
if machine.is_none() {
|
if machine.is_none() {
|
||||||
return Ok(true); // we can't detect current arch so try installing anyway.
|
// we can't detect current os arch so try installing anyway
|
||||||
}
|
|
||||||
|
|
||||||
let mut machine = machine.unwrap();
|
|
||||||
let is_win_11 = is_os_version_or_greater("11")?;
|
|
||||||
|
|
||||||
if machine.is_empty() || architecture.is_empty() {
|
|
||||||
return Ok(true);
|
return Ok(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://github.com/stanislav-tkach/os_info/blob/master/os_info/src/windows/winapi.rs#L82
|
let architecture = RuntimeArch::from_str(architecture);
|
||||||
if machine == "x86_64" {
|
if architecture.is_none() {
|
||||||
machine = "x64";
|
// no arch specified in this package, so install on any arch
|
||||||
} else if machine == "i386" {
|
return Ok(true);
|
||||||
machine = "x86";
|
|
||||||
} else if machine == "aarch64" {
|
|
||||||
machine = "arm64";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if machine == "x86" {
|
let machine = machine.unwrap();
|
||||||
|
let architecture = architecture.unwrap();
|
||||||
|
let is_win_11 = is_os_version_or_greater("11")?;
|
||||||
|
|
||||||
|
if machine == RuntimeArch::X86 {
|
||||||
// windows x86 only supports x86
|
// windows x86 only supports x86
|
||||||
Ok(architecture == "x86")
|
Ok(architecture == RuntimeArch::X86)
|
||||||
} else if machine == "x64" {
|
} else if machine == RuntimeArch::X64 {
|
||||||
// windows x64 only supports x86 and x64
|
// windows x64 only supports x86 and x64
|
||||||
Ok(architecture == "x86" || architecture == "x64")
|
Ok(architecture == RuntimeArch::X86 || architecture == RuntimeArch::X64)
|
||||||
} else if machine == "arm64" {
|
} else if machine == RuntimeArch::Arm64 {
|
||||||
// windows arm64 supports x86, and arm64, and only on windows 11 does it support x64
|
// windows arm64 supports x86, and arm64, and only on windows 11 does it support x64
|
||||||
Ok(architecture == "x86" || (architecture == "x64" && is_win_11) || architecture == "arm64")
|
Ok(architecture == RuntimeArch::X86 || (architecture == RuntimeArch::X64 && is_win_11) || architecture == RuntimeArch::Arm64)
|
||||||
} else {
|
} else {
|
||||||
// we don't know what this is, so try installing anyway
|
// we don't know what this is, so try installing anyway
|
||||||
Ok(true)
|
Ok(true)
|
||||||
@@ -341,7 +335,7 @@ pub fn is_cpu_architecture_supported(architecture: &str) -> Result<bool> {
|
|||||||
#[test]
|
#[test]
|
||||||
pub fn test_x64_and_x86_is_supported_but_not_arm64_or_invalid() {
|
pub fn test_x64_and_x86_is_supported_but_not_arm64_or_invalid() {
|
||||||
assert!(!is_cpu_architecture_supported("arm64").unwrap());
|
assert!(!is_cpu_architecture_supported("arm64").unwrap());
|
||||||
assert!(!is_cpu_architecture_supported("invalid").unwrap());
|
assert!(is_cpu_architecture_supported("invalid").unwrap());
|
||||||
assert!(is_cpu_architecture_supported("x64").unwrap());
|
assert!(is_cpu_architecture_supported("x64").unwrap());
|
||||||
assert!(is_cpu_architecture_supported("x86").unwrap());
|
assert!(is_cpu_architecture_supported("x86").unwrap());
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user