Vendor things

This commit is contained in:
John Doty 2024-03-08 11:03:01 -08:00
parent 5deceec006
commit 977e3c17e5
19434 changed files with 10682014 additions and 0 deletions

60
third-party/vendor/nix/test/sys/mod.rs vendored Normal file
View file

@ -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;

View file

@ -0,0 +1,626 @@
use std::{
io::{Read, Seek, SeekFrom, 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;
lazy_static! {
pub static ref 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().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().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.seek(SeekFrom::Start(0)).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.seek(SeekFrom::Start(0)).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.seek(SeekFrom::Start(0)).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.seek(SeekFrom::Start(0)).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);
}

View file

@ -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();
}

View file

@ -0,0 +1,24 @@
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();
}

View file

@ -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);
}

View file

@ -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));
}
}

View file

@ -0,0 +1,117 @@
use nix::sys::mman::{mmap, MapFlags, ProtFlags};
#[test]
fn test_mmap_anonymous() {
unsafe {
let ptr = mmap(
std::ptr::null_mut(),
1,
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
MapFlags::MAP_PRIVATE | MapFlags::MAP_ANONYMOUS,
-1,
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 slice: &mut [u8] = unsafe {
let mem = mmap(
std::ptr::null_mut(),
ONE_K,
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
MapFlags::MAP_ANONYMOUS | MapFlags::MAP_PRIVATE,
-1,
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};
const ONE_K: size_t = 1024;
let slice: &mut [u8] = unsafe {
let mem = mmap(
std::ptr::null_mut(),
10 * ONE_K,
ProtFlags::PROT_READ | ProtFlags::PROT_WRITE,
MapFlags::MAP_ANONYMOUS | MapFlags::MAP_PRIVATE,
-1,
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,
10 * ONE_K,
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);
}

View file

@ -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.");
}

View file

@ -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))
);
}
}
}

View file

@ -0,0 +1,81 @@
use nix::sys::select::*;
use nix::sys::signal::SigSet;
use nix::sys::time::{TimeSpec, TimeValLike};
use nix::unistd::{pipe, write};
#[test]
pub fn test_pselect() {
let _mtx = crate::SIGNAL_MTX.lock();
let (r1, w1) = pipe().unwrap();
write(w1, b"hi!").unwrap();
let (r2, _w2) = pipe().unwrap();
let mut fd_set = FdSet::new();
fd_set.insert(r1);
fd_set.insert(r2);
let 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 (r2, _w2) = pipe().unwrap();
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, r2) + 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() {
FdSet::new().$method($fd);
}
)*
}
}
mod test_fdset_negative_fd {
use super::*;
generate_fdset_bad_fd_tests!(-1, insert, remove, contains);
}
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,
);
}

View file

@ -0,0 +1,147 @@
#[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 is already blocked, please change to a \
different one",
SIGNAL
);
// 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 {:?} to be blocked",
SIGNAL
);
// Reset the signal.
sigprocmask(SigmaskHow::SIG_UNBLOCK, Some(&signal_set), None)
.expect("expect to be able to block signals");
}
lazy_static! {
static ref 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();
}

View file

@ -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);
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,356 @@
#[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};
// 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(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::{close, 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, &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, &sock_addr).unwrap();
let rsess = accept(rsock).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);
}
}
close(rsock).unwrap();
close(ssock).unwrap();
}
#[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;
require_capability!("test_so_type", CAP_NET_RAW);
let sockfd = unsafe { libc::socket(libc::AF_PACKET, libc::SOCK_PACKET, 0) };
assert!(sockfd >= 0, "Error opening socket: {}", nix::Error::last());
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 = "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",
);
}

View file

@ -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);
}

View file

@ -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()
);
}

View file

@ -0,0 +1,136 @@
use std::os::unix::prelude::*;
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::{close, read, write};
/// Helper function analogous to `std::io::Write::write_all`, but for `RawFd`s
fn write_all(f: RawFd, buf: &[u8]) {
let mut len = 0;
while len < buf.len() {
len += write(f, &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();
close(pty.master).expect("closing the master failed");
close(pty.slave).expect("closing the slave failed");
}
// Test tcgetattr on something that isn't a terminal
#[test]
fn test_tcgetattr_enotty() {
let file = tempfile().unwrap();
assert_eq!(
termios::tcgetattr(file.as_raw_fd()).err(),
Some(Errno::ENOTTY)
);
}
// Test tcgetattr on an invalid file descriptor
#[test]
fn test_tcgetattr_ebadf() {
assert_eq!(termios::tcgetattr(-1).err(), Some(Errno::EBADF));
}
// 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");
assert!(pty.master > 0);
assert!(pty.slave > 0);
let termios = tcgetattr(pty.slave).expect("tcgetattr failed");
close(pty.master).unwrap();
close(pty.slave).unwrap();
termios
};
// 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();
assert!(pty.master > 0);
assert!(pty.slave > 0);
// 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";
close(pty.master).unwrap();
close(pty.slave).unwrap();
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();
assert!(pty.master > 0);
assert!(pty.slave > 0);
let termios = tcgetattr(pty.slave).unwrap();
close(pty.master).unwrap();
close(pty.slave).unwrap();
termios
};
// 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();
assert!(pty.master > 0);
assert!(pty.slave > 0);
// Set the master is in nonblocking mode or reading will never return.
let flags = fcntl::fcntl(pty.master, fcntl::F_GETFL).unwrap();
let new_flags =
fcntl::OFlag::from_bits_truncate(flags) | fcntl::OFlag::O_NONBLOCK;
fcntl::fcntl(pty.master, 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, &mut buf).unwrap_err();
close(pty.master).unwrap();
close(pty.slave).unwrap();
assert_eq!(read, Errno::EAGAIN);
}

View file

@ -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());
}

View file

@ -0,0 +1,270 @@
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::AsRawFd;
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 pipe_res = pipe();
let (reader, writer) = pipe_res.expect("Couldn't create pipe");
// FileDesc will close its filedesc (reader).
let mut read_buf: Vec<u8> = iter::repeat(0u8).take(128 * 16).collect();
// 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(writer).expect("closed writer");
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");
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(reader).expect("couldn't close reader");
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.as_raw_fd(), &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.as_raw_fd(), &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.as_raw_fd(), &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.as_raw_fd(), &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();
}
}
}
}

View file

@ -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),
}
}
}