2021-09-14 01:22:42 +00:00
|
|
|
use actix_rt::task::JoinHandle;
|
2021-10-14 00:06:53 +00:00
|
|
|
use actix_web::web::Bytes;
|
2021-08-31 02:19:47 +00:00
|
|
|
use std::{
|
2021-09-04 00:53:53 +00:00
|
|
|
future::Future,
|
2021-08-31 02:19:47 +00:00
|
|
|
pin::Pin,
|
2021-09-14 01:22:42 +00:00
|
|
|
process::Stdio,
|
2021-08-31 02:19:47 +00:00
|
|
|
task::{Context, Poll},
|
|
|
|
};
|
2021-09-14 01:22:42 +00:00
|
|
|
use tokio::{
|
|
|
|
io::{AsyncRead, AsyncWriteExt, ReadBuf},
|
|
|
|
process::{Child, Command},
|
|
|
|
sync::oneshot::{channel, Receiver},
|
|
|
|
};
|
2021-09-25 20:23:05 +00:00
|
|
|
use tracing::Instrument;
|
|
|
|
use tracing::Span;
|
2021-09-04 00:53:53 +00:00
|
|
|
|
2021-09-09 19:16:12 +00:00
|
|
|
#[derive(Debug)]
|
|
|
|
struct StatusError;
|
|
|
|
|
2021-08-31 02:19:47 +00:00
|
|
|
pub(crate) struct Process {
|
2021-09-14 01:22:42 +00:00
|
|
|
child: Child,
|
2021-09-25 20:23:05 +00:00
|
|
|
span: Span,
|
2021-08-31 02:19:47 +00:00
|
|
|
}
|
|
|
|
|
2021-09-04 00:53:53 +00:00
|
|
|
pub(crate) struct ProcessRead<I> {
|
|
|
|
inner: I,
|
2021-09-25 20:23:05 +00:00
|
|
|
span: Span,
|
2021-09-14 01:22:42 +00:00
|
|
|
err_recv: Receiver<std::io::Error>,
|
2021-09-04 00:53:53 +00:00
|
|
|
err_closed: bool,
|
2021-09-14 01:22:42 +00:00
|
|
|
handle: JoinHandle<()>,
|
2021-09-04 00:53:53 +00:00
|
|
|
}
|
|
|
|
|
2021-08-31 02:19:47 +00:00
|
|
|
impl Process {
|
2021-09-14 01:22:42 +00:00
|
|
|
pub(crate) fn run(command: &str, args: &[&str]) -> std::io::Result<Self> {
|
|
|
|
Self::spawn(Command::new(command).args(args))
|
|
|
|
}
|
|
|
|
|
2021-09-25 20:23:05 +00:00
|
|
|
fn spawn_span(&self) -> Span {
|
|
|
|
let span = tracing::info_span!(parent: None, "Spawned command writer",);
|
|
|
|
|
|
|
|
span.follows_from(self.span.clone());
|
|
|
|
|
|
|
|
span
|
|
|
|
}
|
|
|
|
|
2021-09-14 01:22:42 +00:00
|
|
|
pub(crate) fn spawn(cmd: &mut Command) -> std::io::Result<Self> {
|
2021-09-25 20:23:05 +00:00
|
|
|
let cmd = cmd.stdin(Stdio::piped()).stdout(Stdio::piped());
|
|
|
|
|
|
|
|
let span = tracing::info_span!(
|
|
|
|
"Spawning Command",
|
|
|
|
command = &tracing::field::debug(&cmd),
|
|
|
|
exception.message = &tracing::field::Empty,
|
|
|
|
exception.details = &tracing::field::Empty,
|
|
|
|
);
|
|
|
|
cmd.spawn().map(|child| Process { child, span })
|
2021-08-31 02:19:47 +00:00
|
|
|
}
|
|
|
|
|
2021-09-04 00:53:53 +00:00
|
|
|
pub(crate) fn bytes_read(mut self, mut input: Bytes) -> Option<impl AsyncRead + Unpin> {
|
|
|
|
let mut stdin = self.child.stdin.take()?;
|
|
|
|
let stdout = self.child.stdout.take()?;
|
|
|
|
|
2021-09-14 01:22:42 +00:00
|
|
|
let (tx, rx) = channel();
|
2021-09-04 00:53:53 +00:00
|
|
|
|
2021-09-25 20:23:05 +00:00
|
|
|
let span = self.spawn_span();
|
2021-09-09 19:16:12 +00:00
|
|
|
let mut child = self.child;
|
2021-09-25 20:23:05 +00:00
|
|
|
let handle = actix_rt::spawn(
|
|
|
|
async move {
|
|
|
|
if let Err(e) = stdin.write_all_buf(&mut input).await {
|
2021-09-09 19:16:12 +00:00
|
|
|
let _ = tx.send(e);
|
2021-09-25 20:23:05 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
drop(stdin);
|
|
|
|
|
|
|
|
match child.wait().await {
|
|
|
|
Ok(status) => {
|
|
|
|
if !status.success() {
|
|
|
|
let _ = tx
|
|
|
|
.send(std::io::Error::new(std::io::ErrorKind::Other, &StatusError));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
let _ = tx.send(e);
|
|
|
|
}
|
2021-09-09 19:16:12 +00:00
|
|
|
}
|
2021-09-04 00:53:53 +00:00
|
|
|
}
|
2021-09-25 20:23:05 +00:00
|
|
|
.instrument(span),
|
|
|
|
);
|
2021-09-04 00:53:53 +00:00
|
|
|
|
|
|
|
Some(Box::pin(ProcessRead {
|
|
|
|
inner: stdout,
|
2021-09-25 20:23:05 +00:00
|
|
|
span: self.span,
|
2021-09-04 00:53:53 +00:00
|
|
|
err_recv: rx,
|
|
|
|
err_closed: false,
|
2021-09-11 21:35:38 +00:00
|
|
|
handle,
|
2021-09-04 00:53:53 +00:00
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
2021-10-14 00:06:53 +00:00
|
|
|
pub(crate) fn file_read(
|
2021-09-04 00:53:53 +00:00
|
|
|
mut self,
|
2021-10-14 00:06:53 +00:00
|
|
|
mut input_file: crate::file::File,
|
2021-09-04 00:53:53 +00:00
|
|
|
) -> Option<impl AsyncRead + Unpin> {
|
|
|
|
let mut stdin = self.child.stdin.take()?;
|
|
|
|
let stdout = self.child.stdout.take()?;
|
|
|
|
|
2021-09-14 01:22:42 +00:00
|
|
|
let (tx, rx) = channel();
|
2021-09-04 00:53:53 +00:00
|
|
|
|
2021-09-25 20:23:05 +00:00
|
|
|
let span = self.spawn_span();
|
2021-09-09 19:16:12 +00:00
|
|
|
let mut child = self.child;
|
2021-09-25 20:23:05 +00:00
|
|
|
let handle = actix_rt::spawn(
|
|
|
|
async move {
|
2021-10-14 00:06:53 +00:00
|
|
|
if let Err(e) = input_file.read_to_async_write(&mut stdin).await {
|
2021-09-09 19:16:12 +00:00
|
|
|
let _ = tx.send(e);
|
2021-09-25 20:23:05 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
drop(stdin);
|
|
|
|
|
|
|
|
match child.wait().await {
|
|
|
|
Ok(status) => {
|
|
|
|
if !status.success() {
|
|
|
|
let _ = tx
|
|
|
|
.send(std::io::Error::new(std::io::ErrorKind::Other, &StatusError));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
let _ = tx.send(e);
|
|
|
|
}
|
2021-09-09 19:16:12 +00:00
|
|
|
}
|
2021-09-04 00:53:53 +00:00
|
|
|
}
|
2021-09-25 20:23:05 +00:00
|
|
|
.instrument(span),
|
|
|
|
);
|
2021-09-04 00:53:53 +00:00
|
|
|
|
|
|
|
Some(Box::pin(ProcessRead {
|
|
|
|
inner: stdout,
|
2021-09-25 20:23:05 +00:00
|
|
|
span: self.span,
|
2021-09-04 00:53:53 +00:00
|
|
|
err_recv: rx,
|
|
|
|
err_closed: false,
|
2021-09-11 21:35:38 +00:00
|
|
|
handle,
|
2021-09-04 00:53:53 +00:00
|
|
|
}))
|
|
|
|
}
|
2021-08-31 02:19:47 +00:00
|
|
|
}
|
|
|
|
|
2021-09-04 00:53:53 +00:00
|
|
|
impl<I> AsyncRead for ProcessRead<I>
|
|
|
|
where
|
|
|
|
I: AsyncRead + Unpin,
|
|
|
|
{
|
|
|
|
fn poll_read(
|
|
|
|
mut self: Pin<&mut Self>,
|
|
|
|
cx: &mut Context<'_>,
|
|
|
|
buf: &mut ReadBuf<'_>,
|
|
|
|
) -> Poll<std::io::Result<()>> {
|
2021-09-25 20:23:05 +00:00
|
|
|
let span = self.span.clone();
|
|
|
|
span.in_scope(|| {
|
|
|
|
if !self.err_closed {
|
|
|
|
if let Poll::Ready(res) = Pin::new(&mut self.err_recv).poll(cx) {
|
|
|
|
self.err_closed = true;
|
|
|
|
if let Ok(err) = res {
|
|
|
|
let display = format!("{}", err);
|
|
|
|
let debug = format!("{:?}", err);
|
|
|
|
span.record("exception.message", &display.as_str());
|
|
|
|
span.record("exception.details", &debug.as_str());
|
|
|
|
return Poll::Ready(Err(err));
|
|
|
|
}
|
2021-09-04 00:53:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-25 20:23:05 +00:00
|
|
|
if let Poll::Ready(res) = Pin::new(&mut self.inner).poll_read(cx, buf) {
|
|
|
|
if let Err(err) = &res {
|
|
|
|
let display = format!("{}", err);
|
|
|
|
let debug = format!("{:?}", err);
|
|
|
|
span.record("exception.message", &display.as_str());
|
|
|
|
span.record("exception.details", &debug.as_str());
|
|
|
|
}
|
|
|
|
return Poll::Ready(res);
|
|
|
|
}
|
2021-09-04 00:53:53 +00:00
|
|
|
|
2021-09-25 20:23:05 +00:00
|
|
|
Poll::Pending
|
|
|
|
})
|
2021-09-04 00:53:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-11 21:35:38 +00:00
|
|
|
impl<I> Drop for ProcessRead<I> {
|
|
|
|
fn drop(&mut self) {
|
|
|
|
self.handle.abort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-09 19:16:12 +00:00
|
|
|
impl std::fmt::Display for StatusError {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
|
|
|
write!(f, "Command failed with bad status")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::error::Error for StatusError {}
|