From 4e5d3768a470084752783b8af577251327ce5baf Mon Sep 17 00:00:00 2001 From: Paul Osborne Date: Sat, 29 Oct 2016 17:04:37 -0500 Subject: [PATCH 1/3] unistd: add docs for gethostname and tweak API Although the underlying C API does take a pointer to a set of characters, it is a requirement of almost every operating system that these bytes not contain a premature NUL character or other special characters. In other words, you want a `&str`. Changing this to make the API make a bit more sense. Signed-off-by: Paul Osborne --- CHANGELOG.md | 2 ++ src/unistd.rs | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 43f33569fb..8bd38311f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -64,6 +64,8 @@ This project adheres to [Semantic Versioning](http://semver.org/). - `SigFlags` in `::nix::sys::signal` has be renamed to `SigmaskHow` and its type has changed from `bitflags` to `enum` in order to conform to our conventions. ([#460](https://github.com/nix-rust/nix/pull/460)) +- `sethostname` now takes a `&str` instead of a `&[u8]` as this provides an API + that makes more sense in normal, correct usage of the API. ### Fixed - Fixed multiple issues with Unix domain sockets on non-Linux OSes diff --git a/src/unistd.rs b/src/unistd.rs index f7efbdee61..301801089f 100644 --- a/src/unistd.rs +++ b/src/unistd.rs @@ -480,7 +480,14 @@ pub fn daemon(nochdir: bool, noclose: bool) -> Result<()> { Errno::result(res).map(drop) } -pub fn sethostname(name: &[u8]) -> Result<()> { +/// Set the system host name (see +/// [gethostname(2)](http://man7.org/linux/man-pages/man2/gethostname.2.html)). +/// +/// Given a name, attempt to update the system host name to the given string. +/// On some systems, the host name is limited to as few as 64 bytes. An error +/// will be return if the name is not valid or the current process does not have +/// permissions to update the host name. +pub fn sethostname(name: &str) -> Result<()> { // Handle some differences in type of the len arg across platforms. cfg_if! { if #[cfg(any(target_os = "dragonfly", From 34912343a0997830a7bc49522abeb3b6c551a8dc Mon Sep 17 00:00:00 2001 From: Paul Osborne Date: Sat, 29 Oct 2016 17:19:39 -0500 Subject: [PATCH 2/3] unistd: document and change implementation of gethostname Previously gethostname just mutated a buffer. We now provide a slightly more usable (but still allocation free) API that ensures that the returned buffer is NUL-terminated. We give back a `&CStr` instead of requiring that the user do all of the conversions from `&[u8]` when we know we are dealing with a `&CStr`. Signed-off-by: Paul Osborne --- CHANGELOG.md | 5 +++++ src/unistd.rs | 31 +++++++++++++++++++++++++++---- 2 files changed, 32 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8bd38311f0..02b078997d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -66,6 +66,11 @@ This project adheres to [Semantic Versioning](http://semver.org/). ([#460](https://github.com/nix-rust/nix/pull/460)) - `sethostname` now takes a `&str` instead of a `&[u8]` as this provides an API that makes more sense in normal, correct usage of the API. +- `gethostname` previously did not expose the actual length of the hostname + written from the underlying system call at all. This has been updated to + return a `&CStr` within the provided buffer that is always properly + NUL-terminated (this is not guaranteed by the call with all platforms/libc + implementations). ### Fixed - Fixed multiple issues with Unix domain sockets on non-Linux OSes diff --git a/src/unistd.rs b/src/unistd.rs index 301801089f..69366eeab7 100644 --- a/src/unistd.rs +++ b/src/unistd.rs @@ -506,12 +506,35 @@ pub fn sethostname(name: &str) -> Result<()> { Errno::result(res).map(drop) } -pub fn gethostname(name: &mut [u8]) -> Result<()> { - let ptr = name.as_mut_ptr() as *mut c_char; - let len = name.len() as size_t; +/// Get the host name and store it in the provided buffer, returning a pointer +/// the CStr in that buffer on success (see +/// [gethostname(2)](http://man7.org/linux/man-pages/man2/gethostname.2.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. +/// +/// ```no_run +/// 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"); +/// println!("Hostname: {}", hostname); +/// ``` +pub fn gethostname<'a>(buffer: &'a mut [u8]) -> Result<&'a CStr> { + let ptr = buffer.as_mut_ptr() as *mut c_char; + let len = buffer.len() as size_t; let res = unsafe { libc::gethostname(ptr, len) }; - Errno::result(res).map(drop) + Errno::result(res).map(|_| { + buffer[len - 1] = 0; // ensure always null-terminated + unsafe { CStr::from_ptr(buffer.as_ptr() as *const c_char) } + }) } pub fn close(fd: RawFd) -> Result<()> { From 2c02ee978f9a219548bf2bed0437594a16366df3 Mon Sep 17 00:00:00 2001 From: Paul Osborne Date: Sun, 15 Jan 2017 22:47:12 -0600 Subject: [PATCH 3/3] sethostname: convert to taking OsStr ref Baed on discussions on the related PR, sethostname now takes an `S: AsRef` in order to allow for a greater range of inputs that allow for a more fluid interface. Signed-off-by: Paul Osborne --- src/unistd.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/unistd.rs b/src/unistd.rs index 69366eeab7..6382267fb0 100644 --- a/src/unistd.rs +++ b/src/unistd.rs @@ -5,8 +5,8 @@ use fcntl::{fcntl, OFlag, O_CLOEXEC, FD_CLOEXEC}; use fcntl::FcntlArg::F_SETFD; use libc::{self, c_char, c_void, c_int, c_uint, size_t, pid_t, off_t, uid_t, gid_t, mode_t}; use std::mem; -use std::ffi::{CString, CStr, OsString}; -use std::os::unix::ffi::{OsStringExt}; +use std::ffi::{CString, CStr, OsString, OsStr}; +use std::os::unix::ffi::{OsStringExt, OsStrExt}; use std::os::unix::io::RawFd; use std::path::{PathBuf}; use void::Void; @@ -487,7 +487,7 @@ pub fn daemon(nochdir: bool, noclose: bool) -> Result<()> { /// On some systems, the host name is limited to as few as 64 bytes. An error /// will be return if the name is not valid or the current process does not have /// permissions to update the host name. -pub fn sethostname(name: &str) -> Result<()> { +pub fn sethostname>(name: S) -> Result<()> { // Handle some differences in type of the len arg across platforms. cfg_if! { if #[cfg(any(target_os = "dragonfly", @@ -499,8 +499,8 @@ pub fn sethostname(name: &str) -> Result<()> { type sethostname_len_t = size_t; } } - let ptr = name.as_ptr() as *const c_char; - let len = name.len() as sethostname_len_t; + let ptr = name.as_ref().as_bytes().as_ptr() as *const c_char; + let len = name.as_ref().len() as sethostname_len_t; let res = unsafe { libc::sethostname(ptr, len) }; Errno::result(res).map(drop)