]>
Commit | Line | Data |
---|---|---|
85aaf69f SL |
1 | // Copyright 2014-2015 The Rust Project Developers. See the COPYRIGHT |
2 | // file at the top-level directory of this distribution and at | |
3 | // http://rust-lang.org/COPYRIGHT. | |
4 | // | |
5 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or | |
6 | // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license | |
7 | // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your | |
8 | // option. This file may not be copied, modified, or distributed | |
9 | // except according to those terms. | |
10 | ||
92a42be0 SL |
11 | #![allow(non_snake_case)] |
12 | ||
85aaf69f | 13 | use prelude::v1::*; |
c34b1796 | 14 | use os::unix::prelude::*; |
85aaf69f SL |
15 | |
16 | use collections::HashMap; | |
17 | use env; | |
9346a6ac | 18 | use ffi::{OsString, OsStr, CString, CStr}; |
85aaf69f | 19 | use fmt; |
85aaf69f SL |
20 | use io::{self, Error, ErrorKind}; |
21 | use libc::{self, pid_t, c_void, c_int, gid_t, uid_t}; | |
85aaf69f | 22 | use ptr; |
62682a34 SL |
23 | use sys::fd::FileDesc; |
24 | use sys::fs::{File, OpenOptions}; | |
d9579d0f | 25 | use sys::pipe::AnonPipe; |
92a42be0 | 26 | use sys::{self, cvt, cvt_r}; |
85aaf69f SL |
27 | |
28 | //////////////////////////////////////////////////////////////////////////////// | |
29 | // Command | |
30 | //////////////////////////////////////////////////////////////////////////////// | |
31 | ||
32 | #[derive(Clone)] | |
33 | pub struct Command { | |
34 | pub program: CString, | |
35 | pub args: Vec<CString>, | |
36 | pub env: Option<HashMap<OsString, OsString>>, | |
37 | pub cwd: Option<CString>, | |
38 | pub uid: Option<uid_t>, | |
39 | pub gid: Option<gid_t>, | |
e9174d1e | 40 | pub session_leader: bool, |
85aaf69f SL |
41 | } |
42 | ||
43 | impl Command { | |
44 | pub fn new(program: &OsStr) -> Command { | |
45 | Command { | |
92a42be0 | 46 | program: os2c(program), |
85aaf69f SL |
47 | args: Vec::new(), |
48 | env: None, | |
49 | cwd: None, | |
50 | uid: None, | |
51 | gid: None, | |
e9174d1e | 52 | session_leader: false, |
85aaf69f SL |
53 | } |
54 | } | |
55 | ||
56 | pub fn arg(&mut self, arg: &OsStr) { | |
92a42be0 | 57 | self.args.push(os2c(arg)); |
85aaf69f SL |
58 | } |
59 | pub fn args<'a, I: Iterator<Item = &'a OsStr>>(&mut self, args: I) { | |
92a42be0 | 60 | self.args.extend(args.map(os2c)); |
85aaf69f SL |
61 | } |
62 | fn init_env_map(&mut self) { | |
63 | if self.env.is_none() { | |
64 | self.env = Some(env::vars_os().collect()); | |
65 | } | |
66 | } | |
67 | pub fn env(&mut self, key: &OsStr, val: &OsStr) { | |
68 | self.init_env_map(); | |
69 | self.env.as_mut().unwrap().insert(key.to_os_string(), val.to_os_string()); | |
70 | } | |
71 | pub fn env_remove(&mut self, key: &OsStr) { | |
72 | self.init_env_map(); | |
73 | self.env.as_mut().unwrap().remove(&key.to_os_string()); | |
74 | } | |
75 | pub fn env_clear(&mut self) { | |
76 | self.env = Some(HashMap::new()) | |
77 | } | |
78 | pub fn cwd(&mut self, dir: &OsStr) { | |
92a42be0 | 79 | self.cwd = Some(os2c(dir)); |
85aaf69f SL |
80 | } |
81 | } | |
82 | ||
92a42be0 SL |
83 | fn os2c(s: &OsStr) -> CString { |
84 | CString::new(s.as_bytes()).unwrap() | |
85 | } | |
86 | ||
85aaf69f SL |
87 | //////////////////////////////////////////////////////////////////////////////// |
88 | // Processes | |
89 | //////////////////////////////////////////////////////////////////////////////// | |
90 | ||
91 | /// Unix exit statuses | |
92 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] | |
92a42be0 SL |
93 | pub struct ExitStatus(c_int); |
94 | ||
95 | #[cfg(any(target_os = "linux", target_os = "android", | |
96 | target_os = "nacl"))] | |
97 | mod status_imp { | |
98 | pub fn WIFEXITED(status: i32) -> bool { (status & 0xff) == 0 } | |
99 | pub fn WEXITSTATUS(status: i32) -> i32 { (status >> 8) & 0xff } | |
100 | pub fn WTERMSIG(status: i32) -> i32 { status & 0x7f } | |
101 | } | |
102 | ||
103 | #[cfg(any(target_os = "macos", | |
104 | target_os = "ios", | |
105 | target_os = "freebsd", | |
106 | target_os = "dragonfly", | |
107 | target_os = "bitrig", | |
108 | target_os = "netbsd", | |
109 | target_os = "openbsd"))] | |
110 | mod status_imp { | |
111 | pub fn WIFEXITED(status: i32) -> bool { (status & 0x7f) == 0 } | |
112 | pub fn WEXITSTATUS(status: i32) -> i32 { status >> 8 } | |
113 | pub fn WTERMSIG(status: i32) -> i32 { status & 0o177 } | |
85aaf69f SL |
114 | } |
115 | ||
116 | impl ExitStatus { | |
92a42be0 SL |
117 | fn exited(&self) -> bool { |
118 | status_imp::WIFEXITED(self.0) | |
119 | } | |
120 | ||
85aaf69f | 121 | pub fn success(&self) -> bool { |
92a42be0 | 122 | self.code() == Some(0) |
85aaf69f | 123 | } |
92a42be0 | 124 | |
85aaf69f | 125 | pub fn code(&self) -> Option<i32> { |
92a42be0 SL |
126 | if self.exited() { |
127 | Some(status_imp::WEXITSTATUS(self.0)) | |
128 | } else { | |
129 | None | |
130 | } | |
131 | } | |
132 | ||
133 | pub fn signal(&self) -> Option<i32> { | |
134 | if !self.exited() { | |
135 | Some(status_imp::WTERMSIG(self.0)) | |
136 | } else { | |
137 | None | |
85aaf69f SL |
138 | } |
139 | } | |
140 | } | |
141 | ||
142 | impl fmt::Display for ExitStatus { | |
143 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
92a42be0 SL |
144 | if let Some(code) = self.code() { |
145 | write!(f, "exit code: {}", code) | |
146 | } else { | |
147 | let signal = self.signal().unwrap(); | |
148 | write!(f, "signal: {}", signal) | |
85aaf69f SL |
149 | } |
150 | } | |
151 | } | |
152 | ||
153 | /// The unique id of the process (this should never be negative). | |
154 | pub struct Process { | |
155 | pid: pid_t | |
156 | } | |
157 | ||
9346a6ac AL |
158 | pub enum Stdio { |
159 | Inherit, | |
9346a6ac | 160 | None, |
62682a34 | 161 | Raw(c_int), |
9346a6ac AL |
162 | } |
163 | ||
62682a34 SL |
164 | pub type RawStdio = FileDesc; |
165 | ||
85aaf69f SL |
166 | const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX"; |
167 | ||
168 | impl Process { | |
85aaf69f | 169 | pub unsafe fn kill(&self) -> io::Result<()> { |
92a42be0 | 170 | try!(cvt(libc::kill(self.pid, libc::SIGKILL))); |
85aaf69f SL |
171 | Ok(()) |
172 | } | |
173 | ||
174 | pub fn spawn(cfg: &Command, | |
9346a6ac AL |
175 | in_fd: Stdio, |
176 | out_fd: Stdio, | |
177 | err_fd: Stdio) -> io::Result<Process> { | |
178 | let dirp = cfg.cwd.as_ref().map(|c| c.as_ptr()).unwrap_or(ptr::null()); | |
179 | ||
180 | let (envp, _a, _b) = make_envp(cfg.env.as_ref()); | |
181 | let (argv, _a) = make_argv(&cfg.program, &cfg.args); | |
d9579d0f | 182 | let (input, output) = try!(sys::pipe::anon_pipe()); |
9346a6ac AL |
183 | |
184 | let pid = unsafe { | |
185 | match libc::fork() { | |
186 | 0 => { | |
187 | drop(input); | |
188 | Process::child_after_fork(cfg, output, argv, envp, dirp, | |
189 | in_fd, out_fd, err_fd) | |
190 | } | |
191 | n if n < 0 => return Err(Error::last_os_error()), | |
192 | n => n, | |
193 | } | |
194 | }; | |
195 | ||
196 | let p = Process{ pid: pid }; | |
197 | drop(output); | |
198 | let mut bytes = [0; 8]; | |
199 | ||
200 | // loop to handle EINTR | |
201 | loop { | |
202 | match input.read(&mut bytes) { | |
203 | Ok(0) => return Ok(p), | |
204 | Ok(8) => { | |
205 | assert!(combine(CLOEXEC_MSG_FOOTER) == combine(&bytes[4.. 8]), | |
206 | "Validation on the CLOEXEC pipe failed: {:?}", bytes); | |
207 | let errno = combine(&bytes[0.. 4]); | |
208 | assert!(p.wait().is_ok(), | |
209 | "wait() should either return Ok or panic"); | |
210 | return Err(Error::from_raw_os_error(errno)) | |
211 | } | |
212 | Err(ref e) if e.kind() == ErrorKind::Interrupted => {} | |
213 | Err(e) => { | |
214 | assert!(p.wait().is_ok(), | |
215 | "wait() should either return Ok or panic"); | |
216 | panic!("the CLOEXEC pipe failed: {:?}", e) | |
217 | }, | |
218 | Ok(..) => { // pipe I/O up to PIPE_BUF bytes should be atomic | |
219 | assert!(p.wait().is_ok(), | |
220 | "wait() should either return Ok or panic"); | |
221 | panic!("short read on the CLOEXEC pipe") | |
222 | } | |
85aaf69f SL |
223 | } |
224 | } | |
225 | ||
9346a6ac AL |
226 | fn combine(arr: &[u8]) -> i32 { |
227 | let a = arr[0] as u32; | |
228 | let b = arr[1] as u32; | |
229 | let c = arr[2] as u32; | |
230 | let d = arr[3] as u32; | |
85aaf69f | 231 | |
9346a6ac | 232 | ((a << 24) | (b << 16) | (c << 8) | (d << 0)) as i32 |
c34b1796 | 233 | } |
9346a6ac | 234 | } |
c34b1796 | 235 | |
9346a6ac AL |
236 | // And at this point we've reached a special time in the life of the |
237 | // child. The child must now be considered hamstrung and unable to | |
238 | // do anything other than syscalls really. Consider the following | |
239 | // scenario: | |
240 | // | |
241 | // 1. Thread A of process 1 grabs the malloc() mutex | |
242 | // 2. Thread B of process 1 forks(), creating thread C | |
243 | // 3. Thread C of process 2 then attempts to malloc() | |
244 | // 4. The memory of process 2 is the same as the memory of | |
245 | // process 1, so the mutex is locked. | |
246 | // | |
247 | // This situation looks a lot like deadlock, right? It turns out | |
248 | // that this is what pthread_atfork() takes care of, which is | |
249 | // presumably implemented across platforms. The first thing that | |
250 | // threads to *before* forking is to do things like grab the malloc | |
251 | // mutex, and then after the fork they unlock it. | |
252 | // | |
253 | // Despite this information, libnative's spawn has been witnessed to | |
254 | // deadlock on both OSX and FreeBSD. I'm not entirely sure why, but | |
255 | // all collected backtraces point at malloc/free traffic in the | |
256 | // child spawned process. | |
257 | // | |
258 | // For this reason, the block of code below should contain 0 | |
259 | // invocations of either malloc of free (or their related friends). | |
260 | // | |
261 | // As an example of not having malloc/free traffic, we don't close | |
262 | // this file descriptor by dropping the FileDesc (which contains an | |
263 | // allocation). Instead we just close it manually. This will never | |
264 | // have the drop glue anyway because this code never returns (the | |
265 | // child will either exec() or invoke libc::exit) | |
266 | unsafe fn child_after_fork(cfg: &Command, | |
267 | mut output: AnonPipe, | |
268 | argv: *const *const libc::c_char, | |
269 | envp: *const libc::c_void, | |
270 | dirp: *const libc::c_char, | |
271 | in_fd: Stdio, | |
272 | out_fd: Stdio, | |
273 | err_fd: Stdio) -> ! { | |
274 | fn fail(output: &mut AnonPipe) -> ! { | |
275 | let errno = sys::os::errno() as u32; | |
276 | let bytes = [ | |
277 | (errno >> 24) as u8, | |
278 | (errno >> 16) as u8, | |
279 | (errno >> 8) as u8, | |
280 | (errno >> 0) as u8, | |
281 | CLOEXEC_MSG_FOOTER[0], CLOEXEC_MSG_FOOTER[1], | |
282 | CLOEXEC_MSG_FOOTER[2], CLOEXEC_MSG_FOOTER[3] | |
283 | ]; | |
284 | // pipe I/O up to PIPE_BUF bytes should be atomic, and then we want | |
285 | // to be sure we *don't* run at_exit destructors as we're being torn | |
286 | // down regardless | |
287 | assert!(output.write(&bytes).is_ok()); | |
288 | unsafe { libc::_exit(1) } | |
c34b1796 AL |
289 | } |
290 | ||
9346a6ac | 291 | let setup = |src: Stdio, dst: c_int| { |
62682a34 SL |
292 | match src { |
293 | Stdio::Inherit => true, | |
294 | Stdio::Raw(fd) => cvt_r(|| libc::dup2(fd, dst)).is_ok(), | |
9346a6ac AL |
295 | |
296 | // If a stdio file descriptor is set to be ignored, we open up | |
297 | // /dev/null into that file descriptor. Otherwise, the first | |
298 | // file descriptor opened up in the child would be numbered as | |
299 | // one of the stdio file descriptors, which is likely to wreak | |
300 | // havoc. | |
301 | Stdio::None => { | |
302 | let mut opts = OpenOptions::new(); | |
303 | opts.read(dst == libc::STDIN_FILENO); | |
304 | opts.write(dst != libc::STDIN_FILENO); | |
305 | let devnull = CStr::from_ptr(b"/dev/null\0".as_ptr() | |
306 | as *const _); | |
307 | if let Ok(f) = File::open_c(devnull, &opts) { | |
62682a34 | 308 | cvt_r(|| libc::dup2(f.fd().raw(), dst)).is_ok() |
9346a6ac | 309 | } else { |
62682a34 | 310 | false |
85aaf69f SL |
311 | } |
312 | } | |
62682a34 | 313 | } |
9346a6ac | 314 | }; |
85aaf69f | 315 | |
9346a6ac AL |
316 | if !setup(in_fd, libc::STDIN_FILENO) { fail(&mut output) } |
317 | if !setup(out_fd, libc::STDOUT_FILENO) { fail(&mut output) } | |
318 | if !setup(err_fd, libc::STDERR_FILENO) { fail(&mut output) } | |
85aaf69f | 319 | |
9346a6ac AL |
320 | if let Some(u) = cfg.gid { |
321 | if libc::setgid(u as libc::gid_t) != 0 { | |
85aaf69f | 322 | fail(&mut output); |
9346a6ac AL |
323 | } |
324 | } | |
325 | if let Some(u) = cfg.uid { | |
326 | // When dropping privileges from root, the `setgroups` call | |
327 | // will remove any extraneous groups. If we don't call this, | |
328 | // then even though our uid has dropped, we may still have | |
329 | // groups that enable us to do super-user things. This will | |
330 | // fail if we aren't root, so don't bother checking the | |
331 | // return value, this is just done as an optimistic | |
332 | // privilege dropping function. | |
92a42be0 | 333 | let _ = libc::setgroups(0, ptr::null()); |
9346a6ac AL |
334 | |
335 | if libc::setuid(u as libc::uid_t) != 0 { | |
336 | fail(&mut output); | |
337 | } | |
338 | } | |
e9174d1e | 339 | if cfg.session_leader { |
9346a6ac AL |
340 | // Don't check the error of setsid because it fails if we're the |
341 | // process leader already. We just forked so it shouldn't return | |
342 | // error, but ignore it anyway. | |
343 | let _ = libc::setsid(); | |
344 | } | |
345 | if !dirp.is_null() && libc::chdir(dirp) == -1 { | |
346 | fail(&mut output); | |
347 | } | |
348 | if !envp.is_null() { | |
349 | *sys::os::environ() = envp as *const _; | |
350 | } | |
62682a34 | 351 | |
92a42be0 SL |
352 | #[cfg(not(target_os = "nacl"))] |
353 | unsafe fn reset_signal_handling(output: &mut AnonPipe) { | |
354 | use mem; | |
355 | // Reset signal handling so the child process starts in a | |
356 | // standardized state. libstd ignores SIGPIPE, and signal-handling | |
357 | // libraries often set a mask. Child processes inherit ignored | |
358 | // signals and the signal mask from their parent, but most | |
359 | // UNIX programs do not reset these things on their own, so we | |
360 | // need to clean things up now to avoid confusing the program | |
361 | // we're about to run. | |
362 | let mut set: libc::sigset_t = mem::uninitialized(); | |
363 | if libc::sigemptyset(&mut set) != 0 || | |
364 | libc::pthread_sigmask(libc::SIG_SETMASK, &set, ptr::null_mut()) != 0 || | |
365 | libc::signal( | |
366 | libc::SIGPIPE, mem::transmute(libc::SIG_DFL) | |
367 | ) == mem::transmute(libc::SIG_ERR) | |
368 | { | |
369 | fail(output); | |
370 | } | |
371 | } | |
372 | #[cfg(target_os = "nacl")] | |
373 | unsafe fn reset_signal_handling(_output: &mut AnonPipe) { | |
374 | // NaCl has no signal support. | |
62682a34 | 375 | } |
92a42be0 | 376 | reset_signal_handling(&mut output); |
62682a34 SL |
377 | |
378 | let _ = libc::execvp(*argv, argv); | |
9346a6ac | 379 | fail(&mut output) |
85aaf69f SL |
380 | } |
381 | ||
62682a34 SL |
382 | pub fn id(&self) -> u32 { |
383 | self.pid as u32 | |
384 | } | |
385 | ||
85aaf69f SL |
386 | pub fn wait(&self) -> io::Result<ExitStatus> { |
387 | let mut status = 0 as c_int; | |
92a42be0 SL |
388 | try!(cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })); |
389 | Ok(ExitStatus(status)) | |
85aaf69f SL |
390 | } |
391 | ||
392 | pub fn try_wait(&self) -> Option<ExitStatus> { | |
393 | let mut status = 0 as c_int; | |
9346a6ac | 394 | match cvt_r(|| unsafe { |
92a42be0 | 395 | libc::waitpid(self.pid, &mut status, libc::WNOHANG) |
85aaf69f | 396 | }) { |
9346a6ac | 397 | Ok(0) => None, |
92a42be0 | 398 | Ok(n) if n == self.pid => Some(ExitStatus(status)), |
d9579d0f | 399 | Ok(n) => panic!("unknown pid: {}", n), |
9346a6ac | 400 | Err(e) => panic!("unknown waitpid error: {}", e), |
85aaf69f SL |
401 | } |
402 | } | |
403 | } | |
404 | ||
9346a6ac AL |
405 | fn make_argv(prog: &CString, args: &[CString]) |
406 | -> (*const *const libc::c_char, Vec<*const libc::c_char>) | |
85aaf69f SL |
407 | { |
408 | let mut ptrs: Vec<*const libc::c_char> = Vec::with_capacity(args.len()+1); | |
409 | ||
410 | // Convert the CStrings into an array of pointers. Note: the | |
411 | // lifetime of the various CStrings involved is guaranteed to be | |
412 | // larger than the lifetime of our invocation of cb, but this is | |
413 | // technically unsafe as the callback could leak these pointers | |
414 | // out of our scope. | |
415 | ptrs.push(prog.as_ptr()); | |
416 | ptrs.extend(args.iter().map(|tmp| tmp.as_ptr())); | |
417 | ||
418 | // Add a terminating null pointer (required by libc). | |
419 | ptrs.push(ptr::null()); | |
420 | ||
9346a6ac | 421 | (ptrs.as_ptr(), ptrs) |
85aaf69f SL |
422 | } |
423 | ||
9346a6ac AL |
424 | fn make_envp(env: Option<&HashMap<OsString, OsString>>) |
425 | -> (*const c_void, Vec<Vec<u8>>, Vec<*const libc::c_char>) | |
85aaf69f SL |
426 | { |
427 | // On posixy systems we can pass a char** for envp, which is a | |
428 | // null-terminated array of "k=v\0" strings. Since we must create | |
429 | // these strings locally, yet expose a raw pointer to them, we | |
430 | // create a temporary vector to own the CStrings that outlives the | |
431 | // call to cb. | |
9346a6ac AL |
432 | if let Some(env) = env { |
433 | let mut tmps = Vec::with_capacity(env.len()); | |
434 | ||
435 | for pair in env { | |
436 | let mut kv = Vec::new(); | |
92a42be0 | 437 | kv.extend_from_slice(pair.0.as_bytes()); |
9346a6ac | 438 | kv.push('=' as u8); |
92a42be0 | 439 | kv.extend_from_slice(pair.1.as_bytes()); |
9346a6ac AL |
440 | kv.push(0); // terminating null |
441 | tmps.push(kv); | |
442 | } | |
85aaf69f | 443 | |
9346a6ac AL |
444 | let mut ptrs: Vec<*const libc::c_char> = |
445 | tmps.iter() | |
446 | .map(|tmp| tmp.as_ptr() as *const libc::c_char) | |
447 | .collect(); | |
448 | ptrs.push(ptr::null()); | |
85aaf69f | 449 | |
9346a6ac AL |
450 | (ptrs.as_ptr() as *const _, tmps, ptrs) |
451 | } else { | |
e9174d1e | 452 | (ptr::null(), Vec::new(), Vec::new()) |
85aaf69f SL |
453 | } |
454 | } | |
455 | ||
62682a34 SL |
456 | #[cfg(test)] |
457 | mod tests { | |
458 | use super::*; | |
459 | use prelude::v1::*; | |
460 | ||
461 | use ffi::OsStr; | |
462 | use mem; | |
463 | use ptr; | |
464 | use libc; | |
465 | use slice; | |
92a42be0 | 466 | use sys::{self, cvt, pipe}; |
62682a34 | 467 | |
b039eaaf SL |
468 | macro_rules! t { |
469 | ($e:expr) => { | |
470 | match $e { | |
471 | Ok(t) => t, | |
472 | Err(e) => panic!("received error for `{}`: {}", stringify!($e), e), | |
473 | } | |
474 | } | |
475 | } | |
476 | ||
62682a34 SL |
477 | #[cfg(not(target_os = "android"))] |
478 | extern { | |
b039eaaf | 479 | #[cfg_attr(target_os = "netbsd", link_name = "__sigaddset14")] |
92a42be0 | 480 | fn sigaddset(set: *mut libc::sigset_t, signum: libc::c_int) -> libc::c_int; |
62682a34 SL |
481 | } |
482 | ||
483 | #[cfg(target_os = "android")] | |
92a42be0 SL |
484 | unsafe fn sigaddset(set: *mut libc::sigset_t, signum: libc::c_int) -> libc::c_int { |
485 | let raw = slice::from_raw_parts_mut(set as *mut u8, mem::size_of::<libc::sigset_t>()); | |
62682a34 SL |
486 | let bit = (signum - 1) as usize; |
487 | raw[bit / 8] |= 1 << (bit % 8); | |
488 | return 0; | |
489 | } | |
490 | ||
e9174d1e SL |
491 | // See #14232 for more information, but it appears that signal delivery to a |
492 | // newly spawned process may just be raced in the OSX, so to prevent this | |
493 | // test from being flaky we ignore it on OSX. | |
62682a34 | 494 | #[test] |
e9174d1e | 495 | #[cfg_attr(target_os = "macos", ignore)] |
92a42be0 | 496 | #[cfg_attr(target_os = "nacl", ignore)] // no signals on NaCl. |
62682a34 SL |
497 | fn test_process_mask() { |
498 | unsafe { | |
499 | // Test to make sure that a signal mask does not get inherited. | |
500 | let cmd = Command::new(OsStr::new("cat")); | |
b039eaaf SL |
501 | let (stdin_read, stdin_write) = t!(sys::pipe::anon_pipe()); |
502 | let (stdout_read, stdout_write) = t!(sys::pipe::anon_pipe()); | |
62682a34 | 503 | |
92a42be0 SL |
504 | let mut set: libc::sigset_t = mem::uninitialized(); |
505 | let mut old_set: libc::sigset_t = mem::uninitialized(); | |
506 | t!(cvt(libc::sigemptyset(&mut set))); | |
b039eaaf | 507 | t!(cvt(sigaddset(&mut set, libc::SIGINT))); |
92a42be0 | 508 | t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &set, &mut old_set))); |
62682a34 | 509 | |
b039eaaf SL |
510 | let cat = t!(Process::spawn(&cmd, Stdio::Raw(stdin_read.raw()), |
511 | Stdio::Raw(stdout_write.raw()), | |
512 | Stdio::None)); | |
62682a34 SL |
513 | drop(stdin_read); |
514 | drop(stdout_write); | |
515 | ||
92a42be0 SL |
516 | t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &old_set, |
517 | ptr::null_mut()))); | |
62682a34 | 518 | |
92a42be0 | 519 | t!(cvt(libc::kill(cat.id() as libc::pid_t, libc::SIGINT))); |
62682a34 SL |
520 | // We need to wait until SIGINT is definitely delivered. The |
521 | // easiest way is to write something to cat, and try to read it | |
522 | // back: if SIGINT is unmasked, it'll get delivered when cat is | |
523 | // next scheduled. | |
524 | let _ = stdin_write.write(b"Hello"); | |
525 | drop(stdin_write); | |
526 | ||
527 | // Either EOF or failure (EPIPE) is okay. | |
528 | let mut buf = [0; 5]; | |
529 | if let Ok(ret) = stdout_read.read(&mut buf) { | |
530 | assert!(ret == 0); | |
531 | } | |
532 | ||
b039eaaf | 533 | t!(cat.wait()); |
62682a34 SL |
534 | } |
535 | } | |
536 | } |