From 1a56312f269ffc89db7b4de84aaed2c7bdf2d013 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Mon, 31 May 2021 13:03:07 +0100 Subject: [PATCH] childio: new facility, will be used for ssh child Signed-off-by: Ian Jackson --- src/childio.rs | 111 +++++++++++++++++++++++++++++++++++++++++++++++++ src/lib.rs | 1 + 2 files changed, 112 insertions(+) create mode 100644 src/childio.rs diff --git a/src/childio.rs b/src/childio.rs new file mode 100644 index 00000000..35446b24 --- /dev/null +++ b/src/childio.rs @@ -0,0 +1,111 @@ +// Copyright 2020-2021 Ian Jackson and contributors to Otter +// SPDX-License-Identifier: AGPL-3.0-or-later +// There is NO WARRANTY. + +use crate::prelude::*; + +use std::process::{self, ChildStdin, ChildStdout}; + +#[derive(Debug)] +pub struct ChildIo { + rw: RW, + child: Arc>, +} + +#[derive(Debug)] +struct ChildWrapper { + reported: bool, + desc: String, + child: process::Child, +} + +impl Display for ChildWrapper { + #[throws(fmt::Error)] + fn fmt(&self, f: &mut fmt::Formatter) { + write!(f, "subprocess {} [{}]", &self.desc, self.child.id())? + } +} + +impl ChildIo { + fn rw_result(&self, r: io::Result) -> io::Result { + if r.is_ok() && *r.as_ref().unwrap() > 0 { return r } + let status = self.child.lock().child.try_wait()?; + match status { + None => r, + Some(es) if es.success() => r, + Some(es) => { + let mut child = self.child.lock(); + child.reported = true; + let ae = anyhow!("{} failed: {}", &child, es); + Err(io::Error::new(ErrorKind::Other, ae)) + }, + } + } +} + +pub fn new_pair(mut input: process::Child, desc: String) + -> (ChildIo, ChildIo) { + let stdin = input.stdin .take().expect("ChildIo::pair, no stdin¬"); + let stdout = input.stdout.take().expect("ChildIo::pair, no stdout¬"); + let wrapper = Arc::new(Mutex::new(ChildWrapper { + reported: false, + desc, + child: input, + })); + (ChildIo { rw: stdin, child: wrapper.clone() }, + ChildIo { rw: stdout, child: wrapper }) +} + +#[throws(io::Error)] +pub fn run_pair(mut cmd: process::Command, desc: String) + -> (ChildIo, ChildIo) { + cmd.stdin (Stdio::piped()); + cmd.stdout(Stdio::piped()); + new_pair(cmd.spawn()?, desc) +} + +impl Drop for ChildWrapper { + fn drop(&mut self) { + use nix::sys::signal::{self, Signal::*}; + use nix::unistd::Pid; + + if let Err(e) = (||{ + let es = match self.child.try_wait().context("wait")? { + Some(es) => es, + None => { + let pid = self.child.id(); + let pid = pid.try_into() + .map_err(|_| anyhow!("pid {:?} out of range!", pid))?; + let pid = Pid::from_raw(pid); + signal::kill(pid, SIGTERM).context("kill")?; + self.child.wait().context("wait after kill")? + }, + }; + if ! self.reported && ! es.success() + && es.signal() != Some(SIGPIPE as _) { + warn!("{} failed: {}", &self, es); + } + Ok::<_,AE>(()) + })() { + warn!("{} cleanup failed: {}", &self, e); + } + } +} + +impl Read for ChildIo where R: Read { + fn read(&mut self, buf: &mut [u8]) -> io::Result { + let r = self.rw.read(buf); + self.rw_result(r) + } +} + +impl Write for ChildIo where W: Write { + fn write(&mut self, buf: &[u8]) -> io::Result { + let r = self.rw.write(buf); + self.rw_result(r) + } + fn flush(&mut self) -> io::Result<()> { + let r = self.rw.flush(); + self.rw_result(r.map(|()|0)).map(|_|()) + } +} diff --git a/src/lib.rs b/src/lib.rs index ef9209ff..14363c9f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,7 @@ pub mod accounts; pub mod asseturl; pub mod authproofs; pub mod bundles; +pub mod childio; pub mod clock; pub mod commands; pub mod config; -- 2.30.2