grep_cli/hostname.rs
1use std::{ffi::OsString, io};
2
3/// Returns the hostname of the current system.
4///
5/// It is unusual, although technically possible, for this routine to return
6/// an error. It is difficult to list out the error conditions, but one such
7/// possibility is platform support.
8///
9/// # Platform specific behavior
10///
11/// On Windows, this currently uses the "physical DNS hostname" computer name.
12/// This may change in the future.
13///
14/// On Unix, this returns the result of the `gethostname` function from the
15/// `libc` linked into the program.
16pub fn hostname() -> io::Result<OsString> {
17 #[cfg(windows)]
18 {
19 use winapi_util::sysinfo::{get_computer_name, ComputerNameKind};
20 get_computer_name(ComputerNameKind::PhysicalDnsHostname)
21 }
22 #[cfg(unix)]
23 {
24 gethostname()
25 }
26 #[cfg(not(any(windows, unix)))]
27 {
28 Err(io::Error::new(
29 io::ErrorKind::Other,
30 "hostname could not be found on unsupported platform",
31 ))
32 }
33}
34
35#[cfg(unix)]
36fn gethostname() -> io::Result<OsString> {
37 use std::os::unix::ffi::OsStringExt;
38
39 // SAFETY: There don't appear to be any safety requirements for calling
40 // sysconf.
41 let limit = unsafe { libc::sysconf(libc::_SC_HOST_NAME_MAX) };
42 if limit == -1 {
43 // It is in theory possible for sysconf to return -1 for a limit but
44 // *not* set errno, in which case, io::Error::last_os_error is
45 // indeterminate. But untangling that is super annoying because std
46 // doesn't expose any unix-specific APIs for inspecting the errno. (We
47 // could do it ourselves, but it just doesn't seem worth doing?)
48 return Err(io::Error::last_os_error());
49 }
50 let Ok(maxlen) = usize::try_from(limit) else {
51 let msg = format!("host name max limit ({}) overflowed usize", limit);
52 return Err(io::Error::new(io::ErrorKind::Other, msg));
53 };
54 // maxlen here includes the NUL terminator.
55 let mut buf = vec![0; maxlen];
56 // SAFETY: The pointer we give is valid as it is derived directly from a
57 // Vec. Similarly, `maxlen` is the length of our Vec, and is thus valid
58 // to write to.
59 let rc = unsafe {
60 libc::gethostname(buf.as_mut_ptr().cast::<libc::c_char>(), maxlen)
61 };
62 if rc == -1 {
63 return Err(io::Error::last_os_error());
64 }
65 // POSIX says that if the hostname is bigger than `maxlen`, then it may
66 // write a truncate name back that is not necessarily NUL terminated (wtf,
67 // lol). So if we can't find a NUL terminator, then just give up.
68 let Some(zeropos) = buf.iter().position(|&b| b == 0) else {
69 let msg = "could not find NUL terminator in hostname";
70 return Err(io::Error::new(io::ErrorKind::Other, msg));
71 };
72 buf.truncate(zeropos);
73 buf.shrink_to_fit();
74 Ok(OsString::from_vec(buf))
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80
81 #[test]
82 fn print_hostname() {
83 println!("{:?}", hostname().unwrap());
84 }
85}