// Take a look at the license at the top of the repository in the LICENSE file. use crate::ComponentExt; use std::ptr::null_mut; use winapi::shared::rpcdce::{ RPC_C_AUTHN_LEVEL_CALL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, RPC_C_IMP_LEVEL_IMPERSONATE, }; use winapi::shared::winerror::{FAILED, SUCCEEDED, S_FALSE, S_OK}; use winapi::shared::wtypesbase::CLSCTX_INPROC_SERVER; use winapi::um::combaseapi::{ CoCreateInstance, CoInitializeEx, CoInitializeSecurity, CoSetProxyBlanket, CoUninitialize, }; use winapi::um::oaidl::VARIANT; use winapi::um::objidl::EOAC_NONE; use winapi::um::oleauto::{SysAllocString, SysFreeString, VariantClear}; use winapi::um::wbemcli::{ CLSID_WbemLocator, IEnumWbemClassObject, IID_IWbemLocator, IWbemClassObject, IWbemLocator, IWbemServices, WBEM_FLAG_FORWARD_ONLY, WBEM_FLAG_NONSYSTEM_ONLY, WBEM_FLAG_RETURN_IMMEDIATELY, }; #[doc = include_str!("../../md_doc/component.md")] pub struct Component { temperature: f32, max: f32, critical: Option, label: String, connection: Option, } impl Component { /// Creates a new `Component` with the given information. fn new() -> Option { let mut c = Connection::new() .and_then(|x| x.initialize_security()) .and_then(|x| x.create_instance()) .and_then(|x| x.connect_server()) .and_then(|x| x.set_proxy_blanket()) .and_then(|x| x.exec_query())?; c.temperature(true) .map(|(temperature, critical)| Component { temperature, label: "Computer".to_owned(), max: temperature, critical, connection: Some(c), }) } } impl ComponentExt for Component { fn temperature(&self) -> f32 { self.temperature } fn max(&self) -> f32 { self.max } fn critical(&self) -> Option { self.critical } fn label(&self) -> &str { &self.label } fn refresh(&mut self) { if self.connection.is_none() { self.connection = Connection::new() .and_then(|x| x.initialize_security()) .and_then(|x| x.create_instance()) .and_then(|x| x.connect_server()) .and_then(|x| x.set_proxy_blanket()); } self.connection = if let Some(x) = self.connection.take() { x.exec_query() } else { None }; if let Some(ref mut connection) = self.connection { if let Some((temperature, _)) = connection.temperature(false) { self.temperature = temperature; if self.temperature > self.max { self.max = self.temperature; } } } } } pub(crate) fn get_components() -> Vec { match Component::new() { Some(c) => vec![c], None => Vec::new(), } } struct Instance(*mut IWbemLocator); impl Drop for Instance { fn drop(&mut self) { if !self.0.is_null() { unsafe { (*self.0).Release(); } } } } struct ServerConnection(*mut IWbemServices); impl Drop for ServerConnection { fn drop(&mut self) { if !self.0.is_null() { unsafe { (*self.0).Release(); } } } } struct Enumerator(*mut IEnumWbemClassObject); impl Drop for Enumerator { fn drop(&mut self) { if !self.0.is_null() { unsafe { (*self.0).Release(); } } } } macro_rules! bstr { ($($x:expr),*) => {{ let x: &[u16] = &[$($x as u16),*, 0]; SysAllocString(x.as_ptr()) }} } struct Connection { instance: Option, server_connection: Option, enumerator: Option, initialized: bool, } #[allow(clippy::non_send_fields_in_send_ty)] unsafe impl Send for Connection {} unsafe impl Sync for Connection {} impl Connection { #[allow(clippy::unnecessary_wraps)] fn new() -> Option { unsafe { let val = CoInitializeEx(null_mut(), 0); Some(Connection { instance: None, server_connection: None, enumerator: None, initialized: val == S_OK || val == S_FALSE, }) } } fn initialize_security(self) -> Option { unsafe { if FAILED(CoInitializeSecurity( null_mut(), -1, null_mut(), null_mut(), RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, null_mut(), EOAC_NONE, null_mut(), )) { None } else { Some(self) } } } fn create_instance(mut self) -> Option { let mut p_loc = null_mut(); unsafe { if FAILED(CoCreateInstance( &CLSID_WbemLocator as *const _, null_mut(), CLSCTX_INPROC_SERVER, &IID_IWbemLocator as *const _, &mut p_loc as *mut _ as *mut _, )) { None } else { self.instance = Some(Instance(p_loc)); Some(self) } } } fn connect_server(mut self) -> Option { let mut p_svc = null_mut(); if let Some(ref instance) = self.instance { unsafe { // "root\WMI" let s = bstr!('r', 'o', 'o', 't', '\\', 'W', 'M', 'I'); let res = (*instance.0).ConnectServer( s, null_mut(), null_mut(), null_mut(), 0, null_mut(), null_mut(), &mut p_svc as *mut _, ); SysFreeString(s); if FAILED(res) { return None; } } } else { return None; } self.server_connection = Some(ServerConnection(p_svc)); Some(self) } fn set_proxy_blanket(self) -> Option { if let Some(ref server_connection) = self.server_connection { unsafe { if FAILED(CoSetProxyBlanket( server_connection.0 as *mut _, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, null_mut(), RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, null_mut(), EOAC_NONE, )) { return None; } } } else { return None; } Some(self) } fn exec_query(mut self) -> Option { let mut p_enumerator = null_mut(); if let Some(ref server_connection) = self.server_connection { unsafe { // "WQL" let s = bstr!('W', 'Q', 'L'); // query kind // "SELECT * FROM MSAcpi_ThermalZoneTemperature" let query = bstr!( 'S', 'E', 'L', 'E', 'C', 'T', ' ', '*', ' ', 'F', 'R', 'O', 'M', ' ', 'M', 'S', 'A', 'c', 'p', 'i', '_', 'T', 'h', 'e', 'r', 'm', 'a', 'l', 'Z', 'o', 'n', 'e', 'T', 'e', 'm', 'p', 'e', 'r', 'a', 't', 'u', 'r', 'e' ); let hres = (*server_connection.0).ExecQuery( s, query, (WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY) as _, null_mut(), &mut p_enumerator as *mut _, ); SysFreeString(s); SysFreeString(query); if FAILED(hres) { return None; } } } else { return None; } self.enumerator = Some(Enumerator(p_enumerator)); Some(self) } fn temperature(&mut self, get_critical: bool) -> Option<(f32, Option)> { use winapi::um::wbemcli::WBEM_INFINITE; let p_enum = match self.enumerator.take() { Some(x) => x, None => { return None; } }; let mut p_obj: *mut IWbemClassObject = null_mut(); let mut nb_returned = 0; unsafe { (*p_enum.0).Next( WBEM_INFINITE as _, // Time out 1, // One object &mut p_obj as *mut _, &mut nb_returned, ); if nb_returned == 0 { return None; // not enough rights I suppose... } (*p_obj).BeginEnumeration(WBEM_FLAG_NONSYSTEM_ONLY as _); let mut p_val = std::mem::MaybeUninit::::uninit(); // "CurrentTemperature" let temp = bstr!( 'C', 'u', 'r', 'r', 'e', 'n', 't', 'T', 'e', 'm', 'p', 'e', 'r', 'a', 't', 'u', 'r', 'e' ); let res = (*p_obj).Get(temp, 0, p_val.as_mut_ptr(), null_mut(), null_mut()); let mut p_val = p_val.assume_init(); SysFreeString(temp); VariantClear(&mut p_val as *mut _ as *mut _); let temp = if SUCCEEDED(res) { // temperature is given in tenth of degrees Kelvin (p_val.n1.decVal().Lo64 / 10) as f32 - 273.15 } else { (*p_obj).Release(); return None; }; let mut critical = None; if get_critical { // "CriticalPoint" let crit = bstr!( 'C', 'r', 'i', 't', 'i', 'c', 'a', 'l', 'T', 'r', 'i', 'p', 'P', 'o', 'i', 'n', 't' ); let res = (*p_obj).Get(crit, 0, &mut p_val, null_mut(), null_mut()); SysFreeString(crit); VariantClear(&mut p_val as *mut _ as *mut _); if SUCCEEDED(res) { // temperature is given in tenth of degrees Kelvin critical = Some((p_val.n1.decVal().Lo64 / 10) as f32 - 273.15); } } (*p_obj).Release(); Some((temp, critical)) } } } impl Drop for Connection { fn drop(&mut self) { // Those three calls are here to enforce that they get dropped in the good order. self.enumerator.take(); self.server_connection.take(); self.instance.take(); if self.initialized { unsafe { CoUninitialize(); } } } }