use anyhow::Result; use std::error::Error; use sysinfo::System; #[derive(Debug)] pub struct CpuInfo { pub name: Option, pub cores: Option, pub current_load: Option, pub current_temp: Option, pub uptime: Option, pub host_name: Option, } pub async fn get_cpu_info() -> Result> { let mut sys = System::new_all(); let cpus = sys.cpus(); let uptime = System::uptime() as f64; let host_name = System::host_name().unwrap_or_else(|| "unknown".to_string()); Ok(CpuInfo { name: Some( cpus.first() .map(|c| c.brand().to_string()) .unwrap_or_default(), ), cores: Some(cpus.len() as i32), current_load: get_cpu_load(&mut sys).await.ok(), current_temp: get_cpu_temp().await.ok(), uptime: Some(uptime), host_name: Some(host_name), }) } pub async fn get_cpu_load(sys: &mut System) -> Result> { sys.refresh_cpu_all(); tokio::task::yield_now().await; // Allow other tasks to run Ok(sys.global_cpu_usage() as f64) } pub async fn get_cpu_temp() -> Result> { println!("Attempting to get CPU temperature..."); #[cfg(target_os = "linux")] { use std::fs; use std::process::Command; println!(""); if let Ok(output) = Command::new("sensors").output() { let stdout = String::from_utf8_lossy(&output.stdout); for line in stdout.lines() { if line.contains("Package id") || line.contains("Tdie") || line.contains("CPU Temp") { if let Some(temp_str) = line .split('+') .nth(1) .and_then(|s| s.split_whitespace().next()) { if let Ok(temp) = temp_str.replace("°C", "").parse::() { return Ok(temp.into()); } } } } } // 2. Sysfs (Intel/AMD) if let Ok(content) = fs::read_to_string("/sys/class/thermal/thermal_zone0/temp") { if let Ok(temp) = content.trim().parse::() { return Ok((temp / 1000.0).into()); } } // 3. Alternative Sysfs-Pfade let paths = [ "/sys/class/hwmon/hwmontemp1_input", "/sys/class/hwmon/hwmondevice/temp1_input", ]; for path_pattern in &paths { if let Ok(paths) = glob::glob(path_pattern) { for path in paths.flatten() { if let Ok(content) = fs::read_to_string(&path) { if let Ok(temp) = content.trim().parse::() { return Ok((temp / 1000.0).into()); } } } } } Err(anyhow::anyhow!("Could not find CPU temperature using sensors or sysfs").into()) } #[cfg(target_os = "windows")] fn failed(hr: winapi::shared::winerror::HRESULT) -> bool { hr < 0 } #[cfg(target_os = "windows")] { use com::runtime::init_runtime; use com::sys::CLSCTX_INPROC_SERVER; use widestring::U16CString; use winapi::shared::rpcdce::*; use winapi::shared::wtypes::VT_I4; use winapi::um::oaidl::VARIANT; use winapi::um::objidlbase::EOAC_NONE; use winapi::um::{combaseapi, wbemcli}; init_runtime().ok(); unsafe { use anyhow::Ok; let mut locator: *mut wbemcli::IWbemLocator = std::ptr::null_mut(); let hr = combaseapi::CoCreateInstance( &wbemcli::CLSID_WbemLocator, std::ptr::null_mut(), CLSCTX_INPROC_SERVER, &wbemcli::IID_IWbemLocator, &mut locator as *mut _ as *mut _, ); if hr != 0 { eprintln!("Failed to create WbemLocator (HRESULT: {})", hr); return Err(("Failed to create WbemLocator").into()); } let mut services: *mut wbemcli::IWbemServices = std::ptr::null_mut(); let namespace = U16CString::from_str("root\\cimv2").unwrap(); // Changed to more common namespace let hr = (*locator).ConnectServer( namespace.as_ptr().cast_mut(), std::ptr::null_mut(), std::ptr::null_mut(), std::ptr::null_mut(), 0, std::ptr::null_mut(), std::ptr::null_mut(), &mut services, ); if hr != 0 { eprintln!("Failed to connect to WMI (HRESULT: {})", hr); (*locator).Release(); return Err(("Failed to connect to WMI").into()); } // Set security levels let hr = combaseapi::CoSetProxyBlanket( services as *mut _, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, std::ptr::null_mut(), RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, std::ptr::null_mut(), EOAC_NONE, ); if hr != 0 { eprintln!("Failed to set proxy blanket (HRESULT: {})", hr); (*services).Release(); (*locator).Release(); return Err(("Failed to set proxy blanket").into()); } // Try different temperature queries - some systems might have different WMI classes let queries = [ "SELECT * FROM Win32_PerfFormattedData_Counters_ThermalZoneInformation", "SELECT * FROM MSAcpi_ThermalZoneTemperature", "SELECT * FROM Win32_TemperatureProbe", ]; let mut result = None; for query_str in queries.iter() { let query = U16CString::from_str(query_str).unwrap(); let mut enumerator: *mut wbemcli::IEnumWbemClassObject = std::ptr::null_mut(); let hr = (*services).ExecQuery( U16CString::from_str("WQL").unwrap().as_ptr().cast_mut(), query.as_ptr().cast_mut(), wbemcli::WBEM_FLAG_FORWARD_ONLY as i32, std::ptr::null_mut(), &mut enumerator, ); if hr != 0 { continue; // Try next query if this one fails } let mut obj: *mut wbemcli::IWbemClassObject = std::ptr::null_mut(); let mut returned = 0; let hr = (*enumerator).Next( wbemcli::WBEM_INFINITE as i32, // Fixed: cast directly to i32 1, &mut obj, &mut returned, ); if failed(hr) { eprintln!("Failed to enumerate WMI objects (HRESULT: {})", hr); (*enumerator).Release(); continue; } if returned == 0 { // No more items (*enumerator).Release(); continue; } if hr == 0 && returned > 0 { let mut variant = std::mem::zeroed::(); // Try different possible property names let property_names = ["CurrentTemperature", "Temperature", "CurrentReading"]; for prop in property_names.iter() { let hr = (*obj).Get( U16CString::from_str(prop).unwrap().as_ptr(), 0, &mut variant, std::ptr::null_mut(), std::ptr::null_mut(), ); if hr == 0 && variant.n1.n2().vt as u32 == VT_I4 { let temp_kelvin = *variant.n1.n2().n3.intVal() as f32 / 10.0; result = Some(temp_kelvin - 273.15); // Convert to Celsius break; } } (*obj).Release(); (*enumerator).Release(); if result.is_some() { break; } } if !enumerator.is_null() { (*enumerator).Release(); } } (*services).Release(); (*locator).Release(); Ok(result.unwrap() as f64).map_err(|e| e.into()) } } #[cfg(not(any(target_os = "linux", target_os = "windows")))] { println!("CPU temperature retrieval not supported on this OS."); Err(anyhow::anyhow!("CPU temperature retrieval not supported on this OS").into()) } }