Skip to content

unistd: add {get,set}domainname #816

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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
Expand Down
94 changes: 72 additions & 22 deletions src/unistd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -739,34 +739,87 @@ pub fn sethostname<S: AsRef<OsStr>>(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<OsString> {
// 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<S: AsRef<OsStr>>(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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please prefix this line with a # so they don't render in the final help. You can then also delete the blank line below.

///
/// let domainname_os = unistd::getdomainname().expect("Failed getting domain name");
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can reuse the name domainname here because of Rust's variable shadowing. Doing this is idiomatic. Please rename this variable to domainname.

/// 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<OsString> {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto my comments about CString as in gethostname

// 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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why isn't this constant pulled from libc? I didn't see anywhere in the discussion a comment touching on this.

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

Expand Down Expand Up @@ -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,
Expand Down
21 changes: 21 additions & 0 deletions test/test_unistd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}