Skip to main content

ferrix_lib/
firmware.rs

1/* firmware.rs
2 *
3 * Copyright 2026 Michail Krasnov <mskrasnov07@ya.ru>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 *
18 * SPDX-License-Identifier: GPL-3.0-or-later
19 */
20
21//! Get firmware settings (only for modern systems with UEFI)
22//! 
23//! > **Note:** you need to be `root` for get this information.
24//! 
25//! ## Example
26//! ```no-test
27//! use ferrix_lib::firmware::Firmware;
28//! let f = Firmware::new().unwrap();
29//! println!("WMI Driver: {}", &f.driver_name);
30//! dbg!(&f.attributes);
31//! ```
32
33use anyhow::{Result, anyhow};
34use serde::{Deserialize, Serialize};
35use std::{
36    ffi::OsString,
37    fs::{read_dir, read_to_string},
38    path::Path,
39};
40
41#[derive(Debug, Clone, Serialize, Deserialize)]
42pub struct Firmware {
43    pub driver_name: String,
44    pub attributes: Vec<Attribute>,
45}
46
47impl Firmware {
48    const FIRMWARE_DIR: &'static str = "/sys/class/firmware-attributes/";
49
50    pub fn new() -> Result<Self> {
51        let mut firmware_dir_contents = read_dir(Self::FIRMWARE_DIR)?;
52        let driver_dir = firmware_dir_contents
53            .next()
54            .ok_or_else(|| anyhow!("No `firmware-attributes` directory found."))??;
55
56        let driver_name = os_str_into_str(driver_dir.file_name())?;
57        let attributes_dir_contents = read_dir(driver_dir.path())?;
58        let mut attributes = vec![];
59
60        for attr in attributes_dir_contents {
61            let attr = attr?;
62            let metadata = attr.metadata()?;
63            let fname = os_str_into_str(attr.file_name())?;
64
65            if !metadata.is_dir() || &fname != "attributes" {
66                continue;
67            }
68
69            let attribute = Self::read_attributes(attr.path())?;
70            attributes = attribute; // NOTE: shitcode?
71            break; // NOTE: shitcode?
72        }
73
74        Ok(Self {
75            driver_name,
76            attributes,
77        })
78    }
79
80    fn read_attributes<P>(dir: P) -> Result<Vec<Attribute>>
81    where
82        P: AsRef<Path> + std::fmt::Debug,
83    {
84        let mut attrs = vec![];
85        let dir_contents = read_dir(dir)?;
86
87        for d in dir_contents {
88            let d = d?;
89            if !d.metadata()?.is_dir() {
90                continue;
91            }
92            let attribute = Attribute::read_dir(d.path())?;
93            attrs.push(attribute);
94        }
95        attrs.sort_by_key(|key| key.display_name.clone()); // NOTE: SHITCODE
96
97        Ok(attrs)
98    }
99}
100
101fn os_str_into_str(os_str: OsString) -> Result<String> {
102    let s = os_str
103        .into_string()
104        .map_err(|err| anyhow!("Failed to convert directory name into the string: {err:?}"))?;
105    Ok(s)
106}
107
108#[derive(Debug, Clone, Serialize, Deserialize)]
109pub struct Attribute {
110    pub current_value: String,
111    pub display_name: String,
112    pub possible_values: String,
113    pub param_type: String,
114}
115
116impl Attribute {
117    pub fn read_dir<P>(dir: P) -> Result<Self>
118    where
119        P: AsRef<Path> + std::fmt::Debug,
120    {
121        // NOTE: We assume that we receive a DIRECTORY as input
122        let dir_contents = read_dir(dir)?;
123
124        let mut current_value = String::new();
125        let mut display_name = String::new();
126        let mut possible_values = String::new();
127        let mut param_type = String::new();
128
129        for d in dir_contents {
130            let d = d?;
131            if !d.metadata()?.is_file() {
132                continue;
133            }
134            let name = os_str_into_str(d.file_name())?;
135
136            let read = || -> Result<String> {
137                let s = read_to_string(d.path())?;
138                Ok(s.trim().to_string())
139            };
140
141            match &name as &str {
142                "display_name" => display_name = read()?,
143                "current_value" => current_value = read()?,
144                "possible_values" => possible_values = read()?,
145                "type" => param_type = read()?,
146                _ => {}
147            }
148        }
149
150        Ok(Self {
151            current_value,
152            display_name,
153            possible_values,
154            param_type,
155        })
156    }
157}