1use anyhow::Result;
24use async_std::task;
25use ferrix_lib::dmi::{Baseboard, Bios, Chassis, Processor};
26use serde::{Deserialize, Serialize};
27use std::{env, path::Path, process::Command, sync::LazyLock};
28
29use crate::load_state::{LoadState, ToLoadState};
30
31static PATH: LazyLock<Vec<String>> = LazyLock::new(|| path());
32
33fn path() -> Vec<String> {
34 env::var("PATH")
35 .ok()
36 .and_then(|pth| Some(pth.split(':').map(|p| p.to_string()).collect::<Vec<_>>()))
37 .unwrap_or(vec!["/usr/bin".to_string()])
38}
39
40fn auth_app() -> Option<String> {
41 let apps = ["pkexec", "gksudo"];
42 let bin_dirs = &PATH;
43
44 for a in apps {
45 for b in bin_dirs.as_slice() {
46 let s = Path::new(b).join(a);
47 if s.exists() {
48 return Some(s.display().to_string());
49 }
50 }
51 }
52
53 None
54}
55
56fn fx_polkit_app() -> Option<String> {
57 let app = "ferrix-polkit";
58 let bin_dirs = &PATH;
59 for b in bin_dirs.as_slice() {
60 let s = Path::new(b).join(app);
61 if s.exists() {
62 return Some(s.display().to_string());
63 }
64 }
65 None
66}
67
68pub async fn get_dmi_data() -> LoadState<DMIData> {
69 let auth_app = match auth_app() {
70 Some(auth_app) => auth_app,
71 None => return LoadState::Error("No authentication software found".to_string()),
72 };
73 let fx_app = match fx_polkit_app() {
74 Some(fx_app) => fx_app,
75 None => return LoadState::Error("No `ferrix-app` program found".to_string()),
76 };
77
78 let output =
79 task::spawn_blocking(move || Command::new(auth_app).arg(fx_app).arg("dmi").output()).await;
80
81 if let Err(why) = output {
82 return LoadState::Error(why.to_string());
83 }
84 let output = output.unwrap();
85 if output.status.code().unwrap_or(0) != 0 {
86 return LoadState::Error(format!(
87 "[ferrix-polkit] Non-zero return code:\n{}",
88 String::from_utf8_lossy(&output.stderr)
89 ));
90 }
91
92 let json_str = String::from_utf8_lossy(&output.stdout);
93 let json_data = DMIData::from_json(&json_str);
94
95 match json_data {
96 Ok(data) => LoadState::Loaded(data),
97 Err(why) => LoadState::Error(why.to_string()),
98 }
99}
100
101#[derive(Debug, Serialize, Deserialize, Clone)]
102pub struct DMIData {
103 pub bios: LoadState<Bios>,
104 pub baseboard: LoadState<Baseboard>,
105 pub chassis: LoadState<Chassis>,
106 pub processor: LoadState<Processor>,
107}
108
109impl DMIData {
110 pub fn new() -> Self {
111 Self {
112 bios: Bios::new().to_load_state(),
113 baseboard: Baseboard::new().to_load_state(),
114 chassis: Chassis::new().to_load_state(),
115 processor: Processor::new().to_load_state(),
116 }
117 }
118
119 pub fn to_json(&self) -> Result<String> {
120 let contents = serde_json::to_string(&self)?;
121 Ok(contents)
122 }
123
124 pub fn from_json(json: &str) -> Result<Self> {
125 Ok(serde_json::from_str(json)?)
126 }
127}