]>
Commit | Line | Data |
---|---|---|
476ff2be 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 | ||
11 | use io::{self, Error, ErrorKind}; | |
12 | use libc::{self, c_int, gid_t, pid_t, uid_t}; | |
476ff2be SL |
13 | use ptr; |
14 | ||
15 | use sys::cvt; | |
16 | use sys::process::process_common::*; | |
17 | ||
18 | //////////////////////////////////////////////////////////////////////////////// | |
19 | // Command | |
20 | //////////////////////////////////////////////////////////////////////////////// | |
21 | ||
22 | impl Command { | |
23 | pub fn spawn(&mut self, default: Stdio, needs_stdin: bool) | |
24 | -> io::Result<(Process, StdioPipes)> { | |
25 | use sys; | |
26 | ||
27 | const CLOEXEC_MSG_FOOTER: &'static [u8] = b"NOEX"; | |
28 | ||
ff7c6d11 XL |
29 | let envp = self.capture_env(); |
30 | ||
476ff2be SL |
31 | if self.saw_nul() { |
32 | return Err(io::Error::new(ErrorKind::InvalidInput, | |
33 | "nul byte found in provided data")); | |
34 | } | |
35 | ||
36 | let (ours, theirs) = self.setup_io(default, needs_stdin)?; | |
0531ce1d XL |
37 | |
38 | if let Some(ret) = self.posix_spawn(&theirs, envp.as_ref())? { | |
39 | return Ok((ret, ours)) | |
40 | } | |
41 | ||
476ff2be SL |
42 | let (input, output) = sys::pipe::anon_pipe()?; |
43 | ||
44 | let pid = unsafe { | |
45 | match cvt(libc::fork())? { | |
46 | 0 => { | |
47 | drop(input); | |
ff7c6d11 | 48 | let err = self.do_exec(theirs, envp.as_ref()); |
476ff2be SL |
49 | let errno = err.raw_os_error().unwrap_or(libc::EINVAL) as u32; |
50 | let bytes = [ | |
51 | (errno >> 24) as u8, | |
52 | (errno >> 16) as u8, | |
53 | (errno >> 8) as u8, | |
54 | (errno >> 0) as u8, | |
55 | CLOEXEC_MSG_FOOTER[0], CLOEXEC_MSG_FOOTER[1], | |
56 | CLOEXEC_MSG_FOOTER[2], CLOEXEC_MSG_FOOTER[3] | |
57 | ]; | |
58 | // pipe I/O up to PIPE_BUF bytes should be atomic, and then | |
59 | // we want to be sure we *don't* run at_exit destructors as | |
60 | // we're being torn down regardless | |
61 | assert!(output.write(&bytes).is_ok()); | |
62 | libc::_exit(1) | |
63 | } | |
64 | n => n, | |
65 | } | |
66 | }; | |
67 | ||
68 | let mut p = Process { pid: pid, status: None }; | |
69 | drop(output); | |
70 | let mut bytes = [0; 8]; | |
71 | ||
72 | // loop to handle EINTR | |
73 | loop { | |
74 | match input.read(&mut bytes) { | |
75 | Ok(0) => return Ok((p, ours)), | |
76 | Ok(8) => { | |
77 | assert!(combine(CLOEXEC_MSG_FOOTER) == combine(&bytes[4.. 8]), | |
78 | "Validation on the CLOEXEC pipe failed: {:?}", bytes); | |
79 | let errno = combine(&bytes[0.. 4]); | |
80 | assert!(p.wait().is_ok(), | |
81 | "wait() should either return Ok or panic"); | |
82 | return Err(Error::from_raw_os_error(errno)) | |
83 | } | |
84 | Err(ref e) if e.kind() == ErrorKind::Interrupted => {} | |
85 | Err(e) => { | |
86 | assert!(p.wait().is_ok(), | |
87 | "wait() should either return Ok or panic"); | |
88 | panic!("the CLOEXEC pipe failed: {:?}", e) | |
89 | }, | |
90 | Ok(..) => { // pipe I/O up to PIPE_BUF bytes should be atomic | |
91 | assert!(p.wait().is_ok(), | |
92 | "wait() should either return Ok or panic"); | |
93 | panic!("short read on the CLOEXEC pipe") | |
94 | } | |
95 | } | |
96 | } | |
97 | ||
98 | fn combine(arr: &[u8]) -> i32 { | |
99 | let a = arr[0] as u32; | |
100 | let b = arr[1] as u32; | |
101 | let c = arr[2] as u32; | |
102 | let d = arr[3] as u32; | |
103 | ||
104 | ((a << 24) | (b << 16) | (c << 8) | (d << 0)) as i32 | |
105 | } | |
106 | } | |
107 | ||
108 | pub fn exec(&mut self, default: Stdio) -> io::Error { | |
ff7c6d11 XL |
109 | let envp = self.capture_env(); |
110 | ||
476ff2be SL |
111 | if self.saw_nul() { |
112 | return io::Error::new(ErrorKind::InvalidInput, | |
113 | "nul byte found in provided data") | |
114 | } | |
115 | ||
116 | match self.setup_io(default, true) { | |
ff7c6d11 | 117 | Ok((_, theirs)) => unsafe { self.do_exec(theirs, envp.as_ref()) }, |
476ff2be SL |
118 | Err(e) => e, |
119 | } | |
120 | } | |
121 | ||
122 | // And at this point we've reached a special time in the life of the | |
123 | // child. The child must now be considered hamstrung and unable to | |
124 | // do anything other than syscalls really. Consider the following | |
125 | // scenario: | |
126 | // | |
127 | // 1. Thread A of process 1 grabs the malloc() mutex | |
128 | // 2. Thread B of process 1 forks(), creating thread C | |
129 | // 3. Thread C of process 2 then attempts to malloc() | |
130 | // 4. The memory of process 2 is the same as the memory of | |
131 | // process 1, so the mutex is locked. | |
132 | // | |
133 | // This situation looks a lot like deadlock, right? It turns out | |
134 | // that this is what pthread_atfork() takes care of, which is | |
135 | // presumably implemented across platforms. The first thing that | |
136 | // threads to *before* forking is to do things like grab the malloc | |
137 | // mutex, and then after the fork they unlock it. | |
138 | // | |
139 | // Despite this information, libnative's spawn has been witnessed to | |
cc61c64b | 140 | // deadlock on both macOS and FreeBSD. I'm not entirely sure why, but |
476ff2be SL |
141 | // all collected backtraces point at malloc/free traffic in the |
142 | // child spawned process. | |
143 | // | |
144 | // For this reason, the block of code below should contain 0 | |
145 | // invocations of either malloc of free (or their related friends). | |
146 | // | |
147 | // As an example of not having malloc/free traffic, we don't close | |
148 | // this file descriptor by dropping the FileDesc (which contains an | |
149 | // allocation). Instead we just close it manually. This will never | |
150 | // have the drop glue anyway because this code never returns (the | |
151 | // child will either exec() or invoke libc::exit) | |
ff7c6d11 XL |
152 | unsafe fn do_exec( |
153 | &mut self, | |
154 | stdio: ChildPipes, | |
155 | maybe_envp: Option<&CStringArray> | |
156 | ) -> io::Error { | |
476ff2be SL |
157 | use sys::{self, cvt_r}; |
158 | ||
159 | macro_rules! t { | |
160 | ($e:expr) => (match $e { | |
161 | Ok(e) => e, | |
162 | Err(e) => return e, | |
163 | }) | |
164 | } | |
165 | ||
166 | if let Some(fd) = stdio.stdin.fd() { | |
167 | t!(cvt_r(|| libc::dup2(fd, libc::STDIN_FILENO))); | |
168 | } | |
169 | if let Some(fd) = stdio.stdout.fd() { | |
170 | t!(cvt_r(|| libc::dup2(fd, libc::STDOUT_FILENO))); | |
171 | } | |
172 | if let Some(fd) = stdio.stderr.fd() { | |
173 | t!(cvt_r(|| libc::dup2(fd, libc::STDERR_FILENO))); | |
174 | } | |
175 | ||
ea8adc8c XL |
176 | if cfg!(not(any(target_os = "l4re"))) { |
177 | if let Some(u) = self.get_gid() { | |
178 | t!(cvt(libc::setgid(u as gid_t))); | |
179 | } | |
180 | if let Some(u) = self.get_uid() { | |
181 | // When dropping privileges from root, the `setgroups` call | |
182 | // will remove any extraneous groups. If we don't call this, | |
183 | // then even though our uid has dropped, we may still have | |
184 | // groups that enable us to do super-user things. This will | |
185 | // fail if we aren't root, so don't bother checking the | |
186 | // return value, this is just done as an optimistic | |
187 | // privilege dropping function. | |
188 | let _ = libc::setgroups(0, ptr::null()); | |
189 | ||
190 | t!(cvt(libc::setuid(u as uid_t))); | |
191 | } | |
476ff2be SL |
192 | } |
193 | if let Some(ref cwd) = *self.get_cwd() { | |
194 | t!(cvt(libc::chdir(cwd.as_ptr()))); | |
195 | } | |
ff7c6d11 | 196 | if let Some(envp) = maybe_envp { |
476ff2be SL |
197 | *sys::os::environ() = envp.as_ptr(); |
198 | } | |
199 | ||
ea8adc8c XL |
200 | // emscripten has no signal support. |
201 | #[cfg(not(any(target_os = "emscripten")))] | |
3b2f2976 XL |
202 | { |
203 | use mem; | |
476ff2be SL |
204 | // Reset signal handling so the child process starts in a |
205 | // standardized state. libstd ignores SIGPIPE, and signal-handling | |
206 | // libraries often set a mask. Child processes inherit ignored | |
207 | // signals and the signal mask from their parent, but most | |
208 | // UNIX programs do not reset these things on their own, so we | |
209 | // need to clean things up now to avoid confusing the program | |
210 | // we're about to run. | |
211 | let mut set: libc::sigset_t = mem::uninitialized(); | |
cc61c64b XL |
212 | if cfg!(target_os = "android") { |
213 | // Implementing sigemptyset allow us to support older Android | |
214 | // versions. See the comment about Android and sig* functions in | |
215 | // process_common.rs | |
216 | libc::memset(&mut set as *mut _ as *mut _, | |
217 | 0, | |
218 | mem::size_of::<libc::sigset_t>()); | |
219 | } else { | |
220 | t!(cvt(libc::sigemptyset(&mut set))); | |
221 | } | |
476ff2be SL |
222 | t!(cvt(libc::pthread_sigmask(libc::SIG_SETMASK, &set, |
223 | ptr::null_mut()))); | |
224 | let ret = sys::signal(libc::SIGPIPE, libc::SIG_DFL); | |
225 | if ret == libc::SIG_ERR { | |
226 | return io::Error::last_os_error() | |
227 | } | |
228 | } | |
229 | ||
230 | for callback in self.get_closures().iter_mut() { | |
231 | t!(callback()); | |
232 | } | |
233 | ||
234 | libc::execvp(self.get_argv()[0], self.get_argv().as_ptr()); | |
235 | io::Error::last_os_error() | |
236 | } | |
0531ce1d XL |
237 | |
238 | #[cfg(not(any(target_os = "macos", target_os = "freebsd", | |
239 | all(target_os = "linux", target_env = "gnu"))))] | |
240 | fn posix_spawn(&mut self, _: &ChildPipes, _: Option<&CStringArray>) | |
241 | -> io::Result<Option<Process>> | |
242 | { | |
243 | Ok(None) | |
244 | } | |
245 | ||
246 | // Only support platforms for which posix_spawn() can return ENOENT | |
247 | // directly. | |
248 | #[cfg(any(target_os = "macos", target_os = "freebsd", | |
249 | all(target_os = "linux", target_env = "gnu")))] | |
250 | fn posix_spawn(&mut self, stdio: &ChildPipes, envp: Option<&CStringArray>) | |
251 | -> io::Result<Option<Process>> | |
252 | { | |
253 | use mem; | |
254 | use sys; | |
255 | ||
256 | if self.get_cwd().is_some() || | |
257 | self.get_gid().is_some() || | |
258 | self.get_uid().is_some() || | |
259 | self.env_saw_path() || | |
260 | self.get_closures().len() != 0 { | |
261 | return Ok(None) | |
262 | } | |
263 | ||
264 | // Only glibc 2.24+ posix_spawn() supports returning ENOENT directly. | |
265 | #[cfg(all(target_os = "linux", target_env = "gnu"))] | |
266 | { | |
267 | if let Some(version) = sys::os::glibc_version() { | |
268 | if version < (2, 24) { | |
269 | return Ok(None) | |
270 | } | |
271 | } else { | |
272 | return Ok(None) | |
273 | } | |
274 | } | |
275 | ||
276 | let mut p = Process { pid: 0, status: None }; | |
277 | ||
278 | struct PosixSpawnFileActions(libc::posix_spawn_file_actions_t); | |
279 | ||
280 | impl Drop for PosixSpawnFileActions { | |
281 | fn drop(&mut self) { | |
282 | unsafe { | |
283 | libc::posix_spawn_file_actions_destroy(&mut self.0); | |
284 | } | |
285 | } | |
286 | } | |
287 | ||
288 | struct PosixSpawnattr(libc::posix_spawnattr_t); | |
289 | ||
290 | impl Drop for PosixSpawnattr { | |
291 | fn drop(&mut self) { | |
292 | unsafe { | |
293 | libc::posix_spawnattr_destroy(&mut self.0); | |
294 | } | |
295 | } | |
296 | } | |
297 | ||
298 | unsafe { | |
299 | let mut file_actions = PosixSpawnFileActions(mem::uninitialized()); | |
300 | let mut attrs = PosixSpawnattr(mem::uninitialized()); | |
301 | ||
302 | libc::posix_spawnattr_init(&mut attrs.0); | |
303 | libc::posix_spawn_file_actions_init(&mut file_actions.0); | |
304 | ||
305 | if let Some(fd) = stdio.stdin.fd() { | |
306 | cvt(libc::posix_spawn_file_actions_adddup2(&mut file_actions.0, | |
307 | fd, | |
308 | libc::STDIN_FILENO))?; | |
309 | } | |
310 | if let Some(fd) = stdio.stdout.fd() { | |
311 | cvt(libc::posix_spawn_file_actions_adddup2(&mut file_actions.0, | |
312 | fd, | |
313 | libc::STDOUT_FILENO))?; | |
314 | } | |
315 | if let Some(fd) = stdio.stderr.fd() { | |
316 | cvt(libc::posix_spawn_file_actions_adddup2(&mut file_actions.0, | |
317 | fd, | |
318 | libc::STDERR_FILENO))?; | |
319 | } | |
320 | ||
321 | let mut set: libc::sigset_t = mem::uninitialized(); | |
322 | cvt(libc::sigemptyset(&mut set))?; | |
323 | cvt(libc::posix_spawnattr_setsigmask(&mut attrs.0, | |
324 | &set))?; | |
325 | cvt(libc::sigaddset(&mut set, libc::SIGPIPE))?; | |
326 | cvt(libc::posix_spawnattr_setsigdefault(&mut attrs.0, | |
327 | &set))?; | |
328 | ||
329 | let flags = libc::POSIX_SPAWN_SETSIGDEF | | |
330 | libc::POSIX_SPAWN_SETSIGMASK; | |
331 | cvt(libc::posix_spawnattr_setflags(&mut attrs.0, flags as _))?; | |
332 | ||
333 | let envp = envp.map(|c| c.as_ptr()) | |
0bf4aa26 | 334 | .unwrap_or_else(|| *sys::os::environ() as *const _); |
0531ce1d XL |
335 | let ret = libc::posix_spawnp( |
336 | &mut p.pid, | |
337 | self.get_argv()[0], | |
338 | &file_actions.0, | |
339 | &attrs.0, | |
340 | self.get_argv().as_ptr() as *const _, | |
341 | envp as *const _, | |
342 | ); | |
343 | if ret == 0 { | |
344 | Ok(Some(p)) | |
345 | } else { | |
346 | Err(io::Error::from_raw_os_error(ret)) | |
347 | } | |
348 | } | |
349 | } | |
476ff2be SL |
350 | } |
351 | ||
352 | //////////////////////////////////////////////////////////////////////////////// | |
353 | // Processes | |
354 | //////////////////////////////////////////////////////////////////////////////// | |
355 | ||
356 | /// The unique id of the process (this should never be negative). | |
357 | pub struct Process { | |
358 | pid: pid_t, | |
359 | status: Option<ExitStatus>, | |
360 | } | |
361 | ||
362 | impl Process { | |
363 | pub fn id(&self) -> u32 { | |
364 | self.pid as u32 | |
365 | } | |
366 | ||
367 | pub fn kill(&mut self) -> io::Result<()> { | |
368 | // If we've already waited on this process then the pid can be recycled | |
369 | // and used for another process, and we probably shouldn't be killing | |
370 | // random processes, so just return an error. | |
371 | if self.status.is_some() { | |
372 | Err(Error::new(ErrorKind::InvalidInput, | |
373 | "invalid argument: can't kill an exited process")) | |
374 | } else { | |
375 | cvt(unsafe { libc::kill(self.pid, libc::SIGKILL) }).map(|_| ()) | |
376 | } | |
377 | } | |
32a655c1 | 378 | |
476ff2be SL |
379 | pub fn wait(&mut self) -> io::Result<ExitStatus> { |
380 | use sys::cvt_r; | |
381 | if let Some(status) = self.status { | |
382 | return Ok(status) | |
383 | } | |
384 | let mut status = 0 as c_int; | |
385 | cvt_r(|| unsafe { libc::waitpid(self.pid, &mut status, 0) })?; | |
386 | self.status = Some(ExitStatus::new(status)); | |
387 | Ok(ExitStatus::new(status)) | |
388 | } | |
32a655c1 | 389 | |
8bb4bdeb | 390 | pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> { |
32a655c1 | 391 | if let Some(status) = self.status { |
8bb4bdeb | 392 | return Ok(Some(status)) |
32a655c1 SL |
393 | } |
394 | let mut status = 0 as c_int; | |
395 | let pid = cvt(unsafe { | |
396 | libc::waitpid(self.pid, &mut status, libc::WNOHANG) | |
397 | })?; | |
398 | if pid == 0 { | |
8bb4bdeb | 399 | Ok(None) |
32a655c1 SL |
400 | } else { |
401 | self.status = Some(ExitStatus::new(status)); | |
8bb4bdeb | 402 | Ok(Some(ExitStatus::new(status))) |
32a655c1 SL |
403 | } |
404 | } | |
476ff2be | 405 | } |