#
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}