From 5c578e0ced8a8c305fbbfdd338f001a0c0b97efc Mon Sep 17 00:00:00 2001 From: dormant-user Date: Fri, 27 Sep 2024 17:15:29 -0500 Subject: [PATCH] Add GPU information to basic system information --- src/legacy/{cpu_brand.rs => cpu.rs} | 0 src/legacy/gpu.rs | 134 ++++++++++++++++++++++++++++ src/legacy/mod.rs | 4 +- src/resources/info.rs | 22 ++++- 4 files changed, 157 insertions(+), 3 deletions(-) rename src/legacy/{cpu_brand.rs => cpu.rs} (100%) create mode 100644 src/legacy/gpu.rs diff --git a/src/legacy/cpu_brand.rs b/src/legacy/cpu.rs similarity index 100% rename from src/legacy/cpu_brand.rs rename to src/legacy/cpu.rs diff --git a/src/legacy/gpu.rs b/src/legacy/gpu.rs new file mode 100644 index 0000000..560df41 --- /dev/null +++ b/src/legacy/gpu.rs @@ -0,0 +1,134 @@ +use std::collections::HashMap; +use serde_json::Value; +use crate::squire; + +pub fn get_gpu_info() -> Vec> { + let operating_system = std::env::consts::OS; + match operating_system { + "macos" => get_gpu_info_darwin("/usr/sbin/system_profiler"), + "linux" => get_gpu_info_linux("/usr/bin/lspci"), + "windows" => get_gpu_info_windows("C:\\Windows\\System32\\wbem\\wmic.exe"), + _ => { + log::error!("Unsupported operating system: {}", operating_system); + Vec::new() + } + } +} + +fn get_gpu_info_darwin(lib_path: &str) -> Vec> { + let result = squire::util::run_command( + lib_path, + &["SPDisplaysDataType", "-json"], + true + ); + if result.is_err() { + return Vec::new(); + } + let json: Value = serde_json::from_str(&result.unwrap()).unwrap(); + + let displays = json["SPDisplaysDataType"].as_array().unwrap(); + let mut gpu_info = Vec::new(); + + let na = "N/A".to_string(); + for display in displays { + if let Some(model) = display.get("sppci_model") { + let mut info = HashMap::new(); + info.insert( + "model".to_string(), + model.as_str() + .unwrap_or(na.as_str()) + .to_string() + ); + + // Handle cores + info.insert( + "cores".to_string(), + display.get("sppci_cores") + .or(display.get("spdisplays_cores")) + .and_then(|v| v.as_str()) + .unwrap_or(&na) + .to_string(), + ); + + // Handle memory + info.insert( + "memory".to_string(), + display.get("sppci_vram") + .or(display.get("spdisplays_vram")) + .and_then(|v| v.as_str()) + .unwrap_or(&na) + .to_string(), + ); + + // Handle vendor + info.insert( + "vendor".to_string(), + display.get("sppci_vendor") + .and_then(|v| v.as_str()) + .unwrap_or(&na) + .to_string(), + ); + + gpu_info.push(info); + } + } + gpu_info +} + +fn get_gpu_info_linux(lib_path: &str) -> Vec> { + let result = squire::util::run_command( + lib_path, + &[], + true + ); + if result.is_err() { + return Vec::new(); + } + let mut gpu_info = Vec::new(); + for line in result.unwrap().lines() { + if line.contains("VGA") { + let gpu = line.split(':').last().unwrap().trim(); + let mut info = HashMap::new(); + info.insert("model".to_string(), gpu.to_string()); + gpu_info.push(info); + } + } + gpu_info +} + +fn get_gpu_info_windows(lib_path: &str) -> Vec> { + let result = squire::util::run_command( + lib_path, + &["path", "win32_videocontroller", "get", "Name,AdapterCompatibility", "/format:csv"], + true + ); + if result.is_err() { + return Vec::new(); + } + let stdout = result.unwrap(); + let gpus_raw: Vec<&str> = stdout.lines().filter(|line| !line.trim().is_empty()).collect(); + if gpus_raw.is_empty() { + return vec![]; + } + + let keys: Vec = gpus_raw[0] + .replace("Node", "node") + .replace("AdapterCompatibility", "vendor") + .replace("Name", "model") + .split(',') + .map(|key| key.to_string()) + .collect(); + + let mut gpu_info = Vec::new(); + + for values in gpus_raw[1..].chunks(keys.len()) { + if values.len() == keys.len() { + let mut info = HashMap::new(); + for (key, value) in keys.iter().zip(values.iter()) { + info.insert(key.clone(), value.to_string()); + } + gpu_info.push(info); + } + } + gpu_info +} diff --git a/src/legacy/mod.rs b/src/legacy/mod.rs index 927d4c1..487e2c6 100644 --- a/src/legacy/mod.rs +++ b/src/legacy/mod.rs @@ -1,4 +1,6 @@ /// This module contains functions that gathers CPU brand information. -pub mod cpu_brand; +pub mod cpu; +/// This module contains functions that gathers GPU brand information. +pub mod gpu; /// This module contains disk related functions. pub mod disks; diff --git a/src/resources/info.rs b/src/resources/info.rs index d24ba7b..7edab60 100644 --- a/src/resources/info.rs +++ b/src/resources/info.rs @@ -38,6 +38,19 @@ pub fn get_disks(disks: &Disks) -> Vec> { disks_info } + +fn get_gpu_info() -> Vec { + let gpu_info = legacy::gpu::get_gpu_info(); + log::info!("GPUs: {:?}", gpu_info); + let mut gpus: Vec = vec![]; + for gpu in gpu_info { + if let Some(gpu_model) = gpu.get("model") { + gpus.push(gpu_model.to_string()); + } + } + gpus +} + /// Function to get CPU brand names as a comma separated string. /// /// # Arguments @@ -54,7 +67,7 @@ fn get_cpu_brand(sys: &System) -> String { cpu_brands.insert(cpu.brand().to_string()); } if cpu_brands.is_empty() { - let legacy_cpu_brand_name = legacy::cpu_brand::get_name(); + let legacy_cpu_brand_name = legacy::cpu::get_name(); return if let Some(cpu_brand) = legacy_cpu_brand_name { log::debug!("Using legacy methods for CPU brand!!"); cpu_brand @@ -97,7 +110,7 @@ pub fn get_sys_info(disks: &Disks) -> HashMap<&'static str, HashMap<&'static str // Basic and Memory/Storage Info let os_arch = resources::system::os_arch(); - let basic = HashMap::from_iter(vec![ + let mut basic = HashMap::from_iter(vec![ ("Hostname", System::host_name().unwrap_or("Unknown".to_string())), ("Operating System", squire::util::capwords(&os_arch.name, None)), ("Architecture", os_arch.architecture), @@ -105,6 +118,11 @@ pub fn get_sys_info(disks: &Disks) -> HashMap<&'static str, HashMap<&'static str ("CPU cores", sys.cpus().len().to_string()), ("CPU brand", get_cpu_brand(&sys)) ]); + let gpu_info = get_gpu_info(); + if !gpu_info.is_empty() { + let key = if gpu_info.len() == 1 { "GPU" } else { "GPUs" }; + basic.insert(key, gpu_info.join(", ")); + } let mut hash_vec = vec![ ("Memory", total_memory), ("Storage", total_storage)