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