summaryrefslogtreecommitdiffstats
path: root/third_party/rust/nix/src
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/nix/src')
-rw-r--r--third_party/rust/nix/src/dir.rs276
-rw-r--r--third_party/rust/nix/src/env.rs64
-rw-r--r--third_party/rust/nix/src/errno.rs3133
-rw-r--r--third_party/rust/nix/src/fcntl.rs882
-rw-r--r--third_party/rust/nix/src/features.rs126
-rw-r--r--third_party/rust/nix/src/ifaddrs.rs213
-rw-r--r--third_party/rust/nix/src/kmod.rs128
-rw-r--r--third_party/rust/nix/src/lib.rs333
-rw-r--r--third_party/rust/nix/src/macros.rs328
-rw-r--r--third_party/rust/nix/src/mount/bsd.rs453
-rw-r--r--third_party/rust/nix/src/mount/linux.rs115
-rw-r--r--third_party/rust/nix/src/mount/mod.rs26
-rw-r--r--third_party/rust/nix/src/mqueue.rs276
-rw-r--r--third_party/rust/nix/src/net/if_.rs469
-rw-r--r--third_party/rust/nix/src/net/mod.rs4
-rw-r--r--third_party/rust/nix/src/poll.rs197
-rw-r--r--third_party/rust/nix/src/pty.rs371
-rw-r--r--third_party/rust/nix/src/sched.rs324
-rw-r--r--third_party/rust/nix/src/sys/aio.rs1241
-rw-r--r--third_party/rust/nix/src/sys/epoll.rs128
-rw-r--r--third_party/rust/nix/src/sys/event.rs374
-rw-r--r--third_party/rust/nix/src/sys/eventfd.rs17
-rw-r--r--third_party/rust/nix/src/sys/inotify.rs248
-rw-r--r--third_party/rust/nix/src/sys/ioctl/bsd.rs129
-rw-r--r--third_party/rust/nix/src/sys/ioctl/linux.rs172
-rw-r--r--third_party/rust/nix/src/sys/ioctl/mod.rs786
-rw-r--r--third_party/rust/nix/src/sys/memfd.rs64
-rw-r--r--third_party/rust/nix/src/sys/mman.rs599
-rw-r--r--third_party/rust/nix/src/sys/mod.rs228
-rw-r--r--third_party/rust/nix/src/sys/personality.rs93
-rw-r--r--third_party/rust/nix/src/sys/pthread.rs43
-rw-r--r--third_party/rust/nix/src/sys/ptrace/bsd.rs195
-rw-r--r--third_party/rust/nix/src/sys/ptrace/linux.rs558
-rw-r--r--third_party/rust/nix/src/sys/ptrace/mod.rs25
-rw-r--r--third_party/rust/nix/src/sys/quota.rs338
-rw-r--r--third_party/rust/nix/src/sys/reboot.rs48
-rw-r--r--third_party/rust/nix/src/sys/resource.rs443
-rw-r--r--third_party/rust/nix/src/sys/select.rs455
-rw-r--r--third_party/rust/nix/src/sys/sendfile.rs277
-rw-r--r--third_party/rust/nix/src/sys/signal.rs1348
-rw-r--r--third_party/rust/nix/src/sys/signalfd.rs175
-rw-r--r--third_party/rust/nix/src/sys/socket/addr.rs3247
-rw-r--r--third_party/rust/nix/src/sys/socket/mod.rs2487
-rw-r--r--third_party/rust/nix/src/sys/socket/sockopt.rs1422
-rw-r--r--third_party/rust/nix/src/sys/stat.rs480
-rw-r--r--third_party/rust/nix/src/sys/statfs.rs853
-rw-r--r--third_party/rust/nix/src/sys/statvfs.rs173
-rw-r--r--third_party/rust/nix/src/sys/sysinfo.rs83
-rw-r--r--third_party/rust/nix/src/sys/termios.rs1227
-rw-r--r--third_party/rust/nix/src/sys/time.rs811
-rw-r--r--third_party/rust/nix/src/sys/timer.rs187
-rw-r--r--third_party/rust/nix/src/sys/timerfd.rs214
-rw-r--r--third_party/rust/nix/src/sys/uio.rs291
-rw-r--r--third_party/rust/nix/src/sys/utsname.rs85
-rw-r--r--third_party/rust/nix/src/sys/wait.rs388
-rw-r--r--third_party/rust/nix/src/time.rs283
-rw-r--r--third_party/rust/nix/src/ucontext.rs47
-rw-r--r--third_party/rust/nix/src/unistd.rs3383
58 files changed, 31363 insertions, 0 deletions
diff --git a/third_party/rust/nix/src/dir.rs b/third_party/rust/nix/src/dir.rs
new file mode 100644
index 0000000000..5ce503644e
--- /dev/null
+++ b/third_party/rust/nix/src/dir.rs
@@ -0,0 +1,276 @@
+//! List directory contents
+
+use crate::errno::Errno;
+use crate::fcntl::{self, OFlag};
+use crate::sys;
+use crate::{Error, NixPath, Result};
+use cfg_if::cfg_if;
+use std::ffi;
+use std::os::unix::io::{AsRawFd, IntoRawFd, RawFd};
+use std::ptr;
+
+#[cfg(target_os = "linux")]
+use libc::{dirent64 as dirent, readdir64_r as readdir_r};
+
+#[cfg(not(target_os = "linux"))]
+use libc::{dirent, readdir_r};
+
+/// An open directory.
+///
+/// This is a lower-level interface than `std::fs::ReadDir`. Notable differences:
+/// * can be opened from a file descriptor (as returned by `openat`, perhaps before knowing
+/// if the path represents a file or directory).
+/// * implements `AsRawFd`, so it can be passed to `fstat`, `openat`, etc.
+/// The file descriptor continues to be owned by the `Dir`, so callers must not keep a `RawFd`
+/// after the `Dir` is dropped.
+/// * can be iterated through multiple times without closing and reopening the file
+/// descriptor. Each iteration rewinds when finished.
+/// * returns entries for `.` (current directory) and `..` (parent directory).
+/// * returns entries' names as a `CStr` (no allocation or conversion beyond whatever libc
+/// does).
+#[derive(Debug, Eq, Hash, PartialEq)]
+pub struct Dir(ptr::NonNull<libc::DIR>);
+
+impl Dir {
+ /// Opens the given path as with `fcntl::open`.
+ pub fn open<P: ?Sized + NixPath>(
+ path: &P,
+ oflag: OFlag,
+ mode: sys::stat::Mode,
+ ) -> Result<Self> {
+ let fd = fcntl::open(path, oflag, mode)?;
+ Dir::from_fd(fd)
+ }
+
+ /// Opens the given path as with `fcntl::openat`.
+ pub fn openat<P: ?Sized + NixPath>(
+ dirfd: RawFd,
+ path: &P,
+ oflag: OFlag,
+ mode: sys::stat::Mode,
+ ) -> Result<Self> {
+ let fd = fcntl::openat(dirfd, path, oflag, mode)?;
+ Dir::from_fd(fd)
+ }
+
+ /// Converts from a descriptor-based object, closing the descriptor on success or failure.
+ #[inline]
+ pub fn from<F: IntoRawFd>(fd: F) -> Result<Self> {
+ Dir::from_fd(fd.into_raw_fd())
+ }
+
+ /// Converts from a file descriptor, closing it on success or failure.
+ #[doc(alias("fdopendir"))]
+ pub fn from_fd(fd: RawFd) -> Result<Self> {
+ let d = ptr::NonNull::new(unsafe { libc::fdopendir(fd) }).ok_or_else(
+ || {
+ let e = Error::last();
+ unsafe { libc::close(fd) };
+ e
+ },
+ )?;
+ Ok(Dir(d))
+ }
+
+ /// Returns an iterator of `Result<Entry>` which rewinds when finished.
+ pub fn iter(&mut self) -> Iter {
+ Iter(self)
+ }
+}
+
+// `Dir` is not `Sync`. With the current implementation, it could be, but according to
+// https://www.gnu.org/software/libc/manual/html_node/Reading_002fClosing-Directory.html,
+// future versions of POSIX are likely to obsolete `readdir_r` and specify that it's unsafe to
+// call `readdir` simultaneously from multiple threads.
+//
+// `Dir` is safe to pass from one thread to another, as it's not reference-counted.
+unsafe impl Send for Dir {}
+
+impl AsRawFd for Dir {
+ fn as_raw_fd(&self) -> RawFd {
+ unsafe { libc::dirfd(self.0.as_ptr()) }
+ }
+}
+
+impl Drop for Dir {
+ fn drop(&mut self) {
+ let e = Errno::result(unsafe { libc::closedir(self.0.as_ptr()) });
+ if !std::thread::panicking() && e == Err(Errno::EBADF) {
+ panic!("Closing an invalid file descriptor!");
+ };
+ }
+}
+
+fn next(dir: &mut Dir) -> Option<Result<Entry>> {
+ unsafe {
+ // Note: POSIX specifies that portable applications should dynamically allocate a
+ // buffer with room for a `d_name` field of size `pathconf(..., _PC_NAME_MAX)` plus 1
+ // for the NUL byte. It doesn't look like the std library does this; it just uses
+ // fixed-sized buffers (and libc's dirent seems to be sized so this is appropriate).
+ // Probably fine here too then.
+ let mut ent = std::mem::MaybeUninit::<dirent>::uninit();
+ let mut result = ptr::null_mut();
+ if let Err(e) = Errno::result(readdir_r(
+ dir.0.as_ptr(),
+ ent.as_mut_ptr(),
+ &mut result,
+ )) {
+ return Some(Err(e));
+ }
+ if result.is_null() {
+ return None;
+ }
+ assert_eq!(result, ent.as_mut_ptr());
+ Some(Ok(Entry(ent.assume_init())))
+ }
+}
+
+/// Return type of [`Dir::iter`].
+#[derive(Debug, Eq, Hash, PartialEq)]
+pub struct Iter<'d>(&'d mut Dir);
+
+impl<'d> Iterator for Iter<'d> {
+ type Item = Result<Entry>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ next(self.0)
+ }
+}
+
+impl<'d> Drop for Iter<'d> {
+ fn drop(&mut self) {
+ unsafe { libc::rewinddir((self.0).0.as_ptr()) }
+ }
+}
+
+/// The return type of [Dir::into_iter]
+#[derive(Debug, Eq, Hash, PartialEq)]
+pub struct OwningIter(Dir);
+
+impl Iterator for OwningIter {
+ type Item = Result<Entry>;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ next(&mut self.0)
+ }
+}
+
+/// The file descriptor continues to be owned by the `OwningIter`,
+/// so callers must not keep a `RawFd` after the `OwningIter` is dropped.
+impl AsRawFd for OwningIter {
+ fn as_raw_fd(&self) -> RawFd {
+ self.0.as_raw_fd()
+ }
+}
+
+impl IntoIterator for Dir {
+ type Item = Result<Entry>;
+ type IntoIter = OwningIter;
+
+ /// Creates a owning iterator, that is, one that takes ownership of the
+ /// `Dir`. The `Dir` cannot be used after calling this. This can be useful
+ /// when you have a function that both creates a `Dir` instance and returns
+ /// an `Iterator`.
+ ///
+ /// Example:
+ ///
+ /// ```
+ /// use nix::{dir::Dir, fcntl::OFlag, sys::stat::Mode};
+ /// use std::{iter::Iterator, string::String};
+ ///
+ /// fn ls_upper(dirname: &str) -> impl Iterator<Item=String> {
+ /// let d = Dir::open(dirname, OFlag::O_DIRECTORY, Mode::S_IXUSR).unwrap();
+ /// d.into_iter().map(|x| x.unwrap().file_name().as_ref().to_string_lossy().to_ascii_uppercase())
+ /// }
+ /// ```
+ fn into_iter(self) -> Self::IntoIter {
+ OwningIter(self)
+ }
+}
+
+/// A directory entry, similar to `std::fs::DirEntry`.
+///
+/// Note that unlike the std version, this may represent the `.` or `..` entries.
+#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
+#[repr(transparent)]
+pub struct Entry(dirent);
+
+/// Type of file referenced by a directory entry
+#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
+pub enum Type {
+ /// FIFO (Named pipe)
+ Fifo,
+ /// Character device
+ CharacterDevice,
+ /// Directory
+ Directory,
+ /// Block device
+ BlockDevice,
+ /// Regular file
+ File,
+ /// Symbolic link
+ Symlink,
+ /// Unix-domain socket
+ Socket,
+}
+
+impl Entry {
+ /// Returns the inode number (`d_ino`) of the underlying `dirent`.
+ #[allow(clippy::useless_conversion)] // Not useless on all OSes
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ pub fn ino(&self) -> u64 {
+ cfg_if! {
+ if #[cfg(any(target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "ios",
+ target_os = "l4re",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "solaris"))] {
+ self.0.d_ino as u64
+ } else {
+ u64::from(self.0.d_fileno)
+ }
+ }
+ }
+
+ /// Returns the bare file name of this directory entry without any other leading path component.
+ pub fn file_name(&self) -> &ffi::CStr {
+ unsafe { ::std::ffi::CStr::from_ptr(self.0.d_name.as_ptr()) }
+ }
+
+ /// Returns the type of this directory entry, if known.
+ ///
+ /// See platform `readdir(3)` or `dirent(5)` manpage for when the file type is known;
+ /// notably, some Linux filesystems don't implement this. The caller should use `stat` or
+ /// `fstat` if this returns `None`.
+ pub fn file_type(&self) -> Option<Type> {
+ #[cfg(not(any(
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "haiku"
+ )))]
+ match self.0.d_type {
+ libc::DT_FIFO => Some(Type::Fifo),
+ libc::DT_CHR => Some(Type::CharacterDevice),
+ libc::DT_DIR => Some(Type::Directory),
+ libc::DT_BLK => Some(Type::BlockDevice),
+ libc::DT_REG => Some(Type::File),
+ libc::DT_LNK => Some(Type::Symlink),
+ libc::DT_SOCK => Some(Type::Socket),
+ /* libc::DT_UNKNOWN | */ _ => None,
+ }
+
+ // illumos, Solaris, and Haiku systems do not have the d_type member at all:
+ #[cfg(any(
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "haiku"
+ ))]
+ None
+ }
+}
diff --git a/third_party/rust/nix/src/env.rs b/third_party/rust/nix/src/env.rs
new file mode 100644
index 0000000000..95177a1d2a
--- /dev/null
+++ b/third_party/rust/nix/src/env.rs
@@ -0,0 +1,64 @@
+//! Environment variables
+use cfg_if::cfg_if;
+use std::fmt;
+
+/// Indicates that [`clearenv`] failed for some unknown reason
+#[derive(Clone, Copy, Debug)]
+pub struct ClearEnvError;
+
+impl fmt::Display for ClearEnvError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(f, "clearenv failed")
+ }
+}
+
+impl std::error::Error for ClearEnvError {}
+
+/// Clear the environment of all name-value pairs.
+///
+/// On platforms where libc provides `clearenv()`, it will be used. libc's
+/// `clearenv()` is documented to return an error code but not set errno; if the
+/// return value indicates a failure, this function will return
+/// [`ClearEnvError`].
+///
+/// On platforms where libc does not provide `clearenv()`, a fallback
+/// implementation will be used that iterates over all environment variables and
+/// removes them one-by-one.
+///
+/// # Safety
+///
+/// This function is not threadsafe and can cause undefined behavior in
+/// combination with `std::env` or other program components that access the
+/// environment. See, for example, the discussion on `std::env::remove_var`; this
+/// function is a case of an "inherently unsafe non-threadsafe API" dealing with
+/// the environment.
+///
+/// The caller must ensure no other threads access the process environment while
+/// this function executes and that no raw pointers to an element of libc's
+/// `environ` is currently held. The latter is not an issue if the only other
+/// environment access in the program is via `std::env`, but the requirement on
+/// thread safety must still be upheld.
+pub unsafe fn clearenv() -> std::result::Result<(), ClearEnvError> {
+ cfg_if! {
+ if #[cfg(any(target_os = "fuchsia",
+ target_os = "wasi",
+ target_env = "uclibc",
+ target_os = "linux",
+ target_os = "android",
+ target_os = "emscripten"))] {
+ let ret = libc::clearenv();
+ } else {
+ use std::env;
+ for (name, _) in env::vars_os() {
+ env::remove_var(name);
+ }
+ let ret = 0;
+ }
+ }
+
+ if ret == 0 {
+ Ok(())
+ } else {
+ Err(ClearEnvError)
+ }
+}
diff --git a/third_party/rust/nix/src/errno.rs b/third_party/rust/nix/src/errno.rs
new file mode 100644
index 0000000000..d8ad28de85
--- /dev/null
+++ b/third_party/rust/nix/src/errno.rs
@@ -0,0 +1,3133 @@
+use crate::Result;
+use cfg_if::cfg_if;
+use libc::{c_int, c_void};
+use std::convert::TryFrom;
+use std::{error, fmt, io};
+
+pub use self::consts::*;
+
+cfg_if! {
+ if #[cfg(any(target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos"))] {
+ unsafe fn errno_location() -> *mut c_int {
+ libc::__error()
+ }
+ } else if #[cfg(any(target_os = "android",
+ target_os = "netbsd",
+ target_os = "openbsd"))] {
+ unsafe fn errno_location() -> *mut c_int {
+ libc::__errno()
+ }
+ } else if #[cfg(any(target_os = "linux",
+ target_os = "redox",
+ target_os = "dragonfly",
+ target_os = "fuchsia"))] {
+ unsafe fn errno_location() -> *mut c_int {
+ libc::__errno_location()
+ }
+ } else if #[cfg(any(target_os = "illumos", target_os = "solaris"))] {
+ unsafe fn errno_location() -> *mut c_int {
+ libc::___errno()
+ }
+ } else if #[cfg(any(target_os = "haiku",))] {
+ unsafe fn errno_location() -> *mut c_int {
+ libc::_errnop()
+ }
+ }
+}
+
+/// Sets the platform-specific errno to no-error
+fn clear() {
+ // Safe because errno is a thread-local variable
+ unsafe {
+ *errno_location() = 0;
+ }
+}
+
+/// Returns the platform-specific value of errno
+pub fn errno() -> i32 {
+ unsafe { *errno_location() }
+}
+
+impl Errno {
+ pub fn last() -> Self {
+ last()
+ }
+
+ pub fn desc(self) -> &'static str {
+ desc(self)
+ }
+
+ pub const fn from_i32(err: i32) -> Errno {
+ from_i32(err)
+ }
+
+ pub fn clear() {
+ clear()
+ }
+
+ /// Returns `Ok(value)` if it does not contain the sentinel value. This
+ /// should not be used when `-1` is not the errno sentinel value.
+ #[inline]
+ pub fn result<S: ErrnoSentinel + PartialEq<S>>(value: S) -> Result<S> {
+ if value == S::sentinel() {
+ Err(Self::last())
+ } else {
+ Ok(value)
+ }
+ }
+}
+
+/// The sentinel value indicates that a function failed and more detailed
+/// information about the error can be found in `errno`
+pub trait ErrnoSentinel: Sized {
+ fn sentinel() -> Self;
+}
+
+impl ErrnoSentinel for isize {
+ fn sentinel() -> Self {
+ -1
+ }
+}
+
+impl ErrnoSentinel for i32 {
+ fn sentinel() -> Self {
+ -1
+ }
+}
+
+impl ErrnoSentinel for i64 {
+ fn sentinel() -> Self {
+ -1
+ }
+}
+
+impl ErrnoSentinel for *mut c_void {
+ fn sentinel() -> Self {
+ -1isize as *mut c_void
+ }
+}
+
+impl ErrnoSentinel for libc::sighandler_t {
+ fn sentinel() -> Self {
+ libc::SIG_ERR
+ }
+}
+
+impl error::Error for Errno {}
+
+impl fmt::Display for Errno {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{:?}: {}", self, self.desc())
+ }
+}
+
+impl From<Errno> for io::Error {
+ fn from(err: Errno) -> Self {
+ io::Error::from_raw_os_error(err as i32)
+ }
+}
+
+impl TryFrom<io::Error> for Errno {
+ type Error = io::Error;
+
+ fn try_from(ioerror: io::Error) -> std::result::Result<Self, io::Error> {
+ ioerror.raw_os_error().map(Errno::from_i32).ok_or(ioerror)
+ }
+}
+
+fn last() -> Errno {
+ Errno::from_i32(errno())
+}
+
+fn desc(errno: Errno) -> &'static str {
+ use self::Errno::*;
+ match errno {
+ UnknownErrno => "Unknown errno",
+ EPERM => "Operation not permitted",
+ ENOENT => "No such file or directory",
+ ESRCH => "No such process",
+ EINTR => "Interrupted system call",
+ EIO => "I/O error",
+ ENXIO => "No such device or address",
+ E2BIG => "Argument list too long",
+ ENOEXEC => "Exec format error",
+ EBADF => "Bad file number",
+ ECHILD => "No child processes",
+ EAGAIN => "Try again",
+ ENOMEM => "Out of memory",
+ EACCES => "Permission denied",
+ EFAULT => "Bad address",
+ #[cfg(not(target_os = "haiku"))]
+ ENOTBLK => "Block device required",
+ EBUSY => "Device or resource busy",
+ EEXIST => "File exists",
+ EXDEV => "Cross-device link",
+ ENODEV => "No such device",
+ ENOTDIR => "Not a directory",
+ EISDIR => "Is a directory",
+ EINVAL => "Invalid argument",
+ ENFILE => "File table overflow",
+ EMFILE => "Too many open files",
+ ENOTTY => "Not a typewriter",
+ ETXTBSY => "Text file busy",
+ EFBIG => "File too large",
+ ENOSPC => "No space left on device",
+ ESPIPE => "Illegal seek",
+ EROFS => "Read-only file system",
+ EMLINK => "Too many links",
+ EPIPE => "Broken pipe",
+ EDOM => "Math argument out of domain of func",
+ ERANGE => "Math result not representable",
+ EDEADLK => "Resource deadlock would occur",
+ ENAMETOOLONG => "File name too long",
+ ENOLCK => "No record locks available",
+ ENOSYS => "Function not implemented",
+ ENOTEMPTY => "Directory not empty",
+ ELOOP => "Too many symbolic links encountered",
+ ENOMSG => "No message of desired type",
+ EIDRM => "Identifier removed",
+ EINPROGRESS => "Operation now in progress",
+ EALREADY => "Operation already in progress",
+ ENOTSOCK => "Socket operation on non-socket",
+ EDESTADDRREQ => "Destination address required",
+ EMSGSIZE => "Message too long",
+ EPROTOTYPE => "Protocol wrong type for socket",
+ ENOPROTOOPT => "Protocol not available",
+ EPROTONOSUPPORT => "Protocol not supported",
+ #[cfg(not(target_os = "haiku"))]
+ ESOCKTNOSUPPORT => "Socket type not supported",
+ #[cfg(not(target_os = "haiku"))]
+ EPFNOSUPPORT => "Protocol family not supported",
+ #[cfg(not(target_os = "haiku"))]
+ EAFNOSUPPORT => "Address family not supported by protocol",
+ EADDRINUSE => "Address already in use",
+ EADDRNOTAVAIL => "Cannot assign requested address",
+ ENETDOWN => "Network is down",
+ ENETUNREACH => "Network is unreachable",
+ ENETRESET => "Network dropped connection because of reset",
+ ECONNABORTED => "Software caused connection abort",
+ ECONNRESET => "Connection reset by peer",
+ ENOBUFS => "No buffer space available",
+ EISCONN => "Transport endpoint is already connected",
+ ENOTCONN => "Transport endpoint is not connected",
+ ESHUTDOWN => "Cannot send after transport endpoint shutdown",
+ #[cfg(not(target_os = "haiku"))]
+ ETOOMANYREFS => "Too many references: cannot splice",
+ ETIMEDOUT => "Connection timed out",
+ ECONNREFUSED => "Connection refused",
+ EHOSTDOWN => "Host is down",
+ EHOSTUNREACH => "No route to host",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ECHRNG => "Channel number out of range",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EL2NSYNC => "Level 2 not synchronized",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EL3HLT => "Level 3 halted",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EL3RST => "Level 3 reset",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ELNRNG => "Link number out of range",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EUNATCH => "Protocol driver not attached",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ENOCSI => "No CSI structure available",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EL2HLT => "Level 2 halted",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EBADE => "Invalid exchange",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EBADR => "Invalid request descriptor",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EXFULL => "Exchange full",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ENOANO => "No anode",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EBADRQC => "Invalid request code",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EBADSLT => "Invalid slot",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EBFONT => "Bad font file format",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ENOSTR => "Device not a stream",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ENODATA => "No data available",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ETIME => "Timer expired",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ENOSR => "Out of streams resources",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ENONET => "Machine is not on the network",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ENOPKG => "Package not installed",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EREMOTE => "Object is remote",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ENOLINK => "Link has been severed",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EADV => "Advertise error",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ESRMNT => "Srmount error",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ECOMM => "Communication error on send",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EPROTO => "Protocol error",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EMULTIHOP => "Multihop attempted",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia"
+ ))]
+ EDOTDOT => "RFS specific error",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia"
+ ))]
+ EBADMSG => "Not a data message",
+
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ EBADMSG => "Trying to read unreadable message",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "haiku"
+ ))]
+ EOVERFLOW => "Value too large for defined data type",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ENOTUNIQ => "Name not unique on network",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EBADFD => "File descriptor in bad state",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EREMCHG => "Remote address changed",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ELIBACC => "Can not access a needed shared library",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ELIBBAD => "Accessing a corrupted shared library",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ELIBSCN => ".lib section in a.out corrupted",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ELIBMAX => "Attempting to link in too many shared libraries",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ELIBEXEC => "Cannot exec a shared library directly",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia",
+ target_os = "openbsd"
+ ))]
+ EILSEQ => "Illegal byte sequence",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ERESTART => "Interrupted system call should be restarted",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ ESTRPIPE => "Streams pipe error",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia"
+ ))]
+ EUSERS => "Too many users",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "netbsd",
+ target_os = "redox"
+ ))]
+ EOPNOTSUPP => "Operation not supported on transport endpoint",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia"
+ ))]
+ ESTALE => "Stale file handle",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia"
+ ))]
+ EUCLEAN => "Structure needs cleaning",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia"
+ ))]
+ ENOTNAM => "Not a XENIX named type file",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia"
+ ))]
+ ENAVAIL => "No XENIX semaphores available",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia"
+ ))]
+ EISNAM => "Is a named type file",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia"
+ ))]
+ EREMOTEIO => "Remote I/O error",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia"
+ ))]
+ EDQUOT => "Quota exceeded",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "openbsd",
+ target_os = "dragonfly"
+ ))]
+ ENOMEDIUM => "No medium found",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "openbsd"
+ ))]
+ EMEDIUMTYPE => "Wrong medium type",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "fuchsia",
+ target_os = "haiku"
+ ))]
+ ECANCELED => "Operation canceled",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia"
+ ))]
+ ENOKEY => "Required key not available",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia"
+ ))]
+ EKEYEXPIRED => "Key has expired",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia"
+ ))]
+ EKEYREVOKED => "Key has been revoked",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia"
+ ))]
+ EKEYREJECTED => "Key was rejected by service",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia"
+ ))]
+ EOWNERDEAD => "Owner died",
+
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ EOWNERDEAD => "Process died with lock",
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia"
+ ))]
+ ENOTRECOVERABLE => "State not recoverable",
+
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ ENOTRECOVERABLE => "Lock is not recoverable",
+
+ #[cfg(any(
+ all(target_os = "linux", not(target_arch = "mips")),
+ target_os = "fuchsia"
+ ))]
+ ERFKILL => "Operation not possible due to RF-kill",
+
+ #[cfg(any(
+ all(target_os = "linux", not(target_arch = "mips")),
+ target_os = "fuchsia"
+ ))]
+ EHWPOISON => "Memory page has hardware error",
+
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ EDOOFUS => "Programming error",
+
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "redox"
+ ))]
+ EMULTIHOP => "Multihop attempted",
+
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "redox"
+ ))]
+ ENOLINK => "Link has been severed",
+
+ #[cfg(target_os = "freebsd")]
+ ENOTCAPABLE => "Capabilities insufficient",
+
+ #[cfg(target_os = "freebsd")]
+ ECAPMODE => "Not permitted in capability mode",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "openbsd",
+ target_os = "netbsd"
+ ))]
+ ENEEDAUTH => "Need authenticator",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "redox",
+ target_os = "illumos",
+ target_os = "solaris"
+ ))]
+ EOVERFLOW => "Value too large to be stored in data type",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "netbsd",
+ target_os = "redox",
+ target_os = "haiku"
+ ))]
+ EILSEQ => "Illegal byte sequence",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "haiku"
+ ))]
+ ENOATTR => "Attribute not found",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "redox",
+ target_os = "haiku"
+ ))]
+ EBADMSG => "Bad message",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "redox",
+ target_os = "haiku"
+ ))]
+ EPROTO => "Protocol error",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "openbsd"
+ ))]
+ ENOTRECOVERABLE => "State not recoverable",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "openbsd"
+ ))]
+ EOWNERDEAD => "Previous owner died",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "haiku"
+ ))]
+ ENOTSUP => "Operation not supported",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "openbsd",
+ target_os = "netbsd"
+ ))]
+ EPROCLIM => "Too many processes",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "redox"
+ ))]
+ EUSERS => "Too many users",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "redox",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "haiku"
+ ))]
+ EDQUOT => "Disc quota exceeded",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "redox",
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "haiku"
+ ))]
+ ESTALE => "Stale NFS file handle",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "redox"
+ ))]
+ EREMOTE => "Too many levels of remote in path",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "openbsd",
+ target_os = "netbsd"
+ ))]
+ EBADRPC => "RPC struct is bad",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "openbsd",
+ target_os = "netbsd"
+ ))]
+ ERPCMISMATCH => "RPC version wrong",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "openbsd",
+ target_os = "netbsd"
+ ))]
+ EPROGUNAVAIL => "RPC prog. not avail",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "openbsd",
+ target_os = "netbsd"
+ ))]
+ EPROGMISMATCH => "Program version wrong",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "openbsd",
+ target_os = "netbsd"
+ ))]
+ EPROCUNAVAIL => "Bad procedure for program",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "openbsd",
+ target_os = "netbsd"
+ ))]
+ EFTYPE => "Inappropriate file type or format",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "openbsd",
+ target_os = "netbsd"
+ ))]
+ EAUTH => "Authentication error",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "redox"
+ ))]
+ ECANCELED => "Operation canceled",
+
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ EPWROFF => "Device power is off",
+
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ EDEVERR => "Device error, e.g. paper out",
+
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ EBADEXEC => "Bad executable",
+
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ EBADARCH => "Bad CPU type in executable",
+
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ ESHLIBVERS => "Shared library version mismatch",
+
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ EBADMACHO => "Malformed Macho file",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "netbsd",
+ target_os = "haiku"
+ ))]
+ EMULTIHOP => "Reserved",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "netbsd",
+ target_os = "redox"
+ ))]
+ ENODATA => "No message available on STREAM",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "netbsd",
+ target_os = "haiku"
+ ))]
+ ENOLINK => "Reserved",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "netbsd",
+ target_os = "redox"
+ ))]
+ ENOSR => "No STREAM resources",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "netbsd",
+ target_os = "redox"
+ ))]
+ ENOSTR => "Not a STREAM",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "netbsd",
+ target_os = "redox"
+ ))]
+ ETIME => "STREAM ioctl timeout",
+
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "illumos",
+ target_os = "solaris"
+ ))]
+ EOPNOTSUPP => "Operation not supported on socket",
+
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ ENOPOLICY => "No such policy registered",
+
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ EQFULL => "Interface output queue is full",
+
+ #[cfg(target_os = "openbsd")]
+ EOPNOTSUPP => "Operation not supported",
+
+ #[cfg(target_os = "openbsd")]
+ EIPSEC => "IPsec processing failure",
+
+ #[cfg(target_os = "dragonfly")]
+ EASYNC => "Async",
+
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ EDEADLOCK => "Resource deadlock would occur",
+
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ ELOCKUNMAPPED => "Locked lock was unmapped",
+
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ ENOTACTIVE => "Facility is not active",
+ }
+}
+
+#[cfg(any(target_os = "linux", target_os = "android", target_os = "fuchsia"))]
+mod consts {
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ #[repr(i32)]
+ #[non_exhaustive]
+ pub enum Errno {
+ UnknownErrno = 0,
+ EPERM = libc::EPERM,
+ ENOENT = libc::ENOENT,
+ ESRCH = libc::ESRCH,
+ EINTR = libc::EINTR,
+ EIO = libc::EIO,
+ ENXIO = libc::ENXIO,
+ E2BIG = libc::E2BIG,
+ ENOEXEC = libc::ENOEXEC,
+ EBADF = libc::EBADF,
+ ECHILD = libc::ECHILD,
+ EAGAIN = libc::EAGAIN,
+ ENOMEM = libc::ENOMEM,
+ EACCES = libc::EACCES,
+ EFAULT = libc::EFAULT,
+ ENOTBLK = libc::ENOTBLK,
+ EBUSY = libc::EBUSY,
+ EEXIST = libc::EEXIST,
+ EXDEV = libc::EXDEV,
+ ENODEV = libc::ENODEV,
+ ENOTDIR = libc::ENOTDIR,
+ EISDIR = libc::EISDIR,
+ EINVAL = libc::EINVAL,
+ ENFILE = libc::ENFILE,
+ EMFILE = libc::EMFILE,
+ ENOTTY = libc::ENOTTY,
+ ETXTBSY = libc::ETXTBSY,
+ EFBIG = libc::EFBIG,
+ ENOSPC = libc::ENOSPC,
+ ESPIPE = libc::ESPIPE,
+ EROFS = libc::EROFS,
+ EMLINK = libc::EMLINK,
+ EPIPE = libc::EPIPE,
+ EDOM = libc::EDOM,
+ ERANGE = libc::ERANGE,
+ EDEADLK = libc::EDEADLK,
+ ENAMETOOLONG = libc::ENAMETOOLONG,
+ ENOLCK = libc::ENOLCK,
+ ENOSYS = libc::ENOSYS,
+ ENOTEMPTY = libc::ENOTEMPTY,
+ ELOOP = libc::ELOOP,
+ ENOMSG = libc::ENOMSG,
+ EIDRM = libc::EIDRM,
+ ECHRNG = libc::ECHRNG,
+ EL2NSYNC = libc::EL2NSYNC,
+ EL3HLT = libc::EL3HLT,
+ EL3RST = libc::EL3RST,
+ ELNRNG = libc::ELNRNG,
+ EUNATCH = libc::EUNATCH,
+ ENOCSI = libc::ENOCSI,
+ EL2HLT = libc::EL2HLT,
+ EBADE = libc::EBADE,
+ EBADR = libc::EBADR,
+ EXFULL = libc::EXFULL,
+ ENOANO = libc::ENOANO,
+ EBADRQC = libc::EBADRQC,
+ EBADSLT = libc::EBADSLT,
+ EBFONT = libc::EBFONT,
+ ENOSTR = libc::ENOSTR,
+ ENODATA = libc::ENODATA,
+ ETIME = libc::ETIME,
+ ENOSR = libc::ENOSR,
+ ENONET = libc::ENONET,
+ ENOPKG = libc::ENOPKG,
+ EREMOTE = libc::EREMOTE,
+ ENOLINK = libc::ENOLINK,
+ EADV = libc::EADV,
+ ESRMNT = libc::ESRMNT,
+ ECOMM = libc::ECOMM,
+ EPROTO = libc::EPROTO,
+ EMULTIHOP = libc::EMULTIHOP,
+ EDOTDOT = libc::EDOTDOT,
+ EBADMSG = libc::EBADMSG,
+ EOVERFLOW = libc::EOVERFLOW,
+ ENOTUNIQ = libc::ENOTUNIQ,
+ EBADFD = libc::EBADFD,
+ EREMCHG = libc::EREMCHG,
+ ELIBACC = libc::ELIBACC,
+ ELIBBAD = libc::ELIBBAD,
+ ELIBSCN = libc::ELIBSCN,
+ ELIBMAX = libc::ELIBMAX,
+ ELIBEXEC = libc::ELIBEXEC,
+ EILSEQ = libc::EILSEQ,
+ ERESTART = libc::ERESTART,
+ ESTRPIPE = libc::ESTRPIPE,
+ EUSERS = libc::EUSERS,
+ ENOTSOCK = libc::ENOTSOCK,
+ EDESTADDRREQ = libc::EDESTADDRREQ,
+ EMSGSIZE = libc::EMSGSIZE,
+ EPROTOTYPE = libc::EPROTOTYPE,
+ ENOPROTOOPT = libc::ENOPROTOOPT,
+ EPROTONOSUPPORT = libc::EPROTONOSUPPORT,
+ ESOCKTNOSUPPORT = libc::ESOCKTNOSUPPORT,
+ EOPNOTSUPP = libc::EOPNOTSUPP,
+ EPFNOSUPPORT = libc::EPFNOSUPPORT,
+ EAFNOSUPPORT = libc::EAFNOSUPPORT,
+ EADDRINUSE = libc::EADDRINUSE,
+ EADDRNOTAVAIL = libc::EADDRNOTAVAIL,
+ ENETDOWN = libc::ENETDOWN,
+ ENETUNREACH = libc::ENETUNREACH,
+ ENETRESET = libc::ENETRESET,
+ ECONNABORTED = libc::ECONNABORTED,
+ ECONNRESET = libc::ECONNRESET,
+ ENOBUFS = libc::ENOBUFS,
+ EISCONN = libc::EISCONN,
+ ENOTCONN = libc::ENOTCONN,
+ ESHUTDOWN = libc::ESHUTDOWN,
+ ETOOMANYREFS = libc::ETOOMANYREFS,
+ ETIMEDOUT = libc::ETIMEDOUT,
+ ECONNREFUSED = libc::ECONNREFUSED,
+ EHOSTDOWN = libc::EHOSTDOWN,
+ EHOSTUNREACH = libc::EHOSTUNREACH,
+ EALREADY = libc::EALREADY,
+ EINPROGRESS = libc::EINPROGRESS,
+ ESTALE = libc::ESTALE,
+ EUCLEAN = libc::EUCLEAN,
+ ENOTNAM = libc::ENOTNAM,
+ ENAVAIL = libc::ENAVAIL,
+ EISNAM = libc::EISNAM,
+ EREMOTEIO = libc::EREMOTEIO,
+ EDQUOT = libc::EDQUOT,
+ ENOMEDIUM = libc::ENOMEDIUM,
+ EMEDIUMTYPE = libc::EMEDIUMTYPE,
+ ECANCELED = libc::ECANCELED,
+ ENOKEY = libc::ENOKEY,
+ EKEYEXPIRED = libc::EKEYEXPIRED,
+ EKEYREVOKED = libc::EKEYREVOKED,
+ EKEYREJECTED = libc::EKEYREJECTED,
+ EOWNERDEAD = libc::EOWNERDEAD,
+ ENOTRECOVERABLE = libc::ENOTRECOVERABLE,
+ #[cfg(not(any(target_os = "android", target_arch = "mips")))]
+ ERFKILL = libc::ERFKILL,
+ #[cfg(not(any(target_os = "android", target_arch = "mips")))]
+ EHWPOISON = libc::EHWPOISON,
+ }
+
+ impl Errno {
+ pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
+ pub const EDEADLOCK: Errno = Errno::EDEADLK;
+ pub const ENOTSUP: Errno = Errno::EOPNOTSUPP;
+ }
+
+ pub const fn from_i32(e: i32) -> Errno {
+ use self::Errno::*;
+
+ match e {
+ libc::EPERM => EPERM,
+ libc::ENOENT => ENOENT,
+ libc::ESRCH => ESRCH,
+ libc::EINTR => EINTR,
+ libc::EIO => EIO,
+ libc::ENXIO => ENXIO,
+ libc::E2BIG => E2BIG,
+ libc::ENOEXEC => ENOEXEC,
+ libc::EBADF => EBADF,
+ libc::ECHILD => ECHILD,
+ libc::EAGAIN => EAGAIN,
+ libc::ENOMEM => ENOMEM,
+ libc::EACCES => EACCES,
+ libc::EFAULT => EFAULT,
+ libc::ENOTBLK => ENOTBLK,
+ libc::EBUSY => EBUSY,
+ libc::EEXIST => EEXIST,
+ libc::EXDEV => EXDEV,
+ libc::ENODEV => ENODEV,
+ libc::ENOTDIR => ENOTDIR,
+ libc::EISDIR => EISDIR,
+ libc::EINVAL => EINVAL,
+ libc::ENFILE => ENFILE,
+ libc::EMFILE => EMFILE,
+ libc::ENOTTY => ENOTTY,
+ libc::ETXTBSY => ETXTBSY,
+ libc::EFBIG => EFBIG,
+ libc::ENOSPC => ENOSPC,
+ libc::ESPIPE => ESPIPE,
+ libc::EROFS => EROFS,
+ libc::EMLINK => EMLINK,
+ libc::EPIPE => EPIPE,
+ libc::EDOM => EDOM,
+ libc::ERANGE => ERANGE,
+ libc::EDEADLK => EDEADLK,
+ libc::ENAMETOOLONG => ENAMETOOLONG,
+ libc::ENOLCK => ENOLCK,
+ libc::ENOSYS => ENOSYS,
+ libc::ENOTEMPTY => ENOTEMPTY,
+ libc::ELOOP => ELOOP,
+ libc::ENOMSG => ENOMSG,
+ libc::EIDRM => EIDRM,
+ libc::ECHRNG => ECHRNG,
+ libc::EL2NSYNC => EL2NSYNC,
+ libc::EL3HLT => EL3HLT,
+ libc::EL3RST => EL3RST,
+ libc::ELNRNG => ELNRNG,
+ libc::EUNATCH => EUNATCH,
+ libc::ENOCSI => ENOCSI,
+ libc::EL2HLT => EL2HLT,
+ libc::EBADE => EBADE,
+ libc::EBADR => EBADR,
+ libc::EXFULL => EXFULL,
+ libc::ENOANO => ENOANO,
+ libc::EBADRQC => EBADRQC,
+ libc::EBADSLT => EBADSLT,
+ libc::EBFONT => EBFONT,
+ libc::ENOSTR => ENOSTR,
+ libc::ENODATA => ENODATA,
+ libc::ETIME => ETIME,
+ libc::ENOSR => ENOSR,
+ libc::ENONET => ENONET,
+ libc::ENOPKG => ENOPKG,
+ libc::EREMOTE => EREMOTE,
+ libc::ENOLINK => ENOLINK,
+ libc::EADV => EADV,
+ libc::ESRMNT => ESRMNT,
+ libc::ECOMM => ECOMM,
+ libc::EPROTO => EPROTO,
+ libc::EMULTIHOP => EMULTIHOP,
+ libc::EDOTDOT => EDOTDOT,
+ libc::EBADMSG => EBADMSG,
+ libc::EOVERFLOW => EOVERFLOW,
+ libc::ENOTUNIQ => ENOTUNIQ,
+ libc::EBADFD => EBADFD,
+ libc::EREMCHG => EREMCHG,
+ libc::ELIBACC => ELIBACC,
+ libc::ELIBBAD => ELIBBAD,
+ libc::ELIBSCN => ELIBSCN,
+ libc::ELIBMAX => ELIBMAX,
+ libc::ELIBEXEC => ELIBEXEC,
+ libc::EILSEQ => EILSEQ,
+ libc::ERESTART => ERESTART,
+ libc::ESTRPIPE => ESTRPIPE,
+ libc::EUSERS => EUSERS,
+ libc::ENOTSOCK => ENOTSOCK,
+ libc::EDESTADDRREQ => EDESTADDRREQ,
+ libc::EMSGSIZE => EMSGSIZE,
+ libc::EPROTOTYPE => EPROTOTYPE,
+ libc::ENOPROTOOPT => ENOPROTOOPT,
+ libc::EPROTONOSUPPORT => EPROTONOSUPPORT,
+ libc::ESOCKTNOSUPPORT => ESOCKTNOSUPPORT,
+ libc::EOPNOTSUPP => EOPNOTSUPP,
+ libc::EPFNOSUPPORT => EPFNOSUPPORT,
+ libc::EAFNOSUPPORT => EAFNOSUPPORT,
+ libc::EADDRINUSE => EADDRINUSE,
+ libc::EADDRNOTAVAIL => EADDRNOTAVAIL,
+ libc::ENETDOWN => ENETDOWN,
+ libc::ENETUNREACH => ENETUNREACH,
+ libc::ENETRESET => ENETRESET,
+ libc::ECONNABORTED => ECONNABORTED,
+ libc::ECONNRESET => ECONNRESET,
+ libc::ENOBUFS => ENOBUFS,
+ libc::EISCONN => EISCONN,
+ libc::ENOTCONN => ENOTCONN,
+ libc::ESHUTDOWN => ESHUTDOWN,
+ libc::ETOOMANYREFS => ETOOMANYREFS,
+ libc::ETIMEDOUT => ETIMEDOUT,
+ libc::ECONNREFUSED => ECONNREFUSED,
+ libc::EHOSTDOWN => EHOSTDOWN,
+ libc::EHOSTUNREACH => EHOSTUNREACH,
+ libc::EALREADY => EALREADY,
+ libc::EINPROGRESS => EINPROGRESS,
+ libc::ESTALE => ESTALE,
+ libc::EUCLEAN => EUCLEAN,
+ libc::ENOTNAM => ENOTNAM,
+ libc::ENAVAIL => ENAVAIL,
+ libc::EISNAM => EISNAM,
+ libc::EREMOTEIO => EREMOTEIO,
+ libc::EDQUOT => EDQUOT,
+ libc::ENOMEDIUM => ENOMEDIUM,
+ libc::EMEDIUMTYPE => EMEDIUMTYPE,
+ libc::ECANCELED => ECANCELED,
+ libc::ENOKEY => ENOKEY,
+ libc::EKEYEXPIRED => EKEYEXPIRED,
+ libc::EKEYREVOKED => EKEYREVOKED,
+ libc::EKEYREJECTED => EKEYREJECTED,
+ libc::EOWNERDEAD => EOWNERDEAD,
+ libc::ENOTRECOVERABLE => ENOTRECOVERABLE,
+ #[cfg(not(any(target_os = "android", target_arch = "mips")))]
+ libc::ERFKILL => ERFKILL,
+ #[cfg(not(any(target_os = "android", target_arch = "mips")))]
+ libc::EHWPOISON => EHWPOISON,
+ _ => UnknownErrno,
+ }
+ }
+}
+
+#[cfg(any(target_os = "macos", target_os = "ios"))]
+mod consts {
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ #[repr(i32)]
+ #[non_exhaustive]
+ pub enum Errno {
+ UnknownErrno = 0,
+ EPERM = libc::EPERM,
+ ENOENT = libc::ENOENT,
+ ESRCH = libc::ESRCH,
+ EINTR = libc::EINTR,
+ EIO = libc::EIO,
+ ENXIO = libc::ENXIO,
+ E2BIG = libc::E2BIG,
+ ENOEXEC = libc::ENOEXEC,
+ EBADF = libc::EBADF,
+ ECHILD = libc::ECHILD,
+ EDEADLK = libc::EDEADLK,
+ ENOMEM = libc::ENOMEM,
+ EACCES = libc::EACCES,
+ EFAULT = libc::EFAULT,
+ ENOTBLK = libc::ENOTBLK,
+ EBUSY = libc::EBUSY,
+ EEXIST = libc::EEXIST,
+ EXDEV = libc::EXDEV,
+ ENODEV = libc::ENODEV,
+ ENOTDIR = libc::ENOTDIR,
+ EISDIR = libc::EISDIR,
+ EINVAL = libc::EINVAL,
+ ENFILE = libc::ENFILE,
+ EMFILE = libc::EMFILE,
+ ENOTTY = libc::ENOTTY,
+ ETXTBSY = libc::ETXTBSY,
+ EFBIG = libc::EFBIG,
+ ENOSPC = libc::ENOSPC,
+ ESPIPE = libc::ESPIPE,
+ EROFS = libc::EROFS,
+ EMLINK = libc::EMLINK,
+ EPIPE = libc::EPIPE,
+ EDOM = libc::EDOM,
+ ERANGE = libc::ERANGE,
+ EAGAIN = libc::EAGAIN,
+ EINPROGRESS = libc::EINPROGRESS,
+ EALREADY = libc::EALREADY,
+ ENOTSOCK = libc::ENOTSOCK,
+ EDESTADDRREQ = libc::EDESTADDRREQ,
+ EMSGSIZE = libc::EMSGSIZE,
+ EPROTOTYPE = libc::EPROTOTYPE,
+ ENOPROTOOPT = libc::ENOPROTOOPT,
+ EPROTONOSUPPORT = libc::EPROTONOSUPPORT,
+ ESOCKTNOSUPPORT = libc::ESOCKTNOSUPPORT,
+ ENOTSUP = libc::ENOTSUP,
+ EPFNOSUPPORT = libc::EPFNOSUPPORT,
+ EAFNOSUPPORT = libc::EAFNOSUPPORT,
+ EADDRINUSE = libc::EADDRINUSE,
+ EADDRNOTAVAIL = libc::EADDRNOTAVAIL,
+ ENETDOWN = libc::ENETDOWN,
+ ENETUNREACH = libc::ENETUNREACH,
+ ENETRESET = libc::ENETRESET,
+ ECONNABORTED = libc::ECONNABORTED,
+ ECONNRESET = libc::ECONNRESET,
+ ENOBUFS = libc::ENOBUFS,
+ EISCONN = libc::EISCONN,
+ ENOTCONN = libc::ENOTCONN,
+ ESHUTDOWN = libc::ESHUTDOWN,
+ ETOOMANYREFS = libc::ETOOMANYREFS,
+ ETIMEDOUT = libc::ETIMEDOUT,
+ ECONNREFUSED = libc::ECONNREFUSED,
+ ELOOP = libc::ELOOP,
+ ENAMETOOLONG = libc::ENAMETOOLONG,
+ EHOSTDOWN = libc::EHOSTDOWN,
+ EHOSTUNREACH = libc::EHOSTUNREACH,
+ ENOTEMPTY = libc::ENOTEMPTY,
+ EPROCLIM = libc::EPROCLIM,
+ EUSERS = libc::EUSERS,
+ EDQUOT = libc::EDQUOT,
+ ESTALE = libc::ESTALE,
+ EREMOTE = libc::EREMOTE,
+ EBADRPC = libc::EBADRPC,
+ ERPCMISMATCH = libc::ERPCMISMATCH,
+ EPROGUNAVAIL = libc::EPROGUNAVAIL,
+ EPROGMISMATCH = libc::EPROGMISMATCH,
+ EPROCUNAVAIL = libc::EPROCUNAVAIL,
+ ENOLCK = libc::ENOLCK,
+ ENOSYS = libc::ENOSYS,
+ EFTYPE = libc::EFTYPE,
+ EAUTH = libc::EAUTH,
+ ENEEDAUTH = libc::ENEEDAUTH,
+ EPWROFF = libc::EPWROFF,
+ EDEVERR = libc::EDEVERR,
+ EOVERFLOW = libc::EOVERFLOW,
+ EBADEXEC = libc::EBADEXEC,
+ EBADARCH = libc::EBADARCH,
+ ESHLIBVERS = libc::ESHLIBVERS,
+ EBADMACHO = libc::EBADMACHO,
+ ECANCELED = libc::ECANCELED,
+ EIDRM = libc::EIDRM,
+ ENOMSG = libc::ENOMSG,
+ EILSEQ = libc::EILSEQ,
+ ENOATTR = libc::ENOATTR,
+ EBADMSG = libc::EBADMSG,
+ EMULTIHOP = libc::EMULTIHOP,
+ ENODATA = libc::ENODATA,
+ ENOLINK = libc::ENOLINK,
+ ENOSR = libc::ENOSR,
+ ENOSTR = libc::ENOSTR,
+ EPROTO = libc::EPROTO,
+ ETIME = libc::ETIME,
+ EOPNOTSUPP = libc::EOPNOTSUPP,
+ ENOPOLICY = libc::ENOPOLICY,
+ ENOTRECOVERABLE = libc::ENOTRECOVERABLE,
+ EOWNERDEAD = libc::EOWNERDEAD,
+ EQFULL = libc::EQFULL,
+ }
+
+ impl Errno {
+ pub const ELAST: Errno = Errno::EQFULL;
+ pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
+ pub const EDEADLOCK: Errno = Errno::EDEADLK;
+ }
+
+ pub const fn from_i32(e: i32) -> Errno {
+ use self::Errno::*;
+
+ match e {
+ libc::EPERM => EPERM,
+ libc::ENOENT => ENOENT,
+ libc::ESRCH => ESRCH,
+ libc::EINTR => EINTR,
+ libc::EIO => EIO,
+ libc::ENXIO => ENXIO,
+ libc::E2BIG => E2BIG,
+ libc::ENOEXEC => ENOEXEC,
+ libc::EBADF => EBADF,
+ libc::ECHILD => ECHILD,
+ libc::EDEADLK => EDEADLK,
+ libc::ENOMEM => ENOMEM,
+ libc::EACCES => EACCES,
+ libc::EFAULT => EFAULT,
+ libc::ENOTBLK => ENOTBLK,
+ libc::EBUSY => EBUSY,
+ libc::EEXIST => EEXIST,
+ libc::EXDEV => EXDEV,
+ libc::ENODEV => ENODEV,
+ libc::ENOTDIR => ENOTDIR,
+ libc::EISDIR => EISDIR,
+ libc::EINVAL => EINVAL,
+ libc::ENFILE => ENFILE,
+ libc::EMFILE => EMFILE,
+ libc::ENOTTY => ENOTTY,
+ libc::ETXTBSY => ETXTBSY,
+ libc::EFBIG => EFBIG,
+ libc::ENOSPC => ENOSPC,
+ libc::ESPIPE => ESPIPE,
+ libc::EROFS => EROFS,
+ libc::EMLINK => EMLINK,
+ libc::EPIPE => EPIPE,
+ libc::EDOM => EDOM,
+ libc::ERANGE => ERANGE,
+ libc::EAGAIN => EAGAIN,
+ libc::EINPROGRESS => EINPROGRESS,
+ libc::EALREADY => EALREADY,
+ libc::ENOTSOCK => ENOTSOCK,
+ libc::EDESTADDRREQ => EDESTADDRREQ,
+ libc::EMSGSIZE => EMSGSIZE,
+ libc::EPROTOTYPE => EPROTOTYPE,
+ libc::ENOPROTOOPT => ENOPROTOOPT,
+ libc::EPROTONOSUPPORT => EPROTONOSUPPORT,
+ libc::ESOCKTNOSUPPORT => ESOCKTNOSUPPORT,
+ libc::ENOTSUP => ENOTSUP,
+ libc::EPFNOSUPPORT => EPFNOSUPPORT,
+ libc::EAFNOSUPPORT => EAFNOSUPPORT,
+ libc::EADDRINUSE => EADDRINUSE,
+ libc::EADDRNOTAVAIL => EADDRNOTAVAIL,
+ libc::ENETDOWN => ENETDOWN,
+ libc::ENETUNREACH => ENETUNREACH,
+ libc::ENETRESET => ENETRESET,
+ libc::ECONNABORTED => ECONNABORTED,
+ libc::ECONNRESET => ECONNRESET,
+ libc::ENOBUFS => ENOBUFS,
+ libc::EISCONN => EISCONN,
+ libc::ENOTCONN => ENOTCONN,
+ libc::ESHUTDOWN => ESHUTDOWN,
+ libc::ETOOMANYREFS => ETOOMANYREFS,
+ libc::ETIMEDOUT => ETIMEDOUT,
+ libc::ECONNREFUSED => ECONNREFUSED,
+ libc::ELOOP => ELOOP,
+ libc::ENAMETOOLONG => ENAMETOOLONG,
+ libc::EHOSTDOWN => EHOSTDOWN,
+ libc::EHOSTUNREACH => EHOSTUNREACH,
+ libc::ENOTEMPTY => ENOTEMPTY,
+ libc::EPROCLIM => EPROCLIM,
+ libc::EUSERS => EUSERS,
+ libc::EDQUOT => EDQUOT,
+ libc::ESTALE => ESTALE,
+ libc::EREMOTE => EREMOTE,
+ libc::EBADRPC => EBADRPC,
+ libc::ERPCMISMATCH => ERPCMISMATCH,
+ libc::EPROGUNAVAIL => EPROGUNAVAIL,
+ libc::EPROGMISMATCH => EPROGMISMATCH,
+ libc::EPROCUNAVAIL => EPROCUNAVAIL,
+ libc::ENOLCK => ENOLCK,
+ libc::ENOSYS => ENOSYS,
+ libc::EFTYPE => EFTYPE,
+ libc::EAUTH => EAUTH,
+ libc::ENEEDAUTH => ENEEDAUTH,
+ libc::EPWROFF => EPWROFF,
+ libc::EDEVERR => EDEVERR,
+ libc::EOVERFLOW => EOVERFLOW,
+ libc::EBADEXEC => EBADEXEC,
+ libc::EBADARCH => EBADARCH,
+ libc::ESHLIBVERS => ESHLIBVERS,
+ libc::EBADMACHO => EBADMACHO,
+ libc::ECANCELED => ECANCELED,
+ libc::EIDRM => EIDRM,
+ libc::ENOMSG => ENOMSG,
+ libc::EILSEQ => EILSEQ,
+ libc::ENOATTR => ENOATTR,
+ libc::EBADMSG => EBADMSG,
+ libc::EMULTIHOP => EMULTIHOP,
+ libc::ENODATA => ENODATA,
+ libc::ENOLINK => ENOLINK,
+ libc::ENOSR => ENOSR,
+ libc::ENOSTR => ENOSTR,
+ libc::EPROTO => EPROTO,
+ libc::ETIME => ETIME,
+ libc::EOPNOTSUPP => EOPNOTSUPP,
+ libc::ENOPOLICY => ENOPOLICY,
+ libc::ENOTRECOVERABLE => ENOTRECOVERABLE,
+ libc::EOWNERDEAD => EOWNERDEAD,
+ libc::EQFULL => EQFULL,
+ _ => UnknownErrno,
+ }
+ }
+}
+
+#[cfg(target_os = "freebsd")]
+mod consts {
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ #[repr(i32)]
+ #[non_exhaustive]
+ pub enum Errno {
+ UnknownErrno = 0,
+ EPERM = libc::EPERM,
+ ENOENT = libc::ENOENT,
+ ESRCH = libc::ESRCH,
+ EINTR = libc::EINTR,
+ EIO = libc::EIO,
+ ENXIO = libc::ENXIO,
+ E2BIG = libc::E2BIG,
+ ENOEXEC = libc::ENOEXEC,
+ EBADF = libc::EBADF,
+ ECHILD = libc::ECHILD,
+ EDEADLK = libc::EDEADLK,
+ ENOMEM = libc::ENOMEM,
+ EACCES = libc::EACCES,
+ EFAULT = libc::EFAULT,
+ ENOTBLK = libc::ENOTBLK,
+ EBUSY = libc::EBUSY,
+ EEXIST = libc::EEXIST,
+ EXDEV = libc::EXDEV,
+ ENODEV = libc::ENODEV,
+ ENOTDIR = libc::ENOTDIR,
+ EISDIR = libc::EISDIR,
+ EINVAL = libc::EINVAL,
+ ENFILE = libc::ENFILE,
+ EMFILE = libc::EMFILE,
+ ENOTTY = libc::ENOTTY,
+ ETXTBSY = libc::ETXTBSY,
+ EFBIG = libc::EFBIG,
+ ENOSPC = libc::ENOSPC,
+ ESPIPE = libc::ESPIPE,
+ EROFS = libc::EROFS,
+ EMLINK = libc::EMLINK,
+ EPIPE = libc::EPIPE,
+ EDOM = libc::EDOM,
+ ERANGE = libc::ERANGE,
+ EAGAIN = libc::EAGAIN,
+ EINPROGRESS = libc::EINPROGRESS,
+ EALREADY = libc::EALREADY,
+ ENOTSOCK = libc::ENOTSOCK,
+ EDESTADDRREQ = libc::EDESTADDRREQ,
+ EMSGSIZE = libc::EMSGSIZE,
+ EPROTOTYPE = libc::EPROTOTYPE,
+ ENOPROTOOPT = libc::ENOPROTOOPT,
+ EPROTONOSUPPORT = libc::EPROTONOSUPPORT,
+ ESOCKTNOSUPPORT = libc::ESOCKTNOSUPPORT,
+ ENOTSUP = libc::ENOTSUP,
+ EPFNOSUPPORT = libc::EPFNOSUPPORT,
+ EAFNOSUPPORT = libc::EAFNOSUPPORT,
+ EADDRINUSE = libc::EADDRINUSE,
+ EADDRNOTAVAIL = libc::EADDRNOTAVAIL,
+ ENETDOWN = libc::ENETDOWN,
+ ENETUNREACH = libc::ENETUNREACH,
+ ENETRESET = libc::ENETRESET,
+ ECONNABORTED = libc::ECONNABORTED,
+ ECONNRESET = libc::ECONNRESET,
+ ENOBUFS = libc::ENOBUFS,
+ EISCONN = libc::EISCONN,
+ ENOTCONN = libc::ENOTCONN,
+ ESHUTDOWN = libc::ESHUTDOWN,
+ ETOOMANYREFS = libc::ETOOMANYREFS,
+ ETIMEDOUT = libc::ETIMEDOUT,
+ ECONNREFUSED = libc::ECONNREFUSED,
+ ELOOP = libc::ELOOP,
+ ENAMETOOLONG = libc::ENAMETOOLONG,
+ EHOSTDOWN = libc::EHOSTDOWN,
+ EHOSTUNREACH = libc::EHOSTUNREACH,
+ ENOTEMPTY = libc::ENOTEMPTY,
+ EPROCLIM = libc::EPROCLIM,
+ EUSERS = libc::EUSERS,
+ EDQUOT = libc::EDQUOT,
+ ESTALE = libc::ESTALE,
+ EREMOTE = libc::EREMOTE,
+ EBADRPC = libc::EBADRPC,
+ ERPCMISMATCH = libc::ERPCMISMATCH,
+ EPROGUNAVAIL = libc::EPROGUNAVAIL,
+ EPROGMISMATCH = libc::EPROGMISMATCH,
+ EPROCUNAVAIL = libc::EPROCUNAVAIL,
+ ENOLCK = libc::ENOLCK,
+ ENOSYS = libc::ENOSYS,
+ EFTYPE = libc::EFTYPE,
+ EAUTH = libc::EAUTH,
+ ENEEDAUTH = libc::ENEEDAUTH,
+ EIDRM = libc::EIDRM,
+ ENOMSG = libc::ENOMSG,
+ EOVERFLOW = libc::EOVERFLOW,
+ ECANCELED = libc::ECANCELED,
+ EILSEQ = libc::EILSEQ,
+ ENOATTR = libc::ENOATTR,
+ EDOOFUS = libc::EDOOFUS,
+ EBADMSG = libc::EBADMSG,
+ EMULTIHOP = libc::EMULTIHOP,
+ ENOLINK = libc::ENOLINK,
+ EPROTO = libc::EPROTO,
+ ENOTCAPABLE = libc::ENOTCAPABLE,
+ ECAPMODE = libc::ECAPMODE,
+ ENOTRECOVERABLE = libc::ENOTRECOVERABLE,
+ EOWNERDEAD = libc::EOWNERDEAD,
+ }
+
+ impl Errno {
+ pub const ELAST: Errno = Errno::EOWNERDEAD;
+ pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
+ pub const EDEADLOCK: Errno = Errno::EDEADLK;
+ pub const EOPNOTSUPP: Errno = Errno::ENOTSUP;
+ }
+
+ pub const fn from_i32(e: i32) -> Errno {
+ use self::Errno::*;
+
+ match e {
+ libc::EPERM => EPERM,
+ libc::ENOENT => ENOENT,
+ libc::ESRCH => ESRCH,
+ libc::EINTR => EINTR,
+ libc::EIO => EIO,
+ libc::ENXIO => ENXIO,
+ libc::E2BIG => E2BIG,
+ libc::ENOEXEC => ENOEXEC,
+ libc::EBADF => EBADF,
+ libc::ECHILD => ECHILD,
+ libc::EDEADLK => EDEADLK,
+ libc::ENOMEM => ENOMEM,
+ libc::EACCES => EACCES,
+ libc::EFAULT => EFAULT,
+ libc::ENOTBLK => ENOTBLK,
+ libc::EBUSY => EBUSY,
+ libc::EEXIST => EEXIST,
+ libc::EXDEV => EXDEV,
+ libc::ENODEV => ENODEV,
+ libc::ENOTDIR => ENOTDIR,
+ libc::EISDIR => EISDIR,
+ libc::EINVAL => EINVAL,
+ libc::ENFILE => ENFILE,
+ libc::EMFILE => EMFILE,
+ libc::ENOTTY => ENOTTY,
+ libc::ETXTBSY => ETXTBSY,
+ libc::EFBIG => EFBIG,
+ libc::ENOSPC => ENOSPC,
+ libc::ESPIPE => ESPIPE,
+ libc::EROFS => EROFS,
+ libc::EMLINK => EMLINK,
+ libc::EPIPE => EPIPE,
+ libc::EDOM => EDOM,
+ libc::ERANGE => ERANGE,
+ libc::EAGAIN => EAGAIN,
+ libc::EINPROGRESS => EINPROGRESS,
+ libc::EALREADY => EALREADY,
+ libc::ENOTSOCK => ENOTSOCK,
+ libc::EDESTADDRREQ => EDESTADDRREQ,
+ libc::EMSGSIZE => EMSGSIZE,
+ libc::EPROTOTYPE => EPROTOTYPE,
+ libc::ENOPROTOOPT => ENOPROTOOPT,
+ libc::EPROTONOSUPPORT => EPROTONOSUPPORT,
+ libc::ESOCKTNOSUPPORT => ESOCKTNOSUPPORT,
+ libc::ENOTSUP => ENOTSUP,
+ libc::EPFNOSUPPORT => EPFNOSUPPORT,
+ libc::EAFNOSUPPORT => EAFNOSUPPORT,
+ libc::EADDRINUSE => EADDRINUSE,
+ libc::EADDRNOTAVAIL => EADDRNOTAVAIL,
+ libc::ENETDOWN => ENETDOWN,
+ libc::ENETUNREACH => ENETUNREACH,
+ libc::ENETRESET => ENETRESET,
+ libc::ECONNABORTED => ECONNABORTED,
+ libc::ECONNRESET => ECONNRESET,
+ libc::ENOBUFS => ENOBUFS,
+ libc::EISCONN => EISCONN,
+ libc::ENOTCONN => ENOTCONN,
+ libc::ESHUTDOWN => ESHUTDOWN,
+ libc::ETOOMANYREFS => ETOOMANYREFS,
+ libc::ETIMEDOUT => ETIMEDOUT,
+ libc::ECONNREFUSED => ECONNREFUSED,
+ libc::ELOOP => ELOOP,
+ libc::ENAMETOOLONG => ENAMETOOLONG,
+ libc::EHOSTDOWN => EHOSTDOWN,
+ libc::EHOSTUNREACH => EHOSTUNREACH,
+ libc::ENOTEMPTY => ENOTEMPTY,
+ libc::EPROCLIM => EPROCLIM,
+ libc::EUSERS => EUSERS,
+ libc::EDQUOT => EDQUOT,
+ libc::ESTALE => ESTALE,
+ libc::EREMOTE => EREMOTE,
+ libc::EBADRPC => EBADRPC,
+ libc::ERPCMISMATCH => ERPCMISMATCH,
+ libc::EPROGUNAVAIL => EPROGUNAVAIL,
+ libc::EPROGMISMATCH => EPROGMISMATCH,
+ libc::EPROCUNAVAIL => EPROCUNAVAIL,
+ libc::ENOLCK => ENOLCK,
+ libc::ENOSYS => ENOSYS,
+ libc::EFTYPE => EFTYPE,
+ libc::EAUTH => EAUTH,
+ libc::ENEEDAUTH => ENEEDAUTH,
+ libc::EIDRM => EIDRM,
+ libc::ENOMSG => ENOMSG,
+ libc::EOVERFLOW => EOVERFLOW,
+ libc::ECANCELED => ECANCELED,
+ libc::EILSEQ => EILSEQ,
+ libc::ENOATTR => ENOATTR,
+ libc::EDOOFUS => EDOOFUS,
+ libc::EBADMSG => EBADMSG,
+ libc::EMULTIHOP => EMULTIHOP,
+ libc::ENOLINK => ENOLINK,
+ libc::EPROTO => EPROTO,
+ libc::ENOTCAPABLE => ENOTCAPABLE,
+ libc::ECAPMODE => ECAPMODE,
+ libc::ENOTRECOVERABLE => ENOTRECOVERABLE,
+ libc::EOWNERDEAD => EOWNERDEAD,
+ _ => UnknownErrno,
+ }
+ }
+}
+
+#[cfg(target_os = "dragonfly")]
+mod consts {
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ #[repr(i32)]
+ #[non_exhaustive]
+ pub enum Errno {
+ UnknownErrno = 0,
+ EPERM = libc::EPERM,
+ ENOENT = libc::ENOENT,
+ ESRCH = libc::ESRCH,
+ EINTR = libc::EINTR,
+ EIO = libc::EIO,
+ ENXIO = libc::ENXIO,
+ E2BIG = libc::E2BIG,
+ ENOEXEC = libc::ENOEXEC,
+ EBADF = libc::EBADF,
+ ECHILD = libc::ECHILD,
+ EDEADLK = libc::EDEADLK,
+ ENOMEM = libc::ENOMEM,
+ EACCES = libc::EACCES,
+ EFAULT = libc::EFAULT,
+ ENOTBLK = libc::ENOTBLK,
+ EBUSY = libc::EBUSY,
+ EEXIST = libc::EEXIST,
+ EXDEV = libc::EXDEV,
+ ENODEV = libc::ENODEV,
+ ENOTDIR = libc::ENOTDIR,
+ EISDIR = libc::EISDIR,
+ EINVAL = libc::EINVAL,
+ ENFILE = libc::ENFILE,
+ EMFILE = libc::EMFILE,
+ ENOTTY = libc::ENOTTY,
+ ETXTBSY = libc::ETXTBSY,
+ EFBIG = libc::EFBIG,
+ ENOSPC = libc::ENOSPC,
+ ESPIPE = libc::ESPIPE,
+ EROFS = libc::EROFS,
+ EMLINK = libc::EMLINK,
+ EPIPE = libc::EPIPE,
+ EDOM = libc::EDOM,
+ ERANGE = libc::ERANGE,
+ EAGAIN = libc::EAGAIN,
+ EINPROGRESS = libc::EINPROGRESS,
+ EALREADY = libc::EALREADY,
+ ENOTSOCK = libc::ENOTSOCK,
+ EDESTADDRREQ = libc::EDESTADDRREQ,
+ EMSGSIZE = libc::EMSGSIZE,
+ EPROTOTYPE = libc::EPROTOTYPE,
+ ENOPROTOOPT = libc::ENOPROTOOPT,
+ EPROTONOSUPPORT = libc::EPROTONOSUPPORT,
+ ESOCKTNOSUPPORT = libc::ESOCKTNOSUPPORT,
+ ENOTSUP = libc::ENOTSUP,
+ EPFNOSUPPORT = libc::EPFNOSUPPORT,
+ EAFNOSUPPORT = libc::EAFNOSUPPORT,
+ EADDRINUSE = libc::EADDRINUSE,
+ EADDRNOTAVAIL = libc::EADDRNOTAVAIL,
+ ENETDOWN = libc::ENETDOWN,
+ ENETUNREACH = libc::ENETUNREACH,
+ ENETRESET = libc::ENETRESET,
+ ECONNABORTED = libc::ECONNABORTED,
+ ECONNRESET = libc::ECONNRESET,
+ ENOBUFS = libc::ENOBUFS,
+ EISCONN = libc::EISCONN,
+ ENOTCONN = libc::ENOTCONN,
+ ESHUTDOWN = libc::ESHUTDOWN,
+ ETOOMANYREFS = libc::ETOOMANYREFS,
+ ETIMEDOUT = libc::ETIMEDOUT,
+ ECONNREFUSED = libc::ECONNREFUSED,
+ ELOOP = libc::ELOOP,
+ ENAMETOOLONG = libc::ENAMETOOLONG,
+ EHOSTDOWN = libc::EHOSTDOWN,
+ EHOSTUNREACH = libc::EHOSTUNREACH,
+ ENOTEMPTY = libc::ENOTEMPTY,
+ EPROCLIM = libc::EPROCLIM,
+ EUSERS = libc::EUSERS,
+ EDQUOT = libc::EDQUOT,
+ ESTALE = libc::ESTALE,
+ EREMOTE = libc::EREMOTE,
+ EBADRPC = libc::EBADRPC,
+ ERPCMISMATCH = libc::ERPCMISMATCH,
+ EPROGUNAVAIL = libc::EPROGUNAVAIL,
+ EPROGMISMATCH = libc::EPROGMISMATCH,
+ EPROCUNAVAIL = libc::EPROCUNAVAIL,
+ ENOLCK = libc::ENOLCK,
+ ENOSYS = libc::ENOSYS,
+ EFTYPE = libc::EFTYPE,
+ EAUTH = libc::EAUTH,
+ ENEEDAUTH = libc::ENEEDAUTH,
+ EIDRM = libc::EIDRM,
+ ENOMSG = libc::ENOMSG,
+ EOVERFLOW = libc::EOVERFLOW,
+ ECANCELED = libc::ECANCELED,
+ EILSEQ = libc::EILSEQ,
+ ENOATTR = libc::ENOATTR,
+ EDOOFUS = libc::EDOOFUS,
+ EBADMSG = libc::EBADMSG,
+ EMULTIHOP = libc::EMULTIHOP,
+ ENOLINK = libc::ENOLINK,
+ EPROTO = libc::EPROTO,
+ ENOMEDIUM = libc::ENOMEDIUM,
+ ENOTRECOVERABLE = libc::ENOTRECOVERABLE,
+ EOWNERDEAD = libc::EOWNERDEAD,
+ EASYNC = libc::EASYNC,
+ }
+
+ impl Errno {
+ pub const ELAST: Errno = Errno::EASYNC;
+ pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
+ pub const EDEADLOCK: Errno = Errno::EDEADLK;
+ pub const EOPNOTSUPP: Errno = Errno::ENOTSUP;
+ }
+
+ pub const fn from_i32(e: i32) -> Errno {
+ use self::Errno::*;
+
+ match e {
+ libc::EPERM => EPERM,
+ libc::ENOENT => ENOENT,
+ libc::ESRCH => ESRCH,
+ libc::EINTR => EINTR,
+ libc::EIO => EIO,
+ libc::ENXIO => ENXIO,
+ libc::E2BIG => E2BIG,
+ libc::ENOEXEC => ENOEXEC,
+ libc::EBADF => EBADF,
+ libc::ECHILD => ECHILD,
+ libc::EDEADLK => EDEADLK,
+ libc::ENOMEM => ENOMEM,
+ libc::EACCES => EACCES,
+ libc::EFAULT => EFAULT,
+ libc::ENOTBLK => ENOTBLK,
+ libc::EBUSY => EBUSY,
+ libc::EEXIST => EEXIST,
+ libc::EXDEV => EXDEV,
+ libc::ENODEV => ENODEV,
+ libc::ENOTDIR => ENOTDIR,
+ libc::EISDIR => EISDIR,
+ libc::EINVAL => EINVAL,
+ libc::ENFILE => ENFILE,
+ libc::EMFILE => EMFILE,
+ libc::ENOTTY => ENOTTY,
+ libc::ETXTBSY => ETXTBSY,
+ libc::EFBIG => EFBIG,
+ libc::ENOSPC => ENOSPC,
+ libc::ESPIPE => ESPIPE,
+ libc::EROFS => EROFS,
+ libc::EMLINK => EMLINK,
+ libc::EPIPE => EPIPE,
+ libc::EDOM => EDOM,
+ libc::ERANGE => ERANGE,
+ libc::EAGAIN => EAGAIN,
+ libc::EINPROGRESS => EINPROGRESS,
+ libc::EALREADY => EALREADY,
+ libc::ENOTSOCK => ENOTSOCK,
+ libc::EDESTADDRREQ => EDESTADDRREQ,
+ libc::EMSGSIZE => EMSGSIZE,
+ libc::EPROTOTYPE => EPROTOTYPE,
+ libc::ENOPROTOOPT => ENOPROTOOPT,
+ libc::EPROTONOSUPPORT => EPROTONOSUPPORT,
+ libc::ESOCKTNOSUPPORT => ESOCKTNOSUPPORT,
+ libc::ENOTSUP => ENOTSUP,
+ libc::EPFNOSUPPORT => EPFNOSUPPORT,
+ libc::EAFNOSUPPORT => EAFNOSUPPORT,
+ libc::EADDRINUSE => EADDRINUSE,
+ libc::EADDRNOTAVAIL => EADDRNOTAVAIL,
+ libc::ENETDOWN => ENETDOWN,
+ libc::ENETUNREACH => ENETUNREACH,
+ libc::ENETRESET => ENETRESET,
+ libc::ECONNABORTED => ECONNABORTED,
+ libc::ECONNRESET => ECONNRESET,
+ libc::ENOBUFS => ENOBUFS,
+ libc::EISCONN => EISCONN,
+ libc::ENOTCONN => ENOTCONN,
+ libc::ESHUTDOWN => ESHUTDOWN,
+ libc::ETOOMANYREFS => ETOOMANYREFS,
+ libc::ETIMEDOUT => ETIMEDOUT,
+ libc::ECONNREFUSED => ECONNREFUSED,
+ libc::ELOOP => ELOOP,
+ libc::ENAMETOOLONG => ENAMETOOLONG,
+ libc::EHOSTDOWN => EHOSTDOWN,
+ libc::EHOSTUNREACH => EHOSTUNREACH,
+ libc::ENOTEMPTY => ENOTEMPTY,
+ libc::EPROCLIM => EPROCLIM,
+ libc::EUSERS => EUSERS,
+ libc::EDQUOT => EDQUOT,
+ libc::ESTALE => ESTALE,
+ libc::EREMOTE => EREMOTE,
+ libc::EBADRPC => EBADRPC,
+ libc::ERPCMISMATCH => ERPCMISMATCH,
+ libc::EPROGUNAVAIL => EPROGUNAVAIL,
+ libc::EPROGMISMATCH => EPROGMISMATCH,
+ libc::EPROCUNAVAIL => EPROCUNAVAIL,
+ libc::ENOLCK => ENOLCK,
+ libc::ENOSYS => ENOSYS,
+ libc::EFTYPE => EFTYPE,
+ libc::EAUTH => EAUTH,
+ libc::ENEEDAUTH => ENEEDAUTH,
+ libc::EIDRM => EIDRM,
+ libc::ENOMSG => ENOMSG,
+ libc::EOVERFLOW => EOVERFLOW,
+ libc::ECANCELED => ECANCELED,
+ libc::EILSEQ => EILSEQ,
+ libc::ENOATTR => ENOATTR,
+ libc::EDOOFUS => EDOOFUS,
+ libc::EBADMSG => EBADMSG,
+ libc::EMULTIHOP => EMULTIHOP,
+ libc::ENOLINK => ENOLINK,
+ libc::EPROTO => EPROTO,
+ libc::ENOMEDIUM => ENOMEDIUM,
+ libc::EASYNC => EASYNC,
+ _ => UnknownErrno,
+ }
+ }
+}
+
+#[cfg(target_os = "openbsd")]
+mod consts {
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ #[repr(i32)]
+ #[non_exhaustive]
+ pub enum Errno {
+ UnknownErrno = 0,
+ EPERM = libc::EPERM,
+ ENOENT = libc::ENOENT,
+ ESRCH = libc::ESRCH,
+ EINTR = libc::EINTR,
+ EIO = libc::EIO,
+ ENXIO = libc::ENXIO,
+ E2BIG = libc::E2BIG,
+ ENOEXEC = libc::ENOEXEC,
+ EBADF = libc::EBADF,
+ ECHILD = libc::ECHILD,
+ EDEADLK = libc::EDEADLK,
+ ENOMEM = libc::ENOMEM,
+ EACCES = libc::EACCES,
+ EFAULT = libc::EFAULT,
+ ENOTBLK = libc::ENOTBLK,
+ EBUSY = libc::EBUSY,
+ EEXIST = libc::EEXIST,
+ EXDEV = libc::EXDEV,
+ ENODEV = libc::ENODEV,
+ ENOTDIR = libc::ENOTDIR,
+ EISDIR = libc::EISDIR,
+ EINVAL = libc::EINVAL,
+ ENFILE = libc::ENFILE,
+ EMFILE = libc::EMFILE,
+ ENOTTY = libc::ENOTTY,
+ ETXTBSY = libc::ETXTBSY,
+ EFBIG = libc::EFBIG,
+ ENOSPC = libc::ENOSPC,
+ ESPIPE = libc::ESPIPE,
+ EROFS = libc::EROFS,
+ EMLINK = libc::EMLINK,
+ EPIPE = libc::EPIPE,
+ EDOM = libc::EDOM,
+ ERANGE = libc::ERANGE,
+ EAGAIN = libc::EAGAIN,
+ EINPROGRESS = libc::EINPROGRESS,
+ EALREADY = libc::EALREADY,
+ ENOTSOCK = libc::ENOTSOCK,
+ EDESTADDRREQ = libc::EDESTADDRREQ,
+ EMSGSIZE = libc::EMSGSIZE,
+ EPROTOTYPE = libc::EPROTOTYPE,
+ ENOPROTOOPT = libc::ENOPROTOOPT,
+ EPROTONOSUPPORT = libc::EPROTONOSUPPORT,
+ ESOCKTNOSUPPORT = libc::ESOCKTNOSUPPORT,
+ EOPNOTSUPP = libc::EOPNOTSUPP,
+ EPFNOSUPPORT = libc::EPFNOSUPPORT,
+ EAFNOSUPPORT = libc::EAFNOSUPPORT,
+ EADDRINUSE = libc::EADDRINUSE,
+ EADDRNOTAVAIL = libc::EADDRNOTAVAIL,
+ ENETDOWN = libc::ENETDOWN,
+ ENETUNREACH = libc::ENETUNREACH,
+ ENETRESET = libc::ENETRESET,
+ ECONNABORTED = libc::ECONNABORTED,
+ ECONNRESET = libc::ECONNRESET,
+ ENOBUFS = libc::ENOBUFS,
+ EISCONN = libc::EISCONN,
+ ENOTCONN = libc::ENOTCONN,
+ ESHUTDOWN = libc::ESHUTDOWN,
+ ETOOMANYREFS = libc::ETOOMANYREFS,
+ ETIMEDOUT = libc::ETIMEDOUT,
+ ECONNREFUSED = libc::ECONNREFUSED,
+ ELOOP = libc::ELOOP,
+ ENAMETOOLONG = libc::ENAMETOOLONG,
+ EHOSTDOWN = libc::EHOSTDOWN,
+ EHOSTUNREACH = libc::EHOSTUNREACH,
+ ENOTEMPTY = libc::ENOTEMPTY,
+ EPROCLIM = libc::EPROCLIM,
+ EUSERS = libc::EUSERS,
+ EDQUOT = libc::EDQUOT,
+ ESTALE = libc::ESTALE,
+ EREMOTE = libc::EREMOTE,
+ EBADRPC = libc::EBADRPC,
+ ERPCMISMATCH = libc::ERPCMISMATCH,
+ EPROGUNAVAIL = libc::EPROGUNAVAIL,
+ EPROGMISMATCH = libc::EPROGMISMATCH,
+ EPROCUNAVAIL = libc::EPROCUNAVAIL,
+ ENOLCK = libc::ENOLCK,
+ ENOSYS = libc::ENOSYS,
+ EFTYPE = libc::EFTYPE,
+ EAUTH = libc::EAUTH,
+ ENEEDAUTH = libc::ENEEDAUTH,
+ EIPSEC = libc::EIPSEC,
+ ENOATTR = libc::ENOATTR,
+ EILSEQ = libc::EILSEQ,
+ ENOMEDIUM = libc::ENOMEDIUM,
+ EMEDIUMTYPE = libc::EMEDIUMTYPE,
+ EOVERFLOW = libc::EOVERFLOW,
+ ECANCELED = libc::ECANCELED,
+ EIDRM = libc::EIDRM,
+ ENOMSG = libc::ENOMSG,
+ ENOTSUP = libc::ENOTSUP,
+ EBADMSG = libc::EBADMSG,
+ ENOTRECOVERABLE = libc::ENOTRECOVERABLE,
+ EOWNERDEAD = libc::EOWNERDEAD,
+ EPROTO = libc::EPROTO,
+ }
+
+ impl Errno {
+ pub const ELAST: Errno = Errno::ENOTSUP;
+ pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
+ }
+
+ pub const fn from_i32(e: i32) -> Errno {
+ use self::Errno::*;
+
+ match e {
+ libc::EPERM => EPERM,
+ libc::ENOENT => ENOENT,
+ libc::ESRCH => ESRCH,
+ libc::EINTR => EINTR,
+ libc::EIO => EIO,
+ libc::ENXIO => ENXIO,
+ libc::E2BIG => E2BIG,
+ libc::ENOEXEC => ENOEXEC,
+ libc::EBADF => EBADF,
+ libc::ECHILD => ECHILD,
+ libc::EDEADLK => EDEADLK,
+ libc::ENOMEM => ENOMEM,
+ libc::EACCES => EACCES,
+ libc::EFAULT => EFAULT,
+ libc::ENOTBLK => ENOTBLK,
+ libc::EBUSY => EBUSY,
+ libc::EEXIST => EEXIST,
+ libc::EXDEV => EXDEV,
+ libc::ENODEV => ENODEV,
+ libc::ENOTDIR => ENOTDIR,
+ libc::EISDIR => EISDIR,
+ libc::EINVAL => EINVAL,
+ libc::ENFILE => ENFILE,
+ libc::EMFILE => EMFILE,
+ libc::ENOTTY => ENOTTY,
+ libc::ETXTBSY => ETXTBSY,
+ libc::EFBIG => EFBIG,
+ libc::ENOSPC => ENOSPC,
+ libc::ESPIPE => ESPIPE,
+ libc::EROFS => EROFS,
+ libc::EMLINK => EMLINK,
+ libc::EPIPE => EPIPE,
+ libc::EDOM => EDOM,
+ libc::ERANGE => ERANGE,
+ libc::EAGAIN => EAGAIN,
+ libc::EINPROGRESS => EINPROGRESS,
+ libc::EALREADY => EALREADY,
+ libc::ENOTSOCK => ENOTSOCK,
+ libc::EDESTADDRREQ => EDESTADDRREQ,
+ libc::EMSGSIZE => EMSGSIZE,
+ libc::EPROTOTYPE => EPROTOTYPE,
+ libc::ENOPROTOOPT => ENOPROTOOPT,
+ libc::EPROTONOSUPPORT => EPROTONOSUPPORT,
+ libc::ESOCKTNOSUPPORT => ESOCKTNOSUPPORT,
+ libc::EOPNOTSUPP => EOPNOTSUPP,
+ libc::EPFNOSUPPORT => EPFNOSUPPORT,
+ libc::EAFNOSUPPORT => EAFNOSUPPORT,
+ libc::EADDRINUSE => EADDRINUSE,
+ libc::EADDRNOTAVAIL => EADDRNOTAVAIL,
+ libc::ENETDOWN => ENETDOWN,
+ libc::ENETUNREACH => ENETUNREACH,
+ libc::ENETRESET => ENETRESET,
+ libc::ECONNABORTED => ECONNABORTED,
+ libc::ECONNRESET => ECONNRESET,
+ libc::ENOBUFS => ENOBUFS,
+ libc::EISCONN => EISCONN,
+ libc::ENOTCONN => ENOTCONN,
+ libc::ESHUTDOWN => ESHUTDOWN,
+ libc::ETOOMANYREFS => ETOOMANYREFS,
+ libc::ETIMEDOUT => ETIMEDOUT,
+ libc::ECONNREFUSED => ECONNREFUSED,
+ libc::ELOOP => ELOOP,
+ libc::ENAMETOOLONG => ENAMETOOLONG,
+ libc::EHOSTDOWN => EHOSTDOWN,
+ libc::EHOSTUNREACH => EHOSTUNREACH,
+ libc::ENOTEMPTY => ENOTEMPTY,
+ libc::EPROCLIM => EPROCLIM,
+ libc::EUSERS => EUSERS,
+ libc::EDQUOT => EDQUOT,
+ libc::ESTALE => ESTALE,
+ libc::EREMOTE => EREMOTE,
+ libc::EBADRPC => EBADRPC,
+ libc::ERPCMISMATCH => ERPCMISMATCH,
+ libc::EPROGUNAVAIL => EPROGUNAVAIL,
+ libc::EPROGMISMATCH => EPROGMISMATCH,
+ libc::EPROCUNAVAIL => EPROCUNAVAIL,
+ libc::ENOLCK => ENOLCK,
+ libc::ENOSYS => ENOSYS,
+ libc::EFTYPE => EFTYPE,
+ libc::EAUTH => EAUTH,
+ libc::ENEEDAUTH => ENEEDAUTH,
+ libc::EIPSEC => EIPSEC,
+ libc::ENOATTR => ENOATTR,
+ libc::EILSEQ => EILSEQ,
+ libc::ENOMEDIUM => ENOMEDIUM,
+ libc::EMEDIUMTYPE => EMEDIUMTYPE,
+ libc::EOVERFLOW => EOVERFLOW,
+ libc::ECANCELED => ECANCELED,
+ libc::EIDRM => EIDRM,
+ libc::ENOMSG => ENOMSG,
+ libc::ENOTSUP => ENOTSUP,
+ libc::EBADMSG => EBADMSG,
+ libc::ENOTRECOVERABLE => ENOTRECOVERABLE,
+ libc::EOWNERDEAD => EOWNERDEAD,
+ libc::EPROTO => EPROTO,
+ _ => UnknownErrno,
+ }
+ }
+}
+
+#[cfg(target_os = "netbsd")]
+mod consts {
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ #[repr(i32)]
+ #[non_exhaustive]
+ pub enum Errno {
+ UnknownErrno = 0,
+ EPERM = libc::EPERM,
+ ENOENT = libc::ENOENT,
+ ESRCH = libc::ESRCH,
+ EINTR = libc::EINTR,
+ EIO = libc::EIO,
+ ENXIO = libc::ENXIO,
+ E2BIG = libc::E2BIG,
+ ENOEXEC = libc::ENOEXEC,
+ EBADF = libc::EBADF,
+ ECHILD = libc::ECHILD,
+ EDEADLK = libc::EDEADLK,
+ ENOMEM = libc::ENOMEM,
+ EACCES = libc::EACCES,
+ EFAULT = libc::EFAULT,
+ ENOTBLK = libc::ENOTBLK,
+ EBUSY = libc::EBUSY,
+ EEXIST = libc::EEXIST,
+ EXDEV = libc::EXDEV,
+ ENODEV = libc::ENODEV,
+ ENOTDIR = libc::ENOTDIR,
+ EISDIR = libc::EISDIR,
+ EINVAL = libc::EINVAL,
+ ENFILE = libc::ENFILE,
+ EMFILE = libc::EMFILE,
+ ENOTTY = libc::ENOTTY,
+ ETXTBSY = libc::ETXTBSY,
+ EFBIG = libc::EFBIG,
+ ENOSPC = libc::ENOSPC,
+ ESPIPE = libc::ESPIPE,
+ EROFS = libc::EROFS,
+ EMLINK = libc::EMLINK,
+ EPIPE = libc::EPIPE,
+ EDOM = libc::EDOM,
+ ERANGE = libc::ERANGE,
+ EAGAIN = libc::EAGAIN,
+ EINPROGRESS = libc::EINPROGRESS,
+ EALREADY = libc::EALREADY,
+ ENOTSOCK = libc::ENOTSOCK,
+ EDESTADDRREQ = libc::EDESTADDRREQ,
+ EMSGSIZE = libc::EMSGSIZE,
+ EPROTOTYPE = libc::EPROTOTYPE,
+ ENOPROTOOPT = libc::ENOPROTOOPT,
+ EPROTONOSUPPORT = libc::EPROTONOSUPPORT,
+ ESOCKTNOSUPPORT = libc::ESOCKTNOSUPPORT,
+ EOPNOTSUPP = libc::EOPNOTSUPP,
+ EPFNOSUPPORT = libc::EPFNOSUPPORT,
+ EAFNOSUPPORT = libc::EAFNOSUPPORT,
+ EADDRINUSE = libc::EADDRINUSE,
+ EADDRNOTAVAIL = libc::EADDRNOTAVAIL,
+ ENETDOWN = libc::ENETDOWN,
+ ENETUNREACH = libc::ENETUNREACH,
+ ENETRESET = libc::ENETRESET,
+ ECONNABORTED = libc::ECONNABORTED,
+ ECONNRESET = libc::ECONNRESET,
+ ENOBUFS = libc::ENOBUFS,
+ EISCONN = libc::EISCONN,
+ ENOTCONN = libc::ENOTCONN,
+ ESHUTDOWN = libc::ESHUTDOWN,
+ ETOOMANYREFS = libc::ETOOMANYREFS,
+ ETIMEDOUT = libc::ETIMEDOUT,
+ ECONNREFUSED = libc::ECONNREFUSED,
+ ELOOP = libc::ELOOP,
+ ENAMETOOLONG = libc::ENAMETOOLONG,
+ EHOSTDOWN = libc::EHOSTDOWN,
+ EHOSTUNREACH = libc::EHOSTUNREACH,
+ ENOTEMPTY = libc::ENOTEMPTY,
+ EPROCLIM = libc::EPROCLIM,
+ EUSERS = libc::EUSERS,
+ EDQUOT = libc::EDQUOT,
+ ESTALE = libc::ESTALE,
+ EREMOTE = libc::EREMOTE,
+ EBADRPC = libc::EBADRPC,
+ ERPCMISMATCH = libc::ERPCMISMATCH,
+ EPROGUNAVAIL = libc::EPROGUNAVAIL,
+ EPROGMISMATCH = libc::EPROGMISMATCH,
+ EPROCUNAVAIL = libc::EPROCUNAVAIL,
+ ENOLCK = libc::ENOLCK,
+ ENOSYS = libc::ENOSYS,
+ EFTYPE = libc::EFTYPE,
+ EAUTH = libc::EAUTH,
+ ENEEDAUTH = libc::ENEEDAUTH,
+ EIDRM = libc::EIDRM,
+ ENOMSG = libc::ENOMSG,
+ EOVERFLOW = libc::EOVERFLOW,
+ EILSEQ = libc::EILSEQ,
+ ENOTSUP = libc::ENOTSUP,
+ ECANCELED = libc::ECANCELED,
+ EBADMSG = libc::EBADMSG,
+ ENODATA = libc::ENODATA,
+ ENOSR = libc::ENOSR,
+ ENOSTR = libc::ENOSTR,
+ ETIME = libc::ETIME,
+ ENOATTR = libc::ENOATTR,
+ EMULTIHOP = libc::EMULTIHOP,
+ ENOLINK = libc::ENOLINK,
+ EPROTO = libc::EPROTO,
+ }
+
+ impl Errno {
+ pub const ELAST: Errno = Errno::ENOTSUP;
+ pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
+ }
+
+ pub const fn from_i32(e: i32) -> Errno {
+ use self::Errno::*;
+
+ match e {
+ libc::EPERM => EPERM,
+ libc::ENOENT => ENOENT,
+ libc::ESRCH => ESRCH,
+ libc::EINTR => EINTR,
+ libc::EIO => EIO,
+ libc::ENXIO => ENXIO,
+ libc::E2BIG => E2BIG,
+ libc::ENOEXEC => ENOEXEC,
+ libc::EBADF => EBADF,
+ libc::ECHILD => ECHILD,
+ libc::EDEADLK => EDEADLK,
+ libc::ENOMEM => ENOMEM,
+ libc::EACCES => EACCES,
+ libc::EFAULT => EFAULT,
+ libc::ENOTBLK => ENOTBLK,
+ libc::EBUSY => EBUSY,
+ libc::EEXIST => EEXIST,
+ libc::EXDEV => EXDEV,
+ libc::ENODEV => ENODEV,
+ libc::ENOTDIR => ENOTDIR,
+ libc::EISDIR => EISDIR,
+ libc::EINVAL => EINVAL,
+ libc::ENFILE => ENFILE,
+ libc::EMFILE => EMFILE,
+ libc::ENOTTY => ENOTTY,
+ libc::ETXTBSY => ETXTBSY,
+ libc::EFBIG => EFBIG,
+ libc::ENOSPC => ENOSPC,
+ libc::ESPIPE => ESPIPE,
+ libc::EROFS => EROFS,
+ libc::EMLINK => EMLINK,
+ libc::EPIPE => EPIPE,
+ libc::EDOM => EDOM,
+ libc::ERANGE => ERANGE,
+ libc::EAGAIN => EAGAIN,
+ libc::EINPROGRESS => EINPROGRESS,
+ libc::EALREADY => EALREADY,
+ libc::ENOTSOCK => ENOTSOCK,
+ libc::EDESTADDRREQ => EDESTADDRREQ,
+ libc::EMSGSIZE => EMSGSIZE,
+ libc::EPROTOTYPE => EPROTOTYPE,
+ libc::ENOPROTOOPT => ENOPROTOOPT,
+ libc::EPROTONOSUPPORT => EPROTONOSUPPORT,
+ libc::ESOCKTNOSUPPORT => ESOCKTNOSUPPORT,
+ libc::EOPNOTSUPP => EOPNOTSUPP,
+ libc::EPFNOSUPPORT => EPFNOSUPPORT,
+ libc::EAFNOSUPPORT => EAFNOSUPPORT,
+ libc::EADDRINUSE => EADDRINUSE,
+ libc::EADDRNOTAVAIL => EADDRNOTAVAIL,
+ libc::ENETDOWN => ENETDOWN,
+ libc::ENETUNREACH => ENETUNREACH,
+ libc::ENETRESET => ENETRESET,
+ libc::ECONNABORTED => ECONNABORTED,
+ libc::ECONNRESET => ECONNRESET,
+ libc::ENOBUFS => ENOBUFS,
+ libc::EISCONN => EISCONN,
+ libc::ENOTCONN => ENOTCONN,
+ libc::ESHUTDOWN => ESHUTDOWN,
+ libc::ETOOMANYREFS => ETOOMANYREFS,
+ libc::ETIMEDOUT => ETIMEDOUT,
+ libc::ECONNREFUSED => ECONNREFUSED,
+ libc::ELOOP => ELOOP,
+ libc::ENAMETOOLONG => ENAMETOOLONG,
+ libc::EHOSTDOWN => EHOSTDOWN,
+ libc::EHOSTUNREACH => EHOSTUNREACH,
+ libc::ENOTEMPTY => ENOTEMPTY,
+ libc::EPROCLIM => EPROCLIM,
+ libc::EUSERS => EUSERS,
+ libc::EDQUOT => EDQUOT,
+ libc::ESTALE => ESTALE,
+ libc::EREMOTE => EREMOTE,
+ libc::EBADRPC => EBADRPC,
+ libc::ERPCMISMATCH => ERPCMISMATCH,
+ libc::EPROGUNAVAIL => EPROGUNAVAIL,
+ libc::EPROGMISMATCH => EPROGMISMATCH,
+ libc::EPROCUNAVAIL => EPROCUNAVAIL,
+ libc::ENOLCK => ENOLCK,
+ libc::ENOSYS => ENOSYS,
+ libc::EFTYPE => EFTYPE,
+ libc::EAUTH => EAUTH,
+ libc::ENEEDAUTH => ENEEDAUTH,
+ libc::EIDRM => EIDRM,
+ libc::ENOMSG => ENOMSG,
+ libc::EOVERFLOW => EOVERFLOW,
+ libc::EILSEQ => EILSEQ,
+ libc::ENOTSUP => ENOTSUP,
+ libc::ECANCELED => ECANCELED,
+ libc::EBADMSG => EBADMSG,
+ libc::ENODATA => ENODATA,
+ libc::ENOSR => ENOSR,
+ libc::ENOSTR => ENOSTR,
+ libc::ETIME => ETIME,
+ libc::ENOATTR => ENOATTR,
+ libc::EMULTIHOP => EMULTIHOP,
+ libc::ENOLINK => ENOLINK,
+ libc::EPROTO => EPROTO,
+ _ => UnknownErrno,
+ }
+ }
+}
+
+#[cfg(target_os = "redox")]
+mod consts {
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ #[repr(i32)]
+ #[non_exhaustive]
+ pub enum Errno {
+ UnknownErrno = 0,
+ EPERM = libc::EPERM,
+ ENOENT = libc::ENOENT,
+ ESRCH = libc::ESRCH,
+ EINTR = libc::EINTR,
+ EIO = libc::EIO,
+ ENXIO = libc::ENXIO,
+ E2BIG = libc::E2BIG,
+ ENOEXEC = libc::ENOEXEC,
+ EBADF = libc::EBADF,
+ ECHILD = libc::ECHILD,
+ EDEADLK = libc::EDEADLK,
+ ENOMEM = libc::ENOMEM,
+ EACCES = libc::EACCES,
+ EFAULT = libc::EFAULT,
+ ENOTBLK = libc::ENOTBLK,
+ EBUSY = libc::EBUSY,
+ EEXIST = libc::EEXIST,
+ EXDEV = libc::EXDEV,
+ ENODEV = libc::ENODEV,
+ ENOTDIR = libc::ENOTDIR,
+ EISDIR = libc::EISDIR,
+ EINVAL = libc::EINVAL,
+ ENFILE = libc::ENFILE,
+ EMFILE = libc::EMFILE,
+ ENOTTY = libc::ENOTTY,
+ ETXTBSY = libc::ETXTBSY,
+ EFBIG = libc::EFBIG,
+ ENOSPC = libc::ENOSPC,
+ ESPIPE = libc::ESPIPE,
+ EROFS = libc::EROFS,
+ EMLINK = libc::EMLINK,
+ EPIPE = libc::EPIPE,
+ EDOM = libc::EDOM,
+ ERANGE = libc::ERANGE,
+ EAGAIN = libc::EAGAIN,
+ EINPROGRESS = libc::EINPROGRESS,
+ EALREADY = libc::EALREADY,
+ ENOTSOCK = libc::ENOTSOCK,
+ EDESTADDRREQ = libc::EDESTADDRREQ,
+ EMSGSIZE = libc::EMSGSIZE,
+ EPROTOTYPE = libc::EPROTOTYPE,
+ ENOPROTOOPT = libc::ENOPROTOOPT,
+ EPROTONOSUPPORT = libc::EPROTONOSUPPORT,
+ ESOCKTNOSUPPORT = libc::ESOCKTNOSUPPORT,
+ EOPNOTSUPP = libc::EOPNOTSUPP,
+ EPFNOSUPPORT = libc::EPFNOSUPPORT,
+ EAFNOSUPPORT = libc::EAFNOSUPPORT,
+ EADDRINUSE = libc::EADDRINUSE,
+ EADDRNOTAVAIL = libc::EADDRNOTAVAIL,
+ ENETDOWN = libc::ENETDOWN,
+ ENETUNREACH = libc::ENETUNREACH,
+ ENETRESET = libc::ENETRESET,
+ ECONNABORTED = libc::ECONNABORTED,
+ ECONNRESET = libc::ECONNRESET,
+ ENOBUFS = libc::ENOBUFS,
+ EISCONN = libc::EISCONN,
+ ENOTCONN = libc::ENOTCONN,
+ ESHUTDOWN = libc::ESHUTDOWN,
+ ETOOMANYREFS = libc::ETOOMANYREFS,
+ ETIMEDOUT = libc::ETIMEDOUT,
+ ECONNREFUSED = libc::ECONNREFUSED,
+ ELOOP = libc::ELOOP,
+ ENAMETOOLONG = libc::ENAMETOOLONG,
+ EHOSTDOWN = libc::EHOSTDOWN,
+ EHOSTUNREACH = libc::EHOSTUNREACH,
+ ENOTEMPTY = libc::ENOTEMPTY,
+ EUSERS = libc::EUSERS,
+ EDQUOT = libc::EDQUOT,
+ ESTALE = libc::ESTALE,
+ EREMOTE = libc::EREMOTE,
+ ENOLCK = libc::ENOLCK,
+ ENOSYS = libc::ENOSYS,
+ EIDRM = libc::EIDRM,
+ ENOMSG = libc::ENOMSG,
+ EOVERFLOW = libc::EOVERFLOW,
+ EILSEQ = libc::EILSEQ,
+ ECANCELED = libc::ECANCELED,
+ EBADMSG = libc::EBADMSG,
+ ENODATA = libc::ENODATA,
+ ENOSR = libc::ENOSR,
+ ENOSTR = libc::ENOSTR,
+ ETIME = libc::ETIME,
+ EMULTIHOP = libc::EMULTIHOP,
+ ENOLINK = libc::ENOLINK,
+ EPROTO = libc::EPROTO,
+ }
+
+ impl Errno {
+ pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
+ }
+
+ pub const fn from_i32(e: i32) -> Errno {
+ use self::Errno::*;
+
+ match e {
+ libc::EPERM => EPERM,
+ libc::ENOENT => ENOENT,
+ libc::ESRCH => ESRCH,
+ libc::EINTR => EINTR,
+ libc::EIO => EIO,
+ libc::ENXIO => ENXIO,
+ libc::E2BIG => E2BIG,
+ libc::ENOEXEC => ENOEXEC,
+ libc::EBADF => EBADF,
+ libc::ECHILD => ECHILD,
+ libc::EDEADLK => EDEADLK,
+ libc::ENOMEM => ENOMEM,
+ libc::EACCES => EACCES,
+ libc::EFAULT => EFAULT,
+ libc::ENOTBLK => ENOTBLK,
+ libc::EBUSY => EBUSY,
+ libc::EEXIST => EEXIST,
+ libc::EXDEV => EXDEV,
+ libc::ENODEV => ENODEV,
+ libc::ENOTDIR => ENOTDIR,
+ libc::EISDIR => EISDIR,
+ libc::EINVAL => EINVAL,
+ libc::ENFILE => ENFILE,
+ libc::EMFILE => EMFILE,
+ libc::ENOTTY => ENOTTY,
+ libc::ETXTBSY => ETXTBSY,
+ libc::EFBIG => EFBIG,
+ libc::ENOSPC => ENOSPC,
+ libc::ESPIPE => ESPIPE,
+ libc::EROFS => EROFS,
+ libc::EMLINK => EMLINK,
+ libc::EPIPE => EPIPE,
+ libc::EDOM => EDOM,
+ libc::ERANGE => ERANGE,
+ libc::EAGAIN => EAGAIN,
+ libc::EINPROGRESS => EINPROGRESS,
+ libc::EALREADY => EALREADY,
+ libc::ENOTSOCK => ENOTSOCK,
+ libc::EDESTADDRREQ => EDESTADDRREQ,
+ libc::EMSGSIZE => EMSGSIZE,
+ libc::EPROTOTYPE => EPROTOTYPE,
+ libc::ENOPROTOOPT => ENOPROTOOPT,
+ libc::EPROTONOSUPPORT => EPROTONOSUPPORT,
+ libc::ESOCKTNOSUPPORT => ESOCKTNOSUPPORT,
+ libc::EOPNOTSUPP => EOPNOTSUPP,
+ libc::EPFNOSUPPORT => EPFNOSUPPORT,
+ libc::EAFNOSUPPORT => EAFNOSUPPORT,
+ libc::EADDRINUSE => EADDRINUSE,
+ libc::EADDRNOTAVAIL => EADDRNOTAVAIL,
+ libc::ENETDOWN => ENETDOWN,
+ libc::ENETUNREACH => ENETUNREACH,
+ libc::ENETRESET => ENETRESET,
+ libc::ECONNABORTED => ECONNABORTED,
+ libc::ECONNRESET => ECONNRESET,
+ libc::ENOBUFS => ENOBUFS,
+ libc::EISCONN => EISCONN,
+ libc::ENOTCONN => ENOTCONN,
+ libc::ESHUTDOWN => ESHUTDOWN,
+ libc::ETOOMANYREFS => ETOOMANYREFS,
+ libc::ETIMEDOUT => ETIMEDOUT,
+ libc::ECONNREFUSED => ECONNREFUSED,
+ libc::ELOOP => ELOOP,
+ libc::ENAMETOOLONG => ENAMETOOLONG,
+ libc::EHOSTDOWN => EHOSTDOWN,
+ libc::EHOSTUNREACH => EHOSTUNREACH,
+ libc::ENOTEMPTY => ENOTEMPTY,
+ libc::EUSERS => EUSERS,
+ libc::EDQUOT => EDQUOT,
+ libc::ESTALE => ESTALE,
+ libc::EREMOTE => EREMOTE,
+ libc::ENOLCK => ENOLCK,
+ libc::ENOSYS => ENOSYS,
+ libc::EIDRM => EIDRM,
+ libc::ENOMSG => ENOMSG,
+ libc::EOVERFLOW => EOVERFLOW,
+ libc::EILSEQ => EILSEQ,
+ libc::ECANCELED => ECANCELED,
+ libc::EBADMSG => EBADMSG,
+ libc::ENODATA => ENODATA,
+ libc::ENOSR => ENOSR,
+ libc::ENOSTR => ENOSTR,
+ libc::ETIME => ETIME,
+ libc::EMULTIHOP => EMULTIHOP,
+ libc::ENOLINK => ENOLINK,
+ libc::EPROTO => EPROTO,
+ _ => UnknownErrno,
+ }
+ }
+}
+
+#[cfg(any(target_os = "illumos", target_os = "solaris"))]
+mod consts {
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ #[repr(i32)]
+ #[non_exhaustive]
+ pub enum Errno {
+ UnknownErrno = 0,
+ EPERM = libc::EPERM,
+ ENOENT = libc::ENOENT,
+ ESRCH = libc::ESRCH,
+ EINTR = libc::EINTR,
+ EIO = libc::EIO,
+ ENXIO = libc::ENXIO,
+ E2BIG = libc::E2BIG,
+ ENOEXEC = libc::ENOEXEC,
+ EBADF = libc::EBADF,
+ ECHILD = libc::ECHILD,
+ EAGAIN = libc::EAGAIN,
+ ENOMEM = libc::ENOMEM,
+ EACCES = libc::EACCES,
+ EFAULT = libc::EFAULT,
+ ENOTBLK = libc::ENOTBLK,
+ EBUSY = libc::EBUSY,
+ EEXIST = libc::EEXIST,
+ EXDEV = libc::EXDEV,
+ ENODEV = libc::ENODEV,
+ ENOTDIR = libc::ENOTDIR,
+ EISDIR = libc::EISDIR,
+ EINVAL = libc::EINVAL,
+ ENFILE = libc::ENFILE,
+ EMFILE = libc::EMFILE,
+ ENOTTY = libc::ENOTTY,
+ ETXTBSY = libc::ETXTBSY,
+ EFBIG = libc::EFBIG,
+ ENOSPC = libc::ENOSPC,
+ ESPIPE = libc::ESPIPE,
+ EROFS = libc::EROFS,
+ EMLINK = libc::EMLINK,
+ EPIPE = libc::EPIPE,
+ EDOM = libc::EDOM,
+ ERANGE = libc::ERANGE,
+ ENOMSG = libc::ENOMSG,
+ EIDRM = libc::EIDRM,
+ ECHRNG = libc::ECHRNG,
+ EL2NSYNC = libc::EL2NSYNC,
+ EL3HLT = libc::EL3HLT,
+ EL3RST = libc::EL3RST,
+ ELNRNG = libc::ELNRNG,
+ EUNATCH = libc::EUNATCH,
+ ENOCSI = libc::ENOCSI,
+ EL2HLT = libc::EL2HLT,
+ EDEADLK = libc::EDEADLK,
+ ENOLCK = libc::ENOLCK,
+ ECANCELED = libc::ECANCELED,
+ ENOTSUP = libc::ENOTSUP,
+ EDQUOT = libc::EDQUOT,
+ EBADE = libc::EBADE,
+ EBADR = libc::EBADR,
+ EXFULL = libc::EXFULL,
+ ENOANO = libc::ENOANO,
+ EBADRQC = libc::EBADRQC,
+ EBADSLT = libc::EBADSLT,
+ EDEADLOCK = libc::EDEADLOCK,
+ EBFONT = libc::EBFONT,
+ EOWNERDEAD = libc::EOWNERDEAD,
+ ENOTRECOVERABLE = libc::ENOTRECOVERABLE,
+ ENOSTR = libc::ENOSTR,
+ ENODATA = libc::ENODATA,
+ ETIME = libc::ETIME,
+ ENOSR = libc::ENOSR,
+ ENONET = libc::ENONET,
+ ENOPKG = libc::ENOPKG,
+ EREMOTE = libc::EREMOTE,
+ ENOLINK = libc::ENOLINK,
+ EADV = libc::EADV,
+ ESRMNT = libc::ESRMNT,
+ ECOMM = libc::ECOMM,
+ EPROTO = libc::EPROTO,
+ ELOCKUNMAPPED = libc::ELOCKUNMAPPED,
+ ENOTACTIVE = libc::ENOTACTIVE,
+ EMULTIHOP = libc::EMULTIHOP,
+ EBADMSG = libc::EBADMSG,
+ ENAMETOOLONG = libc::ENAMETOOLONG,
+ EOVERFLOW = libc::EOVERFLOW,
+ ENOTUNIQ = libc::ENOTUNIQ,
+ EBADFD = libc::EBADFD,
+ EREMCHG = libc::EREMCHG,
+ ELIBACC = libc::ELIBACC,
+ ELIBBAD = libc::ELIBBAD,
+ ELIBSCN = libc::ELIBSCN,
+ ELIBMAX = libc::ELIBMAX,
+ ELIBEXEC = libc::ELIBEXEC,
+ EILSEQ = libc::EILSEQ,
+ ENOSYS = libc::ENOSYS,
+ ELOOP = libc::ELOOP,
+ ERESTART = libc::ERESTART,
+ ESTRPIPE = libc::ESTRPIPE,
+ ENOTEMPTY = libc::ENOTEMPTY,
+ EUSERS = libc::EUSERS,
+ ENOTSOCK = libc::ENOTSOCK,
+ EDESTADDRREQ = libc::EDESTADDRREQ,
+ EMSGSIZE = libc::EMSGSIZE,
+ EPROTOTYPE = libc::EPROTOTYPE,
+ ENOPROTOOPT = libc::ENOPROTOOPT,
+ EPROTONOSUPPORT = libc::EPROTONOSUPPORT,
+ ESOCKTNOSUPPORT = libc::ESOCKTNOSUPPORT,
+ EOPNOTSUPP = libc::EOPNOTSUPP,
+ EPFNOSUPPORT = libc::EPFNOSUPPORT,
+ EAFNOSUPPORT = libc::EAFNOSUPPORT,
+ EADDRINUSE = libc::EADDRINUSE,
+ EADDRNOTAVAIL = libc::EADDRNOTAVAIL,
+ ENETDOWN = libc::ENETDOWN,
+ ENETUNREACH = libc::ENETUNREACH,
+ ENETRESET = libc::ENETRESET,
+ ECONNABORTED = libc::ECONNABORTED,
+ ECONNRESET = libc::ECONNRESET,
+ ENOBUFS = libc::ENOBUFS,
+ EISCONN = libc::EISCONN,
+ ENOTCONN = libc::ENOTCONN,
+ ESHUTDOWN = libc::ESHUTDOWN,
+ ETOOMANYREFS = libc::ETOOMANYREFS,
+ ETIMEDOUT = libc::ETIMEDOUT,
+ ECONNREFUSED = libc::ECONNREFUSED,
+ EHOSTDOWN = libc::EHOSTDOWN,
+ EHOSTUNREACH = libc::EHOSTUNREACH,
+ EALREADY = libc::EALREADY,
+ EINPROGRESS = libc::EINPROGRESS,
+ ESTALE = libc::ESTALE,
+ }
+
+ impl Errno {
+ pub const ELAST: Errno = Errno::ESTALE;
+ pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
+ }
+
+ pub const fn from_i32(e: i32) -> Errno {
+ use self::Errno::*;
+
+ match e {
+ libc::EPERM => EPERM,
+ libc::ENOENT => ENOENT,
+ libc::ESRCH => ESRCH,
+ libc::EINTR => EINTR,
+ libc::EIO => EIO,
+ libc::ENXIO => ENXIO,
+ libc::E2BIG => E2BIG,
+ libc::ENOEXEC => ENOEXEC,
+ libc::EBADF => EBADF,
+ libc::ECHILD => ECHILD,
+ libc::EAGAIN => EAGAIN,
+ libc::ENOMEM => ENOMEM,
+ libc::EACCES => EACCES,
+ libc::EFAULT => EFAULT,
+ libc::ENOTBLK => ENOTBLK,
+ libc::EBUSY => EBUSY,
+ libc::EEXIST => EEXIST,
+ libc::EXDEV => EXDEV,
+ libc::ENODEV => ENODEV,
+ libc::ENOTDIR => ENOTDIR,
+ libc::EISDIR => EISDIR,
+ libc::EINVAL => EINVAL,
+ libc::ENFILE => ENFILE,
+ libc::EMFILE => EMFILE,
+ libc::ENOTTY => ENOTTY,
+ libc::ETXTBSY => ETXTBSY,
+ libc::EFBIG => EFBIG,
+ libc::ENOSPC => ENOSPC,
+ libc::ESPIPE => ESPIPE,
+ libc::EROFS => EROFS,
+ libc::EMLINK => EMLINK,
+ libc::EPIPE => EPIPE,
+ libc::EDOM => EDOM,
+ libc::ERANGE => ERANGE,
+ libc::ENOMSG => ENOMSG,
+ libc::EIDRM => EIDRM,
+ libc::ECHRNG => ECHRNG,
+ libc::EL2NSYNC => EL2NSYNC,
+ libc::EL3HLT => EL3HLT,
+ libc::EL3RST => EL3RST,
+ libc::ELNRNG => ELNRNG,
+ libc::EUNATCH => EUNATCH,
+ libc::ENOCSI => ENOCSI,
+ libc::EL2HLT => EL2HLT,
+ libc::EDEADLK => EDEADLK,
+ libc::ENOLCK => ENOLCK,
+ libc::ECANCELED => ECANCELED,
+ libc::ENOTSUP => ENOTSUP,
+ libc::EDQUOT => EDQUOT,
+ libc::EBADE => EBADE,
+ libc::EBADR => EBADR,
+ libc::EXFULL => EXFULL,
+ libc::ENOANO => ENOANO,
+ libc::EBADRQC => EBADRQC,
+ libc::EBADSLT => EBADSLT,
+ libc::EDEADLOCK => EDEADLOCK,
+ libc::EBFONT => EBFONT,
+ libc::EOWNERDEAD => EOWNERDEAD,
+ libc::ENOTRECOVERABLE => ENOTRECOVERABLE,
+ libc::ENOSTR => ENOSTR,
+ libc::ENODATA => ENODATA,
+ libc::ETIME => ETIME,
+ libc::ENOSR => ENOSR,
+ libc::ENONET => ENONET,
+ libc::ENOPKG => ENOPKG,
+ libc::EREMOTE => EREMOTE,
+ libc::ENOLINK => ENOLINK,
+ libc::EADV => EADV,
+ libc::ESRMNT => ESRMNT,
+ libc::ECOMM => ECOMM,
+ libc::EPROTO => EPROTO,
+ libc::ELOCKUNMAPPED => ELOCKUNMAPPED,
+ libc::ENOTACTIVE => ENOTACTIVE,
+ libc::EMULTIHOP => EMULTIHOP,
+ libc::EBADMSG => EBADMSG,
+ libc::ENAMETOOLONG => ENAMETOOLONG,
+ libc::EOVERFLOW => EOVERFLOW,
+ libc::ENOTUNIQ => ENOTUNIQ,
+ libc::EBADFD => EBADFD,
+ libc::EREMCHG => EREMCHG,
+ libc::ELIBACC => ELIBACC,
+ libc::ELIBBAD => ELIBBAD,
+ libc::ELIBSCN => ELIBSCN,
+ libc::ELIBMAX => ELIBMAX,
+ libc::ELIBEXEC => ELIBEXEC,
+ libc::EILSEQ => EILSEQ,
+ libc::ENOSYS => ENOSYS,
+ libc::ELOOP => ELOOP,
+ libc::ERESTART => ERESTART,
+ libc::ESTRPIPE => ESTRPIPE,
+ libc::ENOTEMPTY => ENOTEMPTY,
+ libc::EUSERS => EUSERS,
+ libc::ENOTSOCK => ENOTSOCK,
+ libc::EDESTADDRREQ => EDESTADDRREQ,
+ libc::EMSGSIZE => EMSGSIZE,
+ libc::EPROTOTYPE => EPROTOTYPE,
+ libc::ENOPROTOOPT => ENOPROTOOPT,
+ libc::EPROTONOSUPPORT => EPROTONOSUPPORT,
+ libc::ESOCKTNOSUPPORT => ESOCKTNOSUPPORT,
+ libc::EOPNOTSUPP => EOPNOTSUPP,
+ libc::EPFNOSUPPORT => EPFNOSUPPORT,
+ libc::EAFNOSUPPORT => EAFNOSUPPORT,
+ libc::EADDRINUSE => EADDRINUSE,
+ libc::EADDRNOTAVAIL => EADDRNOTAVAIL,
+ libc::ENETDOWN => ENETDOWN,
+ libc::ENETUNREACH => ENETUNREACH,
+ libc::ENETRESET => ENETRESET,
+ libc::ECONNABORTED => ECONNABORTED,
+ libc::ECONNRESET => ECONNRESET,
+ libc::ENOBUFS => ENOBUFS,
+ libc::EISCONN => EISCONN,
+ libc::ENOTCONN => ENOTCONN,
+ libc::ESHUTDOWN => ESHUTDOWN,
+ libc::ETOOMANYREFS => ETOOMANYREFS,
+ libc::ETIMEDOUT => ETIMEDOUT,
+ libc::ECONNREFUSED => ECONNREFUSED,
+ libc::EHOSTDOWN => EHOSTDOWN,
+ libc::EHOSTUNREACH => EHOSTUNREACH,
+ libc::EALREADY => EALREADY,
+ libc::EINPROGRESS => EINPROGRESS,
+ libc::ESTALE => ESTALE,
+ _ => UnknownErrno,
+ }
+ }
+}
+
+#[cfg(target_os = "haiku")]
+mod consts {
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ #[repr(i32)]
+ #[non_exhaustive]
+ pub enum Errno {
+ UnknownErrno = 0,
+ EPERM = libc::EPERM,
+ ENOENT = libc::ENOENT,
+ ESRCH = libc::ESRCH,
+ EINTR = libc::EINTR,
+ EIO = libc::EIO,
+ ENXIO = libc::ENXIO,
+ E2BIG = libc::E2BIG,
+ ENOEXEC = libc::ENOEXEC,
+ EBADF = libc::EBADF,
+ ECHILD = libc::ECHILD,
+ EDEADLK = libc::EDEADLK,
+ ENOMEM = libc::ENOMEM,
+ EACCES = libc::EACCES,
+ EFAULT = libc::EFAULT,
+ EBUSY = libc::EBUSY,
+ EEXIST = libc::EEXIST,
+ EXDEV = libc::EXDEV,
+ ENODEV = libc::ENODEV,
+ ENOTDIR = libc::ENOTDIR,
+ EISDIR = libc::EISDIR,
+ EINVAL = libc::EINVAL,
+ ENFILE = libc::ENFILE,
+ EMFILE = libc::EMFILE,
+ ENOTTY = libc::ENOTTY,
+ ETXTBSY = libc::ETXTBSY,
+ EFBIG = libc::EFBIG,
+ ENOSPC = libc::ENOSPC,
+ ESPIPE = libc::ESPIPE,
+ EROFS = libc::EROFS,
+ EMLINK = libc::EMLINK,
+ EPIPE = libc::EPIPE,
+ EDOM = libc::EDOM,
+ ERANGE = libc::ERANGE,
+ EAGAIN = libc::EAGAIN,
+ EINPROGRESS = libc::EINPROGRESS,
+ EALREADY = libc::EALREADY,
+ ENOTSOCK = libc::ENOTSOCK,
+ EDESTADDRREQ = libc::EDESTADDRREQ,
+ EMSGSIZE = libc::EMSGSIZE,
+ EPROTOTYPE = libc::EPROTOTYPE,
+ ENOPROTOOPT = libc::ENOPROTOOPT,
+ EPROTONOSUPPORT = libc::EPROTONOSUPPORT,
+ ENOTSUP = libc::ENOTSUP,
+ EADDRINUSE = libc::EADDRINUSE,
+ EADDRNOTAVAIL = libc::EADDRNOTAVAIL,
+ ENETDOWN = libc::ENETDOWN,
+ ENETUNREACH = libc::ENETUNREACH,
+ ENETRESET = libc::ENETRESET,
+ ECONNABORTED = libc::ECONNABORTED,
+ ECONNRESET = libc::ECONNRESET,
+ ENOBUFS = libc::ENOBUFS,
+ EISCONN = libc::EISCONN,
+ ENOTCONN = libc::ENOTCONN,
+ ESHUTDOWN = libc::ESHUTDOWN,
+ ETIMEDOUT = libc::ETIMEDOUT,
+ ECONNREFUSED = libc::ECONNREFUSED,
+ ELOOP = libc::ELOOP,
+ ENAMETOOLONG = libc::ENAMETOOLONG,
+ EHOSTDOWN = libc::EHOSTDOWN,
+ EHOSTUNREACH = libc::EHOSTUNREACH,
+ ENOTEMPTY = libc::ENOTEMPTY,
+ EDQUOT = libc::EDQUOT,
+ ESTALE = libc::ESTALE,
+ ENOLCK = libc::ENOLCK,
+ ENOSYS = libc::ENOSYS,
+ EIDRM = libc::EIDRM,
+ ENOMSG = libc::ENOMSG,
+ EOVERFLOW = libc::EOVERFLOW,
+ ECANCELED = libc::ECANCELED,
+ EILSEQ = libc::EILSEQ,
+ ENOATTR = libc::ENOATTR,
+ EBADMSG = libc::EBADMSG,
+ EMULTIHOP = libc::EMULTIHOP,
+ ENOLINK = libc::ENOLINK,
+ EPROTO = libc::EPROTO,
+ }
+
+ impl Errno {
+ pub const EWOULDBLOCK: Errno = Errno::EAGAIN;
+ pub const EDEADLOCK: Errno = Errno::EDEADLK;
+ pub const EOPNOTSUPP: Errno = Errno::ENOTSUP;
+ }
+
+ pub const fn from_i32(e: i32) -> Errno {
+ use self::Errno::*;
+
+ match e {
+ libc::EPERM => EPERM,
+ libc::ENOENT => ENOENT,
+ libc::ESRCH => ESRCH,
+ libc::EINTR => EINTR,
+ libc::EIO => EIO,
+ libc::ENXIO => ENXIO,
+ libc::E2BIG => E2BIG,
+ libc::ENOEXEC => ENOEXEC,
+ libc::EBADF => EBADF,
+ libc::ECHILD => ECHILD,
+ libc::EDEADLK => EDEADLK,
+ libc::ENOMEM => ENOMEM,
+ libc::EACCES => EACCES,
+ libc::EFAULT => EFAULT,
+ libc::EBUSY => EBUSY,
+ libc::EEXIST => EEXIST,
+ libc::EXDEV => EXDEV,
+ libc::ENODEV => ENODEV,
+ libc::ENOTDIR => ENOTDIR,
+ libc::EISDIR => EISDIR,
+ libc::EINVAL => EINVAL,
+ libc::ENFILE => ENFILE,
+ libc::EMFILE => EMFILE,
+ libc::ENOTTY => ENOTTY,
+ libc::ETXTBSY => ETXTBSY,
+ libc::EFBIG => EFBIG,
+ libc::ENOSPC => ENOSPC,
+ libc::ESPIPE => ESPIPE,
+ libc::EROFS => EROFS,
+ libc::EMLINK => EMLINK,
+ libc::EPIPE => EPIPE,
+ libc::EDOM => EDOM,
+ libc::ERANGE => ERANGE,
+ libc::EAGAIN => EAGAIN,
+ libc::EINPROGRESS => EINPROGRESS,
+ libc::EALREADY => EALREADY,
+ libc::ENOTSOCK => ENOTSOCK,
+ libc::EDESTADDRREQ => EDESTADDRREQ,
+ libc::EMSGSIZE => EMSGSIZE,
+ libc::EPROTOTYPE => EPROTOTYPE,
+ libc::ENOPROTOOPT => ENOPROTOOPT,
+ libc::EPROTONOSUPPORT => EPROTONOSUPPORT,
+ libc::ENOTSUP => ENOTSUP,
+ libc::EADDRINUSE => EADDRINUSE,
+ libc::EADDRNOTAVAIL => EADDRNOTAVAIL,
+ libc::ENETDOWN => ENETDOWN,
+ libc::ENETUNREACH => ENETUNREACH,
+ libc::ENETRESET => ENETRESET,
+ libc::ECONNABORTED => ECONNABORTED,
+ libc::ECONNRESET => ECONNRESET,
+ libc::ENOBUFS => ENOBUFS,
+ libc::EISCONN => EISCONN,
+ libc::ENOTCONN => ENOTCONN,
+ libc::ESHUTDOWN => ESHUTDOWN,
+ libc::ETIMEDOUT => ETIMEDOUT,
+ libc::ECONNREFUSED => ECONNREFUSED,
+ libc::ELOOP => ELOOP,
+ libc::ENAMETOOLONG => ENAMETOOLONG,
+ libc::EHOSTDOWN => EHOSTDOWN,
+ libc::EHOSTUNREACH => EHOSTUNREACH,
+ libc::ENOTEMPTY => ENOTEMPTY,
+ libc::EDQUOT => EDQUOT,
+ libc::ESTALE => ESTALE,
+ libc::ENOLCK => ENOLCK,
+ libc::ENOSYS => ENOSYS,
+ libc::EIDRM => EIDRM,
+ libc::ENOMSG => ENOMSG,
+ libc::EOVERFLOW => EOVERFLOW,
+ libc::ECANCELED => ECANCELED,
+ libc::EILSEQ => EILSEQ,
+ libc::ENOATTR => ENOATTR,
+ libc::EBADMSG => EBADMSG,
+ libc::EMULTIHOP => EMULTIHOP,
+ libc::ENOLINK => ENOLINK,
+ libc::EPROTO => EPROTO,
+ _ => UnknownErrno,
+ }
+ }
+}
diff --git a/third_party/rust/nix/src/fcntl.rs b/third_party/rust/nix/src/fcntl.rs
new file mode 100644
index 0000000000..650828345b
--- /dev/null
+++ b/third_party/rust/nix/src/fcntl.rs
@@ -0,0 +1,882 @@
+use crate::errno::Errno;
+use libc::{self, c_char, c_int, c_uint, size_t, ssize_t};
+use std::ffi::OsString;
+#[cfg(not(target_os = "redox"))]
+use std::os::raw;
+use std::os::unix::ffi::OsStringExt;
+use std::os::unix::io::RawFd;
+
+#[cfg(feature = "fs")]
+use crate::{sys::stat::Mode, NixPath, Result};
+#[cfg(any(target_os = "android", target_os = "linux"))]
+use std::ptr; // For splice and copy_file_range
+
+#[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "wasi",
+ target_env = "uclibc",
+ target_os = "freebsd"
+))]
+#[cfg(feature = "fs")]
+pub use self::posix_fadvise::{posix_fadvise, PosixFadviseAdvice};
+
+#[cfg(not(target_os = "redox"))]
+#[cfg(any(feature = "fs", feature = "process"))]
+libc_bitflags! {
+ #[cfg_attr(docsrs, doc(cfg(any(feature = "fs", feature = "process"))))]
+ pub struct AtFlags: c_int {
+ AT_REMOVEDIR;
+ AT_SYMLINK_FOLLOW;
+ AT_SYMLINK_NOFOLLOW;
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ AT_NO_AUTOMOUNT;
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ AT_EMPTY_PATH;
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ AT_EACCESS;
+ }
+}
+
+#[cfg(any(feature = "fs", feature = "term"))]
+libc_bitflags!(
+ /// Configuration options for opened files.
+ #[cfg_attr(docsrs, doc(cfg(any(feature = "fs", feature = "term"))))]
+ pub struct OFlag: c_int {
+ /// Mask for the access mode of the file.
+ O_ACCMODE;
+ /// Use alternate I/O semantics.
+ #[cfg(target_os = "netbsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ O_ALT_IO;
+ /// Open the file in append-only mode.
+ O_APPEND;
+ /// Generate a signal when input or output becomes possible.
+ #[cfg(not(any(target_os = "illumos", target_os = "solaris", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ O_ASYNC;
+ /// Closes the file descriptor once an `execve` call is made.
+ ///
+ /// Also sets the file offset to the beginning of the file.
+ O_CLOEXEC;
+ /// Create the file if it does not exist.
+ O_CREAT;
+ /// Try to minimize cache effects of the I/O for this file.
+ #[cfg(any(target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "netbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ O_DIRECT;
+ /// If the specified path isn't a directory, fail.
+ #[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ O_DIRECTORY;
+ /// Implicitly follow each `write()` with an `fdatasync()`.
+ #[cfg(any(target_os = "android",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ O_DSYNC;
+ /// Error out if a file was not created.
+ O_EXCL;
+ /// Open for execute only.
+ #[cfg(target_os = "freebsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ O_EXEC;
+ /// Open with an exclusive file lock.
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ O_EXLOCK;
+ /// Same as `O_SYNC`.
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ all(target_os = "linux", not(target_env = "musl")),
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ O_FSYNC;
+ /// Allow files whose sizes can't be represented in an `off_t` to be opened.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ O_LARGEFILE;
+ /// Do not update the file last access time during `read(2)`s.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ O_NOATIME;
+ /// Don't attach the device as the process' controlling terminal.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ O_NOCTTY;
+ /// Same as `O_NONBLOCK`.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ O_NDELAY;
+ /// `open()` will fail if the given path is a symbolic link.
+ O_NOFOLLOW;
+ /// When possible, open the file in nonblocking mode.
+ O_NONBLOCK;
+ /// Don't deliver `SIGPIPE`.
+ #[cfg(target_os = "netbsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ O_NOSIGPIPE;
+ /// Obtain a file descriptor for low-level access.
+ ///
+ /// The file itself is not opened and other file operations will fail.
+ #[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ O_PATH;
+ /// Only allow reading.
+ ///
+ /// This should not be combined with `O_WRONLY` or `O_RDWR`.
+ O_RDONLY;
+ /// Allow both reading and writing.
+ ///
+ /// This should not be combined with `O_WRONLY` or `O_RDONLY`.
+ O_RDWR;
+ /// Similar to `O_DSYNC` but applies to `read`s instead.
+ #[cfg(any(target_os = "linux", target_os = "netbsd", target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ O_RSYNC;
+ /// Skip search permission checks.
+ #[cfg(target_os = "netbsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ O_SEARCH;
+ /// Open with a shared file lock.
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ O_SHLOCK;
+ /// Implicitly follow each `write()` with an `fsync()`.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ O_SYNC;
+ /// Create an unnamed temporary file.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ O_TMPFILE;
+ /// Truncate an existing regular file to 0 length if it allows writing.
+ O_TRUNC;
+ /// Restore default TTY attributes.
+ #[cfg(target_os = "freebsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ O_TTY_INIT;
+ /// Only allow writing.
+ ///
+ /// This should not be combined with `O_RDONLY` or `O_RDWR`.
+ O_WRONLY;
+ }
+);
+
+feature! {
+#![feature = "fs"]
+
+// The conversion is not identical on all operating systems.
+#[allow(clippy::useless_conversion)]
+pub fn open<P: ?Sized + NixPath>(path: &P, oflag: OFlag, mode: Mode) -> Result<RawFd> {
+ let fd = path.with_nix_path(|cstr| {
+ unsafe { libc::open(cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) }
+ })?;
+
+ Errno::result(fd)
+}
+
+// The conversion is not identical on all operating systems.
+#[allow(clippy::useless_conversion)]
+#[cfg(not(target_os = "redox"))]
+pub fn openat<P: ?Sized + NixPath>(
+ dirfd: RawFd,
+ path: &P,
+ oflag: OFlag,
+ mode: Mode,
+) -> Result<RawFd> {
+ let fd = path.with_nix_path(|cstr| {
+ unsafe { libc::openat(dirfd, cstr.as_ptr(), oflag.bits(), mode.bits() as c_uint) }
+ })?;
+ Errno::result(fd)
+}
+
+#[cfg(not(target_os = "redox"))]
+pub fn renameat<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
+ old_dirfd: Option<RawFd>,
+ old_path: &P1,
+ new_dirfd: Option<RawFd>,
+ new_path: &P2,
+) -> Result<()> {
+ let res = old_path.with_nix_path(|old_cstr| {
+ new_path.with_nix_path(|new_cstr| unsafe {
+ libc::renameat(
+ at_rawfd(old_dirfd),
+ old_cstr.as_ptr(),
+ at_rawfd(new_dirfd),
+ new_cstr.as_ptr(),
+ )
+ })
+ })??;
+ Errno::result(res).map(drop)
+}
+}
+
+#[cfg(all(target_os = "linux", target_env = "gnu",))]
+#[cfg(feature = "fs")]
+libc_bitflags! {
+ #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
+ pub struct RenameFlags: u32 {
+ RENAME_EXCHANGE;
+ RENAME_NOREPLACE;
+ RENAME_WHITEOUT;
+ }
+}
+
+feature! {
+#![feature = "fs"]
+#[cfg(all(
+ target_os = "linux",
+ target_env = "gnu",
+))]
+pub fn renameat2<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
+ old_dirfd: Option<RawFd>,
+ old_path: &P1,
+ new_dirfd: Option<RawFd>,
+ new_path: &P2,
+ flags: RenameFlags,
+) -> Result<()> {
+ let res = old_path.with_nix_path(|old_cstr| {
+ new_path.with_nix_path(|new_cstr| unsafe {
+ libc::renameat2(
+ at_rawfd(old_dirfd),
+ old_cstr.as_ptr(),
+ at_rawfd(new_dirfd),
+ new_cstr.as_ptr(),
+ flags.bits(),
+ )
+ })
+ })??;
+ Errno::result(res).map(drop)
+}
+
+fn wrap_readlink_result(mut v: Vec<u8>, len: ssize_t) -> Result<OsString> {
+ unsafe { v.set_len(len as usize) }
+ v.shrink_to_fit();
+ Ok(OsString::from_vec(v.to_vec()))
+}
+
+fn readlink_maybe_at<P: ?Sized + NixPath>(
+ dirfd: Option<RawFd>,
+ path: &P,
+ v: &mut Vec<u8>,
+) -> Result<libc::ssize_t> {
+ path.with_nix_path(|cstr| unsafe {
+ match dirfd {
+ #[cfg(target_os = "redox")]
+ Some(_) => unreachable!(),
+ #[cfg(not(target_os = "redox"))]
+ Some(dirfd) => libc::readlinkat(
+ dirfd,
+ cstr.as_ptr(),
+ v.as_mut_ptr() as *mut c_char,
+ v.capacity() as size_t,
+ ),
+ None => libc::readlink(
+ cstr.as_ptr(),
+ v.as_mut_ptr() as *mut c_char,
+ v.capacity() as size_t,
+ ),
+ }
+ })
+}
+
+fn inner_readlink<P: ?Sized + NixPath>(dirfd: Option<RawFd>, path: &P) -> Result<OsString> {
+ let mut v = Vec::with_capacity(libc::PATH_MAX as usize);
+ // simple case: result is strictly less than `PATH_MAX`
+ let res = readlink_maybe_at(dirfd, path, &mut v)?;
+ let len = Errno::result(res)?;
+ debug_assert!(len >= 0);
+ if (len as usize) < v.capacity() {
+ return wrap_readlink_result(v, res);
+ }
+ // Uh oh, the result is too long...
+ // Let's try to ask lstat how many bytes to allocate.
+ let reported_size = match dirfd {
+ #[cfg(target_os = "redox")]
+ Some(_) => unreachable!(),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ Some(dirfd) => {
+ let flags = if path.is_empty() { AtFlags::AT_EMPTY_PATH } else { AtFlags::empty() };
+ super::sys::stat::fstatat(dirfd, path, flags | AtFlags::AT_SYMLINK_NOFOLLOW)
+ },
+ #[cfg(not(any(target_os = "android", target_os = "linux", target_os = "redox")))]
+ Some(dirfd) => super::sys::stat::fstatat(dirfd, path, AtFlags::AT_SYMLINK_NOFOLLOW),
+ None => super::sys::stat::lstat(path)
+ }
+ .map(|x| x.st_size)
+ .unwrap_or(0);
+ let mut try_size = if reported_size > 0 {
+ // Note: even if `lstat`'s apparently valid answer turns out to be
+ // wrong, we will still read the full symlink no matter what.
+ reported_size as usize + 1
+ } else {
+ // If lstat doesn't cooperate, or reports an error, be a little less
+ // precise.
+ (libc::PATH_MAX as usize).max(128) << 1
+ };
+ loop {
+ v.reserve_exact(try_size);
+ let res = readlink_maybe_at(dirfd, path, &mut v)?;
+ let len = Errno::result(res)?;
+ debug_assert!(len >= 0);
+ if (len as usize) < v.capacity() {
+ break wrap_readlink_result(v, res);
+ } else {
+ // Ugh! Still not big enough!
+ match try_size.checked_shl(1) {
+ Some(next_size) => try_size = next_size,
+ // It's absurd that this would happen, but handle it sanely
+ // anyway.
+ None => break Err(Errno::ENAMETOOLONG),
+ }
+ }
+ }
+}
+
+pub fn readlink<P: ?Sized + NixPath>(path: &P) -> Result<OsString> {
+ inner_readlink(None, path)
+}
+
+#[cfg(not(target_os = "redox"))]
+pub fn readlinkat<P: ?Sized + NixPath>(dirfd: RawFd, path: &P) -> Result<OsString> {
+ inner_readlink(Some(dirfd), path)
+}
+
+/// Computes the raw fd consumed by a function of the form `*at`.
+#[cfg(not(target_os = "redox"))]
+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", target_os = "freebsd"))]
+#[cfg(feature = "fs")]
+libc_bitflags!(
+ /// Additional flags for file sealing, which allows for limiting operations on a file.
+ #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
+ pub struct SealFlag: c_int {
+ /// Prevents further calls to `fcntl()` with `F_ADD_SEALS`.
+ F_SEAL_SEAL;
+ /// The file cannot be reduced in size.
+ F_SEAL_SHRINK;
+ /// The size of the file cannot be increased.
+ F_SEAL_GROW;
+ /// The file contents cannot be modified.
+ F_SEAL_WRITE;
+ }
+);
+
+#[cfg(feature = "fs")]
+libc_bitflags!(
+ /// Additional configuration flags for `fcntl`'s `F_SETFD`.
+ #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
+ pub struct FdFlag: c_int {
+ /// The file descriptor will automatically be closed during a successful `execve(2)`.
+ FD_CLOEXEC;
+ }
+);
+
+feature! {
+#![feature = "fs"]
+
+#[cfg(not(target_os = "redox"))]
+#[derive(Debug, Eq, Hash, PartialEq)]
+#[non_exhaustive]
+pub enum FcntlArg<'a> {
+ F_DUPFD(RawFd),
+ F_DUPFD_CLOEXEC(RawFd),
+ F_GETFD,
+ F_SETFD(FdFlag), // FD_FLAGS
+ F_GETFL,
+ F_SETFL(OFlag), // O_NONBLOCK
+ F_SETLK(&'a libc::flock),
+ F_SETLKW(&'a libc::flock),
+ F_GETLK(&'a mut libc::flock),
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ F_OFD_SETLK(&'a libc::flock),
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ F_OFD_SETLKW(&'a libc::flock),
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ F_OFD_GETLK(&'a mut libc::flock),
+ #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
+ F_ADD_SEALS(SealFlag),
+ #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
+ F_GET_SEALS,
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ F_FULLFSYNC,
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ F_GETPIPE_SZ,
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ F_SETPIPE_SZ(c_int),
+ // TODO: Rest of flags
+}
+
+#[cfg(target_os = "redox")]
+#[derive(Debug, Clone, Copy, Eq, Hash, PartialEq)]
+#[non_exhaustive]
+pub enum FcntlArg {
+ F_DUPFD(RawFd),
+ F_DUPFD_CLOEXEC(RawFd),
+ F_GETFD,
+ F_SETFD(FdFlag), // FD_FLAGS
+ F_GETFL,
+ F_SETFL(OFlag), // O_NONBLOCK
+}
+pub use self::FcntlArg::*;
+
+// TODO: Figure out how to handle value fcntl returns
+pub fn fcntl(fd: RawFd, arg: FcntlArg) -> Result<c_int> {
+ let res = unsafe {
+ match arg {
+ F_DUPFD(rawfd) => libc::fcntl(fd, libc::F_DUPFD, rawfd),
+ F_DUPFD_CLOEXEC(rawfd) => libc::fcntl(fd, libc::F_DUPFD_CLOEXEC, rawfd),
+ F_GETFD => libc::fcntl(fd, libc::F_GETFD),
+ F_SETFD(flag) => libc::fcntl(fd, libc::F_SETFD, flag.bits()),
+ F_GETFL => libc::fcntl(fd, libc::F_GETFL),
+ F_SETFL(flag) => libc::fcntl(fd, libc::F_SETFL, flag.bits()),
+ #[cfg(not(target_os = "redox"))]
+ F_SETLK(flock) => libc::fcntl(fd, libc::F_SETLK, flock),
+ #[cfg(not(target_os = "redox"))]
+ F_SETLKW(flock) => libc::fcntl(fd, libc::F_SETLKW, flock),
+ #[cfg(not(target_os = "redox"))]
+ F_GETLK(flock) => libc::fcntl(fd, libc::F_GETLK, flock),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ F_OFD_SETLK(flock) => libc::fcntl(fd, libc::F_OFD_SETLK, flock),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ F_OFD_SETLKW(flock) => libc::fcntl(fd, libc::F_OFD_SETLKW, flock),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ F_OFD_GETLK(flock) => libc::fcntl(fd, libc::F_OFD_GETLK, flock),
+ #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
+ F_ADD_SEALS(flag) => libc::fcntl(fd, libc::F_ADD_SEALS, flag.bits()),
+ #[cfg(any(target_os = "android", target_os = "linux", target_os = "freebsd"))]
+ F_GET_SEALS => libc::fcntl(fd, libc::F_GET_SEALS),
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ F_FULLFSYNC => libc::fcntl(fd, libc::F_FULLFSYNC),
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ F_GETPIPE_SZ => libc::fcntl(fd, libc::F_GETPIPE_SZ),
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ F_SETPIPE_SZ(size) => libc::fcntl(fd, libc::F_SETPIPE_SZ, size),
+ }
+ };
+
+ Errno::result(res)
+}
+
+// TODO: convert to libc_enum
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[non_exhaustive]
+pub enum FlockArg {
+ LockShared,
+ LockExclusive,
+ Unlock,
+ LockSharedNonblock,
+ LockExclusiveNonblock,
+ UnlockNonblock,
+}
+
+#[cfg(not(target_os = "redox"))]
+pub fn flock(fd: RawFd, arg: FlockArg) -> Result<()> {
+ use self::FlockArg::*;
+
+ let res = unsafe {
+ match arg {
+ LockShared => libc::flock(fd, libc::LOCK_SH),
+ LockExclusive => libc::flock(fd, libc::LOCK_EX),
+ Unlock => libc::flock(fd, libc::LOCK_UN),
+ LockSharedNonblock => libc::flock(fd, libc::LOCK_SH | libc::LOCK_NB),
+ LockExclusiveNonblock => libc::flock(fd, libc::LOCK_EX | libc::LOCK_NB),
+ UnlockNonblock => libc::flock(fd, libc::LOCK_UN | libc::LOCK_NB),
+ }
+ };
+
+ Errno::result(res).map(drop)
+}
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg(feature = "zerocopy")]
+libc_bitflags! {
+ /// Additional flags to `splice` and friends.
+ #[cfg_attr(docsrs, doc(cfg(feature = "zerocopy")))]
+ pub struct SpliceFFlags: c_uint {
+ /// Request that pages be moved instead of copied.
+ ///
+ /// Not applicable to `vmsplice`.
+ SPLICE_F_MOVE;
+ /// Do not block on I/O.
+ SPLICE_F_NONBLOCK;
+ /// Hint that more data will be coming in a subsequent splice.
+ ///
+ /// Not applicable to `vmsplice`.
+ SPLICE_F_MORE;
+ /// Gift the user pages to the kernel.
+ ///
+ /// Not applicable to `splice`.
+ SPLICE_F_GIFT;
+ }
+}
+
+feature! {
+#![feature = "zerocopy"]
+
+/// Copy a range of data from one file to another
+///
+/// The `copy_file_range` system call performs an in-kernel copy between
+/// file descriptors `fd_in` and `fd_out` without the additional cost of
+/// transferring data from the kernel to user space and then back into the
+/// kernel. It copies up to `len` bytes of data from file descriptor `fd_in` to
+/// file descriptor `fd_out`, overwriting any data that exists within the
+/// requested range of the target file.
+///
+/// If the `off_in` and/or `off_out` arguments are used, the values
+/// will be mutated to reflect the new position within the file after
+/// copying. If they are not used, the relevant filedescriptors will be seeked
+/// to the new position.
+///
+/// On successful completion the number of bytes actually copied will be
+/// returned.
+#[cfg(any(target_os = "android", target_os = "linux"))]
+pub fn copy_file_range(
+ fd_in: RawFd,
+ off_in: Option<&mut libc::loff_t>,
+ fd_out: RawFd,
+ off_out: Option<&mut libc::loff_t>,
+ len: usize,
+) -> Result<usize> {
+ let off_in = off_in
+ .map(|offset| offset as *mut libc::loff_t)
+ .unwrap_or(ptr::null_mut());
+ let off_out = off_out
+ .map(|offset| offset as *mut libc::loff_t)
+ .unwrap_or(ptr::null_mut());
+
+ let ret = unsafe {
+ libc::syscall(
+ libc::SYS_copy_file_range,
+ fd_in,
+ off_in,
+ fd_out,
+ off_out,
+ len,
+ 0,
+ )
+ };
+ Errno::result(ret).map(|r| r as usize)
+}
+
+#[cfg(any(target_os = "linux", target_os = "android"))]
+pub fn splice(
+ fd_in: RawFd,
+ off_in: Option<&mut libc::loff_t>,
+ fd_out: RawFd,
+ off_out: Option<&mut libc::loff_t>,
+ len: usize,
+ flags: SpliceFFlags,
+) -> Result<usize> {
+ let off_in = off_in
+ .map(|offset| offset as *mut libc::loff_t)
+ .unwrap_or(ptr::null_mut());
+ let off_out = off_out
+ .map(|offset| offset as *mut libc::loff_t)
+ .unwrap_or(ptr::null_mut());
+
+ let ret = unsafe { libc::splice(fd_in, off_in, fd_out, off_out, len, flags.bits()) };
+ Errno::result(ret).map(|r| r as usize)
+}
+
+#[cfg(any(target_os = "linux", target_os = "android"))]
+pub fn tee(fd_in: RawFd, fd_out: RawFd, len: usize, flags: SpliceFFlags) -> Result<usize> {
+ let ret = unsafe { libc::tee(fd_in, fd_out, len, flags.bits()) };
+ Errno::result(ret).map(|r| r as usize)
+}
+
+#[cfg(any(target_os = "linux", target_os = "android"))]
+pub fn vmsplice(
+ fd: RawFd,
+ iov: &[std::io::IoSlice<'_>],
+ flags: SpliceFFlags
+ ) -> Result<usize>
+{
+ let ret = unsafe {
+ libc::vmsplice(
+ fd,
+ iov.as_ptr() as *const libc::iovec,
+ iov.len(),
+ flags.bits(),
+ )
+ };
+ Errno::result(ret).map(|r| r as usize)
+}
+}
+
+#[cfg(any(target_os = "linux"))]
+#[cfg(feature = "fs")]
+libc_bitflags!(
+ /// Mode argument flags for fallocate determining operation performed on a given range.
+ #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
+ pub struct FallocateFlags: c_int {
+ /// File size is not changed.
+ ///
+ /// offset + len can be greater than file size.
+ FALLOC_FL_KEEP_SIZE;
+ /// Deallocates space by creating a hole.
+ ///
+ /// Must be ORed with FALLOC_FL_KEEP_SIZE. Byte range starts at offset and continues for len bytes.
+ FALLOC_FL_PUNCH_HOLE;
+ /// Removes byte range from a file without leaving a hole.
+ ///
+ /// Byte range to collapse starts at offset and continues for len bytes.
+ FALLOC_FL_COLLAPSE_RANGE;
+ /// Zeroes space in specified byte range.
+ ///
+ /// Byte range starts at offset and continues for len bytes.
+ FALLOC_FL_ZERO_RANGE;
+ /// Increases file space by inserting a hole within the file size.
+ ///
+ /// Does not overwrite existing data. Hole starts at offset and continues for len bytes.
+ FALLOC_FL_INSERT_RANGE;
+ /// Shared file data extants are made private to the file.
+ ///
+ /// Gaurantees that a subsequent write will not fail due to lack of space.
+ FALLOC_FL_UNSHARE_RANGE;
+ }
+);
+
+feature! {
+#![feature = "fs"]
+
+/// Manipulates file space.
+///
+/// Allows the caller to directly manipulate the allocated disk space for the
+/// file referred to by fd.
+#[cfg(any(target_os = "linux"))]
+#[cfg(feature = "fs")]
+pub fn fallocate(
+ fd: RawFd,
+ mode: FallocateFlags,
+ offset: libc::off_t,
+ len: libc::off_t,
+) -> Result<()> {
+ let res = unsafe { libc::fallocate(fd, mode.bits(), offset, len) };
+ Errno::result(res).map(drop)
+}
+
+/// Argument to [`fspacectl`] describing the range to zero. The first member is
+/// the file offset, and the second is the length of the region.
+#[cfg(any(target_os = "freebsd"))]
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub struct SpacectlRange(pub libc::off_t, pub libc::off_t);
+
+#[cfg(any(target_os = "freebsd"))]
+impl SpacectlRange {
+ #[inline]
+ pub fn is_empty(&self) -> bool {
+ self.1 == 0
+ }
+
+ #[inline]
+ pub fn len(&self) -> libc::off_t {
+ self.1
+ }
+
+ #[inline]
+ pub fn offset(&self) -> libc::off_t {
+ self.0
+ }
+}
+
+/// Punch holes in a file.
+///
+/// `fspacectl` instructs the file system to deallocate a portion of a file.
+/// After a successful operation, this region of the file will return all zeroes
+/// if read. If the file system supports deallocation, then it may free the
+/// underlying storage, too.
+///
+/// # Arguments
+///
+/// - `fd` - File to operate on
+/// - `range.0` - File offset at which to begin deallocation
+/// - `range.1` - Length of the region to deallocate
+///
+/// # Returns
+///
+/// The operation may deallocate less than the entire requested region. On
+/// success, it returns the region that still remains to be deallocated. The
+/// caller should loop until the returned region is empty.
+///
+/// # Example
+///
+#[cfg_attr(fbsd14, doc = " ```")]
+#[cfg_attr(not(fbsd14), doc = " ```no_run")]
+/// # use std::io::Write;
+/// # use std::os::unix::fs::FileExt;
+/// # use std::os::unix::io::AsRawFd;
+/// # use nix::fcntl::*;
+/// # use tempfile::tempfile;
+/// const INITIAL: &[u8] = b"0123456789abcdef";
+/// let mut f = tempfile().unwrap();
+/// f.write_all(INITIAL).unwrap();
+/// let mut range = SpacectlRange(3, 6);
+/// while (!range.is_empty()) {
+/// range = fspacectl(f.as_raw_fd(), range).unwrap();
+/// }
+/// let mut buf = vec![0; INITIAL.len()];
+/// f.read_exact_at(&mut buf, 0).unwrap();
+/// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef");
+/// ```
+#[cfg(target_os = "freebsd")]
+pub fn fspacectl(fd: RawFd, range: SpacectlRange) -> Result<SpacectlRange> {
+ let mut rqsr = libc::spacectl_range{r_offset: range.0, r_len: range.1};
+ let res = unsafe { libc::fspacectl(
+ fd,
+ libc::SPACECTL_DEALLOC, // Only one command is supported ATM
+ &rqsr,
+ 0, // No flags are currently supported
+ &mut rqsr
+ )};
+ Errno::result(res).map(|_| SpacectlRange(rqsr.r_offset, rqsr.r_len))
+}
+
+/// Like [`fspacectl`], but will never return incomplete.
+///
+/// # Arguments
+///
+/// - `fd` - File to operate on
+/// - `offset` - File offset at which to begin deallocation
+/// - `len` - Length of the region to deallocate
+///
+/// # Returns
+///
+/// Returns `()` on success. On failure, the region may or may not be partially
+/// deallocated.
+///
+/// # Example
+///
+#[cfg_attr(fbsd14, doc = " ```")]
+#[cfg_attr(not(fbsd14), doc = " ```no_run")]
+/// # use std::io::Write;
+/// # use std::os::unix::fs::FileExt;
+/// # use std::os::unix::io::AsRawFd;
+/// # use nix::fcntl::*;
+/// # use tempfile::tempfile;
+/// const INITIAL: &[u8] = b"0123456789abcdef";
+/// let mut f = tempfile().unwrap();
+/// f.write_all(INITIAL).unwrap();
+/// fspacectl_all(f.as_raw_fd(), 3, 6).unwrap();
+/// let mut buf = vec![0; INITIAL.len()];
+/// f.read_exact_at(&mut buf, 0).unwrap();
+/// assert_eq!(buf, b"012\0\0\0\0\0\09abcdef");
+/// ```
+#[cfg(target_os = "freebsd")]
+pub fn fspacectl_all(fd: RawFd, offset: libc::off_t, len: libc::off_t)
+ -> Result<()>
+{
+ let mut rqsr = libc::spacectl_range{r_offset: offset, r_len: len};
+ while rqsr.r_len > 0 {
+ let res = unsafe { libc::fspacectl(
+ fd,
+ libc::SPACECTL_DEALLOC, // Only one command is supported ATM
+ &rqsr,
+ 0, // No flags are currently supported
+ &mut rqsr
+ )};
+ Errno::result(res)?;
+ }
+ Ok(())
+}
+
+#[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "wasi",
+ target_env = "uclibc",
+ target_os = "freebsd"
+))]
+mod posix_fadvise {
+ use crate::errno::Errno;
+ use std::os::unix::io::RawFd;
+ use crate::Result;
+
+ #[cfg(feature = "fs")]
+ libc_enum! {
+ #[repr(i32)]
+ #[non_exhaustive]
+ #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
+ pub enum PosixFadviseAdvice {
+ POSIX_FADV_NORMAL,
+ POSIX_FADV_SEQUENTIAL,
+ POSIX_FADV_RANDOM,
+ POSIX_FADV_NOREUSE,
+ POSIX_FADV_WILLNEED,
+ POSIX_FADV_DONTNEED,
+ }
+ }
+
+ feature! {
+ #![feature = "fs"]
+ pub fn posix_fadvise(
+ fd: RawFd,
+ offset: libc::off_t,
+ len: libc::off_t,
+ advice: PosixFadviseAdvice,
+ ) -> Result<()> {
+ let res = unsafe { libc::posix_fadvise(fd, offset, len, advice as libc::c_int) };
+
+ if res == 0 {
+ Ok(())
+ } else {
+ Err(Errno::from_i32(res))
+ }
+ }
+ }
+}
+
+#[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "wasi",
+ target_os = "freebsd"
+))]
+pub fn posix_fallocate(fd: RawFd, offset: libc::off_t, len: libc::off_t) -> Result<()> {
+ let res = unsafe { libc::posix_fallocate(fd, offset, len) };
+ match Errno::result(res) {
+ Err(err) => Err(err),
+ Ok(0) => Ok(()),
+ Ok(errno) => Err(Errno::from_i32(errno)),
+ }
+}
+}
diff --git a/third_party/rust/nix/src/features.rs b/third_party/rust/nix/src/features.rs
new file mode 100644
index 0000000000..39d17604e1
--- /dev/null
+++ b/third_party/rust/nix/src/features.rs
@@ -0,0 +1,126 @@
+//! Feature tests for OS functionality
+pub use self::os::*;
+
+#[cfg(any(target_os = "linux", target_os = "android"))]
+mod os {
+ use crate::sys::utsname::uname;
+ use crate::Result;
+ use std::os::unix::ffi::OsStrExt;
+
+ // Features:
+ // * atomic cloexec on socket: 2.6.27
+ // * pipe2: 2.6.27
+ // * accept4: 2.6.28
+
+ static VERS_UNKNOWN: usize = 1;
+ static VERS_2_6_18: usize = 2;
+ static VERS_2_6_27: usize = 3;
+ static VERS_2_6_28: usize = 4;
+ static VERS_3: usize = 5;
+
+ #[inline]
+ fn digit(dst: &mut usize, b: u8) {
+ *dst *= 10;
+ *dst += (b - b'0') as usize;
+ }
+
+ fn parse_kernel_version() -> Result<usize> {
+ let u = uname()?;
+
+ let mut curr: usize = 0;
+ let mut major: usize = 0;
+ let mut minor: usize = 0;
+ let mut patch: usize = 0;
+
+ for &b in u.release().as_bytes() {
+ if curr >= 3 {
+ break;
+ }
+
+ match b {
+ b'.' | b'-' => {
+ curr += 1;
+ }
+ b'0'..=b'9' => match curr {
+ 0 => digit(&mut major, b),
+ 1 => digit(&mut minor, b),
+ _ => digit(&mut patch, b),
+ },
+ _ => break,
+ }
+ }
+
+ Ok(if major >= 3 {
+ VERS_3
+ } else if major >= 2 {
+ if minor >= 7 {
+ VERS_UNKNOWN
+ } else if minor >= 6 {
+ if patch >= 28 {
+ VERS_2_6_28
+ } else if patch >= 27 {
+ VERS_2_6_27
+ } else {
+ VERS_2_6_18
+ }
+ } else {
+ VERS_UNKNOWN
+ }
+ } else {
+ VERS_UNKNOWN
+ })
+ }
+
+ fn kernel_version() -> Result<usize> {
+ static mut KERNEL_VERS: usize = 0;
+
+ unsafe {
+ if KERNEL_VERS == 0 {
+ KERNEL_VERS = parse_kernel_version()?;
+ }
+
+ Ok(KERNEL_VERS)
+ }
+ }
+
+ /// Check if the OS supports atomic close-on-exec for sockets
+ pub fn socket_atomic_cloexec() -> bool {
+ kernel_version()
+ .map(|version| version >= VERS_2_6_27)
+ .unwrap_or(false)
+ }
+
+ #[test]
+ pub fn test_parsing_kernel_version() {
+ assert!(kernel_version().unwrap() > 0);
+ }
+}
+
+#[cfg(any(
+ target_os = "dragonfly", // Since ???
+ target_os = "freebsd", // Since 10.0
+ target_os = "illumos", // Since ???
+ target_os = "netbsd", // Since 6.0
+ target_os = "openbsd", // Since 5.7
+ target_os = "redox", // Since 1-july-2020
+))]
+mod os {
+ /// Check if the OS supports atomic close-on-exec for sockets
+ pub const fn socket_atomic_cloexec() -> bool {
+ true
+ }
+}
+
+#[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "solaris"
+))]
+mod os {
+ /// Check if the OS supports atomic close-on-exec for sockets
+ pub const fn socket_atomic_cloexec() -> bool {
+ false
+ }
+}
diff --git a/third_party/rust/nix/src/ifaddrs.rs b/third_party/rust/nix/src/ifaddrs.rs
new file mode 100644
index 0000000000..70b50b01eb
--- /dev/null
+++ b/third_party/rust/nix/src/ifaddrs.rs
@@ -0,0 +1,213 @@
+//! Query network interface addresses
+//!
+//! Uses the Linux and/or BSD specific function `getifaddrs` to query the list
+//! of interfaces and their associated addresses.
+
+use cfg_if::cfg_if;
+#[cfg(any(target_os = "ios", target_os = "macos"))]
+use std::convert::TryFrom;
+use std::ffi;
+use std::iter::Iterator;
+use std::mem;
+use std::option::Option;
+
+use crate::net::if_::*;
+use crate::sys::socket::{SockaddrLike, SockaddrStorage};
+use crate::{Errno, Result};
+
+/// Describes a single address for an interface as returned by `getifaddrs`.
+#[derive(Clone, Debug, Eq, Hash, PartialEq)]
+pub struct InterfaceAddress {
+ /// Name of the network interface
+ pub interface_name: String,
+ /// Flags as from `SIOCGIFFLAGS` ioctl
+ pub flags: InterfaceFlags,
+ /// Network address of this interface
+ pub address: Option<SockaddrStorage>,
+ /// Netmask of this interface
+ pub netmask: Option<SockaddrStorage>,
+ /// Broadcast address of this interface, if applicable
+ pub broadcast: Option<SockaddrStorage>,
+ /// Point-to-point destination address
+ pub destination: Option<SockaddrStorage>,
+}
+
+cfg_if! {
+ if #[cfg(any(target_os = "android", target_os = "emscripten", target_os = "fuchsia", target_os = "linux"))] {
+ fn get_ifu_from_sockaddr(info: &libc::ifaddrs) -> *const libc::sockaddr {
+ info.ifa_ifu
+ }
+ } else {
+ fn get_ifu_from_sockaddr(info: &libc::ifaddrs) -> *const libc::sockaddr {
+ info.ifa_dstaddr
+ }
+ }
+}
+
+/// Workaround a bug in XNU where netmasks will always have the wrong size in
+/// the sa_len field due to the kernel ignoring trailing zeroes in the structure
+/// when setting the field. See https://github.com/nix-rust/nix/issues/1709#issuecomment-1199304470
+///
+/// To fix this, we stack-allocate a new sockaddr_storage, zero it out, and
+/// memcpy sa_len of the netmask to that new storage. Finally, we reset the
+/// ss_len field to sizeof(sockaddr_storage). This is supposedly valid as all
+/// members of the sockaddr_storage are "ok" with being zeroed out (there are
+/// no pointers).
+#[cfg(any(target_os = "ios", target_os = "macos"))]
+unsafe fn workaround_xnu_bug(info: &libc::ifaddrs) -> Option<SockaddrStorage> {
+ let src_sock = info.ifa_netmask;
+ if src_sock.is_null() {
+ return None;
+ }
+
+ let mut dst_sock = mem::MaybeUninit::<libc::sockaddr_storage>::zeroed();
+
+ // memcpy only sa_len bytes, assume the rest is zero
+ std::ptr::copy_nonoverlapping(
+ src_sock as *const u8,
+ dst_sock.as_mut_ptr() as *mut u8,
+ (*src_sock).sa_len.into(),
+ );
+
+ // Initialize ss_len to sizeof(libc::sockaddr_storage).
+ (*dst_sock.as_mut_ptr()).ss_len =
+ u8::try_from(mem::size_of::<libc::sockaddr_storage>()).unwrap();
+ let dst_sock = dst_sock.assume_init();
+
+ let dst_sock_ptr =
+ &dst_sock as *const libc::sockaddr_storage as *const libc::sockaddr;
+
+ SockaddrStorage::from_raw(dst_sock_ptr, None)
+}
+
+impl InterfaceAddress {
+ /// Create an `InterfaceAddress` from the libc struct.
+ fn from_libc_ifaddrs(info: &libc::ifaddrs) -> InterfaceAddress {
+ let ifname = unsafe { ffi::CStr::from_ptr(info.ifa_name) };
+ let address = unsafe { SockaddrStorage::from_raw(info.ifa_addr, None) };
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ let netmask = unsafe { workaround_xnu_bug(info) };
+ #[cfg(not(any(target_os = "ios", target_os = "macos")))]
+ let netmask =
+ unsafe { SockaddrStorage::from_raw(info.ifa_netmask, None) };
+ let mut addr = InterfaceAddress {
+ interface_name: ifname.to_string_lossy().to_string(),
+ flags: InterfaceFlags::from_bits_truncate(info.ifa_flags as i32),
+ address,
+ netmask,
+ broadcast: None,
+ destination: None,
+ };
+
+ let ifu = get_ifu_from_sockaddr(info);
+ if addr.flags.contains(InterfaceFlags::IFF_POINTOPOINT) {
+ addr.destination = unsafe { SockaddrStorage::from_raw(ifu, None) };
+ } else if addr.flags.contains(InterfaceFlags::IFF_BROADCAST) {
+ addr.broadcast = unsafe { SockaddrStorage::from_raw(ifu, None) };
+ }
+
+ addr
+ }
+}
+
+/// Holds the results of `getifaddrs`.
+///
+/// Use the function `getifaddrs` to create this Iterator. Note that the
+/// actual list of interfaces can be iterated once and will be freed as
+/// soon as the Iterator goes out of scope.
+#[derive(Debug, Eq, Hash, PartialEq)]
+pub struct InterfaceAddressIterator {
+ base: *mut libc::ifaddrs,
+ next: *mut libc::ifaddrs,
+}
+
+impl Drop for InterfaceAddressIterator {
+ fn drop(&mut self) {
+ unsafe { libc::freeifaddrs(self.base) };
+ }
+}
+
+impl Iterator for InterfaceAddressIterator {
+ type Item = InterfaceAddress;
+ fn next(&mut self) -> Option<<Self as Iterator>::Item> {
+ match unsafe { self.next.as_ref() } {
+ Some(ifaddr) => {
+ self.next = ifaddr.ifa_next;
+ Some(InterfaceAddress::from_libc_ifaddrs(ifaddr))
+ }
+ None => None,
+ }
+ }
+}
+
+/// Get interface addresses using libc's `getifaddrs`
+///
+/// Note that the underlying implementation differs between OSes. Only the
+/// most common address families are supported by the nix crate (due to
+/// lack of time and complexity of testing). The address family is encoded
+/// in the specific variant of `SockaddrStorage` returned for the fields
+/// `address`, `netmask`, `broadcast`, and `destination`. For any entry not
+/// supported, the returned list will contain a `None` entry.
+///
+/// # Example
+/// ```
+/// let addrs = nix::ifaddrs::getifaddrs().unwrap();
+/// for ifaddr in addrs {
+/// match ifaddr.address {
+/// Some(address) => {
+/// println!("interface {} address {}",
+/// ifaddr.interface_name, address);
+/// },
+/// None => {
+/// println!("interface {} with unsupported address family",
+/// ifaddr.interface_name);
+/// }
+/// }
+/// }
+/// ```
+pub fn getifaddrs() -> Result<InterfaceAddressIterator> {
+ let mut addrs = mem::MaybeUninit::<*mut libc::ifaddrs>::uninit();
+ unsafe {
+ Errno::result(libc::getifaddrs(addrs.as_mut_ptr())).map(|_| {
+ InterfaceAddressIterator {
+ base: addrs.assume_init(),
+ next: addrs.assume_init(),
+ }
+ })
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ // Only checks if `getifaddrs` can be invoked without panicking.
+ #[test]
+ fn test_getifaddrs() {
+ let _ = getifaddrs();
+ }
+
+ // Ensures getting the netmask works, and in particular that
+ // `workaround_xnu_bug` works properly.
+ #[test]
+ fn test_getifaddrs_netmask_correct() {
+ let addrs = getifaddrs().unwrap();
+ for iface in addrs {
+ let sock = if let Some(sock) = iface.netmask {
+ sock
+ } else {
+ continue;
+ };
+ if sock.family() == Some(crate::sys::socket::AddressFamily::Inet) {
+ let _ = sock.as_sockaddr_in().unwrap();
+ return;
+ } else if sock.family()
+ == Some(crate::sys::socket::AddressFamily::Inet6)
+ {
+ let _ = sock.as_sockaddr_in6().unwrap();
+ return;
+ }
+ }
+ panic!("No address?");
+ }
+}
diff --git a/third_party/rust/nix/src/kmod.rs b/third_party/rust/nix/src/kmod.rs
new file mode 100644
index 0000000000..1fa6c170d9
--- /dev/null
+++ b/third_party/rust/nix/src/kmod.rs
@@ -0,0 +1,128 @@
+//! Load and unload kernel modules.
+//!
+//! For more details see
+
+use std::ffi::CStr;
+use std::os::unix::io::AsRawFd;
+
+use crate::errno::Errno;
+use crate::Result;
+
+/// Loads a kernel module from a buffer.
+///
+/// It loads an ELF image into kernel space,
+/// performs any necessary symbol relocations,
+/// initializes module parameters to values provided by the caller,
+/// and then runs the module's init function.
+///
+/// This function requires `CAP_SYS_MODULE` privilege.
+///
+/// The `module_image` argument points to a buffer containing the binary image
+/// to be loaded. The buffer should contain a valid ELF image
+/// built for the running kernel.
+///
+/// The `param_values` argument is a string containing space-delimited specifications
+/// of the values for module parameters.
+/// Each of the parameter specifications has the form:
+///
+/// `name[=value[,value...]]`
+///
+/// # Example
+///
+/// ```no_run
+/// use std::fs::File;
+/// use std::io::Read;
+/// use std::ffi::CString;
+/// use nix::kmod::init_module;
+///
+/// let mut f = File::open("mykernel.ko").unwrap();
+/// let mut contents: Vec<u8> = Vec::new();
+/// f.read_to_end(&mut contents).unwrap();
+/// init_module(&mut contents, &CString::new("who=Rust when=Now,12").unwrap()).unwrap();
+/// ```
+///
+/// See [`man init_module(2)`](https://man7.org/linux/man-pages/man2/init_module.2.html) for more information.
+pub fn init_module(module_image: &[u8], param_values: &CStr) -> Result<()> {
+ let res = unsafe {
+ libc::syscall(
+ libc::SYS_init_module,
+ module_image.as_ptr(),
+ module_image.len(),
+ param_values.as_ptr(),
+ )
+ };
+
+ Errno::result(res).map(drop)
+}
+
+libc_bitflags!(
+ /// Flags used by the `finit_module` function.
+ pub struct ModuleInitFlags: libc::c_uint {
+ /// Ignore symbol version hashes.
+ MODULE_INIT_IGNORE_MODVERSIONS;
+ /// Ignore kernel version magic.
+ MODULE_INIT_IGNORE_VERMAGIC;
+ }
+);
+
+/// Loads a kernel module from a given file descriptor.
+///
+/// # Example
+///
+/// ```no_run
+/// use std::fs::File;
+/// use std::ffi::CString;
+/// use nix::kmod::{finit_module, ModuleInitFlags};
+///
+/// let f = File::open("mymod.ko").unwrap();
+/// finit_module(&f, &CString::new("").unwrap(), ModuleInitFlags::empty()).unwrap();
+/// ```
+///
+/// See [`man init_module(2)`](https://man7.org/linux/man-pages/man2/init_module.2.html) for more information.
+pub fn finit_module<T: AsRawFd>(
+ fd: &T,
+ param_values: &CStr,
+ flags: ModuleInitFlags,
+) -> Result<()> {
+ let res = unsafe {
+ libc::syscall(
+ libc::SYS_finit_module,
+ fd.as_raw_fd(),
+ param_values.as_ptr(),
+ flags.bits(),
+ )
+ };
+
+ Errno::result(res).map(drop)
+}
+
+libc_bitflags!(
+ /// Flags used by `delete_module`.
+ ///
+ /// See [`man delete_module(2)`](https://man7.org/linux/man-pages/man2/delete_module.2.html)
+ /// for a detailed description how these flags work.
+ pub struct DeleteModuleFlags: libc::c_int {
+ O_NONBLOCK;
+ O_TRUNC;
+ }
+);
+
+/// Unloads the kernel module with the given name.
+///
+/// # Example
+///
+/// ```no_run
+/// use std::ffi::CString;
+/// use nix::kmod::{delete_module, DeleteModuleFlags};
+///
+/// delete_module(&CString::new("mymod").unwrap(), DeleteModuleFlags::O_NONBLOCK).unwrap();
+/// ```
+///
+/// See [`man delete_module(2)`](https://man7.org/linux/man-pages/man2/delete_module.2.html) for more information.
+pub fn delete_module(name: &CStr, flags: DeleteModuleFlags) -> Result<()> {
+ let res = unsafe {
+ libc::syscall(libc::SYS_delete_module, name.as_ptr(), flags.bits())
+ };
+
+ Errno::result(res).map(drop)
+}
diff --git a/third_party/rust/nix/src/lib.rs b/third_party/rust/nix/src/lib.rs
new file mode 100644
index 0000000000..6b82125761
--- /dev/null
+++ b/third_party/rust/nix/src/lib.rs
@@ -0,0 +1,333 @@
+//! Rust friendly bindings to the various *nix system functions.
+//!
+//! Modules are structured according to the C header file that they would be
+//! defined in.
+//!
+//! # Features
+//!
+//! Nix uses the following Cargo features to enable optional functionality.
+//! They may be enabled in any combination.
+//! * `acct` - Process accounting
+//! * `aio` - POSIX AIO
+//! * `dir` - Stuff relating to directory iteration
+//! * `env` - Manipulate environment variables
+//! * `event` - Event-driven APIs, like `kqueue` and `epoll`
+//! * `feature` - Query characteristics of the OS at runtime
+//! * `fs` - File system functionality
+//! * `hostname` - Get and set the system's hostname
+//! * `inotify` - Linux's `inotify` file system notification API
+//! * `ioctl` - The `ioctl` syscall, and wrappers for my specific instances
+//! * `kmod` - Load and unload kernel modules
+//! * `mman` - Stuff relating to memory management
+//! * `mount` - Mount and unmount file systems
+//! * `mqueue` - POSIX message queues
+//! * `net` - Networking-related functionality
+//! * `personality` - Set the process execution domain
+//! * `poll` - APIs like `poll` and `select`
+//! * `process` - Stuff relating to running processes
+//! * `pthread` - POSIX threads
+//! * `ptrace` - Process tracing and debugging
+//! * `quota` - File system quotas
+//! * `reboot` - Reboot the system
+//! * `resource` - Process resource limits
+//! * `sched` - Manipulate process's scheduling
+//! * `socket` - Sockets, whether for networking or local use
+//! * `signal` - Send and receive signals to processes
+//! * `term` - Terminal control APIs
+//! * `time` - Query the operating system's clocks
+//! * `ucontext` - User thread context
+//! * `uio` - Vectored I/O
+//! * `user` - Stuff relating to users and groups
+//! * `zerocopy` - APIs like `sendfile` and `copy_file_range`
+#![crate_name = "nix"]
+#![cfg(unix)]
+#![cfg_attr(docsrs, doc(cfg(all())))]
+#![allow(non_camel_case_types)]
+#![cfg_attr(test, deny(warnings))]
+#![recursion_limit = "500"]
+#![deny(unused)]
+#![allow(unused_macros)]
+#![cfg_attr(not(feature = "default"), allow(unused_imports))]
+#![deny(unstable_features)]
+#![deny(missing_copy_implementations)]
+#![deny(missing_debug_implementations)]
+#![warn(missing_docs)]
+#![cfg_attr(docsrs, feature(doc_cfg))]
+#![deny(clippy::cast_ptr_alignment)]
+
+// Re-exported external crates
+pub use libc;
+
+// Private internal modules
+#[macro_use]
+mod macros;
+
+// Public crates
+#[cfg(not(target_os = "redox"))]
+feature! {
+ #![feature = "dir"]
+ pub mod dir;
+}
+feature! {
+ #![feature = "env"]
+ pub mod env;
+}
+#[allow(missing_docs)]
+pub mod errno;
+feature! {
+ #![feature = "feature"]
+
+ #[deny(missing_docs)]
+ pub mod features;
+}
+#[allow(missing_docs)]
+pub mod fcntl;
+feature! {
+ #![feature = "net"]
+
+ #[cfg(any(target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "illumos",
+ target_os = "openbsd"))]
+ #[deny(missing_docs)]
+ pub mod ifaddrs;
+ #[cfg(not(target_os = "redox"))]
+ #[deny(missing_docs)]
+ pub mod net;
+}
+#[cfg(any(target_os = "android", target_os = "linux"))]
+feature! {
+ #![feature = "kmod"]
+ #[allow(missing_docs)]
+ pub mod kmod;
+}
+feature! {
+ #![feature = "mount"]
+ pub mod mount;
+}
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "netbsd"
+))]
+feature! {
+ #![feature = "mqueue"]
+ pub mod mqueue;
+}
+feature! {
+ #![feature = "poll"]
+ pub mod poll;
+}
+#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
+feature! {
+ #![feature = "term"]
+ #[deny(missing_docs)]
+ pub mod pty;
+}
+feature! {
+ #![feature = "sched"]
+ pub mod sched;
+}
+pub mod sys;
+feature! {
+ #![feature = "time"]
+ #[allow(missing_docs)]
+ pub mod time;
+}
+// This can be implemented for other platforms as soon as libc
+// provides bindings for them.
+#[cfg(all(
+ target_os = "linux",
+ any(target_arch = "s390x", target_arch = "x86", target_arch = "x86_64")
+))]
+feature! {
+ #![feature = "ucontext"]
+ #[allow(missing_docs)]
+ pub mod ucontext;
+}
+#[allow(missing_docs)]
+pub mod unistd;
+
+use std::ffi::{CStr, CString, OsStr};
+use std::mem::MaybeUninit;
+use std::os::unix::ffi::OsStrExt;
+use std::path::{Path, PathBuf};
+use std::{ptr, result, slice};
+
+use errno::Errno;
+
+/// Nix Result Type
+pub type Result<T> = result::Result<T, Errno>;
+
+/// Nix's main error type.
+///
+/// It's a wrapper around Errno. As such, it's very interoperable with
+/// [`std::io::Error`], but it has the advantages of:
+/// * `Clone`
+/// * `Copy`
+/// * `Eq`
+/// * Small size
+/// * Represents all of the system's errnos, instead of just the most common
+/// ones.
+pub type Error = Errno;
+
+/// Common trait used to represent file system paths by many Nix functions.
+pub trait NixPath {
+ /// Is the path empty?
+ fn is_empty(&self) -> bool;
+
+ /// Length of the path in bytes
+ fn len(&self) -> usize;
+
+ /// Execute a function with this path as a `CStr`.
+ ///
+ /// Mostly used internally by Nix.
+ fn with_nix_path<T, F>(&self, f: F) -> Result<T>
+ where
+ F: FnOnce(&CStr) -> T;
+}
+
+impl NixPath for str {
+ fn is_empty(&self) -> bool {
+ NixPath::is_empty(OsStr::new(self))
+ }
+
+ fn len(&self) -> usize {
+ NixPath::len(OsStr::new(self))
+ }
+
+ fn with_nix_path<T, F>(&self, f: F) -> Result<T>
+ where
+ F: FnOnce(&CStr) -> T,
+ {
+ OsStr::new(self).with_nix_path(f)
+ }
+}
+
+impl NixPath for OsStr {
+ fn is_empty(&self) -> bool {
+ self.as_bytes().is_empty()
+ }
+
+ fn len(&self) -> usize {
+ self.as_bytes().len()
+ }
+
+ fn with_nix_path<T, F>(&self, f: F) -> Result<T>
+ where
+ F: FnOnce(&CStr) -> T,
+ {
+ self.as_bytes().with_nix_path(f)
+ }
+}
+
+impl NixPath for CStr {
+ fn is_empty(&self) -> bool {
+ self.to_bytes().is_empty()
+ }
+
+ fn len(&self) -> usize {
+ self.to_bytes().len()
+ }
+
+ fn with_nix_path<T, F>(&self, f: F) -> Result<T>
+ where
+ F: FnOnce(&CStr) -> T,
+ {
+ Ok(f(self))
+ }
+}
+
+impl NixPath for [u8] {
+ fn is_empty(&self) -> bool {
+ self.is_empty()
+ }
+
+ fn len(&self) -> usize {
+ self.len()
+ }
+
+ fn with_nix_path<T, F>(&self, f: F) -> Result<T>
+ where
+ F: FnOnce(&CStr) -> T,
+ {
+ // The real PATH_MAX is typically 4096, but it's statistically unlikely to have a path
+ // longer than ~300 bytes. See the the PR description to get stats for your own machine.
+ // https://github.com/nix-rust/nix/pull/1656
+ //
+ // By being smaller than a memory page, we also avoid the compiler inserting a probe frame:
+ // https://docs.rs/compiler_builtins/latest/compiler_builtins/probestack/index.html
+ const MAX_STACK_ALLOCATION: usize = 1024;
+
+ if self.len() >= MAX_STACK_ALLOCATION {
+ return with_nix_path_allocating(self, f);
+ }
+
+ let mut buf = MaybeUninit::<[u8; MAX_STACK_ALLOCATION]>::uninit();
+ let buf_ptr = buf.as_mut_ptr() as *mut u8;
+
+ unsafe {
+ ptr::copy_nonoverlapping(self.as_ptr(), buf_ptr, self.len());
+ buf_ptr.add(self.len()).write(0);
+ }
+
+ match CStr::from_bytes_with_nul(unsafe {
+ slice::from_raw_parts(buf_ptr, self.len() + 1)
+ }) {
+ Ok(s) => Ok(f(s)),
+ Err(_) => Err(Errno::EINVAL),
+ }
+ }
+}
+
+#[cold]
+#[inline(never)]
+fn with_nix_path_allocating<T, F>(from: &[u8], f: F) -> Result<T>
+where
+ F: FnOnce(&CStr) -> T,
+{
+ match CString::new(from) {
+ Ok(s) => Ok(f(&s)),
+ Err(_) => Err(Errno::EINVAL),
+ }
+}
+
+impl NixPath for Path {
+ fn is_empty(&self) -> bool {
+ NixPath::is_empty(self.as_os_str())
+ }
+
+ fn len(&self) -> usize {
+ NixPath::len(self.as_os_str())
+ }
+
+ fn with_nix_path<T, F>(&self, f: F) -> Result<T>
+ where
+ F: FnOnce(&CStr) -> T,
+ {
+ self.as_os_str().with_nix_path(f)
+ }
+}
+
+impl NixPath for PathBuf {
+ fn is_empty(&self) -> bool {
+ NixPath::is_empty(self.as_os_str())
+ }
+
+ fn len(&self) -> usize {
+ NixPath::len(self.as_os_str())
+ }
+
+ fn with_nix_path<T, F>(&self, f: F) -> Result<T>
+ where
+ F: FnOnce(&CStr) -> T,
+ {
+ self.as_os_str().with_nix_path(f)
+ }
+}
diff --git a/third_party/rust/nix/src/macros.rs b/third_party/rust/nix/src/macros.rs
new file mode 100644
index 0000000000..99e0de8866
--- /dev/null
+++ b/third_party/rust/nix/src/macros.rs
@@ -0,0 +1,328 @@
+// Thanks to Tokio for this macro
+macro_rules! feature {
+ (
+ #![$meta:meta]
+ $($item:item)*
+ ) => {
+ $(
+ #[cfg($meta)]
+ #[cfg_attr(docsrs, doc(cfg($meta)))]
+ $item
+ )*
+ }
+}
+
+/// The `libc_bitflags!` macro helps with a common use case of defining a public bitflags type
+/// with values from the libc crate. It is used the same way as the `bitflags!` macro, except
+/// that only the name of the flag value has to be given.
+///
+/// The `libc` crate must be in scope with the name `libc`.
+///
+/// # Example
+/// ```ignore
+/// libc_bitflags!{
+/// pub struct ProtFlags: libc::c_int {
+/// PROT_NONE;
+/// PROT_READ;
+/// /// PROT_WRITE enables write protect
+/// PROT_WRITE;
+/// PROT_EXEC;
+/// #[cfg(any(target_os = "linux", target_os = "android"))]
+/// PROT_GROWSDOWN;
+/// #[cfg(any(target_os = "linux", target_os = "android"))]
+/// PROT_GROWSUP;
+/// }
+/// }
+/// ```
+///
+/// Example with casting, due to a mistake in libc. In this example, the
+/// various flags have different types, so we cast the broken ones to the right
+/// type.
+///
+/// ```ignore
+/// libc_bitflags!{
+/// pub struct SaFlags: libc::c_ulong {
+/// SA_NOCLDSTOP as libc::c_ulong;
+/// SA_NOCLDWAIT;
+/// SA_NODEFER as libc::c_ulong;
+/// SA_ONSTACK;
+/// SA_RESETHAND as libc::c_ulong;
+/// SA_RESTART as libc::c_ulong;
+/// SA_SIGINFO;
+/// }
+/// }
+/// ```
+macro_rules! libc_bitflags {
+ (
+ $(#[$outer:meta])*
+ pub struct $BitFlags:ident: $T:ty {
+ $(
+ $(#[$inner:ident $($args:tt)*])*
+ $Flag:ident $(as $cast:ty)*;
+ )+
+ }
+ ) => {
+ ::bitflags::bitflags! {
+ $(#[$outer])*
+ pub struct $BitFlags: $T {
+ $(
+ $(#[$inner $($args)*])*
+ const $Flag = libc::$Flag $(as $cast)*;
+ )+
+ }
+ }
+ };
+}
+
+/// The `libc_enum!` macro helps with a common use case of defining an enum exclusively using
+/// values from the `libc` crate. This macro supports both `pub` and private `enum`s.
+///
+/// The `libc` crate must be in scope with the name `libc`.
+///
+/// # Example
+/// ```ignore
+/// libc_enum!{
+/// pub enum ProtFlags {
+/// PROT_NONE,
+/// PROT_READ,
+/// PROT_WRITE,
+/// PROT_EXEC,
+/// #[cfg(any(target_os = "linux", target_os = "android"))]
+/// PROT_GROWSDOWN,
+/// #[cfg(any(target_os = "linux", target_os = "android"))]
+/// PROT_GROWSUP,
+/// }
+/// }
+/// ```
+// Some targets don't use all rules.
+#[allow(unknown_lints)]
+#[allow(unused_macro_rules)]
+macro_rules! libc_enum {
+ // Exit rule.
+ (@make_enum
+ name: $BitFlags:ident,
+ {
+ $v:vis
+ attrs: [$($attrs:tt)*],
+ entries: [$($entries:tt)*],
+ }
+ ) => {
+ $($attrs)*
+ #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
+ $v enum $BitFlags {
+ $($entries)*
+ }
+ };
+
+ // Exit rule including TryFrom
+ (@make_enum
+ name: $BitFlags:ident,
+ {
+ $v:vis
+ attrs: [$($attrs:tt)*],
+ entries: [$($entries:tt)*],
+ from_type: $repr:path,
+ try_froms: [$($try_froms:tt)*]
+ }
+ ) => {
+ $($attrs)*
+ #[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
+ $v enum $BitFlags {
+ $($entries)*
+ }
+ impl ::std::convert::TryFrom<$repr> for $BitFlags {
+ type Error = $crate::Error;
+ #[allow(unused_doc_comments)]
+ fn try_from(x: $repr) -> $crate::Result<Self> {
+ match x {
+ $($try_froms)*
+ _ => Err($crate::Error::EINVAL)
+ }
+ }
+ }
+ };
+
+ // Done accumulating.
+ (@accumulate_entries
+ name: $BitFlags:ident,
+ {
+ $v:vis
+ attrs: $attrs:tt,
+ },
+ $entries:tt,
+ $try_froms:tt;
+ ) => {
+ libc_enum! {
+ @make_enum
+ name: $BitFlags,
+ {
+ $v
+ attrs: $attrs,
+ entries: $entries,
+ }
+ }
+ };
+
+ // Done accumulating and want TryFrom
+ (@accumulate_entries
+ name: $BitFlags:ident,
+ {
+ $v:vis
+ attrs: $attrs:tt,
+ from_type: $repr:path,
+ },
+ $entries:tt,
+ $try_froms:tt;
+ ) => {
+ libc_enum! {
+ @make_enum
+ name: $BitFlags,
+ {
+ $v
+ attrs: $attrs,
+ entries: $entries,
+ from_type: $repr,
+ try_froms: $try_froms
+ }
+ }
+ };
+
+ // Munch an attr.
+ (@accumulate_entries
+ name: $BitFlags:ident,
+ $prefix:tt,
+ [$($entries:tt)*],
+ [$($try_froms:tt)*];
+ #[$attr:meta] $($tail:tt)*
+ ) => {
+ libc_enum! {
+ @accumulate_entries
+ name: $BitFlags,
+ $prefix,
+ [
+ $($entries)*
+ #[$attr]
+ ],
+ [
+ $($try_froms)*
+ #[$attr]
+ ];
+ $($tail)*
+ }
+ };
+
+ // Munch last ident if not followed by a comma.
+ (@accumulate_entries
+ name: $BitFlags:ident,
+ $prefix:tt,
+ [$($entries:tt)*],
+ [$($try_froms:tt)*];
+ $entry:ident
+ ) => {
+ libc_enum! {
+ @accumulate_entries
+ name: $BitFlags,
+ $prefix,
+ [
+ $($entries)*
+ $entry = libc::$entry,
+ ],
+ [
+ $($try_froms)*
+ libc::$entry => Ok($BitFlags::$entry),
+ ];
+ }
+ };
+
+ // Munch an ident; covers terminating comma case.
+ (@accumulate_entries
+ name: $BitFlags:ident,
+ $prefix:tt,
+ [$($entries:tt)*],
+ [$($try_froms:tt)*];
+ $entry:ident,
+ $($tail:tt)*
+ ) => {
+ libc_enum! {
+ @accumulate_entries
+ name: $BitFlags,
+ $prefix,
+ [
+ $($entries)*
+ $entry = libc::$entry,
+ ],
+ [
+ $($try_froms)*
+ libc::$entry => Ok($BitFlags::$entry),
+ ];
+ $($tail)*
+ }
+ };
+
+ // Munch an ident and cast it to the given type; covers terminating comma.
+ (@accumulate_entries
+ name: $BitFlags:ident,
+ $prefix:tt,
+ [$($entries:tt)*],
+ [$($try_froms:tt)*];
+ $entry:ident as $ty:ty,
+ $($tail:tt)*
+ ) => {
+ libc_enum! {
+ @accumulate_entries
+ name: $BitFlags,
+ $prefix,
+ [
+ $($entries)*
+ $entry = libc::$entry as $ty,
+ ],
+ [
+ $($try_froms)*
+ libc::$entry as $ty => Ok($BitFlags::$entry),
+ ];
+ $($tail)*
+ }
+ };
+
+ // Entry rule.
+ (
+ $(#[$attr:meta])*
+ $v:vis enum $BitFlags:ident {
+ $($vals:tt)*
+ }
+ ) => {
+ libc_enum! {
+ @accumulate_entries
+ name: $BitFlags,
+ {
+ $v
+ attrs: [$(#[$attr])*],
+ },
+ [],
+ [];
+ $($vals)*
+ }
+ };
+
+ // Entry rule including TryFrom
+ (
+ $(#[$attr:meta])*
+ $v:vis enum $BitFlags:ident {
+ $($vals:tt)*
+ }
+ impl TryFrom<$repr:path>
+ ) => {
+ libc_enum! {
+ @accumulate_entries
+ name: $BitFlags,
+ {
+ $v
+ attrs: [$(#[$attr])*],
+ from_type: $repr,
+ },
+ [],
+ [];
+ $($vals)*
+ }
+ };
+}
diff --git a/third_party/rust/nix/src/mount/bsd.rs b/third_party/rust/nix/src/mount/bsd.rs
new file mode 100644
index 0000000000..d124f1f9ab
--- /dev/null
+++ b/third_party/rust/nix/src/mount/bsd.rs
@@ -0,0 +1,453 @@
+#[cfg(target_os = "freebsd")]
+use crate::Error;
+use crate::{Errno, NixPath, Result};
+use libc::c_int;
+#[cfg(target_os = "freebsd")]
+use libc::{c_char, c_uint, c_void};
+#[cfg(target_os = "freebsd")]
+use std::{
+ borrow::Cow,
+ ffi::{CStr, CString},
+ fmt, io,
+ marker::PhantomData,
+};
+
+libc_bitflags!(
+ /// Used with [`Nmount::nmount`].
+ pub struct MntFlags: c_int {
+ /// ACL support enabled.
+ #[cfg(any(target_os = "netbsd", target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MNT_ACLS;
+ /// All I/O to the file system should be done asynchronously.
+ MNT_ASYNC;
+ /// dir should instead be a file system ID encoded as “FSID:val0:val1”.
+ #[cfg(target_os = "freebsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MNT_BYFSID;
+ /// Force a read-write mount even if the file system appears to be
+ /// unclean.
+ MNT_FORCE;
+ /// GEOM journal support enabled.
+ #[cfg(target_os = "freebsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MNT_GJOURNAL;
+ /// MAC support for objects.
+ #[cfg(any(target_os = "macos", target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MNT_MULTILABEL;
+ /// Disable read clustering.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MNT_NOCLUSTERR;
+ /// Disable write clustering.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MNT_NOCLUSTERW;
+ /// Enable NFS version 4 ACLs.
+ #[cfg(target_os = "freebsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MNT_NFS4ACLS;
+ /// Do not update access times.
+ MNT_NOATIME;
+ /// Disallow program execution.
+ MNT_NOEXEC;
+ /// Do not honor setuid or setgid bits on files when executing them.
+ MNT_NOSUID;
+ /// Do not follow symlinks.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MNT_NOSYMFOLLOW;
+ /// Mount read-only.
+ MNT_RDONLY;
+ /// Causes the vfs subsystem to update its data structures pertaining to
+ /// the specified already mounted file system.
+ MNT_RELOAD;
+ /// Create a snapshot of the file system.
+ ///
+ /// See [mksnap_ffs(8)](https://www.freebsd.org/cgi/man.cgi?query=mksnap_ffs)
+ #[cfg(any(target_os = "macos", target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MNT_SNAPSHOT;
+ /// Using soft updates.
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MNT_SOFTDEP;
+ /// Directories with the SUID bit set chown new files to their own
+ /// owner.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MNT_SUIDDIR;
+ /// All I/O to the file system should be done synchronously.
+ MNT_SYNCHRONOUS;
+ /// Union with underlying fs.
+ #[cfg(any(
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "netbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MNT_UNION;
+ /// Indicates that the mount command is being applied to an already
+ /// mounted file system.
+ MNT_UPDATE;
+ /// Check vnode use counts.
+ #[cfg(target_os = "freebsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MNT_NONBUSY;
+ }
+);
+
+/// The Error type of [`Nmount::nmount`].
+///
+/// It wraps an [`Errno`], but also may contain an additional message returned
+/// by `nmount(2)`.
+#[cfg(target_os = "freebsd")]
+#[derive(Debug)]
+pub struct NmountError {
+ errno: Error,
+ errmsg: Option<String>,
+}
+
+#[cfg(target_os = "freebsd")]
+impl NmountError {
+ /// Returns the additional error string sometimes generated by `nmount(2)`.
+ pub fn errmsg(&self) -> Option<&str> {
+ self.errmsg.as_deref()
+ }
+
+ /// Returns the inner [`Error`]
+ pub const fn error(&self) -> Error {
+ self.errno
+ }
+
+ fn new(error: Error, errmsg: Option<&CStr>) -> Self {
+ Self {
+ errno: error,
+ errmsg: errmsg.map(CStr::to_string_lossy).map(Cow::into_owned),
+ }
+ }
+}
+
+#[cfg(target_os = "freebsd")]
+impl std::error::Error for NmountError {}
+
+#[cfg(target_os = "freebsd")]
+impl fmt::Display for NmountError {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ if let Some(errmsg) = &self.errmsg {
+ write!(f, "{:?}: {}: {}", self.errno, errmsg, self.errno.desc())
+ } else {
+ write!(f, "{:?}: {}", self.errno, self.errno.desc())
+ }
+ }
+}
+
+#[cfg(target_os = "freebsd")]
+impl From<NmountError> for io::Error {
+ fn from(err: NmountError) -> Self {
+ err.errno.into()
+ }
+}
+
+/// Result type of [`Nmount::nmount`].
+#[cfg(target_os = "freebsd")]
+pub type NmountResult = std::result::Result<(), NmountError>;
+
+/// Mount a FreeBSD file system.
+///
+/// The `nmount(2)` system call works similarly to the `mount(8)` program; it
+/// takes its options as a series of name-value pairs. Most of the values are
+/// strings, as are all of the names. The `Nmount` structure builds up an
+/// argument list and then executes the syscall.
+///
+/// # Examples
+///
+/// To mount `target` onto `mountpoint` with `nullfs`:
+/// ```
+/// # use nix::unistd::Uid;
+/// # use ::sysctl::{CtlValue, Sysctl};
+/// # let ctl = ::sysctl::Ctl::new("vfs.usermount").unwrap();
+/// # if !Uid::current().is_root() && CtlValue::Int(0) == ctl.value().unwrap() {
+/// # return;
+/// # };
+/// use nix::mount::{MntFlags, Nmount, unmount};
+/// use std::ffi::CString;
+/// use tempfile::tempdir;
+///
+/// let mountpoint = tempdir().unwrap();
+/// let target = tempdir().unwrap();
+///
+/// let fstype = CString::new("fstype").unwrap();
+/// let nullfs = CString::new("nullfs").unwrap();
+/// Nmount::new()
+/// .str_opt(&fstype, &nullfs)
+/// .str_opt_owned("fspath", mountpoint.path().to_str().unwrap())
+/// .str_opt_owned("target", target.path().to_str().unwrap())
+/// .nmount(MntFlags::empty()).unwrap();
+///
+/// unmount(mountpoint.path(), MntFlags::empty()).unwrap();
+/// ```
+///
+/// # See Also
+/// * [`nmount(2)`](https://www.freebsd.org/cgi/man.cgi?query=nmount)
+/// * [`nullfs(5)`](https://www.freebsd.org/cgi/man.cgi?query=nullfs)
+#[cfg(target_os = "freebsd")]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+#[derive(Debug, Default)]
+pub struct Nmount<'a> {
+ // n.b. notgull: In reality, this is a list that contains
+ // both mutable and immutable pointers.
+ // Be careful using this.
+ iov: Vec<libc::iovec>,
+ is_owned: Vec<bool>,
+ marker: PhantomData<&'a ()>,
+}
+
+#[cfg(target_os = "freebsd")]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+impl<'a> Nmount<'a> {
+ /// Helper function to push a slice onto the `iov` array.
+ fn push_slice(&mut self, val: &'a [u8], is_owned: bool) {
+ self.iov.push(libc::iovec {
+ iov_base: val.as_ptr() as *mut _,
+ iov_len: val.len(),
+ });
+ self.is_owned.push(is_owned);
+ }
+
+ /// Helper function to push a pointer and its length onto the `iov` array.
+ fn push_pointer_and_length(
+ &mut self,
+ val: *const u8,
+ len: usize,
+ is_owned: bool,
+ ) {
+ self.iov.push(libc::iovec {
+ iov_base: val as *mut _,
+ iov_len: len,
+ });
+ self.is_owned.push(is_owned);
+ }
+
+ /// Helper function to push a `nix` path as owned.
+ fn push_nix_path<P: ?Sized + NixPath>(&mut self, val: &P) {
+ val.with_nix_path(|s| {
+ let len = s.to_bytes_with_nul().len();
+ let ptr = s.to_owned().into_raw() as *const u8;
+
+ self.push_pointer_and_length(ptr, len, true);
+ })
+ .unwrap();
+ }
+
+ /// Add an opaque mount option.
+ ///
+ /// Some file systems take binary-valued mount options. They can be set
+ /// with this method.
+ ///
+ /// # Safety
+ ///
+ /// Unsafe because it will cause `Nmount::nmount` to dereference a raw
+ /// pointer. The user is responsible for ensuring that `val` is valid and
+ /// its lifetime outlives `self`! An easy way to do that is to give the
+ /// value a larger scope than `name`
+ ///
+ /// # Examples
+ /// ```
+ /// use libc::c_void;
+ /// use nix::mount::Nmount;
+ /// use std::ffi::CString;
+ /// use std::mem;
+ ///
+ /// // Note that flags outlives name
+ /// let mut flags: u32 = 0xdeadbeef;
+ /// let name = CString::new("flags").unwrap();
+ /// let p = &mut flags as *mut u32 as *mut c_void;
+ /// let len = mem::size_of_val(&flags);
+ /// let mut nmount = Nmount::new();
+ /// unsafe { nmount.mut_ptr_opt(&name, p, len) };
+ /// ```
+ pub unsafe fn mut_ptr_opt(
+ &mut self,
+ name: &'a CStr,
+ val: *mut c_void,
+ len: usize,
+ ) -> &mut Self {
+ self.push_slice(name.to_bytes_with_nul(), false);
+ self.push_pointer_and_length(val.cast(), len, false);
+ self
+ }
+
+ /// Add a mount option that does not take a value.
+ ///
+ /// # Examples
+ /// ```
+ /// use nix::mount::Nmount;
+ /// use std::ffi::CString;
+ ///
+ /// let read_only = CString::new("ro").unwrap();
+ /// Nmount::new()
+ /// .null_opt(&read_only);
+ /// ```
+ pub fn null_opt(&mut self, name: &'a CStr) -> &mut Self {
+ self.push_slice(name.to_bytes_with_nul(), false);
+ self.push_slice(&[], false);
+ self
+ }
+
+ /// Add a mount option that does not take a value, but whose name must be
+ /// owned.
+ ///
+ ///
+ /// This has higher runtime cost than [`Nmount::null_opt`], but is useful
+ /// when the name's lifetime doesn't outlive the `Nmount`, or it's a
+ /// different string type than `CStr`.
+ ///
+ /// # Examples
+ /// ```
+ /// use nix::mount::Nmount;
+ ///
+ /// let read_only = "ro";
+ /// let mut nmount: Nmount<'static> = Nmount::new();
+ /// nmount.null_opt_owned(read_only);
+ /// ```
+ pub fn null_opt_owned<P: ?Sized + NixPath>(
+ &mut self,
+ name: &P,
+ ) -> &mut Self {
+ self.push_nix_path(name);
+ self.push_slice(&[], false);
+ self
+ }
+
+ /// Add a mount option as a [`CStr`].
+ ///
+ /// # Examples
+ /// ```
+ /// use nix::mount::Nmount;
+ /// use std::ffi::CString;
+ ///
+ /// let fstype = CString::new("fstype").unwrap();
+ /// let nullfs = CString::new("nullfs").unwrap();
+ /// Nmount::new()
+ /// .str_opt(&fstype, &nullfs);
+ /// ```
+ pub fn str_opt(&mut self, name: &'a CStr, val: &'a CStr) -> &mut Self {
+ self.push_slice(name.to_bytes_with_nul(), false);
+ self.push_slice(val.to_bytes_with_nul(), false);
+ self
+ }
+
+ /// Add a mount option as an owned string.
+ ///
+ /// This has higher runtime cost than [`Nmount::str_opt`], but is useful
+ /// when the value's lifetime doesn't outlive the `Nmount`, or it's a
+ /// different string type than `CStr`.
+ ///
+ /// # Examples
+ /// ```
+ /// use nix::mount::Nmount;
+ /// use std::path::Path;
+ ///
+ /// let mountpoint = Path::new("/mnt");
+ /// Nmount::new()
+ /// .str_opt_owned("fspath", mountpoint.to_str().unwrap());
+ /// ```
+ pub fn str_opt_owned<P1, P2>(&mut self, name: &P1, val: &P2) -> &mut Self
+ where
+ P1: ?Sized + NixPath,
+ P2: ?Sized + NixPath,
+ {
+ self.push_nix_path(name);
+ self.push_nix_path(val);
+ self
+ }
+
+ /// Create a new `Nmount` struct with no options
+ pub fn new() -> Self {
+ Self::default()
+ }
+
+ /// Actually mount the file system.
+ pub fn nmount(&mut self, flags: MntFlags) -> NmountResult {
+ const ERRMSG_NAME: &[u8] = b"errmsg\0";
+ let mut errmsg = vec![0u8; 255];
+
+ // nmount can return extra error information via a "errmsg" return
+ // argument.
+ self.push_slice(ERRMSG_NAME, false);
+
+ // SAFETY: we are pushing a mutable iovec here, so we can't use
+ // the above method
+ self.iov.push(libc::iovec {
+ iov_base: errmsg.as_mut_ptr() as *mut c_void,
+ iov_len: errmsg.len(),
+ });
+
+ let niov = self.iov.len() as c_uint;
+ let iovp = self.iov.as_mut_ptr() as *mut libc::iovec;
+ let res = unsafe { libc::nmount(iovp, niov, flags.bits) };
+ match Errno::result(res) {
+ Ok(_) => Ok(()),
+ Err(error) => {
+ let errmsg = match errmsg.iter().position(|&x| x == 0) {
+ None => None,
+ Some(0) => None,
+ Some(n) => {
+ let sl = &errmsg[0..n + 1];
+ Some(CStr::from_bytes_with_nul(sl).unwrap())
+ }
+ };
+ Err(NmountError::new(error, errmsg))
+ }
+ }
+ }
+}
+
+#[cfg(target_os = "freebsd")]
+impl<'a> Drop for Nmount<'a> {
+ fn drop(&mut self) {
+ for (iov, is_owned) in self.iov.iter().zip(self.is_owned.iter()) {
+ if *is_owned {
+ // Free the owned string. Safe because we recorded ownership,
+ // and Nmount does not implement Clone.
+ unsafe {
+ drop(CString::from_raw(iov.iov_base as *mut c_char));
+ }
+ }
+ }
+ }
+}
+
+/// Unmount the file system mounted at `mountpoint`.
+///
+/// Useful flags include
+/// * `MNT_FORCE` - Unmount even if still in use.
+#[cfg_attr(
+ target_os = "freebsd",
+ doc = "
+* `MNT_BYFSID` - `mountpoint` is not a path, but a file system ID
+ encoded as `FSID:val0:val1`, where `val0` and `val1`
+ are the contents of the `fsid_t val[]` array in decimal.
+ The file system that has the specified file system ID
+ will be unmounted. See
+ [`statfs`](crate::sys::statfs::statfs) to determine the
+ `fsid`.
+"
+)]
+pub fn unmount<P>(mountpoint: &P, flags: MntFlags) -> Result<()>
+where
+ P: ?Sized + NixPath,
+{
+ let res = mountpoint.with_nix_path(|cstr| unsafe {
+ libc::unmount(cstr.as_ptr(), flags.bits)
+ })?;
+
+ Errno::result(res).map(drop)
+}
diff --git a/third_party/rust/nix/src/mount/linux.rs b/third_party/rust/nix/src/mount/linux.rs
new file mode 100644
index 0000000000..cf6a60b017
--- /dev/null
+++ b/third_party/rust/nix/src/mount/linux.rs
@@ -0,0 +1,115 @@
+#![allow(missing_docs)]
+use crate::errno::Errno;
+use crate::{NixPath, Result};
+use libc::{self, c_int, c_ulong};
+
+libc_bitflags!(
+ pub struct MsFlags: c_ulong {
+ /// Mount read-only
+ MS_RDONLY;
+ /// Ignore suid and sgid bits
+ MS_NOSUID;
+ /// Disallow access to device special files
+ MS_NODEV;
+ /// Disallow program execution
+ MS_NOEXEC;
+ /// Writes are synced at once
+ MS_SYNCHRONOUS;
+ /// Alter flags of a mounted FS
+ MS_REMOUNT;
+ /// Allow mandatory locks on a FS
+ MS_MANDLOCK;
+ /// Directory modifications are synchronous
+ MS_DIRSYNC;
+ /// Do not update access times
+ MS_NOATIME;
+ /// Do not update directory access times
+ MS_NODIRATIME;
+ /// Linux 2.4.0 - Bind directory at different place
+ MS_BIND;
+ MS_MOVE;
+ MS_REC;
+ MS_SILENT;
+ MS_POSIXACL;
+ MS_UNBINDABLE;
+ MS_PRIVATE;
+ MS_SLAVE;
+ MS_SHARED;
+ MS_RELATIME;
+ MS_KERNMOUNT;
+ MS_I_VERSION;
+ MS_STRICTATIME;
+ MS_LAZYTIME;
+ MS_ACTIVE;
+ MS_NOUSER;
+ MS_RMT_MASK;
+ MS_MGC_VAL;
+ MS_MGC_MSK;
+ }
+);
+
+libc_bitflags!(
+ pub struct MntFlags: c_int {
+ MNT_FORCE;
+ MNT_DETACH;
+ MNT_EXPIRE;
+ UMOUNT_NOFOLLOW;
+ }
+);
+
+pub fn mount<
+ P1: ?Sized + NixPath,
+ P2: ?Sized + NixPath,
+ P3: ?Sized + NixPath,
+ P4: ?Sized + NixPath,
+>(
+ source: Option<&P1>,
+ target: &P2,
+ fstype: Option<&P3>,
+ flags: MsFlags,
+ data: Option<&P4>,
+) -> Result<()> {
+ fn with_opt_nix_path<P, T, F>(p: Option<&P>, f: F) -> Result<T>
+ where
+ P: ?Sized + NixPath,
+ F: FnOnce(*const libc::c_char) -> T,
+ {
+ match p {
+ Some(path) => path.with_nix_path(|p_str| f(p_str.as_ptr())),
+ None => Ok(f(std::ptr::null())),
+ }
+ }
+
+ let res = with_opt_nix_path(source, |s| {
+ target.with_nix_path(|t| {
+ with_opt_nix_path(fstype, |ty| {
+ with_opt_nix_path(data, |d| unsafe {
+ libc::mount(
+ s,
+ t.as_ptr(),
+ ty,
+ flags.bits,
+ d as *const libc::c_void,
+ )
+ })
+ })
+ })
+ })????;
+
+ Errno::result(res).map(drop)
+}
+
+pub fn umount<P: ?Sized + NixPath>(target: &P) -> Result<()> {
+ let res =
+ target.with_nix_path(|cstr| unsafe { libc::umount(cstr.as_ptr()) })?;
+
+ Errno::result(res).map(drop)
+}
+
+pub fn umount2<P: ?Sized + NixPath>(target: &P, flags: MntFlags) -> Result<()> {
+ let res = target.with_nix_path(|cstr| unsafe {
+ libc::umount2(cstr.as_ptr(), flags.bits)
+ })?;
+
+ Errno::result(res).map(drop)
+}
diff --git a/third_party/rust/nix/src/mount/mod.rs b/third_party/rust/nix/src/mount/mod.rs
new file mode 100644
index 0000000000..e98b49c343
--- /dev/null
+++ b/third_party/rust/nix/src/mount/mod.rs
@@ -0,0 +1,26 @@
+//! Mount file systems
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+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"
+))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+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/third_party/rust/nix/src/mqueue.rs b/third_party/rust/nix/src/mqueue.rs
new file mode 100644
index 0000000000..33599bf91d
--- /dev/null
+++ b/third_party/rust/nix/src/mqueue.rs
@@ -0,0 +1,276 @@
+//! Posix Message Queue functions
+//!
+//! # Example
+//!
+// no_run because a kernel module may be required.
+//! ```no_run
+//! # use std::ffi::CString;
+//! # use nix::mqueue::*;
+//! use nix::sys::stat::Mode;
+//!
+//! const MSG_SIZE: mq_attr_member_t = 32;
+//! let mq_name= CString::new("/a_nix_test_queue").unwrap();
+//!
+//! let oflag0 = MQ_OFlag::O_CREAT | MQ_OFlag::O_WRONLY;
+//! let mode = Mode::S_IWUSR | Mode::S_IRUSR | Mode::S_IRGRP | Mode::S_IROTH;
+//! let mqd0 = mq_open(&mq_name, oflag0, mode, None).unwrap();
+//! let msg_to_send = b"msg_1";
+//! mq_send(&mqd0, msg_to_send, 1).unwrap();
+//!
+//! let oflag1 = MQ_OFlag::O_CREAT | MQ_OFlag::O_RDONLY;
+//! let mqd1 = mq_open(&mq_name, oflag1, mode, None).unwrap();
+//! let mut buf = [0u8; 32];
+//! let mut prio = 0u32;
+//! let len = mq_receive(&mqd1, &mut buf, &mut prio).unwrap();
+//! assert_eq!(prio, 1);
+//! assert_eq!(msg_to_send, &buf[0..len]);
+//!
+//! mq_close(mqd1).unwrap();
+//! mq_close(mqd0).unwrap();
+//! ```
+//! [Further reading and details on the C API](https://man7.org/linux/man-pages/man7/mq_overview.7.html)
+
+use crate::errno::Errno;
+use crate::Result;
+
+use crate::sys::stat::Mode;
+use libc::{self, c_char, mqd_t, size_t};
+use std::ffi::CStr;
+use std::mem;
+
+libc_bitflags! {
+ /// Used with [`mq_open`].
+ pub struct MQ_OFlag: libc::c_int {
+ /// Open the message queue for receiving messages.
+ O_RDONLY;
+ /// Open the queue for sending messages.
+ O_WRONLY;
+ /// Open the queue for both receiving and sending messages
+ O_RDWR;
+ /// Create a message queue.
+ O_CREAT;
+ /// If set along with `O_CREAT`, `mq_open` will fail if the message
+ /// queue name exists.
+ O_EXCL;
+ /// `mq_send` and `mq_receive` should fail with `EAGAIN` rather than
+ /// wait for resources that are not currently available.
+ O_NONBLOCK;
+ /// Set the close-on-exec flag for the message queue descriptor.
+ O_CLOEXEC;
+ }
+}
+
+/// A message-queue attribute, optionally used with [`mq_setattr`] and
+/// [`mq_getattr`] and optionally [`mq_open`],
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct MqAttr {
+ mq_attr: libc::mq_attr,
+}
+
+/// Identifies an open POSIX Message Queue
+// A safer wrapper around libc::mqd_t, which is a pointer on some platforms
+// Deliberately is not Clone to prevent use-after-close scenarios
+#[repr(transparent)]
+#[derive(Debug)]
+#[allow(missing_copy_implementations)]
+pub struct MqdT(mqd_t);
+
+// x32 compatibility
+// See https://sourceware.org/bugzilla/show_bug.cgi?id=21279
+/// Size of a message queue attribute member
+#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub type mq_attr_member_t = i64;
+/// Size of a message queue attribute member
+#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub type mq_attr_member_t = libc::c_long;
+
+impl MqAttr {
+ /// Create a new message queue attribute
+ ///
+ /// # Arguments
+ ///
+ /// - `mq_flags`: Either `0` or `O_NONBLOCK`.
+ /// - `mq_maxmsg`: Maximum number of messages on the queue.
+ /// - `mq_msgsize`: Maximum message size in bytes.
+ /// - `mq_curmsgs`: Number of messages currently in the queue.
+ pub fn new(
+ mq_flags: mq_attr_member_t,
+ mq_maxmsg: mq_attr_member_t,
+ mq_msgsize: mq_attr_member_t,
+ mq_curmsgs: mq_attr_member_t,
+ ) -> MqAttr {
+ let mut attr = mem::MaybeUninit::<libc::mq_attr>::uninit();
+ unsafe {
+ let p = attr.as_mut_ptr();
+ (*p).mq_flags = mq_flags;
+ (*p).mq_maxmsg = mq_maxmsg;
+ (*p).mq_msgsize = mq_msgsize;
+ (*p).mq_curmsgs = mq_curmsgs;
+ MqAttr {
+ mq_attr: attr.assume_init(),
+ }
+ }
+ }
+
+ /// The current flags, either `0` or `O_NONBLOCK`.
+ pub const fn flags(&self) -> mq_attr_member_t {
+ self.mq_attr.mq_flags
+ }
+
+ /// The max number of messages that can be held by the queue
+ pub const fn maxmsg(&self) -> mq_attr_member_t {
+ self.mq_attr.mq_maxmsg
+ }
+
+ /// The maximum size of each message (in bytes)
+ pub const fn msgsize(&self) -> mq_attr_member_t {
+ self.mq_attr.mq_msgsize
+ }
+
+ /// The number of messages currently held in the queue
+ pub const fn curmsgs(&self) -> mq_attr_member_t {
+ self.mq_attr.mq_curmsgs
+ }
+}
+
+/// Open a message queue
+///
+/// See also [`mq_open(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_open.html)
+// The mode.bits cast is only lossless on some OSes
+#[allow(clippy::cast_lossless)]
+pub fn mq_open(
+ name: &CStr,
+ oflag: MQ_OFlag,
+ mode: Mode,
+ attr: Option<&MqAttr>,
+) -> Result<MqdT> {
+ let res = match attr {
+ Some(mq_attr) => unsafe {
+ libc::mq_open(
+ name.as_ptr(),
+ oflag.bits(),
+ mode.bits() as libc::c_int,
+ &mq_attr.mq_attr as *const libc::mq_attr,
+ )
+ },
+ None => unsafe { libc::mq_open(name.as_ptr(), oflag.bits()) },
+ };
+ Errno::result(res).map(MqdT)
+}
+
+/// Remove a message queue
+///
+/// See also [`mq_unlink(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_unlink.html)
+pub fn mq_unlink(name: &CStr) -> Result<()> {
+ let res = unsafe { libc::mq_unlink(name.as_ptr()) };
+ Errno::result(res).map(drop)
+}
+
+/// Close a message queue
+///
+/// See also [`mq_close(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_close.html)
+pub fn mq_close(mqdes: MqdT) -> Result<()> {
+ let res = unsafe { libc::mq_close(mqdes.0) };
+ Errno::result(res).map(drop)
+}
+
+/// Receive a message from a message queue
+///
+/// See also [`mq_receive(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_receive.html)
+pub fn mq_receive(
+ mqdes: &MqdT,
+ message: &mut [u8],
+ msg_prio: &mut u32,
+) -> Result<usize> {
+ let len = message.len() as size_t;
+ let res = unsafe {
+ libc::mq_receive(
+ mqdes.0,
+ message.as_mut_ptr() as *mut c_char,
+ len,
+ msg_prio as *mut u32,
+ )
+ };
+ Errno::result(res).map(|r| r as usize)
+}
+
+/// Send a message to a message queue
+///
+/// See also [`mq_send(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_send.html)
+pub fn mq_send(mqdes: &MqdT, message: &[u8], msq_prio: u32) -> Result<()> {
+ let res = unsafe {
+ libc::mq_send(
+ mqdes.0,
+ message.as_ptr() as *const c_char,
+ message.len(),
+ msq_prio,
+ )
+ };
+ Errno::result(res).map(drop)
+}
+
+/// Get message queue attributes
+///
+/// See also [`mq_getattr(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_getattr.html)
+pub fn mq_getattr(mqd: &MqdT) -> Result<MqAttr> {
+ let mut attr = mem::MaybeUninit::<libc::mq_attr>::uninit();
+ let res = unsafe { libc::mq_getattr(mqd.0, attr.as_mut_ptr()) };
+ Errno::result(res).map(|_| unsafe {
+ MqAttr {
+ mq_attr: attr.assume_init(),
+ }
+ })
+}
+
+/// Set the attributes of the message queue. Only `O_NONBLOCK` can be set, everything else will be ignored
+/// Returns the old attributes
+/// It is recommend to use the `mq_set_nonblock()` and `mq_remove_nonblock()` convenience functions as they are easier to use
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mq_setattr.html)
+pub fn mq_setattr(mqd: &MqdT, newattr: &MqAttr) -> Result<MqAttr> {
+ let mut attr = mem::MaybeUninit::<libc::mq_attr>::uninit();
+ let res = unsafe {
+ libc::mq_setattr(
+ mqd.0,
+ &newattr.mq_attr as *const libc::mq_attr,
+ attr.as_mut_ptr(),
+ )
+ };
+ Errno::result(res).map(|_| unsafe {
+ MqAttr {
+ mq_attr: attr.assume_init(),
+ }
+ })
+}
+
+/// Convenience function.
+/// Sets the `O_NONBLOCK` attribute for a given message queue descriptor
+/// Returns the old attributes
+#[allow(clippy::useless_conversion)] // Not useless on all OSes
+pub fn mq_set_nonblock(mqd: &MqdT) -> Result<MqAttr> {
+ let oldattr = mq_getattr(mqd)?;
+ let newattr = MqAttr::new(
+ mq_attr_member_t::from(MQ_OFlag::O_NONBLOCK.bits()),
+ oldattr.mq_attr.mq_maxmsg,
+ oldattr.mq_attr.mq_msgsize,
+ oldattr.mq_attr.mq_curmsgs,
+ );
+ mq_setattr(mqd, &newattr)
+}
+
+/// Convenience function.
+/// Removes `O_NONBLOCK` attribute for a given message queue descriptor
+/// Returns the old attributes
+pub fn mq_remove_nonblock(mqd: &MqdT) -> Result<MqAttr> {
+ let oldattr = mq_getattr(mqd)?;
+ let newattr = MqAttr::new(
+ 0,
+ oldattr.mq_attr.mq_maxmsg,
+ oldattr.mq_attr.mq_msgsize,
+ oldattr.mq_attr.mq_curmsgs,
+ );
+ mq_setattr(mqd, &newattr)
+}
diff --git a/third_party/rust/nix/src/net/if_.rs b/third_party/rust/nix/src/net/if_.rs
new file mode 100644
index 0000000000..b2423bc673
--- /dev/null
+++ b/third_party/rust/nix/src/net/if_.rs
@@ -0,0 +1,469 @@
+//! Network interface name resolution.
+//!
+//! Uses Linux and/or POSIX functions to resolve interface names like "eth0"
+//! or "socan1" into device numbers.
+
+use crate::{Error, NixPath, Result};
+use libc::c_uint;
+
+/// Resolve an interface into a interface number.
+pub fn if_nametoindex<P: ?Sized + NixPath>(name: &P) -> Result<c_uint> {
+ let if_index = name
+ .with_nix_path(|name| unsafe { libc::if_nametoindex(name.as_ptr()) })?;
+
+ if if_index == 0 {
+ Err(Error::last())
+ } else {
+ Ok(if_index)
+ }
+}
+
+libc_bitflags!(
+ /// Standard interface flags, used by `getifaddrs`
+ pub struct InterfaceFlags: libc::c_int {
+ /// Interface is running. (see
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
+ IFF_UP;
+ /// Valid broadcast address set. (see
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
+ IFF_BROADCAST;
+ /// Internal debugging flag. (see
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
+ #[cfg(not(target_os = "haiku"))]
+ IFF_DEBUG;
+ /// Interface is a loopback interface. (see
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
+ IFF_LOOPBACK;
+ /// Interface is a point-to-point link. (see
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
+ IFF_POINTOPOINT;
+ /// Avoid use of trailers. (see
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
+ #[cfg(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "illumos",
+ target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_NOTRAILERS;
+ /// Interface manages own routes.
+ #[cfg(any(target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_SMART;
+ /// Resources allocated. (see
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
+ #[cfg(any(target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_RUNNING;
+ /// No arp protocol, L2 destination address not set. (see
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
+ IFF_NOARP;
+ /// Interface is in promiscuous mode. (see
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
+ IFF_PROMISC;
+ /// Receive all multicast packets. (see
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
+ IFF_ALLMULTI;
+ /// Master of a load balancing bundle. (see
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_MASTER;
+ /// transmission in progress, tx hardware queue is full
+ #[cfg(any(target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "ios"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_OACTIVE;
+ /// Protocol code on board.
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_INTELLIGENT;
+ /// Slave of a load balancing bundle. (see
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_SLAVE;
+ /// Can't hear own transmissions.
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_SIMPLEX;
+ /// Supports multicast. (see
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
+ IFF_MULTICAST;
+ /// Per link layer defined bit.
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "ios"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_LINK0;
+ /// Multicast using broadcast.
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_MULTI_BCAST;
+ /// Is able to select media type via ifmap. (see
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_PORTSEL;
+ /// Per link layer defined bit.
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "ios"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_LINK1;
+ /// Non-unique address.
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_UNNUMBERED;
+ /// Auto media selection active. (see
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_AUTOMEDIA;
+ /// Per link layer defined bit.
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "ios"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_LINK2;
+ /// Use alternate physical connection.
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "ios"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_ALTPHYS;
+ /// DHCP controls interface.
+ #[cfg(any(target_os = "solaris", target_os = "illumos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_DHCPRUNNING;
+ /// The addresses are lost when the interface goes down. (see
+ /// [`netdevice(7)`](https://man7.org/linux/man-pages/man7/netdevice.7.html))
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_DYNAMIC;
+ /// Do not advertise.
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_PRIVATE;
+ /// Driver signals L1 up. Volatile.
+ #[cfg(any(target_os = "fuchsia", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_LOWER_UP;
+ /// Interface is in polling mode.
+ #[cfg(any(target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_POLLING_COMPAT;
+ /// Unconfigurable using ioctl(2).
+ #[cfg(any(target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_CANTCONFIG;
+ /// Do not transmit packets.
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_NOXMIT;
+ /// Driver signals dormant. Volatile.
+ #[cfg(any(target_os = "fuchsia", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_DORMANT;
+ /// User-requested promisc mode.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_PPROMISC;
+ /// Just on-link subnet.
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_NOLOCAL;
+ /// Echo sent packets. Volatile.
+ #[cfg(any(target_os = "fuchsia", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_ECHO;
+ /// User-requested monitor mode.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_MONITOR;
+ /// Address is deprecated.
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_DEPRECATED;
+ /// Static ARP.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_STATICARP;
+ /// Address from stateless addrconf.
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_ADDRCONF;
+ /// Interface is in polling mode.
+ #[cfg(any(target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_NPOLLING;
+ /// Router on interface.
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_ROUTER;
+ /// Interface is in polling mode.
+ #[cfg(any(target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_IDIRECT;
+ /// Interface is winding down
+ #[cfg(any(target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_DYING;
+ /// No NUD on interface.
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_NONUD;
+ /// Interface is being renamed
+ #[cfg(any(target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_RENAMING;
+ /// Anycast address.
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_ANYCAST;
+ /// Don't exchange routing info.
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_NORTEXCH;
+ /// Do not provide packet information
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_NO_PI as libc::c_int;
+ /// TUN device (no Ethernet headers)
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_TUN as libc::c_int;
+ /// TAP device
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_TAP as libc::c_int;
+ /// IPv4 interface.
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_IPV4;
+ /// IPv6 interface.
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_IPV6;
+ /// in.mpathd test address
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_NOFAILOVER;
+ /// Interface has failed
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_FAILED;
+ /// Interface is a hot-spare
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_STANDBY;
+ /// Functioning but not used
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_INACTIVE;
+ /// Interface is offline
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_OFFLINE;
+ #[cfg(target_os = "solaris")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_COS_ENABLED;
+ /// Prefer as source addr.
+ #[cfg(target_os = "solaris")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_PREFERRED;
+ /// RFC3041
+ #[cfg(target_os = "solaris")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_TEMPORARY;
+ /// MTU set with SIOCSLIFMTU
+ #[cfg(target_os = "solaris")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_FIXEDMTU;
+ /// Cannot send / receive packets
+ #[cfg(target_os = "solaris")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_VIRTUAL;
+ /// Local address in use
+ #[cfg(target_os = "solaris")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_DUPLICATE;
+ /// IPMP IP interface
+ #[cfg(target_os = "solaris")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IFF_IPMP;
+ }
+);
+
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+mod if_nameindex {
+ use super::*;
+
+ use std::ffi::CStr;
+ use std::fmt;
+ use std::marker::PhantomData;
+ use std::ptr::NonNull;
+
+ /// A network interface. Has a name like "eth0" or "wlp4s0" or "wlan0", as well as an index
+ /// (1, 2, 3, etc) that identifies it in the OS's networking stack.
+ #[allow(missing_copy_implementations)]
+ #[repr(transparent)]
+ pub struct Interface(libc::if_nameindex);
+
+ impl Interface {
+ /// Obtain the index of this interface.
+ pub fn index(&self) -> c_uint {
+ self.0.if_index
+ }
+
+ /// Obtain the name of this interface.
+ pub fn name(&self) -> &CStr {
+ unsafe { CStr::from_ptr(self.0.if_name) }
+ }
+ }
+
+ impl fmt::Debug for Interface {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("Interface")
+ .field("index", &self.index())
+ .field("name", &self.name())
+ .finish()
+ }
+ }
+
+ /// A list of the network interfaces available on this system. Obtained from [`if_nameindex()`].
+ pub struct Interfaces {
+ ptr: NonNull<libc::if_nameindex>,
+ }
+
+ impl Interfaces {
+ /// Iterate over the interfaces in this list.
+ #[inline]
+ pub fn iter(&self) -> InterfacesIter<'_> {
+ self.into_iter()
+ }
+
+ /// Convert this to a slice of interfaces. Note that the underlying interfaces list is
+ /// null-terminated, so calling this calculates the length. If random access isn't needed,
+ /// [`Interfaces::iter()`] should be used instead.
+ pub fn to_slice(&self) -> &[Interface] {
+ let ifs = self.ptr.as_ptr() as *const Interface;
+ let len = self.iter().count();
+ unsafe { std::slice::from_raw_parts(ifs, len) }
+ }
+ }
+
+ impl Drop for Interfaces {
+ fn drop(&mut self) {
+ unsafe { libc::if_freenameindex(self.ptr.as_ptr()) };
+ }
+ }
+
+ impl fmt::Debug for Interfaces {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ self.to_slice().fmt(f)
+ }
+ }
+
+ impl<'a> IntoIterator for &'a Interfaces {
+ type IntoIter = InterfacesIter<'a>;
+ type Item = &'a Interface;
+ #[inline]
+ fn into_iter(self) -> Self::IntoIter {
+ InterfacesIter {
+ ptr: self.ptr.as_ptr(),
+ _marker: PhantomData,
+ }
+ }
+ }
+
+ /// An iterator over the interfaces in an [`Interfaces`].
+ #[derive(Debug)]
+ pub struct InterfacesIter<'a> {
+ ptr: *const libc::if_nameindex,
+ _marker: PhantomData<&'a Interfaces>,
+ }
+
+ impl<'a> Iterator for InterfacesIter<'a> {
+ type Item = &'a Interface;
+ #[inline]
+ fn next(&mut self) -> Option<Self::Item> {
+ unsafe {
+ if (*self.ptr).if_index == 0 {
+ None
+ } else {
+ let ret = &*(self.ptr as *const Interface);
+ self.ptr = self.ptr.add(1);
+ Some(ret)
+ }
+ }
+ }
+ }
+
+ /// Retrieve a list of the network interfaces available on the local system.
+ ///
+ /// ```
+ /// let interfaces = nix::net::if_::if_nameindex().unwrap();
+ /// for iface in &interfaces {
+ /// println!("Interface #{} is called {}", iface.index(), iface.name().to_string_lossy());
+ /// }
+ /// ```
+ pub fn if_nameindex() -> Result<Interfaces> {
+ unsafe {
+ let ifs = libc::if_nameindex();
+ let ptr = NonNull::new(ifs).ok_or_else(Error::last)?;
+ Ok(Interfaces { ptr })
+ }
+ }
+}
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+))]
+pub use if_nameindex::*;
diff --git a/third_party/rust/nix/src/net/mod.rs b/third_party/rust/nix/src/net/mod.rs
new file mode 100644
index 0000000000..079fcfde6f
--- /dev/null
+++ b/third_party/rust/nix/src/net/mod.rs
@@ -0,0 +1,4 @@
+//! Functionality involving network interfaces
+// To avoid clashing with the keyword "if", we use "if_" as the module name.
+// The original header is called "net/if.h".
+pub mod if_;
diff --git a/third_party/rust/nix/src/poll.rs b/third_party/rust/nix/src/poll.rs
new file mode 100644
index 0000000000..6f227fee9e
--- /dev/null
+++ b/third_party/rust/nix/src/poll.rs
@@ -0,0 +1,197 @@
+//! Wait for events to trigger on specific file descriptors
+use std::os::unix::io::{AsRawFd, RawFd};
+
+use crate::errno::Errno;
+use crate::Result;
+
+/// This is a wrapper around `libc::pollfd`.
+///
+/// It's meant to be used as an argument to the [`poll`](fn.poll.html) and
+/// [`ppoll`](fn.ppoll.html) functions to specify the events of interest
+/// for a specific file descriptor.
+///
+/// After a call to `poll` or `ppoll`, the events that occurred can be
+/// retrieved by calling [`revents()`](#method.revents) on the `PollFd`.
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct PollFd {
+ pollfd: libc::pollfd,
+}
+
+impl PollFd {
+ /// Creates a new `PollFd` specifying the events of interest
+ /// for a given file descriptor.
+ pub const fn new(fd: RawFd, events: PollFlags) -> PollFd {
+ PollFd {
+ pollfd: libc::pollfd {
+ fd,
+ events: events.bits(),
+ revents: PollFlags::empty().bits(),
+ },
+ }
+ }
+
+ /// Returns the events that occurred in the last call to `poll` or `ppoll`. Will only return
+ /// `None` if the kernel provides status flags that Nix does not know about.
+ pub fn revents(self) -> Option<PollFlags> {
+ PollFlags::from_bits(self.pollfd.revents)
+ }
+
+ /// Returns if any of the events of interest occured in the last call to `poll` or `ppoll`. Will
+ /// only return `None` if the kernel provides status flags that Nix does not know about.
+ ///
+ /// Equivalent to `x.revents()? != PollFlags::empty()`.
+ ///
+ /// This is marginally more efficient than [`PollFd::all`].
+ pub fn any(self) -> Option<bool> {
+ Some(self.revents()? != PollFlags::empty())
+ }
+
+ /// Returns if all the events of interest occured in the last call to `poll` or `ppoll`. Will
+ /// only return `None` if the kernel provides status flags that Nix does not know about.
+ ///
+ /// Equivalent to `x.revents()? & x.events() == x.events()`.
+ ///
+ /// This is marginally less efficient than [`PollFd::any`].
+ pub fn all(self) -> Option<bool> {
+ Some(self.revents()? & self.events() == self.events())
+ }
+
+ /// The events of interest for this `PollFd`.
+ pub fn events(self) -> PollFlags {
+ PollFlags::from_bits(self.pollfd.events).unwrap()
+ }
+
+ /// Modify the events of interest for this `PollFd`.
+ pub fn set_events(&mut self, events: PollFlags) {
+ self.pollfd.events = events.bits();
+ }
+}
+
+impl AsRawFd for PollFd {
+ fn as_raw_fd(&self) -> RawFd {
+ self.pollfd.fd
+ }
+}
+
+libc_bitflags! {
+ /// These flags define the different events that can be monitored by `poll` and `ppoll`
+ pub struct PollFlags: libc::c_short {
+ /// There is data to read.
+ POLLIN;
+ /// There is some exceptional condition on the file descriptor.
+ ///
+ /// Possibilities include:
+ ///
+ /// * There is out-of-band data on a TCP socket (see
+ /// [tcp(7)](https://man7.org/linux/man-pages/man7/tcp.7.html)).
+ /// * A pseudoterminal master in packet mode has seen a state
+ /// change on the slave (see
+ /// [ioctl_tty(2)](https://man7.org/linux/man-pages/man2/ioctl_tty.2.html)).
+ /// * A cgroup.events file has been modified (see
+ /// [cgroups(7)](https://man7.org/linux/man-pages/man7/cgroups.7.html)).
+ POLLPRI;
+ /// Writing is now possible, though a write larger that the
+ /// available space in a socket or pipe will still block (unless
+ /// `O_NONBLOCK` is set).
+ POLLOUT;
+ /// Equivalent to [`POLLIN`](constant.POLLIN.html)
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ POLLRDNORM;
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Equivalent to [`POLLOUT`](constant.POLLOUT.html)
+ POLLWRNORM;
+ /// Priority band data can be read (generally unused on Linux).
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ POLLRDBAND;
+ /// Priority data may be written.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ POLLWRBAND;
+ /// Error condition (only returned in
+ /// [`PollFd::revents`](struct.PollFd.html#method.revents);
+ /// ignored in [`PollFd::new`](struct.PollFd.html#method.new)).
+ /// This bit is also set for a file descriptor referring to the
+ /// write end of a pipe when the read end has been closed.
+ POLLERR;
+ /// Hang up (only returned in [`PollFd::revents`](struct.PollFd.html#method.revents);
+ /// ignored in [`PollFd::new`](struct.PollFd.html#method.new)).
+ /// Note that when reading from a channel such as a pipe or a stream
+ /// socket, this event merely indicates that the peer closed its
+ /// end of the channel. Subsequent reads from the channel will
+ /// return 0 (end of file) only after all outstanding data in the
+ /// channel has been consumed.
+ POLLHUP;
+ /// Invalid request: `fd` not open (only returned in
+ /// [`PollFd::revents`](struct.PollFd.html#method.revents);
+ /// ignored in [`PollFd::new`](struct.PollFd.html#method.new)).
+ POLLNVAL;
+ }
+}
+
+/// `poll` waits for one of a set of file descriptors to become ready to perform I/O.
+/// ([`poll(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/poll.html))
+///
+/// `fds` contains all [`PollFd`](struct.PollFd.html) to poll.
+/// The function will return as soon as any event occur for any of these `PollFd`s.
+///
+/// The `timeout` argument specifies the number of milliseconds that `poll()`
+/// should block waiting for a file descriptor to become ready. The call
+/// will block until either:
+///
+/// * a file descriptor becomes ready;
+/// * the call is interrupted by a signal handler; or
+/// * the timeout expires.
+///
+/// Note that the timeout interval will be rounded up to the system clock
+/// granularity, and kernel scheduling delays mean that the blocking
+/// interval may overrun by a small amount. Specifying a negative value
+/// in timeout means an infinite timeout. Specifying a timeout of zero
+/// causes `poll()` to return immediately, even if no file descriptors are
+/// ready.
+pub fn poll(fds: &mut [PollFd], timeout: libc::c_int) -> Result<libc::c_int> {
+ let res = unsafe {
+ libc::poll(
+ fds.as_mut_ptr() as *mut libc::pollfd,
+ fds.len() as libc::nfds_t,
+ timeout,
+ )
+ };
+
+ Errno::result(res)
+}
+
+feature! {
+#![feature = "signal"]
+/// `ppoll()` allows an application to safely wait until either a file
+/// descriptor becomes ready or until a signal is caught.
+/// ([`poll(2)`](https://man7.org/linux/man-pages/man2/poll.2.html))
+///
+/// `ppoll` behaves like `poll`, but let you specify what signals may interrupt it
+/// with the `sigmask` argument. If you want `ppoll` to block indefinitely,
+/// specify `None` as `timeout` (it is like `timeout = -1` for `poll`).
+/// If `sigmask` is `None`, then no signal mask manipulation is performed,
+/// so in that case `ppoll` differs from `poll` only in the precision of the
+/// timeout argument.
+///
+#[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux"))]
+pub fn ppoll(
+ fds: &mut [PollFd],
+ timeout: Option<crate::sys::time::TimeSpec>,
+ sigmask: Option<crate::sys::signal::SigSet>
+ ) -> Result<libc::c_int>
+{
+ let timeout = timeout.as_ref().map_or(core::ptr::null(), |r| r.as_ref());
+ let sigmask = sigmask.as_ref().map_or(core::ptr::null(), |r| r.as_ref());
+ let res = unsafe {
+ libc::ppoll(fds.as_mut_ptr() as *mut libc::pollfd,
+ fds.len() as libc::nfds_t,
+ timeout,
+ sigmask)
+ };
+ Errno::result(res)
+}
+}
diff --git a/third_party/rust/nix/src/pty.rs b/third_party/rust/nix/src/pty.rs
new file mode 100644
index 0000000000..28ae5e924b
--- /dev/null
+++ b/third_party/rust/nix/src/pty.rs
@@ -0,0 +1,371 @@
+//! Create master and slave virtual pseudo-terminals (PTYs)
+
+pub use libc::pid_t as SessionId;
+pub use libc::winsize as Winsize;
+
+use std::ffi::CStr;
+use std::io;
+use std::mem;
+use std::os::unix::prelude::*;
+
+use crate::errno::Errno;
+use crate::sys::termios::Termios;
+#[cfg(feature = "process")]
+use crate::unistd::{ForkResult, Pid};
+use crate::{fcntl, unistd, Result};
+
+/// Representation of a master/slave pty pair
+///
+/// This is returned by `openpty`. Note that this type does *not* implement `Drop`, so the user
+/// must manually close the file descriptors.
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct OpenptyResult {
+ /// The master port in a virtual pty pair
+ pub master: RawFd,
+ /// The slave port in a virtual pty pair
+ pub slave: RawFd,
+}
+
+feature! {
+#![feature = "process"]
+/// Representation of a master with a forked pty
+///
+/// This is returned by `forkpty`. Note that this type does *not* implement `Drop`, so the user
+/// must manually close the file descriptors.
+#[derive(Clone, Copy, Debug)]
+pub struct ForkptyResult {
+ /// The master port in a virtual pty pair
+ pub master: RawFd,
+ /// Metadata about forked process
+ pub fork_result: ForkResult,
+}
+}
+
+/// Representation of the Master device in a master/slave pty pair
+///
+/// While this datatype is a thin wrapper around `RawFd`, it enforces that the available PTY
+/// functions are given the correct file descriptor. Additionally this type implements `Drop`,
+/// so that when it's consumed or goes out of scope, it's automatically cleaned-up.
+#[derive(Debug, Eq, Hash, PartialEq)]
+pub struct PtyMaster(RawFd);
+
+impl AsRawFd for PtyMaster {
+ fn as_raw_fd(&self) -> RawFd {
+ self.0
+ }
+}
+
+impl IntoRawFd for PtyMaster {
+ fn into_raw_fd(self) -> RawFd {
+ let fd = self.0;
+ mem::forget(self);
+ fd
+ }
+}
+
+impl Drop for PtyMaster {
+ fn drop(&mut self) {
+ // On drop, we ignore errors like EINTR and EIO because there's no clear
+ // way to handle them, we can't return anything, and (on FreeBSD at
+ // least) the file descriptor is deallocated in these cases. However,
+ // we must panic on EBADF, because it is always an error to close an
+ // invalid file descriptor. That frequently indicates a double-close
+ // condition, which can cause confusing errors for future I/O
+ // operations.
+ let e = unistd::close(self.0);
+ if e == Err(Errno::EBADF) {
+ panic!("Closing an invalid file descriptor!");
+ };
+ }
+}
+
+impl io::Read for PtyMaster {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ unistd::read(self.0, buf).map_err(io::Error::from)
+ }
+}
+
+impl io::Write for PtyMaster {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ unistd::write(self.0, buf).map_err(io::Error::from)
+ }
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+impl io::Read for &PtyMaster {
+ fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
+ unistd::read(self.0, buf).map_err(io::Error::from)
+ }
+}
+
+impl io::Write for &PtyMaster {
+ fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
+ unistd::write(self.0, buf).map_err(io::Error::from)
+ }
+ fn flush(&mut self) -> io::Result<()> {
+ Ok(())
+ }
+}
+
+/// Grant access to a slave pseudoterminal (see
+/// [`grantpt(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/grantpt.html))
+///
+/// `grantpt()` changes the mode and owner of the slave pseudoterminal device corresponding to the
+/// master pseudoterminal referred to by `fd`. This is a necessary step towards opening the slave.
+#[inline]
+pub fn grantpt(fd: &PtyMaster) -> Result<()> {
+ if unsafe { libc::grantpt(fd.as_raw_fd()) } < 0 {
+ return Err(Errno::last());
+ }
+
+ Ok(())
+}
+
+/// Open a pseudoterminal device (see
+/// [`posix_openpt(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/posix_openpt.html))
+///
+/// `posix_openpt()` returns a file descriptor to an existing unused pseudoterminal master device.
+///
+/// # Examples
+///
+/// A common use case with this function is to open both a master and slave PTY pair. This can be
+/// done as follows:
+///
+/// ```
+/// use std::path::Path;
+/// use nix::fcntl::{OFlag, open};
+/// use nix::pty::{grantpt, posix_openpt, ptsname, unlockpt};
+/// use nix::sys::stat::Mode;
+///
+/// # #[allow(dead_code)]
+/// # fn run() -> nix::Result<()> {
+/// // Open a new PTY master
+/// let master_fd = posix_openpt(OFlag::O_RDWR)?;
+///
+/// // Allow a slave to be generated for it
+/// grantpt(&master_fd)?;
+/// unlockpt(&master_fd)?;
+///
+/// // Get the name of the slave
+/// let slave_name = unsafe { ptsname(&master_fd) }?;
+///
+/// // Try to open the slave
+/// let _slave_fd = open(Path::new(&slave_name), OFlag::O_RDWR, Mode::empty())?;
+/// # Ok(())
+/// # }
+/// ```
+#[inline]
+pub fn posix_openpt(flags: fcntl::OFlag) -> Result<PtyMaster> {
+ let fd = unsafe { libc::posix_openpt(flags.bits()) };
+
+ if fd < 0 {
+ return Err(Errno::last());
+ }
+
+ Ok(PtyMaster(fd))
+}
+
+/// Get the name of the slave pseudoterminal (see
+/// [`ptsname(3)`](https://man7.org/linux/man-pages/man3/ptsname.3.html))
+///
+/// `ptsname()` returns the name of the slave pseudoterminal device corresponding to the master
+/// referred to by `fd`.
+///
+/// This value is useful for opening the slave pty once the master has already been opened with
+/// `posix_openpt()`.
+///
+/// # Safety
+///
+/// `ptsname()` mutates global variables and is *not* threadsafe.
+/// Mutating global variables is always considered `unsafe` by Rust and this
+/// function is marked as `unsafe` to reflect that.
+///
+/// For a threadsafe and non-`unsafe` alternative on Linux, see `ptsname_r()`.
+#[inline]
+pub unsafe fn ptsname(fd: &PtyMaster) -> Result<String> {
+ let name_ptr = libc::ptsname(fd.as_raw_fd());
+ if name_ptr.is_null() {
+ return Err(Errno::last());
+ }
+
+ let name = CStr::from_ptr(name_ptr);
+ Ok(name.to_string_lossy().into_owned())
+}
+
+/// Get the name of the slave pseudoterminal (see
+/// [`ptsname(3)`](https://man7.org/linux/man-pages/man3/ptsname.3.html))
+///
+/// `ptsname_r()` returns the name of the slave pseudoterminal device corresponding to the master
+/// referred to by `fd`. This is the threadsafe version of `ptsname()`, but it is not part of the
+/// POSIX standard and is instead a Linux-specific extension.
+///
+/// This value is useful for opening the slave ptty once the master has already been opened with
+/// `posix_openpt()`.
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+#[inline]
+pub fn ptsname_r(fd: &PtyMaster) -> Result<String> {
+ let mut name_buf = Vec::<libc::c_char>::with_capacity(64);
+ let name_buf_ptr = name_buf.as_mut_ptr();
+ let cname = unsafe {
+ let cap = name_buf.capacity();
+ if libc::ptsname_r(fd.as_raw_fd(), name_buf_ptr, cap) != 0 {
+ return Err(crate::Error::last());
+ }
+ CStr::from_ptr(name_buf.as_ptr())
+ };
+
+ let name = cname.to_string_lossy().into_owned();
+ Ok(name)
+}
+
+/// Unlock a pseudoterminal master/slave pseudoterminal pair (see
+/// [`unlockpt(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlockpt.html))
+///
+/// `unlockpt()` unlocks the slave pseudoterminal device corresponding to the master pseudoterminal
+/// referred to by `fd`. This must be called before trying to open the slave side of a
+/// pseudoterminal.
+#[inline]
+pub fn unlockpt(fd: &PtyMaster) -> Result<()> {
+ if unsafe { libc::unlockpt(fd.as_raw_fd()) } < 0 {
+ return Err(Errno::last());
+ }
+
+ Ok(())
+}
+
+/// Create a new pseudoterminal, returning the slave and master file descriptors
+/// in `OpenptyResult`
+/// (see [`openpty`](https://man7.org/linux/man-pages/man3/openpty.3.html)).
+///
+/// If `winsize` is not `None`, the window size of the slave will be set to
+/// the values in `winsize`. If `termios` is not `None`, the pseudoterminal's
+/// terminal settings of the slave will be set to the values in `termios`.
+#[inline]
+pub fn openpty<
+ 'a,
+ 'b,
+ T: Into<Option<&'a Winsize>>,
+ U: Into<Option<&'b Termios>>,
+>(
+ winsize: T,
+ termios: U,
+) -> Result<OpenptyResult> {
+ use std::ptr;
+
+ let mut slave = mem::MaybeUninit::<libc::c_int>::uninit();
+ let mut master = mem::MaybeUninit::<libc::c_int>::uninit();
+ let ret = {
+ match (termios.into(), winsize.into()) {
+ (Some(termios), Some(winsize)) => {
+ let inner_termios = termios.get_libc_termios();
+ unsafe {
+ libc::openpty(
+ master.as_mut_ptr(),
+ slave.as_mut_ptr(),
+ ptr::null_mut(),
+ &*inner_termios as *const libc::termios as *mut _,
+ winsize as *const Winsize as *mut _,
+ )
+ }
+ }
+ (None, Some(winsize)) => unsafe {
+ libc::openpty(
+ master.as_mut_ptr(),
+ slave.as_mut_ptr(),
+ ptr::null_mut(),
+ ptr::null_mut(),
+ winsize as *const Winsize as *mut _,
+ )
+ },
+ (Some(termios), None) => {
+ let inner_termios = termios.get_libc_termios();
+ unsafe {
+ libc::openpty(
+ master.as_mut_ptr(),
+ slave.as_mut_ptr(),
+ ptr::null_mut(),
+ &*inner_termios as *const libc::termios as *mut _,
+ ptr::null_mut(),
+ )
+ }
+ }
+ (None, None) => unsafe {
+ libc::openpty(
+ master.as_mut_ptr(),
+ slave.as_mut_ptr(),
+ ptr::null_mut(),
+ ptr::null_mut(),
+ ptr::null_mut(),
+ )
+ },
+ }
+ };
+
+ Errno::result(ret)?;
+
+ unsafe {
+ Ok(OpenptyResult {
+ master: master.assume_init(),
+ slave: slave.assume_init(),
+ })
+ }
+}
+
+feature! {
+#![feature = "process"]
+/// Create a new pseudoterminal, returning the master file descriptor and forked pid.
+/// in `ForkptyResult`
+/// (see [`forkpty`](https://man7.org/linux/man-pages/man3/forkpty.3.html)).
+///
+/// If `winsize` is not `None`, the window size of the slave will be set to
+/// the values in `winsize`. If `termios` is not `None`, the pseudoterminal's
+/// terminal settings of the slave will be set to the values in `termios`.
+///
+/// # Safety
+///
+/// In a multithreaded program, only [async-signal-safe] functions like `pause`
+/// and `_exit` may be called by the child (the parent isn't restricted). Note
+/// that memory allocation may **not** be async-signal-safe and thus must be
+/// prevented.
+///
+/// Those functions are only a small subset of your operating system's API, so
+/// special care must be taken to only invoke code you can control and audit.
+///
+/// [async-signal-safe]: https://man7.org/linux/man-pages/man7/signal-safety.7.html
+pub unsafe fn forkpty<'a, 'b, T: Into<Option<&'a Winsize>>, U: Into<Option<&'b Termios>>>(
+ winsize: T,
+ termios: U,
+) -> Result<ForkptyResult> {
+ use std::ptr;
+
+ let mut master = mem::MaybeUninit::<libc::c_int>::uninit();
+
+ let term = match termios.into() {
+ Some(termios) => {
+ let inner_termios = termios.get_libc_termios();
+ &*inner_termios as *const libc::termios as *mut _
+ },
+ None => ptr::null_mut(),
+ };
+
+ let win = winsize
+ .into()
+ .map(|ws| ws as *const Winsize as *mut _)
+ .unwrap_or(ptr::null_mut());
+
+ let res = libc::forkpty(master.as_mut_ptr(), ptr::null_mut(), term, win);
+
+ let fork_result = Errno::result(res).map(|res| match res {
+ 0 => ForkResult::Child,
+ res => ForkResult::Parent { child: Pid::from_raw(res) },
+ })?;
+
+ Ok(ForkptyResult {
+ master: master.assume_init(),
+ fork_result,
+ })
+}
+}
diff --git a/third_party/rust/nix/src/sched.rs b/third_party/rust/nix/src/sched.rs
new file mode 100644
index 0000000000..d5b1233cf3
--- /dev/null
+++ b/third_party/rust/nix/src/sched.rs
@@ -0,0 +1,324 @@
+//! Execution scheduling
+//!
+//! See Also
+//! [sched.h](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sched.h.html)
+use crate::{Errno, Result};
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+pub use self::sched_linux_like::*;
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+mod sched_linux_like {
+ use crate::errno::Errno;
+ use crate::unistd::Pid;
+ use crate::Result;
+ use libc::{self, c_int, c_void};
+ use std::mem;
+ use std::option::Option;
+ use std::os::unix::io::RawFd;
+
+ // For some functions taking with a parameter of type CloneFlags,
+ // only a subset of these flags have an effect.
+ libc_bitflags! {
+ /// Options for use with [`clone`]
+ pub struct CloneFlags: c_int {
+ /// The calling process and the child process run in the same
+ /// memory space.
+ CLONE_VM;
+ /// The caller and the child process share the same filesystem
+ /// information.
+ CLONE_FS;
+ /// The calling process and the child process share the same file
+ /// descriptor table.
+ CLONE_FILES;
+ /// The calling process and the child process share the same table
+ /// of signal handlers.
+ CLONE_SIGHAND;
+ /// If the calling process is being traced, then trace the child
+ /// also.
+ CLONE_PTRACE;
+ /// The execution of the calling process is suspended until the
+ /// child releases its virtual memory resources via a call to
+ /// execve(2) or _exit(2) (as with vfork(2)).
+ CLONE_VFORK;
+ /// The parent of the new child (as returned by getppid(2))
+ /// will be the same as that of the calling process.
+ CLONE_PARENT;
+ /// The child is placed in the same thread group as the calling
+ /// process.
+ CLONE_THREAD;
+ /// The cloned child is started in a new mount namespace.
+ CLONE_NEWNS;
+ /// The child and the calling process share a single list of System
+ /// V semaphore adjustment values
+ CLONE_SYSVSEM;
+ // Not supported by Nix due to lack of varargs support in Rust FFI
+ // CLONE_SETTLS;
+ // Not supported by Nix due to lack of varargs support in Rust FFI
+ // CLONE_PARENT_SETTID;
+ // Not supported by Nix due to lack of varargs support in Rust FFI
+ // CLONE_CHILD_CLEARTID;
+ /// Unused since Linux 2.6.2
+ #[deprecated(since = "0.23.0", note = "Deprecated by Linux 2.6.2")]
+ CLONE_DETACHED;
+ /// A tracing process cannot force `CLONE_PTRACE` on this child
+ /// process.
+ CLONE_UNTRACED;
+ // Not supported by Nix due to lack of varargs support in Rust FFI
+ // CLONE_CHILD_SETTID;
+ /// Create the process in a new cgroup namespace.
+ CLONE_NEWCGROUP;
+ /// Create the process in a new UTS namespace.
+ CLONE_NEWUTS;
+ /// Create the process in a new IPC namespace.
+ CLONE_NEWIPC;
+ /// Create the process in a new user namespace.
+ CLONE_NEWUSER;
+ /// Create the process in a new PID namespace.
+ CLONE_NEWPID;
+ /// Create the process in a new network namespace.
+ CLONE_NEWNET;
+ /// The new process shares an I/O context with the calling process.
+ CLONE_IO;
+ }
+ }
+
+ /// Type for the function executed by [`clone`].
+ pub type CloneCb<'a> = Box<dyn FnMut() -> isize + 'a>;
+
+ /// `clone` create a child process
+ /// ([`clone(2)`](https://man7.org/linux/man-pages/man2/clone.2.html))
+ ///
+ /// `stack` is a reference to an array which will hold the stack of the new
+ /// process. Unlike when calling `clone(2)` from C, the provided stack
+ /// address need not be the highest address of the region. Nix will take
+ /// care of that requirement. The user only needs to provide a reference to
+ /// a normally allocated buffer.
+ pub fn clone(
+ mut cb: CloneCb,
+ stack: &mut [u8],
+ flags: CloneFlags,
+ signal: Option<c_int>,
+ ) -> Result<Pid> {
+ extern "C" fn callback(data: *mut CloneCb) -> c_int {
+ let cb: &mut CloneCb = unsafe { &mut *data };
+ (*cb)() as c_int
+ }
+
+ let res = unsafe {
+ let combined = flags.bits() | signal.unwrap_or(0);
+ let ptr = stack.as_mut_ptr().add(stack.len());
+ let ptr_aligned = ptr.sub(ptr as usize % 16);
+ libc::clone(
+ mem::transmute(
+ callback
+ as extern "C" fn(*mut Box<dyn FnMut() -> isize>) -> i32,
+ ),
+ ptr_aligned as *mut c_void,
+ combined,
+ &mut cb as *mut _ as *mut c_void,
+ )
+ };
+
+ Errno::result(res).map(Pid::from_raw)
+ }
+
+ /// disassociate parts of the process execution context
+ ///
+ /// See also [unshare(2)](https://man7.org/linux/man-pages/man2/unshare.2.html)
+ pub fn unshare(flags: CloneFlags) -> Result<()> {
+ let res = unsafe { libc::unshare(flags.bits()) };
+
+ Errno::result(res).map(drop)
+ }
+
+ /// reassociate thread with a namespace
+ ///
+ /// See also [setns(2)](https://man7.org/linux/man-pages/man2/setns.2.html)
+ pub fn setns(fd: RawFd, nstype: CloneFlags) -> Result<()> {
+ let res = unsafe { libc::setns(fd, nstype.bits()) };
+
+ Errno::result(res).map(drop)
+ }
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux"
+))]
+pub use self::sched_affinity::*;
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux"
+))]
+mod sched_affinity {
+ use crate::errno::Errno;
+ use crate::unistd::Pid;
+ use crate::Result;
+ use std::mem;
+
+ /// CpuSet represent a bit-mask of CPUs.
+ /// CpuSets are used by sched_setaffinity and
+ /// sched_getaffinity for example.
+ ///
+ /// This is a wrapper around `libc::cpu_set_t`.
+ #[repr(transparent)]
+ #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+ pub struct CpuSet {
+ #[cfg(not(target_os = "freebsd"))]
+ cpu_set: libc::cpu_set_t,
+ #[cfg(target_os = "freebsd")]
+ cpu_set: libc::cpuset_t,
+ }
+
+ impl CpuSet {
+ /// Create a new and empty CpuSet.
+ pub fn new() -> CpuSet {
+ CpuSet {
+ cpu_set: unsafe { mem::zeroed() },
+ }
+ }
+
+ /// Test to see if a CPU is in the CpuSet.
+ /// `field` is the CPU id to test
+ pub fn is_set(&self, field: usize) -> Result<bool> {
+ if field >= CpuSet::count() {
+ Err(Errno::EINVAL)
+ } else {
+ Ok(unsafe { libc::CPU_ISSET(field, &self.cpu_set) })
+ }
+ }
+
+ /// Add a CPU to CpuSet.
+ /// `field` is the CPU id to add
+ pub fn set(&mut self, field: usize) -> Result<()> {
+ if field >= CpuSet::count() {
+ Err(Errno::EINVAL)
+ } else {
+ unsafe {
+ libc::CPU_SET(field, &mut self.cpu_set);
+ }
+ Ok(())
+ }
+ }
+
+ /// Remove a CPU from CpuSet.
+ /// `field` is the CPU id to remove
+ pub fn unset(&mut self, field: usize) -> Result<()> {
+ if field >= CpuSet::count() {
+ Err(Errno::EINVAL)
+ } else {
+ unsafe {
+ libc::CPU_CLR(field, &mut self.cpu_set);
+ }
+ Ok(())
+ }
+ }
+
+ /// Return the maximum number of CPU in CpuSet
+ pub const fn count() -> usize {
+ #[cfg(not(target_os = "freebsd"))]
+ let bytes = mem::size_of::<libc::cpu_set_t>();
+ #[cfg(target_os = "freebsd")]
+ let bytes = mem::size_of::<libc::cpuset_t>();
+
+ 8 * bytes
+ }
+ }
+
+ impl Default for CpuSet {
+ fn default() -> Self {
+ Self::new()
+ }
+ }
+
+ /// `sched_setaffinity` set a thread's CPU affinity mask
+ /// ([`sched_setaffinity(2)`](https://man7.org/linux/man-pages/man2/sched_setaffinity.2.html))
+ ///
+ /// `pid` is the thread ID to update.
+ /// If pid is zero, then the calling thread is updated.
+ ///
+ /// The `cpuset` argument specifies the set of CPUs on which the thread
+ /// will be eligible to run.
+ ///
+ /// # Example
+ ///
+ /// Binding the current thread to CPU 0 can be done as follows:
+ ///
+ /// ```rust,no_run
+ /// use nix::sched::{CpuSet, sched_setaffinity};
+ /// use nix::unistd::Pid;
+ ///
+ /// let mut cpu_set = CpuSet::new();
+ /// cpu_set.set(0).unwrap();
+ /// sched_setaffinity(Pid::from_raw(0), &cpu_set).unwrap();
+ /// ```
+ pub fn sched_setaffinity(pid: Pid, cpuset: &CpuSet) -> Result<()> {
+ let res = unsafe {
+ libc::sched_setaffinity(
+ pid.into(),
+ mem::size_of::<CpuSet>() as libc::size_t,
+ &cpuset.cpu_set,
+ )
+ };
+
+ Errno::result(res).map(drop)
+ }
+
+ /// `sched_getaffinity` get a thread's CPU affinity mask
+ /// ([`sched_getaffinity(2)`](https://man7.org/linux/man-pages/man2/sched_getaffinity.2.html))
+ ///
+ /// `pid` is the thread ID to check.
+ /// If pid is zero, then the calling thread is checked.
+ ///
+ /// Returned `cpuset` is the set of CPUs on which the thread
+ /// is eligible to run.
+ ///
+ /// # Example
+ ///
+ /// Checking if the current thread can run on CPU 0 can be done as follows:
+ ///
+ /// ```rust,no_run
+ /// use nix::sched::sched_getaffinity;
+ /// use nix::unistd::Pid;
+ ///
+ /// let cpu_set = sched_getaffinity(Pid::from_raw(0)).unwrap();
+ /// if cpu_set.is_set(0).unwrap() {
+ /// println!("Current thread can run on CPU 0");
+ /// }
+ /// ```
+ pub fn sched_getaffinity(pid: Pid) -> Result<CpuSet> {
+ let mut cpuset = CpuSet::new();
+ let res = unsafe {
+ libc::sched_getaffinity(
+ pid.into(),
+ mem::size_of::<CpuSet>() as libc::size_t,
+ &mut cpuset.cpu_set,
+ )
+ };
+
+ Errno::result(res).and(Ok(cpuset))
+ }
+
+ /// Determines the CPU on which the calling thread is running.
+ pub fn sched_getcpu() -> Result<usize> {
+ let res = unsafe { libc::sched_getcpu() };
+
+ Errno::result(res).map(|int| int as usize)
+ }
+}
+
+/// Explicitly yield the processor to other threads.
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sched_yield.html)
+pub fn sched_yield() -> Result<()> {
+ let res = unsafe { libc::sched_yield() };
+
+ Errno::result(res).map(drop)
+}
diff --git a/third_party/rust/nix/src/sys/aio.rs b/third_party/rust/nix/src/sys/aio.rs
new file mode 100644
index 0000000000..e2ce19b79d
--- /dev/null
+++ b/third_party/rust/nix/src/sys/aio.rs
@@ -0,0 +1,1241 @@
+// vim: tw=80
+//! POSIX Asynchronous I/O
+//!
+//! The POSIX AIO interface is used for asynchronous I/O on files and disk-like
+//! devices. It supports [`read`](struct.AioRead.html#method.new),
+//! [`write`](struct.AioWrite.html#method.new),
+//! [`fsync`](struct.AioFsync.html#method.new),
+//! [`readv`](struct.AioReadv.html#method.new), and
+//! [`writev`](struct.AioWritev.html#method.new), operations, subject to
+//! platform support. Completion
+//! notifications can optionally be delivered via
+//! [signals](../signal/enum.SigevNotify.html#variant.SigevSignal), via the
+//! [`aio_suspend`](fn.aio_suspend.html) function, or via polling. Some
+//! platforms support other completion
+//! notifications, such as
+//! [kevent](../signal/enum.SigevNotify.html#variant.SigevKevent).
+//!
+//! Multiple operations may be submitted in a batch with
+//! [`lio_listio`](fn.lio_listio.html), though the standard does not guarantee
+//! that they will be executed atomically.
+//!
+//! Outstanding operations may be cancelled with
+//! [`cancel`](trait.Aio.html#method.cancel) or
+//! [`aio_cancel_all`](fn.aio_cancel_all.html), though the operating system may
+//! not support this for all filesystems and devices.
+#[cfg(target_os = "freebsd")]
+use std::io::{IoSlice, IoSliceMut};
+use std::{
+ convert::TryFrom,
+ fmt::{self, Debug},
+ marker::{PhantomData, PhantomPinned},
+ mem,
+ os::unix::io::RawFd,
+ pin::Pin,
+ ptr, thread,
+};
+
+use libc::{c_void, off_t};
+use pin_utils::unsafe_pinned;
+
+use crate::{
+ errno::Errno,
+ sys::{signal::*, time::TimeSpec},
+ Result,
+};
+
+libc_enum! {
+ /// Mode for `AioCb::fsync`. Controls whether only data or both data and
+ /// metadata are synced.
+ #[repr(i32)]
+ #[non_exhaustive]
+ pub enum AioFsyncMode {
+ /// do it like `fsync`
+ O_SYNC,
+ /// on supported operating systems only, do it like `fdatasync`
+ #[cfg(any(target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ O_DSYNC
+ }
+ impl TryFrom<i32>
+}
+
+libc_enum! {
+ /// Mode for [`lio_listio`](fn.lio_listio.html)
+ #[repr(i32)]
+ pub enum LioMode {
+ /// Requests that [`lio_listio`](fn.lio_listio.html) block until all
+ /// requested operations have been completed
+ LIO_WAIT,
+ /// Requests that [`lio_listio`](fn.lio_listio.html) return immediately
+ LIO_NOWAIT,
+ }
+}
+
+/// Return values for [`AioCb::cancel`](struct.AioCb.html#method.cancel) and
+/// [`aio_cancel_all`](fn.aio_cancel_all.html)
+#[repr(i32)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub enum AioCancelStat {
+ /// All outstanding requests were canceled
+ AioCanceled = libc::AIO_CANCELED,
+ /// Some requests were not canceled. Their status should be checked with
+ /// `AioCb::error`
+ AioNotCanceled = libc::AIO_NOTCANCELED,
+ /// All of the requests have already finished
+ AioAllDone = libc::AIO_ALLDONE,
+}
+
+/// Newtype that adds Send and Sync to libc::aiocb, which contains raw pointers
+#[repr(transparent)]
+struct LibcAiocb(libc::aiocb);
+
+unsafe impl Send for LibcAiocb {}
+unsafe impl Sync for LibcAiocb {}
+
+/// Base class for all AIO operations. Should only be used directly when
+/// checking for completion.
+// We could create some kind of AsPinnedMut trait, and implement it for all aio
+// ops, allowing the crate's users to get pinned references to `AioCb`. That
+// could save some code for things like polling methods. But IMHO it would
+// provide polymorphism at the wrong level. Instead, the best place for
+// polymorphism is at the level of `Futures`.
+#[repr(C)]
+struct AioCb {
+ aiocb: LibcAiocb,
+ /// Could this `AioCb` potentially have any in-kernel state?
+ // It would be really nice to perform the in-progress check entirely at
+ // compile time. But I can't figure out how, because:
+ // * Future::poll takes a `Pin<&mut self>` rather than `self`, and
+ // * Rust's lack of an equivalent of C++'s Guaranteed Copy Elision means
+ // that there's no way to write an AioCb constructor that neither boxes
+ // the object itself, nor moves it during return.
+ in_progress: bool,
+}
+
+impl AioCb {
+ pin_utils::unsafe_unpinned!(aiocb: LibcAiocb);
+
+ fn aio_return(mut self: Pin<&mut Self>) -> Result<usize> {
+ self.in_progress = false;
+ unsafe {
+ let p: *mut libc::aiocb = &mut self.aiocb.0;
+ Errno::result(libc::aio_return(p))
+ }
+ .map(|r| r as usize)
+ }
+
+ fn cancel(mut self: Pin<&mut Self>) -> Result<AioCancelStat> {
+ let r = unsafe {
+ libc::aio_cancel(self.aiocb.0.aio_fildes, &mut self.aiocb.0)
+ };
+ match r {
+ libc::AIO_CANCELED => Ok(AioCancelStat::AioCanceled),
+ libc::AIO_NOTCANCELED => Ok(AioCancelStat::AioNotCanceled),
+ libc::AIO_ALLDONE => Ok(AioCancelStat::AioAllDone),
+ -1 => Err(Errno::last()),
+ _ => panic!("unknown aio_cancel return value"),
+ }
+ }
+
+ fn common_init(fd: RawFd, prio: i32, sigev_notify: SigevNotify) -> Self {
+ // Use mem::zeroed instead of explicitly zeroing each field, because the
+ // number and name of reserved fields is OS-dependent. On some OSes,
+ // some reserved fields are used the kernel for state, and must be
+ // explicitly zeroed when allocated.
+ let mut a = unsafe { mem::zeroed::<libc::aiocb>() };
+ a.aio_fildes = fd;
+ a.aio_reqprio = prio;
+ a.aio_sigevent = SigEvent::new(sigev_notify).sigevent();
+ AioCb {
+ aiocb: LibcAiocb(a),
+ in_progress: false,
+ }
+ }
+
+ fn error(self: Pin<&mut Self>) -> Result<()> {
+ let r = unsafe { libc::aio_error(&self.aiocb().0) };
+ match r {
+ 0 => Ok(()),
+ num if num > 0 => Err(Errno::from_i32(num)),
+ -1 => Err(Errno::last()),
+ num => panic!("unknown aio_error return value {:?}", num),
+ }
+ }
+
+ fn in_progress(&self) -> bool {
+ self.in_progress
+ }
+
+ fn set_in_progress(mut self: Pin<&mut Self>) {
+ self.as_mut().in_progress = true;
+ }
+
+ /// Update the notification settings for an existing AIO operation that has
+ /// not yet been submitted.
+ // Takes a normal reference rather than a pinned one because this method is
+ // normally called before the object needs to be pinned, that is, before
+ // it's been submitted to the kernel.
+ fn set_sigev_notify(&mut self, sigev_notify: SigevNotify) {
+ assert!(
+ !self.in_progress,
+ "Can't change notification settings for an in-progress operation"
+ );
+ self.aiocb.0.aio_sigevent = SigEvent::new(sigev_notify).sigevent();
+ }
+}
+
+impl Debug for AioCb {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ fmt.debug_struct("AioCb")
+ .field("aiocb", &self.aiocb.0)
+ .field("in_progress", &self.in_progress)
+ .finish()
+ }
+}
+
+impl Drop for AioCb {
+ /// If the `AioCb` has no remaining state in the kernel, just drop it.
+ /// Otherwise, dropping constitutes a resource leak, which is an error
+ fn drop(&mut self) {
+ assert!(
+ thread::panicking() || !self.in_progress,
+ "Dropped an in-progress AioCb"
+ );
+ }
+}
+
+/// Methods common to all AIO operations
+pub trait Aio {
+ /// The return type of [`Aio::aio_return`].
+ type Output;
+
+ /// Retrieve return status of an asynchronous operation.
+ ///
+ /// Should only be called once for each operation, after [`Aio::error`]
+ /// indicates that it has completed. The result is the same as for the
+ /// synchronous `read(2)`, `write(2)`, of `fsync(2)` functions.
+ ///
+ /// # References
+ ///
+ /// [aio_return](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_return.html)
+ fn aio_return(self: Pin<&mut Self>) -> Result<Self::Output>;
+
+ /// Cancels an outstanding AIO request.
+ ///
+ /// The operating system is not required to implement cancellation for all
+ /// file and device types. Even if it does, there is no guarantee that the
+ /// operation has not already completed. So the caller must check the
+ /// result and handle operations that were not canceled or that have already
+ /// completed.
+ ///
+ /// # Examples
+ ///
+ /// Cancel an outstanding aio operation. Note that we must still call
+ /// `aio_return` to free resources, even though we don't care about the
+ /// result.
+ ///
+ /// ```
+ /// # use nix::errno::Errno;
+ /// # use nix::Error;
+ /// # use nix::sys::aio::*;
+ /// # use nix::sys::signal::SigevNotify;
+ /// # use std::{thread, time};
+ /// # use std::io::Write;
+ /// # use std::os::unix::io::AsRawFd;
+ /// # use tempfile::tempfile;
+ /// let wbuf = b"CDEF";
+ /// let mut f = tempfile().unwrap();
+ /// let mut aiocb = Box::pin(AioWrite::new(f.as_raw_fd(),
+ /// 2, //offset
+ /// &wbuf[..],
+ /// 0, //priority
+ /// SigevNotify::SigevNone));
+ /// aiocb.as_mut().submit().unwrap();
+ /// let cs = aiocb.as_mut().cancel().unwrap();
+ /// if cs == AioCancelStat::AioNotCanceled {
+ /// while (aiocb.as_mut().error() == Err(Errno::EINPROGRESS)) {
+ /// thread::sleep(time::Duration::from_millis(10));
+ /// }
+ /// }
+ /// // Must call `aio_return`, but ignore the result
+ /// let _ = aiocb.as_mut().aio_return();
+ /// ```
+ ///
+ /// # References
+ ///
+ /// [aio_cancel](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_cancel.html)
+ fn cancel(self: Pin<&mut Self>) -> Result<AioCancelStat>;
+
+ /// Retrieve error status of an asynchronous operation.
+ ///
+ /// If the request has not yet completed, returns `EINPROGRESS`. Otherwise,
+ /// returns `Ok` or any other error.
+ ///
+ /// # Examples
+ ///
+ /// Issue an aio operation and use `error` to poll for completion. Polling
+ /// is an alternative to `aio_suspend`, used by most of the other examples.
+ ///
+ /// ```
+ /// # use nix::errno::Errno;
+ /// # use nix::Error;
+ /// # use nix::sys::aio::*;
+ /// # use nix::sys::signal::SigevNotify;
+ /// # use std::{thread, time};
+ /// # use std::os::unix::io::AsRawFd;
+ /// # use tempfile::tempfile;
+ /// const WBUF: &[u8] = b"abcdef123456";
+ /// let mut f = tempfile().unwrap();
+ /// let mut aiocb = Box::pin(AioWrite::new(f.as_raw_fd(),
+ /// 2, //offset
+ /// WBUF,
+ /// 0, //priority
+ /// SigevNotify::SigevNone));
+ /// aiocb.as_mut().submit().unwrap();
+ /// while (aiocb.as_mut().error() == Err(Errno::EINPROGRESS)) {
+ /// thread::sleep(time::Duration::from_millis(10));
+ /// }
+ /// assert_eq!(aiocb.as_mut().aio_return().unwrap(), WBUF.len());
+ /// ```
+ ///
+ /// # References
+ ///
+ /// [aio_error](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_error.html)
+ fn error(self: Pin<&mut Self>) -> Result<()>;
+
+ /// Returns the underlying file descriptor associated with the operation.
+ fn fd(&self) -> RawFd;
+
+ /// Does this operation currently have any in-kernel state?
+ ///
+ /// Dropping an operation that does have in-kernel state constitutes a
+ /// resource leak.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use nix::errno::Errno;
+ /// # use nix::Error;
+ /// # use nix::sys::aio::*;
+ /// # use nix::sys::signal::SigevNotify::SigevNone;
+ /// # use std::{thread, time};
+ /// # use std::os::unix::io::AsRawFd;
+ /// # use tempfile::tempfile;
+ /// let f = tempfile().unwrap();
+ /// let mut aiof = Box::pin(AioFsync::new(f.as_raw_fd(), AioFsyncMode::O_SYNC,
+ /// 0, SigevNone));
+ /// assert!(!aiof.as_mut().in_progress());
+ /// aiof.as_mut().submit().expect("aio_fsync failed early");
+ /// assert!(aiof.as_mut().in_progress());
+ /// while (aiof.as_mut().error() == Err(Errno::EINPROGRESS)) {
+ /// thread::sleep(time::Duration::from_millis(10));
+ /// }
+ /// aiof.as_mut().aio_return().expect("aio_fsync failed late");
+ /// assert!(!aiof.as_mut().in_progress());
+ /// ```
+ fn in_progress(&self) -> bool;
+
+ /// Returns the priority of the `AioCb`
+ fn priority(&self) -> i32;
+
+ /// Update the notification settings for an existing AIO operation that has
+ /// not yet been submitted.
+ fn set_sigev_notify(&mut self, sev: SigevNotify);
+
+ /// Returns the `SigEvent` that will be used for notification.
+ fn sigevent(&self) -> SigEvent;
+
+ /// Actually start the I/O operation.
+ ///
+ /// After calling this method and until [`Aio::aio_return`] returns `Ok`,
+ /// the structure may not be moved in memory.
+ fn submit(self: Pin<&mut Self>) -> Result<()>;
+}
+
+macro_rules! aio_methods {
+ () => {
+ fn cancel(self: Pin<&mut Self>) -> Result<AioCancelStat> {
+ self.aiocb().cancel()
+ }
+
+ fn error(self: Pin<&mut Self>) -> Result<()> {
+ self.aiocb().error()
+ }
+
+ fn fd(&self) -> RawFd {
+ self.aiocb.aiocb.0.aio_fildes
+ }
+
+ fn in_progress(&self) -> bool {
+ self.aiocb.in_progress()
+ }
+
+ fn priority(&self) -> i32 {
+ self.aiocb.aiocb.0.aio_reqprio
+ }
+
+ fn set_sigev_notify(&mut self, sev: SigevNotify) {
+ self.aiocb.set_sigev_notify(sev)
+ }
+
+ fn sigevent(&self) -> SigEvent {
+ SigEvent::from(&self.aiocb.aiocb.0.aio_sigevent)
+ }
+ };
+ ($func:ident) => {
+ aio_methods!();
+
+ fn aio_return(self: Pin<&mut Self>) -> Result<<Self as Aio>::Output> {
+ self.aiocb().aio_return()
+ }
+
+ fn submit(mut self: Pin<&mut Self>) -> Result<()> {
+ let p: *mut libc::aiocb = &mut self.as_mut().aiocb().aiocb.0;
+ Errno::result({ unsafe { libc::$func(p) } }).map(|_| {
+ self.aiocb().set_in_progress();
+ })
+ }
+ };
+}
+
+/// An asynchronous version of `fsync(2)`.
+///
+/// # References
+///
+/// [aio_fsync](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_fsync.html)
+/// # Examples
+///
+/// ```
+/// # use nix::errno::Errno;
+/// # use nix::Error;
+/// # use nix::sys::aio::*;
+/// # use nix::sys::signal::SigevNotify::SigevNone;
+/// # use std::{thread, time};
+/// # use std::os::unix::io::AsRawFd;
+/// # use tempfile::tempfile;
+/// let f = tempfile().unwrap();
+/// let mut aiof = Box::pin(AioFsync::new(f.as_raw_fd(), AioFsyncMode::O_SYNC,
+/// 0, SigevNone));
+/// aiof.as_mut().submit().expect("aio_fsync failed early");
+/// while (aiof.as_mut().error() == Err(Errno::EINPROGRESS)) {
+/// thread::sleep(time::Duration::from_millis(10));
+/// }
+/// aiof.as_mut().aio_return().expect("aio_fsync failed late");
+/// ```
+#[derive(Debug)]
+#[repr(transparent)]
+pub struct AioFsync {
+ aiocb: AioCb,
+ _pin: PhantomPinned,
+}
+
+impl AioFsync {
+ unsafe_pinned!(aiocb: AioCb);
+
+ /// Returns the operation's fsync mode: data and metadata or data only?
+ pub fn mode(&self) -> AioFsyncMode {
+ AioFsyncMode::try_from(self.aiocb.aiocb.0.aio_lio_opcode).unwrap()
+ }
+
+ /// Create a new `AioFsync`.
+ ///
+ /// # Arguments
+ ///
+ /// * `fd`: File descriptor to sync.
+ /// * `mode`: Whether to sync file metadata too, or just data.
+ /// * `prio`: If POSIX Prioritized IO is supported, then the
+ /// operation will be prioritized at the process's
+ /// priority level minus `prio`.
+ /// * `sigev_notify`: Determines how you will be notified of event
+ /// completion.
+ pub fn new(
+ fd: RawFd,
+ mode: AioFsyncMode,
+ prio: i32,
+ sigev_notify: SigevNotify,
+ ) -> Self {
+ let mut aiocb = AioCb::common_init(fd, prio, sigev_notify);
+ // To save some memory, store mode in an unused field of the AioCb.
+ // True it isn't very much memory, but downstream creates will likely
+ // create an enum containing this and other AioCb variants and pack
+ // those enums into data structures like Vec, so it adds up.
+ aiocb.aiocb.0.aio_lio_opcode = mode as libc::c_int;
+ AioFsync {
+ aiocb,
+ _pin: PhantomPinned,
+ }
+ }
+}
+
+impl Aio for AioFsync {
+ type Output = ();
+
+ aio_methods!();
+
+ fn aio_return(self: Pin<&mut Self>) -> Result<()> {
+ self.aiocb().aio_return().map(drop)
+ }
+
+ fn submit(mut self: Pin<&mut Self>) -> Result<()> {
+ let aiocb = &mut self.as_mut().aiocb().aiocb.0;
+ let mode = mem::replace(&mut aiocb.aio_lio_opcode, 0);
+ let p: *mut libc::aiocb = aiocb;
+ Errno::result(unsafe { libc::aio_fsync(mode, p) }).map(|_| {
+ self.aiocb().set_in_progress();
+ })
+ }
+}
+
+// AioFsync does not need AsMut, since it can't be used with lio_listio
+
+impl AsRef<libc::aiocb> for AioFsync {
+ fn as_ref(&self) -> &libc::aiocb {
+ &self.aiocb.aiocb.0
+ }
+}
+
+/// Asynchronously reads from a file descriptor into a buffer
+///
+/// # References
+///
+/// [aio_read](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_read.html)
+///
+/// # Examples
+///
+///
+/// ```
+/// # use nix::errno::Errno;
+/// # use nix::Error;
+/// # use nix::sys::aio::*;
+/// # use nix::sys::signal::SigevNotify;
+/// # use std::{thread, time};
+/// # use std::io::Write;
+/// # use std::os::unix::io::AsRawFd;
+/// # use tempfile::tempfile;
+/// const INITIAL: &[u8] = b"abcdef123456";
+/// const LEN: usize = 4;
+/// let mut rbuf = vec![0; LEN];
+/// let mut f = tempfile().unwrap();
+/// f.write_all(INITIAL).unwrap();
+/// {
+/// let mut aior = Box::pin(
+/// AioRead::new(
+/// f.as_raw_fd(),
+/// 2, //offset
+/// &mut rbuf,
+/// 0, //priority
+/// SigevNotify::SigevNone
+/// )
+/// );
+/// aior.as_mut().submit().unwrap();
+/// while (aior.as_mut().error() == Err(Errno::EINPROGRESS)) {
+/// thread::sleep(time::Duration::from_millis(10));
+/// }
+/// assert_eq!(aior.as_mut().aio_return().unwrap(), LEN);
+/// }
+/// assert_eq!(rbuf, b"cdef");
+/// ```
+#[derive(Debug)]
+#[repr(transparent)]
+pub struct AioRead<'a> {
+ aiocb: AioCb,
+ _data: PhantomData<&'a [u8]>,
+ _pin: PhantomPinned,
+}
+
+impl<'a> AioRead<'a> {
+ unsafe_pinned!(aiocb: AioCb);
+
+ /// Returns the requested length of the aio operation in bytes
+ ///
+ /// This method returns the *requested* length of the operation. To get the
+ /// number of bytes actually read or written by a completed operation, use
+ /// `aio_return` instead.
+ pub fn nbytes(&self) -> usize {
+ self.aiocb.aiocb.0.aio_nbytes
+ }
+
+ /// Create a new `AioRead`, placing the data in a mutable slice.
+ ///
+ /// # Arguments
+ ///
+ /// * `fd`: File descriptor to read from
+ /// * `offs`: File offset
+ /// * `buf`: A memory buffer. It must outlive the `AioRead`.
+ /// * `prio`: If POSIX Prioritized IO is supported, then the
+ /// operation will be prioritized at the process's
+ /// priority level minus `prio`
+ /// * `sigev_notify`: Determines how you will be notified of event
+ /// completion.
+ pub fn new(
+ fd: RawFd,
+ offs: off_t,
+ buf: &'a mut [u8],
+ prio: i32,
+ sigev_notify: SigevNotify,
+ ) -> Self {
+ let mut aiocb = AioCb::common_init(fd, prio, sigev_notify);
+ aiocb.aiocb.0.aio_nbytes = buf.len();
+ aiocb.aiocb.0.aio_buf = buf.as_mut_ptr() as *mut c_void;
+ aiocb.aiocb.0.aio_lio_opcode = libc::LIO_READ;
+ aiocb.aiocb.0.aio_offset = offs;
+ AioRead {
+ aiocb,
+ _data: PhantomData,
+ _pin: PhantomPinned,
+ }
+ }
+
+ /// Returns the file offset of the operation.
+ pub fn offset(&self) -> off_t {
+ self.aiocb.aiocb.0.aio_offset
+ }
+}
+
+impl<'a> Aio for AioRead<'a> {
+ type Output = usize;
+
+ aio_methods!(aio_read);
+}
+
+impl<'a> AsMut<libc::aiocb> for AioRead<'a> {
+ fn as_mut(&mut self) -> &mut libc::aiocb {
+ &mut self.aiocb.aiocb.0
+ }
+}
+
+impl<'a> AsRef<libc::aiocb> for AioRead<'a> {
+ fn as_ref(&self) -> &libc::aiocb {
+ &self.aiocb.aiocb.0
+ }
+}
+
+/// Asynchronously reads from a file descriptor into a scatter/gather list of buffers.
+///
+/// # References
+///
+/// [aio_readv](https://www.freebsd.org/cgi/man.cgi?query=aio_readv)
+///
+/// # Examples
+///
+///
+#[cfg_attr(fbsd14, doc = " ```")]
+#[cfg_attr(not(fbsd14), doc = " ```no_run")]
+/// # use nix::errno::Errno;
+/// # use nix::Error;
+/// # use nix::sys::aio::*;
+/// # use nix::sys::signal::SigevNotify;
+/// # use std::{thread, time};
+/// # use std::io::{IoSliceMut, Write};
+/// # use std::os::unix::io::AsRawFd;
+/// # use tempfile::tempfile;
+/// const INITIAL: &[u8] = b"abcdef123456";
+/// let mut rbuf0 = vec![0; 4];
+/// let mut rbuf1 = vec![0; 2];
+/// let expected_len = rbuf0.len() + rbuf1.len();
+/// let mut rbufs = [IoSliceMut::new(&mut rbuf0), IoSliceMut::new(&mut rbuf1)];
+/// let mut f = tempfile().unwrap();
+/// f.write_all(INITIAL).unwrap();
+/// {
+/// let mut aior = Box::pin(
+/// AioReadv::new(
+/// f.as_raw_fd(),
+/// 2, //offset
+/// &mut rbufs,
+/// 0, //priority
+/// SigevNotify::SigevNone
+/// )
+/// );
+/// aior.as_mut().submit().unwrap();
+/// while (aior.as_mut().error() == Err(Errno::EINPROGRESS)) {
+/// thread::sleep(time::Duration::from_millis(10));
+/// }
+/// assert_eq!(aior.as_mut().aio_return().unwrap(), expected_len);
+/// }
+/// assert_eq!(rbuf0, b"cdef");
+/// assert_eq!(rbuf1, b"12");
+/// ```
+#[cfg(target_os = "freebsd")]
+#[derive(Debug)]
+#[repr(transparent)]
+pub struct AioReadv<'a> {
+ aiocb: AioCb,
+ _data: PhantomData<&'a [&'a [u8]]>,
+ _pin: PhantomPinned,
+}
+
+#[cfg(target_os = "freebsd")]
+impl<'a> AioReadv<'a> {
+ unsafe_pinned!(aiocb: AioCb);
+
+ /// Returns the number of buffers the operation will read into.
+ pub fn iovlen(&self) -> usize {
+ self.aiocb.aiocb.0.aio_nbytes
+ }
+
+ /// Create a new `AioReadv`, placing the data in a list of mutable slices.
+ ///
+ /// # Arguments
+ ///
+ /// * `fd`: File descriptor to read from
+ /// * `offs`: File offset
+ /// * `bufs`: A scatter/gather list of memory buffers. They must
+ /// outlive the `AioReadv`.
+ /// * `prio`: If POSIX Prioritized IO is supported, then the
+ /// operation will be prioritized at the process's
+ /// priority level minus `prio`
+ /// * `sigev_notify`: Determines how you will be notified of event
+ /// completion.
+ pub fn new(
+ fd: RawFd,
+ offs: off_t,
+ bufs: &mut [IoSliceMut<'a>],
+ prio: i32,
+ sigev_notify: SigevNotify,
+ ) -> Self {
+ let mut aiocb = AioCb::common_init(fd, prio, sigev_notify);
+ // In vectored mode, aio_nbytes stores the length of the iovec array,
+ // not the byte count.
+ aiocb.aiocb.0.aio_nbytes = bufs.len();
+ aiocb.aiocb.0.aio_buf = bufs.as_mut_ptr() as *mut c_void;
+ aiocb.aiocb.0.aio_lio_opcode = libc::LIO_READV;
+ aiocb.aiocb.0.aio_offset = offs;
+ AioReadv {
+ aiocb,
+ _data: PhantomData,
+ _pin: PhantomPinned,
+ }
+ }
+
+ /// Returns the file offset of the operation.
+ pub fn offset(&self) -> off_t {
+ self.aiocb.aiocb.0.aio_offset
+ }
+}
+
+#[cfg(target_os = "freebsd")]
+impl<'a> Aio for AioReadv<'a> {
+ type Output = usize;
+
+ aio_methods!(aio_readv);
+}
+
+#[cfg(target_os = "freebsd")]
+impl<'a> AsMut<libc::aiocb> for AioReadv<'a> {
+ fn as_mut(&mut self) -> &mut libc::aiocb {
+ &mut self.aiocb.aiocb.0
+ }
+}
+
+#[cfg(target_os = "freebsd")]
+impl<'a> AsRef<libc::aiocb> for AioReadv<'a> {
+ fn as_ref(&self) -> &libc::aiocb {
+ &self.aiocb.aiocb.0
+ }
+}
+
+/// Asynchronously writes from a buffer to a file descriptor
+///
+/// # References
+///
+/// [aio_write](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_write.html)
+///
+/// # Examples
+///
+/// ```
+/// # use nix::errno::Errno;
+/// # use nix::Error;
+/// # use nix::sys::aio::*;
+/// # use nix::sys::signal::SigevNotify;
+/// # use std::{thread, time};
+/// # use std::os::unix::io::AsRawFd;
+/// # use tempfile::tempfile;
+/// const WBUF: &[u8] = b"abcdef123456";
+/// let mut f = tempfile().unwrap();
+/// let mut aiow = Box::pin(
+/// AioWrite::new(
+/// f.as_raw_fd(),
+/// 2, //offset
+/// WBUF,
+/// 0, //priority
+/// SigevNotify::SigevNone
+/// )
+/// );
+/// aiow.as_mut().submit().unwrap();
+/// while (aiow.as_mut().error() == Err(Errno::EINPROGRESS)) {
+/// thread::sleep(time::Duration::from_millis(10));
+/// }
+/// assert_eq!(aiow.as_mut().aio_return().unwrap(), WBUF.len());
+/// ```
+#[derive(Debug)]
+#[repr(transparent)]
+pub struct AioWrite<'a> {
+ aiocb: AioCb,
+ _data: PhantomData<&'a [u8]>,
+ _pin: PhantomPinned,
+}
+
+impl<'a> AioWrite<'a> {
+ unsafe_pinned!(aiocb: AioCb);
+
+ /// Returns the requested length of the aio operation in bytes
+ ///
+ /// This method returns the *requested* length of the operation. To get the
+ /// number of bytes actually read or written by a completed operation, use
+ /// `aio_return` instead.
+ pub fn nbytes(&self) -> usize {
+ self.aiocb.aiocb.0.aio_nbytes
+ }
+
+ /// Construct a new `AioWrite`.
+ ///
+ /// # Arguments
+ ///
+ /// * `fd`: File descriptor to write to
+ /// * `offs`: File offset
+ /// * `buf`: A memory buffer. It must outlive the `AioWrite`.
+ /// * `prio`: If POSIX Prioritized IO is supported, then the
+ /// operation will be prioritized at the process's
+ /// priority level minus `prio`
+ /// * `sigev_notify`: Determines how you will be notified of event
+ /// completion.
+ pub fn new(
+ fd: RawFd,
+ offs: off_t,
+ buf: &'a [u8],
+ prio: i32,
+ sigev_notify: SigevNotify,
+ ) -> Self {
+ let mut aiocb = AioCb::common_init(fd, prio, sigev_notify);
+ aiocb.aiocb.0.aio_nbytes = buf.len();
+ // casting an immutable buffer to a mutable pointer looks unsafe,
+ // but technically its only unsafe to dereference it, not to create
+ // it. Type Safety guarantees that we'll never pass aiocb to
+ // aio_read or aio_readv.
+ aiocb.aiocb.0.aio_buf = buf.as_ptr() as *mut c_void;
+ aiocb.aiocb.0.aio_lio_opcode = libc::LIO_WRITE;
+ aiocb.aiocb.0.aio_offset = offs;
+ AioWrite {
+ aiocb,
+ _data: PhantomData,
+ _pin: PhantomPinned,
+ }
+ }
+
+ /// Returns the file offset of the operation.
+ pub fn offset(&self) -> off_t {
+ self.aiocb.aiocb.0.aio_offset
+ }
+}
+
+impl<'a> Aio for AioWrite<'a> {
+ type Output = usize;
+
+ aio_methods!(aio_write);
+}
+
+impl<'a> AsMut<libc::aiocb> for AioWrite<'a> {
+ fn as_mut(&mut self) -> &mut libc::aiocb {
+ &mut self.aiocb.aiocb.0
+ }
+}
+
+impl<'a> AsRef<libc::aiocb> for AioWrite<'a> {
+ fn as_ref(&self) -> &libc::aiocb {
+ &self.aiocb.aiocb.0
+ }
+}
+
+/// Asynchronously writes from a scatter/gather list of buffers to a file descriptor.
+///
+/// # References
+///
+/// [aio_writev](https://www.freebsd.org/cgi/man.cgi?query=aio_writev)
+///
+/// # Examples
+///
+#[cfg_attr(fbsd14, doc = " ```")]
+#[cfg_attr(not(fbsd14), doc = " ```no_run")]
+/// # use nix::errno::Errno;
+/// # use nix::Error;
+/// # use nix::sys::aio::*;
+/// # use nix::sys::signal::SigevNotify;
+/// # use std::{thread, time};
+/// # use std::io::IoSlice;
+/// # use std::os::unix::io::AsRawFd;
+/// # use tempfile::tempfile;
+/// const wbuf0: &[u8] = b"abcdef";
+/// const wbuf1: &[u8] = b"123456";
+/// let len = wbuf0.len() + wbuf1.len();
+/// let wbufs = [IoSlice::new(wbuf0), IoSlice::new(wbuf1)];
+/// let mut f = tempfile().unwrap();
+/// let mut aiow = Box::pin(
+/// AioWritev::new(
+/// f.as_raw_fd(),
+/// 2, //offset
+/// &wbufs,
+/// 0, //priority
+/// SigevNotify::SigevNone
+/// )
+/// );
+/// aiow.as_mut().submit().unwrap();
+/// while (aiow.as_mut().error() == Err(Errno::EINPROGRESS)) {
+/// thread::sleep(time::Duration::from_millis(10));
+/// }
+/// assert_eq!(aiow.as_mut().aio_return().unwrap(), len);
+/// ```
+#[cfg(target_os = "freebsd")]
+#[derive(Debug)]
+#[repr(transparent)]
+pub struct AioWritev<'a> {
+ aiocb: AioCb,
+ _data: PhantomData<&'a [&'a [u8]]>,
+ _pin: PhantomPinned,
+}
+
+#[cfg(target_os = "freebsd")]
+impl<'a> AioWritev<'a> {
+ unsafe_pinned!(aiocb: AioCb);
+
+ /// Returns the number of buffers the operation will read into.
+ pub fn iovlen(&self) -> usize {
+ self.aiocb.aiocb.0.aio_nbytes
+ }
+
+ /// Construct a new `AioWritev`.
+ ///
+ /// # Arguments
+ ///
+ /// * `fd`: File descriptor to write to
+ /// * `offs`: File offset
+ /// * `bufs`: A scatter/gather list of memory buffers. They must
+ /// outlive the `AioWritev`.
+ /// * `prio`: If POSIX Prioritized IO is supported, then the
+ /// operation will be prioritized at the process's
+ /// priority level minus `prio`
+ /// * `sigev_notify`: Determines how you will be notified of event
+ /// completion.
+ pub fn new(
+ fd: RawFd,
+ offs: off_t,
+ bufs: &[IoSlice<'a>],
+ prio: i32,
+ sigev_notify: SigevNotify,
+ ) -> Self {
+ let mut aiocb = AioCb::common_init(fd, prio, sigev_notify);
+ // In vectored mode, aio_nbytes stores the length of the iovec array,
+ // not the byte count.
+ aiocb.aiocb.0.aio_nbytes = bufs.len();
+ // casting an immutable buffer to a mutable pointer looks unsafe,
+ // but technically its only unsafe to dereference it, not to create
+ // it. Type Safety guarantees that we'll never pass aiocb to
+ // aio_read or aio_readv.
+ aiocb.aiocb.0.aio_buf = bufs.as_ptr() as *mut c_void;
+ aiocb.aiocb.0.aio_lio_opcode = libc::LIO_WRITEV;
+ aiocb.aiocb.0.aio_offset = offs;
+ AioWritev {
+ aiocb,
+ _data: PhantomData,
+ _pin: PhantomPinned,
+ }
+ }
+
+ /// Returns the file offset of the operation.
+ pub fn offset(&self) -> off_t {
+ self.aiocb.aiocb.0.aio_offset
+ }
+}
+
+#[cfg(target_os = "freebsd")]
+impl<'a> Aio for AioWritev<'a> {
+ type Output = usize;
+
+ aio_methods!(aio_writev);
+}
+
+#[cfg(target_os = "freebsd")]
+impl<'a> AsMut<libc::aiocb> for AioWritev<'a> {
+ fn as_mut(&mut self) -> &mut libc::aiocb {
+ &mut self.aiocb.aiocb.0
+ }
+}
+
+#[cfg(target_os = "freebsd")]
+impl<'a> AsRef<libc::aiocb> for AioWritev<'a> {
+ fn as_ref(&self) -> &libc::aiocb {
+ &self.aiocb.aiocb.0
+ }
+}
+
+/// Cancels outstanding AIO requests for a given file descriptor.
+///
+/// # Examples
+///
+/// Issue an aio operation, then cancel all outstanding operations on that file
+/// descriptor.
+///
+/// ```
+/// # use nix::errno::Errno;
+/// # use nix::Error;
+/// # use nix::sys::aio::*;
+/// # use nix::sys::signal::SigevNotify;
+/// # use std::{thread, time};
+/// # use std::io::Write;
+/// # use std::os::unix::io::AsRawFd;
+/// # use tempfile::tempfile;
+/// let wbuf = b"CDEF";
+/// let mut f = tempfile().unwrap();
+/// let mut aiocb = Box::pin(AioWrite::new(f.as_raw_fd(),
+/// 2, //offset
+/// &wbuf[..],
+/// 0, //priority
+/// SigevNotify::SigevNone));
+/// aiocb.as_mut().submit().unwrap();
+/// let cs = aio_cancel_all(f.as_raw_fd()).unwrap();
+/// if cs == AioCancelStat::AioNotCanceled {
+/// while (aiocb.as_mut().error() == Err(Errno::EINPROGRESS)) {
+/// thread::sleep(time::Duration::from_millis(10));
+/// }
+/// }
+/// // Must call `aio_return`, but ignore the result
+/// let _ = aiocb.as_mut().aio_return();
+/// ```
+///
+/// # References
+///
+/// [`aio_cancel`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_cancel.html)
+pub fn aio_cancel_all(fd: RawFd) -> Result<AioCancelStat> {
+ match unsafe { libc::aio_cancel(fd, ptr::null_mut()) } {
+ libc::AIO_CANCELED => Ok(AioCancelStat::AioCanceled),
+ libc::AIO_NOTCANCELED => Ok(AioCancelStat::AioNotCanceled),
+ libc::AIO_ALLDONE => Ok(AioCancelStat::AioAllDone),
+ -1 => Err(Errno::last()),
+ _ => panic!("unknown aio_cancel return value"),
+ }
+}
+
+/// Suspends the calling process until at least one of the specified operations
+/// have completed, a signal is delivered, or the timeout has passed.
+///
+/// If `timeout` is `None`, `aio_suspend` will block indefinitely.
+///
+/// # Examples
+///
+/// Use `aio_suspend` to block until an aio operation completes.
+///
+/// ```
+/// # use nix::sys::aio::*;
+/// # use nix::sys::signal::SigevNotify;
+/// # use std::os::unix::io::AsRawFd;
+/// # use tempfile::tempfile;
+/// const WBUF: &[u8] = b"abcdef123456";
+/// let mut f = tempfile().unwrap();
+/// let mut aiocb = Box::pin(AioWrite::new(f.as_raw_fd(),
+/// 2, //offset
+/// WBUF,
+/// 0, //priority
+/// SigevNotify::SigevNone));
+/// aiocb.as_mut().submit().unwrap();
+/// aio_suspend(&[&*aiocb], None).expect("aio_suspend failed");
+/// assert_eq!(aiocb.as_mut().aio_return().unwrap() as usize, WBUF.len());
+/// ```
+/// # References
+///
+/// [`aio_suspend`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/aio_suspend.html)
+pub fn aio_suspend(
+ list: &[&dyn AsRef<libc::aiocb>],
+ timeout: Option<TimeSpec>,
+) -> Result<()> {
+ let p = list as *const [&dyn AsRef<libc::aiocb>]
+ as *const [*const libc::aiocb] as *const *const libc::aiocb;
+ let timep = match timeout {
+ None => ptr::null::<libc::timespec>(),
+ Some(x) => x.as_ref() as *const libc::timespec,
+ };
+ Errno::result(unsafe { libc::aio_suspend(p, list.len() as i32, timep) })
+ .map(drop)
+}
+
+/// Submits multiple asynchronous I/O requests with a single system call.
+///
+/// They are not guaranteed to complete atomically, and the order in which the
+/// requests are carried out is not specified. Reads, and writes may be freely
+/// mixed.
+///
+/// # Examples
+///
+/// Use `lio_listio` to submit an aio operation and wait for its completion. In
+/// this case, there is no need to use aio_suspend to wait or `error` to poll.
+/// This mode is useful for otherwise-synchronous programs that want to execute
+/// a handful of I/O operations in parallel.
+/// ```
+/// # use std::os::unix::io::AsRawFd;
+/// # use nix::sys::aio::*;
+/// # use nix::sys::signal::SigevNotify;
+/// # use tempfile::tempfile;
+/// const WBUF: &[u8] = b"abcdef123456";
+/// let mut f = tempfile().unwrap();
+/// let mut aiow = Box::pin(AioWrite::new(
+/// f.as_raw_fd(),
+/// 2, // offset
+/// WBUF,
+/// 0, // priority
+/// SigevNotify::SigevNone
+/// ));
+/// lio_listio(LioMode::LIO_WAIT, &mut[aiow.as_mut()], SigevNotify::SigevNone)
+/// .unwrap();
+/// // At this point, we are guaranteed that aiow is complete.
+/// assert_eq!(aiow.as_mut().aio_return().unwrap(), WBUF.len());
+/// ```
+///
+/// Use `lio_listio` to submit multiple asynchronous operations with a single
+/// syscall, but receive notification individually. This is an efficient
+/// technique for reducing overall context-switch overhead, especially when
+/// combined with kqueue.
+/// ```
+/// # use std::os::unix::io::AsRawFd;
+/// # use std::thread;
+/// # use std::time;
+/// # use nix::errno::Errno;
+/// # use nix::sys::aio::*;
+/// # use nix::sys::signal::SigevNotify;
+/// # use tempfile::tempfile;
+/// const WBUF: &[u8] = b"abcdef123456";
+/// let mut f = tempfile().unwrap();
+/// let mut aiow = Box::pin(AioWrite::new(
+/// f.as_raw_fd(),
+/// 2, // offset
+/// WBUF,
+/// 0, // priority
+/// SigevNotify::SigevNone
+/// ));
+/// lio_listio(LioMode::LIO_NOWAIT, &mut[aiow.as_mut()], SigevNotify::SigevNone)
+/// .unwrap();
+/// // We must wait for the completion of each individual operation
+/// while (aiow.as_mut().error() == Err(Errno::EINPROGRESS)) {
+/// thread::sleep(time::Duration::from_millis(10));
+/// }
+/// assert_eq!(aiow.as_mut().aio_return().unwrap(), WBUF.len());
+/// ```
+///
+/// Use `lio_listio` to submit multiple operations, and receive notification
+/// only when all of them are complete. This can be useful when there is some
+/// logical relationship between the operations. But beware! Errors or system
+/// resource limitations may cause `lio_listio` to return `EIO`, `EAGAIN`, or
+/// `EINTR`, in which case some but not all operations may have been submitted.
+/// In that case, you must check the status of each individual operation, and
+/// possibly resubmit some.
+/// ```
+/// # use libc::c_int;
+/// # use std::os::unix::io::AsRawFd;
+/// # use std::sync::atomic::{AtomicBool, Ordering};
+/// # use std::thread;
+/// # use std::time;
+/// # use lazy_static::lazy_static;
+/// # use nix::errno::Errno;
+/// # use nix::sys::aio::*;
+/// # use nix::sys::signal::*;
+/// # use tempfile::tempfile;
+/// lazy_static! {
+/// pub static ref SIGNALED: AtomicBool = AtomicBool::new(false);
+/// }
+///
+/// extern fn sigfunc(_: c_int) {
+/// SIGNALED.store(true, Ordering::Relaxed);
+/// }
+/// let sa = SigAction::new(SigHandler::Handler(sigfunc),
+/// SaFlags::SA_RESETHAND,
+/// SigSet::empty());
+/// SIGNALED.store(false, Ordering::Relaxed);
+/// unsafe { sigaction(Signal::SIGUSR2, &sa) }.unwrap();
+///
+/// const WBUF: &[u8] = b"abcdef123456";
+/// let mut f = tempfile().unwrap();
+/// let mut aiow = Box::pin(AioWrite::new(
+/// f.as_raw_fd(),
+/// 2, // offset
+/// WBUF,
+/// 0, // priority
+/// SigevNotify::SigevNone
+/// ));
+/// let sev = SigevNotify::SigevSignal { signal: Signal::SIGUSR2, si_value: 0 };
+/// lio_listio(LioMode::LIO_NOWAIT, &mut[aiow.as_mut()], sev).unwrap();
+/// while !SIGNALED.load(Ordering::Relaxed) {
+/// thread::sleep(time::Duration::from_millis(10));
+/// }
+/// // At this point, since `lio_listio` returned success and delivered its
+/// // notification, we know that all operations are complete.
+/// assert_eq!(aiow.as_mut().aio_return().unwrap(), WBUF.len());
+/// ```
+pub fn lio_listio(
+ mode: LioMode,
+ list: &mut [Pin<&mut dyn AsMut<libc::aiocb>>],
+ sigev_notify: SigevNotify,
+) -> Result<()> {
+ let p = list as *mut [Pin<&mut dyn AsMut<libc::aiocb>>]
+ as *mut [*mut libc::aiocb] as *mut *mut libc::aiocb;
+ let sigev = SigEvent::new(sigev_notify);
+ let sigevp = &mut sigev.sigevent() as *mut libc::sigevent;
+ Errno::result(unsafe {
+ libc::lio_listio(mode as i32, p, list.len() as i32, sigevp)
+ })
+ .map(drop)
+}
+
+#[cfg(test)]
+mod t {
+ use super::*;
+
+ /// aio_suspend relies on casting Rust Aio* struct pointers to libc::aiocb
+ /// pointers. This test ensures that such casts are valid.
+ #[test]
+ fn casting() {
+ let sev = SigevNotify::SigevNone;
+ let aiof = AioFsync::new(666, AioFsyncMode::O_SYNC, 0, sev);
+ assert_eq!(
+ aiof.as_ref() as *const libc::aiocb,
+ &aiof as *const AioFsync as *const libc::aiocb
+ );
+
+ let mut rbuf = [];
+ let aior = AioRead::new(666, 0, &mut rbuf, 0, sev);
+ assert_eq!(
+ aior.as_ref() as *const libc::aiocb,
+ &aior as *const AioRead as *const libc::aiocb
+ );
+
+ let wbuf = [];
+ let aiow = AioWrite::new(666, 0, &wbuf, 0, sev);
+ assert_eq!(
+ aiow.as_ref() as *const libc::aiocb,
+ &aiow as *const AioWrite as *const libc::aiocb
+ );
+ }
+
+ #[cfg(target_os = "freebsd")]
+ #[test]
+ fn casting_vectored() {
+ let sev = SigevNotify::SigevNone;
+
+ let mut rbuf = [];
+ let mut rbufs = [IoSliceMut::new(&mut rbuf)];
+ let aiorv = AioReadv::new(666, 0, &mut rbufs[..], 0, sev);
+ assert_eq!(
+ aiorv.as_ref() as *const libc::aiocb,
+ &aiorv as *const AioReadv as *const libc::aiocb
+ );
+
+ let wbuf = [];
+ let wbufs = [IoSlice::new(&wbuf)];
+ let aiowv = AioWritev::new(666, 0, &wbufs, 0, sev);
+ assert_eq!(
+ aiowv.as_ref() as *const libc::aiocb,
+ &aiowv as *const AioWritev as *const libc::aiocb
+ );
+ }
+}
diff --git a/third_party/rust/nix/src/sys/epoll.rs b/third_party/rust/nix/src/sys/epoll.rs
new file mode 100644
index 0000000000..58def2e788
--- /dev/null
+++ b/third_party/rust/nix/src/sys/epoll.rs
@@ -0,0 +1,128 @@
+use crate::errno::Errno;
+use crate::Result;
+use libc::{self, c_int};
+use std::mem;
+use std::os::unix::io::RawFd;
+use std::ptr;
+
+libc_bitflags!(
+ pub struct EpollFlags: c_int {
+ EPOLLIN;
+ EPOLLPRI;
+ EPOLLOUT;
+ EPOLLRDNORM;
+ EPOLLRDBAND;
+ EPOLLWRNORM;
+ EPOLLWRBAND;
+ EPOLLMSG;
+ EPOLLERR;
+ EPOLLHUP;
+ EPOLLRDHUP;
+ EPOLLEXCLUSIVE;
+ #[cfg(not(target_arch = "mips"))]
+ EPOLLWAKEUP;
+ EPOLLONESHOT;
+ EPOLLET;
+ }
+);
+
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[repr(i32)]
+#[non_exhaustive]
+pub enum EpollOp {
+ EpollCtlAdd = libc::EPOLL_CTL_ADD,
+ EpollCtlDel = libc::EPOLL_CTL_DEL,
+ EpollCtlMod = libc::EPOLL_CTL_MOD,
+}
+
+libc_bitflags! {
+ pub struct EpollCreateFlags: c_int {
+ EPOLL_CLOEXEC;
+ }
+}
+
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[repr(transparent)]
+pub struct EpollEvent {
+ event: libc::epoll_event,
+}
+
+impl EpollEvent {
+ pub fn new(events: EpollFlags, data: u64) -> Self {
+ EpollEvent {
+ event: libc::epoll_event {
+ events: events.bits() as u32,
+ u64: data,
+ },
+ }
+ }
+
+ pub fn empty() -> Self {
+ unsafe { mem::zeroed::<EpollEvent>() }
+ }
+
+ pub fn events(&self) -> EpollFlags {
+ EpollFlags::from_bits(self.event.events as c_int).unwrap()
+ }
+
+ pub fn data(&self) -> u64 {
+ self.event.u64
+ }
+}
+
+#[inline]
+pub fn epoll_create() -> Result<RawFd> {
+ let res = unsafe { libc::epoll_create(1024) };
+
+ Errno::result(res)
+}
+
+#[inline]
+pub fn epoll_create1(flags: EpollCreateFlags) -> Result<RawFd> {
+ let res = unsafe { libc::epoll_create1(flags.bits()) };
+
+ Errno::result(res)
+}
+
+#[inline]
+pub fn epoll_ctl<'a, T>(
+ epfd: RawFd,
+ op: EpollOp,
+ fd: RawFd,
+ event: T,
+) -> Result<()>
+where
+ T: Into<Option<&'a mut EpollEvent>>,
+{
+ let mut event: Option<&mut EpollEvent> = event.into();
+ if event.is_none() && op != EpollOp::EpollCtlDel {
+ Err(Errno::EINVAL)
+ } else {
+ let res = unsafe {
+ if let Some(ref mut event) = event {
+ libc::epoll_ctl(epfd, op as c_int, fd, &mut event.event)
+ } else {
+ libc::epoll_ctl(epfd, op as c_int, fd, ptr::null_mut())
+ }
+ };
+ Errno::result(res).map(drop)
+ }
+}
+
+#[inline]
+pub fn epoll_wait(
+ epfd: RawFd,
+ events: &mut [EpollEvent],
+ timeout_ms: isize,
+) -> Result<usize> {
+ let res = unsafe {
+ libc::epoll_wait(
+ epfd,
+ events.as_mut_ptr() as *mut libc::epoll_event,
+ events.len() as c_int,
+ timeout_ms as c_int,
+ )
+ };
+
+ Errno::result(res).map(|r| r as usize)
+}
diff --git a/third_party/rust/nix/src/sys/event.rs b/third_party/rust/nix/src/sys/event.rs
new file mode 100644
index 0000000000..d8ad628ea2
--- /dev/null
+++ b/third_party/rust/nix/src/sys/event.rs
@@ -0,0 +1,374 @@
+/* TOOD: Implement for other kqueue based systems
+ */
+
+use crate::{Errno, Result};
+#[cfg(not(target_os = "netbsd"))]
+use libc::{c_int, c_long, intptr_t, time_t, timespec, uintptr_t};
+#[cfg(target_os = "netbsd")]
+use libc::{c_long, intptr_t, size_t, time_t, timespec, uintptr_t};
+use std::convert::TryInto;
+use std::mem;
+use std::os::unix::io::RawFd;
+use std::ptr;
+
+// Redefine kevent in terms of programmer-friendly enums and bitfields.
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct KEvent {
+ kevent: libc::kevent,
+}
+
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "openbsd"
+))]
+type type_of_udata = *mut libc::c_void;
+#[cfg(any(target_os = "netbsd"))]
+type type_of_udata = intptr_t;
+
+#[cfg(target_os = "netbsd")]
+type type_of_event_filter = u32;
+#[cfg(not(target_os = "netbsd"))]
+type type_of_event_filter = i16;
+libc_enum! {
+ #[cfg_attr(target_os = "netbsd", repr(u32))]
+ #[cfg_attr(not(target_os = "netbsd"), repr(i16))]
+ #[non_exhaustive]
+ pub enum EventFilter {
+ EVFILT_AIO,
+ /// Returns whenever there is no remaining data in the write buffer
+ #[cfg(target_os = "freebsd")]
+ EVFILT_EMPTY,
+ #[cfg(target_os = "dragonfly")]
+ EVFILT_EXCEPT,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos"))]
+ EVFILT_FS,
+ #[cfg(target_os = "freebsd")]
+ EVFILT_LIO,
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ EVFILT_MACHPORT,
+ EVFILT_PROC,
+ /// Returns events associated with the process referenced by a given
+ /// process descriptor, created by `pdfork()`. The events to monitor are:
+ ///
+ /// - NOTE_EXIT: the process has exited. The exit status will be stored in data.
+ #[cfg(target_os = "freebsd")]
+ EVFILT_PROCDESC,
+ EVFILT_READ,
+ /// Returns whenever an asynchronous `sendfile()` call completes.
+ #[cfg(target_os = "freebsd")]
+ EVFILT_SENDFILE,
+ EVFILT_SIGNAL,
+ EVFILT_TIMER,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos"))]
+ EVFILT_USER,
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ EVFILT_VM,
+ EVFILT_VNODE,
+ EVFILT_WRITE,
+ }
+ impl TryFrom<type_of_event_filter>
+}
+
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "openbsd"
+))]
+pub type type_of_event_flag = u16;
+#[cfg(any(target_os = "netbsd"))]
+pub type type_of_event_flag = u32;
+libc_bitflags! {
+ pub struct EventFlag: type_of_event_flag {
+ EV_ADD;
+ EV_CLEAR;
+ EV_DELETE;
+ EV_DISABLE;
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd",
+ target_os = "ios", target_os = "macos",
+ target_os = "netbsd", target_os = "openbsd"))]
+ EV_DISPATCH;
+ #[cfg(target_os = "freebsd")]
+ EV_DROP;
+ EV_ENABLE;
+ EV_EOF;
+ EV_ERROR;
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ EV_FLAG0;
+ EV_FLAG1;
+ #[cfg(target_os = "dragonfly")]
+ EV_NODATA;
+ EV_ONESHOT;
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ EV_OOBAND;
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ EV_POLL;
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd",
+ target_os = "ios", target_os = "macos",
+ target_os = "netbsd", target_os = "openbsd"))]
+ EV_RECEIPT;
+ }
+}
+
+libc_bitflags!(
+ pub struct FilterFlag: u32 {
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ NOTE_ABSOLUTE;
+ NOTE_ATTRIB;
+ NOTE_CHILD;
+ NOTE_DELETE;
+ #[cfg(target_os = "openbsd")]
+ NOTE_EOF;
+ NOTE_EXEC;
+ NOTE_EXIT;
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ NOTE_EXITSTATUS;
+ NOTE_EXTEND;
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "dragonfly"))]
+ NOTE_FFAND;
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "dragonfly"))]
+ NOTE_FFCOPY;
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "dragonfly"))]
+ NOTE_FFCTRLMASK;
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "dragonfly"))]
+ NOTE_FFLAGSMASK;
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "dragonfly"))]
+ NOTE_FFNOP;
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "dragonfly"))]
+ NOTE_FFOR;
+ NOTE_FORK;
+ NOTE_LINK;
+ NOTE_LOWAT;
+ #[cfg(target_os = "freebsd")]
+ NOTE_MSECONDS;
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ NOTE_NONE;
+ #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
+ NOTE_NSECONDS;
+ #[cfg(target_os = "dragonfly")]
+ NOTE_OOB;
+ NOTE_PCTRLMASK;
+ NOTE_PDATAMASK;
+ NOTE_RENAME;
+ NOTE_REVOKE;
+ #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
+ NOTE_SECONDS;
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ NOTE_SIGNAL;
+ NOTE_TRACK;
+ NOTE_TRACKERR;
+ #[cfg(any(target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "dragonfly"))]
+ NOTE_TRIGGER;
+ #[cfg(target_os = "openbsd")]
+ NOTE_TRUNCATE;
+ #[cfg(any(target_os = "macos", target_os = "ios", target_os = "freebsd"))]
+ NOTE_USECONDS;
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ NOTE_VM_ERROR;
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ NOTE_VM_PRESSURE;
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ NOTE_VM_PRESSURE_SUDDEN_TERMINATE;
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ NOTE_VM_PRESSURE_TERMINATE;
+ NOTE_WRITE;
+ }
+);
+
+pub fn kqueue() -> Result<RawFd> {
+ let res = unsafe { libc::kqueue() };
+
+ Errno::result(res)
+}
+
+// KEvent can't derive Send because on some operating systems, udata is defined
+// as a void*. However, KEvent's public API always treats udata as an intptr_t,
+// which is safe to Send.
+unsafe impl Send for KEvent {}
+
+impl KEvent {
+ #[allow(clippy::needless_update)] // Not needless on all platforms.
+ pub fn new(
+ ident: uintptr_t,
+ filter: EventFilter,
+ flags: EventFlag,
+ fflags: FilterFlag,
+ data: intptr_t,
+ udata: intptr_t,
+ ) -> KEvent {
+ KEvent {
+ kevent: libc::kevent {
+ ident,
+ filter: filter as type_of_event_filter,
+ flags: flags.bits(),
+ fflags: fflags.bits(),
+ // data can be either i64 or intptr_t, depending on platform
+ data: data as _,
+ udata: udata as type_of_udata,
+ ..unsafe { mem::zeroed() }
+ },
+ }
+ }
+
+ pub fn ident(&self) -> uintptr_t {
+ self.kevent.ident
+ }
+
+ pub fn filter(&self) -> Result<EventFilter> {
+ self.kevent.filter.try_into()
+ }
+
+ pub fn flags(&self) -> EventFlag {
+ EventFlag::from_bits(self.kevent.flags).unwrap()
+ }
+
+ pub fn fflags(&self) -> FilterFlag {
+ FilterFlag::from_bits(self.kevent.fflags).unwrap()
+ }
+
+ pub fn data(&self) -> intptr_t {
+ self.kevent.data as intptr_t
+ }
+
+ pub fn udata(&self) -> intptr_t {
+ self.kevent.udata as intptr_t
+ }
+}
+
+pub fn kevent(
+ kq: RawFd,
+ changelist: &[KEvent],
+ eventlist: &mut [KEvent],
+ timeout_ms: usize,
+) -> Result<usize> {
+ // Convert ms to timespec
+ let timeout = timespec {
+ tv_sec: (timeout_ms / 1000) as time_t,
+ tv_nsec: ((timeout_ms % 1000) * 1_000_000) as c_long,
+ };
+
+ kevent_ts(kq, changelist, eventlist, Some(timeout))
+}
+
+#[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "openbsd"
+))]
+type type_of_nchanges = c_int;
+#[cfg(target_os = "netbsd")]
+type type_of_nchanges = size_t;
+
+pub fn kevent_ts(
+ kq: RawFd,
+ changelist: &[KEvent],
+ eventlist: &mut [KEvent],
+ timeout_opt: Option<timespec>,
+) -> Result<usize> {
+ let res = unsafe {
+ libc::kevent(
+ kq,
+ changelist.as_ptr() as *const libc::kevent,
+ changelist.len() as type_of_nchanges,
+ eventlist.as_mut_ptr() as *mut libc::kevent,
+ eventlist.len() as type_of_nchanges,
+ if let Some(ref timeout) = timeout_opt {
+ timeout as *const timespec
+ } else {
+ ptr::null()
+ },
+ )
+ };
+
+ Errno::result(res).map(|r| r as usize)
+}
+
+#[inline]
+pub fn ev_set(
+ ev: &mut KEvent,
+ ident: usize,
+ filter: EventFilter,
+ flags: EventFlag,
+ fflags: FilterFlag,
+ udata: intptr_t,
+) {
+ ev.kevent.ident = ident as uintptr_t;
+ ev.kevent.filter = filter as type_of_event_filter;
+ ev.kevent.flags = flags.bits();
+ ev.kevent.fflags = fflags.bits();
+ ev.kevent.data = 0;
+ ev.kevent.udata = udata as type_of_udata;
+}
+
+#[test]
+fn test_struct_kevent() {
+ use std::mem;
+
+ let udata: intptr_t = 12345;
+
+ let actual = KEvent::new(
+ 0xdead_beef,
+ EventFilter::EVFILT_READ,
+ EventFlag::EV_ONESHOT | EventFlag::EV_ADD,
+ FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT,
+ 0x1337,
+ udata,
+ );
+ assert_eq!(0xdead_beef, actual.ident());
+ let filter = actual.kevent.filter;
+ assert_eq!(libc::EVFILT_READ, filter);
+ assert_eq!(libc::EV_ONESHOT | libc::EV_ADD, actual.flags().bits());
+ assert_eq!(libc::NOTE_CHILD | libc::NOTE_EXIT, actual.fflags().bits());
+ assert_eq!(0x1337, actual.data());
+ assert_eq!(udata as type_of_udata, actual.udata() as type_of_udata);
+ assert_eq!(mem::size_of::<libc::kevent>(), mem::size_of::<KEvent>());
+}
+
+#[test]
+fn test_kevent_filter() {
+ let udata: intptr_t = 12345;
+
+ let actual = KEvent::new(
+ 0xdead_beef,
+ EventFilter::EVFILT_READ,
+ EventFlag::EV_ONESHOT | EventFlag::EV_ADD,
+ FilterFlag::NOTE_CHILD | FilterFlag::NOTE_EXIT,
+ 0x1337,
+ udata,
+ );
+ assert_eq!(EventFilter::EVFILT_READ, actual.filter().unwrap());
+}
diff --git a/third_party/rust/nix/src/sys/eventfd.rs b/third_party/rust/nix/src/sys/eventfd.rs
new file mode 100644
index 0000000000..cd906720cd
--- /dev/null
+++ b/third_party/rust/nix/src/sys/eventfd.rs
@@ -0,0 +1,17 @@
+use crate::errno::Errno;
+use crate::Result;
+use std::os::unix::io::RawFd;
+
+libc_bitflags! {
+ pub struct EfdFlags: libc::c_int {
+ EFD_CLOEXEC; // Since Linux 2.6.27
+ EFD_NONBLOCK; // Since Linux 2.6.27
+ EFD_SEMAPHORE; // Since Linux 2.6.30
+ }
+}
+
+pub fn eventfd(initval: libc::c_uint, flags: EfdFlags) -> Result<RawFd> {
+ let res = unsafe { libc::eventfd(initval, flags.bits()) };
+
+ Errno::result(res).map(|r| r as RawFd)
+}
diff --git a/third_party/rust/nix/src/sys/inotify.rs b/third_party/rust/nix/src/sys/inotify.rs
new file mode 100644
index 0000000000..84356ec70f
--- /dev/null
+++ b/third_party/rust/nix/src/sys/inotify.rs
@@ -0,0 +1,248 @@
+//! Monitoring API for filesystem events.
+//!
+//! Inotify is a Linux-only API to monitor filesystems events.
+//!
+//! For more documentation, please read [inotify(7)](https://man7.org/linux/man-pages/man7/inotify.7.html).
+//!
+//! # Examples
+//!
+//! Monitor all events happening in directory "test":
+//! ```no_run
+//! # use nix::sys::inotify::{AddWatchFlags,InitFlags,Inotify};
+//! #
+//! // We create a new inotify instance.
+//! let instance = Inotify::init(InitFlags::empty()).unwrap();
+//!
+//! // We add a new watch on directory "test" for all events.
+//! let wd = instance.add_watch("test", AddWatchFlags::IN_ALL_EVENTS).unwrap();
+//!
+//! loop {
+//! // We read from our inotify instance for events.
+//! let events = instance.read_events().unwrap();
+//! println!("Events: {:?}", events);
+//! }
+//! ```
+
+use crate::errno::Errno;
+use crate::unistd::read;
+use crate::NixPath;
+use crate::Result;
+use cfg_if::cfg_if;
+use libc::{c_char, c_int};
+use std::ffi::{CStr, OsStr, OsString};
+use std::mem::{size_of, MaybeUninit};
+use std::os::unix::ffi::OsStrExt;
+use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
+use std::ptr;
+
+libc_bitflags! {
+ /// Configuration options for [`inotify_add_watch`](fn.inotify_add_watch.html).
+ pub struct AddWatchFlags: u32 {
+ /// File was accessed.
+ IN_ACCESS;
+ /// File was modified.
+ IN_MODIFY;
+ /// Metadata changed.
+ IN_ATTRIB;
+ /// Writable file was closed.
+ IN_CLOSE_WRITE;
+ /// Nonwritable file was closed.
+ IN_CLOSE_NOWRITE;
+ /// File was opened.
+ IN_OPEN;
+ /// File was moved from X.
+ IN_MOVED_FROM;
+ /// File was moved to Y.
+ IN_MOVED_TO;
+ /// Subfile was created.
+ IN_CREATE;
+ /// Subfile was deleted.
+ IN_DELETE;
+ /// Self was deleted.
+ IN_DELETE_SELF;
+ /// Self was moved.
+ IN_MOVE_SELF;
+
+ /// Backing filesystem was unmounted.
+ IN_UNMOUNT;
+ /// Event queue overflowed.
+ IN_Q_OVERFLOW;
+ /// File was ignored.
+ IN_IGNORED;
+
+ /// Combination of `IN_CLOSE_WRITE` and `IN_CLOSE_NOWRITE`.
+ IN_CLOSE;
+ /// Combination of `IN_MOVED_FROM` and `IN_MOVED_TO`.
+ IN_MOVE;
+
+ /// Only watch the path if it is a directory.
+ IN_ONLYDIR;
+ /// Don't follow symlinks.
+ IN_DONT_FOLLOW;
+
+ /// Event occurred against directory.
+ IN_ISDIR;
+ /// Only send event once.
+ IN_ONESHOT;
+ /// All of the events.
+ IN_ALL_EVENTS;
+ }
+}
+
+libc_bitflags! {
+ /// Configuration options for [`inotify_init1`](fn.inotify_init1.html).
+ pub struct InitFlags: c_int {
+ /// Set the `FD_CLOEXEC` flag on the file descriptor.
+ IN_CLOEXEC;
+ /// Set the `O_NONBLOCK` flag on the open file description referred to by the new file descriptor.
+ IN_NONBLOCK;
+ }
+}
+
+/// An inotify instance. This is also a file descriptor, you can feed it to
+/// other interfaces consuming file descriptors, epoll for example.
+#[derive(Debug, Clone, Copy)]
+pub struct Inotify {
+ fd: RawFd,
+}
+
+/// This object is returned when you create a new watch on an inotify instance.
+/// It is then returned as part of an event once triggered. It allows you to
+/// know which watch triggered which event.
+#[derive(Debug, Clone, Copy, Hash, Eq, PartialEq, Ord, PartialOrd)]
+pub struct WatchDescriptor {
+ wd: i32,
+}
+
+/// A single inotify event.
+///
+/// For more documentation see, [inotify(7)](https://man7.org/linux/man-pages/man7/inotify.7.html).
+#[derive(Debug)]
+pub struct InotifyEvent {
+ /// Watch descriptor. This field corresponds to the watch descriptor you
+ /// were issued when calling add_watch. It allows you to know which watch
+ /// this event comes from.
+ pub wd: WatchDescriptor,
+ /// Event mask. This field is a bitfield describing the exact event that
+ /// occured.
+ pub mask: AddWatchFlags,
+ /// This cookie is a number that allows you to connect related events. For
+ /// now only IN_MOVED_FROM and IN_MOVED_TO can be connected.
+ pub cookie: u32,
+ /// Filename. This field exists only if the event was triggered for a file
+ /// inside the watched directory.
+ pub name: Option<OsString>,
+}
+
+impl Inotify {
+ /// Initialize a new inotify instance.
+ ///
+ /// Returns a Result containing an inotify instance.
+ ///
+ /// For more information see, [inotify_init(2)](https://man7.org/linux/man-pages/man2/inotify_init.2.html).
+ pub fn init(flags: InitFlags) -> Result<Inotify> {
+ let res = Errno::result(unsafe { libc::inotify_init1(flags.bits()) });
+
+ res.map(|fd| Inotify { fd })
+ }
+
+ /// Adds a new watch on the target file or directory.
+ ///
+ /// Returns a watch descriptor. This is not a File Descriptor!
+ ///
+ /// For more information see, [inotify_add_watch(2)](https://man7.org/linux/man-pages/man2/inotify_add_watch.2.html).
+ pub fn add_watch<P: ?Sized + NixPath>(
+ self,
+ path: &P,
+ mask: AddWatchFlags,
+ ) -> Result<WatchDescriptor> {
+ let res = path.with_nix_path(|cstr| unsafe {
+ libc::inotify_add_watch(self.fd, cstr.as_ptr(), mask.bits())
+ })?;
+
+ Errno::result(res).map(|wd| WatchDescriptor { wd })
+ }
+
+ /// Removes an existing watch using the watch descriptor returned by
+ /// inotify_add_watch.
+ ///
+ /// Returns an EINVAL error if the watch descriptor is invalid.
+ ///
+ /// For more information see, [inotify_rm_watch(2)](https://man7.org/linux/man-pages/man2/inotify_rm_watch.2.html).
+ pub fn rm_watch(self, wd: WatchDescriptor) -> Result<()> {
+ cfg_if! {
+ if #[cfg(target_os = "linux")] {
+ let arg = wd.wd;
+ } else if #[cfg(target_os = "android")] {
+ let arg = wd.wd as u32;
+ }
+ }
+ let res = unsafe { libc::inotify_rm_watch(self.fd, arg) };
+
+ Errno::result(res).map(drop)
+ }
+
+ /// Reads a collection of events from the inotify file descriptor. This call
+ /// can either be blocking or non blocking depending on whether IN_NONBLOCK
+ /// was set at initialization.
+ ///
+ /// Returns as many events as available. If the call was non blocking and no
+ /// events could be read then the EAGAIN error is returned.
+ pub fn read_events(self) -> Result<Vec<InotifyEvent>> {
+ let header_size = size_of::<libc::inotify_event>();
+ const BUFSIZ: usize = 4096;
+ let mut buffer = [0u8; BUFSIZ];
+ let mut events = Vec::new();
+ let mut offset = 0;
+
+ let nread = read(self.fd, &mut buffer)?;
+
+ while (nread - offset) >= header_size {
+ let event = unsafe {
+ let mut event = MaybeUninit::<libc::inotify_event>::uninit();
+ ptr::copy_nonoverlapping(
+ buffer.as_ptr().add(offset),
+ event.as_mut_ptr() as *mut u8,
+ (BUFSIZ - offset).min(header_size),
+ );
+ event.assume_init()
+ };
+
+ let name = match event.len {
+ 0 => None,
+ _ => {
+ let ptr = unsafe {
+ buffer.as_ptr().add(offset + header_size)
+ as *const c_char
+ };
+ let cstr = unsafe { CStr::from_ptr(ptr) };
+
+ Some(OsStr::from_bytes(cstr.to_bytes()).to_owned())
+ }
+ };
+
+ events.push(InotifyEvent {
+ wd: WatchDescriptor { wd: event.wd },
+ mask: AddWatchFlags::from_bits_truncate(event.mask),
+ cookie: event.cookie,
+ name,
+ });
+
+ offset += header_size + event.len as usize;
+ }
+
+ Ok(events)
+ }
+}
+
+impl AsRawFd for Inotify {
+ fn as_raw_fd(&self) -> RawFd {
+ self.fd
+ }
+}
+
+impl FromRawFd for Inotify {
+ unsafe fn from_raw_fd(fd: RawFd) -> Self {
+ Inotify { fd }
+ }
+}
diff --git a/third_party/rust/nix/src/sys/ioctl/bsd.rs b/third_party/rust/nix/src/sys/ioctl/bsd.rs
new file mode 100644
index 0000000000..307994cb96
--- /dev/null
+++ b/third_party/rust/nix/src/sys/ioctl/bsd.rs
@@ -0,0 +1,129 @@
+/// The datatype used for the ioctl number
+#[doc(hidden)]
+#[cfg(not(target_os = "illumos"))]
+pub type ioctl_num_type = ::libc::c_ulong;
+
+#[doc(hidden)]
+#[cfg(target_os = "illumos")]
+pub type ioctl_num_type = ::libc::c_int;
+
+/// The datatype used for the 3rd argument
+#[doc(hidden)]
+pub type ioctl_param_type = ::libc::c_int;
+
+mod consts {
+ use crate::sys::ioctl::ioctl_num_type;
+ #[doc(hidden)]
+ pub const VOID: ioctl_num_type = 0x2000_0000;
+ #[doc(hidden)]
+ pub const OUT: ioctl_num_type = 0x4000_0000;
+ #[doc(hidden)]
+ #[allow(overflowing_literals)]
+ pub const IN: ioctl_num_type = 0x8000_0000;
+ #[doc(hidden)]
+ pub const INOUT: ioctl_num_type = IN | OUT;
+ #[doc(hidden)]
+ pub const IOCPARM_MASK: ioctl_num_type = 0x1fff;
+}
+
+pub use self::consts::*;
+
+#[macro_export]
+#[doc(hidden)]
+macro_rules! ioc {
+ ($inout:expr, $group:expr, $num:expr, $len:expr) => {
+ $inout
+ | (($len as $crate::sys::ioctl::ioctl_num_type
+ & $crate::sys::ioctl::IOCPARM_MASK)
+ << 16)
+ | (($group as $crate::sys::ioctl::ioctl_num_type) << 8)
+ | ($num as $crate::sys::ioctl::ioctl_num_type)
+ };
+}
+
+/// Generate an ioctl request code for a command that passes no data.
+///
+/// This is equivalent to the `_IO()` macro exposed by the C ioctl API.
+///
+/// You should only use this macro directly if the `ioctl` you're working
+/// with is "bad" and you cannot use `ioctl_none!()` directly.
+///
+/// # Example
+///
+/// ```
+/// # #[macro_use] extern crate nix;
+/// const KVMIO: u8 = 0xAE;
+/// ioctl_write_int_bad!(kvm_create_vm, request_code_none!(KVMIO, 0x03));
+/// # fn main() {}
+/// ```
+#[macro_export(local_inner_macros)]
+macro_rules! request_code_none {
+ ($g:expr, $n:expr) => {
+ ioc!($crate::sys::ioctl::VOID, $g, $n, 0)
+ };
+}
+
+/// Generate an ioctl request code for a command that passes an integer
+///
+/// This is equivalent to the `_IOWINT()` macro exposed by the C ioctl API.
+///
+/// You should only use this macro directly if the `ioctl` you're working
+/// with is "bad" and you cannot use `ioctl_write_int!()` directly.
+#[macro_export(local_inner_macros)]
+macro_rules! request_code_write_int {
+ ($g:expr, $n:expr) => {
+ ioc!(
+ $crate::sys::ioctl::VOID,
+ $g,
+ $n,
+ ::std::mem::size_of::<$crate::libc::c_int>()
+ )
+ };
+}
+
+/// Generate an ioctl request code for a command that reads.
+///
+/// This is equivalent to the `_IOR()` macro exposed by the C ioctl API.
+///
+/// You should only use this macro directly if the `ioctl` you're working
+/// with is "bad" and you cannot use `ioctl_read!()` directly.
+///
+/// The read/write direction is relative to userland, so this
+/// command would be userland is reading and the kernel is
+/// writing.
+#[macro_export(local_inner_macros)]
+macro_rules! request_code_read {
+ ($g:expr, $n:expr, $len:expr) => {
+ ioc!($crate::sys::ioctl::OUT, $g, $n, $len)
+ };
+}
+
+/// Generate an ioctl request code for a command that writes.
+///
+/// This is equivalent to the `_IOW()` macro exposed by the C ioctl API.
+///
+/// You should only use this macro directly if the `ioctl` you're working
+/// with is "bad" and you cannot use `ioctl_write!()` directly.
+///
+/// The read/write direction is relative to userland, so this
+/// command would be userland is writing and the kernel is
+/// reading.
+#[macro_export(local_inner_macros)]
+macro_rules! request_code_write {
+ ($g:expr, $n:expr, $len:expr) => {
+ ioc!($crate::sys::ioctl::IN, $g, $n, $len)
+ };
+}
+
+/// Generate an ioctl request code for a command that reads and writes.
+///
+/// This is equivalent to the `_IOWR()` macro exposed by the C ioctl API.
+///
+/// You should only use this macro directly if the `ioctl` you're working
+/// with is "bad" and you cannot use `ioctl_readwrite!()` directly.
+#[macro_export(local_inner_macros)]
+macro_rules! request_code_readwrite {
+ ($g:expr, $n:expr, $len:expr) => {
+ ioc!($crate::sys::ioctl::INOUT, $g, $n, $len)
+ };
+}
diff --git a/third_party/rust/nix/src/sys/ioctl/linux.rs b/third_party/rust/nix/src/sys/ioctl/linux.rs
new file mode 100644
index 0000000000..0c0a209053
--- /dev/null
+++ b/third_party/rust/nix/src/sys/ioctl/linux.rs
@@ -0,0 +1,172 @@
+/// The datatype used for the ioctl number
+#[cfg(any(target_os = "android", target_env = "musl"))]
+#[doc(hidden)]
+pub type ioctl_num_type = ::libc::c_int;
+#[cfg(not(any(target_os = "android", target_env = "musl")))]
+#[doc(hidden)]
+pub type ioctl_num_type = ::libc::c_ulong;
+/// The datatype used for the 3rd argument
+#[doc(hidden)]
+pub type ioctl_param_type = ::libc::c_ulong;
+
+#[doc(hidden)]
+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",
+ target_arch = "sparc64"
+))]
+mod consts {
+ #[doc(hidden)]
+ pub const NONE: u8 = 1;
+ #[doc(hidden)]
+ pub const READ: u8 = 2;
+ #[doc(hidden)]
+ pub const WRITE: u8 = 4;
+ #[doc(hidden)]
+ pub const SIZEBITS: u8 = 13;
+ #[doc(hidden)]
+ pub const DIRBITS: u8 = 3;
+}
+
+// "Generic" ioctl protocol
+#[cfg(any(
+ target_arch = "x86",
+ target_arch = "arm",
+ target_arch = "s390x",
+ target_arch = "x86_64",
+ target_arch = "aarch64",
+ target_arch = "riscv32",
+ target_arch = "riscv64"
+))]
+mod consts {
+ #[doc(hidden)]
+ pub const NONE: u8 = 0;
+ #[doc(hidden)]
+ pub const READ: u8 = 2;
+ #[doc(hidden)]
+ pub const WRITE: u8 = 1;
+ #[doc(hidden)]
+ pub const SIZEBITS: u8 = 14;
+ #[doc(hidden)]
+ pub const DIRBITS: u8 = 2;
+}
+
+pub use self::consts::*;
+
+#[doc(hidden)]
+pub const NRSHIFT: ioctl_num_type = 0;
+#[doc(hidden)]
+pub const TYPESHIFT: ioctl_num_type = NRSHIFT + NRBITS as ioctl_num_type;
+#[doc(hidden)]
+pub const SIZESHIFT: ioctl_num_type = TYPESHIFT + TYPEBITS as ioctl_num_type;
+#[doc(hidden)]
+pub const DIRSHIFT: ioctl_num_type = SIZESHIFT + SIZEBITS as ioctl_num_type;
+
+#[doc(hidden)]
+pub const NRMASK: ioctl_num_type = (1 << NRBITS) - 1;
+#[doc(hidden)]
+pub const TYPEMASK: ioctl_num_type = (1 << TYPEBITS) - 1;
+#[doc(hidden)]
+pub const SIZEMASK: ioctl_num_type = (1 << SIZEBITS) - 1;
+#[doc(hidden)]
+pub const DIRMASK: ioctl_num_type = (1 << DIRBITS) - 1;
+
+/// Encode an ioctl command.
+#[macro_export]
+#[doc(hidden)]
+macro_rules! ioc {
+ ($dir:expr, $ty:expr, $nr:expr, $sz:expr) => {
+ (($dir as $crate::sys::ioctl::ioctl_num_type
+ & $crate::sys::ioctl::DIRMASK)
+ << $crate::sys::ioctl::DIRSHIFT)
+ | (($ty as $crate::sys::ioctl::ioctl_num_type
+ & $crate::sys::ioctl::TYPEMASK)
+ << $crate::sys::ioctl::TYPESHIFT)
+ | (($nr as $crate::sys::ioctl::ioctl_num_type
+ & $crate::sys::ioctl::NRMASK)
+ << $crate::sys::ioctl::NRSHIFT)
+ | (($sz as $crate::sys::ioctl::ioctl_num_type
+ & $crate::sys::ioctl::SIZEMASK)
+ << $crate::sys::ioctl::SIZESHIFT)
+ };
+}
+
+/// Generate an ioctl request code for a command that passes no data.
+///
+/// This is equivalent to the `_IO()` macro exposed by the C ioctl API.
+///
+/// You should only use this macro directly if the `ioctl` you're working
+/// with is "bad" and you cannot use `ioctl_none!()` directly.
+///
+/// # Example
+///
+/// ```
+/// # #[macro_use] extern crate nix;
+/// const KVMIO: u8 = 0xAE;
+/// ioctl_write_int_bad!(kvm_create_vm, request_code_none!(KVMIO, 0x03));
+/// # fn main() {}
+/// ```
+#[macro_export(local_inner_macros)]
+macro_rules! request_code_none {
+ ($ty:expr, $nr:expr) => {
+ ioc!($crate::sys::ioctl::NONE, $ty, $nr, 0)
+ };
+}
+
+/// Generate an ioctl request code for a command that reads.
+///
+/// This is equivalent to the `_IOR()` macro exposed by the C ioctl API.
+///
+/// You should only use this macro directly if the `ioctl` you're working
+/// with is "bad" and you cannot use `ioctl_read!()` directly.
+///
+/// The read/write direction is relative to userland, so this
+/// command would be userland is reading and the kernel is
+/// writing.
+#[macro_export(local_inner_macros)]
+macro_rules! request_code_read {
+ ($ty:expr, $nr:expr, $sz:expr) => {
+ ioc!($crate::sys::ioctl::READ, $ty, $nr, $sz)
+ };
+}
+
+/// Generate an ioctl request code for a command that writes.
+///
+/// This is equivalent to the `_IOW()` macro exposed by the C ioctl API.
+///
+/// You should only use this macro directly if the `ioctl` you're working
+/// with is "bad" and you cannot use `ioctl_write!()` directly.
+///
+/// The read/write direction is relative to userland, so this
+/// command would be userland is writing and the kernel is
+/// reading.
+#[macro_export(local_inner_macros)]
+macro_rules! request_code_write {
+ ($ty:expr, $nr:expr, $sz:expr) => {
+ ioc!($crate::sys::ioctl::WRITE, $ty, $nr, $sz)
+ };
+}
+
+/// Generate an ioctl request code for a command that reads and writes.
+///
+/// This is equivalent to the `_IOWR()` macro exposed by the C ioctl API.
+///
+/// You should only use this macro directly if the `ioctl` you're working
+/// with is "bad" and you cannot use `ioctl_readwrite!()` directly.
+#[macro_export(local_inner_macros)]
+macro_rules! request_code_readwrite {
+ ($ty:expr, $nr:expr, $sz:expr) => {
+ ioc!(
+ $crate::sys::ioctl::READ | $crate::sys::ioctl::WRITE,
+ $ty,
+ $nr,
+ $sz
+ )
+ };
+}
diff --git a/third_party/rust/nix/src/sys/ioctl/mod.rs b/third_party/rust/nix/src/sys/ioctl/mod.rs
new file mode 100644
index 0000000000..98d6b5c99d
--- /dev/null
+++ b/third_party/rust/nix/src/sys/ioctl/mod.rs
@@ -0,0 +1,786 @@
+//! Provide helpers for making ioctl system calls.
+//!
+//! This library is pretty low-level and messy. `ioctl` is not fun.
+//!
+//! What is an `ioctl`?
+//! ===================
+//!
+//! The `ioctl` syscall is the grab-bag syscall on POSIX systems. Don't want to add a new
+//! syscall? Make it an `ioctl`! `ioctl` refers to both the syscall, and the commands that can be
+//! sent with it. `ioctl` stands for "IO control", and the commands are always sent to a file
+//! descriptor.
+//!
+//! It is common to see `ioctl`s used for the following purposes:
+//!
+//! * Provide read/write access to out-of-band data related to a device such as configuration
+//! (for instance, setting serial port options)
+//! * Provide a mechanism for performing full-duplex data transfers (for instance, xfer on SPI
+//! devices).
+//! * Provide access to control functions on a device (for example, on Linux you can send
+//! commands like pause, resume, and eject to the CDROM device.
+//! * Do whatever else the device driver creator thought made most sense.
+//!
+//! `ioctl`s are synchronous system calls and are similar to read and write calls in that regard.
+//! They operate on file descriptors and have an identifier that specifies what the ioctl is.
+//! Additionally they may read or write data and therefore need to pass along a data pointer.
+//! Besides the semantics of the ioctls being confusing, the generation of this identifer can also
+//! be difficult.
+//!
+//! Historically `ioctl` numbers were arbitrary hard-coded values. In Linux (before 2.6) and some
+//! unices this has changed to a more-ordered system where the ioctl numbers are partitioned into
+//! subcomponents (For linux this is documented in
+//! [`Documentation/ioctl/ioctl-number.rst`](https://elixir.bootlin.com/linux/latest/source/Documentation/userspace-api/ioctl/ioctl-number.rst)):
+//!
+//! * Number: The actual ioctl ID
+//! * Type: A grouping of ioctls for a common purpose or driver
+//! * Size: The size in bytes of the data that will be transferred
+//! * Direction: Whether there is any data and if it's read, write, or both
+//!
+//! Newer drivers should not generate complete integer identifiers for their `ioctl`s instead
+//! preferring to use the 4 components above to generate the final ioctl identifier. Because of
+//! how old `ioctl`s are, however, there are many hard-coded `ioctl` identifiers. These are
+//! commonly referred to as "bad" in `ioctl` documentation.
+//!
+//! Defining `ioctl`s
+//! =================
+//!
+//! This library provides several `ioctl_*!` macros for binding `ioctl`s. These generate public
+//! unsafe functions that can then be used for calling the ioctl. This macro has a few different
+//! ways it can be used depending on the specific ioctl you're working with.
+//!
+//! A simple `ioctl` is `SPI_IOC_RD_MODE`. This ioctl works with the SPI interface on Linux. This
+//! specific `ioctl` reads the mode of the SPI device as a `u8`. It's declared in
+//! `/include/uapi/linux/spi/spidev.h` as `_IOR(SPI_IOC_MAGIC, 1, __u8)`. Since it uses the `_IOR`
+//! macro, we know it's a `read` ioctl and can use the `ioctl_read!` macro as follows:
+//!
+//! ```
+//! # #[macro_use] extern crate nix;
+//! const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h
+//! const SPI_IOC_TYPE_MODE: u8 = 1;
+//! ioctl_read!(spi_read_mode, SPI_IOC_MAGIC, SPI_IOC_TYPE_MODE, u8);
+//! # fn main() {}
+//! ```
+//!
+//! This generates the function:
+//!
+//! ```
+//! # #[macro_use] extern crate nix;
+//! # use std::mem;
+//! # use nix::{libc, Result};
+//! # use nix::errno::Errno;
+//! # use nix::libc::c_int as c_int;
+//! # const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h
+//! # const SPI_IOC_TYPE_MODE: u8 = 1;
+//! pub unsafe fn spi_read_mode(fd: c_int, data: *mut u8) -> Result<c_int> {
+//! let res = libc::ioctl(fd, request_code_read!(SPI_IOC_MAGIC, SPI_IOC_TYPE_MODE, mem::size_of::<u8>()), data);
+//! Errno::result(res)
+//! }
+//! # fn main() {}
+//! ```
+//!
+//! The return value for the wrapper functions generated by the `ioctl_*!` macros are `nix::Error`s.
+//! These are generated by assuming the return value of the ioctl is `-1` on error and everything
+//! else is a valid return value. If this is not the case, `Result::map` can be used to map some
+//! of the range of "good" values (-Inf..-2, 0..Inf) into a smaller range in a helper function.
+//!
+//! Writing `ioctl`s generally use pointers as their data source and these should use the
+//! `ioctl_write_ptr!`. But in some cases an `int` is passed directly. For these `ioctl`s use the
+//! `ioctl_write_int!` macro. This variant does not take a type as the last argument:
+//!
+//! ```
+//! # #[macro_use] extern crate nix;
+//! const HCI_IOC_MAGIC: u8 = b'k';
+//! const HCI_IOC_HCIDEVUP: u8 = 1;
+//! ioctl_write_int!(hci_dev_up, HCI_IOC_MAGIC, HCI_IOC_HCIDEVUP);
+//! # fn main() {}
+//! ```
+//!
+//! Some `ioctl`s don't transfer any data, and those should use `ioctl_none!`. This macro
+//! doesn't take a type and so it is declared similar to the `write_int` variant shown above.
+//!
+//! The mode for a given `ioctl` should be clear from the documentation if it has good
+//! documentation. Otherwise it will be clear based on the macro used to generate the `ioctl`
+//! number where `_IO`, `_IOR`, `_IOW`, and `_IOWR` map to "none", "read", "write_*", and "readwrite"
+//! respectively. To determine the specific `write_` variant to use you'll need to find
+//! what the argument type is supposed to be. If it's an `int`, then `write_int` should be used,
+//! otherwise it should be a pointer and `write_ptr` should be used. On Linux the
+//! [`ioctl_list` man page](https://man7.org/linux/man-pages/man2/ioctl_list.2.html) describes a
+//! large number of `ioctl`s and describes their argument data type.
+//!
+//! Using "bad" `ioctl`s
+//! --------------------
+//!
+//! As mentioned earlier, there are many old `ioctl`s that do not use the newer method of
+//! generating `ioctl` numbers and instead use hardcoded values. These can be used with the
+//! `ioctl_*_bad!` macros. This naming comes from the Linux kernel which refers to these
+//! `ioctl`s as "bad". These are a different variant as they bypass calling the macro that generates
+//! the ioctl number and instead use the defined value directly.
+//!
+//! For example the `TCGETS` `ioctl` reads a `termios` data structure for a given file descriptor.
+//! It's defined as `0x5401` in `ioctls.h` on Linux and can be implemented as:
+//!
+//! ```
+//! # #[macro_use] extern crate nix;
+//! # #[cfg(any(target_os = "android", target_os = "linux"))]
+//! # use nix::libc::TCGETS as TCGETS;
+//! # #[cfg(any(target_os = "android", target_os = "linux"))]
+//! # use nix::libc::termios as termios;
+//! # #[cfg(any(target_os = "android", target_os = "linux"))]
+//! ioctl_read_bad!(tcgets, TCGETS, termios);
+//! # fn main() {}
+//! ```
+//!
+//! The generated function has the same form as that generated by `ioctl_read!`:
+//!
+//! ```text
+//! pub unsafe fn tcgets(fd: c_int, data: *mut termios) -> Result<c_int>;
+//! ```
+//!
+//! Working with Arrays
+//! -------------------
+//!
+//! Some `ioctl`s work with entire arrays of elements. These are supported by the `ioctl_*_buf`
+//! family of macros: `ioctl_read_buf`, `ioctl_write_buf`, and `ioctl_readwrite_buf`. Note that
+//! there are no "bad" versions for working with buffers. The generated functions include a `len`
+//! argument to specify the number of elements (where the type of each element is specified in the
+//! macro).
+//!
+//! Again looking to the SPI `ioctl`s on Linux for an example, there is a `SPI_IOC_MESSAGE` `ioctl`
+//! that queues up multiple SPI messages by writing an entire array of `spi_ioc_transfer` structs.
+//! `linux/spi/spidev.h` defines a macro to calculate the `ioctl` number like:
+//!
+//! ```C
+//! #define SPI_IOC_MAGIC 'k'
+//! #define SPI_MSGSIZE(N) ...
+//! #define SPI_IOC_MESSAGE(N) _IOW(SPI_IOC_MAGIC, 0, char[SPI_MSGSIZE(N)])
+//! ```
+//!
+//! The `SPI_MSGSIZE(N)` calculation is already handled by the `ioctl_*!` macros, so all that's
+//! needed to define this `ioctl` is:
+//!
+//! ```
+//! # #[macro_use] extern crate nix;
+//! const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h
+//! const SPI_IOC_TYPE_MESSAGE: u8 = 0;
+//! # pub struct spi_ioc_transfer(u64);
+//! ioctl_write_buf!(spi_transfer, SPI_IOC_MAGIC, SPI_IOC_TYPE_MESSAGE, spi_ioc_transfer);
+//! # fn main() {}
+//! ```
+//!
+//! This generates a function like:
+//!
+//! ```
+//! # #[macro_use] extern crate nix;
+//! # use std::mem;
+//! # use nix::{libc, Result};
+//! # use nix::errno::Errno;
+//! # use nix::libc::c_int as c_int;
+//! # const SPI_IOC_MAGIC: u8 = b'k';
+//! # const SPI_IOC_TYPE_MESSAGE: u8 = 0;
+//! # pub struct spi_ioc_transfer(u64);
+//! pub unsafe fn spi_message(fd: c_int, data: &mut [spi_ioc_transfer]) -> Result<c_int> {
+//! let res = libc::ioctl(fd,
+//! request_code_write!(SPI_IOC_MAGIC, SPI_IOC_TYPE_MESSAGE, data.len() * mem::size_of::<spi_ioc_transfer>()),
+//! data);
+//! Errno::result(res)
+//! }
+//! # fn main() {}
+//! ```
+//!
+//! Finding `ioctl` Documentation
+//! -----------------------------
+//!
+//! For Linux, look at your system's headers. For example, `/usr/include/linux/input.h` has a lot
+//! of lines defining macros which use `_IO`, `_IOR`, `_IOW`, `_IOC`, and `_IOWR`. Some `ioctl`s are
+//! documented directly in the headers defining their constants, but others have more extensive
+//! documentation in man pages (like termios' `ioctl`s which are in `tty_ioctl(4)`).
+//!
+//! Documenting the Generated Functions
+//! ===================================
+//!
+//! In many cases, users will wish for the functions generated by the `ioctl`
+//! macro to be public and documented. For this reason, the generated functions
+//! are public by default. If you wish to hide the ioctl, you will need to put
+//! them in a private module.
+//!
+//! For documentation, it is possible to use doc comments inside the `ioctl_*!` macros. Here is an
+//! example :
+//!
+//! ```
+//! # #[macro_use] extern crate nix;
+//! # use nix::libc::c_int;
+//! ioctl_read! {
+//! /// Make the given terminal the controlling terminal of the calling process. The calling
+//! /// process must be a session leader and not have a controlling terminal already. If the
+//! /// terminal is already the controlling terminal of a different session group then the
+//! /// ioctl will fail with **EPERM**, unless the caller is root (more precisely: has the
+//! /// **CAP_SYS_ADMIN** capability) and arg equals 1, in which case the terminal is stolen
+//! /// and all processes that had it as controlling terminal lose it.
+//! tiocsctty, b't', 19, c_int
+//! }
+//!
+//! # fn main() {}
+//! ```
+use cfg_if::cfg_if;
+
+#[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
+#[macro_use]
+mod linux;
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "redox"
+))]
+pub use self::linux::*;
+
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "haiku",
+ target_os = "openbsd"
+))]
+#[macro_use]
+mod bsd;
+
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "haiku",
+ target_os = "openbsd"
+))]
+pub use self::bsd::*;
+
+/// Convert raw ioctl return value to a Nix result
+#[macro_export]
+#[doc(hidden)]
+macro_rules! convert_ioctl_res {
+ ($w:expr) => {{
+ $crate::errno::Errno::result($w)
+ }};
+}
+
+/// Generates a wrapper function for an ioctl that passes no data to the kernel.
+///
+/// The arguments to this macro are:
+///
+/// * The function name
+/// * The ioctl identifier
+/// * The ioctl sequence number
+///
+/// The generated function has the following signature:
+///
+/// ```rust,ignore
+/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int) -> Result<libc::c_int>
+/// ```
+///
+/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
+///
+/// # Example
+///
+/// The `videodev2` driver on Linux defines the `log_status` `ioctl` as:
+///
+/// ```C
+/// #define VIDIOC_LOG_STATUS _IO('V', 70)
+/// ```
+///
+/// This can be implemented in Rust like:
+///
+/// ```no_run
+/// # #[macro_use] extern crate nix;
+/// ioctl_none!(log_status, b'V', 70);
+/// fn main() {}
+/// ```
+#[macro_export(local_inner_macros)]
+macro_rules! ioctl_none {
+ ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr) => (
+ $(#[$attr])*
+ pub unsafe fn $name(fd: $crate::libc::c_int)
+ -> $crate::Result<$crate::libc::c_int> {
+ convert_ioctl_res!($crate::libc::ioctl(fd, request_code_none!($ioty, $nr) as $crate::sys::ioctl::ioctl_num_type))
+ }
+ )
+}
+
+/// Generates a wrapper function for a "bad" ioctl that passes no data to the kernel.
+///
+/// The arguments to this macro are:
+///
+/// * The function name
+/// * The ioctl request code
+///
+/// The generated function has the following signature:
+///
+/// ```rust,ignore
+/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int) -> Result<libc::c_int>
+/// ```
+///
+/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
+///
+/// # Example
+///
+/// ```no_run
+/// # #[macro_use] extern crate nix;
+/// # use libc::TIOCNXCL;
+/// # use std::fs::File;
+/// # use std::os::unix::io::AsRawFd;
+/// ioctl_none_bad!(tiocnxcl, TIOCNXCL);
+/// fn main() {
+/// let file = File::open("/dev/ttyUSB0").unwrap();
+/// unsafe { tiocnxcl(file.as_raw_fd()) }.unwrap();
+/// }
+/// ```
+// TODO: add an example using request_code_*!()
+#[macro_export(local_inner_macros)]
+macro_rules! ioctl_none_bad {
+ ($(#[$attr:meta])* $name:ident, $nr:expr) => (
+ $(#[$attr])*
+ pub unsafe fn $name(fd: $crate::libc::c_int)
+ -> $crate::Result<$crate::libc::c_int> {
+ convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type))
+ }
+ )
+}
+
+/// Generates a wrapper function for an ioctl that reads data from the kernel.
+///
+/// The arguments to this macro are:
+///
+/// * The function name
+/// * The ioctl identifier
+/// * The ioctl sequence number
+/// * The data type passed by this ioctl
+///
+/// The generated function has the following signature:
+///
+/// ```rust,ignore
+/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: *mut DATA_TYPE) -> Result<libc::c_int>
+/// ```
+///
+/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
+///
+/// # Example
+///
+/// ```
+/// # #[macro_use] extern crate nix;
+/// const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h
+/// const SPI_IOC_TYPE_MODE: u8 = 1;
+/// ioctl_read!(spi_read_mode, SPI_IOC_MAGIC, SPI_IOC_TYPE_MODE, u8);
+/// # fn main() {}
+/// ```
+#[macro_export(local_inner_macros)]
+macro_rules! ioctl_read {
+ ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => (
+ $(#[$attr])*
+ pub unsafe fn $name(fd: $crate::libc::c_int,
+ data: *mut $ty)
+ -> $crate::Result<$crate::libc::c_int> {
+ convert_ioctl_res!($crate::libc::ioctl(fd, request_code_read!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data))
+ }
+ )
+}
+
+/// Generates a wrapper function for a "bad" ioctl that reads data from the kernel.
+///
+/// The arguments to this macro are:
+///
+/// * The function name
+/// * The ioctl request code
+/// * The data type passed by this ioctl
+///
+/// The generated function has the following signature:
+///
+/// ```rust,ignore
+/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: *mut DATA_TYPE) -> Result<libc::c_int>
+/// ```
+///
+/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
+///
+/// # Example
+///
+/// ```
+/// # #[macro_use] extern crate nix;
+/// # #[cfg(any(target_os = "android", target_os = "linux"))]
+/// ioctl_read_bad!(tcgets, libc::TCGETS, libc::termios);
+/// # fn main() {}
+/// ```
+#[macro_export(local_inner_macros)]
+macro_rules! ioctl_read_bad {
+ ($(#[$attr:meta])* $name:ident, $nr:expr, $ty:ty) => (
+ $(#[$attr])*
+ pub unsafe fn $name(fd: $crate::libc::c_int,
+ data: *mut $ty)
+ -> $crate::Result<$crate::libc::c_int> {
+ convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data))
+ }
+ )
+}
+
+/// Generates a wrapper function for an ioctl that writes data through a pointer to the kernel.
+///
+/// The arguments to this macro are:
+///
+/// * The function name
+/// * The ioctl identifier
+/// * The ioctl sequence number
+/// * The data type passed by this ioctl
+///
+/// The generated function has the following signature:
+///
+/// ```rust,ignore
+/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: *const DATA_TYPE) -> Result<libc::c_int>
+/// ```
+///
+/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
+///
+/// # Example
+///
+/// ```
+/// # #[macro_use] extern crate nix;
+/// # pub struct v4l2_audio {}
+/// ioctl_write_ptr!(s_audio, b'V', 34, v4l2_audio);
+/// # fn main() {}
+/// ```
+#[macro_export(local_inner_macros)]
+macro_rules! ioctl_write_ptr {
+ ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => (
+ $(#[$attr])*
+ pub unsafe fn $name(fd: $crate::libc::c_int,
+ data: *const $ty)
+ -> $crate::Result<$crate::libc::c_int> {
+ convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data))
+ }
+ )
+}
+
+/// Generates a wrapper function for a "bad" ioctl that writes data through a pointer to the kernel.
+///
+/// The arguments to this macro are:
+///
+/// * The function name
+/// * The ioctl request code
+/// * The data type passed by this ioctl
+///
+/// The generated function has the following signature:
+///
+/// ```rust,ignore
+/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: *const DATA_TYPE) -> Result<libc::c_int>
+/// ```
+///
+/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
+///
+/// # Example
+///
+/// ```
+/// # #[macro_use] extern crate nix;
+/// # #[cfg(any(target_os = "android", target_os = "linux"))]
+/// ioctl_write_ptr_bad!(tcsets, libc::TCSETS, libc::termios);
+/// # fn main() {}
+/// ```
+#[macro_export(local_inner_macros)]
+macro_rules! ioctl_write_ptr_bad {
+ ($(#[$attr:meta])* $name:ident, $nr:expr, $ty:ty) => (
+ $(#[$attr])*
+ pub unsafe fn $name(fd: $crate::libc::c_int,
+ data: *const $ty)
+ -> $crate::Result<$crate::libc::c_int> {
+ convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data))
+ }
+ )
+}
+
+cfg_if! {
+ if #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))] {
+ /// Generates a wrapper function for a ioctl that writes an integer to the kernel.
+ ///
+ /// The arguments to this macro are:
+ ///
+ /// * The function name
+ /// * The ioctl identifier
+ /// * The ioctl sequence number
+ ///
+ /// The generated function has the following signature:
+ ///
+ /// ```rust,ignore
+ /// 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
+ ///
+ /// ```
+ /// # #[macro_use] extern crate nix;
+ /// ioctl_write_int!(vt_activate, b'v', 4);
+ /// # fn main() {}
+ /// ```
+ #[macro_export(local_inner_macros)]
+ macro_rules! ioctl_write_int {
+ ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr) => (
+ $(#[$attr])*
+ pub unsafe fn $name(fd: $crate::libc::c_int,
+ data: $crate::sys::ioctl::ioctl_param_type)
+ -> $crate::Result<$crate::libc::c_int> {
+ convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write_int!($ioty, $nr) as $crate::sys::ioctl::ioctl_num_type, data))
+ }
+ )
+ }
+ } else {
+ /// Generates a wrapper function for a ioctl that writes an integer to the kernel.
+ ///
+ /// The arguments to this macro are:
+ ///
+ /// * The function name
+ /// * The ioctl identifier
+ /// * The ioctl sequence number
+ ///
+ /// The generated function has the following signature:
+ ///
+ /// ```rust,ignore
+ /// 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
+ ///
+ /// ```
+ /// # #[macro_use] extern crate nix;
+ /// const HCI_IOC_MAGIC: u8 = b'k';
+ /// const HCI_IOC_HCIDEVUP: u8 = 1;
+ /// ioctl_write_int!(hci_dev_up, HCI_IOC_MAGIC, HCI_IOC_HCIDEVUP);
+ /// # fn main() {}
+ /// ```
+ #[macro_export(local_inner_macros)]
+ macro_rules! ioctl_write_int {
+ ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr) => (
+ $(#[$attr])*
+ pub unsafe fn $name(fd: $crate::libc::c_int,
+ data: $crate::sys::ioctl::ioctl_param_type)
+ -> $crate::Result<$crate::libc::c_int> {
+ convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write!($ioty, $nr, ::std::mem::size_of::<$crate::libc::c_int>()) as $crate::sys::ioctl::ioctl_num_type, data))
+ }
+ )
+ }
+ }
+}
+
+/// Generates a wrapper function for a "bad" ioctl that writes an integer to the kernel.
+///
+/// The arguments to this macro are:
+///
+/// * The function name
+/// * The ioctl request code
+///
+/// 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>
+/// ```
+///
+/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
+///
+/// # Examples
+///
+/// ```
+/// # #[macro_use] extern crate nix;
+/// # #[cfg(any(target_os = "android", target_os = "linux"))]
+/// ioctl_write_int_bad!(tcsbrk, libc::TCSBRK);
+/// # fn main() {}
+/// ```
+///
+/// ```rust
+/// # #[macro_use] extern crate nix;
+/// const KVMIO: u8 = 0xAE;
+/// ioctl_write_int_bad!(kvm_create_vm, request_code_none!(KVMIO, 0x03));
+/// # fn main() {}
+/// ```
+#[macro_export(local_inner_macros)]
+macro_rules! ioctl_write_int_bad {
+ ($(#[$attr:meta])* $name:ident, $nr:expr) => (
+ $(#[$attr])*
+ pub unsafe fn $name(fd: $crate::libc::c_int,
+ data: $crate::libc::c_int)
+ -> $crate::Result<$crate::libc::c_int> {
+ convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data))
+ }
+ )
+}
+
+/// Generates a wrapper function for an ioctl that reads and writes data to the kernel.
+///
+/// The arguments to this macro are:
+///
+/// * The function name
+/// * The ioctl identifier
+/// * The ioctl sequence number
+/// * The data type passed by this ioctl
+///
+/// The generated function has the following signature:
+///
+/// ```rust,ignore
+/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: *mut DATA_TYPE) -> Result<libc::c_int>
+/// ```
+///
+/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
+///
+/// # Example
+///
+/// ```
+/// # #[macro_use] extern crate nix;
+/// # pub struct v4l2_audio {}
+/// ioctl_readwrite!(enum_audio, b'V', 65, v4l2_audio);
+/// # fn main() {}
+/// ```
+#[macro_export(local_inner_macros)]
+macro_rules! ioctl_readwrite {
+ ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => (
+ $(#[$attr])*
+ pub unsafe fn $name(fd: $crate::libc::c_int,
+ data: *mut $ty)
+ -> $crate::Result<$crate::libc::c_int> {
+ convert_ioctl_res!($crate::libc::ioctl(fd, request_code_readwrite!($ioty, $nr, ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data))
+ }
+ )
+}
+
+/// Generates a wrapper function for a "bad" ioctl that reads and writes data to the kernel.
+///
+/// The arguments to this macro are:
+///
+/// * The function name
+/// * The ioctl request code
+/// * The data type passed by this ioctl
+///
+/// The generated function has the following signature:
+///
+/// ```rust,ignore
+/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: *mut DATA_TYPE) -> Result<libc::c_int>
+/// ```
+///
+/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
+// TODO: Find an example for ioctl_readwrite_bad
+#[macro_export(local_inner_macros)]
+macro_rules! ioctl_readwrite_bad {
+ ($(#[$attr:meta])* $name:ident, $nr:expr, $ty:ty) => (
+ $(#[$attr])*
+ pub unsafe fn $name(fd: $crate::libc::c_int,
+ data: *mut $ty)
+ -> $crate::Result<$crate::libc::c_int> {
+ convert_ioctl_res!($crate::libc::ioctl(fd, $nr as $crate::sys::ioctl::ioctl_num_type, data))
+ }
+ )
+}
+
+/// Generates a wrapper function for an ioctl that reads an array of elements from the kernel.
+///
+/// The arguments to this macro are:
+///
+/// * The function name
+/// * The ioctl identifier
+/// * The ioctl sequence number
+/// * The data type passed by this ioctl
+///
+/// The generated function has the following signature:
+///
+/// ```rust,ignore
+/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: &mut [DATA_TYPE]) -> Result<libc::c_int>
+/// ```
+///
+/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
+// TODO: Find an example for ioctl_read_buf
+#[macro_export(local_inner_macros)]
+macro_rules! ioctl_read_buf {
+ ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => (
+ $(#[$attr])*
+ pub unsafe fn $name(fd: $crate::libc::c_int,
+ data: &mut [$ty])
+ -> $crate::Result<$crate::libc::c_int> {
+ convert_ioctl_res!($crate::libc::ioctl(fd, request_code_read!($ioty, $nr, data.len() * ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data))
+ }
+ )
+}
+
+/// Generates a wrapper function for an ioctl that writes an array of elements to the kernel.
+///
+/// The arguments to this macro are:
+///
+/// * The function name
+/// * The ioctl identifier
+/// * The ioctl sequence number
+/// * The data type passed by this ioctl
+///
+/// The generated function has the following signature:
+///
+/// ```rust,ignore
+/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: &[DATA_TYPE]) -> Result<libc::c_int>
+/// ```
+///
+/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
+///
+/// # Examples
+///
+/// ```
+/// # #[macro_use] extern crate nix;
+/// const SPI_IOC_MAGIC: u8 = b'k'; // Defined in linux/spi/spidev.h
+/// const SPI_IOC_TYPE_MESSAGE: u8 = 0;
+/// # pub struct spi_ioc_transfer(u64);
+/// ioctl_write_buf!(spi_transfer, SPI_IOC_MAGIC, SPI_IOC_TYPE_MESSAGE, spi_ioc_transfer);
+/// # fn main() {}
+/// ```
+#[macro_export(local_inner_macros)]
+macro_rules! ioctl_write_buf {
+ ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => (
+ $(#[$attr])*
+ pub unsafe fn $name(fd: $crate::libc::c_int,
+ data: &[$ty])
+ -> $crate::Result<$crate::libc::c_int> {
+ convert_ioctl_res!($crate::libc::ioctl(fd, request_code_write!($ioty, $nr, data.len() * ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data))
+ }
+ )
+}
+
+/// Generates a wrapper function for an ioctl that reads and writes an array of elements to the kernel.
+///
+/// The arguments to this macro are:
+///
+/// * The function name
+/// * The ioctl identifier
+/// * The ioctl sequence number
+/// * The data type passed by this ioctl
+///
+/// The generated function has the following signature:
+///
+/// ```rust,ignore
+/// pub unsafe fn FUNCTION_NAME(fd: libc::c_int, data: &mut [DATA_TYPE]) -> Result<libc::c_int>
+/// ```
+///
+/// For a more in-depth explanation of ioctls, see [`::sys::ioctl`](sys/ioctl/index.html).
+// TODO: Find an example for readwrite_buf
+#[macro_export(local_inner_macros)]
+macro_rules! ioctl_readwrite_buf {
+ ($(#[$attr:meta])* $name:ident, $ioty:expr, $nr:expr, $ty:ty) => (
+ $(#[$attr])*
+ pub unsafe fn $name(fd: $crate::libc::c_int,
+ data: &mut [$ty])
+ -> $crate::Result<$crate::libc::c_int> {
+ convert_ioctl_res!($crate::libc::ioctl(fd, request_code_readwrite!($ioty, $nr, data.len() * ::std::mem::size_of::<$ty>()) as $crate::sys::ioctl::ioctl_num_type, data))
+ }
+ )
+}
diff --git a/third_party/rust/nix/src/sys/memfd.rs b/third_party/rust/nix/src/sys/memfd.rs
new file mode 100644
index 0000000000..ad9345e89d
--- /dev/null
+++ b/third_party/rust/nix/src/sys/memfd.rs
@@ -0,0 +1,64 @@
+//! Interfaces for managing memory-backed files.
+
+use cfg_if::cfg_if;
+use std::os::unix::io::RawFd;
+
+use crate::errno::Errno;
+use crate::Result;
+use std::ffi::CStr;
+
+libc_bitflags!(
+ /// Options that change the behavior of [`memfd_create`].
+ pub struct MemFdCreateFlag: libc::c_uint {
+ /// Set the close-on-exec ([`FD_CLOEXEC`]) flag on the new file descriptor.
+ ///
+ /// By default, the new file descriptor is set to remain open across an [`execve`]
+ /// (the `FD_CLOEXEC` flag is initially disabled). This flag can be used to change
+ /// this default. The file offset is set to the beginning of the file (see [`lseek`]).
+ ///
+ /// See also the description of the `O_CLOEXEC` flag in [`open(2)`].
+ ///
+ /// [`execve`]: crate::unistd::execve
+ /// [`lseek`]: crate::unistd::lseek
+ /// [`FD_CLOEXEC`]: crate::fcntl::FdFlag::FD_CLOEXEC
+ /// [`open(2)`]: https://man7.org/linux/man-pages/man2/open.2.html
+ MFD_CLOEXEC;
+ /// Allow sealing operations on this file.
+ ///
+ /// See also the file sealing notes given in [`memfd_create(2)`].
+ ///
+ /// [`memfd_create(2)`]: https://man7.org/linux/man-pages/man2/memfd_create.2.html
+ MFD_ALLOW_SEALING;
+ }
+);
+
+/// Creates an anonymous file that lives in memory, and return a file-descriptor to it.
+///
+/// The file behaves like a regular file, and so can be modified, truncated, memory-mapped, and so on.
+/// However, unlike a regular file, it lives in RAM and has a volatile backing storage.
+///
+/// For more information, see [`memfd_create(2)`].
+///
+/// [`memfd_create(2)`]: https://man7.org/linux/man-pages/man2/memfd_create.2.html
+pub fn memfd_create(name: &CStr, flags: MemFdCreateFlag) -> Result<RawFd> {
+ let res = unsafe {
+ cfg_if! {
+ if #[cfg(all(
+ // Android does not have a memfd_create symbol
+ not(target_os = "android"),
+ any(
+ target_os = "freebsd",
+ // If the OS is Linux, gnu and musl expose a memfd_create symbol but not uclibc
+ target_env = "gnu",
+ target_env = "musl",
+ )))]
+ {
+ libc::memfd_create(name.as_ptr(), flags.bits())
+ } else {
+ libc::syscall(libc::SYS_memfd_create, name.as_ptr(), flags.bits())
+ }
+ }
+ };
+
+ Errno::result(res).map(|r| r as RawFd)
+}
diff --git a/third_party/rust/nix/src/sys/mman.rs b/third_party/rust/nix/src/sys/mman.rs
new file mode 100644
index 0000000000..2bee091610
--- /dev/null
+++ b/third_party/rust/nix/src/sys/mman.rs
@@ -0,0 +1,599 @@
+//! Memory management declarations.
+
+use crate::errno::Errno;
+#[cfg(not(target_os = "android"))]
+use crate::NixPath;
+use crate::Result;
+#[cfg(not(target_os = "android"))]
+#[cfg(feature = "fs")]
+use crate::{fcntl::OFlag, sys::stat::Mode};
+use libc::{self, c_int, c_void, off_t, size_t};
+use std::{os::unix::io::RawFd, num::NonZeroUsize};
+
+libc_bitflags! {
+ /// Desired memory protection of a memory mapping.
+ pub struct ProtFlags: c_int {
+ /// Pages cannot be accessed.
+ PROT_NONE;
+ /// Pages can be read.
+ PROT_READ;
+ /// Pages can be written.
+ PROT_WRITE;
+ /// Pages can be executed
+ PROT_EXEC;
+ /// Apply protection up to the end of a mapping that grows upwards.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ PROT_GROWSDOWN;
+ /// Apply protection down to the beginning of a mapping that grows downwards.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ PROT_GROWSUP;
+ }
+}
+
+libc_bitflags! {
+ /// Additional parameters for [`mmap`].
+ pub struct MapFlags: c_int {
+ /// Compatibility flag. Ignored.
+ MAP_FILE;
+ /// Share this mapping. Mutually exclusive with `MAP_PRIVATE`.
+ MAP_SHARED;
+ /// Create a private copy-on-write mapping. Mutually exclusive with `MAP_SHARED`.
+ MAP_PRIVATE;
+ /// Place the mapping at exactly the address specified in `addr`.
+ MAP_FIXED;
+ /// Place the mapping at exactly the address specified in `addr`, but never clobber an existing range.
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_FIXED_NOREPLACE;
+ /// To be used with `MAP_FIXED`, to forbid the system
+ /// to select a different address than the one specified.
+ #[cfg(target_os = "freebsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_EXCL;
+ /// Synonym for `MAP_ANONYMOUS`.
+ MAP_ANON;
+ /// The mapping is not backed by any file.
+ MAP_ANONYMOUS;
+ /// Put the mapping into the first 2GB of the process address space.
+ #[cfg(any(all(any(target_os = "android", target_os = "linux"),
+ any(target_arch = "x86", target_arch = "x86_64")),
+ all(target_os = "linux", target_env = "musl", any(target_arch = "x86", target_arch = "x86_64")),
+ all(target_os = "freebsd", target_pointer_width = "64")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_32BIT;
+ /// Used for stacks; indicates to the kernel that the mapping should extend downward in memory.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_GROWSDOWN;
+ /// Compatibility flag. Ignored.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_DENYWRITE;
+ /// Compatibility flag. Ignored.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_EXECUTABLE;
+ /// Mark the mmaped region to be locked in the same way as `mlock(2)`.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_LOCKED;
+ /// Do not reserve swap space for this mapping.
+ ///
+ /// This was removed in FreeBSD 11 and is unused in DragonFlyBSD.
+ #[cfg(not(any(target_os = "dragonfly", target_os = "freebsd")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_NORESERVE;
+ /// Populate page tables for a mapping.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_POPULATE;
+ /// Only meaningful when used with `MAP_POPULATE`. Don't perform read-ahead.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_NONBLOCK;
+ /// Allocate the mapping using "huge pages."
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_HUGETLB;
+ /// Make use of 64KB huge page (must be supported by the system)
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_HUGE_64KB;
+ /// Make use of 512KB huge page (must be supported by the system)
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_HUGE_512KB;
+ /// Make use of 1MB huge page (must be supported by the system)
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_HUGE_1MB;
+ /// Make use of 2MB huge page (must be supported by the system)
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_HUGE_2MB;
+ /// Make use of 8MB huge page (must be supported by the system)
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_HUGE_8MB;
+ /// Make use of 16MB huge page (must be supported by the system)
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_HUGE_16MB;
+ /// Make use of 32MB huge page (must be supported by the system)
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_HUGE_32MB;
+ /// Make use of 256MB huge page (must be supported by the system)
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_HUGE_256MB;
+ /// Make use of 512MB huge page (must be supported by the system)
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_HUGE_512MB;
+ /// Make use of 1GB huge page (must be supported by the system)
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_HUGE_1GB;
+ /// Make use of 2GB huge page (must be supported by the system)
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_HUGE_2GB;
+ /// Make use of 16GB huge page (must be supported by the system)
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_HUGE_16GB;
+
+ /// Lock the mapped region into memory as with `mlock(2)`.
+ #[cfg(target_os = "netbsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_WIRED;
+ /// Causes dirtied data in the specified range to be flushed to disk only when necessary.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_NOSYNC;
+ /// Rename private pages to a file.
+ ///
+ /// This was removed in FreeBSD 11 and is unused in DragonFlyBSD.
+ #[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_RENAME;
+ /// Region may contain semaphores.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "netbsd", target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_HASSEMAPHORE;
+ /// Region grows down, like a stack.
+ #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd", target_os = "linux", target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_STACK;
+ /// Pages in this mapping are not retained in the kernel's memory cache.
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_NOCACHE;
+ /// Allows the W/X bit on the page, it's necessary on aarch64 architecture.
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_JIT;
+ /// Allows to use large pages, underlying alignment based on size.
+ #[cfg(target_os = "freebsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_ALIGNED_SUPER;
+ /// Pages will be discarded in the core dumps.
+ #[cfg(target_os = "openbsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_CONCEAL;
+ }
+}
+
+#[cfg(any(target_os = "linux", target_os = "netbsd"))]
+libc_bitflags! {
+ /// Options for [`mremap`].
+ pub struct MRemapFlags: c_int {
+ /// Permit the kernel to relocate the mapping to a new virtual address, if necessary.
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MREMAP_MAYMOVE;
+ /// Place the mapping at exactly the address specified in `new_address`.
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MREMAP_FIXED;
+ /// Place the mapping at exactly the address specified in `new_address`.
+ #[cfg(target_os = "netbsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_FIXED;
+ /// Allows to duplicate the mapping to be able to apply different flags on the copy.
+ #[cfg(target_os = "netbsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MAP_REMAPDUP;
+ }
+}
+
+libc_enum! {
+ /// Usage information for a range of memory to allow for performance optimizations by the kernel.
+ ///
+ /// Used by [`madvise`].
+ #[repr(i32)]
+ #[non_exhaustive]
+ pub enum MmapAdvise {
+ /// No further special treatment. This is the default.
+ MADV_NORMAL,
+ /// Expect random page references.
+ MADV_RANDOM,
+ /// Expect sequential page references.
+ MADV_SEQUENTIAL,
+ /// Expect access in the near future.
+ MADV_WILLNEED,
+ /// Do not expect access in the near future.
+ MADV_DONTNEED,
+ /// Free up a given range of pages and its associated backing store.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_REMOVE,
+ /// Do not make pages in this range available to the child after a `fork(2)`.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_DONTFORK,
+ /// Undo the effect of `MADV_DONTFORK`.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_DOFORK,
+ /// Poison the given pages.
+ ///
+ /// Subsequent references to those pages are treated like hardware memory corruption.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_HWPOISON,
+ /// Enable Kernel Samepage Merging (KSM) for the given pages.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_MERGEABLE,
+ /// Undo the effect of `MADV_MERGEABLE`
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_UNMERGEABLE,
+ /// Preserve the memory of each page but offline the original page.
+ #[cfg(any(target_os = "android",
+ all(target_os = "linux", any(
+ target_arch = "aarch64",
+ target_arch = "arm",
+ target_arch = "powerpc",
+ target_arch = "powerpc64",
+ target_arch = "s390x",
+ target_arch = "x86",
+ target_arch = "x86_64",
+ target_arch = "sparc64"))))]
+ MADV_SOFT_OFFLINE,
+ /// Enable Transparent Huge Pages (THP) for pages in the given range.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_HUGEPAGE,
+ /// Undo the effect of `MADV_HUGEPAGE`.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_NOHUGEPAGE,
+ /// Exclude the given range from a core dump.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_DONTDUMP,
+ /// Undo the effect of an earlier `MADV_DONTDUMP`.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_DODUMP,
+ /// Specify that the application no longer needs the pages in the given range.
+ MADV_FREE,
+ /// Request that the system not flush the current range to disk unless it needs to.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_NOSYNC,
+ /// Undoes the effects of `MADV_NOSYNC` for any future pages dirtied within the given range.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_AUTOSYNC,
+ /// Region is not included in a core file.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_NOCORE,
+ /// Include region in a core file
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_CORE,
+ /// This process should not be killed when swap space is exhausted.
+ #[cfg(any(target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_PROTECT,
+ /// Invalidate the hardware page table for the given region.
+ #[cfg(target_os = "dragonfly")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_INVAL,
+ /// Set the offset of the page directory page to `value` for the virtual page table.
+ #[cfg(target_os = "dragonfly")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_SETMAP,
+ /// Indicates that the application will not need the data in the given range.
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_ZERO_WIRED_PAGES,
+ /// Pages can be reused (by anyone).
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_FREE_REUSABLE,
+ /// Caller wants to reuse those pages.
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MADV_FREE_REUSE,
+ // Darwin doesn't document this flag's behavior.
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ #[allow(missing_docs)]
+ MADV_CAN_REUSE,
+ }
+}
+
+libc_bitflags! {
+ /// Configuration flags for [`msync`].
+ pub struct MsFlags: c_int {
+ /// Schedule an update but return immediately.
+ MS_ASYNC;
+ /// Invalidate all cached data.
+ MS_INVALIDATE;
+ /// Invalidate pages, but leave them mapped.
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MS_KILLPAGES;
+ /// Deactivate pages, but leave them mapped.
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MS_DEACTIVATE;
+ /// Perform an update and wait for it to complete.
+ MS_SYNC;
+ }
+}
+
+#[cfg(not(target_os = "haiku"))]
+libc_bitflags! {
+ /// Flags for [`mlockall`].
+ pub struct MlockAllFlags: c_int {
+ /// Lock pages that are currently mapped into the address space of the process.
+ MCL_CURRENT;
+ /// Lock pages which will become mapped into the address space of the process in the future.
+ MCL_FUTURE;
+ }
+}
+
+/// Locks all memory pages that contain part of the address range with `length`
+/// bytes starting at `addr`.
+///
+/// Locked pages never move to the swap area.
+///
+/// # Safety
+///
+/// `addr` must meet all the requirements described in the [`mlock(2)`] man page.
+///
+/// [`mlock(2)`]: https://man7.org/linux/man-pages/man2/mlock.2.html
+pub unsafe fn mlock(addr: *const c_void, length: size_t) -> Result<()> {
+ Errno::result(libc::mlock(addr, length)).map(drop)
+}
+
+/// Unlocks all memory pages that contain part of the address range with
+/// `length` bytes starting at `addr`.
+///
+/// # Safety
+///
+/// `addr` must meet all the requirements described in the [`munlock(2)`] man
+/// page.
+///
+/// [`munlock(2)`]: https://man7.org/linux/man-pages/man2/munlock.2.html
+pub unsafe fn munlock(addr: *const c_void, length: size_t) -> Result<()> {
+ Errno::result(libc::munlock(addr, length)).map(drop)
+}
+
+/// Locks all memory pages mapped into this process' address space.
+///
+/// Locked pages never move to the swap area. For more information, see [`mlockall(2)`].
+///
+/// [`mlockall(2)`]: https://man7.org/linux/man-pages/man2/mlockall.2.html
+#[cfg(not(target_os = "haiku"))]
+pub fn mlockall(flags: MlockAllFlags) -> Result<()> {
+ unsafe { Errno::result(libc::mlockall(flags.bits())) }.map(drop)
+}
+
+/// Unlocks all memory pages mapped into this process' address space.
+///
+/// For more information, see [`munlockall(2)`].
+///
+/// [`munlockall(2)`]: https://man7.org/linux/man-pages/man2/munlockall.2.html
+#[cfg(not(target_os = "haiku"))]
+pub fn munlockall() -> Result<()> {
+ unsafe { Errno::result(libc::munlockall()) }.map(drop)
+}
+
+/// allocate memory, or map files or devices into memory
+///
+/// # Safety
+///
+/// See the [`mmap(2)`] man page for detailed requirements.
+///
+/// [`mmap(2)`]: https://man7.org/linux/man-pages/man2/mmap.2.html
+pub unsafe fn mmap(
+ addr: Option<NonZeroUsize>,
+ length: NonZeroUsize,
+ prot: ProtFlags,
+ flags: MapFlags,
+ fd: RawFd,
+ offset: off_t,
+) -> Result<*mut c_void> {
+ let ptr = addr.map_or(
+ std::ptr::null_mut(),
+ |a| usize::from(a) as *mut c_void
+ );
+
+ let ret = libc::mmap(ptr, length.into(), prot.bits(), flags.bits(), fd, offset);
+
+ if ret == libc::MAP_FAILED {
+ Err(Errno::last())
+ } else {
+ Ok(ret)
+ }
+}
+
+/// Expands (or shrinks) an existing memory mapping, potentially moving it at
+/// the same time.
+///
+/// # Safety
+///
+/// See the `mremap(2)` [man page](https://man7.org/linux/man-pages/man2/mremap.2.html) for
+/// detailed requirements.
+#[cfg(any(target_os = "linux", target_os = "netbsd"))]
+pub unsafe fn mremap(
+ addr: *mut c_void,
+ old_size: size_t,
+ new_size: size_t,
+ flags: MRemapFlags,
+ new_address: Option<*mut c_void>,
+) -> Result<*mut c_void> {
+ #[cfg(target_os = "linux")]
+ let ret = libc::mremap(
+ addr,
+ old_size,
+ new_size,
+ flags.bits(),
+ new_address.unwrap_or(std::ptr::null_mut()),
+ );
+ #[cfg(target_os = "netbsd")]
+ let ret = libc::mremap(
+ addr,
+ old_size,
+ new_address.unwrap_or(std::ptr::null_mut()),
+ new_size,
+ flags.bits(),
+ );
+
+ if ret == libc::MAP_FAILED {
+ Err(Errno::last())
+ } else {
+ Ok(ret)
+ }
+}
+
+/// remove a mapping
+///
+/// # Safety
+///
+/// `addr` must meet all the requirements described in the [`munmap(2)`] man
+/// page.
+///
+/// [`munmap(2)`]: https://man7.org/linux/man-pages/man2/munmap.2.html
+pub unsafe fn munmap(addr: *mut c_void, len: size_t) -> Result<()> {
+ Errno::result(libc::munmap(addr, len)).map(drop)
+}
+
+/// give advice about use of memory
+///
+/// # Safety
+///
+/// See the [`madvise(2)`] man page. Take special care when using
+/// [`MmapAdvise::MADV_FREE`].
+///
+/// [`madvise(2)`]: https://man7.org/linux/man-pages/man2/madvise.2.html
+pub unsafe fn madvise(
+ addr: *mut c_void,
+ length: size_t,
+ advise: MmapAdvise,
+) -> Result<()> {
+ Errno::result(libc::madvise(addr, length, advise as i32)).map(drop)
+}
+
+/// Set protection of memory mapping.
+///
+/// See [`mprotect(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mprotect.html) for
+/// details.
+///
+/// # Safety
+///
+/// Calls to `mprotect` are inherently unsafe, as changes to memory protections can lead to
+/// SIGSEGVs.
+///
+/// ```
+/// # use nix::libc::size_t;
+/// # use nix::sys::mman::{mmap, mprotect, MapFlags, ProtFlags};
+/// # use std::ptr;
+/// const ONE_K: size_t = 1024;
+/// let one_k_non_zero = std::num::NonZeroUsize::new(ONE_K).unwrap();
+/// let mut slice: &mut [u8] = unsafe {
+/// let mem = mmap(None, one_k_non_zero, ProtFlags::PROT_NONE,
+/// MapFlags::MAP_ANON | MapFlags::MAP_PRIVATE, -1, 0).unwrap();
+/// mprotect(mem, ONE_K, ProtFlags::PROT_READ | ProtFlags::PROT_WRITE).unwrap();
+/// std::slice::from_raw_parts_mut(mem as *mut u8, ONE_K)
+/// };
+/// assert_eq!(slice[0], 0x00);
+/// slice[0] = 0xFF;
+/// assert_eq!(slice[0], 0xFF);
+/// ```
+pub unsafe fn mprotect(
+ addr: *mut c_void,
+ length: size_t,
+ prot: ProtFlags,
+) -> Result<()> {
+ Errno::result(libc::mprotect(addr, length, prot.bits())).map(drop)
+}
+
+/// synchronize a mapped region
+///
+/// # Safety
+///
+/// `addr` must meet all the requirements described in the [`msync(2)`] man
+/// page.
+///
+/// [`msync(2)`]: https://man7.org/linux/man-pages/man2/msync.2.html
+pub unsafe fn msync(
+ addr: *mut c_void,
+ length: size_t,
+ flags: MsFlags,
+) -> Result<()> {
+ Errno::result(libc::msync(addr, length, flags.bits())).map(drop)
+}
+
+#[cfg(not(target_os = "android"))]
+feature! {
+#![feature = "fs"]
+/// Creates and opens a new, or opens an existing, POSIX shared memory object.
+///
+/// For more information, see [`shm_open(3)`].
+///
+/// [`shm_open(3)`]: https://man7.org/linux/man-pages/man3/shm_open.3.html
+pub fn shm_open<P>(
+ name: &P,
+ flag: OFlag,
+ mode: Mode
+ ) -> Result<RawFd>
+ where P: ?Sized + NixPath
+{
+ let ret = name.with_nix_path(|cstr| {
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ unsafe {
+ libc::shm_open(cstr.as_ptr(), flag.bits(), mode.bits() as libc::c_uint)
+ }
+ #[cfg(not(any(target_os = "macos", target_os = "ios")))]
+ unsafe {
+ libc::shm_open(cstr.as_ptr(), flag.bits(), mode.bits() as libc::mode_t)
+ }
+ })?;
+
+ Errno::result(ret)
+}
+}
+
+/// Performs the converse of [`shm_open`], removing an object previously created.
+///
+/// For more information, see [`shm_unlink(3)`].
+///
+/// [`shm_unlink(3)`]: https://man7.org/linux/man-pages/man3/shm_unlink.3.html
+#[cfg(not(target_os = "android"))]
+pub fn shm_unlink<P: ?Sized + NixPath>(name: &P) -> Result<()> {
+ let ret =
+ name.with_nix_path(|cstr| unsafe { libc::shm_unlink(cstr.as_ptr()) })?;
+
+ Errno::result(ret).map(drop)
+}
diff --git a/third_party/rust/nix/src/sys/mod.rs b/third_party/rust/nix/src/sys/mod.rs
new file mode 100644
index 0000000000..2065059de8
--- /dev/null
+++ b/third_party/rust/nix/src/sys/mod.rs
@@ -0,0 +1,228 @@
+//! Mostly platform-specific functionality
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ all(target_os = "linux", not(target_env = "uclibc")),
+ target_os = "macos",
+ target_os = "netbsd"
+))]
+feature! {
+ #![feature = "aio"]
+ pub mod aio;
+}
+
+feature! {
+ #![feature = "event"]
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[allow(missing_docs)]
+ pub mod epoll;
+
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[allow(missing_docs)]
+ pub mod event;
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[allow(missing_docs)]
+ pub mod eventfd;
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "redox",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "illumos",
+ target_os = "openbsd"
+))]
+#[cfg(feature = "ioctl")]
+#[cfg_attr(docsrs, doc(cfg(feature = "ioctl")))]
+#[macro_use]
+pub mod ioctl;
+
+#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+feature! {
+ #![feature = "fs"]
+ pub mod memfd;
+}
+
+#[cfg(not(target_os = "redox"))]
+feature! {
+ #![feature = "mman"]
+ pub mod mman;
+}
+
+#[cfg(target_os = "linux")]
+feature! {
+ #![feature = "personality"]
+ pub mod personality;
+}
+
+feature! {
+ #![feature = "pthread"]
+ pub mod pthread;
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+))]
+feature! {
+ #![feature = "ptrace"]
+ #[allow(missing_docs)]
+ pub mod ptrace;
+}
+
+#[cfg(target_os = "linux")]
+feature! {
+ #![feature = "quota"]
+ pub mod quota;
+}
+
+#[cfg(target_os = "linux")]
+feature! {
+ #![feature = "reboot"]
+ pub mod reboot;
+}
+
+#[cfg(not(any(
+ target_os = "redox",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "haiku"
+)))]
+feature! {
+ #![feature = "resource"]
+ pub mod resource;
+}
+
+#[cfg(not(target_os = "redox"))]
+feature! {
+ #![feature = "poll"]
+ pub mod select;
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"
+))]
+feature! {
+ #![feature = "zerocopy"]
+ pub mod sendfile;
+}
+
+pub mod signal;
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+feature! {
+ #![feature = "signal"]
+ #[allow(missing_docs)]
+ pub mod signalfd;
+}
+
+#[cfg(not(target_os = "redox"))]
+feature! {
+ #![feature = "socket"]
+ #[allow(missing_docs)]
+ pub mod socket;
+}
+
+feature! {
+ #![feature = "fs"]
+ #[allow(missing_docs)]
+ pub mod stat;
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "openbsd"
+))]
+feature! {
+ #![feature = "fs"]
+ pub mod statfs;
+}
+
+feature! {
+ #![feature = "fs"]
+ pub mod statvfs;
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+#[allow(missing_docs)]
+pub mod sysinfo;
+
+feature! {
+ #![feature = "term"]
+ #[allow(missing_docs)]
+ pub mod termios;
+}
+
+#[allow(missing_docs)]
+pub mod time;
+
+feature! {
+ #![feature = "uio"]
+ pub mod uio;
+}
+
+feature! {
+ #![feature = "feature"]
+ pub mod utsname;
+}
+
+feature! {
+ #![feature = "process"]
+ pub mod wait;
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+feature! {
+ #![feature = "inotify"]
+ pub mod inotify;
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+feature! {
+ #![feature = "time"]
+ pub mod timerfd;
+}
+
+#[cfg(all(
+ any(
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd"
+ ),
+ feature = "time",
+ feature = "signal"
+))]
+feature! {
+ #![feature = "time"]
+ pub mod timer;
+}
diff --git a/third_party/rust/nix/src/sys/personality.rs b/third_party/rust/nix/src/sys/personality.rs
new file mode 100644
index 0000000000..f295a05fad
--- /dev/null
+++ b/third_party/rust/nix/src/sys/personality.rs
@@ -0,0 +1,93 @@
+//! Process execution domains
+use crate::errno::Errno;
+use crate::Result;
+
+use libc::{self, c_int, c_ulong};
+
+libc_bitflags! {
+ /// Flags used and returned by [`get()`](fn.get.html) and
+ /// [`set()`](fn.set.html).
+ pub struct Persona: c_int {
+ /// Provide the legacy virtual address space layout.
+ ADDR_COMPAT_LAYOUT;
+ /// Disable address-space-layout randomization.
+ ADDR_NO_RANDOMIZE;
+ /// Limit the address space to 32 bits.
+ ADDR_LIMIT_32BIT;
+ /// Use `0xc0000000` as the offset at which to search a virtual memory
+ /// chunk on [`mmap(2)`], otherwise use `0xffffe000`.
+ ///
+ /// [`mmap(2)`]: https://man7.org/linux/man-pages/man2/mmap.2.html
+ ADDR_LIMIT_3GB;
+ /// User-space function pointers to signal handlers point to descriptors.
+ #[cfg(not(any(target_env = "musl", target_env = "uclibc")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ FDPIC_FUNCPTRS;
+ /// Map page 0 as read-only.
+ MMAP_PAGE_ZERO;
+ /// `PROT_READ` implies `PROT_EXEC` for [`mmap(2)`].
+ ///
+ /// [`mmap(2)`]: https://man7.org/linux/man-pages/man2/mmap.2.html
+ READ_IMPLIES_EXEC;
+ /// No effects.
+ SHORT_INODE;
+ /// [`select(2)`], [`pselect(2)`], and [`ppoll(2)`] do not modify the
+ /// returned timeout argument when interrupted by a signal handler.
+ ///
+ /// [`select(2)`]: https://man7.org/linux/man-pages/man2/select.2.html
+ /// [`pselect(2)`]: https://man7.org/linux/man-pages/man2/pselect.2.html
+ /// [`ppoll(2)`]: https://man7.org/linux/man-pages/man2/ppoll.2.html
+ STICKY_TIMEOUTS;
+ /// Have [`uname(2)`] report a 2.6.40+ version number rather than a 3.x
+ /// version number.
+ ///
+ /// [`uname(2)`]: https://man7.org/linux/man-pages/man2/uname.2.html
+ #[cfg(not(any(target_env = "musl", target_env = "uclibc")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ UNAME26;
+ /// No effects.
+ WHOLE_SECONDS;
+ }
+}
+
+/// Retrieve the current process personality.
+///
+/// Returns a Result containing a Persona instance.
+///
+/// Example:
+///
+/// ```
+/// # use nix::sys::personality::{self, Persona};
+/// let pers = personality::get().unwrap();
+/// assert!(!pers.contains(Persona::WHOLE_SECONDS));
+/// ```
+pub fn get() -> Result<Persona> {
+ let res = unsafe { libc::personality(0xFFFFFFFF) };
+
+ Errno::result(res).map(Persona::from_bits_truncate)
+}
+
+/// Set the current process personality.
+///
+/// Returns a Result containing the *previous* personality for the
+/// process, as a Persona.
+///
+/// For more information, see [personality(2)](https://man7.org/linux/man-pages/man2/personality.2.html)
+///
+/// **NOTE**: This call **replaces** the current personality entirely.
+/// To **update** the personality, first call `get()` and then `set()`
+/// with the modified persona.
+///
+/// Example:
+///
+/// ```
+/// # use nix::sys::personality::{self, Persona};
+/// let mut pers = personality::get().unwrap();
+/// assert!(!pers.contains(Persona::ADDR_NO_RANDOMIZE));
+/// personality::set(pers | Persona::ADDR_NO_RANDOMIZE).unwrap();
+/// ```
+pub fn set(persona: Persona) -> Result<Persona> {
+ let res = unsafe { libc::personality(persona.bits() as c_ulong) };
+
+ Errno::result(res).map(Persona::from_bits_truncate)
+}
diff --git a/third_party/rust/nix/src/sys/pthread.rs b/third_party/rust/nix/src/sys/pthread.rs
new file mode 100644
index 0000000000..6bad03a4d4
--- /dev/null
+++ b/third_party/rust/nix/src/sys/pthread.rs
@@ -0,0 +1,43 @@
+//! Low level threading primitives
+
+#[cfg(not(target_os = "redox"))]
+use crate::errno::Errno;
+#[cfg(not(target_os = "redox"))]
+use crate::Result;
+use libc::{self, pthread_t};
+
+/// Identifies an individual thread.
+pub type Pthread = pthread_t;
+
+/// Obtain ID of the calling thread (see
+/// [`pthread_self(3)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_self.html)
+///
+/// The thread ID returned by `pthread_self()` is not the same thing as
+/// the kernel thread ID returned by a call to `gettid(2)`.
+#[inline]
+pub fn pthread_self() -> Pthread {
+ unsafe { libc::pthread_self() }
+}
+
+feature! {
+#![feature = "signal"]
+
+/// Send a signal to a thread (see [`pthread_kill(3)`]).
+///
+/// If `signal` is `None`, `pthread_kill` will only preform error checking and
+/// won't send any signal.
+///
+/// [`pthread_kill(3)`]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_kill.html
+#[allow(clippy::not_unsafe_ptr_arg_deref)]
+#[cfg(not(target_os = "redox"))]
+pub fn pthread_kill<T>(thread: Pthread, signal: T) -> Result<()>
+ where T: Into<Option<crate::sys::signal::Signal>>
+{
+ let sig = match signal.into() {
+ Some(s) => s as libc::c_int,
+ None => 0,
+ };
+ let res = unsafe { libc::pthread_kill(thread, sig) };
+ Errno::result(res).map(drop)
+}
+}
diff --git a/third_party/rust/nix/src/sys/ptrace/bsd.rs b/third_party/rust/nix/src/sys/ptrace/bsd.rs
new file mode 100644
index 0000000000..ba267c6577
--- /dev/null
+++ b/third_party/rust/nix/src/sys/ptrace/bsd.rs
@@ -0,0 +1,195 @@
+use crate::errno::Errno;
+use crate::sys::signal::Signal;
+use crate::unistd::Pid;
+use crate::Result;
+use cfg_if::cfg_if;
+use libc::{self, c_int};
+use std::ptr;
+
+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.
+ #[non_exhaustive]
+ pub enum Request {
+ PT_TRACE_ME,
+ PT_READ_I,
+ PT_READ_D,
+ #[cfg(target_os = "macos")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ PT_READ_U,
+ PT_WRITE_I,
+ PT_WRITE_D,
+ #[cfg(target_os = "macos")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ PT_SIGEXC,
+ #[cfg(target_os = "macos")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ PT_THUPDATE,
+ #[cfg(target_os = "macos")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ 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 by `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 by `pid` allowing it to run freely, optionally delivering a
+/// signal specified by `sig`.
+pub fn detach<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_DETACH, pid, ptr::null_mut(), data).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
+/// use nix::sys::ptrace::step;
+/// use nix::unistd::Pid;
+/// use nix::sys::signal::Signal;
+/// use nix::sys::wait::*;
+/// // 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
+// Technically, ptrace doesn't dereference the pointer. It passes it directly
+// to the kernel.
+#[allow(clippy::not_unsafe_ptr_arg_deref)]
+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
+// Technically, ptrace doesn't dereference the pointer. It passes it directly
+// to the kernel.
+#[allow(clippy::not_unsafe_ptr_arg_deref)]
+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/third_party/rust/nix/src/sys/ptrace/linux.rs b/third_party/rust/nix/src/sys/ptrace/linux.rs
new file mode 100644
index 0000000000..9687e05d42
--- /dev/null
+++ b/third_party/rust/nix/src/sys/ptrace/linux.rs
@@ -0,0 +1,558 @@
+//! For detailed description of the ptrace requests, consult `man ptrace`.
+
+use crate::errno::Errno;
+use crate::sys::signal::Signal;
+use crate::unistd::Pid;
+use crate::Result;
+use cfg_if::cfg_if;
+use libc::{self, c_long, c_void, siginfo_t};
+use std::{mem, ptr};
+
+pub type AddressType = *mut ::libc::c_void;
+
+#[cfg(all(
+ target_os = "linux",
+ any(
+ all(
+ target_arch = "x86_64",
+ any(target_env = "gnu", target_env = "musl")
+ ),
+ all(target_arch = "x86", target_env = "gnu")
+ )
+))]
+use libc::user_regs_struct;
+
+cfg_if! {
+ if #[cfg(any(all(target_os = "linux", target_arch = "s390x"),
+ all(target_os = "linux", target_env = "gnu"),
+ target_env = "uclibc"))] {
+ #[doc(hidden)]
+ pub type RequestType = ::libc::c_uint;
+ } else {
+ #[doc(hidden)]
+ pub type RequestType = ::libc::c_int;
+ }
+}
+
+libc_enum! {
+ #[cfg_attr(not(any(target_env = "musl", target_env = "uclibc", target_os = "android")), repr(u32))]
+ #[cfg_attr(any(target_env = "musl", target_env = "uclibc", target_os = "android"), repr(i32))]
+ /// Ptrace Request enum defining the action to be taken.
+ #[non_exhaustive]
+ pub enum Request {
+ PTRACE_TRACEME,
+ PTRACE_PEEKTEXT,
+ PTRACE_PEEKDATA,
+ PTRACE_PEEKUSER,
+ PTRACE_POKETEXT,
+ PTRACE_POKEDATA,
+ PTRACE_POKEUSER,
+ PTRACE_CONT,
+ PTRACE_KILL,
+ PTRACE_SINGLESTEP,
+ #[cfg(any(all(target_os = "android", target_pointer_width = "32"),
+ all(target_os = "linux", any(target_env = "musl",
+ target_arch = "mips",
+ target_arch = "mips64",
+ target_arch = "x86_64",
+ target_pointer_width = "32"))))]
+ PTRACE_GETREGS,
+ #[cfg(any(all(target_os = "android", target_pointer_width = "32"),
+ all(target_os = "linux", any(target_env = "musl",
+ target_arch = "mips",
+ target_arch = "mips64",
+ target_arch = "x86_64",
+ target_pointer_width = "32"))))]
+ PTRACE_SETREGS,
+ #[cfg(any(all(target_os = "android", target_pointer_width = "32"),
+ all(target_os = "linux", any(target_env = "musl",
+ target_arch = "mips",
+ target_arch = "mips64",
+ target_arch = "x86_64",
+ target_pointer_width = "32"))))]
+ PTRACE_GETFPREGS,
+ #[cfg(any(all(target_os = "android", target_pointer_width = "32"),
+ all(target_os = "linux", any(target_env = "musl",
+ target_arch = "mips",
+ target_arch = "mips64",
+ target_arch = "x86_64",
+ target_pointer_width = "32"))))]
+ PTRACE_SETFPREGS,
+ PTRACE_ATTACH,
+ PTRACE_DETACH,
+ #[cfg(all(target_os = "linux", any(target_env = "musl",
+ target_arch = "mips",
+ target_arch = "mips64",
+ target_arch = "x86",
+ target_arch = "x86_64")))]
+ PTRACE_GETFPXREGS,
+ #[cfg(all(target_os = "linux", any(target_env = "musl",
+ target_arch = "mips",
+ target_arch = "mips64",
+ target_arch = "x86",
+ target_arch = "x86_64")))]
+ PTRACE_SETFPXREGS,
+ PTRACE_SYSCALL,
+ PTRACE_SETOPTIONS,
+ PTRACE_GETEVENTMSG,
+ PTRACE_GETSIGINFO,
+ PTRACE_SETSIGINFO,
+ #[cfg(all(target_os = "linux", not(any(target_arch = "mips",
+ target_arch = "mips64"))))]
+ PTRACE_GETREGSET,
+ #[cfg(all(target_os = "linux", not(any(target_arch = "mips",
+ target_arch = "mips64"))))]
+ PTRACE_SETREGSET,
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ PTRACE_SEIZE,
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ PTRACE_INTERRUPT,
+ #[cfg(all(target_os = "linux", not(any(target_arch = "mips",
+ target_arch = "mips64"))))]
+ PTRACE_LISTEN,
+ #[cfg(all(target_os = "linux", not(any(target_arch = "mips",
+ target_arch = "mips64"))))]
+ PTRACE_PEEKSIGINFO,
+ #[cfg(all(target_os = "linux", target_env = "gnu",
+ any(target_arch = "x86", target_arch = "x86_64")))]
+ PTRACE_SYSEMU,
+ #[cfg(all(target_os = "linux", target_env = "gnu",
+ any(target_arch = "x86", target_arch = "x86_64")))]
+ PTRACE_SYSEMU_SINGLESTEP,
+ }
+}
+
+libc_enum! {
+ #[repr(i32)]
+ /// Using the ptrace options the tracer can configure the tracee to stop
+ /// at certain events. This enum is used to define those events as defined
+ /// in `man ptrace`.
+ #[non_exhaustive]
+ pub enum Event {
+ /// Event that stops before a return from fork or clone.
+ PTRACE_EVENT_FORK,
+ /// Event that stops before a return from vfork or clone.
+ PTRACE_EVENT_VFORK,
+ /// Event that stops before a return from clone.
+ PTRACE_EVENT_CLONE,
+ /// Event that stops before a return from execve.
+ PTRACE_EVENT_EXEC,
+ /// Event for a return from vfork.
+ PTRACE_EVENT_VFORK_DONE,
+ /// Event for a stop before an exit. Unlike the waitpid Exit status program.
+ /// registers can still be examined
+ PTRACE_EVENT_EXIT,
+ /// Stop triggered by a seccomp rule on a tracee.
+ PTRACE_EVENT_SECCOMP,
+ /// Stop triggered by the `INTERRUPT` syscall, or a group stop,
+ /// or when a new child is attached.
+ PTRACE_EVENT_STOP,
+ }
+}
+
+libc_bitflags! {
+ /// Ptrace options used in conjunction with the PTRACE_SETOPTIONS request.
+ /// See `man ptrace` for more details.
+ pub struct Options: libc::c_int {
+ /// When delivering system call traps set a bit to allow tracer to
+ /// distinguish between normal stops or syscall stops. May not work on
+ /// all systems.
+ PTRACE_O_TRACESYSGOOD;
+ /// Stop tracee at next fork and start tracing the forked process.
+ PTRACE_O_TRACEFORK;
+ /// Stop tracee at next vfork call and trace the vforked process.
+ PTRACE_O_TRACEVFORK;
+ /// Stop tracee at next clone call and trace the cloned process.
+ PTRACE_O_TRACECLONE;
+ /// Stop tracee at next execve call.
+ PTRACE_O_TRACEEXEC;
+ /// Stop tracee at vfork completion.
+ PTRACE_O_TRACEVFORKDONE;
+ /// Stop tracee at next exit call. Stops before exit commences allowing
+ /// tracer to see location of exit and register states.
+ PTRACE_O_TRACEEXIT;
+ /// Stop tracee when a SECCOMP_RET_TRACE rule is triggered. See `man seccomp` for more
+ /// details.
+ PTRACE_O_TRACESECCOMP;
+ /// Send a SIGKILL to the tracee if the tracer exits. This is useful
+ /// for ptrace jailers to prevent tracees from escaping their control.
+ PTRACE_O_EXITKILL;
+ }
+}
+
+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)
+ };
+ match Errno::result(ret) {
+ Ok(..) | Err(Errno::UnknownErrno) => Ok(ret),
+ err @ Err(..) => err,
+ }
+}
+
+/// Get user registers, as with `ptrace(PTRACE_GETREGS, ...)`
+#[cfg(all(
+ target_os = "linux",
+ any(
+ all(
+ target_arch = "x86_64",
+ any(target_env = "gnu", target_env = "musl")
+ ),
+ all(target_arch = "x86", target_env = "gnu")
+ )
+))]
+pub fn getregs(pid: Pid) -> Result<user_regs_struct> {
+ ptrace_get_data::<user_regs_struct>(Request::PTRACE_GETREGS, pid)
+}
+
+/// Set user registers, as with `ptrace(PTRACE_SETREGS, ...)`
+#[cfg(all(
+ target_os = "linux",
+ any(
+ all(
+ target_arch = "x86_64",
+ any(target_env = "gnu", target_env = "musl")
+ ),
+ all(target_arch = "x86", target_env = "gnu")
+ )
+))]
+pub fn setregs(pid: Pid, regs: user_regs_struct) -> Result<()> {
+ let res = unsafe {
+ libc::ptrace(
+ Request::PTRACE_SETREGS as RequestType,
+ libc::pid_t::from(pid),
+ ptr::null_mut::<c_void>(),
+ &regs as *const _ as *const c_void,
+ )
+ };
+ Errno::result(res).map(drop)
+}
+
+/// Function for ptrace requests that return values from the data field.
+/// Some ptrace get requests populate structs or larger elements than `c_long`
+/// and therefore use the data field to return values. This function handles these
+/// requests.
+fn ptrace_get_data<T>(request: Request, pid: Pid) -> Result<T> {
+ let mut data = mem::MaybeUninit::uninit();
+ let res = unsafe {
+ libc::ptrace(
+ request as RequestType,
+ libc::pid_t::from(pid),
+ ptr::null_mut::<T>(),
+ data.as_mut_ptr() as *const _ as *const c_void,
+ )
+ };
+ Errno::result(res)?;
+ Ok(unsafe { data.assume_init() })
+}
+
+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)
+}
+
+/// Set options, as with `ptrace(PTRACE_SETOPTIONS,...)`.
+pub fn setoptions(pid: Pid, options: Options) -> Result<()> {
+ let res = unsafe {
+ libc::ptrace(
+ Request::PTRACE_SETOPTIONS as RequestType,
+ libc::pid_t::from(pid),
+ ptr::null_mut::<c_void>(),
+ options.bits() as *mut c_void,
+ )
+ };
+ Errno::result(res).map(drop)
+}
+
+/// Gets a ptrace event as described by `ptrace(PTRACE_GETEVENTMSG,...)`
+pub fn getevent(pid: Pid) -> Result<c_long> {
+ ptrace_get_data::<c_long>(Request::PTRACE_GETEVENTMSG, pid)
+}
+
+/// Get siginfo as with `ptrace(PTRACE_GETSIGINFO,...)`
+pub fn getsiginfo(pid: Pid) -> Result<siginfo_t> {
+ ptrace_get_data::<siginfo_t>(Request::PTRACE_GETSIGINFO, pid)
+}
+
+/// Set siginfo as with `ptrace(PTRACE_SETSIGINFO,...)`
+pub fn setsiginfo(pid: Pid, sig: &siginfo_t) -> Result<()> {
+ let ret = unsafe {
+ Errno::clear();
+ libc::ptrace(
+ Request::PTRACE_SETSIGINFO as RequestType,
+ libc::pid_t::from(pid),
+ ptr::null_mut::<c_void>(),
+ sig as *const _ as *const c_void,
+ )
+ };
+ match Errno::result(ret) {
+ Ok(_) => Ok(()),
+ Err(e) => Err(e),
+ }
+}
+
+/// Sets the process as traceable, as with `ptrace(PTRACE_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::PTRACE_TRACEME,
+ Pid::from_raw(0),
+ ptr::null_mut(),
+ ptr::null_mut(),
+ )
+ .map(drop) // ignore the useless return value
+ }
+}
+
+/// Continue execution until the next syscall, as with `ptrace(PTRACE_SYSCALL, ...)`
+///
+/// Arranges for the tracee to be stopped at the next entry to or exit from a system call,
+/// optionally delivering a signal specified by `sig`.
+pub fn syscall<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
+ let data = match sig.into() {
+ Some(s) => s as i32 as *mut c_void,
+ None => ptr::null_mut(),
+ };
+ unsafe {
+ ptrace_other(Request::PTRACE_SYSCALL, pid, ptr::null_mut(), data)
+ .map(drop) // ignore the useless return value
+ }
+}
+
+/// Continue execution until the next syscall, as with `ptrace(PTRACE_SYSEMU, ...)`
+///
+/// In contrast to the `syscall` function, the syscall stopped at will not be executed.
+/// Thus the the tracee will only be stopped once per syscall,
+/// optionally delivering a signal specified by `sig`.
+#[cfg(all(
+ target_os = "linux",
+ target_env = "gnu",
+ any(target_arch = "x86", target_arch = "x86_64")
+))]
+pub fn sysemu<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
+ let data = match sig.into() {
+ Some(s) => s as i32 as *mut c_void,
+ None => ptr::null_mut(),
+ };
+ unsafe {
+ ptrace_other(Request::PTRACE_SYSEMU, pid, ptr::null_mut(), data)
+ .map(drop)
+ // ignore the useless return value
+ }
+}
+
+/// Attach to a running process, as with `ptrace(PTRACE_ATTACH, ...)`
+///
+/// Attaches to the process specified by `pid`, making it a tracee of the calling process.
+pub fn attach(pid: Pid) -> Result<()> {
+ unsafe {
+ ptrace_other(
+ Request::PTRACE_ATTACH,
+ pid,
+ ptr::null_mut(),
+ ptr::null_mut(),
+ )
+ .map(drop) // ignore the useless return value
+ }
+}
+
+/// Attach to a running process, as with `ptrace(PTRACE_SEIZE, ...)`
+///
+/// Attaches to the process specified in pid, making it a tracee of the calling process.
+#[cfg(target_os = "linux")]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub fn seize(pid: Pid, options: Options) -> Result<()> {
+ unsafe {
+ ptrace_other(
+ Request::PTRACE_SEIZE,
+ pid,
+ ptr::null_mut(),
+ options.bits() as *mut c_void,
+ )
+ .map(drop) // ignore the useless return value
+ }
+}
+
+/// Detaches the current running process, as with `ptrace(PTRACE_DETACH, ...)`
+///
+/// Detaches from the process specified by `pid` allowing it to run freely, optionally delivering a
+/// signal specified by `sig`.
+pub fn detach<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
+ let data = match sig.into() {
+ Some(s) => s as i32 as *mut c_void,
+ None => ptr::null_mut(),
+ };
+ unsafe {
+ ptrace_other(Request::PTRACE_DETACH, pid, ptr::null_mut(), data)
+ .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 i32 as *mut c_void,
+ None => ptr::null_mut(),
+ };
+ unsafe {
+ ptrace_other(Request::PTRACE_CONT, pid, ptr::null_mut(), data).map(drop)
+ // ignore the useless return value
+ }
+}
+
+/// Stop a tracee, as with `ptrace(PTRACE_INTERRUPT, ...)`
+///
+/// This request is equivalent to `ptrace(PTRACE_INTERRUPT, ...)`
+#[cfg(target_os = "linux")]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub fn interrupt(pid: Pid) -> Result<()> {
+ unsafe {
+ ptrace_other(
+ Request::PTRACE_INTERRUPT,
+ pid,
+ ptr::null_mut(),
+ ptr::null_mut(),
+ )
+ .map(drop)
+ }
+}
+
+/// 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)
+ }
+}
+
+/// Move the stopped tracee process forward by a single step as with
+/// `ptrace(PTRACE_SINGLESTEP, ...)`
+///
+/// Advances the execution of the process with PID `pid` by a single step optionally delivering a
+/// signal specified by `sig`.
+///
+/// # Example
+/// ```rust
+/// use nix::sys::ptrace::step;
+/// use nix::unistd::Pid;
+/// use nix::sys::signal::Signal;
+/// use nix::sys::wait::*;
+///
+/// // 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);
+/// }
+/// _ => {},
+/// }
+/// ```
+pub fn step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
+ let data = match sig.into() {
+ Some(s) => s as i32 as *mut c_void,
+ None => ptr::null_mut(),
+ };
+ unsafe {
+ ptrace_other(Request::PTRACE_SINGLESTEP, pid, ptr::null_mut(), data)
+ .map(drop)
+ }
+}
+
+/// Move the stopped tracee process forward by a single step or stop at the next syscall
+/// as with `ptrace(PTRACE_SYSEMU_SINGLESTEP, ...)`
+///
+/// Advances the execution by a single step or until the next syscall.
+/// In case the tracee is stopped at a syscall, the syscall will not be executed.
+/// Optionally, the signal specified by `sig` is delivered to the tracee upon continuation.
+#[cfg(all(
+ target_os = "linux",
+ target_env = "gnu",
+ any(target_arch = "x86", target_arch = "x86_64")
+))]
+pub fn sysemu_step<T: Into<Option<Signal>>>(pid: Pid, sig: T) -> Result<()> {
+ let data = match sig.into() {
+ Some(s) => s as i32 as *mut c_void,
+ None => ptr::null_mut(),
+ };
+ unsafe {
+ ptrace_other(
+ Request::PTRACE_SYSEMU_SINGLESTEP,
+ pid,
+ ptr::null_mut(),
+ data,
+ )
+ .map(drop) // ignore the useless return value
+ }
+}
+
+/// 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
+///
+/// # Safety
+///
+/// The `data` argument is passed directly to `ptrace(2)`. Read that man page
+/// for guidance.
+pub unsafe fn write(
+ pid: Pid,
+ addr: AddressType,
+ data: *mut c_void,
+) -> Result<()> {
+ ptrace_other(Request::PTRACE_POKEDATA, pid, addr, data).map(drop)
+}
+
+/// Reads a word from a user area at `offset`.
+/// The user struct definition can be found in `/usr/include/sys/user.h`.
+pub fn read_user(pid: Pid, offset: AddressType) -> Result<c_long> {
+ ptrace_peek(Request::PTRACE_PEEKUSER, pid, offset, ptr::null_mut())
+}
+
+/// Writes a word to a user area at `offset`.
+/// The user struct definition can be found in `/usr/include/sys/user.h`.
+///
+/// # Safety
+///
+/// The `data` argument is passed directly to `ptrace(2)`. Read that man page
+/// for guidance.
+pub unsafe fn write_user(
+ pid: Pid,
+ offset: AddressType,
+ data: *mut c_void,
+) -> Result<()> {
+ ptrace_other(Request::PTRACE_POKEUSER, pid, offset, data).map(drop)
+}
diff --git a/third_party/rust/nix/src/sys/ptrace/mod.rs b/third_party/rust/nix/src/sys/ptrace/mod.rs
new file mode 100644
index 0000000000..2b121c0b4d
--- /dev/null
+++ b/third_party/rust/nix/src/sys/ptrace/mod.rs
@@ -0,0 +1,25 @@
+///! 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/third_party/rust/nix/src/sys/quota.rs b/third_party/rust/nix/src/sys/quota.rs
new file mode 100644
index 0000000000..b3c44ca705
--- /dev/null
+++ b/third_party/rust/nix/src/sys/quota.rs
@@ -0,0 +1,338 @@
+//! Set and configure disk quotas for users, groups, or projects.
+//!
+//! # Examples
+//!
+//! Enabling and setting a quota:
+//!
+//! ```rust,no_run
+//! # use nix::sys::quota::{Dqblk, quotactl_on, quotactl_set, QuotaFmt, QuotaType, QuotaValidFlags};
+//! quotactl_on(QuotaType::USRQUOTA, "/dev/sda1", QuotaFmt::QFMT_VFS_V1, "aquota.user").unwrap();
+//! let mut dqblk: Dqblk = Default::default();
+//! dqblk.set_blocks_hard_limit(10000);
+//! dqblk.set_blocks_soft_limit(8000);
+//! quotactl_set(QuotaType::USRQUOTA, "/dev/sda1", 50, &dqblk, QuotaValidFlags::QIF_BLIMITS).unwrap();
+//! ```
+use crate::errno::Errno;
+use crate::{NixPath, Result};
+use libc::{self, c_char, c_int};
+use std::default::Default;
+use std::{mem, ptr};
+
+struct QuotaCmd(QuotaSubCmd, QuotaType);
+
+impl QuotaCmd {
+ #[allow(unused_unsafe)]
+ fn as_int(&self) -> c_int {
+ unsafe { libc::QCMD(self.0 as i32, self.1 as i32) }
+ }
+}
+
+// linux quota version >= 2
+libc_enum! {
+ #[repr(i32)]
+ enum QuotaSubCmd {
+ Q_SYNC,
+ Q_QUOTAON,
+ Q_QUOTAOFF,
+ Q_GETQUOTA,
+ Q_SETQUOTA,
+ }
+}
+
+libc_enum! {
+ /// The scope of the quota.
+ #[repr(i32)]
+ #[non_exhaustive]
+ pub enum QuotaType {
+ /// Specify a user quota
+ USRQUOTA,
+ /// Specify a group quota
+ GRPQUOTA,
+ }
+}
+
+libc_enum! {
+ /// The type of quota format to use.
+ #[repr(i32)]
+ #[non_exhaustive]
+ pub enum QuotaFmt {
+ /// Use the original quota format.
+ QFMT_VFS_OLD,
+ /// Use the standard VFS v0 quota format.
+ ///
+ /// Handles 32-bit UIDs/GIDs and quota limits up to 2<sup>32</sup> bytes/2<sup>32</sup> inodes.
+ QFMT_VFS_V0,
+ /// Use the VFS v1 quota format.
+ ///
+ /// Handles 32-bit UIDs/GIDs and quota limits of 2<sup>64</sup> bytes/2<sup>64</sup> inodes.
+ QFMT_VFS_V1,
+ }
+}
+
+libc_bitflags!(
+ /// Indicates the quota fields that are valid to read from.
+ #[derive(Default)]
+ pub struct QuotaValidFlags: u32 {
+ /// The block hard & soft limit fields.
+ QIF_BLIMITS;
+ /// The current space field.
+ QIF_SPACE;
+ /// The inode hard & soft limit fields.
+ QIF_ILIMITS;
+ /// The current inodes field.
+ QIF_INODES;
+ /// The disk use time limit field.
+ QIF_BTIME;
+ /// The file quote time limit field.
+ QIF_ITIME;
+ /// All block & inode limits.
+ QIF_LIMITS;
+ /// The space & inodes usage fields.
+ QIF_USAGE;
+ /// The time limit fields.
+ QIF_TIMES;
+ /// All fields.
+ QIF_ALL;
+ }
+);
+
+/// Wrapper type for `if_dqblk`
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct Dqblk(libc::dqblk);
+
+impl Default for Dqblk {
+ fn default() -> Dqblk {
+ Dqblk(libc::dqblk {
+ dqb_bhardlimit: 0,
+ dqb_bsoftlimit: 0,
+ dqb_curspace: 0,
+ dqb_ihardlimit: 0,
+ dqb_isoftlimit: 0,
+ dqb_curinodes: 0,
+ dqb_btime: 0,
+ dqb_itime: 0,
+ dqb_valid: 0,
+ })
+ }
+}
+
+impl Dqblk {
+ /// The absolute limit on disk quota blocks allocated.
+ pub fn blocks_hard_limit(&self) -> Option<u64> {
+ let valid_fields =
+ QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
+ if valid_fields.contains(QuotaValidFlags::QIF_BLIMITS) {
+ Some(self.0.dqb_bhardlimit)
+ } else {
+ None
+ }
+ }
+
+ /// Set the absolute limit on disk quota blocks allocated.
+ pub fn set_blocks_hard_limit(&mut self, limit: u64) {
+ self.0.dqb_bhardlimit = limit;
+ }
+
+ /// Preferred limit on disk quota blocks
+ pub fn blocks_soft_limit(&self) -> Option<u64> {
+ let valid_fields =
+ QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
+ if valid_fields.contains(QuotaValidFlags::QIF_BLIMITS) {
+ Some(self.0.dqb_bsoftlimit)
+ } else {
+ None
+ }
+ }
+
+ /// Set the preferred limit on disk quota blocks allocated.
+ pub fn set_blocks_soft_limit(&mut self, limit: u64) {
+ self.0.dqb_bsoftlimit = limit;
+ }
+
+ /// Current occupied space (bytes).
+ pub fn occupied_space(&self) -> Option<u64> {
+ let valid_fields =
+ QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
+ if valid_fields.contains(QuotaValidFlags::QIF_SPACE) {
+ Some(self.0.dqb_curspace)
+ } else {
+ None
+ }
+ }
+
+ /// Maximum number of allocated inodes.
+ pub fn inodes_hard_limit(&self) -> Option<u64> {
+ let valid_fields =
+ QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
+ if valid_fields.contains(QuotaValidFlags::QIF_ILIMITS) {
+ Some(self.0.dqb_ihardlimit)
+ } else {
+ None
+ }
+ }
+
+ /// Set the maximum number of allocated inodes.
+ pub fn set_inodes_hard_limit(&mut self, limit: u64) {
+ self.0.dqb_ihardlimit = limit;
+ }
+
+ /// Preferred inode limit
+ pub fn inodes_soft_limit(&self) -> Option<u64> {
+ let valid_fields =
+ QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
+ if valid_fields.contains(QuotaValidFlags::QIF_ILIMITS) {
+ Some(self.0.dqb_isoftlimit)
+ } else {
+ None
+ }
+ }
+
+ /// Set the preferred limit of allocated inodes.
+ pub fn set_inodes_soft_limit(&mut self, limit: u64) {
+ self.0.dqb_isoftlimit = limit;
+ }
+
+ /// Current number of allocated inodes.
+ pub fn allocated_inodes(&self) -> Option<u64> {
+ let valid_fields =
+ QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
+ if valid_fields.contains(QuotaValidFlags::QIF_INODES) {
+ Some(self.0.dqb_curinodes)
+ } else {
+ None
+ }
+ }
+
+ /// Time limit for excessive disk use.
+ pub fn block_time_limit(&self) -> Option<u64> {
+ let valid_fields =
+ QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
+ if valid_fields.contains(QuotaValidFlags::QIF_BTIME) {
+ Some(self.0.dqb_btime)
+ } else {
+ None
+ }
+ }
+
+ /// Set the time limit for excessive disk use.
+ pub fn set_block_time_limit(&mut self, limit: u64) {
+ self.0.dqb_btime = limit;
+ }
+
+ /// Time limit for excessive files.
+ pub fn inode_time_limit(&self) -> Option<u64> {
+ let valid_fields =
+ QuotaValidFlags::from_bits_truncate(self.0.dqb_valid);
+ if valid_fields.contains(QuotaValidFlags::QIF_ITIME) {
+ Some(self.0.dqb_itime)
+ } else {
+ None
+ }
+ }
+
+ /// Set the time limit for excessive files.
+ pub fn set_inode_time_limit(&mut self, limit: u64) {
+ self.0.dqb_itime = limit;
+ }
+}
+
+fn quotactl<P: ?Sized + NixPath>(
+ cmd: QuotaCmd,
+ special: Option<&P>,
+ id: c_int,
+ addr: *mut c_char,
+) -> Result<()> {
+ unsafe {
+ Errno::clear();
+ let res = match special {
+ Some(dev) => dev.with_nix_path(|path| {
+ libc::quotactl(cmd.as_int(), path.as_ptr(), id, addr)
+ }),
+ None => Ok(libc::quotactl(cmd.as_int(), ptr::null(), id, addr)),
+ }?;
+
+ Errno::result(res).map(drop)
+ }
+}
+
+/// Turn on disk quotas for a block device.
+pub fn quotactl_on<P: ?Sized + NixPath>(
+ which: QuotaType,
+ special: &P,
+ format: QuotaFmt,
+ quota_file: &P,
+) -> Result<()> {
+ quota_file.with_nix_path(|path| {
+ let mut path_copy = path.to_bytes_with_nul().to_owned();
+ let p: *mut c_char = path_copy.as_mut_ptr() as *mut c_char;
+ quotactl(
+ QuotaCmd(QuotaSubCmd::Q_QUOTAON, which),
+ Some(special),
+ format as c_int,
+ p,
+ )
+ })?
+}
+
+/// Disable disk quotas for a block device.
+pub fn quotactl_off<P: ?Sized + NixPath>(
+ which: QuotaType,
+ special: &P,
+) -> Result<()> {
+ quotactl(
+ QuotaCmd(QuotaSubCmd::Q_QUOTAOFF, which),
+ Some(special),
+ 0,
+ ptr::null_mut(),
+ )
+}
+
+/// Update the on-disk copy of quota usages for a filesystem.
+///
+/// If `special` is `None`, then all file systems with active quotas are sync'd.
+pub fn quotactl_sync<P: ?Sized + NixPath>(
+ which: QuotaType,
+ special: Option<&P>,
+) -> Result<()> {
+ quotactl(
+ QuotaCmd(QuotaSubCmd::Q_SYNC, which),
+ special,
+ 0,
+ ptr::null_mut(),
+ )
+}
+
+/// Get disk quota limits and current usage for the given user/group id.
+pub fn quotactl_get<P: ?Sized + NixPath>(
+ which: QuotaType,
+ special: &P,
+ id: c_int,
+) -> Result<Dqblk> {
+ let mut dqblk = mem::MaybeUninit::uninit();
+ quotactl(
+ QuotaCmd(QuotaSubCmd::Q_GETQUOTA, which),
+ Some(special),
+ id,
+ dqblk.as_mut_ptr() as *mut c_char,
+ )?;
+ Ok(unsafe { Dqblk(dqblk.assume_init()) })
+}
+
+/// Configure quota values for the specified fields for a given user/group id.
+pub fn quotactl_set<P: ?Sized + NixPath>(
+ which: QuotaType,
+ special: &P,
+ id: c_int,
+ dqblk: &Dqblk,
+ fields: QuotaValidFlags,
+) -> Result<()> {
+ let mut dqblk_copy = *dqblk;
+ dqblk_copy.0.dqb_valid = fields.bits();
+ quotactl(
+ QuotaCmd(QuotaSubCmd::Q_SETQUOTA, which),
+ Some(special),
+ id,
+ &mut dqblk_copy as *mut _ as *mut c_char,
+ )
+}
diff --git a/third_party/rust/nix/src/sys/reboot.rs b/third_party/rust/nix/src/sys/reboot.rs
new file mode 100644
index 0000000000..02d98162bd
--- /dev/null
+++ b/third_party/rust/nix/src/sys/reboot.rs
@@ -0,0 +1,48 @@
+//! Reboot/shutdown or enable/disable Ctrl-Alt-Delete.
+
+use crate::errno::Errno;
+use crate::Result;
+use std::convert::Infallible;
+use std::mem::drop;
+
+libc_enum! {
+ /// How exactly should the system be rebooted.
+ ///
+ /// See [`set_cad_enabled()`](fn.set_cad_enabled.html) for
+ /// enabling/disabling Ctrl-Alt-Delete.
+ #[repr(i32)]
+ #[non_exhaustive]
+ pub enum RebootMode {
+ /// Halt the system.
+ RB_HALT_SYSTEM,
+ /// Execute a kernel that has been loaded earlier with
+ /// [`kexec_load(2)`](https://man7.org/linux/man-pages/man2/kexec_load.2.html).
+ RB_KEXEC,
+ /// Stop the system and switch off power, if possible.
+ RB_POWER_OFF,
+ /// Restart the system.
+ RB_AUTOBOOT,
+ // we do not support Restart2.
+ /// Suspend the system using software suspend.
+ RB_SW_SUSPEND,
+ }
+}
+
+/// Reboots or shuts down the system.
+pub fn reboot(how: RebootMode) -> Result<Infallible> {
+ unsafe { libc::reboot(how as libc::c_int) };
+ Err(Errno::last())
+}
+
+/// Enable or disable the reboot keystroke (Ctrl-Alt-Delete).
+///
+/// Corresponds to calling `reboot(RB_ENABLE_CAD)` or `reboot(RB_DISABLE_CAD)` in C.
+pub fn set_cad_enabled(enable: bool) -> Result<()> {
+ let cmd = if enable {
+ libc::RB_ENABLE_CAD
+ } else {
+ libc::RB_DISABLE_CAD
+ };
+ let res = unsafe { libc::reboot(cmd) };
+ Errno::result(res).map(drop)
+}
diff --git a/third_party/rust/nix/src/sys/resource.rs b/third_party/rust/nix/src/sys/resource.rs
new file mode 100644
index 0000000000..8927737763
--- /dev/null
+++ b/third_party/rust/nix/src/sys/resource.rs
@@ -0,0 +1,443 @@
+//! Configure the process resource limits.
+use cfg_if::cfg_if;
+use libc::{c_int, c_long, rusage};
+
+use crate::errno::Errno;
+use crate::sys::time::TimeVal;
+use crate::Result;
+pub use libc::rlim_t;
+pub use libc::RLIM_INFINITY;
+use std::mem;
+
+cfg_if! {
+ if #[cfg(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))]{
+ use libc::{__rlimit_resource_t, rlimit};
+ } else if #[cfg(any(
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "android",
+ target_os = "dragonfly",
+ all(target_os = "linux", not(target_env = "gnu"))
+ ))]{
+ use libc::rlimit;
+ }
+}
+
+libc_enum! {
+ /// Types of process resources.
+ ///
+ /// The Resource enum is platform dependent. Check different platform
+ /// manuals for more details. Some platform links have been provided for
+ /// easier reference (non-exhaustive).
+ ///
+ /// * [Linux](https://man7.org/linux/man-pages/man2/getrlimit.2.html)
+ /// * [FreeBSD](https://www.freebsd.org/cgi/man.cgi?query=setrlimit)
+ /// * [NetBSD](https://man.netbsd.org/setrlimit.2)
+
+ // linux-gnu uses u_int as resource enum, which is implemented in libc as
+ // well.
+ //
+ // https://gcc.gnu.org/legacy-ml/gcc/2015-08/msg00441.html
+ // https://github.com/rust-lang/libc/blob/master/src/unix/linux_like/linux/gnu/mod.rs
+ #[cfg_attr(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")), repr(u32))]
+ #[cfg_attr(any(
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "android",
+ target_os = "dragonfly",
+ all(target_os = "linux", not(any(target_env = "gnu", target_env = "uclibc")))
+ ), repr(i32))]
+ #[non_exhaustive]
+ pub enum Resource {
+ #[cfg(not(any(target_os = "freebsd", target_os = "netbsd", target_os = "openbsd")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The maximum amount (in bytes) of virtual memory the process is
+ /// allowed to map.
+ RLIMIT_AS,
+ /// The largest size (in bytes) core(5) file that may be created.
+ RLIMIT_CORE,
+ /// The maximum amount of cpu time (in seconds) to be used by each
+ /// process.
+ RLIMIT_CPU,
+ /// The maximum size (in bytes) of the data segment for a process
+ RLIMIT_DATA,
+ /// The largest size (in bytes) file that may be created.
+ RLIMIT_FSIZE,
+ /// The maximum number of open files for this process.
+ RLIMIT_NOFILE,
+ /// The maximum size (in bytes) of the stack segment for a process.
+ RLIMIT_STACK,
+
+ #[cfg(target_os = "freebsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The maximum number of kqueues this user id is allowed to create.
+ RLIMIT_KQUEUES,
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// A limit on the combined number of flock locks and fcntl leases that
+ /// this process may establish.
+ RLIMIT_LOCKS,
+
+ #[cfg(any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "linux",
+ target_os = "netbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The maximum size (in bytes) which a process may lock into memory
+ /// using the mlock(2) system call.
+ RLIMIT_MEMLOCK,
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// A limit on the number of bytes that can be allocated for POSIX
+ /// message queues for the real user ID of the calling process.
+ RLIMIT_MSGQUEUE,
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// A ceiling to which the process's nice value can be raised using
+ /// setpriority or nice.
+ RLIMIT_NICE,
+
+ #[cfg(any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "linux",
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The maximum number of simultaneous processes for this user id.
+ RLIMIT_NPROC,
+
+ #[cfg(target_os = "freebsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The maximum number of pseudo-terminals this user id is allowed to
+ /// create.
+ RLIMIT_NPTS,
+
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "linux",
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// When there is memory pressure and swap is available, prioritize
+ /// eviction of a process' resident pages beyond this amount (in bytes).
+ RLIMIT_RSS,
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// A ceiling on the real-time priority that may be set for this process
+ /// using sched_setscheduler and sched_set‐ param.
+ RLIMIT_RTPRIO,
+
+ #[cfg(any(target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// A limit (in microseconds) on the amount of CPU time that a process
+ /// scheduled under a real-time scheduling policy may con‐ sume without
+ /// making a blocking system call.
+ RLIMIT_RTTIME,
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// A limit on the number of signals that may be queued for the real
+ /// user ID of the calling process.
+ RLIMIT_SIGPENDING,
+
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The maximum size (in bytes) of socket buffer usage for this user.
+ RLIMIT_SBSIZE,
+
+ #[cfg(target_os = "freebsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The maximum size (in bytes) of the swap space that may be reserved
+ /// or used by all of this user id's processes.
+ RLIMIT_SWAP,
+
+ #[cfg(target_os = "freebsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// An alias for RLIMIT_AS.
+ RLIMIT_VMEM,
+ }
+}
+
+/// Get the current processes resource limits
+///
+/// The special value [`RLIM_INFINITY`] indicates that no limit will be
+/// enforced.
+///
+/// # Parameters
+///
+/// * `resource`: The [`Resource`] that we want to get the limits of.
+///
+/// # Examples
+///
+/// ```
+/// # use nix::sys::resource::{getrlimit, Resource};
+///
+/// let (soft_limit, hard_limit) = getrlimit(Resource::RLIMIT_NOFILE).unwrap();
+/// println!("current soft_limit: {}", soft_limit);
+/// println!("current hard_limit: {}", hard_limit);
+/// ```
+///
+/// # References
+///
+/// [getrlimit(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html#tag_16_215)
+///
+/// [`Resource`]: enum.Resource.html
+pub fn getrlimit(resource: Resource) -> Result<(rlim_t, rlim_t)> {
+ let mut old_rlim = mem::MaybeUninit::<rlimit>::uninit();
+
+ cfg_if! {
+ if #[cfg(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))]{
+ let res = unsafe { libc::getrlimit(resource as __rlimit_resource_t, old_rlim.as_mut_ptr()) };
+ } else {
+ let res = unsafe { libc::getrlimit(resource as c_int, old_rlim.as_mut_ptr()) };
+ }
+ }
+
+ Errno::result(res).map(|_| {
+ let rlimit { rlim_cur, rlim_max } = unsafe { old_rlim.assume_init() };
+ (rlim_cur, rlim_max)
+ })
+}
+
+/// Set the current processes resource limits
+///
+/// # Parameters
+///
+/// * `resource`: The [`Resource`] that we want to set the limits of.
+/// * `soft_limit`: The value that the kernel enforces for the corresponding
+/// resource.
+/// * `hard_limit`: The ceiling for the soft limit. Must be lower or equal to
+/// the current hard limit for non-root users.
+///
+/// The special value [`RLIM_INFINITY`] indicates that no limit will be
+/// enforced.
+///
+/// # Examples
+///
+/// ```
+/// # use nix::sys::resource::{setrlimit, Resource};
+///
+/// let soft_limit = 512;
+/// let hard_limit = 1024;
+/// setrlimit(Resource::RLIMIT_NOFILE, soft_limit, hard_limit).unwrap();
+/// ```
+///
+/// # References
+///
+/// [setrlimit(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getrlimit.html#tag_16_215)
+///
+/// [`Resource`]: enum.Resource.html
+///
+/// Note: `setrlimit` provides a safe wrapper to libc's `setrlimit`.
+pub fn setrlimit(
+ resource: Resource,
+ soft_limit: rlim_t,
+ hard_limit: rlim_t,
+) -> Result<()> {
+ let new_rlim = rlimit {
+ rlim_cur: soft_limit,
+ rlim_max: hard_limit,
+ };
+ cfg_if! {
+ if #[cfg(all(target_os = "linux", any(target_env = "gnu", target_env = "uclibc")))]{
+ let res = unsafe { libc::setrlimit(resource as __rlimit_resource_t, &new_rlim as *const rlimit) };
+ }else{
+ let res = unsafe { libc::setrlimit(resource as c_int, &new_rlim as *const rlimit) };
+ }
+ }
+
+ Errno::result(res).map(drop)
+}
+
+libc_enum! {
+ /// Whose resource usage should be returned by [`getrusage`].
+ #[repr(i32)]
+ #[non_exhaustive]
+ pub enum UsageWho {
+ /// Resource usage for the current process.
+ RUSAGE_SELF,
+
+ /// Resource usage for all the children that have terminated and been waited for.
+ RUSAGE_CHILDREN,
+
+ #[cfg(any(target_os = "linux", target_os = "freebsd", target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Resource usage for the calling thread.
+ RUSAGE_THREAD,
+ }
+}
+
+/// Output of `getrusage` with information about resource usage. Some of the fields
+/// may be unused in some platforms, and will be always zeroed out. See their manuals
+/// for details.
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct Usage(rusage);
+
+impl AsRef<rusage> for Usage {
+ fn as_ref(&self) -> &rusage {
+ &self.0
+ }
+}
+
+impl AsMut<rusage> for Usage {
+ fn as_mut(&mut self) -> &mut rusage {
+ &mut self.0
+ }
+}
+
+impl Usage {
+ /// Total amount of time spent executing in user mode.
+ pub fn user_time(&self) -> TimeVal {
+ TimeVal::from(self.0.ru_utime)
+ }
+
+ /// Total amount of time spent executing in kernel mode.
+ pub fn system_time(&self) -> TimeVal {
+ TimeVal::from(self.0.ru_stime)
+ }
+
+ /// The resident set size at its peak, in kilobytes.
+ pub fn max_rss(&self) -> c_long {
+ self.0.ru_maxrss
+ }
+
+ /// Integral value expressed in kilobytes times ticks of execution indicating
+ /// the amount of text memory shared with other processes.
+ pub fn shared_integral(&self) -> c_long {
+ self.0.ru_ixrss
+ }
+
+ /// Integral value expressed in kilobytes times ticks of execution indicating
+ /// the amount of unshared memory used by data.
+ pub fn unshared_data_integral(&self) -> c_long {
+ self.0.ru_idrss
+ }
+
+ /// Integral value expressed in kilobytes times ticks of execution indicating
+ /// the amount of unshared memory used for stack space.
+ pub fn unshared_stack_integral(&self) -> c_long {
+ self.0.ru_isrss
+ }
+
+ /// Number of page faults that were served without resorting to I/O, with pages
+ /// that have been allocated previously by the kernel.
+ pub fn minor_page_faults(&self) -> c_long {
+ self.0.ru_minflt
+ }
+
+ /// Number of page faults that were served through I/O (i.e. swap).
+ pub fn major_page_faults(&self) -> c_long {
+ self.0.ru_majflt
+ }
+
+ /// Number of times all of the memory was fully swapped out.
+ pub fn full_swaps(&self) -> c_long {
+ self.0.ru_nswap
+ }
+
+ /// Number of times a read was done from a block device.
+ pub fn block_reads(&self) -> c_long {
+ self.0.ru_inblock
+ }
+
+ /// Number of times a write was done to a block device.
+ pub fn block_writes(&self) -> c_long {
+ self.0.ru_oublock
+ }
+
+ /// Number of IPC messages sent.
+ pub fn ipc_sends(&self) -> c_long {
+ self.0.ru_msgsnd
+ }
+
+ /// Number of IPC messages received.
+ pub fn ipc_receives(&self) -> c_long {
+ self.0.ru_msgrcv
+ }
+
+ /// Number of signals received.
+ pub fn signals(&self) -> c_long {
+ self.0.ru_nsignals
+ }
+
+ /// Number of times a context switch was voluntarily invoked.
+ pub fn voluntary_context_switches(&self) -> c_long {
+ self.0.ru_nvcsw
+ }
+
+ /// Number of times a context switch was imposed by the kernel (usually due to
+ /// time slice expiring or preemption by a higher priority process).
+ pub fn involuntary_context_switches(&self) -> c_long {
+ self.0.ru_nivcsw
+ }
+}
+
+/// Get usage information for a process, its children or the current thread
+///
+/// Real time information can be obtained for either the current process or (in some
+/// systems) thread, but information about children processes is only provided for
+/// those that have terminated and been waited for (see [`super::wait::wait`]).
+///
+/// Some information may be missing depending on the platform, and the way information
+/// is provided for children may also vary. Check the manuals for details.
+///
+/// # References
+///
+/// * [getrusage(2)](https://pubs.opengroup.org/onlinepubs/009696699/functions/getrusage.html)
+/// * [Linux](https://man7.org/linux/man-pages/man2/getrusage.2.html)
+/// * [FreeBSD](https://www.freebsd.org/cgi/man.cgi?query=getrusage)
+/// * [NetBSD](https://man.netbsd.org/getrusage.2)
+/// * [MacOS](https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/getrusage.2.html)
+///
+/// [`UsageWho`]: enum.UsageWho.html
+///
+/// Note: `getrusage` provides a safe wrapper to libc's [`libc::getrusage`].
+pub fn getrusage(who: UsageWho) -> Result<Usage> {
+ unsafe {
+ let mut rusage = mem::MaybeUninit::<rusage>::uninit();
+ let res = libc::getrusage(who as c_int, rusage.as_mut_ptr());
+ Errno::result(res).map(|_| Usage(rusage.assume_init()))
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use super::{getrusage, UsageWho};
+
+ #[test]
+ pub fn test_self_cpu_time() {
+ // Make sure some CPU time is used.
+ let mut numbers: Vec<i32> = (1..1_000_000).collect();
+ numbers.iter_mut().for_each(|item| *item *= 2);
+
+ // FIXME: this is here to help ensure the compiler does not optimize the whole
+ // thing away. Replace the assert with test::black_box once stabilized.
+ assert_eq!(numbers[100..200].iter().sum::<i32>(), 30_100);
+
+ let usage = getrusage(UsageWho::RUSAGE_SELF)
+ .expect("Failed to call getrusage for SELF");
+ let rusage = usage.as_ref();
+
+ let user = usage.user_time();
+ assert!(user.tv_sec() > 0 || user.tv_usec() > 0);
+ assert_eq!(user.tv_sec(), rusage.ru_utime.tv_sec);
+ assert_eq!(user.tv_usec(), rusage.ru_utime.tv_usec);
+ }
+}
diff --git a/third_party/rust/nix/src/sys/select.rs b/third_party/rust/nix/src/sys/select.rs
new file mode 100644
index 0000000000..7a94cff87e
--- /dev/null
+++ b/third_party/rust/nix/src/sys/select.rs
@@ -0,0 +1,455 @@
+//! Portably monitor a group of file descriptors for readiness.
+use crate::errno::Errno;
+use crate::sys::time::{TimeSpec, TimeVal};
+use crate::Result;
+use libc::{self, c_int};
+use std::convert::TryFrom;
+use std::iter::FusedIterator;
+use std::mem;
+use std::ops::Range;
+use std::os::unix::io::RawFd;
+use std::ptr::{null, null_mut};
+
+pub use libc::FD_SETSIZE;
+
+/// Contains a set of file descriptors used by [`select`]
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct FdSet(libc::fd_set);
+
+fn assert_fd_valid(fd: RawFd) {
+ assert!(
+ usize::try_from(fd).map_or(false, |fd| fd < FD_SETSIZE),
+ "fd must be in the range 0..FD_SETSIZE",
+ );
+}
+
+impl FdSet {
+ /// Create an empty `FdSet`
+ pub fn new() -> FdSet {
+ let mut fdset = mem::MaybeUninit::uninit();
+ unsafe {
+ libc::FD_ZERO(fdset.as_mut_ptr());
+ FdSet(fdset.assume_init())
+ }
+ }
+
+ /// Add a file descriptor to an `FdSet`
+ pub fn insert(&mut self, fd: RawFd) {
+ assert_fd_valid(fd);
+ unsafe { libc::FD_SET(fd, &mut self.0) };
+ }
+
+ /// Remove a file descriptor from an `FdSet`
+ pub fn remove(&mut self, fd: RawFd) {
+ assert_fd_valid(fd);
+ unsafe { libc::FD_CLR(fd, &mut self.0) };
+ }
+
+ /// Test an `FdSet` for the presence of a certain file descriptor.
+ pub fn contains(&self, fd: RawFd) -> bool {
+ assert_fd_valid(fd);
+ unsafe { libc::FD_ISSET(fd, &self.0) }
+ }
+
+ /// Remove all file descriptors from this `FdSet`.
+ pub fn clear(&mut self) {
+ unsafe { libc::FD_ZERO(&mut self.0) };
+ }
+
+ /// Finds the highest file descriptor in the set.
+ ///
+ /// Returns `None` if the set is empty.
+ ///
+ /// This can be used to calculate the `nfds` parameter of the [`select`] function.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # use nix::sys::select::FdSet;
+ /// let mut set = FdSet::new();
+ /// set.insert(4);
+ /// set.insert(9);
+ /// assert_eq!(set.highest(), Some(9));
+ /// ```
+ ///
+ /// [`select`]: fn.select.html
+ pub fn highest(&self) -> Option<RawFd> {
+ self.fds(None).next_back()
+ }
+
+ /// Returns an iterator over the file descriptors in the set.
+ ///
+ /// For performance, it takes an optional higher bound: the iterator will
+ /// not return any elements of the set greater than the given file
+ /// descriptor.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # use nix::sys::select::FdSet;
+ /// # use std::os::unix::io::RawFd;
+ /// let mut set = FdSet::new();
+ /// set.insert(4);
+ /// set.insert(9);
+ /// let fds: Vec<RawFd> = set.fds(None).collect();
+ /// assert_eq!(fds, vec![4, 9]);
+ /// ```
+ #[inline]
+ pub fn fds(&self, highest: Option<RawFd>) -> Fds {
+ Fds {
+ set: self,
+ range: 0..highest.map(|h| h as usize + 1).unwrap_or(FD_SETSIZE),
+ }
+ }
+}
+
+impl Default for FdSet {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+/// Iterator over `FdSet`.
+#[derive(Debug)]
+pub struct Fds<'a> {
+ set: &'a FdSet,
+ range: Range<usize>,
+}
+
+impl<'a> Iterator for Fds<'a> {
+ type Item = RawFd;
+
+ fn next(&mut self) -> Option<RawFd> {
+ for i in &mut self.range {
+ if self.set.contains(i as RawFd) {
+ return Some(i as RawFd);
+ }
+ }
+ None
+ }
+
+ #[inline]
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ let (_, upper) = self.range.size_hint();
+ (0, upper)
+ }
+}
+
+impl<'a> DoubleEndedIterator for Fds<'a> {
+ #[inline]
+ fn next_back(&mut self) -> Option<RawFd> {
+ while let Some(i) = self.range.next_back() {
+ if self.set.contains(i as RawFd) {
+ return Some(i as RawFd);
+ }
+ }
+ None
+ }
+}
+
+impl<'a> FusedIterator for Fds<'a> {}
+
+/// Monitors file descriptors for readiness
+///
+/// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
+/// file descriptors that are ready for the given operation are set.
+///
+/// When this function returns, `timeout` has an implementation-defined value.
+///
+/// # Parameters
+///
+/// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this
+/// is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1
+/// to the maximum of that.
+/// * `readfds`: File descriptors to check for being ready to read.
+/// * `writefds`: File descriptors to check for being ready to write.
+/// * `errorfds`: File descriptors to check for pending error conditions.
+/// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block
+/// indefinitely).
+///
+/// # References
+///
+/// [select(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/select.html)
+///
+/// [`FdSet::highest`]: struct.FdSet.html#method.highest
+pub fn select<'a, N, R, W, E, T>(
+ nfds: N,
+ readfds: R,
+ writefds: W,
+ errorfds: E,
+ timeout: T,
+) -> Result<c_int>
+where
+ N: Into<Option<c_int>>,
+ R: Into<Option<&'a mut FdSet>>,
+ W: Into<Option<&'a mut FdSet>>,
+ E: Into<Option<&'a mut FdSet>>,
+ T: Into<Option<&'a mut TimeVal>>,
+{
+ let mut readfds = readfds.into();
+ let mut writefds = writefds.into();
+ let mut errorfds = errorfds.into();
+ let timeout = timeout.into();
+
+ let nfds = nfds.into().unwrap_or_else(|| {
+ readfds
+ .iter_mut()
+ .chain(writefds.iter_mut())
+ .chain(errorfds.iter_mut())
+ .map(|set| set.highest().unwrap_or(-1))
+ .max()
+ .unwrap_or(-1)
+ + 1
+ });
+
+ let readfds = readfds
+ .map(|set| set as *mut _ as *mut libc::fd_set)
+ .unwrap_or(null_mut());
+ let writefds = writefds
+ .map(|set| set as *mut _ as *mut libc::fd_set)
+ .unwrap_or(null_mut());
+ let errorfds = errorfds
+ .map(|set| set as *mut _ as *mut libc::fd_set)
+ .unwrap_or(null_mut());
+ let timeout = timeout
+ .map(|tv| tv as *mut _ as *mut libc::timeval)
+ .unwrap_or(null_mut());
+
+ let res =
+ unsafe { libc::select(nfds, readfds, writefds, errorfds, timeout) };
+
+ Errno::result(res)
+}
+
+feature! {
+#![feature = "signal"]
+
+use crate::sys::signal::SigSet;
+
+/// Monitors file descriptors for readiness with an altered signal mask.
+///
+/// Returns the total number of ready file descriptors in all sets. The sets are changed so that all
+/// file descriptors that are ready for the given operation are set.
+///
+/// When this function returns, the original signal mask is restored.
+///
+/// Unlike [`select`](#fn.select), `pselect` does not mutate the `timeout` value.
+///
+/// # Parameters
+///
+/// * `nfds`: The highest file descriptor set in any of the passed `FdSet`s, plus 1. If `None`, this
+/// is calculated automatically by calling [`FdSet::highest`] on all descriptor sets and adding 1
+/// to the maximum of that.
+/// * `readfds`: File descriptors to check for read readiness
+/// * `writefds`: File descriptors to check for write readiness
+/// * `errorfds`: File descriptors to check for pending error conditions.
+/// * `timeout`: Maximum time to wait for descriptors to become ready (`None` to block
+/// indefinitely).
+/// * `sigmask`: Signal mask to activate while waiting for file descriptors to turn
+/// ready (`None` to set no alternative signal mask).
+///
+/// # References
+///
+/// [pselect(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pselect.html)
+///
+/// [The new pselect() system call](https://lwn.net/Articles/176911/)
+///
+/// [`FdSet::highest`]: struct.FdSet.html#method.highest
+pub fn pselect<'a, N, R, W, E, T, S>(nfds: N,
+ readfds: R,
+ writefds: W,
+ errorfds: E,
+ timeout: T,
+ sigmask: S) -> Result<c_int>
+where
+ N: Into<Option<c_int>>,
+ R: Into<Option<&'a mut FdSet>>,
+ W: Into<Option<&'a mut FdSet>>,
+ E: Into<Option<&'a mut FdSet>>,
+ T: Into<Option<&'a TimeSpec>>,
+ S: Into<Option<&'a SigSet>>,
+{
+ let mut readfds = readfds.into();
+ let mut writefds = writefds.into();
+ let mut errorfds = errorfds.into();
+ let sigmask = sigmask.into();
+ let timeout = timeout.into();
+
+ let nfds = nfds.into().unwrap_or_else(|| {
+ readfds.iter_mut()
+ .chain(writefds.iter_mut())
+ .chain(errorfds.iter_mut())
+ .map(|set| set.highest().unwrap_or(-1))
+ .max()
+ .unwrap_or(-1) + 1
+ });
+
+ let readfds = readfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
+ let writefds = writefds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
+ let errorfds = errorfds.map(|set| set as *mut _ as *mut libc::fd_set).unwrap_or(null_mut());
+ let timeout = timeout.map(|ts| ts.as_ref() as *const libc::timespec).unwrap_or(null());
+ let sigmask = sigmask.map(|sm| sm.as_ref() as *const libc::sigset_t).unwrap_or(null());
+
+ let res = unsafe {
+ libc::pselect(nfds, readfds, writefds, errorfds, timeout, sigmask)
+ };
+
+ Errno::result(res)
+}
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::sys::time::{TimeVal, TimeValLike};
+ use crate::unistd::{pipe, write};
+ use std::os::unix::io::RawFd;
+
+ #[test]
+ fn fdset_insert() {
+ let mut fd_set = FdSet::new();
+
+ for i in 0..FD_SETSIZE {
+ assert!(!fd_set.contains(i as RawFd));
+ }
+
+ fd_set.insert(7);
+
+ assert!(fd_set.contains(7));
+ }
+
+ #[test]
+ fn fdset_remove() {
+ let mut fd_set = FdSet::new();
+
+ for i in 0..FD_SETSIZE {
+ assert!(!fd_set.contains(i as RawFd));
+ }
+
+ fd_set.insert(7);
+ fd_set.remove(7);
+
+ for i in 0..FD_SETSIZE {
+ assert!(!fd_set.contains(i as RawFd));
+ }
+ }
+
+ #[test]
+ fn fdset_clear() {
+ let mut fd_set = FdSet::new();
+ fd_set.insert(1);
+ fd_set.insert((FD_SETSIZE / 2) as RawFd);
+ fd_set.insert((FD_SETSIZE - 1) as RawFd);
+
+ fd_set.clear();
+
+ for i in 0..FD_SETSIZE {
+ assert!(!fd_set.contains(i as RawFd));
+ }
+ }
+
+ #[test]
+ fn fdset_highest() {
+ let mut set = FdSet::new();
+ assert_eq!(set.highest(), None);
+ set.insert(0);
+ assert_eq!(set.highest(), Some(0));
+ set.insert(90);
+ assert_eq!(set.highest(), Some(90));
+ set.remove(0);
+ assert_eq!(set.highest(), Some(90));
+ set.remove(90);
+ assert_eq!(set.highest(), None);
+
+ set.insert(4);
+ set.insert(5);
+ set.insert(7);
+ assert_eq!(set.highest(), Some(7));
+ }
+
+ #[test]
+ fn fdset_fds() {
+ let mut set = FdSet::new();
+ assert_eq!(set.fds(None).collect::<Vec<_>>(), vec![]);
+ set.insert(0);
+ assert_eq!(set.fds(None).collect::<Vec<_>>(), vec![0]);
+ set.insert(90);
+ assert_eq!(set.fds(None).collect::<Vec<_>>(), vec![0, 90]);
+
+ // highest limit
+ assert_eq!(set.fds(Some(89)).collect::<Vec<_>>(), vec![0]);
+ assert_eq!(set.fds(Some(90)).collect::<Vec<_>>(), vec![0, 90]);
+ }
+
+ #[test]
+ fn test_select() {
+ let (r1, w1) = pipe().unwrap();
+ write(w1, b"hi!").unwrap();
+ let (r2, _w2) = pipe().unwrap();
+
+ let mut fd_set = FdSet::new();
+ fd_set.insert(r1);
+ fd_set.insert(r2);
+
+ let mut timeout = TimeVal::seconds(10);
+ assert_eq!(
+ 1,
+ select(None, &mut fd_set, None, None, &mut timeout).unwrap()
+ );
+ assert!(fd_set.contains(r1));
+ assert!(!fd_set.contains(r2));
+ }
+
+ #[test]
+ fn test_select_nfds() {
+ let (r1, w1) = pipe().unwrap();
+ write(w1, b"hi!").unwrap();
+ let (r2, _w2) = pipe().unwrap();
+
+ let mut fd_set = FdSet::new();
+ fd_set.insert(r1);
+ fd_set.insert(r2);
+
+ let mut timeout = TimeVal::seconds(10);
+ assert_eq!(
+ 1,
+ select(
+ Some(fd_set.highest().unwrap() + 1),
+ &mut fd_set,
+ None,
+ None,
+ &mut timeout
+ )
+ .unwrap()
+ );
+ assert!(fd_set.contains(r1));
+ assert!(!fd_set.contains(r2));
+ }
+
+ #[test]
+ fn test_select_nfds2() {
+ let (r1, w1) = pipe().unwrap();
+ write(w1, b"hi!").unwrap();
+ let (r2, _w2) = pipe().unwrap();
+
+ let mut fd_set = FdSet::new();
+ fd_set.insert(r1);
+ fd_set.insert(r2);
+
+ let mut timeout = TimeVal::seconds(10);
+ assert_eq!(
+ 1,
+ select(
+ ::std::cmp::max(r1, r2) + 1,
+ &mut fd_set,
+ None,
+ None,
+ &mut timeout
+ )
+ .unwrap()
+ );
+ assert!(fd_set.contains(r1));
+ assert!(!fd_set.contains(r2));
+ }
+}
diff --git a/third_party/rust/nix/src/sys/sendfile.rs b/third_party/rust/nix/src/sys/sendfile.rs
new file mode 100644
index 0000000000..fb293a4e74
--- /dev/null
+++ b/third_party/rust/nix/src/sys/sendfile.rs
@@ -0,0 +1,277 @@
+//! Send data from a file to a socket, bypassing userland.
+
+use cfg_if::cfg_if;
+use std::os::unix::io::RawFd;
+use std::ptr;
+
+use libc::{self, off_t};
+
+use crate::errno::Errno;
+use crate::Result;
+
+/// Copy up to `count` bytes to `out_fd` from `in_fd` starting at `offset`.
+///
+/// Returns a `Result` with the number of bytes written.
+///
+/// If `offset` is `None`, `sendfile` will begin reading at the current offset of `in_fd`and will
+/// update the offset of `in_fd`. If `offset` is `Some`, `sendfile` will begin at the specified
+/// offset and will not update the offset of `in_fd`. Instead, it will mutate `offset` to point to
+/// the byte after the last byte copied.
+///
+/// `in_fd` must support `mmap`-like operations and therefore cannot be a socket.
+///
+/// For more information, see [the sendfile(2) man page.](https://man7.org/linux/man-pages/man2/sendfile.2.html)
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub fn sendfile(
+ out_fd: RawFd,
+ in_fd: RawFd,
+ offset: Option<&mut off_t>,
+ count: usize,
+) -> Result<usize> {
+ let offset = offset
+ .map(|offset| offset as *mut _)
+ .unwrap_or(ptr::null_mut());
+ let ret = unsafe { libc::sendfile(out_fd, in_fd, offset, count) };
+ Errno::result(ret).map(|r| r as usize)
+}
+
+/// Copy up to `count` bytes to `out_fd` from `in_fd` starting at `offset`.
+///
+/// Returns a `Result` with the number of bytes written.
+///
+/// If `offset` is `None`, `sendfile` will begin reading at the current offset of `in_fd`and will
+/// update the offset of `in_fd`. If `offset` is `Some`, `sendfile` will begin at the specified
+/// offset and will not update the offset of `in_fd`. Instead, it will mutate `offset` to point to
+/// the byte after the last byte copied.
+///
+/// `in_fd` must support `mmap`-like operations and therefore cannot be a socket.
+///
+/// For more information, see [the sendfile(2) man page.](https://man7.org/linux/man-pages/man2/sendfile.2.html)
+#[cfg(target_os = "linux")]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub fn sendfile64(
+ out_fd: RawFd,
+ in_fd: RawFd,
+ offset: Option<&mut libc::off64_t>,
+ count: usize,
+) -> Result<usize> {
+ let offset = offset
+ .map(|offset| offset as *mut _)
+ .unwrap_or(ptr::null_mut());
+ let ret = unsafe { libc::sendfile64(out_fd, in_fd, offset, count) };
+ Errno::result(ret).map(|r| r as usize)
+}
+
+cfg_if! {
+ if #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos"))] {
+ use std::io::IoSlice;
+
+ #[derive(Clone, Debug)]
+ struct SendfileHeaderTrailer<'a>(
+ libc::sf_hdtr,
+ Option<Vec<IoSlice<'a>>>,
+ Option<Vec<IoSlice<'a>>>,
+ );
+
+ impl<'a> SendfileHeaderTrailer<'a> {
+ fn new(
+ headers: Option<&'a [&'a [u8]]>,
+ trailers: Option<&'a [&'a [u8]]>
+ ) -> SendfileHeaderTrailer<'a> {
+ let header_iovecs: Option<Vec<IoSlice<'_>>> =
+ headers.map(|s| s.iter().map(|b| IoSlice::new(b)).collect());
+ let trailer_iovecs: Option<Vec<IoSlice<'_>>> =
+ trailers.map(|s| s.iter().map(|b| IoSlice::new(b)).collect());
+ SendfileHeaderTrailer(
+ libc::sf_hdtr {
+ headers: {
+ header_iovecs
+ .as_ref()
+ .map_or(ptr::null(), |v| v.as_ptr()) as *mut libc::iovec
+ },
+ hdr_cnt: header_iovecs.as_ref().map(|v| v.len()).unwrap_or(0) as i32,
+ trailers: {
+ trailer_iovecs
+ .as_ref()
+ .map_or(ptr::null(), |v| v.as_ptr()) as *mut libc::iovec
+ },
+ trl_cnt: trailer_iovecs.as_ref().map(|v| v.len()).unwrap_or(0) as i32
+ },
+ header_iovecs,
+ trailer_iovecs,
+ )
+ }
+ }
+ }
+}
+
+cfg_if! {
+ if #[cfg(target_os = "freebsd")] {
+ use libc::c_int;
+
+ libc_bitflags!{
+ /// Configuration options for [`sendfile`.](fn.sendfile.html)
+ pub struct SfFlags: c_int {
+ /// Causes `sendfile` to return EBUSY instead of blocking when attempting to read a
+ /// busy page.
+ SF_NODISKIO;
+ /// Causes `sendfile` to sleep until the network stack releases its reference to the
+ /// VM pages read. When `sendfile` returns, the data is not guaranteed to have been
+ /// sent, but it is safe to modify the file.
+ SF_SYNC;
+ /// Causes `sendfile` to cache exactly the number of pages specified in the
+ /// `readahead` parameter, disabling caching heuristics.
+ SF_USER_READAHEAD;
+ /// Causes `sendfile` not to cache the data read.
+ SF_NOCACHE;
+ }
+ }
+
+ /// Read up to `count` bytes from `in_fd` starting at `offset` and write to `out_sock`.
+ ///
+ /// Returns a `Result` and a count of bytes written. Bytes written may be non-zero even if
+ /// an error occurs.
+ ///
+ /// `in_fd` must describe a regular file or shared memory object. `out_sock` must describe a
+ /// stream socket.
+ ///
+ /// If `offset` falls past the end of the file, the function returns success and zero bytes
+ /// written.
+ ///
+ /// If `count` is `None` or 0, bytes will be read from `in_fd` until reaching the end of
+ /// file (EOF).
+ ///
+ /// `headers` and `trailers` specify optional slices of byte slices to be sent before and
+ /// after the data read from `in_fd`, respectively. The length of headers and trailers sent
+ /// is included in the returned count of bytes written. The values of `offset` and `count`
+ /// do not apply to headers or trailers.
+ ///
+ /// `readahead` specifies the minimum number of pages to cache in memory ahead of the page
+ /// currently being sent.
+ ///
+ /// For more information, see
+ /// [the sendfile(2) man page.](https://www.freebsd.org/cgi/man.cgi?query=sendfile&sektion=2)
+ #[allow(clippy::too_many_arguments)]
+ pub fn sendfile(
+ in_fd: RawFd,
+ out_sock: RawFd,
+ offset: off_t,
+ count: Option<usize>,
+ headers: Option<&[&[u8]]>,
+ trailers: Option<&[&[u8]]>,
+ flags: SfFlags,
+ readahead: u16
+ ) -> (Result<()>, off_t) {
+ // Readahead goes in upper 16 bits
+ // Flags goes in lower 16 bits
+ // see `man 2 sendfile`
+ let ra32 = u32::from(readahead);
+ let flags: u32 = (ra32 << 16) | (flags.bits() as u32);
+ let mut bytes_sent: off_t = 0;
+ let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers));
+ let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.0 as *const libc::sf_hdtr);
+ let return_code = unsafe {
+ libc::sendfile(in_fd,
+ out_sock,
+ offset,
+ count.unwrap_or(0),
+ hdtr_ptr as *mut libc::sf_hdtr,
+ &mut bytes_sent as *mut off_t,
+ flags as c_int)
+ };
+ (Errno::result(return_code).and(Ok(())), bytes_sent)
+ }
+ } else if #[cfg(target_os = "dragonfly")] {
+ /// Read up to `count` bytes from `in_fd` starting at `offset` and write to `out_sock`.
+ ///
+ /// Returns a `Result` and a count of bytes written. Bytes written may be non-zero even if
+ /// an error occurs.
+ ///
+ /// `in_fd` must describe a regular file. `out_sock` must describe a stream socket.
+ ///
+ /// If `offset` falls past the end of the file, the function returns success and zero bytes
+ /// written.
+ ///
+ /// If `count` is `None` or 0, bytes will be read from `in_fd` until reaching the end of
+ /// file (EOF).
+ ///
+ /// `headers` and `trailers` specify optional slices of byte slices to be sent before and
+ /// after the data read from `in_fd`, respectively. The length of headers and trailers sent
+ /// is included in the returned count of bytes written. The values of `offset` and `count`
+ /// do not apply to headers or trailers.
+ ///
+ /// For more information, see
+ /// [the sendfile(2) man page.](https://leaf.dragonflybsd.org/cgi/web-man?command=sendfile&section=2)
+ pub fn sendfile(
+ in_fd: RawFd,
+ out_sock: RawFd,
+ offset: off_t,
+ count: Option<usize>,
+ headers: Option<&[&[u8]]>,
+ trailers: Option<&[&[u8]]>,
+ ) -> (Result<()>, off_t) {
+ let mut bytes_sent: off_t = 0;
+ let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers));
+ let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.0 as *const libc::sf_hdtr);
+ let return_code = unsafe {
+ libc::sendfile(in_fd,
+ out_sock,
+ offset,
+ count.unwrap_or(0),
+ hdtr_ptr as *mut libc::sf_hdtr,
+ &mut bytes_sent as *mut off_t,
+ 0)
+ };
+ (Errno::result(return_code).and(Ok(())), bytes_sent)
+ }
+ } else if #[cfg(any(target_os = "ios", target_os = "macos"))] {
+ /// Read bytes from `in_fd` starting at `offset` and write up to `count` bytes to
+ /// `out_sock`.
+ ///
+ /// Returns a `Result` and a count of bytes written. Bytes written may be non-zero even if
+ /// an error occurs.
+ ///
+ /// `in_fd` must describe a regular file. `out_sock` must describe a stream socket.
+ ///
+ /// If `offset` falls past the end of the file, the function returns success and zero bytes
+ /// written.
+ ///
+ /// If `count` is `None` or 0, bytes will be read from `in_fd` until reaching the end of
+ /// file (EOF).
+ ///
+ /// `hdtr` specifies an optional list of headers and trailers to be sent before and after
+ /// the data read from `in_fd`, respectively. The length of headers and trailers sent is
+ /// included in the returned count of bytes written. If any headers are specified and
+ /// `count` is non-zero, the length of the headers will be counted in the limit of total
+ /// bytes sent. Trailers do not count toward the limit of bytes sent and will always be sent
+ /// regardless. The value of `offset` does not affect headers or trailers.
+ ///
+ /// For more information, see
+ /// [the sendfile(2) man page.](https://developer.apple.com/legacy/library/documentation/Darwin/Reference/ManPages/man2/sendfile.2.html)
+ pub fn sendfile(
+ in_fd: RawFd,
+ out_sock: RawFd,
+ offset: off_t,
+ count: Option<off_t>,
+ headers: Option<&[&[u8]]>,
+ trailers: Option<&[&[u8]]>
+ ) -> (Result<()>, off_t) {
+ let mut len = count.unwrap_or(0);
+ let hdtr = headers.or(trailers).map(|_| SendfileHeaderTrailer::new(headers, trailers));
+ let hdtr_ptr = hdtr.as_ref().map_or(ptr::null(), |s| &s.0 as *const libc::sf_hdtr);
+ let return_code = unsafe {
+ libc::sendfile(in_fd,
+ out_sock,
+ offset,
+ &mut len as *mut off_t,
+ hdtr_ptr as *mut libc::sf_hdtr,
+ 0)
+ };
+ (Errno::result(return_code).and(Ok(())), len)
+ }
+ }
+}
diff --git a/third_party/rust/nix/src/sys/signal.rs b/third_party/rust/nix/src/sys/signal.rs
new file mode 100644
index 0000000000..d3746e609a
--- /dev/null
+++ b/third_party/rust/nix/src/sys/signal.rs
@@ -0,0 +1,1348 @@
+// Portions of this file are Copyright 2014 The Rust Project Developers.
+// See https://www.rust-lang.org/policies/licenses.
+
+//! Operating system signals.
+
+use crate::errno::Errno;
+use crate::{Error, Result};
+use cfg_if::cfg_if;
+use std::fmt;
+use std::mem;
+#[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+use std::os::unix::io::RawFd;
+use std::ptr;
+use std::str::FromStr;
+
+#[cfg(not(any(target_os = "openbsd", target_os = "redox")))]
+#[cfg(any(feature = "aio", feature = "signal"))]
+pub use self::sigevent::*;
+
+#[cfg(any(feature = "aio", feature = "process", feature = "signal"))]
+libc_enum! {
+ /// Types of operating system signals
+ // Currently there is only one definition of c_int in libc, as well as only one
+ // type for signal constants.
+ // We would prefer to use the libc::c_int alias in the repr attribute. Unfortunately
+ // this is not (yet) possible.
+ #[repr(i32)]
+ #[non_exhaustive]
+ #[cfg_attr(docsrs, doc(cfg(any(feature = "aio", feature = "signal"))))]
+ pub enum Signal {
+ /// Hangup
+ SIGHUP,
+ /// Interrupt
+ SIGINT,
+ /// Quit
+ SIGQUIT,
+ /// Illegal instruction (not reset when caught)
+ SIGILL,
+ /// Trace trap (not reset when caught)
+ SIGTRAP,
+ /// Abort
+ SIGABRT,
+ /// Bus error
+ SIGBUS,
+ /// Floating point exception
+ SIGFPE,
+ /// Kill (cannot be caught or ignored)
+ SIGKILL,
+ /// User defined signal 1
+ SIGUSR1,
+ /// Segmentation violation
+ SIGSEGV,
+ /// User defined signal 2
+ SIGUSR2,
+ /// Write on a pipe with no one to read it
+ SIGPIPE,
+ /// Alarm clock
+ SIGALRM,
+ /// Software termination signal from kill
+ SIGTERM,
+ /// Stack fault (obsolete)
+ #[cfg(all(any(target_os = "android", target_os = "emscripten",
+ target_os = "fuchsia", target_os = "linux"),
+ not(any(target_arch = "mips", target_arch = "mips64",
+ target_arch = "sparc64"))))]
+ SIGSTKFLT,
+ /// To parent on child stop or exit
+ SIGCHLD,
+ /// Continue a stopped process
+ SIGCONT,
+ /// Sendable stop signal not from tty
+ SIGSTOP,
+ /// Stop signal from tty
+ SIGTSTP,
+ /// To readers pgrp upon background tty read
+ SIGTTIN,
+ /// Like TTIN if (tp->t_local&LTOSTOP)
+ SIGTTOU,
+ /// Urgent condition on IO channel
+ SIGURG,
+ /// Exceeded CPU time limit
+ SIGXCPU,
+ /// Exceeded file size limit
+ SIGXFSZ,
+ /// Virtual time alarm
+ SIGVTALRM,
+ /// Profiling time alarm
+ SIGPROF,
+ /// Window size changes
+ SIGWINCH,
+ /// Input/output possible signal
+ #[cfg(not(target_os = "haiku"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ SIGIO,
+ #[cfg(any(target_os = "android", target_os = "emscripten",
+ target_os = "fuchsia", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Power failure imminent.
+ SIGPWR,
+ /// Bad system call
+ SIGSYS,
+ #[cfg(not(any(target_os = "android", target_os = "emscripten",
+ target_os = "fuchsia", target_os = "linux",
+ target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Emulator trap
+ SIGEMT,
+ #[cfg(not(any(target_os = "android", target_os = "emscripten",
+ target_os = "fuchsia", target_os = "linux",
+ target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Information request
+ SIGINFO,
+ }
+ impl TryFrom<i32>
+}
+
+#[cfg(feature = "signal")]
+impl FromStr for Signal {
+ type Err = Error;
+ fn from_str(s: &str) -> Result<Signal> {
+ Ok(match s {
+ "SIGHUP" => Signal::SIGHUP,
+ "SIGINT" => Signal::SIGINT,
+ "SIGQUIT" => Signal::SIGQUIT,
+ "SIGILL" => Signal::SIGILL,
+ "SIGTRAP" => Signal::SIGTRAP,
+ "SIGABRT" => Signal::SIGABRT,
+ "SIGBUS" => Signal::SIGBUS,
+ "SIGFPE" => Signal::SIGFPE,
+ "SIGKILL" => Signal::SIGKILL,
+ "SIGUSR1" => Signal::SIGUSR1,
+ "SIGSEGV" => Signal::SIGSEGV,
+ "SIGUSR2" => Signal::SIGUSR2,
+ "SIGPIPE" => Signal::SIGPIPE,
+ "SIGALRM" => Signal::SIGALRM,
+ "SIGTERM" => Signal::SIGTERM,
+ #[cfg(all(
+ any(
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "linux"
+ ),
+ not(any(
+ target_arch = "mips",
+ target_arch = "mips64",
+ target_arch = "sparc64"
+ ))
+ ))]
+ "SIGSTKFLT" => Signal::SIGSTKFLT,
+ "SIGCHLD" => Signal::SIGCHLD,
+ "SIGCONT" => Signal::SIGCONT,
+ "SIGSTOP" => Signal::SIGSTOP,
+ "SIGTSTP" => Signal::SIGTSTP,
+ "SIGTTIN" => Signal::SIGTTIN,
+ "SIGTTOU" => Signal::SIGTTOU,
+ "SIGURG" => Signal::SIGURG,
+ "SIGXCPU" => Signal::SIGXCPU,
+ "SIGXFSZ" => Signal::SIGXFSZ,
+ "SIGVTALRM" => Signal::SIGVTALRM,
+ "SIGPROF" => Signal::SIGPROF,
+ "SIGWINCH" => Signal::SIGWINCH,
+ #[cfg(not(target_os = "haiku"))]
+ "SIGIO" => Signal::SIGIO,
+ #[cfg(any(
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "linux"
+ ))]
+ "SIGPWR" => Signal::SIGPWR,
+ "SIGSYS" => Signal::SIGSYS,
+ #[cfg(not(any(
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "linux",
+ target_os = "redox",
+ target_os = "haiku"
+ )))]
+ "SIGEMT" => Signal::SIGEMT,
+ #[cfg(not(any(
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "linux",
+ target_os = "redox",
+ target_os = "haiku"
+ )))]
+ "SIGINFO" => Signal::SIGINFO,
+ _ => return Err(Errno::EINVAL),
+ })
+ }
+}
+
+#[cfg(feature = "signal")]
+impl Signal {
+ /// Returns name of signal.
+ ///
+ /// This function is equivalent to `<Signal as AsRef<str>>::as_ref()`,
+ /// with difference that returned string is `'static`
+ /// and not bound to `self`'s lifetime.
+ pub const fn as_str(self) -> &'static str {
+ match self {
+ Signal::SIGHUP => "SIGHUP",
+ Signal::SIGINT => "SIGINT",
+ Signal::SIGQUIT => "SIGQUIT",
+ Signal::SIGILL => "SIGILL",
+ Signal::SIGTRAP => "SIGTRAP",
+ Signal::SIGABRT => "SIGABRT",
+ Signal::SIGBUS => "SIGBUS",
+ Signal::SIGFPE => "SIGFPE",
+ Signal::SIGKILL => "SIGKILL",
+ Signal::SIGUSR1 => "SIGUSR1",
+ Signal::SIGSEGV => "SIGSEGV",
+ Signal::SIGUSR2 => "SIGUSR2",
+ Signal::SIGPIPE => "SIGPIPE",
+ Signal::SIGALRM => "SIGALRM",
+ Signal::SIGTERM => "SIGTERM",
+ #[cfg(all(
+ any(
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "linux"
+ ),
+ not(any(
+ target_arch = "mips",
+ target_arch = "mips64",
+ target_arch = "sparc64"
+ ))
+ ))]
+ Signal::SIGSTKFLT => "SIGSTKFLT",
+ Signal::SIGCHLD => "SIGCHLD",
+ Signal::SIGCONT => "SIGCONT",
+ Signal::SIGSTOP => "SIGSTOP",
+ Signal::SIGTSTP => "SIGTSTP",
+ Signal::SIGTTIN => "SIGTTIN",
+ Signal::SIGTTOU => "SIGTTOU",
+ Signal::SIGURG => "SIGURG",
+ Signal::SIGXCPU => "SIGXCPU",
+ Signal::SIGXFSZ => "SIGXFSZ",
+ Signal::SIGVTALRM => "SIGVTALRM",
+ Signal::SIGPROF => "SIGPROF",
+ Signal::SIGWINCH => "SIGWINCH",
+ #[cfg(not(target_os = "haiku"))]
+ Signal::SIGIO => "SIGIO",
+ #[cfg(any(
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "linux"
+ ))]
+ Signal::SIGPWR => "SIGPWR",
+ Signal::SIGSYS => "SIGSYS",
+ #[cfg(not(any(
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "linux",
+ target_os = "redox",
+ target_os = "haiku"
+ )))]
+ Signal::SIGEMT => "SIGEMT",
+ #[cfg(not(any(
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "linux",
+ target_os = "redox",
+ target_os = "haiku"
+ )))]
+ Signal::SIGINFO => "SIGINFO",
+ }
+ }
+}
+
+#[cfg(feature = "signal")]
+impl AsRef<str> for Signal {
+ fn as_ref(&self) -> &str {
+ self.as_str()
+ }
+}
+
+#[cfg(feature = "signal")]
+impl fmt::Display for Signal {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.write_str(self.as_ref())
+ }
+}
+
+#[cfg(feature = "signal")]
+pub use self::Signal::*;
+
+#[cfg(target_os = "redox")]
+#[cfg(feature = "signal")]
+const SIGNALS: [Signal; 29] = [
+ SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGKILL,
+ SIGUSR1, SIGSEGV, SIGUSR2, SIGPIPE, SIGALRM, SIGTERM, SIGCHLD, SIGCONT,
+ SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGXCPU, SIGXFSZ, SIGVTALRM,
+ SIGPROF, SIGWINCH, SIGIO, SIGSYS,
+];
+#[cfg(target_os = "haiku")]
+#[cfg(feature = "signal")]
+const SIGNALS: [Signal; 28] = [
+ SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGKILL,
+ SIGUSR1, SIGSEGV, SIGUSR2, SIGPIPE, SIGALRM, SIGTERM, SIGCHLD, SIGCONT,
+ SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGXCPU, SIGXFSZ, SIGVTALRM,
+ SIGPROF, SIGWINCH, SIGSYS,
+];
+#[cfg(all(
+ any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia"
+ ),
+ not(any(
+ target_arch = "mips",
+ target_arch = "mips64",
+ target_arch = "sparc64"
+ ))
+))]
+#[cfg(feature = "signal")]
+const SIGNALS: [Signal; 31] = [
+ SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGKILL,
+ SIGUSR1, SIGSEGV, SIGUSR2, SIGPIPE, SIGALRM, SIGTERM, SIGSTKFLT, SIGCHLD,
+ SIGCONT, SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGXCPU, SIGXFSZ,
+ SIGVTALRM, SIGPROF, SIGWINCH, SIGIO, SIGPWR, SIGSYS,
+];
+#[cfg(all(
+ any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia"
+ ),
+ any(target_arch = "mips", target_arch = "mips64", target_arch = "sparc64")
+))]
+#[cfg(feature = "signal")]
+const SIGNALS: [Signal; 30] = [
+ SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGKILL,
+ SIGUSR1, SIGSEGV, SIGUSR2, SIGPIPE, SIGALRM, SIGTERM, SIGCHLD, SIGCONT,
+ SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGXCPU, SIGXFSZ, SIGVTALRM,
+ SIGPROF, SIGWINCH, SIGIO, SIGPWR, SIGSYS,
+];
+#[cfg(not(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "emscripten",
+ target_os = "redox",
+ target_os = "haiku"
+)))]
+#[cfg(feature = "signal")]
+const SIGNALS: [Signal; 31] = [
+ SIGHUP, SIGINT, SIGQUIT, SIGILL, SIGTRAP, SIGABRT, SIGBUS, SIGFPE, SIGKILL,
+ SIGUSR1, SIGSEGV, SIGUSR2, SIGPIPE, SIGALRM, SIGTERM, SIGCHLD, SIGCONT,
+ SIGSTOP, SIGTSTP, SIGTTIN, SIGTTOU, SIGURG, SIGXCPU, SIGXFSZ, SIGVTALRM,
+ SIGPROF, SIGWINCH, SIGIO, SIGSYS, SIGEMT, SIGINFO,
+];
+
+feature! {
+#![feature = "signal"]
+
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+/// Iterate through all signals defined by this operating system
+pub struct SignalIterator {
+ next: usize,
+}
+
+impl Iterator for SignalIterator {
+ type Item = Signal;
+
+ fn next(&mut self) -> Option<Signal> {
+ if self.next < SIGNALS.len() {
+ let next_signal = SIGNALS[self.next];
+ self.next += 1;
+ Some(next_signal)
+ } else {
+ None
+ }
+ }
+}
+
+impl Signal {
+ /// Iterate through all signals defined by this OS
+ pub const fn iterator() -> SignalIterator {
+ SignalIterator{next: 0}
+ }
+}
+
+/// Alias for [`SIGABRT`]
+pub const SIGIOT : Signal = SIGABRT;
+/// Alias for [`SIGIO`]
+#[cfg(not(target_os = "haiku"))]
+pub const SIGPOLL : Signal = SIGIO;
+/// Alias for [`SIGSYS`]
+pub const SIGUNUSED : Signal = SIGSYS;
+
+cfg_if! {
+ if #[cfg(target_os = "redox")] {
+ type SaFlags_t = libc::c_ulong;
+ } else if #[cfg(target_env = "uclibc")] {
+ type SaFlags_t = libc::c_ulong;
+ } else {
+ type SaFlags_t = libc::c_int;
+ }
+}
+}
+
+#[cfg(feature = "signal")]
+libc_bitflags! {
+ /// Controls the behavior of a [`SigAction`]
+ #[cfg_attr(docsrs, doc(cfg(feature = "signal")))]
+ pub struct SaFlags: SaFlags_t {
+ /// When catching a [`Signal::SIGCHLD`] signal, the signal will be
+ /// generated only when a child process exits, not when a child process
+ /// stops.
+ SA_NOCLDSTOP;
+ /// When catching a [`Signal::SIGCHLD`] signal, the system will not
+ /// create zombie processes when children of the calling process exit.
+ SA_NOCLDWAIT;
+ /// Further occurrences of the delivered signal are not masked during
+ /// the execution of the handler.
+ SA_NODEFER;
+ /// The system will deliver the signal to the process on a signal stack,
+ /// specified by each thread with sigaltstack(2).
+ SA_ONSTACK;
+ /// The handler is reset back to the default at the moment the signal is
+ /// delivered.
+ SA_RESETHAND;
+ /// Requests that certain system calls restart if interrupted by this
+ /// signal. See the man page for complete details.
+ SA_RESTART;
+ /// This flag is controlled internally by Nix.
+ SA_SIGINFO;
+ }
+}
+
+#[cfg(feature = "signal")]
+libc_enum! {
+ /// Specifies how certain functions should manipulate a signal mask
+ #[repr(i32)]
+ #[non_exhaustive]
+ #[cfg_attr(docsrs, doc(cfg(feature = "signal")))]
+ pub enum SigmaskHow {
+ /// The new mask is the union of the current mask and the specified set.
+ SIG_BLOCK,
+ /// The new mask is the intersection of the current mask and the
+ /// complement of the specified set.
+ SIG_UNBLOCK,
+ /// The current mask is replaced by the specified set.
+ SIG_SETMASK,
+ }
+}
+
+feature! {
+#![feature = "signal"]
+
+use crate::unistd::Pid;
+use std::iter::Extend;
+use std::iter::FromIterator;
+use std::iter::IntoIterator;
+
+/// Specifies a set of [`Signal`]s that may be blocked, waited for, etc.
+// We are using `transparent` here to be super sure that `SigSet`
+// is represented exactly like the `sigset_t` struct from C.
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct SigSet {
+ sigset: libc::sigset_t
+}
+
+impl SigSet {
+ /// Initialize to include all signals.
+ #[doc(alias("sigfillset"))]
+ pub fn all() -> SigSet {
+ let mut sigset = mem::MaybeUninit::uninit();
+ let _ = unsafe { libc::sigfillset(sigset.as_mut_ptr()) };
+
+ unsafe{ SigSet { sigset: sigset.assume_init() } }
+ }
+
+ /// Initialize to include nothing.
+ #[doc(alias("sigemptyset"))]
+ pub fn empty() -> SigSet {
+ let mut sigset = mem::MaybeUninit::uninit();
+ let _ = unsafe { libc::sigemptyset(sigset.as_mut_ptr()) };
+
+ unsafe{ SigSet { sigset: sigset.assume_init() } }
+ }
+
+ /// Add the specified signal to the set.
+ #[doc(alias("sigaddset"))]
+ pub fn add(&mut self, signal: Signal) {
+ unsafe { libc::sigaddset(&mut self.sigset as *mut libc::sigset_t, signal as libc::c_int) };
+ }
+
+ /// Remove all signals from this set.
+ #[doc(alias("sigemptyset"))]
+ pub fn clear(&mut self) {
+ unsafe { libc::sigemptyset(&mut self.sigset as *mut libc::sigset_t) };
+ }
+
+ /// Remove the specified signal from this set.
+ #[doc(alias("sigdelset"))]
+ pub fn remove(&mut self, signal: Signal) {
+ unsafe { libc::sigdelset(&mut self.sigset as *mut libc::sigset_t, signal as libc::c_int) };
+ }
+
+ /// Return whether this set includes the specified signal.
+ #[doc(alias("sigismember"))]
+ pub fn contains(&self, signal: Signal) -> bool {
+ let res = unsafe { libc::sigismember(&self.sigset as *const libc::sigset_t, signal as libc::c_int) };
+
+ match res {
+ 1 => true,
+ 0 => false,
+ _ => unreachable!("unexpected value from sigismember"),
+ }
+ }
+
+ /// Returns an iterator that yields the signals contained in this set.
+ pub fn iter(&self) -> SigSetIter<'_> {
+ self.into_iter()
+ }
+
+ /// Gets the currently blocked (masked) set of signals for the calling thread.
+ pub fn thread_get_mask() -> Result<SigSet> {
+ let mut oldmask = mem::MaybeUninit::uninit();
+ do_pthread_sigmask(SigmaskHow::SIG_SETMASK, None, Some(oldmask.as_mut_ptr()))?;
+ Ok(unsafe{ SigSet{sigset: oldmask.assume_init()}})
+ }
+
+ /// Sets the set of signals as the signal mask for the calling thread.
+ pub fn thread_set_mask(&self) -> Result<()> {
+ pthread_sigmask(SigmaskHow::SIG_SETMASK, Some(self), None)
+ }
+
+ /// Adds the set of signals to the signal mask for the calling thread.
+ pub fn thread_block(&self) -> Result<()> {
+ pthread_sigmask(SigmaskHow::SIG_BLOCK, Some(self), None)
+ }
+
+ /// Removes the set of signals from the signal mask for the calling thread.
+ pub fn thread_unblock(&self) -> Result<()> {
+ pthread_sigmask(SigmaskHow::SIG_UNBLOCK, Some(self), None)
+ }
+
+ /// Sets the set of signals as the signal mask, and returns the old mask.
+ pub fn thread_swap_mask(&self, how: SigmaskHow) -> Result<SigSet> {
+ let mut oldmask = mem::MaybeUninit::uninit();
+ do_pthread_sigmask(how, Some(self), Some(oldmask.as_mut_ptr()))?;
+ Ok(unsafe{ SigSet{sigset: oldmask.assume_init()}})
+ }
+
+ /// Suspends execution of the calling thread until one of the signals in the
+ /// signal mask becomes pending, and returns the accepted signal.
+ #[cfg(not(target_os = "redox"))] // RedoxFS does not yet support sigwait
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn wait(&self) -> Result<Signal> {
+ use std::convert::TryFrom;
+
+ let mut signum = mem::MaybeUninit::uninit();
+ let res = unsafe { libc::sigwait(&self.sigset as *const libc::sigset_t, signum.as_mut_ptr()) };
+
+ Errno::result(res).map(|_| unsafe {
+ Signal::try_from(signum.assume_init()).unwrap()
+ })
+ }
+
+ /// Converts a `libc::sigset_t` object to a [`SigSet`] without checking whether the
+ /// `libc::sigset_t` is already initialized.
+ ///
+ /// # Safety
+ ///
+ /// The `sigset` passed in must be a valid an initialized `libc::sigset_t` by calling either
+ /// [`sigemptyset(3)`](https://man7.org/linux/man-pages/man3/sigemptyset.3p.html) or
+ /// [`sigfillset(3)`](https://man7.org/linux/man-pages/man3/sigfillset.3p.html).
+ /// Otherwise, the results are undefined.
+ pub unsafe fn from_sigset_t_unchecked(sigset: libc::sigset_t) -> SigSet {
+ SigSet { sigset }
+ }
+}
+
+impl AsRef<libc::sigset_t> for SigSet {
+ fn as_ref(&self) -> &libc::sigset_t {
+ &self.sigset
+ }
+}
+
+// TODO: Consider specialization for the case where T is &SigSet and libc::sigorset is available.
+impl Extend<Signal> for SigSet {
+ fn extend<T>(&mut self, iter: T)
+ where T: IntoIterator<Item = Signal> {
+ for signal in iter {
+ self.add(signal);
+ }
+ }
+}
+
+impl FromIterator<Signal> for SigSet {
+ fn from_iter<T>(iter: T) -> Self
+ where T: IntoIterator<Item = Signal> {
+ let mut sigset = SigSet::empty();
+ sigset.extend(iter);
+ sigset
+ }
+}
+
+/// Iterator for a [`SigSet`].
+///
+/// Call [`SigSet::iter`] to create an iterator.
+#[derive(Clone, Debug)]
+pub struct SigSetIter<'a> {
+ sigset: &'a SigSet,
+ inner: SignalIterator,
+}
+
+impl Iterator for SigSetIter<'_> {
+ type Item = Signal;
+ fn next(&mut self) -> Option<Signal> {
+ loop {
+ match self.inner.next() {
+ None => return None,
+ Some(signal) if self.sigset.contains(signal) => return Some(signal),
+ Some(_signal) => continue,
+ }
+ }
+ }
+}
+
+impl<'a> IntoIterator for &'a SigSet {
+ type Item = Signal;
+ type IntoIter = SigSetIter<'a>;
+ fn into_iter(self) -> Self::IntoIter {
+ SigSetIter { sigset: self, inner: Signal::iterator() }
+ }
+}
+
+/// A signal handler.
+#[allow(unknown_lints)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub enum SigHandler {
+ /// Default signal handling.
+ SigDfl,
+ /// Request that the signal be ignored.
+ SigIgn,
+ /// Use the given signal-catching function, which takes in the signal.
+ Handler(extern fn(libc::c_int)),
+ /// Use the given signal-catching function, which takes in the signal, information about how
+ /// the signal was generated, and a pointer to the threads `ucontext_t`.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ SigAction(extern fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void))
+}
+
+/// Action to take on receipt of a signal. Corresponds to `sigaction`.
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct SigAction {
+ sigaction: libc::sigaction
+}
+
+impl SigAction {
+ /// Creates a new action.
+ ///
+ /// The `SA_SIGINFO` bit in the `flags` argument is ignored (it will be set only if `handler`
+ /// is the `SigAction` variant). `mask` specifies other signals to block during execution of
+ /// the signal-catching function.
+ pub fn new(handler: SigHandler, flags: SaFlags, mask: SigSet) -> SigAction {
+ unsafe fn install_sig(p: *mut libc::sigaction, handler: SigHandler) {
+ (*p).sa_sigaction = match handler {
+ SigHandler::SigDfl => libc::SIG_DFL,
+ SigHandler::SigIgn => libc::SIG_IGN,
+ SigHandler::Handler(f) => f as *const extern fn(libc::c_int) as usize,
+ #[cfg(not(target_os = "redox"))]
+ SigHandler::SigAction(f) => f as *const extern fn(libc::c_int, *mut libc::siginfo_t, *mut libc::c_void) as usize,
+ };
+ }
+
+ let mut s = mem::MaybeUninit::<libc::sigaction>::uninit();
+ unsafe {
+ let p = s.as_mut_ptr();
+ install_sig(p, handler);
+ (*p).sa_flags = match handler {
+ #[cfg(not(target_os = "redox"))]
+ SigHandler::SigAction(_) => (flags | SaFlags::SA_SIGINFO).bits(),
+ _ => (flags - SaFlags::SA_SIGINFO).bits(),
+ };
+ (*p).sa_mask = mask.sigset;
+
+ SigAction { sigaction: s.assume_init() }
+ }
+ }
+
+ /// Returns the flags set on the action.
+ pub fn flags(&self) -> SaFlags {
+ SaFlags::from_bits_truncate(self.sigaction.sa_flags)
+ }
+
+ /// Returns the set of signals that are blocked during execution of the action's
+ /// signal-catching function.
+ pub fn mask(&self) -> SigSet {
+ SigSet { sigset: self.sigaction.sa_mask }
+ }
+
+ /// Returns the action's handler.
+ pub fn handler(&self) -> SigHandler {
+ match self.sigaction.sa_sigaction {
+ libc::SIG_DFL => SigHandler::SigDfl,
+ libc::SIG_IGN => SigHandler::SigIgn,
+ #[cfg(not(target_os = "redox"))]
+ p if self.flags().contains(SaFlags::SA_SIGINFO) =>
+ SigHandler::SigAction(
+ // Safe for one of two reasons:
+ // * The SigHandler was created by SigHandler::new, in which
+ // case the pointer is correct, or
+ // * The SigHandler was created by signal or sigaction, which
+ // are unsafe functions, so the caller should've somehow
+ // ensured that it is correctly initialized.
+ unsafe{
+ *(&p as *const usize
+ as *const extern fn(_, _, _))
+ }
+ as extern fn(_, _, _)),
+ p => SigHandler::Handler(
+ // Safe for one of two reasons:
+ // * The SigHandler was created by SigHandler::new, in which
+ // case the pointer is correct, or
+ // * The SigHandler was created by signal or sigaction, which
+ // are unsafe functions, so the caller should've somehow
+ // ensured that it is correctly initialized.
+ unsafe{
+ *(&p as *const usize
+ as *const extern fn(libc::c_int))
+ }
+ as extern fn(libc::c_int)),
+ }
+ }
+}
+
+/// Changes the action taken by a process on receipt of a specific signal.
+///
+/// `signal` can be any signal except `SIGKILL` or `SIGSTOP`. On success, it returns the previous
+/// action for the given signal. If `sigaction` fails, no new signal handler is installed.
+///
+/// # Safety
+///
+/// * Signal handlers may be called at any point during execution, which limits
+/// what is safe to do in the body of the signal-catching function. Be certain
+/// to only make syscalls that are explicitly marked safe for signal handlers
+/// and only share global data using atomics.
+///
+/// * There is also no guarantee that the old signal handler was installed
+/// correctly. If it was installed by this crate, it will be. But if it was
+/// installed by, for example, C code, then there is no guarantee its function
+/// pointer is valid. In that case, this function effectively dereferences a
+/// raw pointer of unknown provenance.
+pub unsafe fn sigaction(signal: Signal, sigaction: &SigAction) -> Result<SigAction> {
+ let mut oldact = mem::MaybeUninit::<libc::sigaction>::uninit();
+
+ let res = libc::sigaction(signal as libc::c_int,
+ &sigaction.sigaction as *const libc::sigaction,
+ oldact.as_mut_ptr());
+
+ Errno::result(res).map(|_| SigAction { sigaction: oldact.assume_init() })
+}
+
+/// Signal management (see [signal(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/signal.html))
+///
+/// Installs `handler` for the given `signal`, returning the previous signal
+/// handler. `signal` should only be used following another call to `signal` or
+/// if the current handler is the default. The return value of `signal` is
+/// undefined after setting the handler with [`sigaction`][SigActionFn].
+///
+/// # Safety
+///
+/// If the pointer to the previous signal handler is invalid, undefined
+/// behavior could be invoked when casting it back to a [`SigAction`][SigActionStruct].
+///
+/// # Examples
+///
+/// Ignore `SIGINT`:
+///
+/// ```no_run
+/// # use nix::sys::signal::{self, Signal, SigHandler};
+/// unsafe { signal::signal(Signal::SIGINT, SigHandler::SigIgn) }.unwrap();
+/// ```
+///
+/// Use a signal handler to set a flag variable:
+///
+/// ```no_run
+/// # #[macro_use] extern crate lazy_static;
+/// # use std::convert::TryFrom;
+/// # use std::sync::atomic::{AtomicBool, Ordering};
+/// # use nix::sys::signal::{self, Signal, SigHandler};
+/// lazy_static! {
+/// static ref SIGNALED: AtomicBool = AtomicBool::new(false);
+/// }
+///
+/// extern fn handle_sigint(signal: libc::c_int) {
+/// let signal = Signal::try_from(signal).unwrap();
+/// SIGNALED.store(signal == Signal::SIGINT, Ordering::Relaxed);
+/// }
+///
+/// fn main() {
+/// let handler = SigHandler::Handler(handle_sigint);
+/// unsafe { signal::signal(Signal::SIGINT, handler) }.unwrap();
+/// }
+/// ```
+///
+/// # Errors
+///
+/// Returns [`Error(Errno::EOPNOTSUPP)`] if `handler` is
+/// [`SigAction`][SigActionStruct]. Use [`sigaction`][SigActionFn] instead.
+///
+/// `signal` also returns any error from `libc::signal`, such as when an attempt
+/// is made to catch a signal that cannot be caught or to ignore a signal that
+/// cannot be ignored.
+///
+/// [`Error::UnsupportedOperation`]: ../../enum.Error.html#variant.UnsupportedOperation
+/// [SigActionStruct]: struct.SigAction.html
+/// [sigactionFn]: fn.sigaction.html
+pub unsafe fn signal(signal: Signal, handler: SigHandler) -> Result<SigHandler> {
+ let signal = signal as libc::c_int;
+ let res = match handler {
+ SigHandler::SigDfl => libc::signal(signal, libc::SIG_DFL),
+ SigHandler::SigIgn => libc::signal(signal, libc::SIG_IGN),
+ SigHandler::Handler(handler) => libc::signal(signal, handler as libc::sighandler_t),
+ #[cfg(not(target_os = "redox"))]
+ SigHandler::SigAction(_) => return Err(Errno::ENOTSUP),
+ };
+ Errno::result(res).map(|oldhandler| {
+ match oldhandler {
+ libc::SIG_DFL => SigHandler::SigDfl,
+ libc::SIG_IGN => SigHandler::SigIgn,
+ p => SigHandler::Handler(
+ *(&p as *const usize
+ as *const extern fn(libc::c_int))
+ as extern fn(libc::c_int)),
+ }
+ })
+}
+
+fn do_pthread_sigmask(how: SigmaskHow,
+ set: Option<&SigSet>,
+ oldset: Option<*mut libc::sigset_t>) -> Result<()> {
+ if set.is_none() && oldset.is_none() {
+ return Ok(())
+ }
+
+ let res = unsafe {
+ // if set or oldset is None, pass in null pointers instead
+ libc::pthread_sigmask(how as libc::c_int,
+ set.map_or_else(ptr::null::<libc::sigset_t>,
+ |s| &s.sigset as *const libc::sigset_t),
+ oldset.unwrap_or(ptr::null_mut())
+ )
+ };
+
+ Errno::result(res).map(drop)
+}
+
+/// Manages the signal mask (set of blocked signals) for the calling thread.
+///
+/// If the `set` parameter is `Some(..)`, then the signal mask will be updated with the signal set.
+/// The `how` flag decides the type of update. If `set` is `None`, `how` will be ignored,
+/// and no modification will take place.
+///
+/// If the 'oldset' parameter is `Some(..)` then the current signal mask will be written into it.
+///
+/// If both `set` and `oldset` is `Some(..)`, the current signal mask will be written into oldset,
+/// and then it will be updated with `set`.
+///
+/// If both `set` and `oldset` is None, this function is a no-op.
+///
+/// For more information, visit the [`pthread_sigmask`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_sigmask.html),
+/// or [`sigprocmask`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigprocmask.html) man pages.
+pub fn pthread_sigmask(how: SigmaskHow,
+ set: Option<&SigSet>,
+ oldset: Option<&mut SigSet>) -> Result<()>
+{
+ do_pthread_sigmask(how, set, oldset.map(|os| &mut os.sigset as *mut _ ))
+}
+
+/// Examine and change blocked signals.
+///
+/// For more information see the [`sigprocmask` man
+/// pages](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sigprocmask.html).
+pub fn sigprocmask(how: SigmaskHow, set: Option<&SigSet>, oldset: Option<&mut SigSet>) -> Result<()> {
+ if set.is_none() && oldset.is_none() {
+ return Ok(())
+ }
+
+ let res = unsafe {
+ // if set or oldset is None, pass in null pointers instead
+ libc::sigprocmask(how as libc::c_int,
+ set.map_or_else(ptr::null::<libc::sigset_t>,
+ |s| &s.sigset as *const libc::sigset_t),
+ oldset.map_or_else(ptr::null_mut::<libc::sigset_t>,
+ |os| &mut os.sigset as *mut libc::sigset_t))
+ };
+
+ Errno::result(res).map(drop)
+}
+
+/// Send a signal to a process
+///
+/// # Arguments
+///
+/// * `pid` - Specifies which processes should receive the signal.
+/// - If positive, specifies an individual process.
+/// - If zero, the signal will be sent to all processes whose group
+/// ID is equal to the process group ID of the sender. This is a
+#[cfg_attr(target_os = "fuchsia", doc = "variant of `killpg`.")]
+#[cfg_attr(not(target_os = "fuchsia"), doc = "variant of [`killpg`].")]
+/// - If `-1` and the process has super-user privileges, the signal
+/// is sent to all processes exclusing system processes.
+/// - If less than `-1`, the signal is sent to all processes whose
+/// process group ID is equal to the absolute value of `pid`.
+/// * `signal` - Signal to send. If `None`, error checking is performed
+/// but no signal is actually sent.
+///
+/// See Also
+/// [`kill(2)`](https://pubs.opengroup.org/onlinepubs/9699919799/functions/kill.html)
+pub fn kill<T: Into<Option<Signal>>>(pid: Pid, signal: T) -> Result<()> {
+ let res = unsafe { libc::kill(pid.into(),
+ match signal.into() {
+ Some(s) => s as libc::c_int,
+ None => 0,
+ }) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Send a signal to a process group
+///
+/// # Arguments
+///
+/// * `pgrp` - Process group to signal. If less then or equal 1, the behavior
+/// is platform-specific.
+/// * `signal` - Signal to send. If `None`, `killpg` will only preform error
+/// checking and won't send any signal.
+///
+/// See Also [killpg(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/killpg.html).
+#[cfg(not(target_os = "fuchsia"))]
+pub fn killpg<T: Into<Option<Signal>>>(pgrp: Pid, signal: T) -> Result<()> {
+ let res = unsafe { libc::killpg(pgrp.into(),
+ match signal.into() {
+ Some(s) => s as libc::c_int,
+ None => 0,
+ }) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Send a signal to the current thread
+///
+/// See Also [raise(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/raise.html)
+pub fn raise(signal: Signal) -> Result<()> {
+ let res = unsafe { libc::raise(signal as libc::c_int) };
+
+ Errno::result(res).map(drop)
+}
+}
+
+feature! {
+#![any(feature = "aio", feature = "signal")]
+
+/// Identifies a thread for [`SigevNotify::SigevThreadId`]
+#[cfg(target_os = "freebsd")]
+pub type type_of_thread_id = libc::lwpid_t;
+/// Identifies a thread for [`SigevNotify::SigevThreadId`]
+#[cfg(target_os = "linux")]
+pub type type_of_thread_id = libc::pid_t;
+
+/// Specifies the notification method used by a [`SigEvent`]
+// sigval is actually a union of a int and a void*. But it's never really used
+// as a pointer, because neither libc nor the kernel ever dereference it. nix
+// therefore presents it as an intptr_t, which is how kevent uses it.
+#[cfg(not(any(target_os = "openbsd", target_os = "redox")))]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub enum SigevNotify {
+ /// No notification will be delivered
+ SigevNone,
+ /// Notify by delivering a signal to the process.
+ SigevSignal {
+ /// Signal to deliver
+ signal: Signal,
+ /// Will be present in the `si_value` field of the [`libc::siginfo_t`]
+ /// structure of the queued signal.
+ si_value: libc::intptr_t
+ },
+ // Note: SIGEV_THREAD is not implemented because libc::sigevent does not
+ // expose a way to set the union members needed by SIGEV_THREAD.
+ /// Notify by delivering an event to a kqueue.
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ SigevKevent {
+ /// File descriptor of the kqueue to notify.
+ kq: RawFd,
+ /// Will be contained in the kevent's `udata` field.
+ udata: libc::intptr_t
+ },
+ /// Notify by delivering a signal to a thread.
+ #[cfg(any(target_os = "freebsd", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ SigevThreadId {
+ /// Signal to send
+ signal: Signal,
+ /// LWP ID of the thread to notify
+ thread_id: type_of_thread_id,
+ /// Will be present in the `si_value` field of the [`libc::siginfo_t`]
+ /// structure of the queued signal.
+ si_value: libc::intptr_t
+ },
+}
+}
+
+#[cfg(not(any(target_os = "openbsd", target_os = "redox")))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+mod sigevent {
+ feature! {
+ #![any(feature = "aio", feature = "signal")]
+
+ use std::mem;
+ use std::ptr;
+ use super::SigevNotify;
+ #[cfg(any(target_os = "freebsd", target_os = "linux"))]
+ use super::type_of_thread_id;
+
+ /// Used to request asynchronous notification of the completion of certain
+ /// events, such as POSIX AIO and timers.
+ #[repr(C)]
+ #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+ pub struct SigEvent {
+ sigevent: libc::sigevent
+ }
+
+ impl SigEvent {
+ /// **Note:** this constructor does not allow the user to set the
+ /// `sigev_notify_kevent_flags` field. That's considered ok because on FreeBSD
+ /// at least those flags don't do anything useful. That field is part of a
+ /// union that shares space with the more genuinely useful fields.
+ ///
+ /// **Note:** This constructor also doesn't allow the caller to set the
+ /// `sigev_notify_function` or `sigev_notify_attributes` fields, which are
+ /// required for `SIGEV_THREAD`. That's considered ok because on no operating
+ /// system is `SIGEV_THREAD` the most efficient way to deliver AIO
+ /// notification. FreeBSD and DragonFly BSD programs should prefer `SIGEV_KEVENT`.
+ /// Linux, Solaris, and portable programs should prefer `SIGEV_THREAD_ID` or
+ /// `SIGEV_SIGNAL`. That field is part of a union that shares space with the
+ /// more genuinely useful `sigev_notify_thread_id`
+ // Allow invalid_value warning on Fuchsia only.
+ // See https://github.com/nix-rust/nix/issues/1441
+ #[cfg_attr(target_os = "fuchsia", allow(invalid_value))]
+ pub fn new(sigev_notify: SigevNotify) -> SigEvent {
+ let mut sev = unsafe { mem::MaybeUninit::<libc::sigevent>::zeroed().assume_init() };
+ sev.sigev_notify = match sigev_notify {
+ SigevNotify::SigevNone => libc::SIGEV_NONE,
+ SigevNotify::SigevSignal{..} => libc::SIGEV_SIGNAL,
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ SigevNotify::SigevKevent{..} => libc::SIGEV_KEVENT,
+ #[cfg(target_os = "freebsd")]
+ SigevNotify::SigevThreadId{..} => libc::SIGEV_THREAD_ID,
+ #[cfg(all(target_os = "linux", target_env = "gnu", not(target_arch = "mips")))]
+ SigevNotify::SigevThreadId{..} => libc::SIGEV_THREAD_ID,
+ #[cfg(all(target_os = "linux", target_env = "uclibc"))]
+ SigevNotify::SigevThreadId{..} => libc::SIGEV_THREAD_ID,
+ #[cfg(any(all(target_os = "linux", target_env = "musl"), target_arch = "mips"))]
+ SigevNotify::SigevThreadId{..} => 4 // No SIGEV_THREAD_ID defined
+ };
+ sev.sigev_signo = match sigev_notify {
+ SigevNotify::SigevSignal{ signal, .. } => signal as libc::c_int,
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ SigevNotify::SigevKevent{ kq, ..} => kq,
+ #[cfg(any(target_os = "linux", target_os = "freebsd"))]
+ SigevNotify::SigevThreadId{ signal, .. } => signal as libc::c_int,
+ _ => 0
+ };
+ sev.sigev_value.sival_ptr = match sigev_notify {
+ SigevNotify::SigevNone => ptr::null_mut::<libc::c_void>(),
+ SigevNotify::SigevSignal{ si_value, .. } => si_value as *mut libc::c_void,
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ SigevNotify::SigevKevent{ udata, .. } => udata as *mut libc::c_void,
+ #[cfg(any(target_os = "freebsd", target_os = "linux"))]
+ SigevNotify::SigevThreadId{ si_value, .. } => si_value as *mut libc::c_void,
+ };
+ SigEvent::set_tid(&mut sev, &sigev_notify);
+ SigEvent{sigevent: sev}
+ }
+
+ #[cfg(any(target_os = "freebsd", target_os = "linux"))]
+ fn set_tid(sev: &mut libc::sigevent, sigev_notify: &SigevNotify) {
+ sev.sigev_notify_thread_id = match *sigev_notify {
+ SigevNotify::SigevThreadId { thread_id, .. } => thread_id,
+ _ => 0 as type_of_thread_id
+ };
+ }
+
+ #[cfg(not(any(target_os = "freebsd", target_os = "linux")))]
+ fn set_tid(_sev: &mut libc::sigevent, _sigev_notify: &SigevNotify) {
+ }
+
+ /// Return a copy of the inner structure
+ pub fn sigevent(&self) -> libc::sigevent {
+ self.sigevent
+ }
+
+ /// Returns a mutable pointer to the `sigevent` wrapped by `self`
+ pub fn as_mut_ptr(&mut self) -> *mut libc::sigevent {
+ &mut self.sigevent
+ }
+ }
+
+ impl<'a> From<&'a libc::sigevent> for SigEvent {
+ fn from(sigevent: &libc::sigevent) -> Self {
+ SigEvent{ sigevent: *sigevent }
+ }
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ #[cfg(not(target_os = "redox"))]
+ use std::thread;
+
+ #[test]
+ fn test_contains() {
+ let mut mask = SigSet::empty();
+ mask.add(SIGUSR1);
+
+ assert!(mask.contains(SIGUSR1));
+ assert!(!mask.contains(SIGUSR2));
+
+ let all = SigSet::all();
+ assert!(all.contains(SIGUSR1));
+ assert!(all.contains(SIGUSR2));
+ }
+
+ #[test]
+ fn test_clear() {
+ let mut set = SigSet::all();
+ set.clear();
+ for signal in Signal::iterator() {
+ assert!(!set.contains(signal));
+ }
+ }
+
+ #[test]
+ fn test_from_str_round_trips() {
+ for signal in Signal::iterator() {
+ assert_eq!(signal.as_ref().parse::<Signal>().unwrap(), signal);
+ assert_eq!(signal.to_string().parse::<Signal>().unwrap(), signal);
+ }
+ }
+
+ #[test]
+ fn test_from_str_invalid_value() {
+ let errval = Err(Errno::EINVAL);
+ assert_eq!("NOSIGNAL".parse::<Signal>(), errval);
+ assert_eq!("kill".parse::<Signal>(), errval);
+ assert_eq!("9".parse::<Signal>(), errval);
+ }
+
+ #[test]
+ fn test_extend() {
+ let mut one_signal = SigSet::empty();
+ one_signal.add(SIGUSR1);
+
+ let mut two_signals = SigSet::empty();
+ two_signals.add(SIGUSR2);
+ two_signals.extend(&one_signal);
+
+ assert!(two_signals.contains(SIGUSR1));
+ assert!(two_signals.contains(SIGUSR2));
+ }
+
+ #[test]
+ #[cfg(not(target_os = "redox"))]
+ fn test_thread_signal_set_mask() {
+ thread::spawn(|| {
+ let prev_mask = SigSet::thread_get_mask()
+ .expect("Failed to get existing signal mask!");
+
+ let mut test_mask = prev_mask;
+ test_mask.add(SIGUSR1);
+
+ test_mask.thread_set_mask().expect("assertion failed");
+ let new_mask =
+ SigSet::thread_get_mask().expect("Failed to get new mask!");
+
+ assert!(new_mask.contains(SIGUSR1));
+ assert!(!new_mask.contains(SIGUSR2));
+
+ prev_mask
+ .thread_set_mask()
+ .expect("Failed to revert signal mask!");
+ })
+ .join()
+ .unwrap();
+ }
+
+ #[test]
+ #[cfg(not(target_os = "redox"))]
+ fn test_thread_signal_block() {
+ thread::spawn(|| {
+ let mut mask = SigSet::empty();
+ mask.add(SIGUSR1);
+
+ mask.thread_block().expect("assertion failed");
+
+ assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR1));
+ })
+ .join()
+ .unwrap();
+ }
+
+ #[test]
+ #[cfg(not(target_os = "redox"))]
+ fn test_thread_signal_unblock() {
+ thread::spawn(|| {
+ let mut mask = SigSet::empty();
+ mask.add(SIGUSR1);
+
+ mask.thread_unblock().expect("assertion failed");
+
+ assert!(!SigSet::thread_get_mask().unwrap().contains(SIGUSR1));
+ })
+ .join()
+ .unwrap();
+ }
+
+ #[test]
+ #[cfg(not(target_os = "redox"))]
+ fn test_thread_signal_swap() {
+ thread::spawn(|| {
+ let mut mask = SigSet::empty();
+ mask.add(SIGUSR1);
+ mask.thread_block().unwrap();
+
+ assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR1));
+
+ let mut mask2 = SigSet::empty();
+ mask2.add(SIGUSR2);
+
+ let oldmask =
+ mask2.thread_swap_mask(SigmaskHow::SIG_SETMASK).unwrap();
+
+ assert!(oldmask.contains(SIGUSR1));
+ assert!(!oldmask.contains(SIGUSR2));
+
+ assert!(SigSet::thread_get_mask().unwrap().contains(SIGUSR2));
+ })
+ .join()
+ .unwrap();
+ }
+
+ #[test]
+ fn test_from_and_into_iterator() {
+ let sigset = SigSet::from_iter(vec![Signal::SIGUSR1, Signal::SIGUSR2]);
+ let signals = sigset.into_iter().collect::<Vec<Signal>>();
+ assert_eq!(signals, [Signal::SIGUSR1, Signal::SIGUSR2]);
+ }
+
+ #[test]
+ #[cfg(not(target_os = "redox"))]
+ fn test_sigaction() {
+ thread::spawn(|| {
+ extern "C" fn test_sigaction_handler(_: libc::c_int) {}
+ extern "C" fn test_sigaction_action(
+ _: libc::c_int,
+ _: *mut libc::siginfo_t,
+ _: *mut libc::c_void,
+ ) {
+ }
+
+ let handler_sig = SigHandler::Handler(test_sigaction_handler);
+
+ let flags =
+ SaFlags::SA_ONSTACK | SaFlags::SA_RESTART | SaFlags::SA_SIGINFO;
+
+ let mut mask = SigSet::empty();
+ mask.add(SIGUSR1);
+
+ let action_sig = SigAction::new(handler_sig, flags, mask);
+
+ assert_eq!(
+ action_sig.flags(),
+ SaFlags::SA_ONSTACK | SaFlags::SA_RESTART
+ );
+ assert_eq!(action_sig.handler(), handler_sig);
+
+ mask = action_sig.mask();
+ assert!(mask.contains(SIGUSR1));
+ assert!(!mask.contains(SIGUSR2));
+
+ let handler_act = SigHandler::SigAction(test_sigaction_action);
+ let action_act = SigAction::new(handler_act, flags, mask);
+ assert_eq!(action_act.handler(), handler_act);
+
+ let action_dfl = SigAction::new(SigHandler::SigDfl, flags, mask);
+ assert_eq!(action_dfl.handler(), SigHandler::SigDfl);
+
+ let action_ign = SigAction::new(SigHandler::SigIgn, flags, mask);
+ assert_eq!(action_ign.handler(), SigHandler::SigIgn);
+ })
+ .join()
+ .unwrap();
+ }
+
+ #[test]
+ #[cfg(not(target_os = "redox"))]
+ fn test_sigwait() {
+ thread::spawn(|| {
+ let mut mask = SigSet::empty();
+ mask.add(SIGUSR1);
+ mask.add(SIGUSR2);
+ mask.thread_block().unwrap();
+
+ raise(SIGUSR1).unwrap();
+ assert_eq!(mask.wait().unwrap(), SIGUSR1);
+ })
+ .join()
+ .unwrap();
+ }
+
+ #[test]
+ fn test_from_sigset_t_unchecked() {
+ let src_set = SigSet::empty();
+ let set = unsafe { SigSet::from_sigset_t_unchecked(src_set.sigset) };
+
+ for signal in Signal::iterator() {
+ assert!(!set.contains(signal));
+ }
+
+ let src_set = SigSet::all();
+ let set = unsafe { SigSet::from_sigset_t_unchecked(src_set.sigset) };
+
+ for signal in Signal::iterator() {
+ assert!(set.contains(signal));
+ }
+ }
+}
diff --git a/third_party/rust/nix/src/sys/signalfd.rs b/third_party/rust/nix/src/sys/signalfd.rs
new file mode 100644
index 0000000000..095e590850
--- /dev/null
+++ b/third_party/rust/nix/src/sys/signalfd.rs
@@ -0,0 +1,175 @@
+//! Interface for the `signalfd` syscall.
+//!
+//! # Signal discarding
+//! When a signal can't be delivered to a process (or thread), it will become a pending signal.
+//! Failure to deliver could happen if the signal is blocked by every thread in the process or if
+//! the signal handler is still handling a previous signal.
+//!
+//! If a signal is sent to a process (or thread) that already has a pending signal of the same
+//! type, it will be discarded. This means that if signals of the same type are received faster than
+//! they are processed, some of those signals will be dropped. Because of this limitation,
+//! `signalfd` in itself cannot be used for reliable communication between processes or threads.
+//!
+//! Once the signal is unblocked, or the signal handler is finished, and a signal is still pending
+//! (ie. not consumed from a signalfd) it will be delivered to the signal handler.
+//!
+//! Please note that signal discarding is not specific to `signalfd`, but also happens with regular
+//! signal handlers.
+use crate::errno::Errno;
+pub use crate::sys::signal::{self, SigSet};
+use crate::unistd;
+use crate::Result;
+pub use libc::signalfd_siginfo as siginfo;
+
+use std::mem;
+use std::os::unix::io::{AsRawFd, RawFd};
+
+libc_bitflags! {
+ pub struct SfdFlags: libc::c_int {
+ SFD_NONBLOCK;
+ SFD_CLOEXEC;
+ }
+}
+
+pub const SIGNALFD_NEW: RawFd = -1;
+#[deprecated(since = "0.23.0", note = "use mem::size_of::<siginfo>() instead")]
+pub const SIGNALFD_SIGINFO_SIZE: usize = mem::size_of::<siginfo>();
+
+/// Creates a new file descriptor for reading signals.
+///
+/// **Important:** please read the module level documentation about signal discarding before using
+/// this function!
+///
+/// The `mask` parameter specifies the set of signals that can be accepted via this file descriptor.
+///
+/// A signal must be blocked on every thread in a process, otherwise it won't be visible from
+/// signalfd (the default handler will be invoked instead).
+///
+/// See [the signalfd man page for more information](https://man7.org/linux/man-pages/man2/signalfd.2.html)
+pub fn signalfd(fd: RawFd, mask: &SigSet, flags: SfdFlags) -> Result<RawFd> {
+ unsafe {
+ Errno::result(libc::signalfd(
+ fd as libc::c_int,
+ mask.as_ref(),
+ flags.bits(),
+ ))
+ }
+}
+
+/// A helper struct for creating, reading and closing a `signalfd` instance.
+///
+/// **Important:** please read the module level documentation about signal discarding before using
+/// this struct!
+///
+/// # Examples
+///
+/// ```
+/// # use nix::sys::signalfd::*;
+/// // Set the thread to block the SIGUSR1 signal, otherwise the default handler will be used
+/// let mut mask = SigSet::empty();
+/// mask.add(signal::SIGUSR1);
+/// mask.thread_block().unwrap();
+///
+/// // Signals are queued up on the file descriptor
+/// let mut sfd = SignalFd::with_flags(&mask, SfdFlags::SFD_NONBLOCK).unwrap();
+///
+/// match sfd.read_signal() {
+/// // we caught a signal
+/// Ok(Some(sig)) => (),
+/// // there were no signals waiting (only happens when the SFD_NONBLOCK flag is set,
+/// // otherwise the read_signal call blocks)
+/// Ok(None) => (),
+/// Err(err) => (), // some error happend
+/// }
+/// ```
+#[derive(Debug, Eq, Hash, PartialEq)]
+pub struct SignalFd(RawFd);
+
+impl SignalFd {
+ pub fn new(mask: &SigSet) -> Result<SignalFd> {
+ Self::with_flags(mask, SfdFlags::empty())
+ }
+
+ pub fn with_flags(mask: &SigSet, flags: SfdFlags) -> Result<SignalFd> {
+ let fd = signalfd(SIGNALFD_NEW, mask, flags)?;
+
+ Ok(SignalFd(fd))
+ }
+
+ pub fn set_mask(&mut self, mask: &SigSet) -> Result<()> {
+ signalfd(self.0, mask, SfdFlags::empty()).map(drop)
+ }
+
+ pub fn read_signal(&mut self) -> Result<Option<siginfo>> {
+ let mut buffer = mem::MaybeUninit::<siginfo>::uninit();
+
+ let size = mem::size_of_val(&buffer);
+ let res = Errno::result(unsafe {
+ libc::read(self.0, buffer.as_mut_ptr() as *mut libc::c_void, size)
+ })
+ .map(|r| r as usize);
+ match res {
+ Ok(x) if x == size => Ok(Some(unsafe { buffer.assume_init() })),
+ Ok(_) => unreachable!("partial read on signalfd"),
+ Err(Errno::EAGAIN) => Ok(None),
+ Err(error) => Err(error),
+ }
+ }
+}
+
+impl Drop for SignalFd {
+ fn drop(&mut self) {
+ let e = unistd::close(self.0);
+ if !std::thread::panicking() && e == Err(Errno::EBADF) {
+ panic!("Closing an invalid file descriptor!");
+ };
+ }
+}
+
+impl AsRawFd for SignalFd {
+ fn as_raw_fd(&self) -> RawFd {
+ self.0
+ }
+}
+
+impl Iterator for SignalFd {
+ type Item = siginfo;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ match self.read_signal() {
+ Ok(Some(sig)) => Some(sig),
+ Ok(None) | Err(_) => None,
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ #[test]
+ fn create_signalfd() {
+ let mask = SigSet::empty();
+ SignalFd::new(&mask).unwrap();
+ }
+
+ #[test]
+ fn create_signalfd_with_opts() {
+ let mask = SigSet::empty();
+ SignalFd::with_flags(
+ &mask,
+ SfdFlags::SFD_CLOEXEC | SfdFlags::SFD_NONBLOCK,
+ )
+ .unwrap();
+ }
+
+ #[test]
+ fn read_empty_signalfd() {
+ let mask = SigSet::empty();
+ let mut fd =
+ SignalFd::with_flags(&mask, SfdFlags::SFD_NONBLOCK).unwrap();
+
+ let res = fd.read_signal();
+ assert!(res.unwrap().is_none());
+ }
+}
diff --git a/third_party/rust/nix/src/sys/socket/addr.rs b/third_party/rust/nix/src/sys/socket/addr.rs
new file mode 100644
index 0000000000..4e565a5b68
--- /dev/null
+++ b/third_party/rust/nix/src/sys/socket/addr.rs
@@ -0,0 +1,3247 @@
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "haiku",
+ target_os = "fuchsia"
+))]
+#[cfg(feature = "net")]
+pub use self::datalink::LinkAddr;
+#[cfg(any(target_os = "android", target_os = "linux"))]
+pub use self::vsock::VsockAddr;
+use super::sa_family_t;
+use crate::errno::Errno;
+#[cfg(any(target_os = "android", target_os = "linux"))]
+use crate::sys::socket::addr::alg::AlgAddr;
+#[cfg(any(target_os = "android", target_os = "linux"))]
+use crate::sys::socket::addr::netlink::NetlinkAddr;
+#[cfg(all(
+ feature = "ioctl",
+ any(target_os = "ios", target_os = "macos")
+))]
+use crate::sys::socket::addr::sys_control::SysControlAddr;
+use crate::{NixPath, Result};
+use cfg_if::cfg_if;
+use memoffset::offset_of;
+use std::convert::TryInto;
+use std::ffi::OsStr;
+use std::hash::{Hash, Hasher};
+use std::os::unix::ffi::OsStrExt;
+#[cfg(any(target_os = "ios", target_os = "macos"))]
+use std::os::unix::io::RawFd;
+use std::path::Path;
+use std::{fmt, mem, net, ptr, slice};
+
+/// Convert a std::net::Ipv4Addr into the libc form.
+#[cfg(feature = "net")]
+pub(crate) const fn ipv4addr_to_libc(addr: net::Ipv4Addr) -> libc::in_addr {
+ static_assertions::assert_eq_size!(net::Ipv4Addr, libc::in_addr);
+ // Safe because both types have the same memory layout, and no fancy Drop
+ // impls.
+ unsafe {
+ mem::transmute(addr)
+ }
+}
+
+/// Convert a std::net::Ipv6Addr into the libc form.
+#[cfg(feature = "net")]
+pub(crate) const fn ipv6addr_to_libc(addr: &net::Ipv6Addr) -> libc::in6_addr {
+ static_assertions::assert_eq_size!(net::Ipv6Addr, libc::in6_addr);
+ // Safe because both are Newtype wrappers around the same libc type
+ unsafe {
+ mem::transmute(*addr)
+ }
+}
+
+/// These constants specify the protocol family to be used
+/// in [`socket`](fn.socket.html) and [`socketpair`](fn.socketpair.html)
+///
+/// # References
+///
+/// [address_families(7)](https://man7.org/linux/man-pages/man7/address_families.7.html)
+// Should this be u8?
+#[repr(i32)]
+#[non_exhaustive]
+#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
+pub enum AddressFamily {
+ /// Local communication (see [`unix(7)`](https://man7.org/linux/man-pages/man7/unix.7.html))
+ Unix = libc::AF_UNIX,
+ /// IPv4 Internet protocols (see [`ip(7)`](https://man7.org/linux/man-pages/man7/ip.7.html))
+ Inet = libc::AF_INET,
+ /// IPv6 Internet protocols (see [`ipv6(7)`](https://man7.org/linux/man-pages/man7/ipv6.7.html))
+ Inet6 = libc::AF_INET6,
+ /// Kernel user interface device (see [`netlink(7)`](https://man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Netlink = libc::AF_NETLINK,
+ /// Low level packet interface (see [`packet(7)`](https://man7.org/linux/man-pages/man7/packet.7.html))
+ #[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "illumos",
+ target_os = "fuchsia",
+ target_os = "solaris"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Packet = libc::AF_PACKET,
+ /// KEXT Controls and Notifications
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ System = libc::AF_SYSTEM,
+ /// Amateur radio AX.25 protocol
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Ax25 = libc::AF_AX25,
+ /// IPX - Novell protocols
+ Ipx = libc::AF_IPX,
+ /// AppleTalk
+ AppleTalk = libc::AF_APPLETALK,
+ /// AX.25 packet layer protocol.
+ /// (see [netrom(4)](https://www.unix.com/man-page/linux/4/netrom/))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetRom = libc::AF_NETROM,
+ /// Can't be used for creating sockets; mostly used for bridge
+ /// links in
+ /// [rtnetlink(7)](https://man7.org/linux/man-pages/man7/rtnetlink.7.html)
+ /// protocol commands.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Bridge = libc::AF_BRIDGE,
+ /// Access to raw ATM PVCs
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ AtmPvc = libc::AF_ATMPVC,
+ /// ITU-T X.25 / ISO-8208 protocol (see [`x25(7)`](https://man7.org/linux/man-pages/man7/x25.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ X25 = libc::AF_X25,
+ /// RATS (Radio Amateur Telecommunications Society) Open
+ /// Systems environment (ROSE) AX.25 packet layer protocol.
+ /// (see [netrom(4)](https://www.unix.com/man-page/linux/4/netrom/))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Rose = libc::AF_ROSE,
+ /// DECet protocol sockets.
+ #[cfg(not(target_os = "haiku"))]
+ Decnet = libc::AF_DECnet,
+ /// Reserved for "802.2LLC project"; never used.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetBeui = libc::AF_NETBEUI,
+ /// This was a short-lived (between Linux 2.1.30 and
+ /// 2.1.99pre2) protocol family for firewall upcalls.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Security = libc::AF_SECURITY,
+ /// Key management protocol.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Key = libc::AF_KEY,
+ #[allow(missing_docs)] // Not documented anywhere that I can find
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Ash = libc::AF_ASH,
+ /// Acorn Econet protocol
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Econet = libc::AF_ECONET,
+ /// Access to ATM Switched Virtual Circuits
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ AtmSvc = libc::AF_ATMSVC,
+ /// Reliable Datagram Sockets (RDS) protocol
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Rds = libc::AF_RDS,
+ /// IBM SNA
+ #[cfg(not(target_os = "haiku"))]
+ Sna = libc::AF_SNA,
+ /// Socket interface over IrDA
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Irda = libc::AF_IRDA,
+ /// Generic PPP transport layer, for setting up L2 tunnels (L2TP and PPPoE)
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Pppox = libc::AF_PPPOX,
+ /// Legacy protocol for wide area network (WAN) connectivity that was used
+ /// by Sangoma WAN cards
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Wanpipe = libc::AF_WANPIPE,
+ /// Logical link control (IEEE 802.2 LLC) protocol
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Llc = libc::AF_LLC,
+ /// InfiniBand native addressing
+ #[cfg(all(target_os = "linux", not(target_env = "uclibc")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Ib = libc::AF_IB,
+ /// Multiprotocol Label Switching
+ #[cfg(all(target_os = "linux", not(target_env = "uclibc")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Mpls = libc::AF_MPLS,
+ /// Controller Area Network automotive bus protocol
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Can = libc::AF_CAN,
+ /// TIPC, "cluster domain sockets" protocol
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Tipc = libc::AF_TIPC,
+ /// Bluetooth low-level socket protocol
+ #[cfg(not(any(
+ target_os = "illumos",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "solaris"
+ )))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Bluetooth = libc::AF_BLUETOOTH,
+ /// IUCV (inter-user communication vehicle) z/VM protocol for
+ /// hypervisor-guest interaction
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Iucv = libc::AF_IUCV,
+ /// Rx, Andrew File System remote procedure call protocol
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ RxRpc = libc::AF_RXRPC,
+ /// New "modular ISDN" driver interface protocol
+ #[cfg(not(any(
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "haiku"
+ )))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Isdn = libc::AF_ISDN,
+ /// Nokia cellular modem IPC/RPC interface
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Phonet = libc::AF_PHONET,
+ /// IEEE 802.15.4 WPAN (wireless personal area network) raw packet protocol
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Ieee802154 = libc::AF_IEEE802154,
+ /// Ericsson's Communication CPU to Application CPU interface (CAIF)
+ /// protocol.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Caif = libc::AF_CAIF,
+ /// Interface to kernel crypto API
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Alg = libc::AF_ALG,
+ /// Near field communication
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Nfc = libc::AF_NFC,
+ /// VMWare VSockets protocol for hypervisor-guest interaction.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Vsock = libc::AF_VSOCK,
+ /// ARPANet IMP addresses
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ImpLink = libc::AF_IMPLINK,
+ /// PUP protocols, e.g. BSP
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Pup = libc::AF_PUP,
+ /// MIT CHAOS protocols
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Chaos = libc::AF_CHAOS,
+ /// Novell and Xerox protocol
+ #[cfg(any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Ns = libc::AF_NS,
+ #[allow(missing_docs)] // Not documented anywhere that I can find
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Iso = libc::AF_ISO,
+ /// Bell Labs virtual circuit switch ?
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Datakit = libc::AF_DATAKIT,
+ /// CCITT protocols, X.25 etc
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Ccitt = libc::AF_CCITT,
+ /// DEC Direct data link interface
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Dli = libc::AF_DLI,
+ #[allow(missing_docs)] // Not documented anywhere that I can find
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Lat = libc::AF_LAT,
+ /// NSC Hyperchannel
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Hylink = libc::AF_HYLINK,
+ /// Link layer interface
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Link = libc::AF_LINK,
+ /// connection-oriented IP, aka ST II
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Coip = libc::AF_COIP,
+ /// Computer Network Technology
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Cnt = libc::AF_CNT,
+ /// Native ATM access
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Natm = libc::AF_NATM,
+ /// Unspecified address family, (see [`getaddrinfo(3)`](https://man7.org/linux/man-pages/man3/getaddrinfo.3.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Unspec = libc::AF_UNSPEC,
+}
+
+impl AddressFamily {
+ /// Create a new `AddressFamily` from an integer value retrieved from `libc`, usually from
+ /// the `sa_family` field of a `sockaddr`.
+ ///
+ /// Currently only supports these address families: Unix, Inet (v4 & v6), Netlink, Link/Packet
+ /// and System. Returns None for unsupported or unknown address families.
+ pub const fn from_i32(family: i32) -> Option<AddressFamily> {
+ match family {
+ libc::AF_UNIX => Some(AddressFamily::Unix),
+ libc::AF_INET => Some(AddressFamily::Inet),
+ libc::AF_INET6 => Some(AddressFamily::Inet6),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ libc::AF_NETLINK => Some(AddressFamily::Netlink),
+ #[cfg(any(target_os = "macos", target_os = "macos"))]
+ libc::AF_SYSTEM => Some(AddressFamily::System),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ libc::AF_PACKET => Some(AddressFamily::Packet),
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "illumos",
+ target_os = "openbsd"
+ ))]
+ libc::AF_LINK => Some(AddressFamily::Link),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ libc::AF_VSOCK => Some(AddressFamily::Vsock),
+ _ => None,
+ }
+ }
+}
+
+feature! {
+#![feature = "net"]
+
+#[deprecated(
+ since = "0.24.0",
+ note = "use SockaddrIn, SockaddrIn6, or SockaddrStorage instead"
+)]
+#[allow(missing_docs)] // Since they're all deprecated anyway
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub enum InetAddr {
+ V4(libc::sockaddr_in),
+ V6(libc::sockaddr_in6),
+}
+
+#[allow(missing_docs)] // It's deprecated anyway
+#[allow(deprecated)]
+impl InetAddr {
+ #[allow(clippy::needless_update)] // It isn't needless on all OSes
+ pub fn from_std(std: &net::SocketAddr) -> InetAddr {
+ match *std {
+ net::SocketAddr::V4(ref addr) => {
+ InetAddr::V4(libc::sockaddr_in {
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd",
+ target_os = "haiku", target_os = "hermit",
+ target_os = "ios", target_os = "macos",
+ target_os = "netbsd", target_os = "openbsd"))]
+ sin_len: mem::size_of::<libc::sockaddr_in>() as u8,
+ sin_family: AddressFamily::Inet as sa_family_t,
+ sin_port: addr.port().to_be(), // network byte order
+ sin_addr: Ipv4Addr::from_std(addr.ip()).0,
+ .. unsafe { mem::zeroed() }
+ })
+ }
+ net::SocketAddr::V6(ref addr) => {
+ InetAddr::V6(libc::sockaddr_in6 {
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd",
+ target_os = "haiku", target_os = "hermit",
+ target_os = "ios", target_os = "macos",
+ target_os = "netbsd", target_os = "openbsd"))]
+ sin6_len: mem::size_of::<libc::sockaddr_in6>() as u8,
+ sin6_family: AddressFamily::Inet6 as sa_family_t,
+ sin6_port: addr.port().to_be(), // network byte order
+ sin6_addr: Ipv6Addr::from_std(addr.ip()).0,
+ sin6_flowinfo: addr.flowinfo(), // host byte order
+ sin6_scope_id: addr.scope_id(), // host byte order
+ .. unsafe { mem::zeroed() }
+ })
+ }
+ }
+ }
+
+ #[allow(clippy::needless_update)] // It isn't needless on all OSes
+ pub fn new(ip: IpAddr, port: u16) -> InetAddr {
+ match ip {
+ IpAddr::V4(ref ip) => {
+ InetAddr::V4(libc::sockaddr_in {
+ sin_family: AddressFamily::Inet as sa_family_t,
+ sin_port: port.to_be(),
+ sin_addr: ip.0,
+ .. unsafe { mem::zeroed() }
+ })
+ }
+ IpAddr::V6(ref ip) => {
+ InetAddr::V6(libc::sockaddr_in6 {
+ sin6_family: AddressFamily::Inet6 as sa_family_t,
+ sin6_port: port.to_be(),
+ sin6_addr: ip.0,
+ .. unsafe { mem::zeroed() }
+ })
+ }
+ }
+ }
+ /// Gets the IP address associated with this socket address.
+ pub const fn ip(&self) -> IpAddr {
+ match *self {
+ InetAddr::V4(ref sa) => IpAddr::V4(Ipv4Addr(sa.sin_addr)),
+ InetAddr::V6(ref sa) => IpAddr::V6(Ipv6Addr(sa.sin6_addr)),
+ }
+ }
+
+ /// Gets the port number associated with this socket address
+ pub const fn port(&self) -> u16 {
+ match *self {
+ InetAddr::V6(ref sa) => u16::from_be(sa.sin6_port),
+ InetAddr::V4(ref sa) => u16::from_be(sa.sin_port),
+ }
+ }
+
+ pub fn to_std(&self) -> net::SocketAddr {
+ match *self {
+ InetAddr::V4(ref sa) => net::SocketAddr::V4(
+ net::SocketAddrV4::new(
+ Ipv4Addr(sa.sin_addr).to_std(),
+ self.port())),
+ InetAddr::V6(ref sa) => net::SocketAddr::V6(
+ net::SocketAddrV6::new(
+ Ipv6Addr(sa.sin6_addr).to_std(),
+ self.port(),
+ sa.sin6_flowinfo,
+ sa.sin6_scope_id)),
+ }
+ }
+
+ #[deprecated(since = "0.23.0", note = "use .to_string() instead")]
+ pub fn to_str(&self) -> String {
+ format!("{}", self)
+ }
+}
+
+#[allow(deprecated)]
+impl fmt::Display for InetAddr {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ InetAddr::V4(_) => write!(f, "{}:{}", self.ip(), self.port()),
+ InetAddr::V6(_) => write!(f, "[{}]:{}", self.ip(), self.port()),
+ }
+ }
+}
+
+/*
+ *
+ * ===== IpAddr =====
+ *
+ */
+#[allow(missing_docs)] // Since they're all deprecated anyway
+#[allow(deprecated)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[deprecated(
+ since = "0.24.0",
+ note = "Use std::net::IpAddr instead"
+)]
+pub enum IpAddr {
+ V4(Ipv4Addr),
+ V6(Ipv6Addr),
+}
+
+#[allow(deprecated)]
+#[allow(missing_docs)] // Since they're all deprecated anyway
+impl IpAddr {
+ /// Create a new IpAddr that contains an IPv4 address.
+ ///
+ /// The result will represent the IP address a.b.c.d
+ pub const fn new_v4(a: u8, b: u8, c: u8, d: u8) -> IpAddr {
+ IpAddr::V4(Ipv4Addr::new(a, b, c, d))
+ }
+
+ /// Create a new IpAddr that contains an IPv6 address.
+ ///
+ /// The result will represent the IP address a:b:c:d:e:f
+ #[allow(clippy::many_single_char_names)]
+ #[allow(clippy::too_many_arguments)]
+ pub const fn new_v6(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> IpAddr {
+ IpAddr::V6(Ipv6Addr::new(a, b, c, d, e, f, g, h))
+ }
+
+ pub fn from_std(std: &net::IpAddr) -> IpAddr {
+ match *std {
+ net::IpAddr::V4(ref std) => IpAddr::V4(Ipv4Addr::from_std(std)),
+ net::IpAddr::V6(ref std) => IpAddr::V6(Ipv6Addr::from_std(std)),
+ }
+ }
+
+ pub const fn to_std(&self) -> net::IpAddr {
+ match *self {
+ IpAddr::V4(ref ip) => net::IpAddr::V4(ip.to_std()),
+ IpAddr::V6(ref ip) => net::IpAddr::V6(ip.to_std()),
+ }
+ }
+}
+
+#[allow(deprecated)]
+impl fmt::Display for IpAddr {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ IpAddr::V4(ref v4) => v4.fmt(f),
+ IpAddr::V6(ref v6) => v6.fmt(f)
+ }
+ }
+}
+
+/*
+ *
+ * ===== Ipv4Addr =====
+ *
+ */
+
+#[deprecated(
+ since = "0.24.0",
+ note = "Use std::net::Ipv4Addr instead"
+)]
+#[allow(missing_docs)] // Since they're all deprecated anyway
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[repr(transparent)]
+pub struct Ipv4Addr(pub libc::in_addr);
+
+#[allow(deprecated)]
+#[allow(missing_docs)] // Since they're all deprecated anyway
+impl Ipv4Addr {
+ #[allow(clippy::identity_op)] // More readable this way
+ pub const fn new(a: u8, b: u8, c: u8, d: u8) -> Ipv4Addr {
+ let ip = (((a as u32) << 24) |
+ ((b as u32) << 16) |
+ ((c as u32) << 8) |
+ ((d as u32) << 0)).to_be();
+
+ Ipv4Addr(libc::in_addr { s_addr: ip })
+ }
+
+ // Use pass by reference for symmetry with Ipv6Addr::from_std
+ #[allow(clippy::trivially_copy_pass_by_ref)]
+ pub fn from_std(std: &net::Ipv4Addr) -> Ipv4Addr {
+ let bits = std.octets();
+ Ipv4Addr::new(bits[0], bits[1], bits[2], bits[3])
+ }
+
+ pub const fn any() -> Ipv4Addr {
+ Ipv4Addr(libc::in_addr { s_addr: libc::INADDR_ANY })
+ }
+
+ pub const fn octets(self) -> [u8; 4] {
+ let bits = u32::from_be(self.0.s_addr);
+ [(bits >> 24) as u8, (bits >> 16) as u8, (bits >> 8) as u8, bits as u8]
+ }
+
+ pub const fn to_std(self) -> net::Ipv4Addr {
+ let bits = self.octets();
+ net::Ipv4Addr::new(bits[0], bits[1], bits[2], bits[3])
+ }
+}
+
+#[allow(deprecated)]
+impl fmt::Display for Ipv4Addr {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ let octets = self.octets();
+ write!(fmt, "{}.{}.{}.{}", octets[0], octets[1], octets[2], octets[3])
+ }
+}
+
+/*
+ *
+ * ===== Ipv6Addr =====
+ *
+ */
+
+#[deprecated(
+ since = "0.24.0",
+ note = "Use std::net::Ipv6Addr instead"
+)]
+#[allow(missing_docs)] // Since they're all deprecated anyway
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[repr(transparent)]
+pub struct Ipv6Addr(pub libc::in6_addr);
+
+// Note that IPv6 addresses are stored in big endian order on all architectures.
+// See https://tools.ietf.org/html/rfc1700 or consult your favorite search
+// engine.
+
+macro_rules! to_u8_array {
+ ($($num:ident),*) => {
+ [ $(($num>>8) as u8, ($num&0xff) as u8,)* ]
+ }
+}
+
+macro_rules! to_u16_array {
+ ($slf:ident, $($first:expr, $second:expr),*) => {
+ [$( (($slf.0.s6_addr[$first] as u16) << 8) + $slf.0.s6_addr[$second] as u16,)*]
+ }
+}
+
+#[allow(deprecated)]
+#[allow(missing_docs)] // Since they're all deprecated anyway
+impl Ipv6Addr {
+ #[allow(clippy::many_single_char_names)]
+ #[allow(clippy::too_many_arguments)]
+ pub const fn new(a: u16, b: u16, c: u16, d: u16, e: u16, f: u16, g: u16, h: u16) -> Ipv6Addr {
+ Ipv6Addr(libc::in6_addr{s6_addr: to_u8_array!(a,b,c,d,e,f,g,h)})
+ }
+
+ pub fn from_std(std: &net::Ipv6Addr) -> Ipv6Addr {
+ let s = std.segments();
+ Ipv6Addr::new(s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7])
+ }
+
+ /// Return the eight 16-bit segments that make up this address
+ pub const fn segments(&self) -> [u16; 8] {
+ to_u16_array!(self, 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15)
+ }
+
+ pub const fn to_std(&self) -> net::Ipv6Addr {
+ let s = self.segments();
+ net::Ipv6Addr::new(s[0], s[1], s[2], s[3], s[4], s[5], s[6], s[7])
+ }
+}
+
+#[allow(deprecated)]
+impl fmt::Display for Ipv6Addr {
+ fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
+ self.to_std().fmt(fmt)
+ }
+}
+}
+
+/// A wrapper around `sockaddr_un`.
+#[derive(Clone, Copy, Debug)]
+#[repr(C)]
+pub struct UnixAddr {
+ // INVARIANT: sun & sun_len are valid as defined by docs for from_raw_parts
+ sun: libc::sockaddr_un,
+ /// The length of the valid part of `sun`, including the sun_family field
+ /// but excluding any trailing nul.
+ // On the BSDs, this field is built into sun
+ #[cfg(any(
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux"
+ ))]
+ sun_len: u8,
+}
+
+// linux man page unix(7) says there are 3 kinds of unix socket:
+// pathname: addrlen = offsetof(struct sockaddr_un, sun_path) + strlen(sun_path) + 1
+// unnamed: addrlen = sizeof(sa_family_t)
+// abstract: addren > sizeof(sa_family_t), name = sun_path[..(addrlen - sizeof(sa_family_t))]
+//
+// what we call path_len = addrlen - offsetof(struct sockaddr_un, sun_path)
+#[derive(PartialEq, Eq, Hash)]
+enum UnixAddrKind<'a> {
+ Pathname(&'a Path),
+ Unnamed,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ Abstract(&'a [u8]),
+}
+impl<'a> UnixAddrKind<'a> {
+ /// Safety: sun & sun_len must be valid
+ unsafe fn get(sun: &'a libc::sockaddr_un, sun_len: u8) -> Self {
+ assert!(sun_len as usize >= offset_of!(libc::sockaddr_un, sun_path));
+ let path_len =
+ sun_len as usize - offset_of!(libc::sockaddr_un, sun_path);
+ if path_len == 0 {
+ return Self::Unnamed;
+ }
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ if sun.sun_path[0] == 0 {
+ let name = slice::from_raw_parts(
+ sun.sun_path.as_ptr().add(1) as *const u8,
+ path_len - 1,
+ );
+ return Self::Abstract(name);
+ }
+ let pathname =
+ slice::from_raw_parts(sun.sun_path.as_ptr() as *const u8, path_len);
+ if pathname.last() == Some(&0) {
+ // A trailing NUL is not considered part of the path, and it does
+ // not need to be included in the addrlen passed to functions like
+ // bind(). However, Linux adds a trailing NUL, even if one was not
+ // originally present, when returning addrs from functions like
+ // getsockname() (the BSDs do not do that). So we need to filter
+ // out any trailing NUL here, so sockaddrs can round-trip through
+ // the kernel and still compare equal.
+ Self::Pathname(Path::new(OsStr::from_bytes(
+ &pathname[0..pathname.len() - 1],
+ )))
+ } else {
+ Self::Pathname(Path::new(OsStr::from_bytes(pathname)))
+ }
+ }
+}
+
+impl UnixAddr {
+ /// Create a new sockaddr_un representing a filesystem path.
+ pub fn new<P: ?Sized + NixPath>(path: &P) -> Result<UnixAddr> {
+ path.with_nix_path(|cstr| unsafe {
+ let mut ret = libc::sockaddr_un {
+ sun_family: AddressFamily::Unix as sa_family_t,
+ ..mem::zeroed()
+ };
+
+ let bytes = cstr.to_bytes();
+
+ if bytes.len() >= ret.sun_path.len() {
+ return Err(Errno::ENAMETOOLONG);
+ }
+
+ let sun_len = (bytes.len()
+ + offset_of!(libc::sockaddr_un, sun_path))
+ .try_into()
+ .unwrap();
+
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ {
+ ret.sun_len = sun_len;
+ }
+ ptr::copy_nonoverlapping(
+ bytes.as_ptr(),
+ ret.sun_path.as_mut_ptr() as *mut u8,
+ bytes.len(),
+ );
+
+ Ok(UnixAddr::from_raw_parts(ret, sun_len))
+ })?
+ }
+
+ /// Create a new `sockaddr_un` representing an address in the "abstract namespace".
+ ///
+ /// The leading nul byte for the abstract namespace is automatically added;
+ /// thus the input `path` is expected to be the bare name, not NUL-prefixed.
+ /// This is a Linux-specific extension, primarily used to allow chrooted
+ /// processes to communicate with processes having a different filesystem view.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn new_abstract(path: &[u8]) -> Result<UnixAddr> {
+ unsafe {
+ let mut ret = libc::sockaddr_un {
+ sun_family: AddressFamily::Unix as sa_family_t,
+ ..mem::zeroed()
+ };
+
+ if path.len() >= ret.sun_path.len() {
+ return Err(Errno::ENAMETOOLONG);
+ }
+ let sun_len =
+ (path.len() + 1 + offset_of!(libc::sockaddr_un, sun_path))
+ .try_into()
+ .unwrap();
+
+ // Abstract addresses are represented by sun_path[0] ==
+ // b'\0', so copy starting one byte in.
+ ptr::copy_nonoverlapping(
+ path.as_ptr(),
+ ret.sun_path.as_mut_ptr().offset(1) as *mut u8,
+ path.len(),
+ );
+
+ Ok(UnixAddr::from_raw_parts(ret, sun_len))
+ }
+ }
+
+ /// Create a new `sockaddr_un` representing an "unnamed" unix socket address.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn new_unnamed() -> UnixAddr {
+ let ret = libc::sockaddr_un {
+ sun_family: AddressFamily::Unix as sa_family_t,
+ .. unsafe { mem::zeroed() }
+ };
+
+ let sun_len: u8 = offset_of!(libc::sockaddr_un, sun_path).try_into().unwrap();
+
+ unsafe { UnixAddr::from_raw_parts(ret, sun_len) }
+ }
+
+ /// Create a UnixAddr from a raw `sockaddr_un` struct and a size. `sun_len`
+ /// is the size of the valid portion of the struct, excluding any trailing
+ /// NUL.
+ ///
+ /// # Safety
+ /// This pair of sockaddr_un & sun_len must be a valid unix addr, which
+ /// means:
+ /// - sun_len >= offset_of(sockaddr_un, sun_path)
+ /// - sun_len <= sockaddr_un.sun_path.len() - offset_of(sockaddr_un, sun_path)
+ /// - if this is a unix addr with a pathname, sun.sun_path is a
+ /// fs path, not necessarily nul-terminated.
+ pub(crate) unsafe fn from_raw_parts(
+ sun: libc::sockaddr_un,
+ sun_len: u8,
+ ) -> UnixAddr {
+ cfg_if! {
+ if #[cfg(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux"
+ ))]
+ {
+ UnixAddr { sun, sun_len }
+ } else {
+ assert_eq!(sun_len, sun.sun_len);
+ UnixAddr {sun}
+ }
+ }
+ }
+
+ fn kind(&self) -> UnixAddrKind<'_> {
+ // SAFETY: our sockaddr is always valid because of the invariant on the struct
+ unsafe { UnixAddrKind::get(&self.sun, self.sun_len()) }
+ }
+
+ /// If this address represents a filesystem path, return that path.
+ pub fn path(&self) -> Option<&Path> {
+ match self.kind() {
+ UnixAddrKind::Pathname(path) => Some(path),
+ _ => None,
+ }
+ }
+
+ /// If this address represents an abstract socket, return its name.
+ ///
+ /// For abstract sockets only the bare name is returned, without the
+ /// leading NUL byte. `None` is returned for unnamed or path-backed sockets.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn as_abstract(&self) -> Option<&[u8]> {
+ match self.kind() {
+ UnixAddrKind::Abstract(name) => Some(name),
+ _ => None,
+ }
+ }
+
+ /// Check if this address is an "unnamed" unix socket address.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ #[inline]
+ pub fn is_unnamed(&self) -> bool {
+ matches!(self.kind(), UnixAddrKind::Unnamed)
+ }
+
+ /// Returns the addrlen of this socket - `offsetof(struct sockaddr_un, sun_path)`
+ #[inline]
+ pub fn path_len(&self) -> usize {
+ self.sun_len() as usize - offset_of!(libc::sockaddr_un, sun_path)
+ }
+ /// Returns a pointer to the raw `sockaddr_un` struct
+ #[inline]
+ pub fn as_ptr(&self) -> *const libc::sockaddr_un {
+ &self.sun
+ }
+ /// Returns a mutable pointer to the raw `sockaddr_un` struct
+ #[inline]
+ pub fn as_mut_ptr(&mut self) -> *mut libc::sockaddr_un {
+ &mut self.sun
+ }
+
+ fn sun_len(&self) -> u8 {
+ cfg_if! {
+ if #[cfg(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux"
+ ))]
+ {
+ self.sun_len
+ } else {
+ self.sun.sun_len
+ }
+ }
+ }
+}
+
+impl private::SockaddrLikePriv for UnixAddr {}
+impl SockaddrLike for UnixAddr {
+ #[cfg(any(
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux"
+ ))]
+ fn len(&self) -> libc::socklen_t {
+ self.sun_len.into()
+ }
+
+ unsafe fn from_raw(
+ addr: *const libc::sockaddr,
+ len: Option<libc::socklen_t>,
+ ) -> Option<Self>
+ where
+ Self: Sized,
+ {
+ if let Some(l) = len {
+ if (l as usize) < offset_of!(libc::sockaddr_un, sun_path)
+ || l > u8::MAX as libc::socklen_t
+ {
+ return None;
+ }
+ }
+ if (*addr).sa_family as i32 != libc::AF_UNIX {
+ return None;
+ }
+ let mut su: libc::sockaddr_un = mem::zeroed();
+ let sup = &mut su as *mut libc::sockaddr_un as *mut u8;
+ cfg_if! {
+ if #[cfg(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux"
+ ))] {
+ let su_len = len.unwrap_or(
+ mem::size_of::<libc::sockaddr_un>() as libc::socklen_t
+ );
+ } else {
+ let su_len = len.unwrap_or((*addr).sa_len as libc::socklen_t);
+ }
+ };
+ ptr::copy(addr as *const u8, sup, su_len as usize);
+ Some(Self::from_raw_parts(su, su_len as u8))
+ }
+
+ fn size() -> libc::socklen_t
+ where
+ Self: Sized,
+ {
+ mem::size_of::<libc::sockaddr_un>() as libc::socklen_t
+ }
+}
+
+impl AsRef<libc::sockaddr_un> for UnixAddr {
+ fn as_ref(&self) -> &libc::sockaddr_un {
+ &self.sun
+ }
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+fn fmt_abstract(abs: &[u8], f: &mut fmt::Formatter) -> fmt::Result {
+ use fmt::Write;
+ f.write_str("@\"")?;
+ for &b in abs {
+ use fmt::Display;
+ char::from(b).escape_default().fmt(f)?;
+ }
+ f.write_char('"')?;
+ Ok(())
+}
+
+impl fmt::Display for UnixAddr {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match self.kind() {
+ UnixAddrKind::Pathname(path) => path.display().fmt(f),
+ UnixAddrKind::Unnamed => f.pad("<unbound UNIX socket>"),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ UnixAddrKind::Abstract(name) => fmt_abstract(name, f),
+ }
+ }
+}
+
+impl PartialEq for UnixAddr {
+ fn eq(&self, other: &UnixAddr) -> bool {
+ self.kind() == other.kind()
+ }
+}
+
+impl Eq for UnixAddr {}
+
+impl Hash for UnixAddr {
+ fn hash<H: Hasher>(&self, s: &mut H) {
+ self.kind().hash(s)
+ }
+}
+
+/// Anything that, in C, can be cast back and forth to `sockaddr`.
+///
+/// Most implementors also implement `AsRef<libc::XXX>` to access their
+/// inner type read-only.
+#[allow(clippy::len_without_is_empty)]
+pub trait SockaddrLike: private::SockaddrLikePriv {
+ /// Returns a raw pointer to the inner structure. Useful for FFI.
+ fn as_ptr(&self) -> *const libc::sockaddr {
+ self as *const Self as *const libc::sockaddr
+ }
+
+ /// Unsafe constructor from a variable length source
+ ///
+ /// Some C APIs from provide `len`, and others do not. If it's provided it
+ /// will be validated. If not, it will be guessed based on the family.
+ ///
+ /// # Arguments
+ ///
+ /// - `addr`: raw pointer to something that can be cast to a
+ /// `libc::sockaddr`. For example, `libc::sockaddr_in`,
+ /// `libc::sockaddr_in6`, etc.
+ /// - `len`: For fixed-width types like `sockaddr_in`, it will be
+ /// validated if present and ignored if not. For variable-width
+ /// types it is required and must be the total length of valid
+ /// data. For example, if `addr` points to a
+ /// named `sockaddr_un`, then `len` must be the length of the
+ /// structure up to but not including the trailing NUL.
+ ///
+ /// # Safety
+ ///
+ /// `addr` must be valid for the specific type of sockaddr. `len`, if
+ /// present, must not exceed the length of valid data in `addr`.
+ unsafe fn from_raw(
+ addr: *const libc::sockaddr,
+ len: Option<libc::socklen_t>,
+ ) -> Option<Self>
+ where
+ Self: Sized;
+
+ /// Return the address family of this socket
+ ///
+ /// # Examples
+ /// One common use is to match on the family of a union type, like this:
+ /// ```
+ /// # use nix::sys::socket::*;
+ /// let fd = socket(AddressFamily::Inet, SockType::Stream,
+ /// SockFlag::empty(), None).unwrap();
+ /// let ss: SockaddrStorage = getsockname(fd).unwrap();
+ /// match ss.family().unwrap() {
+ /// AddressFamily::Inet => println!("{}", ss.as_sockaddr_in().unwrap()),
+ /// AddressFamily::Inet6 => println!("{}", ss.as_sockaddr_in6().unwrap()),
+ /// _ => println!("Unexpected address family")
+ /// }
+ /// ```
+ fn family(&self) -> Option<AddressFamily> {
+ // Safe since all implementors have a sa_family field at the same
+ // address, and they're all repr(C)
+ AddressFamily::from_i32(unsafe {
+ (*(self as *const Self as *const libc::sockaddr)).sa_family as i32
+ })
+ }
+
+ cfg_if! {
+ if #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))] {
+ /// Return the length of valid data in the sockaddr structure.
+ ///
+ /// For fixed-size sockaddrs, this should be the size of the
+ /// structure. But for variable-sized types like [`UnixAddr`] it
+ /// may be less.
+ fn len(&self) -> libc::socklen_t {
+ // Safe since all implementors have a sa_len field at the same
+ // address, and they're all repr(transparent).
+ // Robust for all implementors.
+ unsafe {
+ (*(self as *const Self as *const libc::sockaddr)).sa_len
+ }.into()
+ }
+ } else {
+ /// Return the length of valid data in the sockaddr structure.
+ ///
+ /// For fixed-size sockaddrs, this should be the size of the
+ /// structure. But for variable-sized types like [`UnixAddr`] it
+ /// may be less.
+ fn len(&self) -> libc::socklen_t {
+ // No robust default implementation is possible without an
+ // sa_len field. Implementors with a variable size must
+ // override this method.
+ mem::size_of_val(self) as libc::socklen_t
+ }
+ }
+ }
+
+ /// Return the available space in the structure
+ fn size() -> libc::socklen_t
+ where
+ Self: Sized,
+ {
+ mem::size_of::<Self>() as libc::socklen_t
+ }
+}
+
+impl private::SockaddrLikePriv for () {
+ fn as_mut_ptr(&mut self) -> *mut libc::sockaddr {
+ ptr::null_mut()
+ }
+}
+
+/// `()` can be used in place of a real Sockaddr when no address is expected,
+/// for example for a field of `Option<S> where S: SockaddrLike`.
+// If this RFC ever stabilizes, then ! will be a better choice.
+// https://github.com/rust-lang/rust/issues/35121
+impl SockaddrLike for () {
+ fn as_ptr(&self) -> *const libc::sockaddr {
+ ptr::null()
+ }
+
+ unsafe fn from_raw(
+ _: *const libc::sockaddr,
+ _: Option<libc::socklen_t>,
+ ) -> Option<Self>
+ where
+ Self: Sized,
+ {
+ None
+ }
+
+ fn family(&self) -> Option<AddressFamily> {
+ None
+ }
+
+ fn len(&self) -> libc::socklen_t {
+ 0
+ }
+}
+
+/// An IPv4 socket address
+// This is identical to net::SocketAddrV4. But the standard library
+// doesn't allow direct access to the libc fields, which we need. So we
+// reimplement it here.
+#[cfg(feature = "net")]
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct SockaddrIn(libc::sockaddr_in);
+
+#[cfg(feature = "net")]
+impl SockaddrIn {
+ /// Returns the IP address associated with this socket address, in native
+ /// endian.
+ pub const fn ip(&self) -> libc::in_addr_t {
+ u32::from_be(self.0.sin_addr.s_addr)
+ }
+
+ /// Creates a new socket address from IPv4 octets and a port number.
+ pub fn new(a: u8, b: u8, c: u8, d: u8, port: u16) -> Self {
+ Self(libc::sockaddr_in {
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "haiku",
+ target_os = "openbsd"
+ ))]
+ sin_len: Self::size() as u8,
+ sin_family: AddressFamily::Inet as sa_family_t,
+ sin_port: u16::to_be(port),
+ sin_addr: libc::in_addr {
+ s_addr: u32::from_ne_bytes([a, b, c, d]),
+ },
+ sin_zero: unsafe { mem::zeroed() },
+ })
+ }
+
+ /// Returns the port number associated with this socket address, in native
+ /// endian.
+ pub const fn port(&self) -> u16 {
+ u16::from_be(self.0.sin_port)
+ }
+}
+
+#[cfg(feature = "net")]
+impl private::SockaddrLikePriv for SockaddrIn {}
+#[cfg(feature = "net")]
+impl SockaddrLike for SockaddrIn {
+ unsafe fn from_raw(
+ addr: *const libc::sockaddr,
+ len: Option<libc::socklen_t>,
+ ) -> Option<Self>
+ where
+ Self: Sized,
+ {
+ if let Some(l) = len {
+ if l != mem::size_of::<libc::sockaddr_in>() as libc::socklen_t {
+ return None;
+ }
+ }
+ if (*addr).sa_family as i32 != libc::AF_INET {
+ return None;
+ }
+ Some(Self(ptr::read_unaligned(addr as *const _)))
+ }
+}
+
+#[cfg(feature = "net")]
+impl AsRef<libc::sockaddr_in> for SockaddrIn {
+ fn as_ref(&self) -> &libc::sockaddr_in {
+ &self.0
+ }
+}
+
+#[cfg(feature = "net")]
+impl fmt::Display for SockaddrIn {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let ne = u32::from_be(self.0.sin_addr.s_addr);
+ let port = u16::from_be(self.0.sin_port);
+ write!(
+ f,
+ "{}.{}.{}.{}:{}",
+ ne >> 24,
+ (ne >> 16) & 0xFF,
+ (ne >> 8) & 0xFF,
+ ne & 0xFF,
+ port
+ )
+ }
+}
+
+#[cfg(feature = "net")]
+impl From<net::SocketAddrV4> for SockaddrIn {
+ fn from(addr: net::SocketAddrV4) -> Self {
+ Self(libc::sockaddr_in {
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "haiku",
+ target_os = "hermit",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ sin_len: mem::size_of::<libc::sockaddr_in>() as u8,
+ sin_family: AddressFamily::Inet as sa_family_t,
+ sin_port: addr.port().to_be(), // network byte order
+ sin_addr: ipv4addr_to_libc(*addr.ip()),
+ ..unsafe { mem::zeroed() }
+ })
+ }
+}
+
+#[cfg(feature = "net")]
+impl From<SockaddrIn> for net::SocketAddrV4 {
+ fn from(addr: SockaddrIn) -> Self {
+ net::SocketAddrV4::new(
+ net::Ipv4Addr::from(addr.0.sin_addr.s_addr.to_ne_bytes()),
+ u16::from_be(addr.0.sin_port),
+ )
+ }
+}
+
+#[cfg(feature = "net")]
+impl std::str::FromStr for SockaddrIn {
+ type Err = net::AddrParseError;
+
+ fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
+ net::SocketAddrV4::from_str(s).map(SockaddrIn::from)
+ }
+}
+
+/// An IPv6 socket address
+#[cfg(feature = "net")]
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct SockaddrIn6(libc::sockaddr_in6);
+
+#[cfg(feature = "net")]
+impl SockaddrIn6 {
+ /// Returns the flow information associated with this address.
+ pub const fn flowinfo(&self) -> u32 {
+ self.0.sin6_flowinfo
+ }
+
+ /// Returns the IP address associated with this socket address.
+ pub fn ip(&self) -> net::Ipv6Addr {
+ net::Ipv6Addr::from(self.0.sin6_addr.s6_addr)
+ }
+
+ /// Returns the port number associated with this socket address, in native
+ /// endian.
+ pub const fn port(&self) -> u16 {
+ u16::from_be(self.0.sin6_port)
+ }
+
+ /// Returns the scope ID associated with this address.
+ pub const fn scope_id(&self) -> u32 {
+ self.0.sin6_scope_id
+ }
+}
+
+#[cfg(feature = "net")]
+impl private::SockaddrLikePriv for SockaddrIn6 {}
+#[cfg(feature = "net")]
+impl SockaddrLike for SockaddrIn6 {
+ unsafe fn from_raw(
+ addr: *const libc::sockaddr,
+ len: Option<libc::socklen_t>,
+ ) -> Option<Self>
+ where
+ Self: Sized,
+ {
+ if let Some(l) = len {
+ if l != mem::size_of::<libc::sockaddr_in6>() as libc::socklen_t {
+ return None;
+ }
+ }
+ if (*addr).sa_family as i32 != libc::AF_INET6 {
+ return None;
+ }
+ Some(Self(ptr::read_unaligned(addr as *const _)))
+ }
+}
+
+#[cfg(feature = "net")]
+impl AsRef<libc::sockaddr_in6> for SockaddrIn6 {
+ fn as_ref(&self) -> &libc::sockaddr_in6 {
+ &self.0
+ }
+}
+
+#[cfg(feature = "net")]
+impl fmt::Display for SockaddrIn6 {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ // These things are really hard to display properly. Easier to let std
+ // do it.
+ let std = net::SocketAddrV6::new(
+ self.ip(),
+ self.port(),
+ self.flowinfo(),
+ self.scope_id(),
+ );
+ std.fmt(f)
+ }
+}
+
+#[cfg(feature = "net")]
+impl From<net::SocketAddrV6> for SockaddrIn6 {
+ fn from(addr: net::SocketAddrV6) -> Self {
+ #[allow(clippy::needless_update)] // It isn't needless on Illumos
+ Self(libc::sockaddr_in6 {
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "haiku",
+ target_os = "hermit",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ sin6_len: mem::size_of::<libc::sockaddr_in6>() as u8,
+ sin6_family: AddressFamily::Inet6 as sa_family_t,
+ sin6_port: addr.port().to_be(), // network byte order
+ sin6_addr: ipv6addr_to_libc(addr.ip()),
+ sin6_flowinfo: addr.flowinfo(), // host byte order
+ sin6_scope_id: addr.scope_id(), // host byte order
+ ..unsafe { mem::zeroed() }
+ })
+ }
+}
+
+#[cfg(feature = "net")]
+impl From<SockaddrIn6> for net::SocketAddrV6 {
+ fn from(addr: SockaddrIn6) -> Self {
+ net::SocketAddrV6::new(
+ net::Ipv6Addr::from(addr.0.sin6_addr.s6_addr),
+ u16::from_be(addr.0.sin6_port),
+ addr.0.sin6_flowinfo,
+ addr.0.sin6_scope_id,
+ )
+ }
+}
+
+#[cfg(feature = "net")]
+impl std::str::FromStr for SockaddrIn6 {
+ type Err = net::AddrParseError;
+
+ fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
+ net::SocketAddrV6::from_str(s).map(SockaddrIn6::from)
+ }
+}
+
+/// A container for any sockaddr type
+///
+/// Just like C's `sockaddr_storage`, this type is large enough to hold any type
+/// of sockaddr. It can be used as an argument with functions like
+/// [`bind`](super::bind) and [`getsockname`](super::getsockname). Though it is
+/// a union, it can be safely accessed through the `as_*` methods.
+///
+/// # Example
+/// ```
+/// # use nix::sys::socket::*;
+/// # use std::str::FromStr;
+/// let localhost = SockaddrIn::from_str("127.0.0.1:8081").unwrap();
+/// let fd = socket(AddressFamily::Inet, SockType::Stream, SockFlag::empty(),
+/// None).unwrap();
+/// bind(fd, &localhost).expect("bind");
+/// let ss: SockaddrStorage = getsockname(fd).expect("getsockname");
+/// assert_eq!(&localhost, ss.as_sockaddr_in().unwrap());
+/// ```
+#[derive(Clone, Copy, Eq)]
+#[repr(C)]
+pub union SockaddrStorage {
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ alg: AlgAddr,
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ dl: LinkAddr,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ nl: NetlinkAddr,
+ #[cfg(all(
+ feature = "ioctl",
+ any(target_os = "ios", target_os = "macos")
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(feature = "ioctl")))]
+ sctl: SysControlAddr,
+ #[cfg(feature = "net")]
+ sin: SockaddrIn,
+ #[cfg(feature = "net")]
+ sin6: SockaddrIn6,
+ ss: libc::sockaddr_storage,
+ su: UnixAddr,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ vsock: VsockAddr,
+}
+impl private::SockaddrLikePriv for SockaddrStorage {}
+impl SockaddrLike for SockaddrStorage {
+ unsafe fn from_raw(
+ addr: *const libc::sockaddr,
+ l: Option<libc::socklen_t>,
+ ) -> Option<Self>
+ where
+ Self: Sized,
+ {
+ if addr.is_null() {
+ return None;
+ }
+ if let Some(len) = l {
+ let ulen = len as usize;
+ if ulen < offset_of!(libc::sockaddr, sa_data)
+ || ulen > mem::size_of::<libc::sockaddr_storage>()
+ {
+ None
+ } else {
+ let mut ss: libc::sockaddr_storage = mem::zeroed();
+ let ssp = &mut ss as *mut libc::sockaddr_storage as *mut u8;
+ ptr::copy(addr as *const u8, ssp, len as usize);
+ #[cfg(any(
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux"
+ ))]
+ if i32::from(ss.ss_family) == libc::AF_UNIX {
+ // Safe because we UnixAddr is strictly smaller than
+ // SockaddrStorage, and we just initialized the structure.
+ (*(&mut ss as *mut libc::sockaddr_storage as *mut UnixAddr)).sun_len = len as u8;
+ }
+ Some(Self { ss })
+ }
+ } else {
+ // If length is not available and addr is of a fixed-length type,
+ // copy it. If addr is of a variable length type and len is not
+ // available, then there's nothing we can do.
+ match (*addr).sa_family as i32 {
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ libc::AF_ALG => {
+ AlgAddr::from_raw(addr, l).map(|alg| Self { alg })
+ }
+ #[cfg(feature = "net")]
+ libc::AF_INET => {
+ SockaddrIn::from_raw(addr, l).map(|sin| Self { sin })
+ }
+ #[cfg(feature = "net")]
+ libc::AF_INET6 => {
+ SockaddrIn6::from_raw(addr, l).map(|sin6| Self { sin6 })
+ }
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "haiku",
+ target_os = "openbsd"
+ ))]
+ #[cfg(feature = "net")]
+ libc::AF_LINK => {
+ LinkAddr::from_raw(addr, l).map(|dl| Self { dl })
+ }
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ libc::AF_NETLINK => {
+ NetlinkAddr::from_raw(addr, l).map(|nl| Self { nl })
+ }
+ #[cfg(any(
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "linux"
+ ))]
+ #[cfg(feature = "net")]
+ libc::AF_PACKET => {
+ LinkAddr::from_raw(addr, l).map(|dl| Self { dl })
+ }
+ #[cfg(all(
+ feature = "ioctl",
+ any(target_os = "ios", target_os = "macos")
+ ))]
+ libc::AF_SYSTEM => {
+ SysControlAddr::from_raw(addr, l).map(|sctl| Self { sctl })
+ }
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ libc::AF_VSOCK => {
+ VsockAddr::from_raw(addr, l).map(|vsock| Self { vsock })
+ }
+ _ => None,
+ }
+ }
+ }
+
+ #[cfg(any(
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux"
+ ))]
+ fn len(&self) -> libc::socklen_t {
+ match self.as_unix_addr() {
+ // The UnixAddr type knows its own length
+ Some(ua) => ua.len(),
+ // For all else, we're just a boring SockaddrStorage
+ None => mem::size_of_val(self) as libc::socklen_t
+ }
+ }
+}
+
+macro_rules! accessors {
+ (
+ $fname:ident,
+ $fname_mut:ident,
+ $sockty:ty,
+ $family:expr,
+ $libc_ty:ty,
+ $field:ident) => {
+ /// Safely and falliably downcast to an immutable reference
+ pub fn $fname(&self) -> Option<&$sockty> {
+ if self.family() == Some($family)
+ && self.len() >= mem::size_of::<$libc_ty>() as libc::socklen_t
+ {
+ // Safe because family and len are validated
+ Some(unsafe { &self.$field })
+ } else {
+ None
+ }
+ }
+
+ /// Safely and falliably downcast to a mutable reference
+ pub fn $fname_mut(&mut self) -> Option<&mut $sockty> {
+ if self.family() == Some($family)
+ && self.len() >= mem::size_of::<$libc_ty>() as libc::socklen_t
+ {
+ // Safe because family and len are validated
+ Some(unsafe { &mut self.$field })
+ } else {
+ None
+ }
+ }
+ };
+}
+
+impl SockaddrStorage {
+ /// Downcast to an immutable `[UnixAddr]` reference.
+ pub fn as_unix_addr(&self) -> Option<&UnixAddr> {
+ cfg_if! {
+ if #[cfg(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux"
+ ))]
+ {
+ let p = unsafe{ &self.ss as *const libc::sockaddr_storage };
+ // Safe because UnixAddr is strictly smaller than
+ // sockaddr_storage, and we're fully initialized
+ let len = unsafe {
+ (*(p as *const UnixAddr )).sun_len as usize
+ };
+ } else {
+ let len = self.len() as usize;
+ }
+ }
+ // Sanity checks
+ if self.family() != Some(AddressFamily::Unix) ||
+ len < offset_of!(libc::sockaddr_un, sun_path) ||
+ len > mem::size_of::<libc::sockaddr_un>() {
+ None
+ } else {
+ Some(unsafe{&self.su})
+ }
+ }
+
+ /// Downcast to a mutable `[UnixAddr]` reference.
+ pub fn as_unix_addr_mut(&mut self) -> Option<&mut UnixAddr> {
+ cfg_if! {
+ if #[cfg(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux"
+ ))]
+ {
+ let p = unsafe{ &self.ss as *const libc::sockaddr_storage };
+ // Safe because UnixAddr is strictly smaller than
+ // sockaddr_storage, and we're fully initialized
+ let len = unsafe {
+ (*(p as *const UnixAddr )).sun_len as usize
+ };
+ } else {
+ let len = self.len() as usize;
+ }
+ }
+ // Sanity checks
+ if self.family() != Some(AddressFamily::Unix) ||
+ len < offset_of!(libc::sockaddr_un, sun_path) ||
+ len > mem::size_of::<libc::sockaddr_un>() {
+ None
+ } else {
+ Some(unsafe{&mut self.su})
+ }
+ }
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ accessors! {as_alg_addr, as_alg_addr_mut, AlgAddr,
+ AddressFamily::Alg, libc::sockaddr_alg, alg}
+
+ #[cfg(any(
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "linux"
+ ))]
+ #[cfg(feature = "net")]
+ accessors! {
+ as_link_addr, as_link_addr_mut, LinkAddr,
+ AddressFamily::Packet, libc::sockaddr_ll, dl}
+
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg(feature = "net")]
+ accessors! {
+ as_link_addr, as_link_addr_mut, LinkAddr,
+ AddressFamily::Link, libc::sockaddr_dl, dl}
+
+ #[cfg(feature = "net")]
+ accessors! {
+ as_sockaddr_in, as_sockaddr_in_mut, SockaddrIn,
+ AddressFamily::Inet, libc::sockaddr_in, sin}
+
+ #[cfg(feature = "net")]
+ accessors! {
+ as_sockaddr_in6, as_sockaddr_in6_mut, SockaddrIn6,
+ AddressFamily::Inet6, libc::sockaddr_in6, sin6}
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ accessors! {as_netlink_addr, as_netlink_addr_mut, NetlinkAddr,
+ AddressFamily::Netlink, libc::sockaddr_nl, nl}
+
+ #[cfg(all(feature = "ioctl", any(target_os = "ios", target_os = "macos")))]
+ #[cfg_attr(docsrs, doc(cfg(feature = "ioctl")))]
+ accessors! {as_sys_control_addr, as_sys_control_addr_mut, SysControlAddr,
+ AddressFamily::System, libc::sockaddr_ctl, sctl}
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ accessors! {as_vsock_addr, as_vsock_addr_mut, VsockAddr,
+ AddressFamily::Vsock, libc::sockaddr_vm, vsock}
+}
+
+impl fmt::Debug for SockaddrStorage {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ f.debug_struct("SockaddrStorage")
+ // Safe because sockaddr_storage has the least specific
+ // field types
+ .field("ss", unsafe { &self.ss })
+ .finish()
+ }
+}
+
+impl fmt::Display for SockaddrStorage {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ unsafe {
+ match self.ss.ss_family as i32 {
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ libc::AF_ALG => self.alg.fmt(f),
+ #[cfg(feature = "net")]
+ libc::AF_INET => self.sin.fmt(f),
+ #[cfg(feature = "net")]
+ libc::AF_INET6 => self.sin6.fmt(f),
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg(feature = "net")]
+ libc::AF_LINK => self.dl.fmt(f),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ libc::AF_NETLINK => self.nl.fmt(f),
+ #[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "fuchsia"
+ ))]
+ #[cfg(feature = "net")]
+ libc::AF_PACKET => self.dl.fmt(f),
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[cfg(feature = "ioctl")]
+ libc::AF_SYSTEM => self.sctl.fmt(f),
+ libc::AF_UNIX => self.su.fmt(f),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ libc::AF_VSOCK => self.vsock.fmt(f),
+ _ => "<Address family unspecified>".fmt(f),
+ }
+ }
+ }
+}
+
+#[cfg(feature = "net")]
+impl From<net::SocketAddrV4> for SockaddrStorage {
+ fn from(s: net::SocketAddrV4) -> Self {
+ unsafe {
+ let mut ss: Self = mem::zeroed();
+ ss.sin = SockaddrIn::from(s);
+ ss
+ }
+ }
+}
+
+#[cfg(feature = "net")]
+impl From<net::SocketAddrV6> for SockaddrStorage {
+ fn from(s: net::SocketAddrV6) -> Self {
+ unsafe {
+ let mut ss: Self = mem::zeroed();
+ ss.sin6 = SockaddrIn6::from(s);
+ ss
+ }
+ }
+}
+
+#[cfg(feature = "net")]
+impl From<net::SocketAddr> for SockaddrStorage {
+ fn from(s: net::SocketAddr) -> Self {
+ match s {
+ net::SocketAddr::V4(sa4) => Self::from(sa4),
+ net::SocketAddr::V6(sa6) => Self::from(sa6),
+ }
+ }
+}
+
+impl Hash for SockaddrStorage {
+ fn hash<H: Hasher>(&self, s: &mut H) {
+ unsafe {
+ match self.ss.ss_family as i32 {
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ libc::AF_ALG => self.alg.hash(s),
+ #[cfg(feature = "net")]
+ libc::AF_INET => self.sin.hash(s),
+ #[cfg(feature = "net")]
+ libc::AF_INET6 => self.sin6.hash(s),
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg(feature = "net")]
+ libc::AF_LINK => self.dl.hash(s),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ libc::AF_NETLINK => self.nl.hash(s),
+ #[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "fuchsia"
+ ))]
+ #[cfg(feature = "net")]
+ libc::AF_PACKET => self.dl.hash(s),
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[cfg(feature = "ioctl")]
+ libc::AF_SYSTEM => self.sctl.hash(s),
+ libc::AF_UNIX => self.su.hash(s),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ libc::AF_VSOCK => self.vsock.hash(s),
+ _ => self.ss.hash(s),
+ }
+ }
+ }
+}
+
+impl PartialEq for SockaddrStorage {
+ fn eq(&self, other: &Self) -> bool {
+ unsafe {
+ match (self.ss.ss_family as i32, other.ss.ss_family as i32) {
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ (libc::AF_ALG, libc::AF_ALG) => self.alg == other.alg,
+ #[cfg(feature = "net")]
+ (libc::AF_INET, libc::AF_INET) => self.sin == other.sin,
+ #[cfg(feature = "net")]
+ (libc::AF_INET6, libc::AF_INET6) => self.sin6 == other.sin6,
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg(feature = "net")]
+ (libc::AF_LINK, libc::AF_LINK) => self.dl == other.dl,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ (libc::AF_NETLINK, libc::AF_NETLINK) => self.nl == other.nl,
+ #[cfg(any(
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "linux"
+ ))]
+ #[cfg(feature = "net")]
+ (libc::AF_PACKET, libc::AF_PACKET) => self.dl == other.dl,
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[cfg(feature = "ioctl")]
+ (libc::AF_SYSTEM, libc::AF_SYSTEM) => self.sctl == other.sctl,
+ (libc::AF_UNIX, libc::AF_UNIX) => self.su == other.su,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ (libc::AF_VSOCK, libc::AF_VSOCK) => self.vsock == other.vsock,
+ _ => false,
+ }
+ }
+ }
+}
+
+mod private {
+ pub trait SockaddrLikePriv {
+ /// Returns a mutable raw pointer to the inner structure.
+ ///
+ /// # Safety
+ ///
+ /// This method is technically safe, but modifying the inner structure's
+ /// `family` or `len` fields may result in violating Nix's invariants.
+ /// It is best to use this method only with foreign functions that do
+ /// not change the sockaddr type.
+ fn as_mut_ptr(&mut self) -> *mut libc::sockaddr {
+ self as *mut Self as *mut libc::sockaddr
+ }
+ }
+}
+
+/// Represents a socket address
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[deprecated(
+ since = "0.24.0",
+ note = "use SockaddrLike or SockaddrStorage instead"
+)]
+#[allow(missing_docs)] // Since they're all deprecated anyway
+#[allow(deprecated)]
+#[non_exhaustive]
+pub enum SockAddr {
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Inet(InetAddr),
+ Unix(UnixAddr),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Netlink(NetlinkAddr),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Alg(AlgAddr),
+ #[cfg(all(
+ feature = "ioctl",
+ any(target_os = "ios", target_os = "macos")
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(feature = "ioctl")))]
+ SysControl(SysControlAddr),
+ /// Datalink address (MAC)
+ #[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Link(LinkAddr),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ Vsock(VsockAddr),
+}
+
+#[allow(missing_docs)] // Since they're all deprecated anyway
+#[allow(deprecated)]
+impl SockAddr {
+ feature! {
+ #![feature = "net"]
+ pub fn new_inet(addr: InetAddr) -> SockAddr {
+ SockAddr::Inet(addr)
+ }
+ }
+
+ pub fn new_unix<P: ?Sized + NixPath>(path: &P) -> Result<SockAddr> {
+ Ok(SockAddr::Unix(UnixAddr::new(path)?))
+ }
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn new_netlink(pid: u32, groups: u32) -> SockAddr {
+ SockAddr::Netlink(NetlinkAddr::new(pid, groups))
+ }
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn new_alg(alg_type: &str, alg_name: &str) -> SockAddr {
+ SockAddr::Alg(AlgAddr::new(alg_type, alg_name))
+ }
+
+ feature! {
+ #![feature = "ioctl"]
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ pub fn new_sys_control(sockfd: RawFd, name: &str, unit: u32) -> Result<SockAddr> {
+ SysControlAddr::from_name(sockfd, name, unit).map(SockAddr::SysControl)
+ }
+ }
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn new_vsock(cid: u32, port: u32) -> SockAddr {
+ SockAddr::Vsock(VsockAddr::new(cid, port))
+ }
+
+ pub fn family(&self) -> AddressFamily {
+ match *self {
+ #[cfg(feature = "net")]
+ SockAddr::Inet(InetAddr::V4(..)) => AddressFamily::Inet,
+ #[cfg(feature = "net")]
+ SockAddr::Inet(InetAddr::V6(..)) => AddressFamily::Inet6,
+ SockAddr::Unix(..) => AddressFamily::Unix,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ SockAddr::Netlink(..) => AddressFamily::Netlink,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ SockAddr::Alg(..) => AddressFamily::Alg,
+ #[cfg(all(
+ feature = "ioctl",
+ any(target_os = "ios", target_os = "macos")
+ ))]
+ SockAddr::SysControl(..) => AddressFamily::System,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ SockAddr::Link(..) => AddressFamily::Packet,
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "illumos",
+ target_os = "openbsd"
+ ))]
+ #[cfg(feature = "net")]
+ SockAddr::Link(..) => AddressFamily::Link,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ SockAddr::Vsock(..) => AddressFamily::Vsock,
+ }
+ }
+
+ #[deprecated(since = "0.23.0", note = "use .to_string() instead")]
+ pub fn to_str(&self) -> String {
+ format!("{}", self)
+ }
+
+ /// Creates a `SockAddr` struct from libc's sockaddr.
+ ///
+ /// Supports only the following address families: Unix, Inet (v4 & v6), Netlink and System.
+ /// Returns None for unsupported families.
+ ///
+ /// # Safety
+ ///
+ /// unsafe because it takes a raw pointer as argument. The caller must
+ /// ensure that the pointer is valid.
+ #[cfg(not(target_os = "fuchsia"))]
+ #[cfg(feature = "net")]
+ pub(crate) unsafe fn from_libc_sockaddr(
+ addr: *const libc::sockaddr,
+ ) -> Option<SockAddr> {
+ if addr.is_null() {
+ None
+ } else {
+ match AddressFamily::from_i32(i32::from((*addr).sa_family)) {
+ Some(AddressFamily::Unix) => None,
+ #[cfg(feature = "net")]
+ Some(AddressFamily::Inet) => Some(SockAddr::Inet(
+ InetAddr::V4(ptr::read_unaligned(addr as *const _)),
+ )),
+ #[cfg(feature = "net")]
+ Some(AddressFamily::Inet6) => Some(SockAddr::Inet(
+ InetAddr::V6(ptr::read_unaligned(addr as *const _)),
+ )),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ Some(AddressFamily::Netlink) => Some(SockAddr::Netlink(
+ NetlinkAddr(ptr::read_unaligned(addr as *const _)),
+ )),
+ #[cfg(all(
+ feature = "ioctl",
+ any(target_os = "ios", target_os = "macos")
+ ))]
+ Some(AddressFamily::System) => Some(SockAddr::SysControl(
+ SysControlAddr(ptr::read_unaligned(addr as *const _)),
+ )),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ Some(AddressFamily::Packet) => Some(SockAddr::Link(LinkAddr(
+ ptr::read_unaligned(addr as *const _),
+ ))),
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "illumos",
+ target_os = "openbsd"
+ ))]
+ #[cfg(feature = "net")]
+ Some(AddressFamily::Link) => {
+ let ether_addr =
+ LinkAddr(ptr::read_unaligned(addr as *const _));
+ if ether_addr.is_empty() {
+ None
+ } else {
+ Some(SockAddr::Link(ether_addr))
+ }
+ }
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ Some(AddressFamily::Vsock) => Some(SockAddr::Vsock(VsockAddr(
+ ptr::read_unaligned(addr as *const _),
+ ))),
+ // Other address families are currently not supported and simply yield a None
+ // entry instead of a proper conversion to a `SockAddr`.
+ Some(_) | None => None,
+ }
+ }
+ }
+
+ /// Conversion from nix's SockAddr type to the underlying libc sockaddr type.
+ ///
+ /// This is useful for interfacing with other libc functions that don't yet have nix wrappers.
+ /// Returns a reference to the underlying data type (as a sockaddr reference) along
+ /// with the size of the actual data type. sockaddr is commonly used as a proxy for
+ /// a superclass as C doesn't support inheritance, so many functions that take
+ /// a sockaddr * need to take the size of the underlying type as well and then internally cast it back.
+ pub fn as_ffi_pair(&self) -> (&libc::sockaddr, libc::socklen_t) {
+ match *self {
+ #[cfg(feature = "net")]
+ SockAddr::Inet(InetAddr::V4(ref addr)) => (
+ // This cast is always allowed in C
+ unsafe {
+ &*(addr as *const libc::sockaddr_in
+ as *const libc::sockaddr)
+ },
+ mem::size_of_val(addr) as libc::socklen_t,
+ ),
+ #[cfg(feature = "net")]
+ SockAddr::Inet(InetAddr::V6(ref addr)) => (
+ // This cast is always allowed in C
+ unsafe {
+ &*(addr as *const libc::sockaddr_in6
+ as *const libc::sockaddr)
+ },
+ mem::size_of_val(addr) as libc::socklen_t,
+ ),
+ SockAddr::Unix(ref unix_addr) => (
+ // This cast is always allowed in C
+ unsafe {
+ &*(&unix_addr.sun as *const libc::sockaddr_un
+ as *const libc::sockaddr)
+ },
+ unix_addr.sun_len() as libc::socklen_t,
+ ),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ SockAddr::Netlink(NetlinkAddr(ref sa)) => (
+ // This cast is always allowed in C
+ unsafe {
+ &*(sa as *const libc::sockaddr_nl as *const libc::sockaddr)
+ },
+ mem::size_of_val(sa) as libc::socklen_t,
+ ),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ SockAddr::Alg(AlgAddr(ref sa)) => (
+ // This cast is always allowed in C
+ unsafe {
+ &*(sa as *const libc::sockaddr_alg as *const libc::sockaddr)
+ },
+ mem::size_of_val(sa) as libc::socklen_t,
+ ),
+ #[cfg(all(
+ feature = "ioctl",
+ any(target_os = "ios", target_os = "macos")
+ ))]
+ SockAddr::SysControl(SysControlAddr(ref sa)) => (
+ // This cast is always allowed in C
+ unsafe {
+ &*(sa as *const libc::sockaddr_ctl as *const libc::sockaddr)
+ },
+ mem::size_of_val(sa) as libc::socklen_t,
+ ),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ SockAddr::Link(LinkAddr(ref addr)) => (
+ // This cast is always allowed in C
+ unsafe {
+ &*(addr as *const libc::sockaddr_ll
+ as *const libc::sockaddr)
+ },
+ mem::size_of_val(addr) as libc::socklen_t,
+ ),
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[cfg(feature = "net")]
+ SockAddr::Link(LinkAddr(ref addr)) => (
+ // This cast is always allowed in C
+ unsafe {
+ &*(addr as *const libc::sockaddr_dl
+ as *const libc::sockaddr)
+ },
+ mem::size_of_val(addr) as libc::socklen_t,
+ ),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ SockAddr::Vsock(VsockAddr(ref sa)) => (
+ // This cast is always allowed in C
+ unsafe {
+ &*(sa as *const libc::sockaddr_vm as *const libc::sockaddr)
+ },
+ mem::size_of_val(sa) as libc::socklen_t,
+ ),
+ }
+ }
+}
+
+#[allow(deprecated)]
+impl fmt::Display for SockAddr {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ match *self {
+ #[cfg(feature = "net")]
+ SockAddr::Inet(ref inet) => inet.fmt(f),
+ SockAddr::Unix(ref unix) => unix.fmt(f),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ SockAddr::Netlink(ref nl) => nl.fmt(f),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ SockAddr::Alg(ref nl) => nl.fmt(f),
+ #[cfg(all(
+ feature = "ioctl",
+ any(target_os = "ios", target_os = "macos")
+ ))]
+ SockAddr::SysControl(ref sc) => sc.fmt(f),
+ #[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "illumos",
+ target_os = "openbsd"
+ ))]
+ #[cfg(feature = "net")]
+ SockAddr::Link(ref ether_addr) => ether_addr.fmt(f),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ SockAddr::Vsock(ref svm) => svm.fmt(f),
+ }
+ }
+}
+
+#[cfg(not(target_os = "fuchsia"))]
+#[cfg(feature = "net")]
+#[allow(deprecated)]
+impl private::SockaddrLikePriv for SockAddr {}
+#[cfg(not(target_os = "fuchsia"))]
+#[cfg(feature = "net")]
+#[allow(deprecated)]
+impl SockaddrLike for SockAddr {
+ unsafe fn from_raw(
+ addr: *const libc::sockaddr,
+ _len: Option<libc::socklen_t>,
+ ) -> Option<Self> {
+ Self::from_libc_sockaddr(addr)
+ }
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub mod netlink {
+ use super::*;
+ use crate::sys::socket::addr::AddressFamily;
+ use libc::{sa_family_t, sockaddr_nl};
+ use std::{fmt, mem};
+
+ /// Address for the Linux kernel user interface device.
+ ///
+ /// # References
+ ///
+ /// [netlink(7)](https://man7.org/linux/man-pages/man7/netlink.7.html)
+ #[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
+ #[repr(transparent)]
+ pub struct NetlinkAddr(pub(in super::super) sockaddr_nl);
+
+ impl NetlinkAddr {
+ /// Construct a new socket address from its port ID and multicast groups
+ /// mask.
+ pub fn new(pid: u32, groups: u32) -> NetlinkAddr {
+ let mut addr: sockaddr_nl = unsafe { mem::zeroed() };
+ addr.nl_family = AddressFamily::Netlink as sa_family_t;
+ addr.nl_pid = pid;
+ addr.nl_groups = groups;
+
+ NetlinkAddr(addr)
+ }
+
+ /// Return the socket's port ID.
+ pub const fn pid(&self) -> u32 {
+ self.0.nl_pid
+ }
+
+ /// Return the socket's multicast groups mask
+ pub const fn groups(&self) -> u32 {
+ self.0.nl_groups
+ }
+ }
+
+ impl private::SockaddrLikePriv for NetlinkAddr {}
+ impl SockaddrLike for NetlinkAddr {
+ unsafe fn from_raw(
+ addr: *const libc::sockaddr,
+ len: Option<libc::socklen_t>,
+ ) -> Option<Self>
+ where
+ Self: Sized,
+ {
+ if let Some(l) = len {
+ if l != mem::size_of::<libc::sockaddr_nl>() as libc::socklen_t {
+ return None;
+ }
+ }
+ if (*addr).sa_family as i32 != libc::AF_NETLINK {
+ return None;
+ }
+ Some(Self(ptr::read_unaligned(addr as *const _)))
+ }
+ }
+
+ impl AsRef<libc::sockaddr_nl> for NetlinkAddr {
+ fn as_ref(&self) -> &libc::sockaddr_nl {
+ &self.0
+ }
+ }
+
+ impl fmt::Display for NetlinkAddr {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "pid: {} groups: {}", self.pid(), self.groups())
+ }
+ }
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub mod alg {
+ use super::*;
+ use libc::{c_char, sockaddr_alg, AF_ALG};
+ use std::ffi::CStr;
+ use std::hash::{Hash, Hasher};
+ use std::{fmt, mem, str};
+
+ /// Socket address for the Linux kernel crypto API
+ #[derive(Copy, Clone)]
+ #[repr(transparent)]
+ pub struct AlgAddr(pub(in super::super) sockaddr_alg);
+
+ impl private::SockaddrLikePriv for AlgAddr {}
+ impl SockaddrLike for AlgAddr {
+ unsafe fn from_raw(
+ addr: *const libc::sockaddr,
+ l: Option<libc::socklen_t>,
+ ) -> Option<Self>
+ where
+ Self: Sized,
+ {
+ if let Some(l) = l {
+ if l != mem::size_of::<libc::sockaddr_alg>() as libc::socklen_t
+ {
+ return None;
+ }
+ }
+ if (*addr).sa_family as i32 != libc::AF_ALG {
+ return None;
+ }
+ Some(Self(ptr::read_unaligned(addr as *const _)))
+ }
+ }
+
+ impl AsRef<libc::sockaddr_alg> for AlgAddr {
+ fn as_ref(&self) -> &libc::sockaddr_alg {
+ &self.0
+ }
+ }
+
+ // , PartialEq, Eq, Debug, Hash
+ impl PartialEq for AlgAddr {
+ fn eq(&self, other: &Self) -> bool {
+ let (inner, other) = (self.0, other.0);
+ (
+ inner.salg_family,
+ &inner.salg_type[..],
+ inner.salg_feat,
+ inner.salg_mask,
+ &inner.salg_name[..],
+ ) == (
+ other.salg_family,
+ &other.salg_type[..],
+ other.salg_feat,
+ other.salg_mask,
+ &other.salg_name[..],
+ )
+ }
+ }
+
+ impl Eq for AlgAddr {}
+
+ impl Hash for AlgAddr {
+ fn hash<H: Hasher>(&self, s: &mut H) {
+ let inner = self.0;
+ (
+ inner.salg_family,
+ &inner.salg_type[..],
+ inner.salg_feat,
+ inner.salg_mask,
+ &inner.salg_name[..],
+ )
+ .hash(s);
+ }
+ }
+
+ impl AlgAddr {
+ /// Construct an `AF_ALG` socket from its cipher name and type.
+ pub fn new(alg_type: &str, alg_name: &str) -> AlgAddr {
+ let mut addr: sockaddr_alg = unsafe { mem::zeroed() };
+ addr.salg_family = AF_ALG as u16;
+ addr.salg_type[..alg_type.len()]
+ .copy_from_slice(alg_type.to_string().as_bytes());
+ addr.salg_name[..alg_name.len()]
+ .copy_from_slice(alg_name.to_string().as_bytes());
+
+ AlgAddr(addr)
+ }
+
+ /// Return the socket's cipher type, for example `hash` or `aead`.
+ pub fn alg_type(&self) -> &CStr {
+ unsafe {
+ CStr::from_ptr(self.0.salg_type.as_ptr() as *const c_char)
+ }
+ }
+
+ /// Return the socket's cipher name, for example `sha1`.
+ pub fn alg_name(&self) -> &CStr {
+ unsafe {
+ CStr::from_ptr(self.0.salg_name.as_ptr() as *const c_char)
+ }
+ }
+ }
+
+ impl fmt::Display for AlgAddr {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(
+ f,
+ "type: {} alg: {}",
+ self.alg_name().to_string_lossy(),
+ self.alg_type().to_string_lossy()
+ )
+ }
+ }
+
+ impl fmt::Debug for AlgAddr {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Display::fmt(self, f)
+ }
+ }
+}
+
+feature! {
+#![feature = "ioctl"]
+#[cfg(any(target_os = "ios", target_os = "macos"))]
+pub mod sys_control {
+ use crate::sys::socket::addr::AddressFamily;
+ use libc::{self, c_uchar};
+ use std::{fmt, mem, ptr};
+ use std::os::unix::io::RawFd;
+ use crate::{Errno, Result};
+ use super::{private, SockaddrLike};
+
+ // FIXME: Move type into `libc`
+ #[repr(C)]
+ #[derive(Clone, Copy)]
+ #[allow(missing_debug_implementations)]
+ pub struct ctl_ioc_info {
+ pub ctl_id: u32,
+ pub ctl_name: [c_uchar; MAX_KCTL_NAME],
+ }
+
+ const CTL_IOC_MAGIC: u8 = b'N';
+ const CTL_IOC_INFO: u8 = 3;
+ const MAX_KCTL_NAME: usize = 96;
+
+ ioctl_readwrite!(ctl_info, CTL_IOC_MAGIC, CTL_IOC_INFO, ctl_ioc_info);
+
+ /// Apple system control socket
+ ///
+ /// # References
+ ///
+ /// <https://developer.apple.com/documentation/kernel/sockaddr_ctl>
+ #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+ #[repr(transparent)]
+ pub struct SysControlAddr(pub(in super::super) libc::sockaddr_ctl);
+
+ impl private::SockaddrLikePriv for SysControlAddr {}
+ impl SockaddrLike for SysControlAddr {
+ unsafe fn from_raw(addr: *const libc::sockaddr, len: Option<libc::socklen_t>)
+ -> Option<Self> where Self: Sized
+ {
+ if let Some(l) = len {
+ if l != mem::size_of::<libc::sockaddr_ctl>() as libc::socklen_t {
+ return None;
+ }
+ }
+ if (*addr).sa_family as i32 != libc::AF_SYSTEM {
+ return None;
+ }
+ Some(Self(ptr::read_unaligned(addr as *const _)))
+ }
+ }
+
+ impl AsRef<libc::sockaddr_ctl> for SysControlAddr {
+ fn as_ref(&self) -> &libc::sockaddr_ctl {
+ &self.0
+ }
+ }
+
+ impl SysControlAddr {
+ /// Construct a new `SysControlAddr` from its kernel unique identifier
+ /// and unit number.
+ pub const fn new(id: u32, unit: u32) -> SysControlAddr {
+ let addr = libc::sockaddr_ctl {
+ sc_len: mem::size_of::<libc::sockaddr_ctl>() as c_uchar,
+ sc_family: AddressFamily::System as c_uchar,
+ ss_sysaddr: libc::AF_SYS_CONTROL as u16,
+ sc_id: id,
+ sc_unit: unit,
+ sc_reserved: [0; 5]
+ };
+
+ SysControlAddr(addr)
+ }
+
+ /// Construct a new `SysControlAddr` from its human readable name and
+ /// unit number.
+ pub fn from_name(sockfd: RawFd, name: &str, unit: u32) -> Result<SysControlAddr> {
+ if name.len() > MAX_KCTL_NAME {
+ return Err(Errno::ENAMETOOLONG);
+ }
+
+ let mut ctl_name = [0; MAX_KCTL_NAME];
+ ctl_name[..name.len()].clone_from_slice(name.as_bytes());
+ let mut info = ctl_ioc_info { ctl_id: 0, ctl_name };
+
+ unsafe { ctl_info(sockfd, &mut info)?; }
+
+ Ok(SysControlAddr::new(info.ctl_id, unit))
+ }
+
+ /// Return the kernel unique identifier
+ pub const fn id(&self) -> u32 {
+ self.0.sc_id
+ }
+
+ /// Return the kernel controller private unit number.
+ pub const fn unit(&self) -> u32 {
+ self.0.sc_unit
+ }
+ }
+
+ impl fmt::Display for SysControlAddr {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Debug::fmt(self, f)
+ }
+ }
+}
+}
+
+#[cfg(any(target_os = "android", target_os = "linux", target_os = "fuchsia"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+mod datalink {
+ feature! {
+ #![feature = "net"]
+ use super::{fmt, mem, private, ptr, SockaddrLike};
+
+ /// Hardware Address
+ #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+ #[repr(transparent)]
+ pub struct LinkAddr(pub(in super::super) libc::sockaddr_ll);
+
+ impl LinkAddr {
+ /// Physical-layer protocol
+ pub fn protocol(&self) -> u16 {
+ self.0.sll_protocol
+ }
+
+ /// Interface number
+ pub fn ifindex(&self) -> usize {
+ self.0.sll_ifindex as usize
+ }
+
+ /// ARP hardware type
+ pub fn hatype(&self) -> u16 {
+ self.0.sll_hatype
+ }
+
+ /// Packet type
+ pub fn pkttype(&self) -> u8 {
+ self.0.sll_pkttype
+ }
+
+ /// Length of MAC address
+ pub fn halen(&self) -> usize {
+ self.0.sll_halen as usize
+ }
+
+ /// Physical-layer address (MAC)
+ // Returns an Option just for cross-platform compatibility
+ pub fn addr(&self) -> Option<[u8; 6]> {
+ Some([
+ self.0.sll_addr[0],
+ self.0.sll_addr[1],
+ self.0.sll_addr[2],
+ self.0.sll_addr[3],
+ self.0.sll_addr[4],
+ self.0.sll_addr[5],
+ ])
+ }
+ }
+
+ impl fmt::Display for LinkAddr {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ if let Some(addr) = self.addr() {
+ write!(f, "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
+ addr[0],
+ addr[1],
+ addr[2],
+ addr[3],
+ addr[4],
+ addr[5])
+ } else {
+ Ok(())
+ }
+ }
+ }
+ impl private::SockaddrLikePriv for LinkAddr {}
+ impl SockaddrLike for LinkAddr {
+ unsafe fn from_raw(addr: *const libc::sockaddr,
+ len: Option<libc::socklen_t>)
+ -> Option<Self> where Self: Sized
+ {
+ if let Some(l) = len {
+ if l != mem::size_of::<libc::sockaddr_ll>() as libc::socklen_t {
+ return None;
+ }
+ }
+ if (*addr).sa_family as i32 != libc::AF_PACKET {
+ return None;
+ }
+ Some(Self(ptr::read_unaligned(addr as *const _)))
+ }
+ }
+
+ impl AsRef<libc::sockaddr_ll> for LinkAddr {
+ fn as_ref(&self) -> &libc::sockaddr_ll {
+ &self.0
+ }
+ }
+
+ }
+}
+
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "illumos",
+ target_os = "netbsd",
+ target_os = "haiku",
+ target_os = "openbsd"
+))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+mod datalink {
+ feature! {
+ #![feature = "net"]
+ use super::{fmt, mem, private, ptr, SockaddrLike};
+
+ /// Hardware Address
+ #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+ #[repr(transparent)]
+ pub struct LinkAddr(pub(in super::super) libc::sockaddr_dl);
+
+ impl LinkAddr {
+ /// interface index, if != 0, system given index for interface
+ #[cfg(not(target_os = "haiku"))]
+ pub fn ifindex(&self) -> usize {
+ self.0.sdl_index as usize
+ }
+
+ /// Datalink type
+ #[cfg(not(target_os = "haiku"))]
+ pub fn datalink_type(&self) -> u8 {
+ self.0.sdl_type
+ }
+
+ /// MAC address start position
+ pub fn nlen(&self) -> usize {
+ self.0.sdl_nlen as usize
+ }
+
+ /// link level address length
+ pub fn alen(&self) -> usize {
+ self.0.sdl_alen as usize
+ }
+
+ /// link layer selector length
+ #[cfg(not(target_os = "haiku"))]
+ pub fn slen(&self) -> usize {
+ self.0.sdl_slen as usize
+ }
+
+ /// if link level address length == 0,
+ /// or `sdl_data` not be larger.
+ pub fn is_empty(&self) -> bool {
+ let nlen = self.nlen();
+ let alen = self.alen();
+ let data_len = self.0.sdl_data.len();
+
+ alen == 0 || nlen + alen >= data_len
+ }
+
+ /// Physical-layer address (MAC)
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ pub fn addr(&self) -> Option<[u8; 6]> {
+ let nlen = self.nlen();
+ let data = self.0.sdl_data;
+
+ if self.is_empty() {
+ None
+ } else {
+ Some([
+ data[nlen] as u8,
+ data[nlen + 1] as u8,
+ data[nlen + 2] as u8,
+ data[nlen + 3] as u8,
+ data[nlen + 4] as u8,
+ data[nlen + 5] as u8,
+ ])
+ }
+ }
+ }
+
+ impl fmt::Display for LinkAddr {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ if let Some(addr) = self.addr() {
+ write!(f, "{:02x}:{:02x}:{:02x}:{:02x}:{:02x}:{:02x}",
+ addr[0],
+ addr[1],
+ addr[2],
+ addr[3],
+ addr[4],
+ addr[5])
+ } else {
+ Ok(())
+ }
+ }
+ }
+ impl private::SockaddrLikePriv for LinkAddr {}
+ impl SockaddrLike for LinkAddr {
+ unsafe fn from_raw(addr: *const libc::sockaddr,
+ len: Option<libc::socklen_t>)
+ -> Option<Self> where Self: Sized
+ {
+ if let Some(l) = len {
+ if l != mem::size_of::<libc::sockaddr_dl>() as libc::socklen_t {
+ return None;
+ }
+ }
+ if (*addr).sa_family as i32 != libc::AF_LINK {
+ return None;
+ }
+ Some(Self(ptr::read_unaligned(addr as *const _)))
+ }
+ }
+
+ impl AsRef<libc::sockaddr_dl> for LinkAddr {
+ fn as_ref(&self) -> &libc::sockaddr_dl {
+ &self.0
+ }
+ }
+
+ }
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub mod vsock {
+ use super::*;
+ use crate::sys::socket::addr::AddressFamily;
+ use libc::{sa_family_t, sockaddr_vm};
+ use std::hash::{Hash, Hasher};
+ use std::{fmt, mem};
+
+ /// Socket address for VMWare VSockets protocol
+ ///
+ /// # References
+ ///
+ /// [vsock(7)](https://man7.org/linux/man-pages/man7/vsock.7.html)
+ #[derive(Copy, Clone)]
+ #[repr(transparent)]
+ pub struct VsockAddr(pub(in super::super) sockaddr_vm);
+
+ impl private::SockaddrLikePriv for VsockAddr {}
+ impl SockaddrLike for VsockAddr {
+ unsafe fn from_raw(
+ addr: *const libc::sockaddr,
+ len: Option<libc::socklen_t>,
+ ) -> Option<Self>
+ where
+ Self: Sized,
+ {
+ if let Some(l) = len {
+ if l != mem::size_of::<libc::sockaddr_vm>() as libc::socklen_t {
+ return None;
+ }
+ }
+ if (*addr).sa_family as i32 != libc::AF_VSOCK {
+ return None;
+ }
+ Some(Self(ptr::read_unaligned(addr as *const _)))
+ }
+ }
+
+ impl AsRef<libc::sockaddr_vm> for VsockAddr {
+ fn as_ref(&self) -> &libc::sockaddr_vm {
+ &self.0
+ }
+ }
+
+ impl PartialEq for VsockAddr {
+ fn eq(&self, other: &Self) -> bool {
+ let (inner, other) = (self.0, other.0);
+ (inner.svm_family, inner.svm_cid, inner.svm_port)
+ == (other.svm_family, other.svm_cid, other.svm_port)
+ }
+ }
+
+ impl Eq for VsockAddr {}
+
+ impl Hash for VsockAddr {
+ fn hash<H: Hasher>(&self, s: &mut H) {
+ let inner = self.0;
+ (inner.svm_family, inner.svm_cid, inner.svm_port).hash(s);
+ }
+ }
+
+ /// VSOCK Address
+ ///
+ /// The address for AF_VSOCK socket is defined as a combination of a
+ /// 32-bit Context Identifier (CID) and a 32-bit port number.
+ impl VsockAddr {
+ /// Construct a `VsockAddr` from its raw fields.
+ pub fn new(cid: u32, port: u32) -> VsockAddr {
+ let mut addr: sockaddr_vm = unsafe { mem::zeroed() };
+ addr.svm_family = AddressFamily::Vsock as sa_family_t;
+ addr.svm_cid = cid;
+ addr.svm_port = port;
+
+ VsockAddr(addr)
+ }
+
+ /// Context Identifier (CID)
+ pub fn cid(&self) -> u32 {
+ self.0.svm_cid
+ }
+
+ /// Port number
+ pub fn port(&self) -> u32 {
+ self.0.svm_port
+ }
+ }
+
+ impl fmt::Display for VsockAddr {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "cid: {} port: {}", self.cid(), self.port())
+ }
+ }
+
+ impl fmt::Debug for VsockAddr {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Display::fmt(self, f)
+ }
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ mod types {
+ use super::*;
+
+ #[test]
+ fn test_ipv4addr_to_libc() {
+ let s = std::net::Ipv4Addr::new(1, 2, 3, 4);
+ let l = ipv4addr_to_libc(s);
+ assert_eq!(l.s_addr, u32::to_be(0x01020304));
+ }
+
+ #[test]
+ fn test_ipv6addr_to_libc() {
+ let s = std::net::Ipv6Addr::new(1, 2, 3, 4, 5, 6, 7, 8);
+ let l = ipv6addr_to_libc(&s);
+ assert_eq!(
+ l.s6_addr,
+ [0, 1, 0, 2, 0, 3, 0, 4, 0, 5, 0, 6, 0, 7, 0, 8]
+ );
+ }
+ }
+
+ mod link {
+ #![allow(clippy::cast_ptr_alignment)]
+
+ #[cfg(any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "illumos"
+ ))]
+ use super::super::super::socklen_t;
+ use super::*;
+
+ /// Don't panic when trying to display an empty datalink address
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ))]
+ #[test]
+ fn test_datalink_display() {
+ use super::super::LinkAddr;
+ use std::mem;
+
+ let la = LinkAddr(libc::sockaddr_dl {
+ sdl_len: 56,
+ sdl_family: 18,
+ sdl_index: 5,
+ sdl_type: 24,
+ sdl_nlen: 3,
+ sdl_alen: 0,
+ sdl_slen: 0,
+ ..unsafe { mem::zeroed() }
+ });
+ format!("{}", la);
+ }
+
+ #[cfg(all(
+ any(
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "linux"
+ ),
+ target_endian = "little"
+ ))]
+ #[test]
+ fn linux_loopback() {
+ #[repr(align(2))]
+ struct Raw([u8; 20]);
+
+ let bytes = Raw([
+ 17u8, 0, 0, 0, 1, 0, 0, 0, 4, 3, 0, 6, 1, 2, 3, 4, 5, 6, 0, 0,
+ ]);
+ let sa = bytes.0.as_ptr() as *const libc::sockaddr;
+ let len = None;
+ let sock_addr =
+ unsafe { SockaddrStorage::from_raw(sa, len) }.unwrap();
+ assert_eq!(sock_addr.family(), Some(AddressFamily::Packet));
+ match sock_addr.as_link_addr() {
+ Some(dl) => assert_eq!(dl.addr(), Some([1, 2, 3, 4, 5, 6])),
+ None => panic!("Can't unwrap sockaddr storage"),
+ }
+ }
+
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[test]
+ fn macos_loopback() {
+ let bytes =
+ [20i8, 18, 1, 0, 24, 3, 0, 0, 108, 111, 48, 0, 0, 0, 0, 0];
+ let sa = bytes.as_ptr() as *const libc::sockaddr;
+ let len = Some(bytes.len() as socklen_t);
+ let sock_addr =
+ unsafe { SockaddrStorage::from_raw(sa, len) }.unwrap();
+ assert_eq!(sock_addr.family(), Some(AddressFamily::Link));
+ match sock_addr.as_link_addr() {
+ Some(dl) => {
+ assert!(dl.addr().is_none());
+ }
+ None => panic!("Can't unwrap sockaddr storage"),
+ }
+ }
+
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[test]
+ fn macos_tap() {
+ let bytes = [
+ 20i8, 18, 7, 0, 6, 3, 6, 0, 101, 110, 48, 24, 101, -112, -35,
+ 76, -80,
+ ];
+ let ptr = bytes.as_ptr();
+ let sa = ptr as *const libc::sockaddr;
+ let len = Some(bytes.len() as socklen_t);
+
+ let sock_addr =
+ unsafe { SockaddrStorage::from_raw(sa, len).unwrap() };
+ assert_eq!(sock_addr.family(), Some(AddressFamily::Link));
+ match sock_addr.as_link_addr() {
+ Some(dl) => {
+ assert_eq!(dl.addr(), Some([24u8, 101, 144, 221, 76, 176]))
+ }
+ None => panic!("Can't unwrap sockaddr storage"),
+ }
+ }
+
+ #[cfg(target_os = "illumos")]
+ #[test]
+ fn illumos_tap() {
+ let bytes = [25u8, 0, 0, 0, 6, 0, 6, 0, 24, 101, 144, 221, 76, 176];
+ let ptr = bytes.as_ptr();
+ let sa = ptr as *const libc::sockaddr;
+ let len = Some(bytes.len() as socklen_t);
+ let _sock_addr = unsafe { SockaddrStorage::from_raw(sa, len) };
+
+ assert!(_sock_addr.is_some());
+
+ let sock_addr = _sock_addr.unwrap();
+
+ assert_eq!(sock_addr.family().unwrap(), AddressFamily::Link);
+
+ assert_eq!(
+ sock_addr.as_link_addr().unwrap().addr(),
+ Some([24u8, 101, 144, 221, 76, 176])
+ );
+ }
+
+ #[test]
+ fn size() {
+ #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "illumos",
+ target_os = "openbsd",
+ target_os = "haiku"
+ ))]
+ let l = mem::size_of::<libc::sockaddr_dl>();
+ #[cfg(any(
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "linux"
+ ))]
+ let l = mem::size_of::<libc::sockaddr_ll>();
+ assert_eq!(LinkAddr::size() as usize, l);
+ }
+ }
+
+ mod sockaddr_in {
+ use super::*;
+ use std::str::FromStr;
+
+ #[test]
+ fn display() {
+ let s = "127.0.0.1:8080";
+ let addr = SockaddrIn::from_str(s).unwrap();
+ assert_eq!(s, format!("{}", addr));
+ }
+
+ #[test]
+ fn size() {
+ assert_eq!(
+ mem::size_of::<libc::sockaddr_in>(),
+ SockaddrIn::size() as usize
+ );
+ }
+ }
+
+ mod sockaddr_in6 {
+ use super::*;
+ use std::str::FromStr;
+
+ #[test]
+ fn display() {
+ let s = "[1234:5678:90ab:cdef::1111:2222]:8080";
+ let addr = SockaddrIn6::from_str(s).unwrap();
+ assert_eq!(s, format!("{}", addr));
+ }
+
+ #[test]
+ fn size() {
+ assert_eq!(
+ mem::size_of::<libc::sockaddr_in6>(),
+ SockaddrIn6::size() as usize
+ );
+ }
+
+ #[test]
+ // Ensure that we can convert to-and-from std::net variants without change.
+ fn to_and_from() {
+ let s = "[1234:5678:90ab:cdef::1111:2222]:8080";
+ let mut nix_sin6 = SockaddrIn6::from_str(s).unwrap();
+ nix_sin6.0.sin6_flowinfo = 0x12345678;
+ nix_sin6.0.sin6_scope_id = 0x9abcdef0;
+
+ let std_sin6 : std::net::SocketAddrV6 = nix_sin6.into();
+ assert_eq!(nix_sin6, std_sin6.into());
+ }
+ }
+
+ mod sockaddr_storage {
+ use super::*;
+
+ #[test]
+ fn from_sockaddr_un_named() {
+ let ua = UnixAddr::new("/var/run/mysock").unwrap();
+ let ptr = ua.as_ptr() as *const libc::sockaddr;
+ let ss = unsafe {
+ SockaddrStorage::from_raw(ptr, Some(ua.len()))
+ }.unwrap();
+ assert_eq!(ss.len(), ua.len());
+ }
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[test]
+ fn from_sockaddr_un_abstract_named() {
+ let name = String::from("nix\0abstract\0test");
+ let ua = UnixAddr::new_abstract(name.as_bytes()).unwrap();
+ let ptr = ua.as_ptr() as *const libc::sockaddr;
+ let ss = unsafe {
+ SockaddrStorage::from_raw(ptr, Some(ua.len()))
+ }.unwrap();
+ assert_eq!(ss.len(), ua.len());
+ }
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[test]
+ fn from_sockaddr_un_abstract_unnamed() {
+ let ua = UnixAddr::new_unnamed();
+ let ptr = ua.as_ptr() as *const libc::sockaddr;
+ let ss = unsafe {
+ SockaddrStorage::from_raw(ptr, Some(ua.len()))
+ }.unwrap();
+ assert_eq!(ss.len(), ua.len());
+ }
+ }
+
+ mod unixaddr {
+ use super::*;
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[test]
+ fn abstract_sun_path() {
+ let name = String::from("nix\0abstract\0test");
+ let addr = UnixAddr::new_abstract(name.as_bytes()).unwrap();
+
+ let sun_path1 =
+ unsafe { &(*addr.as_ptr()).sun_path[..addr.path_len()] };
+ let sun_path2 = [
+ 0, 110, 105, 120, 0, 97, 98, 115, 116, 114, 97, 99, 116, 0,
+ 116, 101, 115, 116,
+ ];
+ assert_eq!(sun_path1, sun_path2);
+ }
+
+ #[test]
+ fn size() {
+ assert_eq!(
+ mem::size_of::<libc::sockaddr_un>(),
+ UnixAddr::size() as usize
+ );
+ }
+ }
+}
diff --git a/third_party/rust/nix/src/sys/socket/mod.rs b/third_party/rust/nix/src/sys/socket/mod.rs
new file mode 100644
index 0000000000..8513b6fbe7
--- /dev/null
+++ b/third_party/rust/nix/src/sys/socket/mod.rs
@@ -0,0 +1,2487 @@
+//! Socket interface functions
+//!
+//! [Further reading](https://man7.org/linux/man-pages/man7/socket.7.html)
+#[cfg(target_os = "linux")]
+#[cfg(feature = "uio")]
+use crate::sys::time::TimeSpec;
+#[cfg(feature = "uio")]
+use crate::sys::time::TimeVal;
+use crate::{errno::Errno, Result};
+use cfg_if::cfg_if;
+use libc::{
+ self, c_int, c_void, iovec, size_t, socklen_t, CMSG_DATA, CMSG_FIRSTHDR,
+ CMSG_LEN, CMSG_NXTHDR,
+};
+use std::convert::{TryFrom, TryInto};
+use std::io::{IoSlice, IoSliceMut};
+#[cfg(feature = "net")]
+use std::net;
+use std::os::unix::io::RawFd;
+use std::{mem, ptr, slice};
+
+#[deny(missing_docs)]
+mod addr;
+#[deny(missing_docs)]
+pub mod sockopt;
+
+/*
+ *
+ * ===== Re-exports =====
+ *
+ */
+
+pub use self::addr::{SockaddrLike, SockaddrStorage};
+
+#[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
+#[allow(deprecated)]
+pub use self::addr::{AddressFamily, SockAddr, UnixAddr};
+#[cfg(any(target_os = "illumos", target_os = "solaris"))]
+#[allow(deprecated)]
+pub use self::addr::{AddressFamily, SockAddr, UnixAddr};
+#[allow(deprecated)]
+#[cfg(not(any(
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "haiku"
+)))]
+#[cfg(feature = "net")]
+pub use self::addr::{
+ InetAddr, IpAddr, Ipv4Addr, Ipv6Addr, LinkAddr, SockaddrIn, SockaddrIn6,
+};
+#[allow(deprecated)]
+#[cfg(any(
+ target_os = "illumos",
+ target_os = "solaris",
+ target_os = "haiku"
+))]
+#[cfg(feature = "net")]
+pub use self::addr::{
+ InetAddr, IpAddr, Ipv4Addr, Ipv6Addr, SockaddrIn, SockaddrIn6,
+};
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+pub use crate::sys::socket::addr::alg::AlgAddr;
+#[cfg(any(target_os = "android", target_os = "linux"))]
+pub use crate::sys::socket::addr::netlink::NetlinkAddr;
+#[cfg(any(target_os = "ios", target_os = "macos"))]
+#[cfg(feature = "ioctl")]
+pub use crate::sys::socket::addr::sys_control::SysControlAddr;
+#[cfg(any(target_os = "android", target_os = "linux"))]
+pub use crate::sys::socket::addr::vsock::VsockAddr;
+
+#[cfg(feature = "uio")]
+pub use libc::{cmsghdr, msghdr};
+pub use libc::{sa_family_t, sockaddr, sockaddr_storage, sockaddr_un};
+#[cfg(feature = "net")]
+pub use libc::{sockaddr_in, sockaddr_in6};
+
+// Needed by the cmsg_space macro
+#[doc(hidden)]
+pub use libc::{c_uint, CMSG_SPACE};
+
+#[cfg(feature = "net")]
+use crate::sys::socket::addr::{ipv4addr_to_libc, ipv6addr_to_libc};
+
+/// These constants are used to specify the communication semantics
+/// when creating a socket with [`socket()`](fn.socket.html)
+#[derive(Clone, Copy, PartialEq, Eq, Debug)]
+#[repr(i32)]
+#[non_exhaustive]
+pub enum SockType {
+ /// Provides sequenced, reliable, two-way, connection-
+ /// based byte streams. An out-of-band data transmission
+ /// mechanism may be supported.
+ Stream = libc::SOCK_STREAM,
+ /// Supports datagrams (connectionless, unreliable
+ /// messages of a fixed maximum length).
+ Datagram = libc::SOCK_DGRAM,
+ /// Provides a sequenced, reliable, two-way connection-
+ /// based data transmission path for datagrams of fixed
+ /// maximum length; a consumer is required to read an
+ /// entire packet with each input system call.
+ SeqPacket = libc::SOCK_SEQPACKET,
+ /// Provides raw network protocol access.
+ Raw = libc::SOCK_RAW,
+ /// Provides a reliable datagram layer that does not
+ /// guarantee ordering.
+ #[cfg(not(any(target_os = "haiku")))]
+ Rdm = libc::SOCK_RDM,
+}
+// The TryFrom impl could've been derived using libc_enum!. But for
+// backwards-compatibility with Nix-0.25.0 we manually implement it, so as to
+// keep the old variant names.
+impl TryFrom<i32> for SockType {
+ type Error = crate::Error;
+
+ fn try_from(x: i32) -> Result<Self> {
+ match x {
+ libc::SOCK_STREAM => Ok(Self::Stream),
+ libc::SOCK_DGRAM => Ok(Self::Datagram),
+ libc::SOCK_SEQPACKET => Ok(Self::SeqPacket),
+ libc::SOCK_RAW => Ok(Self::Raw),
+ #[cfg(not(any(target_os = "haiku")))]
+ libc::SOCK_RDM => Ok(Self::Rdm),
+ _ => Err(Errno::EINVAL)
+ }
+ }
+}
+
+/// Constants used in [`socket`](fn.socket.html) and [`socketpair`](fn.socketpair.html)
+/// to specify the protocol to use.
+#[repr(i32)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[non_exhaustive]
+pub enum SockProtocol {
+ /// TCP protocol ([ip(7)](https://man7.org/linux/man-pages/man7/ip.7.html))
+ Tcp = libc::IPPROTO_TCP,
+ /// UDP protocol ([ip(7)](https://man7.org/linux/man-pages/man7/ip.7.html))
+ Udp = libc::IPPROTO_UDP,
+ /// Raw sockets ([raw(7)](https://man7.org/linux/man-pages/man7/raw.7.html))
+ Raw = libc::IPPROTO_RAW,
+ /// Allows applications and other KEXTs to be notified when certain kernel events occur
+ /// ([ref](https://developer.apple.com/library/content/documentation/Darwin/Conceptual/NKEConceptual/control/control.html))
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ KextEvent = libc::SYSPROTO_EVENT,
+ /// Allows applications to configure and control a KEXT
+ /// ([ref](https://developer.apple.com/library/content/documentation/Darwin/Conceptual/NKEConceptual/control/control.html))
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ KextControl = libc::SYSPROTO_CONTROL,
+ /// Receives routing and link updates and may be used to modify the routing tables (both IPv4 and IPv6), IP addresses, link
+ // parameters, neighbor setups, queueing disciplines, traffic classes and packet classifiers
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkRoute = libc::NETLINK_ROUTE,
+ /// Reserved for user-mode socket protocols
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkUserSock = libc::NETLINK_USERSOCK,
+ /// Query information about sockets of various protocol families from the kernel
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkSockDiag = libc::NETLINK_SOCK_DIAG,
+ /// SELinux event notifications.
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkSELinux = libc::NETLINK_SELINUX,
+ /// Open-iSCSI
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkISCSI = libc::NETLINK_ISCSI,
+ /// Auditing
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkAudit = libc::NETLINK_AUDIT,
+ /// Access to FIB lookup from user space
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkFIBLookup = libc::NETLINK_FIB_LOOKUP,
+ /// Netfilter subsystem
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkNetFilter = libc::NETLINK_NETFILTER,
+ /// SCSI Transports
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkSCSITransport = libc::NETLINK_SCSITRANSPORT,
+ /// Infiniband RDMA
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkRDMA = libc::NETLINK_RDMA,
+ /// Transport IPv6 packets from netfilter to user space. Used by ip6_queue kernel module.
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkIPv6Firewall = libc::NETLINK_IP6_FW,
+ /// DECnet routing messages
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkDECNetRoutingMessage = libc::NETLINK_DNRTMSG,
+ /// Kernel messages to user space
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkKObjectUEvent = libc::NETLINK_KOBJECT_UEVENT,
+ /// Netlink interface to request information about ciphers registered with the kernel crypto API as well as allow
+ /// configuration of the kernel crypto API.
+ /// ([ref](https://www.man7.org/linux/man-pages/man7/netlink.7.html))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NetlinkCrypto = libc::NETLINK_CRYPTO,
+ /// Non-DIX type protocol number defined for the Ethernet IEEE 802.3 interface that allows packets of all protocols
+ /// defined in the interface to be received.
+ /// ([ref](https://man7.org/linux/man-pages/man7/packet.7.html))
+ // The protocol number is fed into the socket syscall in network byte order.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ EthAll = libc::ETH_P_ALL.to_be(),
+}
+
+#[cfg(any(target_os = "linux"))]
+libc_bitflags! {
+ /// Configuration flags for `SO_TIMESTAMPING` interface
+ ///
+ /// For use with [`Timestamping`][sockopt::Timestamping].
+ /// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
+ pub struct TimestampingFlag: c_uint {
+ /// Report any software timestamps when available.
+ SOF_TIMESTAMPING_SOFTWARE;
+ /// Report hardware timestamps as generated by SOF_TIMESTAMPING_TX_HARDWARE when available.
+ SOF_TIMESTAMPING_RAW_HARDWARE;
+ /// Collect transmiting timestamps as reported by hardware
+ SOF_TIMESTAMPING_TX_HARDWARE;
+ /// Collect transmiting timestamps as reported by software
+ SOF_TIMESTAMPING_TX_SOFTWARE;
+ /// Collect receiving timestamps as reported by hardware
+ SOF_TIMESTAMPING_RX_HARDWARE;
+ /// Collect receiving timestamps as reported by software
+ SOF_TIMESTAMPING_RX_SOFTWARE;
+ }
+}
+
+libc_bitflags! {
+ /// Additional socket options
+ pub struct SockFlag: c_int {
+ /// Set non-blocking mode on the new socket
+ #[cfg(any(target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ SOCK_NONBLOCK;
+ /// Set close-on-exec on the new descriptor
+ #[cfg(any(target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ SOCK_CLOEXEC;
+ /// Return `EPIPE` instead of raising `SIGPIPE`
+ #[cfg(target_os = "netbsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ SOCK_NOSIGPIPE;
+ /// For domains `AF_INET(6)`, only allow `connect(2)`, `sendto(2)`, or `sendmsg(2)`
+ /// to the DNS port (typically 53)
+ #[cfg(target_os = "openbsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ SOCK_DNS;
+ }
+}
+
+libc_bitflags! {
+ /// Flags for send/recv and their relatives
+ pub struct MsgFlags: c_int {
+ /// Sends or requests out-of-band data on sockets that support this notion
+ /// (e.g., of type [`Stream`](enum.SockType.html)); the underlying protocol must also
+ /// support out-of-band data.
+ #[allow(deprecated)] // Suppress useless warnings from libc PR 2963
+ MSG_OOB;
+ /// Peeks at an incoming message. The data is treated as unread and the next
+ /// [`recv()`](fn.recv.html)
+ /// or similar function shall still return this data.
+ #[allow(deprecated)] // Suppress useless warnings from libc PR 2963
+ MSG_PEEK;
+ /// Receive operation blocks until the full amount of data can be
+ /// returned. The function may return smaller amount of data if a signal
+ /// is caught, an error or disconnect occurs.
+ #[allow(deprecated)] // Suppress useless warnings from libc PR 2963
+ MSG_WAITALL;
+ /// Enables nonblocking operation; if the operation would block,
+ /// `EAGAIN` or `EWOULDBLOCK` is returned. This provides similar
+ /// behavior to setting the `O_NONBLOCK` flag
+ /// (via the [`fcntl`](../../fcntl/fn.fcntl.html)
+ /// `F_SETFL` operation), but differs in that `MSG_DONTWAIT` is a per-
+ /// call option, whereas `O_NONBLOCK` is a setting on the open file
+ /// description (see [open(2)](https://man7.org/linux/man-pages/man2/open.2.html)),
+ /// which will affect all threads in
+ /// the calling process and as well as other processes that hold
+ /// file descriptors referring to the same open file description.
+ #[allow(deprecated)] // Suppress useless warnings from libc PR 2963
+ MSG_DONTWAIT;
+ /// Receive flags: Control Data was discarded (buffer too small)
+ #[allow(deprecated)] // Suppress useless warnings from libc PR 2963
+ MSG_CTRUNC;
+ /// For raw ([`Packet`](addr/enum.AddressFamily.html)), Internet datagram
+ /// (since Linux 2.4.27/2.6.8),
+ /// netlink (since Linux 2.6.22) and UNIX datagram (since Linux 3.4)
+ /// sockets: return the real length of the packet or datagram, even
+ /// when it was longer than the passed buffer. Not implemented for UNIX
+ /// domain ([unix(7)](https://linux.die.net/man/7/unix)) sockets.
+ ///
+ /// For use with Internet stream sockets, see [tcp(7)](https://linux.die.net/man/7/tcp).
+ #[allow(deprecated)] // Suppress useless warnings from libc PR 2963
+ MSG_TRUNC;
+ /// Terminates a record (when this notion is supported, as for
+ /// sockets of type [`SeqPacket`](enum.SockType.html)).
+ #[allow(deprecated)] // Suppress useless warnings from libc PR 2963
+ MSG_EOR;
+ /// This flag specifies that queued errors should be received from
+ /// the socket error queue. (For more details, see
+ /// [recvfrom(2)](https://linux.die.net/man/2/recvfrom))
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ #[allow(deprecated)] // Suppress useless warnings from libc PR 2963
+ MSG_ERRQUEUE;
+ /// Set the `close-on-exec` flag for the file descriptor received via a UNIX domain
+ /// file descriptor using the `SCM_RIGHTS` operation (described in
+ /// [unix(7)](https://linux.die.net/man/7/unix)).
+ /// This flag is useful for the same reasons as the `O_CLOEXEC` flag of
+ /// [open(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/open.html).
+ ///
+ /// Only used in [`recvmsg`](fn.recvmsg.html) function.
+ #[cfg(any(target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ #[allow(deprecated)] // Suppress useless warnings from libc PR 2963
+ MSG_CMSG_CLOEXEC;
+ /// Requests not to send `SIGPIPE` errors when the other end breaks the connection.
+ /// (For more details, see [send(2)](https://linux.die.net/man/2/send)).
+ #[cfg(any(target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ #[allow(deprecated)] // Suppress useless warnings from libc PR 2963
+ MSG_NOSIGNAL;
+ }
+}
+
+cfg_if! {
+ if #[cfg(any(target_os = "android", target_os = "linux"))] {
+ /// Unix credentials of the sending process.
+ ///
+ /// This struct is used with the `SO_PEERCRED` ancillary message
+ /// and the `SCM_CREDENTIALS` control message for UNIX sockets.
+ #[repr(transparent)]
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ pub struct UnixCredentials(libc::ucred);
+
+ impl UnixCredentials {
+ /// Creates a new instance with the credentials of the current process
+ pub fn new() -> Self {
+ // Safe because these FFI functions are inherently safe
+ unsafe {
+ UnixCredentials(libc::ucred {
+ pid: libc::getpid(),
+ uid: libc::getuid(),
+ gid: libc::getgid()
+ })
+ }
+ }
+
+ /// Returns the process identifier
+ pub fn pid(&self) -> libc::pid_t {
+ self.0.pid
+ }
+
+ /// Returns the user identifier
+ pub fn uid(&self) -> libc::uid_t {
+ self.0.uid
+ }
+
+ /// Returns the group identifier
+ pub fn gid(&self) -> libc::gid_t {
+ self.0.gid
+ }
+ }
+
+ impl Default for UnixCredentials {
+ fn default() -> Self {
+ Self::new()
+ }
+ }
+
+ impl From<libc::ucred> for UnixCredentials {
+ fn from(cred: libc::ucred) -> Self {
+ UnixCredentials(cred)
+ }
+ }
+
+ impl From<UnixCredentials> for libc::ucred {
+ fn from(uc: UnixCredentials) -> Self {
+ uc.0
+ }
+ }
+ } else if #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))] {
+ /// Unix credentials of the sending process.
+ ///
+ /// This struct is used with the `SCM_CREDS` ancillary message for UNIX sockets.
+ #[repr(transparent)]
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ pub struct UnixCredentials(libc::cmsgcred);
+
+ impl UnixCredentials {
+ /// Returns the process identifier
+ pub fn pid(&self) -> libc::pid_t {
+ self.0.cmcred_pid
+ }
+
+ /// Returns the real user identifier
+ pub fn uid(&self) -> libc::uid_t {
+ self.0.cmcred_uid
+ }
+
+ /// Returns the effective user identifier
+ pub fn euid(&self) -> libc::uid_t {
+ self.0.cmcred_euid
+ }
+
+ /// Returns the real group identifier
+ pub fn gid(&self) -> libc::gid_t {
+ self.0.cmcred_gid
+ }
+
+ /// Returns a list group identifiers (the first one being the effective GID)
+ pub fn groups(&self) -> &[libc::gid_t] {
+ unsafe {
+ slice::from_raw_parts(
+ self.0.cmcred_groups.as_ptr() as *const libc::gid_t,
+ self.0.cmcred_ngroups as _
+ )
+ }
+ }
+ }
+
+ impl From<libc::cmsgcred> for UnixCredentials {
+ fn from(cred: libc::cmsgcred) -> Self {
+ UnixCredentials(cred)
+ }
+ }
+ }
+}
+
+cfg_if! {
+ if #[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "ios"
+ ))] {
+ /// Return type of [`LocalPeerCred`](crate::sys::socket::sockopt::LocalPeerCred)
+ #[repr(transparent)]
+ #[derive(Clone, Copy, Debug, Eq, PartialEq)]
+ pub struct XuCred(libc::xucred);
+
+ impl XuCred {
+ /// Structure layout version
+ pub fn version(&self) -> u32 {
+ self.0.cr_version
+ }
+
+ /// Effective user ID
+ pub fn uid(&self) -> libc::uid_t {
+ self.0.cr_uid
+ }
+
+ /// Returns a list of group identifiers (the first one being the
+ /// effective GID)
+ pub fn groups(&self) -> &[libc::gid_t] {
+ &self.0.cr_groups
+ }
+ }
+ }
+}
+
+feature! {
+#![feature = "net"]
+/// Request for multicast socket operations
+///
+/// This is a wrapper type around `ip_mreq`.
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub struct IpMembershipRequest(libc::ip_mreq);
+
+impl IpMembershipRequest {
+ /// Instantiate a new `IpMembershipRequest`
+ ///
+ /// If `interface` is `None`, then `Ipv4Addr::any()` will be used for the interface.
+ pub fn new(group: net::Ipv4Addr, interface: Option<net::Ipv4Addr>)
+ -> Self
+ {
+ let imr_addr = match interface {
+ None => net::Ipv4Addr::UNSPECIFIED,
+ Some(addr) => addr
+ };
+ IpMembershipRequest(libc::ip_mreq {
+ imr_multiaddr: ipv4addr_to_libc(group),
+ imr_interface: ipv4addr_to_libc(imr_addr)
+ })
+ }
+}
+
+/// Request for ipv6 multicast socket operations
+///
+/// This is a wrapper type around `ipv6_mreq`.
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub struct Ipv6MembershipRequest(libc::ipv6_mreq);
+
+impl Ipv6MembershipRequest {
+ /// Instantiate a new `Ipv6MembershipRequest`
+ pub const fn new(group: net::Ipv6Addr) -> Self {
+ Ipv6MembershipRequest(libc::ipv6_mreq {
+ ipv6mr_multiaddr: ipv6addr_to_libc(&group),
+ ipv6mr_interface: 0,
+ })
+ }
+}
+}
+
+feature! {
+#![feature = "uio"]
+
+/// Create a buffer large enough for storing some control messages as returned
+/// by [`recvmsg`](fn.recvmsg.html).
+///
+/// # Examples
+///
+/// ```
+/// # #[macro_use] extern crate nix;
+/// # use nix::sys::time::TimeVal;
+/// # use std::os::unix::io::RawFd;
+/// # fn main() {
+/// // Create a buffer for a `ControlMessageOwned::ScmTimestamp` message
+/// let _ = cmsg_space!(TimeVal);
+/// // Create a buffer big enough for a `ControlMessageOwned::ScmRights` message
+/// // with two file descriptors
+/// let _ = cmsg_space!([RawFd; 2]);
+/// // Create a buffer big enough for a `ControlMessageOwned::ScmRights` message
+/// // and a `ControlMessageOwned::ScmTimestamp` message
+/// let _ = cmsg_space!(RawFd, TimeVal);
+/// # }
+/// ```
+// Unfortunately, CMSG_SPACE isn't a const_fn, or else we could return a
+// stack-allocated array.
+#[macro_export]
+macro_rules! cmsg_space {
+ ( $( $x:ty ),* ) => {
+ {
+ let mut space = 0;
+ $(
+ // CMSG_SPACE is always safe
+ space += unsafe {
+ $crate::sys::socket::CMSG_SPACE(::std::mem::size_of::<$x>() as $crate::sys::socket::c_uint)
+ } as usize;
+ )*
+ Vec::<u8>::with_capacity(space)
+ }
+ }
+}
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+/// Contains outcome of sending or receiving a message
+///
+/// Use [`cmsgs`][RecvMsg::cmsgs] to access all the control messages present, and
+/// [`iovs`][RecvMsg::iovs`] to access underlying io slices.
+pub struct RecvMsg<'a, 's, S> {
+ pub bytes: usize,
+ cmsghdr: Option<&'a cmsghdr>,
+ pub address: Option<S>,
+ pub flags: MsgFlags,
+ iobufs: std::marker::PhantomData<& 's()>,
+ mhdr: msghdr,
+}
+
+impl<'a, S> RecvMsg<'a, '_, S> {
+ /// Iterate over the valid control messages pointed to by this
+ /// msghdr.
+ pub fn cmsgs(&self) -> CmsgIterator {
+ CmsgIterator {
+ cmsghdr: self.cmsghdr,
+ mhdr: &self.mhdr
+ }
+ }
+}
+
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+pub struct CmsgIterator<'a> {
+ /// Control message buffer to decode from. Must adhere to cmsg alignment.
+ cmsghdr: Option<&'a cmsghdr>,
+ mhdr: &'a msghdr
+}
+
+impl<'a> Iterator for CmsgIterator<'a> {
+ type Item = ControlMessageOwned;
+
+ fn next(&mut self) -> Option<ControlMessageOwned> {
+ match self.cmsghdr {
+ None => None, // No more messages
+ Some(hdr) => {
+ // Get the data.
+ // Safe if cmsghdr points to valid data returned by recvmsg(2)
+ let cm = unsafe { Some(ControlMessageOwned::decode_from(hdr))};
+ // Advance the internal pointer. Safe if mhdr and cmsghdr point
+ // to valid data returned by recvmsg(2)
+ self.cmsghdr = unsafe {
+ let p = CMSG_NXTHDR(self.mhdr as *const _, hdr as *const _);
+ p.as_ref()
+ };
+ cm
+ }
+ }
+ }
+}
+
+/// A type-safe wrapper around a single control message, as used with
+/// [`recvmsg`](#fn.recvmsg).
+///
+/// [Further reading](https://man7.org/linux/man-pages/man3/cmsg.3.html)
+// Nix version 0.13.0 and earlier used ControlMessage for both recvmsg and
+// sendmsg. However, on some platforms the messages returned by recvmsg may be
+// unaligned. ControlMessageOwned takes those messages by copy, obviating any
+// alignment issues.
+//
+// See https://github.com/nix-rust/nix/issues/999
+#[derive(Clone, Debug, Eq, PartialEq)]
+#[non_exhaustive]
+pub enum ControlMessageOwned {
+ /// Received version of [`ControlMessage::ScmRights`]
+ ScmRights(Vec<RawFd>),
+ /// Received version of [`ControlMessage::ScmCredentials`]
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ScmCredentials(UnixCredentials),
+ /// Received version of [`ControlMessage::ScmCreds`]
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ScmCreds(UnixCredentials),
+ /// A message of type `SCM_TIMESTAMP`, containing the time the
+ /// packet was received by the kernel.
+ ///
+ /// See the kernel's explanation in "SO_TIMESTAMP" of
+ /// [networking/timestamping](https://www.kernel.org/doc/Documentation/networking/timestamping.txt).
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// # #[macro_use] extern crate nix;
+ /// # use nix::sys::socket::*;
+ /// # use nix::sys::time::*;
+ /// # use std::io::{IoSlice, IoSliceMut};
+ /// # use std::time::*;
+ /// # use std::str::FromStr;
+ /// # fn main() {
+ /// // Set up
+ /// let message = "Ohayō!".as_bytes();
+ /// let in_socket = socket(
+ /// AddressFamily::Inet,
+ /// SockType::Datagram,
+ /// SockFlag::empty(),
+ /// None).unwrap();
+ /// setsockopt(in_socket, sockopt::ReceiveTimestamp, &true).unwrap();
+ /// let localhost = SockaddrIn::from_str("127.0.0.1:0").unwrap();
+ /// bind(in_socket, &localhost).unwrap();
+ /// let address: SockaddrIn = getsockname(in_socket).unwrap();
+ /// // Get initial time
+ /// let time0 = SystemTime::now();
+ /// // Send the message
+ /// let iov = [IoSlice::new(message)];
+ /// let flags = MsgFlags::empty();
+ /// let l = sendmsg(in_socket, &iov, &[], flags, Some(&address)).unwrap();
+ /// assert_eq!(message.len(), l);
+ /// // Receive the message
+ /// let mut buffer = vec![0u8; message.len()];
+ /// let mut cmsgspace = cmsg_space!(TimeVal);
+ /// let mut iov = [IoSliceMut::new(&mut buffer)];
+ /// let r = recvmsg::<SockaddrIn>(in_socket, &mut iov, Some(&mut cmsgspace), flags)
+ /// .unwrap();
+ /// let rtime = match r.cmsgs().next() {
+ /// Some(ControlMessageOwned::ScmTimestamp(rtime)) => rtime,
+ /// Some(_) => panic!("Unexpected control message"),
+ /// None => panic!("No control message")
+ /// };
+ /// // Check the final time
+ /// let time1 = SystemTime::now();
+ /// // the packet's received timestamp should lie in-between the two system
+ /// // times, unless the system clock was adjusted in the meantime.
+ /// let rduration = Duration::new(rtime.tv_sec() as u64,
+ /// rtime.tv_usec() as u32 * 1000);
+ /// assert!(time0.duration_since(UNIX_EPOCH).unwrap() <= rduration);
+ /// assert!(rduration <= time1.duration_since(UNIX_EPOCH).unwrap());
+ /// // Close socket
+ /// nix::unistd::close(in_socket).unwrap();
+ /// # }
+ /// ```
+ ScmTimestamp(TimeVal),
+ /// A set of nanosecond resolution timestamps
+ ///
+ /// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
+ #[cfg(all(target_os = "linux"))]
+ ScmTimestampsns(Timestamps),
+ /// Nanoseconds resolution timestamp
+ ///
+ /// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
+ #[cfg(all(target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ScmTimestampns(TimeSpec),
+ #[cfg(any(
+ target_os = "android",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ ))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv4PacketInfo(libc::in_pktinfo),
+ #[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ ))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv6PacketInfo(libc::in6_pktinfo),
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv4RecvIf(libc::sockaddr_dl),
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv4RecvDstAddr(libc::in_addr),
+ #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv4OrigDstAddr(libc::sockaddr_in),
+ #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv6OrigDstAddr(libc::sockaddr_in6),
+
+ /// UDP Generic Receive Offload (GRO) allows receiving multiple UDP
+ /// packets from a single sender.
+ /// Fixed-size payloads are following one by one in a receive buffer.
+ /// This Control Message indicates the size of all smaller packets,
+ /// except, maybe, the last one.
+ ///
+ /// `UdpGroSegment` socket option should be enabled on a socket
+ /// to allow receiving GRO packets.
+ #[cfg(target_os = "linux")]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ UdpGroSegments(u16),
+
+ /// SO_RXQ_OVFL indicates that an unsigned 32 bit value
+ /// ancilliary msg (cmsg) should be attached to recieved
+ /// skbs indicating the number of packets dropped by the
+ /// socket between the last recieved packet and this
+ /// received packet.
+ ///
+ /// `RxqOvfl` socket option should be enabled on a socket
+ /// to allow receiving the drop counter.
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ RxqOvfl(u32),
+
+ /// Socket error queue control messages read with the `MSG_ERRQUEUE` flag.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv4RecvErr(libc::sock_extended_err, Option<sockaddr_in>),
+ /// Socket error queue control messages read with the `MSG_ERRQUEUE` flag.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv6RecvErr(libc::sock_extended_err, Option<sockaddr_in6>),
+
+ /// Catch-all variant for unimplemented cmsg types.
+ #[doc(hidden)]
+ Unknown(UnknownCmsg),
+}
+
+/// For representing packet timestamps via `SO_TIMESTAMPING` interface
+#[cfg(all(target_os = "linux"))]
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub struct Timestamps {
+ /// software based timestamp, usually one containing data
+ pub system: TimeSpec,
+ /// legacy timestamp, usually empty
+ pub hw_trans: TimeSpec,
+ /// hardware based timestamp
+ pub hw_raw: TimeSpec,
+}
+
+impl ControlMessageOwned {
+ /// Decodes a `ControlMessageOwned` from raw bytes.
+ ///
+ /// This is only safe to call if the data is correct for the message type
+ /// specified in the header. Normally, the kernel ensures that this is the
+ /// case. "Correct" in this case includes correct length, alignment and
+ /// actual content.
+ // Clippy complains about the pointer alignment of `p`, not understanding
+ // that it's being fed to a function that can handle that.
+ #[allow(clippy::cast_ptr_alignment)]
+ unsafe fn decode_from(header: &cmsghdr) -> ControlMessageOwned
+ {
+ let p = CMSG_DATA(header);
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ let len = header as *const _ as usize + header.cmsg_len as usize
+ - p as usize;
+ match (header.cmsg_level, header.cmsg_type) {
+ (libc::SOL_SOCKET, libc::SCM_RIGHTS) => {
+ let n = len / mem::size_of::<RawFd>();
+ let mut fds = Vec::with_capacity(n);
+ for i in 0..n {
+ let fdp = (p as *const RawFd).add(i);
+ fds.push(ptr::read_unaligned(fdp));
+ }
+ ControlMessageOwned::ScmRights(fds)
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ (libc::SOL_SOCKET, libc::SCM_CREDENTIALS) => {
+ let cred: libc::ucred = ptr::read_unaligned(p as *const _);
+ ControlMessageOwned::ScmCredentials(cred.into())
+ }
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ (libc::SOL_SOCKET, libc::SCM_CREDS) => {
+ let cred: libc::cmsgcred = ptr::read_unaligned(p as *const _);
+ ControlMessageOwned::ScmCreds(cred.into())
+ }
+ #[cfg(not(target_os = "haiku"))]
+ (libc::SOL_SOCKET, libc::SCM_TIMESTAMP) => {
+ let tv: libc::timeval = ptr::read_unaligned(p as *const _);
+ ControlMessageOwned::ScmTimestamp(TimeVal::from(tv))
+ },
+ #[cfg(all(target_os = "linux"))]
+ (libc::SOL_SOCKET, libc::SCM_TIMESTAMPNS) => {
+ let ts: libc::timespec = ptr::read_unaligned(p as *const _);
+ ControlMessageOwned::ScmTimestampns(TimeSpec::from(ts))
+ }
+ #[cfg(all(target_os = "linux"))]
+ (libc::SOL_SOCKET, libc::SCM_TIMESTAMPING) => {
+ let tp = p as *const libc::timespec;
+ let ts: libc::timespec = ptr::read_unaligned(tp);
+ let system = TimeSpec::from(ts);
+ let ts: libc::timespec = ptr::read_unaligned(tp.add(1));
+ let hw_trans = TimeSpec::from(ts);
+ let ts: libc::timespec = ptr::read_unaligned(tp.add(2));
+ let hw_raw = TimeSpec::from(ts);
+ let timestamping = Timestamps { system, hw_trans, hw_raw };
+ ControlMessageOwned::ScmTimestampsns(timestamping)
+ }
+ #[cfg(any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"
+ ))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IPV6, libc::IPV6_PKTINFO) => {
+ let info = ptr::read_unaligned(p as *const libc::in6_pktinfo);
+ ControlMessageOwned::Ipv6PacketInfo(info)
+ }
+ #[cfg(any(
+ target_os = "android",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ ))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IP, libc::IP_PKTINFO) => {
+ let info = ptr::read_unaligned(p as *const libc::in_pktinfo);
+ ControlMessageOwned::Ipv4PacketInfo(info)
+ }
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IP, libc::IP_RECVIF) => {
+ let dl = ptr::read_unaligned(p as *const libc::sockaddr_dl);
+ ControlMessageOwned::Ipv4RecvIf(dl)
+ },
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ ))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IP, libc::IP_RECVDSTADDR) => {
+ let dl = ptr::read_unaligned(p as *const libc::in_addr);
+ ControlMessageOwned::Ipv4RecvDstAddr(dl)
+ },
+ #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IP, libc::IP_ORIGDSTADDR) => {
+ let dl = ptr::read_unaligned(p as *const libc::sockaddr_in);
+ ControlMessageOwned::Ipv4OrigDstAddr(dl)
+ },
+ #[cfg(target_os = "linux")]
+ #[cfg(feature = "net")]
+ (libc::SOL_UDP, libc::UDP_GRO) => {
+ let gso_size: u16 = ptr::read_unaligned(p as *const _);
+ ControlMessageOwned::UdpGroSegments(gso_size)
+ },
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ (libc::SOL_SOCKET, libc::SO_RXQ_OVFL) => {
+ let drop_counter = ptr::read_unaligned(p as *const u32);
+ ControlMessageOwned::RxqOvfl(drop_counter)
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IP, libc::IP_RECVERR) => {
+ let (err, addr) = Self::recv_err_helper::<sockaddr_in>(p, len);
+ ControlMessageOwned::Ipv4RecvErr(err, addr)
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IPV6, libc::IPV6_RECVERR) => {
+ let (err, addr) = Self::recv_err_helper::<sockaddr_in6>(p, len);
+ ControlMessageOwned::Ipv6RecvErr(err, addr)
+ },
+ #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ (libc::IPPROTO_IPV6, libc::IPV6_ORIGDSTADDR) => {
+ let dl = ptr::read_unaligned(p as *const libc::sockaddr_in6);
+ ControlMessageOwned::Ipv6OrigDstAddr(dl)
+ },
+ (_, _) => {
+ let sl = slice::from_raw_parts(p, len);
+ let ucmsg = UnknownCmsg(*header, Vec::<u8>::from(sl));
+ ControlMessageOwned::Unknown(ucmsg)
+ }
+ }
+ }
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ #[allow(clippy::cast_ptr_alignment)] // False positive
+ unsafe fn recv_err_helper<T>(p: *mut libc::c_uchar, len: usize) -> (libc::sock_extended_err, Option<T>) {
+ let ee = p as *const libc::sock_extended_err;
+ let err = ptr::read_unaligned(ee);
+
+ // For errors originating on the network, SO_EE_OFFENDER(ee) points inside the p[..len]
+ // CMSG_DATA buffer. For local errors, there is no address included in the control
+ // message, and SO_EE_OFFENDER(ee) points beyond the end of the buffer. So, we need to
+ // validate that the address object is in-bounds before we attempt to copy it.
+ let addrp = libc::SO_EE_OFFENDER(ee) as *const T;
+
+ if addrp.offset(1) as usize - (p as usize) > len {
+ (err, None)
+ } else {
+ (err, Some(ptr::read_unaligned(addrp)))
+ }
+ }
+}
+
+/// A type-safe zero-copy wrapper around a single control message, as used wih
+/// [`sendmsg`](#fn.sendmsg). More types may be added to this enum; do not
+/// exhaustively pattern-match it.
+///
+/// [Further reading](https://man7.org/linux/man-pages/man3/cmsg.3.html)
+#[derive(Clone, Copy, Debug, Eq, PartialEq)]
+#[non_exhaustive]
+pub enum ControlMessage<'a> {
+ /// A message of type `SCM_RIGHTS`, containing an array of file
+ /// descriptors passed between processes.
+ ///
+ /// See the description in the "Ancillary messages" section of the
+ /// [unix(7) man page](https://man7.org/linux/man-pages/man7/unix.7.html).
+ ///
+ /// Using multiple `ScmRights` messages for a single `sendmsg` call isn't
+ /// recommended since it causes platform-dependent behaviour: It might
+ /// swallow all but the first `ScmRights` message or fail with `EINVAL`.
+ /// Instead, you can put all fds to be passed into a single `ScmRights`
+ /// message.
+ ScmRights(&'a [RawFd]),
+ /// A message of type `SCM_CREDENTIALS`, containing the pid, uid and gid of
+ /// a process connected to the socket.
+ ///
+ /// This is similar to the socket option `SO_PEERCRED`, but requires a
+ /// process to explicitly send its credentials. A process running as root is
+ /// allowed to specify any credentials, while credentials sent by other
+ /// processes are verified by the kernel.
+ ///
+ /// For further information, please refer to the
+ /// [`unix(7)`](https://man7.org/linux/man-pages/man7/unix.7.html) man page.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ScmCredentials(&'a UnixCredentials),
+ /// A message of type `SCM_CREDS`, containing the pid, uid, euid, gid and groups of
+ /// a process connected to the socket.
+ ///
+ /// This is similar to the socket options `LOCAL_CREDS` and `LOCAL_PEERCRED`, but
+ /// requires a process to explicitly send its credentials.
+ ///
+ /// Credentials are always overwritten by the kernel, so this variant does have
+ /// any data, unlike the receive-side
+ /// [`ControlMessageOwned::ScmCreds`].
+ ///
+ /// For further information, please refer to the
+ /// [`unix(4)`](https://www.freebsd.org/cgi/man.cgi?query=unix) man page.
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ScmCreds,
+
+ /// Set IV for `AF_ALG` crypto API.
+ ///
+ /// For further information, please refer to the
+ /// [`documentation`](https://kernel.readthedocs.io/en/sphinx-samples/crypto-API.html)
+ #[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ AlgSetIv(&'a [u8]),
+ /// Set crypto operation for `AF_ALG` crypto API. It may be one of
+ /// `ALG_OP_ENCRYPT` or `ALG_OP_DECRYPT`
+ ///
+ /// For further information, please refer to the
+ /// [`documentation`](https://kernel.readthedocs.io/en/sphinx-samples/crypto-API.html)
+ #[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ AlgSetOp(&'a libc::c_int),
+ /// Set the length of associated authentication data (AAD) (applicable only to AEAD algorithms)
+ /// for `AF_ALG` crypto API.
+ ///
+ /// For further information, please refer to the
+ /// [`documentation`](https://kernel.readthedocs.io/en/sphinx-samples/crypto-API.html)
+ #[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ AlgSetAeadAssoclen(&'a u32),
+
+ /// UDP GSO makes it possible for applications to generate network packets
+ /// for a virtual MTU much greater than the real one.
+ /// The length of the send data no longer matches the expected length on
+ /// the wire.
+ /// The size of the datagram payload as it should appear on the wire may be
+ /// passed through this control message.
+ /// Send buffer should consist of multiple fixed-size wire payloads
+ /// following one by one, and the last, possibly smaller one.
+ #[cfg(target_os = "linux")]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ UdpGsoSegments(&'a u16),
+
+ /// Configure the sending addressing and interface for v4
+ ///
+ /// For further information, please refer to the
+ /// [`ip(7)`](https://man7.org/linux/man-pages/man7/ip.7.html) man page.
+ #[cfg(any(target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "android",
+ target_os = "ios",))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv4PacketInfo(&'a libc::in_pktinfo),
+
+ /// Configure the sending addressing and interface for v6
+ ///
+ /// For further information, please refer to the
+ /// [`ipv6(7)`](https://man7.org/linux/man-pages/man7/ipv6.7.html) man page.
+ #[cfg(any(target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "freebsd",
+ target_os = "android",
+ target_os = "ios",))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv6PacketInfo(&'a libc::in6_pktinfo),
+
+ /// Configure the IPv4 source address with `IP_SENDSRCADDR`.
+ #[cfg(any(
+ target_os = "netbsd",
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "dragonfly",
+ ))]
+ #[cfg(feature = "net")]
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ Ipv4SendSrcAddr(&'a libc::in_addr),
+
+ /// SO_RXQ_OVFL indicates that an unsigned 32 bit value
+ /// ancilliary msg (cmsg) should be attached to recieved
+ /// skbs indicating the number of packets dropped by the
+ /// socket between the last recieved packet and this
+ /// received packet.
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ RxqOvfl(&'a u32),
+
+ /// Configure the transmission time of packets.
+ ///
+ /// For further information, please refer to the
+ /// [`tc-etf(8)`](https://man7.org/linux/man-pages/man8/tc-etf.8.html) man
+ /// page.
+ #[cfg(target_os = "linux")]
+ TxTime(&'a u64),
+}
+
+// An opaque structure used to prevent cmsghdr from being a public type
+#[doc(hidden)]
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct UnknownCmsg(cmsghdr, Vec<u8>);
+
+impl<'a> ControlMessage<'a> {
+ /// The value of CMSG_SPACE on this message.
+ /// Safe because CMSG_SPACE is always safe
+ fn space(&self) -> usize {
+ unsafe{CMSG_SPACE(self.len() as libc::c_uint) as usize}
+ }
+
+ /// The value of CMSG_LEN on this message.
+ /// Safe because CMSG_LEN is always safe
+ #[cfg(any(target_os = "android",
+ all(target_os = "linux", not(target_env = "musl"))))]
+ fn cmsg_len(&self) -> usize {
+ unsafe{CMSG_LEN(self.len() as libc::c_uint) as usize}
+ }
+
+ #[cfg(not(any(target_os = "android",
+ all(target_os = "linux", not(target_env = "musl")))))]
+ fn cmsg_len(&self) -> libc::c_uint {
+ unsafe{CMSG_LEN(self.len() as libc::c_uint)}
+ }
+
+ /// Return a reference to the payload data as a byte pointer
+ fn copy_to_cmsg_data(&self, cmsg_data: *mut u8) {
+ let data_ptr = match *self {
+ ControlMessage::ScmRights(fds) => {
+ fds as *const _ as *const u8
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::ScmCredentials(creds) => {
+ &creds.0 as *const libc::ucred as *const u8
+ }
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ ControlMessage::ScmCreds => {
+ // The kernel overwrites the data, we just zero it
+ // to make sure it's not uninitialized memory
+ unsafe { ptr::write_bytes(cmsg_data, 0, self.len()) };
+ return
+ }
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::AlgSetIv(iv) => {
+ #[allow(deprecated)] // https://github.com/rust-lang/libc/issues/1501
+ let af_alg_iv = libc::af_alg_iv {
+ ivlen: iv.len() as u32,
+ iv: [0u8; 0],
+ };
+
+ let size = mem::size_of_val(&af_alg_iv);
+
+ unsafe {
+ ptr::copy_nonoverlapping(
+ &af_alg_iv as *const _ as *const u8,
+ cmsg_data,
+ size,
+ );
+ ptr::copy_nonoverlapping(
+ iv.as_ptr(),
+ cmsg_data.add(size),
+ iv.len()
+ );
+ };
+
+ return
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::AlgSetOp(op) => {
+ op as *const _ as *const u8
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::AlgSetAeadAssoclen(len) => {
+ len as *const _ as *const u8
+ },
+ #[cfg(target_os = "linux")]
+ #[cfg(feature = "net")]
+ ControlMessage::UdpGsoSegments(gso_size) => {
+ gso_size as *const _ as *const u8
+ },
+ #[cfg(any(target_os = "linux", target_os = "macos",
+ target_os = "netbsd", target_os = "android",
+ target_os = "ios",))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv4PacketInfo(info) => info as *const _ as *const u8,
+ #[cfg(any(target_os = "linux", target_os = "macos",
+ target_os = "netbsd", target_os = "freebsd",
+ target_os = "android", target_os = "ios",))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv6PacketInfo(info) => info as *const _ as *const u8,
+ #[cfg(any(target_os = "netbsd", target_os = "freebsd",
+ target_os = "openbsd", target_os = "dragonfly"))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv4SendSrcAddr(addr) => addr as *const _ as *const u8,
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ ControlMessage::RxqOvfl(drop_count) => {
+ drop_count as *const _ as *const u8
+ },
+ #[cfg(target_os = "linux")]
+ ControlMessage::TxTime(tx_time) => {
+ tx_time as *const _ as *const u8
+ },
+ };
+ unsafe {
+ ptr::copy_nonoverlapping(
+ data_ptr,
+ cmsg_data,
+ self.len()
+ )
+ };
+ }
+
+ /// The size of the payload, excluding its cmsghdr
+ fn len(&self) -> usize {
+ match *self {
+ ControlMessage::ScmRights(fds) => {
+ mem::size_of_val(fds)
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::ScmCredentials(creds) => {
+ mem::size_of_val(creds)
+ }
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ ControlMessage::ScmCreds => {
+ mem::size_of::<libc::cmsgcred>()
+ }
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::AlgSetIv(iv) => {
+ mem::size_of::<&[u8]>() + iv.len()
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::AlgSetOp(op) => {
+ mem::size_of_val(op)
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::AlgSetAeadAssoclen(len) => {
+ mem::size_of_val(len)
+ },
+ #[cfg(target_os = "linux")]
+ #[cfg(feature = "net")]
+ ControlMessage::UdpGsoSegments(gso_size) => {
+ mem::size_of_val(gso_size)
+ },
+ #[cfg(any(target_os = "linux", target_os = "macos",
+ target_os = "netbsd", target_os = "android",
+ target_os = "ios",))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv4PacketInfo(info) => mem::size_of_val(info),
+ #[cfg(any(target_os = "linux", target_os = "macos",
+ target_os = "netbsd", target_os = "freebsd",
+ target_os = "android", target_os = "ios",))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv6PacketInfo(info) => mem::size_of_val(info),
+ #[cfg(any(target_os = "netbsd", target_os = "freebsd",
+ target_os = "openbsd", target_os = "dragonfly"))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv4SendSrcAddr(addr) => mem::size_of_val(addr),
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ ControlMessage::RxqOvfl(drop_count) => {
+ mem::size_of_val(drop_count)
+ },
+ #[cfg(target_os = "linux")]
+ ControlMessage::TxTime(tx_time) => {
+ mem::size_of_val(tx_time)
+ },
+ }
+ }
+
+ /// Returns the value to put into the `cmsg_level` field of the header.
+ fn cmsg_level(&self) -> libc::c_int {
+ match *self {
+ ControlMessage::ScmRights(_) => libc::SOL_SOCKET,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::ScmCredentials(_) => libc::SOL_SOCKET,
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ ControlMessage::ScmCreds => libc::SOL_SOCKET,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::AlgSetIv(_) | ControlMessage::AlgSetOp(_) |
+ ControlMessage::AlgSetAeadAssoclen(_) => libc::SOL_ALG,
+ #[cfg(target_os = "linux")]
+ #[cfg(feature = "net")]
+ ControlMessage::UdpGsoSegments(_) => libc::SOL_UDP,
+ #[cfg(any(target_os = "linux", target_os = "macos",
+ target_os = "netbsd", target_os = "android",
+ target_os = "ios",))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv4PacketInfo(_) => libc::IPPROTO_IP,
+ #[cfg(any(target_os = "linux", target_os = "macos",
+ target_os = "netbsd", target_os = "freebsd",
+ target_os = "android", target_os = "ios",))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv6PacketInfo(_) => libc::IPPROTO_IPV6,
+ #[cfg(any(target_os = "netbsd", target_os = "freebsd",
+ target_os = "openbsd", target_os = "dragonfly"))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv4SendSrcAddr(_) => libc::IPPROTO_IP,
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ ControlMessage::RxqOvfl(_) => libc::SOL_SOCKET,
+ #[cfg(target_os = "linux")]
+ ControlMessage::TxTime(_) => libc::SOL_SOCKET,
+ }
+ }
+
+ /// Returns the value to put into the `cmsg_type` field of the header.
+ fn cmsg_type(&self) -> libc::c_int {
+ match *self {
+ ControlMessage::ScmRights(_) => libc::SCM_RIGHTS,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::ScmCredentials(_) => libc::SCM_CREDENTIALS,
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ ControlMessage::ScmCreds => libc::SCM_CREDS,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::AlgSetIv(_) => {
+ libc::ALG_SET_IV
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::AlgSetOp(_) => {
+ libc::ALG_SET_OP
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessage::AlgSetAeadAssoclen(_) => {
+ libc::ALG_SET_AEAD_ASSOCLEN
+ },
+ #[cfg(target_os = "linux")]
+ #[cfg(feature = "net")]
+ ControlMessage::UdpGsoSegments(_) => {
+ libc::UDP_SEGMENT
+ },
+ #[cfg(any(target_os = "linux", target_os = "macos",
+ target_os = "netbsd", target_os = "android",
+ target_os = "ios",))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv4PacketInfo(_) => libc::IP_PKTINFO,
+ #[cfg(any(target_os = "linux", target_os = "macos",
+ target_os = "netbsd", target_os = "freebsd",
+ target_os = "android", target_os = "ios",))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv6PacketInfo(_) => libc::IPV6_PKTINFO,
+ #[cfg(any(target_os = "netbsd", target_os = "freebsd",
+ target_os = "openbsd", target_os = "dragonfly"))]
+ #[cfg(feature = "net")]
+ ControlMessage::Ipv4SendSrcAddr(_) => libc::IP_SENDSRCADDR,
+ #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+ ControlMessage::RxqOvfl(_) => {
+ libc::SO_RXQ_OVFL
+ },
+ #[cfg(target_os = "linux")]
+ ControlMessage::TxTime(_) => {
+ libc::SCM_TXTIME
+ },
+ }
+ }
+
+ // Unsafe: cmsg must point to a valid cmsghdr with enough space to
+ // encode self.
+ unsafe fn encode_into(&self, cmsg: *mut cmsghdr) {
+ (*cmsg).cmsg_level = self.cmsg_level();
+ (*cmsg).cmsg_type = self.cmsg_type();
+ (*cmsg).cmsg_len = self.cmsg_len();
+ self.copy_to_cmsg_data(CMSG_DATA(cmsg));
+ }
+}
+
+
+/// Send data in scatter-gather vectors to a socket, possibly accompanied
+/// by ancillary data. Optionally direct the message at the given address,
+/// as with sendto.
+///
+/// Allocates if cmsgs is nonempty.
+///
+/// # Examples
+/// When not directing to any specific address, use `()` for the generic type
+/// ```
+/// # use nix::sys::socket::*;
+/// # use nix::unistd::pipe;
+/// # use std::io::IoSlice;
+/// let (fd1, fd2) = socketpair(AddressFamily::Unix, SockType::Stream, None,
+/// SockFlag::empty())
+/// .unwrap();
+/// let (r, w) = pipe().unwrap();
+///
+/// let iov = [IoSlice::new(b"hello")];
+/// let fds = [r];
+/// let cmsg = ControlMessage::ScmRights(&fds);
+/// sendmsg::<()>(fd1, &iov, &[cmsg], MsgFlags::empty(), None).unwrap();
+/// ```
+/// When directing to a specific address, the generic type will be inferred.
+/// ```
+/// # use nix::sys::socket::*;
+/// # use nix::unistd::pipe;
+/// # use std::io::IoSlice;
+/// # use std::str::FromStr;
+/// let localhost = SockaddrIn::from_str("1.2.3.4:8080").unwrap();
+/// let fd = socket(AddressFamily::Inet, SockType::Datagram, SockFlag::empty(),
+/// None).unwrap();
+/// let (r, w) = pipe().unwrap();
+///
+/// let iov = [IoSlice::new(b"hello")];
+/// let fds = [r];
+/// let cmsg = ControlMessage::ScmRights(&fds);
+/// sendmsg(fd, &iov, &[cmsg], MsgFlags::empty(), Some(&localhost)).unwrap();
+/// ```
+pub fn sendmsg<S>(fd: RawFd, iov: &[IoSlice<'_>], cmsgs: &[ControlMessage],
+ flags: MsgFlags, addr: Option<&S>) -> Result<usize>
+ where S: SockaddrLike
+{
+ let capacity = cmsgs.iter().map(|c| c.space()).sum();
+
+ // First size the buffer needed to hold the cmsgs. It must be zeroed,
+ // because subsequent code will not clear the padding bytes.
+ let mut cmsg_buffer = vec![0u8; capacity];
+
+ let mhdr = pack_mhdr_to_send(&mut cmsg_buffer[..], iov, cmsgs, addr);
+
+ let ret = unsafe { libc::sendmsg(fd, &mhdr, flags.bits()) };
+
+ Errno::result(ret).map(|r| r as usize)
+}
+
+
+/// An extension of `sendmsg` that allows the caller to transmit multiple
+/// messages on a socket using a single system call. This has performance
+/// benefits for some applications.
+///
+/// Allocations are performed for cmsgs and to build `msghdr` buffer
+///
+/// # Arguments
+///
+/// * `fd`: Socket file descriptor
+/// * `data`: Struct that implements `IntoIterator` with `SendMmsgData` items
+/// * `flags`: Optional flags passed directly to the operating system.
+///
+/// # Returns
+/// `Vec` with numbers of sent bytes on each sent message.
+///
+/// # References
+/// [`sendmsg`](fn.sendmsg.html)
+#[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "netbsd",
+))]
+pub fn sendmmsg<'a, XS, AS, C, I, S>(
+ fd: RawFd,
+ data: &'a mut MultiHeaders<S>,
+ slices: XS,
+ // one address per group of slices
+ addrs: AS,
+ // shared across all the messages
+ cmsgs: C,
+ flags: MsgFlags
+) -> crate::Result<MultiResults<'a, S>>
+ where
+ XS: IntoIterator<Item = &'a I>,
+ AS: AsRef<[Option<S>]>,
+ I: AsRef<[IoSlice<'a>]> + 'a,
+ C: AsRef<[ControlMessage<'a>]> + 'a,
+ S: SockaddrLike + 'a
+{
+
+ let mut count = 0;
+
+
+ for (i, ((slice, addr), mmsghdr)) in slices.into_iter().zip(addrs.as_ref()).zip(data.items.iter_mut() ).enumerate() {
+ let mut p = &mut mmsghdr.msg_hdr;
+ p.msg_iov = slice.as_ref().as_ptr() as *mut libc::iovec;
+ p.msg_iovlen = slice.as_ref().len() as _;
+
+ p.msg_namelen = addr.as_ref().map_or(0, S::len);
+ p.msg_name = addr.as_ref().map_or(ptr::null(), S::as_ptr) as _;
+
+ // Encode each cmsg. This must happen after initializing the header because
+ // CMSG_NEXT_HDR and friends read the msg_control and msg_controllen fields.
+ // CMSG_FIRSTHDR is always safe
+ let mut pmhdr: *mut cmsghdr = unsafe { CMSG_FIRSTHDR(p) };
+ for cmsg in cmsgs.as_ref() {
+ assert_ne!(pmhdr, ptr::null_mut());
+ // Safe because we know that pmhdr is valid, and we initialized it with
+ // sufficient space
+ unsafe { cmsg.encode_into(pmhdr) };
+ // Safe because mhdr is valid
+ pmhdr = unsafe { CMSG_NXTHDR(p, pmhdr) };
+ }
+
+ count = i+1;
+ }
+
+ let sent = Errno::result(unsafe {
+ libc::sendmmsg(
+ fd,
+ data.items.as_mut_ptr(),
+ count as _,
+ flags.bits() as _
+ )
+ })? as usize;
+
+ Ok(MultiResults {
+ rmm: data,
+ current_index: 0,
+ received: sent
+ })
+
+}
+
+
+#[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "netbsd",
+))]
+#[derive(Debug)]
+/// Preallocated structures needed for [`recvmmsg`] and [`sendmmsg`] functions
+pub struct MultiHeaders<S> {
+ // preallocated boxed slice of mmsghdr
+ items: Box<[libc::mmsghdr]>,
+ addresses: Box<[mem::MaybeUninit<S>]>,
+ // while we are not using it directly - this is used to store control messages
+ // and we retain pointers to them inside items array
+ #[allow(dead_code)]
+ cmsg_buffers: Option<Box<[u8]>>,
+ msg_controllen: usize,
+}
+
+#[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "netbsd",
+))]
+impl<S> MultiHeaders<S> {
+ /// Preallocate structure used by [`recvmmsg`] and [`sendmmsg`] takes number of headers to preallocate
+ ///
+ /// `cmsg_buffer` should be created with [`cmsg_space!`] if needed
+ pub fn preallocate(num_slices: usize, cmsg_buffer: Option<Vec<u8>>) -> Self
+ where
+ S: Copy + SockaddrLike,
+ {
+ // we will be storing pointers to addresses inside mhdr - convert it into boxed
+ // slice so it can'be changed later by pushing anything into self.addresses
+ let mut addresses = vec![std::mem::MaybeUninit::uninit(); num_slices].into_boxed_slice();
+
+ let msg_controllen = cmsg_buffer.as_ref().map_or(0, |v| v.capacity());
+
+ // we'll need a cmsg_buffer for each slice, we preallocate a vector and split
+ // it into "slices" parts
+ let cmsg_buffers =
+ cmsg_buffer.map(|v| vec![0u8; v.capacity() * num_slices].into_boxed_slice());
+
+ let items = addresses
+ .iter_mut()
+ .enumerate()
+ .map(|(ix, address)| {
+ let (ptr, cap) = match &cmsg_buffers {
+ Some(v) => ((&v[ix * msg_controllen] as *const u8), msg_controllen),
+ None => (std::ptr::null(), 0),
+ };
+ let msg_hdr = unsafe { pack_mhdr_to_receive(std::ptr::null(), 0, ptr, cap, address.as_mut_ptr()) };
+ libc::mmsghdr {
+ msg_hdr,
+ msg_len: 0,
+ }
+ })
+ .collect::<Vec<_>>();
+
+ Self {
+ items: items.into_boxed_slice(),
+ addresses,
+ cmsg_buffers,
+ msg_controllen,
+ }
+ }
+}
+
+/// An extension of recvmsg that allows the caller to receive multiple messages from a socket using a single system call.
+///
+/// This has performance benefits for some applications.
+///
+/// This method performs no allocations.
+///
+/// Returns an iterator producing [`RecvMsg`], one per received messages. Each `RecvMsg` can produce
+/// iterators over [`IoSlice`] with [`iovs`][RecvMsg::iovs`] and
+/// `ControlMessageOwned` with [`cmsgs`][RecvMsg::cmsgs].
+///
+/// # Bugs (in underlying implementation, at least in Linux)
+/// The timeout argument does not work as intended. The timeout is checked only after the receipt
+/// of each datagram, so that if up to `vlen`-1 datagrams are received before the timeout expires,
+/// but then no further datagrams are received, the call will block forever.
+///
+/// If an error occurs after at least one message has been received, the call succeeds, and returns
+/// the number of messages received. The error code is expected to be returned on a subsequent
+/// call to recvmmsg(). In the current implementation, however, the error code can be
+/// overwritten in the meantime by an unrelated network event on a socket, for example an
+/// incoming ICMP packet.
+
+// On aarch64 linux using recvmmsg and trying to get hardware/kernel timestamps might not
+// always produce the desired results - see https://github.com/nix-rust/nix/pull/1744 for more
+// details
+
+#[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "netbsd",
+))]
+pub fn recvmmsg<'a, XS, S, I>(
+ fd: RawFd,
+ data: &'a mut MultiHeaders<S>,
+ slices: XS,
+ flags: MsgFlags,
+ mut timeout: Option<crate::sys::time::TimeSpec>,
+) -> crate::Result<MultiResults<'a, S>>
+where
+ XS: IntoIterator<Item = &'a I>,
+ I: AsRef<[IoSliceMut<'a>]> + 'a,
+{
+ let mut count = 0;
+ for (i, (slice, mmsghdr)) in slices.into_iter().zip(data.items.iter_mut()).enumerate() {
+ let mut p = &mut mmsghdr.msg_hdr;
+ p.msg_iov = slice.as_ref().as_ptr() as *mut libc::iovec;
+ p.msg_iovlen = slice.as_ref().len() as _;
+ count = i + 1;
+ }
+
+ let timeout_ptr = timeout
+ .as_mut()
+ .map_or_else(std::ptr::null_mut, |t| t as *mut _ as *mut libc::timespec);
+
+ let received = Errno::result(unsafe {
+ libc::recvmmsg(
+ fd,
+ data.items.as_mut_ptr(),
+ count as _,
+ flags.bits() as _,
+ timeout_ptr,
+ )
+ })? as usize;
+
+ Ok(MultiResults {
+ rmm: data,
+ current_index: 0,
+ received,
+ })
+}
+
+#[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "netbsd",
+))]
+#[derive(Debug)]
+/// Iterator over results of [`recvmmsg`]/[`sendmmsg`]
+///
+///
+pub struct MultiResults<'a, S> {
+ // preallocated structures
+ rmm: &'a MultiHeaders<S>,
+ current_index: usize,
+ received: usize,
+}
+
+#[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "netbsd",
+))]
+impl<'a, S> Iterator for MultiResults<'a, S>
+where
+ S: Copy + SockaddrLike,
+{
+ type Item = RecvMsg<'a, 'a, S>;
+
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.current_index >= self.received {
+ return None;
+ }
+ let mmsghdr = self.rmm.items[self.current_index];
+
+ // as long as we are not reading past the index writen by recvmmsg - address
+ // will be initialized
+ let address = unsafe { self.rmm.addresses[self.current_index].assume_init() };
+
+ self.current_index += 1;
+ Some(unsafe {
+ read_mhdr(
+ mmsghdr.msg_hdr,
+ mmsghdr.msg_len as isize,
+ self.rmm.msg_controllen,
+ address,
+ )
+ })
+ }
+}
+
+impl<'a, S> RecvMsg<'_, 'a, S> {
+ /// Iterate over the filled io slices pointed by this msghdr
+ pub fn iovs(&self) -> IoSliceIterator<'a> {
+ IoSliceIterator {
+ index: 0,
+ remaining: self.bytes,
+ slices: unsafe {
+ // safe for as long as mgdr is properly initialized and references are valid.
+ // for multi messages API we initialize it with an empty
+ // slice and replace with a concrete buffer
+ // for single message API we hold a lifetime reference to ioslices
+ std::slice::from_raw_parts(self.mhdr.msg_iov as *const _, self.mhdr.msg_iovlen as _)
+ },
+ }
+ }
+}
+
+#[derive(Debug)]
+pub struct IoSliceIterator<'a> {
+ index: usize,
+ remaining: usize,
+ slices: &'a [IoSlice<'a>],
+}
+
+impl<'a> Iterator for IoSliceIterator<'a> {
+ type Item = &'a [u8];
+
+ fn next(&mut self) -> Option<Self::Item> {
+ if self.index >= self.slices.len() {
+ return None;
+ }
+ let slice = &self.slices[self.index][..self.remaining.min(self.slices[self.index].len())];
+ self.remaining -= slice.len();
+ self.index += 1;
+ if slice.is_empty() {
+ return None;
+ }
+
+ Some(slice)
+ }
+}
+
+// test contains both recvmmsg and timestaping which is linux only
+// there are existing tests for recvmmsg only in tests/
+#[cfg(target_os = "linux")]
+#[cfg(test)]
+mod test {
+ use crate::sys::socket::{AddressFamily, ControlMessageOwned};
+ use crate::*;
+ use std::str::FromStr;
+
+ #[cfg_attr(qemu, ignore)]
+ #[test]
+ fn test_recvmm2() -> crate::Result<()> {
+ use crate::sys::socket::{
+ sendmsg, setsockopt, socket, sockopt::Timestamping, MsgFlags, SockFlag, SockType,
+ SockaddrIn, TimestampingFlag,
+ };
+ use std::io::{IoSlice, IoSliceMut};
+
+ let sock_addr = SockaddrIn::from_str("127.0.0.1:6790").unwrap();
+
+ let ssock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )?;
+
+ let rsock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::SOCK_NONBLOCK,
+ None,
+ )?;
+
+ crate::sys::socket::bind(rsock, &sock_addr)?;
+
+ setsockopt(rsock, Timestamping, &TimestampingFlag::all())?;
+
+ let sbuf = (0..400).map(|i| i as u8).collect::<Vec<_>>();
+
+ let mut recv_buf = vec![0; 1024];
+
+ let mut recv_iovs = Vec::new();
+ let mut pkt_iovs = Vec::new();
+
+ for (ix, chunk) in recv_buf.chunks_mut(256).enumerate() {
+ pkt_iovs.push(IoSliceMut::new(chunk));
+ if ix % 2 == 1 {
+ recv_iovs.push(pkt_iovs);
+ pkt_iovs = Vec::new();
+ }
+ }
+ drop(pkt_iovs);
+
+ let flags = MsgFlags::empty();
+ let iov1 = [IoSlice::new(&sbuf)];
+
+ let cmsg = cmsg_space!(crate::sys::socket::Timestamps);
+ sendmsg(ssock, &iov1, &[], flags, Some(&sock_addr)).unwrap();
+
+ let mut data = super::MultiHeaders::<()>::preallocate(recv_iovs.len(), Some(cmsg));
+
+ let t = sys::time::TimeSpec::from_duration(std::time::Duration::from_secs(10));
+
+ let recv = super::recvmmsg(rsock, &mut data, recv_iovs.iter(), flags, Some(t))?;
+
+ for rmsg in recv {
+ #[cfg(not(any(qemu, target_arch = "aarch64")))]
+ let mut saw_time = false;
+ let mut recvd = 0;
+ for cmsg in rmsg.cmsgs() {
+ if let ControlMessageOwned::ScmTimestampsns(timestamps) = cmsg {
+ let ts = timestamps.system;
+
+ let sys_time =
+ crate::time::clock_gettime(crate::time::ClockId::CLOCK_REALTIME)?;
+ let diff = if ts > sys_time {
+ ts - sys_time
+ } else {
+ sys_time - ts
+ };
+ assert!(std::time::Duration::from(diff).as_secs() < 60);
+ #[cfg(not(any(qemu, target_arch = "aarch64")))]
+ {
+ saw_time = true;
+ }
+ }
+ }
+
+ #[cfg(not(any(qemu, target_arch = "aarch64")))]
+ assert!(saw_time);
+
+ for iov in rmsg.iovs() {
+ recvd += iov.len();
+ }
+ assert_eq!(recvd, 400);
+ }
+
+ Ok(())
+ }
+}
+unsafe fn read_mhdr<'a, 'i, S>(
+ mhdr: msghdr,
+ r: isize,
+ msg_controllen: usize,
+ address: S,
+) -> RecvMsg<'a, 'i, S>
+ where S: SockaddrLike
+{
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ let cmsghdr = {
+ if mhdr.msg_controllen > 0 {
+ debug_assert!(!mhdr.msg_control.is_null());
+ debug_assert!(msg_controllen >= mhdr.msg_controllen as usize);
+ CMSG_FIRSTHDR(&mhdr as *const msghdr)
+ } else {
+ ptr::null()
+ }.as_ref()
+ };
+
+ RecvMsg {
+ bytes: r as usize,
+ cmsghdr,
+ address: Some(address),
+ flags: MsgFlags::from_bits_truncate(mhdr.msg_flags),
+ mhdr,
+ iobufs: std::marker::PhantomData,
+ }
+}
+
+/// Pack pointers to various structures into into msghdr
+///
+/// # Safety
+/// `iov_buffer` and `iov_buffer_len` must point to a slice
+/// of `IoSliceMut` and number of available elements or be a null pointer and 0
+///
+/// `cmsg_buffer` and `cmsg_capacity` must point to a byte buffer used
+/// to store control headers later or be a null pointer and 0 if control
+/// headers are not used
+///
+/// Buffers must remain valid for the whole lifetime of msghdr
+unsafe fn pack_mhdr_to_receive<S>(
+ iov_buffer: *const IoSliceMut,
+ iov_buffer_len: usize,
+ cmsg_buffer: *const u8,
+ cmsg_capacity: usize,
+ address: *mut S,
+) -> msghdr
+ where
+ S: SockaddrLike
+{
+ // Musl's msghdr has private fields, so this is the only way to
+ // initialize it.
+ let mut mhdr = mem::MaybeUninit::<msghdr>::zeroed();
+ let p = mhdr.as_mut_ptr();
+ (*p).msg_name = (*address).as_mut_ptr() as *mut c_void;
+ (*p).msg_namelen = S::size();
+ (*p).msg_iov = iov_buffer as *mut iovec;
+ (*p).msg_iovlen = iov_buffer_len as _;
+ (*p).msg_control = cmsg_buffer as *mut c_void;
+ (*p).msg_controllen = cmsg_capacity as _;
+ (*p).msg_flags = 0;
+ mhdr.assume_init()
+}
+
+fn pack_mhdr_to_send<'a, I, C, S>(
+ cmsg_buffer: &mut [u8],
+ iov: I,
+ cmsgs: C,
+ addr: Option<&S>
+) -> msghdr
+ where
+ I: AsRef<[IoSlice<'a>]>,
+ C: AsRef<[ControlMessage<'a>]>,
+ S: SockaddrLike + 'a
+{
+ let capacity = cmsg_buffer.len();
+
+ // The message header must be initialized before the individual cmsgs.
+ let cmsg_ptr = if capacity > 0 {
+ cmsg_buffer.as_ptr() as *mut c_void
+ } else {
+ ptr::null_mut()
+ };
+
+ let mhdr = unsafe {
+ // Musl's msghdr has private fields, so this is the only way to
+ // initialize it.
+ let mut mhdr = mem::MaybeUninit::<msghdr>::zeroed();
+ let p = mhdr.as_mut_ptr();
+ (*p).msg_name = addr.map(S::as_ptr).unwrap_or(ptr::null()) as *mut _;
+ (*p).msg_namelen = addr.map(S::len).unwrap_or(0);
+ // transmute iov into a mutable pointer. sendmsg doesn't really mutate
+ // the buffer, but the standard says that it takes a mutable pointer
+ (*p).msg_iov = iov.as_ref().as_ptr() as *mut _;
+ (*p).msg_iovlen = iov.as_ref().len() as _;
+ (*p).msg_control = cmsg_ptr;
+ (*p).msg_controllen = capacity as _;
+ (*p).msg_flags = 0;
+ mhdr.assume_init()
+ };
+
+ // Encode each cmsg. This must happen after initializing the header because
+ // CMSG_NEXT_HDR and friends read the msg_control and msg_controllen fields.
+ // CMSG_FIRSTHDR is always safe
+ let mut pmhdr: *mut cmsghdr = unsafe { CMSG_FIRSTHDR(&mhdr as *const msghdr) };
+ for cmsg in cmsgs.as_ref() {
+ assert_ne!(pmhdr, ptr::null_mut());
+ // Safe because we know that pmhdr is valid, and we initialized it with
+ // sufficient space
+ unsafe { cmsg.encode_into(pmhdr) };
+ // Safe because mhdr is valid
+ pmhdr = unsafe { CMSG_NXTHDR(&mhdr as *const msghdr, pmhdr) };
+ }
+
+ mhdr
+}
+
+/// Receive message in scatter-gather vectors from a socket, and
+/// optionally receive ancillary data into the provided buffer.
+/// If no ancillary data is desired, use () as the type parameter.
+///
+/// # Arguments
+///
+/// * `fd`: Socket file descriptor
+/// * `iov`: Scatter-gather list of buffers to receive the message
+/// * `cmsg_buffer`: Space to receive ancillary data. Should be created by
+/// [`cmsg_space!`](../../macro.cmsg_space.html)
+/// * `flags`: Optional flags passed directly to the operating system.
+///
+/// # References
+/// [recvmsg(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvmsg.html)
+pub fn recvmsg<'a, 'outer, 'inner, S>(fd: RawFd, iov: &'outer mut [IoSliceMut<'inner>],
+ mut cmsg_buffer: Option<&'a mut Vec<u8>>,
+ flags: MsgFlags) -> Result<RecvMsg<'a, 'inner, S>>
+ where S: SockaddrLike + 'a,
+ 'inner: 'outer
+{
+ let mut address = mem::MaybeUninit::uninit();
+
+ let (msg_control, msg_controllen) = cmsg_buffer.as_mut()
+ .map(|v| (v.as_mut_ptr(), v.capacity()))
+ .unwrap_or((ptr::null_mut(), 0));
+ let mut mhdr = unsafe {
+ pack_mhdr_to_receive(iov.as_ref().as_ptr(), iov.len(), msg_control, msg_controllen, address.as_mut_ptr())
+ };
+
+ let ret = unsafe { libc::recvmsg(fd, &mut mhdr, flags.bits()) };
+
+ let r = Errno::result(ret)?;
+
+ Ok(unsafe { read_mhdr(mhdr, r, msg_controllen, address.assume_init()) })
+}
+}
+
+/// Create an endpoint for communication
+///
+/// The `protocol` specifies a particular protocol to be used with the
+/// socket. Normally only a single protocol exists to support a
+/// particular socket type within a given protocol family, in which case
+/// protocol can be specified as `None`. However, it is possible that many
+/// protocols may exist, in which case a particular protocol must be
+/// specified in this manner.
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/socket.html)
+pub fn socket<T: Into<Option<SockProtocol>>>(
+ domain: AddressFamily,
+ ty: SockType,
+ flags: SockFlag,
+ protocol: T,
+) -> Result<RawFd> {
+ let protocol = match protocol.into() {
+ None => 0,
+ Some(p) => p as c_int,
+ };
+
+ // SockFlags are usually embedded into `ty`, but we don't do that in `nix` because it's a
+ // little easier to understand by separating it out. So we have to merge these bitfields
+ // here.
+ let mut ty = ty as c_int;
+ ty |= flags.bits();
+
+ let res = unsafe { libc::socket(domain as c_int, ty, protocol) };
+
+ Errno::result(res)
+}
+
+/// Create a pair of connected sockets
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/socketpair.html)
+pub fn socketpair<T: Into<Option<SockProtocol>>>(
+ domain: AddressFamily,
+ ty: SockType,
+ protocol: T,
+ flags: SockFlag,
+) -> Result<(RawFd, RawFd)> {
+ let protocol = match protocol.into() {
+ None => 0,
+ Some(p) => p as c_int,
+ };
+
+ // SockFlags are usually embedded into `ty`, but we don't do that in `nix` because it's a
+ // little easier to understand by separating it out. So we have to merge these bitfields
+ // here.
+ let mut ty = ty as c_int;
+ ty |= flags.bits();
+
+ let mut fds = [-1, -1];
+
+ let res = unsafe {
+ libc::socketpair(domain as c_int, ty, protocol, fds.as_mut_ptr())
+ };
+ Errno::result(res)?;
+
+ Ok((fds[0], fds[1]))
+}
+
+/// Listen for connections on a socket
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/listen.html)
+pub fn listen(sockfd: RawFd, backlog: usize) -> Result<()> {
+ let res = unsafe { libc::listen(sockfd, backlog as c_int) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Bind a name to a socket
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/bind.html)
+pub fn bind(fd: RawFd, addr: &dyn SockaddrLike) -> Result<()> {
+ let res = unsafe { libc::bind(fd, addr.as_ptr(), addr.len()) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Accept a connection on a socket
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/accept.html)
+pub fn accept(sockfd: RawFd) -> Result<RawFd> {
+ let res = unsafe { libc::accept(sockfd, ptr::null_mut(), ptr::null_mut()) };
+
+ Errno::result(res)
+}
+
+/// Accept a connection on a socket
+///
+/// [Further reading](https://man7.org/linux/man-pages/man2/accept.2.html)
+#[cfg(any(
+ all(
+ target_os = "android",
+ any(
+ target_arch = "aarch64",
+ target_arch = "x86",
+ target_arch = "x86_64"
+ )
+ ),
+ target_os = "dragonfly",
+ target_os = "emscripten",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd"
+))]
+pub fn accept4(sockfd: RawFd, flags: SockFlag) -> Result<RawFd> {
+ let res = unsafe {
+ libc::accept4(sockfd, ptr::null_mut(), ptr::null_mut(), flags.bits())
+ };
+
+ Errno::result(res)
+}
+
+/// Initiate a connection on a socket
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/connect.html)
+pub fn connect(fd: RawFd, addr: &dyn SockaddrLike) -> Result<()> {
+ let res = unsafe { libc::connect(fd, addr.as_ptr(), addr.len()) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Receive data from a connection-oriented socket. Returns the number of
+/// bytes read
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/recv.html)
+pub fn recv(sockfd: RawFd, buf: &mut [u8], flags: MsgFlags) -> Result<usize> {
+ unsafe {
+ let ret = libc::recv(
+ sockfd,
+ buf.as_ptr() as *mut c_void,
+ buf.len() as size_t,
+ flags.bits(),
+ );
+
+ Errno::result(ret).map(|r| r as usize)
+ }
+}
+
+/// Receive data from a connectionless or connection-oriented socket. Returns
+/// the number of bytes read and, for connectionless sockets, the socket
+/// address of the sender.
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/recvfrom.html)
+pub fn recvfrom<T: SockaddrLike>(
+ sockfd: RawFd,
+ buf: &mut [u8],
+) -> Result<(usize, Option<T>)> {
+ unsafe {
+ let mut addr = mem::MaybeUninit::<T>::uninit();
+ let mut len = mem::size_of_val(&addr) as socklen_t;
+
+ let ret = Errno::result(libc::recvfrom(
+ sockfd,
+ buf.as_ptr() as *mut c_void,
+ buf.len() as size_t,
+ 0,
+ addr.as_mut_ptr() as *mut libc::sockaddr,
+ &mut len as *mut socklen_t,
+ ))? as usize;
+
+ Ok((
+ ret,
+ T::from_raw(
+ addr.assume_init().as_ptr() as *const libc::sockaddr,
+ Some(len),
+ ),
+ ))
+ }
+}
+
+/// Send a message to a socket
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sendto.html)
+pub fn sendto(
+ fd: RawFd,
+ buf: &[u8],
+ addr: &dyn SockaddrLike,
+ flags: MsgFlags,
+) -> Result<usize> {
+ let ret = unsafe {
+ libc::sendto(
+ fd,
+ buf.as_ptr() as *const c_void,
+ buf.len() as size_t,
+ flags.bits(),
+ addr.as_ptr(),
+ addr.len(),
+ )
+ };
+
+ Errno::result(ret).map(|r| r as usize)
+}
+
+/// Send data to a connection-oriented socket. Returns the number of bytes read
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/send.html)
+pub fn send(fd: RawFd, buf: &[u8], flags: MsgFlags) -> Result<usize> {
+ let ret = unsafe {
+ libc::send(
+ fd,
+ buf.as_ptr() as *const c_void,
+ buf.len() as size_t,
+ flags.bits(),
+ )
+ };
+
+ Errno::result(ret).map(|r| r as usize)
+}
+
+/*
+ *
+ * ===== Socket Options =====
+ *
+ */
+
+/// Represents a socket option that can be retrieved.
+pub trait GetSockOpt: Copy {
+ type Val;
+
+ /// Look up the value of this socket option on the given socket.
+ fn get(&self, fd: RawFd) -> Result<Self::Val>;
+}
+
+/// Represents a socket option that can be set.
+pub trait SetSockOpt: Clone {
+ type Val;
+
+ /// Set the value of this socket option on the given socket.
+ fn set(&self, fd: RawFd, val: &Self::Val) -> Result<()>;
+}
+
+/// Get the current value for the requested socket option
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockopt.html)
+pub fn getsockopt<O: GetSockOpt>(fd: RawFd, opt: O) -> Result<O::Val> {
+ opt.get(fd)
+}
+
+/// Sets the value for the requested socket option
+///
+/// [Further reading](https://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)
+}
+
+/// Get the address of the peer connected to the socket `fd`.
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpeername.html)
+pub fn getpeername<T: SockaddrLike>(fd: RawFd) -> Result<T> {
+ unsafe {
+ let mut addr = mem::MaybeUninit::<T>::uninit();
+ let mut len = T::size();
+
+ let ret = libc::getpeername(
+ fd,
+ addr.as_mut_ptr() as *mut libc::sockaddr,
+ &mut len,
+ );
+
+ Errno::result(ret)?;
+
+ T::from_raw(addr.assume_init().as_ptr(), Some(len)).ok_or(Errno::EINVAL)
+ }
+}
+
+/// Get the current address to which the socket `fd` is bound.
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsockname.html)
+pub fn getsockname<T: SockaddrLike>(fd: RawFd) -> Result<T> {
+ unsafe {
+ let mut addr = mem::MaybeUninit::<T>::uninit();
+ let mut len = T::size();
+
+ let ret = libc::getsockname(
+ fd,
+ addr.as_mut_ptr() as *mut libc::sockaddr,
+ &mut len,
+ );
+
+ Errno::result(ret)?;
+
+ T::from_raw(addr.assume_init().as_ptr(), Some(len)).ok_or(Errno::EINVAL)
+ }
+}
+
+/// Return the appropriate `SockAddr` type from a `sockaddr_storage` of a
+/// certain size.
+///
+/// In C this would usually be done by casting. The `len` argument
+/// should be the number of bytes in the `sockaddr_storage` that are actually
+/// allocated and valid. It must be at least as large as all the useful parts
+/// of the structure. Note that in the case of a `sockaddr_un`, `len` need not
+/// include the terminating null.
+#[deprecated(
+ since = "0.24.0",
+ note = "use SockaddrLike or SockaddrStorage instead"
+)]
+#[allow(deprecated)]
+pub fn sockaddr_storage_to_addr(
+ addr: &sockaddr_storage,
+ len: usize,
+) -> Result<SockAddr> {
+ assert!(len <= mem::size_of::<sockaddr_storage>());
+ if len < mem::size_of_val(&addr.ss_family) {
+ return Err(Errno::ENOTCONN);
+ }
+
+ match c_int::from(addr.ss_family) {
+ #[cfg(feature = "net")]
+ libc::AF_INET => {
+ assert!(len >= mem::size_of::<sockaddr_in>());
+ let sin = unsafe {
+ *(addr as *const sockaddr_storage as *const sockaddr_in)
+ };
+ Ok(SockAddr::Inet(InetAddr::V4(sin)))
+ }
+ #[cfg(feature = "net")]
+ libc::AF_INET6 => {
+ assert!(len >= mem::size_of::<sockaddr_in6>());
+ let sin6 = unsafe { *(addr as *const _ as *const sockaddr_in6) };
+ Ok(SockAddr::Inet(InetAddr::V6(sin6)))
+ }
+ libc::AF_UNIX => unsafe {
+ let sun = *(addr as *const _ as *const sockaddr_un);
+ let sun_len = len.try_into().unwrap();
+ Ok(SockAddr::Unix(UnixAddr::from_raw_parts(sun, sun_len)))
+ },
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg(feature = "net")]
+ libc::AF_PACKET => {
+ use libc::sockaddr_ll;
+ // Don't assert anything about the size.
+ // Apparently the Linux kernel can return smaller sizes when
+ // the value in the last element of sockaddr_ll (`sll_addr`) is
+ // smaller than the declared size of that field
+ let sll = unsafe { *(addr as *const _ as *const sockaddr_ll) };
+ Ok(SockAddr::Link(LinkAddr(sll)))
+ }
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ libc::AF_NETLINK => {
+ use libc::sockaddr_nl;
+ let snl = unsafe { *(addr as *const _ as *const sockaddr_nl) };
+ Ok(SockAddr::Netlink(NetlinkAddr(snl)))
+ }
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ libc::AF_ALG => {
+ use libc::sockaddr_alg;
+ let salg = unsafe { *(addr as *const _ as *const sockaddr_alg) };
+ Ok(SockAddr::Alg(AlgAddr(salg)))
+ }
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ libc::AF_VSOCK => {
+ use libc::sockaddr_vm;
+ let svm = unsafe { *(addr as *const _ as *const sockaddr_vm) };
+ Ok(SockAddr::Vsock(VsockAddr(svm)))
+ }
+ af => panic!("unexpected address family {}", af),
+ }
+}
+
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub enum Shutdown {
+ /// Further receptions will be disallowed.
+ Read,
+ /// Further transmissions will be disallowed.
+ Write,
+ /// Further receptions and transmissions will be disallowed.
+ Both,
+}
+
+/// Shut down part of a full-duplex connection.
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/9699919799/functions/shutdown.html)
+pub fn shutdown(df: RawFd, how: Shutdown) -> Result<()> {
+ unsafe {
+ use libc::shutdown;
+
+ let how = match how {
+ Shutdown::Read => libc::SHUT_RD,
+ Shutdown::Write => libc::SHUT_WR,
+ Shutdown::Both => libc::SHUT_RDWR,
+ };
+
+ Errno::result(shutdown(df, how)).map(drop)
+ }
+}
+
+#[cfg(test)]
+mod tests {
+ #[test]
+ fn can_use_cmsg_space() {
+ let _ = cmsg_space!(u8);
+ }
+}
diff --git a/third_party/rust/nix/src/sys/socket/sockopt.rs b/third_party/rust/nix/src/sys/socket/sockopt.rs
new file mode 100644
index 0000000000..06e9ee4563
--- /dev/null
+++ b/third_party/rust/nix/src/sys/socket/sockopt.rs
@@ -0,0 +1,1422 @@
+//! Socket options as used by `setsockopt` and `getsockopt`.
+use super::{GetSockOpt, SetSockOpt};
+use crate::errno::Errno;
+use crate::sys::time::TimeVal;
+use crate::Result;
+use cfg_if::cfg_if;
+use libc::{self, c_int, c_void, socklen_t};
+use std::ffi::{OsStr, OsString};
+use std::{
+ convert::TryFrom,
+ mem::{self, MaybeUninit}
+};
+#[cfg(target_family = "unix")]
+use std::os::unix::ffi::OsStrExt;
+use std::os::unix::io::RawFd;
+
+// Constants
+// TCP_CA_NAME_MAX isn't defined in user space include files
+#[cfg(any(target_os = "freebsd", target_os = "linux"))]
+#[cfg(feature = "net")]
+const TCP_CA_NAME_MAX: usize = 16;
+
+/// Helper for implementing `SetSockOpt` for a given socket option. See
+/// [`::sys::socket::SetSockOpt`](sys/socket/trait.SetSockOpt.html).
+///
+/// This macro aims to help implementing `SetSockOpt` for different socket options that accept
+/// different kinds of data to be used with `setsockopt`.
+///
+/// Instead of using this macro directly consider using `sockopt_impl!`, especially if the option
+/// you are implementing represents a simple type.
+///
+/// # Arguments
+///
+/// * `$name:ident`: name of the type you want to implement `SetSockOpt` for.
+/// * `$level:expr` : socket layer, or a `protocol level`: could be *raw sockets*
+/// (`libc::SOL_SOCKET`), *ip protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`),
+/// and more. Please refer to your system manual for more options. Will be passed as the second
+/// argument (`level`) to the `setsockopt` call.
+/// * `$flag:path`: a flag name to set. Some examples: `libc::SO_REUSEADDR`, `libc::TCP_NODELAY`,
+/// `libc::IP_ADD_MEMBERSHIP` and others. Will be passed as the third argument (`option_name`)
+/// to the `setsockopt` call.
+/// * Type of the value that you are going to set.
+/// * Type that implements the `Set` trait for the type from the previous item (like `SetBool` for
+/// `bool`, `SetUsize` for `usize`, etc.).
+macro_rules! setsockopt_impl {
+ ($name:ident, $level:expr, $flag:path, $ty:ty, $setter:ty) => {
+ impl SetSockOpt for $name {
+ type Val = $ty;
+
+ fn set(&self, fd: RawFd, val: &$ty) -> Result<()> {
+ unsafe {
+ let setter: $setter = Set::new(val);
+
+ let res = libc::setsockopt(
+ fd,
+ $level,
+ $flag,
+ setter.ffi_ptr(),
+ setter.ffi_len(),
+ );
+ Errno::result(res).map(drop)
+ }
+ }
+ }
+ };
+}
+
+/// Helper for implementing `GetSockOpt` for a given socket option. See
+/// [`::sys::socket::GetSockOpt`](sys/socket/trait.GetSockOpt.html).
+///
+/// This macro aims to help implementing `GetSockOpt` for different socket options that accept
+/// different kinds of data to be use with `getsockopt`.
+///
+/// Instead of using this macro directly consider using `sockopt_impl!`, especially if the option
+/// you are implementing represents a simple type.
+///
+/// # Arguments
+///
+/// * Name of the type you want to implement `GetSockOpt` for.
+/// * Socket layer, or a `protocol level`: could be *raw sockets* (`lic::SOL_SOCKET`), *ip
+/// protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`), and more. Please refer
+/// to your system manual for more options. Will be passed as the second argument (`level`) to
+/// the `getsockopt` call.
+/// * A flag to set. Some examples: `libc::SO_REUSEADDR`, `libc::TCP_NODELAY`,
+/// `libc::SO_ORIGINAL_DST` and others. Will be passed as the third argument (`option_name`) to
+/// the `getsockopt` call.
+/// * Type of the value that you are going to get.
+/// * Type that implements the `Get` trait for the type from the previous item (`GetBool` for
+/// `bool`, `GetUsize` for `usize`, etc.).
+macro_rules! getsockopt_impl {
+ ($name:ident, $level:expr, $flag:path, $ty:ty, $getter:ty) => {
+ impl GetSockOpt for $name {
+ type Val = $ty;
+
+ fn get(&self, fd: RawFd) -> Result<$ty> {
+ unsafe {
+ let mut getter: $getter = Get::uninit();
+
+ let res = libc::getsockopt(
+ fd,
+ $level,
+ $flag,
+ getter.ffi_ptr(),
+ getter.ffi_len(),
+ );
+ Errno::result(res)?;
+
+ match <$ty>::try_from(getter.assume_init()) {
+ Err(_) => Err(Errno::EINVAL),
+ Ok(r) => Ok(r)
+ }
+ }
+ }
+ }
+ };
+}
+
+/// Helper to generate the sockopt accessors. See
+/// [`::sys::socket::GetSockOpt`](sys/socket/trait.GetSockOpt.html) and
+/// [`::sys::socket::SetSockOpt`](sys/socket/trait.SetSockOpt.html).
+///
+/// This macro aims to help implementing `GetSockOpt` and `SetSockOpt` for different socket options
+/// that accept different kinds of data to be use with `getsockopt` and `setsockopt` respectively.
+///
+/// Basically this macro wraps up the [`getsockopt_impl!`](macro.getsockopt_impl.html) and
+/// [`setsockopt_impl!`](macro.setsockopt_impl.html) macros.
+///
+/// # Arguments
+///
+/// * `GetOnly`, `SetOnly` or `Both`: whether you want to implement only getter, only setter or
+/// both of them.
+/// * `$name:ident`: name of type `GetSockOpt`/`SetSockOpt` will be implemented for.
+/// * `$level:expr` : socket layer, or a `protocol level`: could be *raw sockets*
+/// (`lic::SOL_SOCKET`), *ip protocol* (libc::IPPROTO_IP), *tcp protocol* (`libc::IPPROTO_TCP`),
+/// and more. Please refer to your system manual for more options. Will be passed as the second
+/// argument (`level`) to the `getsockopt`/`setsockopt` call.
+/// * `$flag:path`: a flag name to set. Some examples: `libc::SO_REUSEADDR`, `libc::TCP_NODELAY`,
+/// `libc::IP_ADD_MEMBERSHIP` and others. Will be passed as the third argument (`option_name`)
+/// to the `setsockopt`/`getsockopt` call.
+/// * `$ty:ty`: type of the value that will be get/set.
+/// * `$getter:ty`: `Get` implementation; optional; only for `GetOnly` and `Both`.
+/// * `$setter:ty`: `Set` implementation; optional; only for `SetOnly` and `Both`.
+// Some targets don't use all rules.
+#[allow(unknown_lints)]
+#[allow(unused_macro_rules)]
+macro_rules! sockopt_impl {
+ ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, bool) => {
+ sockopt_impl!($(#[$attr])*
+ $name, GetOnly, $level, $flag, bool, GetBool);
+ };
+
+ ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, u8) => {
+ sockopt_impl!($(#[$attr])* $name, GetOnly, $level, $flag, u8, GetU8);
+ };
+
+ ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, usize) =>
+ {
+ sockopt_impl!($(#[$attr])*
+ $name, GetOnly, $level, $flag, usize, GetUsize);
+ };
+
+ ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, bool) => {
+ sockopt_impl!($(#[$attr])*
+ $name, SetOnly, $level, $flag, bool, SetBool);
+ };
+
+ ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, u8) => {
+ sockopt_impl!($(#[$attr])* $name, SetOnly, $level, $flag, u8, SetU8);
+ };
+
+ ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, usize) =>
+ {
+ sockopt_impl!($(#[$attr])*
+ $name, SetOnly, $level, $flag, usize, SetUsize);
+ };
+
+ ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, bool) => {
+ sockopt_impl!($(#[$attr])*
+ $name, Both, $level, $flag, bool, GetBool, SetBool);
+ };
+
+ ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, u8) => {
+ sockopt_impl!($(#[$attr])*
+ $name, Both, $level, $flag, u8, GetU8, SetU8);
+ };
+
+ ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, usize) => {
+ sockopt_impl!($(#[$attr])*
+ $name, Both, $level, $flag, usize, GetUsize, SetUsize);
+ };
+
+ ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path,
+ OsString<$array:ty>) =>
+ {
+ sockopt_impl!($(#[$attr])*
+ $name, Both, $level, $flag, OsString, GetOsString<$array>,
+ SetOsString);
+ };
+
+ /*
+ * Matchers with generic getter types must be placed at the end, so
+ * they'll only match _after_ specialized matchers fail
+ */
+ ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, $ty:ty) =>
+ {
+ sockopt_impl!($(#[$attr])*
+ $name, GetOnly, $level, $flag, $ty, GetStruct<$ty>);
+ };
+
+ ($(#[$attr:meta])* $name:ident, GetOnly, $level:expr, $flag:path, $ty:ty,
+ $getter:ty) =>
+ {
+ $(#[$attr])*
+ #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+ pub struct $name;
+
+ getsockopt_impl!($name, $level, $flag, $ty, $getter);
+ };
+
+ ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, $ty:ty) =>
+ {
+ sockopt_impl!($(#[$attr])*
+ $name, SetOnly, $level, $flag, $ty, SetStruct<$ty>);
+ };
+
+ ($(#[$attr:meta])* $name:ident, SetOnly, $level:expr, $flag:path, $ty:ty,
+ $setter:ty) =>
+ {
+ $(#[$attr])*
+ #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+ pub struct $name;
+
+ setsockopt_impl!($name, $level, $flag, $ty, $setter);
+ };
+
+ ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, $ty:ty,
+ $getter:ty, $setter:ty) =>
+ {
+ $(#[$attr])*
+ #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+ pub struct $name;
+
+ setsockopt_impl!($name, $level, $flag, $ty, $setter);
+ getsockopt_impl!($name, $level, $flag, $ty, $getter);
+ };
+
+ ($(#[$attr:meta])* $name:ident, Both, $level:expr, $flag:path, $ty:ty) => {
+ sockopt_impl!($(#[$attr])*
+ $name, Both, $level, $flag, $ty, GetStruct<$ty>,
+ SetStruct<$ty>);
+ };
+}
+
+/*
+ *
+ * ===== Define sockopts =====
+ *
+ */
+
+sockopt_impl!(
+ /// Enables local address reuse
+ ReuseAddr,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_REUSEADDR,
+ bool
+);
+#[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
+sockopt_impl!(
+ /// Permits multiple AF_INET or AF_INET6 sockets to be bound to an
+ /// identical socket address.
+ ReusePort,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_REUSEPORT,
+ bool
+);
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Under most circumstances, TCP sends data when it is presented; when
+ /// outstanding data has not yet been acknowledged, it gathers small amounts
+ /// of output to be sent in a single packet once an acknowledgement is
+ /// received. For a small number of clients, such as window systems that
+ /// send a stream of mouse events which receive no replies, this
+ /// packetization may cause significant delays. The boolean option
+ /// TCP_NODELAY defeats this algorithm.
+ TcpNoDelay,
+ Both,
+ libc::IPPROTO_TCP,
+ libc::TCP_NODELAY,
+ bool
+);
+sockopt_impl!(
+ /// When enabled, a close(2) or shutdown(2) will not return until all
+ /// queued messages for the socket have been successfully sent or the
+ /// linger timeout has been reached.
+ Linger,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_LINGER,
+ libc::linger
+);
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Join a multicast group
+ IpAddMembership,
+ SetOnly,
+ libc::IPPROTO_IP,
+ libc::IP_ADD_MEMBERSHIP,
+ super::IpMembershipRequest
+);
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Leave a multicast group.
+ IpDropMembership,
+ SetOnly,
+ libc::IPPROTO_IP,
+ libc::IP_DROP_MEMBERSHIP,
+ super::IpMembershipRequest
+);
+cfg_if! {
+ if #[cfg(any(target_os = "android", target_os = "linux"))] {
+ #[cfg(feature = "net")]
+ sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Join an IPv6 multicast group.
+ Ipv6AddMembership, SetOnly, libc::IPPROTO_IPV6, libc::IPV6_ADD_MEMBERSHIP, super::Ipv6MembershipRequest);
+ #[cfg(feature = "net")]
+ sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Leave an IPv6 multicast group.
+ Ipv6DropMembership, SetOnly, libc::IPPROTO_IPV6, libc::IPV6_DROP_MEMBERSHIP, super::Ipv6MembershipRequest);
+ } else if #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris"))] {
+ #[cfg(feature = "net")]
+ sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Join an IPv6 multicast group.
+ Ipv6AddMembership, SetOnly, libc::IPPROTO_IPV6,
+ libc::IPV6_JOIN_GROUP, super::Ipv6MembershipRequest);
+ #[cfg(feature = "net")]
+ sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Leave an IPv6 multicast group.
+ Ipv6DropMembership, SetOnly, libc::IPPROTO_IPV6,
+ libc::IPV6_LEAVE_GROUP, super::Ipv6MembershipRequest);
+ }
+}
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Set or read the time-to-live value of outgoing multicast packets for
+ /// this socket.
+ IpMulticastTtl,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_MULTICAST_TTL,
+ u8
+);
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Set or read a boolean integer argument that determines whether sent
+ /// multicast packets should be looped back to the local sockets.
+ IpMulticastLoop,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_MULTICAST_LOOP,
+ bool
+);
+#[cfg(target_os = "linux")]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Set the protocol-defined priority for all packets to be
+ /// sent on this socket
+ Priority,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_PRIORITY,
+ libc::c_int
+);
+#[cfg(target_os = "linux")]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Set or receive the Type-Of-Service (TOS) field that is
+ /// sent with every IP packet originating from this socket
+ IpTos,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_TOS,
+ libc::c_int
+);
+#[cfg(target_os = "linux")]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Traffic class associated with outgoing packets
+ Ipv6TClass,
+ Both,
+ libc::IPPROTO_IPV6,
+ libc::IPV6_TCLASS,
+ libc::c_int
+);
+#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// If enabled, this boolean option allows binding to an IP address that
+ /// is nonlocal or does not (yet) exist.
+ IpFreebind,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_FREEBIND,
+ bool
+);
+sockopt_impl!(
+ /// Specify the receiving timeout until reporting an error.
+ ReceiveTimeout,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_RCVTIMEO,
+ TimeVal
+);
+sockopt_impl!(
+ /// Specify the sending timeout until reporting an error.
+ SendTimeout,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_SNDTIMEO,
+ TimeVal
+);
+sockopt_impl!(
+ /// Set or get the broadcast flag.
+ Broadcast,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_BROADCAST,
+ bool
+);
+sockopt_impl!(
+ /// If this option is enabled, out-of-band data is directly placed into
+ /// the receive data stream.
+ OobInline,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_OOBINLINE,
+ bool
+);
+sockopt_impl!(
+ /// Get and clear the pending socket error.
+ SocketError,
+ GetOnly,
+ libc::SOL_SOCKET,
+ libc::SO_ERROR,
+ i32
+);
+sockopt_impl!(
+ /// Set or get the don't route flag.
+ DontRoute,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_DONTROUTE,
+ bool
+);
+sockopt_impl!(
+ /// Enable sending of keep-alive messages on connection-oriented sockets.
+ KeepAlive,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_KEEPALIVE,
+ bool
+);
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "ios"
+))]
+sockopt_impl!(
+ /// Get the credentials of the peer process of a connected unix domain
+ /// socket.
+ LocalPeerCred,
+ GetOnly,
+ 0,
+ libc::LOCAL_PEERCRED,
+ super::XuCred
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ /// Return the credentials of the foreign process connected to this socket.
+ PeerCredentials,
+ GetOnly,
+ libc::SOL_SOCKET,
+ libc::SO_PEERCRED,
+ super::UnixCredentials
+);
+#[cfg(any(target_os = "ios", target_os = "macos"))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Specify the amount of time, in seconds, that the connection must be idle
+ /// before keepalive probes (if enabled) are sent.
+ TcpKeepAlive,
+ Both,
+ libc::IPPROTO_TCP,
+ libc::TCP_KEEPALIVE,
+ u32
+);
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux"
+))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// The time (in seconds) the connection needs to remain idle before TCP
+ /// starts sending keepalive probes
+ TcpKeepIdle,
+ Both,
+ libc::IPPROTO_TCP,
+ libc::TCP_KEEPIDLE,
+ u32
+);
+cfg_if! {
+ if #[cfg(any(target_os = "android", target_os = "linux"))] {
+ sockopt_impl!(
+ /// The maximum segment size for outgoing TCP packets.
+ TcpMaxSeg, Both, libc::IPPROTO_TCP, libc::TCP_MAXSEG, u32);
+ } else {
+ sockopt_impl!(
+ /// The maximum segment size for outgoing TCP packets.
+ TcpMaxSeg, GetOnly, libc::IPPROTO_TCP, libc::TCP_MAXSEG, u32);
+ }
+}
+#[cfg(not(any(target_os = "openbsd", target_os = "haiku")))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// The maximum number of keepalive probes TCP should send before
+ /// dropping the connection.
+ TcpKeepCount,
+ Both,
+ libc::IPPROTO_TCP,
+ libc::TCP_KEEPCNT,
+ u32
+);
+#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+sockopt_impl!(
+ #[allow(missing_docs)]
+ // Not documented by Linux!
+ TcpRepair,
+ Both,
+ libc::IPPROTO_TCP,
+ libc::TCP_REPAIR,
+ u32
+);
+#[cfg(not(any(target_os = "openbsd", target_os = "haiku")))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// The time (in seconds) between individual keepalive probes.
+ TcpKeepInterval,
+ Both,
+ libc::IPPROTO_TCP,
+ libc::TCP_KEEPINTVL,
+ u32
+);
+#[cfg(any(target_os = "fuchsia", target_os = "linux"))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Specifies the maximum amount of time in milliseconds that transmitted
+ /// data may remain unacknowledged before TCP will forcibly close the
+ /// corresponding connection
+ TcpUserTimeout,
+ Both,
+ libc::IPPROTO_TCP,
+ libc::TCP_USER_TIMEOUT,
+ u32
+);
+sockopt_impl!(
+ /// Sets or gets the maximum socket receive buffer in bytes.
+ RcvBuf,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_RCVBUF,
+ usize
+);
+sockopt_impl!(
+ /// Sets or gets the maximum socket send buffer in bytes.
+ SndBuf,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_SNDBUF,
+ usize
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ /// Using this socket option, a privileged (`CAP_NET_ADMIN`) process can
+ /// perform the same task as `SO_RCVBUF`, but the `rmem_max limit` can be
+ /// overridden.
+ RcvBufForce,
+ SetOnly,
+ libc::SOL_SOCKET,
+ libc::SO_RCVBUFFORCE,
+ usize
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ /// Using this socket option, a privileged (`CAP_NET_ADMIN`) process can
+ /// perform the same task as `SO_SNDBUF`, but the `wmem_max` limit can be
+ /// overridden.
+ SndBufForce,
+ SetOnly,
+ libc::SOL_SOCKET,
+ libc::SO_SNDBUFFORCE,
+ usize
+);
+sockopt_impl!(
+ /// Gets the socket type as an integer.
+ SockType,
+ GetOnly,
+ libc::SOL_SOCKET,
+ libc::SO_TYPE,
+ super::SockType,
+ GetStruct<i32>
+);
+sockopt_impl!(
+ /// Returns a value indicating whether or not this socket has been marked to
+ /// accept connections with `listen(2)`.
+ AcceptConn,
+ GetOnly,
+ libc::SOL_SOCKET,
+ libc::SO_ACCEPTCONN,
+ bool
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ /// Bind this socket to a particular device like “eth0”.
+ BindToDevice,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_BINDTODEVICE,
+ OsString<[u8; libc::IFNAMSIZ]>
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ #[allow(missing_docs)]
+ // Not documented by Linux!
+ OriginalDst,
+ GetOnly,
+ libc::SOL_IP,
+ libc::SO_ORIGINAL_DST,
+ libc::sockaddr_in
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ #[allow(missing_docs)]
+ // Not documented by Linux!
+ Ip6tOriginalDst,
+ GetOnly,
+ libc::SOL_IPV6,
+ libc::IP6T_SO_ORIGINAL_DST,
+ libc::sockaddr_in6
+);
+#[cfg(any(target_os = "linux"))]
+sockopt_impl!(
+ /// Specifies exact type of timestamping information collected by the kernel
+ /// [Further reading](https://www.kernel.org/doc/html/latest/networking/timestamping.html)
+ Timestamping,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_TIMESTAMPING,
+ super::TimestampingFlag
+);
+#[cfg(not(target_os = "haiku"))]
+sockopt_impl!(
+ /// Enable or disable the receiving of the `SO_TIMESTAMP` control message.
+ ReceiveTimestamp,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_TIMESTAMP,
+ bool
+);
+#[cfg(all(target_os = "linux"))]
+sockopt_impl!(
+ /// Enable or disable the receiving of the `SO_TIMESTAMPNS` control message.
+ ReceiveTimestampns,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_TIMESTAMPNS,
+ bool
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Setting this boolean option enables transparent proxying on this socket.
+ IpTransparent,
+ Both,
+ libc::SOL_IP,
+ libc::IP_TRANSPARENT,
+ bool
+);
+#[cfg(target_os = "openbsd")]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Allows the socket to be bound to addresses which are not local to the
+ /// machine, so it can be used to make a transparent proxy.
+ BindAny,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_BINDANY,
+ bool
+);
+#[cfg(target_os = "freebsd")]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Can `bind(2)` to any address, even one not bound to any available
+ /// network interface in the system.
+ BindAny,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_BINDANY,
+ bool
+);
+#[cfg(target_os = "linux")]
+sockopt_impl!(
+ /// Set the mark for each packet sent through this socket (similar to the
+ /// netfilter MARK target but socket-based).
+ Mark,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_MARK,
+ u32
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ /// Enable or disable the receiving of the `SCM_CREDENTIALS` control
+ /// message.
+ PassCred,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_PASSCRED,
+ bool
+);
+#[cfg(any(target_os = "freebsd", target_os = "linux"))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// This option allows the caller to set the TCP congestion control
+ /// algorithm to be used, on a per-socket basis.
+ TcpCongestion,
+ Both,
+ libc::IPPROTO_TCP,
+ libc::TCP_CONGESTION,
+ OsString<[u8; TCP_CA_NAME_MAX]>
+);
+#[cfg(any(
+ target_os = "android",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Pass an `IP_PKTINFO` ancillary message that contains a pktinfo
+ /// structure that supplies some information about the incoming packet.
+ Ipv4PacketInfo,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_PKTINFO,
+ bool
+);
+#[cfg(any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// Set delivery of the `IPV6_PKTINFO` control message on incoming
+ /// datagrams.
+ Ipv6RecvPacketInfo,
+ Both,
+ libc::IPPROTO_IPV6,
+ libc::IPV6_RECVPKTINFO,
+ bool
+);
+#[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// The `recvmsg(2)` call returns a `struct sockaddr_dl` corresponding to
+ /// the interface on which the packet was received.
+ Ipv4RecvIf,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_RECVIF,
+ bool
+);
+#[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// The `recvmsg(2)` call will return the destination IP address for a UDP
+ /// datagram.
+ Ipv4RecvDstAddr,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_RECVDSTADDR,
+ bool
+);
+#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// The `recvmsg(2)` call will return the destination IP address for a UDP
+ /// datagram.
+ Ipv4OrigDstAddr,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_ORIGDSTADDR,
+ bool
+);
+#[cfg(target_os = "linux")]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ #[allow(missing_docs)]
+ // Not documented by Linux!
+ UdpGsoSegment,
+ Both,
+ libc::SOL_UDP,
+ libc::UDP_SEGMENT,
+ libc::c_int
+);
+#[cfg(target_os = "linux")]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ #[allow(missing_docs)]
+ // Not documented by Linux!
+ UdpGroSegment,
+ Both,
+ libc::IPPROTO_UDP,
+ libc::UDP_GRO,
+ bool
+);
+#[cfg(target_os = "linux")]
+sockopt_impl!(
+ /// Configures the behavior of time-based transmission of packets, for use
+ /// with the `TxTime` control message.
+ TxTime,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_TXTIME,
+ libc::sock_txtime
+);
+#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+sockopt_impl!(
+ /// Indicates that an unsigned 32-bit value ancillary message (cmsg) should
+ /// be attached to received skbs indicating the number of packets dropped by
+ /// the socket since its creation.
+ RxqOvfl,
+ Both,
+ libc::SOL_SOCKET,
+ libc::SO_RXQ_OVFL,
+ libc::c_int
+);
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// The socket is restricted to sending and receiving IPv6 packets only.
+ Ipv6V6Only,
+ Both,
+ libc::IPPROTO_IPV6,
+ libc::IPV6_V6ONLY,
+ bool
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ /// Enable extended reliable error message passing.
+ Ipv4RecvErr,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_RECVERR,
+ bool
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ /// Control receiving of asynchronous error options.
+ Ipv6RecvErr,
+ Both,
+ libc::IPPROTO_IPV6,
+ libc::IPV6_RECVERR,
+ bool
+);
+#[cfg(any(target_os = "android", target_os = "linux"))]
+sockopt_impl!(
+ /// Fetch the current system-estimated Path MTU.
+ IpMtu,
+ GetOnly,
+ libc::IPPROTO_IP,
+ libc::IP_MTU,
+ libc::c_int
+);
+#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+sockopt_impl!(
+ /// Set or retrieve the current time-to-live field that is used in every
+ /// packet sent from this socket.
+ Ipv4Ttl,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_TTL,
+ libc::c_int
+);
+#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+sockopt_impl!(
+ /// Set the unicast hop limit for the socket.
+ Ipv6Ttl,
+ Both,
+ libc::IPPROTO_IPV6,
+ libc::IPV6_UNICAST_HOPS,
+ libc::c_int
+);
+#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+#[cfg(feature = "net")]
+sockopt_impl!(
+ #[cfg_attr(docsrs, doc(cfg(feature = "net")))]
+ /// The `recvmsg(2)` call will return the destination IP address for a UDP
+ /// datagram.
+ Ipv6OrigDstAddr,
+ Both,
+ libc::IPPROTO_IPV6,
+ libc::IPV6_ORIGDSTADDR,
+ bool
+);
+#[cfg(any(target_os = "ios", target_os = "macos"))]
+sockopt_impl!(
+ /// Set "don't fragment packet" flag on the IP packet.
+ IpDontFrag,
+ Both,
+ libc::IPPROTO_IP,
+ libc::IP_DONTFRAG,
+ bool
+);
+#[cfg(any(
+ target_os = "android",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+))]
+sockopt_impl!(
+ /// Set "don't fragment packet" flag on the IPv6 packet.
+ Ipv6DontFrag,
+ Both,
+ libc::IPPROTO_IPV6,
+ libc::IPV6_DONTFRAG,
+ bool
+);
+
+#[allow(missing_docs)]
+// Not documented by Linux!
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[derive(Copy, Clone, Debug)]
+pub struct AlgSetAeadAuthSize;
+
+// ALG_SET_AEAD_AUTH_SIZE read the length from passed `option_len`
+// See https://elixir.bootlin.com/linux/v4.4/source/crypto/af_alg.c#L222
+#[cfg(any(target_os = "android", target_os = "linux"))]
+impl SetSockOpt for AlgSetAeadAuthSize {
+ type Val = usize;
+
+ fn set(&self, fd: RawFd, val: &usize) -> Result<()> {
+ unsafe {
+ let res = libc::setsockopt(
+ fd,
+ libc::SOL_ALG,
+ libc::ALG_SET_AEAD_AUTHSIZE,
+ ::std::ptr::null(),
+ *val as libc::socklen_t,
+ );
+ Errno::result(res).map(drop)
+ }
+ }
+}
+
+#[allow(missing_docs)]
+// Not documented by Linux!
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[derive(Clone, Debug)]
+pub struct AlgSetKey<T>(::std::marker::PhantomData<T>);
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+impl<T> Default for AlgSetKey<T> {
+ fn default() -> Self {
+ AlgSetKey(Default::default())
+ }
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+impl<T> SetSockOpt for AlgSetKey<T>
+where
+ T: AsRef<[u8]> + Clone,
+{
+ type Val = T;
+
+ fn set(&self, fd: RawFd, val: &T) -> Result<()> {
+ unsafe {
+ let res = libc::setsockopt(
+ fd,
+ libc::SOL_ALG,
+ libc::ALG_SET_KEY,
+ val.as_ref().as_ptr() as *const _,
+ val.as_ref().len() as libc::socklen_t,
+ );
+ Errno::result(res).map(drop)
+ }
+ }
+}
+
+/*
+ *
+ * ===== Accessor helpers =====
+ *
+ */
+
+/// Helper trait that describes what is expected from a `GetSockOpt` getter.
+trait Get<T> {
+ /// Returns an uninitialized value.
+ fn uninit() -> Self;
+ /// Returns a pointer to the stored value. This pointer will be passed to the system's
+ /// `getsockopt` call (`man 3p getsockopt`, argument `option_value`).
+ fn ffi_ptr(&mut self) -> *mut c_void;
+ /// Returns length of the stored value. This pointer will be passed to the system's
+ /// `getsockopt` call (`man 3p getsockopt`, argument `option_len`).
+ fn ffi_len(&mut self) -> *mut socklen_t;
+ /// Returns the hopefully initialized inner value.
+ unsafe fn assume_init(self) -> T;
+}
+
+/// Helper trait that describes what is expected from a `SetSockOpt` setter.
+trait Set<'a, T> {
+ /// Initialize the setter with a given value.
+ fn new(val: &'a T) -> Self;
+ /// Returns a pointer to the stored value. This pointer will be passed to the system's
+ /// `setsockopt` call (`man 3p setsockopt`, argument `option_value`).
+ fn ffi_ptr(&self) -> *const c_void;
+ /// Returns length of the stored value. This pointer will be passed to the system's
+ /// `setsockopt` call (`man 3p setsockopt`, argument `option_len`).
+ fn ffi_len(&self) -> socklen_t;
+}
+
+/// Getter for an arbitrary `struct`.
+struct GetStruct<T> {
+ len: socklen_t,
+ val: MaybeUninit<T>,
+}
+
+impl<T> Get<T> for GetStruct<T> {
+ fn uninit() -> Self {
+ GetStruct {
+ len: mem::size_of::<T>() as socklen_t,
+ val: MaybeUninit::uninit(),
+ }
+ }
+
+ fn ffi_ptr(&mut self) -> *mut c_void {
+ self.val.as_mut_ptr() as *mut c_void
+ }
+
+ fn ffi_len(&mut self) -> *mut socklen_t {
+ &mut self.len
+ }
+
+ unsafe fn assume_init(self) -> T {
+ assert_eq!(
+ self.len as usize,
+ mem::size_of::<T>(),
+ "invalid getsockopt implementation"
+ );
+ self.val.assume_init()
+ }
+}
+
+/// Setter for an arbitrary `struct`.
+struct SetStruct<'a, T: 'static> {
+ ptr: &'a T,
+}
+
+impl<'a, T> Set<'a, T> for SetStruct<'a, T> {
+ fn new(ptr: &'a T) -> SetStruct<'a, T> {
+ SetStruct { ptr }
+ }
+
+ fn ffi_ptr(&self) -> *const c_void {
+ self.ptr as *const T as *const c_void
+ }
+
+ fn ffi_len(&self) -> socklen_t {
+ mem::size_of::<T>() as socklen_t
+ }
+}
+
+/// Getter for a boolean value.
+struct GetBool {
+ len: socklen_t,
+ val: MaybeUninit<c_int>,
+}
+
+impl Get<bool> for GetBool {
+ fn uninit() -> Self {
+ GetBool {
+ len: mem::size_of::<c_int>() as socklen_t,
+ val: MaybeUninit::uninit(),
+ }
+ }
+
+ fn ffi_ptr(&mut self) -> *mut c_void {
+ self.val.as_mut_ptr() as *mut c_void
+ }
+
+ fn ffi_len(&mut self) -> *mut socklen_t {
+ &mut self.len
+ }
+
+ unsafe fn assume_init(self) -> bool {
+ assert_eq!(
+ self.len as usize,
+ mem::size_of::<c_int>(),
+ "invalid getsockopt implementation"
+ );
+ self.val.assume_init() != 0
+ }
+}
+
+/// Setter for a boolean value.
+struct SetBool {
+ val: c_int,
+}
+
+impl<'a> Set<'a, bool> for SetBool {
+ fn new(val: &'a bool) -> SetBool {
+ SetBool {
+ val: i32::from(*val),
+ }
+ }
+
+ fn ffi_ptr(&self) -> *const c_void {
+ &self.val as *const c_int as *const c_void
+ }
+
+ fn ffi_len(&self) -> socklen_t {
+ mem::size_of::<c_int>() as socklen_t
+ }
+}
+
+/// Getter for an `u8` value.
+struct GetU8 {
+ len: socklen_t,
+ val: MaybeUninit<u8>,
+}
+
+impl Get<u8> for GetU8 {
+ fn uninit() -> Self {
+ GetU8 {
+ len: mem::size_of::<u8>() as socklen_t,
+ val: MaybeUninit::uninit(),
+ }
+ }
+
+ fn ffi_ptr(&mut self) -> *mut c_void {
+ self.val.as_mut_ptr() as *mut c_void
+ }
+
+ fn ffi_len(&mut self) -> *mut socklen_t {
+ &mut self.len
+ }
+
+ unsafe fn assume_init(self) -> u8 {
+ assert_eq!(
+ self.len as usize,
+ mem::size_of::<u8>(),
+ "invalid getsockopt implementation"
+ );
+ self.val.assume_init()
+ }
+}
+
+/// Setter for an `u8` value.
+struct SetU8 {
+ val: u8,
+}
+
+impl<'a> Set<'a, u8> for SetU8 {
+ fn new(val: &'a u8) -> SetU8 {
+ SetU8 { val: *val }
+ }
+
+ fn ffi_ptr(&self) -> *const c_void {
+ &self.val as *const u8 as *const c_void
+ }
+
+ fn ffi_len(&self) -> socklen_t {
+ mem::size_of::<c_int>() as socklen_t
+ }
+}
+
+/// Getter for an `usize` value.
+struct GetUsize {
+ len: socklen_t,
+ val: MaybeUninit<c_int>,
+}
+
+impl Get<usize> for GetUsize {
+ fn uninit() -> Self {
+ GetUsize {
+ len: mem::size_of::<c_int>() as socklen_t,
+ val: MaybeUninit::uninit(),
+ }
+ }
+
+ fn ffi_ptr(&mut self) -> *mut c_void {
+ self.val.as_mut_ptr() as *mut c_void
+ }
+
+ fn ffi_len(&mut self) -> *mut socklen_t {
+ &mut self.len
+ }
+
+ unsafe fn assume_init(self) -> usize {
+ assert_eq!(
+ self.len as usize,
+ mem::size_of::<c_int>(),
+ "invalid getsockopt implementation"
+ );
+ self.val.assume_init() as usize
+ }
+}
+
+/// Setter for an `usize` value.
+struct SetUsize {
+ val: c_int,
+}
+
+impl<'a> Set<'a, usize> for SetUsize {
+ fn new(val: &'a usize) -> SetUsize {
+ SetUsize { val: *val as c_int }
+ }
+
+ fn ffi_ptr(&self) -> *const c_void {
+ &self.val as *const c_int as *const c_void
+ }
+
+ fn ffi_len(&self) -> socklen_t {
+ mem::size_of::<c_int>() as socklen_t
+ }
+}
+
+/// Getter for a `OsString` value.
+struct GetOsString<T: AsMut<[u8]>> {
+ len: socklen_t,
+ val: MaybeUninit<T>,
+}
+
+impl<T: AsMut<[u8]>> Get<OsString> for GetOsString<T> {
+ fn uninit() -> Self {
+ GetOsString {
+ len: mem::size_of::<T>() as socklen_t,
+ val: MaybeUninit::uninit(),
+ }
+ }
+
+ fn ffi_ptr(&mut self) -> *mut c_void {
+ self.val.as_mut_ptr() as *mut c_void
+ }
+
+ fn ffi_len(&mut self) -> *mut socklen_t {
+ &mut self.len
+ }
+
+ unsafe fn assume_init(self) -> OsString {
+ let len = self.len as usize;
+ let mut v = self.val.assume_init();
+ OsStr::from_bytes(&v.as_mut()[0..len]).to_owned()
+ }
+}
+
+/// Setter for a `OsString` value.
+struct SetOsString<'a> {
+ val: &'a OsStr,
+}
+
+impl<'a> Set<'a, OsString> for SetOsString<'a> {
+ fn new(val: &'a OsString) -> SetOsString {
+ SetOsString {
+ val: val.as_os_str(),
+ }
+ }
+
+ fn ffi_ptr(&self) -> *const c_void {
+ self.val.as_bytes().as_ptr() as *const c_void
+ }
+
+ fn ffi_len(&self) -> socklen_t {
+ self.val.len() as socklen_t
+ }
+}
+
+#[cfg(test)]
+mod test {
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[test]
+ fn can_get_peercred_on_unix_socket() {
+ use super::super::*;
+
+ let (a, b) = socketpair(
+ AddressFamily::Unix,
+ SockType::Stream,
+ None,
+ SockFlag::empty(),
+ )
+ .unwrap();
+ let a_cred = getsockopt(a, super::PeerCredentials).unwrap();
+ let b_cred = getsockopt(b, super::PeerCredentials).unwrap();
+ assert_eq!(a_cred, b_cred);
+ assert_ne!(a_cred.pid(), 0);
+ }
+
+ #[test]
+ fn is_socket_type_unix() {
+ use super::super::*;
+ use crate::unistd::close;
+
+ let (a, b) = socketpair(
+ AddressFamily::Unix,
+ SockType::Stream,
+ None,
+ SockFlag::empty(),
+ )
+ .unwrap();
+ let a_type = getsockopt(a, super::SockType).unwrap();
+ assert_eq!(a_type, SockType::Stream);
+ close(a).unwrap();
+ close(b).unwrap();
+ }
+
+ #[test]
+ fn is_socket_type_dgram() {
+ use super::super::*;
+ use crate::unistd::close;
+
+ let s = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+ let s_type = getsockopt(s, super::SockType).unwrap();
+ assert_eq!(s_type, SockType::Datagram);
+ close(s).unwrap();
+ }
+
+ #[cfg(any(target_os = "freebsd", target_os = "linux"))]
+ #[test]
+ fn can_get_listen_on_tcp_socket() {
+ use super::super::*;
+ use crate::unistd::close;
+
+ let s = socket(
+ AddressFamily::Inet,
+ SockType::Stream,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+ let s_listening = getsockopt(s, super::AcceptConn).unwrap();
+ assert!(!s_listening);
+ listen(s, 10).unwrap();
+ let s_listening2 = getsockopt(s, super::AcceptConn).unwrap();
+ assert!(s_listening2);
+ close(s).unwrap();
+ }
+}
diff --git a/third_party/rust/nix/src/sys/stat.rs b/third_party/rust/nix/src/sys/stat.rs
new file mode 100644
index 0000000000..78203bfbe3
--- /dev/null
+++ b/third_party/rust/nix/src/sys/stat.rs
@@ -0,0 +1,480 @@
+#[cfg(any(target_os = "macos", target_os = "ios", target_os = "openbsd"))]
+pub use libc::c_uint;
+#[cfg(any(
+ target_os = "netbsd",
+ target_os = "freebsd",
+ target_os = "dragonfly"
+))]
+pub use libc::c_ulong;
+pub use libc::stat as FileStat;
+pub use libc::{dev_t, mode_t};
+
+#[cfg(not(target_os = "redox"))]
+use crate::fcntl::{at_rawfd, AtFlags};
+use crate::sys::time::{TimeSpec, TimeVal};
+use crate::{errno::Errno, NixPath, Result};
+use std::mem;
+use std::os::unix::io::RawFd;
+
+libc_bitflags!(
+ /// "File type" flags for `mknod` and related functions.
+ pub struct SFlag: mode_t {
+ S_IFIFO;
+ S_IFCHR;
+ S_IFDIR;
+ S_IFBLK;
+ S_IFREG;
+ S_IFLNK;
+ S_IFSOCK;
+ S_IFMT;
+ }
+);
+
+libc_bitflags! {
+ /// "File mode / permissions" flags.
+ pub struct Mode: mode_t {
+ /// Read, write and execute for owner.
+ S_IRWXU;
+ /// Read for owner.
+ S_IRUSR;
+ /// Write for owner.
+ S_IWUSR;
+ /// Execute for owner.
+ S_IXUSR;
+ /// Read write and execute for group.
+ S_IRWXG;
+ /// Read fr group.
+ S_IRGRP;
+ /// Write for group.
+ S_IWGRP;
+ /// Execute for group.
+ S_IXGRP;
+ /// Read, write and execute for other.
+ S_IRWXO;
+ /// Read for other.
+ S_IROTH;
+ /// Write for other.
+ S_IWOTH;
+ /// Execute for other.
+ S_IXOTH;
+ /// Set user id on execution.
+ S_ISUID as mode_t;
+ /// Set group id on execution.
+ S_ISGID as mode_t;
+ S_ISVTX as mode_t;
+ }
+}
+
+#[cfg(any(target_os = "macos", target_os = "ios", target_os = "openbsd"))]
+pub type type_of_file_flag = c_uint;
+#[cfg(any(
+ target_os = "netbsd",
+ target_os = "freebsd",
+ target_os = "dragonfly"
+))]
+pub type type_of_file_flag = c_ulong;
+
+#[cfg(any(
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "macos",
+ target_os = "ios"
+))]
+libc_bitflags! {
+ /// File flags.
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub struct FileFlag: type_of_file_flag {
+ /// The file may only be appended to.
+ SF_APPEND;
+ /// The file has been archived.
+ SF_ARCHIVED;
+ #[cfg(any(target_os = "dragonfly"))]
+ SF_CACHE;
+ /// The file may not be changed.
+ SF_IMMUTABLE;
+ /// Indicates a WAPBL journal file.
+ #[cfg(any(target_os = "netbsd"))]
+ SF_LOG;
+ /// Do not retain history for file
+ #[cfg(any(target_os = "dragonfly"))]
+ SF_NOHISTORY;
+ /// The file may not be renamed or deleted.
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ SF_NOUNLINK;
+ /// Mask of superuser changeable flags
+ SF_SETTABLE;
+ /// Snapshot is invalid.
+ #[cfg(any(target_os = "netbsd"))]
+ SF_SNAPINVAL;
+ /// The file is a snapshot file.
+ #[cfg(any(target_os = "netbsd", target_os = "freebsd"))]
+ SF_SNAPSHOT;
+ #[cfg(any(target_os = "dragonfly"))]
+ SF_XLINK;
+ /// The file may only be appended to.
+ UF_APPEND;
+ /// The file needs to be archived.
+ #[cfg(any(target_os = "freebsd"))]
+ UF_ARCHIVE;
+ #[cfg(any(target_os = "dragonfly"))]
+ UF_CACHE;
+ /// File is compressed at the file system level.
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ UF_COMPRESSED;
+ /// The file may be hidden from directory listings at the application's
+ /// discretion.
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "ios",
+ ))]
+ UF_HIDDEN;
+ /// The file may not be changed.
+ UF_IMMUTABLE;
+ /// Do not dump the file.
+ UF_NODUMP;
+ #[cfg(any(target_os = "dragonfly"))]
+ UF_NOHISTORY;
+ /// The file may not be renamed or deleted.
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ UF_NOUNLINK;
+ /// The file is offline, or has the Windows and CIFS
+ /// `FILE_ATTRIBUTE_OFFLINE` attribute.
+ #[cfg(any(target_os = "freebsd"))]
+ UF_OFFLINE;
+ /// The directory is opaque when viewed through a union stack.
+ UF_OPAQUE;
+ /// The file is read only, and may not be written or appended.
+ #[cfg(any(target_os = "freebsd"))]
+ UF_READONLY;
+ /// The file contains a Windows reparse point.
+ #[cfg(any(target_os = "freebsd"))]
+ UF_REPARSE;
+ /// Mask of owner changeable flags.
+ UF_SETTABLE;
+ /// The file has the Windows `FILE_ATTRIBUTE_SPARSE_FILE` attribute.
+ #[cfg(any(target_os = "freebsd"))]
+ UF_SPARSE;
+ /// The file has the DOS, Windows and CIFS `FILE_ATTRIBUTE_SYSTEM`
+ /// attribute.
+ #[cfg(any(target_os = "freebsd"))]
+ UF_SYSTEM;
+ /// File renames and deletes are tracked.
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ UF_TRACKED;
+ #[cfg(any(target_os = "dragonfly"))]
+ UF_XLINK;
+ }
+}
+
+/// Create a special or ordinary file, by pathname.
+pub fn mknod<P: ?Sized + NixPath>(
+ path: &P,
+ kind: SFlag,
+ perm: Mode,
+ dev: dev_t,
+) -> Result<()> {
+ let res = path.with_nix_path(|cstr| unsafe {
+ libc::mknod(cstr.as_ptr(), kind.bits | perm.bits() as mode_t, dev)
+ })?;
+
+ Errno::result(res).map(drop)
+}
+
+/// Create a special or ordinary file, relative to a given directory.
+#[cfg(not(any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "redox",
+ target_os = "haiku"
+)))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub fn mknodat<P: ?Sized + NixPath>(
+ dirfd: RawFd,
+ path: &P,
+ kind: SFlag,
+ perm: Mode,
+ dev: dev_t,
+) -> Result<()> {
+ let res = path.with_nix_path(|cstr| unsafe {
+ libc::mknodat(
+ dirfd,
+ cstr.as_ptr(),
+ kind.bits | perm.bits() as mode_t,
+ dev,
+ )
+ })?;
+
+ Errno::result(res).map(drop)
+}
+
+#[cfg(target_os = "linux")]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub const fn major(dev: dev_t) -> u64 {
+ ((dev >> 32) & 0xffff_f000) | ((dev >> 8) & 0x0000_0fff)
+}
+
+#[cfg(target_os = "linux")]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub const fn minor(dev: dev_t) -> u64 {
+ ((dev >> 12) & 0xffff_ff00) | ((dev) & 0x0000_00ff)
+}
+
+#[cfg(target_os = "linux")]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub const fn makedev(major: u64, minor: u64) -> dev_t {
+ ((major & 0xffff_f000) << 32)
+ | ((major & 0x0000_0fff) << 8)
+ | ((minor & 0xffff_ff00) << 12)
+ | (minor & 0x0000_00ff)
+}
+
+pub fn umask(mode: Mode) -> Mode {
+ let prev = unsafe { libc::umask(mode.bits() as mode_t) };
+ Mode::from_bits(prev).expect("[BUG] umask returned invalid Mode")
+}
+
+pub fn stat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> {
+ let mut dst = mem::MaybeUninit::uninit();
+ let res = path.with_nix_path(|cstr| unsafe {
+ libc::stat(cstr.as_ptr(), dst.as_mut_ptr())
+ })?;
+
+ Errno::result(res)?;
+
+ Ok(unsafe { dst.assume_init() })
+}
+
+pub fn lstat<P: ?Sized + NixPath>(path: &P) -> Result<FileStat> {
+ let mut dst = mem::MaybeUninit::uninit();
+ let res = path.with_nix_path(|cstr| unsafe {
+ libc::lstat(cstr.as_ptr(), dst.as_mut_ptr())
+ })?;
+
+ Errno::result(res)?;
+
+ Ok(unsafe { dst.assume_init() })
+}
+
+pub fn fstat(fd: RawFd) -> Result<FileStat> {
+ let mut dst = mem::MaybeUninit::uninit();
+ let res = unsafe { libc::fstat(fd, dst.as_mut_ptr()) };
+
+ Errno::result(res)?;
+
+ Ok(unsafe { dst.assume_init() })
+}
+
+#[cfg(not(target_os = "redox"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub fn fstatat<P: ?Sized + NixPath>(
+ dirfd: RawFd,
+ pathname: &P,
+ f: AtFlags,
+) -> Result<FileStat> {
+ let mut dst = mem::MaybeUninit::uninit();
+ let res = pathname.with_nix_path(|cstr| unsafe {
+ libc::fstatat(
+ dirfd,
+ cstr.as_ptr(),
+ dst.as_mut_ptr(),
+ f.bits() as libc::c_int,
+ )
+ })?;
+
+ Errno::result(res)?;
+
+ Ok(unsafe { dst.assume_init() })
+}
+
+/// Change the file permission bits of the file specified by a file descriptor.
+///
+/// # References
+///
+/// [fchmod(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmod.html).
+pub fn fchmod(fd: RawFd, mode: Mode) -> Result<()> {
+ let res = unsafe { libc::fchmod(fd, mode.bits() as mode_t) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Flags for `fchmodat` function.
+#[derive(Clone, Copy, Debug)]
+pub enum FchmodatFlags {
+ FollowSymlink,
+ NoFollowSymlink,
+}
+
+/// Change the file permission bits.
+///
+/// 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 `FchmodatFlags::NoFollowSymlink` and `path` names a symbolic link,
+/// then the mode of the symbolic link is changed.
+///
+/// `fchmodat(None, path, mode, FchmodatFlags::FollowSymlink)` is identical to
+/// a call `libc::chmod(path, mode)`. That's why `chmod` is unimplemented
+/// in the `nix` crate.
+///
+/// # References
+///
+/// [fchmodat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchmodat.html).
+#[cfg(not(target_os = "redox"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub fn fchmodat<P: ?Sized + NixPath>(
+ dirfd: Option<RawFd>,
+ path: &P,
+ mode: Mode,
+ flag: FchmodatFlags,
+) -> Result<()> {
+ let atflag = match flag {
+ FchmodatFlags::FollowSymlink => AtFlags::empty(),
+ FchmodatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW,
+ };
+ let res = path.with_nix_path(|cstr| unsafe {
+ libc::fchmodat(
+ at_rawfd(dirfd),
+ cstr.as_ptr(),
+ mode.bits() as mode_t,
+ atflag.bits() as libc::c_int,
+ )
+ })?;
+
+ Errno::result(res).map(drop)
+}
+
+/// Change the access and modification times of a file.
+///
+/// `utimes(path, times)` is identical to
+/// `utimensat(None, path, times, UtimensatFlags::FollowSymlink)`. The former
+/// is a deprecated API so prefer using the latter if the platforms you care
+/// about support it.
+///
+/// # References
+///
+/// [utimes(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimes.html).
+pub fn utimes<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::utimes(cstr.as_ptr(), &times[0])
+ })?;
+
+ 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)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/lutimes.html).
+#[cfg(any(
+ target_os = "linux",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "netbsd"
+))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+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(), &times[0])
+ })?;
+
+ Errno::result(res).map(drop)
+}
+
+/// Change the access and modification times of the file specified by a file descriptor.
+///
+/// # References
+///
+/// [futimens(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/futimens.html).
+#[inline]
+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, &times[0]) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Flags for `utimensat` function.
+// TODO: replace with fcntl::AtFlags
+#[derive(Clone, Copy, Debug)]
+pub enum UtimensatFlags {
+ FollowSymlink,
+ NoFollowSymlink,
+}
+
+/// Change the access and modification times of a file.
+///
+/// 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 `UtimensatFlags::NoFollowSymlink` and `path` names a symbolic link,
+/// then the mode of the symbolic link is changed.
+///
+/// `utimensat(None, path, times, UtimensatFlags::FollowSymlink)` is identical to
+/// `utimes(path, times)`. The latter is a deprecated API so prefer using the
+/// former if the platforms you care about support it.
+///
+/// # References
+///
+/// [utimensat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/utimens.html).
+#[cfg(not(target_os = "redox"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub fn utimensat<P: ?Sized + NixPath>(
+ dirfd: Option<RawFd>,
+ path: &P,
+ atime: &TimeSpec,
+ mtime: &TimeSpec,
+ flag: UtimensatFlags,
+) -> Result<()> {
+ let atflag = match flag {
+ UtimensatFlags::FollowSymlink => AtFlags::empty(),
+ UtimensatFlags::NoFollowSymlink => AtFlags::AT_SYMLINK_NOFOLLOW,
+ };
+ let times: [libc::timespec; 2] = [*atime.as_ref(), *mtime.as_ref()];
+ let res = path.with_nix_path(|cstr| unsafe {
+ libc::utimensat(
+ at_rawfd(dirfd),
+ cstr.as_ptr(),
+ &times[0],
+ atflag.bits() as libc::c_int,
+ )
+ })?;
+
+ Errno::result(res).map(drop)
+}
+
+#[cfg(not(target_os = "redox"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub fn mkdirat<P: ?Sized + NixPath>(
+ fd: RawFd,
+ path: &P,
+ mode: Mode,
+) -> Result<()> {
+ let res = path.with_nix_path(|cstr| unsafe {
+ libc::mkdirat(fd, cstr.as_ptr(), mode.bits() as mode_t)
+ })?;
+
+ Errno::result(res).map(drop)
+}
diff --git a/third_party/rust/nix/src/sys/statfs.rs b/third_party/rust/nix/src/sys/statfs.rs
new file mode 100644
index 0000000000..9be8ca6667
--- /dev/null
+++ b/third_party/rust/nix/src/sys/statfs.rs
@@ -0,0 +1,853 @@
+//! Get filesystem statistics, non-portably
+//!
+//! See [`statvfs`](crate::sys::statvfs) for a portable alternative.
+#[cfg(not(any(target_os = "linux", target_os = "android")))]
+use std::ffi::CStr;
+use std::fmt::{self, Debug};
+use std::mem;
+use std::os::unix::io::AsRawFd;
+
+use cfg_if::cfg_if;
+
+#[cfg(all(
+ feature = "mount",
+ any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ )
+))]
+use crate::mount::MntFlags;
+#[cfg(target_os = "linux")]
+use crate::sys::statvfs::FsFlags;
+use crate::{errno::Errno, NixPath, Result};
+
+/// Identifies a mounted file system
+#[cfg(target_os = "android")]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub type fsid_t = libc::__fsid_t;
+/// Identifies a mounted file system
+#[cfg(not(target_os = "android"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub type fsid_t = libc::fsid_t;
+
+cfg_if! {
+ if #[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))] {
+ type type_of_statfs = libc::statfs64;
+ const LIBC_FSTATFS: unsafe extern fn
+ (fd: libc::c_int, buf: *mut type_of_statfs) -> libc::c_int
+ = libc::fstatfs64;
+ const LIBC_STATFS: unsafe extern fn
+ (path: *const libc::c_char, buf: *mut type_of_statfs) -> libc::c_int
+ = libc::statfs64;
+ } else {
+ type type_of_statfs = libc::statfs;
+ const LIBC_FSTATFS: unsafe extern fn
+ (fd: libc::c_int, buf: *mut type_of_statfs) -> libc::c_int
+ = libc::fstatfs;
+ const LIBC_STATFS: unsafe extern fn
+ (path: *const libc::c_char, buf: *mut type_of_statfs) -> libc::c_int
+ = libc::statfs;
+ }
+}
+
+/// Describes a mounted file system
+#[derive(Clone, Copy)]
+#[repr(transparent)]
+pub struct Statfs(type_of_statfs);
+
+#[cfg(target_os = "freebsd")]
+type fs_type_t = u32;
+#[cfg(target_os = "android")]
+type fs_type_t = libc::c_ulong;
+#[cfg(all(target_os = "linux", target_arch = "s390x"))]
+type fs_type_t = libc::c_uint;
+#[cfg(all(target_os = "linux", target_env = "musl"))]
+type fs_type_t = libc::c_ulong;
+#[cfg(all(target_os = "linux", target_env = "uclibc"))]
+type fs_type_t = libc::c_int;
+#[cfg(all(
+ target_os = "linux",
+ not(any(
+ target_arch = "s390x",
+ target_env = "musl",
+ target_env = "uclibc"
+ ))
+))]
+type fs_type_t = libc::__fsword_t;
+
+/// Describes the file system type as known by the operating system.
+#[cfg(any(
+ target_os = "freebsd",
+ target_os = "android",
+ all(target_os = "linux", target_arch = "s390x"),
+ all(target_os = "linux", target_env = "musl"),
+ all(
+ target_os = "linux",
+ not(any(target_arch = "s390x", target_env = "musl"))
+ ),
+))]
+#[derive(Eq, Copy, Clone, PartialEq, Debug)]
+pub struct FsType(pub fs_type_t);
+
+// These constants are defined without documentation in the Linux headers, so we
+// can't very well document them here.
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const ADFS_SUPER_MAGIC: FsType =
+ FsType(libc::ADFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const AFFS_SUPER_MAGIC: FsType =
+ FsType(libc::AFFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const AFS_SUPER_MAGIC: FsType = FsType(libc::AFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const AUTOFS_SUPER_MAGIC: FsType =
+ FsType(libc::AUTOFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const BPF_FS_MAGIC: FsType = FsType(libc::BPF_FS_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const BTRFS_SUPER_MAGIC: FsType =
+ FsType(libc::BTRFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const CGROUP2_SUPER_MAGIC: FsType =
+ FsType(libc::CGROUP2_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const CGROUP_SUPER_MAGIC: FsType =
+ FsType(libc::CGROUP_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const CODA_SUPER_MAGIC: FsType =
+ FsType(libc::CODA_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const CRAMFS_MAGIC: FsType = FsType(libc::CRAMFS_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const DEBUGFS_MAGIC: FsType = FsType(libc::DEBUGFS_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const DEVPTS_SUPER_MAGIC: FsType =
+ FsType(libc::DEVPTS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const ECRYPTFS_SUPER_MAGIC: FsType =
+ FsType(libc::ECRYPTFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const EFS_SUPER_MAGIC: FsType = FsType(libc::EFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const EXT2_SUPER_MAGIC: FsType =
+ FsType(libc::EXT2_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const EXT3_SUPER_MAGIC: FsType =
+ FsType(libc::EXT3_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const EXT4_SUPER_MAGIC: FsType =
+ FsType(libc::EXT4_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const F2FS_SUPER_MAGIC: FsType =
+ FsType(libc::F2FS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const FUSE_SUPER_MAGIC: FsType =
+ FsType(libc::FUSE_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const FUTEXFS_SUPER_MAGIC: FsType =
+ FsType(libc::FUTEXFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const HOSTFS_SUPER_MAGIC: FsType =
+ FsType(libc::HOSTFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const HPFS_SUPER_MAGIC: FsType =
+ FsType(libc::HPFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const HUGETLBFS_MAGIC: FsType = FsType(libc::HUGETLBFS_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const ISOFS_SUPER_MAGIC: FsType =
+ FsType(libc::ISOFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const JFFS2_SUPER_MAGIC: FsType =
+ FsType(libc::JFFS2_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const MINIX2_SUPER_MAGIC2: FsType =
+ FsType(libc::MINIX2_SUPER_MAGIC2 as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const MINIX2_SUPER_MAGIC: FsType =
+ FsType(libc::MINIX2_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const MINIX3_SUPER_MAGIC: FsType =
+ FsType(libc::MINIX3_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const MINIX_SUPER_MAGIC2: FsType =
+ FsType(libc::MINIX_SUPER_MAGIC2 as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const MINIX_SUPER_MAGIC: FsType =
+ FsType(libc::MINIX_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const MSDOS_SUPER_MAGIC: FsType =
+ FsType(libc::MSDOS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const NCP_SUPER_MAGIC: FsType = FsType(libc::NCP_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const NFS_SUPER_MAGIC: FsType = FsType(libc::NFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const NILFS_SUPER_MAGIC: FsType =
+ FsType(libc::NILFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const OCFS2_SUPER_MAGIC: FsType =
+ FsType(libc::OCFS2_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const OPENPROM_SUPER_MAGIC: FsType =
+ FsType(libc::OPENPROM_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const OVERLAYFS_SUPER_MAGIC: FsType =
+ FsType(libc::OVERLAYFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const PROC_SUPER_MAGIC: FsType =
+ FsType(libc::PROC_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const QNX4_SUPER_MAGIC: FsType =
+ FsType(libc::QNX4_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const QNX6_SUPER_MAGIC: FsType =
+ FsType(libc::QNX6_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const RDTGROUP_SUPER_MAGIC: FsType =
+ FsType(libc::RDTGROUP_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const REISERFS_SUPER_MAGIC: FsType =
+ FsType(libc::REISERFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const SECURITYFS_MAGIC: FsType =
+ FsType(libc::SECURITYFS_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const SELINUX_MAGIC: FsType = FsType(libc::SELINUX_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const SMACK_MAGIC: FsType = FsType(libc::SMACK_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const SMB_SUPER_MAGIC: FsType = FsType(libc::SMB_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const SYSFS_MAGIC: FsType = FsType(libc::SYSFS_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const TMPFS_MAGIC: FsType = FsType(libc::TMPFS_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const TRACEFS_MAGIC: FsType = FsType(libc::TRACEFS_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const UDF_SUPER_MAGIC: FsType = FsType(libc::UDF_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const USBDEVICE_SUPER_MAGIC: FsType =
+ FsType(libc::USBDEVICE_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const XENFS_SUPER_MAGIC: FsType =
+ FsType(libc::XENFS_SUPER_MAGIC as fs_type_t);
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[allow(missing_docs)]
+pub const NSFS_MAGIC: FsType = FsType(libc::NSFS_MAGIC as fs_type_t);
+#[cfg(all(
+ any(target_os = "linux", target_os = "android"),
+ not(target_env = "musl")
+))]
+#[allow(missing_docs)]
+pub const XFS_SUPER_MAGIC: FsType = FsType(libc::XFS_SUPER_MAGIC as fs_type_t);
+
+impl Statfs {
+ /// Magic code defining system type
+ #[cfg(not(any(
+ target_os = "openbsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "macos"
+ )))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn filesystem_type(&self) -> FsType {
+ FsType(self.0.f_type)
+ }
+
+ /// Magic code defining system type
+ #[cfg(not(any(target_os = "linux", target_os = "android")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn filesystem_type_name(&self) -> &str {
+ let c_str = unsafe { CStr::from_ptr(self.0.f_fstypename.as_ptr()) };
+ c_str.to_str().unwrap()
+ }
+
+ /// Optimal transfer block size
+ #[cfg(any(target_os = "ios", target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn optimal_transfer_size(&self) -> i32 {
+ self.0.f_iosize
+ }
+
+ /// Optimal transfer block size
+ #[cfg(target_os = "openbsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn optimal_transfer_size(&self) -> u32 {
+ self.0.f_iosize
+ }
+
+ /// Optimal transfer block size
+ #[cfg(all(target_os = "linux", target_arch = "s390x"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn optimal_transfer_size(&self) -> u32 {
+ self.0.f_bsize
+ }
+
+ /// Optimal transfer block size
+ #[cfg(any(
+ target_os = "android",
+ all(target_os = "linux", target_env = "musl")
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn optimal_transfer_size(&self) -> libc::c_ulong {
+ self.0.f_bsize
+ }
+
+ /// Optimal transfer block size
+ #[cfg(all(
+ target_os = "linux",
+ not(any(
+ target_arch = "s390x",
+ target_env = "musl",
+ target_env = "uclibc"
+ ))
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn optimal_transfer_size(&self) -> libc::__fsword_t {
+ self.0.f_bsize
+ }
+
+ /// Optimal transfer block size
+ #[cfg(all(target_os = "linux", target_env = "uclibc"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn optimal_transfer_size(&self) -> libc::c_int {
+ self.0.f_bsize
+ }
+
+ /// Optimal transfer block size
+ #[cfg(target_os = "dragonfly")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn optimal_transfer_size(&self) -> libc::c_long {
+ self.0.f_iosize
+ }
+
+ /// Optimal transfer block size
+ #[cfg(target_os = "freebsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn optimal_transfer_size(&self) -> u64 {
+ self.0.f_iosize
+ }
+
+ /// Size of a block
+ #[cfg(any(target_os = "ios", target_os = "macos", target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn block_size(&self) -> u32 {
+ self.0.f_bsize
+ }
+
+ /// Size of a block
+ // f_bsize on linux: https://github.com/torvalds/linux/blob/master/fs/nfs/super.c#L471
+ #[cfg(all(target_os = "linux", target_arch = "s390x"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn block_size(&self) -> u32 {
+ self.0.f_bsize
+ }
+
+ /// Size of a block
+ // f_bsize on linux: https://github.com/torvalds/linux/blob/master/fs/nfs/super.c#L471
+ #[cfg(all(target_os = "linux", target_env = "musl"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn block_size(&self) -> libc::c_ulong {
+ self.0.f_bsize
+ }
+
+ /// Size of a block
+ // f_bsize on linux: https://github.com/torvalds/linux/blob/master/fs/nfs/super.c#L471
+ #[cfg(all(target_os = "linux", target_env = "uclibc"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn block_size(&self) -> libc::c_int {
+ self.0.f_bsize
+ }
+
+ /// Size of a block
+ // f_bsize on linux: https://github.com/torvalds/linux/blob/master/fs/nfs/super.c#L471
+ #[cfg(all(
+ target_os = "linux",
+ not(any(
+ target_arch = "s390x",
+ target_env = "musl",
+ target_env = "uclibc"
+ ))
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn block_size(&self) -> libc::__fsword_t {
+ self.0.f_bsize
+ }
+
+ /// Size of a block
+ #[cfg(target_os = "freebsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn block_size(&self) -> u64 {
+ self.0.f_bsize
+ }
+
+ /// Size of a block
+ #[cfg(target_os = "android")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn block_size(&self) -> libc::c_ulong {
+ self.0.f_bsize
+ }
+
+ /// Size of a block
+ #[cfg(target_os = "dragonfly")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn block_size(&self) -> libc::c_long {
+ self.0.f_bsize
+ }
+
+ /// Get the mount flags
+ #[cfg(all(
+ feature = "mount",
+ any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ )
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ #[allow(clippy::unnecessary_cast)] // Not unnecessary on all arches
+ pub fn flags(&self) -> MntFlags {
+ MntFlags::from_bits_truncate(self.0.f_flags as i32)
+ }
+
+ /// Get the mount flags
+ // The f_flags field exists on Android and Fuchsia too, but without man
+ // pages I can't tell if it can be cast to FsFlags.
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn flags(&self) -> FsFlags {
+ FsFlags::from_bits_truncate(self.0.f_flags as libc::c_ulong)
+ }
+
+ /// Maximum length of filenames
+ #[cfg(any(target_os = "freebsd", target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn maximum_name_length(&self) -> u32 {
+ self.0.f_namemax
+ }
+
+ /// Maximum length of filenames
+ #[cfg(all(target_os = "linux", target_arch = "s390x"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn maximum_name_length(&self) -> u32 {
+ self.0.f_namelen
+ }
+
+ /// Maximum length of filenames
+ #[cfg(all(target_os = "linux", target_env = "musl"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn maximum_name_length(&self) -> libc::c_ulong {
+ self.0.f_namelen
+ }
+
+ /// Maximum length of filenames
+ #[cfg(all(target_os = "linux", target_env = "uclibc"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn maximum_name_length(&self) -> libc::c_int {
+ self.0.f_namelen
+ }
+
+ /// Maximum length of filenames
+ #[cfg(all(
+ target_os = "linux",
+ not(any(
+ target_arch = "s390x",
+ target_env = "musl",
+ target_env = "uclibc"
+ ))
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn maximum_name_length(&self) -> libc::__fsword_t {
+ self.0.f_namelen
+ }
+
+ /// Maximum length of filenames
+ #[cfg(target_os = "android")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn maximum_name_length(&self) -> libc::c_ulong {
+ self.0.f_namelen
+ }
+
+ /// Total data blocks in filesystem
+ #[cfg(any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "openbsd",
+ target_os = "linux",
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn blocks(&self) -> u64 {
+ self.0.f_blocks
+ }
+
+ /// Total data blocks in filesystem
+ #[cfg(target_os = "dragonfly")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn blocks(&self) -> libc::c_long {
+ self.0.f_blocks
+ }
+
+ /// Total data blocks in filesystem
+ #[cfg(target_os = "emscripten")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn blocks(&self) -> u32 {
+ self.0.f_blocks
+ }
+
+ /// Free blocks in filesystem
+ #[cfg(any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "openbsd",
+ target_os = "linux",
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn blocks_free(&self) -> u64 {
+ self.0.f_bfree
+ }
+
+ /// Free blocks in filesystem
+ #[cfg(target_os = "dragonfly")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn blocks_free(&self) -> libc::c_long {
+ self.0.f_bfree
+ }
+
+ /// Free blocks in filesystem
+ #[cfg(target_os = "emscripten")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn blocks_free(&self) -> u32 {
+ self.0.f_bfree
+ }
+
+ /// Free blocks available to unprivileged user
+ #[cfg(any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "linux",
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn blocks_available(&self) -> u64 {
+ self.0.f_bavail
+ }
+
+ /// Free blocks available to unprivileged user
+ #[cfg(target_os = "dragonfly")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn blocks_available(&self) -> libc::c_long {
+ self.0.f_bavail
+ }
+
+ /// Free blocks available to unprivileged user
+ #[cfg(any(target_os = "freebsd", target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn blocks_available(&self) -> i64 {
+ self.0.f_bavail
+ }
+
+ /// Free blocks available to unprivileged user
+ #[cfg(target_os = "emscripten")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn blocks_available(&self) -> u32 {
+ self.0.f_bavail
+ }
+
+ /// Total file nodes in filesystem
+ #[cfg(any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "openbsd",
+ target_os = "linux",
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn files(&self) -> u64 {
+ self.0.f_files
+ }
+
+ /// Total file nodes in filesystem
+ #[cfg(target_os = "dragonfly")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn files(&self) -> libc::c_long {
+ self.0.f_files
+ }
+
+ /// Total file nodes in filesystem
+ #[cfg(target_os = "emscripten")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn files(&self) -> u32 {
+ self.0.f_files
+ }
+
+ /// Free file nodes in filesystem
+ #[cfg(any(
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "android",
+ target_os = "fuchsia",
+ target_os = "openbsd",
+ target_os = "linux",
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn files_free(&self) -> u64 {
+ self.0.f_ffree
+ }
+
+ /// Free file nodes in filesystem
+ #[cfg(target_os = "dragonfly")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn files_free(&self) -> libc::c_long {
+ self.0.f_ffree
+ }
+
+ /// Free file nodes in filesystem
+ #[cfg(target_os = "freebsd")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn files_free(&self) -> i64 {
+ self.0.f_ffree
+ }
+
+ /// Free file nodes in filesystem
+ #[cfg(target_os = "emscripten")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn files_free(&self) -> u32 {
+ self.0.f_ffree
+ }
+
+ /// Filesystem ID
+ pub fn filesystem_id(&self) -> fsid_t {
+ self.0.f_fsid
+ }
+}
+
+impl Debug for Statfs {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let mut ds = f.debug_struct("Statfs");
+ ds.field("optimal_transfer_size", &self.optimal_transfer_size());
+ ds.field("block_size", &self.block_size());
+ ds.field("blocks", &self.blocks());
+ ds.field("blocks_free", &self.blocks_free());
+ ds.field("blocks_available", &self.blocks_available());
+ ds.field("files", &self.files());
+ ds.field("files_free", &self.files_free());
+ ds.field("filesystem_id", &self.filesystem_id());
+ #[cfg(all(
+ feature = "mount",
+ any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ )
+ ))]
+ ds.field("flags", &self.flags());
+ ds.finish()
+ }
+}
+
+/// Describes a mounted file system.
+///
+/// The result is OS-dependent. For a portable alternative, see
+/// [`statvfs`](crate::sys::statvfs::statvfs).
+///
+/// # Arguments
+///
+/// `path` - Path to any file within the file system to describe
+pub fn statfs<P: ?Sized + NixPath>(path: &P) -> Result<Statfs> {
+ unsafe {
+ let mut stat = mem::MaybeUninit::<type_of_statfs>::uninit();
+ let res = path.with_nix_path(|path| {
+ LIBC_STATFS(path.as_ptr(), stat.as_mut_ptr())
+ })?;
+ Errno::result(res).map(|_| Statfs(stat.assume_init()))
+ }
+}
+
+/// Describes a mounted file system.
+///
+/// The result is OS-dependent. For a portable alternative, see
+/// [`fstatvfs`](crate::sys::statvfs::fstatvfs).
+///
+/// # Arguments
+///
+/// `fd` - File descriptor of any open file within the file system to describe
+pub fn fstatfs<T: AsRawFd>(fd: &T) -> Result<Statfs> {
+ unsafe {
+ let mut stat = mem::MaybeUninit::<type_of_statfs>::uninit();
+ Errno::result(LIBC_FSTATFS(fd.as_raw_fd(), stat.as_mut_ptr()))
+ .map(|_| Statfs(stat.assume_init()))
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use std::fs::File;
+
+ use crate::sys::statfs::*;
+ use crate::sys::statvfs::*;
+ use std::path::Path;
+
+ #[test]
+ fn statfs_call() {
+ check_statfs("/tmp");
+ check_statfs("/dev");
+ check_statfs("/run");
+ check_statfs("/");
+ }
+
+ #[test]
+ fn fstatfs_call() {
+ check_fstatfs("/tmp");
+ check_fstatfs("/dev");
+ check_fstatfs("/run");
+ check_fstatfs("/");
+ }
+
+ fn check_fstatfs(path: &str) {
+ if !Path::new(path).exists() {
+ return;
+ }
+ let vfs = statvfs(path.as_bytes()).unwrap();
+ let file = File::open(path).unwrap();
+ let fs = fstatfs(&file).unwrap();
+ assert_fs_equals(fs, vfs);
+ }
+
+ fn check_statfs(path: &str) {
+ if !Path::new(path).exists() {
+ return;
+ }
+ let vfs = statvfs(path.as_bytes()).unwrap();
+ let fs = statfs(path.as_bytes()).unwrap();
+ assert_fs_equals(fs, vfs);
+ }
+
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ fn assert_fs_equals(fs: Statfs, vfs: Statvfs) {
+ assert_eq!(fs.files() as u64, vfs.files() as u64);
+ assert_eq!(fs.blocks() as u64, vfs.blocks() as u64);
+ assert_eq!(fs.block_size() as u64, vfs.fragment_size() as u64);
+ }
+
+ // This test is ignored because files_free/blocks_free can change after statvfs call and before
+ // statfs call.
+ #[test]
+ #[ignore]
+ fn statfs_call_strict() {
+ check_statfs_strict("/tmp");
+ check_statfs_strict("/dev");
+ check_statfs_strict("/run");
+ check_statfs_strict("/");
+ }
+
+ // This test is ignored because files_free/blocks_free can change after statvfs call and before
+ // fstatfs call.
+ #[test]
+ #[ignore]
+ fn fstatfs_call_strict() {
+ check_fstatfs_strict("/tmp");
+ check_fstatfs_strict("/dev");
+ check_fstatfs_strict("/run");
+ check_fstatfs_strict("/");
+ }
+
+ fn check_fstatfs_strict(path: &str) {
+ if !Path::new(path).exists() {
+ return;
+ }
+ let vfs = statvfs(path.as_bytes());
+ let file = File::open(path).unwrap();
+ let fs = fstatfs(&file);
+ assert_fs_equals_strict(fs.unwrap(), vfs.unwrap())
+ }
+
+ fn check_statfs_strict(path: &str) {
+ if !Path::new(path).exists() {
+ return;
+ }
+ let vfs = statvfs(path.as_bytes());
+ let fs = statfs(path.as_bytes());
+ assert_fs_equals_strict(fs.unwrap(), vfs.unwrap())
+ }
+
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ fn assert_fs_equals_strict(fs: Statfs, vfs: Statvfs) {
+ assert_eq!(fs.files_free() as u64, vfs.files_free() as u64);
+ assert_eq!(fs.blocks_free() as u64, vfs.blocks_free() as u64);
+ assert_eq!(fs.blocks_available() as u64, vfs.blocks_available() as u64);
+ assert_eq!(fs.files() as u64, vfs.files() as u64);
+ assert_eq!(fs.blocks() as u64, vfs.blocks() as u64);
+ assert_eq!(fs.block_size() as u64, vfs.fragment_size() as u64);
+ }
+}
diff --git a/third_party/rust/nix/src/sys/statvfs.rs b/third_party/rust/nix/src/sys/statvfs.rs
new file mode 100644
index 0000000000..8de369f421
--- /dev/null
+++ b/third_party/rust/nix/src/sys/statvfs.rs
@@ -0,0 +1,173 @@
+//! Get filesystem statistics
+//!
+//! See [the man pages](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fstatvfs.html)
+//! for more details.
+use std::mem;
+use std::os::unix::io::AsRawFd;
+
+use libc::{self, c_ulong};
+
+use crate::{errno::Errno, NixPath, Result};
+
+#[cfg(not(target_os = "redox"))]
+libc_bitflags!(
+ /// File system mount Flags
+ #[repr(C)]
+ #[derive(Default)]
+ pub struct FsFlags: c_ulong {
+ /// Read Only
+ #[cfg(not(target_os = "haiku"))]
+ ST_RDONLY;
+ /// Do not allow the set-uid bits to have an effect
+ #[cfg(not(target_os = "haiku"))]
+ ST_NOSUID;
+ /// Do not interpret character or block-special devices
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ST_NODEV;
+ /// Do not allow execution of binaries on the filesystem
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ST_NOEXEC;
+ /// All IO should be done synchronously
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ST_SYNCHRONOUS;
+ /// Allow mandatory locks on the filesystem
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ST_MANDLOCK;
+ /// Write on file/directory/symlink
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ST_WRITE;
+ /// Append-only file
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ST_APPEND;
+ /// Immutable file
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ST_IMMUTABLE;
+ /// Do not update access times on files
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ST_NOATIME;
+ /// Do not update access times on files
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ST_NODIRATIME;
+ /// Update access time relative to modify/change time
+ #[cfg(any(target_os = "android", all(target_os = "linux", not(target_env = "musl"))))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ST_RELATIME;
+ }
+);
+
+/// Wrapper around the POSIX `statvfs` struct
+///
+/// For more information see the [`statvfs(3)` man pages](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_statvfs.h.html).
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct Statvfs(libc::statvfs);
+
+impl Statvfs {
+ /// get the file system block size
+ pub fn block_size(&self) -> c_ulong {
+ self.0.f_bsize
+ }
+
+ /// Get the fundamental file system block size
+ pub fn fragment_size(&self) -> c_ulong {
+ self.0.f_frsize
+ }
+
+ /// Get the number of blocks.
+ ///
+ /// Units are in units of `fragment_size()`
+ pub fn blocks(&self) -> libc::fsblkcnt_t {
+ self.0.f_blocks
+ }
+
+ /// Get the number of free blocks in the file system
+ pub fn blocks_free(&self) -> libc::fsblkcnt_t {
+ self.0.f_bfree
+ }
+
+ /// Get the number of free blocks for unprivileged users
+ pub fn blocks_available(&self) -> libc::fsblkcnt_t {
+ self.0.f_bavail
+ }
+
+ /// Get the total number of file inodes
+ pub fn files(&self) -> libc::fsfilcnt_t {
+ self.0.f_files
+ }
+
+ /// Get the number of free file inodes
+ pub fn files_free(&self) -> libc::fsfilcnt_t {
+ self.0.f_ffree
+ }
+
+ /// Get the number of free file inodes for unprivileged users
+ pub fn files_available(&self) -> libc::fsfilcnt_t {
+ self.0.f_favail
+ }
+
+ /// Get the file system id
+ pub fn filesystem_id(&self) -> c_ulong {
+ self.0.f_fsid
+ }
+
+ /// Get the mount flags
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn flags(&self) -> FsFlags {
+ FsFlags::from_bits_truncate(self.0.f_flag)
+ }
+
+ /// Get the maximum filename length
+ pub fn name_max(&self) -> c_ulong {
+ self.0.f_namemax
+ }
+}
+
+/// Return a `Statvfs` object with information about the `path`
+pub fn statvfs<P: ?Sized + NixPath>(path: &P) -> Result<Statvfs> {
+ unsafe {
+ Errno::clear();
+ let mut stat = mem::MaybeUninit::<libc::statvfs>::uninit();
+ let res = path.with_nix_path(|path| {
+ libc::statvfs(path.as_ptr(), stat.as_mut_ptr())
+ })?;
+
+ Errno::result(res).map(|_| Statvfs(stat.assume_init()))
+ }
+}
+
+/// Return a `Statvfs` object with information about `fd`
+pub fn fstatvfs<T: AsRawFd>(fd: &T) -> Result<Statvfs> {
+ unsafe {
+ Errno::clear();
+ let mut stat = mem::MaybeUninit::<libc::statvfs>::uninit();
+ Errno::result(libc::fstatvfs(fd.as_raw_fd(), stat.as_mut_ptr()))
+ .map(|_| Statvfs(stat.assume_init()))
+ }
+}
+
+#[cfg(test)]
+mod test {
+ use crate::sys::statvfs::*;
+ use std::fs::File;
+
+ #[test]
+ fn statvfs_call() {
+ statvfs(&b"/"[..]).unwrap();
+ }
+
+ #[test]
+ fn fstatvfs_call() {
+ let root = File::open("/").unwrap();
+ fstatvfs(&root).unwrap();
+ }
+}
diff --git a/third_party/rust/nix/src/sys/sysinfo.rs b/third_party/rust/nix/src/sys/sysinfo.rs
new file mode 100644
index 0000000000..e8aa00b00d
--- /dev/null
+++ b/third_party/rust/nix/src/sys/sysinfo.rs
@@ -0,0 +1,83 @@
+use libc::{self, SI_LOAD_SHIFT};
+use std::time::Duration;
+use std::{cmp, mem};
+
+use crate::errno::Errno;
+use crate::Result;
+
+/// System info structure returned by `sysinfo`.
+#[derive(Copy, Clone, Debug, Eq, Hash, PartialEq)]
+#[repr(transparent)]
+pub struct SysInfo(libc::sysinfo);
+
+// The fields are c_ulong on 32-bit linux, u64 on 64-bit linux; x32's ulong is u32
+#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
+type mem_blocks_t = u64;
+#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
+type mem_blocks_t = libc::c_ulong;
+
+impl SysInfo {
+ /// Returns the load average tuple.
+ ///
+ /// The returned values represent the load average over time intervals of
+ /// 1, 5, and 15 minutes, respectively.
+ pub fn load_average(&self) -> (f64, f64, f64) {
+ (
+ self.0.loads[0] as f64 / (1 << SI_LOAD_SHIFT) as f64,
+ self.0.loads[1] as f64 / (1 << SI_LOAD_SHIFT) as f64,
+ self.0.loads[2] as f64 / (1 << SI_LOAD_SHIFT) as f64,
+ )
+ }
+
+ /// Returns the time since system boot.
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ pub fn uptime(&self) -> Duration {
+ // Truncate negative values to 0
+ Duration::from_secs(cmp::max(self.0.uptime, 0) as u64)
+ }
+
+ /// Current number of processes.
+ pub fn process_count(&self) -> u16 {
+ self.0.procs
+ }
+
+ /// Returns the amount of swap memory in Bytes.
+ pub fn swap_total(&self) -> u64 {
+ self.scale_mem(self.0.totalswap)
+ }
+
+ /// Returns the amount of unused swap memory in Bytes.
+ pub fn swap_free(&self) -> u64 {
+ self.scale_mem(self.0.freeswap)
+ }
+
+ /// Returns the total amount of installed RAM in Bytes.
+ pub fn ram_total(&self) -> u64 {
+ self.scale_mem(self.0.totalram)
+ }
+
+ /// Returns the amount of completely unused RAM in Bytes.
+ ///
+ /// "Unused" in this context means that the RAM in neither actively used by
+ /// programs, nor by the operating system as disk cache or buffer. It is
+ /// "wasted" RAM since it currently serves no purpose.
+ pub fn ram_unused(&self) -> u64 {
+ self.scale_mem(self.0.freeram)
+ }
+
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ fn scale_mem(&self, units: mem_blocks_t) -> u64 {
+ units as u64 * self.0.mem_unit as u64
+ }
+}
+
+/// Returns system information.
+///
+/// [See `sysinfo(2)`](https://man7.org/linux/man-pages/man2/sysinfo.2.html).
+pub fn sysinfo() -> Result<SysInfo> {
+ let mut info = mem::MaybeUninit::uninit();
+ let res = unsafe { libc::sysinfo(info.as_mut_ptr()) };
+ Errno::result(res).map(|_| unsafe { SysInfo(info.assume_init()) })
+}
diff --git a/third_party/rust/nix/src/sys/termios.rs b/third_party/rust/nix/src/sys/termios.rs
new file mode 100644
index 0000000000..fba2cd8268
--- /dev/null
+++ b/third_party/rust/nix/src/sys/termios.rs
@@ -0,0 +1,1227 @@
+//! An interface for controlling asynchronous communication ports
+//!
+//! This interface provides a safe wrapper around the termios subsystem defined by POSIX. The
+//! underlying types are all implemented in libc for most platforms and either wrapped in safer
+//! types here or exported directly.
+//!
+//! If you are unfamiliar with the `termios` API, you should first read the
+//! [API documentation](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/termios.h.html) and
+//! then come back to understand how `nix` safely wraps it.
+//!
+//! It should be noted that this API incurs some runtime overhead above the base `libc` definitions.
+//! As this interface is not used with high-bandwidth information, this should be fine in most
+//! cases. The primary cost when using this API is that the `Termios` datatype here duplicates the
+//! standard fields of the underlying `termios` struct and uses safe type wrappers for those fields.
+//! This means that when crossing the FFI interface to the underlying C library, data is first
+//! copied into the underlying `termios` struct, then the operation is done, and the data is copied
+//! back (with additional sanity checking) into the safe wrapper types. The `termios` struct is
+//! relatively small across all platforms (on the order of 32-64 bytes).
+//!
+//! The following examples highlight some of the API use cases such that users coming from using C
+//! or reading the standard documentation will understand how to use the safe API exposed here.
+//!
+//! Example disabling processing of the end-of-file control character:
+//!
+//! ```
+//! # use self::nix::sys::termios::SpecialCharacterIndices::VEOF;
+//! # use self::nix::sys::termios::{_POSIX_VDISABLE, Termios};
+//! # let mut termios: Termios = unsafe { std::mem::zeroed() };
+//! termios.control_chars[VEOF as usize] = _POSIX_VDISABLE;
+//! ```
+//!
+//! The flags within `Termios` are defined as bitfields using the `bitflags` crate. This provides
+//! an interface for working with bitfields that is similar to working with the raw unsigned
+//! integer types but offers type safety because of the internal checking that values will always
+//! be a valid combination of the defined flags.
+//!
+//! An example showing some of the basic operations for interacting with the control flags:
+//!
+//! ```
+//! # use self::nix::sys::termios::{ControlFlags, Termios};
+//! # let mut termios: Termios = unsafe { std::mem::zeroed() };
+//! termios.control_flags & ControlFlags::CSIZE == ControlFlags::CS5;
+//! termios.control_flags |= ControlFlags::CS5;
+//! ```
+//!
+//! # Baud rates
+//!
+//! This API is not consistent across platforms when it comes to `BaudRate`: Android and Linux both
+//! only support the rates specified by the `BaudRate` enum through their termios API while the BSDs
+//! support arbitrary baud rates as the values of the `BaudRate` enum constants are the same integer
+//! value of the constant (`B9600` == `9600`). Therefore the `nix::termios` API uses the following
+//! conventions:
+//!
+//! * `cfgetispeed()` - Returns `u32` on BSDs, `BaudRate` on Android/Linux
+//! * `cfgetospeed()` - Returns `u32` on BSDs, `BaudRate` on Android/Linux
+//! * `cfsetispeed()` - Takes `u32` or `BaudRate` on BSDs, `BaudRate` on Android/Linux
+//! * `cfsetospeed()` - Takes `u32` or `BaudRate` on BSDs, `BaudRate` on Android/Linux
+//! * `cfsetspeed()` - Takes `u32` or `BaudRate` on BSDs, `BaudRate` on Android/Linux
+//!
+//! The most common use case of specifying a baud rate using the enum will work the same across
+//! platforms:
+//!
+//! ```rust
+//! # use nix::sys::termios::{BaudRate, cfsetispeed, cfsetospeed, cfsetspeed, Termios};
+//! # fn main() {
+//! # let mut t: Termios = unsafe { std::mem::zeroed() };
+//! cfsetispeed(&mut t, BaudRate::B9600).unwrap();
+//! cfsetospeed(&mut t, BaudRate::B9600).unwrap();
+//! cfsetspeed(&mut t, BaudRate::B9600).unwrap();
+//! # }
+//! ```
+//!
+//! Additionally round-tripping baud rates is consistent across platforms:
+//!
+//! ```rust
+//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetispeed, cfsetspeed, Termios};
+//! # fn main() {
+//! # let mut t: Termios = unsafe { std::mem::zeroed() };
+//! # cfsetspeed(&mut t, BaudRate::B9600).unwrap();
+//! let speed = cfgetispeed(&t);
+//! assert_eq!(speed, cfgetospeed(&t));
+//! cfsetispeed(&mut t, speed).unwrap();
+//! # }
+//! ```
+//!
+//! On non-BSDs, `cfgetispeed()` and `cfgetospeed()` both return a `BaudRate`:
+//!
+#![cfg_attr(
+ any(
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ),
+ doc = " ```rust,ignore"
+)]
+#![cfg_attr(
+ not(any(
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ )),
+ doc = " ```rust"
+)]
+//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetspeed, Termios};
+//! # fn main() {
+//! # let mut t: Termios = unsafe { std::mem::zeroed() };
+//! # cfsetspeed(&mut t, BaudRate::B9600);
+//! assert_eq!(cfgetispeed(&t), BaudRate::B9600);
+//! assert_eq!(cfgetospeed(&t), BaudRate::B9600);
+//! # }
+//! ```
+//!
+//! But on the BSDs, `cfgetispeed()` and `cfgetospeed()` both return `u32`s:
+//!
+#![cfg_attr(
+ any(
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ),
+ doc = " ```rust"
+)]
+#![cfg_attr(
+ not(any(
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ )),
+ doc = " ```rust,ignore"
+)]
+//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfgetospeed, cfsetspeed, Termios};
+//! # fn main() {
+//! # let mut t: Termios = unsafe { std::mem::zeroed() };
+//! # cfsetspeed(&mut t, 9600u32);
+//! assert_eq!(cfgetispeed(&t), 9600u32);
+//! assert_eq!(cfgetospeed(&t), 9600u32);
+//! # }
+//! ```
+//!
+//! It's trivial to convert from a `BaudRate` to a `u32` on BSDs:
+//!
+#![cfg_attr(
+ any(
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ),
+ doc = " ```rust"
+)]
+#![cfg_attr(
+ not(any(
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ )),
+ doc = " ```rust,ignore"
+)]
+//! # use nix::sys::termios::{BaudRate, cfgetispeed, cfsetspeed, Termios};
+//! # fn main() {
+//! # let mut t: Termios = unsafe { std::mem::zeroed() };
+//! # cfsetspeed(&mut t, 9600u32);
+//! assert_eq!(cfgetispeed(&t), BaudRate::B9600.into());
+//! assert_eq!(u32::from(BaudRate::B9600), 9600u32);
+//! # }
+//! ```
+//!
+//! And on BSDs you can specify arbitrary baud rates (**note** this depends on hardware support)
+//! by specifying baud rates directly using `u32`s:
+//!
+#![cfg_attr(
+ any(
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ ),
+ doc = " ```rust"
+)]
+#![cfg_attr(
+ not(any(
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+ )),
+ doc = " ```rust,ignore"
+)]
+//! # use nix::sys::termios::{cfsetispeed, cfsetospeed, cfsetspeed, Termios};
+//! # fn main() {
+//! # let mut t: Termios = unsafe { std::mem::zeroed() };
+//! cfsetispeed(&mut t, 9600u32);
+//! cfsetospeed(&mut t, 9600u32);
+//! cfsetspeed(&mut t, 9600u32);
+//! # }
+//! ```
+use crate::errno::Errno;
+use crate::Result;
+use cfg_if::cfg_if;
+use libc::{self, c_int, tcflag_t};
+use std::cell::{Ref, RefCell};
+use std::convert::From;
+use std::mem;
+use std::os::unix::io::RawFd;
+
+#[cfg(feature = "process")]
+use crate::unistd::Pid;
+
+/// Stores settings for the termios API
+///
+/// This is a wrapper around the `libc::termios` struct that provides a safe interface for the
+/// standard fields. The only safe way to obtain an instance of this struct is to extract it from
+/// an open port using `tcgetattr()`.
+#[derive(Clone, Debug, Eq, PartialEq)]
+pub struct Termios {
+ inner: RefCell<libc::termios>,
+ /// Input mode flags (see `termios.c_iflag` documentation)
+ pub input_flags: InputFlags,
+ /// Output mode flags (see `termios.c_oflag` documentation)
+ pub output_flags: OutputFlags,
+ /// Control mode flags (see `termios.c_cflag` documentation)
+ pub control_flags: ControlFlags,
+ /// Local mode flags (see `termios.c_lflag` documentation)
+ pub local_flags: LocalFlags,
+ /// Control characters (see `termios.c_cc` documentation)
+ pub control_chars: [libc::cc_t; NCCS],
+ /// Line discipline (see `termios.c_line` documentation)
+ #[cfg(any(target_os = "linux", target_os = "android",))]
+ pub line_discipline: libc::cc_t,
+ /// Line discipline (see `termios.c_line` documentation)
+ #[cfg(target_os = "haiku")]
+ pub line_discipline: libc::c_char,
+}
+
+impl Termios {
+ /// Exposes an immutable reference to the underlying `libc::termios` data structure.
+ ///
+ /// This is not part of `nix`'s public API because it requires additional work to maintain type
+ /// safety.
+ pub(crate) fn get_libc_termios(&self) -> Ref<libc::termios> {
+ {
+ let mut termios = self.inner.borrow_mut();
+ termios.c_iflag = self.input_flags.bits();
+ termios.c_oflag = self.output_flags.bits();
+ termios.c_cflag = self.control_flags.bits();
+ termios.c_lflag = self.local_flags.bits();
+ termios.c_cc = self.control_chars;
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "haiku",
+ ))]
+ {
+ termios.c_line = self.line_discipline;
+ }
+ }
+ self.inner.borrow()
+ }
+
+ /// Exposes the inner `libc::termios` datastore within `Termios`.
+ ///
+ /// This is unsafe because if this is used to modify the inner `libc::termios` struct, it will
+ /// not automatically update the safe wrapper type around it. In this case it should also be
+ /// paired with a call to `update_wrapper()` so that the wrapper-type and internal
+ /// representation stay consistent.
+ pub(crate) unsafe fn get_libc_termios_mut(&mut self) -> *mut libc::termios {
+ {
+ let mut termios = self.inner.borrow_mut();
+ termios.c_iflag = self.input_flags.bits();
+ termios.c_oflag = self.output_flags.bits();
+ termios.c_cflag = self.control_flags.bits();
+ termios.c_lflag = self.local_flags.bits();
+ termios.c_cc = self.control_chars;
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "haiku",
+ ))]
+ {
+ termios.c_line = self.line_discipline;
+ }
+ }
+ self.inner.as_ptr()
+ }
+
+ /// Updates the wrapper values from the internal `libc::termios` data structure.
+ pub(crate) fn update_wrapper(&mut self) {
+ let termios = *self.inner.borrow_mut();
+ self.input_flags = InputFlags::from_bits_truncate(termios.c_iflag);
+ self.output_flags = OutputFlags::from_bits_truncate(termios.c_oflag);
+ self.control_flags = ControlFlags::from_bits_truncate(termios.c_cflag);
+ self.local_flags = LocalFlags::from_bits_truncate(termios.c_lflag);
+ self.control_chars = termios.c_cc;
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "haiku",
+ ))]
+ {
+ self.line_discipline = termios.c_line;
+ }
+ }
+}
+
+impl From<libc::termios> for Termios {
+ fn from(termios: libc::termios) -> Self {
+ Termios {
+ inner: RefCell::new(termios),
+ input_flags: InputFlags::from_bits_truncate(termios.c_iflag),
+ output_flags: OutputFlags::from_bits_truncate(termios.c_oflag),
+ control_flags: ControlFlags::from_bits_truncate(termios.c_cflag),
+ local_flags: LocalFlags::from_bits_truncate(termios.c_lflag),
+ control_chars: termios.c_cc,
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "haiku",
+ ))]
+ line_discipline: termios.c_line,
+ }
+ }
+}
+
+impl From<Termios> for libc::termios {
+ fn from(termios: Termios) -> Self {
+ termios.inner.into_inner()
+ }
+}
+
+libc_enum! {
+ /// Baud rates supported by the system.
+ ///
+ /// For the BSDs, arbitrary baud rates can be specified by using `u32`s directly instead of this
+ /// enum.
+ ///
+ /// B0 is special and will disable the port.
+ #[cfg_attr(all(any(target_os = "haiku"), target_pointer_width = "64"), repr(u8))]
+ #[cfg_attr(all(any(target_os = "ios", target_os = "macos"), target_pointer_width = "64"), repr(u64))]
+ #[cfg_attr(not(all(any(target_os = "ios", target_os = "macos", target_os = "haiku"), target_pointer_width = "64")), repr(u32))]
+ #[non_exhaustive]
+ pub enum BaudRate {
+ B0,
+ B50,
+ B75,
+ B110,
+ B134,
+ B150,
+ B200,
+ B300,
+ B600,
+ B1200,
+ B1800,
+ B2400,
+ B4800,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B7200,
+ B9600,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B14400,
+ B19200,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B28800,
+ B38400,
+ B57600,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B76800,
+ B115200,
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B153600,
+ B230400,
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B307200,
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B460800,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B500000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B576000,
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B921600,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B1000000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B1152000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B1500000,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B2000000,
+ #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B2500000,
+ #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B3000000,
+ #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B3500000,
+ #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "sparc64"))))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ B4000000,
+ }
+ impl TryFrom<libc::speed_t>
+}
+
+#[cfg(any(
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+))]
+impl From<BaudRate> for u32 {
+ fn from(b: BaudRate) -> u32 {
+ b as u32
+ }
+}
+
+#[cfg(target_os = "haiku")]
+impl From<BaudRate> for u8 {
+ fn from(b: BaudRate) -> u8 {
+ b as u8
+ }
+}
+
+// TODO: Add TCSASOFT, which will require treating this as a bitfield.
+libc_enum! {
+ /// Specify when a port configuration change should occur.
+ ///
+ /// Used as an argument to `tcsetattr()`
+ #[repr(i32)]
+ #[non_exhaustive]
+ pub enum SetArg {
+ /// The change will occur immediately
+ TCSANOW,
+ /// The change occurs after all output has been written
+ TCSADRAIN,
+ /// Same as `TCSADRAIN`, but will also flush the input buffer
+ TCSAFLUSH,
+ }
+}
+
+libc_enum! {
+ /// Specify a combination of the input and output buffers to flush
+ ///
+ /// Used as an argument to `tcflush()`.
+ #[repr(i32)]
+ #[non_exhaustive]
+ pub enum FlushArg {
+ /// Flush data that was received but not read
+ TCIFLUSH,
+ /// Flush data written but not transmitted
+ TCOFLUSH,
+ /// Flush both received data not read and written data not transmitted
+ TCIOFLUSH,
+ }
+}
+
+libc_enum! {
+ /// Specify how transmission flow should be altered
+ ///
+ /// Used as an argument to `tcflow()`.
+ #[repr(i32)]
+ #[non_exhaustive]
+ pub enum FlowArg {
+ /// Suspend transmission
+ TCOOFF,
+ /// Resume transmission
+ TCOON,
+ /// Transmit a STOP character, which should disable a connected terminal device
+ TCIOFF,
+ /// Transmit a START character, which should re-enable a connected terminal device
+ TCION,
+ }
+}
+
+// TODO: Make this usable directly as a slice index.
+#[cfg(not(target_os = "haiku"))]
+libc_enum! {
+ /// Indices into the `termios.c_cc` array for special characters.
+ #[repr(usize)]
+ #[non_exhaustive]
+ pub enum SpecialCharacterIndices {
+ VDISCARD,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ VDSUSP,
+ VEOF,
+ VEOL,
+ VEOL2,
+ VERASE,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ VERASE2,
+ VINTR,
+ VKILL,
+ VLNEXT,
+ #[cfg(not(any(all(target_os = "linux", target_arch = "sparc64"),
+ target_os = "illumos", target_os = "solaris")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ VMIN,
+ VQUIT,
+ VREPRINT,
+ VSTART,
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ VSTATUS,
+ VSTOP,
+ VSUSP,
+ #[cfg(target_os = "linux")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ VSWTC,
+ #[cfg(any(target_os = "haiku", target_os = "illumos", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ VSWTCH,
+ #[cfg(not(any(all(target_os = "linux", target_arch = "sparc64"),
+ target_os = "illumos", target_os = "solaris")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ VTIME,
+ VWERASE,
+ #[cfg(target_os = "dragonfly")]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ VCHECKPT,
+ }
+}
+
+#[cfg(any(
+ all(target_os = "linux", target_arch = "sparc64"),
+ target_os = "illumos",
+ target_os = "solaris"
+))]
+impl SpecialCharacterIndices {
+ pub const VMIN: SpecialCharacterIndices = SpecialCharacterIndices::VEOF;
+ pub const VTIME: SpecialCharacterIndices = SpecialCharacterIndices::VEOL;
+}
+
+pub use libc::NCCS;
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub use libc::_POSIX_VDISABLE;
+
+libc_bitflags! {
+ /// Flags for configuring the input mode of a terminal
+ pub struct InputFlags: tcflag_t {
+ IGNBRK;
+ BRKINT;
+ IGNPAR;
+ PARMRK;
+ INPCK;
+ ISTRIP;
+ INLCR;
+ IGNCR;
+ ICRNL;
+ IXON;
+ IXOFF;
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IXANY;
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IMAXBEL;
+ #[cfg(any(target_os = "android", target_os = "linux", target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IUTF8;
+ }
+}
+
+libc_bitflags! {
+ /// Flags for configuring the output mode of a terminal
+ pub struct OutputFlags: tcflag_t {
+ OPOST;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "linux",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ OLCUC;
+ ONLCR;
+ OCRNL as tcflag_t;
+ ONOCR as tcflag_t;
+ ONLRET as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ OFILL as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ OFDEL as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NL0 as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NL1 as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CR0 as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CR1 as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CR2 as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CR3 as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ TAB0 as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ TAB1 as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ TAB2 as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ TAB3 as tcflag_t;
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ XTABS;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ BS0 as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ BS1 as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ VT0 as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ VT1 as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ FF0 as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ FF1 as tcflag_t;
+ #[cfg(any(target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ OXTABS;
+ #[cfg(any(target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ONOEOT as tcflag_t;
+
+ // Bitmasks for use with OutputFlags to select specific settings
+ // These should be moved to be a mask once https://github.com/rust-lang-nursery/bitflags/issues/110
+ // is resolved.
+
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NLDLY as tcflag_t; // FIXME: Datatype needs to be corrected in libc for mac
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CRDLY as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ TABDLY as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ BSDLY as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ VTDLY as tcflag_t;
+ #[cfg(any(target_os = "android",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ FFDLY as tcflag_t;
+ }
+}
+
+libc_bitflags! {
+ /// Flags for setting the control mode of a terminal
+ pub struct ControlFlags: tcflag_t {
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CIGNORE;
+ CS5;
+ CS6;
+ CS7;
+ CS8;
+ CSTOPB;
+ CREAD;
+ PARENB;
+ PARODD;
+ HUPCL;
+ CLOCAL;
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CRTSCTS;
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CBAUD;
+ #[cfg(any(target_os = "android", all(target_os = "linux", not(target_arch = "mips"))))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CMSPAR;
+ #[cfg(any(target_os = "android",
+ all(target_os = "linux",
+ not(any(target_arch = "powerpc", target_arch = "powerpc64")))))]
+ CIBAUD;
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CBAUDEX;
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MDMBUF;
+ #[cfg(any(target_os = "netbsd", target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CHWFLOW;
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CCTS_OFLOW;
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CRTS_IFLOW;
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CDTR_IFLOW;
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CDSR_OFLOW;
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ CCAR_OFLOW;
+
+ // Bitmasks for use with ControlFlags to select specific settings
+ // These should be moved to be a mask once https://github.com/rust-lang-nursery/bitflags/issues/110
+ // is resolved.
+
+ CSIZE;
+ }
+}
+
+libc_bitflags! {
+ /// Flags for setting any local modes
+ pub struct LocalFlags: tcflag_t {
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ECHOKE;
+ ECHOE;
+ ECHOK;
+ ECHO;
+ ECHONL;
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ECHOPRT;
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ECHOCTL;
+ ISIG;
+ ICANON;
+ #[cfg(any(target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ALTWERASE;
+ IEXTEN;
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ EXTPROC;
+ TOSTOP;
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ FLUSHO;
+ #[cfg(any(target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ NOKERNINFO;
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ PENDIN;
+ NOFLSH;
+ }
+}
+
+cfg_if! {
+ if #[cfg(any(target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"))] {
+ /// Get input baud rate (see
+ /// [cfgetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html)).
+ ///
+ /// `cfgetispeed()` extracts the input baud rate from the given `Termios` structure.
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ pub fn cfgetispeed(termios: &Termios) -> u32 {
+ let inner_termios = termios.get_libc_termios();
+ unsafe { libc::cfgetispeed(&*inner_termios) as u32 }
+ }
+
+ /// Get output baud rate (see
+ /// [cfgetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html)).
+ ///
+ /// `cfgetospeed()` extracts the output baud rate from the given `Termios` structure.
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ pub fn cfgetospeed(termios: &Termios) -> u32 {
+ let inner_termios = termios.get_libc_termios();
+ unsafe { libc::cfgetospeed(&*inner_termios) as u32 }
+ }
+
+ /// Set input baud rate (see
+ /// [cfsetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)).
+ ///
+ /// `cfsetispeed()` sets the intput baud rate in the given `Termios` structure.
+ pub fn cfsetispeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> {
+ 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(drop)
+ }
+
+ /// Set output baud rate (see
+ /// [cfsetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetospeed.html)).
+ ///
+ /// `cfsetospeed()` sets the output baud rate in the given termios structure.
+ pub fn cfsetospeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> {
+ 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(drop)
+ }
+
+ /// Set both the input and output baud rates (see
+ /// [termios(3)](https://www.freebsd.org/cgi/man.cgi?query=cfsetspeed)).
+ ///
+ /// `cfsetspeed()` sets the input and output baud rate in the given termios structure. Note that
+ /// this is part of the 4.4BSD standard and not part of POSIX.
+ pub fn cfsetspeed<T: Into<u32>>(termios: &mut Termios, baud: T) -> Result<()> {
+ 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(drop)
+ }
+ } else {
+ use std::convert::TryInto;
+
+ /// Get input baud rate (see
+ /// [cfgetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetispeed.html)).
+ ///
+ /// `cfgetispeed()` extracts the input baud rate from the given `Termios` structure.
+ pub fn cfgetispeed(termios: &Termios) -> BaudRate {
+ let inner_termios = termios.get_libc_termios();
+ unsafe { libc::cfgetispeed(&*inner_termios) }.try_into().unwrap()
+ }
+
+ /// Get output baud rate (see
+ /// [cfgetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfgetospeed.html)).
+ ///
+ /// `cfgetospeed()` extracts the output baud rate from the given `Termios` structure.
+ pub fn cfgetospeed(termios: &Termios) -> BaudRate {
+ let inner_termios = termios.get_libc_termios();
+ unsafe { libc::cfgetospeed(&*inner_termios) }.try_into().unwrap()
+ }
+
+ /// Set input baud rate (see
+ /// [cfsetispeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetispeed.html)).
+ ///
+ /// `cfsetispeed()` sets the intput baud rate in the given `Termios` structure.
+ pub fn cfsetispeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
+ 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(drop)
+ }
+
+ /// Set output baud rate (see
+ /// [cfsetospeed(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/cfsetospeed.html)).
+ ///
+ /// `cfsetospeed()` sets the output baud rate in the given `Termios` structure.
+ pub fn cfsetospeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
+ 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(drop)
+ }
+
+ /// Set both the input and output baud rates (see
+ /// [termios(3)](https://www.freebsd.org/cgi/man.cgi?query=cfsetspeed)).
+ ///
+ /// `cfsetspeed()` sets the input and output baud rate in the given `Termios` structure. Note that
+ /// this is part of the 4.4BSD standard and not part of POSIX.
+ #[cfg(not(target_os = "haiku"))]
+ pub fn cfsetspeed(termios: &mut Termios, baud: BaudRate) -> Result<()> {
+ 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(drop)
+ }
+ }
+}
+
+/// Configures the port to something like the "raw" mode of the old Version 7 terminal driver (see
+/// [termios(3)](https://man7.org/linux/man-pages/man3/termios.3.html)).
+///
+/// `cfmakeraw()` configures the termios structure such that input is available character-by-
+/// character, echoing is disabled, and all special input and output processing is disabled. Note
+/// that this is a non-standard function, but is available on Linux and BSDs.
+pub fn cfmakeraw(termios: &mut Termios) {
+ let inner_termios = unsafe { termios.get_libc_termios_mut() };
+ unsafe {
+ libc::cfmakeraw(inner_termios);
+ }
+ termios.update_wrapper();
+}
+
+/// Configures the port to "sane" mode (like the configuration of a newly created terminal) (see
+/// [tcsetattr(3)](https://www.freebsd.org/cgi/man.cgi?query=tcsetattr)).
+///
+/// Note that this is a non-standard function, available on FreeBSD.
+#[cfg(target_os = "freebsd")]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub fn cfmakesane(termios: &mut Termios) {
+ let inner_termios = unsafe { termios.get_libc_termios_mut() };
+ unsafe {
+ libc::cfmakesane(inner_termios);
+ }
+ termios.update_wrapper();
+}
+
+/// Return the configuration of a port
+/// [tcgetattr(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetattr.html)).
+///
+/// `tcgetattr()` returns a `Termios` structure with the current configuration for a port. Modifying
+/// this structure *will not* reconfigure the port, instead the modifications should be done to
+/// the `Termios` structure and then the port should be reconfigured using `tcsetattr()`.
+pub fn tcgetattr(fd: RawFd) -> Result<Termios> {
+ let mut termios = mem::MaybeUninit::uninit();
+
+ let res = unsafe { libc::tcgetattr(fd, termios.as_mut_ptr()) };
+
+ Errno::result(res)?;
+
+ unsafe { Ok(termios.assume_init().into()) }
+}
+
+/// Set the configuration for a terminal (see
+/// [tcsetattr(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsetattr.html)).
+///
+/// `tcsetattr()` reconfigures the given port based on a given `Termios` structure. This change
+/// takes affect at a time specified by `actions`. Note that this function may return success if
+/// *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(drop)
+}
+
+/// Block until all output data is written (see
+/// [tcdrain(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcdrain.html)).
+pub fn tcdrain(fd: RawFd) -> Result<()> {
+ Errno::result(unsafe { libc::tcdrain(fd) }).map(drop)
+}
+
+/// Suspend or resume the transmission or reception of data (see
+/// [tcflow(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcflow.html)).
+///
+/// `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(drop)
+}
+
+/// Discard data in the output or input queue (see
+/// [tcflush(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcflush.html)).
+///
+/// `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(drop)
+}
+
+/// Send a break for a specific duration (see
+/// [tcsendbreak(3p)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsendbreak.html)).
+///
+/// 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(drop)
+}
+
+feature! {
+#![feature = "process"]
+/// Get the session controlled by the given terminal (see
+/// [tcgetsid(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetsid.html)).
+pub fn tcgetsid(fd: RawFd) -> Result<Pid> {
+ let res = unsafe { libc::tcgetsid(fd) };
+
+ Errno::result(res).map(Pid::from_raw)
+}
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use std::convert::TryFrom;
+
+ #[test]
+ fn try_from() {
+ assert_eq!(Ok(BaudRate::B0), BaudRate::try_from(libc::B0));
+ #[cfg(not(target_os = "haiku"))]
+ BaudRate::try_from(999999999).expect_err("assertion failed");
+ #[cfg(target_os = "haiku")]
+ BaudRate::try_from(99).expect_err("assertion failed");
+ }
+}
diff --git a/third_party/rust/nix/src/sys/time.rs b/third_party/rust/nix/src/sys/time.rs
new file mode 100644
index 0000000000..0042c45084
--- /dev/null
+++ b/third_party/rust/nix/src/sys/time.rs
@@ -0,0 +1,811 @@
+#[cfg_attr(target_env = "musl", allow(deprecated))]
+// https://github.com/rust-lang/libc/issues/1848
+pub use libc::{suseconds_t, time_t};
+use libc::{timespec, timeval};
+use std::convert::From;
+use std::time::Duration;
+use std::{cmp, fmt, ops};
+
+const fn zero_init_timespec() -> timespec {
+ // `std::mem::MaybeUninit::zeroed()` is not yet a const fn
+ // (https://github.com/rust-lang/rust/issues/91850) so we will instead initialize an array of
+ // the appropriate size to zero and then transmute it to a timespec value.
+ unsafe { std::mem::transmute([0u8; std::mem::size_of::<timespec>()]) }
+}
+
+#[cfg(any(
+ all(feature = "time", any(target_os = "android", target_os = "linux")),
+ all(
+ any(
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd"
+ ),
+ feature = "time",
+ feature = "signal"
+ )
+))]
+pub(crate) mod timer {
+ use crate::sys::time::{zero_init_timespec, TimeSpec};
+ use bitflags::bitflags;
+
+ #[derive(Debug, Clone, Copy)]
+ pub(crate) struct TimerSpec(libc::itimerspec);
+
+ impl TimerSpec {
+ pub const fn none() -> Self {
+ Self(libc::itimerspec {
+ it_interval: zero_init_timespec(),
+ it_value: zero_init_timespec(),
+ })
+ }
+ }
+
+ impl AsMut<libc::itimerspec> for TimerSpec {
+ fn as_mut(&mut self) -> &mut libc::itimerspec {
+ &mut self.0
+ }
+ }
+
+ impl AsRef<libc::itimerspec> for TimerSpec {
+ fn as_ref(&self) -> &libc::itimerspec {
+ &self.0
+ }
+ }
+
+ impl From<Expiration> for TimerSpec {
+ fn from(expiration: Expiration) -> TimerSpec {
+ match expiration {
+ Expiration::OneShot(t) => TimerSpec(libc::itimerspec {
+ it_interval: zero_init_timespec(),
+ it_value: *t.as_ref(),
+ }),
+ Expiration::IntervalDelayed(start, interval) => {
+ TimerSpec(libc::itimerspec {
+ it_interval: *interval.as_ref(),
+ it_value: *start.as_ref(),
+ })
+ }
+ Expiration::Interval(t) => TimerSpec(libc::itimerspec {
+ it_interval: *t.as_ref(),
+ it_value: *t.as_ref(),
+ }),
+ }
+ }
+ }
+
+ /// An enumeration allowing the definition of the expiration time of an alarm,
+ /// recurring or not.
+ #[derive(Debug, Clone, Copy, Eq, PartialEq)]
+ pub enum Expiration {
+ /// Alarm will trigger once after the time given in `TimeSpec`
+ OneShot(TimeSpec),
+ /// Alarm will trigger after a specified delay and then every interval of
+ /// time.
+ IntervalDelayed(TimeSpec, TimeSpec),
+ /// Alarm will trigger every specified interval of time.
+ Interval(TimeSpec),
+ }
+
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ bitflags! {
+ /// Flags that are used for arming the timer.
+ pub struct TimerSetTimeFlags: libc::c_int {
+ const TFD_TIMER_ABSTIME = libc::TFD_TIMER_ABSTIME;
+ }
+ }
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "netbsd",
+ target_os = "dragonfly",
+ target_os = "illumos"
+ ))]
+ bitflags! {
+ /// Flags that are used for arming the timer.
+ pub struct TimerSetTimeFlags: libc::c_int {
+ const TFD_TIMER_ABSTIME = libc::TIMER_ABSTIME;
+ }
+ }
+
+ impl From<TimerSpec> for Expiration {
+ fn from(timerspec: TimerSpec) -> Expiration {
+ match timerspec {
+ TimerSpec(libc::itimerspec {
+ it_interval:
+ libc::timespec {
+ tv_sec: 0,
+ tv_nsec: 0,
+ ..
+ },
+ it_value: ts,
+ }) => Expiration::OneShot(ts.into()),
+ TimerSpec(libc::itimerspec {
+ it_interval: int_ts,
+ it_value: val_ts,
+ }) => {
+ if (int_ts.tv_sec == val_ts.tv_sec)
+ && (int_ts.tv_nsec == val_ts.tv_nsec)
+ {
+ Expiration::Interval(int_ts.into())
+ } else {
+ Expiration::IntervalDelayed(
+ val_ts.into(),
+ int_ts.into(),
+ )
+ }
+ }
+ }
+ }
+ }
+}
+
+pub trait TimeValLike: Sized {
+ #[inline]
+ fn zero() -> Self {
+ Self::seconds(0)
+ }
+
+ #[inline]
+ fn hours(hours: i64) -> Self {
+ let secs = hours
+ .checked_mul(SECS_PER_HOUR)
+ .expect("TimeValLike::hours ouf of bounds");
+ Self::seconds(secs)
+ }
+
+ #[inline]
+ fn minutes(minutes: i64) -> Self {
+ let secs = minutes
+ .checked_mul(SECS_PER_MINUTE)
+ .expect("TimeValLike::minutes out of bounds");
+ Self::seconds(secs)
+ }
+
+ fn seconds(seconds: i64) -> Self;
+ fn milliseconds(milliseconds: i64) -> Self;
+ fn microseconds(microseconds: i64) -> Self;
+ fn nanoseconds(nanoseconds: i64) -> Self;
+
+ #[inline]
+ fn num_hours(&self) -> i64 {
+ self.num_seconds() / 3600
+ }
+
+ #[inline]
+ fn num_minutes(&self) -> i64 {
+ self.num_seconds() / 60
+ }
+
+ fn num_seconds(&self) -> i64;
+ fn num_milliseconds(&self) -> i64;
+ fn num_microseconds(&self) -> i64;
+ fn num_nanoseconds(&self) -> i64;
+}
+
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct TimeSpec(timespec);
+
+const NANOS_PER_SEC: i64 = 1_000_000_000;
+const SECS_PER_MINUTE: i64 = 60;
+const SECS_PER_HOUR: i64 = 3600;
+
+#[cfg(target_pointer_width = "64")]
+const TS_MAX_SECONDS: i64 = (i64::MAX / NANOS_PER_SEC) - 1;
+
+#[cfg(target_pointer_width = "32")]
+const TS_MAX_SECONDS: i64 = isize::MAX as i64;
+
+const TS_MIN_SECONDS: i64 = -TS_MAX_SECONDS;
+
+// x32 compatibility
+// See https://sourceware.org/bugzilla/show_bug.cgi?id=16437
+#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
+type timespec_tv_nsec_t = i64;
+#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
+type timespec_tv_nsec_t = libc::c_long;
+
+impl From<timespec> for TimeSpec {
+ fn from(ts: timespec) -> Self {
+ Self(ts)
+ }
+}
+
+impl From<Duration> for TimeSpec {
+ fn from(duration: Duration) -> Self {
+ Self::from_duration(duration)
+ }
+}
+
+impl From<TimeSpec> for Duration {
+ fn from(timespec: TimeSpec) -> Self {
+ Duration::new(timespec.0.tv_sec as u64, timespec.0.tv_nsec as u32)
+ }
+}
+
+impl AsRef<timespec> for TimeSpec {
+ fn as_ref(&self) -> &timespec {
+ &self.0
+ }
+}
+
+impl AsMut<timespec> for TimeSpec {
+ fn as_mut(&mut self) -> &mut timespec {
+ &mut self.0
+ }
+}
+
+impl Ord for TimeSpec {
+ // The implementation of cmp is simplified by assuming that the struct is
+ // normalized. That is, tv_nsec must always be within [0, 1_000_000_000)
+ fn cmp(&self, other: &TimeSpec) -> cmp::Ordering {
+ if self.tv_sec() == other.tv_sec() {
+ self.tv_nsec().cmp(&other.tv_nsec())
+ } else {
+ self.tv_sec().cmp(&other.tv_sec())
+ }
+ }
+}
+
+impl PartialOrd for TimeSpec {
+ fn partial_cmp(&self, other: &TimeSpec) -> Option<cmp::Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl TimeValLike for TimeSpec {
+ #[inline]
+ #[cfg_attr(target_env = "musl", allow(deprecated))]
+ // https://github.com/rust-lang/libc/issues/1848
+ fn seconds(seconds: i64) -> TimeSpec {
+ assert!(
+ (TS_MIN_SECONDS..=TS_MAX_SECONDS).contains(&seconds),
+ "TimeSpec out of bounds; seconds={}",
+ seconds
+ );
+ let mut ts = zero_init_timespec();
+ ts.tv_sec = seconds as time_t;
+ TimeSpec(ts)
+ }
+
+ #[inline]
+ fn milliseconds(milliseconds: i64) -> TimeSpec {
+ let nanoseconds = milliseconds
+ .checked_mul(1_000_000)
+ .expect("TimeSpec::milliseconds out of bounds");
+
+ TimeSpec::nanoseconds(nanoseconds)
+ }
+
+ /// Makes a new `TimeSpec` with given number of microseconds.
+ #[inline]
+ fn microseconds(microseconds: i64) -> TimeSpec {
+ let nanoseconds = microseconds
+ .checked_mul(1_000)
+ .expect("TimeSpec::milliseconds out of bounds");
+
+ TimeSpec::nanoseconds(nanoseconds)
+ }
+
+ /// Makes a new `TimeSpec` with given number of nanoseconds.
+ #[inline]
+ #[cfg_attr(target_env = "musl", allow(deprecated))]
+ // https://github.com/rust-lang/libc/issues/1848
+ fn nanoseconds(nanoseconds: i64) -> TimeSpec {
+ let (secs, nanos) = div_mod_floor_64(nanoseconds, NANOS_PER_SEC);
+ assert!(
+ (TS_MIN_SECONDS..=TS_MAX_SECONDS).contains(&secs),
+ "TimeSpec out of bounds"
+ );
+ let mut ts = zero_init_timespec();
+ ts.tv_sec = secs as time_t;
+ ts.tv_nsec = nanos as timespec_tv_nsec_t;
+ TimeSpec(ts)
+ }
+
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ fn num_seconds(&self) -> i64 {
+ if self.tv_sec() < 0 && self.tv_nsec() > 0 {
+ (self.tv_sec() + 1) as i64
+ } else {
+ self.tv_sec() as i64
+ }
+ }
+
+ fn num_milliseconds(&self) -> i64 {
+ self.num_nanoseconds() / 1_000_000
+ }
+
+ fn num_microseconds(&self) -> i64 {
+ self.num_nanoseconds() / 1_000
+ }
+
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ fn num_nanoseconds(&self) -> i64 {
+ let secs = self.num_seconds() * 1_000_000_000;
+ let nsec = self.nanos_mod_sec();
+ secs + nsec as i64
+ }
+}
+
+impl TimeSpec {
+ /// Construct a new `TimeSpec` from its components
+ #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
+ pub const fn new(seconds: time_t, nanoseconds: timespec_tv_nsec_t) -> Self {
+ let mut ts = zero_init_timespec();
+ ts.tv_sec = seconds;
+ ts.tv_nsec = nanoseconds;
+ Self(ts)
+ }
+
+ fn nanos_mod_sec(&self) -> timespec_tv_nsec_t {
+ if self.tv_sec() < 0 && self.tv_nsec() > 0 {
+ self.tv_nsec() - NANOS_PER_SEC as timespec_tv_nsec_t
+ } else {
+ self.tv_nsec()
+ }
+ }
+
+ #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
+ pub const fn tv_sec(&self) -> time_t {
+ self.0.tv_sec
+ }
+
+ pub const fn tv_nsec(&self) -> timespec_tv_nsec_t {
+ self.0.tv_nsec
+ }
+
+ #[cfg_attr(target_env = "musl", allow(deprecated))]
+ // https://github.com/rust-lang/libc/issues/1848
+ pub const fn from_duration(duration: Duration) -> Self {
+ let mut ts = zero_init_timespec();
+ ts.tv_sec = duration.as_secs() as time_t;
+ ts.tv_nsec = duration.subsec_nanos() as timespec_tv_nsec_t;
+ TimeSpec(ts)
+ }
+
+ pub const fn from_timespec(timespec: timespec) -> Self {
+ Self(timespec)
+ }
+}
+
+impl ops::Neg for TimeSpec {
+ type Output = TimeSpec;
+
+ fn neg(self) -> TimeSpec {
+ TimeSpec::nanoseconds(-self.num_nanoseconds())
+ }
+}
+
+impl ops::Add for TimeSpec {
+ type Output = TimeSpec;
+
+ fn add(self, rhs: TimeSpec) -> TimeSpec {
+ TimeSpec::nanoseconds(self.num_nanoseconds() + rhs.num_nanoseconds())
+ }
+}
+
+impl ops::Sub for TimeSpec {
+ type Output = TimeSpec;
+
+ fn sub(self, rhs: TimeSpec) -> TimeSpec {
+ TimeSpec::nanoseconds(self.num_nanoseconds() - rhs.num_nanoseconds())
+ }
+}
+
+impl ops::Mul<i32> for TimeSpec {
+ type Output = TimeSpec;
+
+ fn mul(self, rhs: i32) -> TimeSpec {
+ let usec = self
+ .num_nanoseconds()
+ .checked_mul(i64::from(rhs))
+ .expect("TimeSpec multiply out of bounds");
+
+ TimeSpec::nanoseconds(usec)
+ }
+}
+
+impl ops::Div<i32> for TimeSpec {
+ type Output = TimeSpec;
+
+ fn div(self, rhs: i32) -> TimeSpec {
+ let usec = self.num_nanoseconds() / i64::from(rhs);
+ TimeSpec::nanoseconds(usec)
+ }
+}
+
+impl fmt::Display for TimeSpec {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let (abs, sign) = if self.tv_sec() < 0 {
+ (-*self, "-")
+ } else {
+ (*self, "")
+ };
+
+ let sec = abs.tv_sec();
+
+ write!(f, "{}", sign)?;
+
+ if abs.tv_nsec() == 0 {
+ if abs.tv_sec() == 1 {
+ write!(f, "{} second", sec)?;
+ } else {
+ write!(f, "{} seconds", sec)?;
+ }
+ } else if abs.tv_nsec() % 1_000_000 == 0 {
+ write!(f, "{}.{:03} seconds", sec, abs.tv_nsec() / 1_000_000)?;
+ } else if abs.tv_nsec() % 1_000 == 0 {
+ write!(f, "{}.{:06} seconds", sec, abs.tv_nsec() / 1_000)?;
+ } else {
+ write!(f, "{}.{:09} seconds", sec, abs.tv_nsec())?;
+ }
+
+ Ok(())
+ }
+}
+
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct TimeVal(timeval);
+
+const MICROS_PER_SEC: i64 = 1_000_000;
+
+#[cfg(target_pointer_width = "64")]
+const TV_MAX_SECONDS: i64 = (i64::MAX / MICROS_PER_SEC) - 1;
+
+#[cfg(target_pointer_width = "32")]
+const TV_MAX_SECONDS: i64 = isize::MAX as i64;
+
+const TV_MIN_SECONDS: i64 = -TV_MAX_SECONDS;
+
+impl AsRef<timeval> for TimeVal {
+ fn as_ref(&self) -> &timeval {
+ &self.0
+ }
+}
+
+impl AsMut<timeval> for TimeVal {
+ fn as_mut(&mut self) -> &mut timeval {
+ &mut self.0
+ }
+}
+
+impl Ord for TimeVal {
+ // The implementation of cmp is simplified by assuming that the struct is
+ // normalized. That is, tv_usec must always be within [0, 1_000_000)
+ fn cmp(&self, other: &TimeVal) -> cmp::Ordering {
+ if self.tv_sec() == other.tv_sec() {
+ self.tv_usec().cmp(&other.tv_usec())
+ } else {
+ self.tv_sec().cmp(&other.tv_sec())
+ }
+ }
+}
+
+impl PartialOrd for TimeVal {
+ fn partial_cmp(&self, other: &TimeVal) -> Option<cmp::Ordering> {
+ Some(self.cmp(other))
+ }
+}
+
+impl TimeValLike for TimeVal {
+ #[inline]
+ fn seconds(seconds: i64) -> TimeVal {
+ assert!(
+ (TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&seconds),
+ "TimeVal out of bounds; seconds={}",
+ seconds
+ );
+ #[cfg_attr(target_env = "musl", allow(deprecated))]
+ // https://github.com/rust-lang/libc/issues/1848
+ TimeVal(timeval {
+ tv_sec: seconds as time_t,
+ tv_usec: 0,
+ })
+ }
+
+ #[inline]
+ fn milliseconds(milliseconds: i64) -> TimeVal {
+ let microseconds = milliseconds
+ .checked_mul(1_000)
+ .expect("TimeVal::milliseconds out of bounds");
+
+ TimeVal::microseconds(microseconds)
+ }
+
+ /// Makes a new `TimeVal` with given number of microseconds.
+ #[inline]
+ fn microseconds(microseconds: i64) -> TimeVal {
+ let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC);
+ assert!(
+ (TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&secs),
+ "TimeVal out of bounds"
+ );
+ #[cfg_attr(target_env = "musl", allow(deprecated))]
+ // https://github.com/rust-lang/libc/issues/1848
+ TimeVal(timeval {
+ tv_sec: secs as time_t,
+ tv_usec: micros as suseconds_t,
+ })
+ }
+
+ /// Makes a new `TimeVal` with given number of nanoseconds. Some precision
+ /// will be lost
+ #[inline]
+ fn nanoseconds(nanoseconds: i64) -> TimeVal {
+ let microseconds = nanoseconds / 1000;
+ let (secs, micros) = div_mod_floor_64(microseconds, MICROS_PER_SEC);
+ assert!(
+ (TV_MIN_SECONDS..=TV_MAX_SECONDS).contains(&secs),
+ "TimeVal out of bounds"
+ );
+ #[cfg_attr(target_env = "musl", allow(deprecated))]
+ // https://github.com/rust-lang/libc/issues/1848
+ TimeVal(timeval {
+ tv_sec: secs as time_t,
+ tv_usec: micros as suseconds_t,
+ })
+ }
+
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ fn num_seconds(&self) -> i64 {
+ if self.tv_sec() < 0 && self.tv_usec() > 0 {
+ (self.tv_sec() + 1) as i64
+ } else {
+ self.tv_sec() as i64
+ }
+ }
+
+ fn num_milliseconds(&self) -> i64 {
+ self.num_microseconds() / 1_000
+ }
+
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ fn num_microseconds(&self) -> i64 {
+ let secs = self.num_seconds() * 1_000_000;
+ let usec = self.micros_mod_sec();
+ secs + usec as i64
+ }
+
+ fn num_nanoseconds(&self) -> i64 {
+ self.num_microseconds() * 1_000
+ }
+}
+
+impl TimeVal {
+ /// Construct a new `TimeVal` from its components
+ #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
+ pub const fn new(seconds: time_t, microseconds: suseconds_t) -> Self {
+ Self(timeval {
+ tv_sec: seconds,
+ tv_usec: microseconds,
+ })
+ }
+
+ fn micros_mod_sec(&self) -> suseconds_t {
+ if self.tv_sec() < 0 && self.tv_usec() > 0 {
+ self.tv_usec() - MICROS_PER_SEC as suseconds_t
+ } else {
+ self.tv_usec()
+ }
+ }
+
+ #[cfg_attr(target_env = "musl", allow(deprecated))] // https://github.com/rust-lang/libc/issues/1848
+ pub const fn tv_sec(&self) -> time_t {
+ self.0.tv_sec
+ }
+
+ pub const fn tv_usec(&self) -> suseconds_t {
+ self.0.tv_usec
+ }
+}
+
+impl ops::Neg for TimeVal {
+ type Output = TimeVal;
+
+ fn neg(self) -> TimeVal {
+ TimeVal::microseconds(-self.num_microseconds())
+ }
+}
+
+impl ops::Add for TimeVal {
+ type Output = TimeVal;
+
+ fn add(self, rhs: TimeVal) -> TimeVal {
+ TimeVal::microseconds(self.num_microseconds() + rhs.num_microseconds())
+ }
+}
+
+impl ops::Sub for TimeVal {
+ type Output = TimeVal;
+
+ fn sub(self, rhs: TimeVal) -> TimeVal {
+ TimeVal::microseconds(self.num_microseconds() - rhs.num_microseconds())
+ }
+}
+
+impl ops::Mul<i32> for TimeVal {
+ type Output = TimeVal;
+
+ fn mul(self, rhs: i32) -> TimeVal {
+ let usec = self
+ .num_microseconds()
+ .checked_mul(i64::from(rhs))
+ .expect("TimeVal multiply out of bounds");
+
+ TimeVal::microseconds(usec)
+ }
+}
+
+impl ops::Div<i32> for TimeVal {
+ type Output = TimeVal;
+
+ fn div(self, rhs: i32) -> TimeVal {
+ let usec = self.num_microseconds() / i64::from(rhs);
+ TimeVal::microseconds(usec)
+ }
+}
+
+impl fmt::Display for TimeVal {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ let (abs, sign) = if self.tv_sec() < 0 {
+ (-*self, "-")
+ } else {
+ (*self, "")
+ };
+
+ let sec = abs.tv_sec();
+
+ write!(f, "{}", sign)?;
+
+ if abs.tv_usec() == 0 {
+ if abs.tv_sec() == 1 {
+ write!(f, "{} second", sec)?;
+ } else {
+ write!(f, "{} seconds", sec)?;
+ }
+ } else if abs.tv_usec() % 1000 == 0 {
+ write!(f, "{}.{:03} seconds", sec, abs.tv_usec() / 1000)?;
+ } else {
+ write!(f, "{}.{:06} seconds", sec, abs.tv_usec())?;
+ }
+
+ Ok(())
+ }
+}
+
+impl From<timeval> for TimeVal {
+ fn from(tv: timeval) -> Self {
+ TimeVal(tv)
+ }
+}
+
+#[inline]
+fn div_mod_floor_64(this: i64, other: i64) -> (i64, i64) {
+ (div_floor_64(this, other), mod_floor_64(this, other))
+}
+
+#[inline]
+fn div_floor_64(this: i64, other: i64) -> i64 {
+ match div_rem_64(this, other) {
+ (d, r) if (r > 0 && other < 0) || (r < 0 && other > 0) => d - 1,
+ (d, _) => d,
+ }
+}
+
+#[inline]
+fn mod_floor_64(this: i64, other: i64) -> i64 {
+ match this % other {
+ r if (r > 0 && other < 0) || (r < 0 && other > 0) => r + other,
+ r => r,
+ }
+}
+
+#[inline]
+fn div_rem_64(this: i64, other: i64) -> (i64, i64) {
+ (this / other, this % other)
+}
+
+#[cfg(test)]
+mod test {
+ use super::{TimeSpec, TimeVal, TimeValLike};
+ use std::time::Duration;
+
+ #[test]
+ pub fn test_timespec() {
+ assert_ne!(TimeSpec::seconds(1), TimeSpec::zero());
+ assert_eq!(
+ TimeSpec::seconds(1) + TimeSpec::seconds(2),
+ TimeSpec::seconds(3)
+ );
+ assert_eq!(
+ TimeSpec::minutes(3) + TimeSpec::seconds(2),
+ TimeSpec::seconds(182)
+ );
+ }
+
+ #[test]
+ pub fn test_timespec_from() {
+ let duration = Duration::new(123, 123_456_789);
+ let timespec = TimeSpec::nanoseconds(123_123_456_789);
+
+ assert_eq!(TimeSpec::from(duration), timespec);
+ assert_eq!(Duration::from(timespec), duration);
+ }
+
+ #[test]
+ pub fn test_timespec_neg() {
+ let a = TimeSpec::seconds(1) + TimeSpec::nanoseconds(123);
+ let b = TimeSpec::seconds(-1) + TimeSpec::nanoseconds(-123);
+
+ assert_eq!(a, -b);
+ }
+
+ #[test]
+ pub fn test_timespec_ord() {
+ assert_eq!(TimeSpec::seconds(1), TimeSpec::nanoseconds(1_000_000_000));
+ assert!(TimeSpec::seconds(1) < TimeSpec::nanoseconds(1_000_000_001));
+ assert!(TimeSpec::seconds(1) > TimeSpec::nanoseconds(999_999_999));
+ assert!(TimeSpec::seconds(-1) < TimeSpec::nanoseconds(-999_999_999));
+ assert!(TimeSpec::seconds(-1) > TimeSpec::nanoseconds(-1_000_000_001));
+ }
+
+ #[test]
+ pub fn test_timespec_fmt() {
+ assert_eq!(TimeSpec::zero().to_string(), "0 seconds");
+ assert_eq!(TimeSpec::seconds(42).to_string(), "42 seconds");
+ assert_eq!(TimeSpec::milliseconds(42).to_string(), "0.042 seconds");
+ assert_eq!(TimeSpec::microseconds(42).to_string(), "0.000042 seconds");
+ assert_eq!(
+ TimeSpec::nanoseconds(42).to_string(),
+ "0.000000042 seconds"
+ );
+ assert_eq!(TimeSpec::seconds(-86401).to_string(), "-86401 seconds");
+ }
+
+ #[test]
+ pub fn test_timeval() {
+ assert_ne!(TimeVal::seconds(1), TimeVal::zero());
+ assert_eq!(
+ TimeVal::seconds(1) + TimeVal::seconds(2),
+ TimeVal::seconds(3)
+ );
+ assert_eq!(
+ TimeVal::minutes(3) + TimeVal::seconds(2),
+ TimeVal::seconds(182)
+ );
+ }
+
+ #[test]
+ pub fn test_timeval_ord() {
+ assert_eq!(TimeVal::seconds(1), TimeVal::microseconds(1_000_000));
+ assert!(TimeVal::seconds(1) < TimeVal::microseconds(1_000_001));
+ assert!(TimeVal::seconds(1) > TimeVal::microseconds(999_999));
+ assert!(TimeVal::seconds(-1) < TimeVal::microseconds(-999_999));
+ assert!(TimeVal::seconds(-1) > TimeVal::microseconds(-1_000_001));
+ }
+
+ #[test]
+ pub fn test_timeval_neg() {
+ let a = TimeVal::seconds(1) + TimeVal::microseconds(123);
+ let b = TimeVal::seconds(-1) + TimeVal::microseconds(-123);
+
+ assert_eq!(a, -b);
+ }
+
+ #[test]
+ pub fn test_timeval_fmt() {
+ assert_eq!(TimeVal::zero().to_string(), "0 seconds");
+ assert_eq!(TimeVal::seconds(42).to_string(), "42 seconds");
+ assert_eq!(TimeVal::milliseconds(42).to_string(), "0.042 seconds");
+ assert_eq!(TimeVal::microseconds(42).to_string(), "0.000042 seconds");
+ assert_eq!(TimeVal::nanoseconds(1402).to_string(), "0.000001 seconds");
+ assert_eq!(TimeVal::seconds(-86401).to_string(), "-86401 seconds");
+ }
+}
diff --git a/third_party/rust/nix/src/sys/timer.rs b/third_party/rust/nix/src/sys/timer.rs
new file mode 100644
index 0000000000..e1a34051e8
--- /dev/null
+++ b/third_party/rust/nix/src/sys/timer.rs
@@ -0,0 +1,187 @@
+//! Timer API via signals.
+//!
+//! Timer is a POSIX API to create timers and get expiration notifications
+//! through queued Unix signals, for the current process. This is similar to
+//! Linux's timerfd mechanism, except that API is specific to Linux and makes
+//! use of file polling.
+//!
+//! For more documentation, please read [timer_create](https://pubs.opengroup.org/onlinepubs/9699919799/functions/timer_create.html).
+//!
+//! # Examples
+//!
+//! Create an interval timer that signals SIGALARM every 250 milliseconds.
+//!
+//! ```no_run
+//! use nix::sys::signal::{self, SigEvent, SigHandler, SigevNotify, Signal};
+//! use nix::sys::timer::{Expiration, Timer, TimerSetTimeFlags};
+//! use nix::time::ClockId;
+//! use std::convert::TryFrom;
+//! use std::sync::atomic::{AtomicU64, Ordering};
+//! use std::thread::yield_now;
+//! use std::time::Duration;
+//!
+//! const SIG: Signal = Signal::SIGALRM;
+//! static ALARMS: AtomicU64 = AtomicU64::new(0);
+//!
+//! extern "C" fn handle_alarm(signal: libc::c_int) {
+//! let signal = Signal::try_from(signal).unwrap();
+//! if signal == SIG {
+//! ALARMS.fetch_add(1, Ordering::Relaxed);
+//! }
+//! }
+//!
+//! fn main() {
+//! let clockid = ClockId::CLOCK_MONOTONIC;
+//! let sigevent = SigEvent::new(SigevNotify::SigevSignal {
+//! signal: SIG,
+//! si_value: 0,
+//! });
+//!
+//! let mut timer = Timer::new(clockid, sigevent).unwrap();
+//! let expiration = Expiration::Interval(Duration::from_millis(250).into());
+//! let flags = TimerSetTimeFlags::empty();
+//! timer.set(expiration, flags).expect("could not set timer");
+//!
+//! let handler = SigHandler::Handler(handle_alarm);
+//! unsafe { signal::signal(SIG, handler) }.unwrap();
+//!
+//! loop {
+//! let alarms = ALARMS.load(Ordering::Relaxed);
+//! if alarms >= 10 {
+//! println!("total alarms handled: {}", alarms);
+//! break;
+//! }
+//! yield_now()
+//! }
+//! }
+//! ```
+use crate::sys::signal::SigEvent;
+use crate::sys::time::timer::TimerSpec;
+pub use crate::sys::time::timer::{Expiration, TimerSetTimeFlags};
+use crate::time::ClockId;
+use crate::{errno::Errno, Result};
+use core::mem;
+
+/// A Unix signal per-process timer.
+#[derive(Debug)]
+#[repr(transparent)]
+pub struct Timer(libc::timer_t);
+
+impl Timer {
+ /// Creates a new timer based on the clock defined by `clockid`. The details
+ /// of the signal and its handler are defined by the passed `sigevent`.
+ #[doc(alias("timer_create"))]
+ pub fn new(clockid: ClockId, mut sigevent: SigEvent) -> Result<Self> {
+ let mut timer_id: mem::MaybeUninit<libc::timer_t> =
+ mem::MaybeUninit::uninit();
+ Errno::result(unsafe {
+ libc::timer_create(
+ clockid.as_raw(),
+ sigevent.as_mut_ptr(),
+ timer_id.as_mut_ptr(),
+ )
+ })
+ .map(|_| {
+ // SAFETY: libc::timer_create is responsible for initializing
+ // timer_id.
+ unsafe { Self(timer_id.assume_init()) }
+ })
+ }
+
+ /// Set a new alarm on the timer.
+ ///
+ /// # Types of alarm
+ ///
+ /// There are 3 types of alarms you can set:
+ ///
+ /// - one shot: the alarm will trigger once after the specified amount of
+ /// time.
+ /// Example: I want an alarm to go off in 60s and then disable itself.
+ ///
+ /// - interval: the alarm will trigger every specified interval of time.
+ /// Example: I want an alarm to go off every 60s. The alarm will first
+ /// go off 60s after I set it and every 60s after that. The alarm will
+ /// not disable itself.
+ ///
+ /// - interval delayed: the alarm will trigger after a certain amount of
+ /// time and then trigger at a specified interval.
+ /// Example: I want an alarm to go off every 60s but only start in 1h.
+ /// The alarm will first trigger 1h after I set it and then every 60s
+ /// after that. The alarm will not disable itself.
+ ///
+ /// # Relative vs absolute alarm
+ ///
+ /// If you do not set any `TimerSetTimeFlags`, then the `TimeSpec` you pass
+ /// to the `Expiration` you want is relative. If however you want an alarm
+ /// to go off at a certain point in time, you can set `TFD_TIMER_ABSTIME`.
+ /// Then the one shot TimeSpec and the delay TimeSpec of the delayed
+ /// interval are going to be interpreted as absolute.
+ ///
+ /// # Disabling alarms
+ ///
+ /// Note: Only one alarm can be set for any given timer. Setting a new alarm
+ /// actually removes the previous one.
+ ///
+ /// Note: Setting a one shot alarm with a 0s TimeSpec disable the alarm
+ /// altogether.
+ #[doc(alias("timer_settime"))]
+ pub fn set(
+ &mut self,
+ expiration: Expiration,
+ flags: TimerSetTimeFlags,
+ ) -> Result<()> {
+ let timerspec: TimerSpec = expiration.into();
+ Errno::result(unsafe {
+ libc::timer_settime(
+ self.0,
+ flags.bits(),
+ timerspec.as_ref(),
+ core::ptr::null_mut(),
+ )
+ })
+ .map(drop)
+ }
+
+ /// Get the parameters for the alarm currently set, if any.
+ #[doc(alias("timer_gettime"))]
+ pub fn get(&self) -> Result<Option<Expiration>> {
+ let mut timerspec = TimerSpec::none();
+ Errno::result(unsafe {
+ libc::timer_gettime(self.0, timerspec.as_mut())
+ })
+ .map(|_| {
+ if timerspec.as_ref().it_interval.tv_sec == 0
+ && timerspec.as_ref().it_interval.tv_nsec == 0
+ && timerspec.as_ref().it_value.tv_sec == 0
+ && timerspec.as_ref().it_value.tv_nsec == 0
+ {
+ None
+ } else {
+ Some(timerspec.into())
+ }
+ })
+ }
+
+ /// Return the number of timers that have overrun
+ ///
+ /// Each timer is able to queue one signal to the process at a time, meaning
+ /// if the signal is not handled before the next expiration the timer has
+ /// 'overrun'. This function returns how many times that has happened to
+ /// this timer, up to `libc::DELAYTIMER_MAX`. If more than the maximum
+ /// number of overruns have happened the return is capped to the maximum.
+ #[doc(alias("timer_getoverrun"))]
+ pub fn overruns(&self) -> i32 {
+ unsafe { libc::timer_getoverrun(self.0) }
+ }
+}
+
+impl Drop for Timer {
+ fn drop(&mut self) {
+ if !std::thread::panicking() {
+ let result = Errno::result(unsafe { libc::timer_delete(self.0) });
+ if let Err(Errno::EINVAL) = result {
+ panic!("close of Timer encountered EINVAL");
+ }
+ }
+ }
+}
diff --git a/third_party/rust/nix/src/sys/timerfd.rs b/third_party/rust/nix/src/sys/timerfd.rs
new file mode 100644
index 0000000000..a35fc927f4
--- /dev/null
+++ b/third_party/rust/nix/src/sys/timerfd.rs
@@ -0,0 +1,214 @@
+//! Timer API via file descriptors.
+//!
+//! Timer FD is a Linux-only API to create timers and get expiration
+//! notifications through file descriptors.
+//!
+//! For more documentation, please read [timerfd_create(2)](https://man7.org/linux/man-pages/man2/timerfd_create.2.html).
+//!
+//! # Examples
+//!
+//! Create a new one-shot timer that expires after 1 second.
+//! ```
+//! # use std::os::unix::io::AsRawFd;
+//! # use nix::sys::timerfd::{TimerFd, ClockId, TimerFlags, TimerSetTimeFlags,
+//! # Expiration};
+//! # use nix::sys::time::{TimeSpec, TimeValLike};
+//! # use nix::unistd::read;
+//! #
+//! // We create a new monotonic timer.
+//! let timer = TimerFd::new(ClockId::CLOCK_MONOTONIC, TimerFlags::empty())
+//! .unwrap();
+//!
+//! // We set a new one-shot timer in 1 seconds.
+//! timer.set(
+//! Expiration::OneShot(TimeSpec::seconds(1)),
+//! TimerSetTimeFlags::empty()
+//! ).unwrap();
+//!
+//! // We wait for the timer to expire.
+//! timer.wait().unwrap();
+//! ```
+use crate::sys::time::timer::TimerSpec;
+pub use crate::sys::time::timer::{Expiration, TimerSetTimeFlags};
+use crate::unistd::read;
+use crate::{errno::Errno, Result};
+use libc::c_int;
+use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
+
+/// A timerfd instance. This is also a file descriptor, you can feed it to
+/// other interfaces consuming file descriptors, epoll for example.
+#[derive(Debug)]
+pub struct TimerFd {
+ fd: RawFd,
+}
+
+impl AsRawFd for TimerFd {
+ fn as_raw_fd(&self) -> RawFd {
+ self.fd
+ }
+}
+
+impl FromRawFd for TimerFd {
+ unsafe fn from_raw_fd(fd: RawFd) -> Self {
+ TimerFd { fd }
+ }
+}
+
+libc_enum! {
+ /// The type of the clock used to mark the progress of the timer. For more
+ /// details on each kind of clock, please refer to [timerfd_create(2)](https://man7.org/linux/man-pages/man2/timerfd_create.2.html).
+ #[repr(i32)]
+ #[non_exhaustive]
+ pub enum ClockId {
+ /// A settable system-wide real-time clock.
+ CLOCK_REALTIME,
+ /// A non-settable monotonically increasing clock.
+ ///
+ /// Does not change after system startup.
+ /// Does not measure time while the system is suspended.
+ CLOCK_MONOTONIC,
+ /// Like `CLOCK_MONOTONIC`, except that `CLOCK_BOOTTIME` includes the time
+ /// that the system was suspended.
+ CLOCK_BOOTTIME,
+ /// Like `CLOCK_REALTIME`, but will wake the system if it is suspended.
+ CLOCK_REALTIME_ALARM,
+ /// Like `CLOCK_BOOTTIME`, but will wake the system if it is suspended.
+ CLOCK_BOOTTIME_ALARM,
+ }
+}
+
+libc_bitflags! {
+ /// Additional flags to change the behaviour of the file descriptor at the
+ /// time of creation.
+ pub struct TimerFlags: c_int {
+ /// Set the `O_NONBLOCK` flag on the open file description referred to by the new file descriptor.
+ TFD_NONBLOCK;
+ /// Set the `FD_CLOEXEC` flag on the file descriptor.
+ TFD_CLOEXEC;
+ }
+}
+
+impl TimerFd {
+ /// Creates a new timer based on the clock defined by `clockid`. The
+ /// underlying fd can be assigned specific flags with `flags` (CLOEXEC,
+ /// NONBLOCK). The underlying fd will be closed on drop.
+ #[doc(alias("timerfd_create"))]
+ pub fn new(clockid: ClockId, flags: TimerFlags) -> Result<Self> {
+ Errno::result(unsafe {
+ libc::timerfd_create(clockid as i32, flags.bits())
+ })
+ .map(|fd| Self { fd })
+ }
+
+ /// Sets a new alarm on the timer.
+ ///
+ /// # Types of alarm
+ ///
+ /// There are 3 types of alarms you can set:
+ ///
+ /// - one shot: the alarm will trigger once after the specified amount of
+ /// time.
+ /// Example: I want an alarm to go off in 60s and then disable itself.
+ ///
+ /// - interval: the alarm will trigger every specified interval of time.
+ /// Example: I want an alarm to go off every 60s. The alarm will first
+ /// go off 60s after I set it and every 60s after that. The alarm will
+ /// not disable itself.
+ ///
+ /// - interval delayed: the alarm will trigger after a certain amount of
+ /// time and then trigger at a specified interval.
+ /// Example: I want an alarm to go off every 60s but only start in 1h.
+ /// The alarm will first trigger 1h after I set it and then every 60s
+ /// after that. The alarm will not disable itself.
+ ///
+ /// # Relative vs absolute alarm
+ ///
+ /// If you do not set any `TimerSetTimeFlags`, then the `TimeSpec` you pass
+ /// to the `Expiration` you want is relative. If however you want an alarm
+ /// to go off at a certain point in time, you can set `TFD_TIMER_ABSTIME`.
+ /// Then the one shot TimeSpec and the delay TimeSpec of the delayed
+ /// interval are going to be interpreted as absolute.
+ ///
+ /// # Disabling alarms
+ ///
+ /// Note: Only one alarm can be set for any given timer. Setting a new alarm
+ /// actually removes the previous one.
+ ///
+ /// Note: Setting a one shot alarm with a 0s TimeSpec disables the alarm
+ /// altogether.
+ #[doc(alias("timerfd_settime"))]
+ pub fn set(
+ &self,
+ expiration: Expiration,
+ flags: TimerSetTimeFlags,
+ ) -> Result<()> {
+ let timerspec: TimerSpec = expiration.into();
+ Errno::result(unsafe {
+ libc::timerfd_settime(
+ self.fd,
+ flags.bits(),
+ timerspec.as_ref(),
+ std::ptr::null_mut(),
+ )
+ })
+ .map(drop)
+ }
+
+ /// Get the parameters for the alarm currently set, if any.
+ #[doc(alias("timerfd_gettime"))]
+ pub fn get(&self) -> Result<Option<Expiration>> {
+ let mut timerspec = TimerSpec::none();
+ Errno::result(unsafe {
+ libc::timerfd_gettime(self.fd, timerspec.as_mut())
+ })
+ .map(|_| {
+ if timerspec.as_ref().it_interval.tv_sec == 0
+ && timerspec.as_ref().it_interval.tv_nsec == 0
+ && timerspec.as_ref().it_value.tv_sec == 0
+ && timerspec.as_ref().it_value.tv_nsec == 0
+ {
+ None
+ } else {
+ Some(timerspec.into())
+ }
+ })
+ }
+
+ /// Remove the alarm if any is set.
+ #[doc(alias("timerfd_settime"))]
+ pub fn unset(&self) -> Result<()> {
+ Errno::result(unsafe {
+ libc::timerfd_settime(
+ self.fd,
+ TimerSetTimeFlags::empty().bits(),
+ TimerSpec::none().as_ref(),
+ std::ptr::null_mut(),
+ )
+ })
+ .map(drop)
+ }
+
+ /// Wait for the configured alarm to expire.
+ ///
+ /// Note: If the alarm is unset, then you will wait forever.
+ pub fn wait(&self) -> Result<()> {
+ while let Err(e) = read(self.fd, &mut [0u8; 8]) {
+ if e != Errno::EINTR {
+ return Err(e);
+ }
+ }
+
+ Ok(())
+ }
+}
+
+impl Drop for TimerFd {
+ fn drop(&mut self) {
+ if !std::thread::panicking() {
+ let result = Errno::result(unsafe { libc::close(self.fd) });
+ if let Err(Errno::EBADF) = result {
+ panic!("close of TimerFd encountered EBADF");
+ }
+ }
+ }
+}
diff --git a/third_party/rust/nix/src/sys/uio.rs b/third_party/rust/nix/src/sys/uio.rs
new file mode 100644
index 0000000000..b31c3068a7
--- /dev/null
+++ b/third_party/rust/nix/src/sys/uio.rs
@@ -0,0 +1,291 @@
+//! Vectored I/O
+
+use crate::errno::Errno;
+use crate::Result;
+use libc::{self, c_int, c_void, off_t, size_t};
+use std::io::{IoSlice, IoSliceMut};
+use std::marker::PhantomData;
+use std::os::unix::io::RawFd;
+
+/// Low-level vectored write to a raw file descriptor
+///
+/// See also [writev(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/writev.html)
+pub fn writev(fd: RawFd, iov: &[IoSlice<'_>]) -> Result<usize> {
+ // SAFETY: to quote the documentation for `IoSlice`:
+ //
+ // [IoSlice] is semantically a wrapper around a &[u8], but is
+ // guaranteed to be ABI compatible with the iovec type on Unix
+ // platforms.
+ //
+ // Because it is ABI compatible, a pointer cast here is valid
+ let res = unsafe {
+ libc::writev(fd, iov.as_ptr() as *const libc::iovec, iov.len() as c_int)
+ };
+
+ Errno::result(res).map(|r| r as usize)
+}
+
+/// Low-level vectored read from a raw file descriptor
+///
+/// See also [readv(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/readv.html)
+pub fn readv(fd: RawFd, iov: &mut [IoSliceMut<'_>]) -> Result<usize> {
+ // SAFETY: same as in writev(), IoSliceMut is ABI-compatible with iovec
+ let res = unsafe {
+ libc::readv(fd, iov.as_ptr() as *const libc::iovec, iov.len() as c_int)
+ };
+
+ Errno::result(res).map(|r| r as usize)
+}
+
+/// Write to `fd` at `offset` from buffers in `iov`.
+///
+/// Buffers in `iov` will be written in order until all buffers have been written
+/// or an error occurs. The file offset is not changed.
+///
+/// See also: [`writev`](fn.writev.html) and [`pwrite`](fn.pwrite.html)
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub fn pwritev(fd: RawFd, iov: &[IoSlice<'_>], offset: off_t) -> Result<usize> {
+ #[cfg(target_env = "uclibc")]
+ let offset = offset as libc::off64_t; // uclibc doesn't use off_t
+
+ // SAFETY: same as in writev()
+ let res = unsafe {
+ libc::pwritev(
+ fd,
+ iov.as_ptr() as *const libc::iovec,
+ iov.len() as c_int,
+ offset,
+ )
+ };
+
+ Errno::result(res).map(|r| r as usize)
+}
+
+/// Read from `fd` at `offset` filling buffers in `iov`.
+///
+/// Buffers in `iov` will be filled in order until all buffers have been filled,
+/// no more bytes are available, or an error occurs. The file offset is not
+/// changed.
+///
+/// See also: [`readv`](fn.readv.html) and [`pread`](fn.pread.html)
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub fn preadv(
+ fd: RawFd,
+ iov: &mut [IoSliceMut<'_>],
+ offset: off_t,
+) -> Result<usize> {
+ #[cfg(target_env = "uclibc")]
+ let offset = offset as libc::off64_t; // uclibc doesn't use off_t
+
+ // SAFETY: same as in readv()
+ let res = unsafe {
+ libc::preadv(
+ fd,
+ iov.as_ptr() as *const libc::iovec,
+ iov.len() as c_int,
+ offset,
+ )
+ };
+
+ Errno::result(res).map(|r| r as usize)
+}
+
+/// Low-level write to a file, with specified offset.
+///
+/// See also [pwrite(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pwrite.html)
+// TODO: move to unistd
+pub fn pwrite(fd: RawFd, buf: &[u8], offset: off_t) -> Result<usize> {
+ let res = unsafe {
+ libc::pwrite(
+ fd,
+ buf.as_ptr() as *const c_void,
+ buf.len() as size_t,
+ offset,
+ )
+ };
+
+ Errno::result(res).map(|r| r as usize)
+}
+
+/// Low-level read from a file, with specified offset.
+///
+/// See also [pread(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pread.html)
+// TODO: move to unistd
+pub fn pread(fd: RawFd, buf: &mut [u8], offset: off_t) -> Result<usize> {
+ let res = unsafe {
+ libc::pread(
+ fd,
+ buf.as_mut_ptr() as *mut c_void,
+ buf.len() as size_t,
+ offset,
+ )
+ };
+
+ Errno::result(res).map(|r| r as usize)
+}
+
+/// A slice of memory in a remote process, starting at address `base`
+/// and consisting of `len` bytes.
+///
+/// This is the same underlying C structure as `IoSlice`,
+/// except that it refers to memory in some other process, and is
+/// therefore not represented in Rust by an actual slice as `IoSlice` is. It
+/// is used with [`process_vm_readv`](fn.process_vm_readv.html)
+/// and [`process_vm_writev`](fn.process_vm_writev.html).
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+#[repr(C)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct RemoteIoVec {
+ /// The starting address of this slice (`iov_base`).
+ pub base: usize,
+ /// The number of bytes in this slice (`iov_len`).
+ pub len: usize,
+}
+
+/// A vector of buffers.
+///
+/// Vectored I/O methods like [`writev`] and [`readv`] use this structure for
+/// both reading and writing. Each `IoVec` specifies the base address and
+/// length of an area in memory.
+#[deprecated(
+ since = "0.24.0",
+ note = "`IoVec` is no longer used in the public interface, use `IoSlice` or `IoSliceMut` instead"
+)]
+#[repr(transparent)]
+#[allow(renamed_and_removed_lints)]
+#[allow(clippy::unknown_clippy_lints)]
+// Clippy false positive: https://github.com/rust-lang/rust-clippy/issues/8867
+#[allow(clippy::derive_partial_eq_without_eq)]
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct IoVec<T>(pub(crate) libc::iovec, PhantomData<T>);
+
+#[allow(deprecated)]
+impl<T> IoVec<T> {
+ /// View the `IoVec` as a Rust slice.
+ #[deprecated(
+ since = "0.24.0",
+ note = "Use the `Deref` impl of `IoSlice` or `IoSliceMut` instead"
+ )]
+ #[inline]
+ pub fn as_slice(&self) -> &[u8] {
+ use std::slice;
+
+ unsafe {
+ slice::from_raw_parts(self.0.iov_base as *const u8, self.0.iov_len)
+ }
+ }
+}
+
+#[allow(deprecated)]
+impl<'a> IoVec<&'a [u8]> {
+ /// Create an `IoVec` from a Rust slice.
+ #[deprecated(since = "0.24.0", note = "Use `IoSlice::new` instead")]
+ pub fn from_slice(buf: &'a [u8]) -> IoVec<&'a [u8]> {
+ IoVec(
+ libc::iovec {
+ iov_base: buf.as_ptr() as *mut c_void,
+ iov_len: buf.len() as size_t,
+ },
+ PhantomData,
+ )
+ }
+}
+
+#[allow(deprecated)]
+impl<'a> IoVec<&'a mut [u8]> {
+ /// Create an `IoVec` from a mutable Rust slice.
+ #[deprecated(since = "0.24.0", note = "Use `IoSliceMut::new` instead")]
+ pub fn from_mut_slice(buf: &'a mut [u8]) -> IoVec<&'a mut [u8]> {
+ IoVec(
+ libc::iovec {
+ iov_base: buf.as_ptr() as *mut c_void,
+ iov_len: buf.len() as size_t,
+ },
+ PhantomData,
+ )
+ }
+}
+
+// The only reason IoVec isn't automatically Send+Sync is because libc::iovec
+// contains raw pointers.
+#[allow(deprecated)]
+unsafe impl<T> Send for IoVec<T> where T: Send {}
+#[allow(deprecated)]
+unsafe impl<T> Sync for IoVec<T> where T: Sync {}
+
+feature! {
+#![feature = "process"]
+
+/// Write data directly to another process's virtual memory
+/// (see [`process_vm_writev`(2)]).
+///
+/// `local_iov` is a list of [`IoSlice`]s containing the data to be written,
+/// and `remote_iov` is a list of [`RemoteIoVec`]s identifying where the
+/// data should be written in the target process. On success, returns the
+/// number of bytes written, which will always be a whole
+/// number of `remote_iov` chunks.
+///
+/// This requires the same permissions as debugging the process using
+/// [ptrace]: you must either be a privileged process (with
+/// `CAP_SYS_PTRACE`), or you must be running as the same user as the
+/// target process and the OS must have unprivileged debugging enabled.
+///
+/// This function is only available on Linux and Android(SDK23+).
+///
+/// [`process_vm_writev`(2)]: https://man7.org/linux/man-pages/man2/process_vm_writev.2.html
+/// [ptrace]: ../ptrace/index.html
+/// [`IoSlice`]: https://doc.rust-lang.org/std/io/struct.IoSlice.html
+/// [`RemoteIoVec`]: struct.RemoteIoVec.html
+#[cfg(all(any(target_os = "linux", target_os = "android"), not(target_env = "uclibc")))]
+pub fn process_vm_writev(
+ pid: crate::unistd::Pid,
+ local_iov: &[IoSlice<'_>],
+ remote_iov: &[RemoteIoVec]) -> Result<usize>
+{
+ let res = unsafe {
+ libc::process_vm_writev(pid.into(),
+ local_iov.as_ptr() as *const libc::iovec, local_iov.len() as libc::c_ulong,
+ remote_iov.as_ptr() as *const libc::iovec, remote_iov.len() as libc::c_ulong, 0)
+ };
+
+ Errno::result(res).map(|r| r as usize)
+}
+
+/// Read data directly from another process's virtual memory
+/// (see [`process_vm_readv`(2)]).
+///
+/// `local_iov` is a list of [`IoSliceMut`]s containing the buffer to copy
+/// data into, and `remote_iov` is a list of [`RemoteIoVec`]s identifying
+/// where the source data is in the target process. On success,
+/// returns the number of bytes written, which will always be a whole
+/// number of `remote_iov` chunks.
+///
+/// This requires the same permissions as debugging the process using
+/// [`ptrace`]: you must either be a privileged process (with
+/// `CAP_SYS_PTRACE`), or you must be running as the same user as the
+/// target process and the OS must have unprivileged debugging enabled.
+///
+/// This function is only available on Linux and Android(SDK23+).
+///
+/// [`process_vm_readv`(2)]: https://man7.org/linux/man-pages/man2/process_vm_readv.2.html
+/// [`ptrace`]: ../ptrace/index.html
+/// [`IoSliceMut`]: https://doc.rust-lang.org/std/io/struct.IoSliceMut.html
+/// [`RemoteIoVec`]: struct.RemoteIoVec.html
+#[cfg(all(any(target_os = "linux", target_os = "android"), not(target_env = "uclibc")))]
+pub fn process_vm_readv(
+ pid: crate::unistd::Pid,
+ local_iov: &mut [IoSliceMut<'_>],
+ remote_iov: &[RemoteIoVec]) -> Result<usize>
+{
+ let res = unsafe {
+ libc::process_vm_readv(pid.into(),
+ local_iov.as_ptr() as *const libc::iovec, local_iov.len() as libc::c_ulong,
+ remote_iov.as_ptr() as *const libc::iovec, remote_iov.len() as libc::c_ulong, 0)
+ };
+
+ Errno::result(res).map(|r| r as usize)
+}
+}
diff --git a/third_party/rust/nix/src/sys/utsname.rs b/third_party/rust/nix/src/sys/utsname.rs
new file mode 100644
index 0000000000..b48ed9f45e
--- /dev/null
+++ b/third_party/rust/nix/src/sys/utsname.rs
@@ -0,0 +1,85 @@
+//! Get system identification
+use crate::{Errno, Result};
+use libc::c_char;
+use std::ffi::OsStr;
+use std::mem;
+use std::os::unix::ffi::OsStrExt;
+
+/// Describes the running system. Return type of [`uname`].
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[repr(transparent)]
+pub struct UtsName(libc::utsname);
+
+impl UtsName {
+ /// Name of the operating system implementation.
+ pub fn sysname(&self) -> &OsStr {
+ cast_and_trim(&self.0.sysname)
+ }
+
+ /// Network name of this machine.
+ pub fn nodename(&self) -> &OsStr {
+ cast_and_trim(&self.0.nodename)
+ }
+
+ /// Release level of the operating system.
+ pub fn release(&self) -> &OsStr {
+ cast_and_trim(&self.0.release)
+ }
+
+ /// Version level of the operating system.
+ pub fn version(&self) -> &OsStr {
+ cast_and_trim(&self.0.version)
+ }
+
+ /// Machine hardware platform.
+ pub fn machine(&self) -> &OsStr {
+ cast_and_trim(&self.0.machine)
+ }
+
+ /// NIS or YP domain name of this machine.
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ pub fn domainname(&self) -> &OsStr {
+ cast_and_trim(&self.0.domainname)
+ }
+}
+
+/// Get system identification
+pub fn uname() -> Result<UtsName> {
+ unsafe {
+ let mut ret = mem::MaybeUninit::zeroed();
+ Errno::result(libc::uname(ret.as_mut_ptr()))?;
+ Ok(UtsName(ret.assume_init()))
+ }
+}
+
+fn cast_and_trim(slice: &[c_char]) -> &OsStr {
+ let length = slice
+ .iter()
+ .position(|&byte| byte == 0)
+ .unwrap_or(slice.len());
+ let bytes =
+ unsafe { std::slice::from_raw_parts(slice.as_ptr().cast(), length) };
+
+ OsStr::from_bytes(bytes)
+}
+
+#[cfg(test)]
+mod test {
+ #[cfg(target_os = "linux")]
+ #[test]
+ pub fn test_uname_linux() {
+ assert_eq!(super::uname().unwrap().sysname(), "Linux");
+ }
+
+ #[cfg(any(target_os = "macos", target_os = "ios"))]
+ #[test]
+ pub fn test_uname_darwin() {
+ assert_eq!(super::uname().unwrap().sysname(), "Darwin");
+ }
+
+ #[cfg(target_os = "freebsd")]
+ #[test]
+ pub fn test_uname_freebsd() {
+ assert_eq!(super::uname().unwrap().sysname(), "FreeBSD");
+ }
+}
diff --git a/third_party/rust/nix/src/sys/wait.rs b/third_party/rust/nix/src/sys/wait.rs
new file mode 100644
index 0000000000..b6524e8661
--- /dev/null
+++ b/third_party/rust/nix/src/sys/wait.rs
@@ -0,0 +1,388 @@
+//! Wait for a process to change status
+use crate::errno::Errno;
+use crate::sys::signal::Signal;
+use crate::unistd::Pid;
+use crate::Result;
+use cfg_if::cfg_if;
+use libc::{self, c_int};
+use std::convert::TryFrom;
+#[cfg(any(
+ target_os = "android",
+ all(target_os = "linux", not(target_env = "uclibc")),
+))]
+use std::os::unix::io::RawFd;
+
+libc_bitflags!(
+ /// Controls the behavior of [`waitpid`].
+ pub struct WaitPidFlag: c_int {
+ /// Do not block when there are no processes wishing to report status.
+ WNOHANG;
+ /// Report the status of selected processes which are stopped due to a
+ /// [`SIGTTIN`](crate::sys::signal::Signal::SIGTTIN),
+ /// [`SIGTTOU`](crate::sys::signal::Signal::SIGTTOU),
+ /// [`SIGTSTP`](crate::sys::signal::Signal::SIGTSTP), or
+ /// [`SIGSTOP`](crate::sys::signal::Signal::SIGSTOP) signal.
+ WUNTRACED;
+ /// Report the status of selected processes which have terminated.
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "redox",
+ target_os = "macos",
+ target_os = "netbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ WEXITED;
+ /// Report the status of selected processes that have continued from a
+ /// job control stop by receiving a
+ /// [`SIGCONT`](crate::sys::signal::Signal::SIGCONT) signal.
+ WCONTINUED;
+ /// An alias for WUNTRACED.
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "redox",
+ target_os = "macos",
+ target_os = "netbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ WSTOPPED;
+ /// Don't reap, just poll status.
+ #[cfg(any(target_os = "android",
+ target_os = "freebsd",
+ target_os = "haiku",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "redox",
+ target_os = "macos",
+ target_os = "netbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ WNOWAIT;
+ /// Don't wait on children of other threads in this group
+ #[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ __WNOTHREAD;
+ /// Wait on all children, regardless of type
+ #[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ __WALL;
+ /// Wait for "clone" children only.
+ #[cfg(any(target_os = "android", target_os = "linux", target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ __WCLONE;
+ }
+);
+
+/// Possible return values from `wait()` or `waitpid()`.
+///
+/// Each status (other than `StillAlive`) describes a state transition
+/// in a child process `Pid`, such as the process exiting or stopping,
+/// plus additional data about the transition if any.
+///
+/// Note that there are two Linux-specific enum variants, `PtraceEvent`
+/// and `PtraceSyscall`. Portable code should avoid exhaustively
+/// matching on `WaitStatus`.
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub enum WaitStatus {
+ /// The process exited normally (as with `exit()` or returning from
+ /// `main`) with the given exit code. This case matches the C macro
+ /// `WIFEXITED(status)`; the second field is `WEXITSTATUS(status)`.
+ Exited(Pid, i32),
+ /// The process was killed by the given signal. The third field
+ /// indicates whether the signal generated a core dump. This case
+ /// matches the C macro `WIFSIGNALED(status)`; the last two fields
+ /// correspond to `WTERMSIG(status)` and `WCOREDUMP(status)`.
+ Signaled(Pid, Signal, bool),
+ /// The process is alive, but was stopped by the given signal. This
+ /// is only reported if `WaitPidFlag::WUNTRACED` was passed. This
+ /// case matches the C macro `WIFSTOPPED(status)`; the second field
+ /// is `WSTOPSIG(status)`.
+ Stopped(Pid, Signal),
+ /// The traced process was stopped by a `PTRACE_EVENT_*` event. See
+ /// [`nix::sys::ptrace`] and [`ptrace`(2)] for more information. All
+ /// currently-defined events use `SIGTRAP` as the signal; the third
+ /// field is the `PTRACE_EVENT_*` value of the event.
+ ///
+ /// [`nix::sys::ptrace`]: ../ptrace/index.html
+ /// [`ptrace`(2)]: https://man7.org/linux/man-pages/man2/ptrace.2.html
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ PtraceEvent(Pid, Signal, c_int),
+ /// The traced process was stopped by execution of a system call,
+ /// and `PTRACE_O_TRACESYSGOOD` is in effect. See [`ptrace`(2)] for
+ /// more information.
+ ///
+ /// [`ptrace`(2)]: https://man7.org/linux/man-pages/man2/ptrace.2.html
+ #[cfg(any(target_os = "linux", target_os = "android"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ PtraceSyscall(Pid),
+ /// The process was previously stopped but has resumed execution
+ /// after receiving a `SIGCONT` signal. This is only reported if
+ /// `WaitPidFlag::WCONTINUED` was passed. This case matches the C
+ /// macro `WIFCONTINUED(status)`.
+ Continued(Pid),
+ /// There are currently no state changes to report in any awaited
+ /// child process. This is only returned if `WaitPidFlag::WNOHANG`
+ /// was used (otherwise `wait()` or `waitpid()` would block until
+ /// there was something to report).
+ StillAlive,
+}
+
+impl WaitStatus {
+ /// Extracts the PID from the WaitStatus unless it equals StillAlive.
+ pub fn pid(&self) -> Option<Pid> {
+ use self::WaitStatus::*;
+ match *self {
+ Exited(p, _) | Signaled(p, _, _) | Stopped(p, _) | Continued(p) => {
+ Some(p)
+ }
+ StillAlive => None,
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ PtraceEvent(p, _, _) | PtraceSyscall(p) => Some(p),
+ }
+ }
+}
+
+fn exited(status: i32) -> bool {
+ libc::WIFEXITED(status)
+}
+
+fn exit_status(status: i32) -> i32 {
+ libc::WEXITSTATUS(status)
+}
+
+fn signaled(status: i32) -> bool {
+ libc::WIFSIGNALED(status)
+}
+
+fn term_signal(status: i32) -> Result<Signal> {
+ Signal::try_from(libc::WTERMSIG(status))
+}
+
+fn dumped_core(status: i32) -> bool {
+ libc::WCOREDUMP(status)
+}
+
+fn stopped(status: i32) -> bool {
+ libc::WIFSTOPPED(status)
+}
+
+fn stop_signal(status: i32) -> Result<Signal> {
+ Signal::try_from(libc::WSTOPSIG(status))
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+fn syscall_stop(status: i32) -> bool {
+ // From ptrace(2), setting PTRACE_O_TRACESYSGOOD has the effect
+ // of delivering SIGTRAP | 0x80 as the signal number for syscall
+ // stops. This allows easily distinguishing syscall stops from
+ // genuine SIGTRAP signals.
+ libc::WSTOPSIG(status) == libc::SIGTRAP | 0x80
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+fn stop_additional(status: i32) -> c_int {
+ (status >> 16) as c_int
+}
+
+fn continued(status: i32) -> bool {
+ libc::WIFCONTINUED(status)
+}
+
+impl WaitStatus {
+ /// Convert a raw `wstatus` as returned by `waitpid`/`wait` into a `WaitStatus`
+ ///
+ /// # Errors
+ ///
+ /// Returns an `Error` corresponding to `EINVAL` for invalid status values.
+ ///
+ /// # Examples
+ ///
+ /// Convert a `wstatus` obtained from `libc::waitpid` into a `WaitStatus`:
+ ///
+ /// ```
+ /// use nix::sys::wait::WaitStatus;
+ /// use nix::sys::signal::Signal;
+ /// let pid = nix::unistd::Pid::from_raw(1);
+ /// let status = WaitStatus::from_raw(pid, 0x0002);
+ /// assert_eq!(status, Ok(WaitStatus::Signaled(pid, Signal::SIGINT, false)));
+ /// ```
+ pub fn from_raw(pid: Pid, status: i32) -> Result<WaitStatus> {
+ Ok(if exited(status) {
+ WaitStatus::Exited(pid, exit_status(status))
+ } else if signaled(status) {
+ WaitStatus::Signaled(pid, term_signal(status)?, dumped_core(status))
+ } else if stopped(status) {
+ cfg_if! {
+ if #[cfg(any(target_os = "android", target_os = "linux"))] {
+ fn decode_stopped(pid: Pid, status: i32) -> Result<WaitStatus> {
+ let status_additional = stop_additional(status);
+ Ok(if syscall_stop(status) {
+ WaitStatus::PtraceSyscall(pid)
+ } else if status_additional == 0 {
+ WaitStatus::Stopped(pid, stop_signal(status)?)
+ } else {
+ WaitStatus::PtraceEvent(pid, stop_signal(status)?,
+ stop_additional(status))
+ })
+ }
+ } else {
+ fn decode_stopped(pid: Pid, status: i32) -> Result<WaitStatus> {
+ Ok(WaitStatus::Stopped(pid, stop_signal(status)?))
+ }
+ }
+ }
+ return decode_stopped(pid, status);
+ } else {
+ assert!(continued(status));
+ WaitStatus::Continued(pid)
+ })
+ }
+
+ /// Convert a `siginfo_t` as returned by `waitid` to a `WaitStatus`
+ ///
+ /// # Errors
+ ///
+ /// Returns an `Error` corresponding to `EINVAL` for invalid values.
+ ///
+ /// # Safety
+ ///
+ /// siginfo_t is actually a union, not all fields may be initialized.
+ /// The functions si_pid() and si_status() must be valid to call on
+ /// the passed siginfo_t.
+ #[cfg(any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "haiku",
+ all(target_os = "linux", not(target_env = "uclibc")),
+ ))]
+ unsafe fn from_siginfo(siginfo: &libc::siginfo_t) -> Result<WaitStatus> {
+ let si_pid = siginfo.si_pid();
+ if si_pid == 0 {
+ return Ok(WaitStatus::StillAlive);
+ }
+
+ assert_eq!(siginfo.si_signo, libc::SIGCHLD);
+
+ let pid = Pid::from_raw(si_pid);
+ let si_status = siginfo.si_status();
+
+ let status = match siginfo.si_code {
+ libc::CLD_EXITED => WaitStatus::Exited(pid, si_status),
+ libc::CLD_KILLED | libc::CLD_DUMPED => WaitStatus::Signaled(
+ pid,
+ Signal::try_from(si_status)?,
+ siginfo.si_code == libc::CLD_DUMPED,
+ ),
+ libc::CLD_STOPPED => {
+ WaitStatus::Stopped(pid, Signal::try_from(si_status)?)
+ }
+ libc::CLD_CONTINUED => WaitStatus::Continued(pid),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ libc::CLD_TRAPPED => {
+ if si_status == libc::SIGTRAP | 0x80 {
+ WaitStatus::PtraceSyscall(pid)
+ } else {
+ WaitStatus::PtraceEvent(
+ pid,
+ Signal::try_from(si_status & 0xff)?,
+ (si_status >> 8) as c_int,
+ )
+ }
+ }
+ _ => return Err(Errno::EINVAL),
+ };
+
+ Ok(status)
+ }
+}
+
+/// Wait for a process to change status
+///
+/// See also [waitpid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/waitpid.html)
+pub fn waitpid<P: Into<Option<Pid>>>(
+ pid: P,
+ options: Option<WaitPidFlag>,
+) -> Result<WaitStatus> {
+ use self::WaitStatus::*;
+
+ let mut status: i32 = 0;
+
+ let option_bits = match options {
+ Some(bits) => bits.bits(),
+ None => 0,
+ };
+
+ let res = unsafe {
+ libc::waitpid(
+ pid.into().unwrap_or_else(|| Pid::from_raw(-1)).into(),
+ &mut status as *mut c_int,
+ option_bits,
+ )
+ };
+
+ match Errno::result(res)? {
+ 0 => Ok(StillAlive),
+ res => WaitStatus::from_raw(Pid::from_raw(res), status),
+ }
+}
+
+/// Wait for any child process to change status or a signal is received.
+///
+/// See also [wait(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/wait.html)
+pub fn wait() -> Result<WaitStatus> {
+ waitpid(None, None)
+}
+
+/// The ID argument for `waitid`
+#[cfg(any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "haiku",
+ all(target_os = "linux", not(target_env = "uclibc")),
+))]
+#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub enum Id {
+ /// Wait for any child
+ All,
+ /// Wait for the child whose process ID matches the given PID
+ Pid(Pid),
+ /// Wait for the child whose process group ID matches the given PID
+ ///
+ /// If the PID is zero, the caller's process group is used since Linux 5.4.
+ PGid(Pid),
+ /// Wait for the child referred to by the given PID file descriptor
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ PIDFd(RawFd),
+}
+
+/// Wait for a process to change status
+///
+/// See also [waitid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/waitid.html)
+#[cfg(any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "haiku",
+ all(target_os = "linux", not(target_env = "uclibc")),
+))]
+pub fn waitid(id: Id, flags: WaitPidFlag) -> Result<WaitStatus> {
+ let (idtype, idval) = match id {
+ Id::All => (libc::P_ALL, 0),
+ Id::Pid(pid) => (libc::P_PID, pid.as_raw() as libc::id_t),
+ Id::PGid(pid) => (libc::P_PGID, pid.as_raw() as libc::id_t),
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ Id::PIDFd(fd) => (libc::P_PIDFD, fd as libc::id_t),
+ };
+
+ let siginfo = unsafe {
+ // Memory is zeroed rather than uninitialized, as not all platforms
+ // initialize the memory in the StillAlive case
+ let mut siginfo: libc::siginfo_t = std::mem::zeroed();
+ Errno::result(libc::waitid(idtype, idval, &mut siginfo, flags.bits()))?;
+ siginfo
+ };
+
+ unsafe { WaitStatus::from_siginfo(&siginfo) }
+}
diff --git a/third_party/rust/nix/src/time.rs b/third_party/rust/nix/src/time.rs
new file mode 100644
index 0000000000..2e03c46cf4
--- /dev/null
+++ b/third_party/rust/nix/src/time.rs
@@ -0,0 +1,283 @@
+use crate::sys::time::TimeSpec;
+#[cfg(any(
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "linux",
+ target_os = "android",
+ target_os = "emscripten",
+))]
+#[cfg(feature = "process")]
+use crate::unistd::Pid;
+use crate::{Errno, Result};
+use libc::{self, clockid_t};
+use std::mem::MaybeUninit;
+
+/// Clock identifier
+///
+/// Newtype pattern around `clockid_t` (which is just alias). It prevents bugs caused by
+/// accidentally passing wrong value.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+pub struct ClockId(clockid_t);
+
+impl ClockId {
+ /// Creates `ClockId` from raw `clockid_t`
+ pub const fn from_raw(clk_id: clockid_t) -> Self {
+ ClockId(clk_id)
+ }
+
+ feature! {
+ #![feature = "process"]
+ /// Returns `ClockId` of a `pid` CPU-time clock
+ #[cfg(any(
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "linux",
+ target_os = "android",
+ target_os = "emscripten",
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn pid_cpu_clock_id(pid: Pid) -> Result<Self> {
+ clock_getcpuclockid(pid)
+ }
+ }
+
+ /// Returns resolution of the clock id
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn res(self) -> Result<TimeSpec> {
+ clock_getres(self)
+ }
+
+ /// Returns the current time on the clock id
+ pub fn now(self) -> Result<TimeSpec> {
+ clock_gettime(self)
+ }
+
+ /// Sets time to `timespec` on the clock id
+ #[cfg(not(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "redox",
+ target_os = "hermit",
+ )))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub fn set_time(self, timespec: TimeSpec) -> Result<()> {
+ clock_settime(self, timespec)
+ }
+
+ /// Gets the raw `clockid_t` wrapped by `self`
+ pub const fn as_raw(self) -> clockid_t {
+ self.0
+ }
+
+ #[cfg(any(
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "linux"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_BOOTTIME: ClockId = ClockId(libc::CLOCK_BOOTTIME);
+ #[cfg(any(
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "linux"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_BOOTTIME_ALARM: ClockId =
+ ClockId(libc::CLOCK_BOOTTIME_ALARM);
+ pub const CLOCK_MONOTONIC: ClockId = ClockId(libc::CLOCK_MONOTONIC);
+ #[cfg(any(
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "linux"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_MONOTONIC_COARSE: ClockId =
+ ClockId(libc::CLOCK_MONOTONIC_COARSE);
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_MONOTONIC_FAST: ClockId =
+ ClockId(libc::CLOCK_MONOTONIC_FAST);
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_MONOTONIC_PRECISE: ClockId =
+ ClockId(libc::CLOCK_MONOTONIC_PRECISE);
+ #[cfg(any(
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "linux"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_MONOTONIC_RAW: ClockId = ClockId(libc::CLOCK_MONOTONIC_RAW);
+ #[cfg(any(
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "redox",
+ target_os = "linux"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_PROCESS_CPUTIME_ID: ClockId =
+ ClockId(libc::CLOCK_PROCESS_CPUTIME_ID);
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_PROF: ClockId = ClockId(libc::CLOCK_PROF);
+ pub const CLOCK_REALTIME: ClockId = ClockId(libc::CLOCK_REALTIME);
+ #[cfg(any(
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "linux"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_REALTIME_ALARM: ClockId =
+ ClockId(libc::CLOCK_REALTIME_ALARM);
+ #[cfg(any(
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "linux"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_REALTIME_COARSE: ClockId =
+ ClockId(libc::CLOCK_REALTIME_COARSE);
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_REALTIME_FAST: ClockId = ClockId(libc::CLOCK_REALTIME_FAST);
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_REALTIME_PRECISE: ClockId =
+ ClockId(libc::CLOCK_REALTIME_PRECISE);
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_SECOND: ClockId = ClockId(libc::CLOCK_SECOND);
+ #[cfg(any(
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ all(target_os = "linux", target_env = "musl")
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_SGI_CYCLE: ClockId = ClockId(libc::CLOCK_SGI_CYCLE);
+ #[cfg(any(
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "linux"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_TAI: ClockId = ClockId(libc::CLOCK_TAI);
+ #[cfg(any(
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "fuchsia",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "linux"
+ ))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_THREAD_CPUTIME_ID: ClockId =
+ ClockId(libc::CLOCK_THREAD_CPUTIME_ID);
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_UPTIME: ClockId = ClockId(libc::CLOCK_UPTIME);
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_UPTIME_FAST: ClockId = ClockId(libc::CLOCK_UPTIME_FAST);
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_UPTIME_PRECISE: ClockId =
+ ClockId(libc::CLOCK_UPTIME_PRECISE);
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub const CLOCK_VIRTUAL: ClockId = ClockId(libc::CLOCK_VIRTUAL);
+}
+
+impl From<ClockId> for clockid_t {
+ fn from(clock_id: ClockId) -> Self {
+ clock_id.as_raw()
+ }
+}
+
+impl From<clockid_t> for ClockId {
+ fn from(clk_id: clockid_t) -> Self {
+ ClockId::from_raw(clk_id)
+ }
+}
+
+impl std::fmt::Display for ClockId {
+ fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
+ std::fmt::Display::fmt(&self.0, f)
+ }
+}
+
+/// Get the resolution of the specified clock, (see
+/// [clock_getres(2)](https://pubs.opengroup.org/onlinepubs/7908799/xsh/clock_getres.html)).
+#[cfg(not(target_os = "redox"))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub fn clock_getres(clock_id: ClockId) -> Result<TimeSpec> {
+ let mut c_time: MaybeUninit<libc::timespec> = MaybeUninit::uninit();
+ let ret =
+ unsafe { libc::clock_getres(clock_id.as_raw(), c_time.as_mut_ptr()) };
+ Errno::result(ret)?;
+ let res = unsafe { c_time.assume_init() };
+ Ok(TimeSpec::from(res))
+}
+
+/// Get the time of the specified clock, (see
+/// [clock_gettime(2)](https://pubs.opengroup.org/onlinepubs/7908799/xsh/clock_gettime.html)).
+pub fn clock_gettime(clock_id: ClockId) -> Result<TimeSpec> {
+ let mut c_time: MaybeUninit<libc::timespec> = MaybeUninit::uninit();
+ let ret =
+ unsafe { libc::clock_gettime(clock_id.as_raw(), c_time.as_mut_ptr()) };
+ Errno::result(ret)?;
+ let res = unsafe { c_time.assume_init() };
+ Ok(TimeSpec::from(res))
+}
+
+/// Set the time of the specified clock, (see
+/// [clock_settime(2)](https://pubs.opengroup.org/onlinepubs/7908799/xsh/clock_settime.html)).
+#[cfg(not(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "redox",
+ target_os = "hermit",
+)))]
+#[cfg_attr(docsrs, doc(cfg(all())))]
+pub fn clock_settime(clock_id: ClockId, timespec: TimeSpec) -> Result<()> {
+ let ret =
+ unsafe { libc::clock_settime(clock_id.as_raw(), timespec.as_ref()) };
+ Errno::result(ret).map(drop)
+}
+
+/// Get the clock id of the specified process id, (see
+/// [clock_getcpuclockid(3)](https://pubs.opengroup.org/onlinepubs/009695399/functions/clock_getcpuclockid.html)).
+#[cfg(any(
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "linux",
+ target_os = "android",
+ target_os = "emscripten",
+))]
+#[cfg(feature = "process")]
+#[cfg_attr(docsrs, doc(cfg(feature = "process")))]
+pub fn clock_getcpuclockid(pid: Pid) -> Result<ClockId> {
+ let mut clk_id: MaybeUninit<libc::clockid_t> = MaybeUninit::uninit();
+ let ret =
+ unsafe { libc::clock_getcpuclockid(pid.into(), clk_id.as_mut_ptr()) };
+ if ret == 0 {
+ let res = unsafe { clk_id.assume_init() };
+ Ok(ClockId::from(res))
+ } else {
+ Err(Errno::from_i32(ret))
+ }
+}
diff --git a/third_party/rust/nix/src/ucontext.rs b/third_party/rust/nix/src/ucontext.rs
new file mode 100644
index 0000000000..b2a39f7699
--- /dev/null
+++ b/third_party/rust/nix/src/ucontext.rs
@@ -0,0 +1,47 @@
+#[cfg(not(target_env = "musl"))]
+use crate::errno::Errno;
+use crate::sys::signal::SigSet;
+#[cfg(not(target_env = "musl"))]
+use crate::Result;
+#[cfg(not(target_env = "musl"))]
+use std::mem;
+
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+pub struct UContext {
+ context: libc::ucontext_t,
+}
+
+impl UContext {
+ #[cfg(not(target_env = "musl"))]
+ pub fn get() -> Result<UContext> {
+ let mut context = mem::MaybeUninit::<libc::ucontext_t>::uninit();
+ let res = unsafe { libc::getcontext(context.as_mut_ptr()) };
+ Errno::result(res).map(|_| unsafe {
+ UContext {
+ context: context.assume_init(),
+ }
+ })
+ }
+
+ #[cfg(not(target_env = "musl"))]
+ pub fn set(&self) -> Result<()> {
+ let res = unsafe {
+ libc::setcontext(&self.context as *const libc::ucontext_t)
+ };
+ Errno::result(res).map(drop)
+ }
+
+ pub fn sigmask_mut(&mut self) -> &mut SigSet {
+ unsafe {
+ &mut *(&mut self.context.uc_sigmask as *mut libc::sigset_t
+ as *mut SigSet)
+ }
+ }
+
+ pub fn sigmask(&self) -> &SigSet {
+ unsafe {
+ &*(&self.context.uc_sigmask as *const libc::sigset_t
+ as *const SigSet)
+ }
+ }
+}
diff --git a/third_party/rust/nix/src/unistd.rs b/third_party/rust/nix/src/unistd.rs
new file mode 100644
index 0000000000..ca07b34a2e
--- /dev/null
+++ b/third_party/rust/nix/src/unistd.rs
@@ -0,0 +1,3383 @@
+//! Safe wrappers around functions found in libc "unistd.h" header
+
+use crate::errno::{self, Errno};
+#[cfg(not(target_os = "redox"))]
+#[cfg(feature = "fs")]
+use crate::fcntl::{at_rawfd, AtFlags};
+#[cfg(feature = "fs")]
+use crate::fcntl::{fcntl, FcntlArg::F_SETFD, FdFlag, OFlag};
+#[cfg(all(
+ feature = "fs",
+ any(
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "macos",
+ target_os = "ios"
+ )
+))]
+use crate::sys::stat::FileFlag;
+#[cfg(feature = "fs")]
+use crate::sys::stat::Mode;
+use crate::{Error, NixPath, Result};
+#[cfg(not(target_os = "redox"))]
+use cfg_if::cfg_if;
+use libc::{
+ self, c_char, c_int, c_long, c_uint, c_void, gid_t, mode_t, off_t, pid_t,
+ size_t, uid_t, PATH_MAX,
+};
+use std::convert::Infallible;
+use std::ffi::{CStr, OsString};
+#[cfg(not(target_os = "redox"))]
+use std::ffi::{CString, OsStr};
+#[cfg(not(target_os = "redox"))]
+use std::os::unix::ffi::OsStrExt;
+use std::os::unix::ffi::OsStringExt;
+use std::os::unix::io::RawFd;
+use std::path::PathBuf;
+use std::{fmt, mem, ptr};
+
+feature! {
+ #![feature = "fs"]
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ pub use self::pivot_root::*;
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "openbsd"
+))]
+pub use self::setres::*;
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "openbsd"
+))]
+pub use self::getres::*;
+
+feature! {
+#![feature = "user"]
+
+/// User identifier
+///
+/// Newtype pattern around `uid_t` (which is just alias). It prevents bugs caused by accidentally
+/// passing wrong value.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+pub struct Uid(uid_t);
+
+impl Uid {
+ /// Creates `Uid` from raw `uid_t`.
+ pub const fn from_raw(uid: uid_t) -> Self {
+ Uid(uid)
+ }
+
+ /// Returns Uid of calling process. This is practically a more Rusty alias for `getuid`.
+ #[doc(alias("getuid"))]
+ pub fn current() -> Self {
+ getuid()
+ }
+
+ /// Returns effective Uid of calling process. This is practically a more Rusty alias for `geteuid`.
+ #[doc(alias("geteuid"))]
+ pub fn effective() -> Self {
+ geteuid()
+ }
+
+ /// Returns true if the `Uid` represents privileged user - root. (If it equals zero.)
+ pub const fn is_root(self) -> bool {
+ self.0 == ROOT.0
+ }
+
+ /// Get the raw `uid_t` wrapped by `self`.
+ pub const fn as_raw(self) -> uid_t {
+ self.0
+ }
+}
+
+impl From<Uid> for uid_t {
+ fn from(uid: Uid) -> Self {
+ uid.0
+ }
+}
+
+impl From<uid_t> for Uid {
+ fn from(uid: uid_t) -> Self {
+ Uid(uid)
+ }
+}
+
+impl fmt::Display for Uid {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Display::fmt(&self.0, f)
+ }
+}
+
+/// Constant for UID = 0
+pub const ROOT: Uid = Uid(0);
+
+/// Group identifier
+///
+/// Newtype pattern around `gid_t` (which is just alias). It prevents bugs caused by accidentally
+/// passing wrong value.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
+pub struct Gid(gid_t);
+
+impl Gid {
+ /// Creates `Gid` from raw `gid_t`.
+ pub const fn from_raw(gid: gid_t) -> Self {
+ Gid(gid)
+ }
+
+ /// Returns Gid of calling process. This is practically a more Rusty alias for `getgid`.
+ #[doc(alias("getgid"))]
+ pub fn current() -> Self {
+ getgid()
+ }
+
+ /// Returns effective Gid of calling process. This is practically a more Rusty alias for `getegid`.
+ #[doc(alias("getegid"))]
+ pub fn effective() -> Self {
+ getegid()
+ }
+
+ /// Get the raw `gid_t` wrapped by `self`.
+ pub const fn as_raw(self) -> gid_t {
+ self.0
+ }
+}
+
+impl From<Gid> for gid_t {
+ fn from(gid: Gid) -> Self {
+ gid.0
+ }
+}
+
+impl From<gid_t> for Gid {
+ fn from(gid: gid_t) -> Self {
+ Gid(gid)
+ }
+}
+
+impl fmt::Display for Gid {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Display::fmt(&self.0, f)
+ }
+}
+}
+
+feature! {
+#![feature = "process"]
+/// Process identifier
+///
+/// Newtype pattern around `pid_t` (which is just alias). It prevents bugs caused by accidentally
+/// passing wrong value.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
+pub struct Pid(pid_t);
+
+impl Pid {
+ /// Creates `Pid` from raw `pid_t`.
+ pub const fn from_raw(pid: pid_t) -> Self {
+ Pid(pid)
+ }
+
+ /// Returns PID of calling process
+ #[doc(alias("getpid"))]
+ pub fn this() -> Self {
+ getpid()
+ }
+
+ /// Returns PID of parent of calling process
+ #[doc(alias("getppid"))]
+ pub fn parent() -> Self {
+ getppid()
+ }
+
+ /// Get the raw `pid_t` wrapped by `self`.
+ pub const fn as_raw(self) -> pid_t {
+ self.0
+ }
+}
+
+impl From<Pid> for pid_t {
+ fn from(pid: Pid) -> Self {
+ pid.0
+ }
+}
+
+impl fmt::Display for Pid {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ fmt::Display::fmt(&self.0, f)
+ }
+}
+
+
+/// Represents the successful result of calling `fork`
+///
+/// When `fork` is called, the process continues execution in the parent process
+/// and in the new child. This return type can be examined to determine whether
+/// you are now executing in the parent process or in the child.
+#[derive(Clone, Copy, Debug)]
+pub enum ForkResult {
+ Parent { child: Pid },
+ Child,
+}
+
+impl ForkResult {
+
+ /// Return `true` if this is the child process of the `fork()`
+ #[inline]
+ pub fn is_child(self) -> bool {
+ matches!(self, ForkResult::Child)
+ }
+
+ /// Returns `true` if this is the parent process of the `fork()`
+ #[inline]
+ pub fn is_parent(self) -> bool {
+ !self.is_child()
+ }
+}
+
+/// Create a new child process duplicating the parent process ([see
+/// fork(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fork.html)).
+///
+/// After successfully calling the fork system call, a second process will
+/// be created which is identical to the original except for the pid and the
+/// return value of this function. As an example:
+///
+/// ```
+/// use nix::{sys::wait::waitpid,unistd::{fork, ForkResult, write}};
+///
+/// match unsafe{fork()} {
+/// Ok(ForkResult::Parent { child, .. }) => {
+/// println!("Continuing execution in parent process, new child has pid: {}", child);
+/// waitpid(child, None).unwrap();
+/// }
+/// Ok(ForkResult::Child) => {
+/// // Unsafe to use `println!` (or `unwrap`) here. See Safety.
+/// write(libc::STDOUT_FILENO, "I'm a new child process\n".as_bytes()).ok();
+/// unsafe { libc::_exit(0) };
+/// }
+/// Err(_) => println!("Fork failed"),
+/// }
+/// ```
+///
+/// This will print something like the following (order nondeterministic). The
+/// thing to note is that you end up with two processes continuing execution
+/// immediately after the fork call but with different match arms.
+///
+/// ```text
+/// Continuing execution in parent process, new child has pid: 1234
+/// I'm a new child process
+/// ```
+///
+/// # Safety
+///
+/// In a multithreaded program, only [async-signal-safe] functions like `pause`
+/// and `_exit` may be called by the child (the parent isn't restricted). Note
+/// that memory allocation may **not** be async-signal-safe and thus must be
+/// prevented.
+///
+/// Those functions are only a small subset of your operating system's API, so
+/// special care must be taken to only invoke code you can control and audit.
+///
+/// [async-signal-safe]: https://man7.org/linux/man-pages/man7/signal-safety.7.html
+#[inline]
+pub unsafe fn fork() -> Result<ForkResult> {
+ use self::ForkResult::*;
+ let res = libc::fork();
+
+ Errno::result(res).map(|res| match res {
+ 0 => Child,
+ res => Parent { child: Pid(res) },
+ })
+}
+
+/// Get the pid of this process (see
+/// [getpid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpid.html)).
+///
+/// Since you are running code, there is always a pid to return, so there
+/// is no error case that needs to be handled.
+#[inline]
+pub fn getpid() -> Pid {
+ Pid(unsafe { libc::getpid() })
+}
+
+/// Get the pid of this processes' parent (see
+/// [getpid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getppid.html)).
+///
+/// There is always a parent pid to return, so there is no error case that needs
+/// to be handled.
+#[inline]
+pub fn getppid() -> Pid {
+ Pid(unsafe { libc::getppid() }) // no error handling, according to man page: "These functions are always successful."
+}
+
+/// Set a process group ID (see
+/// [setpgid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/setpgid.html)).
+///
+/// Set the process group id (PGID) of a particular process. If a pid of zero
+/// is specified, then the pid of the calling process is used. Process groups
+/// may be used to group together a set of processes in order for the OS to
+/// apply some operations across the group.
+///
+/// `setsid()` may be used to create a new process group.
+#[inline]
+pub fn setpgid(pid: Pid, pgid: Pid) -> Result<()> {
+ let res = unsafe { libc::setpgid(pid.into(), pgid.into()) };
+ Errno::result(res).map(drop)
+}
+#[inline]
+pub fn getpgid(pid: Option<Pid>) -> Result<Pid> {
+ let res = unsafe { libc::getpgid(pid.unwrap_or(Pid(0)).into()) };
+ Errno::result(res).map(Pid)
+}
+
+/// Create new session and set process group id (see
+/// [setsid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/setsid.html)).
+#[inline]
+pub fn setsid() -> Result<Pid> {
+ Errno::result(unsafe { libc::setsid() }).map(Pid)
+}
+
+/// Get the process group ID of a session leader
+/// [getsid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getsid.html).
+///
+/// Obtain the process group ID of the process that is the session leader of the process specified
+/// by pid. If pid is zero, it specifies the calling process.
+#[inline]
+#[cfg(not(target_os = "redox"))]
+pub fn getsid(pid: Option<Pid>) -> Result<Pid> {
+ let res = unsafe { libc::getsid(pid.unwrap_or(Pid(0)).into()) };
+ Errno::result(res).map(Pid)
+}
+}
+
+feature! {
+#![all(feature = "process", feature = "term")]
+/// Get the terminal foreground process group (see
+/// [tcgetpgrp(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcgetpgrp.html)).
+///
+/// Get the group process id (GPID) of the foreground process group on the
+/// terminal associated to file descriptor (FD).
+#[inline]
+pub fn tcgetpgrp(fd: c_int) -> Result<Pid> {
+ let res = unsafe { libc::tcgetpgrp(fd) };
+ Errno::result(res).map(Pid)
+}
+/// Set the terminal foreground process group (see
+/// [tcgetpgrp(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/tcsetpgrp.html)).
+///
+/// Get the group process id (PGID) to the foreground process group on the
+/// terminal associated to file descriptor (FD).
+#[inline]
+pub fn tcsetpgrp(fd: c_int, pgrp: Pid) -> Result<()> {
+ let res = unsafe { libc::tcsetpgrp(fd, pgrp.into()) };
+ Errno::result(res).map(drop)
+}
+}
+
+feature! {
+#![feature = "process"]
+/// Get the group id of the calling process (see
+///[getpgrp(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpgrp.html)).
+///
+/// Get the process group id (PGID) of the calling process.
+/// According to the man page it is always successful.
+#[inline]
+pub fn getpgrp() -> Pid {
+ Pid(unsafe { libc::getpgrp() })
+}
+
+/// Get the caller's thread ID (see
+/// [gettid(2)](https://man7.org/linux/man-pages/man2/gettid.2.html).
+///
+/// This function is only available on Linux based systems. In a single
+/// threaded process, the main thread will have the same ID as the process. In
+/// a multithreaded process, each thread will have a unique thread id but the
+/// same process ID.
+///
+/// No error handling is required as a thread id should always exist for any
+/// process, even if threads are not being used.
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[inline]
+pub fn gettid() -> Pid {
+ Pid(unsafe { libc::syscall(libc::SYS_gettid) as pid_t })
+}
+}
+
+feature! {
+#![feature = "fs"]
+/// Create a copy of the specified file descriptor (see
+/// [dup(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/dup.html)).
+///
+/// The new file descriptor will have a new index but refer to the same
+/// resource as the old file descriptor and the old and new file descriptors may
+/// be used interchangeably. The new and old file descriptor share the same
+/// underlying resource, offset, and file status flags. The actual index used
+/// for the file descriptor will be the lowest fd index that is available.
+///
+/// The two file descriptors do not share file descriptor flags (e.g. `OFlag::FD_CLOEXEC`).
+#[inline]
+pub fn dup(oldfd: RawFd) -> Result<RawFd> {
+ let res = unsafe { libc::dup(oldfd) };
+
+ Errno::result(res)
+}
+
+/// Create a copy of the specified file descriptor using the specified fd (see
+/// [dup(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/dup.html)).
+///
+/// This function behaves similar to `dup()` except that it will try to use the
+/// specified fd instead of allocating a new one. See the man pages for more
+/// detail on the exact behavior of this function.
+#[inline]
+pub fn dup2(oldfd: RawFd, newfd: RawFd) -> Result<RawFd> {
+ let res = unsafe { libc::dup2(oldfd, newfd) };
+
+ Errno::result(res)
+}
+
+/// Create a new copy of the specified file descriptor using the specified fd
+/// and flags (see [dup(2)](https://man7.org/linux/man-pages/man2/dup.2.html)).
+///
+/// This function behaves similar to `dup2()` but allows for flags to be
+/// specified.
+pub fn dup3(oldfd: RawFd, newfd: RawFd, flags: OFlag) -> Result<RawFd> {
+ dup3_polyfill(oldfd, newfd, flags)
+}
+
+#[inline]
+fn dup3_polyfill(oldfd: RawFd, newfd: RawFd, flags: OFlag) -> Result<RawFd> {
+ if oldfd == newfd {
+ return Err(Errno::EINVAL);
+ }
+
+ let fd = dup2(oldfd, newfd)?;
+
+ if flags.contains(OFlag::O_CLOEXEC) {
+ if let Err(e) = fcntl(fd, F_SETFD(FdFlag::FD_CLOEXEC)) {
+ let _ = close(fd);
+ return Err(e);
+ }
+ }
+
+ Ok(fd)
+}
+
+/// Change the current working directory of the calling process (see
+/// [chdir(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/chdir.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 chdir<P: ?Sized + NixPath>(path: &P) -> Result<()> {
+ let res = path.with_nix_path(|cstr| {
+ unsafe { libc::chdir(cstr.as_ptr()) }
+ })?;
+
+ 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)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchdir.html)).
+///
+/// This function may fail in a number of different scenarios. See the man
+/// pages for additional details on possible failure cases.
+#[inline]
+#[cfg(not(target_os = "fuchsia"))]
+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`. (see [mkdir(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkdir.html))
+///
+/// # Errors
+///
+/// There are several situations where mkdir might fail:
+///
+/// - current user has insufficient rights in the parent directory
+/// - the path already exists
+/// - the path name is too long (longer than `PATH_MAX`, usually 4096 on linux, 1024 on OS X)
+///
+/// # Example
+///
+/// ```rust
+/// use nix::unistd;
+/// use nix::sys::stat;
+/// use tempfile::tempdir;
+///
+/// let tmp_dir1 = tempdir().unwrap();
+/// let tmp_dir2 = tmp_dir1.path().join("new_dir");
+///
+/// // create new directory and give read, write and execute rights to the owner
+/// match unistd::mkdir(&tmp_dir2, stat::Mode::S_IRWXU) {
+/// Ok(_) => println!("created {:?}", tmp_dir2),
+/// Err(err) => println!("Error creating directory: {}", err),
+/// }
+/// ```
+#[inline]
+pub fn mkdir<P: ?Sized + NixPath>(path: &P, mode: Mode) -> Result<()> {
+ let res = path.with_nix_path(|cstr| {
+ unsafe { libc::mkdir(cstr.as_ptr(), mode.bits() as mode_t) }
+ })?;
+
+ Errno::result(res).map(drop)
+}
+
+/// Creates new fifo special file (named pipe) with path `path` and access rights `mode`.
+///
+/// # Errors
+///
+/// There are several situations where mkfifo might fail:
+///
+/// - current user has insufficient rights in the parent directory
+/// - the path already exists
+/// - the path name is too long (longer than `PATH_MAX`, usually 4096 on linux, 1024 on OS X)
+///
+/// For a full list consult
+/// [posix specification](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkfifo.html)
+///
+/// # Example
+///
+/// ```rust
+/// use nix::unistd;
+/// use nix::sys::stat;
+/// use tempfile::tempdir;
+///
+/// let tmp_dir = tempdir().unwrap();
+/// let fifo_path = tmp_dir.path().join("foo.pipe");
+///
+/// // create new fifo and give read, write and execute rights to the owner
+/// match unistd::mkfifo(&fifo_path, stat::Mode::S_IRWXU) {
+/// Ok(_) => println!("created {:?}", fifo_path),
+/// Err(err) => println!("Error creating fifo: {}", err),
+/// }
+/// ```
+#[inline]
+#[cfg(not(target_os = "redox"))] // RedoxFS does not support fifo yet
+pub fn mkfifo<P: ?Sized + NixPath>(path: &P, mode: Mode) -> Result<()> {
+ let res = path.with_nix_path(|cstr| {
+ unsafe { libc::mkfifo(cstr.as_ptr(), mode.bits() as mode_t) }
+ })?;
+
+ Errno::result(res).map(drop)
+}
+
+/// Creates new fifo special file (named pipe) with path `path` and access rights `mode`.
+///
+/// If `dirfd` has a value, then `path` is relative to directory associated with the file descriptor.
+///
+/// If `dirfd` is `None`, then `path` is relative to the current working directory.
+///
+/// # References
+///
+/// [mkfifoat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkfifoat.html).
+// mkfifoat is not implemented in OSX or android
+#[inline]
+#[cfg(not(any(
+ target_os = "macos", target_os = "ios", target_os = "haiku",
+ target_os = "android", target_os = "redox")))]
+pub fn mkfifoat<P: ?Sized + NixPath>(dirfd: Option<RawFd>, path: &P, mode: Mode) -> Result<()> {
+ let res = path.with_nix_path(|cstr| unsafe {
+ libc::mkfifoat(at_rawfd(dirfd), cstr.as_ptr(), mode.bits() as mode_t)
+ })?;
+
+ Errno::result(res).map(drop)
+}
+
+/// Creates a symbolic link at `path2` which points to `path1`.
+///
+/// If `dirfd` has a value, then `path2` is relative to directory associated
+/// with the file descriptor.
+///
+/// If `dirfd` is `None`, then `path2` is relative to the current working
+/// directory. This is identical to `libc::symlink(path1, path2)`.
+///
+/// See also [symlinkat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/symlinkat.html).
+#[cfg(not(target_os = "redox"))]
+pub fn symlinkat<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
+ path1: &P1,
+ dirfd: Option<RawFd>,
+ path2: &P2) -> Result<()> {
+ let res =
+ path1.with_nix_path(|path1| {
+ path2.with_nix_path(|path2| {
+ unsafe {
+ libc::symlinkat(
+ path1.as_ptr(),
+ dirfd.unwrap_or(libc::AT_FDCWD),
+ path2.as_ptr()
+ )
+ }
+ })
+ })??;
+ Errno::result(res).map(drop)
+}
+}
+
+// Double the buffer capacity up to limit. In case it already has
+// reached the limit, return Errno::ERANGE.
+#[cfg(any(feature = "fs", feature = "user"))]
+fn reserve_double_buffer_size<T>(buf: &mut Vec<T>, limit: usize) -> Result<()> {
+ use std::cmp::min;
+
+ if buf.capacity() >= limit {
+ return Err(Errno::ERANGE);
+ }
+
+ let capacity = min(buf.capacity() * 2, limit);
+ buf.reserve(capacity);
+
+ Ok(())
+}
+
+feature! {
+#![feature = "fs"]
+
+/// Returns the current directory as a `PathBuf`
+///
+/// Err is returned if the current user doesn't have the permission to read or search a component
+/// of the current path.
+///
+/// # Example
+///
+/// ```rust
+/// use nix::unistd;
+///
+/// // assume that we are allowed to get current directory
+/// let dir = unistd::getcwd().unwrap();
+/// println!("The current directory is {:?}", dir);
+/// ```
+#[inline]
+pub fn getcwd() -> Result<PathBuf> {
+ let mut buf = Vec::with_capacity(512);
+ loop {
+ unsafe {
+ let ptr = buf.as_mut_ptr() as *mut c_char;
+
+ // The buffer must be large enough to store the absolute pathname plus
+ // a terminating null byte, or else null is returned.
+ // To safely handle this we start with a reasonable size (512 bytes)
+ // and double the buffer size upon every error
+ if !libc::getcwd(ptr, buf.capacity()).is_null() {
+ let len = CStr::from_ptr(buf.as_ptr() as *const c_char).to_bytes().len();
+ buf.set_len(len);
+ buf.shrink_to_fit();
+ return Ok(PathBuf::from(OsString::from_vec(buf)));
+ } else {
+ let error = Errno::last();
+ // ERANGE means buffer was too small to store directory name
+ if error != Errno::ERANGE {
+ return Err(error);
+ }
+ }
+
+ // Trigger the internal buffer resizing logic.
+ reserve_double_buffer_size(&mut buf, PATH_MAX as usize)?;
+ }
+ }
+}
+}
+
+feature! {
+#![all(feature = "user", feature = "fs")]
+
+/// Computes the raw UID and GID values to pass to a `*chown` call.
+// The cast is not unnecessary on all platforms.
+#[allow(clippy::unnecessary_cast)]
+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_else(|| (0 as uid_t).wrapping_sub(1));
+ let gid = group.map(Into::into)
+ .unwrap_or_else(|| (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)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/chown.html)).
+///
+/// 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.
+#[inline]
+pub fn chown<P: ?Sized + NixPath>(path: &P, owner: Option<Uid>, group: Option<Gid>) -> Result<()> {
+ let res = path.with_nix_path(|cstr| {
+ let (uid, gid) = chown_raw_ids(owner, group);
+ unsafe { libc::chown(cstr.as_ptr(), uid, gid) }
+ })?;
+
+ Errno::result(res).map(drop)
+}
+
+/// Change the ownership of the file referred to by the open file descriptor `fd` to be owned by
+/// the specified `owner` (user) and `group` (see
+/// [fchown(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchown.html)).
+///
+/// The owner/group for the provided file 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.
+#[inline]
+pub fn fchown(fd: RawFd, owner: Option<Uid>, group: Option<Gid>) -> Result<()> {
+ let (uid, gid) = chown_raw_ids(owner, group);
+ let res = unsafe { libc::fchown(fd, 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, owner, group, FchownatFlags::NoFollowSymlink)` is identical to
+/// a call `libc::lchown(path, owner, group)`. That's why `lchown` is unimplemented in
+/// the `nix` crate.
+///
+/// # References
+///
+/// [fchownat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fchownat.html).
+#[cfg(not(target_os = "redox"))]
+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)
+}
+}
+
+feature! {
+#![feature = "process"]
+fn to_exec_array<S: AsRef<CStr>>(args: &[S]) -> Vec<*const c_char> {
+ use std::iter::once;
+ args.iter()
+ .map(|s| s.as_ref().as_ptr())
+ .chain(once(ptr::null()))
+ .collect()
+}
+
+/// Replace the current process image with a new one (see
+/// [exec(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html)).
+///
+/// See the `::nix::unistd::execve` system call for additional details. `execv`
+/// performs the same action but does not allow for customization of the
+/// environment for the new process.
+#[inline]
+pub fn execv<S: AsRef<CStr>>(path: &CStr, argv: &[S]) -> Result<Infallible> {
+ let args_p = to_exec_array(argv);
+
+ unsafe {
+ libc::execv(path.as_ptr(), args_p.as_ptr())
+ };
+
+ Err(Errno::last())
+}
+
+
+/// Replace the current process image with a new one (see
+/// [execve(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html)).
+///
+/// The execve system call allows for another process to be "called" which will
+/// replace the current process image. That is, this process becomes the new
+/// command that is run. On success, this function will not return. Instead,
+/// the new program will run until it exits.
+///
+/// `::nix::unistd::execv` and `::nix::unistd::execve` take as arguments a slice
+/// of `::std::ffi::CString`s for `args` and `env` (for `execve`). Each element
+/// in the `args` list is an argument to the new process. Each element in the
+/// `env` list should be a string in the form "key=value".
+#[inline]
+pub fn execve<SA: AsRef<CStr>, SE: AsRef<CStr>>(path: &CStr, args: &[SA], env: &[SE]) -> Result<Infallible> {
+ let args_p = to_exec_array(args);
+ let env_p = to_exec_array(env);
+
+ unsafe {
+ libc::execve(path.as_ptr(), args_p.as_ptr(), env_p.as_ptr())
+ };
+
+ Err(Errno::last())
+}
+
+/// Replace the current process image with a new one and replicate shell `PATH`
+/// searching behavior (see
+/// [exec(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/exec.html)).
+///
+/// See `::nix::unistd::execve` for additional details. `execvp` behaves the
+/// same as execv except that it will examine the `PATH` environment variables
+/// for file names not specified with a leading slash. For example, `execv`
+/// would not work if "bash" was specified for the path argument, but `execvp`
+/// would assuming that a bash executable was on the system `PATH`.
+#[inline]
+pub fn execvp<S: AsRef<CStr>>(filename: &CStr, args: &[S]) -> Result<Infallible> {
+ let args_p = to_exec_array(args);
+
+ unsafe {
+ libc::execvp(filename.as_ptr(), args_p.as_ptr())
+ };
+
+ Err(Errno::last())
+}
+
+/// Replace the current process image with a new one and replicate shell `PATH`
+/// searching behavior (see
+/// [`execvpe(3)`](https://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<SA: AsRef<CStr>, SE: AsRef<CStr>>(filename: &CStr, args: &[SA], env: &[SE]) -> Result<Infallible> {
+ 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(Errno::last())
+}
+
+/// Replace the current process image with a new one (see
+/// [fexecve(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fexecve.html)).
+///
+/// The `fexecve` function allows for another process to be "called" which will
+/// replace the current process image. That is, this process becomes the new
+/// command that is run. On success, this function will not return. Instead,
+/// the new program will run until it exits.
+///
+/// This function is similar to `execve`, except that the program to be executed
+/// is referenced as a file descriptor instead of a path.
+#[cfg(any(target_os = "android",
+ target_os = "linux",
+ target_os = "dragonfly",
+ target_os = "freebsd"))]
+#[inline]
+pub fn fexecve<SA: AsRef<CStr> ,SE: AsRef<CStr>>(fd: RawFd, args: &[SA], env: &[SE]) -> Result<Infallible> {
+ let args_p = to_exec_array(args);
+ let env_p = to_exec_array(env);
+
+ unsafe {
+ libc::fexecve(fd, args_p.as_ptr(), env_p.as_ptr())
+ };
+
+ Err(Errno::last())
+}
+
+/// Execute program relative to a directory file descriptor (see
+/// [execveat(2)](https://man7.org/linux/man-pages/man2/execveat.2.html)).
+///
+/// The `execveat` function allows for another process to be "called" which will
+/// replace the current process image. That is, this process becomes the new
+/// command that is run. On success, this function will not return. Instead,
+/// the new program will run until it exits.
+///
+/// This function is similar to `execve`, except that the program to be executed
+/// is referenced as a file descriptor to the base directory plus a path.
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[inline]
+pub fn execveat<SA: AsRef<CStr>,SE: AsRef<CStr>>(dirfd: RawFd, pathname: &CStr, args: &[SA],
+ env: &[SE], flags: super::fcntl::AtFlags) -> Result<Infallible> {
+ let args_p = to_exec_array(args);
+ let env_p = to_exec_array(env);
+
+ unsafe {
+ libc::syscall(libc::SYS_execveat, dirfd, pathname.as_ptr(),
+ args_p.as_ptr(), env_p.as_ptr(), flags);
+ };
+
+ Err(Errno::last())
+}
+
+/// Daemonize this process by detaching from the controlling terminal (see
+/// [daemon(3)](https://man7.org/linux/man-pages/man3/daemon.3.html)).
+///
+/// When a process is launched it is typically associated with a parent and it,
+/// in turn, by its controlling terminal/process. In order for a process to run
+/// in the "background" it must daemonize itself by detaching itself. Under
+/// posix, this is done by doing the following:
+///
+/// 1. Parent process (this one) forks
+/// 2. Parent process exits
+/// 3. Child process continues to run.
+///
+/// `nochdir`:
+///
+/// * `nochdir = true`: The current working directory after daemonizing will
+/// be the current working directory.
+/// * `nochdir = false`: The current working directory after daemonizing will
+/// be the root direcory, `/`.
+///
+/// `noclose`:
+///
+/// * `noclose = true`: The process' current stdin, stdout, and stderr file
+/// descriptors will remain identical after daemonizing.
+/// * `noclose = false`: The process' stdin, stdout, and stderr will point to
+/// `/dev/null` after daemonizing.
+#[cfg(any(target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris"))]
+pub fn daemon(nochdir: bool, noclose: bool) -> Result<()> {
+ let res = unsafe { libc::daemon(nochdir as c_int, noclose as c_int) };
+ Errno::result(res).map(drop)
+}
+}
+
+feature! {
+#![feature = "hostname"]
+
+/// Set the system host name (see
+/// [sethostname(2)](https://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 returned if the name is not valid or the current process does not
+/// have permissions to update the host name.
+#[cfg(not(target_os = "redox"))]
+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",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "solaris", ))] {
+ type sethostname_len_t = c_int;
+ } else {
+ type sethostname_len_t = size_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)
+}
+
+/// Get the host name and store it in an internally allocated buffer, returning an
+/// `OsString` on success (see
+/// [gethostname(2)](https://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 an internal buffer, returning it as an `OsString` if successful.
+///
+/// ```no_run
+/// use nix::unistd;
+///
+/// let hostname = unistd::gethostname().expect("Failed getting hostname");
+/// let hostname = hostname.into_string().expect("Hostname wasn't valid UTF-8");
+/// println!("Hostname: {}", hostname);
+/// ```
+pub fn gethostname() -> Result<OsString> {
+ // The capacity is the max length of a hostname plus the NUL terminator.
+ let mut buffer: Vec<u8> = Vec::with_capacity(256);
+ let ptr = buffer.as_mut_ptr() as *mut c_char;
+ let len = buffer.capacity() as size_t;
+
+ let res = unsafe { libc::gethostname(ptr, len) };
+ Errno::result(res).map(|_| {
+ unsafe {
+ buffer.as_mut_ptr().wrapping_add(len - 1).write(0); // ensure always null-terminated
+ let len = CStr::from_ptr(buffer.as_ptr() as *const c_char).len();
+ buffer.set_len(len);
+ }
+ OsString::from_vec(buffer)
+ })
+}
+}
+
+/// Close a raw file descriptor
+///
+/// Be aware that many Rust types implicitly close-on-drop, including
+/// `std::fs::File`. Explicitly closing them with this method too can result in
+/// a double-close condition, which can cause confusing `EBADF` errors in
+/// seemingly unrelated code. Caveat programmer. See also
+/// [close(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/close.html).
+///
+/// # Examples
+///
+/// ```no_run
+/// use std::os::unix::io::AsRawFd;
+/// use nix::unistd::close;
+///
+/// let f = tempfile::tempfile().unwrap();
+/// close(f.as_raw_fd()).unwrap(); // Bad! f will also close on drop!
+/// ```
+///
+/// ```rust
+/// use std::os::unix::io::IntoRawFd;
+/// use nix::unistd::close;
+///
+/// let f = tempfile::tempfile().unwrap();
+/// close(f.into_raw_fd()).unwrap(); // Good. into_raw_fd consumes f
+/// ```
+pub fn close(fd: RawFd) -> Result<()> {
+ let res = unsafe { libc::close(fd) };
+ Errno::result(res).map(drop)
+}
+
+/// Read from a raw file descriptor.
+///
+/// See also [read(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html)
+pub fn read(fd: RawFd, buf: &mut [u8]) -> Result<usize> {
+ let res = unsafe {
+ libc::read(fd, buf.as_mut_ptr() as *mut c_void, buf.len() as size_t)
+ };
+
+ Errno::result(res).map(|r| r as usize)
+}
+
+/// Write to a raw file descriptor.
+///
+/// See also [write(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/write.html)
+pub fn write(fd: RawFd, buf: &[u8]) -> Result<usize> {
+ let res = unsafe {
+ libc::write(fd, buf.as_ptr() as *const c_void, buf.len() as size_t)
+ };
+
+ Errno::result(res).map(|r| r as usize)
+}
+
+feature! {
+#![feature = "fs"]
+
+/// Directive that tells [`lseek`] and [`lseek64`] what the offset is relative to.
+///
+/// [`lseek`]: ./fn.lseek.html
+/// [`lseek64`]: ./fn.lseek64.html
+#[repr(i32)]
+#[derive(Clone, Copy, Debug)]
+pub enum Whence {
+ /// Specify an offset relative to the start of the file.
+ SeekSet = libc::SEEK_SET,
+ /// Specify an offset relative to the current file location.
+ SeekCur = libc::SEEK_CUR,
+ /// Specify an offset relative to the end of the file.
+ SeekEnd = libc::SEEK_END,
+ /// Specify an offset relative to the next location in the file greater than or
+ /// equal to offset that contains some data. If offset points to
+ /// some data, then the file offset is set to offset.
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris"))]
+ SeekData = libc::SEEK_DATA,
+ /// Specify an offset relative to the next hole in the file greater than
+ /// or equal to offset. If offset points into the middle of a hole, then
+ /// the file offset should be set to offset. If there is no hole past offset,
+ /// then the file offset should be adjusted to the end of the file (i.e., there
+ /// is an implicit hole at the end of any file).
+ #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris"))]
+ SeekHole = libc::SEEK_HOLE
+}
+
+/// Move the read/write file offset.
+///
+/// See also [lseek(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/lseek.html)
+pub fn lseek(fd: RawFd, offset: off_t, whence: Whence) -> Result<off_t> {
+ let res = unsafe { libc::lseek(fd, offset, whence as i32) };
+
+ Errno::result(res).map(|r| r as off_t)
+}
+
+#[cfg(any(target_os = "linux", target_os = "android"))]
+pub fn lseek64(fd: RawFd, offset: libc::off64_t, whence: Whence) -> Result<libc::off64_t> {
+ let res = unsafe { libc::lseek64(fd, offset, whence as i32) };
+
+ Errno::result(res).map(|r| r as libc::off64_t)
+}
+}
+
+/// Create an interprocess channel.
+///
+/// See also [pipe(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pipe.html)
+pub fn pipe() -> std::result::Result<(RawFd, RawFd), Error> {
+ let mut fds = mem::MaybeUninit::<[c_int; 2]>::uninit();
+
+ let res = unsafe { libc::pipe(fds.as_mut_ptr() as *mut c_int) };
+
+ Error::result(res)?;
+
+ unsafe { Ok((fds.assume_init()[0], fds.assume_init()[1])) }
+}
+
+feature! {
+#![feature = "fs"]
+/// Like `pipe`, but allows setting certain file descriptor flags.
+///
+/// The following flags are supported, and will be set atomically as the pipe is
+/// created:
+///
+/// - `O_CLOEXEC`: Set the close-on-exec flag for the new file descriptors.
+#[cfg_attr(target_os = "linux", doc = "- `O_DIRECT`: Create a pipe that performs I/O in \"packet\" mode.")]
+#[cfg_attr(target_os = "netbsd", doc = "- `O_NOSIGPIPE`: Return `EPIPE` instead of raising `SIGPIPE`.")]
+/// - `O_NONBLOCK`: Set the non-blocking flag for the ends of the pipe.
+///
+/// See also [pipe(2)](https://man7.org/linux/man-pages/man2/pipe.2.html)
+#[cfg(any(target_os = "android",
+ target_os = "dragonfly",
+ target_os = "emscripten",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "redox",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "solaris"))]
+pub fn pipe2(flags: OFlag) -> Result<(RawFd, RawFd)> {
+ let mut fds = mem::MaybeUninit::<[c_int; 2]>::uninit();
+
+ let res = unsafe {
+ libc::pipe2(fds.as_mut_ptr() as *mut c_int, flags.bits())
+ };
+
+ Errno::result(res)?;
+
+ unsafe { Ok((fds.assume_init()[0], fds.assume_init()[1])) }
+}
+
+/// Truncate a file to a specified length
+///
+/// See also
+/// [truncate(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/truncate.html)
+#[cfg(not(any(target_os = "redox", target_os = "fuchsia")))]
+pub fn truncate<P: ?Sized + NixPath>(path: &P, len: off_t) -> Result<()> {
+ let res = 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)](https://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)
+}
+
+pub fn isatty(fd: RawFd) -> Result<bool> {
+ unsafe {
+ // ENOTTY means `fd` is a valid file descriptor, but not a TTY, so
+ // we return `Ok(false)`
+ if libc::isatty(fd) == 1 {
+ Ok(true)
+ } else {
+ match Errno::last() {
+ Errno::ENOTTY => Ok(false),
+ err => Err(err),
+ }
+ }
+ }
+}
+
+/// Flags for `linkat` function.
+#[derive(Clone, Copy, Debug)]
+pub enum LinkatFlags {
+ SymlinkFollow,
+ NoSymlinkFollow,
+}
+
+/// Link one file to another file
+///
+/// Creates a new link (directory entry) at `newpath` for the existing file at `oldpath`. In the
+/// case of a relative `oldpath`, the path is interpreted relative to the directory associated
+/// with file descriptor `olddirfd` instead of the current working directory and similiarly for
+/// `newpath` and file descriptor `newdirfd`. In case `flag` is LinkatFlags::SymlinkFollow and
+/// `oldpath` names a symoblic link, a new link for the target of the symbolic link is created.
+/// If either `olddirfd` or `newdirfd` is `None`, `AT_FDCWD` is used respectively where `oldpath`
+/// and/or `newpath` is then interpreted relative to the current working directory of the calling
+/// process. If either `oldpath` or `newpath` is absolute, then `dirfd` is ignored.
+///
+/// # References
+/// See also [linkat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/linkat.html)
+#[cfg(not(target_os = "redox"))] // RedoxFS does not support symlinks yet
+pub fn linkat<P: ?Sized + NixPath>(
+ olddirfd: Option<RawFd>,
+ oldpath: &P,
+ newdirfd: Option<RawFd>,
+ newpath: &P,
+ flag: LinkatFlags,
+) -> Result<()> {
+
+ let atflag =
+ match flag {
+ LinkatFlags::SymlinkFollow => AtFlags::AT_SYMLINK_FOLLOW,
+ LinkatFlags::NoSymlinkFollow => AtFlags::empty(),
+ };
+
+ let res =
+ oldpath.with_nix_path(|oldcstr| {
+ newpath.with_nix_path(|newcstr| {
+ unsafe {
+ libc::linkat(
+ at_rawfd(olddirfd),
+ oldcstr.as_ptr(),
+ at_rawfd(newdirfd),
+ newcstr.as_ptr(),
+ atflag.bits() as libc::c_int
+ )
+ }
+ })
+ })??;
+ Errno::result(res).map(drop)
+}
+
+
+/// Remove a directory entry
+///
+/// See also [unlink(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlink.html)
+pub fn unlink<P: ?Sized + NixPath>(path: &P) -> Result<()> {
+ let res = path.with_nix_path(|cstr| {
+ unsafe {
+ libc::unlink(cstr.as_ptr())
+ }
+ })?;
+ Errno::result(res).map(drop)
+}
+
+/// Flags for `unlinkat` function.
+#[derive(Clone, Copy, Debug)]
+pub enum UnlinkatFlags {
+ RemoveDir,
+ NoRemoveDir,
+}
+
+/// Remove a directory entry
+///
+/// In the case of a relative path, the directory entry to be removed is determined relative to
+/// the directory associated with the file descriptor `dirfd` or the current working directory
+/// if `dirfd` is `None`. In the case of an absolute `path` `dirfd` is ignored. If `flag` is
+/// `UnlinkatFlags::RemoveDir` then removal of the directory entry specified by `dirfd` and `path`
+/// is performed.
+///
+/// # References
+/// See also [unlinkat(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/unlinkat.html)
+#[cfg(not(target_os = "redox"))]
+pub fn unlinkat<P: ?Sized + NixPath>(
+ dirfd: Option<RawFd>,
+ path: &P,
+ flag: UnlinkatFlags,
+) -> Result<()> {
+ let atflag =
+ match flag {
+ UnlinkatFlags::RemoveDir => AtFlags::AT_REMOVEDIR,
+ UnlinkatFlags::NoRemoveDir => AtFlags::empty(),
+ };
+ let res = path.with_nix_path(|cstr| {
+ unsafe {
+ libc::unlinkat(at_rawfd(dirfd), cstr.as_ptr(), atflag.bits() as libc::c_int)
+ }
+ })?;
+ Errno::result(res).map(drop)
+}
+
+
+#[inline]
+#[cfg(not(target_os = "fuchsia"))]
+pub fn chroot<P: ?Sized + NixPath>(path: &P) -> Result<()> {
+ let res = path.with_nix_path(|cstr| {
+ unsafe { libc::chroot(cstr.as_ptr()) }
+ })?;
+
+ Errno::result(res).map(drop)
+}
+
+/// Commit filesystem caches to disk
+///
+/// See also [sync(2)](https://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() };
+}
+
+/// Commit filesystem caches containing file referred to by the open file
+/// descriptor `fd` to disk
+///
+/// See also [syncfs(2)](https://man7.org/linux/man-pages/man2/sync.2.html)
+#[cfg(target_os = "linux")]
+pub fn syncfs(fd: RawFd) -> Result<()> {
+ let res = unsafe { libc::syncfs(fd) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Synchronize changes to a file
+///
+/// See also [fsync(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fsync.html)
+#[inline]
+pub fn fsync(fd: RawFd) -> Result<()> {
+ let res = unsafe { libc::fsync(fd) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Synchronize the data of a file
+///
+/// See also
+/// [fdatasync(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/fdatasync.html)
+#[cfg(any(target_os = "linux",
+ target_os = "android",
+ target_os = "emscripten",
+ target_os = "freebsd",
+ target_os = "fuchsia",
+ target_os = "netbsd",
+ target_os = "openbsd",
+ target_os = "illumos",
+ target_os = "solaris"))]
+#[inline]
+pub fn fdatasync(fd: RawFd) -> Result<()> {
+ let res = unsafe { libc::fdatasync(fd) };
+
+ Errno::result(res).map(drop)
+}
+}
+
+feature! {
+#![feature = "user"]
+
+/// Get a real user ID
+///
+/// See also [getuid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getuid.html)
+// POSIX requires that getuid is always successful, so no need to check return
+// value or errno.
+#[inline]
+pub fn getuid() -> Uid {
+ Uid(unsafe { libc::getuid() })
+}
+
+/// Get the effective user ID
+///
+/// See also [geteuid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/geteuid.html)
+// POSIX requires that geteuid is always successful, so no need to check return
+// value or errno.
+#[inline]
+pub fn geteuid() -> Uid {
+ Uid(unsafe { libc::geteuid() })
+}
+
+/// Get the real group ID
+///
+/// See also [getgid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getgid.html)
+// POSIX requires that getgid is always successful, so no need to check return
+// value or errno.
+#[inline]
+pub fn getgid() -> Gid {
+ Gid(unsafe { libc::getgid() })
+}
+
+/// Get the effective group ID
+///
+/// See also [getegid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getegid.html)
+// POSIX requires that getegid is always successful, so no need to check return
+// value or errno.
+#[inline]
+pub fn getegid() -> Gid {
+ Gid(unsafe { libc::getegid() })
+}
+
+/// Set the effective user ID
+///
+/// See also [seteuid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/seteuid.html)
+#[inline]
+pub fn seteuid(euid: Uid) -> Result<()> {
+ let res = unsafe { libc::seteuid(euid.into()) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Set the effective group ID
+///
+/// See also [setegid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/setegid.html)
+#[inline]
+pub fn setegid(egid: Gid) -> Result<()> {
+ let res = unsafe { libc::setegid(egid.into()) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Set the user ID
+///
+/// See also [setuid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/setuid.html)
+#[inline]
+pub fn setuid(uid: Uid) -> Result<()> {
+ let res = unsafe { libc::setuid(uid.into()) };
+
+ Errno::result(res).map(drop)
+}
+
+/// Set the group ID
+///
+/// See also [setgid(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/setgid.html)
+#[inline]
+pub fn setgid(gid: Gid) -> Result<()> {
+ let res = unsafe { libc::setgid(gid.into()) };
+
+ Errno::result(res).map(drop)
+}
+}
+
+feature! {
+#![all(feature = "fs", feature = "user")]
+/// Set the user identity used for filesystem checks per-thread.
+/// On both success and failure, this call returns the previous filesystem user
+/// ID of the caller.
+///
+/// See also [setfsuid(2)](https://man7.org/linux/man-pages/man2/setfsuid.2.html)
+#[cfg(any(target_os = "linux", target_os = "android"))]
+pub fn setfsuid(uid: Uid) -> Uid {
+ let prev_fsuid = unsafe { libc::setfsuid(uid.into()) };
+ Uid::from_raw(prev_fsuid as uid_t)
+}
+
+/// Set the group identity used for filesystem checks per-thread.
+/// On both success and failure, this call returns the previous filesystem group
+/// ID of the caller.
+///
+/// See also [setfsgid(2)](https://man7.org/linux/man-pages/man2/setfsgid.2.html)
+#[cfg(any(target_os = "linux", target_os = "android"))]
+pub fn setfsgid(gid: Gid) -> Gid {
+ let prev_fsgid = unsafe { libc::setfsgid(gid.into()) };
+ Gid::from_raw(prev_fsgid as gid_t)
+}
+}
+
+feature! {
+#![feature = "user"]
+
+/// Get the list of supplementary group IDs of the calling process.
+///
+/// [Further reading](https://pubs.opengroup.org/onlinepubs/009695399/functions/getgroups.html)
+///
+/// **Note:** This function is not available for Apple platforms. On those
+/// platforms, checking group membership should be achieved via communication
+/// with the `opendirectoryd` service.
+#[cfg(not(any(target_os = "ios", target_os = "macos")))]
+pub fn getgroups() -> Result<Vec<Gid>> {
+ // First get the maximum number of groups. The value returned
+ // shall always be greater than or equal to one and less than or
+ // equal to the value of {NGROUPS_MAX} + 1.
+ let ngroups_max = match sysconf(SysconfVar::NGROUPS_MAX) {
+ Ok(Some(n)) => (n + 1) as usize,
+ Ok(None) | Err(_) => <usize>::max_value(),
+ };
+
+ // Next, get the number of groups so we can size our Vec
+ let ngroups = unsafe { libc::getgroups(0, ptr::null_mut()) };
+
+ // If there are no supplementary groups, return early.
+ // This prevents a potential buffer over-read if the number of groups
+ // increases from zero before the next call. It would return the total
+ // number of groups beyond the capacity of the buffer.
+ if ngroups == 0 {
+ return Ok(Vec::new());
+ }
+
+ // Now actually get the groups. We try multiple times in case the number of
+ // groups has changed since the first call to getgroups() and the buffer is
+ // now too small.
+ let mut groups = Vec::<Gid>::with_capacity(Errno::result(ngroups)? as usize);
+ loop {
+ // FIXME: On the platforms we currently support, the `Gid` struct has
+ // the same representation in memory as a bare `gid_t`. This is not
+ // necessarily the case on all Rust platforms, though. See RFC 1785.
+ let ngroups = unsafe {
+ libc::getgroups(groups.capacity() as c_int, groups.as_mut_ptr() as *mut gid_t)
+ };
+
+ match Errno::result(ngroups) {
+ Ok(s) => {
+ unsafe { groups.set_len(s as usize) };
+ return Ok(groups);
+ },
+ Err(Errno::EINVAL) => {
+ // EINVAL indicates that the buffer size was too
+ // small, resize it up to ngroups_max as limit.
+ reserve_double_buffer_size(&mut groups, ngroups_max)
+ .or(Err(Errno::EINVAL))?;
+ },
+ Err(e) => return Err(e)
+ }
+ }
+}
+
+/// Set the list of supplementary group IDs for the calling process.
+///
+/// [Further reading](https://man7.org/linux/man-pages/man2/getgroups.2.html)
+///
+/// **Note:** This function is not available for Apple platforms. On those
+/// platforms, group membership management should be achieved via communication
+/// with the `opendirectoryd` service.
+///
+/// # Examples
+///
+/// `setgroups` can be used when dropping privileges from the root user to a
+/// specific user and group. For example, given the user `www-data` with UID
+/// `33` and the group `backup` with the GID `34`, one could switch the user as
+/// follows:
+///
+/// ```rust,no_run
+/// # use std::error::Error;
+/// # use nix::unistd::*;
+/// #
+/// # fn try_main() -> Result<(), Box<dyn Error>> {
+/// let uid = Uid::from_raw(33);
+/// let gid = Gid::from_raw(34);
+/// setgroups(&[gid])?;
+/// setgid(gid)?;
+/// setuid(uid)?;
+/// #
+/// # Ok(())
+/// # }
+/// #
+/// # try_main().unwrap();
+/// ```
+#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox", target_os = "haiku")))]
+pub fn setgroups(groups: &[Gid]) -> Result<()> {
+ cfg_if! {
+ if #[cfg(any(target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "illumos",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "illumos",
+ target_os = "openbsd"))] {
+ type setgroups_ngroups_t = c_int;
+ } else {
+ type setgroups_ngroups_t = size_t;
+ }
+ }
+ // FIXME: On the platforms we currently support, the `Gid` struct has the
+ // same representation in memory as a bare `gid_t`. This is not necessarily
+ // the case on all Rust platforms, though. See RFC 1785.
+ let res = unsafe {
+ libc::setgroups(groups.len() as setgroups_ngroups_t, groups.as_ptr() as *const gid_t)
+ };
+
+ Errno::result(res).map(drop)
+}
+
+/// Calculate the supplementary group access list.
+///
+/// Gets the group IDs of all groups that `user` is a member of. The additional
+/// group `group` is also added to the list.
+///
+/// [Further reading](https://man7.org/linux/man-pages/man3/getgrouplist.3.html)
+///
+/// **Note:** This function is not available for Apple platforms. On those
+/// platforms, checking group membership should be achieved via communication
+/// with the `opendirectoryd` service.
+///
+/// # Errors
+///
+/// Although the `getgrouplist()` call does not return any specific
+/// errors on any known platforms, this implementation will return a system
+/// error of `EINVAL` if the number of groups to be fetched exceeds the
+/// `NGROUPS_MAX` sysconf value. This mimics the behaviour of `getgroups()`
+/// and `setgroups()`. Additionally, while some implementations will return a
+/// partial list of groups when `NGROUPS_MAX` is exceeded, this implementation
+/// will only ever return the complete list or else an error.
+#[cfg(not(any(target_os = "illumos",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "redox")))]
+pub fn getgrouplist(user: &CStr, group: Gid) -> Result<Vec<Gid>> {
+ let ngroups_max = match sysconf(SysconfVar::NGROUPS_MAX) {
+ Ok(Some(n)) => n as c_int,
+ Ok(None) | Err(_) => <c_int>::max_value(),
+ };
+ use std::cmp::min;
+ let mut groups = Vec::<Gid>::with_capacity(min(ngroups_max, 8) as usize);
+ cfg_if! {
+ if #[cfg(any(target_os = "ios", target_os = "macos"))] {
+ type getgrouplist_group_t = c_int;
+ } else {
+ type getgrouplist_group_t = gid_t;
+ }
+ }
+ let gid: gid_t = group.into();
+ loop {
+ let mut ngroups = groups.capacity() as i32;
+ let ret = unsafe {
+ libc::getgrouplist(user.as_ptr(),
+ gid as getgrouplist_group_t,
+ groups.as_mut_ptr() as *mut getgrouplist_group_t,
+ &mut ngroups)
+ };
+
+ // BSD systems only return 0 or -1, Linux returns ngroups on success.
+ if ret >= 0 {
+ unsafe { groups.set_len(ngroups as usize) };
+ return Ok(groups);
+ } else if ret == -1 {
+ // Returns -1 if ngroups is too small, but does not set errno.
+ // BSD systems will still fill the groups buffer with as many
+ // groups as possible, but Linux manpages do not mention this
+ // behavior.
+ reserve_double_buffer_size(&mut groups, ngroups_max as usize)
+ .map_err(|_| Errno::EINVAL)?;
+ }
+ }
+}
+
+/// Initialize the supplementary group access list.
+///
+/// Sets the supplementary group IDs for the calling process using all groups
+/// that `user` is a member of. The additional group `group` is also added to
+/// the list.
+///
+/// [Further reading](https://man7.org/linux/man-pages/man3/initgroups.3.html)
+///
+/// **Note:** This function is not available for Apple platforms. On those
+/// platforms, group membership management should be achieved via communication
+/// with the `opendirectoryd` service.
+///
+/// # Examples
+///
+/// `initgroups` can be used when dropping privileges from the root user to
+/// another user. For example, given the user `www-data`, we could look up the
+/// UID and GID for the user in the system's password database (usually found
+/// in `/etc/passwd`). If the `www-data` user's UID and GID were `33` and `33`,
+/// respectively, one could switch the user as follows:
+///
+/// ```rust,no_run
+/// # use std::error::Error;
+/// # use std::ffi::CString;
+/// # use nix::unistd::*;
+/// #
+/// # fn try_main() -> Result<(), Box<dyn Error>> {
+/// let user = CString::new("www-data").unwrap();
+/// let uid = Uid::from_raw(33);
+/// let gid = Gid::from_raw(33);
+/// initgroups(&user, gid)?;
+/// setgid(gid)?;
+/// setuid(uid)?;
+/// #
+/// # Ok(())
+/// # }
+/// #
+/// # try_main().unwrap();
+/// ```
+#[cfg(not(any(target_os = "ios", target_os = "macos", target_os = "redox", target_os = "haiku")))]
+pub fn initgroups(user: &CStr, group: Gid) -> Result<()> {
+ cfg_if! {
+ if #[cfg(any(target_os = "ios", target_os = "macos"))] {
+ type initgroups_group_t = c_int;
+ } else {
+ type initgroups_group_t = gid_t;
+ }
+ }
+ let gid: gid_t = group.into();
+ let res = unsafe { libc::initgroups(user.as_ptr(), gid as initgroups_group_t) };
+
+ Errno::result(res).map(drop)
+}
+}
+
+feature! {
+#![feature = "signal"]
+
+/// Suspend the thread until a signal is received.
+///
+/// See also [pause(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pause.html).
+#[inline]
+#[cfg(not(target_os = "redox"))]
+pub fn pause() {
+ unsafe { libc::pause() };
+}
+
+pub mod alarm {
+ //! Alarm signal scheduling.
+ //!
+ //! Scheduling an alarm will trigger a `SIGALRM` signal when the time has
+ //! elapsed, which has to be caught, because the default action for the
+ //! signal is to terminate the program. This signal also can't be ignored
+ //! because the system calls like `pause` will not be interrupted, see the
+ //! second example below.
+ //!
+ //! # Examples
+ //!
+ //! Canceling an alarm:
+ //!
+ //! ```
+ //! use nix::unistd::alarm;
+ //!
+ //! // Set an alarm for 60 seconds from now.
+ //! alarm::set(60);
+ //!
+ //! // Cancel the above set alarm, which returns the number of seconds left
+ //! // of the previously set alarm.
+ //! assert_eq!(alarm::cancel(), Some(60));
+ //! ```
+ //!
+ //! Scheduling an alarm and waiting for the signal:
+ //!
+#![cfg_attr(target_os = "redox", doc = " ```rust,ignore")]
+#![cfg_attr(not(target_os = "redox"), doc = " ```rust")]
+ //! use std::time::{Duration, Instant};
+ //!
+ //! use nix::unistd::{alarm, pause};
+ //! use nix::sys::signal::*;
+ //!
+ //! // We need to setup an empty signal handler to catch the alarm signal,
+ //! // otherwise the program will be terminated once the signal is delivered.
+ //! extern fn signal_handler(_: nix::libc::c_int) { }
+ //! let sa = SigAction::new(
+ //! SigHandler::Handler(signal_handler),
+ //! SaFlags::SA_RESTART,
+ //! SigSet::empty()
+ //! );
+ //! unsafe {
+ //! sigaction(Signal::SIGALRM, &sa);
+ //! }
+ //!
+ //! let start = Instant::now();
+ //!
+ //! // Set an alarm for 1 second from now.
+ //! alarm::set(1);
+ //!
+ //! // Pause the process until the alarm signal is received.
+ //! let mut sigset = SigSet::empty();
+ //! sigset.add(Signal::SIGALRM);
+ //! sigset.wait();
+ //!
+ //! assert!(start.elapsed() >= Duration::from_secs(1));
+ //! ```
+ //!
+ //! # References
+ //!
+ //! See also [alarm(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/alarm.html).
+
+ /// Schedule an alarm signal.
+ ///
+ /// This will cause the system to generate a `SIGALRM` signal for the
+ /// process after the specified number of seconds have elapsed.
+ ///
+ /// Returns the leftover time of a previously set alarm if there was one.
+ pub fn set(secs: libc::c_uint) -> Option<libc::c_uint> {
+ assert!(secs != 0, "passing 0 to `alarm::set` is not allowed, to cancel an alarm use `alarm::cancel`");
+ alarm(secs)
+ }
+
+ /// Cancel an previously set alarm signal.
+ ///
+ /// Returns the leftover time of a previously set alarm if there was one.
+ pub fn cancel() -> Option<libc::c_uint> {
+ alarm(0)
+ }
+
+ fn alarm(secs: libc::c_uint) -> Option<libc::c_uint> {
+ match unsafe { libc::alarm(secs) } {
+ 0 => None,
+ secs => Some(secs),
+ }
+ }
+}
+}
+
+/// Suspend execution for an interval of time
+///
+/// See also [sleep(2)](https://pubs.opengroup.org/onlinepubs/009695399/functions/sleep.html#tag_03_705_05)
+// Per POSIX, does not fail
+#[inline]
+pub fn sleep(seconds: c_uint) -> c_uint {
+ unsafe { libc::sleep(seconds) }
+}
+
+feature! {
+#![feature = "acct"]
+
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+pub mod acct {
+ use crate::{Result, NixPath};
+ use crate::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 = 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)
+ }
+}
+}
+
+feature! {
+#![feature = "fs"]
+/// 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`
+/// * returns: tuple of file descriptor and filename
+///
+/// Err is returned either if no temporary filename could be created or the template doesn't
+/// end with XXXXXX
+///
+/// See also [mkstemp(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/mkstemp.html)
+///
+/// # Example
+///
+/// ```rust
+/// use nix::unistd;
+///
+/// let _ = match unistd::mkstemp("/tmp/tempfile_XXXXXX") {
+/// Ok((fd, path)) => {
+/// unistd::unlink(path.as_path()).unwrap(); // flag file to be deleted at app termination
+/// fd
+/// }
+/// Err(e) => panic!("mkstemp failed: {}", e)
+/// };
+/// // do something with fd
+/// ```
+#[inline]
+pub fn mkstemp<P: ?Sized + NixPath>(template: &P) -> Result<(RawFd, PathBuf)> {
+ let mut path = template.with_nix_path(|path| {path.to_bytes_with_nul().to_owned()})?;
+ let p = path.as_mut_ptr() as *mut _;
+ let fd = unsafe { libc::mkstemp(p) };
+ let last = path.pop(); // drop the trailing nul
+ debug_assert!(last == Some(b'\0'));
+ let pathname = OsString::from_vec(path);
+ Errno::result(fd)?;
+ Ok((fd, PathBuf::from(pathname)))
+}
+}
+
+feature! {
+#![all(feature = "fs", feature = "feature")]
+
+/// Variable names for `pathconf`
+///
+/// Nix uses the same naming convention for these variables as the
+/// [getconf(1)](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/getconf.html) utility.
+/// That is, `PathconfVar` variables have the same name as the abstract
+/// variables shown in the `pathconf(2)` man page. Usually, it's the same as
+/// the C variable name without the leading `_PC_`.
+///
+/// POSIX 1003.1-2008 standardizes all of these variables, but some OSes choose
+/// not to implement variables that cannot change at runtime.
+///
+/// # References
+///
+/// - [pathconf(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pathconf.html)
+/// - [limits.h](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/limits.h.html)
+/// - [unistd.h](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/unistd.h.html)
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[repr(i32)]
+#[non_exhaustive]
+pub enum PathconfVar {
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "linux",
+ target_os = "netbsd", target_os = "openbsd", target_os = "redox"))]
+ /// Minimum number of bits needed to represent, as a signed integer value,
+ /// the maximum size of a regular file allowed in the specified directory.
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ FILESIZEBITS = libc::_PC_FILESIZEBITS,
+ /// Maximum number of links to a single file.
+ LINK_MAX = libc::_PC_LINK_MAX,
+ /// Maximum number of bytes in a terminal canonical input line.
+ MAX_CANON = libc::_PC_MAX_CANON,
+ /// Minimum number of bytes for which space is available in a terminal input
+ /// queue; therefore, the maximum number of bytes a conforming application
+ /// may require to be typed as input before reading them.
+ MAX_INPUT = libc::_PC_MAX_INPUT,
+ /// Maximum number of bytes in a filename (not including the terminating
+ /// null of a filename string).
+ NAME_MAX = libc::_PC_NAME_MAX,
+ /// Maximum number of bytes the implementation will store as a pathname in a
+ /// user-supplied buffer of unspecified size, including the terminating null
+ /// character. Minimum number the implementation will accept as the maximum
+ /// number of bytes in a pathname.
+ PATH_MAX = libc::_PC_PATH_MAX,
+ /// Maximum number of bytes that is guaranteed to be atomic when writing to
+ /// a pipe.
+ PIPE_BUF = libc::_PC_PIPE_BUF,
+ #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "illumos",
+ target_os = "linux", target_os = "netbsd", target_os = "openbsd",
+ target_os = "redox", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Symbolic links can be created.
+ POSIX2_SYMLINKS = libc::_PC_2_SYMLINKS,
+ #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
+ target_os = "linux", target_os = "openbsd", target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Minimum number of bytes of storage actually allocated for any portion of
+ /// a file.
+ POSIX_ALLOC_SIZE_MIN = libc::_PC_ALLOC_SIZE_MIN,
+ #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
+ target_os = "linux", target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Recommended increment for file transfer sizes between the
+ /// `POSIX_REC_MIN_XFER_SIZE` and `POSIX_REC_MAX_XFER_SIZE` values.
+ POSIX_REC_INCR_XFER_SIZE = libc::_PC_REC_INCR_XFER_SIZE,
+ #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
+ target_os = "linux", target_os = "openbsd", target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Maximum recommended file transfer size.
+ POSIX_REC_MAX_XFER_SIZE = libc::_PC_REC_MAX_XFER_SIZE,
+ #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
+ target_os = "linux", target_os = "openbsd", target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Minimum recommended file transfer size.
+ POSIX_REC_MIN_XFER_SIZE = libc::_PC_REC_MIN_XFER_SIZE,
+ #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
+ target_os = "linux", target_os = "openbsd", target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Recommended file transfer buffer alignment.
+ POSIX_REC_XFER_ALIGN = libc::_PC_REC_XFER_ALIGN,
+ #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
+ target_os = "illumos", target_os = "linux", target_os = "netbsd",
+ target_os = "openbsd", target_os = "redox", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Maximum number of bytes in a symbolic link.
+ SYMLINK_MAX = libc::_PC_SYMLINK_MAX,
+ /// The use of `chown` and `fchown` is restricted to a process with
+ /// appropriate privileges, and to changing the group ID of a file only to
+ /// the effective group ID of the process or to one of its supplementary
+ /// group IDs.
+ _POSIX_CHOWN_RESTRICTED = libc::_PC_CHOWN_RESTRICTED,
+ /// Pathname components longer than {NAME_MAX} generate an error.
+ _POSIX_NO_TRUNC = libc::_PC_NO_TRUNC,
+ /// This symbol shall be defined to be the value of a character that shall
+ /// disable terminal special character handling.
+ _POSIX_VDISABLE = libc::_PC_VDISABLE,
+ #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
+ target_os = "illumos", target_os = "linux", target_os = "openbsd",
+ target_os = "redox", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Asynchronous input or output operations may be performed for the
+ /// associated file.
+ _POSIX_ASYNC_IO = libc::_PC_ASYNC_IO,
+ #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
+ target_os = "illumos", target_os = "linux", target_os = "openbsd",
+ target_os = "redox", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Prioritized input or output operations may be performed for the
+ /// associated file.
+ _POSIX_PRIO_IO = libc::_PC_PRIO_IO,
+ #[cfg(any(target_os = "android", target_os = "dragonfly", target_os = "freebsd",
+ target_os = "illumos", target_os = "linux", target_os = "netbsd",
+ target_os = "openbsd", target_os = "redox", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Synchronized input or output operations may be performed for the
+ /// associated file.
+ _POSIX_SYNC_IO = libc::_PC_SYNC_IO,
+ #[cfg(any(target_os = "dragonfly", target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The resolution in nanoseconds for all file timestamps.
+ _POSIX_TIMESTAMP_RESOLUTION = libc::_PC_TIMESTAMP_RESOLUTION
+}
+
+/// Like `pathconf`, but works with file descriptors instead of paths (see
+/// [fpathconf(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pathconf.html))
+///
+/// # Parameters
+///
+/// - `fd`: The file descriptor whose variable should be interrogated
+/// - `var`: The pathconf variable to lookup
+///
+/// # Returns
+///
+/// - `Ok(Some(x))`: the variable's limit (for limit variables) or its
+/// implementation level (for option variables). Implementation levels are
+/// usually a decimal-coded date, such as 200112 for POSIX 2001.12
+/// - `Ok(None)`: the variable has no limit (for limit variables) or is
+/// unsupported (for option variables)
+/// - `Err(x)`: an error occurred
+pub fn fpathconf(fd: RawFd, var: PathconfVar) -> Result<Option<c_long>> {
+ let raw = unsafe {
+ Errno::clear();
+ libc::fpathconf(fd, var as c_int)
+ };
+ if raw == -1 {
+ if errno::errno() == 0 {
+ Ok(None)
+ } else {
+ Err(Errno::last())
+ }
+ } else {
+ Ok(Some(raw))
+ }
+}
+
+/// Get path-dependent configurable system variables (see
+/// [pathconf(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pathconf.html))
+///
+/// Returns the value of a path-dependent configurable system variable. Most
+/// supported variables also have associated compile-time constants, but POSIX
+/// allows their values to change at runtime. There are generally two types of
+/// `pathconf` variables: options and limits. See [pathconf(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/pathconf.html) for more details.
+///
+/// # Parameters
+///
+/// - `path`: Lookup the value of `var` for this file or directory
+/// - `var`: The `pathconf` variable to lookup
+///
+/// # Returns
+///
+/// - `Ok(Some(x))`: the variable's limit (for limit variables) or its
+/// implementation level (for option variables). Implementation levels are
+/// usually a decimal-coded date, such as 200112 for POSIX 2001.12
+/// - `Ok(None)`: the variable has no limit (for limit variables) or is
+/// unsupported (for option variables)
+/// - `Err(x)`: an error occurred
+pub fn pathconf<P: ?Sized + NixPath>(path: &P, var: PathconfVar) -> Result<Option<c_long>> {
+ let raw = path.with_nix_path(|cstr| {
+ unsafe {
+ Errno::clear();
+ libc::pathconf(cstr.as_ptr(), var as c_int)
+ }
+ })?;
+ if raw == -1 {
+ if errno::errno() == 0 {
+ Ok(None)
+ } else {
+ Err(Errno::last())
+ }
+ } else {
+ Ok(Some(raw))
+ }
+}
+}
+
+feature! {
+#![feature = "feature"]
+
+/// Variable names for `sysconf`
+///
+/// Nix uses the same naming convention for these variables as the
+/// [getconf(1)](https://pubs.opengroup.org/onlinepubs/9699919799/utilities/getconf.html) utility.
+/// That is, `SysconfVar` variables have the same name as the abstract variables
+/// shown in the `sysconf(3)` man page. Usually, it's the same as the C
+/// variable name without the leading `_SC_`.
+///
+/// All of these symbols are standardized by POSIX 1003.1-2008, but haven't been
+/// implemented by all platforms.
+///
+/// # References
+///
+/// - [sysconf(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sysconf.html)
+/// - [unistd.h](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/unistd.h.html)
+/// - [limits.h](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/limits.h.html)
+#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
+#[repr(i32)]
+#[non_exhaustive]
+pub enum SysconfVar {
+ /// Maximum number of I/O operations in a single list I/O call supported by
+ /// the implementation.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ AIO_LISTIO_MAX = libc::_SC_AIO_LISTIO_MAX,
+ /// Maximum number of outstanding asynchronous I/O operations supported by
+ /// the implementation.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ AIO_MAX = libc::_SC_AIO_MAX,
+ #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The maximum amount by which a process can decrease its asynchronous I/O
+ /// priority level from its own scheduling priority.
+ AIO_PRIO_DELTA_MAX = libc::_SC_AIO_PRIO_DELTA_MAX,
+ /// Maximum length of argument to the exec functions including environment data.
+ ARG_MAX = libc::_SC_ARG_MAX,
+ /// Maximum number of functions that may be registered with `atexit`.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ ATEXIT_MAX = libc::_SC_ATEXIT_MAX,
+ /// Maximum obase values allowed by the bc utility.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ BC_BASE_MAX = libc::_SC_BC_BASE_MAX,
+ /// Maximum number of elements permitted in an array by the bc utility.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ BC_DIM_MAX = libc::_SC_BC_DIM_MAX,
+ /// Maximum scale value allowed by the bc utility.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ BC_SCALE_MAX = libc::_SC_BC_SCALE_MAX,
+ /// Maximum length of a string constant accepted by the bc utility.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ BC_STRING_MAX = libc::_SC_BC_STRING_MAX,
+ /// Maximum number of simultaneous processes per real user ID.
+ CHILD_MAX = libc::_SC_CHILD_MAX,
+ // The number of clock ticks per second.
+ CLK_TCK = libc::_SC_CLK_TCK,
+ /// Maximum number of weights that can be assigned to an entry of the
+ /// LC_COLLATE order keyword in the locale definition file
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ COLL_WEIGHTS_MAX = libc::_SC_COLL_WEIGHTS_MAX,
+ /// Maximum number of timer expiration overruns.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ DELAYTIMER_MAX = libc::_SC_DELAYTIMER_MAX,
+ /// Maximum number of expressions that can be nested within parentheses by
+ /// the expr utility.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ EXPR_NEST_MAX = libc::_SC_EXPR_NEST_MAX,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="netbsd", target_os="openbsd", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// 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,
+ /// Maximum number of iovec structures that one process has available for
+ /// use with `readv` or `writev`.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ IOV_MAX = libc::_SC_IOV_MAX,
+ /// Unless otherwise noted, the maximum length, in bytes, of a utility's
+ /// input line (either standard input or another file), when the utility is
+ /// described as processing text files. The length includes room for the
+ /// trailing newline.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ LINE_MAX = libc::_SC_LINE_MAX,
+ /// Maximum length of a login name.
+ #[cfg(not(target_os = "haiku"))]
+ LOGIN_NAME_MAX = libc::_SC_LOGIN_NAME_MAX,
+ /// Maximum number of simultaneous supplementary group IDs per process.
+ NGROUPS_MAX = libc::_SC_NGROUPS_MAX,
+ /// Initial size of `getgrgid_r` and `getgrnam_r` data buffers
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ GETGR_R_SIZE_MAX = libc::_SC_GETGR_R_SIZE_MAX,
+ /// Initial size of `getpwuid_r` and `getpwnam_r` data buffers
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ GETPW_R_SIZE_MAX = libc::_SC_GETPW_R_SIZE_MAX,
+ /// The maximum number of open message queue descriptors a process may hold.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MQ_OPEN_MAX = libc::_SC_MQ_OPEN_MAX,
+ /// The maximum number of message priorities supported by the implementation.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ MQ_PRIO_MAX = libc::_SC_MQ_PRIO_MAX,
+ /// A value one greater than the maximum value that the system may assign to
+ /// a newly-created file descriptor.
+ OPEN_MAX = libc::_SC_OPEN_MAX,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Advisory Information option.
+ _POSIX_ADVISORY_INFO = libc::_SC_ADVISORY_INFO,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="netbsd", target_os="openbsd", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports barriers.
+ _POSIX_BARRIERS = libc::_SC_BARRIERS,
+ /// The implementation supports asynchronous input and output.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_ASYNCHRONOUS_IO = libc::_SC_ASYNCHRONOUS_IO,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="netbsd", target_os="openbsd", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports clock selection.
+ _POSIX_CLOCK_SELECTION = libc::_SC_CLOCK_SELECTION,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="netbsd", target_os="openbsd", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Process CPU-Time Clocks option.
+ _POSIX_CPUTIME = libc::_SC_CPUTIME,
+ /// The implementation supports the File Synchronization option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_FSYNC = libc::_SC_FSYNC,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the IPv6 option.
+ _POSIX_IPV6 = libc::_SC_IPV6,
+ /// The implementation supports job control.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_JOB_CONTROL = libc::_SC_JOB_CONTROL,
+ /// The implementation supports memory mapped Files.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_MAPPED_FILES = libc::_SC_MAPPED_FILES,
+ /// The implementation supports the Process Memory Locking option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_MEMLOCK = libc::_SC_MEMLOCK,
+ /// The implementation supports the Range Memory Locking option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_MEMLOCK_RANGE = libc::_SC_MEMLOCK_RANGE,
+ /// The implementation supports memory protection.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_MEMORY_PROTECTION = libc::_SC_MEMORY_PROTECTION,
+ /// The implementation supports the Message Passing option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_MESSAGE_PASSING = libc::_SC_MESSAGE_PASSING,
+ /// The implementation supports the Monotonic Clock option.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_MONOTONIC_CLOCK = libc::_SC_MONOTONIC_CLOCK,
+ #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
+ target_os = "illumos", target_os = "ios", target_os="linux",
+ target_os = "macos", target_os="openbsd", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Prioritized Input and Output option.
+ _POSIX_PRIORITIZED_IO = libc::_SC_PRIORITIZED_IO,
+ /// The implementation supports the Process Scheduling option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_PRIORITY_SCHEDULING = libc::_SC_PRIORITY_SCHEDULING,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Raw Sockets option.
+ _POSIX_RAW_SOCKETS = libc::_SC_RAW_SOCKETS,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="netbsd", target_os="openbsd", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports read-write locks.
+ _POSIX_READER_WRITER_LOCKS = libc::_SC_READER_WRITER_LOCKS,
+ #[cfg(any(target_os = "android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports realtime signals.
+ _POSIX_REALTIME_SIGNALS = libc::_SC_REALTIME_SIGNALS,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "illumos",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="netbsd", target_os="openbsd", target_os = "solaris"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Regular Expression Handling option.
+ _POSIX_REGEXP = libc::_SC_REGEXP,
+ /// Each process has a saved set-user-ID and a saved set-group-ID.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_SAVED_IDS = libc::_SC_SAVED_IDS,
+ /// The implementation supports semaphores.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_SEMAPHORES = libc::_SC_SEMAPHORES,
+ /// The implementation supports the Shared Memory Objects option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_SHARED_MEMORY_OBJECTS = libc::_SC_SHARED_MEMORY_OBJECTS,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the POSIX shell.
+ _POSIX_SHELL = libc::_SC_SHELL,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Spawn option.
+ _POSIX_SPAWN = libc::_SC_SPAWN,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports spin locks.
+ _POSIX_SPIN_LOCKS = libc::_SC_SPIN_LOCKS,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Process Sporadic Server option.
+ _POSIX_SPORADIC_SERVER = libc::_SC_SPORADIC_SERVER,
+ #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_SS_REPL_MAX = libc::_SC_SS_REPL_MAX,
+ /// The implementation supports the Synchronized Input and Output option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_SYNCHRONIZED_IO = libc::_SC_SYNCHRONIZED_IO,
+ /// The implementation supports the Thread Stack Address Attribute option.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_THREAD_ATTR_STACKADDR = libc::_SC_THREAD_ATTR_STACKADDR,
+ /// The implementation supports the Thread Stack Size Attribute option.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_THREAD_ATTR_STACKSIZE = libc::_SC_THREAD_ATTR_STACKSIZE,
+ #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="netbsd", target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Thread CPU-Time Clocks option.
+ _POSIX_THREAD_CPUTIME = libc::_SC_THREAD_CPUTIME,
+ /// The implementation supports the Non-Robust Mutex Priority Inheritance
+ /// option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_THREAD_PRIO_INHERIT = libc::_SC_THREAD_PRIO_INHERIT,
+ /// The implementation supports the Non-Robust Mutex Priority Protection option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_THREAD_PRIO_PROTECT = libc::_SC_THREAD_PRIO_PROTECT,
+ /// The implementation supports the Thread Execution Scheduling option.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_THREAD_PRIORITY_SCHEDULING = libc::_SC_THREAD_PRIORITY_SCHEDULING,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Thread Process-Shared Synchronization
+ /// option.
+ _POSIX_THREAD_PROCESS_SHARED = libc::_SC_THREAD_PROCESS_SHARED,
+ #[cfg(any(target_os="dragonfly", target_os="linux", target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Robust Mutex Priority Inheritance option.
+ _POSIX_THREAD_ROBUST_PRIO_INHERIT = libc::_SC_THREAD_ROBUST_PRIO_INHERIT,
+ #[cfg(any(target_os="dragonfly", target_os="linux", target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Robust Mutex Priority Protection option.
+ _POSIX_THREAD_ROBUST_PRIO_PROTECT = libc::_SC_THREAD_ROBUST_PRIO_PROTECT,
+ /// The implementation supports thread-safe functions.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_THREAD_SAFE_FUNCTIONS = libc::_SC_THREAD_SAFE_FUNCTIONS,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Thread Sporadic Server option.
+ _POSIX_THREAD_SPORADIC_SERVER = libc::_SC_THREAD_SPORADIC_SERVER,
+ /// The implementation supports threads.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_THREADS = libc::_SC_THREADS,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports timeouts.
+ _POSIX_TIMEOUTS = libc::_SC_TIMEOUTS,
+ /// The implementation supports timers.
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_TIMERS = libc::_SC_TIMERS,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Trace option.
+ _POSIX_TRACE = libc::_SC_TRACE,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Trace Event Filter option.
+ _POSIX_TRACE_EVENT_FILTER = libc::_SC_TRACE_EVENT_FILTER,
+ #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_TRACE_EVENT_NAME_MAX = libc::_SC_TRACE_EVENT_NAME_MAX,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Trace Inherit option.
+ _POSIX_TRACE_INHERIT = libc::_SC_TRACE_INHERIT,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Trace Log option.
+ _POSIX_TRACE_LOG = libc::_SC_TRACE_LOG,
+ #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_TRACE_NAME_MAX = libc::_SC_TRACE_NAME_MAX,
+ #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_TRACE_SYS_MAX = libc::_SC_TRACE_SYS_MAX,
+ #[cfg(any(target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX_TRACE_USER_EVENT_MAX = libc::_SC_TRACE_USER_EVENT_MAX,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Typed Memory Objects option.
+ _POSIX_TYPED_MEMORY_OBJECTS = libc::_SC_TYPED_MEMORY_OBJECTS,
+ /// Integer value indicating version of this standard (C-language binding)
+ /// to which the implementation conforms. For implementations conforming to
+ /// POSIX.1-2008, the value shall be 200809L.
+ _POSIX_VERSION = libc::_SC_VERSION,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation provides a C-language compilation environment with
+ /// 32-bit `int`, `long`, `pointer`, and `off_t` types.
+ _POSIX_V6_ILP32_OFF32 = libc::_SC_V6_ILP32_OFF32,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation provides a C-language compilation environment with
+ /// 32-bit `int`, `long`, and pointer types and an `off_t` type using at
+ /// least 64 bits.
+ _POSIX_V6_ILP32_OFFBIG = libc::_SC_V6_ILP32_OFFBIG,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation provides a C-language compilation environment with
+ /// 32-bit `int` and 64-bit `long`, `pointer`, and `off_t` types.
+ _POSIX_V6_LP64_OFF64 = libc::_SC_V6_LP64_OFF64,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation provides a C-language compilation environment with an
+ /// `int` type using at least 32 bits and `long`, pointer, and `off_t` types
+ /// using at least 64 bits.
+ _POSIX_V6_LPBIG_OFFBIG = libc::_SC_V6_LPBIG_OFFBIG,
+ /// The implementation supports the C-Language Binding option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX2_C_BIND = libc::_SC_2_C_BIND,
+ /// The implementation supports the C-Language Development Utilities option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX2_C_DEV = libc::_SC_2_C_DEV,
+ /// The implementation supports the Terminal Characteristics option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX2_CHAR_TERM = libc::_SC_2_CHAR_TERM,
+ /// The implementation supports the FORTRAN Development Utilities option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX2_FORT_DEV = libc::_SC_2_FORT_DEV,
+ /// The implementation supports the FORTRAN Runtime Utilities option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX2_FORT_RUN = libc::_SC_2_FORT_RUN,
+ /// The implementation supports the creation of locales by the localedef
+ /// utility.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX2_LOCALEDEF = libc::_SC_2_LOCALEDEF,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Batch Environment Services and Utilities
+ /// option.
+ _POSIX2_PBS = libc::_SC_2_PBS,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Batch Accounting option.
+ _POSIX2_PBS_ACCOUNTING = libc::_SC_2_PBS_ACCOUNTING,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Batch Checkpoint/Restart option.
+ _POSIX2_PBS_CHECKPOINT = libc::_SC_2_PBS_CHECKPOINT,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Locate Batch Job Request option.
+ _POSIX2_PBS_LOCATE = libc::_SC_2_PBS_LOCATE,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Batch Job Message Request option.
+ _POSIX2_PBS_MESSAGE = libc::_SC_2_PBS_MESSAGE,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Track Batch Job Request option.
+ _POSIX2_PBS_TRACK = libc::_SC_2_PBS_TRACK,
+ /// The implementation supports the Software Development Utilities option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX2_SW_DEV = libc::_SC_2_SW_DEV,
+ /// The implementation supports the User Portability Utilities option.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX2_UPE = libc::_SC_2_UPE,
+ /// Integer value indicating version of the Shell and Utilities volume of
+ /// POSIX.1 to which the implementation conforms.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _POSIX2_VERSION = libc::_SC_2_VERSION,
+ /// The size of a system page in bytes.
+ ///
+ /// POSIX also defines an alias named `PAGESIZE`, but Rust does not allow two
+ /// enum constants to have the same value, so nix omits `PAGESIZE`.
+ PAGE_SIZE = libc::_SC_PAGE_SIZE,
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ PTHREAD_DESTRUCTOR_ITERATIONS = libc::_SC_THREAD_DESTRUCTOR_ITERATIONS,
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ PTHREAD_KEYS_MAX = libc::_SC_THREAD_KEYS_MAX,
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ PTHREAD_STACK_MIN = libc::_SC_THREAD_STACK_MIN,
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ PTHREAD_THREADS_MAX = libc::_SC_THREAD_THREADS_MAX,
+ #[cfg(not(target_os = "haiku"))]
+ RE_DUP_MAX = libc::_SC_RE_DUP_MAX,
+ #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ RTSIG_MAX = libc::_SC_RTSIG_MAX,
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ SEM_NSEMS_MAX = libc::_SC_SEM_NSEMS_MAX,
+ #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ SEM_VALUE_MAX = libc::_SC_SEM_VALUE_MAX,
+ #[cfg(any(target_os = "android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os = "openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ SIGQUEUE_MAX = libc::_SC_SIGQUEUE_MAX,
+ STREAM_MAX = libc::_SC_STREAM_MAX,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="netbsd",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ SYMLOOP_MAX = libc::_SC_SYMLOOP_MAX,
+ #[cfg(not(target_os = "redox"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ TIMER_MAX = libc::_SC_TIMER_MAX,
+ TTY_NAME_MAX = libc::_SC_TTY_NAME_MAX,
+ TZNAME_MAX = libc::_SC_TZNAME_MAX,
+ #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the X/Open Encryption Option Group.
+ _XOPEN_CRYPT = libc::_SC_XOPEN_CRYPT,
+ #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the Issue 4, Version 2 Enhanced
+ /// Internationalization Option Group.
+ _XOPEN_ENH_I18N = libc::_SC_XOPEN_ENH_I18N,
+ #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _XOPEN_LEGACY = libc::_SC_XOPEN_LEGACY,
+ #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the X/Open Realtime Option Group.
+ _XOPEN_REALTIME = libc::_SC_XOPEN_REALTIME,
+ #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the X/Open Realtime Threads Option Group.
+ _XOPEN_REALTIME_THREADS = libc::_SC_XOPEN_REALTIME_THREADS,
+ /// The implementation supports the Issue 4, Version 2 Shared Memory Option
+ /// Group.
+ #[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ _XOPEN_SHM = libc::_SC_XOPEN_SHM,
+ #[cfg(any(target_os="dragonfly", target_os="freebsd", target_os = "ios",
+ target_os="linux", target_os = "macos", target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the XSI STREAMS Option Group.
+ _XOPEN_STREAMS = libc::_SC_XOPEN_STREAMS,
+ #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// The implementation supports the XSI option
+ _XOPEN_UNIX = libc::_SC_XOPEN_UNIX,
+ #[cfg(any(target_os="android", target_os="dragonfly", target_os="freebsd",
+ target_os = "ios", target_os="linux", target_os = "macos",
+ target_os="openbsd"))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ /// Integer value indicating version of the X/Open Portability Guide to
+ /// which the implementation conforms.
+ _XOPEN_VERSION = libc::_SC_XOPEN_VERSION,
+ /// The number of pages of physical memory. Note that it is possible for
+ /// the product of this value to overflow.
+ #[cfg(any(target_os="android", target_os="linux"))]
+ _PHYS_PAGES = libc::_SC_PHYS_PAGES,
+ /// The number of currently available pages of physical memory.
+ #[cfg(any(target_os="android", target_os="linux"))]
+ _AVPHYS_PAGES = libc::_SC_AVPHYS_PAGES,
+ /// The number of processors configured.
+ #[cfg(any(target_os="android", target_os="linux"))]
+ _NPROCESSORS_CONF = libc::_SC_NPROCESSORS_CONF,
+ /// The number of processors currently online (available).
+ #[cfg(any(target_os="android", target_os="linux"))]
+ _NPROCESSORS_ONLN = libc::_SC_NPROCESSORS_ONLN,
+}
+
+/// Get configurable system variables (see
+/// [sysconf(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/sysconf.html))
+///
+/// Returns the value of a configurable system variable. Most supported
+/// variables also have associated compile-time constants, but POSIX
+/// allows their values to change at runtime. There are generally two types of
+/// sysconf variables: options and limits. See sysconf(3) for more details.
+///
+/// # Returns
+///
+/// - `Ok(Some(x))`: the variable's limit (for limit variables) or its
+/// implementation level (for option variables). Implementation levels are
+/// usually a decimal-coded date, such as 200112 for POSIX 2001.12
+/// - `Ok(None)`: the variable has no limit (for limit variables) or is
+/// unsupported (for option variables)
+/// - `Err(x)`: an error occurred
+pub fn sysconf(var: SysconfVar) -> Result<Option<c_long>> {
+ let raw = unsafe {
+ Errno::clear();
+ libc::sysconf(var as c_int)
+ };
+ if raw == -1 {
+ if errno::errno() == 0 {
+ Ok(None)
+ } else {
+ Err(Errno::last())
+ }
+ } else {
+ Ok(Some(raw))
+ }
+}
+}
+
+feature! {
+#![feature = "fs"]
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+mod pivot_root {
+ use crate::{Result, NixPath};
+ use crate::errno::Errno;
+
+ pub fn pivot_root<P1: ?Sized + NixPath, P2: ?Sized + NixPath>(
+ new_root: &P1, put_old: &P2) -> Result<()> {
+ let res = new_root.with_nix_path(|new_root| {
+ put_old.with_nix_path(|put_old| {
+ unsafe {
+ libc::syscall(libc::SYS_pivot_root, new_root.as_ptr(), put_old.as_ptr())
+ }
+ })
+ })??;
+
+ Errno::result(res).map(drop)
+ }
+}
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "openbsd"
+))]
+mod setres {
+ feature! {
+ #![feature = "user"]
+
+ use crate::Result;
+ use crate::errno::Errno;
+ use super::{Uid, Gid};
+
+ /// Sets the real, effective, and saved uid.
+ /// ([see setresuid(2)](https://man7.org/linux/man-pages/man2/setresuid.2.html))
+ ///
+ /// * `ruid`: real user id
+ /// * `euid`: effective user id
+ /// * `suid`: saved user id
+ /// * returns: Ok or libc error code.
+ ///
+ /// Err is returned if the user doesn't have permission to set this UID.
+ #[inline]
+ pub fn setresuid(ruid: Uid, euid: Uid, suid: Uid) -> Result<()> {
+ let res = unsafe { libc::setresuid(ruid.into(), euid.into(), suid.into()) };
+
+ Errno::result(res).map(drop)
+ }
+
+ /// Sets the real, effective, and saved gid.
+ /// ([see setresuid(2)](https://man7.org/linux/man-pages/man2/setresuid.2.html))
+ ///
+ /// * `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.
+ #[inline]
+ pub fn setresgid(rgid: Gid, egid: Gid, sgid: Gid) -> Result<()> {
+ let res = unsafe { libc::setresgid(rgid.into(), egid.into(), sgid.into()) };
+
+ Errno::result(res).map(drop)
+ }
+ }
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "openbsd"
+))]
+mod getres {
+ feature! {
+ #![feature = "user"]
+
+ use crate::Result;
+ use crate::errno::Errno;
+ use super::{Uid, Gid};
+
+ /// Real, effective and saved user IDs.
+ #[derive(Debug, Copy, Clone, Eq, PartialEq)]
+ pub struct ResUid {
+ pub real: Uid,
+ pub effective: Uid,
+ pub saved: Uid
+ }
+
+ /// Real, effective and saved group IDs.
+ #[derive(Debug, Copy, Clone, Eq, PartialEq)]
+ pub struct ResGid {
+ pub real: Gid,
+ pub effective: Gid,
+ pub saved: Gid
+ }
+
+ /// Gets the real, effective, and saved user IDs.
+ ///
+ /// ([see getresuid(2)](http://man7.org/linux/man-pages/man2/getresuid.2.html))
+ ///
+ /// #Returns
+ ///
+ /// - `Ok((Uid, Uid, Uid))`: tuple of real, effective and saved uids on success.
+ /// - `Err(x)`: libc error code on failure.
+ ///
+ #[inline]
+ pub fn getresuid() -> Result<ResUid> {
+ let mut ruid = libc::uid_t::max_value();
+ let mut euid = libc::uid_t::max_value();
+ let mut suid = libc::uid_t::max_value();
+ let res = unsafe { libc::getresuid(&mut ruid, &mut euid, &mut suid) };
+
+ Errno::result(res).map(|_| ResUid{ real: Uid(ruid), effective: Uid(euid), saved: Uid(suid) })
+ }
+
+ /// Gets the real, effective, and saved group IDs.
+ ///
+ /// ([see getresgid(2)](http://man7.org/linux/man-pages/man2/getresgid.2.html))
+ ///
+ /// #Returns
+ ///
+ /// - `Ok((Gid, Gid, Gid))`: tuple of real, effective and saved gids on success.
+ /// - `Err(x)`: libc error code on failure.
+ ///
+ #[inline]
+ pub fn getresgid() -> Result<ResGid> {
+ let mut rgid = libc::gid_t::max_value();
+ let mut egid = libc::gid_t::max_value();
+ let mut sgid = libc::gid_t::max_value();
+ let res = unsafe { libc::getresgid(&mut rgid, &mut egid, &mut sgid) };
+
+ Errno::result(res).map(|_| ResGid { real: Gid(rgid), effective: Gid(egid), saved: Gid(sgid) } )
+ }
+ }
+}
+
+#[cfg(feature = "fs")]
+libc_bitflags! {
+ /// Options for access()
+ #[cfg_attr(docsrs, doc(cfg(feature = "fs")))]
+ pub struct AccessFlags : c_int {
+ /// Test for existence of file.
+ F_OK;
+ /// Test for read permission.
+ R_OK;
+ /// Test for write permission.
+ W_OK;
+ /// Test for execute (search) permission.
+ X_OK;
+ }
+}
+
+feature! {
+#![feature = "fs"]
+
+/// Checks the file named by `path` for accessibility according to the flags given by `amode`
+/// See [access(2)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/access.html)
+pub fn access<P: ?Sized + NixPath>(path: &P, amode: AccessFlags) -> Result<()> {
+ let res = path.with_nix_path(|cstr| {
+ unsafe {
+ libc::access(cstr.as_ptr(), amode.bits)
+ }
+ })?;
+ Errno::result(res).map(drop)
+}
+
+/// Checks the file named by `path` for accessibility according to the flags given by `mode`
+///
+/// If `dirfd` has a value, then `path` is relative to directory associated with the file descriptor.
+///
+/// If `dirfd` is `None`, then `path` is relative to the current working directory.
+///
+/// # References
+///
+/// [faccessat(2)](http://pubs.opengroup.org/onlinepubs/9699919799/functions/faccessat.html)
+// redox: does not appear to support the *at family of syscalls.
+#[cfg(not(target_os = "redox"))]
+pub fn faccessat<P: ?Sized + NixPath>(dirfd: Option<RawFd>, path: &P, mode: AccessFlags, flags: AtFlags) -> Result<()> {
+ let res = path.with_nix_path(|cstr| {
+ unsafe {
+ libc::faccessat(at_rawfd(dirfd), cstr.as_ptr(), mode.bits(), flags.bits())
+ }
+ })?;
+ Errno::result(res).map(drop)
+}
+
+/// Checks the file named by `path` for accessibility according to the flags given
+/// by `mode` using effective UID, effective GID and supplementary group lists.
+///
+/// # References
+///
+/// * [FreeBSD man page](https://www.freebsd.org/cgi/man.cgi?query=eaccess&sektion=2&n=1)
+/// * [Linux man page](https://man7.org/linux/man-pages/man3/euidaccess.3.html)
+#[cfg(any(
+ all(target_os = "linux", not(target_env = "uclibc")),
+ target_os = "freebsd",
+ target_os = "dragonfly"
+))]
+pub fn eaccess<P: ?Sized + NixPath>(path: &P, mode: AccessFlags) -> Result<()> {
+ let res = path.with_nix_path(|cstr| {
+ unsafe {
+ libc::eaccess(cstr.as_ptr(), mode.bits)
+ }
+ })?;
+ Errno::result(res).map(drop)
+}
+}
+
+feature! {
+#![feature = "user"]
+
+/// Representation of a User, based on `libc::passwd`
+///
+/// The reason some fields in this struct are `String` and others are `CString` is because some
+/// fields are based on the user's locale, which could be non-UTF8, while other fields are
+/// guaranteed to conform to [`NAME_REGEX`](https://serverfault.com/a/73101/407341), which only
+/// contains ASCII.
+#[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct User {
+ /// Username
+ pub name: String,
+ /// User password (probably hashed)
+ pub passwd: CString,
+ /// User ID
+ pub uid: Uid,
+ /// Group ID
+ pub gid: Gid,
+ /// User information
+ #[cfg(not(all(target_os = "android", target_pointer_width = "32")))]
+ pub gecos: CString,
+ /// Home directory
+ pub dir: PathBuf,
+ /// Path to shell
+ pub shell: PathBuf,
+ /// Login class
+ #[cfg(not(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub class: CString,
+ /// Last password change
+ #[cfg(not(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub change: libc::time_t,
+ /// Expiration time of account
+ #[cfg(not(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris")))]
+ #[cfg_attr(docsrs, doc(cfg(all())))]
+ pub expire: libc::time_t
+}
+
+#[cfg(not(target_os = "redox"))] //RedoxFS does not support passwd
+impl From<&libc::passwd> for User {
+ fn from(pw: &libc::passwd) -> User {
+ unsafe {
+ User {
+ name: if pw.pw_name.is_null() { Default::default() } else { CStr::from_ptr(pw.pw_name).to_string_lossy().into_owned() },
+ passwd: if pw.pw_passwd.is_null() { Default::default() } else { CString::new(CStr::from_ptr(pw.pw_passwd).to_bytes()).unwrap() },
+ #[cfg(not(all(target_os = "android", target_pointer_width = "32")))]
+ gecos: if pw.pw_gecos.is_null() { Default::default() } else { CString::new(CStr::from_ptr(pw.pw_gecos).to_bytes()).unwrap() },
+ dir: if pw.pw_dir.is_null() { Default::default() } else { PathBuf::from(OsStr::from_bytes(CStr::from_ptr(pw.pw_dir).to_bytes())) },
+ shell: if pw.pw_shell.is_null() { Default::default() } else { PathBuf::from(OsStr::from_bytes(CStr::from_ptr(pw.pw_shell).to_bytes())) },
+ uid: Uid::from_raw(pw.pw_uid),
+ gid: Gid::from_raw(pw.pw_gid),
+ #[cfg(not(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris")))]
+ class: CString::new(CStr::from_ptr(pw.pw_class).to_bytes()).unwrap(),
+ #[cfg(not(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris")))]
+ change: pw.pw_change,
+ #[cfg(not(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris")))]
+ expire: pw.pw_expire
+ }
+ }
+ }
+}
+
+#[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd
+impl From<User> for libc::passwd {
+ fn from(u: User) -> Self {
+ let name = match CString::new(u.name) {
+ Ok(n) => n.into_raw(),
+ Err(_) => CString::new("").unwrap().into_raw(),
+ };
+ let dir = match u.dir.into_os_string().into_string() {
+ Ok(s) => CString::new(s.as_str()).unwrap().into_raw(),
+ Err(_) => CString::new("").unwrap().into_raw(),
+ };
+ let shell = match u.shell.into_os_string().into_string() {
+ Ok(s) => CString::new(s.as_str()).unwrap().into_raw(),
+ Err(_) => CString::new("").unwrap().into_raw(),
+ };
+ Self {
+ pw_name: name,
+ pw_passwd: u.passwd.into_raw(),
+ #[cfg(not(all(target_os = "android", target_pointer_width = "32")))]
+ pw_gecos: u.gecos.into_raw(),
+ pw_dir: dir,
+ pw_shell: shell,
+ pw_uid: u.uid.0,
+ pw_gid: u.gid.0,
+ #[cfg(not(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris")))]
+ pw_class: u.class.into_raw(),
+ #[cfg(not(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris")))]
+ pw_change: u.change,
+ #[cfg(not(any(target_os = "android",
+ target_os = "fuchsia",
+ target_os = "haiku",
+ target_os = "illumos",
+ target_os = "linux",
+ target_os = "solaris")))]
+ pw_expire: u.expire,
+ #[cfg(target_os = "illumos")]
+ pw_age: CString::new("").unwrap().into_raw(),
+ #[cfg(target_os = "illumos")]
+ pw_comment: CString::new("").unwrap().into_raw(),
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ pw_fields: 0,
+ }
+ }
+}
+
+#[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd
+impl User {
+ fn from_anything<F>(f: F) -> Result<Option<Self>>
+ where
+ F: Fn(*mut libc::passwd,
+ *mut c_char,
+ libc::size_t,
+ *mut *mut libc::passwd) -> libc::c_int
+ {
+ let buflimit = 1048576;
+ let bufsize = match sysconf(SysconfVar::GETPW_R_SIZE_MAX) {
+ Ok(Some(n)) => n as usize,
+ Ok(None) | Err(_) => 16384,
+ };
+
+ let mut cbuf = Vec::with_capacity(bufsize);
+ let mut pwd = mem::MaybeUninit::<libc::passwd>::uninit();
+ let mut res = ptr::null_mut();
+
+ loop {
+ let error = f(pwd.as_mut_ptr(), cbuf.as_mut_ptr(), cbuf.capacity(), &mut res);
+ if error == 0 {
+ if res.is_null() {
+ return Ok(None);
+ } else {
+ let pwd = unsafe { pwd.assume_init() };
+ return Ok(Some(User::from(&pwd)));
+ }
+ } else if Errno::last() == Errno::ERANGE {
+ // Trigger the internal buffer resizing logic.
+ reserve_double_buffer_size(&mut cbuf, buflimit)?;
+ } else {
+ return Err(Errno::last());
+ }
+ }
+ }
+
+ /// Get a user by UID.
+ ///
+ /// Internally, this function calls
+ /// [getpwuid_r(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid_r.html)
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use nix::unistd::{Uid, User};
+ /// // Returns an Result<Option<User>>, thus the double unwrap.
+ /// let res = User::from_uid(Uid::from_raw(0)).unwrap().unwrap();
+ /// assert_eq!(res.name, "root");
+ /// ```
+ pub fn from_uid(uid: Uid) -> Result<Option<Self>> {
+ User::from_anything(|pwd, cbuf, cap, res| {
+ unsafe { libc::getpwuid_r(uid.0, pwd, cbuf, cap, res) }
+ })
+ }
+
+ /// Get a user by name.
+ ///
+ /// Internally, this function calls
+ /// [getpwnam_r(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid_r.html)
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use nix::unistd::User;
+ /// // Returns an Result<Option<User>>, thus the double unwrap.
+ /// let res = User::from_name("root").unwrap().unwrap();
+ /// assert_eq!(res.name, "root");
+ /// ```
+ pub fn from_name(name: &str) -> Result<Option<Self>> {
+ let name = match CString::new(name) {
+ Ok(c_str) => c_str,
+ Err(_nul_error) => return Ok(None),
+ };
+ User::from_anything(|pwd, cbuf, cap, res| {
+ unsafe { libc::getpwnam_r(name.as_ptr(), pwd, cbuf, cap, res) }
+ })
+ }
+}
+
+/// Representation of a Group, based on `libc::group`
+#[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd
+#[derive(Debug, Clone, Eq, PartialEq)]
+pub struct Group {
+ /// Group name
+ pub name: String,
+ /// Group password
+ pub passwd: CString,
+ /// Group ID
+ pub gid: Gid,
+ /// List of Group members
+ pub mem: Vec<String>
+}
+
+#[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd
+impl From<&libc::group> for Group {
+ fn from(gr: &libc::group) -> Group {
+ unsafe {
+ Group {
+ name: CStr::from_ptr(gr.gr_name).to_string_lossy().into_owned(),
+ passwd: CString::new(CStr::from_ptr(gr.gr_passwd).to_bytes()).unwrap(),
+ gid: Gid::from_raw(gr.gr_gid),
+ mem: Group::members(gr.gr_mem)
+ }
+ }
+ }
+}
+
+#[cfg(not(target_os = "redox"))] // RedoxFS does not support passwd
+impl Group {
+ unsafe fn members(mem: *mut *mut c_char) -> Vec<String> {
+ let mut ret = Vec::new();
+
+ for i in 0.. {
+ let u = mem.offset(i);
+ if (*u).is_null() {
+ break;
+ } else {
+ let s = CStr::from_ptr(*u).to_string_lossy().into_owned();
+ ret.push(s);
+ }
+ }
+
+ ret
+ }
+
+ fn from_anything<F>(f: F) -> Result<Option<Self>>
+ where
+ F: Fn(*mut libc::group,
+ *mut c_char,
+ libc::size_t,
+ *mut *mut libc::group) -> libc::c_int
+ {
+ let buflimit = 1048576;
+ let bufsize = match sysconf(SysconfVar::GETGR_R_SIZE_MAX) {
+ Ok(Some(n)) => n as usize,
+ Ok(None) | Err(_) => 16384,
+ };
+
+ let mut cbuf = Vec::with_capacity(bufsize);
+ let mut grp = mem::MaybeUninit::<libc::group>::uninit();
+ let mut res = ptr::null_mut();
+
+ loop {
+ let error = f(grp.as_mut_ptr(), cbuf.as_mut_ptr(), cbuf.capacity(), &mut res);
+ if error == 0 {
+ if res.is_null() {
+ return Ok(None);
+ } else {
+ let grp = unsafe { grp.assume_init() };
+ return Ok(Some(Group::from(&grp)));
+ }
+ } else if Errno::last() == Errno::ERANGE {
+ // Trigger the internal buffer resizing logic.
+ reserve_double_buffer_size(&mut cbuf, buflimit)?;
+ } else {
+ return Err(Errno::last());
+ }
+ }
+ }
+
+ /// Get a group by GID.
+ ///
+ /// Internally, this function calls
+ /// [getgrgid_r(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid_r.html)
+ ///
+ /// # Examples
+ ///
+ // Disable this test on all OS except Linux as root group may not exist.
+ #[cfg_attr(not(target_os = "linux"), doc = " ```no_run")]
+ #[cfg_attr(target_os = "linux", doc = " ```")]
+ /// use nix::unistd::{Gid, Group};
+ /// // Returns an Result<Option<Group>>, thus the double unwrap.
+ /// let res = Group::from_gid(Gid::from_raw(0)).unwrap().unwrap();
+ /// assert!(res.name == "root");
+ /// ```
+ pub fn from_gid(gid: Gid) -> Result<Option<Self>> {
+ Group::from_anything(|grp, cbuf, cap, res| {
+ unsafe { libc::getgrgid_r(gid.0, grp, cbuf, cap, res) }
+ })
+ }
+
+ /// Get a group by name.
+ ///
+ /// Internally, this function calls
+ /// [getgrnam_r(3)](https://pubs.opengroup.org/onlinepubs/9699919799/functions/getpwuid_r.html)
+ ///
+ /// # Examples
+ ///
+ // Disable this test on all OS except Linux as root group may not exist.
+ #[cfg_attr(not(target_os = "linux"), doc = " ```no_run")]
+ #[cfg_attr(target_os = "linux", doc = " ```")]
+ /// use nix::unistd::Group;
+ /// // Returns an Result<Option<Group>>, thus the double unwrap.
+ /// let res = Group::from_name("root").unwrap().unwrap();
+ /// assert!(res.name == "root");
+ /// ```
+ pub fn from_name(name: &str) -> Result<Option<Self>> {
+ let name = match CString::new(name) {
+ Ok(c_str) => c_str,
+ Err(_nul_error) => return Ok(None),
+ };
+ Group::from_anything(|grp, cbuf, cap, res| {
+ unsafe { libc::getgrnam_r(name.as_ptr(), grp, cbuf, cap, res) }
+ })
+ }
+}
+}
+
+feature! {
+#![feature = "term"]
+
+/// Get the name of the terminal device that is open on file descriptor fd
+/// (see [`ttyname(3)`](https://man7.org/linux/man-pages/man3/ttyname.3.html)).
+#[cfg(not(target_os = "fuchsia"))]
+pub fn ttyname(fd: RawFd) -> Result<PathBuf> {
+ const PATH_MAX: usize = libc::PATH_MAX as usize;
+ let mut buf = vec![0_u8; PATH_MAX];
+ let c_buf = buf.as_mut_ptr() as *mut libc::c_char;
+
+ let ret = unsafe { libc::ttyname_r(fd, c_buf, buf.len()) };
+ if ret != 0 {
+ return Err(Errno::from_i32(ret));
+ }
+
+ let nul = buf.iter().position(|c| *c == b'\0').unwrap();
+ buf.truncate(nul);
+ Ok(OsString::from_vec(buf).into())
+}
+}
+
+feature! {
+#![all(feature = "socket", feature = "user")]
+
+/// Get the effective user ID and group ID associated with a Unix domain socket.
+///
+/// See also [getpeereid(3)](https://www.freebsd.org/cgi/man.cgi?query=getpeereid)
+#[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "dragonfly",
+))]
+pub fn getpeereid(fd: RawFd) -> Result<(Uid, Gid)> {
+ let mut uid = 1;
+ let mut gid = 1;
+
+ let ret = unsafe { libc::getpeereid(fd, &mut uid, &mut gid) };
+
+ Errno::result(ret).map(|_| (Uid(uid), Gid(gid)))
+}
+}
+
+feature! {
+#![all(feature = "fs")]
+
+/// Set the file flags.
+///
+/// See also [chflags(2)](https://www.freebsd.org/cgi/man.cgi?query=chflags&sektion=2)
+#[cfg(any(
+ target_os = "openbsd",
+ target_os = "netbsd",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+ target_os = "macos",
+ target_os = "ios"
+))]
+pub fn chflags<P: ?Sized + NixPath>(path: &P, flags: FileFlag) -> Result<()> {
+ let res = path.with_nix_path(|cstr| unsafe {
+ libc::chflags(cstr.as_ptr(), flags.bits())
+ })?;
+
+ Errno::result(res).map(drop)
+}
+}