nixfleet_agent/activation/
profile.rs1use anyhow::{Context, Result, anyhow};
5use tokio::process::Command;
6
7pub(super) async fn self_correct_profile(expected_store_path: &str) -> Result<()> {
9 let profile = "/nix/var/nix/profiles/system";
10 if profile_matches(expected_store_path, profile) {
11 return Ok(());
12 }
13
14 tracing::warn!(
15 expected = %expected_store_path,
16 profile = profile,
17 "agent: profile mismatch after fire-and-forget - re-running nix-env --set",
18 );
19 let status = Command::new("nix-env")
20 .arg("--profile")
21 .arg(profile)
22 .arg("--set")
23 .arg(expected_store_path)
24 .status()
25 .await
26 .with_context(|| "spawn nix-env --set (self-correction)")?;
27 if !status.success() {
28 return Err(anyhow!(
29 "nix-env --set self-correction exited {:?}",
30 status.code()
31 ));
32 }
33 if !profile_matches(expected_store_path, profile) {
34 return Err(anyhow!(
35 "profile still mismatched after nix-env --set self-correction",
36 ));
37 }
38 tracing::info!("agent: profile self-corrected successfully");
39 Ok(())
40}
41
42fn profile_matches(expected_store_path: &str, profile_path: &str) -> bool {
44 let Ok(gen_link) = std::fs::read_link(profile_path) else {
45 return false;
46 };
47 let abs_gen_link = if gen_link.is_relative() {
48 let parent = std::path::Path::new(profile_path)
49 .parent()
50 .unwrap_or(std::path::Path::new("/"));
51 parent.join(&gen_link)
52 } else {
53 gen_link
54 };
55 let final_target = match std::fs::read_link(&abs_gen_link) {
56 Ok(t) => t,
57 Err(_) => abs_gen_link,
58 };
59 final_target.to_string_lossy() == expected_store_path
60}
61
62pub(super) fn resolve_profile_target() -> Result<String> {
63 let profile = std::path::Path::new("/nix/var/nix/profiles/system");
64 let gen_link =
65 std::fs::read_link(profile).with_context(|| "readlink /nix/var/nix/profiles/system")?;
66 let abs_gen_link = if gen_link.is_relative() {
67 profile
68 .parent()
69 .unwrap_or(std::path::Path::new("/"))
70 .join(&gen_link)
71 } else {
72 gen_link.clone()
73 };
74 let store_path = std::fs::read_link(&abs_gen_link)
75 .with_context(|| format!("readlink {}", abs_gen_link.display()))?;
76 let basename = store_path
77 .file_name()
78 .and_then(|n| n.to_str())
79 .ok_or_else(|| anyhow!("non-utf8 basename: {}", store_path.display()))?
80 .to_string();
81 Ok(basename)
82}