use std::io::Write;
#[cfg(unix)]
use std::os::unix::process::CommandExt;
use std::process::{Command, Stdio};
use crate::error::Error;
use crate::vault::Vault;
use super::context::prepare_execution;
pub fn pipe(secret_name: &str, command: &[String]) -> Result<(), Error> {
pipe_with_stdio_isolation(secret_name, command, false)
}
pub fn pipe_with_stdio_isolation(
secret_name: &str,
command: &[String],
isolate_stdio: bool,
) -> Result<(), Error> {
crate::guard::check_self_preload()?;
crate::guard::harden_process();
let vault = Vault::open_default()?;
let mappings = [(secret_name, "STDIN_PIPE")];
let prepared = prepare_execution(&vault, &mappings, command)?;
let secret_value = prepared
.env_pairs
.first()
.map(|(_, v)| zeroize::Zeroizing::new(v.as_bytes().to_vec()))
.unwrap_or_default();
let mut cmd = Command::new(&prepared.exec_path);
#[cfg(unix)]
cmd.arg0(&prepared.binary_path);
cmd.args(&prepared.args);
cmd.env_clear();
for (k, v) in &prepared.clean_env {
cmd.env(k, v);
}
cmd.stdin(Stdio::piped());
if isolate_stdio {
cmd.stdout(Stdio::null()).stderr(Stdio::null());
}
let mut child = cmd.spawn().map_err(Error::ExecFailed)?;
if let Some(ref mut stdin) = child.stdin {
stdin.write_all(&secret_value).map_err(Error::StorageIo)?;
}
drop(child.stdin.take());
let status = child.wait().map_err(Error::ExecFailed)?;
if !status.success() {
return Err(Error::ExecFailed(std::io::Error::other(format!(
"piped process exited with status {status}"
))));
}
Ok(())
}