]>
Commit | Line | Data |
---|---|---|
476ff2be SL |
1 | // Copyright 2016 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 | ||
ff7c6d11 | 11 | use env::{split_paths}; |
476ff2be | 12 | use ffi::OsStr; |
3b2f2976 | 13 | use os::unix::ffi::OsStrExt; |
476ff2be SL |
14 | use fmt; |
15 | use io::{self, Error, ErrorKind}; | |
0531ce1d | 16 | use libc::{EXIT_SUCCESS, EXIT_FAILURE}; |
3b2f2976 | 17 | use path::{Path, PathBuf}; |
476ff2be SL |
18 | use sys::fd::FileDesc; |
19 | use sys::fs::{File, OpenOptions}; | |
20 | use sys::pipe::{self, AnonPipe}; | |
21 | use sys::{cvt, syscall}; | |
ff7c6d11 | 22 | use sys_common::process::{CommandEnv, DefaultEnvKey}; |
476ff2be SL |
23 | |
24 | //////////////////////////////////////////////////////////////////////////////// | |
25 | // Command | |
26 | //////////////////////////////////////////////////////////////////////////////// | |
27 | ||
28 | pub struct Command { | |
29 | // Currently we try hard to ensure that the call to `.exec()` doesn't | |
30 | // actually allocate any memory. While many platforms try to ensure that | |
31 | // memory allocation works after a fork in a multithreaded process, it's | |
32 | // been observed to be buggy and somewhat unreliable, so we do our best to | |
33 | // just not do it at all! | |
34 | // | |
35 | // Along those lines, the `argv` and `envp` raw pointers here are exactly | |
36 | // what's gonna get passed to `execvp`. The `argv` array starts with the | |
37 | // `program` and ends with a NULL, and the `envp` pointer, if present, is | |
38 | // also null-terminated. | |
39 | // | |
40 | // Right now we don't support removing arguments, so there's no much fancy | |
41 | // support there, but we support adding and removing environment variables, | |
42 | // so a side table is used to track where in the `envp` array each key is | |
43 | // located. Whenever we add a key we update it in place if it's already | |
44 | // present, and whenever we remove a key we update the locations of all | |
45 | // other keys. | |
46 | program: String, | |
47 | args: Vec<String>, | |
ff7c6d11 | 48 | env: CommandEnv<DefaultEnvKey>, |
476ff2be SL |
49 | |
50 | cwd: Option<String>, | |
51 | uid: Option<u32>, | |
52 | gid: Option<u32>, | |
53 | saw_nul: bool, | |
54 | closures: Vec<Box<FnMut() -> io::Result<()> + Send + Sync>>, | |
55 | stdin: Option<Stdio>, | |
56 | stdout: Option<Stdio>, | |
57 | stderr: Option<Stdio>, | |
58 | } | |
59 | ||
60 | // passed back to std::process with the pipes connected to the child, if any | |
61 | // were requested | |
62 | pub struct StdioPipes { | |
63 | pub stdin: Option<AnonPipe>, | |
64 | pub stdout: Option<AnonPipe>, | |
65 | pub stderr: Option<AnonPipe>, | |
66 | } | |
67 | ||
68 | // passed to do_exec() with configuration of what the child stdio should look | |
69 | // like | |
70 | struct ChildPipes { | |
71 | stdin: ChildStdio, | |
72 | stdout: ChildStdio, | |
73 | stderr: ChildStdio, | |
74 | } | |
75 | ||
76 | enum ChildStdio { | |
77 | Inherit, | |
78 | Explicit(usize), | |
79 | Owned(FileDesc), | |
80 | } | |
81 | ||
82 | pub enum Stdio { | |
83 | Inherit, | |
84 | Null, | |
85 | MakePipe, | |
86 | Fd(FileDesc), | |
87 | } | |
88 | ||
89 | impl Command { | |
90 | pub fn new(program: &OsStr) -> Command { | |
91 | Command { | |
92 | program: program.to_str().unwrap().to_owned(), | |
93 | args: Vec::new(), | |
ff7c6d11 | 94 | env: Default::default(), |
476ff2be SL |
95 | cwd: None, |
96 | uid: None, | |
97 | gid: None, | |
98 | saw_nul: false, | |
99 | closures: Vec::new(), | |
100 | stdin: None, | |
101 | stdout: None, | |
102 | stderr: None, | |
103 | } | |
104 | } | |
105 | ||
106 | pub fn arg(&mut self, arg: &OsStr) { | |
107 | self.args.push(arg.to_str().unwrap().to_owned()); | |
108 | } | |
109 | ||
ff7c6d11 XL |
110 | pub fn env_mut(&mut self) -> &mut CommandEnv<DefaultEnvKey> { |
111 | &mut self.env | |
476ff2be SL |
112 | } |
113 | ||
114 | pub fn cwd(&mut self, dir: &OsStr) { | |
115 | self.cwd = Some(dir.to_str().unwrap().to_owned()); | |
116 | } | |
117 | pub fn uid(&mut self, id: u32) { | |
118 | self.uid = Some(id); | |
119 | } | |
120 | pub fn gid(&mut self, id: u32) { | |
121 | self.gid = Some(id); | |
122 | } | |
123 | ||
124 | pub fn before_exec(&mut self, | |
125 | f: Box<FnMut() -> io::Result<()> + Send + Sync>) { | |
126 | self.closures.push(f); | |
127 | } | |
128 | ||
129 | pub fn stdin(&mut self, stdin: Stdio) { | |
130 | self.stdin = Some(stdin); | |
131 | } | |
132 | pub fn stdout(&mut self, stdout: Stdio) { | |
133 | self.stdout = Some(stdout); | |
134 | } | |
135 | pub fn stderr(&mut self, stderr: Stdio) { | |
136 | self.stderr = Some(stderr); | |
137 | } | |
138 | ||
139 | pub fn spawn(&mut self, default: Stdio, needs_stdin: bool) | |
140 | -> io::Result<(Process, StdioPipes)> { | |
141 | const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX"; | |
142 | ||
143 | if self.saw_nul { | |
144 | return Err(io::Error::new(ErrorKind::InvalidInput, | |
145 | "nul byte found in provided data")); | |
146 | } | |
147 | ||
148 | let (ours, theirs) = self.setup_io(default, needs_stdin)?; | |
149 | let (input, output) = pipe::anon_pipe()?; | |
150 | ||
151 | let pid = unsafe { | |
152 | match cvt(syscall::clone(0))? { | |
153 | 0 => { | |
154 | drop(input); | |
155 | let err = self.do_exec(theirs); | |
156 | let errno = err.raw_os_error().unwrap_or(syscall::EINVAL) as u32; | |
157 | let bytes = [ | |
158 | (errno >> 24) as u8, | |
159 | (errno >> 16) as u8, | |
160 | (errno >> 8) as u8, | |
161 | (errno >> 0) as u8, | |
162 | CLOEXEC_MSG_FOOTER[0], CLOEXEC_MSG_FOOTER[1], | |
163 | CLOEXEC_MSG_FOOTER[2], CLOEXEC_MSG_FOOTER[3] | |
164 | ]; | |
165 | // pipe I/O up to PIPE_BUF bytes should be atomic, and then | |
166 | // we want to be sure we *don't* run at_exit destructors as | |
167 | // we're being torn down regardless | |
168 | assert!(output.write(&bytes).is_ok()); | |
169 | let _ = syscall::exit(1); | |
170 | panic!("failed to exit"); | |
171 | } | |
172 | n => n, | |
173 | } | |
174 | }; | |
175 | ||
176 | let mut p = Process { pid: pid, status: None }; | |
177 | drop(output); | |
178 | let mut bytes = [0; 8]; | |
179 | ||
180 | // loop to handle EINTR | |
181 | loop { | |
182 | match input.read(&mut bytes) { | |
183 | Ok(0) => return Ok((p, ours)), | |
184 | Ok(8) => { | |
185 | assert!(combine(CLOEXEC_MSG_FOOTER) == combine(&bytes[4.. 8]), | |
186 | "Validation on the CLOEXEC pipe failed: {:?}", bytes); | |
187 | let errno = combine(&bytes[0.. 4]); | |
188 | assert!(p.wait().is_ok(), | |
189 | "wait() should either return Ok or panic"); | |
190 | return Err(Error::from_raw_os_error(errno)) | |
191 | } | |
192 | Err(ref e) if e.kind() == ErrorKind::Interrupted => {} | |
193 | Err(e) => { | |
194 | assert!(p.wait().is_ok(), | |
195 | "wait() should either return Ok or panic"); | |
196 | panic!("the CLOEXEC pipe failed: {:?}", e) | |
197 | }, | |
198 | Ok(..) => { // pipe I/O up to PIPE_BUF bytes should be atomic | |
199 | assert!(p.wait().is_ok(), | |
200 | "wait() should either return Ok or panic"); | |
201 | panic!("short read on the CLOEXEC pipe") | |
202 | } | |
203 | } | |
204 | } | |
205 | ||
206 | fn combine(arr: &[u8]) -> i32 { | |
207 | let a = arr[0] as u32; | |
208 | let b = arr[1] as u32; | |
209 | let c = arr[2] as u32; | |
210 | let d = arr[3] as u32; | |
211 | ||
212 | ((a << 24) | (b << 16) | (c << 8) | (d << 0)) as i32 | |
213 | } | |
214 | } | |
215 | ||
216 | pub fn exec(&mut self, default: Stdio) -> io::Error { | |
217 | if self.saw_nul { | |
218 | return io::Error::new(ErrorKind::InvalidInput, | |
219 | "nul byte found in provided data") | |
220 | } | |
221 | ||
222 | match self.setup_io(default, true) { | |
223 | Ok((_, theirs)) => unsafe { self.do_exec(theirs) }, | |
224 | Err(e) => e, | |
225 | } | |
226 | } | |
227 | ||
228 | // And at this point we've reached a special time in the life of the | |
229 | // child. The child must now be considered hamstrung and unable to | |
230 | // do anything other than syscalls really. Consider the following | |
231 | // scenario: | |
232 | // | |
233 | // 1. Thread A of process 1 grabs the malloc() mutex | |
234 | // 2. Thread B of process 1 forks(), creating thread C | |
235 | // 3. Thread C of process 2 then attempts to malloc() | |
236 | // 4. The memory of process 2 is the same as the memory of | |
237 | // process 1, so the mutex is locked. | |
238 | // | |
239 | // This situation looks a lot like deadlock, right? It turns out | |
240 | // that this is what pthread_atfork() takes care of, which is | |
241 | // presumably implemented across platforms. The first thing that | |
242 | // threads to *before* forking is to do things like grab the malloc | |
243 | // mutex, and then after the fork they unlock it. | |
244 | // | |
245 | // Despite this information, libnative's spawn has been witnessed to | |
cc61c64b | 246 | // deadlock on both macOS and FreeBSD. I'm not entirely sure why, but |
476ff2be SL |
247 | // all collected backtraces point at malloc/free traffic in the |
248 | // child spawned process. | |
249 | // | |
250 | // For this reason, the block of code below should contain 0 | |
251 | // invocations of either malloc of free (or their related friends). | |
252 | // | |
253 | // As an example of not having malloc/free traffic, we don't close | |
254 | // this file descriptor by dropping the FileDesc (which contains an | |
255 | // allocation). Instead we just close it manually. This will never | |
256 | // have the drop glue anyway because this code never returns (the | |
257 | // child will either exec() or invoke syscall::exit) | |
258 | unsafe fn do_exec(&mut self, stdio: ChildPipes) -> io::Error { | |
259 | macro_rules! t { | |
260 | ($e:expr) => (match $e { | |
261 | Ok(e) => e, | |
262 | Err(e) => return e, | |
263 | }) | |
264 | } | |
265 | ||
266 | if let Some(fd) = stdio.stderr.fd() { | |
cc61c64b | 267 | t!(cvt(syscall::dup2(fd, 2, &[]))); |
3b2f2976 | 268 | let mut flags = t!(cvt(syscall::fcntl(2, syscall::F_GETFD, 0))); |
cc61c64b | 269 | flags &= ! syscall::O_CLOEXEC; |
3b2f2976 | 270 | t!(cvt(syscall::fcntl(2, syscall::F_SETFD, flags))); |
476ff2be SL |
271 | } |
272 | if let Some(fd) = stdio.stdout.fd() { | |
cc61c64b | 273 | t!(cvt(syscall::dup2(fd, 1, &[]))); |
3b2f2976 | 274 | let mut flags = t!(cvt(syscall::fcntl(1, syscall::F_GETFD, 0))); |
cc61c64b | 275 | flags &= ! syscall::O_CLOEXEC; |
3b2f2976 | 276 | t!(cvt(syscall::fcntl(1, syscall::F_SETFD, flags))); |
476ff2be SL |
277 | } |
278 | if let Some(fd) = stdio.stdin.fd() { | |
cc61c64b | 279 | t!(cvt(syscall::dup2(fd, 0, &[]))); |
3b2f2976 | 280 | let mut flags = t!(cvt(syscall::fcntl(0, syscall::F_GETFD, 0))); |
cc61c64b | 281 | flags &= ! syscall::O_CLOEXEC; |
3b2f2976 | 282 | t!(cvt(syscall::fcntl(0, syscall::F_SETFD, flags))); |
476ff2be SL |
283 | } |
284 | ||
285 | if let Some(g) = self.gid { | |
286 | t!(cvt(syscall::setregid(g as usize, g as usize))); | |
287 | } | |
288 | if let Some(u) = self.uid { | |
289 | t!(cvt(syscall::setreuid(u as usize, u as usize))); | |
290 | } | |
291 | if let Some(ref cwd) = self.cwd { | |
292 | t!(cvt(syscall::chdir(cwd))); | |
293 | } | |
294 | ||
295 | for callback in self.closures.iter_mut() { | |
296 | t!(callback()); | |
297 | } | |
298 | ||
299 | let mut args: Vec<[usize; 2]> = Vec::new(); | |
300 | args.push([self.program.as_ptr() as usize, self.program.len()]); | |
301 | for arg in self.args.iter() { | |
302 | args.push([arg.as_ptr() as usize, arg.len()]); | |
303 | } | |
304 | ||
ff7c6d11 | 305 | self.env.apply(); |
476ff2be SL |
306 | |
307 | let program = if self.program.contains(':') || self.program.contains('/') { | |
3b2f2976 XL |
308 | Some(PathBuf::from(&self.program)) |
309 | } else if let Ok(path_env) = ::env::var("PATH") { | |
310 | let mut program = None; | |
311 | for mut path in split_paths(&path_env) { | |
312 | path.push(&self.program); | |
313 | if path.exists() { | |
314 | program = Some(path); | |
315 | break; | |
316 | } | |
476ff2be | 317 | } |
3b2f2976 XL |
318 | program |
319 | } else { | |
320 | None | |
476ff2be SL |
321 | }; |
322 | ||
3b2f2976 XL |
323 | if let Some(program) = program { |
324 | if let Err(err) = syscall::execve(program.as_os_str().as_bytes(), &args) { | |
325 | io::Error::from_raw_os_error(err.errno as i32) | |
326 | } else { | |
327 | panic!("return from exec without err"); | |
328 | } | |
476ff2be | 329 | } else { |
3b2f2976 | 330 | io::Error::from_raw_os_error(syscall::ENOENT) |
476ff2be SL |
331 | } |
332 | } | |
333 | ||
334 | ||
335 | fn setup_io(&self, default: Stdio, needs_stdin: bool) | |
336 | -> io::Result<(StdioPipes, ChildPipes)> { | |
337 | let null = Stdio::Null; | |
338 | let default_stdin = if needs_stdin {&default} else {&null}; | |
339 | let stdin = self.stdin.as_ref().unwrap_or(default_stdin); | |
340 | let stdout = self.stdout.as_ref().unwrap_or(&default); | |
341 | let stderr = self.stderr.as_ref().unwrap_or(&default); | |
342 | let (their_stdin, our_stdin) = stdin.to_child_stdio(true)?; | |
343 | let (their_stdout, our_stdout) = stdout.to_child_stdio(false)?; | |
344 | let (their_stderr, our_stderr) = stderr.to_child_stdio(false)?; | |
345 | let ours = StdioPipes { | |
346 | stdin: our_stdin, | |
347 | stdout: our_stdout, | |
348 | stderr: our_stderr, | |
349 | }; | |
350 | let theirs = ChildPipes { | |
351 | stdin: their_stdin, | |
352 | stdout: their_stdout, | |
353 | stderr: their_stderr, | |
354 | }; | |
355 | Ok((ours, theirs)) | |
356 | } | |
357 | } | |
358 | ||
359 | impl Stdio { | |
360 | fn to_child_stdio(&self, readable: bool) | |
361 | -> io::Result<(ChildStdio, Option<AnonPipe>)> { | |
362 | match *self { | |
363 | Stdio::Inherit => Ok((ChildStdio::Inherit, None)), | |
364 | ||
365 | // Make sure that the source descriptors are not an stdio | |
366 | // descriptor, otherwise the order which we set the child's | |
367 | // descriptors may blow away a descriptor which we are hoping to | |
368 | // save. For example, suppose we want the child's stderr to be the | |
369 | // parent's stdout, and the child's stdout to be the parent's | |
370 | // stderr. No matter which we dup first, the second will get | |
371 | // overwritten prematurely. | |
372 | Stdio::Fd(ref fd) => { | |
373 | if fd.raw() <= 2 { | |
374 | Ok((ChildStdio::Owned(fd.duplicate()?), None)) | |
375 | } else { | |
376 | Ok((ChildStdio::Explicit(fd.raw()), None)) | |
377 | } | |
378 | } | |
379 | ||
380 | Stdio::MakePipe => { | |
381 | let (reader, writer) = pipe::anon_pipe()?; | |
382 | let (ours, theirs) = if readable { | |
383 | (writer, reader) | |
384 | } else { | |
385 | (reader, writer) | |
386 | }; | |
387 | Ok((ChildStdio::Owned(theirs.into_fd()), Some(ours))) | |
388 | } | |
389 | ||
390 | Stdio::Null => { | |
391 | let mut opts = OpenOptions::new(); | |
392 | opts.read(readable); | |
393 | opts.write(!readable); | |
3b2f2976 | 394 | let fd = File::open(Path::new("null:"), &opts)?; |
476ff2be SL |
395 | Ok((ChildStdio::Owned(fd.into_fd()), None)) |
396 | } | |
397 | } | |
398 | } | |
399 | } | |
400 | ||
041b39d2 XL |
401 | impl From<AnonPipe> for Stdio { |
402 | fn from(pipe: AnonPipe) -> Stdio { | |
403 | Stdio::Fd(pipe.into_fd()) | |
404 | } | |
405 | } | |
406 | ||
407 | impl From<File> for Stdio { | |
408 | fn from(file: File) -> Stdio { | |
409 | Stdio::Fd(file.into_fd()) | |
410 | } | |
411 | } | |
412 | ||
476ff2be SL |
413 | impl ChildStdio { |
414 | fn fd(&self) -> Option<usize> { | |
415 | match *self { | |
416 | ChildStdio::Inherit => None, | |
417 | ChildStdio::Explicit(fd) => Some(fd), | |
418 | ChildStdio::Owned(ref fd) => Some(fd.raw()), | |
419 | } | |
420 | } | |
421 | } | |
422 | ||
423 | impl fmt::Debug for Command { | |
424 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
425 | write!(f, "{:?}", self.program)?; | |
426 | for arg in &self.args { | |
427 | write!(f, " {:?}", arg)?; | |
428 | } | |
429 | Ok(()) | |
430 | } | |
431 | } | |
432 | ||
433 | //////////////////////////////////////////////////////////////////////////////// | |
434 | // Processes | |
435 | //////////////////////////////////////////////////////////////////////////////// | |
436 | ||
437 | /// Unix exit statuses | |
438 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] | |
439 | pub struct ExitStatus(i32); | |
440 | ||
441 | impl ExitStatus { | |
442 | fn exited(&self) -> bool { | |
443 | self.0 & 0x7F == 0 | |
444 | } | |
445 | ||
446 | pub fn success(&self) -> bool { | |
447 | self.code() == Some(0) | |
448 | } | |
449 | ||
450 | pub fn code(&self) -> Option<i32> { | |
451 | if self.exited() { | |
452 | Some((self.0 >> 8) & 0xFF) | |
453 | } else { | |
454 | None | |
455 | } | |
456 | } | |
457 | ||
458 | pub fn signal(&self) -> Option<i32> { | |
459 | if !self.exited() { | |
460 | Some(self.0 & 0x7F) | |
461 | } else { | |
462 | None | |
463 | } | |
464 | } | |
465 | } | |
466 | ||
467 | impl From<i32> for ExitStatus { | |
468 | fn from(a: i32) -> ExitStatus { | |
469 | ExitStatus(a) | |
470 | } | |
471 | } | |
472 | ||
473 | impl fmt::Display for ExitStatus { | |
474 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
475 | if let Some(code) = self.code() { | |
476 | write!(f, "exit code: {}", code) | |
477 | } else { | |
478 | let signal = self.signal().unwrap(); | |
479 | write!(f, "signal: {}", signal) | |
480 | } | |
481 | } | |
482 | } | |
483 | ||
0531ce1d XL |
484 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] |
485 | pub struct ExitCode(u8); | |
486 | ||
487 | impl ExitCode { | |
488 | pub const SUCCESS: ExitCode = ExitCode(EXIT_SUCCESS as _); | |
489 | pub const FAILURE: ExitCode = ExitCode(EXIT_FAILURE as _); | |
490 | ||
491 | pub fn as_i32(&self) -> i32 { | |
492 | self.0 as i32 | |
493 | } | |
494 | } | |
495 | ||
476ff2be SL |
496 | /// The unique id of the process (this should never be negative). |
497 | pub struct Process { | |
498 | pid: usize, | |
499 | status: Option<ExitStatus>, | |
500 | } | |
501 | ||
502 | impl Process { | |
503 | pub fn id(&self) -> u32 { | |
504 | self.pid as u32 | |
505 | } | |
506 | ||
507 | pub fn kill(&mut self) -> io::Result<()> { | |
508 | // If we've already waited on this process then the pid can be recycled | |
509 | // and used for another process, and we probably shouldn't be killing | |
510 | // random processes, so just return an error. | |
511 | if self.status.is_some() { | |
512 | Err(Error::new(ErrorKind::InvalidInput, | |
513 | "invalid argument: can't kill an exited process")) | |
514 | } else { | |
515 | cvt(syscall::kill(self.pid, syscall::SIGKILL))?; | |
516 | Ok(()) | |
517 | } | |
518 | } | |
519 | ||
520 | pub fn wait(&mut self) -> io::Result<ExitStatus> { | |
521 | if let Some(status) = self.status { | |
522 | return Ok(status) | |
523 | } | |
524 | let mut status = 0; | |
525 | cvt(syscall::waitpid(self.pid, &mut status, 0))?; | |
526 | self.status = Some(ExitStatus(status as i32)); | |
527 | Ok(ExitStatus(status as i32)) | |
528 | } | |
32a655c1 | 529 | |
8bb4bdeb | 530 | pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> { |
32a655c1 | 531 | if let Some(status) = self.status { |
8bb4bdeb | 532 | return Ok(Some(status)) |
32a655c1 SL |
533 | } |
534 | let mut status = 0; | |
535 | let pid = cvt(syscall::waitpid(self.pid, &mut status, syscall::WNOHANG))?; | |
536 | if pid == 0 { | |
8bb4bdeb | 537 | Ok(None) |
32a655c1 SL |
538 | } else { |
539 | self.status = Some(ExitStatus(status as i32)); | |
8bb4bdeb | 540 | Ok(Some(ExitStatus(status as i32))) |
32a655c1 SL |
541 | } |
542 | } | |
476ff2be | 543 | } |