diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/fcntl.rs | 9 | ||||
-rw-r--r-- | src/lib.rs | 17 | ||||
-rw-r--r-- | src/sys/aio.rs | 4 | ||||
-rw-r--r-- | src/sys/ioctl/linux.rs | 2 | ||||
-rw-r--r-- | src/sys/ioctl/mod.rs | 15 | ||||
-rw-r--r-- | src/sys/mod.rs | 8 | ||||
-rw-r--r-- | src/sys/ptrace/bsd.rs | 170 | ||||
-rw-r--r-- | src/sys/ptrace/linux.rs (renamed from src/sys/ptrace.rs) | 50 | ||||
-rw-r--r-- | src/sys/ptrace/mod.rs | 22 | ||||
-rw-r--r-- | src/sys/signal.rs | 10 | ||||
-rw-r--r-- | src/sys/signalfd.rs | 2 | ||||
-rw-r--r-- | src/sys/socket/addr.rs | 3 | ||||
-rw-r--r-- | src/sys/socket/mod.rs | 14 | ||||
-rw-r--r-- | src/sys/stat.rs | 50 | ||||
-rw-r--r-- | src/sys/termios.rs | 45 | ||||
-rw-r--r-- | src/sys/time.rs | 3 | ||||
-rw-r--r-- | src/unistd.rs | 153 |
17 files changed, 496 insertions, 81 deletions
diff --git a/src/fcntl.rs b/src/fcntl.rs index 58de3b31..5942506b 100644 --- a/src/fcntl.rs +++ b/src/fcntl.rs @@ -2,6 +2,7 @@ use {Error, Result, NixPath}; use errno::Errno; use libc::{self, c_int, c_uint, c_char, size_t, ssize_t}; use sys::stat::Mode; +use std::os::raw; use std::os::unix::io::RawFd; use std::ffi::OsStr; use std::os::unix::ffi::OsStrExt; @@ -182,6 +183,14 @@ pub fn readlinkat<'a, P: ?Sized + NixPath>(dirfd: RawFd, path: &P, buffer: &'a m wrap_readlink_result(buffer, res) } +/// Computes the raw fd consumed by a function of the form `*at`. +pub(crate) fn at_rawfd(fd: Option<RawFd>) -> raw::c_int { + match fd { + None => libc::AT_FDCWD, + Some(fd) => fd, + } +} + #[cfg(any(target_os = "android", target_os = "linux"))] libc_bitflags!( /// Additional flags for file sealing, which allows for limiting operations on a file. @@ -110,6 +110,23 @@ pub enum Error { } impl Error { + /// Convert this `Error` to an [`Errno`](enum.Errno.html). + /// + /// # Example + /// + /// ``` + /// # use nix::Error; + /// # use nix::errno::Errno; + /// let e = Error::from(Errno::EPERM); + /// assert_eq!(Some(Errno::EPERM), e.as_errno()); + /// ``` + pub fn as_errno(&self) -> Option<Errno> { + if let &Error::Sys(ref e) = self { + Some(*e) + } else { + None + } + } /// Create a nix Error from a given errno pub fn from_errno(errno: Errno) -> Error { diff --git a/src/sys/aio.rs b/src/sys/aio.rs index 3d539821..c54c2e31 100644 --- a/src/sys/aio.rs +++ b/src/sys/aio.rs @@ -1138,7 +1138,7 @@ impl<'a> LioCb<'a> { let p = self.list.as_ptr(); Errno::result(unsafe { libc::lio_listio(mode as i32, p, self.list.len() as i32, sigevp) - }).map(|_| ()) + }).map(drop) } /// Resubmits any incomplete operations with [`lio_listio`]. @@ -1229,7 +1229,7 @@ impl<'a> LioCb<'a> { let p = self.list.as_ptr(); Errno::result(unsafe { libc::lio_listio(mode as i32, p, self.list.len() as i32, sigevp) - }).map(|_| ()) + }).map(drop) } /// Collect final status for an individual `AioCb` submitted as part of an diff --git a/src/sys/ioctl/linux.rs b/src/sys/ioctl/linux.rs index 17c8cff9..750a1c98 100644 --- a/src/sys/ioctl/linux.rs +++ b/src/sys/ioctl/linux.rs @@ -14,7 +14,7 @@ pub const NRBITS: ioctl_num_type = 8; #[doc(hidden)] pub const TYPEBITS: ioctl_num_type = 8; -#[cfg(any(target_arch = "mips", target_arch = "mips64", target_arch = "powerpc", target_arch = "powerpc64"))] +#[cfg(any(target_arch = "mips", target_arch = "mips64", target_arch = "powerpc", target_arch = "powerpc64", target_arch = "sparc64"))] mod consts { #[doc(hidden)] pub const NONE: u8 = 1; diff --git a/src/sys/ioctl/mod.rs b/src/sys/ioctl/mod.rs index e00e5934..35d508b1 100644 --- a/src/sys/ioctl/mod.rs +++ b/src/sys/ioctl/mod.rs @@ -221,7 +221,6 @@ //! //! # fn main() {} //! ``` -//! #[cfg(any(target_os = "android", target_os = "linux"))] #[macro_use] mod linux; @@ -502,9 +501,13 @@ cfg_if!{ /// The generated function has the following signature: /// /// ```rust,ignore - /// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: libc::c_int) -> Result<libc::c_int> + /// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: nix::sys::ioctl::ioctl_param_type) -> Result<libc::c_int> /// ``` /// + /// `nix::sys::ioctl::ioctl_param_type` depends on the OS: + /// * BSD - `libc::c_int` + /// * Linux - `libc::c_ulong` + /// /// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html). /// /// # Example @@ -537,9 +540,13 @@ cfg_if!{ /// The generated function has the following signature: /// /// ```rust,ignore - /// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: libc::c_int) -> Result<libc::c_int> + /// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: nix::sys::ioctl::ioctl_param_type) -> Result<libc::c_int> /// ``` /// + /// `nix::sys::ioctl::ioctl_param_type` depends on the OS: + /// * BSD - `libc::c_int` + /// * Linux - `libc::c_ulong` + /// /// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html). /// /// # Example @@ -655,7 +662,7 @@ macro_rules! ioctl_readwrite { /// /// The generated function has the following signature: /// -/// ```rust,ignorerust,ignore +/// ```rust,ignore /// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: *mut DATA_TYPE) -> Result<libc::c_int> /// ``` /// diff --git a/src/sys/mod.rs b/src/sys/mod.rs index e6c7880c..72d59649 100644 --- a/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -38,7 +38,13 @@ pub mod mman; pub mod pthread; -#[cfg(any(target_os = "android", target_os = "linux"))] +#[cfg(any(target_os = "android", + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] pub mod ptrace; #[cfg(target_os = "linux")] diff --git a/src/sys/ptrace/bsd.rs b/src/sys/ptrace/bsd.rs new file mode 100644 index 00000000..7797d106 --- /dev/null +++ b/src/sys/ptrace/bsd.rs @@ -0,0 +1,170 @@ +use errno::Errno; +use libc::{self, c_int}; +use std::ptr; +use sys::signal::Signal; +use unistd::Pid; +use Result; + +pub type RequestType = c_int; + +cfg_if! { + if #[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "openbsd"))] { + #[doc(hidden)] + pub type AddressType = *mut ::libc::c_char; + } else { + #[doc(hidden)] + pub type AddressType = *mut ::libc::c_void; + } +} + +libc_enum! { + #[repr(i32)] + /// Ptrace Request enum defining the action to be taken. + pub enum Request { + PT_TRACE_ME, + PT_READ_I, + PT_READ_D, + #[cfg(target_os = "macos")] + PT_READ_U, + PT_WRITE_I, + PT_WRITE_D, + #[cfg(target_os = "macos")] + PT_WRITE_U, + PT_CONTINUE, + PT_KILL, + #[cfg(any(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos"), + all(target_os = "openbsd", target_arch = "x86_64"), + all(target_os = "netbsd", any(target_arch = "x86_64", + target_arch = "powerpc"))))] + PT_STEP, + PT_ATTACH, + PT_DETACH, + #[cfg(target_os = "macos")] + PT_SIGEXC, + #[cfg(target_os = "macos")] + PT_THUPDATE, + #[cfg(target_os = "macos")] + PT_ATTACHEXC + } +} + +unsafe fn ptrace_other( + request: Request, + pid: Pid, + addr: AddressType, + data: c_int, +) -> Result<c_int> { + Errno::result(libc::ptrace( + request as RequestType, + libc::pid_t::from(pid), + addr, + data, + )).map(|_| 0) +} + +/// Sets the process as traceable, as with `ptrace(PT_TRACEME, ...)` +/// +/// Indicates that this process is to be traced by its parent. +/// This is the only ptrace request to be issued by the tracee. +pub fn traceme() -> Result<()> { + unsafe { ptrace_other(Request::PT_TRACE_ME, Pid::from_raw(0), ptr::null_mut(), 0).map(drop) } +} + +/// Attach to a running process, as with `ptrace(PT_ATTACH, ...)` +/// +/// Attaches to the process specified in pid, making it a tracee of the calling process. +pub fn attach(pid: Pid) -> Result<()> { + unsafe { ptrace_other(Request::PT_ATTACH, pid, ptr::null_mut(), 0).map(drop) } +} + +/// Detaches the current running process, as with `ptrace(PT_DETACH, ...)` +/// +/// Detaches from the process specified in pid allowing it to run freely +pub fn detach(pid: Pid) -> Result<()> { + unsafe { ptrace_other(Request::PT_DETACH, pid, ptr::null_mut(), 0).map(drop) } +} + +/// Restart the stopped tracee process, as with `ptrace(PTRACE_CONT, ...)` +/// +/// Continues the execution of the process with PID `pid`, optionally +/// delivering a signal specified by `sig`. +pub fn cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> { + let data = match sig.into() { + Some(s) => s as c_int, + None => 0, + }; + unsafe { + // Ignore the useless return value + ptrace_other(Request::PT_CONTINUE, pid, 1 as AddressType, data).map(drop) + } +} + +/// Issues a kill request as with `ptrace(PT_KILL, ...)` +/// +/// This request is equivalent to `ptrace(PT_CONTINUE, ..., SIGKILL);` +pub fn kill(pid: Pid) -> Result<()> { + unsafe { + ptrace_other(Request::PT_KILL, pid, 0 as AddressType, 0).map(drop) + } +} + +/// Move the stopped tracee process forward by a single step as with +/// `ptrace(PT_STEP, ...)` +/// +/// Advances the execution of the process with PID `pid` by a single step optionally delivering a +/// signal specified by `sig`. +/// +/// # Example +/// ```rust +/// extern crate nix; +/// use nix::sys::ptrace::step; +/// use nix::unistd::Pid; +/// use nix::sys::signal::Signal; +/// use nix::sys::wait::*; +/// fn main() { +/// // If a process changes state to the stopped state because of a SIGUSR1 +/// // signal, this will step the process forward and forward the user +/// // signal to the stopped process +/// match waitpid(Pid::from_raw(-1), None) { +/// Ok(WaitStatus::Stopped(pid, Signal::SIGUSR1)) => { +/// let _ = step(pid, Signal::SIGUSR1); +/// } +/// _ => {}, +/// } +/// } +/// ``` +#[cfg( + any( + any(target_os = "dragonfly", target_os = "freebsd", target_os = "macos"), + all(target_os = "openbsd", target_arch = "x86_64"), + all(target_os = "netbsd", + any(target_arch = "x86_64", target_arch = "powerpc") + ) + ) +)] +pub fn step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> { + let data = match sig.into() { + Some(s) => s as c_int, + None => 0, + }; + unsafe { ptrace_other(Request::PT_STEP, pid, ptr::null_mut(), data).map(drop) } +} + +/// Reads a word from a processes memory at the given address +pub fn read(pid: Pid, addr: AddressType) -> Result<c_int> { + unsafe { + // Traditionally there was a difference between reading data or + // instruction memory but not in modern systems. + ptrace_other(Request::PT_READ_D, pid, addr, 0) + } +} + +/// Writes a word into the processes memory at the given address +pub fn write(pid: Pid, addr: AddressType, data: c_int) -> Result<()> { + unsafe { ptrace_other(Request::PT_WRITE_D, pid, addr, data).map(drop) } +} diff --git a/src/sys/ptrace.rs b/src/sys/ptrace/linux.rs index 4bb7e06f..f10c6d83 100644 --- a/src/sys/ptrace.rs +++ b/src/sys/ptrace/linux.rs @@ -7,9 +7,10 @@ use libc::{self, c_void, c_long, siginfo_t}; use ::unistd::Pid; use sys::signal::Signal; +pub type AddressType = *mut ::libc::c_void; cfg_if! { - if #[cfg(any(all(target_os = "linux", arch = "s390x"), + if #[cfg(any(all(target_os = "linux", target_arch = "s390x"), all(target_os = "linux", target_env = "gnu")))] { #[doc(hidden)] pub type RequestType = ::libc::c_uint; @@ -170,16 +171,17 @@ libc_bitflags! { since="0.10.0", note="usages of `ptrace()` should be replaced with the specialized helper functions instead" )] -pub unsafe fn ptrace(request: Request, pid: Pid, addr: *mut c_void, data: *mut c_void) -> Result<c_long> { +pub unsafe fn ptrace(request: Request, pid: Pid, addr: AddressType, data: *mut c_void) -> Result<c_long> { use self::Request::*; match request { - PTRACE_PEEKTEXT | PTRACE_PEEKDATA | PTRACE_PEEKUSER => ptrace_peek(request, pid, addr, data), - PTRACE_GETSIGINFO | PTRACE_GETEVENTMSG | PTRACE_SETSIGINFO | PTRACE_SETOPTIONS => Err(Error::UnsupportedOperation), + PTRACE_PEEKTEXT | PTRACE_PEEKDATA | PTRACE_GETSIGINFO | + PTRACE_GETEVENTMSG | PTRACE_SETSIGINFO | PTRACE_SETOPTIONS | + PTRACE_POKETEXT | PTRACE_POKEDATA | PTRACE_KILL => Err(Error::UnsupportedOperation), _ => ptrace_other(request, pid, addr, data) } } -fn ptrace_peek(request: Request, pid: Pid, addr: *mut c_void, data: *mut c_void) -> Result<c_long> { +fn ptrace_peek(request: Request, pid: Pid, addr: AddressType, data: *mut c_void) -> Result<c_long> { let ret = unsafe { Errno::clear(); libc::ptrace(request as RequestType, libc::pid_t::from(pid), addr, data) @@ -207,7 +209,7 @@ fn ptrace_get_data<T>(request: Request, pid: Pid) -> Result<T> { Ok(data) } -unsafe fn ptrace_other(request: Request, pid: Pid, addr: *mut c_void, data: *mut c_void) -> Result<c_long> { +unsafe fn ptrace_other(request: Request, pid: Pid, addr: AddressType, data: *mut c_void) -> Result<c_long> { Errno::result(libc::ptrace(request as RequestType, libc::pid_t::from(pid), addr, data)).map(|_| 0) } @@ -221,7 +223,7 @@ pub fn setoptions(pid: Pid, options: Options) -> Result<()> { ptr::null_mut::<c_void>(), options.bits() as *mut c_void) }; - Errno::result(res).map(|_| ()) + Errno::result(res).map(drop) } /// Gets a ptrace event as described by `ptrace(PTRACE_GETEVENTMSG,...)` @@ -260,7 +262,7 @@ pub fn traceme() -> Result<()> { Pid::from_raw(0), ptr::null_mut(), ptr::null_mut(), - ).map(|_| ()) // ignore the useless return value + ).map(drop) // ignore the useless return value } } @@ -274,7 +276,7 @@ pub fn syscall(pid: Pid) -> Result<()> { pid, ptr::null_mut(), ptr::null_mut(), - ).map(|_| ()) // ignore the useless return value + ).map(drop) // ignore the useless return value } } @@ -288,7 +290,7 @@ pub fn attach(pid: Pid) -> Result<()> { pid, ptr::null_mut(), ptr::null_mut(), - ).map(|_| ()) // ignore the useless return value + ).map(drop) // ignore the useless return value } } @@ -302,7 +304,7 @@ pub fn detach(pid: Pid) -> Result<()> { pid, ptr::null_mut(), ptr::null_mut() - ).map(|_| ()) + ).map(drop) } } @@ -316,7 +318,16 @@ pub fn cont<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> { None => ptr::null_mut(), }; unsafe { - ptrace_other(Request::PTRACE_CONT, pid, ptr::null_mut(), data).map(|_| ()) // ignore the useless return value + ptrace_other(Request::PTRACE_CONT, pid, ptr::null_mut(), data).map(drop) // ignore the useless return value + } +} + +/// Issues a kill request as with `ptrace(PTRACE_KILL, ...)` +/// +/// This request is equivalent to `ptrace(PTRACE_CONT, ..., SIGKILL);` +pub fn kill(pid: Pid) -> Result<()> { + unsafe { + ptrace_other(Request::PTRACE_KILL, pid, ptr::null_mut(), ptr::null_mut()).map(drop) } } @@ -351,6 +362,19 @@ pub fn step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> { None => ptr::null_mut(), }; unsafe { - ptrace_other(Request::PTRACE_SINGLESTEP, pid, ptr::null_mut(), data).map(|_| ()) + ptrace_other(Request::PTRACE_SINGLESTEP, pid, ptr::null_mut(), data).map(drop) + } +} + + +/// Reads a word from a processes memory at the given address +pub fn read(pid: Pid, addr: AddressType) -> Result<c_long> { + ptrace_peek(Request::PTRACE_PEEKDATA, pid, addr, ptr::null_mut()) +} + +/// Writes a word into the processes memory at the given address +pub fn write(pid: Pid, addr: AddressType, data: *mut c_void) -> Result<()> { + unsafe { + ptrace_other(Request::PTRACE_POKEDATA, pid, addr, data).map(drop) } } diff --git a/src/sys/ptrace/mod.rs b/src/sys/ptrace/mod.rs new file mode 100644 index 00000000..782c3040 --- /dev/null +++ b/src/sys/ptrace/mod.rs @@ -0,0 +1,22 @@ +///! Provides helpers for making ptrace system calls + +#[cfg(any(target_os = "android", target_os = "linux"))] +mod linux; + +#[cfg(any(target_os = "android", target_os = "linux"))] +pub use self::linux::*; + +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd"))] +mod bsd; + +#[cfg(any(target_os = "dragonfly", + target_os = "freebsd", + target_os = "macos", + target_os = "netbsd", + target_os = "openbsd" + ))] +pub use self::bsd::*; diff --git a/src/sys/signal.rs b/src/sys/signal.rs index c2dd856c..c9826d72 100644 --- a/src/sys/signal.rs +++ b/src/sys/signal.rs @@ -39,7 +39,7 @@ libc_enum!{ SIGALRM, SIGTERM, #[cfg(all(any(target_os = "android", target_os = "emscripten", target_os = "linux"), - not(any(target_arch = "mips", target_arch = "mips64"))))] + not(any(target_arch = "mips", target_arch = "mips64", target_arch = "sparc64"))))] SIGSTKFLT, SIGCHLD, SIGCONT, @@ -84,7 +84,7 @@ impl FromStr for Signal { "SIGALRM" => Signal::SIGALRM, "SIGTERM" => Signal::SIGTERM, #[cfg(all(any(target_os = "android", target_os = "emscripten", target_os = "linux"), - not(any(target_arch = "mips", target_arch = "mips64"))))] + not(any(target_arch = "mips", target_arch = "mips64", target_arch = "sparc64"))))] "SIGSTKFLT" => Signal::SIGSTKFLT, "SIGCHLD" => Signal::SIGCHLD, "SIGCONT" => Signal::SIGCONT, @@ -130,7 +130,7 @@ impl AsRef<str> for Signal { Signal::SIGALRM => "SIGALRM", Signal::SIGTERM => "SIGTERM", #[cfg(all(any(target_os = "android", target_os = "emscripten", target_os = "linux"), - not(any(target_arch = "mips", target_arch = "mips64"))))] + not(any(target_arch = "mips", target_arch = "mips64", target_arch = "sparc64"))))] Signal::SIGSTKFLT => "SIGSTKFLT", Signal::SIGCHLD => "SIGCHLD", Signal::SIGCONT => "SIGCONT", @@ -164,7 +164,7 @@ impl fmt::Display for Signal { pub use self::Signal::*; -#[cfg(all(any(target_os = "linux", target_os = "android", target_os = "emscripten"), not(any(target_arch = "mips", target_arch = "mips64"))))] +#[cfg(all(any(target_os = "linux", target_os = "android", target_os = "emscripten"), not(any(target_arch = "mips", target_arch = "mips64", target_arch = "sparc64"))))] const SIGNALS: [Signal; 31] = [ SIGHUP, SIGINT, @@ -197,7 +197,7 @@ const SIGNALS: [Signal; 31] = [ SIGIO, SIGPWR, SIGSYS]; -#[cfg(all(any(target_os = "linux", target_os = "android", target_os = "emscripten"), any(target_arch = "mips", target_arch = "mips64")))] +#[cfg(all(any(target_os = "linux", target_os = "android", target_os = "emscripten"), any(target_arch = "mips", target_arch = "mips64", target_arch = "sparc64")))] const SIGNALS: [Signal; 30] = [ SIGHUP, SIGINT, diff --git a/src/sys/signalfd.rs b/src/sys/signalfd.rs index 52027d36..65c09ea4 100644 --- a/src/sys/signalfd.rs +++ b/src/sys/signalfd.rs @@ -94,7 +94,7 @@ impl SignalFd { } pub fn set_mask(&mut self, mask: &SigSet) -> Result<()> { - signalfd(self.0, mask, SfdFlags::empty()).map(|_| ()) + signalfd(self.0, mask, SfdFlags::empty()).map(drop) } pub fn read_signal(&mut self) -> Result<Option<siginfo>> { diff --git a/src/sys/socket/addr.rs b/src/sys/socket/addr.rs index 93014f1c..ebd32c1f 100644 --- a/src/sys/socket/addr.rs +++ b/src/sys/socket/addr.rs @@ -209,6 +209,9 @@ pub enum AddressFamily { target_os = "netbsd", target_os = "openbsd"))] Natm = libc::AF_NATM, + /// Unspecified address family, (see [`getaddrinfo(3)`](http://man7.org/linux/man-pages/man3/getaddrinfo.3.html)) + #[cfg(any(target_os = "android", target_os = "linux"))] + Unspec = libc::AF_UNSPEC, } impl AddressFamily { diff --git a/src/sys/socket/mod.rs b/src/sys/socket/mod.rs index 4ee1acee..85acaf4c 100644 --- a/src/sys/socket/mod.rs +++ b/src/sys/socket/mod.rs @@ -976,6 +976,20 @@ pub fn getsockopt<O: GetSockOpt>(fd: RawFd, opt: O) -> Result<O::Val> { /// Sets the value for the requested socket option /// /// [Further reading](http://pubs.opengroup.org/onlinepubs/9699919799/functions/setsockopt.html) +/// +/// # Examples +/// +/// ``` +/// use nix::sys::socket::setsockopt; +/// use nix::sys::socket::sockopt::KeepAlive; +/// use std::net::TcpListener; +/// use std::os::unix::io::AsRawFd; +/// +/// let listener = TcpListener::bind("0.0.0.0:0").unwrap(); +/// let fd = listener.as_raw_fd(); +/// let res = setsockopt(fd, KeepAlive, &true); +/// assert!(res.is_ok()); +/// ``` pub fn setsockopt<O: SetSockOpt>(fd: RawFd, opt: O, val: &O::Val) -> Result<()> { opt.set(fd, val) } diff --git a/src/sys/stat.rs b/src/sys/stat.rs index b810c167..f3a2e7e3 100644 --- a/src/sys/stat.rs +++ b/src/sys/stat.rs @@ -1,12 +1,11 @@ -pub use libc::dev_t; +pub use libc::{dev_t, mode_t}; pub use libc::stat as FileStat; use {Result, NixPath}; use errno::Errno; -use fcntl::AtFlags; -use libc::{self, mode_t}; +use fcntl::{AtFlags, at_rawfd}; +use libc; use std::mem; -use std::os::raw; use std::os::unix::io::RawFd; use sys::time::{TimeSpec, TimeVal}; @@ -132,16 +131,7 @@ pub fn fstatat<P: ?Sized + NixPath>(dirfd: RawFd, pathname: &P, f: AtFlags) -> R pub fn fchmod(fd: RawFd, mode: Mode) -> Result<()> { let res = unsafe { libc::fchmod(fd, mode.bits() as mode_t) }; - Errno::result(res).map(|_| ()) -} - -/// Computes the raw fd consumed by a function of the form `*at`. -#[inline] -fn actual_atfd(fd: Option<RawFd>) -> raw::c_int { - match fd { - None => libc::AT_FDCWD, - Some(fd) => fd, - } + Errno::result(res).map(drop) } /// Flags for `fchmodat` function. @@ -180,14 +170,14 @@ pub fn fchmodat<P: ?Sized + NixPath>( }; let res = path.with_nix_path(|cstr| unsafe { libc::fchmodat( - actual_atfd(dirfd), + at_rawfd(dirfd), cstr.as_ptr(), mode.bits() as mode_t, atflag.bits() as libc::c_int, ) })?; - Errno::result(res).map(|_| ()) + Errno::result(res).map(drop) } /// Change the access and modification times of a file. @@ -206,7 +196,27 @@ pub fn utimes<P: ?Sized + NixPath>(path: &P, atime: &TimeVal, mtime: &TimeVal) - libc::utimes(cstr.as_ptr(), ×[0]) })?; - Errno::result(res).map(|_| ()) + Errno::result(res).map(drop) +} + +/// Change the access and modification times of a file without following symlinks. +/// +/// `lutimes(path, times)` is identical to +/// `utimensat(None, path, times, UtimensatFlags::NoFollowSymlink)`. The former +/// is a deprecated API so prefer using the latter if the platforms you care +/// about support it. +/// +/// # References +/// +/// [lutimes(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/lutimes.html). +#[cfg(not(target_os = "android"))] +pub fn lutimes<P: ?Sized + NixPath>(path: &P, atime: &TimeVal, mtime: &TimeVal) -> Result<()> { + let times: [libc::timeval; 2] = [*atime.as_ref(), *mtime.as_ref()]; + let res = path.with_nix_path(|cstr| unsafe { + libc::lutimes(cstr.as_ptr(), ×[0]) + })?; + + Errno::result(res).map(drop) } /// Change the access and modification times of the file specified by a file descriptor. @@ -219,7 +229,7 @@ pub fn futimens(fd: RawFd, atime: &TimeSpec, mtime: &TimeSpec) -> Result<()> { let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()]; let res = unsafe { libc::futimens(fd, ×[0]) }; - Errno::result(res).map(|_| ()) + Errno::result(res).map(drop) } /// Flags for `utimensat` function. @@ -260,12 +270,12 @@ pub fn utimensat<P: ?Sized + NixPath>( let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()]; let res = path.with_nix_path(|cstr| unsafe { libc::utimensat( - actual_atfd(dirfd), + at_rawfd(dirfd), cstr.as_ptr(), ×[0], atflag.bits() as libc::c_int, ) })?; - Errno::result(res).map(|_| ()) + Errno::result(res).map(drop) } diff --git a/src/sys/termios.rs b/src/sys/termios.rs index 11cacd7c..8a99c1ab 100644 --- a/src/sys/termios.rs +++ b/src/sys/termios.rs @@ -366,13 +366,13 @@ libc_enum!{ B1500000, #[cfg(any(target_os = "android", target_os = "linux"))] B2000000, - #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))] B2500000, - #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))] B3000000, - #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))] B3500000, - #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))] B4000000, } } @@ -383,8 +383,9 @@ impl From<libc::speed_t> for BaudRate { use libc::{B0, B50, B75, B110, B134, B150, B200, B300, B600, B1200, B1800, B2400, B4800, B9600, B19200, B38400, B57600, B115200, B230400}; #[cfg(any(target_os = "android", target_os = "linux"))] - use libc::{B500000, B576000, B1000000, B1152000, B1500000, B2000000, B2500000, B3000000, - B3500000, B4000000}; + use libc::{B500000, B576000, B1000000, B1152000, B1500000, B2000000}; + #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))] + use libc::{B2500000, B3000000, B3500000, B4000000}; #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "macos", @@ -463,13 +464,13 @@ impl From<libc::speed_t> for BaudRate { B1500000 => BaudRate::B1500000, #[cfg(any(target_os = "android", target_os = "linux"))] B2000000 => BaudRate::B2000000, - #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))] B2500000 => BaudRate::B2500000, - #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))] B3000000 => BaudRate::B3000000, - #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))] B3500000 => BaudRate::B3500000, - #[cfg(any(target_os = "android", target_os = "linux"))] + #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))] B4000000 => BaudRate::B4000000, b => unreachable!("Invalid baud constant: {}", b), } @@ -558,6 +559,7 @@ libc_enum! { VINTR, VKILL, VLNEXT, + #[cfg(not(all(target_os = "linux", target_arch = "sparc64")))] VMIN, VQUIT, VREPRINT, @@ -574,6 +576,7 @@ libc_enum! { VSWTC, #[cfg(target_os = "haiku")] VSWTCH, + #[cfg(not(all(target_os = "linux", target_arch = "sparc64")))] VTIME, VWERASE, #[cfg(target_os = "dragonfly")] @@ -929,7 +932,7 @@ cfg_if!{ let inner_termios = unsafe { termios.get_libc_termios_mut() }; let res = unsafe { libc::cfsetispeed(inner_termios, baud.into() as libc::speed_t) }; termios.update_wrapper(); - Errno::result(res).map(|_| ()) + Errno::result(res).map(drop) } /// Set output baud rate (see @@ -940,7 +943,7 @@ cfg_if!{ let inner_termios = unsafe { termios.get_libc_termios_mut() }; let res = unsafe { libc::cfsetospeed(inner_termios, baud.into() as libc::speed_t) }; termios.update_wrapper(); - Errno::result(res).map(|_| ()) + Errno::result(res).map(drop) } /// Set both the input and output baud rates (see @@ -952,7 +955,7 @@ cfg_if!{ let inner_termios = unsafe { termios.get_libc_termios_mut() }; let res = unsafe { libc::cfsetspeed(inner_termios, baud.into() as libc::speed_t) }; termios.update_wrapper(); - Errno::result(res).map(|_| ()) + Errno::result(res).map(drop) } } else { /// Get input baud rate (see @@ -981,7 +984,7 @@ cfg_if!{ let inner_termios = unsafe { termios.get_libc_termios_mut() }; let res = unsafe { libc::cfsetispeed(inner_termios, baud as libc::speed_t) }; termios.update_wrapper(); - Errno::result(res).map(|_| ()) + Errno::result(res).map(drop) } /// Set output baud rate (see @@ -992,7 +995,7 @@ cfg_if!{ let inner_termios = unsafe { termios.get_libc_termios_mut() }; let res = unsafe { libc::cfsetospeed(inner_termios, baud as libc::speed_t) }; termios.update_wrapper(); - Errno::result(res).map(|_| ()) + Errno::result(res).map(drop) } /// Set both the input and output baud rates (see @@ -1004,7 +1007,7 @@ cfg_if!{ let inner_termios = unsafe { termios.get_libc_termios_mut() }; let res = unsafe { libc::cfsetspeed(inner_termios, baud as libc::speed_t) }; termios.update_wrapper(); - Errno::result(res).map(|_| ()) + Errno::result(res).map(drop) } } } @@ -1060,13 +1063,13 @@ pub fn tcgetattr(fd: RawFd) -> Result<Termios> { /// *any* of the parameters were successfully set, not only if all were set successfully. pub fn tcsetattr(fd: RawFd, actions: SetArg, termios: &Termios) -> Result<()> { let inner_termios = termios.get_libc_termios(); - Errno::result(unsafe { libc::tcsetattr(fd, actions as c_int, &*inner_termios) }).map(|_| ()) + Errno::result(unsafe { libc::tcsetattr(fd, actions as c_int, &*inner_termios) }).map(drop) } /// Block until all output data is written (see /// [tcdrain(3p)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/tcdrain.html)). pub fn tcdrain(fd: RawFd) -> Result<()> { - Errno::result(unsafe { libc::tcdrain(fd) }).map(|_| ()) + Errno::result(unsafe { libc::tcdrain(fd) }).map(drop) } /// Suspend or resume the transmission or reception of data (see @@ -1075,7 +1078,7 @@ pub fn tcdrain(fd: RawFd) -> Result<()> { /// `tcflow()` suspends of resumes the transmission or reception of data for the given port /// depending on the value of `action`. pub fn tcflow(fd: RawFd, action: FlowArg) -> Result<()> { - Errno::result(unsafe { libc::tcflow(fd, action as c_int) }).map(|_| ()) + Errno::result(unsafe { libc::tcflow(fd, action as c_int) }).map(drop) } /// Discard data in the output or input queue (see @@ -1084,7 +1087,7 @@ pub fn tcflow(fd: RawFd, action: FlowArg) -> Result<()> { /// `tcflush()` will discard data for a terminal port in the input queue, output queue, or both /// depending on the value of `action`. pub fn tcflush(fd: RawFd, action: FlushArg) -> Result<()> { - Errno::result(unsafe { libc::tcflush(fd, action as c_int) }).map(|_| ()) + Errno::result(unsafe { libc::tcflush(fd, action as c_int) }).map(drop) } /// Send a break for a specific duration (see @@ -1093,7 +1096,7 @@ pub fn tcflush(fd: RawFd, action: FlushArg) -> Result<()> { /// When using asynchronous data transmission `tcsendbreak()` will transmit a continuous stream /// of zero-valued bits for an implementation-defined duration. pub fn tcsendbreak(fd: RawFd, duration: c_int) -> Result<()> { - Errno::result(unsafe { libc::tcsendbreak(fd, duration) }).map(|_| ()) + Errno::result(unsafe { libc::tcsendbreak(fd, duration) }).map(drop) } /// Get the session controlled by the given terminal (see diff --git a/src/sys/time.rs b/src/sys/time.rs index 51286a06..e300cfe7 100644 --- a/src/sys/time.rs +++ b/src/sys/time.rs @@ -1,5 +1,6 @@ use std::{cmp, fmt, ops}; -use libc::{c_long, time_t, suseconds_t, timespec, timeval}; +use libc::{c_long, timespec, timeval}; +pub use libc::{time_t, suseconds_t}; pub trait TimeValLike: Sized { #[inline] diff --git a/src/unistd.rs b/src/unistd.rs index 1f357c41..07f1e6fd 100644 --- a/src/unistd.rs +++ b/src/unistd.rs @@ -2,7 +2,7 @@ use errno::{self, Errno}; use {Error, Result, NixPath}; -use fcntl::{fcntl, FdFlag, OFlag}; +use fcntl::{AtFlags, at_rawfd, fcntl, FdFlag, OFlag}; use fcntl::FcntlArg::F_SETFD; use libc::{self, c_char, c_void, c_int, c_long, c_uint, size_t, pid_t, off_t, uid_t, gid_t, mode_t}; @@ -557,6 +557,16 @@ pub fn getcwd() -> Result<PathBuf> { } } +/// Computes the raw UID and GID values to pass to a `*chown` call. +fn chown_raw_ids(owner: Option<Uid>, group: Option<Gid>) -> (libc::uid_t, libc::gid_t) { + // According to the POSIX specification, -1 is used to indicate that owner and group + // are not to be changed. Since uid_t and gid_t are unsigned types, we have to wrap + // around to get -1. + let uid = owner.map(Into::into).unwrap_or((0 as uid_t).wrapping_sub(1)); + let gid = group.map(Into::into).unwrap_or((0 as gid_t).wrapping_sub(1)); + (uid, gid) +} + /// Change the ownership of the file at `path` to be owned by the specified /// `owner` (user) and `group` (see /// [chown(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/chown.html)). @@ -567,17 +577,62 @@ pub fn getcwd() -> Result<PathBuf> { #[inline] pub fn chown<P: ?Sized + NixPath>(path: &P, owner: Option<Uid>, group: Option<Gid>) -> Result<()> { let res = try!(path.with_nix_path(|cstr| { - // According to the POSIX specification, -1 is used to indicate that - // owner and group, respectively, are not to be changed. Since uid_t and - // gid_t are unsigned types, we use wrapping_sub to get '-1'. - unsafe { libc::chown(cstr.as_ptr(), - owner.map(Into::into).unwrap_or((0 as uid_t).wrapping_sub(1)), - group.map(Into::into).unwrap_or((0 as gid_t).wrapping_sub(1))) } + let (uid, gid) = chown_raw_ids(owner, group); + unsafe { libc::chown(cstr.as_ptr(), uid, gid) } })); Errno::result(res).map(drop) } +/// Flags for `fchownat` function. +#[derive(Clone, Copy, Debug)] +pub enum FchownatFlags { + FollowSymlink, + NoFollowSymlink, +} + +/// Change the ownership of the file at `path` to be owned by the specified +/// `owner` (user) and `group`. +/// +/// The owner/group for the provided path name will not be modified if `None` is +/// provided for that argument. Ownership change will be attempted for the path +/// only if `Some` owner/group is provided. +/// +/// The file to be changed is determined relative to the directory associated +/// with the file descriptor `dirfd` or the current working directory +/// if `dirfd` is `None`. +/// +/// If `flag` is `FchownatFlags::NoFollowSymlink` and `path` names a symbolic link, +/// then the mode of the symbolic link is changed. +/// +/// `fchownat(None, path, mode, FchownatFlags::NoFollowSymlink)` is identical to +/// a call `libc::lchown(path, mode)`. That's why `lchmod` is unimplemented in +/// the `nix` crate. +/// +/// # References +/// +/// [fchownat(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fchownat.html). +pub fn fchownat<P: ?Sized + NixPath>( + dirfd: Option<RawFd>, + path: &P, + owner: Option<Uid>, + group: Option<Gid>, + flag: FchownatFlags, +) -> Result<()> { + let atflag = + match flag { + FchownatFlags::FollowSymlink => AtFlags::empty(), + FchownatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW, + }; + let res = path.with_nix_path(|cstr| unsafe { + let (uid, gid) = chown_raw_ids(owner, group); + libc::fchownat(at_rawfd(dirfd), cstr.as_ptr(), uid, gid, + atflag.bits() as libc::c_int) + })?; + + Errno::result(res).map(drop) +} + fn to_exec_array(args: &[CString]) -> Vec<*const c_char> { let mut args_p: Vec<*const c_char> = args.iter().map(|s| s.as_ptr()).collect(); args_p.push(ptr::null()); @@ -646,6 +701,27 @@ pub fn execvp(filename: &CString, args: &[CString]) -> Result<Void> { Err(Error::Sys(Errno::last())) } +/// Replace the current process image with a new one and replicate shell `PATH` +/// searching behavior (see +/// [`execvpe(3)`](http://man7.org/linux/man-pages/man3/exec.3.html)). +/// +/// This functions like a combination of `execvp(2)` and `execve(2)` to pass an +/// environment and have a search path. See these two for additional +/// information. +#[cfg(any(target_os = "haiku", + target_os = "linux", + target_os = "openbsd"))] +pub fn execvpe(filename: &CString, args: &[CString], env: &[CString]) -> Result<Void> { + let args_p = to_exec_array(args); + let env_p = to_exec_array(env); + + unsafe { + libc::execvpe(filename.as_ptr(), args_p.as_ptr(), env_p.as_ptr()) + }; + + Err(Error::Sys(Errno::last())) +} + /// Replace the current process image with a new one (see /// [fexecve(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fexecve.html)). /// @@ -989,6 +1065,20 @@ fn pipe2_setflags(fd1: RawFd, fd2: RawFd, flags: OFlag) -> Result<()> { /// Truncate a file to a specified length /// /// See also +/// [truncate(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/truncate.html) +pub fn truncate<P: ?Sized + NixPath>(path: &P, len: off_t) -> Result<()> { + let res = try!(path.with_nix_path(|cstr| { + unsafe { + libc::truncate(cstr.as_ptr(), len) + } + })); + + Errno::result(res).map(drop) +} + +/// Truncate a file to a specified length +/// +/// See also /// [ftruncate(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/ftruncate.html) pub fn ftruncate(fd: RawFd, len: off_t) -> Result<()> { Errno::result(unsafe { libc::ftruncate(fd, len) }).map(drop) @@ -1032,6 +1122,20 @@ pub fn chroot<P: ?Sized + NixPath>(path: &P) -> Result<()> { Errno::result(res).map(drop) } +/// Commit filesystem caches to disk +/// +/// See also [sync(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/sync.html) +#[cfg(any( + target_os = "dragonfly", + target_os = "freebsd", + target_os = "linux", + target_os = "netbsd", + target_os = "openbsd" +))] +pub fn sync() -> () { + unsafe { libc::sync() }; +} + /// Synchronize changes to a file /// /// See also [fsync(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/fsync.html) @@ -1214,7 +1318,7 @@ pub fn setgroups(groups: &[Gid]) -> Result<()> { libc::setgroups(groups.len() as setgroups_ngroups_t, groups.as_ptr() as *const gid_t) }; - Errno::result(res).map(|_| ()) + Errno::result(res).map(drop) } /// Calculate the supplementary group access list. @@ -1340,7 +1444,7 @@ pub fn initgroups(user: &CStr, group: Gid) -> Result<()> { let gid: gid_t = group.into(); let res = unsafe { libc::initgroups(user.as_ptr(), gid as initgroups_group_t) }; - Errno::result(res).map(|_| ()) + Errno::result(res).map(drop) } /// Suspend the thread until a signal is received. @@ -1439,6 +1543,31 @@ pub fn sleep(seconds: c_uint) -> c_uint { unsafe { libc::sleep(seconds) } } +pub mod acct { + use libc; + use {Result, NixPath}; + use errno::Errno; + use std::ptr; + + /// Enable process accounting + /// + /// See also [acct(2)](https://linux.die.net/man/2/acct) + pub fn enable<P: ?Sized + NixPath>(filename: &P) -> Result<()> { + let res = try!(filename.with_nix_path(|cstr| { + unsafe { libc::acct(cstr.as_ptr()) } + })); + + Errno::result(res).map(drop) + } + + /// Disable process accounting + pub fn disable() -> Result<()> { + let res = unsafe { libc::acct(ptr::null()) }; + + Errno::result(res).map(drop) + } +} + /// Creates a regular file which persists even after process termination /// /// * `template`: a path whose 6 rightmost characters must be X, e.g. `/tmp/tmpfile_XXXXXX` @@ -2138,9 +2267,9 @@ mod setres { /// Sets the real, effective, and saved gid. /// ([see setresuid(2)](http://man7.org/linux/man-pages/man2/setresuid.2.html)) /// - /// * `rgid`: real user id - /// * `egid`: effective user id - /// * `sgid`: saved user id + /// * `rgid`: real group id + /// * `egid`: effective group id + /// * `sgid`: saved group id /// * returns: Ok or libc error code. /// /// Err is returned if the user doesn't have permission to set this GID. |