diff --git a/CHANGELOG.md b/CHANGELOG.md index 350d18f55b..c9e0c3fa1a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ([#857](https://github.com/nix-rust/nix/pull/857)) - Added `request_code_write_int!` on FreeBSD/DragonFlyBSD ([#833](https://github.com/nix-rust/nix/pull/833)) +- Added `nix::unistd::{getdomainname, setdomainname}` for all platforms except Android. + ([#816](https://github.com/nix-rust/nix/pull/816)) ### Changed - Display and Debug for SysControlAddr now includes all fields. @@ -49,6 +51,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). ([#892](https://github.com/nix-rust/nix/pull/892)) - Remove `IFF_NOTRAILERS` on OpenBSD, as it has been removed in OpenBSD 6.3 ([#893](https://github.com/nix-rust/nix/pull/893)) +- Changed `nix::unistd::gethostname` to internally allocate its buffer. + ([#816](https://github.com/nix-rust/nix/pull/816)) ### Fixed - Fixed possible panics when using `SigAction::flags` on Linux diff --git a/src/unistd.rs b/src/unistd.rs index 8022aa0b2c..2a4458f38b 100644 --- a/src/unistd.rs +++ b/src/unistd.rs @@ -739,34 +739,87 @@ pub fn sethostname>(name: S) -> Result<()> { Errno::result(res).map(drop) } -/// Get the host name and store it in the provided buffer, returning a pointer -/// the `CStr` in that buffer on success (see +/// Get the host name, returning a `OsString` on success (see /// [gethostname(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/gethostname.html)). /// -/// This function call attempts to get the host name for the running system and -/// store it in a provided buffer. The buffer will be populated with bytes up -/// to the length of the provided slice including a NUL terminating byte. If -/// the hostname is longer than the length provided, no error will be provided. -/// The posix specification does not specify whether implementations will -/// null-terminate in this case, but the nix implementation will ensure that the -/// buffer is null terminated in this case. +/// This function call attempts to get the host name for the +/// running system. /// -/// ```no_run +/// # Examples +/// +/// ``` /// use nix::unistd; /// -/// let mut buf = [0u8; 64]; -/// let hostname_cstr = unistd::gethostname(&mut buf).expect("Failed getting hostname"); -/// let hostname = hostname_cstr.to_str().expect("Hostname wasn't valid UTF-8"); +/// let hostname_os = unistd::gethostname().expect("Failed getting hostname"); +/// let hostname = hostname_os.into_string().expect("Hostname wasn't valid UTF-8"); /// println!("Hostname: {}", hostname); /// ``` -pub fn gethostname(buffer: &mut [u8]) -> Result<&CStr> { - let ptr = buffer.as_mut_ptr() as *mut c_char; - let len = buffer.len() as size_t; +pub fn gethostname() -> Result { + // Minimum hostname maximum length as defined by POSIX, + // http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/limits.h.html + const _POSIX_HOST_NAME_MAX: c_long = 255; + let buf_len = sysconf(SysconfVar::HOST_NAME_MAX) + .unwrap_or(None).unwrap_or(_POSIX_HOST_NAME_MAX + 1); + let mut buf = vec![0; buf_len as usize]; + + let res = unsafe { libc::gethostname(buf.as_mut_ptr() as *mut c_char, buf_len as usize) }; + Errno::result(res).map(|_| { + buf[(buf_len - 1) as usize] = 0; + OsString::from_vec(buf) + }) +} - let res = unsafe { libc::gethostname(ptr, len) }; +cfg_if!{ + if #[cfg(any(target_os = "dragonfly", target_os = "freebsd", + target_os = "ios", target_os = "macos"))] { + type namelen_t = c_int; + } else if #[cfg(not(target_os = "android"))] { + type namelen_t = size_t; + } +} + +/// Set the NIS domain name (see +/// [setdomainname(2)](http://man7.org/linux/man-pages/man2/setdomainname.2.html)). +#[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "ios", + target_os = "linux", target_os = "macos", target_os = "netbsd", + target_os = "openbsd"))] +pub fn setdomainname>(name: S) -> Result<()> { + let ptr = name.as_ref().as_bytes().as_ptr() as *const c_char; + let len = name.as_ref().as_bytes().len() as namelen_t; + let res = unsafe { libc::setdomainname(ptr, len) }; + Errno::result(res).map(drop) +} + +/// Get the NIS domain name, returning a `OsString` on success (see +/// [getdomainname(2)](http://man7.org/linux/man-pages/man2/getdomainname.2.html)). +/// +/// This function call attempts to get the NIS domain name for the +/// running system. +/// +/// # Examples +/// +/// ``` +/// use nix::unistd; +/// +/// let domainname_os = unistd::getdomainname().expect("Failed getting domain name"); +/// let domainname = domainname_os.into_string().expect("Domain name wasn't valid UTF-8"); +/// println!("Domain name: {}", domainname); +/// ``` +#[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "ios", + target_os = "linux", target_os = "macos", target_os = "netbsd", + target_os = "openbsd"))] +pub fn getdomainname() -> Result { + // Minimum hostname maximum length as defined by POSIX, + // http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/limits.h.html + const _POSIX_HOST_NAME_MAX: c_long = 255; + let buf_len = sysconf(SysconfVar::HOST_NAME_MAX) + .unwrap_or(None).unwrap_or(_POSIX_HOST_NAME_MAX + 1); + let mut buf = vec![0; buf_len as usize]; + + let res = unsafe { libc::getdomainname(buf.as_mut_ptr() as *mut c_char, buf_len as namelen_t) }; Errno::result(res).map(|_| { - buffer[len - 1] = 0; // ensure always null-terminated - unsafe { CStr::from_ptr(buffer.as_ptr() as *const c_char) } + buf[(buf_len - 1) as usize] = 0; + OsString::from_vec(buf) }) } @@ -1688,9 +1741,6 @@ pub enum SysconfVar { /// Maximum number of expressions that can be nested within parentheses by /// the expr utility. EXPR_NEST_MAX = libc::_SC_EXPR_NEST_MAX, - #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios", - target_os="linux", target_os = "macos", target_os="netbsd", - target_os="openbsd"))] /// Maximum length of a host name (not including the terminating null) as /// returned from the `gethostname` function HOST_NAME_MAX = libc::_SC_HOST_NAME_MAX, diff --git a/test/test_unistd.rs b/test/test_unistd.rs index fe33b1d06c..2c6f66b45d 100644 --- a/test/test_unistd.rs +++ b/test/test_unistd.rs @@ -457,3 +457,24 @@ fn test_canceling_alarm() { assert_eq!(alarm::set(60), None); assert_eq!(alarm::cancel(), Some(60)); } + +#[test] +fn test_gethostname() { + let hn1 = gethostname().expect("first gethostname failed") + .into_string().expect("hostname contains invalid data"); + let hn2 = gethostname().expect("second gethostname failed") + .into_string().expect("hostname contains invalid data"); + assert_eq!(hn1, hn2); +} + +#[test] +#[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "ios", + target_os = "linux", target_os = "macos", target_os = "netbsd", + target_os = "openbsd"))] +fn test_getdomainname() { + let dn1 = getdomainname().expect("first getdomainname failed") + .into_string().expect("domainname contains invalid data"); + let dn2 = getdomainname().expect("second getdomainname failed") + .into_string().expect("domainname contains invalid data"); + assert_eq!(dn1, dn2); +}