summaryrefslogtreecommitdiffstats
path: root/third_party/rust/nix/src/sys
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/rust/nix/src/sys')
-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
37 files changed, 19932 insertions, 0 deletions
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) }
+}