#![allow(
unsafe_code,
reason = "reviewed async-signal-safe post-fork descriptor operations imported from Profiler"
)]
use rustix::io::{FdFlags, fcntl_setfd};
use std::{
ffi::{OsStr, OsString},
fs::File,
io,
os::{fd::AsRawFd, unix::process::CommandExt},
process::Command,
};
#[derive(Debug)]
pub enum LaunchGateError {
Pipe(io::Error),
Descriptor(io::Error),
}
impl std::fmt::Display for LaunchGateError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Pipe(e) | Self::Descriptor(e) => e.fmt(f),
}
}
}
impl std::error::Error for LaunchGateError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Pipe(e) | Self::Descriptor(e) => Some(e),
}
}
}
pub fn gated_command(
program: &OsStr,
arguments: &[OsString],
launcher_name: &OsStr,
) -> Result<(Command, File), LaunchGateError> {
let (read, write) =
rustix::pipe::pipe().map_err(|source| LaunchGateError::Pipe(source.into()))?;
for fd in [&read, &write] {
fcntl_setfd(fd, FdFlags::CLOEXEC)
.map_err(|source| LaunchGateError::Descriptor(source.into()))?;
}
let write_fd = write.as_raw_fd();
let mut command = Command::new("/bin/sh");
command
.args(["-c", "read -r _ <&3 || exit 125; exec 3<&-; exec \"$@\""])
.arg(launcher_name)
.arg(program)
.args(arguments);
unsafe {
command.pre_exec(move || {
let source = read.as_raw_fd();
if libc::dup2(source, 3) < 0 || libc::fcntl(3, libc::F_SETFD, 0) < 0 {
return Err(std::io::Error::last_os_error());
}
if source != 3 {
libc::close(source);
}
if write_fd != 3 {
libc::close(write_fd);
}
Ok(())
});
}
Ok((command, std::fs::File::from(write)))
}