From 1391e2fb2c18d0f0d2a0f65a3f49ee11aca0cdd4 Mon Sep 17 00:00:00 2001 From: NGnius Date: Thu, 20 Jul 2023 19:28:59 -0400 Subject: [PATCH] Add root syspath and hwmon functionality --- src/hwmon.rs | 179 +++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 20 +++-- src/os_str_util.rs | 12 +++ src/syspath.rs | 47 ++++++++++++ 4 files changed, 253 insertions(+), 5 deletions(-) create mode 100644 src/hwmon.rs create mode 100644 src/os_str_util.rs create mode 100644 src/syspath.rs diff --git a/src/hwmon.rs b/src/hwmon.rs new file mode 100644 index 0000000..f7ff4c7 --- /dev/null +++ b/src/hwmon.rs @@ -0,0 +1,179 @@ +use std::path::{Path, PathBuf}; +use std::convert::AsRef; +use std::io::Result as IoResult; + +const HWMON_DIR_PATH: &'static str = "sys/class/hwmon/"; + +/// Attribute representation. +/// Attribute files are usually in the format `_`. +#[derive(Clone, Copy)] +pub struct HwMonAttribute { + inner: HwMonAttributeInner, +} + +#[derive(Clone, Copy)] +enum HwMonAttributeInner { + Name, + Standard { + ty: HwMonAttributeType, + number: u64, + item: HwMonAttributeItem, + }, + Uevent, +} + +/// Attribute type in the format `_` +#[derive(Clone, Copy)] +pub enum HwMonAttributeType { + /// Voltage + In, + /// Temperature + Temp, + /// Fan + Fan, + /// Current + Curr, +} + +impl HwMonAttributeType { + fn to_attr_str(&self) -> &str { + match self { + Self::In => "in", + Self::Temp => "out", + Self::Fan => "fan", + Self::Curr => "curr", + } + } +} + +/// Attribute item in the format `_` +#[derive(Clone, Copy)] +pub enum HwMonAttributeItem { + /// Input + Input, + /// Minimum + Min, + /// Maximum + Max, +} + +impl HwMonAttributeItem { + fn to_attr_str(&self) -> &str { + match self { + Self::Input => "input", + Self::Min => "min", + Self::Max => "max", + } + } +} + +impl HwMonAttribute { + /// `name` attribute + pub const fn name() -> Self { + Self { + inner: HwMonAttributeInner::Name + } + } + + /// Custom attribute in standard format + pub const fn new(ty: HwMonAttributeType, number: u64, item: HwMonAttributeItem) -> Self { + Self { + inner: HwMonAttributeInner::Standard { ty, number, item } + } + } + + /// `uevent` attribute + pub const fn uevent() -> Self { + Self { + inner: HwMonAttributeInner::Uevent + } + } + + fn to_attr_str(&self) -> String { + match self.inner { + HwMonAttributeInner::Name => "name".to_string(), + HwMonAttributeInner::Standard { ty, number, item } => format!("{}{}_{}", ty.to_attr_str(), number, item.to_attr_str()), + HwMonAttributeInner::Uevent => "uevent".to_string(), + } + } +} +/// hwmon/ directory +pub struct HwMonPath { + path: PathBuf +} + +impl HwMonPath { + pub(crate) fn all(root: impl AsRef) -> IoResult>> { + let hwmon_dir_path = root.as_ref().join(HWMON_DIR_PATH); + hwmon_dir_path.read_dir() + .map( + |iter| iter.filter_map( + |entry| entry.map( + |entry| if crate::os_str_util::starts_with(&entry.file_name(), "hwmon".as_ref()) { + Some(Self { + path: entry.path(), + }) + } else { + None + }).transpose())) + } + + /// Get a hwmon entry by index. + /// This does not check if it exists. + pub(crate) fn entry(root: &crate::SysPath, i: u64) -> Self { + Self { + path: root.as_ref().join(HWMON_DIR_PATH).join(format!("hwmon{}", i)), + } + } + + /// Get a hwmon entry by name, if it exists + pub(crate) fn name(root: &crate::SysPath, name: &str) -> IoResult> { + for entry in Self::all(root)? { + let entry = entry?; + if entry.attribute(HwMonAttribute::name())? == name { + return Ok(Some(entry)) + } + } + Ok(None) + } + + /// Get a hwmon attribute (file contents) by attribute filename. + /// It is recommended to use HwMon.attribute unless the attribute has a non-standard name + pub fn attribute_str(&self, name: &str) -> IoResult { + std::fs::read_to_string(self.path.join(name)) + } + + /// Get a hwmon attribute (file contents) + pub fn attribute(&self, attr: HwMonAttribute) -> IoResult { + self.attribute_str(&attr.to_attr_str()) + } + + /// Get the path to a hwmon attribute. + /// Use `HwMonPath.as_ref().join(attribute_str)` for non-standard attributes + pub fn path_to(&self, attr: HwMonAttribute) -> PathBuf { + self.path.join(&attr.to_attr_str()) + } +} + +impl AsRef for HwMonPath { + fn as_ref(&self) -> &Path { + self.path.as_path() + } +} + + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn hwmon_all() -> std::io::Result<()> { + let sys = crate::SysPath::default(); + let all_hwmon: Vec<_> = HwMonPath::all(sys)?.collect(); + assert!(!all_hwmon.is_empty()); + for hwmon in all_hwmon.into_iter() { + assert!(hwmon?.attribute(HwMonAttribute::name())? != ""); + } + Ok(()) + } +} diff --git a/src/lib.rs b/src/lib.rs index 7d12d9a..4ee7097 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,16 @@ -pub fn add(left: usize, right: usize) -> usize { - left + right -} +#![warn(missing_docs)] +//! Utility library for manipulating files in the Linux sysfs +//! + +pub(crate) const DEFAULT_ROOT: &'static str = "/"; + +mod hwmon; +pub use hwmon::{HwMonPath, HwMonAttribute, HwMonAttributeType, HwMonAttributeItem}; + +mod syspath; +pub use syspath::SysPath; + +pub(crate) mod os_str_util; #[cfg(test)] mod tests { @@ -8,7 +18,7 @@ mod tests { #[test] fn it_works() { - let result = add(2, 2); - assert_eq!(result, 4); + SysPath::default(); + SysPath::path("/"); } } diff --git a/src/os_str_util.rs b/src/os_str_util.rs new file mode 100644 index 0000000..6897c08 --- /dev/null +++ b/src/os_str_util.rs @@ -0,0 +1,12 @@ +use std::ffi::OsStr; +use std::os::unix::ffi::OsStrExt; + +pub fn starts_with(src: &OsStr, contains: &OsStr) -> bool { + let src_bytes = src.as_bytes(); + for (index, byte) in contains.as_bytes().iter().enumerate() { + if src_bytes[index] != *byte { + return false; + } + } + true +} diff --git a/src/syspath.rs b/src/syspath.rs new file mode 100644 index 0000000..7812f67 --- /dev/null +++ b/src/syspath.rs @@ -0,0 +1,47 @@ +use std::path::{Path, PathBuf}; +use std::convert::AsRef; + +/// sysfs root +pub struct SysPath { + root: PathBuf, +} + +impl SysPath { + /// Initialize with a custom root. + /// Use SysPath::default() to use the default root `/`. + pub fn path(root: impl AsRef) -> Self { + Self { + root: root.as_ref().to_path_buf() + } + } + + /// Find a hardware monitor entry by name + pub fn hwmon_by_name(&self, name: &str) -> std::io::Result { + match crate::HwMonPath::name(self, name) { + Ok(None) => Err(std::io::Error::from_raw_os_error(std::io::ErrorKind::NotFound as _)), + Ok(Some(x)) => Ok(x), + Err(e) => Err(e) + } + } + + /// Find a hardware monitor entry by index + pub fn hwmon_by_index(&self, index: u64) -> std::io::Result { + let entry = crate::HwMonPath::entry(self, index); + entry.attribute(crate::HwMonAttribute::name())?; + Ok(entry) + } +} + +impl std::default::Default for SysPath { + fn default() -> Self { + Self { + root: PathBuf::from(crate::DEFAULT_ROOT) + } + } +} + +impl AsRef for SysPath { + fn as_ref(&self) -> &Path { + self.root.as_path() + } +}