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