arete_sdk/
system.rs

1use super::{Client, Error, Node};
2use crate::client::{DEFAULT_TIMEOUT_SECS, Format};
3use std::{sync::Arc, time::Duration};
4use uuid::Uuid;
5
6#[derive(Clone)]
7pub struct System {
8    client: Client,
9    pub id: Uuid,
10}
11
12impl System {
13    pub(crate) fn new(client: Client, id: Uuid) -> Self {
14        Self { client, id }
15    }
16
17    pub fn node(&self, id: &str, name: &str, upstream: bool, token: Option<String>) -> Result<Arc<Node>, Error> {
18        let upstream_arg = if upstream { "yes".to_string() } else { "no".to_string() };
19        let token_arg = token.unwrap_or("$uuid".to_string());
20        let args = vec![
21            self.id.to_string(),
22            id.to_string(),
23            name.to_string(),
24            upstream_arg,
25            token_arg,
26        ];
27        let mut client = self.client.clone();
28        let transaction = client.send(Format::Json, "nodes", &args)?;
29        let _res = client.wait_for_response(transaction, Duration::from_secs(DEFAULT_TIMEOUT_SECS))?;
30        Ok(Arc::new(Node::new(client, self.clone(), id.to_string())))
31    }
32}
33
34#[cfg(target_os = "macos")]
35#[allow(unused)]
36pub fn get_system_id() -> Result<Uuid, Error> {
37    use serde::Deserialize;
38    use std::process::Command;
39    use std::str::FromStr;
40
41    #[derive(Deserialize)]
42    struct HardwareOverview {
43        #[serde(rename = "platform_UUID")]
44        platform_uuid: String,
45    }
46
47    #[derive(Deserialize)]
48    struct Wrapper {
49        #[serde(rename = "SPHardwareDataType")]
50        sp_hardware_data_type: Vec<HardwareOverview>,
51    }
52
53    let output = Command::new("system_profiler")
54        .arg("SPHardwareDataType")
55        .arg("-json")
56        .output()
57        .map_err(|e| Error::Default("Failed invoking system_profiler to lookup SPHardwareDataType".to_string()))?;
58    let wrapper = serde_json::from_slice::<Wrapper>(&output.stdout).map_err(|e| {
59        Error::Serialization("Failed parsing system_profiler SPHardwareDataType lookup response".to_string())
60    })?;
61    match wrapper.sp_hardware_data_type.first() {
62        None => Err(Error::Serialization(
63            "Failed finding a hardware datatype in system_profiler SPHardwareDataType lookup".to_string(),
64        )),
65        Some(hardware_overview) => {
66            let id = Uuid::from_str(&hardware_overview.platform_uuid)
67                .map_err(|e| Error::Serialization("Failed parsing uuid".to_string()))?;
68            Ok(id)
69        }
70    }
71}
72
73#[cfg(target_os = "linux")]
74#[allow(unused)]
75pub fn get_system_id() -> Result<Uuid, Error> {
76    let model = get_model()?;
77    let serial_number = get_serial_number()?;
78    let model_plus_serial_number = format!("{model}:{serial_number}");
79    let id = Uuid::new_v5(&Uuid::NAMESPACE_OID, model_plus_serial_number.as_bytes());
80    Ok(id)
81}
82
83#[cfg(target_os = "linux")]
84fn get_model() -> Result<String, Error> {
85    const MODEL_FILENAME: &str = "/sys/firmware/devicetree/base/model";
86    std::fs::read_to_string(MODEL_FILENAME)
87        .map_err(|e| Error::Io(format!("Failed reading {MODEL_FILENAME} to obtain model ({e:?})")))
88}
89
90#[cfg(target_os = "linux")]
91fn get_serial_number() -> Result<String, Error> {
92    const SN_FILENAME: &str = "/sys/firmware/devicetree/base/serial-number";
93    std::fs::read_to_string(SN_FILENAME)
94        .map_err(|e| Error::Io(format!("Failed reading {SN_FILENAME} to obtain serial number ({e:?})")))
95}
96
97#[cfg(test)]
98mod tests {
99    use crate::system::get_system_id;
100
101    #[test]
102    #[cfg(target_os = "macos")]
103    fn can_get_system_id_on_macos() {
104        let _system_id = get_system_id().unwrap();
105    }
106
107    #[test]
108    #[cfg(target_os = "linux")]
109    fn can_get_system_id_on_linux() {
110        use std::path::Path;
111        const MODEL_FILENAME: &str = "/sys/firmware/devicetree/base/model";
112        const SN_FILENAME: &str = "/sys/firmware/devicetree/base/serial-number";
113        if !Path::new(MODEL_FILENAME).exists() || !Path::new(SN_FILENAME).exists() {
114            return; // Skip because device tree is hidden; we are probably running in the CI env
115        }
116
117        let _system_id = get_system_id().unwrap();
118    }
119}