sysfs_serde/
sysfs_serde.rs

1use crate::pci_attributes::PciAttributes;
2use crate::uevent::UEvent;
3use crate::usb_attributes::UsbAttributes;
4#[cfg(feature = "serde")]
5use serde::Serialize;
6use std::collections::HashMap;
7use std::fmt;
8use std::fs::*;
9use std::os::unix::fs::MetadataExt;
10use std::path::Path;
11use std::str::FromStr;
12
13/// De-serialized generic SysFs /sys/class/* to a Rust structure.
14/// Note most fields are stored in raw format in the HashMap<String, String>
15#[cfg_attr(feature = "serde", derive(Serialize))]
16#[derive(Default)]
17pub struct GenericClassAttributes {
18    #[cfg_attr(feature = "serde", serde(skip_serializing_if = "Option::is_none"))]
19    pub uevent: Option<UEvent>,
20    #[cfg_attr(
21        feature = "serde",
22        serde(flatten, skip_serializing_if = "HashMap::is_empty")
23    )]
24    pub attributes: HashMap<String, String>,
25}
26
27type SysPath = String;
28pub type UsbDevices = HashMap<SysPath, UsbAttributes>;
29pub type PciDevices = HashMap<SysPath, PciAttributes>;
30pub type DmiInfo = HashMap<SysPath, GenericClassAttributes>;
31pub type ThermalInfo = HashMap<SysPath, GenericClassAttributes>;
32
33/// De-serialized SysFs bus/class types supported by this crate
34pub enum SysFsType {
35    BusUSB(UsbDevices),
36    BusPCI(PciDevices),
37    ClassDMI(DmiInfo),
38    ClassThermal(ThermalInfo),
39}
40
41impl fmt::Display for SysFsType {
42    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
43        use crate::SysFsType::*;
44        write!(
45            f,
46            "{}",
47            match self {
48                BusUSB(_) => "USB",
49                BusPCI(_) => "PCI",
50                ClassDMI(_) => "DMI",
51                ClassThermal(_) => "Thermal",
52            }
53        )
54    }
55}
56
57impl From<&mut SysFsType> for &Path {
58    fn from(kind: &mut SysFsType) -> Self {
59        use crate::SysFsType::*;
60        match kind {
61            BusUSB(_) => Path::new("/sys/bus/usb/devices/"),
62            BusPCI(_) => Path::new("/sys/bus/pci/devices/"),
63            ClassDMI(_) => Path::new("/sys/class/dmi/"),
64            ClassThermal(_) => Path::new("/sys/class/thermal"),
65        }
66    }
67}
68
69/// Builds up Rust struct based on what kind of SysFs type.
70/// See [`SysFsType`] for supported types.
71#[cfg_attr(feature = "serde", derive(Serialize))]
72#[derive(Default)]
73pub struct SysFs {}
74impl SysFs {
75    /// Read path and pass it to anonymous function
76    fn read_generic<F, P>(path: P, mut f: F) -> Result<(), std::io::Error>
77    where
78        F: FnMut(&str, Result<Vec<u8>, std::io::Error>),
79        P: AsRef<Path>,
80    {
81        for entry in read_dir(path)? {
82            if let Ok(entry) = entry {
83                if metadata(entry.path())?.mode() & 0o400 != 0 && entry.path().is_file() {
84                    if let Some(key) = entry.path().file_name() {
85                        let key = &key.to_string_lossy();
86                        match std::fs::read(&mut entry.path()) {
87                            Ok(value) => {
88                                f(&key, Ok(value));
89                            }
90                            Err(e) => {
91                                f(&key, Err(e));
92                            }
93                        }
94                    }
95                }
96            }
97        }
98        Ok(())
99    }
100
101    fn iterate(kind: &mut SysFsType) -> Result<(), std::io::Error> {
102        let path: &Path = (kind).into();
103        if path.is_dir() {
104            for entry in read_dir(&path)? {
105                if let Ok(entry) = entry {
106                    if entry.path().is_dir() {
107                        if let Ok(link) = read_link(entry.path()) {
108                            let p = Path::new(&path).join(link).canonicalize();
109                            if let Ok(ref p) = p {
110                                let key: String =
111                                    p.clone().into_os_string().to_string_lossy().into();
112                                match kind {
113                                    SysFsType::BusUSB(map) => {
114                                        let mut attrs = UsbAttributes::default();
115                                        Self::read_generic(p, |key, value| match value {
116                                            Ok(value) => {
117                                                attrs.add(
118                                                    key, value,
119                                                )
120                                                .unwrap_or_else(|e| {
121                                                    log::error!(
122                                                        "Ignored attribute: '{}' for {} cause of: {}",
123                                                        key,
124                                                        p.display(),
125                                                        e
126                                                    );
127                                                });
128                                            }
129                                            Err(e) => {
130                                                log::warn!(
131                                                    "Could not read read USB: {}/{} cause: {}",
132                                                    p.to_string_lossy(),
133                                                    key,
134                                                    e
135                                                );
136                                            }
137                                        })?;
138                                        if !attrs.descriptors.is_empty() {
139                                            map.insert(key, attrs);
140                                        }
141                                    }
142                                    SysFsType::BusPCI(map) => {
143                                        let mut attrs = PciAttributes::default();
144                                        Self::read_generic(p, |key, value| match value {
145                                            Ok(value) => {
146                                                attrs.add(key, value).unwrap();
147                                            }
148                                            Err(e) => {
149                                                log::warn!(
150                                                    "Could not read read pci: {}/{} cause: {}",
151                                                    p.to_string_lossy(),
152                                                    key,
153                                                    e
154                                                );
155                                            }
156                                        })?;
157                                        map.insert(key, attrs);
158                                    }
159                                    SysFsType::ClassDMI(map) => {
160                                        map.insert(key, Self::iterate_generic_attributes(p)?);
161                                    }
162                                    SysFsType::ClassThermal(map) => {
163                                        map.insert(key, Self::iterate_generic_attributes(p)?);
164                                    }
165                                }
166                            }
167                        }
168                    }
169                }
170            }
171        }
172        Ok(())
173    }
174
175    fn iterate_generic_attributes(p: &Path) -> Result<GenericClassAttributes, std::io::Error> {
176        let mut inner = GenericClassAttributes::default();
177        Self::read_generic(p, |key, value| match value {
178            Ok(value) => {
179                let value = String::from_utf8_lossy(&value)
180                    .to_string()
181                    .trim()
182                    .to_string();
183                match &key[0..] {
184                    "uevent" => inner.uevent = UEvent::from_str(&value).ok(),
185                    _ => {
186                        inner.attributes.insert(key.to_string(), value);
187                    }
188                }
189            }
190            Err(e) => {
191                log::warn!("Could not read {} key: '{}' cause: {}", p.display(), key, e);
192            }
193        })?;
194        Ok(inner)
195    }
196
197    /// Get all USB devices found on system.
198    pub fn usb_devices() -> Result<UsbDevices, std::io::Error> {
199        let mut kind = SysFsType::BusUSB(UsbDevices::new());
200        Self::iterate(&mut kind)?;
201        match kind {
202            SysFsType::BusUSB(usb) => Ok(usb),
203            _ => panic!(""),
204        }
205    }
206
207    /// Get all PCI devices found on system.
208    pub fn pci_devices() -> Result<PciDevices, std::io::Error> {
209        let mut kind = SysFsType::BusPCI(PciDevices::new());
210        Self::iterate(&mut kind)?;
211        match kind {
212            SysFsType::BusPCI(pci) => Ok(pci),
213            _ => panic!(""),
214        }
215    }
216
217    /// Get DMI info
218    pub fn dmi_info() -> Result<DmiInfo, std::io::Error> {
219        let mut kind = SysFsType::ClassDMI(DmiInfo::new());
220        Self::iterate(&mut kind)?;
221        match kind {
222            SysFsType::ClassDMI(dmi) => Ok(dmi),
223            _ => panic!(""),
224        }
225    }
226
227    /// Get all thermal devices on the system.
228    pub fn thermal_info() -> Result<ThermalInfo, std::io::Error> {
229        let mut kind = SysFsType::ClassThermal(ThermalInfo::new());
230        Self::iterate(&mut kind)?;
231        match kind {
232            SysFsType::ClassThermal(temp) => Ok(temp),
233            _ => panic!(""),
234        }
235    }
236}