use super::{Error, Result};
pub(crate) struct Peer;
#[crate::interface(
name = "org.freedesktop.DBus.Peer",
introspection_docs = false,
proxy(visibility = "pub")
)]
impl Peer {
fn ping(&self) {}
fn get_machine_id(&self) -> Result<String> {
#[cfg(any(
target_os = "freebsd",
target_os = "dragonfly",
target_os = "openbsd",
target_os = "netbsd"
))]
if let Some(id) = read_dbus_machine_id() {
return Ok(id);
}
get_platform_machine_id()
}
}
#[cfg(target_os = "linux")]
fn get_platform_machine_id() -> Result<String> {
let mut id = match std::fs::read_to_string("/var/lib/dbus/machine-id") {
Ok(id) => id,
Err(e) => {
if let Ok(id) = std::fs::read_to_string("/etc/machine-id") {
id
} else {
return Err(Error::IOError(format!(
"Failed to read from /var/lib/dbus/machine-id or /etc/machine-id: {e}"
)));
}
}
};
let len = id.trim_end().len();
id.truncate(len);
Ok(id)
}
#[cfg(target_os = "macos")]
fn get_platform_machine_id() -> Result<String> {
unsafe extern "C" {
fn gethostuuid(id: *mut u8, wait: *const libc::timespec) -> libc::c_int;
}
let mut uuid = [0u8; 16];
let timeout = libc::timespec {
tv_sec: 1,
tv_nsec: 0,
};
let ret = unsafe { gethostuuid(uuid.as_mut_ptr(), &timeout) };
if ret != 0 {
return Err(Error::IOError(format!(
"gethostuuid failed: {}",
std::io::Error::last_os_error()
)));
}
Ok(uuid.iter().map(|b| format!("{b:02x}")).collect())
}
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
fn get_platform_machine_id() -> Result<String> {
use std::ffi::CStr;
let mut buf = [0u8; 64];
let mut len = buf.len();
let mib_name = c"kern.hostuuid";
let ret = unsafe {
libc::sysctlbyname(
mib_name.as_ptr(),
buf.as_mut_ptr() as *mut libc::c_void,
&mut len,
std::ptr::null(),
0,
)
};
if ret != 0 {
return Err(Error::IOError(format!(
"sysctlbyname(kern.hostuuid) failed: {}",
std::io::Error::last_os_error()
)));
}
let uuid_str = CStr::from_bytes_until_nul(&buf[..len])
.map_err(|e| Error::IOError(format!("Invalid UTF-8 in hostuuid: {e}")))?
.to_str()
.map_err(|e| Error::IOError(format!("Invalid UTF-8 in hostuuid: {e}")))?;
let machine_id: String = uuid_str.chars().filter(|c| *c != '-').collect();
if machine_id.len() != 32 || !machine_id.chars().all(|c| c.is_ascii_hexdigit()) {
return Err(Error::IOError(format!(
"Invalid hostuuid format: {uuid_str}"
)));
}
Ok(machine_id)
}
#[cfg(any(
target_os = "freebsd",
target_os = "dragonfly",
target_os = "openbsd",
target_os = "netbsd"
))]
fn read_dbus_machine_id() -> Option<String> {
const MACHINE_ID_PATHS: &[&str] = &[
"/var/lib/dbus/machine-id",
"/etc/machine-id",
"/var/db/dbus/machine-id",
];
for path in MACHINE_ID_PATHS {
if let Ok(mut id) = std::fs::read_to_string(path) {
let len = id.trim_end().len();
id.truncate(len);
if !id.is_empty() {
return Some(id);
}
}
}
None
}
#[cfg(any(target_os = "openbsd", target_os = "netbsd"))]
fn get_platform_machine_id() -> Result<String> {
Err(Error::IOError(
"No machine-id found. Please ensure D-Bus is properly installed and \
/var/db/dbus/machine-id or /etc/machine-id exists."
.to_string(),
))
}
#[cfg(all(
unix,
not(any(
target_os = "linux",
target_os = "macos",
target_os = "freebsd",
target_os = "dragonfly",
target_os = "openbsd",
target_os = "netbsd"
))
))]
fn get_platform_machine_id() -> Result<String> {
Err(Error::NotSupported(
"get_machine_id is not yet implemented on this platform".to_string(),
))
}
#[cfg(windows)]
fn get_platform_machine_id() -> Result<String> {
crate::win32::machine_id().map_err(|e| Error::IOError(e.to_string()))
}
#[cfg(test)]
mod tests {
#[allow(unused)]
use super::*;
#[test]
#[cfg(target_os = "linux")]
fn linux_machine_id() {
if let Ok(id) = get_platform_machine_id() {
assert_eq!(id.len(), 32, "machine ID should be 32 hex characters");
assert!(
id.chars().all(|c| c.is_ascii_hexdigit()),
"machine ID should only contain hex characters"
);
}
}
#[test]
#[cfg(target_os = "macos")]
fn macos_machine_id() {
let id = get_platform_machine_id().expect("gethostuuid should succeed on macOS");
assert_eq!(id.len(), 32, "machine ID should be 32 hex characters");
assert!(
id.chars().all(|c| c.is_ascii_hexdigit()),
"machine ID should only contain hex characters"
);
}
#[test]
#[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
fn freebsd_machine_id() {
let peer = Peer;
let id = peer
.get_machine_id()
.expect("should get machine ID on FreeBSD/DragonFlyBSD");
assert_eq!(id.len(), 32, "machine ID should be 32 hex characters");
assert!(
id.chars().all(|c| c.is_ascii_hexdigit()),
"machine ID should only contain hex characters"
);
}
#[test]
#[cfg(any(target_os = "openbsd", target_os = "netbsd"))]
fn openbsd_netbsd_machine_id() {
let peer = Peer;
if let Ok(id) = peer.get_machine_id() {
assert_eq!(id.len(), 32, "machine ID should be 32 hex characters");
assert!(
id.chars().all(|c| c.is_ascii_hexdigit()),
"machine ID should only contain hex characters"
);
}
}
}