use anyhow;
use std::ffi::{OsStr, OsString};
use std::os::unix::ffi::OsStrExt;
use std::os::unix::process::ExitStatusExt;
use std::process::{Command, ExitStatus, Stdio};
use std::time::Duration;
use tracing::*;
use wait_timeout::ChildExt;
pub fn exec_job<T: Into<Stdio>>(
command: &OsString,
params: &[OsString],
env: Vec<(Vec<u8>, Vec<u8>)>,
payload: Option<T>,
stdoutput: Stdio,
stderr: Stdio,
timeoutsecs: Option<u64>,
) -> Result<ExitStatus, anyhow::Error> {
debug!("Preparing to run {:?} with params {:?}", command, params);
let mut command = Command::new(command);
command
.args(params)
.envs(
env.iter()
.map(|(k, v)| (OsStr::from_bytes(k), OsStr::from_bytes(v))),
)
.stdout(stdoutput)
.stderr(stderr);
if let Some(payload) = payload {
command.stdin(payload);
}
let mut child = command.spawn()?;
debug!("Command PID {} started successfully", child.id());
std::mem::drop(command);
let exitstatus = if let Some(timeout) = timeoutsecs {
debug!("Waiting up to {} seconds for command to exit", timeout);
match child.wait_timeout(Duration::from_secs(timeout))? {
None => {
debug!("Command timed out; sending termination signal");
child.kill()?;
std::thread::sleep(Duration::from_millis(500));
match child.try_wait()? {
Some(r) => {
debug!("Command terminated after termination signal");
r
}
None => {
debug!("Command didn't terminate even after signal; proceeding with an error report anyway");
ExitStatus::from_raw(0x7f) }
}
}
Some(r) => r, }
} else {
debug!("Waiting indefinitely for command to exit");
child.wait()?
};
if exitstatus.success() {
debug!("Command exited successfully with status {:?}", exitstatus);
} else {
error!("Command exited abnormally with status {:?}", exitstatus);
}
Ok(exitstatus)
}