diff options
-rw-r--r-- | CHANGELOG.md | 12 | ||||
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | src/sys/epoll.rs | 35 | ||||
-rw-r--r-- | src/sys/eventfd.rs | 6 | ||||
-rw-r--r-- | src/sys/socket/mod.rs | 18 | ||||
-rw-r--r-- | src/unistd.rs | 61 | ||||
-rw-r--r-- | test/sys/mod.rs | 3 | ||||
-rw-r--r-- | test/sys/test_epoll.rs | 24 | ||||
-rw-r--r-- | test/test_unistd.rs | 19 |
9 files changed, 150 insertions, 29 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index c87c867e..335098bf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,8 +31,13 @@ This project adheres to [Semantic Versioning](http://semver.org/). ([#457](https://github.com/nix-rust/nix/pull/457)) - Added `getpgrp` in `::nix::unistd` ([#491](https://github.com/nix-rust/nix/pull/491)) +- Added `fchdir` in `::nix::unistd` + ([#497](https://github.com/nix-rust/nix/pull/497)) ### Changed +- `epoll_ctl` now could accept None as argument `event` + when op is `EpollOp::EpollCtlDel`. + ([#480](https://github.com/nix-rust/nix/pull/480)) - Removed the `bad` keyword from the `ioctl!` macro ([#478](https://github.com/nix-rust/nix/pull/478)) - Changed `TimeVal` into an opaque Newtype @@ -64,6 +69,13 @@ 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. +- `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 @@ -7,6 +7,7 @@ authors = ["The nix-rust Project Developers"] homepage = "https://github.com/nix-rust/nix" repository = "https://github.com/nix-rust/nix" license = "MIT" +categories = ["os::unix-apis"] exclude = [ ".gitignore", ".travis.yml", diff --git a/src/sys/epoll.rs b/src/sys/epoll.rs index 9774318f..eb28ffb9 100644 --- a/src/sys/epoll.rs +++ b/src/sys/epoll.rs @@ -1,6 +1,9 @@ use {Errno, Result}; use libc::{self, c_int}; use std::os::unix::io::RawFd; +use std::ptr; +use std::mem; +use ::Error; bitflags!( #[repr(C)] @@ -23,7 +26,7 @@ bitflags!( } ); -#[derive(Clone, Copy)] +#[derive(Clone, Copy, Eq, PartialEq)] #[repr(C)] pub enum EpollOp { EpollCtlAdd = 1, @@ -44,10 +47,14 @@ pub struct EpollEvent { } impl EpollEvent { - pub fn new(events: EpollFlags, data: u64) -> EpollEvent { + pub fn new(events: EpollFlags, data: u64) -> Self { EpollEvent { event: libc::epoll_event { events: events.bits(), u64: data } } } + pub fn empty() -> Self { + unsafe { mem::zeroed::<EpollEvent>() } + } + pub fn events(&self) -> EpollFlags { EpollFlags::from_bits(self.event.events).unwrap() } @@ -57,6 +64,16 @@ impl EpollEvent { } } +impl<'a> Into<&'a mut EpollEvent> for Option<&'a mut EpollEvent> { + #[inline] + fn into(self) -> &'a mut EpollEvent { + match self { + Some(epoll_event) => epoll_event, + None => unsafe { &mut *ptr::null_mut::<EpollEvent>() } + } + } +} + #[inline] pub fn epoll_create() -> Result<RawFd> { let res = unsafe { libc::epoll_create(1024) }; @@ -72,10 +89,16 @@ pub fn epoll_create1(flags: EpollCreateFlags) -> Result<RawFd> { } #[inline] -pub fn epoll_ctl(epfd: RawFd, op: EpollOp, fd: RawFd, event: &mut EpollEvent) -> Result<()> { - let res = unsafe { libc::epoll_ctl(epfd, op as c_int, fd, &mut event.event) }; - - Errno::result(res).map(drop) +pub fn epoll_ctl<'a, T>(epfd: RawFd, op: EpollOp, fd: RawFd, event: T) -> Result<()> + where T: Into<&'a mut EpollEvent> +{ + let event: &mut EpollEvent = event.into(); + if event as *const EpollEvent == ptr::null() && op != EpollOp::EpollCtlDel { + Err(Error::Sys(Errno::EINVAL)) + } else { + let res = unsafe { libc::epoll_ctl(epfd, op as c_int, fd, &mut event.event) }; + Errno::result(res).map(drop) + } } #[inline] diff --git a/src/sys/eventfd.rs b/src/sys/eventfd.rs index e6e410ec..8058e207 100644 --- a/src/sys/eventfd.rs +++ b/src/sys/eventfd.rs @@ -4,9 +4,9 @@ use {Errno, Result}; libc_bitflags! { flags EfdFlags: libc::c_int { - const EFD_CLOEXEC, // Since Linux 2.6.27 - const EFD_NONBLOCK, // Since Linux 2.6.27 - const EFD_SEMAPHORE, // Since Linux 2.6.30 + EFD_CLOEXEC, // Since Linux 2.6.27 + EFD_NONBLOCK, // Since Linux 2.6.27 + EFD_SEMAPHORE, // Since Linux 2.6.30 } } diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs index e041e6b6..645dfe41 100644 --- a/src/sys/socket/mod.rs +++ b/src/sys/socket/mod.rs @@ -50,17 +50,8 @@ pub use self::multicast::{ }; pub use self::consts::*; -#[cfg(any(not(target_os = "linux"), not(target_arch = "x86")))] pub use libc::sockaddr_storage; -// Working around rust-lang/rust#23425 -#[cfg(all(target_os = "linux", target_arch = "x86"))] -pub struct sockaddr_storage { - pub ss_family: sa_family_t, - pub __ss_align: u32, - pub __ss_pad2: [u8; 120], -} - #[derive(Clone, Copy, PartialEq, Eq, Debug)] #[repr(i32)] pub enum SockType { @@ -238,8 +229,13 @@ impl<'a> ControlMessage<'a> { let padlen = cmsg_align(mem::size_of_val(&cmsg)) - mem::size_of_val(&cmsg); - let buf2 = &mut &mut buf[padlen..]; - copy_bytes(fds, buf2); + + let mut tmpbuf = &mut [][..]; + mem::swap(&mut tmpbuf, buf); + let (_padding, mut remainder) = tmpbuf.split_at_mut(padlen); + mem::swap(buf, &mut remainder); + + copy_bytes(fds, buf); }, ControlMessage::Unknown(UnknownCmsg(orig_cmsg, bytes)) => { copy_bytes(orig_cmsg, buf); diff --git a/src/unistd.rs b/src/unistd.rs index f7efbdee..187154bd 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; @@ -249,6 +249,19 @@ pub fn chdir<P: ?Sized + NixPath>(path: &P) -> Result<()> { Errno::result(res).map(drop) } +/// Change the current working directory of the process to the one +/// given as an open file descriptor (see +/// [fchdir(2)](http://man7.org/linux/man-pages/man2/fchdir.2.html)). +/// +/// This function may fail in a number of different scenarios. See the man +/// pages for additional details on possible failure cases. +#[inline] +pub fn fchdir(dirfd: RawFd) -> Result<()> { + let res = unsafe { libc::fchdir(dirfd) }; + + Errno::result(res).map(drop) +} + /// Creates new directory `path` with access rights `mode`. /// /// # Errors @@ -480,7 +493,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<S: AsRef<OsStr>>(name: S) -> Result<()> { // Handle some differences in type of the len arg across platforms. cfg_if! { if #[cfg(any(target_os = "dragonfly", @@ -492,19 +512,42 @@ pub fn sethostname(name: &[u8]) -> 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) } -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<()> { diff --git a/test/sys/mod.rs b/test/sys/mod.rs index 25c9a92f..5e5eed41 100644 --- a/test/sys/mod.rs +++ b/test/sys/mod.rs @@ -9,3 +9,6 @@ mod test_ioctl; mod test_wait; mod test_select; mod test_uio; + +#[cfg(target_os = "linux")] +mod test_epoll; diff --git a/test/sys/test_epoll.rs b/test/sys/test_epoll.rs new file mode 100644 index 00000000..a73fea6d --- /dev/null +++ b/test/sys/test_epoll.rs @@ -0,0 +1,24 @@ +use nix::sys::epoll::{EpollCreateFlags, EpollOp, EpollEvent}; +use nix::sys::epoll::{EPOLLIN, EPOLLERR}; +use nix::sys::epoll::{epoll_create1, epoll_ctl}; +use nix::{Error, Errno}; + +#[test] +pub fn test_epoll_errno() { + let efd = epoll_create1(EpollCreateFlags::empty()).unwrap(); + let result = epoll_ctl(efd, EpollOp::EpollCtlDel, 1, None); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), Error::Sys(Errno::ENOENT)); + + let result = epoll_ctl(efd, EpollOp::EpollCtlAdd, 1, None); + assert!(result.is_err()); + assert_eq!(result.unwrap_err(), Error::Sys(Errno::EINVAL)); +} + +#[test] +pub fn test_epoll_ctl() { + let efd = epoll_create1(EpollCreateFlags::empty()).unwrap(); + let mut event = EpollEvent::new(EPOLLIN | EPOLLERR, 1); + epoll_ctl(efd, EpollOp::EpollCtlAdd, 1, &mut event).unwrap(); + epoll_ctl(efd, EpollOp::EpollCtlDel, 1, None).unwrap(); +} diff --git a/test/test_unistd.rs b/test/test_unistd.rs index d281f9b2..76ab442a 100644 --- a/test/test_unistd.rs +++ b/test/test_unistd.rs @@ -6,6 +6,7 @@ use nix::sys::wait::*; use nix::sys::stat; use std::iter; use std::ffi::CString; +use std::fs::File; use std::io::{Write, Read}; use std::os::unix::prelude::*; use std::env::current_dir; @@ -142,6 +143,24 @@ macro_rules! execve_test_factory( ); #[test] +fn test_fchdir() { + let tmpdir = TempDir::new("test_fchdir").unwrap(); + let tmpdir_path = tmpdir.path().canonicalize().unwrap(); + let tmpdir_fd = File::open(&tmpdir_path).unwrap().into_raw_fd(); + let olddir_path = getcwd().unwrap(); + let olddir_fd = File::open(&olddir_path).unwrap().into_raw_fd(); + + assert!(fchdir(tmpdir_fd).is_ok()); + assert_eq!(getcwd().unwrap(), tmpdir_path); + + assert!(fchdir(olddir_fd).is_ok()); + assert_eq!(getcwd().unwrap(), olddir_path); + + assert!(close(olddir_fd).is_ok()); + assert!(close(tmpdir_fd).is_ok()); +} + +#[test] fn test_getcwd() { let tmp_dir = TempDir::new("test_getcwd").unwrap(); assert!(chdir(tmp_dir.path()).is_ok()); |