use std::ffi::OsString;
use std::io::Write as _;
use std::process::ExitCode;
use secrecy::SecretString;
use prick_exec::{EnvGuard, LaunchSpec};
const SEPARATOR: u8 = 0;
fn main() -> ExitCode {
match dispatch() {
Ok(code) => ExitCode::from(u8::try_from(code).unwrap_or(1)),
Err(message) => {
let mut stderr = std::io::stderr().lock();
let _ = writeln!(stderr, "prick-exec-child: {message}");
ExitCode::from(2)
}
}
}
fn dispatch() -> Result<i32, String> {
let mut secrets: Vec<(String, SecretString)> = Vec::new();
let mut guard = EnvGuard::strict();
let mut argv: Vec<OsString> = Vec::new();
let mut remaining = std::env::args_os().skip(1);
while let Some(arg) = remaining.next() {
match arg.to_str() {
Some("--set") => {
let pair = remaining.next().ok_or("--set needs KEY=VALUE")?;
let pair = pair.to_string_lossy().into_owned();
let (key, value) = pair.split_once('=').ok_or("--set needs KEY=VALUE")?;
secrets.push((key.to_owned(), SecretString::from(value)));
}
Some("--allow-unsafe-env") => guard = EnvGuard::permissive(),
Some("--print-argv") => return print_argv(remaining),
Some("--print-env") => {
let name = remaining.next().ok_or("--print-env needs NAME")?;
return print_env(&name);
}
Some("--exit-code") => {
let code = remaining.next().ok_or("--exit-code needs N")?;
return code.to_string_lossy().parse::<i32>().map_err(|err| err.to_string());
}
Some("--") => {
argv.extend(remaining);
break;
}
_ => {
argv.push(arg);
argv.extend(remaining);
break;
}
}
}
let spec = LaunchSpec::new(argv)
.and_then(|spec| spec.with_secrets(guard, secrets))
.map_err(|err| err.to_string())?;
match prick_exec::run(&spec) {
Err(err) => {
let mut stderr = std::io::stderr().lock();
let _ = writeln!(stderr, "prick-exec-child: {err}");
Ok(err.exit_code())
}
}
}
fn print_argv(args: impl Iterator<Item = OsString>) -> Result<i32, String> {
let mut stdout = std::io::stdout().lock();
for (index, arg) in args.enumerate() {
if index > 0 {
stdout.write_all(&[SEPARATOR]).map_err(|err| err.to_string())?;
}
stdout.write_all(&os_bytes(&arg)).map_err(|err| err.to_string())?;
}
stdout.flush().map_err(|err| err.to_string())?;
Ok(0)
}
fn print_env(name: &OsString) -> Result<i32, String> {
let value = std::env::var_os(name).unwrap_or_default();
let mut stdout = std::io::stdout().lock();
stdout.write_all(&os_bytes(&value)).map_err(|err| err.to_string())?;
stdout.flush().map_err(|err| err.to_string())?;
Ok(0)
}
#[cfg(unix)]
fn os_bytes(value: &OsString) -> Vec<u8> {
use std::os::unix::ffi::OsStrExt as _;
value.as_os_str().as_bytes().to_vec()
}
#[cfg(not(unix))]
fn os_bytes(value: &OsString) -> Vec<u8> {
value.to_string_lossy().into_owned().into_bytes()
}