nixfleet_agent/host_facts/
linux.rs1use std::fs;
4
5use anyhow::{Context, Result};
6use nixfleet_proto::agent_wire::PendingGeneration;
7
8use super::{closure_hash_from_path, current_closure_hash};
9
10const BOOTED_SYSTEM: &str = "/run/booted-system";
11const BOOT_ID_PATH: &str = "/proc/sys/kernel/random/boot_id";
12
13pub fn boot_id() -> Result<String> {
14 let raw = fs::read_to_string(BOOT_ID_PATH).with_context(|| format!("read {BOOT_ID_PATH}"))?;
15 Ok(raw.trim().to_string())
16}
17
18pub fn pending_generation() -> Result<Option<PendingGeneration>> {
19 let current = current_closure_hash()?;
20 let booted = booted_closure_hash()?;
21 if current == booted {
22 return Ok(None);
23 }
24 Ok(Some(PendingGeneration {
25 closure_hash: current,
26 }))
27}
28
29fn booted_closure_hash() -> Result<String> {
30 let target =
31 fs::read_link(BOOTED_SYSTEM).with_context(|| format!("readlink {BOOTED_SYSTEM}"))?;
32 Ok(closure_hash_from_path(&target))
33}
34
35#[cfg(test)]
36mod tests {
37 use super::*;
38
39 #[test]
40 fn boot_id_returns_a_non_empty_string() {
41 let id = boot_id().expect("boot_id() must succeed on linux");
42 assert!(!id.is_empty(), "boot_id() returned an empty string");
43 }
44
45 #[test]
46 fn boot_id_is_stable_within_a_process() {
47 let a = boot_id().unwrap();
48 let b = boot_id().unwrap();
49 assert_eq!(a, b, "boot_id must be stable within the running process");
50 }
51}