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}