use std::ffi::OsString;
pub(crate) fn hostname() -> Option<OsString> {
#[cfg(unix)]
{
nix::unistd::gethostname().ok()
}
#[cfg(windows)]
{
use std::os::windows::ffi::OsStringExt;
pub const COMPUTER_NAME_PHYSICAL_DNS_HOSTNAME: i32 = 5;
::windows_targets::link!("kernel32.dll" "system" fn GetComputerNameExW(nametype: i32, lpbuffer: *mut u16, nsize: *mut u32) -> i32);
let mut buffer_size: u32 = 0;
unsafe {
GetComputerNameExW(
COMPUTER_NAME_PHYSICAL_DNS_HOSTNAME,
std::ptr::null_mut(),
&mut buffer_size,
)
};
assert!(
0 < buffer_size,
"GetComputerNameExW did not provide buffer size"
);
let mut buffer = vec![0_u16; buffer_size as usize];
unsafe {
if GetComputerNameExW(
COMPUTER_NAME_PHYSICAL_DNS_HOSTNAME,
buffer.as_mut_ptr(),
&mut buffer_size,
) == 0
{
panic!(
"GetComputerNameExW failed to read hostname.
Please report this issue to <https://github.com/swsnr/gethostname.rs/issues>!"
);
}
}
assert!(
buffer_size as usize == buffer.len() - 1,
"GetComputerNameExW changed the buffer size unexpectedly"
);
let end = buffer.iter().position(|&b| b == 0).unwrap_or(buffer.len());
Some(OsString::from_wide(&buffer[0..end]))
}
}
#[cfg(test)]
mod tests {
use std::process::Command;
#[test]
fn test_hostname_matches_system_hostname() {
let output = Command::new("hostname")
.output()
.expect("failed to get hostname");
if output.status.success() {
let system_hostname = String::from_utf8_lossy(&output.stdout);
let system_hostname = system_hostname.trim_end().to_lowercase();
assert!(!system_hostname.is_empty());
let hostname = super::hostname().unwrap();
let hostname = hostname.into_string().unwrap().to_lowercase();
println!("system_hostname={system_hostname}, hostname={hostname}");
assert_eq!(system_hostname, hostname);
} else {
panic!(
"failed to get hostname: {}",
String::from_utf8_lossy(&output.stderr)
);
}
}
}