2021-12-07 02:35:22 +00:00
|
|
|
use crate::store::Store;
|
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,
|
2023-07-10 20:29:41 +00:00
|
|
|
process::{ExitStatus, 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},
|
2022-09-26 01:39:09 +00:00
|
|
|
process::{Child, ChildStdin, Command},
|
2021-09-14 01:22:42 +00:00
|
|
|
sync::oneshot::{channel, Receiver},
|
|
|
|
};
|
2022-04-07 18:28:28 +00:00
|
|
|
use tracing::{Instrument, Span};
|
2021-09-04 00:53:53 +00:00
|
|
|
|
2021-09-09 19:16:12 +00:00
|
|
|
#[derive(Debug)]
|
2023-07-11 22:52:46 +00:00
|
|
|
struct StatusError(ExitStatus);
|
2021-09-09 19:16:12 +00:00
|
|
|
|
2021-08-31 02:19:47 +00:00
|
|
|
pub(crate) struct Process {
|
2021-09-14 01:22:42 +00:00
|
|
|
child: Child,
|
2022-04-07 02:40:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl std::fmt::Debug for Process {
|
|
|
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
|
|
f.debug_struct("Process").field("child", &"Child").finish()
|
|
|
|
}
|
2021-08-31 02:19:47 +00:00
|
|
|
}
|
|
|
|
|
2021-10-20 23:58:32 +00:00
|
|
|
struct DropHandle {
|
|
|
|
inner: JoinHandle<()>,
|
|
|
|
}
|
|
|
|
|
|
|
|
pin_project_lite::pin_project! {
|
|
|
|
struct ProcessRead<I> {
|
|
|
|
#[pin]
|
|
|
|
inner: I,
|
|
|
|
err_recv: Receiver<std::io::Error>,
|
|
|
|
err_closed: bool,
|
|
|
|
handle: DropHandle,
|
2023-07-11 22:52:46 +00:00
|
|
|
eof: bool,
|
2021-10-20 23:58:32 +00:00
|
|
|
}
|
2021-09-04 00:53:53 +00:00
|
|
|
}
|
|
|
|
|
2023-07-10 20:29:41 +00:00
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
|
|
pub(crate) enum ProcessError {
|
2023-07-10 22:15:43 +00:00
|
|
|
#[error("Required command {0} not found")]
|
2023-07-10 20:29:41 +00:00
|
|
|
NotFound(String),
|
|
|
|
|
|
|
|
#[error("Reached process spawn limit")]
|
|
|
|
LimitReached,
|
|
|
|
|
|
|
|
#[error("Failed with status {0}")]
|
|
|
|
Status(ExitStatus),
|
|
|
|
|
|
|
|
#[error("Unknown process error")]
|
|
|
|
Other(#[source] std::io::Error),
|
|
|
|
}
|
|
|
|
|
2021-08-31 02:19:47 +00:00
|
|
|
impl Process {
|
2023-07-10 20:29:41 +00:00
|
|
|
pub(crate) fn run(command: &str, args: &[&str]) -> Result<Self, ProcessError> {
|
|
|
|
let res = tracing::trace_span!(parent: None, "Create command")
|
|
|
|
.in_scope(|| Self::spawn(Command::new(command).args(args)));
|
|
|
|
|
|
|
|
match res {
|
|
|
|
Ok(this) => Ok(this),
|
|
|
|
Err(e) => match e.kind() {
|
|
|
|
std::io::ErrorKind::NotFound => Err(ProcessError::NotFound(command.to_string())),
|
|
|
|
std::io::ErrorKind::WouldBlock => Err(ProcessError::LimitReached),
|
|
|
|
_ => Err(ProcessError::Other(e)),
|
|
|
|
},
|
|
|
|
}
|
2021-09-14 01:22:42 +00:00
|
|
|
}
|
|
|
|
|
2023-07-10 20:29:41 +00:00
|
|
|
fn spawn(cmd: &mut Command) -> std::io::Result<Self> {
|
2022-04-07 17:56:40 +00:00
|
|
|
tracing::trace_span!(parent: None, "Spawn command").in_scope(|| {
|
2023-07-10 20:29:41 +00:00
|
|
|
let cmd = cmd
|
|
|
|
.stdin(Stdio::piped())
|
|
|
|
.stdout(Stdio::piped())
|
|
|
|
.kill_on_drop(true);
|
2021-09-25 20:23:05 +00:00
|
|
|
|
2022-04-07 17:56:40 +00:00
|
|
|
cmd.spawn().map(|child| Process { child })
|
|
|
|
})
|
2021-08-31 02:19:47 +00:00
|
|
|
}
|
|
|
|
|
2022-10-02 03:47:52 +00:00
|
|
|
#[tracing::instrument(skip(self))]
|
2023-07-10 20:29:41 +00:00
|
|
|
pub(crate) async fn wait(mut self) -> Result<(), ProcessError> {
|
|
|
|
let res = self.child.wait().await;
|
|
|
|
|
|
|
|
match res {
|
|
|
|
Ok(status) if status.success() => Ok(()),
|
|
|
|
Ok(status) => Err(ProcessError::Status(status)),
|
|
|
|
Err(e) => Err(ProcessError::Other(e)),
|
2021-10-23 19:14:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-09-25 22:35:52 +00:00
|
|
|
pub(crate) fn bytes_read(self, input: Bytes) -> impl AsyncRead + Unpin {
|
2022-10-02 02:17:18 +00:00
|
|
|
self.spawn_fn(move |mut stdin| {
|
2022-09-25 22:35:52 +00:00
|
|
|
let mut input = input;
|
|
|
|
async move { stdin.write_all_buf(&mut input).await }
|
|
|
|
})
|
2021-09-04 00:53:53 +00:00
|
|
|
}
|
|
|
|
|
2022-09-25 22:35:52 +00:00
|
|
|
pub(crate) fn read(self) -> impl AsyncRead + Unpin {
|
2022-10-02 02:17:18 +00:00
|
|
|
self.spawn_fn(|_| async { Ok(()) })
|
2022-09-25 20:17:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn pipe_async_read<A: AsyncRead + Unpin + 'static>(
|
2022-09-25 22:35:52 +00:00
|
|
|
self,
|
2022-09-25 20:17:33 +00:00
|
|
|
mut async_read: A,
|
|
|
|
) -> impl AsyncRead + Unpin {
|
2022-10-02 02:17:18 +00:00
|
|
|
self.spawn_fn(move |mut stdin| async move {
|
2022-09-26 01:39:09 +00:00
|
|
|
tokio::io::copy(&mut async_read, &mut stdin)
|
|
|
|
.await
|
|
|
|
.map(|_| ())
|
|
|
|
})
|
2021-10-23 19:14:12 +00:00
|
|
|
}
|
|
|
|
|
2022-04-01 21:51:12 +00:00
|
|
|
pub(crate) fn store_read<S: Store + 'static>(
|
2022-09-25 22:35:52 +00:00
|
|
|
self,
|
2021-10-23 04:48:56 +00:00
|
|
|
store: S,
|
|
|
|
identifier: S::Identifier,
|
2022-04-07 02:40:49 +00:00
|
|
|
) -> impl AsyncRead + Unpin {
|
2022-10-02 02:17:18 +00:00
|
|
|
self.spawn_fn(move |mut stdin| {
|
2022-09-25 22:35:52 +00:00
|
|
|
let store = store;
|
|
|
|
let identifier = identifier;
|
|
|
|
|
|
|
|
async move { store.read_into(&identifier, &mut stdin).await }
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-06-01 22:33:43 +00:00
|
|
|
#[allow(unknown_lints)]
|
|
|
|
#[allow(clippy::let_with_type_underscore)]
|
2022-10-02 03:47:52 +00:00
|
|
|
#[tracing::instrument(level = "trace", skip_all)]
|
2022-10-02 02:17:18 +00:00
|
|
|
fn spawn_fn<F, Fut>(mut self, f: F) -> impl AsyncRead + Unpin
|
2022-09-25 22:35:52 +00:00
|
|
|
where
|
|
|
|
F: FnOnce(ChildStdin) -> Fut + 'static,
|
|
|
|
Fut: Future<Output = std::io::Result<()>>,
|
|
|
|
{
|
|
|
|
let stdin = self.child.stdin.take().expect("stdin exists");
|
2022-04-07 02:40:49 +00:00
|
|
|
let stdout = self.child.stdout.take().expect("stdout exists");
|
2021-09-04 00:53:53 +00:00
|
|
|
|
2022-09-25 22:35:52 +00:00
|
|
|
let (tx, rx) = tracing::trace_span!(parent: None, "Create channel")
|
|
|
|
.in_scope(channel::<std::io::Error>);
|
2021-09-04 00:53:53 +00:00
|
|
|
|
2022-09-25 22:35:52 +00:00
|
|
|
let span = tracing::info_span!(parent: None, "Background process task");
|
2022-04-07 18:28:28 +00:00
|
|
|
span.follows_from(Span::current());
|
|
|
|
|
2021-09-09 19:16:12 +00:00
|
|
|
let mut child = self.child;
|
2022-04-07 17:56:40 +00:00
|
|
|
let handle = tracing::trace_span!(parent: None, "Spawn task").in_scope(|| {
|
2022-04-07 18:28:28 +00:00
|
|
|
actix_rt::spawn(
|
|
|
|
async move {
|
2022-09-25 22:35:52 +00:00
|
|
|
if let Err(e) = (f)(stdin).await {
|
2022-04-07 17:56:40 +00:00
|
|
|
let _ = tx.send(e);
|
2022-04-07 18:28:28 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
match child.wait().await {
|
|
|
|
Ok(status) => {
|
|
|
|
if !status.success() {
|
|
|
|
let _ = tx.send(std::io::Error::new(
|
|
|
|
std::io::ErrorKind::Other,
|
2023-07-11 22:52:46 +00:00
|
|
|
StatusError(status),
|
2022-04-07 18:28:28 +00:00
|
|
|
));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Err(e) => {
|
|
|
|
let _ = tx.send(e);
|
|
|
|
}
|
2022-04-07 17:56:40 +00:00
|
|
|
}
|
|
|
|
}
|
2022-04-07 18:28:28 +00:00
|
|
|
.instrument(span),
|
|
|
|
)
|
2022-04-07 02:40:49 +00:00
|
|
|
});
|
2021-09-04 00:53:53 +00:00
|
|
|
|
2022-04-07 02:40:49 +00:00
|
|
|
ProcessRead {
|
2021-09-04 00:53:53 +00:00
|
|
|
inner: stdout,
|
|
|
|
err_recv: rx,
|
|
|
|
err_closed: false,
|
2021-10-20 23:58:32 +00:00
|
|
|
handle: DropHandle { inner: handle },
|
2023-07-11 22:52:46 +00:00
|
|
|
eof: false,
|
2022-04-07 02:40:49 +00:00
|
|
|
}
|
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
|
2021-10-20 23:58:32 +00:00
|
|
|
I: AsyncRead,
|
2021-09-04 00:53:53 +00:00
|
|
|
{
|
|
|
|
fn poll_read(
|
|
|
|
mut self: Pin<&mut Self>,
|
|
|
|
cx: &mut Context<'_>,
|
|
|
|
buf: &mut ReadBuf<'_>,
|
|
|
|
) -> Poll<std::io::Result<()>> {
|
2021-10-20 23:58:32 +00:00
|
|
|
let this = self.as_mut().project();
|
|
|
|
|
|
|
|
let err_recv = this.err_recv;
|
|
|
|
let err_closed = this.err_closed;
|
2023-07-11 22:52:46 +00:00
|
|
|
let eof = this.eof;
|
2021-10-20 23:58:32 +00:00
|
|
|
let inner = this.inner;
|
2021-12-06 18:43:03 +00:00
|
|
|
|
2022-04-07 02:40:49 +00:00
|
|
|
if !*err_closed {
|
|
|
|
if let Poll::Ready(res) = Pin::new(err_recv).poll(cx) {
|
|
|
|
*err_closed = true;
|
2023-07-11 22:52:46 +00:00
|
|
|
|
2022-04-07 02:40:49 +00:00
|
|
|
if let Ok(err) = res {
|
|
|
|
return Poll::Ready(Err(err));
|
2021-09-04 00:53:53 +00:00
|
|
|
}
|
2023-07-11 22:52:46 +00:00
|
|
|
|
|
|
|
if *eof {
|
|
|
|
return Poll::Ready(Ok(()));
|
|
|
|
}
|
2021-09-04 00:53:53 +00:00
|
|
|
}
|
2022-04-07 02:40:49 +00:00
|
|
|
}
|
2021-09-04 00:53:53 +00:00
|
|
|
|
2023-07-11 22:52:46 +00:00
|
|
|
if !*eof {
|
|
|
|
let before_size = buf.filled().len();
|
|
|
|
|
|
|
|
return match inner.poll_read(cx, buf) {
|
|
|
|
Poll::Ready(Ok(())) => {
|
|
|
|
if buf.filled().len() == before_size {
|
|
|
|
*eof = true;
|
|
|
|
|
|
|
|
if !*err_closed {
|
|
|
|
// reached end of stream & haven't received process signal
|
|
|
|
return Poll::Pending;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Poll::Ready(Ok(()))
|
|
|
|
}
|
|
|
|
Poll::Ready(Err(e)) => {
|
|
|
|
*eof = true;
|
|
|
|
|
|
|
|
Poll::Ready(Err(e))
|
|
|
|
}
|
|
|
|
Poll::Pending => Poll::Pending,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
if *err_closed && *eof {
|
|
|
|
return Poll::Ready(Ok(()));
|
|
|
|
}
|
|
|
|
|
|
|
|
Poll::Pending
|
2021-09-04 00:53:53 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-10-20 23:58:32 +00:00
|
|
|
impl Drop for DropHandle {
|
2021-09-11 21:35:38 +00:00
|
|
|
fn drop(&mut self) {
|
2021-10-20 23:58:32 +00:00
|
|
|
self.inner.abort();
|
2021-09-11 21:35:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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 {
|
2023-07-11 22:52:46 +00:00
|
|
|
write!(f, "Command failed with bad status: {}", self.0)
|
2021-09-09 19:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl std::error::Error for StatusError {}
|