command_group/stdlib/child/
unix.rs1use std::{
2 convert::TryInto,
3 io::{Error, Read, Result},
4 os::{
5 fd::BorrowedFd,
6 unix::{
7 io::{AsRawFd, RawFd},
8 process::ExitStatusExt,
9 },
10 },
11 process::{Child, ChildStderr, ChildStdin, ChildStdout, ExitStatus},
12};
13
14use nix::{
15 errno::Errno,
16 libc,
17 poll::{poll, PollFd, PollFlags},
18 sys::{
19 signal::{killpg, Signal},
20 wait::WaitPidFlag,
21 },
22 unistd::Pid,
23};
24
25pub(super) struct ChildImp {
26 pgid: Pid,
27 inner: Child,
28}
29
30impl ChildImp {
31 pub(super) fn new(inner: Child) -> Self {
32 Self {
33 pgid: Pid::from_raw(inner.id().try_into().expect("Command PID > i32::MAX")),
34 inner,
35 }
36 }
37
38 pub(super) fn take_stdin(&mut self) -> Option<ChildStdin> {
39 self.inner.stdin.take()
40 }
41
42 pub(super) fn take_stdout(&mut self) -> Option<ChildStdout> {
43 self.inner.stdout.take()
44 }
45
46 pub(super) fn take_stderr(&mut self) -> Option<ChildStderr> {
47 self.inner.stderr.take()
48 }
49
50 pub fn inner(&mut self) -> &mut Child {
51 &mut self.inner
52 }
53
54 pub fn into_inner(self) -> Child {
55 self.inner
56 }
57
58 pub(super) fn signal_imp(&self, sig: Signal) -> Result<()> {
59 killpg(self.pgid, sig).map_err(Error::from)
60 }
61
62 pub fn kill(&mut self) -> Result<()> {
63 self.signal_imp(Signal::SIGKILL)
64 }
65
66 pub fn id(&self) -> u32 {
67 self.inner.id()
68 }
69
70 fn wait_imp(&mut self, flag: WaitPidFlag) -> Result<Option<ExitStatus>> {
71 let negpid = Pid::from_raw(-self.pgid.as_raw());
72
73 let mut parent_exit_status: Option<ExitStatus> = None;
79 loop {
80 let mut status: i32 = 0;
84 match unsafe {
85 libc::waitpid(negpid.into(), &mut status as *mut libc::c_int, flag.bits())
86 } {
87 0 => {
88 return Ok(None);
91 }
92 -1 => {
93 match Errno::last() {
94 Errno::ECHILD => {
95 return Ok(parent_exit_status);
98 }
99 errno => {
100 return Err(Error::from(errno));
101 }
102 }
103 }
104 pid => {
105 if self.pgid.as_raw() == pid {
110 parent_exit_status = Some(ExitStatus::from_raw(status));
111 } else {
112 }
114 }
115 };
116 }
117 }
118
119 pub fn wait(&mut self) -> Result<ExitStatus> {
120 if let Some(status) = self.try_wait()? {
121 return Ok(status);
122 }
123
124 match self.wait_imp(WaitPidFlag::empty()).transpose() {
125 None => self.inner.wait(),
126 Some(status) => status,
127 }
128 }
129
130 pub fn try_wait(&mut self) -> Result<Option<ExitStatus>> {
131 match self.wait_imp(WaitPidFlag::WNOHANG) {
132 Ok(None) => self.inner.try_wait(),
133 otherwise => otherwise,
134 }
135 }
136
137 pub(super) fn read_both(
138 mut out_r: ChildStdout,
139 out_v: &mut Vec<u8>,
140 mut err_r: ChildStderr,
141 err_v: &mut Vec<u8>,
142 ) -> Result<()> {
143 let out_fd = out_r.as_raw_fd();
144 let err_fd = err_r.as_raw_fd();
145 set_nonblocking(out_fd, true)?;
146 set_nonblocking(err_fd, true)?;
147
148 let out_bfd = unsafe { BorrowedFd::borrow_raw(out_fd) };
150 let err_bfd = unsafe { BorrowedFd::borrow_raw(err_fd) };
151
152 let mut fds = [
153 PollFd::new(&out_bfd, PollFlags::POLLIN),
154 PollFd::new(&err_bfd, PollFlags::POLLIN),
155 ];
156
157 loop {
158 poll(&mut fds, -1)?;
159
160 if fds[0].revents().is_some() && read(&mut out_r, out_v)? {
161 set_nonblocking(err_fd, false)?;
162 return err_r.read_to_end(err_v).map(drop);
163 }
164 if fds[1].revents().is_some() && read(&mut err_r, err_v)? {
165 set_nonblocking(out_fd, false)?;
166 return out_r.read_to_end(out_v).map(drop);
167 }
168 }
169
170 fn read(r: &mut impl Read, dst: &mut Vec<u8>) -> Result<bool> {
171 match r.read_to_end(dst) {
172 Ok(_) => Ok(true),
173 Err(e) => {
174 if e.raw_os_error() == Some(libc::EWOULDBLOCK)
175 || e.raw_os_error() == Some(libc::EAGAIN)
176 {
177 Ok(false)
178 } else {
179 Err(e)
180 }
181 }
182 }
183 }
184
185 #[cfg(target_os = "linux")]
186 fn set_nonblocking(fd: RawFd, nonblocking: bool) -> Result<()> {
187 let v = nonblocking as libc::c_int;
188 let res = unsafe { libc::ioctl(fd, libc::FIONBIO, &v) };
189
190 Errno::result(res).map_err(Error::from).map(drop)
191 }
192
193 #[cfg(not(target_os = "linux"))]
194 fn set_nonblocking(fd: RawFd, nonblocking: bool) -> Result<()> {
195 use nix::fcntl::{fcntl, FcntlArg, OFlag};
196
197 let mut flags = OFlag::from_bits_truncate(fcntl(fd, FcntlArg::F_GETFL)?);
198 flags.set(OFlag::O_NONBLOCK, nonblocking);
199
200 fcntl(fd, FcntlArg::F_SETFL(flags))
201 .map_err(Error::from)
202 .map(drop)
203 }
204 }
205}
206
207pub trait UnixChildExt {
208 fn signal(&self, sig: Signal) -> Result<()>;
209}
210
211impl UnixChildExt for ChildImp {
212 fn signal(&self, sig: Signal) -> Result<()> {
213 self.signal_imp(sig)
214 }
215}