use std::io;
pub use crate::{
host_boot_id as boot_id, host_current_process_privilege as current_process_privilege,
host_current_user as current_user,
host_environment_keys_are_case_insensitive as environment_keys_are_case_insensitive,
host_filesystem_device_id as filesystem_device_id, host_home_dir as home_dir,
host_hostname as hostname, host_is_elevated as is_elevated,
host_login_environment as login_environment, host_machine_id as machine_id,
host_namespace_id as namespace_id, host_user_machine_identity as user_machine_identity,
HostPrivilegedIdentity as PrivilegedIdentity,
};
pub use crate::host_login_environment_block as login_environment_block;
pub use crate::host_cpu_compatibility_features as cpu_compatibility_features;
#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
pub struct ProcessTarget {
pub os: &'static str,
pub architecture: &'static str,
}
pub const fn process_target() -> ProcessTarget {
ProcessTarget {
os: std::env::consts::OS,
architecture: std::env::consts::ARCH,
}
}
pub const fn target_is_windows() -> bool {
matches!(std::env::consts::OS.as_bytes(), b"windows")
}
pub const fn target_is_macos() -> bool {
matches!(std::env::consts::OS.as_bytes(), b"macos")
}
pub const fn target_is_linux() -> bool {
matches!(std::env::consts::OS.as_bytes(), b"linux")
}
pub fn available_parallelism() -> Option<usize> {
std::thread::available_parallelism()
.ok()
.map(std::num::NonZeroUsize::get)
}
pub fn cpu_identity_material() -> String {
let mut material = format!(
"arch={}\0os={}",
std::env::consts::ARCH,
std::env::consts::OS
);
if let Some(id) = machine_id() {
material.push_str("\0machine-id=");
material.push_str(&id);
} else if let Some(name) = hostname() {
material.push_str("\0hostname=");
material.push_str(&name);
} else {
material.push_str("\0pid=");
material.push_str(&std::process::id().to_string());
}
append_cpu_feature_material(&mut material);
material
}
fn append_cpu_feature_material(material: &mut String) {
for name in cpu_compatibility_features() {
material.push_str("\0feature=");
material.push_str(name);
}
}
#[allow(dead_code)]
pub(crate) fn machine_id_from(machine_id_paths: &[&str], boot_id_path: &str) -> io::Result<String> {
for path in machine_id_paths {
match std::fs::read_to_string(path) {
Ok(s) => {
let trimmed = s.trim();
if !trimmed.is_empty() {
return Ok(trimmed.to_string());
}
}
Err(err) if err.kind() == io::ErrorKind::NotFound => continue,
Err(err) => return Err(io::Error::other(format!("read {path}: {err}"))),
}
}
if let Ok(s) = std::fs::read_to_string(boot_id_path) {
let trimmed = s.trim();
if !trimmed.is_empty() {
return Ok(format!("boot:{trimmed}"));
}
}
Err(io::Error::other(
"no /etc/machine-id or /var/lib/dbus/machine-id found, and no usable boot_id fallback",
))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn exactly_one_target_predicate_holds_and_matches_process_target() {
let predicates = [
("windows", target_is_windows()),
("macos", target_is_macos()),
("linux", target_is_linux()),
];
let named: Vec<_> = predicates
.iter()
.filter_map(|(name, holds)| holds.then_some(*name))
.collect();
assert_eq!(named.len(), 1, "one supported host answers: {named:?}");
assert_eq!(named[0], process_target().os);
}
#[test]
fn target_predicates_are_constant_expressions() {
const WINDOWS: bool = target_is_windows();
const MACOS: bool = target_is_macos();
const LINUX: bool = target_is_linux();
assert_eq!([WINDOWS, MACOS, LINUX].iter().filter(|held| **held).count(), 1);
}
#[test]
fn cpu_compatibility_features_are_ordered_and_unique() {
const DOCUMENTED: [&str; 8] = [
"sse2", "sse4.2", "avx", "avx2", "avx512f", "fma", "bmi1", "bmi2",
];
let reported = cpu_compatibility_features();
let mut positions = reported
.iter()
.map(|name| DOCUMENTED.iter().position(|known| known == name));
assert!(
positions.all(|position| position.is_some()),
"every reported feature is one this facade documents: {reported:?}"
);
let indices: Vec<_> = reported
.iter()
.filter_map(|name| DOCUMENTED.iter().position(|known| known == name))
.collect();
assert!(
indices.windows(2).all(|pair| pair[0] < pair[1]),
"documented order is preserved, with no repeats: {reported:?}"
);
}
#[test]
fn identity_material_still_names_each_reported_feature() {
let material = cpu_identity_material();
for name in cpu_compatibility_features() {
assert!(
material.contains(&format!("\0feature={name}")),
"{name} is part of the identity material"
);
}
}
#[test]
fn current_process_privilege_is_stable() {
let first = current_process_privilege().expect("privilege lookup must succeed");
let second = current_process_privilege().expect("repeat lookup must succeed");
assert_eq!(first, second);
}
#[test]
fn is_elevated_answers_without_erroring() {
assert!(is_elevated().is_ok());
}
#[test]
fn current_user_and_home_dir_answer_on_a_normal_session() {
assert!(current_user().is_some_and(|name| !name.is_empty()));
assert!(home_dir().is_some_and(|dir| !dir.as_os_str().is_empty()));
}
#[test]
fn cpu_identity_material_is_present_and_stable() {
let first = cpu_identity_material();
assert!(!first.is_empty());
assert_eq!(first, cpu_identity_material());
assert!(first.contains(&format!("arch={}", std::env::consts::ARCH)));
assert!(first.contains(&format!("os={}", std::env::consts::OS)));
}
#[test]
fn available_parallelism_reports_at_least_one_cpu() {
assert!(available_parallelism().is_some_and(|count| count >= 1));
}
mod machine_id_sources {
use super::super::machine_id_from;
fn temp_dir(label: &str) -> std::path::PathBuf {
let dir = std::env::temp_dir().join(format!(
"rp-host-{label}-{}-{:?}",
std::process::id(),
std::thread::current().id(),
));
std::fs::create_dir_all(&dir).expect("create temp dir");
dir
}
fn write(dir: &std::path::Path, name: &str, content: &str) -> String {
let path = dir.join(name);
std::fs::write(&path, content).expect("write fixture file");
path.to_string_lossy().into_owned()
}
#[test]
fn machine_id_file_wins_over_boot_fallback() {
let dir = temp_dir("wins");
let machine = write(
&dir,
"machine-id",
" abc123
",
);
let boot = write(
&dir, "boot-id", "zzz
",
);
assert_eq!(
machine_id_from(&[&machine], &boot).expect("resolve"),
"abc123"
);
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn second_path_is_consulted_when_first_is_missing() {
let dir = temp_dir("second");
let missing = dir.join("absent").to_string_lossy().into_owned();
let machine = write(
&dir,
"machine-id",
"def456
",
);
let boot = write(
&dir, "boot-id", "zzz
",
);
assert_eq!(
machine_id_from(&[&missing, &machine], &boot).expect("resolve"),
"def456"
);
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn missing_machine_id_files_fall_back_to_boot_id() {
let dir = temp_dir("fallback");
let missing = dir.join("absent").to_string_lossy().into_owned();
let boot = write(
&dir,
"boot-id",
"boot-value
",
);
assert_eq!(
machine_id_from(&[&missing], &boot).expect("resolve"),
"boot:boot-value"
);
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn empty_machine_id_file_falls_through_to_boot_id() {
let dir = temp_dir("empty");
let machine = write(
&dir,
"machine-id",
"
",
);
let boot = write(
&dir,
"boot-id",
"boot-value
",
);
assert_eq!(
machine_id_from(&[&machine], &boot).expect("resolve"),
"boot:boot-value"
);
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn unreadable_machine_id_stays_a_hard_error_despite_boot_fallback() {
let dir = temp_dir("unreadable");
let as_dir = dir.join("machine-id-dir");
std::fs::create_dir_all(&as_dir).expect("create dir fixture");
let as_dir = as_dir.to_string_lossy().into_owned();
let boot = write(&dir, "boot-id", "boot-uuid\n");
machine_id_from(&[&as_dir], &boot)
.expect_err("unreadable machine-id must not fall through");
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn everything_missing_is_an_error() {
let dir = temp_dir("nothing");
let missing = dir.join("absent").to_string_lossy().into_owned();
let no_boot = dir.join("absent-boot").to_string_lossy().into_owned();
assert!(machine_id_from(&[&missing], &no_boot).is_err());
let _ = std::fs::remove_dir_all(&dir);
}
}
#[test]
fn privileged_identities_describe_themselves_concretely() {
assert_eq!(
PrivilegedIdentity::UnixRoot.to_string(),
"root (effective uid 0)"
);
assert_eq!(
PrivilegedIdentity::WindowsLocalSystem.to_string(),
"Windows LocalSystem (S-1-5-18)"
);
}
}