shrub_core/sys/unix/
commands.rs1pub use std::os::unix::process::{CommandExt, ExitStatusExt};
4
5use command_fds::{CommandFdExt, FdMapping};
6
7use crate::{ShellFd, error, openfiles};
8
9pub trait CommandFdInjectionExt {
11 fn inject_fds(
24 &mut self,
25 open_files: impl Iterator<Item = (ShellFd, openfiles::OpenFile)>,
26 ) -> Result<(), error::Error>;
27}
28
29impl CommandFdInjectionExt for std::process::Command {
30 fn inject_fds(
31 &mut self,
32 open_files: impl Iterator<Item = (ShellFd, openfiles::OpenFile)>,
33 ) -> Result<(), error::Error> {
34 let fd_mappings: Vec<FdMapping> = open_files
35 .map(|(child_fd, open_file)| -> Result<FdMapping, error::Error> {
36 let parent_fd = open_file.try_clone_to_owned()?;
37 Ok(FdMapping {
38 child_fd,
39 parent_fd,
40 })
41 })
42 .collect::<Result<Vec<_>, _>>()?;
43
44 self.fd_mappings(fd_mappings)
45 .map_err(|_e| error::ErrorKind::ChildCreationFailure)?;
46
47 Ok(())
48 }
49}
50
51pub trait CommandFgControlExt {
53 fn take_foreground(&mut self);
55 fn lead_session(&mut self);
57}
58
59impl CommandFgControlExt for std::process::Command {
60 fn take_foreground(&mut self) {
61 unsafe {
66 self.pre_exec(pre_exec_take_foreground);
67 }
68 }
69
70 fn lead_session(&mut self) {
71 unsafe {
76 self.pre_exec(pre_exec_lead_session);
77 }
78 }
79}
80
81fn pre_exec_take_foreground() -> Result<(), std::io::Error> {
82 use crate::sys;
83
84 sys::terminal::move_self_to_foreground()?;
85 Ok(())
86}
87
88fn pre_exec_lead_session() -> Result<(), std::io::Error> {
89 if let Err(e) = nix::unistd::setsid() {
90 return Err(std::io::Error::other(format!(
91 "failed to become session leader: {e}"
92 )));
93 }
94
95 #[cfg(not(target_os = "macos"))]
96 let control = libc::TIOCSCTTY;
97 #[cfg(target_os = "macos")]
98 let control: u64 = libc::TIOCSCTTY.into();
99
100 let result = unsafe { libc::ioctl(0, control, 0) };
103 if result != 0 {
104 return Err(std::io::Error::other("failed to set controlling terminal"));
105 }
106
107 Ok(())
108}