55 lines
1.4 KiB
Rust
55 lines
1.4 KiB
Rust
use std::process::Stdio;
|
|
|
|
use tokio::{
|
|
io::{AsyncRead, AsyncReadExt},
|
|
process::Command,
|
|
};
|
|
|
|
pub async fn execute(command: &str, max_output_bytes: u64) -> (i32, String, String) {
|
|
let mut child = match Command::new("sh")
|
|
.arg("-c")
|
|
.arg(command)
|
|
.stdout(Stdio::piped())
|
|
.stderr(Stdio::piped())
|
|
.spawn()
|
|
{
|
|
Ok(child) => child,
|
|
Err(error) => return (-1, String::new(), error.to_string()),
|
|
};
|
|
|
|
let Some(stdout) = child.stdout.take() else {
|
|
return (-1, String::new(), "stdout pipe unavailable".to_string());
|
|
};
|
|
let Some(stderr) = child.stderr.take() else {
|
|
return (-1, String::new(), "stderr pipe unavailable".to_string());
|
|
};
|
|
|
|
let (stdout_data, stderr_data) = tokio::join!(
|
|
read_limited_output(stdout, max_output_bytes),
|
|
read_limited_output(stderr, max_output_bytes),
|
|
);
|
|
|
|
child.kill().await.ok();
|
|
let exit_code = child
|
|
.wait()
|
|
.await
|
|
.ok()
|
|
.and_then(|status| status.code())
|
|
.unwrap_or(-1);
|
|
|
|
(
|
|
exit_code,
|
|
String::from_utf8_lossy(&stdout_data).into_owned(),
|
|
String::from_utf8_lossy(&stderr_data).into_owned(),
|
|
)
|
|
}
|
|
|
|
async fn read_limited_output<R>(reader: R, max_output_bytes: u64) -> Vec<u8>
|
|
where
|
|
R: AsyncRead + Unpin,
|
|
{
|
|
let mut buffer = Vec::new();
|
|
let _ = reader.take(max_output_bytes).read_to_end(&mut buffer).await;
|
|
buffer
|
|
}
|