#[macro_use] extern crate cfg_if; #[cfg_attr(not(target_os = "redox"), macro_use)] extern crate nix; #[macro_use] extern crate lazy_static; macro_rules! skip { ($($reason: expr),+) => { use ::std::io::{self, Write}; let stderr = io::stderr(); let mut handle = stderr.lock(); writeln!(handle, $($reason),+).unwrap(); return; } } cfg_if! { if #[cfg(any(target_os = "android", target_os = "linux"))] { macro_rules! require_capability { ($capname:ident) => { use ::caps::{Capability, CapSet, has_cap}; if !has_cap(None, CapSet::Effective, Capability::$capname) .unwrap() { skip!("Insufficient capabilities. Skipping test."); } } } } else if #[cfg(not(target_os = "redox"))] { macro_rules! require_capability { ($capname:ident) => {} } } } #[cfg(target_os = "freebsd")] macro_rules! skip_if_jailed { ($name:expr) => { use ::sysctl::CtlValue; if let CtlValue::Int(1) = ::sysctl::value("security.jail.jailed") .unwrap() { skip!("{} cannot run in a jail. Skipping test.", $name); } } } #[cfg(not(target_os = "redox"))] macro_rules! skip_if_not_root { ($name:expr) => { use nix::unistd::Uid; if !Uid::current().is_root() { skip!("{} requires root privileges. Skipping test.", $name); } }; } cfg_if! { if #[cfg(any(target_os = "android", target_os = "linux"))] { macro_rules! skip_if_seccomp { ($name:expr) => { if let Ok(s) = std::fs::read_to_string("/proc/self/status") { for l in s.lines() { let mut fields = l.split_whitespace(); if fields.next() == Some("Seccomp:") && fields.next() != Some("0") { skip!("{} cannot be run in Seccomp mode. Skipping test.", stringify!($name)); } } } } } } else if #[cfg(not(target_os = "redox"))] { macro_rules! skip_if_seccomp { ($name:expr) => {} } } } cfg_if! { if #[cfg(target_os = "linux")] { macro_rules! require_kernel_version { ($name:expr, $version_requirement:expr) => { use semver::{Version, VersionReq}; let version_requirement = VersionReq::parse($version_requirement) .expect("Bad match_version provided"); let uname = nix::sys::utsname::uname(); let mut version = Version::parse(uname.release()).unwrap(); //Keep only numeric parts version.pre.clear(); version.build.clear(); if !version_requirement.matches(&version) { skip!("Skip {} because kernel version `{}` doesn't match the requirement `{}`", stringify!($name), version, version_requirement); } } } } } mod sys; #[cfg(not(target_os = "redox"))] mod test_dir; mod test_fcntl; #[cfg(any(target_os = "android", target_os = "linux"))] mod test_kmod; #[cfg(any(target_os = "dragonfly", target_os = "freebsd", target_os = "fushsia", target_os = "linux", target_os = "netbsd"))] mod test_mq; #[cfg(not(target_os = "redox"))] mod test_net; mod test_nix_path; mod test_poll; #[cfg(not(target_os = "redox"))] mod test_pty; #[cfg(any(target_os = "android", target_os = "linux"))] mod test_sched; #[cfg(any(target_os = "android", target_os = "freebsd", target_os = "ios", target_os = "linux", target_os = "macos"))] mod test_sendfile; mod test_stat; mod test_time; mod test_unistd; use std::os::unix::io::RawFd; use std::path::PathBuf; use std::sync::{Mutex, RwLock, RwLockWriteGuard}; use nix::unistd::{chdir, getcwd, read}; /// Helper function analogous to `std::io::Read::read_exact`, but for `RawFD`s fn read_exact(f: RawFd, buf: &mut [u8]) { let mut len = 0; while len < buf.len() { // get_mut would be better than split_at_mut, but it requires nightly let (_, remaining) = buf.split_at_mut(len); len += read(f, remaining).unwrap(); } } lazy_static! { /// Any test that changes the process's current working directory must grab /// the RwLock exclusively. Any process that cares about the current /// working directory must grab it shared. pub static ref CWD_LOCK: RwLock<()> = RwLock::new(()); /// Any test that creates child processes must grab this mutex, regardless /// of what it does with those children. pub static ref FORK_MTX: Mutex<()> = Mutex::new(()); /// Any test that changes the process's supplementary groups must grab this /// mutex pub static ref GROUPS_MTX: Mutex<()> = Mutex::new(()); /// Any tests that loads or unloads kernel modules must grab this mutex pub static ref KMOD_MTX: Mutex<()> = Mutex::new(()); /// Any test that calls ptsname(3) must grab this mutex. pub static ref PTSNAME_MTX: Mutex<()> = Mutex::new(()); /// Any test that alters signal handling must grab this mutex. pub static ref SIGNAL_MTX: Mutex<()> = Mutex::new(()); } /// RAII object that restores a test's original directory on drop struct DirRestore<'a> { d: PathBuf, _g: RwLockWriteGuard<'a, ()> } impl<'a> DirRestore<'a> { fn new() -> Self { let guard = crate::CWD_LOCK.write() .expect("Lock got poisoned by another test"); DirRestore{ _g: guard, d: getcwd().unwrap(), } } } impl<'a> Drop for DirRestore<'a> { fn drop(&mut self) { let r = chdir(&self.d); if std::thread::panicking() { r.unwrap(); } } }