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