1 use super::*;
2
3 use crate::std::ffi::OsStr;
4 use crate::std::mem;
5 use crate::std::ptr;
6 use crate::std::sys::{cvt, cvt_nz};
7 use dlibc;
8
9 macro_rules! t {
10 ($e:expr) => {
11 match $e {
12 Ok(t) => t,
13 Err(e) => panic!("received error for `{}`: {}", stringify!($e), e),
14 }
15 };
16 }
17
18 #[test]
19 #[cfg_attr(
20 any(
21 // See #14232 for more information, but it appears that signal delivery to a
22 // newly spawned process may just be raced in the macOS, so to prevent this
23 // test from being flaky we ignore it on macOS.
24 target_os = "macos",
25 // When run under our current QEMU emulation test suite this test fails,
26 // although the reason isn't very clear as to why. For now this test is
27 // ignored there.
28 target_arch = "arm",
29 target_arch = "aarch64",
30 target_arch = "riscv64",
31 ),
32 ignore
33 )]
test_process_mask()34 fn test_process_mask() {
35 // Test to make sure that a signal mask *does* get inherited.
36 fn test_inner(mut cmd: Command) {
37 unsafe {
38 let mut set = mem::MaybeUninit::<dlibc::sigset_t>::uninit();
39 let mut old_set = mem::MaybeUninit::<dlibc::sigset_t>::uninit();
40 t!(cvt(sigemptyset(set.as_mut_ptr())));
41 t!(cvt(sigaddset(set.as_mut_ptr(), dlibc::SIGINT)));
42 t!(cvt_nz(dlibc::pthread_sigmask(
43 dlibc::SIG_SETMASK,
44 set.as_ptr(),
45 old_set.as_mut_ptr()
46 )));
47
48 cmd.stdin(Stdio::MakePipe);
49 cmd.stdout(Stdio::MakePipe);
50
51 let (mut cat, mut pipes) = t!(cmd.spawn(Stdio::Null, true));
52 let stdin_write = pipes.stdin.take().unwrap();
53 let stdout_read = pipes.stdout.take().unwrap();
54
55 t!(cvt_nz(dlibc::pthread_sigmask(
56 dlibc::SIG_SETMASK,
57 old_set.as_ptr(),
58 ptr::null_mut()
59 )));
60
61 t!(cvt(dlibc::kill(cat.id() as dlibc::pid_t, dlibc::SIGINT)));
62 // We need to wait until SIGINT is definitely delivered. The
63 // easiest way is to write something to cat, and try to read it
64 // back: if SIGINT is unmasked, it'll get delivered when cat is
65 // next scheduled.
66 let _ = stdin_write.write(b"Hello");
67 drop(stdin_write);
68
69 // Exactly 5 bytes should be read.
70 let mut buf = [0; 5];
71 let ret = t!(stdout_read.read(&mut buf));
72 assert_eq!(ret, 5);
73 assert_eq!(&buf, b"Hello");
74
75 t!(cat.wait());
76 }
77 }
78
79 // A plain `Command::new` uses the posix_spawn path on many platforms.
80 let cmd = Command::new(OsStr::new("cat"));
81 test_inner(cmd);
82
83 // Specifying `pre_exec` forces the fork/exec path.
84 let mut cmd = Command::new(OsStr::new("cat"));
85 unsafe { cmd.pre_exec(Box::new(|| Ok(()))) };
86 test_inner(cmd);
87 }
88
89 #[test]
90 #[cfg_attr(
91 any(
92 // See test_process_mask
93 target_os = "macos",
94 target_arch = "arm",
95 target_arch = "aarch64",
96 target_arch = "riscv64",
97 ),
98 ignore
99 )]
test_process_group_posix_spawn()100 fn test_process_group_posix_spawn() {
101 unsafe {
102 // Spawn a cat subprocess that's just going to hang since there is no I/O.
103 let mut cmd = Command::new(OsStr::new("cat"));
104 cmd.pgroup(0);
105 cmd.stdin(Stdio::MakePipe);
106 cmd.stdout(Stdio::MakePipe);
107 let (mut cat, _pipes) = t!(cmd.spawn(Stdio::Null, true));
108
109 // Check that we can kill its process group, which means there *is* one.
110 t!(cvt(dlibc::kill(-(cat.id() as dlibc::pid_t), dlibc::SIGINT)));
111
112 t!(cat.wait());
113 }
114 }
115
116 #[test]
117 #[cfg_attr(
118 any(
119 // See test_process_mask
120 target_os = "macos",
121 target_arch = "arm",
122 target_arch = "aarch64",
123 target_arch = "riscv64",
124 ),
125 ignore
126 )]
test_process_group_no_posix_spawn()127 fn test_process_group_no_posix_spawn() {
128 unsafe {
129 // Same as above, create hang-y cat. This time, force using the non-posix_spawnp path.
130 let mut cmd = Command::new(OsStr::new("cat"));
131 cmd.pgroup(0);
132 cmd.pre_exec(Box::new(|| Ok(()))); // pre_exec forces fork + exec
133 cmd.stdin(Stdio::MakePipe);
134 cmd.stdout(Stdio::MakePipe);
135 let (mut cat, _pipes) = t!(cmd.spawn(Stdio::Null, true));
136
137 // Check that we can kill its process group, which means there *is* one.
138 t!(cvt(dlibc::kill(-(cat.id() as dlibc::pid_t), dlibc::SIGINT)));
139
140 t!(cat.wait());
141 }
142 }
143
144 #[test]
test_program_kind()145 fn test_program_kind() {
146 let vectors = &[
147 ("foo", ProgramKind::PathLookup),
148 ("foo.out", ProgramKind::PathLookup),
149 ("./foo", ProgramKind::Relative),
150 ("../foo", ProgramKind::Relative),
151 ("dir/foo", ProgramKind::Relative),
152 // Note that paths on Unix can't contain / in them, so this is actually the directory "fo\\"
153 // followed by the file "o".
154 ("fo\\/o", ProgramKind::Relative),
155 ("/foo", ProgramKind::Absolute),
156 ("/dir/../foo", ProgramKind::Absolute),
157 ];
158
159 for (program, expected_kind) in vectors {
160 assert_eq!(
161 ProgramKind::new(program.as_ref()),
162 *expected_kind,
163 "actual != expected program kind for input {program}",
164 );
165 }
166 }
167