pub use std::os::unix::process::CommandExt;
pub use std::os::unix::process::ExitStatusExt;
use command_fds::{CommandFdExt, FdMapping};
use crate::ShellFd;
use crate::error;
use crate::openfiles;
pub trait CommandFdInjectionExt {
fn inject_fds(
&mut self,
open_files: impl Iterator<Item = (ShellFd, openfiles::OpenFile)>,
) -> Result<(), error::Error>;
}
impl CommandFdInjectionExt for std::process::Command {
fn inject_fds(
&mut self,
open_files: impl Iterator<Item = (ShellFd, openfiles::OpenFile)>,
) -> Result<(), error::Error> {
let fd_mappings: Vec<FdMapping> = open_files
.map(|(child_fd, open_file)| -> Result<FdMapping, error::Error> {
let parent_fd = open_file.try_clone_to_owned()?;
Ok(FdMapping {
child_fd,
parent_fd,
})
})
.collect::<Result<Vec<_>, _>>()?;
self.fd_mappings(fd_mappings)
.map_err(|_e| error::ErrorKind::ChildCreationFailure)?;
Ok(())
}
}
pub trait CommandFgControlExt {
fn take_foreground(&mut self);
fn lead_session(&mut self);
}
impl CommandFgControlExt for std::process::Command {
fn take_foreground(&mut self) {
unsafe {
self.pre_exec(pre_exec_take_foreground);
}
}
fn lead_session(&mut self) {
unsafe {
self.pre_exec(pre_exec_lead_session);
}
}
}
fn pre_exec_take_foreground() -> Result<(), std::io::Error> {
use crate::sys;
sys::terminal::move_self_to_foreground()?;
Ok(())
}
fn pre_exec_lead_session() -> Result<(), std::io::Error> {
if let Err(e) = nix::unistd::setsid() {
return Err(std::io::Error::other(format!(
"failed to become session leader: {e}"
)));
}
#[cfg(not(target_os = "macos"))]
let control = libc::TIOCSCTTY;
#[cfg(target_os = "macos")]
let control: u64 = libc::TIOCSCTTY.into();
let result = unsafe { libc::ioctl(0, control, 0) };
if result != 0 {
return Err(std::io::Error::other("failed to set controlling terminal"));
}
Ok(())
}