use serde::{Deserialize, Serialize};
#[cfg(target_os = "linux")]
use std::fs;
#[cfg(target_os = "linux")]
use std::path::Path;
use std::process::Command;
#[cfg(feature = "rhai")]
pub mod rhai;
#[derive(Debug, Serialize, Deserialize, Clone, Default)]
pub struct VirtInfo {
pub is_virtualized: bool,
pub virtualization_type: Option<String>,
pub hypervisor: Option<String>,
pub container: Option<String>,
pub platform: String, }
pub fn get_virt_what() -> VirtInfo {
let mut info = VirtInfo::default();
#[cfg(target_os = "linux")]
detect_linux(&mut info);
#[cfg(target_os = "macos")]
detect_macos(&mut info);
info.is_virtualized = info.virtualization_type.is_some() || info.container.is_some();
info
}
#[cfg(target_os = "linux")]
fn detect_linux(info: &mut VirtInfo) {
info.platform = "linux".to_string();
#[cfg(target_arch = "x86_64")]
{
info.platform = "linux-x86_64".to_string();
}
#[cfg(target_arch = "aarch64")]
{
info.platform = "linux-aarch64".to_string();
}
if Path::new("/.dockerenv").exists() {
info.container = Some("docker".to_string());
} else if let Ok(cgroup) = fs::read_to_string("/proc/self/cgroup") {
if cgroup.contains("docker") {
info.container = Some("docker".to_string());
}
}
if Path::new("/run/.containerenv").exists() {
info.container = Some("podman".to_string());
}
if let Ok(product_name) = fs::read_to_string("/sys/class/dmi/id/product_name") {
let product_name = product_name.trim().to_lowercase();
if product_name.contains("kvm") {
info.virtualization_type = Some("kvm".to_string());
} else if product_name.contains("qemu") {
info.virtualization_type = Some("qemu".to_string());
} else if product_name.contains("vmware") {
info.virtualization_type = Some("vmware".to_string());
} else if product_name.contains("virtualbox") {
info.virtualization_type = Some("virtualbox".to_string());
} else if product_name.contains("xen") {
info.virtualization_type = Some("xen".to_string());
} else if product_name.contains("hyper-v") {
info.virtualization_type = Some("hyper-v".to_string());
}
}
if let Ok(version) = fs::read_to_string("/proc/version") {
if version.to_lowercase().contains("microsoft") || version.to_lowercase().contains("wsl") {
info.virtualization_type = Some("wsl".to_string());
info.hypervisor = Some("hyper-v".to_string());
}
}
if let Ok(cpuinfo) = fs::read_to_string("/proc/cpuinfo") {
if cpuinfo.contains("hypervisor") {
if info.virtualization_type.is_none() {
info.virtualization_type = Some("generic-vm".to_string());
}
}
}
}
#[cfg(target_os = "macos")]
fn detect_macos(info: &mut VirtInfo) {
info.platform = "macos".to_string();
#[cfg(target_arch = "aarch64")]
{
info.platform = "apple-silicon".to_string();
}
#[cfg(target_arch = "x86_64")]
{
info.platform = "intel-mac".to_string();
}
let output = Command::new("sysctl")
.args(["-n", "machdep.cpu.features"])
.output();
if let Ok(output) = output {
let stdout = String::from_utf8_lossy(&output.stdout).to_lowercase();
if stdout.contains("vmm") {
info.virtualization_type = Some("generic-vm".to_string());
}
}
let ioreg = Command::new("ioreg")
.args(["-l"])
.output();
if let Ok(ioreg) = ioreg {
let stdout = String::from_utf8_lossy(&ioreg.stdout).to_lowercase();
if stdout.contains("apple-virt-io") || stdout.contains("applevirtualplatform") {
info.virtualization_type = Some("apple-virtualization".to_string());
} else if stdout.contains("vmware") {
info.virtualization_type = Some("vmware".to_string());
} else if stdout.contains("parallels") {
info.virtualization_type = Some("parallels".to_string());
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_virt_what() {
let info = get_virt_what();
println!("Detected info: {:?}", info);
assert!(!info.platform.is_empty());
#[cfg(target_os = "macos")]
{
assert!(info.platform.contains("mac") || info.platform.contains("apple"));
}
#[cfg(target_os = "linux")]
{
assert!(info.platform.contains("linux"));
}
}
#[cfg(feature = "rhai")]
#[test]
fn test_virtwhat_rhai() {
use ::rhai::Engine;
let mut engine = Engine::new();
crate::virt_what::rhai::register_virtwhat_module(&mut engine).unwrap();
let result = engine.eval::<::rhai::Map>("virt_what()").unwrap();
assert!(result.contains_key("platform"));
assert!(result.contains_key("is_virtualized"));
let platform = result
.get("platform")
.unwrap()
.clone()
.into_string()
.unwrap();
assert!(!platform.is_empty());
}
#[test]
#[cfg(target_os = "macos")]
fn test_detect_macos() {
let mut info = VirtInfo::default();
detect_macos(&mut info);
assert!(info.platform.contains("mac") || info.platform.contains("apple"));
}
}