ic_host_process/child/gate/
mod.rs1#![expect(
3 unsafe_code,
4 reason = "reviewed async-signal-safe post-fork descriptor operations imported from Profiler"
5)]
6use rustix::io::{FdFlags, fcntl_setfd};
7use std::{
8 ffi::{OsStr, OsString},
9 fs::File,
10 io,
11 os::{fd::AsRawFd, unix::process::CommandExt},
12 process::Command,
13};
14
15#[derive(Debug)]
17pub enum LaunchGateError {
18 Pipe(io::Error),
20 Descriptor(io::Error),
22}
23impl std::fmt::Display for LaunchGateError {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 match self {
26 Self::Pipe(e) | Self::Descriptor(e) => e.fmt(f),
27 }
28 }
29}
30impl std::error::Error for LaunchGateError {
31 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
32 match self {
33 Self::Pipe(e) | Self::Descriptor(e) => Some(e),
34 }
35 }
36}
37
38pub fn gated_command(
46 program: &OsStr,
47 arguments: &[OsString],
48 launcher_name: &OsStr,
49) -> Result<(Command, File), LaunchGateError> {
50 let (read, write) =
51 rustix::pipe::pipe().map_err(|source| LaunchGateError::Pipe(source.into()))?;
52 for fd in [&read, &write] {
53 fcntl_setfd(fd, FdFlags::CLOEXEC)
54 .map_err(|source| LaunchGateError::Descriptor(source.into()))?;
55 }
56 let write_fd = write.as_raw_fd();
57 let mut command = Command::new("/bin/sh");
58 command
59 .args(["-c", "read -r _ <&3 || exit 125; exec 3<&-; exec \"$@\""])
60 .arg(launcher_name)
61 .arg(program)
62 .args(arguments);
63 unsafe {
66 command.pre_exec(move || {
67 let source = read.as_raw_fd();
68 if libc::dup2(source, 3) < 0 || libc::fcntl(3, libc::F_SETFD, 0) < 0 {
69 return Err(std::io::Error::last_os_error());
70 }
71 if source != 3 {
72 libc::close(source);
73 }
74 if write_fd != 3 {
75 libc::close(write_fd);
76 }
77 Ok(())
78 });
79 }
80 Ok((command, std::fs::File::from(write)))
81}