1 extern crate tempdir;
2
3 use nix::fcntl::{fcntl, FcntlArg, FdFlag, OFlag};
4 use nix::unistd::*;
5 use nix::unistd::ForkResult::*;
6 use nix::sys::signal::{SaFlags, SigAction, SigHandler, SigSet, Signal, sigaction};
7 use nix::sys::wait::*;
8 use nix::sys::stat::{self, Mode, SFlag};
9 use std::{self, env, iter};
10 use std::ffi::CString;
11 use std::fs::File;
12 use std::io::Write;
13 use std::os::unix::prelude::*;
14 use tempfile::tempfile;
15 use tempdir::TempDir;
16 use libc::{self, _exit, off_t};
17
18 #[test]
test_fork_and_waitpid()19 fn test_fork_and_waitpid() {
20 #[allow(unused_variables)]
21 let m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test");
22
23 // Safe: Child only calls `_exit`, which is signal-safe
24 match fork().expect("Error: Fork Failed") {
25 Child => unsafe { _exit(0) },
26 Parent { child } => {
27 // assert that child was created and pid > 0
28 let child_raw: ::libc::pid_t = child.into();
29 assert!(child_raw > 0);
30 let wait_status = waitpid(child, None);
31 match wait_status {
32 // assert that waitpid returned correct status and the pid is the one of the child
33 Ok(WaitStatus::Exited(pid_t, _)) => assert!(pid_t == child),
34
35 // panic, must never happen
36 s @ Ok(_) => panic!("Child exited {:?}, should never happen", s),
37
38 // panic, waitpid should never fail
39 Err(s) => panic!("Error: waitpid returned Err({:?}", s)
40 }
41
42 },
43 }
44 }
45
46 #[test]
test_wait()47 fn test_wait() {
48 // Grab FORK_MTX so wait doesn't reap a different test's child process
49 #[allow(unused_variables)]
50 let m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test");
51
52 // Safe: Child only calls `_exit`, which is signal-safe
53 match fork().expect("Error: Fork Failed") {
54 Child => unsafe { _exit(0) },
55 Parent { child } => {
56 let wait_status = wait();
57
58 // just assert that (any) one child returns with WaitStatus::Exited
59 assert_eq!(wait_status, Ok(WaitStatus::Exited(child, 0)));
60 },
61 }
62 }
63
64 #[test]
test_mkstemp()65 fn test_mkstemp() {
66 let mut path = env::temp_dir();
67 path.push("nix_tempfile.XXXXXX");
68
69 let result = mkstemp(&path);
70 match result {
71 Ok((fd, path)) => {
72 close(fd).unwrap();
73 unlink(path.as_path()).unwrap();
74 },
75 Err(e) => panic!("mkstemp failed: {}", e)
76 }
77 }
78
79 #[test]
test_mkstemp_directory()80 fn test_mkstemp_directory() {
81 // mkstemp should fail if a directory is given
82 assert!(mkstemp(&env::temp_dir()).is_err());
83 }
84
85 #[test]
test_mkfifo()86 fn test_mkfifo() {
87 let tempdir = TempDir::new("nix-test_mkfifo").unwrap();
88 let mkfifo_fifo = tempdir.path().join("mkfifo_fifo");
89
90 mkfifo(&mkfifo_fifo, Mode::S_IRUSR).unwrap();
91
92 let stats = stat::stat(&mkfifo_fifo).unwrap();
93 let typ = stat::SFlag::from_bits_truncate(stats.st_mode);
94 assert!(typ == SFlag::S_IFIFO);
95 }
96
97 #[test]
test_mkfifo_directory()98 fn test_mkfifo_directory() {
99 // mkfifo should fail if a directory is given
100 assert!(mkfifo(&env::temp_dir(), Mode::S_IRUSR).is_err());
101 }
102
103 #[test]
test_getpid()104 fn test_getpid() {
105 let pid: ::libc::pid_t = getpid().into();
106 let ppid: ::libc::pid_t = getppid().into();
107 assert!(pid > 0);
108 assert!(ppid > 0);
109 }
110
111 #[test]
test_getsid()112 fn test_getsid() {
113 let none_sid: ::libc::pid_t = getsid(None).unwrap().into();
114 let pid_sid: ::libc::pid_t = getsid(Some(getpid())).unwrap().into();
115 assert!(none_sid > 0);
116 assert!(none_sid == pid_sid);
117 }
118
119 #[cfg(any(target_os = "linux", target_os = "android"))]
120 mod linux_android {
121 use nix::unistd::gettid;
122
123 #[test]
test_gettid()124 fn test_gettid() {
125 let tid: ::libc::pid_t = gettid().into();
126 assert!(tid > 0);
127 }
128 }
129
130 #[test]
131 // `getgroups()` and `setgroups()` do not behave as expected on Apple platforms
132 #[cfg(not(any(target_os = "ios", target_os = "macos")))]
test_setgroups()133 fn test_setgroups() {
134 // Skip this test when not run as root as `setgroups()` requires root.
135 if !Uid::current().is_root() {
136 let stderr = std::io::stderr();
137 let mut handle = stderr.lock();
138 writeln!(handle, "test_setgroups requires root privileges. Skipping test.").unwrap();
139 return;
140 }
141
142 #[allow(unused_variables)]
143 let m = ::GROUPS_MTX.lock().expect("Mutex got poisoned by another test");
144
145 // Save the existing groups
146 let old_groups = getgroups().unwrap();
147
148 // Set some new made up groups
149 let groups = [Gid::from_raw(123), Gid::from_raw(456)];
150 setgroups(&groups).unwrap();
151
152 let new_groups = getgroups().unwrap();
153 assert_eq!(new_groups, groups);
154
155 // Revert back to the old groups
156 setgroups(&old_groups).unwrap();
157 }
158
159 #[test]
160 // `getgroups()` and `setgroups()` do not behave as expected on Apple platforms
161 #[cfg(not(any(target_os = "ios", target_os = "macos")))]
test_initgroups()162 fn test_initgroups() {
163 // Skip this test when not run as root as `initgroups()` and `setgroups()`
164 // require root.
165 if !Uid::current().is_root() {
166 let stderr = std::io::stderr();
167 let mut handle = stderr.lock();
168 writeln!(handle, "test_initgroups requires root privileges. Skipping test.").unwrap();
169 return;
170 }
171
172 #[allow(unused_variables)]
173 let m = ::GROUPS_MTX.lock().expect("Mutex got poisoned by another test");
174
175 // Save the existing groups
176 let old_groups = getgroups().unwrap();
177
178 // It doesn't matter if the root user is not called "root" or if a user
179 // called "root" doesn't exist. We are just checking that the extra,
180 // made-up group, `123`, is set.
181 // FIXME: Test the other half of initgroups' functionality: whether the
182 // groups that the user belongs to are also set.
183 let user = CString::new("root").unwrap();
184 let group = Gid::from_raw(123);
185 let group_list = getgrouplist(&user, group).unwrap();
186 assert!(group_list.contains(&group));
187
188 initgroups(&user, group).unwrap();
189
190 let new_groups = getgroups().unwrap();
191 assert_eq!(new_groups, group_list);
192
193 // Revert back to the old groups
194 setgroups(&old_groups).unwrap();
195 }
196
197 macro_rules! execve_test_factory(
198 ($test_name:ident, $syscall:ident, $exe: expr $(, $pathname:expr, $flags:expr)*) => (
199 #[test]
200 fn $test_name() {
201 #[allow(unused_variables)]
202 let m = ::FORK_MTX.lock().expect("Mutex got poisoned by another test");
203 // The `exec`d process will write to `writer`, and we'll read that
204 // data from `reader`.
205 let (reader, writer) = pipe().unwrap();
206
207 // Safe: Child calls `exit`, `dup`, `close` and the provided `exec*` family function.
208 // NOTE: Technically, this makes the macro unsafe to use because you could pass anything.
209 // The tests make sure not to do that, though.
210 match fork().unwrap() {
211 Child => {
212 // Close stdout.
213 close(1).unwrap();
214 // Make `writer` be the stdout of the new process.
215 dup(writer).unwrap();
216 // exec!
217 $syscall(
218 $exe,
219 $(&CString::new($pathname).unwrap(), )*
220 &[CString::new(b"".as_ref()).unwrap(),
221 CString::new(b"-c".as_ref()).unwrap(),
222 CString::new(b"echo nix!!! && echo foo=$foo && echo baz=$baz"
223 .as_ref()).unwrap()],
224 &[CString::new(b"foo=bar".as_ref()).unwrap(),
225 CString::new(b"baz=quux".as_ref()).unwrap()]
226 $(, $flags)*).unwrap();
227 },
228 Parent { child } => {
229 // Wait for the child to exit.
230 waitpid(child, None).unwrap();
231 // Read 1024 bytes.
232 let mut buf = [0u8; 1024];
233 read(reader, &mut buf).unwrap();
234 // It should contain the things we printed using `/bin/sh`.
235 let string = String::from_utf8_lossy(&buf);
236 assert!(string.contains("nix!!!"));
237 assert!(string.contains("foo=bar"));
238 assert!(string.contains("baz=quux"));
239 }
240 }
241 }
242 )
243 );
244
245 cfg_if!{
246 if #[cfg(target_os = "android")] {
247 execve_test_factory!(test_execve, execve, &CString::new("/system/bin/sh").unwrap());
248 execve_test_factory!(test_fexecve, fexecve, File::open("/system/bin/sh").unwrap().into_raw_fd());
249 } else if #[cfg(any(target_os = "freebsd",
250 target_os = "linux",
251 target_os = "netbsd",
252 target_os = "openbsd"))] {
253 execve_test_factory!(test_execve, execve, &CString::new("/bin/sh").unwrap());
254 execve_test_factory!(test_fexecve, fexecve, File::open("/bin/sh").unwrap().into_raw_fd());
255 } else if #[cfg(any(target_os = "dragonfly",
256 target_os = "ios",
257 target_os = "macos"))] {
258 execve_test_factory!(test_execve, execve, &CString::new("/bin/sh").unwrap());
259 // No fexecve() on macos/ios and DragonFly.
260 }
261 }
262
263 cfg_if!{
264 if #[cfg(target_os = "android")] {
265 use nix::fcntl::AtFlags;
266 execve_test_factory!(test_execveat_empty, execveat, File::open("/system/bin/sh").unwrap().into_raw_fd(),
267 "", AtFlags::AT_EMPTY_PATH);
268 execve_test_factory!(test_execveat_relative, execveat, File::open("/system/bin/").unwrap().into_raw_fd(),
269 "./sh", AtFlags::empty());
270 execve_test_factory!(test_execveat_absolute, execveat, File::open("/").unwrap().into_raw_fd(),
271 "/system/bin/sh", AtFlags::empty());
272 } else if #[cfg(all(target_os = "linux"), any(target_arch ="x86_64", target_arch ="x86"))] {
273 use nix::fcntl::AtFlags;
274 execve_test_factory!(test_execveat_empty, execveat, File::open("/bin/sh").unwrap().into_raw_fd(),
275 "", AtFlags::AT_EMPTY_PATH);
276 execve_test_factory!(test_execveat_relative, execveat, File::open("/bin/").unwrap().into_raw_fd(),
277 "./sh", AtFlags::empty());
278 execve_test_factory!(test_execveat_absolute, execveat, File::open("/").unwrap().into_raw_fd(),
279 "/bin/sh", AtFlags::empty());
280 }
281 }
282
283 #[test]
test_fchdir()284 fn test_fchdir() {
285 // fchdir changes the process's cwd
286 #[allow(unused_variables)]
287 let m = ::CWD_MTX.lock().expect("Mutex got poisoned by another test");
288
289 let tmpdir = TempDir::new("test_fchdir").unwrap();
290 let tmpdir_path = tmpdir.path().canonicalize().unwrap();
291 let tmpdir_fd = File::open(&tmpdir_path).unwrap().into_raw_fd();
292
293 assert!(fchdir(tmpdir_fd).is_ok());
294 assert_eq!(getcwd().unwrap(), tmpdir_path);
295
296 assert!(close(tmpdir_fd).is_ok());
297 }
298
299 #[test]
test_getcwd()300 fn test_getcwd() {
301 // chdir changes the process's cwd
302 #[allow(unused_variables)]
303 let m = ::CWD_MTX.lock().expect("Mutex got poisoned by another test");
304
305 let tmpdir = TempDir::new("test_getcwd").unwrap();
306 let tmpdir_path = tmpdir.path().canonicalize().unwrap();
307 assert!(chdir(&tmpdir_path).is_ok());
308 assert_eq!(getcwd().unwrap(), tmpdir_path);
309
310 // make path 500 chars longer so that buffer doubling in getcwd
311 // kicks in. Note: One path cannot be longer than 255 bytes
312 // (NAME_MAX) whole path cannot be longer than PATH_MAX (usually
313 // 4096 on linux, 1024 on macos)
314 let mut inner_tmp_dir = tmpdir_path.to_path_buf();
315 for _ in 0..5 {
316 let newdir = iter::repeat("a").take(100).collect::<String>();
317 inner_tmp_dir.push(newdir);
318 assert!(mkdir(inner_tmp_dir.as_path(), Mode::S_IRWXU).is_ok());
319 }
320 assert!(chdir(inner_tmp_dir.as_path()).is_ok());
321 assert_eq!(getcwd().unwrap(), inner_tmp_dir.as_path());
322 }
323
324 #[test]
test_lseek()325 fn test_lseek() {
326 const CONTENTS: &[u8] = b"abcdef123456";
327 let mut tmp = tempfile().unwrap();
328 tmp.write_all(CONTENTS).unwrap();
329 let tmpfd = tmp.into_raw_fd();
330
331 let offset: off_t = 5;
332 lseek(tmpfd, offset, Whence::SeekSet).unwrap();
333
334 let mut buf = [0u8; 7];
335 ::read_exact(tmpfd, &mut buf);
336 assert_eq!(b"f123456", &buf);
337
338 close(tmpfd).unwrap();
339 }
340
341 #[cfg(any(target_os = "linux", target_os = "android"))]
342 #[test]
test_lseek64()343 fn test_lseek64() {
344 const CONTENTS: &[u8] = b"abcdef123456";
345 let mut tmp = tempfile().unwrap();
346 tmp.write_all(CONTENTS).unwrap();
347 let tmpfd = tmp.into_raw_fd();
348
349 lseek64(tmpfd, 5, Whence::SeekSet).unwrap();
350
351 let mut buf = [0u8; 7];
352 ::read_exact(tmpfd, &mut buf);
353 assert_eq!(b"f123456", &buf);
354
355 close(tmpfd).unwrap();
356 }
357
358 #[test]
test_fpathconf_limited()359 fn test_fpathconf_limited() {
360 let f = tempfile().unwrap();
361 // AFAIK, PATH_MAX is limited on all platforms, so it makes a good test
362 let path_max = fpathconf(f.as_raw_fd(), PathconfVar::PATH_MAX);
363 assert!(path_max.expect("fpathconf failed").expect("PATH_MAX is unlimited") > 0);
364 }
365
366 #[test]
test_pathconf_limited()367 fn test_pathconf_limited() {
368 // AFAIK, PATH_MAX is limited on all platforms, so it makes a good test
369 let path_max = pathconf("/", PathconfVar::PATH_MAX);
370 assert!(path_max.expect("pathconf failed").expect("PATH_MAX is unlimited") > 0);
371 }
372
373 #[test]
test_sysconf_limited()374 fn test_sysconf_limited() {
375 // AFAIK, OPEN_MAX is limited on all platforms, so it makes a good test
376 let open_max = sysconf(SysconfVar::OPEN_MAX);
377 assert!(open_max.expect("sysconf failed").expect("OPEN_MAX is unlimited") > 0);
378 }
379
380 #[cfg(target_os = "freebsd")]
381 #[test]
test_sysconf_unsupported()382 fn test_sysconf_unsupported() {
383 // I know of no sysconf variables that are unsupported everywhere, but
384 // _XOPEN_CRYPT is unsupported on FreeBSD 11.0, which is one of the platforms
385 // we test.
386 let open_max = sysconf(SysconfVar::_XOPEN_CRYPT);
387 assert!(open_max.expect("sysconf failed").is_none())
388 }
389
390 // Test that we can create a pair of pipes. No need to verify that they pass
391 // data; that's the domain of the OS, not nix.
392 #[test]
test_pipe()393 fn test_pipe() {
394 let (fd0, fd1) = pipe().unwrap();
395 let m0 = stat::SFlag::from_bits_truncate(stat::fstat(fd0).unwrap().st_mode);
396 // S_IFIFO means it's a pipe
397 assert_eq!(m0, SFlag::S_IFIFO);
398 let m1 = stat::SFlag::from_bits_truncate(stat::fstat(fd1).unwrap().st_mode);
399 assert_eq!(m1, SFlag::S_IFIFO);
400 }
401
402 // pipe2(2) is the same as pipe(2), except it allows setting some flags. Check
403 // that we can set a flag.
404 #[test]
test_pipe2()405 fn test_pipe2() {
406 let (fd0, fd1) = pipe2(OFlag::O_CLOEXEC).unwrap();
407 let f0 = FdFlag::from_bits_truncate(fcntl(fd0, FcntlArg::F_GETFD).unwrap());
408 assert!(f0.contains(FdFlag::FD_CLOEXEC));
409 let f1 = FdFlag::from_bits_truncate(fcntl(fd1, FcntlArg::F_GETFD).unwrap());
410 assert!(f1.contains(FdFlag::FD_CLOEXEC));
411 }
412
413 // Used in `test_alarm`.
414 static mut ALARM_CALLED: bool = false;
415
416 // Used in `test_alarm`.
alarm_signal_handler(raw_signal: libc::c_int)417 pub extern fn alarm_signal_handler(raw_signal: libc::c_int) {
418 assert_eq!(raw_signal, libc::SIGALRM, "unexpected signal: {}", raw_signal);
419 unsafe { ALARM_CALLED = true };
420 }
421
422 #[test]
test_alarm()423 fn test_alarm() {
424 let _m = ::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test");
425
426 let handler = SigHandler::Handler(alarm_signal_handler);
427 let signal_action = SigAction::new(handler, SaFlags::SA_RESTART, SigSet::empty());
428 let old_handler = unsafe {
429 sigaction(Signal::SIGALRM, &signal_action)
430 .expect("unable to set signal handler for alarm")
431 };
432
433 // Set an alarm.
434 assert_eq!(alarm::set(60), None);
435
436 // Overwriting an alarm should return the old alarm.
437 assert_eq!(alarm::set(1), Some(60));
438
439 // We should be woken up after 1 second by the alarm, so we'll sleep for 2
440 // seconds to be sure.
441 sleep(2);
442 assert_eq!(unsafe { ALARM_CALLED }, true, "expected our alarm signal handler to be called");
443
444 // Reset the signal.
445 unsafe {
446 sigaction(Signal::SIGALRM, &old_handler)
447 .expect("unable to set signal handler for alarm");
448 }
449 }
450
451 #[test]
test_canceling_alarm()452 fn test_canceling_alarm() {
453 let _m = ::SIGNAL_MTX.lock().expect("Mutex got poisoned by another test");
454
455 assert_eq!(alarm::cancel(), None);
456
457 assert_eq!(alarm::set(60), None);
458 assert_eq!(alarm::cancel(), Some(60));
459 }
460