mirror of
https://github.com/ClementTsang/bottom.git
synced 2025-07-26 15:14:13 +02:00
bug: fix missing temp path locations to check on Linux (#816)
* bug: fix missing temp path locations to check on Linux * remember to divide by a thousand in thermal_zone
This commit is contained in:
parent
cf95f2c2a6
commit
7fec637360
@ -6,8 +6,11 @@ use crate::app::{
|
|||||||
Filter,
|
Filter,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
|
use std::{fs, path::Path};
|
||||||
|
|
||||||
/// Get temperature sensors from the linux sysfs interface `/sys/class/hwmon`
|
/// Get temperature sensors from the linux sysfs interface `/sys/class/hwmon`.
|
||||||
|
/// See [here](https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-hwmon) for
|
||||||
|
/// details.
|
||||||
///
|
///
|
||||||
/// This method will return `0` as the temperature for devices, such as GPUs,
|
/// This method will return `0` as the temperature for devices, such as GPUs,
|
||||||
/// that support power management features and power themselves off.
|
/// that support power management features and power themselves off.
|
||||||
@ -16,20 +19,14 @@ use anyhow::{anyhow, Result};
|
|||||||
/// entering ACPI D3cold, reading the temperature sensors will wake it,
|
/// entering ACPI D3cold, reading the temperature sensors will wake it,
|
||||||
/// and keep it awake, wasting power.
|
/// and keep it awake, wasting power.
|
||||||
///
|
///
|
||||||
/// For such devices, this method will only query the sensors IF the
|
/// For such devices, this method will only query the sensors *only* if
|
||||||
/// device is already in ACPI D0
|
/// the device is already in ACPI D0. This has the notable issue that
|
||||||
///
|
/// once this happens, the device will be *kept* on through the sensor
|
||||||
/// This has the notable issue that once this happens,
|
/// reading, and not be able to re-enter ACPI D3cold.
|
||||||
/// the device will be *kept* on through the sensor reading,
|
fn get_from_hwmon(
|
||||||
/// and not be able to re-enter ACPI D3cold.
|
|
||||||
pub fn get_temperature_data(
|
|
||||||
temp_type: &TemperatureType, filter: &Option<Filter>,
|
temp_type: &TemperatureType, filter: &Option<Filter>,
|
||||||
) -> Result<Option<Vec<TempHarvest>>> {
|
) -> Result<Vec<TempHarvest>> {
|
||||||
use std::{fs, path::Path};
|
let mut temperature_vec: Vec<TempHarvest> = vec![];
|
||||||
|
|
||||||
let mut temperature_vec: Vec<TempHarvest> = Vec::new();
|
|
||||||
|
|
||||||
// Documented at https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-hwmon
|
|
||||||
let path = Path::new("/sys/class/hwmon");
|
let path = Path::new("/sys/class/hwmon");
|
||||||
|
|
||||||
// NOTE: Technically none of this is async, *but* sysfs is in memory,
|
// NOTE: Technically none of this is async, *but* sysfs is in memory,
|
||||||
@ -45,21 +42,33 @@ pub fn get_temperature_data(
|
|||||||
// It would probably be more ideal to use a proper async runtime..
|
// It would probably be more ideal to use a proper async runtime..
|
||||||
for entry in path.read_dir()? {
|
for entry in path.read_dir()? {
|
||||||
let file = entry?;
|
let file = entry?;
|
||||||
let path = file.path();
|
let mut file_path = file.path();
|
||||||
|
|
||||||
// hwmon includes many sensors, we only want ones with at least one temperature sensor
|
// hwmon includes many sensors, we only want ones with at least one temperature sensor
|
||||||
// Reading this file will wake the device, but we're only checking existence.
|
// Reading this file will wake the device, but we're only checking existence.
|
||||||
if !path.join("temp1_input").exists() {
|
if !file_path.join("temp1_input").exists() {
|
||||||
continue;
|
// Note we also check for a `device` subdirectory (e.g. `/sys/class/hwmon/hwmon*/device/`).
|
||||||
|
// This is needed for CentOS, which adds this extra `/device` directory. See:
|
||||||
|
// - https://github.com/nicolargo/glances/issues/1060
|
||||||
|
// - https://github.com/giampaolo/psutil/issues/971
|
||||||
|
// - https://github.com/giampaolo/psutil/blob/642438375e685403b4cd60b0c0e25b80dd5a813d/psutil/_pslinux.py#L1316
|
||||||
|
//
|
||||||
|
// If it does match, then add the `device/` directory to the path.
|
||||||
|
if file_path.join("device/temp1_input").exists() {
|
||||||
|
file_path.push("device");
|
||||||
|
} else {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let hwmon_name = path.join("name");
|
let hwmon_name = file_path.join("name");
|
||||||
let hwmon_name = Some(fs::read_to_string(&hwmon_name)?);
|
let hwmon_name = Some(fs::read_to_string(&hwmon_name)?);
|
||||||
|
|
||||||
// Whether the temperature should *actually* be read during enumeration
|
// Whether the temperature should *actually* be read during enumeration
|
||||||
// Set to false if the device is in ACPI D3cold
|
// Set to false if the device is in ACPI D3cold.
|
||||||
let should_read_temp = {
|
let should_read_temp = {
|
||||||
// Documented at https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-devices-power_state
|
// Documented at https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-devices-power_state
|
||||||
let device = path.join("device");
|
let device = file_path.join("device");
|
||||||
let power_state = device.join("power_state");
|
let power_state = device.join("power_state");
|
||||||
if power_state.exists() {
|
if power_state.exists() {
|
||||||
let state = fs::read_to_string(power_state)?;
|
let state = fs::read_to_string(power_state)?;
|
||||||
@ -76,7 +85,7 @@ pub fn get_temperature_data(
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Enumerate the devices temperature sensors
|
// Enumerate the devices temperature sensors
|
||||||
for entry in path.read_dir()? {
|
for entry in file_path.read_dir()? {
|
||||||
let file = entry?;
|
let file = entry?;
|
||||||
let name = file.file_name();
|
let name = file.file_name();
|
||||||
// This should always be ASCII
|
// This should always be ASCII
|
||||||
@ -88,7 +97,7 @@ pub fn get_temperature_data(
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let temp = file.path();
|
let temp = file.path();
|
||||||
let temp_label = path.join(name.replace("input", "label"));
|
let temp_label = file_path.join(name.replace("input", "label"));
|
||||||
let temp_label = fs::read_to_string(temp_label).ok();
|
let temp_label = fs::read_to_string(temp_label).ok();
|
||||||
|
|
||||||
let name = match (&hwmon_name, &temp_label) {
|
let name = match (&hwmon_name, &temp_label) {
|
||||||
@ -121,6 +130,64 @@ pub fn get_temperature_data(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Ok(temperature_vec)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets data from `/sys/class/thermal/thermal_zone*`. This should only be used if
|
||||||
|
/// [`get_from_hwmon`] doesn't return anything. See
|
||||||
|
/// [here](https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-thermal) for details.
|
||||||
|
fn get_from_thermal_zone(
|
||||||
|
temp_type: &TemperatureType, filter: &Option<Filter>,
|
||||||
|
) -> Result<Vec<TempHarvest>> {
|
||||||
|
let mut temperatures = vec![];
|
||||||
|
let path = Path::new("/sys/class/thermal");
|
||||||
|
for entry in path.read_dir()? {
|
||||||
|
let file = entry?;
|
||||||
|
if file
|
||||||
|
.file_name()
|
||||||
|
.to_string_lossy()
|
||||||
|
.starts_with("thermal_zone")
|
||||||
|
{
|
||||||
|
let file_path = file.path();
|
||||||
|
let name_path = file_path.join("type");
|
||||||
|
|
||||||
|
let name = fs::read_to_string(name_path)?.trim_end().to_string();
|
||||||
|
|
||||||
|
if is_temp_filtered(filter, &name) {
|
||||||
|
let temp_path = file_path.join("temp");
|
||||||
|
let temp = fs::read_to_string(temp_path)?
|
||||||
|
.trim_end()
|
||||||
|
.parse::<f32>()
|
||||||
|
.map_err(|e| {
|
||||||
|
crate::utils::error::BottomError::ConversionError(e.to_string())
|
||||||
|
})?
|
||||||
|
/ 1_000.0;
|
||||||
|
temperatures.push(TempHarvest {
|
||||||
|
name,
|
||||||
|
temperature: match temp_type {
|
||||||
|
TemperatureType::Celsius => temp,
|
||||||
|
TemperatureType::Kelvin => convert_celsius_to_kelvin(temp),
|
||||||
|
TemperatureType::Fahrenheit => convert_celsius_to_fahrenheit(temp),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(temperatures)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gets temperature sensors and data.
|
||||||
|
pub fn get_temperature_data(
|
||||||
|
temp_type: &TemperatureType, filter: &Option<Filter>,
|
||||||
|
) -> Result<Option<Vec<TempHarvest>>> {
|
||||||
|
let mut temperature_vec: Vec<TempHarvest> = get_from_hwmon(temp_type, filter)?;
|
||||||
|
|
||||||
|
if temperature_vec.is_empty() {
|
||||||
|
// If it's empty, fall back to checking `thermal_zone*`.
|
||||||
|
temperature_vec = get_from_thermal_zone(temp_type, filter)?;
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "nvidia")]
|
#[cfg(feature = "nvidia")]
|
||||||
{
|
{
|
||||||
super::nvidia::add_nvidia_data(&mut temperature_vec, temp_type, filter)?;
|
super::nvidia::add_nvidia_data(&mut temperature_vec, temp_type, filter)?;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user