blob: 2d43196c8779c9b7539ac6af18409fc0cf800daf [file] [log] [blame]
#[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_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();
}
}
}