]> git.lizzy.rs Git - rust.git/blob - src/libstd/sys/unix/process/process_unix.rs
f389c60615f244e28fa34a3694e6f567adae9433
[rust.git] / src / libstd / sys / unix / process / process_unix.rs
1 use crate::fmt;
2 use crate::io::{self, Error, ErrorKind};
3 use crate::ptr;
4 use crate::sys;
5 use crate::sys::cvt;
6 use crate::sys::process::process_common::*;
7
8 use libc::{c_int, gid_t, pid_t, uid_t};
9
10 ////////////////////////////////////////////////////////////////////////////////
11 // Command
12 ////////////////////////////////////////////////////////////////////////////////
13
14 impl Command {
15     pub fn spawn(
16         &mut self,
17         default: Stdio,
18         needs_stdin: bool,
19     ) -> io::Result<(Process, StdioPipes)> {
20         const CLOEXEC_MSG_FOOTER: &[u8] = b"NOEX";
21
22         let envp = self.capture_env();
23
24         if self.saw_nul() {
25             return Err(io::Error::new(ErrorKind::InvalidInput, "nul byte found in provided data"));
26         }
27
28         let (ours, theirs) = self.setup_io(default, needs_stdin)?;
29
30         if let Some(ret) = self.posix_spawn(&theirs, envp.as_ref())? {
31             return Ok((ret, ours));
32         }
33
34         let (input, output) = sys::pipe::anon_pipe()?;
35
36         // Whatever happens after the fork is almost for sure going to touch or
37         // look at the environment in one way or another (PATH in `execvp` or
38         // accessing the `environ` pointer ourselves). Make sure no other thread
39         // is accessing the environment when we do the fork itself.
40         //
41         // Note that as soon as we're done with the fork there's no need to hold
42         // a lock any more because the parent won't do anything and the child is
43         // in its own process.
44         let result = unsafe {
45             let _env_lock = sys::os::env_lock();
46             cvt(libc::fork())?
47         };
48
49         let pid = unsafe {
50             match result {
51                 0 => {
52                     drop(input);
53                     let Err(err) = self.do_exec(theirs, envp.as_ref());
54                     let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32;
55                     let bytes = [
56                         (errno >> 24) as u8,
57                         (errno >> 16) as u8,
58                         (errno >> 8) as u8,
59                         (errno >> 0) as u8,
60                         CLOEXEC_MSG_FOOTER[0],
61                         CLOEXEC_MSG_FOOTER[1],
62                         CLOEXEC_MSG_FOOTER[2],
63                         CLOEXEC_MSG_FOOTER[3],
64                     ];
65                     // pipe I/O up to PIPE_BUF bytes should be atomic, and then
66                     // we want to be sure we *don't* run at_exit destructors as
67                     // we're being torn down regardless
68                     assert!(output.write(&bytes).is_ok());
69                     libc::_exit(1)
70                 }
71                 n => n,
72             }
73         };
74
75         let mut p = Process { pid, status: None };
76         drop(output);
77         let mut bytes = [0; 8];
78
79         // loop to handle EINTR
80         loop {
81             match input.read(&mut bytes) {
82                 Ok(0) => return Ok((p, ours)),
83                 Ok(8) => {
84                     assert!(
85                         combine(CLOEXEC_MSG_FOOTER) == combine(&bytes[4..8]),
86                         "Validation on the CLOEXEC pipe failed: {:?}",
87                         bytes
88                     );
89                     let errno = combine(&bytes[0..4]);
90                     assert!(p.wait().is_ok(), "wait() should either return Ok or panic");
91                     return Err(Error::from_raw_os_error(errno));
92                 }
93                 Err(ref e) if e.kind() == ErrorKind::Interrupted => {}
94                 Err(e) => {
95                     assert!(p.wait().is_ok(), "wait() should either return Ok or panic");
96                     panic!("the CLOEXEC pipe failed: {:?}", e)
97                 }
98                 Ok(..) => {
99                     // pipe I/O up to PIPE_BUF bytes should be atomic
100                     assert!(p.wait().is_ok(), "wait() should either return Ok or panic");
101                     panic!("short read on the CLOEXEC pipe")
102                 }
103             }
104         }
105
106         fn combine(arr: &[u8]) -> i32 {
107             let a = arr[0] as u32;
108             let b = arr[1] as u32;
109             let c = arr[2] as u32;
110             let d = arr[3] as u32;
111
112             ((a << 24) | (b << 16) | (c << 8) | (d << 0)) as i32
113         }
114     }
115
116     pub fn exec(&mut self, default: Stdio) -> io::Error {
117         let envp = self.capture_env();
118
119         if self.saw_nul() {
120             return io::Error::new(ErrorKind::InvalidInput, "nul byte found in provided data");
121         }
122
123         match self.setup_io(default, true) {
124             Ok((_, theirs)) => {
125                 unsafe {
126                     // Similar to when forking, we want to ensure that access to
127                     // the environment is synchronized, so make sure to grab the
128                     // environment lock before we try to exec.
129                     let _lock = sys::os::env_lock();
130
131                     let Err(e) = self.do_exec(theirs, envp.as_ref());
132                     e
133                 }
134             }
135             Err(e) => e,
136         }
137     }
138
139     // And at this point we've reached a special time in the life of the
140     // child. The child must now be considered hamstrung and unable to
141     // do anything other than syscalls really. Consider the following
142     // scenario:
143     //
144     //      1. Thread A of process 1 grabs the malloc() mutex
145     //      2. Thread B of process 1 forks(), creating thread C
146     //      3. Thread C of process 2 then attempts to malloc()
147     //      4. The memory of process 2 is the same as the memory of
148     //         process 1, so the mutex is locked.
149     //
150     // This situation looks a lot like deadlock, right? It turns out
151     // that this is what pthread_atfork() takes care of, which is
152     // presumably implemented across platforms. The first thing that
153     // threads to *before* forking is to do things like grab the malloc
154     // mutex, and then after the fork they unlock it.
155     //
156     // Despite this information, libnative's spawn has been witnessed to
157     // deadlock on both macOS and FreeBSD. I'm not entirely sure why, but
158     // all collected backtraces point at malloc/free traffic in the
159     // child spawned process.
160     //
161     // For this reason, the block of code below should contain 0
162     // invocations of either malloc of free (or their related friends).
163     //
164     // As an example of not having malloc/free traffic, we don't close
165     // this file descriptor by dropping the FileDesc (which contains an
166     // allocation). Instead we just close it manually. This will never
167     // have the drop glue anyway because this code never returns (the
168     // child will either exec() or invoke libc::exit)
169     unsafe fn do_exec(
170         &mut self,
171         stdio: ChildPipes,
172         maybe_envp: Option<&CStringArray>,
173     ) -> Result<!, io::Error> {
174         use crate::sys::{self, cvt_r};
175
176         if let Some(fd) = stdio.stdin.fd() {
177             cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO))?;
178         }
179         if let Some(fd) = stdio.stdout.fd() {
180             cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO))?;
181         }
182         if let Some(fd) = stdio.stderr.fd() {
183             cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO))?;
184         }
185
186         #[cfg(not(target_os = "l4re"))]
187         {
188             if let Some(u) = self.get_gid() {
189                 cvt(libc::setgid(u as gid_t))?;
190             }
191             if let Some(u) = self.get_uid() {
192                 // When dropping privileges from root, the `setgroups` call
193                 // will remove any extraneous groups. If we don't call this,
194                 // then even though our uid has dropped, we may still have
195                 // groups that enable us to do super-user things. This will
196                 // fail if we aren't root, so don't bother checking the
197                 // return value, this is just done as an optimistic
198                 // privilege dropping function.
199                 //FIXME: Redox kernel does not support setgroups yet
200                 #[cfg(not(target_os = "redox"))]
201                 let _ = libc::setgroups(0, ptr::null());
202                 cvt(libc::setuid(u as uid_t))?;
203             }
204         }
205         if let Some(ref cwd) = *self.get_cwd() {
206             cvt(libc::chdir(cwd.as_ptr()))?;
207         }
208
209         // emscripten has no signal support.
210         #[cfg(not(target_os = "emscripten"))]
211         {
212             use crate::mem::MaybeUninit;
213             // Reset signal handling so the child process starts in a
214             // standardized state. libstd ignores SIGPIPE, and signal-handling
215             // libraries often set a mask. Child processes inherit ignored
216             // signals and the signal mask from their parent, but most
217             // UNIX programs do not reset these things on their own, so we
218             // need to clean things up now to avoid confusing the program
219             // we're about to run.
220             let mut set = MaybeUninit::<libc::sigset_t>::uninit();
221             cvt(sigemptyset(set.as_mut_ptr()))?;
222             cvt(libc::pthread_sigmask(libc::SIG_SETMASK, set.as_ptr(), ptr::null_mut()))?;
223             let ret = sys::signal(libc::SIGPIPE, libc::SIG_DFL);
224             if ret == libc::SIG_ERR {
225                 return Err(io::Error::last_os_error());
226             }
227         }
228
229         for callback in self.get_closures().iter_mut() {
230             callback()?;
231         }
232
233         // Although we're performing an exec here we may also return with an
234         // error from this function (without actually exec'ing) in which case we
235         // want to be sure to restore the global environment back to what it
236         // once was, ensuring that our temporary override, when free'd, doesn't
237         // corrupt our process's environment.
238         let mut _reset = None;
239         if let Some(envp) = maybe_envp {
240             struct Reset(*const *const libc::c_char);
241
242             impl Drop for Reset {
243                 fn drop(&mut self) {
244                     unsafe {
245                         *sys::os::environ() = self.0;
246                     }
247                 }
248             }
249
250             _reset = Some(Reset(*sys::os::environ()));
251             *sys::os::environ() = envp.as_ptr();
252         }
253
254         libc::execvp(self.get_program().as_ptr(), self.get_argv().as_ptr());
255         Err(io::Error::last_os_error())
256     }
257
258     #[cfg(not(any(
259         target_os = "macos",
260         target_os = "freebsd",
261         all(target_os = "linux", target_env = "gnu")
262     )))]
263     fn posix_spawn(
264         &mut self,
265         _: &ChildPipes,
266         _: Option<&CStringArray>,
267     ) -> io::Result<Option<Process>> {
268         Ok(None)
269     }
270
271     // Only support platforms for which posix_spawn() can return ENOENT
272     // directly.
273     #[cfg(any(
274         target_os = "macos",
275         target_os = "freebsd",
276         all(target_os = "linux", target_env = "gnu")
277     ))]
278     fn posix_spawn(
279         &mut self,
280         stdio: &ChildPipes,
281         envp: Option<&CStringArray>,
282     ) -> io::Result<Option<Process>> {
283         use crate::mem::MaybeUninit;
284         use crate::sys;
285
286         if self.get_gid().is_some()
287             || self.get_uid().is_some()
288             || self.env_saw_path()
289             || !self.get_closures().is_empty()
290         {
291             return Ok(None);
292         }
293
294         // Only glibc 2.24+ posix_spawn() supports returning ENOENT directly.
295         #[cfg(all(target_os = "linux", target_env = "gnu"))]
296         {
297             if let Some(version) = sys::os::glibc_version() {
298                 if version < (2, 24) {
299                     return Ok(None);
300                 }
301             } else {
302                 return Ok(None);
303             }
304         }
305
306         // Solaris and glibc 2.29+ can set a new working directory, and maybe
307         // others will gain this non-POSIX function too. We'll check for this
308         // weak symbol as soon as it's needed, so we can return early otherwise
309         // to do a manual chdir before exec.
310         weak! {
311             fn posix_spawn_file_actions_addchdir_np(
312                 *mut libc::posix_spawn_file_actions_t,
313                 *const libc::c_char
314             ) -> libc::c_int
315         }
316         let addchdir = match self.get_cwd() {
317             Some(cwd) => match posix_spawn_file_actions_addchdir_np.get() {
318                 Some(f) => Some((f, cwd)),
319                 None => return Ok(None),
320             },
321             None => None,
322         };
323
324         let mut p = Process { pid: 0, status: None };
325
326         struct PosixSpawnFileActions(MaybeUninit<libc::posix_spawn_file_actions_t>);
327
328         impl Drop for PosixSpawnFileActions {
329             fn drop(&mut self) {
330                 unsafe {
331                     libc::posix_spawn_file_actions_destroy(self.0.as_mut_ptr());
332                 }
333             }
334         }
335
336         struct PosixSpawnattr(MaybeUninit<libc::posix_spawnattr_t>);
337
338         impl Drop for PosixSpawnattr {
339             fn drop(&mut self) {
340                 unsafe {
341                     libc::posix_spawnattr_destroy(self.0.as_mut_ptr());
342                 }
343             }
344         }
345
346         unsafe {
347             let mut file_actions = PosixSpawnFileActions(MaybeUninit::uninit());
348             let mut attrs = PosixSpawnattr(MaybeUninit::uninit());
349
350             libc::posix_spawnattr_init(attrs.0.as_mut_ptr());
351             libc::posix_spawn_file_actions_init(file_actions.0.as_mut_ptr());
352
353             if let Some(fd) = stdio.stdin.fd() {
354                 cvt(libc::posix_spawn_file_actions_adddup2(
355                     file_actions.0.as_mut_ptr(),
356                     fd,
357                     libc::STDIN_FILENO,
358                 ))?;
359             }
360             if let Some(fd) = stdio.stdout.fd() {
361                 cvt(libc::posix_spawn_file_actions_adddup2(
362                     file_actions.0.as_mut_ptr(),
363                     fd,
364                     libc::STDOUT_FILENO,
365                 ))?;
366             }
367             if let Some(fd) = stdio.stderr.fd() {
368                 cvt(libc::posix_spawn_file_actions_adddup2(
369                     file_actions.0.as_mut_ptr(),
370                     fd,
371                     libc::STDERR_FILENO,
372                 ))?;
373             }
374             if let Some((f, cwd)) = addchdir {
375                 cvt(f(file_actions.0.as_mut_ptr(), cwd.as_ptr()))?;
376             }
377
378             let mut set = MaybeUninit::<libc::sigset_t>::uninit();
379             cvt(sigemptyset(set.as_mut_ptr()))?;
380             cvt(libc::posix_spawnattr_setsigmask(attrs.0.as_mut_ptr(), set.as_ptr()))?;
381             cvt(sigaddset(set.as_mut_ptr(), libc::SIGPIPE))?;
382             cvt(libc::posix_spawnattr_setsigdefault(attrs.0.as_mut_ptr(), set.as_ptr()))?;
383
384             let flags = libc::POSIX_SPAWN_SETSIGDEF | libc::POSIX_SPAWN_SETSIGMASK;
385             cvt(libc::posix_spawnattr_setflags(attrs.0.as_mut_ptr(), flags as _))?;
386
387             // Make sure we synchronize access to the global `environ` resource
388             let _env_lock = sys::os::env_lock();
389             let envp = envp.map(|c| c.as_ptr()).unwrap_or_else(|| *sys::os::environ() as *const _);
390             let ret = libc::posix_spawnp(
391                 &mut p.pid,
392                 self.get_program().as_ptr(),
393                 file_actions.0.as_ptr(),
394                 attrs.0.as_ptr(),
395                 self.get_argv().as_ptr() as *const _,
396                 envp as *const _,
397             );
398             if ret == 0 { Ok(Some(p)) } else { Err(io::Error::from_raw_os_error(ret)) }
399         }
400     }
401 }
402
403 ////////////////////////////////////////////////////////////////////////////////
404 // Processes
405 ////////////////////////////////////////////////////////////////////////////////
406
407 /// The unique ID of the process (this should never be negative).
408 pub struct Process {
409     pid: pid_t,
410     status: Option<ExitStatus>,
411 }
412
413 impl Process {
414     pub fn id(&self) -> u32 {
415         self.pid as u32
416     }
417
418     pub fn kill(&mut self) -> io::Result<()> {
419         // If we've already waited on this process then the pid can be recycled
420         // and used for another process, and we probably shouldn't be killing
421         // random processes, so just return an error.
422         if self.status.is_some() {
423             Err(Error::new(
424                 ErrorKind::InvalidInput,
425                 "invalid argument: can't kill an exited process",
426             ))
427         } else {
428             cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(drop)
429         }
430     }
431
432     pub fn wait(&mut self) -> io::Result<ExitStatus> {
433         use crate::sys::cvt_r;
434         if let Some(status) = self.status {
435             return Ok(status);
436         }
437         let mut status = 0 as c_int;
438         cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?;
439         self.status = Some(ExitStatus::new(status));
440         Ok(ExitStatus::new(status))
441     }
442
443     pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
444         if let Some(status) = self.status {
445             return Ok(Some(status));
446         }
447         let mut status = 0 as c_int;
448         let pid = cvt(unsafe { libc::waitpid(self.pid, &mut status, libc::WNOHANG) })?;
449         if pid == 0 {
450             Ok(None)
451         } else {
452             self.status = Some(ExitStatus::new(status));
453             Ok(Some(ExitStatus::new(status)))
454         }
455     }
456 }
457
458 /// Unix exit statuses
459 #[derive(PartialEq, Eq, Clone, Copy, Debug)]
460 pub struct ExitStatus(c_int);
461
462 impl ExitStatus {
463     pub fn new(status: c_int) -> ExitStatus {
464         ExitStatus(status)
465     }
466
467     fn exited(&self) -> bool {
468         unsafe { libc::WIFEXITED(self.0) }
469     }
470
471     pub fn success(&self) -> bool {
472         self.code() == Some(0)
473     }
474
475     pub fn code(&self) -> Option<i32> {
476         if self.exited() { Some(unsafe { libc::WEXITSTATUS(self.0) }) } else { None }
477     }
478
479     pub fn signal(&self) -> Option<i32> {
480         if !self.exited() { Some(unsafe { libc::WTERMSIG(self.0) }) } else { None }
481     }
482 }
483
484 impl From<c_int> for ExitStatus {
485     fn from(a: c_int) -> ExitStatus {
486         ExitStatus(a)
487     }
488 }
489
490 impl fmt::Display for ExitStatus {
491     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
492         if let Some(code) = self.code() {
493             write!(f, "exit code: {}", code)
494         } else {
495             let signal = self.signal().unwrap();
496             write!(f, "signal: {}", signal)
497         }
498     }
499 }