1 // Copyright (c) 2023 Huawei Device Co., Ltd.
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 //     http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 use std::io;
15 use std::io::IoSlice;
16 #[cfg(unix)]
17 use std::os::unix::io::{AsFd, AsRawFd, BorrowedFd, OwnedFd, RawFd};
18 use std::pin::Pin;
19 use std::process::{Child as StdChild, ExitStatus, Output};
20 use std::task::{Context, Poll};
21 
22 use crate::io::{AsyncRead, AsyncReadExt, AsyncWrite, ReadBuf};
23 use crate::process::sys::ChildStdio;
24 
25 #[derive(Debug)]
26 pub(crate) enum ChildState {
27     Pending(super::sys::Child),
28     Ready(ExitStatus),
29 }
30 
31 /// Handle of child process
32 #[derive(Debug)]
33 pub struct Child {
34     state: ChildState,
35     // Weather kill the child when drop
36     kill_on_drop: bool,
37     /// Options of stdin
38     stdin: Option<ChildStdin>,
39     /// Options of stdout
40     stdout: Option<ChildStdout>,
41     /// Options of stderr
42     stderr: Option<ChildStderr>,
43 }
44 
45 impl Child {
new( child: StdChild, kill_on_drop: bool, stdin: Option<ChildStdin>, stdout: Option<ChildStdout>, stderr: Option<ChildStderr>, ) -> io::Result<Self>46     pub(crate) fn new(
47         child: StdChild,
48         kill_on_drop: bool,
49         stdin: Option<ChildStdin>,
50         stdout: Option<ChildStdout>,
51         stderr: Option<ChildStderr>,
52     ) -> io::Result<Self> {
53         Ok(Self {
54             state: ChildState::Pending(super::sys::Child::new(child)?),
55             kill_on_drop,
56             stdin,
57             stdout,
58             stderr,
59         })
60     }
61 
62     /// Gets the OS-assigned process identifier associated with this child.
63     ///
64     /// If the child process is exited, it returns `None`.
65     ///
66     /// # Example
67     ///
68     /// ```no_run
69     /// use ylong_runtime::process::Command;
70     ///
71     /// fn command() {
72     ///     let mut child = Command::new("ls")
73     ///         .spawn()
74     ///         .expect("ls command failed to start");
75     ///     let _id = child.id().expect("the child process is exited");
76     /// }
77     /// ```
id(&self) -> Option<u32>78     pub fn id(&self) -> Option<u32> {
79         match &self.state {
80             ChildState::Pending(child) => Some(child.id()),
81             ChildState::Ready(_) => None,
82         }
83     }
84 
85     /// Takes the stdin of this child, remain `None`.
86     ///
87     /// # Example
88     ///
89     /// ```no_run
90     /// use std::process::Stdio;
91     ///
92     /// use ylong_runtime::process::Command;
93     ///
94     /// fn command() {
95     ///     let mut child = Command::new("ls")
96     ///         .stdin(Stdio::piped())
97     ///         .spawn()
98     ///         .expect("ls command failed to start");
99     ///     let _stdin = child.take_stdin().unwrap();
100     /// }
101     /// ```
take_stdin(&mut self) -> Option<ChildStdin>102     pub fn take_stdin(&mut self) -> Option<ChildStdin> {
103         self.stdin.take()
104     }
105 
106     /// Takes the stdout of this child, remain `None`.
107     ///
108     /// # Example
109     ///
110     /// ```no_run
111     /// use std::process::Stdio;
112     ///
113     /// use ylong_runtime::process::Command;
114     ///
115     /// fn command() {
116     ///     let mut child = Command::new("ls")
117     ///         .stdout(Stdio::piped())
118     ///         .spawn()
119     ///         .expect("ls command failed to start");
120     ///     let _stdout = child.take_stdout().unwrap();
121     /// }
122     /// ```
take_stdout(&mut self) -> Option<ChildStdout>123     pub fn take_stdout(&mut self) -> Option<ChildStdout> {
124         self.stdout.take()
125     }
126 
127     /// Takes the stderr of this child, remain `None`.
128     ///
129     /// # Example
130     ///
131     /// ```no_run
132     /// use std::process::Stdio;
133     ///
134     /// use ylong_runtime::process::Command;
135     ///
136     /// fn command() {
137     ///     let mut child = Command::new("ls")
138     ///         .stderr(Stdio::piped())
139     ///         .spawn()
140     ///         .expect("ls command failed to start");
141     ///     let _stderr = child.take_stderr().unwrap();
142     /// }
143     /// ```
take_stderr(&mut self) -> Option<ChildStderr>144     pub fn take_stderr(&mut self) -> Option<ChildStderr> {
145         self.stderr.take()
146     }
147 
148     /// Tries to kill the child process, but doesn't wait for it to take effect.
149     ///
150     /// On Unix, this is equivalent to sending a SIGKILL. User should ensure
151     /// either `child.wait().await` or `child.try_wait()` is invoked
152     /// successfully, otherwise the child process will be a Zombie Process.
153     ///
154     /// # Example
155     ///
156     /// ```no_run
157     /// use std::io;
158     /// use std::process::ExitStatus;
159     ///
160     /// use ylong_runtime::process::Command;
161     ///
162     /// async fn command() -> io::Result<ExitStatus> {
163     ///     let mut child = Command::new("ls")
164     ///         .spawn()
165     ///         .expect("ls command failed to start");
166     ///     child.start_kill().expect("failed to start_kill");
167     ///     child.wait().await
168     /// }
169     /// ```
start_kill(&mut self) -> io::Result<()>170     pub fn start_kill(&mut self) -> io::Result<()> {
171         match &mut self.state {
172             ChildState::Pending(ref mut child) => {
173                 let res = child.kill();
174                 if res.is_ok() {
175                     self.kill_on_drop = false;
176                 }
177                 res
178             }
179             ChildState::Ready(_) => Err(io::Error::new(
180                 io::ErrorKind::InvalidInput,
181                 "can not kill an exited process",
182             )),
183         }
184     }
185 
186     /// Kills the child process and wait().
187     ///
188     /// # Example
189     ///
190     /// ```no_run
191     /// use ylong_runtime::process::Command;
192     ///
193     /// async fn command() {
194     ///     let mut child = Command::new("ls")
195     ///         .spawn()
196     ///         .expect("ls command failed to start");
197     ///     child.kill().await.expect("failed to kill");
198     /// }
199     /// ```
kill(&mut self) -> io::Result<()>200     pub async fn kill(&mut self) -> io::Result<()> {
201         self.start_kill()?;
202         self.wait().await.map(|_| ())
203     }
204 
205     /// Waits for the child process to exit, and return the status.
206     ///
207     /// # Example
208     ///
209     /// ```no_run
210     /// use ylong_runtime::process::Command;
211     ///
212     /// async fn command() {
213     ///     let mut child = Command::new("ls")
214     ///         .spawn()
215     ///         .expect("ls command failed to start");
216     ///     let res = child.wait().await.expect("failed to kill");
217     /// }
218     /// ```
wait(&mut self) -> io::Result<ExitStatus>219     pub async fn wait(&mut self) -> io::Result<ExitStatus> {
220         // take stdin to avoid deadlock.
221         drop(self.take_stdin());
222         match &mut self.state {
223             ChildState::Pending(child) => {
224                 let res = child.await;
225 
226                 if let Ok(exit_status) = res {
227                     self.kill_on_drop = false;
228                     self.state = ChildState::Ready(exit_status);
229                 }
230 
231                 res
232             }
233             ChildState::Ready(exit_status) => Ok(*exit_status),
234         }
235     }
236 
237     /// Tries to get th exit status of the child if it is already exited.
238     ///
239     /// # Example
240     ///
241     /// ```no_run
242     /// use ylong_runtime::process::Command;
243     ///
244     /// async fn command() {
245     ///     let mut child = Command::new("ls")
246     ///         .spawn()
247     ///         .expect("ls command failed to start");
248     ///     let res = child.try_wait().expect("failed to try_wait!");
249     /// }
250     /// ```
try_wait(&mut self) -> io::Result<Option<ExitStatus>>251     pub fn try_wait(&mut self) -> io::Result<Option<ExitStatus>> {
252         match &mut self.state {
253             ChildState::Pending(child) => {
254                 let res = child.try_wait();
255 
256                 if let Ok(Some(exit_status)) = res {
257                     // the child is exited, no need for a kill
258                     self.kill_on_drop = false;
259                     self.state = ChildState::Ready(exit_status);
260                 }
261 
262                 res
263             }
264             ChildState::Ready(exit_status) => Ok(Some(*exit_status)),
265         }
266     }
267 
268     /// Returns the `Output` with exit status, stdout and stderr of child
269     /// process.
270     ///
271     /// # Example
272     ///
273     /// ```no_run
274     /// use ylong_runtime::process::Command;
275     ///
276     /// async fn command() {
277     ///     let mut child = Command::new("ls")
278     ///         .spawn()
279     ///         .expect("ls command failed to start");
280     ///     let res = child.output_wait().await.expect("failed to output_wait");
281     /// }
282     /// ```
output_wait(&mut self) -> io::Result<Output>283     pub async fn output_wait(&mut self) -> io::Result<Output> {
284         async fn read_to_end<T: AsyncRead + Unpin>(io: &mut Option<T>) -> io::Result<Vec<u8>> {
285             let mut vec = Vec::new();
286             if let Some(io) = io.as_mut() {
287                 io.read_to_end(&mut vec).await?;
288             }
289             Ok(vec)
290         }
291 
292         let mut child_stdout = self.take_stdout();
293         let mut child_stderr = self.take_stderr();
294 
295         let fut1 = self.wait();
296         let fut2 = read_to_end(&mut child_stdout);
297         let fut3 = read_to_end(&mut child_stderr);
298 
299         let (status, stdout, stderr) =
300             crate::process::try_join3::try_join3(fut1, fut2, fut3).await?;
301 
302         drop(child_stdout);
303         drop(child_stderr);
304 
305         Ok(Output {
306             status,
307             stdout,
308             stderr,
309         })
310     }
311 }
312 
313 impl Drop for Child {
drop(&mut self)314     fn drop(&mut self) {
315         if self.kill_on_drop {
316             if let ChildState::Pending(child) = &mut self.state {
317                 let _ = child.kill();
318             }
319         }
320     }
321 }
322 
323 /// Standard input stream of Child which implements `AsyncWrite` trait
324 #[derive(Debug)]
325 pub struct ChildStdin {
326     inner: ChildStdio,
327 }
328 
329 /// Standard output stream of Child which implements `AsyncRead` trait
330 #[derive(Debug)]
331 pub struct ChildStdout {
332     inner: ChildStdio,
333 }
334 
335 /// Standard err stream of Child which implements `AsyncRead` trait
336 #[derive(Debug)]
337 pub struct ChildStderr {
338     inner: ChildStdio,
339 }
340 
341 impl AsyncWrite for ChildStdin {
poll_write( mut self: Pin<&mut Self>, ctx: &mut Context<'_>, buffer: &[u8], ) -> Poll<io::Result<usize>>342     fn poll_write(
343         mut self: Pin<&mut Self>,
344         ctx: &mut Context<'_>,
345         buffer: &[u8],
346     ) -> Poll<io::Result<usize>> {
347         Pin::new(&mut self.inner).poll_write(ctx, buffer)
348     }
349 
poll_write_vectored( mut self: Pin<&mut Self>, ctx: &mut Context<'_>, bufs: &[IoSlice<'_>], ) -> Poll<io::Result<usize>>350     fn poll_write_vectored(
351         mut self: Pin<&mut Self>,
352         ctx: &mut Context<'_>,
353         bufs: &[IoSlice<'_>],
354     ) -> Poll<io::Result<usize>> {
355         Pin::new(&mut self.inner).poll_write_vectored(ctx, bufs)
356     }
357 
is_write_vectored(&self) -> bool358     fn is_write_vectored(&self) -> bool {
359         self.inner.is_write_vectored()
360     }
361 
poll_flush(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::Result<()>>362     fn poll_flush(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::Result<()>> {
363         Pin::new(&mut self.inner).poll_flush(ctx)
364     }
365 
poll_shutdown(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::Result<()>>366     fn poll_shutdown(mut self: Pin<&mut Self>, ctx: &mut Context<'_>) -> Poll<io::Result<()>> {
367         Pin::new(&mut self.inner).poll_shutdown(ctx)
368     }
369 }
370 
371 impl AsyncRead for ChildStdout {
poll_read( mut self: Pin<&mut Self>, ctx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll<io::Result<()>>372     fn poll_read(
373         mut self: Pin<&mut Self>,
374         ctx: &mut Context<'_>,
375         buf: &mut ReadBuf<'_>,
376     ) -> Poll<io::Result<()>> {
377         Pin::new(&mut self.inner).poll_read(ctx, buf)
378     }
379 }
380 
381 impl AsyncRead for ChildStderr {
poll_read( mut self: Pin<&mut Self>, ctx: &mut Context<'_>, buf: &mut ReadBuf<'_>, ) -> Poll<io::Result<()>>382     fn poll_read(
383         mut self: Pin<&mut Self>,
384         ctx: &mut Context<'_>,
385         buf: &mut ReadBuf<'_>,
386     ) -> Poll<io::Result<()>> {
387         Pin::new(&mut self.inner).poll_read(ctx, buf)
388     }
389 }
390 
391 macro_rules! impl_common_traits {
392     ($ident:ident) => {
393         impl $ident {
394             pub(crate) fn new(inner: ChildStdio) -> Self {
395                 Self { inner }
396             }
397 
398             /// Creates an async `ChildStd` from `std::process::ChildStd`
399             pub fn from_std(inner: std::process::$ident) -> io::Result<Self> {
400                 super::sys::stdio(inner).map(|inner| Self { inner })
401             }
402 
403             /// Convert to OwnedFd
404             #[cfg(unix)]
405             pub fn into_owned_fd(self) -> io::Result<OwnedFd> {
406                 self.inner.into_owned_fd()
407             }
408         }
409 
410         impl TryInto<std::process::Stdio> for $ident {
411             type Error = io::Error;
412 
413             fn try_into(self) -> Result<std::process::Stdio, Self::Error> {
414                 super::sys::to_stdio(self.inner)
415             }
416         }
417 
418         #[cfg(unix)]
419         impl AsRawFd for $ident {
420             fn as_raw_fd(&self) -> RawFd {
421                 self.inner.as_raw_fd()
422             }
423         }
424 
425         #[cfg(unix)]
426         impl AsFd for $ident {
427             fn as_fd(&self) -> BorrowedFd<'_> {
428                 self.inner.as_fd()
429             }
430         }
431     };
432 }
433 
434 impl_common_traits!(ChildStdin);
435 impl_common_traits!(ChildStdout);
436 impl_common_traits!(ChildStderr);
437 
438 #[cfg(test)]
439 mod test {
440     use std::os::fd::{AsFd, AsRawFd};
441     use std::process::Stdio;
442 
443     use crate::process::{Child, ChildStderr, ChildStdin, ChildStdout, Command};
444 
445     /// UT test cases for Child.
446     ///
447     /// # Brief
448     /// 1. Create a ylong_runtime's `Child` with std `Child`.
449     /// 2. Check stdin/stdout/stderr.
450     /// 3. Call `wait()` to exit Child.
451     #[test]
ut_process_child_new_test()452     fn ut_process_child_new_test() {
453         let mut command = std::process::Command::new("echo");
454         let std_child = command.spawn().unwrap();
455 
456         let handle = crate::spawn(async move {
457             let mut child = Child::new(std_child, false, None, None, None).unwrap();
458             assert!(!child.kill_on_drop);
459             assert!(child.stdin.is_none());
460             assert!(child.stdout.is_none());
461             assert!(child.stderr.is_none());
462             assert!(child.id().is_some());
463             let status = child.wait().await.unwrap();
464             assert!(status.success());
465         });
466         crate::block_on(handle).unwrap();
467     }
468 
469     /// UT test cases for Child.
470     ///
471     /// # Brief
472     /// 1. Create a `Command` with arg.
473     /// 2. Use `spawn()` create a child handle
474     /// 3. Use `try_wait()` waiting result until the child handle is ok.
475     #[test]
ut_process_try_wait_test()476     fn ut_process_try_wait_test() {
477         let mut command = std::process::Command::new("echo");
478         let std_child = command.spawn().unwrap();
479         let handle = crate::spawn(async {
480             let mut child = Child::new(std_child, false, None, None, None).unwrap();
481 
482             loop {
483                 if child.try_wait().unwrap().is_some() {
484                     break;
485                 }
486             }
487             assert!(child.try_wait().unwrap().is_some());
488         });
489         crate::block_on(handle).unwrap();
490     }
491 
492     /// UT test cases for stdio.
493     ///
494     /// # Brief
495     /// 1. Create a `Command` with arg.
496     /// 2. Use `spawn()` create a child handle
497     /// 3. Use `wait()` waiting result.
498     #[test]
ut_process_stdio_test()499     fn ut_process_stdio_test() {
500         let handle = crate::spawn(async {
501             let mut command = Command::new("echo");
502             command
503                 .arg("Hello, world!")
504                 .stdin(Stdio::piped())
505                 .stdout(Stdio::piped())
506                 .stderr(Stdio::piped());
507             let mut child = command.spawn().unwrap();
508 
509             let child_stdin = child.take_stdin().unwrap();
510             assert!(child_stdin.into_owned_fd().is_ok());
511             let child_stdout = child.take_stdout().unwrap();
512             assert!(child_stdout.into_owned_fd().is_ok());
513             let child_stderr = child.take_stderr().unwrap();
514             assert!(child_stderr.into_owned_fd().is_ok());
515 
516             drop(child);
517 
518             let mut child = command.spawn().unwrap();
519 
520             let child_stdin = child.take_stdin().unwrap();
521             assert!(child_stdin.as_fd().as_raw_fd() >= 0);
522             assert!(child_stdin.as_raw_fd() >= 0);
523             assert!(TryInto::<Stdio>::try_into(child_stdin).is_ok());
524 
525             let child_stdout = child.take_stdout().unwrap();
526             assert!(child_stdout.as_fd().as_raw_fd() >= 0);
527             assert!(child_stdout.as_raw_fd() >= 0);
528             assert!(TryInto::<Stdio>::try_into(child_stdout).is_ok());
529 
530             let child_stderr = child.take_stderr().unwrap();
531             assert!(child_stderr.as_fd().as_raw_fd() >= 0);
532             assert!(child_stderr.as_raw_fd() >= 0);
533             assert!(TryInto::<Stdio>::try_into(child_stderr).is_ok());
534             drop(child);
535         });
536         crate::block_on(handle).unwrap();
537     }
538 
539     /// UT test cases for ChildStd.
540     ///
541     /// # Brief
542     /// 1. Create a `std::process::Command`.
543     /// 2. Use `spawn()` create a child handle
544     /// 3. Use `from_std()` to convert std to ylong_runtime::process::ChildStd.
545     #[test]
ut_process_child_stdio_convert_test()546     fn ut_process_child_stdio_convert_test() {
547         let mut command = std::process::Command::new("echo");
548         command
549             .stdin(Stdio::piped())
550             .stdout(Stdio::piped())
551             .stderr(Stdio::piped());
552         let mut child = command.spawn().unwrap();
553         let handle = crate::spawn(async move {
554             let stdin = child.stdin.take().unwrap();
555             assert!(ChildStdin::from_std(stdin).is_ok());
556             let stdout = child.stdout.take().unwrap();
557             assert!(ChildStdout::from_std(stdout).is_ok());
558             let stderr = child.stderr.take().unwrap();
559             assert!(ChildStderr::from_std(stderr).is_ok());
560         });
561         crate::block_on(handle).unwrap();
562     }
563 }
564