]>
Commit | Line | Data |
---|---|---|
85aaf69f SL |
1 | // Copyright 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 | //! Working with processes. | |
12 | ||
c34b1796 | 13 | #![stable(feature = "process", since = "1.0.0")] |
85aaf69f SL |
14 | |
15 | use prelude::v1::*; | |
16 | use io::prelude::*; | |
17 | ||
c34b1796 | 18 | use ffi::OsStr; |
85aaf69f | 19 | use fmt; |
7453a54e SL |
20 | use io; |
21 | use path::Path; | |
9cc50fc6 | 22 | use str; |
54a0048b | 23 | use sys::pipe::{read2, AnonPipe}; |
62682a34 | 24 | use sys::process as imp; |
c1a9b12d | 25 | use sys_common::{AsInner, AsInnerMut, FromInner, IntoInner}; |
85aaf69f SL |
26 | |
27 | /// Representation of a running or exited child process. | |
28 | /// | |
29 | /// This structure is used to represent and manage child processes. A child | |
30 | /// process is created via the `Command` struct, which configures the spawning | |
31 | /// process and can itself be constructed using a builder-style interface. | |
32 | /// | |
c34b1796 | 33 | /// # Examples |
85aaf69f | 34 | /// |
c34b1796 | 35 | /// ```should_panic |
85aaf69f SL |
36 | /// use std::process::Command; |
37 | /// | |
bd371182 AL |
38 | /// let mut child = Command::new("/bin/cat") |
39 | /// .arg("file.txt") | |
40 | /// .spawn() | |
41 | /// .unwrap_or_else(|e| { panic!("failed to execute child: {}", e) }); | |
42 | /// | |
43 | /// let ecode = child.wait() | |
44 | /// .unwrap_or_else(|e| { panic!("failed to wait on child: {}", e) }); | |
45 | /// | |
46 | /// assert!(ecode.success()); | |
85aaf69f | 47 | /// ``` |
7453a54e SL |
48 | /// |
49 | /// # Note | |
50 | /// | |
51 | /// Take note that there is no implementation of | |
52 | /// [`Drop`](../../core/ops/trait.Drop.html) for child processes, so if you | |
53 | /// do not ensure the `Child` has exited then it will continue to run, even | |
54 | /// after the `Child` handle to the child process has gone out of scope. | |
55 | /// | |
56 | /// Calling `wait` (or other functions that wrap around it) will make the | |
57 | /// parent process wait until the child has actually exited before continuing. | |
c34b1796 | 58 | #[stable(feature = "process", since = "1.0.0")] |
85aaf69f | 59 | pub struct Child { |
62682a34 | 60 | handle: imp::Process, |
85aaf69f | 61 | |
85aaf69f | 62 | /// The handle for writing to the child's stdin, if it has been captured |
c34b1796 | 63 | #[stable(feature = "process", since = "1.0.0")] |
85aaf69f SL |
64 | pub stdin: Option<ChildStdin>, |
65 | ||
66 | /// The handle for reading from the child's stdout, if it has been captured | |
c34b1796 | 67 | #[stable(feature = "process", since = "1.0.0")] |
85aaf69f SL |
68 | pub stdout: Option<ChildStdout>, |
69 | ||
70 | /// The handle for reading from the child's stderr, if it has been captured | |
c34b1796 | 71 | #[stable(feature = "process", since = "1.0.0")] |
85aaf69f SL |
72 | pub stderr: Option<ChildStderr>, |
73 | } | |
74 | ||
62682a34 SL |
75 | impl AsInner<imp::Process> for Child { |
76 | fn as_inner(&self) -> &imp::Process { &self.handle } | |
77 | } | |
78 | ||
7453a54e SL |
79 | impl FromInner<(imp::Process, imp::StdioPipes)> for Child { |
80 | fn from_inner((handle, io): (imp::Process, imp::StdioPipes)) -> Child { | |
81 | Child { | |
82 | handle: handle, | |
83 | stdin: io.stdin.map(ChildStdin::from_inner), | |
84 | stdout: io.stdout.map(ChildStdout::from_inner), | |
85 | stderr: io.stderr.map(ChildStderr::from_inner), | |
86 | } | |
87 | } | |
88 | } | |
89 | ||
c1a9b12d SL |
90 | impl IntoInner<imp::Process> for Child { |
91 | fn into_inner(self) -> imp::Process { self.handle } | |
92 | } | |
93 | ||
b039eaaf | 94 | /// A handle to a child process's stdin |
c34b1796 | 95 | #[stable(feature = "process", since = "1.0.0")] |
85aaf69f SL |
96 | pub struct ChildStdin { |
97 | inner: AnonPipe | |
98 | } | |
99 | ||
c34b1796 | 100 | #[stable(feature = "process", since = "1.0.0")] |
85aaf69f SL |
101 | impl Write for ChildStdin { |
102 | fn write(&mut self, buf: &[u8]) -> io::Result<usize> { | |
103 | self.inner.write(buf) | |
104 | } | |
105 | ||
106 | fn flush(&mut self) -> io::Result<()> { | |
107 | Ok(()) | |
108 | } | |
109 | } | |
110 | ||
62682a34 SL |
111 | impl AsInner<AnonPipe> for ChildStdin { |
112 | fn as_inner(&self) -> &AnonPipe { &self.inner } | |
113 | } | |
114 | ||
c1a9b12d SL |
115 | impl IntoInner<AnonPipe> for ChildStdin { |
116 | fn into_inner(self) -> AnonPipe { self.inner } | |
117 | } | |
118 | ||
7453a54e SL |
119 | impl FromInner<AnonPipe> for ChildStdin { |
120 | fn from_inner(pipe: AnonPipe) -> ChildStdin { | |
121 | ChildStdin { inner: pipe } | |
122 | } | |
123 | } | |
124 | ||
b039eaaf | 125 | /// A handle to a child process's stdout |
c34b1796 | 126 | #[stable(feature = "process", since = "1.0.0")] |
85aaf69f SL |
127 | pub struct ChildStdout { |
128 | inner: AnonPipe | |
129 | } | |
130 | ||
c34b1796 | 131 | #[stable(feature = "process", since = "1.0.0")] |
85aaf69f SL |
132 | impl Read for ChildStdout { |
133 | fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | |
134 | self.inner.read(buf) | |
135 | } | |
54a0048b SL |
136 | fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { |
137 | self.inner.read_to_end(buf) | |
138 | } | |
85aaf69f SL |
139 | } |
140 | ||
62682a34 SL |
141 | impl AsInner<AnonPipe> for ChildStdout { |
142 | fn as_inner(&self) -> &AnonPipe { &self.inner } | |
143 | } | |
144 | ||
c1a9b12d SL |
145 | impl IntoInner<AnonPipe> for ChildStdout { |
146 | fn into_inner(self) -> AnonPipe { self.inner } | |
147 | } | |
148 | ||
7453a54e SL |
149 | impl FromInner<AnonPipe> for ChildStdout { |
150 | fn from_inner(pipe: AnonPipe) -> ChildStdout { | |
151 | ChildStdout { inner: pipe } | |
152 | } | |
153 | } | |
154 | ||
b039eaaf | 155 | /// A handle to a child process's stderr |
c34b1796 | 156 | #[stable(feature = "process", since = "1.0.0")] |
85aaf69f SL |
157 | pub struct ChildStderr { |
158 | inner: AnonPipe | |
159 | } | |
160 | ||
c34b1796 | 161 | #[stable(feature = "process", since = "1.0.0")] |
85aaf69f SL |
162 | impl Read for ChildStderr { |
163 | fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> { | |
164 | self.inner.read(buf) | |
165 | } | |
54a0048b SL |
166 | fn read_to_end(&mut self, buf: &mut Vec<u8>) -> io::Result<usize> { |
167 | self.inner.read_to_end(buf) | |
168 | } | |
85aaf69f SL |
169 | } |
170 | ||
62682a34 SL |
171 | impl AsInner<AnonPipe> for ChildStderr { |
172 | fn as_inner(&self) -> &AnonPipe { &self.inner } | |
173 | } | |
174 | ||
c1a9b12d SL |
175 | impl IntoInner<AnonPipe> for ChildStderr { |
176 | fn into_inner(self) -> AnonPipe { self.inner } | |
177 | } | |
178 | ||
7453a54e SL |
179 | impl FromInner<AnonPipe> for ChildStderr { |
180 | fn from_inner(pipe: AnonPipe) -> ChildStderr { | |
181 | ChildStderr { inner: pipe } | |
182 | } | |
183 | } | |
184 | ||
85aaf69f SL |
185 | /// The `Command` type acts as a process builder, providing fine-grained control |
186 | /// over how a new process should be spawned. A default configuration can be | |
187 | /// generated using `Command::new(program)`, where `program` gives a path to the | |
188 | /// program to be executed. Additional builder methods allow the configuration | |
189 | /// to be changed (for example, by adding arguments) prior to spawning: | |
190 | /// | |
191 | /// ``` | |
85aaf69f SL |
192 | /// use std::process::Command; |
193 | /// | |
bd371182 AL |
194 | /// let output = Command::new("sh") |
195 | /// .arg("-c") | |
196 | /// .arg("echo hello") | |
197 | /// .output() | |
198 | /// .unwrap_or_else(|e| { panic!("failed to execute process: {}", e) }); | |
85aaf69f SL |
199 | /// let hello = output.stdout; |
200 | /// ``` | |
c34b1796 | 201 | #[stable(feature = "process", since = "1.0.0")] |
85aaf69f | 202 | pub struct Command { |
62682a34 | 203 | inner: imp::Command, |
85aaf69f SL |
204 | } |
205 | ||
206 | impl Command { | |
207 | /// Constructs a new `Command` for launching the program at | |
208 | /// path `program`, with the following default configuration: | |
209 | /// | |
210 | /// * No arguments to the program | |
211 | /// * Inherit the current process's environment | |
212 | /// * Inherit the current process's working directory | |
bd371182 | 213 | /// * Inherit stdin/stdout/stderr for `spawn` or `status`, but create pipes for `output` |
85aaf69f SL |
214 | /// |
215 | /// Builder methods are provided to change these defaults and | |
216 | /// otherwise configure the process. | |
c34b1796 AL |
217 | #[stable(feature = "process", since = "1.0.0")] |
218 | pub fn new<S: AsRef<OsStr>>(program: S) -> Command { | |
7453a54e | 219 | Command { inner: imp::Command::new(program.as_ref()) } |
85aaf69f SL |
220 | } |
221 | ||
222 | /// Add an argument to pass to the program. | |
c34b1796 AL |
223 | #[stable(feature = "process", since = "1.0.0")] |
224 | pub fn arg<S: AsRef<OsStr>>(&mut self, arg: S) -> &mut Command { | |
225 | self.inner.arg(arg.as_ref()); | |
85aaf69f SL |
226 | self |
227 | } | |
228 | ||
229 | /// Add multiple arguments to pass to the program. | |
c34b1796 AL |
230 | #[stable(feature = "process", since = "1.0.0")] |
231 | pub fn args<S: AsRef<OsStr>>(&mut self, args: &[S]) -> &mut Command { | |
7453a54e SL |
232 | for arg in args { |
233 | self.arg(arg.as_ref()); | |
234 | } | |
85aaf69f SL |
235 | self |
236 | } | |
237 | ||
238 | /// Inserts or updates an environment variable mapping. | |
239 | /// | |
240 | /// Note that environment variable names are case-insensitive (but case-preserving) on Windows, | |
241 | /// and case-sensitive on all other platforms. | |
c34b1796 AL |
242 | #[stable(feature = "process", since = "1.0.0")] |
243 | pub fn env<K, V>(&mut self, key: K, val: V) -> &mut Command | |
244 | where K: AsRef<OsStr>, V: AsRef<OsStr> | |
85aaf69f | 245 | { |
c34b1796 | 246 | self.inner.env(key.as_ref(), val.as_ref()); |
85aaf69f SL |
247 | self |
248 | } | |
249 | ||
250 | /// Removes an environment variable mapping. | |
c34b1796 AL |
251 | #[stable(feature = "process", since = "1.0.0")] |
252 | pub fn env_remove<K: AsRef<OsStr>>(&mut self, key: K) -> &mut Command { | |
253 | self.inner.env_remove(key.as_ref()); | |
85aaf69f SL |
254 | self |
255 | } | |
256 | ||
257 | /// Clears the entire environment map for the child process. | |
c34b1796 | 258 | #[stable(feature = "process", since = "1.0.0")] |
85aaf69f SL |
259 | pub fn env_clear(&mut self) -> &mut Command { |
260 | self.inner.env_clear(); | |
261 | self | |
262 | } | |
263 | ||
9346a6ac | 264 | /// Sets the working directory for the child process. |
c34b1796 | 265 | #[stable(feature = "process", since = "1.0.0")] |
7453a54e | 266 | pub fn current_dir<P: AsRef<Path>>(&mut self, dir: P) -> &mut Command { |
c34b1796 | 267 | self.inner.cwd(dir.as_ref().as_ref()); |
85aaf69f SL |
268 | self |
269 | } | |
270 | ||
271 | /// Configuration for the child process's stdin handle (file descriptor 0). | |
c34b1796 | 272 | #[stable(feature = "process", since = "1.0.0")] |
85aaf69f | 273 | pub fn stdin(&mut self, cfg: Stdio) -> &mut Command { |
7453a54e | 274 | self.inner.stdin(cfg.0); |
85aaf69f SL |
275 | self |
276 | } | |
277 | ||
278 | /// Configuration for the child process's stdout handle (file descriptor 1). | |
c34b1796 | 279 | #[stable(feature = "process", since = "1.0.0")] |
85aaf69f | 280 | pub fn stdout(&mut self, cfg: Stdio) -> &mut Command { |
7453a54e | 281 | self.inner.stdout(cfg.0); |
85aaf69f SL |
282 | self |
283 | } | |
284 | ||
285 | /// Configuration for the child process's stderr handle (file descriptor 2). | |
c34b1796 | 286 | #[stable(feature = "process", since = "1.0.0")] |
85aaf69f | 287 | pub fn stderr(&mut self, cfg: Stdio) -> &mut Command { |
7453a54e | 288 | self.inner.stderr(cfg.0); |
85aaf69f SL |
289 | self |
290 | } | |
291 | ||
85aaf69f SL |
292 | /// Executes the command as a child process, returning a handle to it. |
293 | /// | |
62682a34 | 294 | /// By default, stdin, stdout and stderr are inherited from the parent. |
c34b1796 | 295 | #[stable(feature = "process", since = "1.0.0")] |
85aaf69f | 296 | pub fn spawn(&mut self) -> io::Result<Child> { |
54a0048b | 297 | self.inner.spawn(imp::Stdio::Inherit, true).map(Child::from_inner) |
85aaf69f SL |
298 | } |
299 | ||
300 | /// Executes the command as a child process, waiting for it to finish and | |
301 | /// collecting all of its output. | |
302 | /// | |
303 | /// By default, stdin, stdout and stderr are captured (and used to | |
304 | /// provide the resulting output). | |
305 | /// | |
c34b1796 | 306 | /// # Examples |
85aaf69f SL |
307 | /// |
308 | /// ``` | |
85aaf69f | 309 | /// use std::process::Command; |
9346a6ac | 310 | /// let output = Command::new("cat").arg("foo.txt").output().unwrap_or_else(|e| { |
85aaf69f SL |
311 | /// panic!("failed to execute process: {}", e) |
312 | /// }); | |
313 | /// | |
314 | /// println!("status: {}", output.status); | |
c34b1796 AL |
315 | /// println!("stdout: {}", String::from_utf8_lossy(&output.stdout)); |
316 | /// println!("stderr: {}", String::from_utf8_lossy(&output.stderr)); | |
85aaf69f | 317 | /// ``` |
c34b1796 | 318 | #[stable(feature = "process", since = "1.0.0")] |
85aaf69f | 319 | pub fn output(&mut self) -> io::Result<Output> { |
54a0048b | 320 | self.inner.spawn(imp::Stdio::MakePipe, false).map(Child::from_inner) |
7453a54e | 321 | .and_then(|p| p.wait_with_output()) |
85aaf69f SL |
322 | } |
323 | ||
324 | /// Executes a command as a child process, waiting for it to finish and | |
325 | /// collecting its exit status. | |
326 | /// | |
62682a34 | 327 | /// By default, stdin, stdout and stderr are inherited from the parent. |
85aaf69f | 328 | /// |
c34b1796 | 329 | /// # Examples |
85aaf69f SL |
330 | /// |
331 | /// ``` | |
85aaf69f SL |
332 | /// use std::process::Command; |
333 | /// | |
334 | /// let status = Command::new("ls").status().unwrap_or_else(|e| { | |
335 | /// panic!("failed to execute process: {}", e) | |
336 | /// }); | |
337 | /// | |
338 | /// println!("process exited with: {}", status); | |
339 | /// ``` | |
c34b1796 | 340 | #[stable(feature = "process", since = "1.0.0")] |
85aaf69f | 341 | pub fn status(&mut self) -> io::Result<ExitStatus> { |
54a0048b SL |
342 | self.inner.spawn(imp::Stdio::Inherit, true).map(Child::from_inner) |
343 | .and_then(|mut p| p.wait()) | |
85aaf69f SL |
344 | } |
345 | } | |
346 | ||
347 | #[stable(feature = "rust1", since = "1.0.0")] | |
348 | impl fmt::Debug for Command { | |
349 | /// Format the program and arguments of a Command for display. Any | |
350 | /// non-utf8 data is lossily converted using the utf8 replacement | |
351 | /// character. | |
352 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
7453a54e | 353 | self.inner.fmt(f) |
85aaf69f SL |
354 | } |
355 | } | |
356 | ||
62682a34 SL |
357 | impl AsInner<imp::Command> for Command { |
358 | fn as_inner(&self) -> &imp::Command { &self.inner } | |
85aaf69f SL |
359 | } |
360 | ||
62682a34 SL |
361 | impl AsInnerMut<imp::Command> for Command { |
362 | fn as_inner_mut(&mut self) -> &mut imp::Command { &mut self.inner } | |
85aaf69f SL |
363 | } |
364 | ||
85aaf69f SL |
365 | /// The output of a finished process. |
366 | #[derive(PartialEq, Eq, Clone)] | |
c34b1796 | 367 | #[stable(feature = "process", since = "1.0.0")] |
85aaf69f SL |
368 | pub struct Output { |
369 | /// The status (exit code) of the process. | |
c34b1796 | 370 | #[stable(feature = "process", since = "1.0.0")] |
85aaf69f SL |
371 | pub status: ExitStatus, |
372 | /// The data that the process wrote to stdout. | |
c34b1796 | 373 | #[stable(feature = "process", since = "1.0.0")] |
85aaf69f SL |
374 | pub stdout: Vec<u8>, |
375 | /// The data that the process wrote to stderr. | |
c34b1796 | 376 | #[stable(feature = "process", since = "1.0.0")] |
85aaf69f SL |
377 | pub stderr: Vec<u8>, |
378 | } | |
379 | ||
9cc50fc6 SL |
380 | // If either stderr or stdout are valid utf8 strings it prints the valid |
381 | // strings, otherwise it prints the byte sequence instead | |
382 | #[stable(feature = "process_output_debug", since = "1.7.0")] | |
383 | impl fmt::Debug for Output { | |
384 | fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { | |
385 | ||
386 | let stdout_utf8 = str::from_utf8(&self.stdout); | |
387 | let stdout_debug: &fmt::Debug = match stdout_utf8 { | |
388 | Ok(ref str) => str, | |
389 | Err(_) => &self.stdout | |
390 | }; | |
391 | ||
392 | let stderr_utf8 = str::from_utf8(&self.stderr); | |
393 | let stderr_debug: &fmt::Debug = match stderr_utf8 { | |
394 | Ok(ref str) => str, | |
395 | Err(_) => &self.stderr | |
396 | }; | |
397 | ||
398 | fmt.debug_struct("Output") | |
399 | .field("status", &self.status) | |
400 | .field("stdout", stdout_debug) | |
401 | .field("stderr", stderr_debug) | |
402 | .finish() | |
403 | } | |
404 | } | |
405 | ||
bd371182 | 406 | /// Describes what to do with a standard I/O stream for a child process. |
c34b1796 | 407 | #[stable(feature = "process", since = "1.0.0")] |
7453a54e | 408 | pub struct Stdio(imp::Stdio); |
85aaf69f SL |
409 | |
410 | impl Stdio { | |
411 | /// A new pipe should be arranged to connect the parent and child processes. | |
c34b1796 | 412 | #[stable(feature = "process", since = "1.0.0")] |
7453a54e | 413 | pub fn piped() -> Stdio { Stdio(imp::Stdio::MakePipe) } |
85aaf69f SL |
414 | |
415 | /// The child inherits from the corresponding parent descriptor. | |
c34b1796 | 416 | #[stable(feature = "process", since = "1.0.0")] |
7453a54e | 417 | pub fn inherit() -> Stdio { Stdio(imp::Stdio::Inherit) } |
85aaf69f SL |
418 | |
419 | /// This stream will be ignored. This is the equivalent of attaching the | |
420 | /// stream to `/dev/null` | |
c34b1796 | 421 | #[stable(feature = "process", since = "1.0.0")] |
7453a54e | 422 | pub fn null() -> Stdio { Stdio(imp::Stdio::Null) } |
62682a34 SL |
423 | } |
424 | ||
7453a54e SL |
425 | impl FromInner<imp::Stdio> for Stdio { |
426 | fn from_inner(inner: imp::Stdio) -> Stdio { | |
427 | Stdio(inner) | |
62682a34 | 428 | } |
85aaf69f SL |
429 | } |
430 | ||
431 | /// Describes the result of a process after it has terminated. | |
432 | #[derive(PartialEq, Eq, Clone, Copy, Debug)] | |
c34b1796 | 433 | #[stable(feature = "process", since = "1.0.0")] |
62682a34 | 434 | pub struct ExitStatus(imp::ExitStatus); |
85aaf69f SL |
435 | |
436 | impl ExitStatus { | |
437 | /// Was termination successful? Signal termination not considered a success, | |
438 | /// and success is defined as a zero exit status. | |
c34b1796 | 439 | #[stable(feature = "process", since = "1.0.0")] |
85aaf69f SL |
440 | pub fn success(&self) -> bool { |
441 | self.0.success() | |
442 | } | |
443 | ||
9346a6ac | 444 | /// Returns the exit code of the process, if any. |
85aaf69f SL |
445 | /// |
446 | /// On Unix, this will return `None` if the process was terminated | |
447 | /// by a signal; `std::os::unix` provides an extension trait for | |
448 | /// extracting the signal and other details from the `ExitStatus`. | |
c34b1796 | 449 | #[stable(feature = "process", since = "1.0.0")] |
85aaf69f SL |
450 | pub fn code(&self) -> Option<i32> { |
451 | self.0.code() | |
452 | } | |
453 | } | |
454 | ||
62682a34 SL |
455 | impl AsInner<imp::ExitStatus> for ExitStatus { |
456 | fn as_inner(&self) -> &imp::ExitStatus { &self.0 } | |
85aaf69f SL |
457 | } |
458 | ||
c34b1796 | 459 | #[stable(feature = "process", since = "1.0.0")] |
85aaf69f SL |
460 | impl fmt::Display for ExitStatus { |
461 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { | |
462 | self.0.fmt(f) | |
463 | } | |
464 | } | |
465 | ||
466 | impl Child { | |
467 | /// Forces the child to exit. This is equivalent to sending a | |
468 | /// SIGKILL on unix platforms. | |
c34b1796 | 469 | #[stable(feature = "process", since = "1.0.0")] |
85aaf69f | 470 | pub fn kill(&mut self) -> io::Result<()> { |
7453a54e | 471 | self.handle.kill() |
85aaf69f SL |
472 | } |
473 | ||
62682a34 | 474 | /// Returns the OS-assigned process identifier associated with this child. |
c1a9b12d | 475 | #[stable(feature = "process_id", since = "1.3.0")] |
62682a34 SL |
476 | pub fn id(&self) -> u32 { |
477 | self.handle.id() | |
478 | } | |
479 | ||
9346a6ac | 480 | /// Waits for the child to exit completely, returning the status that it |
85aaf69f SL |
481 | /// exited with. This function will continue to have the same return value |
482 | /// after it has been called at least once. | |
483 | /// | |
484 | /// The stdin handle to the child process, if any, will be closed | |
485 | /// before waiting. This helps avoid deadlock: it ensures that the | |
486 | /// child does not block waiting for input from the parent, while | |
487 | /// the parent waits for the child to exit. | |
c34b1796 | 488 | #[stable(feature = "process", since = "1.0.0")] |
85aaf69f SL |
489 | pub fn wait(&mut self) -> io::Result<ExitStatus> { |
490 | drop(self.stdin.take()); | |
7453a54e | 491 | self.handle.wait().map(ExitStatus) |
85aaf69f SL |
492 | } |
493 | ||
9346a6ac | 494 | /// Simultaneously waits for the child to exit and collect all remaining |
92a42be0 | 495 | /// output on the stdout/stderr handles, returning an `Output` |
85aaf69f SL |
496 | /// instance. |
497 | /// | |
498 | /// The stdin handle to the child process, if any, will be closed | |
499 | /// before waiting. This helps avoid deadlock: it ensures that the | |
500 | /// child does not block waiting for input from the parent, while | |
501 | /// the parent waits for the child to exit. | |
c34b1796 | 502 | #[stable(feature = "process", since = "1.0.0")] |
85aaf69f SL |
503 | pub fn wait_with_output(mut self) -> io::Result<Output> { |
504 | drop(self.stdin.take()); | |
54a0048b SL |
505 | |
506 | let (mut stdout, mut stderr) = (Vec::new(), Vec::new()); | |
507 | match (self.stdout.take(), self.stderr.take()) { | |
508 | (None, None) => {} | |
509 | (Some(mut out), None) => { | |
510 | let res = out.read_to_end(&mut stdout); | |
511 | res.unwrap(); | |
512 | } | |
513 | (None, Some(mut err)) => { | |
514 | let res = err.read_to_end(&mut stderr); | |
515 | res.unwrap(); | |
516 | } | |
517 | (Some(out), Some(err)) => { | |
518 | let res = read2(out.inner, &mut stdout, err.inner, &mut stderr); | |
519 | res.unwrap(); | |
520 | } | |
85aaf69f | 521 | } |
85aaf69f | 522 | |
54a0048b | 523 | let status = self.wait()?; |
85aaf69f SL |
524 | Ok(Output { |
525 | status: status, | |
54a0048b SL |
526 | stdout: stdout, |
527 | stderr: stderr, | |
85aaf69f SL |
528 | }) |
529 | } | |
530 | } | |
531 | ||
c34b1796 AL |
532 | /// Terminates the current process with the specified exit code. |
533 | /// | |
534 | /// This function will never return and will immediately terminate the current | |
535 | /// process. The exit code is passed through to the underlying OS and will be | |
536 | /// available for consumption by another process. | |
537 | /// | |
538 | /// Note that because this function never returns, and that it terminates the | |
539 | /// process, no destructors on the current stack or any other thread's stack | |
540 | /// will be run. If a clean shutdown is needed it is recommended to only call | |
541 | /// this function at a known point where there are no more destructors left | |
542 | /// to run. | |
543 | #[stable(feature = "rust1", since = "1.0.0")] | |
544 | pub fn exit(code: i32) -> ! { | |
e9174d1e | 545 | ::sys_common::cleanup(); |
c34b1796 AL |
546 | ::sys::os::exit(code) |
547 | } | |
548 | ||
85aaf69f SL |
549 | #[cfg(test)] |
550 | mod tests { | |
c34b1796 | 551 | use prelude::v1::*; |
85aaf69f | 552 | use io::prelude::*; |
c34b1796 AL |
553 | |
554 | use io::ErrorKind; | |
85aaf69f | 555 | use str; |
c34b1796 | 556 | use super::{Command, Output, Stdio}; |
85aaf69f SL |
557 | |
558 | // FIXME(#10380) these tests should not all be ignored on android. | |
559 | ||
85aaf69f | 560 | #[test] |
7453a54e | 561 | #[cfg_attr(target_os = "android", ignore)] |
85aaf69f SL |
562 | fn smoke() { |
563 | let p = Command::new("true").spawn(); | |
564 | assert!(p.is_ok()); | |
565 | let mut p = p.unwrap(); | |
566 | assert!(p.wait().unwrap().success()); | |
567 | } | |
568 | ||
85aaf69f | 569 | #[test] |
7453a54e | 570 | #[cfg_attr(target_os = "android", ignore)] |
85aaf69f SL |
571 | fn smoke_failure() { |
572 | match Command::new("if-this-is-a-binary-then-the-world-has-ended").spawn() { | |
573 | Ok(..) => panic!(), | |
574 | Err(..) => {} | |
575 | } | |
576 | } | |
577 | ||
85aaf69f | 578 | #[test] |
7453a54e | 579 | #[cfg_attr(target_os = "android", ignore)] |
85aaf69f SL |
580 | fn exit_reported_right() { |
581 | let p = Command::new("false").spawn(); | |
582 | assert!(p.is_ok()); | |
583 | let mut p = p.unwrap(); | |
584 | assert!(p.wait().unwrap().code() == Some(1)); | |
c34b1796 | 585 | drop(p.wait()); |
85aaf69f SL |
586 | } |
587 | ||
85aaf69f | 588 | #[test] |
7453a54e SL |
589 | #[cfg(unix)] |
590 | #[cfg_attr(target_os = "android", ignore)] | |
85aaf69f | 591 | fn signal_reported_right() { |
c34b1796 | 592 | use os::unix::process::ExitStatusExt; |
85aaf69f | 593 | |
b039eaaf SL |
594 | let mut p = Command::new("/bin/sh") |
595 | .arg("-c").arg("read a") | |
596 | .stdin(Stdio::piped()) | |
597 | .spawn().unwrap(); | |
598 | p.kill().unwrap(); | |
85aaf69f SL |
599 | match p.wait().unwrap().signal() { |
600 | Some(9) => {}, | |
b039eaaf SL |
601 | result => panic!("not terminated by signal 9 (instead, {:?})", |
602 | result), | |
85aaf69f SL |
603 | } |
604 | } | |
605 | ||
606 | pub fn run_output(mut cmd: Command) -> String { | |
607 | let p = cmd.spawn(); | |
608 | assert!(p.is_ok()); | |
609 | let mut p = p.unwrap(); | |
610 | assert!(p.stdout.is_some()); | |
611 | let mut ret = String::new(); | |
612 | p.stdout.as_mut().unwrap().read_to_string(&mut ret).unwrap(); | |
613 | assert!(p.wait().unwrap().success()); | |
614 | return ret; | |
615 | } | |
616 | ||
85aaf69f | 617 | #[test] |
7453a54e | 618 | #[cfg_attr(target_os = "android", ignore)] |
85aaf69f SL |
619 | fn stdout_works() { |
620 | let mut cmd = Command::new("echo"); | |
c34b1796 | 621 | cmd.arg("foobar").stdout(Stdio::piped()); |
85aaf69f SL |
622 | assert_eq!(run_output(cmd), "foobar\n"); |
623 | } | |
624 | ||
85aaf69f | 625 | #[test] |
7453a54e | 626 | #[cfg_attr(any(windows, target_os = "android"), ignore)] |
85aaf69f SL |
627 | fn set_current_dir_works() { |
628 | let mut cmd = Command::new("/bin/sh"); | |
629 | cmd.arg("-c").arg("pwd") | |
630 | .current_dir("/") | |
c34b1796 | 631 | .stdout(Stdio::piped()); |
85aaf69f SL |
632 | assert_eq!(run_output(cmd), "/\n"); |
633 | } | |
634 | ||
85aaf69f | 635 | #[test] |
7453a54e | 636 | #[cfg_attr(any(windows, target_os = "android"), ignore)] |
85aaf69f SL |
637 | fn stdin_works() { |
638 | let mut p = Command::new("/bin/sh") | |
639 | .arg("-c").arg("read line; echo $line") | |
c34b1796 AL |
640 | .stdin(Stdio::piped()) |
641 | .stdout(Stdio::piped()) | |
85aaf69f SL |
642 | .spawn().unwrap(); |
643 | p.stdin.as_mut().unwrap().write("foobar".as_bytes()).unwrap(); | |
644 | drop(p.stdin.take()); | |
645 | let mut out = String::new(); | |
646 | p.stdout.as_mut().unwrap().read_to_string(&mut out).unwrap(); | |
647 | assert!(p.wait().unwrap().success()); | |
648 | assert_eq!(out, "foobar\n"); | |
649 | } | |
650 | ||
651 | ||
85aaf69f | 652 | #[test] |
7453a54e SL |
653 | #[cfg_attr(target_os = "android", ignore)] |
654 | #[cfg(unix)] | |
85aaf69f | 655 | fn uid_works() { |
c34b1796 | 656 | use os::unix::prelude::*; |
85aaf69f SL |
657 | use libc; |
658 | let mut p = Command::new("/bin/sh") | |
659 | .arg("-c").arg("true") | |
660 | .uid(unsafe { libc::getuid() }) | |
661 | .gid(unsafe { libc::getgid() }) | |
662 | .spawn().unwrap(); | |
663 | assert!(p.wait().unwrap().success()); | |
664 | } | |
665 | ||
85aaf69f | 666 | #[test] |
7453a54e SL |
667 | #[cfg_attr(target_os = "android", ignore)] |
668 | #[cfg(unix)] | |
85aaf69f | 669 | fn uid_to_root_fails() { |
c34b1796 | 670 | use os::unix::prelude::*; |
85aaf69f SL |
671 | use libc; |
672 | ||
673 | // if we're already root, this isn't a valid test. Most of the bots run | |
674 | // as non-root though (android is an exception). | |
675 | if unsafe { libc::getuid() == 0 } { return } | |
676 | assert!(Command::new("/bin/ls").uid(0).gid(0).spawn().is_err()); | |
677 | } | |
678 | ||
85aaf69f | 679 | #[test] |
7453a54e | 680 | #[cfg_attr(target_os = "android", ignore)] |
85aaf69f SL |
681 | fn test_process_status() { |
682 | let mut status = Command::new("false").status().unwrap(); | |
683 | assert!(status.code() == Some(1)); | |
684 | ||
685 | status = Command::new("true").status().unwrap(); | |
686 | assert!(status.success()); | |
687 | } | |
688 | ||
689 | #[test] | |
690 | fn test_process_output_fail_to_start() { | |
691 | match Command::new("/no-binary-by-this-name-should-exist").output() { | |
c34b1796 | 692 | Err(e) => assert_eq!(e.kind(), ErrorKind::NotFound), |
85aaf69f SL |
693 | Ok(..) => panic!() |
694 | } | |
695 | } | |
696 | ||
85aaf69f | 697 | #[test] |
7453a54e | 698 | #[cfg_attr(target_os = "android", ignore)] |
85aaf69f SL |
699 | fn test_process_output_output() { |
700 | let Output {status, stdout, stderr} | |
701 | = Command::new("echo").arg("hello").output().unwrap(); | |
c34b1796 | 702 | let output_str = str::from_utf8(&stdout).unwrap(); |
85aaf69f SL |
703 | |
704 | assert!(status.success()); | |
705 | assert_eq!(output_str.trim().to_string(), "hello"); | |
c1a9b12d | 706 | assert_eq!(stderr, Vec::new()); |
85aaf69f SL |
707 | } |
708 | ||
85aaf69f | 709 | #[test] |
7453a54e | 710 | #[cfg_attr(target_os = "android", ignore)] |
85aaf69f SL |
711 | fn test_process_output_error() { |
712 | let Output {status, stdout, stderr} | |
713 | = Command::new("mkdir").arg(".").output().unwrap(); | |
714 | ||
715 | assert!(status.code() == Some(1)); | |
716 | assert_eq!(stdout, Vec::new()); | |
717 | assert!(!stderr.is_empty()); | |
718 | } | |
719 | ||
85aaf69f | 720 | #[test] |
7453a54e | 721 | #[cfg_attr(target_os = "android", ignore)] |
85aaf69f SL |
722 | fn test_finish_once() { |
723 | let mut prog = Command::new("false").spawn().unwrap(); | |
724 | assert!(prog.wait().unwrap().code() == Some(1)); | |
725 | } | |
726 | ||
85aaf69f | 727 | #[test] |
7453a54e | 728 | #[cfg_attr(target_os = "android", ignore)] |
85aaf69f SL |
729 | fn test_finish_twice() { |
730 | let mut prog = Command::new("false").spawn().unwrap(); | |
731 | assert!(prog.wait().unwrap().code() == Some(1)); | |
732 | assert!(prog.wait().unwrap().code() == Some(1)); | |
733 | } | |
734 | ||
85aaf69f | 735 | #[test] |
7453a54e | 736 | #[cfg_attr(target_os = "android", ignore)] |
85aaf69f | 737 | fn test_wait_with_output_once() { |
c34b1796 | 738 | let prog = Command::new("echo").arg("hello").stdout(Stdio::piped()) |
85aaf69f SL |
739 | .spawn().unwrap(); |
740 | let Output {status, stdout, stderr} = prog.wait_with_output().unwrap(); | |
c34b1796 | 741 | let output_str = str::from_utf8(&stdout).unwrap(); |
85aaf69f SL |
742 | |
743 | assert!(status.success()); | |
744 | assert_eq!(output_str.trim().to_string(), "hello"); | |
c1a9b12d | 745 | assert_eq!(stderr, Vec::new()); |
85aaf69f SL |
746 | } |
747 | ||
85aaf69f SL |
748 | #[cfg(all(unix, not(target_os="android")))] |
749 | pub fn env_cmd() -> Command { | |
750 | Command::new("env") | |
751 | } | |
752 | #[cfg(target_os="android")] | |
753 | pub fn env_cmd() -> Command { | |
754 | let mut cmd = Command::new("/system/bin/sh"); | |
755 | cmd.arg("-c").arg("set"); | |
756 | cmd | |
757 | } | |
758 | ||
759 | #[cfg(windows)] | |
760 | pub fn env_cmd() -> Command { | |
761 | let mut cmd = Command::new("cmd"); | |
762 | cmd.arg("/c").arg("set"); | |
763 | cmd | |
764 | } | |
765 | ||
85aaf69f SL |
766 | #[test] |
767 | fn test_inherit_env() { | |
c1a9b12d | 768 | use env; |
85aaf69f SL |
769 | |
770 | let result = env_cmd().output().unwrap(); | |
771 | let output = String::from_utf8(result.stdout).unwrap(); | |
772 | ||
c34b1796 | 773 | for (ref k, ref v) in env::vars() { |
7453a54e SL |
774 | // don't check android RANDOM variables |
775 | if cfg!(target_os = "android") && *k == "RANDOM" { | |
776 | continue | |
777 | } | |
778 | ||
92a42be0 SL |
779 | // Windows has hidden environment variables whose names start with |
780 | // equals signs (`=`). Those do not show up in the output of the | |
781 | // `set` command. | |
782 | assert!((cfg!(windows) && k.starts_with("=")) || | |
783 | k.starts_with("DYLD") || | |
7453a54e SL |
784 | output.contains(&format!("{}={}", *k, *v)) || |
785 | output.contains(&format!("{}='{}'", *k, *v)), | |
85aaf69f SL |
786 | "output doesn't contain `{}={}`\n{}", |
787 | k, v, output); | |
788 | } | |
789 | } | |
85aaf69f SL |
790 | |
791 | #[test] | |
792 | fn test_override_env() { | |
793 | use env; | |
794 | ||
795 | // In some build environments (such as chrooted Nix builds), `env` can | |
796 | // only be found in the explicitly-provided PATH env variable, not in | |
797 | // default places such as /bin or /usr/bin. So we need to pass through | |
798 | // PATH to our sub-process. | |
799 | let mut cmd = env_cmd(); | |
800 | cmd.env_clear().env("RUN_TEST_NEW_ENV", "123"); | |
801 | if let Some(p) = env::var_os("PATH") { | |
802 | cmd.env("PATH", &p); | |
803 | } | |
804 | let result = cmd.output().unwrap(); | |
c34b1796 | 805 | let output = String::from_utf8_lossy(&result.stdout).to_string(); |
85aaf69f SL |
806 | |
807 | assert!(output.contains("RUN_TEST_NEW_ENV=123"), | |
808 | "didn't find RUN_TEST_NEW_ENV inside of:\n\n{}", output); | |
809 | } | |
810 | ||
811 | #[test] | |
812 | fn test_add_to_env() { | |
813 | let result = env_cmd().env("RUN_TEST_NEW_ENV", "123").output().unwrap(); | |
c34b1796 | 814 | let output = String::from_utf8_lossy(&result.stdout).to_string(); |
85aaf69f SL |
815 | |
816 | assert!(output.contains("RUN_TEST_NEW_ENV=123"), | |
817 | "didn't find RUN_TEST_NEW_ENV inside of:\n\n{}", output); | |
818 | } | |
7453a54e SL |
819 | |
820 | // Regression tests for #30858. | |
821 | #[test] | |
822 | fn test_interior_nul_in_progname_is_error() { | |
823 | match Command::new("has-some-\0\0s-inside").spawn() { | |
824 | Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), | |
825 | Ok(_) => panic!(), | |
826 | } | |
827 | } | |
828 | ||
829 | #[test] | |
830 | fn test_interior_nul_in_arg_is_error() { | |
831 | match Command::new("echo").arg("has-some-\0\0s-inside").spawn() { | |
832 | Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), | |
833 | Ok(_) => panic!(), | |
834 | } | |
835 | } | |
836 | ||
837 | #[test] | |
838 | fn test_interior_nul_in_args_is_error() { | |
839 | match Command::new("echo").args(&["has-some-\0\0s-inside"]).spawn() { | |
840 | Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), | |
841 | Ok(_) => panic!(), | |
842 | } | |
843 | } | |
844 | ||
845 | #[test] | |
846 | fn test_interior_nul_in_current_dir_is_error() { | |
847 | match Command::new("echo").current_dir("has-some-\0\0s-inside").spawn() { | |
848 | Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), | |
849 | Ok(_) => panic!(), | |
850 | } | |
851 | } | |
852 | ||
853 | // Regression tests for #30862. | |
854 | #[test] | |
855 | fn test_interior_nul_in_env_key_is_error() { | |
856 | match env_cmd().env("has-some-\0\0s-inside", "value").spawn() { | |
857 | Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), | |
858 | Ok(_) => panic!(), | |
859 | } | |
860 | } | |
861 | ||
862 | #[test] | |
863 | fn test_interior_nul_in_env_value_is_error() { | |
864 | match env_cmd().env("key", "has-some-\0\0s-inside").spawn() { | |
865 | Err(e) => assert_eq!(e.kind(), ErrorKind::InvalidInput), | |
866 | Ok(_) => panic!(), | |
867 | } | |
868 | } | |
85aaf69f | 869 | } |