1 #[macro_use]
2 extern crate cfg_if;
3 #[cfg_attr(not(target_os = "redox"), macro_use)]
4 extern crate nix;
5 #[macro_use]
6 extern crate lazy_static;
7 
8 macro_rules! skip {
9     ($($reason: expr),+) => {
10         use ::std::io::{self, Write};
11 
12         let stderr = io::stderr();
13         let mut handle = stderr.lock();
14         writeln!(handle, $($reason),+).unwrap();
15         return;
16     }
17 }
18 
19 cfg_if! {
20     if #[cfg(any(target_os = "android", target_os = "linux"))] {
21         macro_rules! require_capability {
22             ($capname:ident) => {
23                 use ::caps::{Capability, CapSet, has_cap};
24 
25                 if !has_cap(None, CapSet::Effective, Capability::$capname)
26                     .unwrap()
27                 {
28                     skip!("Insufficient capabilities. Skipping test.");
29                 }
30             }
31         }
32     } else if #[cfg(not(target_os = "redox"))] {
33         macro_rules! require_capability {
34             ($capname:ident) => {}
35         }
36     }
37 }
38 
39 #[cfg(target_os = "freebsd")]
40 macro_rules! skip_if_jailed {
41     ($name:expr) => {
42         use ::sysctl::CtlValue;
43 
44         if let CtlValue::Int(1) = ::sysctl::value("security.jail.jailed")
45             .unwrap()
46         {
47             skip!("{} cannot run in a jail. Skipping test.", $name);
48         }
49     }
50 }
51 
52 #[cfg(not(target_os = "redox"))]
53 macro_rules! skip_if_not_root {
54     ($name:expr) => {
55         use nix::unistd::Uid;
56 
57         if !Uid::current().is_root() {
58             skip!("{} requires root privileges. Skipping test.", $name);
59         }
60     };
61 }
62 
63 cfg_if! {
64     if #[cfg(any(target_os = "android", target_os = "linux"))] {
65         macro_rules! skip_if_seccomp {
66             ($name:expr) => {
67                 if let Ok(s) = std::fs::read_to_string("/proc/self/status") {
68                     for l in s.lines() {
69                         let mut fields = l.split_whitespace();
70                         if fields.next() == Some("Seccomp:") &&
71                             fields.next() != Some("0")
72                         {
73                             skip!("{} cannot be run in Seccomp mode.  Skipping test.",
74                                 stringify!($name));
75                         }
76                     }
77                 }
78             }
79         }
80     } else if #[cfg(not(target_os = "redox"))] {
81         macro_rules! skip_if_seccomp {
82             ($name:expr) => {}
83         }
84     }
85 }
86 
87 cfg_if! {
88     if #[cfg(target_os = "linux")] {
89         macro_rules! require_kernel_version {
90             ($name:expr, $version_requirement:expr) => {
91                 use semver::{Version, VersionReq};
92 
93                 let version_requirement = VersionReq::parse($version_requirement)
94                         .expect("Bad match_version provided");
95 
96                 let uname = nix::sys::utsname::uname();
97 
98                 let mut version = Version::parse(uname.release()).unwrap();
99 
100                 //Keep only numeric parts
101                 version.pre.clear();
102                 version.build.clear();
103 
104                 if !version_requirement.matches(&version) {
105                     skip!("Skip {} because kernel version `{}` doesn't match the requirement `{}`",
106                         stringify!($name), version, version_requirement);
107                 }
108             }
109         }
110     }
111 }
112 
113 mod sys;
114 #[cfg(not(target_os = "redox"))]
115 mod test_dir;
116 mod test_fcntl;
117 #[cfg(any(target_os = "android",
118           target_os = "linux"))]
119 mod test_kmod;
120 #[cfg(any(target_os = "dragonfly",
121           target_os = "freebsd",
122           target_os = "fushsia",
123           target_os = "linux",
124           target_os = "netbsd"))]
125 mod test_mq;
126 #[cfg(not(target_os = "redox"))]
127 mod test_net;
128 mod test_nix_path;
129 mod test_poll;
130 #[cfg(not(target_os = "redox"))]
131 mod test_pty;
132 #[cfg(any(target_os = "android",
133           target_os = "linux"))]
134 mod test_sched;
135 #[cfg(any(target_os = "android",
136           target_os = "freebsd",
137           target_os = "ios",
138           target_os = "linux",
139           target_os = "macos"))]
140 mod test_sendfile;
141 mod test_stat;
142 mod test_time;
143 mod test_unistd;
144 
145 use std::os::unix::io::RawFd;
146 use std::path::PathBuf;
147 use std::sync::{Mutex, RwLock, RwLockWriteGuard};
148 use nix::unistd::{chdir, getcwd, read};
149 
150 /// Helper function analogous to `std::io::Read::read_exact`, but for `RawFD`s
read_exact(f: RawFd, buf: &mut [u8])151 fn read_exact(f: RawFd, buf: &mut  [u8]) {
152     let mut len = 0;
153     while len < buf.len() {
154         // get_mut would be better than split_at_mut, but it requires nightly
155         let (_, remaining) = buf.split_at_mut(len);
156         len += read(f, remaining).unwrap();
157     }
158 }
159 
160 lazy_static! {
161     /// Any test that changes the process's current working directory must grab
162     /// the RwLock exclusively.  Any process that cares about the current
163     /// working directory must grab it shared.
164     pub static ref CWD_LOCK: RwLock<()> = RwLock::new(());
165     /// Any test that creates child processes must grab this mutex, regardless
166     /// of what it does with those children.
167     pub static ref FORK_MTX: Mutex<()> = Mutex::new(());
168     /// Any test that changes the process's supplementary groups must grab this
169     /// mutex
170     pub static ref GROUPS_MTX: Mutex<()> = Mutex::new(());
171     /// Any tests that loads or unloads kernel modules must grab this mutex
172     pub static ref KMOD_MTX: Mutex<()> = Mutex::new(());
173     /// Any test that calls ptsname(3) must grab this mutex.
174     pub static ref PTSNAME_MTX: Mutex<()> = Mutex::new(());
175     /// Any test that alters signal handling must grab this mutex.
176     pub static ref SIGNAL_MTX: Mutex<()> = Mutex::new(());
177 }
178 
179 /// RAII object that restores a test's original directory on drop
180 struct DirRestore<'a> {
181     d: PathBuf,
182     _g: RwLockWriteGuard<'a, ()>
183 }
184 
185 impl<'a> DirRestore<'a> {
new() -> Self186     fn new() -> Self {
187         let guard = crate::CWD_LOCK.write()
188             .expect("Lock got poisoned by another test");
189         DirRestore{
190             _g: guard,
191             d: getcwd().unwrap(),
192         }
193     }
194 }
195 
196 impl<'a> Drop for DirRestore<'a> {
drop(&mut self)197     fn drop(&mut self) {
198         let r = chdir(&self.d);
199         if std::thread::panicking() {
200             r.unwrap();
201         }
202     }
203 }
204