wasi_net/process/child.rs
1use std::fs::File;
2use std::io::{self, Read, Write};
3use std::sync::mpsc;
4use std::sync::Arc;
5use std::sync::Mutex;
6
7use super::*;
8use crate::backend::{StdioMode, utils::*};
9use crate::backend::Command as BackendCommand;
10use crate::backend::MessageProcess;
11use crate::backend::Response as BackendResponse;
12
13/// Representation of a running or exited child process.
14///
15/// This structure is used to represent and manage child processes. A child
16/// process is created via the [`Command`] struct, which configures the
17/// spawning process and can itself be constructed using a builder-style
18/// interface.
19///
20/// There is no implementation of [`Drop`] for child processes,
21/// so if you do not ensure the `Child` has exited then it will continue to
22/// run, even after the `Child` handle to the child process has gone out of
23/// scope.
24///
25/// Calling [`wait`] (or other functions that wrap around it) will make
26/// the parent process wait until the child has actually exited before
27/// continuing.
28///
29/// # Examples
30///
31/// ```should_panic
32/// use std::process::Command;
33///
34/// let mut child = Command::new("/bin/cat")
35/// .arg("file.txt")
36/// .spawn()
37/// .expect("failed to execute child");
38///
39/// let ecode = child.wait()
40/// .expect("failed to wait on child");
41///
42/// assert!(ecode.success());
43/// ```
44///
45/// [`wait`]: Child::wait
46#[derive(Debug)]
47pub struct Child {
48 pid: u32,
49 cmd: Command,
50 worker: Arc<Mutex<Worker>>,
51 rx_exit: mpsc::Receiver<ExitStatus>,
52 stdin_mode: StdioMode,
53 stdout_mode: StdioMode,
54 stderr_mode: StdioMode,
55 pre_open: Vec<String>,
56
57 /// The handle for writing to the child's standard input (stdin), if it has
58 /// been captured. To avoid partially moving
59 /// the `child` and thus blocking yourself from calling
60 /// functions on `child` while using `stdin`,
61 /// you might find it helpful:
62 ///
63 /// ```compile_fail,E0425
64 /// let stdin = child.stdin.take().unwrap();
65 /// ```
66 pub stdin: Option<ChildStdin>,
67
68 /// The handle for reading from the child's standard output (stdout), if it
69 /// has been captured. You might find it helpful to do
70 ///
71 /// ```compile_fail,E0425
72 /// let stdout = child.stdout.take().unwrap();
73 /// ```
74 ///
75 /// to avoid partially moving the `child` and thus blocking yourself from calling
76 /// functions on `child` while using `stdout`.
77 pub stdout: Option<ChildStdout>,
78
79 /// The handle for reading from the child's standard error (stderr), if it
80 /// has been captured. You might find it helpful to do
81 ///
82 /// ```compile_fail,E0425
83 /// let stderr = child.stderr.take().unwrap();
84 /// ```
85 ///
86 /// to avoid partially moving the `child` and thus blocking yourself from calling
87 /// functions on `child` while using `stderr`.
88 pub stderr: Option<ChildStderr>,
89}
90
91impl Child {
92 // Starts the child process
93 pub(super) fn new(cmd: &Command, stdin_mode: StdioMode, stdout_mode: StdioMode, stderr_mode: StdioMode, pre_open: Vec<String>,) -> Result<Child> {
94 let submit = BackendCommand::SpawnProcessVersion1 {
95 path: cmd.path.clone(),
96 current_dir: cmd.current_dir.clone(),
97 args: cmd.args.clone(),
98 stdin_mode,
99 stdout_mode,
100 stderr_mode,
101 pre_open: pre_open.clone()
102 };
103 let mut submit = submit.serialize()?;
104 submit += "\n";
105
106 let mut file = File::open("/dev/exec")?;
107
108 let _ = file.write_all(submit.as_bytes());
109
110 let res = read_response(&mut file)?;
111 let pid = match res {
112 BackendResponse::SpawnedProcessVersion1 { pid } => pid,
113 _ => {
114 return Err(std::io::Error::new(
115 std::io::ErrorKind::Other,
116 "the socket does not support this response type",
117 ));
118 }
119 };
120
121 let (worker, stdin, stdout, stderr, rx_exit)
122 = Worker::new(file);
123
124 Ok(Child {
125 pid,
126 worker,
127 cmd: cmd.clone(),
128 stdin_mode,
129 stdout_mode,
130 stderr_mode,
131 stdin: if stdin_mode == StdioMode::Piped { Some(stdin) } else { None },
132 stdout: if stdout_mode == StdioMode::Piped { Some(stdout) } else { None },
133 stderr: if stderr_mode == StdioMode::Piped { Some(stderr) } else { None },
134 rx_exit,
135 pre_open,
136 })
137 }
138
139 /// Forces the child process to exit. If the child has already exited, an [`InvalidInput`]
140 /// error is returned.
141 ///
142 /// The mapping to [`ErrorKind`]s is not part of the compatibility contract of the function,
143 /// especially the [`Other`] kind might change to more specific kinds in the future.
144 ///
145 /// This is equivalent to sending a SIGKILL on Unix platforms.
146 ///
147 /// # Examples
148 ///
149 /// Basic usage:
150 ///
151 /// ```no_run
152 /// use wasi_net::Command;
153 ///
154 /// let mut command = Command::new("yes");
155 /// if let Ok(mut child) = command.spawn() {
156 /// child.kill().expect("command wasn't running");
157 /// } else {
158 /// println!("yes command didn't start");
159 /// }
160 /// ```
161 ///
162 /// [`ErrorKind`]: io::ErrorKind
163 /// [`InvalidInput`]: io::ErrorKind::InvalidInput
164 /// [`Other`]: io::ErrorKind::Other
165 pub fn kill(&mut self) -> io::Result<()> {
166 self.worker
167 .lock()
168 .unwrap()
169 .send(MessageProcess::Kill)
170 .or_else(|_| {
171 Err(std::io::Error::new(
172 std::io::ErrorKind::BrokenPipe,
173 "failed to notify the process to kill itself",
174 ))
175 })?;
176 Ok(())
177 }
178
179 /// Returns the OS-assigned process identifier associated with this child.
180 ///
181 /// # Examples
182 ///
183 /// Basic usage:
184 ///
185 /// ```no_run
186 /// use wasi_net::Command;
187 ///
188 /// let mut command = Command::new("ls");
189 /// if let Ok(child) = command.spawn() {
190 /// println!("Child's ID is {}", child.id());
191 /// } else {
192 /// println!("ls command didn't start");
193 /// }
194 /// ```
195 pub fn id(&self) -> u32 {
196 self.pid
197 }
198
199 /// Waits for the child to exit completely, returning the status that it
200 /// exited with. This function will continue to have the same return value
201 /// after it has been called at least once.
202 ///
203 /// The stdin handle to the child process, if any, will be closed
204 /// before waiting. This helps avoid deadlock: it ensures that the
205 /// child does not block waiting for input from the parent, while
206 /// the parent waits for the child to exit.
207 ///
208 /// # Examples
209 ///
210 /// Basic usage:
211 ///
212 /// ```no_run
213 /// use wasi_net::Command;
214 ///
215 /// let mut command = Command::new("ls");
216 /// if let Ok(mut child) = command.spawn() {
217 /// child.wait().expect("command wasn't running");
218 /// println!("Child has finished its execution!");
219 /// } else {
220 /// println!("ls command didn't start");
221 /// }
222 /// ```
223 pub fn wait(&mut self) -> io::Result<ExitStatus> {
224 loop {
225 match self.worker.lock().unwrap().work() {
226 Ok(_) => {}
227 Err(err) if err.kind() == ErrorKind::WouldBlock => {
228 std::thread::sleep(std::time::Duration::from_millis(1));
229 }
230 Err(err) => {
231 return Err(err);
232 }
233 };
234 match self.rx_exit.try_recv() {
235 Ok(exitcode) => {
236 return Ok(exitcode);
237 }
238 Err(mpsc::TryRecvError::Disconnected) => {
239 return Err(std::io::Error::new(
240 std::io::ErrorKind::BrokenPipe,
241 "the process worker already exited",
242 ));
243 }
244 _ => {}
245 }
246 }
247 }
248
249 /// Attempts to collect the exit status of the child if it has already
250 /// exited.
251 ///
252 /// This function will not block the calling thread and will only
253 /// check to see if the child process has exited or not. If the child has
254 /// exited then on Unix the process ID is reaped. This function is
255 /// guaranteed to repeatedly return a successful exit status so long as the
256 /// child has already exited.
257 ///
258 /// If the child has exited, then `Ok(Some(status))` is returned. If the
259 /// exit status is not available at this time then `Ok(None)` is returned.
260 /// If an error occurs, then that error is returned.
261 ///
262 /// Note that unlike `wait`, this function will not attempt to drop stdin.
263 ///
264 /// # Examples
265 ///
266 /// Basic usage:
267 ///
268 /// ```no_run
269 /// use wasi_net::Command;
270 ///
271 /// let mut child = Command::new("ls").spawn().unwrap();
272 ///
273 /// match child.try_wait() {
274 /// Ok(Some(status)) => println!("exited with: {}", status),
275 /// Ok(None) => {
276 /// println!("status not ready yet, let's really wait");
277 /// let res = child.wait();
278 /// println!("result: {:?}", res);
279 /// }
280 /// Err(e) => println!("error attempting to wait: {}", e),
281 /// }
282 /// ```
283 pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
284 match self.rx_exit.try_recv() {
285 Ok(exitcode) => Ok(Some(exitcode)),
286 Err(mpsc::TryRecvError::Disconnected) => Err(std::io::Error::new(
287 std::io::ErrorKind::BrokenPipe,
288 "the process worker exited",
289 )),
290 Err(mpsc::TryRecvError::Empty) => Ok(None),
291 }
292 }
293
294 /// Simultaneously waits for the child to exit and collect all remaining
295 /// output on the stdout/stderr handles, returning an `Output`
296 /// instance.
297 ///
298 /// The stdin handle to the child process, if any, will be closed
299 /// before waiting. This helps avoid deadlock: it ensures that the
300 /// child does not block waiting for input from the parent, while
301 /// the parent waits for the child to exit.
302 ///
303 /// By default, stdin, stdout and stderr are inherited from the parent.
304 /// In order to capture the output into this `Result<Output>` it is
305 /// necessary to create new pipes between parent and child. Use
306 /// `stdout(Stdio::piped())` or `stderr(Stdio::piped())`, respectively.
307 ///
308 /// # Examples
309 ///
310 /// ```should_panic
311 /// use wasi_net::{Command, Stdio};
312 ///
313 /// let child = Command::new("/bin/cat")
314 /// .arg("file.txt")
315 /// .stdout(Stdio::piped())
316 /// .spawn()
317 /// .expect("failed to execute child");
318 ///
319 /// let output = child
320 /// .wait_with_output()
321 /// .expect("failed to wait on child");
322 ///
323 /// assert!(output.status.success());
324 /// ```
325 ///
326 pub fn wait_with_output(mut self) -> io::Result<Output> {
327 drop(self.stdin.take());
328
329 let status = self.wait()?;
330
331 let (mut stdout, mut stderr) = (Vec::new(), Vec::new());
332 match (self.stdout.take(), self.stderr.take()) {
333 (None, None) => {}
334 (Some(mut out), None) => {
335 out.read_to_end(&mut stdout).unwrap();
336 }
337 (None, Some(mut err)) => {
338 err.read_to_end(&mut stderr).unwrap();
339 }
340 (Some(mut out), Some(mut err)) => {
341 out.read_to_end(&mut stdout).unwrap();
342 err.read_to_end(&mut stderr).unwrap();
343 }
344 }
345
346 Ok(Output {
347 status,
348 stdout,
349 stderr,
350 })
351 }
352}