running_process_platform_internal/platform/
host.rs1use std::io;
9
10pub use crate::{
11 host_boot_id as boot_id, host_current_process_privilege as current_process_privilege,
12 host_environment_keys_are_case_insensitive as environment_keys_are_case_insensitive,
13 host_filesystem_device_id as filesystem_device_id, host_hostname as hostname,
14 host_login_environment as login_environment, host_machine_id as machine_id,
15 host_namespace_id as namespace_id, host_process_cgroup as process_cgroup,
16 host_user_machine_identity as user_machine_identity,
17 HostPrivilegedIdentity as PrivilegedIdentity,
18};
19
20pub use crate::host_login_environment_block as login_environment_block;
21
22#[allow(dead_code)]
34pub(crate) fn machine_id_from(machine_id_paths: &[&str], boot_id_path: &str) -> io::Result<String> {
35 for path in machine_id_paths {
36 match std::fs::read_to_string(path) {
37 Ok(s) => {
38 let trimmed = s.trim();
39 if !trimmed.is_empty() {
40 return Ok(trimmed.to_string());
41 }
42 }
43 Err(err) if err.kind() == io::ErrorKind::NotFound => continue,
44 Err(err) => return Err(io::Error::other(format!("read {path}: {err}"))),
50 }
51 }
52 if let Ok(s) = std::fs::read_to_string(boot_id_path) {
59 let trimmed = s.trim();
60 if !trimmed.is_empty() {
61 return Ok(format!("boot:{trimmed}"));
62 }
63 }
64 Err(io::Error::other(
65 "no /etc/machine-id or /var/lib/dbus/machine-id found, and no usable boot_id fallback",
66 ))
67}
68
69#[cfg(test)]
70mod tests {
71 use super::*;
72
73 #[test]
80 fn an_ordinary_test_process_is_not_privileged() {
81 let privilege = current_process_privilege().expect("privilege lookup must succeed");
82 assert_eq!(
83 privilege, None,
84 "test runs are expected unprivileged; got {privilege:?}"
85 );
86 }
87
88 mod machine_id_sources {
89 use super::super::machine_id_from;
90
91 fn temp_dir(label: &str) -> std::path::PathBuf {
92 let dir = std::env::temp_dir().join(format!(
93 "rp-host-{label}-{}-{:?}",
94 std::process::id(),
95 std::thread::current().id(),
96 ));
97 std::fs::create_dir_all(&dir).expect("create temp dir");
98 dir
99 }
100
101 fn write(dir: &std::path::Path, name: &str, content: &str) -> String {
102 let path = dir.join(name);
103 std::fs::write(&path, content).expect("write fixture file");
104 path.to_string_lossy().into_owned()
105 }
106
107 #[test]
108 fn machine_id_file_wins_over_boot_fallback() {
109 let dir = temp_dir("wins");
110 let machine = write(
111 &dir,
112 "machine-id",
113 " abc123
114",
115 );
116 let boot = write(
117 &dir, "boot-id", "zzz
118",
119 );
120 assert_eq!(
121 machine_id_from(&[&machine], &boot).expect("resolve"),
122 "abc123"
123 );
124 let _ = std::fs::remove_dir_all(&dir);
125 }
126
127 #[test]
128 fn second_path_is_consulted_when_first_is_missing() {
129 let dir = temp_dir("second");
130 let missing = dir.join("absent").to_string_lossy().into_owned();
131 let machine = write(
132 &dir,
133 "machine-id",
134 "def456
135",
136 );
137 let boot = write(
138 &dir, "boot-id", "zzz
139",
140 );
141 assert_eq!(
142 machine_id_from(&[&missing, &machine], &boot).expect("resolve"),
143 "def456"
144 );
145 let _ = std::fs::remove_dir_all(&dir);
146 }
147
148 #[test]
149 fn missing_machine_id_files_fall_back_to_boot_id() {
150 let dir = temp_dir("fallback");
151 let missing = dir.join("absent").to_string_lossy().into_owned();
152 let boot = write(
153 &dir,
154 "boot-id",
155 "boot-value
156",
157 );
158 assert_eq!(
159 machine_id_from(&[&missing], &boot).expect("resolve"),
160 "boot:boot-value"
161 );
162 let _ = std::fs::remove_dir_all(&dir);
163 }
164
165 #[test]
166 fn empty_machine_id_file_falls_through_to_boot_id() {
167 let dir = temp_dir("empty");
168 let machine = write(
169 &dir,
170 "machine-id",
171 "
172",
173 );
174 let boot = write(
175 &dir,
176 "boot-id",
177 "boot-value
178",
179 );
180 assert_eq!(
181 machine_id_from(&[&machine], &boot).expect("resolve"),
182 "boot:boot-value"
183 );
184 let _ = std::fs::remove_dir_all(&dir);
185 }
186
187 #[test]
188 fn unreadable_machine_id_stays_a_hard_error_despite_boot_fallback() {
189 let dir = temp_dir("unreadable");
190 let as_dir = dir.join("machine-id-dir");
193 std::fs::create_dir_all(&as_dir).expect("create dir fixture");
194 let as_dir = as_dir.to_string_lossy().into_owned();
195 let boot = write(&dir, "boot-id", "boot-uuid\n");
196 machine_id_from(&[&as_dir], &boot)
197 .expect_err("unreadable machine-id must not fall through");
198 let _ = std::fs::remove_dir_all(&dir);
199 }
200
201 #[test]
202 fn everything_missing_is_an_error() {
203 let dir = temp_dir("nothing");
204 let missing = dir.join("absent").to_string_lossy().into_owned();
205 let no_boot = dir.join("absent-boot").to_string_lossy().into_owned();
206 assert!(machine_id_from(&[&missing], &no_boot).is_err());
207 let _ = std::fs::remove_dir_all(&dir);
208 }
209 }
210
211 #[test]
214 fn process_cgroup_is_readable_where_the_host_has_cgroups() {
215 if let Some(membership) = process_cgroup() {
216 let text = membership.expect("own cgroup membership must be readable");
217 assert!(!text.trim().is_empty(), "empty cgroup membership");
218 }
219 }
220
221 #[test]
223 fn privileged_identities_describe_themselves_concretely() {
224 assert_eq!(
225 PrivilegedIdentity::UnixRoot.to_string(),
226 "root (effective uid 0)"
227 );
228 assert_eq!(
229 PrivilegedIdentity::WindowsLocalSystem.to_string(),
230 "Windows LocalSystem (S-1-5-18)"
231 );
232 }
233}