1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161
//! Extends and wraps `std::process::Child` to make it clonable. Thus eliminating the problem that //! `libstd` does not have a cross platfrom and simple way to kill a child while also waiting for //! it. //! //! ## Getting Started //! //! Add the dependency to your Cargo.toml: //! //! ```toml //! [dependencies] //! clonablechild = "0.1" //! ``` //! //! ## Example //! //! Use it in your program to kill a sleep process before it terminates naturally: //! //! ```rust,ignore //! extern crate clonablechild; //! //! use clonablechild::{ChildExt, ClonableChild}; //! //! use std::process::Command; //! use std::thread; //! use std::time::Duration; //! //! fn main() { //! // This command is specific to unix systems. See tests for Windows examples. //! let child = Command::new("sleep").arg("10").spawn().unwrap(); //! let clonable_child = child.into_clonable(); //! //! kill_async(clonable_child.clone()); //! let exit_status = clonable_child.wait().unwrap(); //! //! // Assert child was killed by a signal and did not exit cleanly //! assert_eq!(None, exit_status.code()); //! assert!(!exit_status.success()); //! } //! //! fn kill_async(child: ClonableChild) { //! thread::spawn(move || { //! thread::sleep(Duration::new(1, 0)); //! child.kill().expect("Expected to be able to kill subprocess"); //! }); //! } //! ``` #![deny(missing_docs)] extern crate libc; use std::io; use std::process::{Child, ExitStatus, ChildStdin, ChildStdout, ChildStderr}; use std::sync::{Arc, Mutex}; #[cfg(unix)] #[path = "unix.rs"] mod imp; #[cfg(windows)] #[path = "windows.rs"] mod imp; /// A trait that extends `Child` with a method to convert it into a `ClonableChild`. pub trait ChildExt { /// Consumes the child and wraps it in a `ClonableChild`. Results in an object with a similar /// API, but that can be cloned. fn into_clonable(self) -> ClonableChild; } impl ChildExt for Child { fn into_clonable(self) -> ClonableChild { ClonableChild::new(self) } } struct ChildIo { pub stdin: Option<ChildStdin>, pub stdout: Option<ChildStdout>, pub stderr: Option<ChildStderr>, } impl ChildIo { pub fn new(child: &mut Child) -> Self { ChildIo { stdin: child.stdin.take(), stdout: child.stdout.take(), stderr: child.stderr.take(), } } } /// Representation of a clonable `std::process::Child`. #[derive(Clone)] pub struct ClonableChild { id: u32, child: Arc<Mutex<Child>>, imp_child: imp::ClonableChild, io: Arc<Mutex<ChildIo>>, } impl ClonableChild { /// Creates a new `ClonableChild` by consuming and wrapping the given `Child`. pub fn new(mut child: Child) -> Self { let imp_child = imp::ClonableChild::new(&child); let io = Arc::new(Mutex::new(ChildIo::new(&mut child))); ClonableChild { id: child.id(), child: Arc::new(Mutex::new(child)), imp_child: imp_child, io: io, } } /// Forces the child to exit. This is equivalent to sending a SIGKILL on unix platforms and /// calling TerminateProcess on Windows. /// /// This method first tries to use the ordinary `Child::kill()`, but if that is blocked by /// another thread waiting for the child it will kill it itself in the same way `Child::kill()` /// would have done. pub fn kill(&self) -> io::Result<()> { let child = self.child.try_lock(); match child { Ok(mut child) => child.kill(), Err(..) => self.imp_child.kill(), } } /// Returns the OS-assigned process identifier associated with this child. This value is /// obtained from `Child::id()` in `ClonableChild::new()` and then that value is returned every /// time. pub fn id(&self) -> u32 { self.id } /// Behaves just like `Child::wait()`, see documentation for that method. pub fn wait(&self) -> io::Result<ExitStatus> { let mut child = self.child.lock().unwrap(); child.wait() } /// Retrieve the stdin stream from the child if one exist. Will only return something on the /// first call. pub fn stdin(&mut self) -> Option<ChildStdin> { self.io.lock().unwrap().stdin.take() } /// Retrieve the stdout stream from the child if one exist. Will only return something on the /// first call. pub fn stdout(&mut self) -> Option<ChildStdout> { self.io.lock().unwrap().stdout.take() } /// Retrieve the stderr stream from the child if one exist. Will only return something on the /// first call. pub fn stderr(&mut self) -> Option<ChildStderr> { self.io.lock().unwrap().stderr.take() } }