summaryrefslogtreecommitdiffstats
path: root/third_party/rust/nix/test/sys
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /third_party/rust/nix/test/sys
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/nix/test/sys')
-rw-r--r--third_party/rust/nix/test/sys/mod.rs60
-rw-r--r--third_party/rust/nix/test/sys/test_aio.rs624
-rw-r--r--third_party/rust/nix/test/sys/test_aio_drop.rs35
-rw-r--r--third_party/rust/nix/test/sys/test_epoll.rs26
-rw-r--r--third_party/rust/nix/test/sys/test_inotify.rs65
-rw-r--r--third_party/rust/nix/test/sys/test_ioctl.rs376
-rw-r--r--third_party/rust/nix/test/sys/test_mman.rs122
-rw-r--r--third_party/rust/nix/test/sys/test_prctl.rs125
-rw-r--r--third_party/rust/nix/test/sys/test_pthread.rs22
-rw-r--r--third_party/rust/nix/test/sys/test_ptrace.rs275
-rw-r--r--third_party/rust/nix/test/sys/test_select.rs82
-rw-r--r--third_party/rust/nix/test/sys/test_signal.rs143
-rw-r--r--third_party/rust/nix/test/sys/test_signalfd.rs27
-rw-r--r--third_party/rust/nix/test/sys/test_socket.rs2687
-rw-r--r--third_party/rust/nix/test/sys/test_sockopt.rs448
-rw-r--r--third_party/rust/nix/test/sys/test_stat.rs29
-rw-r--r--third_party/rust/nix/test/sys/test_sysinfo.rs20
-rw-r--r--third_party/rust/nix/test/sys/test_termios.rs107
-rw-r--r--third_party/rust/nix/test/sys/test_timerfd.rs69
-rw-r--r--third_party/rust/nix/test/sys/test_uio.rs277
-rw-r--r--third_party/rust/nix/test/sys/test_wait.rs257
21 files changed, 5876 insertions, 0 deletions
diff --git a/third_party/rust/nix/test/sys/mod.rs b/third_party/rust/nix/test/sys/mod.rs
new file mode 100644
index 0000000000..20312120a6
--- /dev/null
+++ b/third_party/rust/nix/test/sys/mod.rs
@@ -0,0 +1,60 @@
+mod test_signal;
+
+// NOTE: DragonFly lacks a kernel-level implementation of Posix AIO as of
+// this writing. There is an user-level implementation, but whether aio
+// works or not heavily depends on which pthread implementation is chosen
+// by the user at link time. For this reason we do not want to run aio test
+// cases on DragonFly.
+#[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ all(target_os = "linux", not(target_env = "uclibc")),
+ target_os = "macos",
+ target_os = "netbsd"
+))]
+mod test_aio;
+#[cfg(not(any(
+ target_os = "redox",
+ target_os = "fuchsia",
+ target_os = "haiku"
+)))]
+mod test_ioctl;
+#[cfg(not(target_os = "redox"))]
+mod test_mman;
+#[cfg(not(target_os = "redox"))]
+mod test_select;
+#[cfg(target_os = "linux")]
+mod test_signalfd;
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+mod test_socket;
+#[cfg(not(any(target_os = "redox")))]
+mod test_sockopt;
+mod test_stat;
+#[cfg(any(target_os = "android", target_os = "linux"))]
+mod test_sysinfo;
+#[cfg(not(any(
+ target_os = "redox",
+ target_os = "fuchsia",
+ target_os = "haiku"
+)))]
+mod test_termios;
+mod test_uio;
+mod test_wait;
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+mod test_epoll;
+#[cfg(target_os = "linux")]
+mod test_inotify;
+mod test_pthread;
+#[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+))]
+mod test_ptrace;
+#[cfg(any(target_os = "android", target_os = "linux"))]
+mod test_timerfd;
diff --git a/third_party/rust/nix/test/sys/test_aio.rs b/third_party/rust/nix/test/sys/test_aio.rs
new file mode 100644
index 0000000000..5035b5a08f
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_aio.rs
@@ -0,0 +1,624 @@
+use std::{
+ io::{Read, Seek, Write},
+ ops::Deref,
+ os::unix::io::AsRawFd,
+ pin::Pin,
+ sync::atomic::{AtomicBool, Ordering},
+ thread, time,
+};
+
+use libc::c_int;
+use nix::{
+ errno::*,
+ sys::{
+ aio::*,
+ signal::{
+ sigaction, SaFlags, SigAction, SigHandler, SigSet, SigevNotify,
+ Signal,
+ },
+ time::{TimeSpec, TimeValLike},
+ },
+};
+use tempfile::tempfile;
+
+pub static SIGNALED: AtomicBool = AtomicBool::new(false);
+
+extern "C" fn sigfunc(_: c_int) {
+ SIGNALED.store(true, Ordering::Relaxed);
+}
+
+// Helper that polls an AioCb for completion or error
+macro_rules! poll_aio {
+ ($aiocb: expr) => {
+ loop {
+ let err = $aiocb.as_mut().error();
+ if err != Err(Errno::EINPROGRESS) {
+ break err;
+ };
+ thread::sleep(time::Duration::from_millis(10));
+ }
+ };
+}
+
+mod aio_fsync {
+ use super::*;
+
+ #[test]
+ fn test_accessors() {
+ let aiocb = AioFsync::new(
+ 1001,
+ AioFsyncMode::O_SYNC,
+ 42,
+ SigevNotify::SigevSignal {
+ signal: Signal::SIGUSR2,
+ si_value: 99,
+ },
+ );
+ assert_eq!(1001, aiocb.fd());
+ assert_eq!(AioFsyncMode::O_SYNC, aiocb.mode());
+ assert_eq!(42, aiocb.priority());
+ let sev = aiocb.sigevent().sigevent();
+ assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo);
+ assert_eq!(99, sev.sigev_value.sival_ptr as i64);
+ }
+
+ /// `AioFsync::submit` should not modify the `AioCb` object if
+ /// `libc::aio_fsync` returns an error
+ // Skip on Linux, because Linux's AIO implementation can't detect errors
+ // synchronously
+ #[test]
+ #[cfg(any(target_os = "freebsd", target_os = "macos"))]
+ fn error() {
+ use std::mem;
+
+ const INITIAL: &[u8] = b"abcdef123456";
+ // Create an invalid AioFsyncMode
+ let mode = unsafe { mem::transmute(666) };
+ let mut f = tempfile().unwrap();
+ f.write_all(INITIAL).unwrap();
+ let mut aiof = Box::pin(AioFsync::new(
+ f.as_raw_fd(),
+ mode,
+ 0,
+ SigevNotify::SigevNone,
+ ));
+ let err = aiof.as_mut().submit();
+ err.expect_err("assertion failed");
+ }
+
+ #[test]
+ #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+ fn ok() {
+ const INITIAL: &[u8] = b"abcdef123456";
+ let mut f = tempfile().unwrap();
+ f.write_all(INITIAL).unwrap();
+ let fd = f.as_raw_fd();
+ let mut aiof = Box::pin(AioFsync::new(
+ fd,
+ AioFsyncMode::O_SYNC,
+ 0,
+ SigevNotify::SigevNone,
+ ));
+ aiof.as_mut().submit().unwrap();
+ poll_aio!(&mut aiof).unwrap();
+ aiof.as_mut().aio_return().unwrap();
+ }
+}
+
+mod aio_read {
+ use super::*;
+
+ #[test]
+ fn test_accessors() {
+ let mut rbuf = vec![0; 4];
+ let aiocb = AioRead::new(
+ 1001,
+ 2, //offset
+ &mut rbuf,
+ 42, //priority
+ SigevNotify::SigevSignal {
+ signal: Signal::SIGUSR2,
+ si_value: 99,
+ },
+ );
+ assert_eq!(1001, aiocb.fd());
+ assert_eq!(4, aiocb.nbytes());
+ assert_eq!(2, aiocb.offset());
+ assert_eq!(42, aiocb.priority());
+ let sev = aiocb.sigevent().sigevent();
+ assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo);
+ assert_eq!(99, sev.sigev_value.sival_ptr as i64);
+ }
+
+ // Tests AioWrite.cancel. We aren't trying to test the OS's implementation,
+ // only our bindings. So it's sufficient to check that cancel
+ // returned any AioCancelStat value.
+ #[test]
+ #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+ fn cancel() {
+ const INITIAL: &[u8] = b"abcdef123456";
+ let mut rbuf = vec![0; 4];
+ let mut f = tempfile().unwrap();
+ f.write_all(INITIAL).unwrap();
+ let fd = f.as_raw_fd();
+ let mut aior =
+ Box::pin(AioRead::new(fd, 2, &mut rbuf, 0, SigevNotify::SigevNone));
+ aior.as_mut().submit().unwrap();
+
+ aior.as_mut().cancel().unwrap();
+
+ // Wait for aiow to complete, but don't care whether it succeeded
+ let _ = poll_aio!(&mut aior);
+ let _ = aior.as_mut().aio_return();
+ }
+
+ /// `AioRead::submit` should not modify the `AioCb` object if
+ /// `libc::aio_read` returns an error
+ // Skip on Linux, because Linux's AIO implementation can't detect errors
+ // synchronously
+ #[test]
+ #[cfg(any(target_os = "freebsd", target_os = "macos"))]
+ fn error() {
+ const INITIAL: &[u8] = b"abcdef123456";
+ let mut rbuf = vec![0; 4];
+ let mut f = tempfile().unwrap();
+ f.write_all(INITIAL).unwrap();
+ let mut aior = Box::pin(AioRead::new(
+ f.as_raw_fd(),
+ -1, //an invalid offset
+ &mut rbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ ));
+ aior.as_mut().submit().expect_err("assertion failed");
+ }
+
+ // Test a simple aio operation with no completion notification. We must
+ // poll for completion
+ #[test]
+ #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+ fn ok() {
+ const INITIAL: &[u8] = b"abcdef123456";
+ let mut rbuf = vec![0; 4];
+ const EXPECT: &[u8] = b"cdef";
+ let mut f = tempfile().unwrap();
+ f.write_all(INITIAL).unwrap();
+ {
+ let fd = f.as_raw_fd();
+ let mut aior = Box::pin(AioRead::new(
+ fd,
+ 2,
+ &mut rbuf,
+ 0,
+ SigevNotify::SigevNone,
+ ));
+ aior.as_mut().submit().unwrap();
+
+ let err = poll_aio!(&mut aior);
+ assert_eq!(err, Ok(()));
+ assert_eq!(aior.as_mut().aio_return().unwrap(), EXPECT.len());
+ }
+ assert_eq!(EXPECT, rbuf.deref());
+ }
+
+ // Like ok, but allocates the structure on the stack.
+ #[test]
+ #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+ fn on_stack() {
+ const INITIAL: &[u8] = b"abcdef123456";
+ let mut rbuf = vec![0; 4];
+ const EXPECT: &[u8] = b"cdef";
+ let mut f = tempfile().unwrap();
+ f.write_all(INITIAL).unwrap();
+ {
+ let fd = f.as_raw_fd();
+ let mut aior =
+ AioRead::new(fd, 2, &mut rbuf, 0, SigevNotify::SigevNone);
+ let mut aior = unsafe { Pin::new_unchecked(&mut aior) };
+ aior.as_mut().submit().unwrap();
+
+ let err = poll_aio!(&mut aior);
+ assert_eq!(err, Ok(()));
+ assert_eq!(aior.as_mut().aio_return().unwrap(), EXPECT.len());
+ }
+ assert_eq!(EXPECT, rbuf.deref());
+ }
+}
+
+#[cfg(target_os = "freebsd")]
+#[cfg(fbsd14)]
+mod aio_readv {
+ use std::io::IoSliceMut;
+
+ use super::*;
+
+ #[test]
+ fn test_accessors() {
+ let mut rbuf0 = vec![0; 4];
+ let mut rbuf1 = vec![0; 8];
+ let mut rbufs =
+ [IoSliceMut::new(&mut rbuf0), IoSliceMut::new(&mut rbuf1)];
+ let aiocb = AioReadv::new(
+ 1001,
+ 2, //offset
+ &mut rbufs,
+ 42, //priority
+ SigevNotify::SigevSignal {
+ signal: Signal::SIGUSR2,
+ si_value: 99,
+ },
+ );
+ assert_eq!(1001, aiocb.fd());
+ assert_eq!(2, aiocb.iovlen());
+ assert_eq!(2, aiocb.offset());
+ assert_eq!(42, aiocb.priority());
+ let sev = aiocb.sigevent().sigevent();
+ assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo);
+ assert_eq!(99, sev.sigev_value.sival_ptr as i64);
+ }
+
+ #[test]
+ #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+ fn ok() {
+ const INITIAL: &[u8] = b"abcdef123456";
+ let mut rbuf0 = vec![0; 4];
+ let mut rbuf1 = vec![0; 2];
+ let mut rbufs =
+ [IoSliceMut::new(&mut rbuf0), IoSliceMut::new(&mut rbuf1)];
+ const EXPECT0: &[u8] = b"cdef";
+ const EXPECT1: &[u8] = b"12";
+ let mut f = tempfile().unwrap();
+ f.write_all(INITIAL).unwrap();
+ {
+ let fd = f.as_raw_fd();
+ let mut aior = Box::pin(AioReadv::new(
+ fd,
+ 2,
+ &mut rbufs,
+ 0,
+ SigevNotify::SigevNone,
+ ));
+ aior.as_mut().submit().unwrap();
+
+ let err = poll_aio!(&mut aior);
+ assert_eq!(err, Ok(()));
+ assert_eq!(
+ aior.as_mut().aio_return().unwrap(),
+ EXPECT0.len() + EXPECT1.len()
+ );
+ }
+ assert_eq!(&EXPECT0, &rbuf0);
+ assert_eq!(&EXPECT1, &rbuf1);
+ }
+}
+
+mod aio_write {
+ use super::*;
+
+ #[test]
+ fn test_accessors() {
+ let wbuf = vec![0; 4];
+ let aiocb = AioWrite::new(
+ 1001,
+ 2, //offset
+ &wbuf,
+ 42, //priority
+ SigevNotify::SigevSignal {
+ signal: Signal::SIGUSR2,
+ si_value: 99,
+ },
+ );
+ assert_eq!(1001, aiocb.fd());
+ assert_eq!(4, aiocb.nbytes());
+ assert_eq!(2, aiocb.offset());
+ assert_eq!(42, aiocb.priority());
+ let sev = aiocb.sigevent().sigevent();
+ assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo);
+ assert_eq!(99, sev.sigev_value.sival_ptr as i64);
+ }
+
+ // Tests AioWrite.cancel. We aren't trying to test the OS's implementation,
+ // only our bindings. So it's sufficient to check that cancel
+ // returned any AioCancelStat value.
+ #[test]
+ #[cfg_attr(target_env = "musl", ignore)]
+ fn cancel() {
+ let wbuf: &[u8] = b"CDEF";
+
+ let f = tempfile().unwrap();
+ let mut aiow = Box::pin(AioWrite::new(
+ f.as_raw_fd(),
+ 0,
+ wbuf,
+ 0,
+ SigevNotify::SigevNone,
+ ));
+ aiow.as_mut().submit().unwrap();
+ let err = aiow.as_mut().error();
+ assert!(err == Ok(()) || err == Err(Errno::EINPROGRESS));
+
+ aiow.as_mut().cancel().unwrap();
+
+ // Wait for aiow to complete, but don't care whether it succeeded
+ let _ = poll_aio!(&mut aiow);
+ let _ = aiow.as_mut().aio_return();
+ }
+
+ // Test a simple aio operation with no completion notification. We must
+ // poll for completion.
+ #[test]
+ #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+ fn ok() {
+ const INITIAL: &[u8] = b"abcdef123456";
+ let wbuf = "CDEF".to_string().into_bytes();
+ let mut rbuf = Vec::new();
+ const EXPECT: &[u8] = b"abCDEF123456";
+
+ let mut f = tempfile().unwrap();
+ f.write_all(INITIAL).unwrap();
+ let mut aiow = Box::pin(AioWrite::new(
+ f.as_raw_fd(),
+ 2,
+ &wbuf,
+ 0,
+ SigevNotify::SigevNone,
+ ));
+ aiow.as_mut().submit().unwrap();
+
+ let err = poll_aio!(&mut aiow);
+ assert_eq!(err, Ok(()));
+ assert_eq!(aiow.as_mut().aio_return().unwrap(), wbuf.len());
+
+ f.rewind().unwrap();
+ let len = f.read_to_end(&mut rbuf).unwrap();
+ assert_eq!(len, EXPECT.len());
+ assert_eq!(rbuf, EXPECT);
+ }
+
+ // Like ok, but allocates the structure on the stack.
+ #[test]
+ #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+ fn on_stack() {
+ const INITIAL: &[u8] = b"abcdef123456";
+ let wbuf = "CDEF".to_string().into_bytes();
+ let mut rbuf = Vec::new();
+ const EXPECT: &[u8] = b"abCDEF123456";
+
+ let mut f = tempfile().unwrap();
+ f.write_all(INITIAL).unwrap();
+ let mut aiow = AioWrite::new(
+ f.as_raw_fd(),
+ 2, //offset
+ &wbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ );
+ let mut aiow = unsafe { Pin::new_unchecked(&mut aiow) };
+ aiow.as_mut().submit().unwrap();
+
+ let err = poll_aio!(&mut aiow);
+ assert_eq!(err, Ok(()));
+ assert_eq!(aiow.as_mut().aio_return().unwrap(), wbuf.len());
+
+ f.rewind().unwrap();
+ let len = f.read_to_end(&mut rbuf).unwrap();
+ assert_eq!(len, EXPECT.len());
+ assert_eq!(rbuf, EXPECT);
+ }
+
+ /// `AioWrite::write` should not modify the `AioCb` object if
+ /// `libc::aio_write` returns an error.
+ // Skip on Linux, because Linux's AIO implementation can't detect errors
+ // synchronously
+ #[test]
+ #[cfg(any(target_os = "freebsd", target_os = "macos"))]
+ fn error() {
+ let wbuf = "CDEF".to_string().into_bytes();
+ let mut aiow = Box::pin(AioWrite::new(
+ 666, // An invalid file descriptor
+ 0, //offset
+ &wbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ ));
+ aiow.as_mut().submit().expect_err("assertion failed");
+ // Dropping the AioWrite at this point should not panic
+ }
+}
+
+#[cfg(target_os = "freebsd")]
+#[cfg(fbsd14)]
+mod aio_writev {
+ use std::io::IoSlice;
+
+ use super::*;
+
+ #[test]
+ fn test_accessors() {
+ let wbuf0 = vec![0; 4];
+ let wbuf1 = vec![0; 8];
+ let wbufs = [IoSlice::new(&wbuf0), IoSlice::new(&wbuf1)];
+ let aiocb = AioWritev::new(
+ 1001,
+ 2, //offset
+ &wbufs,
+ 42, //priority
+ SigevNotify::SigevSignal {
+ signal: Signal::SIGUSR2,
+ si_value: 99,
+ },
+ );
+ assert_eq!(1001, aiocb.fd());
+ assert_eq!(2, aiocb.iovlen());
+ assert_eq!(2, aiocb.offset());
+ assert_eq!(42, aiocb.priority());
+ let sev = aiocb.sigevent().sigevent();
+ assert_eq!(Signal::SIGUSR2 as i32, sev.sigev_signo);
+ assert_eq!(99, sev.sigev_value.sival_ptr as i64);
+ }
+
+ // Test a simple aio operation with no completion notification. We must
+ // poll for completion.
+ #[test]
+ #[cfg_attr(all(target_env = "musl", target_arch = "x86_64"), ignore)]
+ fn ok() {
+ const INITIAL: &[u8] = b"abcdef123456";
+ let wbuf0 = b"BC";
+ let wbuf1 = b"DEF";
+ let wbufs = [IoSlice::new(wbuf0), IoSlice::new(wbuf1)];
+ let wlen = wbuf0.len() + wbuf1.len();
+ let mut rbuf = Vec::new();
+ const EXPECT: &[u8] = b"aBCDEF123456";
+
+ let mut f = tempfile().unwrap();
+ f.write_all(INITIAL).unwrap();
+ let mut aiow = Box::pin(AioWritev::new(
+ f.as_raw_fd(),
+ 1,
+ &wbufs,
+ 0,
+ SigevNotify::SigevNone,
+ ));
+ aiow.as_mut().submit().unwrap();
+
+ let err = poll_aio!(&mut aiow);
+ assert_eq!(err, Ok(()));
+ assert_eq!(aiow.as_mut().aio_return().unwrap(), wlen);
+
+ f.rewind().unwrap();
+ let len = f.read_to_end(&mut rbuf).unwrap();
+ assert_eq!(len, EXPECT.len());
+ assert_eq!(rbuf, EXPECT);
+ }
+}
+
+// Test an aio operation with completion delivered by a signal
+#[test]
+#[cfg_attr(
+ any(
+ all(target_env = "musl", target_arch = "x86_64"),
+ target_arch = "mips",
+ target_arch = "mips64"
+ ),
+ ignore
+)]
+fn sigev_signal() {
+ let _m = crate::SIGNAL_MTX.lock();
+ let sa = SigAction::new(
+ SigHandler::Handler(sigfunc),
+ SaFlags::SA_RESETHAND,
+ SigSet::empty(),
+ );
+ SIGNALED.store(false, Ordering::Relaxed);
+ unsafe { sigaction(Signal::SIGUSR2, &sa) }.unwrap();
+
+ const INITIAL: &[u8] = b"abcdef123456";
+ const WBUF: &[u8] = b"CDEF";
+ let mut rbuf = Vec::new();
+ const EXPECT: &[u8] = b"abCDEF123456";
+
+ let mut f = tempfile().unwrap();
+ f.write_all(INITIAL).unwrap();
+ let mut aiow = Box::pin(AioWrite::new(
+ f.as_raw_fd(),
+ 2, //offset
+ WBUF,
+ 0, //priority
+ SigevNotify::SigevSignal {
+ signal: Signal::SIGUSR2,
+ si_value: 0, //TODO: validate in sigfunc
+ },
+ ));
+ aiow.as_mut().submit().unwrap();
+ while !SIGNALED.load(Ordering::Relaxed) {
+ thread::sleep(time::Duration::from_millis(10));
+ }
+
+ assert_eq!(aiow.as_mut().aio_return().unwrap(), WBUF.len());
+ f.rewind().unwrap();
+ let len = f.read_to_end(&mut rbuf).unwrap();
+ assert_eq!(len, EXPECT.len());
+ assert_eq!(rbuf, EXPECT);
+}
+
+// Tests using aio_cancel_all for all outstanding IOs.
+#[test]
+#[cfg_attr(target_env = "musl", ignore)]
+fn test_aio_cancel_all() {
+ let wbuf: &[u8] = b"CDEF";
+
+ let f = tempfile().unwrap();
+ let mut aiocb = Box::pin(AioWrite::new(
+ f.as_raw_fd(),
+ 0, //offset
+ wbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ ));
+ aiocb.as_mut().submit().unwrap();
+ let err = aiocb.as_mut().error();
+ assert!(err == Ok(()) || err == Err(Errno::EINPROGRESS));
+
+ aio_cancel_all(f.as_raw_fd()).unwrap();
+
+ // Wait for aiocb to complete, but don't care whether it succeeded
+ let _ = poll_aio!(&mut aiocb);
+ let _ = aiocb.as_mut().aio_return();
+}
+
+#[test]
+// On Cirrus on Linux, this test fails due to a glibc bug.
+// https://github.com/nix-rust/nix/issues/1099
+#[cfg_attr(target_os = "linux", ignore)]
+// On Cirrus, aio_suspend is failing with EINVAL
+// https://github.com/nix-rust/nix/issues/1361
+#[cfg_attr(target_os = "macos", ignore)]
+fn test_aio_suspend() {
+ const INITIAL: &[u8] = b"abcdef123456";
+ const WBUF: &[u8] = b"CDEFG";
+ let timeout = TimeSpec::seconds(10);
+ let mut rbuf = vec![0; 4];
+ let rlen = rbuf.len();
+ let mut f = tempfile().unwrap();
+ f.write_all(INITIAL).unwrap();
+
+ let mut wcb = Box::pin(AioWrite::new(
+ f.as_raw_fd(),
+ 2, //offset
+ WBUF,
+ 0, //priority
+ SigevNotify::SigevNone,
+ ));
+
+ let mut rcb = Box::pin(AioRead::new(
+ f.as_raw_fd(),
+ 8, //offset
+ &mut rbuf,
+ 0, //priority
+ SigevNotify::SigevNone,
+ ));
+ wcb.as_mut().submit().unwrap();
+ rcb.as_mut().submit().unwrap();
+ loop {
+ {
+ let cbbuf = [
+ &*wcb as &dyn AsRef<libc::aiocb>,
+ &*rcb as &dyn AsRef<libc::aiocb>,
+ ];
+ let r = aio_suspend(&cbbuf[..], Some(timeout));
+ match r {
+ Err(Errno::EINTR) => continue,
+ Err(e) => panic!("aio_suspend returned {e:?}"),
+ Ok(_) => (),
+ };
+ }
+ if rcb.as_mut().error() != Err(Errno::EINPROGRESS)
+ && wcb.as_mut().error() != Err(Errno::EINPROGRESS)
+ {
+ break;
+ }
+ }
+
+ assert_eq!(wcb.as_mut().aio_return().unwrap(), WBUF.len());
+ assert_eq!(rcb.as_mut().aio_return().unwrap(), rlen);
+}
diff --git a/third_party/rust/nix/test/sys/test_aio_drop.rs b/third_party/rust/nix/test/sys/test_aio_drop.rs
new file mode 100644
index 0000000000..bbe6623fd7
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_aio_drop.rs
@@ -0,0 +1,35 @@
+// Test dropping an AioCb that hasn't yet finished.
+// This must happen in its own process, because on OSX this test seems to hose
+// the AIO subsystem and causes subsequent tests to fail
+#[test]
+#[should_panic(expected = "Dropped an in-progress AioCb")]
+#[cfg(all(
+ not(target_env = "musl"),
+ not(target_env = "uclibc"),
+ any(
+ target_os = "linux",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "freebsd",
+ target_os = "netbsd"
+ )
+))]
+fn test_drop() {
+ use nix::sys::aio::*;
+ use nix::sys::signal::*;
+ use std::os::unix::io::AsRawFd;
+ use tempfile::tempfile;
+
+ const WBUF: &[u8] = b"CDEF";
+
+ let f = tempfile().unwrap();
+ f.set_len(6).unwrap();
+ let mut aiocb = Box::pin(AioWrite::new(
+ f.as_raw_fd(),
+ 2, //offset
+ WBUF,
+ 0, //priority
+ SigevNotify::SigevNone,
+ ));
+ aiocb.as_mut().submit().unwrap();
+}
diff --git a/third_party/rust/nix/test/sys/test_epoll.rs b/third_party/rust/nix/test/sys/test_epoll.rs
new file mode 100644
index 0000000000..84b100c1e9
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_epoll.rs
@@ -0,0 +1,26 @@
+#![allow(deprecated)]
+
+use nix::errno::Errno;
+use nix::sys::epoll::{epoll_create1, epoll_ctl};
+use nix::sys::epoll::{EpollCreateFlags, EpollEvent, EpollFlags, EpollOp};
+
+#[test]
+pub fn test_epoll_errno() {
+ let efd = epoll_create1(EpollCreateFlags::empty()).unwrap();
+ let result = epoll_ctl(efd, EpollOp::EpollCtlDel, 1, None);
+ result.expect_err("assertion failed");
+ assert_eq!(result.unwrap_err(), Errno::ENOENT);
+
+ let result = epoll_ctl(efd, EpollOp::EpollCtlAdd, 1, None);
+ result.expect_err("assertion failed");
+ assert_eq!(result.unwrap_err(), Errno::EINVAL);
+}
+
+#[test]
+pub fn test_epoll_ctl() {
+ let efd = epoll_create1(EpollCreateFlags::empty()).unwrap();
+ let mut event =
+ EpollEvent::new(EpollFlags::EPOLLIN | EpollFlags::EPOLLERR, 1);
+ epoll_ctl(efd, EpollOp::EpollCtlAdd, 1, &mut event).unwrap();
+ epoll_ctl(efd, EpollOp::EpollCtlDel, 1, None).unwrap();
+}
diff --git a/third_party/rust/nix/test/sys/test_inotify.rs b/third_party/rust/nix/test/sys/test_inotify.rs
new file mode 100644
index 0000000000..bb5851a903
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_inotify.rs
@@ -0,0 +1,65 @@
+use nix::errno::Errno;
+use nix::sys::inotify::{AddWatchFlags, InitFlags, Inotify};
+use std::ffi::OsString;
+use std::fs::{rename, File};
+
+#[test]
+pub fn test_inotify() {
+ let instance = Inotify::init(InitFlags::IN_NONBLOCK).unwrap();
+ let tempdir = tempfile::tempdir().unwrap();
+
+ instance
+ .add_watch(tempdir.path(), AddWatchFlags::IN_ALL_EVENTS)
+ .unwrap();
+
+ let events = instance.read_events();
+ assert_eq!(events.unwrap_err(), Errno::EAGAIN);
+
+ File::create(tempdir.path().join("test")).unwrap();
+
+ let events = instance.read_events().unwrap();
+ assert_eq!(events[0].name, Some(OsString::from("test")));
+}
+
+#[test]
+pub fn test_inotify_multi_events() {
+ let instance = Inotify::init(InitFlags::IN_NONBLOCK).unwrap();
+ let tempdir = tempfile::tempdir().unwrap();
+
+ instance
+ .add_watch(tempdir.path(), AddWatchFlags::IN_ALL_EVENTS)
+ .unwrap();
+
+ let events = instance.read_events();
+ assert_eq!(events.unwrap_err(), Errno::EAGAIN);
+
+ File::create(tempdir.path().join("test")).unwrap();
+ rename(tempdir.path().join("test"), tempdir.path().join("test2")).unwrap();
+
+ // Now there should be 5 events in queue:
+ // - IN_CREATE on test
+ // - IN_OPEN on test
+ // - IN_CLOSE_WRITE on test
+ // - IN_MOVED_FROM on test with a cookie
+ // - IN_MOVED_TO on test2 with the same cookie
+
+ let events = instance.read_events().unwrap();
+ assert_eq!(events.len(), 5);
+
+ assert_eq!(events[0].mask, AddWatchFlags::IN_CREATE);
+ assert_eq!(events[0].name, Some(OsString::from("test")));
+
+ assert_eq!(events[1].mask, AddWatchFlags::IN_OPEN);
+ assert_eq!(events[1].name, Some(OsString::from("test")));
+
+ assert_eq!(events[2].mask, AddWatchFlags::IN_CLOSE_WRITE);
+ assert_eq!(events[2].name, Some(OsString::from("test")));
+
+ assert_eq!(events[3].mask, AddWatchFlags::IN_MOVED_FROM);
+ assert_eq!(events[3].name, Some(OsString::from("test")));
+
+ assert_eq!(events[4].mask, AddWatchFlags::IN_MOVED_TO);
+ assert_eq!(events[4].name, Some(OsString::from("test2")));
+
+ assert_eq!(events[3].cookie, events[4].cookie);
+}
diff --git a/third_party/rust/nix/test/sys/test_ioctl.rs b/third_party/rust/nix/test/sys/test_ioctl.rs
new file mode 100644
index 0000000000..40f60cfdbc
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_ioctl.rs
@@ -0,0 +1,376 @@
+#![allow(dead_code)]
+
+// Simple tests to ensure macro generated fns compile
+ioctl_none_bad!(do_bad, 0x1234);
+ioctl_read_bad!(do_bad_read, 0x1234, u16);
+ioctl_write_int_bad!(do_bad_write_int, 0x1234);
+ioctl_write_ptr_bad!(do_bad_write_ptr, 0x1234, u8);
+ioctl_readwrite_bad!(do_bad_readwrite, 0x1234, u32);
+ioctl_none!(do_none, 0, 0);
+ioctl_read!(read_test, 0, 0, u32);
+ioctl_write_int!(write_ptr_int, 0, 0);
+ioctl_write_ptr!(write_ptr_u8, 0, 0, u8);
+ioctl_write_ptr!(write_ptr_u32, 0, 0, u32);
+ioctl_write_ptr!(write_ptr_u64, 0, 0, u64);
+ioctl_readwrite!(readwrite_test, 0, 0, u64);
+ioctl_read_buf!(readbuf_test, 0, 0, u32);
+const SPI_IOC_MAGIC: u8 = b'k';
+const SPI_IOC_MESSAGE: u8 = 0;
+ioctl_write_buf!(writebuf_test_consts, SPI_IOC_MAGIC, SPI_IOC_MESSAGE, u8);
+ioctl_write_buf!(writebuf_test_u8, 0, 0, u8);
+ioctl_write_buf!(writebuf_test_u32, 0, 0, u32);
+ioctl_write_buf!(writebuf_test_u64, 0, 0, u64);
+ioctl_readwrite_buf!(readwritebuf_test, 0, 0, u32);
+
+// See C code for source of values for op calculations (does NOT work for mips/powerpc):
+// https://gist.github.com/posborne/83ea6880770a1aef332e
+//
+// TODO: Need a way to compute these constants at test time. Using precomputed
+// values is fragile and needs to be maintained.
+
+#[cfg(any(target_os = "linux", target_os = "android"))]
+mod linux {
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ #[test]
+ fn test_op_none() {
+ if cfg!(any(
+ target_arch = "mips",
+ target_arch = "mips64",
+ target_arch = "powerpc",
+ target_arch = "powerpc64"
+ )) {
+ assert_eq!(request_code_none!(b'q', 10) as u32, 0x2000_710A);
+ assert_eq!(request_code_none!(b'a', 255) as u32, 0x2000_61FF);
+ } else {
+ assert_eq!(request_code_none!(b'q', 10) as u32, 0x0000_710A);
+ assert_eq!(request_code_none!(b'a', 255) as u32, 0x0000_61FF);
+ }
+ }
+
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ #[test]
+ fn test_op_write() {
+ if cfg!(any(
+ target_arch = "mips",
+ target_arch = "mips64",
+ target_arch = "powerpc",
+ target_arch = "powerpc64"
+ )) {
+ assert_eq!(request_code_write!(b'z', 10, 1) as u32, 0x8001_7A0A);
+ assert_eq!(request_code_write!(b'z', 10, 512) as u32, 0x8200_7A0A);
+ } else {
+ assert_eq!(request_code_write!(b'z', 10, 1) as u32, 0x4001_7A0A);
+ assert_eq!(request_code_write!(b'z', 10, 512) as u32, 0x4200_7A0A);
+ }
+ }
+
+ #[cfg(target_pointer_width = "64")]
+ #[test]
+ fn test_op_write_64() {
+ if cfg!(any(target_arch = "mips64", target_arch = "powerpc64")) {
+ assert_eq!(
+ request_code_write!(b'z', 10, 1u64 << 32) as u32,
+ 0x8000_7A0A
+ );
+ } else {
+ assert_eq!(
+ request_code_write!(b'z', 10, 1u64 << 32) as u32,
+ 0x4000_7A0A
+ );
+ }
+ }
+
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ #[test]
+ fn test_op_read() {
+ if cfg!(any(
+ target_arch = "mips",
+ target_arch = "mips64",
+ target_arch = "powerpc",
+ target_arch = "powerpc64"
+ )) {
+ assert_eq!(request_code_read!(b'z', 10, 1) as u32, 0x4001_7A0A);
+ assert_eq!(request_code_read!(b'z', 10, 512) as u32, 0x4200_7A0A);
+ } else {
+ assert_eq!(request_code_read!(b'z', 10, 1) as u32, 0x8001_7A0A);
+ assert_eq!(request_code_read!(b'z', 10, 512) as u32, 0x8200_7A0A);
+ }
+ }
+
+ #[cfg(target_pointer_width = "64")]
+ #[test]
+ fn test_op_read_64() {
+ if cfg!(any(target_arch = "mips64", target_arch = "powerpc64")) {
+ assert_eq!(
+ request_code_read!(b'z', 10, 1u64 << 32) as u32,
+ 0x4000_7A0A
+ );
+ } else {
+ assert_eq!(
+ request_code_read!(b'z', 10, 1u64 << 32) as u32,
+ 0x8000_7A0A
+ );
+ }
+ }
+
+ // The cast is not unnecessary on all platforms.
+ #[allow(clippy::unnecessary_cast)]
+ #[test]
+ fn test_op_read_write() {
+ assert_eq!(request_code_readwrite!(b'z', 10, 1) as u32, 0xC001_7A0A);
+ assert_eq!(request_code_readwrite!(b'z', 10, 512) as u32, 0xC200_7A0A);
+ }
+
+ #[cfg(target_pointer_width = "64")]
+ #[test]
+ fn test_op_read_write_64() {
+ assert_eq!(
+ request_code_readwrite!(b'z', 10, 1u64 << 32) as u32,
+ 0xC000_7A0A
+ );
+ }
+}
+
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd"
+))]
+mod bsd {
+ #[test]
+ fn test_op_none() {
+ assert_eq!(request_code_none!(b'q', 10), 0x2000_710A);
+ assert_eq!(request_code_none!(b'a', 255), 0x2000_61FF);
+ }
+
+ #[cfg(any(target_os = "dragonfly", target_os = "freebsd"))]
+ #[test]
+ fn test_op_write_int() {
+ assert_eq!(request_code_write_int!(b'v', 4), 0x2004_7604);
+ assert_eq!(request_code_write_int!(b'p', 2), 0x2004_7002);
+ }
+
+ #[test]
+ fn test_op_write() {
+ assert_eq!(request_code_write!(b'z', 10, 1), 0x8001_7A0A);
+ assert_eq!(request_code_write!(b'z', 10, 512), 0x8200_7A0A);
+ }
+
+ #[cfg(target_pointer_width = "64")]
+ #[test]
+ fn test_op_write_64() {
+ assert_eq!(request_code_write!(b'z', 10, 1u64 << 32), 0x8000_7A0A);
+ }
+
+ #[test]
+ fn test_op_read() {
+ assert_eq!(request_code_read!(b'z', 10, 1), 0x4001_7A0A);
+ assert_eq!(request_code_read!(b'z', 10, 512), 0x4200_7A0A);
+ }
+
+ #[cfg(target_pointer_width = "64")]
+ #[test]
+ fn test_op_read_64() {
+ assert_eq!(request_code_read!(b'z', 10, 1u64 << 32), 0x4000_7A0A);
+ }
+
+ #[test]
+ fn test_op_read_write() {
+ assert_eq!(request_code_readwrite!(b'z', 10, 1), 0xC001_7A0A);
+ assert_eq!(request_code_readwrite!(b'z', 10, 512), 0xC200_7A0A);
+ }
+
+ #[cfg(target_pointer_width = "64")]
+ #[test]
+ fn test_op_read_write_64() {
+ assert_eq!(request_code_readwrite!(b'z', 10, 1u64 << 32), 0xC000_7A0A);
+ }
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+mod linux_ioctls {
+ use std::mem;
+ use std::os::unix::io::AsRawFd;
+
+ use libc::{termios, TCGETS, TCSBRK, TCSETS, TIOCNXCL};
+ use tempfile::tempfile;
+
+ use nix::errno::Errno;
+
+ ioctl_none_bad!(tiocnxcl, TIOCNXCL);
+ #[test]
+ fn test_ioctl_none_bad() {
+ let file = tempfile().unwrap();
+ let res = unsafe { tiocnxcl(file.as_raw_fd()) };
+ assert_eq!(res, Err(Errno::ENOTTY));
+ }
+
+ ioctl_read_bad!(tcgets, TCGETS, termios);
+ #[test]
+ fn test_ioctl_read_bad() {
+ let file = tempfile().unwrap();
+ let mut termios = unsafe { mem::zeroed() };
+ let res = unsafe { tcgets(file.as_raw_fd(), &mut termios) };
+ assert_eq!(res, Err(Errno::ENOTTY));
+ }
+
+ ioctl_write_int_bad!(tcsbrk, TCSBRK);
+ #[test]
+ fn test_ioctl_write_int_bad() {
+ let file = tempfile().unwrap();
+ let res = unsafe { tcsbrk(file.as_raw_fd(), 0) };
+ assert_eq!(res, Err(Errno::ENOTTY));
+ }
+
+ ioctl_write_ptr_bad!(tcsets, TCSETS, termios);
+ #[test]
+ fn test_ioctl_write_ptr_bad() {
+ let file = tempfile().unwrap();
+ let termios: termios = unsafe { mem::zeroed() };
+ let res = unsafe { tcsets(file.as_raw_fd(), &termios) };
+ assert_eq!(res, Err(Errno::ENOTTY));
+ }
+
+ // FIXME: Find a suitable example for `ioctl_readwrite_bad`
+
+ // From linux/videodev2.h
+ ioctl_none!(log_status, b'V', 70);
+ #[test]
+ fn test_ioctl_none() {
+ let file = tempfile().unwrap();
+ let res = unsafe { log_status(file.as_raw_fd()) };
+ assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
+ }
+
+ #[repr(C)]
+ pub struct v4l2_audio {
+ index: u32,
+ name: [u8; 32],
+ capability: u32,
+ mode: u32,
+ reserved: [u32; 2],
+ }
+
+ // From linux/videodev2.h
+ ioctl_write_ptr!(s_audio, b'V', 34, v4l2_audio);
+ #[test]
+ fn test_ioctl_write_ptr() {
+ let file = tempfile().unwrap();
+ let data: v4l2_audio = unsafe { mem::zeroed() };
+ let res = unsafe { s_audio(file.as_raw_fd(), &data) };
+ assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
+ }
+
+ // From linux/net/bluetooth/hci_sock.h
+ const HCI_IOC_MAGIC: u8 = b'H';
+ const HCI_IOC_HCIDEVUP: u8 = 201;
+ ioctl_write_int!(hcidevup, HCI_IOC_MAGIC, HCI_IOC_HCIDEVUP);
+ #[test]
+ fn test_ioctl_write_int() {
+ let file = tempfile().unwrap();
+ let res = unsafe { hcidevup(file.as_raw_fd(), 0) };
+ assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
+ }
+
+ // From linux/videodev2.h
+ ioctl_read!(g_audio, b'V', 33, v4l2_audio);
+ #[test]
+ fn test_ioctl_read() {
+ let file = tempfile().unwrap();
+ let mut data: v4l2_audio = unsafe { mem::zeroed() };
+ let res = unsafe { g_audio(file.as_raw_fd(), &mut data) };
+ assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
+ }
+
+ // From linux/videodev2.h
+ ioctl_readwrite!(enum_audio, b'V', 65, v4l2_audio);
+ #[test]
+ fn test_ioctl_readwrite() {
+ let file = tempfile().unwrap();
+ let mut data: v4l2_audio = unsafe { mem::zeroed() };
+ let res = unsafe { enum_audio(file.as_raw_fd(), &mut data) };
+ assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
+ }
+
+ // FIXME: Find a suitable example for `ioctl_read_buf`.
+
+ #[repr(C)]
+ pub struct spi_ioc_transfer {
+ tx_buf: u64,
+ rx_buf: u64,
+ len: u32,
+ speed_hz: u32,
+ delay_usecs: u16,
+ bits_per_word: u8,
+ cs_change: u8,
+ tx_nbits: u8,
+ rx_nbits: u8,
+ pad: u16,
+ }
+
+ // From linux/spi/spidev.h
+ ioctl_write_buf!(
+ spi_ioc_message,
+ super::SPI_IOC_MAGIC,
+ super::SPI_IOC_MESSAGE,
+ spi_ioc_transfer
+ );
+ #[test]
+ fn test_ioctl_write_buf() {
+ let file = tempfile().unwrap();
+ let data: [spi_ioc_transfer; 4] = unsafe { mem::zeroed() };
+ let res = unsafe { spi_ioc_message(file.as_raw_fd(), &data[..]) };
+ assert!(res == Err(Errno::ENOTTY) || res == Err(Errno::ENOSYS));
+ }
+
+ // FIXME: Find a suitable example for `ioctl_readwrite_buf`.
+}
+
+#[cfg(target_os = "freebsd")]
+mod freebsd_ioctls {
+ use std::mem;
+ use std::os::unix::io::AsRawFd;
+
+ use libc::termios;
+ use tempfile::tempfile;
+
+ use nix::errno::Errno;
+
+ // From sys/sys/ttycom.h
+ const TTY_IOC_MAGIC: u8 = b't';
+ const TTY_IOC_TYPE_NXCL: u8 = 14;
+ const TTY_IOC_TYPE_GETA: u8 = 19;
+ const TTY_IOC_TYPE_SETA: u8 = 20;
+
+ ioctl_none!(tiocnxcl, TTY_IOC_MAGIC, TTY_IOC_TYPE_NXCL);
+ #[test]
+ fn test_ioctl_none() {
+ let file = tempfile().unwrap();
+ let res = unsafe { tiocnxcl(file.as_raw_fd()) };
+ assert_eq!(res, Err(Errno::ENOTTY));
+ }
+
+ ioctl_read!(tiocgeta, TTY_IOC_MAGIC, TTY_IOC_TYPE_GETA, termios);
+ #[test]
+ fn test_ioctl_read() {
+ let file = tempfile().unwrap();
+ let mut termios = unsafe { mem::zeroed() };
+ let res = unsafe { tiocgeta(file.as_raw_fd(), &mut termios) };
+ assert_eq!(res, Err(Errno::ENOTTY));
+ }
+
+ ioctl_write_ptr!(tiocseta, TTY_IOC_MAGIC, TTY_IOC_TYPE_SETA, termios);
+ #[test]
+ fn test_ioctl_write_ptr() {
+ let file = tempfile().unwrap();
+ let termios: termios = unsafe { mem::zeroed() };
+ let res = unsafe { tiocseta(file.as_raw_fd(), &termios) };
+ assert_eq!(res, Err(Errno::ENOTTY));
+ }
+}
diff --git a/third_party/rust/nix/test/sys/test_mman.rs b/third_party/rust/nix/test/sys/test_mman.rs
new file mode 100644
index 0000000000..b4674e53fa
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_mman.rs
@@ -0,0 +1,122 @@
+use nix::sys::mman::{mmap, MapFlags, ProtFlags};
+use std::{num::NonZeroUsize, os::unix::io::BorrowedFd};
+
+#[test]
+fn test_mmap_anonymous() {
+ unsafe {
+ let ptr = mmap::<BorrowedFd>(
+ None,
+ NonZeroUsize::new(1).unwrap(),
+ ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
+ MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS,
+ None,
+ 0,
+ )
+ .unwrap() as *mut u8;
+ assert_eq!(*ptr, 0x00u8);
+ *ptr = 0xffu8;
+ assert_eq!(*ptr, 0xffu8);
+ }
+}
+
+#[test]
+#[cfg(any(target_os = "linux", target_os = "netbsd"))]
+fn test_mremap_grow() {
+ use nix::libc::{c_void, size_t};
+ use nix::sys::mman::{mremap, MRemapFlags};
+
+ const ONE_K: size_t = 1024;
+ let one_k_non_zero = NonZeroUsize::new(ONE_K).unwrap();
+
+ let slice: &mut [u8] = unsafe {
+ let mem = mmap::<BorrowedFd>(
+ None,
+ one_k_non_zero,
+ ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
+ MapFlags::MAP_ANONYMOUS | MapFlags::MAP_PRIVATE,
+ None,
+ 0,
+ )
+ .unwrap();
+ std::slice::from_raw_parts_mut(mem as *mut u8, ONE_K)
+ };
+ assert_eq!(slice[ONE_K - 1], 0x00);
+ slice[ONE_K - 1] = 0xFF;
+ assert_eq!(slice[ONE_K - 1], 0xFF);
+
+ let slice: &mut [u8] = unsafe {
+ #[cfg(target_os = "linux")]
+ let mem = mremap(
+ slice.as_mut_ptr() as *mut c_void,
+ ONE_K,
+ 10 * ONE_K,
+ MRemapFlags::MREMAP_MAYMOVE,
+ None,
+ )
+ .unwrap();
+ #[cfg(target_os = "netbsd")]
+ let mem = mremap(
+ slice.as_mut_ptr() as *mut c_void,
+ ONE_K,
+ 10 * ONE_K,
+ MRemapFlags::MAP_REMAPDUP,
+ None,
+ )
+ .unwrap();
+ std::slice::from_raw_parts_mut(mem as *mut u8, 10 * ONE_K)
+ };
+
+ // The first KB should still have the old data in it.
+ assert_eq!(slice[ONE_K - 1], 0xFF);
+
+ // The additional range should be zero-init'd and accessible.
+ assert_eq!(slice[10 * ONE_K - 1], 0x00);
+ slice[10 * ONE_K - 1] = 0xFF;
+ assert_eq!(slice[10 * ONE_K - 1], 0xFF);
+}
+
+#[test]
+#[cfg(any(target_os = "linux", target_os = "netbsd"))]
+// Segfaults for unknown reasons under QEMU for 32-bit targets
+#[cfg_attr(all(target_pointer_width = "32", qemu), ignore)]
+fn test_mremap_shrink() {
+ use nix::libc::{c_void, size_t};
+ use nix::sys::mman::{mremap, MRemapFlags};
+ use std::num::NonZeroUsize;
+
+ const ONE_K: size_t = 1024;
+ let ten_one_k = NonZeroUsize::new(10 * ONE_K).unwrap();
+ let slice: &mut [u8] = unsafe {
+ let mem = mmap::<BorrowedFd>(
+ None,
+ ten_one_k,
+ ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
+ MapFlags::MAP_ANONYMOUS | MapFlags::MAP_PRIVATE,
+ None,
+ 0,
+ )
+ .unwrap();
+ std::slice::from_raw_parts_mut(mem as *mut u8, ONE_K)
+ };
+ assert_eq!(slice[ONE_K - 1], 0x00);
+ slice[ONE_K - 1] = 0xFF;
+ assert_eq!(slice[ONE_K - 1], 0xFF);
+
+ let slice: &mut [u8] = unsafe {
+ let mem = mremap(
+ slice.as_mut_ptr() as *mut c_void,
+ ten_one_k.into(),
+ ONE_K,
+ MRemapFlags::empty(),
+ None,
+ )
+ .unwrap();
+ // Since we didn't supply MREMAP_MAYMOVE, the address should be the
+ // same.
+ assert_eq!(mem, slice.as_mut_ptr() as *mut c_void);
+ std::slice::from_raw_parts_mut(mem as *mut u8, ONE_K)
+ };
+
+ // The first KB should still be accessible and have the old data in it.
+ assert_eq!(slice[ONE_K - 1], 0xFF);
+}
diff --git a/third_party/rust/nix/test/sys/test_prctl.rs b/third_party/rust/nix/test/sys/test_prctl.rs
new file mode 100644
index 0000000000..351213b7ef
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_prctl.rs
@@ -0,0 +1,125 @@
+#[cfg(target_os = "linux")]
+#[cfg(feature = "process")]
+mod test_prctl {
+ use std::ffi::CStr;
+
+ use nix::sys::prctl;
+
+ #[cfg_attr(qemu, ignore)]
+ #[test]
+ fn test_get_set_subreaper() {
+ let original = prctl::get_child_subreaper().unwrap();
+
+ prctl::set_child_subreaper(true).unwrap();
+ let subreaper = prctl::get_child_subreaper().unwrap();
+ assert!(subreaper);
+
+ prctl::set_child_subreaper(original).unwrap();
+ }
+
+ #[test]
+ fn test_get_set_dumpable() {
+ let original = prctl::get_dumpable().unwrap();
+
+ prctl::set_dumpable(false).unwrap();
+ let dumpable = prctl::get_dumpable().unwrap();
+ assert!(!dumpable);
+
+ prctl::set_dumpable(original).unwrap();
+ }
+
+ #[test]
+ fn test_get_set_keepcaps() {
+ let original = prctl::get_keepcaps().unwrap();
+
+ prctl::set_keepcaps(true).unwrap();
+ let keepcaps = prctl::get_keepcaps().unwrap();
+ assert!(keepcaps);
+
+ prctl::set_keepcaps(original).unwrap();
+ }
+
+ #[test]
+ fn test_get_set_clear_mce_kill() {
+ use prctl::PrctlMCEKillPolicy::*;
+
+ prctl::set_mce_kill(PR_MCE_KILL_LATE).unwrap();
+ let mce = prctl::get_mce_kill().unwrap();
+ assert_eq!(mce, PR_MCE_KILL_LATE);
+
+ prctl::clear_mce_kill().unwrap();
+ let mce = prctl::get_mce_kill().unwrap();
+ assert_eq!(mce, PR_MCE_KILL_DEFAULT);
+ }
+
+ #[cfg_attr(qemu, ignore)]
+ #[test]
+ fn test_get_set_pdeathsig() {
+ use nix::sys::signal::Signal;
+
+ let original = prctl::get_pdeathsig().unwrap();
+
+ prctl::set_pdeathsig(Signal::SIGUSR1).unwrap();
+ let sig = prctl::get_pdeathsig().unwrap();
+ assert_eq!(sig, Some(Signal::SIGUSR1));
+
+ prctl::set_pdeathsig(original).unwrap();
+ }
+
+ #[test]
+ fn test_get_set_name() {
+ let original = prctl::get_name().unwrap();
+
+ let long_name =
+ CStr::from_bytes_with_nul(b"0123456789abcdefghijklmn\0").unwrap();
+ prctl::set_name(long_name).unwrap();
+ let res = prctl::get_name().unwrap();
+
+ // name truncated by kernel to TASK_COMM_LEN
+ assert_eq!(&long_name.to_str().unwrap()[..15], res.to_str().unwrap());
+
+ let short_name = CStr::from_bytes_with_nul(b"01234567\0").unwrap();
+ prctl::set_name(short_name).unwrap();
+ let res = prctl::get_name().unwrap();
+ assert_eq!(short_name.to_str().unwrap(), res.to_str().unwrap());
+
+ prctl::set_name(&original).unwrap();
+ }
+
+ #[cfg_attr(qemu, ignore)]
+ #[test]
+ fn test_get_set_timerslack() {
+ let original = prctl::get_timerslack().unwrap();
+
+ let slack = 60_000;
+ prctl::set_timerslack(slack).unwrap();
+ let res = prctl::get_timerslack().unwrap();
+ assert_eq!(slack, res as u64);
+
+ prctl::set_timerslack(original as u64).unwrap();
+ }
+
+ #[test]
+ fn test_disable_enable_perf_events() {
+ prctl::task_perf_events_disable().unwrap();
+ prctl::task_perf_events_enable().unwrap();
+ }
+
+ #[test]
+ fn test_get_set_no_new_privs() {
+ prctl::set_no_new_privs().unwrap();
+ let no_new_privs = prctl::get_no_new_privs().unwrap();
+ assert!(no_new_privs);
+ }
+
+ #[test]
+ fn test_get_set_thp_disable() {
+ let original = prctl::get_thp_disable().unwrap();
+
+ prctl::set_thp_disable(true).unwrap();
+ let thp_disable = prctl::get_thp_disable().unwrap();
+ assert!(thp_disable);
+
+ prctl::set_thp_disable(original).unwrap();
+ }
+}
diff --git a/third_party/rust/nix/test/sys/test_pthread.rs b/third_party/rust/nix/test/sys/test_pthread.rs
new file mode 100644
index 0000000000..ce048bae60
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_pthread.rs
@@ -0,0 +1,22 @@
+use nix::sys::pthread::*;
+
+#[cfg(any(target_env = "musl", target_os = "redox"))]
+#[test]
+fn test_pthread_self() {
+ let tid = pthread_self();
+ assert!(!tid.is_null());
+}
+
+#[cfg(not(any(target_env = "musl", target_os = "redox")))]
+#[test]
+fn test_pthread_self() {
+ let tid = pthread_self();
+ assert_ne!(tid, 0);
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_pthread_kill_none() {
+ pthread_kill(pthread_self(), None)
+ .expect("Should be able to send signal to my thread.");
+}
diff --git a/third_party/rust/nix/test/sys/test_ptrace.rs b/third_party/rust/nix/test/sys/test_ptrace.rs
new file mode 100644
index 0000000000..530560fe17
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_ptrace.rs
@@ -0,0 +1,275 @@
+#[cfg(all(
+ target_os = "linux",
+ any(target_arch = "x86_64", target_arch = "x86"),
+ target_env = "gnu"
+))]
+use memoffset::offset_of;
+use nix::errno::Errno;
+use nix::sys::ptrace;
+#[cfg(any(target_os = "android", target_os = "linux"))]
+use nix::sys::ptrace::Options;
+use nix::unistd::getpid;
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+use std::mem;
+
+use crate::*;
+
+#[test]
+fn test_ptrace() {
+ // Just make sure ptrace can be called at all, for now.
+ // FIXME: qemu-user doesn't implement ptrace on all arches, so permit ENOSYS
+ require_capability!("test_ptrace", CAP_SYS_PTRACE);
+ let err = ptrace::attach(getpid()).unwrap_err();
+ assert!(
+ err == Errno::EPERM || err == Errno::EINVAL || err == Errno::ENOSYS
+ );
+}
+
+// Just make sure ptrace_setoptions can be called at all, for now.
+#[test]
+#[cfg(any(target_os = "android", target_os = "linux"))]
+fn test_ptrace_setoptions() {
+ require_capability!("test_ptrace_setoptions", CAP_SYS_PTRACE);
+ let err = ptrace::setoptions(getpid(), Options::PTRACE_O_TRACESYSGOOD)
+ .unwrap_err();
+ assert_ne!(err, Errno::EOPNOTSUPP);
+}
+
+// Just make sure ptrace_getevent can be called at all, for now.
+#[test]
+#[cfg(any(target_os = "android", target_os = "linux"))]
+fn test_ptrace_getevent() {
+ require_capability!("test_ptrace_getevent", CAP_SYS_PTRACE);
+ let err = ptrace::getevent(getpid()).unwrap_err();
+ assert_ne!(err, Errno::EOPNOTSUPP);
+}
+
+// Just make sure ptrace_getsiginfo can be called at all, for now.
+#[test]
+#[cfg(any(target_os = "android", target_os = "linux"))]
+fn test_ptrace_getsiginfo() {
+ require_capability!("test_ptrace_getsiginfo", CAP_SYS_PTRACE);
+ if let Err(Errno::EOPNOTSUPP) = ptrace::getsiginfo(getpid()) {
+ panic!("ptrace_getsiginfo returns Errno::EOPNOTSUPP!");
+ }
+}
+
+// Just make sure ptrace_setsiginfo can be called at all, for now.
+#[test]
+#[cfg(any(target_os = "android", target_os = "linux"))]
+fn test_ptrace_setsiginfo() {
+ require_capability!("test_ptrace_setsiginfo", CAP_SYS_PTRACE);
+ let siginfo = unsafe { mem::zeroed() };
+ if let Err(Errno::EOPNOTSUPP) = ptrace::setsiginfo(getpid(), &siginfo) {
+ panic!("ptrace_setsiginfo returns Errno::EOPNOTSUPP!");
+ }
+}
+
+#[test]
+fn test_ptrace_cont() {
+ use nix::sys::ptrace;
+ use nix::sys::signal::{raise, Signal};
+ use nix::sys::wait::{waitpid, WaitPidFlag, WaitStatus};
+ use nix::unistd::fork;
+ use nix::unistd::ForkResult::*;
+
+ require_capability!("test_ptrace_cont", CAP_SYS_PTRACE);
+
+ let _m = crate::FORK_MTX.lock();
+
+ // FIXME: qemu-user doesn't implement ptrace on all architectures
+ // and returns ENOSYS in this case.
+ // We (ab)use this behavior to detect the affected platforms
+ // and skip the test then.
+ // On valid platforms the ptrace call should return Errno::EPERM, this
+ // is already tested by `test_ptrace`.
+ let err = ptrace::attach(getpid()).unwrap_err();
+ if err == Errno::ENOSYS {
+ return;
+ }
+
+ match unsafe { fork() }.expect("Error: Fork Failed") {
+ Child => {
+ ptrace::traceme().unwrap();
+ // As recommended by ptrace(2), raise SIGTRAP to pause the child
+ // until the parent is ready to continue
+ loop {
+ raise(Signal::SIGTRAP).unwrap();
+ }
+ }
+ Parent { child } => {
+ assert_eq!(
+ waitpid(child, None),
+ Ok(WaitStatus::Stopped(child, Signal::SIGTRAP))
+ );
+ ptrace::cont(child, None).unwrap();
+ assert_eq!(
+ waitpid(child, None),
+ Ok(WaitStatus::Stopped(child, Signal::SIGTRAP))
+ );
+ ptrace::cont(child, Some(Signal::SIGKILL)).unwrap();
+ match waitpid(child, None) {
+ Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _))
+ if pid == child =>
+ {
+ // FIXME It's been observed on some systems (apple) the
+ // tracee may not be killed but remain as a zombie process
+ // affecting other wait based tests. Add an extra kill just
+ // to make sure there are no zombies.
+ let _ = waitpid(child, Some(WaitPidFlag::WNOHANG));
+ while ptrace::cont(child, Some(Signal::SIGKILL)).is_ok() {
+ let _ = waitpid(child, Some(WaitPidFlag::WNOHANG));
+ }
+ }
+ _ => panic!("The process should have been killed"),
+ }
+ }
+ }
+}
+
+#[cfg(target_os = "linux")]
+#[test]
+fn test_ptrace_interrupt() {
+ use nix::sys::ptrace;
+ use nix::sys::signal::Signal;
+ use nix::sys::wait::{waitpid, WaitPidFlag, WaitStatus};
+ use nix::unistd::fork;
+ use nix::unistd::ForkResult::*;
+ use std::thread::sleep;
+ use std::time::Duration;
+
+ require_capability!("test_ptrace_interrupt", CAP_SYS_PTRACE);
+
+ let _m = crate::FORK_MTX.lock();
+
+ match unsafe { fork() }.expect("Error: Fork Failed") {
+ Child => loop {
+ sleep(Duration::from_millis(1000));
+ },
+ Parent { child } => {
+ ptrace::seize(child, ptrace::Options::PTRACE_O_TRACESYSGOOD)
+ .unwrap();
+ ptrace::interrupt(child).unwrap();
+ assert_eq!(
+ waitpid(child, None),
+ Ok(WaitStatus::PtraceEvent(child, Signal::SIGTRAP, 128))
+ );
+ ptrace::syscall(child, None).unwrap();
+ assert_eq!(
+ waitpid(child, None),
+ Ok(WaitStatus::PtraceSyscall(child))
+ );
+ ptrace::detach(child, Some(Signal::SIGKILL)).unwrap();
+ match waitpid(child, None) {
+ Ok(WaitStatus::Signaled(pid, Signal::SIGKILL, _))
+ if pid == child =>
+ {
+ let _ = waitpid(child, Some(WaitPidFlag::WNOHANG));
+ while ptrace::cont(child, Some(Signal::SIGKILL)).is_ok() {
+ let _ = waitpid(child, Some(WaitPidFlag::WNOHANG));
+ }
+ }
+ _ => panic!("The process should have been killed"),
+ }
+ }
+ }
+}
+
+// ptrace::{setoptions, getregs} are only available in these platforms
+#[cfg(all(
+ target_os = "linux",
+ any(target_arch = "x86_64", target_arch = "x86"),
+ target_env = "gnu"
+))]
+#[test]
+fn test_ptrace_syscall() {
+ use nix::sys::ptrace;
+ use nix::sys::signal::kill;
+ use nix::sys::signal::Signal;
+ use nix::sys::wait::{waitpid, WaitStatus};
+ use nix::unistd::fork;
+ use nix::unistd::getpid;
+ use nix::unistd::ForkResult::*;
+
+ require_capability!("test_ptrace_syscall", CAP_SYS_PTRACE);
+
+ let _m = crate::FORK_MTX.lock();
+
+ match unsafe { fork() }.expect("Error: Fork Failed") {
+ Child => {
+ ptrace::traceme().unwrap();
+ // first sigstop until parent is ready to continue
+ let pid = getpid();
+ kill(pid, Signal::SIGSTOP).unwrap();
+ kill(pid, Signal::SIGTERM).unwrap();
+ unsafe {
+ ::libc::_exit(0);
+ }
+ }
+
+ Parent { child } => {
+ assert_eq!(
+ waitpid(child, None),
+ Ok(WaitStatus::Stopped(child, Signal::SIGSTOP))
+ );
+
+ // set this option to recognize syscall-stops
+ ptrace::setoptions(child, ptrace::Options::PTRACE_O_TRACESYSGOOD)
+ .unwrap();
+
+ #[cfg(target_arch = "x86_64")]
+ let get_syscall_id =
+ || ptrace::getregs(child).unwrap().orig_rax as libc::c_long;
+
+ #[cfg(target_arch = "x86")]
+ let get_syscall_id =
+ || ptrace::getregs(child).unwrap().orig_eax as libc::c_long;
+
+ // this duplicates `get_syscall_id` for the purpose of testing `ptrace::read_user`.
+ #[cfg(target_arch = "x86_64")]
+ let rax_offset = offset_of!(libc::user_regs_struct, orig_rax);
+ #[cfg(target_arch = "x86")]
+ let rax_offset = offset_of!(libc::user_regs_struct, orig_eax);
+
+ let get_syscall_from_user_area = || {
+ // Find the offset of `user.regs.rax` (or `user.regs.eax` for x86)
+ let rax_offset = offset_of!(libc::user, regs) + rax_offset;
+ ptrace::read_user(child, rax_offset as _).unwrap()
+ as libc::c_long
+ };
+
+ // kill entry
+ ptrace::syscall(child, None).unwrap();
+ assert_eq!(
+ waitpid(child, None),
+ Ok(WaitStatus::PtraceSyscall(child))
+ );
+ assert_eq!(get_syscall_id(), ::libc::SYS_kill);
+ assert_eq!(get_syscall_from_user_area(), ::libc::SYS_kill);
+
+ // kill exit
+ ptrace::syscall(child, None).unwrap();
+ assert_eq!(
+ waitpid(child, None),
+ Ok(WaitStatus::PtraceSyscall(child))
+ );
+ assert_eq!(get_syscall_id(), ::libc::SYS_kill);
+ assert_eq!(get_syscall_from_user_area(), ::libc::SYS_kill);
+
+ // receive signal
+ ptrace::syscall(child, None).unwrap();
+ assert_eq!(
+ waitpid(child, None),
+ Ok(WaitStatus::Stopped(child, Signal::SIGTERM))
+ );
+
+ // inject signal
+ ptrace::syscall(child, Signal::SIGTERM).unwrap();
+ assert_eq!(
+ waitpid(child, None),
+ Ok(WaitStatus::Signaled(child, Signal::SIGTERM, false))
+ );
+ }
+ }
+}
diff --git a/third_party/rust/nix/test/sys/test_select.rs b/third_party/rust/nix/test/sys/test_select.rs
new file mode 100644
index 0000000000..79f75de3b4
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_select.rs
@@ -0,0 +1,82 @@
+use nix::sys::select::*;
+use nix::sys::signal::SigSet;
+use nix::sys::time::{TimeSpec, TimeValLike};
+use nix::unistd::{pipe, write};
+use std::os::unix::io::{AsRawFd, BorrowedFd, FromRawFd, OwnedFd};
+
+#[test]
+pub fn test_pselect() {
+ let _mtx = crate::SIGNAL_MTX.lock();
+
+ let (r1, w1) = pipe().unwrap();
+ write(w1, b"hi!").unwrap();
+ let r1 = unsafe { OwnedFd::from_raw_fd(r1) };
+ let (r2, _w2) = pipe().unwrap();
+ let r2 = unsafe { OwnedFd::from_raw_fd(r2) };
+
+ let mut fd_set = FdSet::new();
+ fd_set.insert(&r1);
+ fd_set.insert(&r2);
+
+ let timeout = TimeSpec::seconds(10);
+ let sigmask = SigSet::empty();
+ assert_eq!(
+ 1,
+ pselect(None, &mut fd_set, None, None, &timeout, &sigmask).unwrap()
+ );
+ assert!(fd_set.contains(&r1));
+ assert!(!fd_set.contains(&r2));
+}
+
+#[test]
+pub fn test_pselect_nfds2() {
+ let (r1, w1) = pipe().unwrap();
+ write(w1, b"hi!").unwrap();
+ let r1 = unsafe { OwnedFd::from_raw_fd(r1) };
+ let (r2, _w2) = pipe().unwrap();
+ let r2 = unsafe { OwnedFd::from_raw_fd(r2) };
+
+ let mut fd_set = FdSet::new();
+ fd_set.insert(&r1);
+ fd_set.insert(&r2);
+
+ let timeout = TimeSpec::seconds(10);
+ assert_eq!(
+ 1,
+ pselect(
+ std::cmp::max(r1.as_raw_fd(), r2.as_raw_fd()) + 1,
+ &mut fd_set,
+ None,
+ None,
+ &timeout,
+ None
+ )
+ .unwrap()
+ );
+ assert!(fd_set.contains(&r1));
+ assert!(!fd_set.contains(&r2));
+}
+
+macro_rules! generate_fdset_bad_fd_tests {
+ ($fd:expr, $($method:ident),* $(,)?) => {
+ $(
+ #[test]
+ #[should_panic]
+ fn $method() {
+ let bad_fd = unsafe{BorrowedFd::borrow_raw($fd)};
+ FdSet::new().$method(&bad_fd);
+ }
+ )*
+ }
+}
+
+mod test_fdset_too_large_fd {
+ use super::*;
+ use std::convert::TryInto;
+ generate_fdset_bad_fd_tests!(
+ FD_SETSIZE.try_into().unwrap(),
+ insert,
+ remove,
+ contains,
+ );
+}
diff --git a/third_party/rust/nix/test/sys/test_signal.rs b/third_party/rust/nix/test/sys/test_signal.rs
new file mode 100644
index 0000000000..ca25ff9ab0
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_signal.rs
@@ -0,0 +1,143 @@
+#[cfg(not(target_os = "redox"))]
+use nix::errno::Errno;
+use nix::sys::signal::*;
+use nix::unistd::*;
+use std::convert::TryFrom;
+use std::sync::atomic::{AtomicBool, Ordering};
+
+#[test]
+fn test_kill_none() {
+ kill(getpid(), None).expect("Should be able to send signal to myself.");
+}
+
+#[test]
+#[cfg(not(target_os = "fuchsia"))]
+fn test_killpg_none() {
+ killpg(getpgrp(), None)
+ .expect("Should be able to send signal to my process group.");
+}
+
+#[test]
+fn test_old_sigaction_flags() {
+ let _m = crate::SIGNAL_MTX.lock();
+
+ extern "C" fn handler(_: ::libc::c_int) {}
+ let act = SigAction::new(
+ SigHandler::Handler(handler),
+ SaFlags::empty(),
+ SigSet::empty(),
+ );
+ let oact = unsafe { sigaction(SIGINT, &act) }.unwrap();
+ let _flags = oact.flags();
+ let oact = unsafe { sigaction(SIGINT, &act) }.unwrap();
+ let _flags = oact.flags();
+}
+
+#[test]
+fn test_sigprocmask_noop() {
+ sigprocmask(SigmaskHow::SIG_BLOCK, None, None)
+ .expect("this should be an effective noop");
+}
+
+#[test]
+fn test_sigprocmask() {
+ let _m = crate::SIGNAL_MTX.lock();
+
+ // This needs to be a signal that rust doesn't use in the test harness.
+ const SIGNAL: Signal = Signal::SIGCHLD;
+
+ let mut old_signal_set = SigSet::empty();
+ sigprocmask(SigmaskHow::SIG_BLOCK, None, Some(&mut old_signal_set))
+ .expect("expect to be able to retrieve old signals");
+
+ // Make sure the old set doesn't contain the signal, otherwise the following
+ // test don't make sense.
+ assert!(
+ !old_signal_set.contains(SIGNAL),
+ "the {SIGNAL:?} signal is already blocked, please change to a \
+ different one"
+ );
+
+ // Now block the signal.
+ let mut signal_set = SigSet::empty();
+ signal_set.add(SIGNAL);
+ sigprocmask(SigmaskHow::SIG_BLOCK, Some(&signal_set), None)
+ .expect("expect to be able to block signals");
+
+ // And test it again, to make sure the change was effective.
+ old_signal_set.clear();
+ sigprocmask(SigmaskHow::SIG_BLOCK, None, Some(&mut old_signal_set))
+ .expect("expect to be able to retrieve old signals");
+ assert!(
+ old_signal_set.contains(SIGNAL),
+ "expected the {SIGNAL:?} to be blocked"
+ );
+
+ // Reset the signal.
+ sigprocmask(SigmaskHow::SIG_UNBLOCK, Some(&signal_set), None)
+ .expect("expect to be able to block signals");
+}
+
+static SIGNALED: AtomicBool = AtomicBool::new(false);
+
+extern "C" fn test_sigaction_handler(signal: libc::c_int) {
+ let signal = Signal::try_from(signal).unwrap();
+ SIGNALED.store(signal == Signal::SIGINT, Ordering::Relaxed);
+}
+
+#[cfg(not(target_os = "redox"))]
+extern "C" fn test_sigaction_action(
+ _: libc::c_int,
+ _: *mut libc::siginfo_t,
+ _: *mut libc::c_void,
+) {
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_signal_sigaction() {
+ let _m = crate::SIGNAL_MTX.lock();
+
+ let action_handler = SigHandler::SigAction(test_sigaction_action);
+ assert_eq!(
+ unsafe { signal(Signal::SIGINT, action_handler) }.unwrap_err(),
+ Errno::ENOTSUP
+ );
+}
+
+#[test]
+fn test_signal() {
+ let _m = crate::SIGNAL_MTX.lock();
+
+ unsafe { signal(Signal::SIGINT, SigHandler::SigIgn) }.unwrap();
+ raise(Signal::SIGINT).unwrap();
+ assert_eq!(
+ unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(),
+ SigHandler::SigIgn
+ );
+
+ let handler = SigHandler::Handler(test_sigaction_handler);
+ assert_eq!(
+ unsafe { signal(Signal::SIGINT, handler) }.unwrap(),
+ SigHandler::SigDfl
+ );
+ raise(Signal::SIGINT).unwrap();
+ assert!(SIGNALED.load(Ordering::Relaxed));
+
+ #[cfg(not(any(target_os = "illumos", target_os = "solaris")))]
+ assert_eq!(
+ unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(),
+ handler
+ );
+
+ // System V based OSes (e.g. illumos and Solaris) always resets the
+ // disposition to SIG_DFL prior to calling the signal handler
+ #[cfg(any(target_os = "illumos", target_os = "solaris"))]
+ assert_eq!(
+ unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap(),
+ SigHandler::SigDfl
+ );
+
+ // Restore default signal handler
+ unsafe { signal(Signal::SIGINT, SigHandler::SigDfl) }.unwrap();
+}
diff --git a/third_party/rust/nix/test/sys/test_signalfd.rs b/third_party/rust/nix/test/sys/test_signalfd.rs
new file mode 100644
index 0000000000..87153c9572
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_signalfd.rs
@@ -0,0 +1,27 @@
+use std::convert::TryFrom;
+
+#[test]
+fn test_signalfd() {
+ use nix::sys::signal::{self, raise, SigSet, Signal};
+ use nix::sys::signalfd::SignalFd;
+
+ // Grab the mutex for altering signals so we don't interfere with other tests.
+ let _m = crate::SIGNAL_MTX.lock();
+
+ // Block the SIGUSR1 signal from automatic processing for this thread
+ let mut mask = SigSet::empty();
+ mask.add(signal::SIGUSR1);
+ mask.thread_block().unwrap();
+
+ let mut fd = SignalFd::new(&mask).unwrap();
+
+ // Send a SIGUSR1 signal to the current process. Note that this uses `raise` instead of `kill`
+ // because `kill` with `getpid` isn't correct during multi-threaded execution like during a
+ // cargo test session. Instead use `raise` which does the correct thing by default.
+ raise(signal::SIGUSR1).expect("Error: raise(SIGUSR1) failed");
+
+ // And now catch that same signal.
+ let res = fd.read_signal().unwrap().unwrap();
+ let signo = Signal::try_from(res.ssi_signo as i32).unwrap();
+ assert_eq!(signo, signal::SIGUSR1);
+}
diff --git a/third_party/rust/nix/test/sys/test_socket.rs b/third_party/rust/nix/test/sys/test_socket.rs
new file mode 100644
index 0000000000..ed1686e87d
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_socket.rs
@@ -0,0 +1,2687 @@
+#[cfg(any(target_os = "linux", target_os = "android"))]
+use crate::*;
+use libc::c_char;
+use nix::sys::socket::{getsockname, AddressFamily, UnixAddr};
+use std::collections::hash_map::DefaultHasher;
+use std::hash::{Hash, Hasher};
+use std::net::{SocketAddrV4, SocketAddrV6};
+use std::os::unix::io::{AsRawFd, RawFd};
+use std::path::Path;
+use std::slice;
+use std::str::FromStr;
+
+#[cfg(target_os = "linux")]
+#[cfg_attr(qemu, ignore)]
+#[test]
+pub fn test_timestamping() {
+ use nix::sys::socket::{
+ recvmsg, sendmsg, setsockopt, socket, sockopt::Timestamping,
+ ControlMessageOwned, 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,
+ )
+ .expect("send socket failed");
+
+ let rsock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+ nix::sys::socket::bind(rsock.as_raw_fd(), &sock_addr).unwrap();
+
+ setsockopt(&rsock, Timestamping, &TimestampingFlag::all()).unwrap();
+
+ let sbuf = [0u8; 2048];
+ let mut rbuf = [0u8; 2048];
+ let flags = MsgFlags::empty();
+ let iov1 = [IoSlice::new(&sbuf)];
+ let mut iov2 = [IoSliceMut::new(&mut rbuf)];
+
+ let mut cmsg = cmsg_space!(nix::sys::socket::Timestamps);
+ sendmsg(ssock.as_raw_fd(), &iov1, &[], flags, Some(&sock_addr)).unwrap();
+ let recv =
+ recvmsg::<()>(rsock.as_raw_fd(), &mut iov2, Some(&mut cmsg), flags)
+ .unwrap();
+
+ let mut ts = None;
+ for c in recv.cmsgs() {
+ if let ControlMessageOwned::ScmTimestampsns(timestamps) = c {
+ ts = Some(timestamps.system);
+ }
+ }
+ let ts = ts.expect("ScmTimestampns is present");
+ let sys_time =
+ ::nix::time::clock_gettime(::nix::time::ClockId::CLOCK_REALTIME)
+ .unwrap();
+ let diff = if ts > sys_time {
+ ts - sys_time
+ } else {
+ sys_time - ts
+ };
+ assert!(std::time::Duration::from(diff).as_secs() < 60);
+}
+
+#[test]
+pub fn test_path_to_sock_addr() {
+ let path = "/foo/bar";
+ let actual = Path::new(path);
+ let addr = UnixAddr::new(actual).unwrap();
+
+ let expect: &[c_char] = unsafe {
+ slice::from_raw_parts(path.as_ptr() as *const c_char, path.len())
+ };
+ assert_eq!(unsafe { &(*addr.as_ptr()).sun_path[..8] }, expect);
+
+ assert_eq!(addr.path(), Some(actual));
+}
+
+fn calculate_hash<T: Hash>(t: &T) -> u64 {
+ let mut s = DefaultHasher::new();
+ t.hash(&mut s);
+ s.finish()
+}
+
+#[test]
+pub fn test_addr_equality_path() {
+ let path = "/foo/bar";
+ let actual = Path::new(path);
+ let addr1 = UnixAddr::new(actual).unwrap();
+ let mut addr2 = addr1;
+
+ unsafe { (*addr2.as_mut_ptr()).sun_path[10] = 127 };
+
+ assert_eq!(addr1, addr2);
+ assert_eq!(calculate_hash(&addr1), calculate_hash(&addr2));
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[test]
+pub fn test_abstract_sun_path_too_long() {
+ let name = String::from("nix\0abstract\0tesnix\0abstract\0tesnix\0abstract\0tesnix\0abstract\0tesnix\0abstract\0testttttnix\0abstract\0test\0make\0sure\0this\0is\0long\0enough");
+ let addr = UnixAddr::new_abstract(name.as_bytes());
+ addr.expect_err("assertion failed");
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[test]
+pub fn test_addr_equality_abstract() {
+ let name = String::from("nix\0abstract\0test");
+ let addr1 = UnixAddr::new_abstract(name.as_bytes()).unwrap();
+ let mut addr2 = addr1;
+
+ assert_eq!(addr1, addr2);
+ assert_eq!(calculate_hash(&addr1), calculate_hash(&addr2));
+
+ unsafe { (*addr2.as_mut_ptr()).sun_path[17] = 127 };
+ assert_ne!(addr1, addr2);
+ assert_ne!(calculate_hash(&addr1), calculate_hash(&addr2));
+}
+
+// Test getting/setting abstract addresses (without unix socket creation)
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[test]
+pub fn test_abstract_uds_addr() {
+ let empty = String::new();
+ let addr = UnixAddr::new_abstract(empty.as_bytes()).unwrap();
+ let sun_path: [u8; 0] = [];
+ assert_eq!(addr.as_abstract(), Some(&sun_path[..]));
+
+ let name = String::from("nix\0abstract\0test");
+ let addr = UnixAddr::new_abstract(name.as_bytes()).unwrap();
+ let sun_path = [
+ 110u8, 105, 120, 0, 97, 98, 115, 116, 114, 97, 99, 116, 0, 116, 101,
+ 115, 116,
+ ];
+ assert_eq!(addr.as_abstract(), Some(&sun_path[..]));
+ assert_eq!(addr.path(), None);
+
+ // Internally, name is null-prefixed (abstract namespace)
+ assert_eq!(unsafe { (*addr.as_ptr()).sun_path[0] }, 0);
+}
+
+// Test getting an unnamed address (without unix socket creation)
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[test]
+pub fn test_unnamed_uds_addr() {
+ use crate::nix::sys::socket::SockaddrLike;
+
+ let addr = UnixAddr::new_unnamed();
+
+ assert!(addr.is_unnamed());
+ assert_eq!(addr.len(), 2);
+ assert!(addr.path().is_none());
+ assert_eq!(addr.path_len(), 0);
+
+ assert!(addr.as_abstract().is_none());
+}
+
+#[test]
+pub fn test_getsockname() {
+ use nix::sys::socket::bind;
+ use nix::sys::socket::{socket, AddressFamily, SockFlag, SockType};
+
+ let tempdir = tempfile::tempdir().unwrap();
+ let sockname = tempdir.path().join("sock");
+ let sock = socket(
+ AddressFamily::Unix,
+ SockType::Stream,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("socket failed");
+ let sockaddr = UnixAddr::new(&sockname).unwrap();
+ bind(sock.as_raw_fd(), &sockaddr).expect("bind failed");
+ assert_eq!(
+ sockaddr,
+ getsockname(sock.as_raw_fd()).expect("getsockname failed")
+ );
+}
+
+#[test]
+pub fn test_socketpair() {
+ use nix::sys::socket::{socketpair, AddressFamily, SockFlag, SockType};
+ use nix::unistd::{read, write};
+
+ let (fd1, fd2) = socketpair(
+ AddressFamily::Unix,
+ SockType::Stream,
+ None,
+ SockFlag::empty(),
+ )
+ .unwrap();
+ write(fd1.as_raw_fd(), b"hello").unwrap();
+ let mut buf = [0; 5];
+ read(fd2.as_raw_fd(), &mut buf).unwrap();
+
+ assert_eq!(&buf[..], b"hello");
+}
+
+#[test]
+pub fn test_recvmsg_sockaddr_un() {
+ use nix::sys::socket::{
+ self, bind, socket, AddressFamily, MsgFlags, SockFlag, SockType,
+ };
+
+ let tempdir = tempfile::tempdir().unwrap();
+ let sockname = tempdir.path().join("sock");
+ let sock = socket(
+ AddressFamily::Unix,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("socket failed");
+ let sockaddr = UnixAddr::new(&sockname).unwrap();
+ bind(sock.as_raw_fd(), &sockaddr).expect("bind failed");
+
+ // Send a message
+ let send_buffer = "hello".as_bytes();
+ if let Err(e) = socket::sendmsg(
+ sock.as_raw_fd(),
+ &[std::io::IoSlice::new(send_buffer)],
+ &[],
+ MsgFlags::empty(),
+ Some(&sockaddr),
+ ) {
+ crate::skip!("Couldn't send ({e:?}), so skipping test");
+ }
+
+ // Receive the message
+ let mut recv_buffer = [0u8; 32];
+ let mut iov = [std::io::IoSliceMut::new(&mut recv_buffer)];
+ let received =
+ socket::recvmsg(sock.as_raw_fd(), &mut iov, None, MsgFlags::empty())
+ .unwrap();
+ // Check the address in the received message
+ assert_eq!(sockaddr, received.address.unwrap());
+}
+
+#[test]
+pub fn test_std_conversions() {
+ use nix::sys::socket::*;
+
+ let std_sa = SocketAddrV4::from_str("127.0.0.1:6789").unwrap();
+ let sock_addr = SockaddrIn::from(std_sa);
+ assert_eq!(std_sa, sock_addr.into());
+
+ let std_sa = SocketAddrV6::from_str("[::1]:6000").unwrap();
+ let sock_addr: SockaddrIn6 = SockaddrIn6::from(std_sa);
+ assert_eq!(std_sa, sock_addr.into());
+}
+
+mod recvfrom {
+ use super::*;
+ use nix::sys::socket::*;
+ use nix::{errno::Errno, Result};
+ use std::thread;
+
+ const MSG: &[u8] = b"Hello, World!";
+
+ fn sendrecv<Fs, Fr>(
+ rsock: RawFd,
+ ssock: RawFd,
+ f_send: Fs,
+ mut f_recv: Fr,
+ ) -> Option<SockaddrStorage>
+ where
+ Fs: Fn(RawFd, &[u8], MsgFlags) -> Result<usize> + Send + 'static,
+ Fr: FnMut(usize, Option<SockaddrStorage>),
+ {
+ let mut buf: [u8; 13] = [0u8; 13];
+ let mut l = 0;
+ let mut from = None;
+
+ let send_thread = thread::spawn(move || {
+ let mut l = 0;
+ while l < std::mem::size_of_val(MSG) {
+ l += f_send(ssock, &MSG[l..], MsgFlags::empty()).unwrap();
+ }
+ });
+
+ while l < std::mem::size_of_val(MSG) {
+ let (len, from_) = recvfrom(rsock, &mut buf[l..]).unwrap();
+ f_recv(len, from_);
+ from = from_;
+ l += len;
+ }
+ assert_eq!(&buf, MSG);
+ send_thread.join().unwrap();
+ from
+ }
+
+ #[test]
+ pub fn stream() {
+ let (fd2, fd1) = socketpair(
+ AddressFamily::Unix,
+ SockType::Stream,
+ None,
+ SockFlag::empty(),
+ )
+ .unwrap();
+ // Ignore from for stream sockets
+ let _ = sendrecv(fd1.as_raw_fd(), fd2.as_raw_fd(), send, |_, _| {});
+ }
+
+ #[test]
+ pub fn udp() {
+ let std_sa = SocketAddrV4::from_str("127.0.0.1:6789").unwrap();
+ let sock_addr = SockaddrIn::from(std_sa);
+ let rsock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+ bind(rsock.as_raw_fd(), &sock_addr).unwrap();
+ let ssock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("send socket failed");
+ let from = sendrecv(
+ rsock.as_raw_fd(),
+ ssock.as_raw_fd(),
+ move |s, m, flags| sendto(s.as_raw_fd(), m, &sock_addr, flags),
+ |_, _| {},
+ );
+ // UDP sockets should set the from address
+ assert_eq!(AddressFamily::Inet, from.unwrap().family().unwrap());
+ }
+
+ #[cfg(target_os = "linux")]
+ mod udp_offload {
+ use super::*;
+ use nix::sys::socket::sockopt::{UdpGroSegment, UdpGsoSegment};
+ use std::io::IoSlice;
+
+ #[test]
+ // Disable the test under emulation because it fails in Cirrus-CI. Lack
+ // of QEMU support is suspected.
+ #[cfg_attr(qemu, ignore)]
+ pub fn gso() {
+ require_kernel_version!(udp_offload::gso, ">= 4.18");
+
+ // In this test, we send the data and provide a GSO segment size.
+ // Since we are sending the buffer of size 13, six UDP packets
+ // with size 2 and two UDP packet with size 1 will be sent.
+ let segment_size: u16 = 2;
+
+ let sock_addr = SockaddrIn::new(127, 0, 0, 1, 6791);
+ let rsock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+
+ setsockopt(&rsock, UdpGsoSegment, &(segment_size as _))
+ .expect("setsockopt UDP_SEGMENT failed");
+
+ bind(rsock.as_raw_fd(), &sock_addr).unwrap();
+ let ssock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("send socket failed");
+
+ let mut num_packets_received: i32 = 0;
+
+ sendrecv(
+ rsock.as_raw_fd(),
+ ssock.as_raw_fd(),
+ move |s, m, flags| {
+ let iov = [IoSlice::new(m)];
+ let cmsg = ControlMessage::UdpGsoSegments(&segment_size);
+ sendmsg(
+ s.as_raw_fd(),
+ &iov,
+ &[cmsg],
+ flags,
+ Some(&sock_addr),
+ )
+ },
+ {
+ let num_packets_received_ref = &mut num_packets_received;
+
+ move |len, _| {
+ // check that we receive UDP packets with payload size
+ // less or equal to segment size
+ assert!(len <= segment_size as usize);
+ *num_packets_received_ref += 1;
+ }
+ },
+ );
+
+ // Buffer size is 13, we will receive six packets of size 2,
+ // and one packet of size 1.
+ assert_eq!(7, num_packets_received);
+ }
+
+ #[test]
+ // Disable the test on emulated platforms because it fails in Cirrus-CI.
+ // Lack of QEMU support is suspected.
+ #[cfg_attr(qemu, ignore)]
+ pub fn gro() {
+ require_kernel_version!(udp_offload::gro, ">= 5.3");
+
+ // It's hard to guarantee receiving GRO packets. Just checking
+ // that `setsockopt` doesn't fail with error
+
+ let rsock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+
+ setsockopt(&rsock, UdpGroSegment, &true)
+ .expect("setsockopt UDP_GRO failed");
+ }
+ }
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ ))]
+ #[test]
+ pub fn udp_sendmmsg() {
+ use std::io::IoSlice;
+
+ let std_sa = SocketAddrV4::from_str("127.0.0.1:6793").unwrap();
+ let std_sa2 = SocketAddrV4::from_str("127.0.0.1:6794").unwrap();
+ let sock_addr = SockaddrIn::from(std_sa);
+ let sock_addr2 = SockaddrIn::from(std_sa2);
+
+ let rsock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+ bind(rsock.as_raw_fd(), &sock_addr).unwrap();
+ let ssock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("send socket failed");
+
+ let from = sendrecv(
+ rsock.as_raw_fd(),
+ ssock.as_raw_fd(),
+ move |s, m, flags| {
+ let batch_size = 15;
+ let mut iovs = Vec::with_capacity(1 + batch_size);
+ let mut addrs = Vec::with_capacity(1 + batch_size);
+ let mut data = MultiHeaders::preallocate(1 + batch_size, None);
+ let iov = IoSlice::new(m);
+ // first chunk:
+ iovs.push([iov]);
+ addrs.push(Some(sock_addr));
+
+ for _ in 0..batch_size {
+ iovs.push([iov]);
+ addrs.push(Some(sock_addr2));
+ }
+
+ let res = sendmmsg(s, &mut data, &iovs, addrs, [], flags)?;
+ let mut sent_messages = 0;
+ let mut sent_bytes = 0;
+ for item in res {
+ sent_messages += 1;
+ sent_bytes += item.bytes;
+ }
+ //
+ assert_eq!(sent_messages, iovs.len());
+ assert_eq!(sent_bytes, sent_messages * m.len());
+ Ok(sent_messages)
+ },
+ |_, _| {},
+ );
+ // UDP sockets should set the from address
+ assert_eq!(AddressFamily::Inet, from.unwrap().family().unwrap());
+ }
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ ))]
+ #[test]
+ pub fn udp_recvmmsg() {
+ use nix::sys::socket::{recvmmsg, MsgFlags};
+ use std::io::IoSliceMut;
+
+ const NUM_MESSAGES_SENT: usize = 2;
+ const DATA: [u8; 2] = [1, 2];
+
+ let inet_addr = SocketAddrV4::from_str("127.0.0.1:6798").unwrap();
+ let sock_addr = SockaddrIn::from(inet_addr);
+
+ let rsock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+ bind(rsock.as_raw_fd(), &sock_addr).unwrap();
+ let ssock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("send socket failed");
+
+ let send_thread = thread::spawn(move || {
+ for _ in 0..NUM_MESSAGES_SENT {
+ sendto(
+ ssock.as_raw_fd(),
+ &DATA[..],
+ &sock_addr,
+ MsgFlags::empty(),
+ )
+ .unwrap();
+ }
+ });
+
+ let mut msgs = std::collections::LinkedList::new();
+
+ // Buffers to receive exactly `NUM_MESSAGES_SENT` messages
+ let mut receive_buffers = [[0u8; 32]; NUM_MESSAGES_SENT];
+ msgs.extend(
+ receive_buffers
+ .iter_mut()
+ .map(|buf| [IoSliceMut::new(&mut buf[..])]),
+ );
+
+ let mut data =
+ MultiHeaders::<SockaddrIn>::preallocate(msgs.len(), None);
+
+ let res: Vec<RecvMsg<SockaddrIn>> = recvmmsg(
+ rsock.as_raw_fd(),
+ &mut data,
+ msgs.iter(),
+ MsgFlags::empty(),
+ None,
+ )
+ .expect("recvmmsg")
+ .collect();
+ assert_eq!(res.len(), DATA.len());
+
+ for RecvMsg { address, bytes, .. } in res.into_iter() {
+ assert_eq!(AddressFamily::Inet, address.unwrap().family().unwrap());
+ assert_eq!(DATA.len(), bytes);
+ }
+
+ for buf in &receive_buffers {
+ assert_eq!(&buf[..DATA.len()], DATA);
+ }
+
+ send_thread.join().unwrap();
+ }
+
+ #[cfg(any(
+ target_os = "linux",
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "netbsd",
+ ))]
+ #[test]
+ pub fn udp_recvmmsg_dontwait_short_read() {
+ use nix::sys::socket::{recvmmsg, MsgFlags};
+ use std::io::IoSliceMut;
+
+ const NUM_MESSAGES_SENT: usize = 2;
+ const DATA: [u8; 4] = [1, 2, 3, 4];
+
+ let inet_addr = SocketAddrV4::from_str("127.0.0.1:6799").unwrap();
+ let sock_addr = SockaddrIn::from(inet_addr);
+
+ let rsock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+ bind(rsock.as_raw_fd(), &sock_addr).unwrap();
+ let ssock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("send socket failed");
+
+ let send_thread = thread::spawn(move || {
+ for _ in 0..NUM_MESSAGES_SENT {
+ sendto(
+ ssock.as_raw_fd(),
+ &DATA[..],
+ &sock_addr,
+ MsgFlags::empty(),
+ )
+ .unwrap();
+ }
+ });
+ // Ensure we've sent all the messages before continuing so `recvmmsg`
+ // will return right away
+ send_thread.join().unwrap();
+
+ let mut msgs = std::collections::LinkedList::new();
+
+ // Buffers to receive >`NUM_MESSAGES_SENT` messages to ensure `recvmmsg`
+ // will return when there are fewer than requested messages in the
+ // kernel buffers when using `MSG_DONTWAIT`.
+ let mut receive_buffers = [[0u8; 32]; NUM_MESSAGES_SENT + 2];
+ msgs.extend(
+ receive_buffers
+ .iter_mut()
+ .map(|buf| [IoSliceMut::new(&mut buf[..])]),
+ );
+
+ let mut data = MultiHeaders::<SockaddrIn>::preallocate(
+ NUM_MESSAGES_SENT + 2,
+ None,
+ );
+
+ let res: Vec<RecvMsg<SockaddrIn>> = recvmmsg(
+ rsock.as_raw_fd(),
+ &mut data,
+ msgs.iter(),
+ MsgFlags::MSG_DONTWAIT,
+ None,
+ )
+ .expect("recvmmsg")
+ .collect();
+ assert_eq!(res.len(), NUM_MESSAGES_SENT);
+
+ for RecvMsg { address, bytes, .. } in res.into_iter() {
+ assert_eq!(AddressFamily::Inet, address.unwrap().family().unwrap());
+ assert_eq!(DATA.len(), bytes);
+ }
+
+ for buf in &receive_buffers[..NUM_MESSAGES_SENT] {
+ assert_eq!(&buf[..DATA.len()], DATA);
+ }
+ }
+
+ #[test]
+ pub fn udp_inet6() {
+ let addr = std::net::Ipv6Addr::from_str("::1").unwrap();
+ let rport = 6789;
+ let rstd_sa = SocketAddrV6::new(addr, rport, 0, 0);
+ let raddr = SockaddrIn6::from(rstd_sa);
+ let sport = 6790;
+ let sstd_sa = SocketAddrV6::new(addr, sport, 0, 0);
+ let saddr = SockaddrIn6::from(sstd_sa);
+ let rsock = socket(
+ AddressFamily::Inet6,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("receive socket failed");
+ match bind(rsock.as_raw_fd(), &raddr) {
+ Err(Errno::EADDRNOTAVAIL) => {
+ println!("IPv6 not available, skipping test.");
+ return;
+ }
+ Err(e) => panic!("bind: {e}"),
+ Ok(()) => (),
+ }
+ let ssock = socket(
+ AddressFamily::Inet6,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("send socket failed");
+ bind(ssock.as_raw_fd(), &saddr).unwrap();
+ let from = sendrecv(
+ rsock.as_raw_fd(),
+ ssock.as_raw_fd(),
+ move |s, m, flags| sendto(s.as_raw_fd(), m, &raddr, flags),
+ |_, _| {},
+ );
+ assert_eq!(AddressFamily::Inet6, from.unwrap().family().unwrap());
+ let osent_addr = from.unwrap();
+ let sent_addr = osent_addr.as_sockaddr_in6().unwrap();
+ assert_eq!(sent_addr.ip(), addr);
+ assert_eq!(sent_addr.port(), sport);
+ }
+}
+
+// Test error handling of our recvmsg wrapper
+#[test]
+pub fn test_recvmsg_ebadf() {
+ use nix::errno::Errno;
+ use nix::sys::socket::{recvmsg, MsgFlags};
+ use std::io::IoSliceMut;
+
+ let mut buf = [0u8; 5];
+ let mut iov = [IoSliceMut::new(&mut buf[..])];
+
+ let fd = -1; // Bad file descriptor
+ let r = recvmsg::<()>(fd.as_raw_fd(), &mut iov, None, MsgFlags::empty());
+
+ assert_eq!(r.err().unwrap(), Errno::EBADF);
+}
+
+// Disable the test on emulated platforms due to a bug in QEMU versions <
+// 2.12.0. https://bugs.launchpad.net/qemu/+bug/1701808
+#[cfg_attr(qemu, ignore)]
+#[test]
+pub fn test_scm_rights() {
+ use nix::sys::socket::{
+ recvmsg, sendmsg, socketpair, AddressFamily, ControlMessage,
+ ControlMessageOwned, MsgFlags, SockFlag, SockType,
+ };
+ use nix::unistd::{close, pipe, read, write};
+ use std::io::{IoSlice, IoSliceMut};
+
+ let (fd1, fd2) = socketpair(
+ AddressFamily::Unix,
+ SockType::Stream,
+ None,
+ SockFlag::empty(),
+ )
+ .unwrap();
+ let (r, w) = pipe().unwrap();
+ let mut received_r: Option<RawFd> = None;
+
+ {
+ let iov = [IoSlice::new(b"hello")];
+ let fds = [r];
+ let cmsg = ControlMessage::ScmRights(&fds);
+ assert_eq!(
+ sendmsg::<()>(
+ fd1.as_raw_fd(),
+ &iov,
+ &[cmsg],
+ MsgFlags::empty(),
+ None
+ )
+ .unwrap(),
+ 5
+ );
+ close(r).unwrap();
+ }
+
+ {
+ let mut buf = [0u8; 5];
+
+ let mut iov = [IoSliceMut::new(&mut buf[..])];
+ let mut cmsgspace = cmsg_space!([RawFd; 1]);
+ let msg = recvmsg::<()>(
+ fd2.as_raw_fd(),
+ &mut iov,
+ Some(&mut cmsgspace),
+ MsgFlags::empty(),
+ )
+ .unwrap();
+
+ for cmsg in msg.cmsgs() {
+ if let ControlMessageOwned::ScmRights(fd) = cmsg {
+ assert_eq!(received_r, None);
+ assert_eq!(fd.len(), 1);
+ received_r = Some(fd[0]);
+ } else {
+ panic!("unexpected cmsg");
+ }
+ }
+ assert_eq!(msg.bytes, 5);
+ assert!(!msg
+ .flags
+ .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
+ }
+
+ let received_r = received_r.expect("Did not receive passed fd");
+ // Ensure that the received file descriptor works
+ write(w.as_raw_fd(), b"world").unwrap();
+ let mut buf = [0u8; 5];
+ read(received_r.as_raw_fd(), &mut buf).unwrap();
+ assert_eq!(&buf[..], b"world");
+ close(received_r).unwrap();
+ close(w).unwrap();
+}
+
+// Disable the test on emulated platforms due to not enabled support of AF_ALG in QEMU from rust cross
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[cfg_attr(qemu, ignore)]
+#[test]
+pub fn test_af_alg_cipher() {
+ use nix::sys::socket::sockopt::AlgSetKey;
+ use nix::sys::socket::{
+ accept, bind, sendmsg, setsockopt, socket, AddressFamily, AlgAddr,
+ ControlMessage, MsgFlags, SockFlag, SockType,
+ };
+ use nix::unistd::read;
+ use std::io::IoSlice;
+
+ skip_if_cirrus!("Fails for an unknown reason Cirrus CI. Bug #1352");
+ // Travis's seccomp profile blocks AF_ALG
+ // https://docs.docker.com/engine/security/seccomp/
+ skip_if_seccomp!(test_af_alg_cipher);
+
+ let alg_type = "skcipher";
+ let alg_name = "ctr-aes-aesni";
+ // 256-bits secret key
+ let key = vec![0u8; 32];
+ // 16-bytes IV
+ let iv_len = 16;
+ let iv = vec![1u8; iv_len];
+ // 256-bytes plain payload
+ let payload_len = 256;
+ let payload = vec![2u8; payload_len];
+
+ let sock = socket(
+ AddressFamily::Alg,
+ SockType::SeqPacket,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("socket failed");
+
+ let sockaddr = AlgAddr::new(alg_type, alg_name);
+ bind(sock.as_raw_fd(), &sockaddr).expect("bind failed");
+
+ assert_eq!(sockaddr.alg_name().to_string_lossy(), alg_name);
+ assert_eq!(sockaddr.alg_type().to_string_lossy(), alg_type);
+
+ setsockopt(&sock, AlgSetKey::default(), &key).expect("setsockopt");
+ let session_socket = accept(sock.as_raw_fd()).expect("accept failed");
+
+ let msgs = [
+ ControlMessage::AlgSetOp(&libc::ALG_OP_ENCRYPT),
+ ControlMessage::AlgSetIv(iv.as_slice()),
+ ];
+ let iov = IoSlice::new(&payload);
+ sendmsg::<()>(
+ session_socket.as_raw_fd(),
+ &[iov],
+ &msgs,
+ MsgFlags::empty(),
+ None,
+ )
+ .expect("sendmsg encrypt");
+
+ // allocate buffer for encrypted data
+ let mut encrypted = vec![0u8; payload_len];
+ let num_bytes =
+ read(session_socket.as_raw_fd(), &mut encrypted).expect("read encrypt");
+ assert_eq!(num_bytes, payload_len);
+
+ let iov = IoSlice::new(&encrypted);
+
+ let iv = vec![1u8; iv_len];
+
+ let msgs = [
+ ControlMessage::AlgSetOp(&libc::ALG_OP_DECRYPT),
+ ControlMessage::AlgSetIv(iv.as_slice()),
+ ];
+ sendmsg::<()>(
+ session_socket.as_raw_fd(),
+ &[iov],
+ &msgs,
+ MsgFlags::empty(),
+ None,
+ )
+ .expect("sendmsg decrypt");
+
+ // allocate buffer for decrypted data
+ let mut decrypted = vec![0u8; payload_len];
+ let num_bytes =
+ read(session_socket.as_raw_fd(), &mut decrypted).expect("read decrypt");
+
+ assert_eq!(num_bytes, payload_len);
+ assert_eq!(decrypted, payload);
+}
+
+// Disable the test on emulated platforms due to not enabled support of AF_ALG
+// in QEMU from rust cross
+#[cfg(any(target_os = "linux", target_os = "android"))]
+#[cfg_attr(qemu, ignore)]
+#[test]
+pub fn test_af_alg_aead() {
+ use libc::{ALG_OP_DECRYPT, ALG_OP_ENCRYPT};
+ use nix::fcntl::{fcntl, FcntlArg, OFlag};
+ use nix::sys::socket::sockopt::{AlgSetAeadAuthSize, AlgSetKey};
+ use nix::sys::socket::{
+ accept, bind, sendmsg, setsockopt, socket, AddressFamily, AlgAddr,
+ ControlMessage, MsgFlags, SockFlag, SockType,
+ };
+ use nix::unistd::read;
+ use std::io::IoSlice;
+
+ skip_if_cirrus!("Fails for an unknown reason Cirrus CI. Bug #1352");
+ // Travis's seccomp profile blocks AF_ALG
+ // https://docs.docker.com/engine/security/seccomp/
+ skip_if_seccomp!(test_af_alg_aead);
+
+ let auth_size = 4usize;
+ let assoc_size = 16u32;
+
+ let alg_type = "aead";
+ let alg_name = "gcm(aes)";
+ // 256-bits secret key
+ let key = vec![0u8; 32];
+ // 12-bytes IV
+ let iv_len = 12;
+ let iv = vec![1u8; iv_len];
+ // 256-bytes plain payload
+ let payload_len = 256;
+ let mut payload =
+ vec![2u8; payload_len + (assoc_size as usize) + auth_size];
+
+ for i in 0..assoc_size {
+ payload[i as usize] = 10;
+ }
+
+ let len = payload.len();
+
+ for i in 0..auth_size {
+ payload[len - 1 - i] = 0;
+ }
+
+ let sock = socket(
+ AddressFamily::Alg,
+ SockType::SeqPacket,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("socket failed");
+
+ let sockaddr = AlgAddr::new(alg_type, alg_name);
+ bind(sock.as_raw_fd(), &sockaddr).expect("bind failed");
+
+ setsockopt(&sock, AlgSetAeadAuthSize, &auth_size)
+ .expect("setsockopt AlgSetAeadAuthSize");
+ setsockopt(&sock, AlgSetKey::default(), &key)
+ .expect("setsockopt AlgSetKey");
+ let session_socket = accept(sock.as_raw_fd()).expect("accept failed");
+
+ let msgs = [
+ ControlMessage::AlgSetOp(&ALG_OP_ENCRYPT),
+ ControlMessage::AlgSetIv(iv.as_slice()),
+ ControlMessage::AlgSetAeadAssoclen(&assoc_size),
+ ];
+
+ let iov = IoSlice::new(&payload);
+ sendmsg::<()>(
+ session_socket.as_raw_fd(),
+ &[iov],
+ &msgs,
+ MsgFlags::empty(),
+ None,
+ )
+ .expect("sendmsg encrypt");
+
+ // allocate buffer for encrypted data
+ let mut encrypted =
+ vec![0u8; (assoc_size as usize) + payload_len + auth_size];
+ let num_bytes =
+ read(session_socket.as_raw_fd(), &mut encrypted).expect("read encrypt");
+ assert_eq!(num_bytes, payload_len + auth_size + (assoc_size as usize));
+
+ for i in 0..assoc_size {
+ encrypted[i as usize] = 10;
+ }
+
+ let iov = IoSlice::new(&encrypted);
+
+ let iv = vec![1u8; iv_len];
+
+ let session_socket = accept(sock.as_raw_fd()).expect("accept failed");
+
+ let msgs = [
+ ControlMessage::AlgSetOp(&ALG_OP_DECRYPT),
+ ControlMessage::AlgSetIv(iv.as_slice()),
+ ControlMessage::AlgSetAeadAssoclen(&assoc_size),
+ ];
+ sendmsg::<()>(
+ session_socket.as_raw_fd(),
+ &[iov],
+ &msgs,
+ MsgFlags::empty(),
+ None,
+ )
+ .expect("sendmsg decrypt");
+
+ // allocate buffer for decrypted data
+ let mut decrypted =
+ vec![0u8; payload_len + (assoc_size as usize) + auth_size];
+ // Starting with kernel 4.9, the interface changed slightly such that the
+ // authentication tag memory is only needed in the output buffer for encryption
+ // and in the input buffer for decryption.
+ // Do not block on read, as we may have fewer bytes than buffer size
+ fcntl(session_socket, FcntlArg::F_SETFL(OFlag::O_NONBLOCK))
+ .expect("fcntl non_blocking");
+ let num_bytes =
+ read(session_socket.as_raw_fd(), &mut decrypted).expect("read decrypt");
+
+ assert!(num_bytes >= payload_len + (assoc_size as usize));
+ assert_eq!(
+ decrypted[(assoc_size as usize)..(payload_len + (assoc_size as usize))],
+ payload[(assoc_size as usize)..payload_len + (assoc_size as usize)]
+ );
+}
+
+// Verify `ControlMessage::Ipv4PacketInfo` for `sendmsg`.
+// This creates a (udp) socket bound to localhost, then sends a message to
+// itself but uses Ipv4PacketInfo to force the source address to be localhost.
+//
+// This would be a more interesting test if we could assume that the test host
+// has more than one IP address (since we could select a different address to
+// test from).
+#[cfg(any(target_os = "linux", target_os = "macos", target_os = "netbsd"))]
+#[test]
+pub fn test_sendmsg_ipv4packetinfo() {
+ use cfg_if::cfg_if;
+ use nix::sys::socket::{
+ bind, sendmsg, socket, AddressFamily, ControlMessage, MsgFlags,
+ SockFlag, SockType, SockaddrIn,
+ };
+ use std::io::IoSlice;
+
+ let sock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("socket failed");
+
+ let sock_addr = SockaddrIn::new(127, 0, 0, 1, 4000);
+
+ bind(sock.as_raw_fd(), &sock_addr).expect("bind failed");
+
+ let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
+ let iov = [IoSlice::new(&slice)];
+
+ cfg_if! {
+ if #[cfg(target_os = "netbsd")] {
+ let pi = libc::in_pktinfo {
+ ipi_ifindex: 0, /* Unspecified interface */
+ ipi_addr: libc::in_addr { s_addr: 0 },
+ };
+ } else {
+ let pi = libc::in_pktinfo {
+ ipi_ifindex: 0, /* Unspecified interface */
+ ipi_addr: libc::in_addr { s_addr: 0 },
+ ipi_spec_dst: sock_addr.as_ref().sin_addr,
+ };
+ }
+ }
+
+ let cmsg = [ControlMessage::Ipv4PacketInfo(&pi)];
+
+ sendmsg(
+ sock.as_raw_fd(),
+ &iov,
+ &cmsg,
+ MsgFlags::empty(),
+ Some(&sock_addr),
+ )
+ .expect("sendmsg");
+}
+
+// Verify `ControlMessage::Ipv6PacketInfo` for `sendmsg`.
+// This creates a (udp) socket bound to ip6-localhost, then sends a message to
+// itself but uses Ipv6PacketInfo to force the source address to be
+// ip6-localhost.
+//
+// This would be a more interesting test if we could assume that the test host
+// has more than one IP address (since we could select a different address to
+// test from).
+#[cfg(any(
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "freebsd"
+))]
+#[test]
+pub fn test_sendmsg_ipv6packetinfo() {
+ use nix::errno::Errno;
+ use nix::sys::socket::{
+ bind, sendmsg, socket, AddressFamily, ControlMessage, MsgFlags,
+ SockFlag, SockType, SockaddrIn6,
+ };
+ use std::io::IoSlice;
+
+ let sock = socket(
+ AddressFamily::Inet6,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("socket failed");
+
+ let std_sa = SocketAddrV6::from_str("[::1]:6000").unwrap();
+ let sock_addr: SockaddrIn6 = SockaddrIn6::from(std_sa);
+
+ if let Err(Errno::EADDRNOTAVAIL) = bind(sock.as_raw_fd(), &sock_addr) {
+ println!("IPv6 not available, skipping test.");
+ return;
+ }
+
+ let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
+ let iov = [IoSlice::new(&slice)];
+
+ let pi = libc::in6_pktinfo {
+ ipi6_ifindex: 0, /* Unspecified interface */
+ ipi6_addr: sock_addr.as_ref().sin6_addr,
+ };
+
+ let cmsg = [ControlMessage::Ipv6PacketInfo(&pi)];
+
+ sendmsg::<SockaddrIn6>(
+ sock.as_raw_fd(),
+ &iov,
+ &cmsg,
+ MsgFlags::empty(),
+ Some(&sock_addr),
+ )
+ .expect("sendmsg");
+}
+
+// Verify that ControlMessage::Ipv4SendSrcAddr works for sendmsg. This
+// creates a UDP socket bound to all local interfaces (0.0.0.0). It then
+// sends message to itself at 127.0.0.1 while explicitly specifying
+// 127.0.0.1 as the source address through an Ipv4SendSrcAddr
+// (IP_SENDSRCADDR) control message.
+//
+// Note that binding to 0.0.0.0 is *required* on FreeBSD; sendmsg
+// returns EINVAL otherwise. (See FreeBSD's ip(4) man page.)
+#[cfg(any(
+ target_os = "netbsd",
+ target_os = "freebsd",
+ target_os = "openbsd",
+ target_os = "dragonfly",
+))]
+#[test]
+pub fn test_sendmsg_ipv4sendsrcaddr() {
+ use nix::sys::socket::{
+ bind, sendmsg, socket, AddressFamily, ControlMessage, MsgFlags,
+ SockFlag, SockType, SockaddrIn,
+ };
+ use std::io::IoSlice;
+
+ let sock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("socket failed");
+
+ let unspec_sock_addr = SockaddrIn::new(0, 0, 0, 0, 0);
+ bind(sock.as_raw_fd(), &unspec_sock_addr).expect("bind failed");
+ let bound_sock_addr: SockaddrIn = getsockname(sock.as_raw_fd()).unwrap();
+ let localhost_sock_addr: SockaddrIn =
+ SockaddrIn::new(127, 0, 0, 1, bound_sock_addr.port());
+
+ let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
+ let iov = [IoSlice::new(&slice)];
+ let cmsg = [ControlMessage::Ipv4SendSrcAddr(
+ &localhost_sock_addr.as_ref().sin_addr,
+ )];
+
+ sendmsg(
+ sock.as_raw_fd(),
+ &iov,
+ &cmsg,
+ MsgFlags::empty(),
+ Some(&localhost_sock_addr),
+ )
+ .expect("sendmsg");
+}
+
+/// Tests that passing multiple fds using a single `ControlMessage` works.
+// Disable the test on emulated platforms due to a bug in QEMU versions <
+// 2.12.0. https://bugs.launchpad.net/qemu/+bug/1701808
+#[cfg_attr(qemu, ignore)]
+#[test]
+fn test_scm_rights_single_cmsg_multiple_fds() {
+ use nix::sys::socket::{
+ recvmsg, sendmsg, ControlMessage, ControlMessageOwned, MsgFlags,
+ };
+ use std::io::{IoSlice, IoSliceMut};
+ use std::os::unix::io::{AsRawFd, RawFd};
+ use std::os::unix::net::UnixDatagram;
+ use std::thread;
+
+ let (send, receive) = UnixDatagram::pair().unwrap();
+ let thread = thread::spawn(move || {
+ let mut buf = [0u8; 8];
+ let mut iovec = [IoSliceMut::new(&mut buf)];
+
+ let mut space = cmsg_space!([RawFd; 2]);
+ let msg = recvmsg::<()>(
+ receive.as_raw_fd(),
+ &mut iovec,
+ Some(&mut space),
+ MsgFlags::empty(),
+ )
+ .unwrap();
+ assert!(!msg
+ .flags
+ .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
+
+ let mut cmsgs = msg.cmsgs();
+ match cmsgs.next() {
+ Some(ControlMessageOwned::ScmRights(fds)) => {
+ assert_eq!(
+ fds.len(),
+ 2,
+ "unexpected fd count (expected 2 fds, got {})",
+ fds.len()
+ );
+ }
+ _ => panic!(),
+ }
+ assert!(cmsgs.next().is_none(), "unexpected control msg");
+
+ assert_eq!(msg.bytes, 8);
+ assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]);
+ });
+
+ let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
+ let iov = [IoSlice::new(&slice)];
+ let fds = [libc::STDIN_FILENO, libc::STDOUT_FILENO]; // pass stdin and stdout
+ let cmsg = [ControlMessage::ScmRights(&fds)];
+ sendmsg::<()>(send.as_raw_fd(), &iov, &cmsg, MsgFlags::empty(), None)
+ .unwrap();
+ thread.join().unwrap();
+}
+
+// Verify `sendmsg` builds a valid `msghdr` when passing an empty
+// `cmsgs` argument. This should result in a msghdr with a nullptr
+// msg_control field and a msg_controllen of 0 when calling into the
+// raw `sendmsg`.
+#[test]
+pub fn test_sendmsg_empty_cmsgs() {
+ use nix::sys::socket::{
+ recvmsg, sendmsg, socketpair, AddressFamily, MsgFlags, SockFlag,
+ SockType,
+ };
+ use std::io::{IoSlice, IoSliceMut};
+
+ let (fd1, fd2) = socketpair(
+ AddressFamily::Unix,
+ SockType::Stream,
+ None,
+ SockFlag::empty(),
+ )
+ .unwrap();
+
+ {
+ let iov = [IoSlice::new(b"hello")];
+ assert_eq!(
+ sendmsg::<()>(fd1.as_raw_fd(), &iov, &[], MsgFlags::empty(), None)
+ .unwrap(),
+ 5
+ );
+ }
+
+ {
+ let mut buf = [0u8; 5];
+ let mut iov = [IoSliceMut::new(&mut buf[..])];
+
+ let mut cmsgspace = cmsg_space!([RawFd; 1]);
+ let msg = recvmsg::<()>(
+ fd2.as_raw_fd(),
+ &mut iov,
+ Some(&mut cmsgspace),
+ MsgFlags::empty(),
+ )
+ .unwrap();
+
+ for _ in msg.cmsgs() {
+ panic!("unexpected cmsg");
+ }
+ assert!(!msg
+ .flags
+ .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
+ assert_eq!(msg.bytes, 5);
+ }
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "freebsd",
+ target_os = "dragonfly",
+))]
+#[test]
+fn test_scm_credentials() {
+ use nix::sys::socket::{
+ recvmsg, sendmsg, socketpair, AddressFamily, ControlMessage,
+ ControlMessageOwned, MsgFlags, SockFlag, SockType, UnixCredentials,
+ };
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ use nix::sys::socket::{setsockopt, sockopt::PassCred};
+ use nix::unistd::{getgid, getpid, getuid};
+ use std::io::{IoSlice, IoSliceMut};
+
+ let (send, recv) = socketpair(
+ AddressFamily::Unix,
+ SockType::Stream,
+ None,
+ SockFlag::empty(),
+ )
+ .unwrap();
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ setsockopt(&recv, PassCred, &true).unwrap();
+
+ {
+ let iov = [IoSlice::new(b"hello")];
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ let cred = UnixCredentials::new();
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ let cmsg = ControlMessage::ScmCredentials(&cred);
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ let cmsg = ControlMessage::ScmCreds;
+ assert_eq!(
+ sendmsg::<()>(
+ send.as_raw_fd(),
+ &iov,
+ &[cmsg],
+ MsgFlags::empty(),
+ None
+ )
+ .unwrap(),
+ 5
+ );
+ }
+
+ {
+ let mut buf = [0u8; 5];
+ let mut iov = [IoSliceMut::new(&mut buf[..])];
+
+ let mut cmsgspace = cmsg_space!(UnixCredentials);
+ let msg = recvmsg::<()>(
+ recv.as_raw_fd(),
+ &mut iov,
+ Some(&mut cmsgspace),
+ MsgFlags::empty(),
+ )
+ .unwrap();
+ let mut received_cred = None;
+
+ for cmsg in msg.cmsgs() {
+ let cred = match cmsg {
+ #[cfg(any(target_os = "android", target_os = "linux"))]
+ ControlMessageOwned::ScmCredentials(cred) => cred,
+ #[cfg(any(target_os = "freebsd", target_os = "dragonfly"))]
+ ControlMessageOwned::ScmCreds(cred) => cred,
+ other => panic!("unexpected cmsg {other:?}"),
+ };
+ assert!(received_cred.is_none());
+ assert_eq!(cred.pid(), getpid().as_raw());
+ assert_eq!(cred.uid(), getuid().as_raw());
+ assert_eq!(cred.gid(), getgid().as_raw());
+ received_cred = Some(cred);
+ }
+ received_cred.expect("no creds received");
+ assert_eq!(msg.bytes, 5);
+ assert!(!msg
+ .flags
+ .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
+ }
+}
+
+/// Ensure that we can send `SCM_CREDENTIALS` and `SCM_RIGHTS` with a single
+/// `sendmsg` call.
+#[cfg(any(target_os = "android", target_os = "linux"))]
+// qemu's handling of multiple cmsgs is bugged, ignore tests under emulation
+// see https://bugs.launchpad.net/qemu/+bug/1781280
+#[cfg_attr(qemu, ignore)]
+#[test]
+fn test_scm_credentials_and_rights() {
+ let space = cmsg_space!(libc::ucred, RawFd);
+ test_impl_scm_credentials_and_rights(space);
+}
+
+/// Ensure that passing a an oversized control message buffer to recvmsg
+/// still works.
+#[cfg(any(target_os = "android", target_os = "linux"))]
+// qemu's handling of multiple cmsgs is bugged, ignore tests under emulation
+// see https://bugs.launchpad.net/qemu/+bug/1781280
+#[cfg_attr(qemu, ignore)]
+#[test]
+fn test_too_large_cmsgspace() {
+ let space = vec![0u8; 1024];
+ test_impl_scm_credentials_and_rights(space);
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+fn test_impl_scm_credentials_and_rights(mut space: Vec<u8>) {
+ use libc::ucred;
+ use nix::sys::socket::sockopt::PassCred;
+ use nix::sys::socket::{
+ recvmsg, sendmsg, setsockopt, socketpair, ControlMessage,
+ ControlMessageOwned, MsgFlags, SockFlag, SockType,
+ };
+ use nix::unistd::{close, getgid, getpid, getuid, pipe, write};
+ use std::io::{IoSlice, IoSliceMut};
+
+ let (send, recv) = socketpair(
+ AddressFamily::Unix,
+ SockType::Stream,
+ None,
+ SockFlag::empty(),
+ )
+ .unwrap();
+ setsockopt(&recv, PassCred, &true).unwrap();
+
+ let (r, w) = pipe().unwrap();
+ let mut received_r: Option<RawFd> = None;
+
+ {
+ let iov = [IoSlice::new(b"hello")];
+ let cred = ucred {
+ pid: getpid().as_raw(),
+ uid: getuid().as_raw(),
+ gid: getgid().as_raw(),
+ }
+ .into();
+ let fds = [r];
+ let cmsgs = [
+ ControlMessage::ScmCredentials(&cred),
+ ControlMessage::ScmRights(&fds),
+ ];
+ assert_eq!(
+ sendmsg::<()>(
+ send.as_raw_fd(),
+ &iov,
+ &cmsgs,
+ MsgFlags::empty(),
+ None
+ )
+ .unwrap(),
+ 5
+ );
+ close(r).unwrap();
+ }
+
+ {
+ let mut buf = [0u8; 5];
+ let mut iov = [IoSliceMut::new(&mut buf[..])];
+ let msg = recvmsg::<()>(
+ recv.as_raw_fd(),
+ &mut iov,
+ Some(&mut space),
+ MsgFlags::empty(),
+ )
+ .unwrap();
+ let mut received_cred = None;
+
+ assert_eq!(msg.cmsgs().count(), 2, "expected 2 cmsgs");
+
+ for cmsg in msg.cmsgs() {
+ match cmsg {
+ ControlMessageOwned::ScmRights(fds) => {
+ assert_eq!(received_r, None, "already received fd");
+ assert_eq!(fds.len(), 1);
+ received_r = Some(fds[0]);
+ }
+ ControlMessageOwned::ScmCredentials(cred) => {
+ assert!(received_cred.is_none());
+ assert_eq!(cred.pid(), getpid().as_raw());
+ assert_eq!(cred.uid(), getuid().as_raw());
+ assert_eq!(cred.gid(), getgid().as_raw());
+ received_cred = Some(cred);
+ }
+ _ => panic!("unexpected cmsg"),
+ }
+ }
+ received_cred.expect("no creds received");
+ assert_eq!(msg.bytes, 5);
+ assert!(!msg
+ .flags
+ .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
+ }
+
+ let received_r = received_r.expect("Did not receive passed fd");
+ // Ensure that the received file descriptor works
+ write(w.as_raw_fd(), b"world").unwrap();
+ let mut buf = [0u8; 5];
+ read(received_r.as_raw_fd(), &mut buf).unwrap();
+ assert_eq!(&buf[..], b"world");
+ close(received_r).unwrap();
+ close(w).unwrap();
+}
+
+// Test creating and using named unix domain sockets
+#[test]
+pub fn test_named_unixdomain() {
+ use nix::sys::socket::{accept, bind, connect, listen, socket, UnixAddr};
+ use nix::sys::socket::{SockFlag, SockType};
+ use nix::unistd::{read, write};
+ use std::thread;
+
+ let tempdir = tempfile::tempdir().unwrap();
+ let sockname = tempdir.path().join("sock");
+ let s1 = socket(
+ AddressFamily::Unix,
+ SockType::Stream,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("socket failed");
+ let sockaddr = UnixAddr::new(&sockname).unwrap();
+ bind(s1.as_raw_fd(), &sockaddr).expect("bind failed");
+ listen(&s1, 10).expect("listen failed");
+
+ let thr = thread::spawn(move || {
+ let s2 = socket(
+ AddressFamily::Unix,
+ SockType::Stream,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("socket failed");
+ connect(s2.as_raw_fd(), &sockaddr).expect("connect failed");
+ write(s2.as_raw_fd(), b"hello").expect("write failed");
+ });
+
+ let s3 = accept(s1.as_raw_fd()).expect("accept failed");
+
+ let mut buf = [0; 5];
+ read(s3.as_raw_fd(), &mut buf).unwrap();
+ thr.join().unwrap();
+
+ assert_eq!(&buf[..], b"hello");
+}
+
+// Test using unnamed unix domain addresses
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[test]
+pub fn test_unnamed_unixdomain() {
+ use nix::sys::socket::{getsockname, socketpair};
+ use nix::sys::socket::{SockFlag, SockType};
+
+ let (fd_1, _fd_2) = socketpair(
+ AddressFamily::Unix,
+ SockType::Stream,
+ None,
+ SockFlag::empty(),
+ )
+ .expect("socketpair failed");
+
+ let addr_1: UnixAddr =
+ getsockname(fd_1.as_raw_fd()).expect("getsockname failed");
+ assert!(addr_1.is_unnamed());
+}
+
+// Test creating and using unnamed unix domain addresses for autobinding sockets
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[test]
+pub fn test_unnamed_unixdomain_autobind() {
+ use nix::sys::socket::{bind, getsockname, socket};
+ use nix::sys::socket::{SockFlag, SockType};
+
+ let fd = socket(
+ AddressFamily::Unix,
+ SockType::Stream,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("socket failed");
+
+ // unix(7): "If a bind(2) call specifies addrlen as `sizeof(sa_family_t)`, or [...], then the
+ // socket is autobound to an abstract address"
+ bind(fd.as_raw_fd(), &UnixAddr::new_unnamed()).expect("bind failed");
+
+ let addr: UnixAddr =
+ getsockname(fd.as_raw_fd()).expect("getsockname failed");
+ let addr = addr.as_abstract().unwrap();
+
+ // changed from 8 to 5 bytes in Linux 2.3.15, and rust's minimum supported Linux version is 3.2
+ // (as of 2022-11)
+ assert_eq!(addr.len(), 5);
+}
+
+// Test creating and using named system control sockets
+#[cfg(any(target_os = "macos", target_os = "ios"))]
+#[test]
+pub fn test_syscontrol() {
+ use nix::errno::Errno;
+ use nix::sys::socket::{
+ socket, SockFlag, SockProtocol, SockType, SysControlAddr,
+ };
+
+ let fd = socket(
+ AddressFamily::System,
+ SockType::Datagram,
+ SockFlag::empty(),
+ SockProtocol::KextControl,
+ )
+ .expect("socket failed");
+ SysControlAddr::from_name(fd.as_raw_fd(), "com.apple.net.utun_control", 0)
+ .expect("resolving sys_control name failed");
+ assert_eq!(
+ SysControlAddr::from_name(fd.as_raw_fd(), "foo.bar.lol", 0).err(),
+ Some(Errno::ENOENT)
+ );
+
+ // requires root privileges
+ // connect(fd.as_raw_fd(), &sockaddr).expect("connect failed");
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+))]
+fn loopback_address(
+ family: AddressFamily,
+) -> Option<nix::ifaddrs::InterfaceAddress> {
+ use nix::ifaddrs::getifaddrs;
+ use nix::net::if_::*;
+ use nix::sys::socket::SockaddrLike;
+ use std::io;
+ use std::io::Write;
+
+ let mut addrs = match getifaddrs() {
+ Ok(iter) => iter,
+ Err(e) => {
+ let stdioerr = io::stderr();
+ let mut handle = stdioerr.lock();
+ writeln!(handle, "getifaddrs: {e:?}").unwrap();
+ return None;
+ }
+ };
+ // return first address matching family
+ addrs.find(|ifaddr| {
+ ifaddr.flags.contains(InterfaceFlags::IFF_LOOPBACK)
+ && ifaddr.address.as_ref().and_then(SockaddrLike::family)
+ == Some(family)
+ })
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+))]
+// qemu doesn't seem to be emulating this correctly in these architectures
+#[cfg_attr(
+ all(
+ qemu,
+ any(
+ target_arch = "mips",
+ target_arch = "mips64",
+ target_arch = "powerpc64",
+ )
+ ),
+ ignore
+)]
+#[test]
+pub fn test_recv_ipv4pktinfo() {
+ use nix::net::if_::*;
+ use nix::sys::socket::sockopt::Ipv4PacketInfo;
+ use nix::sys::socket::{bind, SockFlag, SockType, SockaddrIn};
+ use nix::sys::socket::{getsockname, setsockopt, socket};
+ use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags};
+ use std::io::{IoSlice, IoSliceMut};
+
+ let lo_ifaddr = loopback_address(AddressFamily::Inet);
+ let (lo_name, lo) = match lo_ifaddr {
+ Some(ifaddr) => (
+ ifaddr.interface_name,
+ ifaddr.address.expect("Expect IPv4 address on interface"),
+ ),
+ None => return,
+ };
+ let receive = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("receive socket failed");
+ bind(receive.as_raw_fd(), &lo).expect("bind failed");
+ let sa: SockaddrIn =
+ getsockname(receive.as_raw_fd()).expect("getsockname failed");
+ setsockopt(&receive, Ipv4PacketInfo, &true).expect("setsockopt failed");
+
+ {
+ let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
+ let iov = [IoSlice::new(&slice)];
+
+ let send = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("send socket failed");
+ sendmsg(send.as_raw_fd(), &iov, &[], MsgFlags::empty(), Some(&sa))
+ .expect("sendmsg failed");
+ }
+
+ {
+ let mut buf = [0u8; 8];
+ let mut iovec = [IoSliceMut::new(&mut buf)];
+
+ let mut space = cmsg_space!(libc::in_pktinfo);
+ let msg = recvmsg::<()>(
+ receive.as_raw_fd(),
+ &mut iovec,
+ Some(&mut space),
+ MsgFlags::empty(),
+ )
+ .expect("recvmsg failed");
+ assert!(!msg
+ .flags
+ .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
+
+ let mut cmsgs = msg.cmsgs();
+ if let Some(ControlMessageOwned::Ipv4PacketInfo(pktinfo)) = cmsgs.next()
+ {
+ let i = if_nametoindex(lo_name.as_bytes()).expect("if_nametoindex");
+ assert_eq!(
+ pktinfo.ipi_ifindex as libc::c_uint, i,
+ "unexpected ifindex (expected {}, got {})",
+ i, pktinfo.ipi_ifindex
+ );
+ }
+ assert!(cmsgs.next().is_none(), "unexpected additional control msg");
+ assert_eq!(msg.bytes, 8);
+ assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]);
+ }
+}
+
+#[cfg(any(
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+))]
+// qemu doesn't seem to be emulating this correctly in these architectures
+#[cfg_attr(
+ all(
+ qemu,
+ any(
+ target_arch = "mips",
+ target_arch = "mips64",
+ target_arch = "powerpc64",
+ )
+ ),
+ ignore
+)]
+#[test]
+pub fn test_recvif() {
+ use nix::net::if_::*;
+ use nix::sys::socket::sockopt::{Ipv4RecvDstAddr, Ipv4RecvIf};
+ use nix::sys::socket::{bind, SockFlag, SockType, SockaddrIn};
+ use nix::sys::socket::{getsockname, setsockopt, socket};
+ use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags};
+ use std::io::{IoSlice, IoSliceMut};
+
+ let lo_ifaddr = loopback_address(AddressFamily::Inet);
+ let (lo_name, lo) = match lo_ifaddr {
+ Some(ifaddr) => (
+ ifaddr.interface_name,
+ ifaddr.address.expect("Expect IPv4 address on interface"),
+ ),
+ None => return,
+ };
+ let receive = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("receive socket failed");
+ bind(receive.as_raw_fd(), &lo).expect("bind failed");
+ let sa: SockaddrIn =
+ getsockname(receive.as_raw_fd()).expect("getsockname failed");
+ setsockopt(&receive, Ipv4RecvIf, &true)
+ .expect("setsockopt IP_RECVIF failed");
+ setsockopt(&receive, Ipv4RecvDstAddr, &true)
+ .expect("setsockopt IP_RECVDSTADDR failed");
+
+ {
+ let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
+ let iov = [IoSlice::new(&slice)];
+
+ let send = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("send socket failed");
+ sendmsg(send.as_raw_fd(), &iov, &[], MsgFlags::empty(), Some(&sa))
+ .expect("sendmsg failed");
+ }
+
+ {
+ let mut buf = [0u8; 8];
+ let mut iovec = [IoSliceMut::new(&mut buf)];
+ let mut space = cmsg_space!(libc::sockaddr_dl, libc::in_addr);
+ let msg = recvmsg::<()>(
+ receive.as_raw_fd(),
+ &mut iovec,
+ Some(&mut space),
+ MsgFlags::empty(),
+ )
+ .expect("recvmsg failed");
+ assert!(!msg
+ .flags
+ .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
+ assert_eq!(msg.cmsgs().count(), 2, "expected 2 cmsgs");
+
+ let mut rx_recvif = false;
+ let mut rx_recvdstaddr = false;
+ for cmsg in msg.cmsgs() {
+ match cmsg {
+ ControlMessageOwned::Ipv4RecvIf(dl) => {
+ rx_recvif = true;
+ let i = if_nametoindex(lo_name.as_bytes())
+ .expect("if_nametoindex");
+ assert_eq!(
+ dl.sdl_index as libc::c_uint, i,
+ "unexpected ifindex (expected {}, got {})",
+ i, dl.sdl_index
+ );
+ }
+ ControlMessageOwned::Ipv4RecvDstAddr(addr) => {
+ rx_recvdstaddr = true;
+ if let Some(sin) = lo.as_sockaddr_in() {
+ assert_eq!(sin.as_ref().sin_addr.s_addr,
+ addr.s_addr,
+ "unexpected destination address (expected {}, got {})",
+ sin.as_ref().sin_addr.s_addr,
+ addr.s_addr);
+ } else {
+ panic!("unexpected Sockaddr");
+ }
+ }
+ _ => panic!("unexpected additional control msg"),
+ }
+ }
+ assert!(rx_recvif);
+ assert!(rx_recvdstaddr);
+ assert_eq!(msg.bytes, 8);
+ assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]);
+ }
+}
+
+#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+#[cfg_attr(qemu, ignore)]
+#[test]
+pub fn test_recvif_ipv4() {
+ use nix::sys::socket::sockopt::Ipv4OrigDstAddr;
+ use nix::sys::socket::{bind, SockFlag, SockType, SockaddrIn};
+ use nix::sys::socket::{getsockname, setsockopt, socket};
+ use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags};
+ use std::io::{IoSlice, IoSliceMut};
+
+ let lo_ifaddr = loopback_address(AddressFamily::Inet);
+ let (_lo_name, lo) = match lo_ifaddr {
+ Some(ifaddr) => (
+ ifaddr.interface_name,
+ ifaddr.address.expect("Expect IPv4 address on interface"),
+ ),
+ None => return,
+ };
+ let receive = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("receive socket failed");
+ bind(receive.as_raw_fd(), &lo).expect("bind failed");
+ let sa: SockaddrIn =
+ getsockname(receive.as_raw_fd()).expect("getsockname failed");
+ setsockopt(&receive, Ipv4OrigDstAddr, &true)
+ .expect("setsockopt IP_ORIGDSTADDR failed");
+
+ {
+ let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
+ let iov = [IoSlice::new(&slice)];
+
+ let send = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("send socket failed");
+ sendmsg(send.as_raw_fd(), &iov, &[], MsgFlags::empty(), Some(&sa))
+ .expect("sendmsg failed");
+ }
+
+ {
+ let mut buf = [0u8; 8];
+ let mut iovec = [IoSliceMut::new(&mut buf)];
+ let mut space = cmsg_space!(libc::sockaddr_in);
+ let msg = recvmsg::<()>(
+ receive.as_raw_fd(),
+ &mut iovec,
+ Some(&mut space),
+ MsgFlags::empty(),
+ )
+ .expect("recvmsg failed");
+ assert!(!msg
+ .flags
+ .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
+ assert_eq!(msg.cmsgs().count(), 1, "expected 1 cmsgs");
+
+ let mut rx_recvorigdstaddr = false;
+ for cmsg in msg.cmsgs() {
+ match cmsg {
+ ControlMessageOwned::Ipv4OrigDstAddr(addr) => {
+ rx_recvorigdstaddr = true;
+ if let Some(sin) = lo.as_sockaddr_in() {
+ assert_eq!(sin.as_ref().sin_addr.s_addr,
+ addr.sin_addr.s_addr,
+ "unexpected destination address (expected {}, got {})",
+ sin.as_ref().sin_addr.s_addr,
+ addr.sin_addr.s_addr);
+ } else {
+ panic!("unexpected Sockaddr");
+ }
+ }
+ _ => panic!("unexpected additional control msg"),
+ }
+ }
+ assert!(rx_recvorigdstaddr);
+ assert_eq!(msg.bytes, 8);
+ assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]);
+ }
+}
+
+#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+#[cfg_attr(qemu, ignore)]
+#[test]
+pub fn test_recvif_ipv6() {
+ use nix::sys::socket::sockopt::Ipv6OrigDstAddr;
+ use nix::sys::socket::{bind, SockFlag, SockType, SockaddrIn6};
+ use nix::sys::socket::{getsockname, setsockopt, socket};
+ use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags};
+ use std::io::{IoSlice, IoSliceMut};
+
+ let lo_ifaddr = loopback_address(AddressFamily::Inet6);
+ let (_lo_name, lo) = match lo_ifaddr {
+ Some(ifaddr) => (
+ ifaddr.interface_name,
+ ifaddr.address.expect("Expect IPv6 address on interface"),
+ ),
+ None => return,
+ };
+ let receive = socket(
+ AddressFamily::Inet6,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("receive socket failed");
+ bind(receive.as_raw_fd(), &lo).expect("bind failed");
+ let sa: SockaddrIn6 =
+ getsockname(receive.as_raw_fd()).expect("getsockname failed");
+ setsockopt(&receive, Ipv6OrigDstAddr, &true)
+ .expect("setsockopt IP_ORIGDSTADDR failed");
+
+ {
+ let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
+ let iov = [IoSlice::new(&slice)];
+
+ let send = socket(
+ AddressFamily::Inet6,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("send socket failed");
+ sendmsg(send.as_raw_fd(), &iov, &[], MsgFlags::empty(), Some(&sa))
+ .expect("sendmsg failed");
+ }
+
+ {
+ let mut buf = [0u8; 8];
+ let mut iovec = [IoSliceMut::new(&mut buf)];
+ let mut space = cmsg_space!(libc::sockaddr_in6);
+ let msg = recvmsg::<()>(
+ receive.as_raw_fd(),
+ &mut iovec,
+ Some(&mut space),
+ MsgFlags::empty(),
+ )
+ .expect("recvmsg failed");
+ assert!(!msg
+ .flags
+ .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
+ assert_eq!(msg.cmsgs().count(), 1, "expected 1 cmsgs");
+
+ let mut rx_recvorigdstaddr = false;
+ for cmsg in msg.cmsgs() {
+ match cmsg {
+ ControlMessageOwned::Ipv6OrigDstAddr(addr) => {
+ rx_recvorigdstaddr = true;
+ if let Some(sin) = lo.as_sockaddr_in6() {
+ assert_eq!(sin.as_ref().sin6_addr.s6_addr,
+ addr.sin6_addr.s6_addr,
+ "unexpected destination address (expected {:?}, got {:?})",
+ sin.as_ref().sin6_addr.s6_addr,
+ addr.sin6_addr.s6_addr);
+ } else {
+ panic!("unexpected Sockaddr");
+ }
+ }
+ _ => panic!("unexpected additional control msg"),
+ }
+ }
+ assert!(rx_recvorigdstaddr);
+ assert_eq!(msg.bytes, 8);
+ assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]);
+ }
+}
+
+#[cfg(any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+ target_os = "netbsd",
+ target_os = "openbsd",
+))]
+// qemu doesn't seem to be emulating this correctly in these architectures
+#[cfg_attr(
+ all(
+ qemu,
+ any(
+ target_arch = "mips",
+ target_arch = "mips64",
+ target_arch = "powerpc64",
+ )
+ ),
+ ignore
+)]
+#[test]
+pub fn test_recv_ipv6pktinfo() {
+ use nix::net::if_::*;
+ use nix::sys::socket::sockopt::Ipv6RecvPacketInfo;
+ use nix::sys::socket::{bind, SockFlag, SockType, SockaddrIn6};
+ use nix::sys::socket::{getsockname, setsockopt, socket};
+ use nix::sys::socket::{recvmsg, sendmsg, ControlMessageOwned, MsgFlags};
+ use std::io::{IoSlice, IoSliceMut};
+
+ let lo_ifaddr = loopback_address(AddressFamily::Inet6);
+ let (lo_name, lo) = match lo_ifaddr {
+ Some(ifaddr) => (
+ ifaddr.interface_name,
+ ifaddr.address.expect("Expect IPv6 address on interface"),
+ ),
+ None => return,
+ };
+ let receive = socket(
+ AddressFamily::Inet6,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("receive socket failed");
+ bind(receive.as_raw_fd(), &lo).expect("bind failed");
+ let sa: SockaddrIn6 =
+ getsockname(receive.as_raw_fd()).expect("getsockname failed");
+ setsockopt(&receive, Ipv6RecvPacketInfo, &true).expect("setsockopt failed");
+
+ {
+ let slice = [1u8, 2, 3, 4, 5, 6, 7, 8];
+ let iov = [IoSlice::new(&slice)];
+
+ let send = socket(
+ AddressFamily::Inet6,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("send socket failed");
+ sendmsg(send.as_raw_fd(), &iov, &[], MsgFlags::empty(), Some(&sa))
+ .expect("sendmsg failed");
+ }
+
+ {
+ let mut buf = [0u8; 8];
+ let mut iovec = [IoSliceMut::new(&mut buf)];
+
+ let mut space = cmsg_space!(libc::in6_pktinfo);
+ let msg = recvmsg::<()>(
+ receive.as_raw_fd(),
+ &mut iovec,
+ Some(&mut space),
+ MsgFlags::empty(),
+ )
+ .expect("recvmsg failed");
+ assert!(!msg
+ .flags
+ .intersects(MsgFlags::MSG_TRUNC | MsgFlags::MSG_CTRUNC));
+
+ let mut cmsgs = msg.cmsgs();
+ if let Some(ControlMessageOwned::Ipv6PacketInfo(pktinfo)) = cmsgs.next()
+ {
+ let i = if_nametoindex(lo_name.as_bytes()).expect("if_nametoindex");
+ assert_eq!(
+ pktinfo.ipi6_ifindex as libc::c_uint, i,
+ "unexpected ifindex (expected {}, got {})",
+ i, pktinfo.ipi6_ifindex
+ );
+ }
+ assert!(cmsgs.next().is_none(), "unexpected additional control msg");
+ assert_eq!(msg.bytes, 8);
+ assert_eq!(*iovec[0], [1u8, 2, 3, 4, 5, 6, 7, 8]);
+ }
+}
+
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[test]
+pub fn test_vsock() {
+ use nix::sys::socket::SockaddrLike;
+ use nix::sys::socket::{AddressFamily, VsockAddr};
+ use std::mem;
+
+ let port: u32 = 3000;
+
+ let addr_local = VsockAddr::new(libc::VMADDR_CID_LOCAL, port);
+ assert_eq!(addr_local.cid(), libc::VMADDR_CID_LOCAL);
+ assert_eq!(addr_local.port(), port);
+
+ let addr_any = VsockAddr::new(libc::VMADDR_CID_ANY, libc::VMADDR_PORT_ANY);
+ assert_eq!(addr_any.cid(), libc::VMADDR_CID_ANY);
+ assert_eq!(addr_any.port(), libc::VMADDR_PORT_ANY);
+
+ assert_ne!(addr_local, addr_any);
+ assert_ne!(calculate_hash(&addr_local), calculate_hash(&addr_any));
+
+ let addr1 = VsockAddr::new(libc::VMADDR_CID_HOST, port);
+ let addr2 = VsockAddr::new(libc::VMADDR_CID_HOST, port);
+ assert_eq!(addr1, addr2);
+ assert_eq!(calculate_hash(&addr1), calculate_hash(&addr2));
+
+ let addr3 = unsafe {
+ VsockAddr::from_raw(
+ addr2.as_ref() as *const libc::sockaddr_vm as *const libc::sockaddr,
+ Some(mem::size_of::<libc::sockaddr_vm>().try_into().unwrap()),
+ )
+ }
+ .unwrap();
+ assert_eq!(
+ addr3.as_ref().svm_family,
+ AddressFamily::Vsock as libc::sa_family_t
+ );
+ assert_eq!(addr3.as_ref().svm_cid, addr1.cid());
+ assert_eq!(addr3.as_ref().svm_port, addr1.port());
+}
+
+#[cfg(target_os = "macos")]
+#[test]
+pub fn test_vsock() {
+ use nix::sys::socket::SockaddrLike;
+ use nix::sys::socket::{AddressFamily, VsockAddr};
+ use std::mem;
+
+ let port: u32 = 3000;
+
+ // macOS doesn't have a VMADDR_CID_LOCAL, so test with host again
+ let addr_host = VsockAddr::new(libc::VMADDR_CID_HOST, port);
+ assert_eq!(addr_host.cid(), libc::VMADDR_CID_HOST);
+ assert_eq!(addr_host.port(), port);
+
+ let addr_any = VsockAddr::new(libc::VMADDR_CID_ANY, libc::VMADDR_PORT_ANY);
+ assert_eq!(addr_any.cid(), libc::VMADDR_CID_ANY);
+ assert_eq!(addr_any.port(), libc::VMADDR_PORT_ANY);
+
+ assert_ne!(addr_host, addr_any);
+ assert_ne!(calculate_hash(&addr_host), calculate_hash(&addr_any));
+
+ let addr1 = VsockAddr::new(libc::VMADDR_CID_HOST, port);
+ let addr2 = VsockAddr::new(libc::VMADDR_CID_HOST, port);
+ assert_eq!(addr1, addr2);
+ assert_eq!(calculate_hash(&addr1), calculate_hash(&addr2));
+
+ let addr3 = unsafe {
+ VsockAddr::from_raw(
+ addr2.as_ref() as *const libc::sockaddr_vm as *const libc::sockaddr,
+ Some(mem::size_of::<libc::sockaddr_vm>().try_into().unwrap()),
+ )
+ }
+ .unwrap();
+ assert_eq!(
+ addr3.as_ref().svm_family,
+ AddressFamily::Vsock as libc::sa_family_t
+ );
+ let cid = addr3.as_ref().svm_cid;
+ let port = addr3.as_ref().svm_port;
+ assert_eq!(cid, addr1.cid());
+ assert_eq!(port, addr1.port());
+}
+
+// Disable the test on emulated platforms because it fails in Cirrus-CI. Lack
+// of QEMU support is suspected.
+#[cfg_attr(qemu, ignore)]
+#[cfg(target_os = "linux")]
+#[test]
+fn test_recvmsg_timestampns() {
+ use nix::sys::socket::*;
+ use nix::sys::time::*;
+ use std::io::{IoSlice, IoSliceMut};
+ use std::time::*;
+
+ // Set up
+ let message = "Ohayō!".as_bytes();
+ let in_socket = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+ setsockopt(&in_socket, sockopt::ReceiveTimestampns, &true).unwrap();
+ let localhost = SockaddrIn::new(127, 0, 0, 1, 0);
+ bind(in_socket.as_raw_fd(), &localhost).unwrap();
+ let address: SockaddrIn = getsockname(in_socket.as_raw_fd()).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.as_raw_fd(), &iov, &[], flags, Some(&address))
+ .unwrap();
+ assert_eq!(message.len(), l);
+ // Receive the message
+ let mut buffer = vec![0u8; message.len()];
+ let mut cmsgspace = nix::cmsg_space!(TimeSpec);
+
+ let mut iov = [IoSliceMut::new(&mut buffer)];
+ let r = recvmsg::<()>(
+ in_socket.as_raw_fd(),
+ &mut iov,
+ Some(&mut cmsgspace),
+ flags,
+ )
+ .unwrap();
+ let rtime = match r.cmsgs().next() {
+ Some(ControlMessageOwned::ScmTimestampns(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_nsec() as u32);
+ assert!(time0.duration_since(UNIX_EPOCH).unwrap() <= rduration);
+ assert!(rduration <= time1.duration_since(UNIX_EPOCH).unwrap());
+}
+
+// Disable the test on emulated platforms because it fails in Cirrus-CI. Lack
+// of QEMU support is suspected.
+#[cfg_attr(qemu, ignore)]
+#[cfg(target_os = "linux")]
+#[test]
+fn test_recvmmsg_timestampns() {
+ use nix::sys::socket::*;
+ use nix::sys::time::*;
+ use std::io::{IoSlice, IoSliceMut};
+ use std::time::*;
+
+ // Set up
+ let message = "Ohayō!".as_bytes();
+ let in_socket = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+ setsockopt(&in_socket, sockopt::ReceiveTimestampns, &true).unwrap();
+ let localhost = SockaddrIn::from_str("127.0.0.1:0").unwrap();
+ bind(in_socket.as_raw_fd(), &localhost).unwrap();
+ let address: SockaddrIn = getsockname(in_socket.as_raw_fd()).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.as_raw_fd(), &iov, &[], flags, Some(&address))
+ .unwrap();
+ assert_eq!(message.len(), l);
+ // Receive the message
+ let mut buffer = vec![0u8; message.len()];
+ let cmsgspace = nix::cmsg_space!(TimeSpec);
+ let iov = vec![[IoSliceMut::new(&mut buffer)]];
+ let mut data = MultiHeaders::preallocate(1, Some(cmsgspace));
+ let r: Vec<RecvMsg<()>> =
+ recvmmsg(in_socket.as_raw_fd(), &mut data, iov.iter(), flags, None)
+ .unwrap()
+ .collect();
+ let rtime = match r[0].cmsgs().next() {
+ Some(ControlMessageOwned::ScmTimestampns(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_nsec() as u32);
+ assert!(time0.duration_since(UNIX_EPOCH).unwrap() <= rduration);
+ assert!(rduration <= time1.duration_since(UNIX_EPOCH).unwrap());
+}
+
+// Disable the test on emulated platforms because it fails in Cirrus-CI. Lack
+// of QEMU support is suspected.
+#[cfg_attr(qemu, ignore)]
+#[cfg(any(target_os = "android", target_os = "fuchsia", target_os = "linux"))]
+#[test]
+fn test_recvmsg_rxq_ovfl() {
+ use nix::sys::socket::sockopt::{RcvBuf, RxqOvfl};
+ use nix::sys::socket::*;
+ use nix::Error;
+ use std::io::{IoSlice, IoSliceMut};
+
+ let message = [0u8; 2048];
+ let bufsize = message.len() * 2;
+
+ let in_socket = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+ let out_socket = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+
+ let localhost = SockaddrIn::from_str("127.0.0.1:0").unwrap();
+ bind(in_socket.as_raw_fd(), &localhost).unwrap();
+
+ let address: SockaddrIn = getsockname(in_socket.as_raw_fd()).unwrap();
+ connect(out_socket.as_raw_fd(), &address).unwrap();
+
+ // Set SO_RXQ_OVFL flag.
+ setsockopt(&in_socket, RxqOvfl, &1).unwrap();
+
+ // Set the receiver buffer size to hold only 2 messages.
+ setsockopt(&in_socket, RcvBuf, &bufsize).unwrap();
+
+ let mut drop_counter = 0;
+
+ for _ in 0..2 {
+ let iov = [IoSlice::new(&message)];
+ let flags = MsgFlags::empty();
+
+ // Send the 3 messages (the receiver buffer can only hold 2 messages)
+ // to create an overflow.
+ for _ in 0..3 {
+ let l = sendmsg(
+ out_socket.as_raw_fd(),
+ &iov,
+ &[],
+ flags,
+ Some(&address),
+ )
+ .unwrap();
+ assert_eq!(message.len(), l);
+ }
+
+ // Receive the message and check the drop counter if any.
+ loop {
+ let mut buffer = vec![0u8; message.len()];
+ let mut cmsgspace = nix::cmsg_space!(u32);
+
+ let mut iov = [IoSliceMut::new(&mut buffer)];
+
+ match recvmsg::<()>(
+ in_socket.as_raw_fd(),
+ &mut iov,
+ Some(&mut cmsgspace),
+ MsgFlags::MSG_DONTWAIT,
+ ) {
+ Ok(r) => {
+ drop_counter = match r.cmsgs().next() {
+ Some(ControlMessageOwned::RxqOvfl(drop_counter)) => {
+ drop_counter
+ }
+ Some(_) => panic!("Unexpected control message"),
+ None => 0,
+ };
+ }
+ Err(Error::EAGAIN) => {
+ break;
+ }
+ _ => {
+ panic!("unknown recvmsg() error");
+ }
+ }
+ }
+ }
+
+ // One packet lost.
+ assert_eq!(drop_counter, 1);
+}
+
+#[cfg(any(target_os = "linux", target_os = "android",))]
+mod linux_errqueue {
+ use super::FromStr;
+ use nix::sys::socket::*;
+ use std::os::unix::io::AsRawFd;
+
+ // Send a UDP datagram to a bogus destination address and observe an ICMP error (v4).
+ //
+ // Disable the test on QEMU because QEMU emulation of IP_RECVERR is broken (as documented on PR
+ // #1514).
+ #[cfg_attr(qemu, ignore)]
+ #[test]
+ fn test_recverr_v4() {
+ #[repr(u8)]
+ enum IcmpTypes {
+ DestUnreach = 3, // ICMP_DEST_UNREACH
+ }
+ #[repr(u8)]
+ enum IcmpUnreachCodes {
+ PortUnreach = 3, // ICMP_PORT_UNREACH
+ }
+
+ test_recverr_impl::<sockaddr_in, _, _>(
+ "127.0.0.1:6800",
+ AddressFamily::Inet,
+ sockopt::Ipv4RecvErr,
+ libc::SO_EE_ORIGIN_ICMP,
+ IcmpTypes::DestUnreach as u8,
+ IcmpUnreachCodes::PortUnreach as u8,
+ // Closure handles protocol-specific testing and returns generic sock_extended_err for
+ // protocol-independent test impl.
+ |cmsg| {
+ if let ControlMessageOwned::Ipv4RecvErr(ext_err, err_addr) =
+ cmsg
+ {
+ if let Some(origin) = err_addr {
+ // Validate that our network error originated from 127.0.0.1:0.
+ assert_eq!(origin.sin_family, AddressFamily::Inet as _);
+ assert_eq!(
+ origin.sin_addr.s_addr,
+ u32::from_be(0x7f000001)
+ );
+ assert_eq!(origin.sin_port, 0);
+ } else {
+ panic!("Expected some error origin");
+ }
+ *ext_err
+ } else {
+ panic!("Unexpected control message {cmsg:?}");
+ }
+ },
+ )
+ }
+
+ // Essentially the same test as v4.
+ //
+ // Disable the test on QEMU because QEMU emulation of IPV6_RECVERR is broken (as documented on
+ // PR #1514).
+ #[cfg_attr(qemu, ignore)]
+ #[test]
+ fn test_recverr_v6() {
+ #[repr(u8)]
+ enum IcmpV6Types {
+ DestUnreach = 1, // ICMPV6_DEST_UNREACH
+ }
+ #[repr(u8)]
+ enum IcmpV6UnreachCodes {
+ PortUnreach = 4, // ICMPV6_PORT_UNREACH
+ }
+
+ test_recverr_impl::<sockaddr_in6, _, _>(
+ "[::1]:6801",
+ AddressFamily::Inet6,
+ sockopt::Ipv6RecvErr,
+ libc::SO_EE_ORIGIN_ICMP6,
+ IcmpV6Types::DestUnreach as u8,
+ IcmpV6UnreachCodes::PortUnreach as u8,
+ // Closure handles protocol-specific testing and returns generic sock_extended_err for
+ // protocol-independent test impl.
+ |cmsg| {
+ if let ControlMessageOwned::Ipv6RecvErr(ext_err, err_addr) =
+ cmsg
+ {
+ if let Some(origin) = err_addr {
+ // Validate that our network error originated from localhost:0.
+ assert_eq!(
+ origin.sin6_family,
+ AddressFamily::Inet6 as _
+ );
+ assert_eq!(
+ origin.sin6_addr.s6_addr,
+ std::net::Ipv6Addr::LOCALHOST.octets()
+ );
+ assert_eq!(origin.sin6_port, 0);
+ } else {
+ panic!("Expected some error origin");
+ }
+ *ext_err
+ } else {
+ panic!("Unexpected control message {cmsg:?}");
+ }
+ },
+ )
+ }
+
+ fn test_recverr_impl<SA, OPT, TESTF>(
+ sa: &str,
+ af: AddressFamily,
+ opt: OPT,
+ ee_origin: u8,
+ ee_type: u8,
+ ee_code: u8,
+ testf: TESTF,
+ ) where
+ OPT: SetSockOpt<Val = bool>,
+ TESTF: FnOnce(&ControlMessageOwned) -> libc::sock_extended_err,
+ {
+ use nix::errno::Errno;
+ use std::io::IoSliceMut;
+
+ const MESSAGE_CONTENTS: &str = "ABCDEF";
+ let std_sa = std::net::SocketAddr::from_str(sa).unwrap();
+ let sock_addr = SockaddrStorage::from(std_sa);
+ let sock = socket(af, SockType::Datagram, SockFlag::SOCK_CLOEXEC, None)
+ .unwrap();
+ setsockopt(&sock, opt, &true).unwrap();
+ if let Err(e) = sendto(
+ sock.as_raw_fd(),
+ MESSAGE_CONTENTS.as_bytes(),
+ &sock_addr,
+ MsgFlags::empty(),
+ ) {
+ assert_eq!(e, Errno::EADDRNOTAVAIL);
+ println!("{af:?} not available, skipping test.");
+ return;
+ }
+
+ let mut buf = [0u8; 8];
+ let mut iovec = [IoSliceMut::new(&mut buf)];
+ let mut cspace = cmsg_space!(libc::sock_extended_err, SA);
+
+ let msg = recvmsg(
+ sock.as_raw_fd(),
+ &mut iovec,
+ Some(&mut cspace),
+ MsgFlags::MSG_ERRQUEUE,
+ )
+ .unwrap();
+ // The sent message / destination associated with the error is returned:
+ assert_eq!(msg.bytes, MESSAGE_CONTENTS.as_bytes().len());
+ // recvmsg(2): "The original destination address of the datagram that caused the error is
+ // supplied via msg_name;" however, this is not literally true. E.g., an earlier version
+ // of this test used 0.0.0.0 (::0) as the destination address, which was mutated into
+ // 127.0.0.1 (::1).
+ assert_eq!(msg.address, Some(sock_addr));
+
+ // Check for expected control message.
+ let ext_err = match msg.cmsgs().next() {
+ Some(cmsg) => testf(&cmsg),
+ None => panic!("No control message"),
+ };
+
+ assert_eq!(ext_err.ee_errno, libc::ECONNREFUSED as u32);
+ assert_eq!(ext_err.ee_origin, ee_origin);
+ // ip(7): ee_type and ee_code are set from the type and code fields of the ICMP (ICMPv6)
+ // header.
+ assert_eq!(ext_err.ee_type, ee_type);
+ assert_eq!(ext_err.ee_code, ee_code);
+ // ip(7): ee_info contains the discovered MTU for EMSGSIZE errors.
+ assert_eq!(ext_err.ee_info, 0);
+
+ let bytes = msg.bytes;
+ assert_eq!(&buf[..bytes], MESSAGE_CONTENTS.as_bytes());
+ }
+}
+
+// Disable the test on emulated platforms because it fails in Cirrus-CI. Lack
+// of QEMU support is suspected.
+#[cfg_attr(qemu, ignore)]
+#[cfg(target_os = "linux")]
+#[test]
+pub fn test_txtime() {
+ use nix::sys::socket::{
+ bind, recvmsg, sendmsg, setsockopt, socket, sockopt, ControlMessage,
+ MsgFlags, SockFlag, SockType, SockaddrIn,
+ };
+ use nix::sys::time::TimeValLike;
+ use nix::time::{clock_gettime, ClockId};
+
+ require_kernel_version!(test_txtime, ">= 5.8");
+
+ let sock_addr = SockaddrIn::from_str("127.0.0.1:6802").unwrap();
+
+ let ssock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .expect("send socket failed");
+
+ let txtime_cfg = libc::sock_txtime {
+ clockid: libc::CLOCK_MONOTONIC,
+ flags: 0,
+ };
+ setsockopt(&ssock, sockopt::TxTime, &txtime_cfg).unwrap();
+
+ let rsock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+ bind(rsock.as_raw_fd(), &sock_addr).unwrap();
+
+ let sbuf = [0u8; 2048];
+ let iov1 = [std::io::IoSlice::new(&sbuf)];
+
+ let now = clock_gettime(ClockId::CLOCK_MONOTONIC).unwrap();
+ let delay = std::time::Duration::from_secs(1).into();
+ let txtime = (now + delay).num_nanoseconds() as u64;
+
+ let cmsg = ControlMessage::TxTime(&txtime);
+ sendmsg(
+ ssock.as_raw_fd(),
+ &iov1,
+ &[cmsg],
+ MsgFlags::empty(),
+ Some(&sock_addr),
+ )
+ .unwrap();
+
+ let mut rbuf = [0u8; 2048];
+ let mut iov2 = [std::io::IoSliceMut::new(&mut rbuf)];
+ recvmsg::<()>(rsock.as_raw_fd(), &mut iov2, None, MsgFlags::empty())
+ .unwrap();
+}
diff --git a/third_party/rust/nix/test/sys/test_sockopt.rs b/third_party/rust/nix/test/sys/test_sockopt.rs
new file mode 100644
index 0000000000..0e34917325
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_sockopt.rs
@@ -0,0 +1,448 @@
+#[cfg(any(target_os = "android", target_os = "linux"))]
+use crate::*;
+use nix::sys::socket::{
+ getsockopt, setsockopt, socket, sockopt, AddressFamily, SockFlag,
+ SockProtocol, SockType,
+};
+use rand::{thread_rng, Rng};
+use std::os::unix::io::AsRawFd;
+
+// NB: FreeBSD supports LOCAL_PEERCRED for SOCK_SEQPACKET, but OSX does not.
+#[cfg(any(target_os = "dragonfly", target_os = "freebsd",))]
+#[test]
+pub fn test_local_peercred_seqpacket() {
+ use nix::{
+ sys::socket::socketpair,
+ unistd::{Gid, Uid},
+ };
+
+ let (fd1, _fd2) = socketpair(
+ AddressFamily::Unix,
+ SockType::SeqPacket,
+ None,
+ SockFlag::empty(),
+ )
+ .unwrap();
+ let xucred = getsockopt(&fd1, sockopt::LocalPeerCred).unwrap();
+ assert_eq!(xucred.version(), 0);
+ assert_eq!(Uid::from_raw(xucred.uid()), Uid::current());
+ assert_eq!(Gid::from_raw(xucred.groups()[0]), Gid::current());
+}
+
+#[cfg(any(
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "macos",
+ target_os = "ios"
+))]
+#[test]
+pub fn test_local_peercred_stream() {
+ use nix::{
+ sys::socket::socketpair,
+ unistd::{Gid, Uid},
+ };
+
+ let (fd1, _fd2) = socketpair(
+ AddressFamily::Unix,
+ SockType::Stream,
+ None,
+ SockFlag::empty(),
+ )
+ .unwrap();
+ let xucred = getsockopt(&fd1, sockopt::LocalPeerCred).unwrap();
+ assert_eq!(xucred.version(), 0);
+ assert_eq!(Uid::from_raw(xucred.uid()), Uid::current());
+ assert_eq!(Gid::from_raw(xucred.groups()[0]), Gid::current());
+}
+
+#[cfg(any(target_os = "ios", target_os = "macos"))]
+#[test]
+pub fn test_local_peer_pid() {
+ use nix::sys::socket::socketpair;
+
+ let (fd1, _fd2) = socketpair(
+ AddressFamily::Unix,
+ SockType::Stream,
+ None,
+ SockFlag::empty(),
+ )
+ .unwrap();
+ let pid = getsockopt(&fd1, sockopt::LocalPeerPid).unwrap();
+ assert_eq!(pid, std::process::id() as _);
+}
+
+#[cfg(target_os = "linux")]
+#[test]
+fn is_so_mark_functional() {
+ use nix::sys::socket::sockopt;
+
+ require_capability!("is_so_mark_functional", CAP_NET_ADMIN);
+
+ let s = socket(
+ AddressFamily::Inet,
+ SockType::Stream,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+ setsockopt(&s, sockopt::Mark, &1337).unwrap();
+ let mark = getsockopt(&s, sockopt::Mark).unwrap();
+ assert_eq!(mark, 1337);
+}
+
+#[test]
+fn test_so_buf() {
+ let fd = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ SockProtocol::Udp,
+ )
+ .unwrap();
+ let bufsize: usize = thread_rng().gen_range(4096..131_072);
+ setsockopt(&fd, sockopt::SndBuf, &bufsize).unwrap();
+ let actual = getsockopt(&fd, sockopt::SndBuf).unwrap();
+ assert!(actual >= bufsize);
+ setsockopt(&fd, sockopt::RcvBuf, &bufsize).unwrap();
+ let actual = getsockopt(&fd, sockopt::RcvBuf).unwrap();
+ assert!(actual >= bufsize);
+}
+
+#[test]
+fn test_so_tcp_maxseg() {
+ use nix::sys::socket::{accept, bind, connect, listen, SockaddrIn};
+ use nix::unistd::write;
+ use std::net::SocketAddrV4;
+ use std::str::FromStr;
+
+ let std_sa = SocketAddrV4::from_str("127.0.0.1:4001").unwrap();
+ let sock_addr = SockaddrIn::from(std_sa);
+
+ let rsock = socket(
+ AddressFamily::Inet,
+ SockType::Stream,
+ SockFlag::empty(),
+ SockProtocol::Tcp,
+ )
+ .unwrap();
+ bind(rsock.as_raw_fd(), &sock_addr).unwrap();
+ listen(&rsock, 10).unwrap();
+ let initial = getsockopt(&rsock, sockopt::TcpMaxSeg).unwrap();
+ // Initial MSS is expected to be 536 (https://tools.ietf.org/html/rfc879#section-1) but some
+ // platforms keep it even lower. This might fail if you've tuned your initial MSS to be larger
+ // than 700
+ cfg_if! {
+ if #[cfg(any(target_os = "android", target_os = "linux"))] {
+ let segsize: u32 = 873;
+ assert!(initial < segsize);
+ setsockopt(&rsock, sockopt::TcpMaxSeg, &segsize).unwrap();
+ } else {
+ assert!(initial < 700);
+ }
+ }
+
+ // Connect and check the MSS that was advertised
+ let ssock = socket(
+ AddressFamily::Inet,
+ SockType::Stream,
+ SockFlag::empty(),
+ SockProtocol::Tcp,
+ )
+ .unwrap();
+ connect(ssock.as_raw_fd(), &sock_addr).unwrap();
+ let rsess = accept(rsock.as_raw_fd()).unwrap();
+ write(rsess, b"hello").unwrap();
+ let actual = getsockopt(&ssock, sockopt::TcpMaxSeg).unwrap();
+ // Actual max segment size takes header lengths into account, max IPv4 options (60 bytes) + max
+ // TCP options (40 bytes) are subtracted from the requested maximum as a lower boundary.
+ cfg_if! {
+ if #[cfg(any(target_os = "android", target_os = "linux"))] {
+ assert!((segsize - 100) <= actual);
+ assert!(actual <= segsize);
+ } else {
+ assert!(initial < actual);
+ assert!(536 < actual);
+ }
+ }
+}
+
+#[test]
+fn test_so_type() {
+ let sockfd = socket(
+ AddressFamily::Inet,
+ SockType::Stream,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+
+ assert_eq!(Ok(SockType::Stream), getsockopt(&sockfd, sockopt::SockType));
+}
+
+/// getsockopt(_, sockopt::SockType) should gracefully handle unknown socket
+/// types. Regression test for https://github.com/nix-rust/nix/issues/1819
+#[cfg(any(target_os = "android", target_os = "linux",))]
+#[test]
+fn test_so_type_unknown() {
+ use nix::errno::Errno;
+ use std::os::unix::io::{FromRawFd, OwnedFd};
+
+ require_capability!("test_so_type", CAP_NET_RAW);
+ let raw_fd = unsafe { libc::socket(libc::AF_PACKET, libc::SOCK_PACKET, 0) };
+ assert!(raw_fd >= 0, "Error opening socket: {}", nix::Error::last());
+ let sockfd = unsafe { OwnedFd::from_raw_fd(raw_fd) };
+
+ assert_eq!(Err(Errno::EINVAL), getsockopt(&sockfd, sockopt::SockType));
+}
+
+// The CI doesn't supported getsockopt and setsockopt on emulated processors.
+// It's believed that a QEMU issue, the tests run ok on a fully emulated system.
+// Current CI just run the binary with QEMU but the Kernel remains the same as the host.
+// So the syscall doesn't work properly unless the kernel is also emulated.
+#[test]
+#[cfg(all(
+ any(target_arch = "x86", target_arch = "x86_64"),
+ any(target_os = "freebsd", target_os = "linux")
+))]
+fn test_tcp_congestion() {
+ use std::ffi::OsString;
+
+ let fd = socket(
+ AddressFamily::Inet,
+ SockType::Stream,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+
+ let val = getsockopt(&fd, sockopt::TcpCongestion).unwrap();
+ setsockopt(&fd, sockopt::TcpCongestion, &val).unwrap();
+
+ setsockopt(
+ &fd,
+ sockopt::TcpCongestion,
+ &OsString::from("tcp_congestion_does_not_exist"),
+ )
+ .unwrap_err();
+
+ assert_eq!(getsockopt(&fd, sockopt::TcpCongestion).unwrap(), val);
+}
+
+#[test]
+#[cfg(any(target_os = "android", target_os = "linux"))]
+fn test_bindtodevice() {
+ skip_if_not_root!("test_bindtodevice");
+
+ let fd = socket(
+ AddressFamily::Inet,
+ SockType::Stream,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+
+ let val = getsockopt(&fd, sockopt::BindToDevice).unwrap();
+ setsockopt(&fd, sockopt::BindToDevice, &val).unwrap();
+
+ assert_eq!(getsockopt(&fd, sockopt::BindToDevice).unwrap(), val);
+}
+
+#[test]
+fn test_so_tcp_keepalive() {
+ let fd = socket(
+ AddressFamily::Inet,
+ SockType::Stream,
+ SockFlag::empty(),
+ SockProtocol::Tcp,
+ )
+ .unwrap();
+ setsockopt(&fd, sockopt::KeepAlive, &true).unwrap();
+ assert!(getsockopt(&fd, sockopt::KeepAlive).unwrap());
+
+ #[cfg(any(
+ target_os = "android",
+ target_os = "dragonfly",
+ target_os = "freebsd",
+ target_os = "linux"
+ ))]
+ {
+ let x = getsockopt(&fd, sockopt::TcpKeepIdle).unwrap();
+ setsockopt(&fd, sockopt::TcpKeepIdle, &(x + 1)).unwrap();
+ assert_eq!(getsockopt(&fd, sockopt::TcpKeepIdle).unwrap(), x + 1);
+
+ let x = getsockopt(&fd, sockopt::TcpKeepCount).unwrap();
+ setsockopt(&fd, sockopt::TcpKeepCount, &(x + 1)).unwrap();
+ assert_eq!(getsockopt(&fd, sockopt::TcpKeepCount).unwrap(), x + 1);
+
+ let x = getsockopt(&fd, sockopt::TcpKeepInterval).unwrap();
+ setsockopt(&fd, sockopt::TcpKeepInterval, &(x + 1)).unwrap();
+ assert_eq!(getsockopt(&fd, sockopt::TcpKeepInterval).unwrap(), x + 1);
+ }
+}
+
+#[test]
+#[cfg(any(target_os = "android", target_os = "linux"))]
+#[cfg_attr(qemu, ignore)]
+fn test_get_mtu() {
+ use nix::sys::socket::{bind, connect, SockaddrIn};
+ use std::net::SocketAddrV4;
+ use std::str::FromStr;
+
+ let std_sa = SocketAddrV4::from_str("127.0.0.1:4001").unwrap();
+ let std_sb = SocketAddrV4::from_str("127.0.0.1:4002").unwrap();
+
+ let usock = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ SockProtocol::Udp,
+ )
+ .unwrap();
+
+ // Bind and initiate connection
+ bind(usock.as_raw_fd(), &SockaddrIn::from(std_sa)).unwrap();
+ connect(usock.as_raw_fd(), &SockaddrIn::from(std_sb)).unwrap();
+
+ // Loopback connections have 2^16 - the maximum - MTU
+ assert_eq!(getsockopt(&usock, sockopt::IpMtu), Ok(u16::MAX as i32))
+}
+
+#[test]
+#[cfg(any(target_os = "android", target_os = "freebsd", target_os = "linux"))]
+fn test_ttl_opts() {
+ let fd4 = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+ setsockopt(&fd4, sockopt::Ipv4Ttl, &1)
+ .expect("setting ipv4ttl on an inet socket should succeed");
+ let fd6 = socket(
+ AddressFamily::Inet6,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+ setsockopt(&fd6, sockopt::Ipv6Ttl, &1)
+ .expect("setting ipv6ttl on an inet6 socket should succeed");
+}
+
+#[test]
+#[cfg(any(target_os = "ios", target_os = "macos"))]
+fn test_dontfrag_opts() {
+ let fd4 = socket(
+ AddressFamily::Inet,
+ SockType::Stream,
+ SockFlag::empty(),
+ SockProtocol::Tcp,
+ )
+ .unwrap();
+ setsockopt(&fd4, sockopt::IpDontFrag, &true)
+ .expect("setting IP_DONTFRAG on an inet stream socket should succeed");
+ setsockopt(&fd4, sockopt::IpDontFrag, &false).expect(
+ "unsetting IP_DONTFRAG on an inet stream socket should succeed",
+ );
+ let fd4d = socket(
+ AddressFamily::Inet,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+ setsockopt(&fd4d, sockopt::IpDontFrag, &true).expect(
+ "setting IP_DONTFRAG on an inet datagram socket should succeed",
+ );
+ setsockopt(&fd4d, sockopt::IpDontFrag, &false).expect(
+ "unsetting IP_DONTFRAG on an inet datagram socket should succeed",
+ );
+}
+
+#[test]
+#[cfg(any(
+ target_os = "android",
+ target_os = "ios",
+ target_os = "linux",
+ target_os = "macos",
+))]
+// Disable the test under emulation because it fails in Cirrus-CI. Lack
+// of QEMU support is suspected.
+#[cfg_attr(qemu, ignore)]
+fn test_v6dontfrag_opts() {
+ let fd6 = socket(
+ AddressFamily::Inet6,
+ SockType::Stream,
+ SockFlag::empty(),
+ SockProtocol::Tcp,
+ )
+ .unwrap();
+ setsockopt(&fd6, sockopt::Ipv6DontFrag, &true).expect(
+ "setting IPV6_DONTFRAG on an inet6 stream socket should succeed",
+ );
+ setsockopt(&fd6, sockopt::Ipv6DontFrag, &false).expect(
+ "unsetting IPV6_DONTFRAG on an inet6 stream socket should succeed",
+ );
+ let fd6d = socket(
+ AddressFamily::Inet6,
+ SockType::Datagram,
+ SockFlag::empty(),
+ None,
+ )
+ .unwrap();
+ setsockopt(&fd6d, sockopt::Ipv6DontFrag, &true).expect(
+ "setting IPV6_DONTFRAG on an inet6 datagram socket should succeed",
+ );
+ setsockopt(&fd6d, sockopt::Ipv6DontFrag, &false).expect(
+ "unsetting IPV6_DONTFRAG on an inet6 datagram socket should succeed",
+ );
+}
+
+#[test]
+#[cfg(target_os = "linux")]
+fn test_so_priority() {
+ let fd = socket(
+ AddressFamily::Inet,
+ SockType::Stream,
+ SockFlag::empty(),
+ SockProtocol::Tcp,
+ )
+ .unwrap();
+ let priority = 3;
+ setsockopt(&fd, sockopt::Priority, &priority).unwrap();
+ assert_eq!(getsockopt(&fd, sockopt::Priority).unwrap(), priority);
+}
+
+#[test]
+#[cfg(target_os = "linux")]
+fn test_ip_tos() {
+ let fd = socket(
+ AddressFamily::Inet,
+ SockType::Stream,
+ SockFlag::empty(),
+ SockProtocol::Tcp,
+ )
+ .unwrap();
+ let tos = 0x80; // CS4
+ setsockopt(&fd, sockopt::IpTos, &tos).unwrap();
+ assert_eq!(getsockopt(&fd, sockopt::IpTos).unwrap(), tos);
+}
+
+#[test]
+#[cfg(target_os = "linux")]
+// Disable the test under emulation because it fails in Cirrus-CI. Lack
+// of QEMU support is suspected.
+#[cfg_attr(qemu, ignore)]
+fn test_ipv6_tclass() {
+ let fd = socket(
+ AddressFamily::Inet6,
+ SockType::Stream,
+ SockFlag::empty(),
+ SockProtocol::Tcp,
+ )
+ .unwrap();
+ let class = 0x80; // CS4
+ setsockopt(&fd, sockopt::Ipv6TClass, &class).unwrap();
+ assert_eq!(getsockopt(&fd, sockopt::Ipv6TClass).unwrap(), class);
+}
diff --git a/third_party/rust/nix/test/sys/test_stat.rs b/third_party/rust/nix/test/sys/test_stat.rs
new file mode 100644
index 0000000000..426b4b6588
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_stat.rs
@@ -0,0 +1,29 @@
+// The conversion is not useless on all platforms.
+#[allow(clippy::useless_conversion)]
+#[cfg(target_os = "freebsd")]
+#[test]
+fn test_chflags() {
+ use nix::{
+ sys::stat::{fstat, FileFlag},
+ unistd::chflags,
+ };
+ use std::os::unix::io::AsRawFd;
+ use tempfile::NamedTempFile;
+
+ let f = NamedTempFile::new().unwrap();
+
+ let initial = FileFlag::from_bits_truncate(
+ fstat(f.as_raw_fd()).unwrap().st_flags.into(),
+ );
+ // UF_OFFLINE is preserved by all FreeBSD file systems, but not interpreted
+ // in any way, so it's handy for testing.
+ let commanded = initial ^ FileFlag::UF_OFFLINE;
+
+ chflags(f.path(), commanded).unwrap();
+
+ let changed = FileFlag::from_bits_truncate(
+ fstat(f.as_raw_fd()).unwrap().st_flags.into(),
+ );
+
+ assert_eq!(commanded, changed);
+}
diff --git a/third_party/rust/nix/test/sys/test_sysinfo.rs b/third_party/rust/nix/test/sys/test_sysinfo.rs
new file mode 100644
index 0000000000..2897366eff
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_sysinfo.rs
@@ -0,0 +1,20 @@
+use nix::sys::sysinfo::*;
+
+#[test]
+fn sysinfo_works() {
+ let info = sysinfo().unwrap();
+
+ let (l1, l5, l15) = info.load_average();
+ assert!(l1 >= 0.0);
+ assert!(l5 >= 0.0);
+ assert!(l15 >= 0.0);
+
+ info.uptime(); // just test Duration construction
+
+ assert!(
+ info.swap_free() <= info.swap_total(),
+ "more swap available than installed (free: {}, total: {})",
+ info.swap_free(),
+ info.swap_total()
+ );
+}
diff --git a/third_party/rust/nix/test/sys/test_termios.rs b/third_party/rust/nix/test/sys/test_termios.rs
new file mode 100644
index 0000000000..83919378a7
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_termios.rs
@@ -0,0 +1,107 @@
+use std::os::unix::io::{AsFd, AsRawFd};
+use tempfile::tempfile;
+
+use nix::errno::Errno;
+use nix::fcntl;
+use nix::pty::openpty;
+use nix::sys::termios::{self, tcgetattr, LocalFlags, OutputFlags};
+use nix::unistd::{read, write};
+
+/// Helper function analogous to `std::io::Write::write_all`, but for `Fd`s
+fn write_all<Fd: AsFd>(f: Fd, buf: &[u8]) {
+ let mut len = 0;
+ while len < buf.len() {
+ len += write(f.as_fd().as_raw_fd(), &buf[len..]).unwrap();
+ }
+}
+
+// Test tcgetattr on a terminal
+#[test]
+fn test_tcgetattr_pty() {
+ // openpty uses ptname(3) internally
+ let _m = crate::PTSNAME_MTX.lock();
+
+ let pty = openpty(None, None).expect("openpty failed");
+ termios::tcgetattr(&pty.slave).unwrap();
+}
+
+// Test tcgetattr on something that isn't a terminal
+#[test]
+fn test_tcgetattr_enotty() {
+ let file = tempfile().unwrap();
+ assert_eq!(termios::tcgetattr(&file).err(), Some(Errno::ENOTTY));
+}
+
+// Test modifying output flags
+#[test]
+fn test_output_flags() {
+ // openpty uses ptname(3) internally
+ let _m = crate::PTSNAME_MTX.lock();
+
+ // Open one pty to get attributes for the second one
+ let mut termios = {
+ let pty = openpty(None, None).expect("openpty failed");
+ tcgetattr(&pty.slave).expect("tcgetattr failed")
+ };
+
+ // Make sure postprocessing '\r' isn't specified by default or this test is useless.
+ assert!(!termios
+ .output_flags
+ .contains(OutputFlags::OPOST | OutputFlags::OCRNL));
+
+ // Specify that '\r' characters should be transformed to '\n'
+ // OPOST is specified to enable post-processing
+ termios
+ .output_flags
+ .insert(OutputFlags::OPOST | OutputFlags::OCRNL);
+
+ // Open a pty
+ let pty = openpty(None, &termios).unwrap();
+
+ // Write into the master
+ let string = "foofoofoo\r";
+ write_all(&pty.master, string.as_bytes());
+
+ // Read from the slave verifying that the output has been properly transformed
+ let mut buf = [0u8; 10];
+ crate::read_exact(&pty.slave, &mut buf);
+ let transformed_string = "foofoofoo\n";
+ assert_eq!(&buf, transformed_string.as_bytes());
+}
+
+// Test modifying local flags
+#[test]
+fn test_local_flags() {
+ // openpty uses ptname(3) internally
+ let _m = crate::PTSNAME_MTX.lock();
+
+ // Open one pty to get attributes for the second one
+ let mut termios = {
+ let pty = openpty(None, None).unwrap();
+ tcgetattr(&pty.slave).unwrap()
+ };
+
+ // Make sure echo is specified by default or this test is useless.
+ assert!(termios.local_flags.contains(LocalFlags::ECHO));
+
+ // Disable local echo
+ termios.local_flags.remove(LocalFlags::ECHO);
+
+ // Open a new pty with our modified termios settings
+ let pty = openpty(None, &termios).unwrap();
+
+ // Set the master is in nonblocking mode or reading will never return.
+ let flags = fcntl::fcntl(pty.master.as_raw_fd(), fcntl::F_GETFL).unwrap();
+ let new_flags =
+ fcntl::OFlag::from_bits_truncate(flags) | fcntl::OFlag::O_NONBLOCK;
+ fcntl::fcntl(pty.master.as_raw_fd(), fcntl::F_SETFL(new_flags)).unwrap();
+
+ // Write into the master
+ let string = "foofoofoo\r";
+ write_all(&pty.master, string.as_bytes());
+
+ // Try to read from the master, which should not have anything as echoing was disabled.
+ let mut buf = [0u8; 10];
+ let read = read(pty.master.as_raw_fd(), &mut buf).unwrap_err();
+ assert_eq!(read, Errno::EAGAIN);
+}
diff --git a/third_party/rust/nix/test/sys/test_timerfd.rs b/third_party/rust/nix/test/sys/test_timerfd.rs
new file mode 100644
index 0000000000..08e292106c
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_timerfd.rs
@@ -0,0 +1,69 @@
+use nix::sys::time::{TimeSpec, TimeValLike};
+use nix::sys::timerfd::{
+ ClockId, Expiration, TimerFd, TimerFlags, TimerSetTimeFlags,
+};
+use std::time::Instant;
+
+#[test]
+pub fn test_timerfd_oneshot() {
+ let timer =
+ TimerFd::new(ClockId::CLOCK_MONOTONIC, TimerFlags::empty()).unwrap();
+
+ let before = Instant::now();
+
+ timer
+ .set(
+ Expiration::OneShot(TimeSpec::seconds(1)),
+ TimerSetTimeFlags::empty(),
+ )
+ .unwrap();
+
+ timer.wait().unwrap();
+
+ let millis = before.elapsed().as_millis();
+ assert!(millis > 900);
+}
+
+#[test]
+pub fn test_timerfd_interval() {
+ let timer =
+ TimerFd::new(ClockId::CLOCK_MONOTONIC, TimerFlags::empty()).unwrap();
+
+ let before = Instant::now();
+ timer
+ .set(
+ Expiration::IntervalDelayed(
+ TimeSpec::seconds(1),
+ TimeSpec::seconds(2),
+ ),
+ TimerSetTimeFlags::empty(),
+ )
+ .unwrap();
+
+ timer.wait().unwrap();
+
+ let start_delay = before.elapsed().as_millis();
+ assert!(start_delay > 900);
+
+ timer.wait().unwrap();
+
+ let interval_delay = before.elapsed().as_millis();
+ assert!(interval_delay > 2900);
+}
+
+#[test]
+pub fn test_timerfd_unset() {
+ let timer =
+ TimerFd::new(ClockId::CLOCK_MONOTONIC, TimerFlags::empty()).unwrap();
+
+ timer
+ .set(
+ Expiration::OneShot(TimeSpec::seconds(1)),
+ TimerSetTimeFlags::empty(),
+ )
+ .unwrap();
+
+ timer.unset().unwrap();
+
+ assert!(timer.get().unwrap().is_none());
+}
diff --git a/third_party/rust/nix/test/sys/test_uio.rs b/third_party/rust/nix/test/sys/test_uio.rs
new file mode 100644
index 0000000000..fc09465f19
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_uio.rs
@@ -0,0 +1,277 @@
+use nix::sys::uio::*;
+use nix::unistd::*;
+use rand::distributions::Alphanumeric;
+use rand::{thread_rng, Rng};
+use std::fs::OpenOptions;
+use std::io::IoSlice;
+use std::os::unix::io::{FromRawFd, OwnedFd};
+use std::{cmp, iter};
+
+#[cfg(not(target_os = "redox"))]
+use std::io::IoSliceMut;
+
+use tempfile::tempdir;
+#[cfg(not(target_os = "redox"))]
+use tempfile::tempfile;
+
+#[test]
+fn test_writev() {
+ let mut to_write = Vec::with_capacity(16 * 128);
+ for _ in 0..16 {
+ let s: String = thread_rng()
+ .sample_iter(&Alphanumeric)
+ .map(char::from)
+ .take(128)
+ .collect();
+ let b = s.as_bytes();
+ to_write.extend(b.iter().cloned());
+ }
+ // Allocate and fill iovecs
+ let mut iovecs = Vec::new();
+ let mut consumed = 0;
+ while consumed < to_write.len() {
+ let left = to_write.len() - consumed;
+ let slice_len = if left <= 64 {
+ left
+ } else {
+ thread_rng().gen_range(64..cmp::min(256, left))
+ };
+ let b = &to_write[consumed..consumed + slice_len];
+ iovecs.push(IoSlice::new(b));
+ consumed += slice_len;
+ }
+ let (reader, writer) = pipe().expect("Couldn't create pipe");
+ // FileDesc will close its filedesc (reader).
+ let mut read_buf: Vec<u8> = iter::repeat(0u8).take(128 * 16).collect();
+
+ // Temporary workaround to cope with the existing RawFd pipe(2), should be
+ // removed when pipe(2) becomes I/O-safe.
+ let writer = unsafe { OwnedFd::from_raw_fd(writer) };
+
+ // Blocking io, should write all data.
+ let write_res = writev(&writer, &iovecs);
+ let written = write_res.expect("couldn't write");
+ // Check whether we written all data
+ assert_eq!(to_write.len(), written);
+ let read_res = read(reader, &mut read_buf[..]);
+ let read = read_res.expect("couldn't read");
+ // Check we have read as much as we written
+ assert_eq!(read, written);
+ // Check equality of written and read data
+ assert_eq!(&to_write, &read_buf);
+ close(reader).expect("closed reader");
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_readv() {
+ let s: String = thread_rng()
+ .sample_iter(&Alphanumeric)
+ .map(char::from)
+ .take(128)
+ .collect();
+ let to_write = s.as_bytes().to_vec();
+ let mut storage = Vec::new();
+ let mut allocated = 0;
+ while allocated < to_write.len() {
+ let left = to_write.len() - allocated;
+ let vec_len = if left <= 64 {
+ left
+ } else {
+ thread_rng().gen_range(64..cmp::min(256, left))
+ };
+ let v: Vec<u8> = iter::repeat(0u8).take(vec_len).collect();
+ storage.push(v);
+ allocated += vec_len;
+ }
+ let mut iovecs = Vec::with_capacity(storage.len());
+ for v in &mut storage {
+ iovecs.push(IoSliceMut::new(&mut v[..]));
+ }
+ let (reader, writer) = pipe().expect("couldn't create pipe");
+ // Blocking io, should write all data.
+ write(writer, &to_write).expect("write failed");
+
+ // Temporary workaround to cope with the existing RawFd pipe(2), should be
+ // removed when pipe(2) becomes I/O-safe.
+ let reader = unsafe { OwnedFd::from_raw_fd(reader) };
+
+ let read = readv(&reader, &mut iovecs[..]).expect("read failed");
+ // Check whether we've read all data
+ assert_eq!(to_write.len(), read);
+ // Cccumulate data from iovecs
+ let mut read_buf = Vec::with_capacity(to_write.len());
+ for iovec in &iovecs {
+ read_buf.extend(iovec.iter().cloned());
+ }
+ // Check whether iovecs contain all written data
+ assert_eq!(read_buf.len(), to_write.len());
+ // Check equality of written and read data
+ assert_eq!(&read_buf, &to_write);
+ close(writer).expect("couldn't close writer");
+}
+
+#[test]
+#[cfg(not(target_os = "redox"))]
+fn test_pwrite() {
+ use std::io::Read;
+
+ let mut file = tempfile().unwrap();
+ let buf = [1u8; 8];
+ assert_eq!(Ok(8), pwrite(&file, &buf, 8));
+ let mut file_content = Vec::new();
+ file.read_to_end(&mut file_content).unwrap();
+ let mut expected = vec![0u8; 8];
+ expected.extend(vec![1; 8]);
+ assert_eq!(file_content, expected);
+}
+
+#[test]
+fn test_pread() {
+ use std::io::Write;
+
+ let tempdir = tempdir().unwrap();
+
+ let path = tempdir.path().join("pread_test_file");
+ let mut file = OpenOptions::new()
+ .write(true)
+ .read(true)
+ .create(true)
+ .truncate(true)
+ .open(path)
+ .unwrap();
+ let file_content: Vec<u8> = (0..64).collect();
+ file.write_all(&file_content).unwrap();
+
+ let mut buf = [0u8; 16];
+ assert_eq!(Ok(16), pread(&file, &mut buf, 16));
+ let expected: Vec<_> = (16..32).collect();
+ assert_eq!(&buf[..], &expected[..]);
+}
+
+#[test]
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+fn test_pwritev() {
+ use std::io::Read;
+
+ let to_write: Vec<u8> = (0..128).collect();
+ let expected: Vec<u8> = [vec![0; 100], to_write.clone()].concat();
+
+ let iovecs = [
+ IoSlice::new(&to_write[0..17]),
+ IoSlice::new(&to_write[17..64]),
+ IoSlice::new(&to_write[64..128]),
+ ];
+
+ let tempdir = tempdir().unwrap();
+
+ // pwritev them into a temporary file
+ let path = tempdir.path().join("pwritev_test_file");
+ let mut file = OpenOptions::new()
+ .write(true)
+ .read(true)
+ .create(true)
+ .truncate(true)
+ .open(path)
+ .unwrap();
+
+ let written = pwritev(&file, &iovecs, 100).ok().unwrap();
+ assert_eq!(written, to_write.len());
+
+ // Read the data back and make sure it matches
+ let mut contents = Vec::new();
+ file.read_to_end(&mut contents).unwrap();
+ assert_eq!(contents, expected);
+}
+
+#[test]
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+fn test_preadv() {
+ use std::io::Write;
+
+ let to_write: Vec<u8> = (0..200).collect();
+ let expected: Vec<u8> = (100..200).collect();
+
+ let tempdir = tempdir().unwrap();
+
+ let path = tempdir.path().join("preadv_test_file");
+
+ let mut file = OpenOptions::new()
+ .read(true)
+ .write(true)
+ .create(true)
+ .truncate(true)
+ .open(path)
+ .unwrap();
+ file.write_all(&to_write).unwrap();
+
+ let mut buffers: Vec<Vec<u8>> = vec![vec![0; 24], vec![0; 1], vec![0; 75]];
+
+ {
+ // Borrow the buffers into IoVecs and preadv into them
+ let mut iovecs: Vec<_> = buffers
+ .iter_mut()
+ .map(|buf| IoSliceMut::new(&mut buf[..]))
+ .collect();
+ assert_eq!(Ok(100), preadv(&file, &mut iovecs, 100));
+ }
+
+ let all = buffers.concat();
+ assert_eq!(all, expected);
+}
+
+#[test]
+#[cfg(all(target_os = "linux", not(target_env = "uclibc")))]
+// uclibc doesn't implement process_vm_readv
+// qemu-user doesn't implement process_vm_readv/writev on most arches
+#[cfg_attr(qemu, ignore)]
+fn test_process_vm_readv() {
+ use crate::*;
+ use nix::sys::signal::*;
+ use nix::sys::wait::*;
+ use nix::unistd::ForkResult::*;
+
+ require_capability!("test_process_vm_readv", CAP_SYS_PTRACE);
+ let _m = crate::FORK_MTX.lock();
+
+ // Pre-allocate memory in the child, since allocation isn't safe
+ // post-fork (~= async-signal-safe)
+ let mut vector = vec![1u8, 2, 3, 4, 5];
+
+ let (r, w) = pipe().unwrap();
+ match unsafe { fork() }.expect("Error: Fork Failed") {
+ Parent { child } => {
+ close(w).unwrap();
+ // wait for child
+ read(r, &mut [0u8]).unwrap();
+ close(r).unwrap();
+
+ let ptr = vector.as_ptr() as usize;
+ let remote_iov = RemoteIoVec { base: ptr, len: 5 };
+ let mut buf = vec![0u8; 5];
+
+ let ret = process_vm_readv(
+ child,
+ &mut [IoSliceMut::new(&mut buf)],
+ &[remote_iov],
+ );
+
+ kill(child, SIGTERM).unwrap();
+ waitpid(child, None).unwrap();
+
+ assert_eq!(Ok(5), ret);
+ assert_eq!(20u8, buf.iter().sum());
+ }
+ Child => {
+ let _ = close(r);
+ for i in &mut vector {
+ *i += 1;
+ }
+ let _ = write(w, b"\0");
+ let _ = close(w);
+ loop {
+ pause();
+ }
+ }
+ }
+}
diff --git a/third_party/rust/nix/test/sys/test_wait.rs b/third_party/rust/nix/test/sys/test_wait.rs
new file mode 100644
index 0000000000..d472f1ec19
--- /dev/null
+++ b/third_party/rust/nix/test/sys/test_wait.rs
@@ -0,0 +1,257 @@
+use libc::_exit;
+use nix::errno::Errno;
+use nix::sys::signal::*;
+use nix::sys::wait::*;
+use nix::unistd::ForkResult::*;
+use nix::unistd::*;
+
+#[test]
+#[cfg(not(any(target_os = "redox", target_os = "haiku")))]
+fn test_wait_signal() {
+ let _m = crate::FORK_MTX.lock();
+
+ // Safe: The child only calls `pause` and/or `_exit`, which are async-signal-safe.
+ match unsafe { fork() }.expect("Error: Fork Failed") {
+ Child => {
+ pause();
+ unsafe { _exit(123) }
+ }
+ Parent { child } => {
+ kill(child, Some(SIGKILL)).expect("Error: Kill Failed");
+ assert_eq!(
+ waitpid(child, None),
+ Ok(WaitStatus::Signaled(child, SIGKILL, false))
+ );
+ }
+ }
+}
+
+#[test]
+#[cfg(any(
+ target_os = "android",
+ target_os = "freebsd",
+ //target_os = "haiku",
+ all(target_os = "linux", not(target_env = "uclibc")),
+))]
+#[cfg(not(any(target_arch = "mips", target_arch = "mips64")))]
+fn test_waitid_signal() {
+ let _m = crate::FORK_MTX.lock();
+
+ // Safe: The child only calls `pause` and/or `_exit`, which are async-signal-safe.
+ match unsafe { fork() }.expect("Error: Fork Failed") {
+ Child => {
+ pause();
+ unsafe { _exit(123) }
+ }
+ Parent { child } => {
+ kill(child, Some(SIGKILL)).expect("Error: Kill Failed");
+ assert_eq!(
+ waitid(Id::Pid(child), WaitPidFlag::WEXITED),
+ Ok(WaitStatus::Signaled(child, SIGKILL, false)),
+ );
+ }
+ }
+}
+
+#[test]
+fn test_wait_exit() {
+ let _m = crate::FORK_MTX.lock();
+
+ // Safe: Child only calls `_exit`, which is async-signal-safe.
+ match unsafe { fork() }.expect("Error: Fork Failed") {
+ Child => unsafe {
+ _exit(12);
+ },
+ Parent { child } => {
+ assert_eq!(waitpid(child, None), Ok(WaitStatus::Exited(child, 12)));
+ }
+ }
+}
+
+#[cfg(not(target_os = "haiku"))]
+#[test]
+#[cfg(any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "haiku",
+ all(target_os = "linux", not(target_env = "uclibc")),
+))]
+#[cfg(not(any(target_arch = "mips", target_arch = "mips64")))]
+fn test_waitid_exit() {
+ let _m = crate::FORK_MTX.lock();
+
+ // Safe: Child only calls `_exit`, which is async-signal-safe.
+ match unsafe { fork() }.expect("Error: Fork Failed") {
+ Child => unsafe {
+ _exit(12);
+ },
+ Parent { child } => {
+ assert_eq!(
+ waitid(Id::Pid(child), WaitPidFlag::WEXITED),
+ Ok(WaitStatus::Exited(child, 12)),
+ );
+ }
+ }
+}
+
+#[test]
+fn test_waitstatus_from_raw() {
+ let pid = Pid::from_raw(1);
+ assert_eq!(
+ WaitStatus::from_raw(pid, 0x0002),
+ Ok(WaitStatus::Signaled(pid, Signal::SIGINT, false))
+ );
+ assert_eq!(
+ WaitStatus::from_raw(pid, 0x0200),
+ Ok(WaitStatus::Exited(pid, 2))
+ );
+ assert_eq!(WaitStatus::from_raw(pid, 0x7f7f), Err(Errno::EINVAL));
+}
+
+#[test]
+fn test_waitstatus_pid() {
+ let _m = crate::FORK_MTX.lock();
+
+ match unsafe { fork() }.unwrap() {
+ Child => unsafe { _exit(0) },
+ Parent { child } => {
+ let status = waitpid(child, None).unwrap();
+ assert_eq!(status.pid(), Some(child));
+ }
+ }
+}
+
+#[test]
+#[cfg(any(
+ target_os = "android",
+ target_os = "freebsd",
+ target_os = "haiku",
+ all(target_os = "linux", not(target_env = "uclibc")),
+))]
+fn test_waitid_pid() {
+ let _m = crate::FORK_MTX.lock();
+
+ match unsafe { fork() }.unwrap() {
+ Child => unsafe { _exit(0) },
+ Parent { child } => {
+ let status = waitid(Id::Pid(child), WaitPidFlag::WEXITED).unwrap();
+ assert_eq!(status.pid(), Some(child));
+ }
+ }
+}
+
+#[cfg(any(target_os = "linux", target_os = "android"))]
+// FIXME: qemu-user doesn't implement ptrace on most arches
+#[cfg(any(target_arch = "x86", target_arch = "x86_64"))]
+mod ptrace {
+ use crate::*;
+ use libc::_exit;
+ use nix::sys::ptrace::{self, Event, Options};
+ use nix::sys::signal::*;
+ use nix::sys::wait::*;
+ use nix::unistd::ForkResult::*;
+ use nix::unistd::*;
+
+ fn ptrace_child() -> ! {
+ ptrace::traceme().unwrap();
+ // As recommended by ptrace(2), raise SIGTRAP to pause the child
+ // until the parent is ready to continue
+ raise(SIGTRAP).unwrap();
+ unsafe { _exit(0) }
+ }
+
+ fn ptrace_wait_parent(child: Pid) {
+ // Wait for the raised SIGTRAP
+ assert_eq!(
+ waitpid(child, None),
+ Ok(WaitStatus::Stopped(child, SIGTRAP))
+ );
+ // We want to test a syscall stop and a PTRACE_EVENT stop
+ ptrace::setoptions(
+ child,
+ Options::PTRACE_O_TRACESYSGOOD | Options::PTRACE_O_TRACEEXIT,
+ )
+ .expect("setoptions failed");
+
+ // First, stop on the next system call, which will be exit()
+ ptrace::syscall(child, None).expect("syscall failed");
+ assert_eq!(waitpid(child, None), Ok(WaitStatus::PtraceSyscall(child)));
+ // Then get the ptrace event for the process exiting
+ ptrace::cont(child, None).expect("cont failed");
+ assert_eq!(
+ waitpid(child, None),
+ Ok(WaitStatus::PtraceEvent(
+ child,
+ SIGTRAP,
+ Event::PTRACE_EVENT_EXIT as i32
+ ))
+ );
+ // Finally get the normal wait() result, now that the process has exited
+ ptrace::cont(child, None).expect("cont failed");
+ assert_eq!(waitpid(child, None), Ok(WaitStatus::Exited(child, 0)));
+ }
+
+ #[cfg(not(target_env = "uclibc"))]
+ fn ptrace_waitid_parent(child: Pid) {
+ // Wait for the raised SIGTRAP
+ //
+ // Unlike waitpid(), waitid() can distinguish trap events from regular
+ // stop events, so unlike ptrace_wait_parent(), we get a PtraceEvent here
+ assert_eq!(
+ waitid(Id::Pid(child), WaitPidFlag::WEXITED),
+ Ok(WaitStatus::PtraceEvent(child, SIGTRAP, 0)),
+ );
+ // We want to test a syscall stop and a PTRACE_EVENT stop
+ ptrace::setoptions(
+ child,
+ Options::PTRACE_O_TRACESYSGOOD | Options::PTRACE_O_TRACEEXIT,
+ )
+ .expect("setopts failed");
+
+ // First, stop on the next system call, which will be exit()
+ ptrace::syscall(child, None).expect("syscall failed");
+ assert_eq!(
+ waitid(Id::Pid(child), WaitPidFlag::WEXITED),
+ Ok(WaitStatus::PtraceSyscall(child)),
+ );
+ // Then get the ptrace event for the process exiting
+ ptrace::cont(child, None).expect("cont failed");
+ assert_eq!(
+ waitid(Id::Pid(child), WaitPidFlag::WEXITED),
+ Ok(WaitStatus::PtraceEvent(
+ child,
+ SIGTRAP,
+ Event::PTRACE_EVENT_EXIT as i32
+ )),
+ );
+ // Finally get the normal wait() result, now that the process has exited
+ ptrace::cont(child, None).expect("cont failed");
+ assert_eq!(
+ waitid(Id::Pid(child), WaitPidFlag::WEXITED),
+ Ok(WaitStatus::Exited(child, 0)),
+ );
+ }
+
+ #[test]
+ fn test_wait_ptrace() {
+ require_capability!("test_wait_ptrace", CAP_SYS_PTRACE);
+ let _m = crate::FORK_MTX.lock();
+
+ match unsafe { fork() }.expect("Error: Fork Failed") {
+ Child => ptrace_child(),
+ Parent { child } => ptrace_wait_parent(child),
+ }
+ }
+
+ #[test]
+ #[cfg(not(target_env = "uclibc"))]
+ fn test_waitid_ptrace() {
+ require_capability!("test_waitid_ptrace", CAP_SYS_PTRACE);
+ let _m = crate::FORK_MTX.lock();
+
+ match unsafe { fork() }.expect("Error: Fork Failed") {
+ Child => ptrace_child(),
+ Parent { child } => ptrace_waitid_parent(child),
+ }
+ }
+}