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(
17 &mut self,
18 open_files: impl Iterator<Item = (ShellFd, openfiles::OpenFile)>,
19 ) -> Result<(), error::Error>;
20}
21
22impl CommandFdInjectionExt for std::process::Command {
23 fn inject_fds(
24 &mut self,
25 open_files: impl Iterator<Item = (ShellFd, openfiles::OpenFile)>,
26 ) -> Result<(), error::Error> {
27 let fd_mappings: Vec<FdMapping> = open_files
28 .map(|(child_fd, open_file)| -> Result<FdMapping, error::Error> {
29 let parent_fd = open_file.try_clone_to_owned()?;
30 Ok(FdMapping {
31 child_fd,
32 parent_fd,
33 })
34 })
35 .collect::<Result<Vec<_>, _>>()?;
36
37 self.fd_mappings(fd_mappings)
38 .map_err(|_e| error::ErrorKind::ChildCreationFailure)?;
39
40 Ok(())
41 }
42}
43
44pub trait CommandFgControlExt {
46 fn take_foreground(&mut self);
48 fn lead_session(&mut self);
50}
51
52impl CommandFgControlExt for std::process::Command {
53 fn take_foreground(&mut self) {
54 unsafe {
59 self.pre_exec(pre_exec_take_foreground);
60 }
61 }
62
63 fn lead_session(&mut self) {
64 unsafe {
69 self.pre_exec(pre_exec_lead_session);
70 }
71 }
72}
73
74fn pre_exec_take_foreground() -> Result<(), std::io::Error> {
75 use crate::sys;
76
77 sys::terminal::move_self_to_foreground()?;
78 Ok(())
79}
80
81fn pre_exec_lead_session() -> Result<(), std::io::Error> {
82 if let Err(e) = nix::unistd::setsid() {
83 return Err(std::io::Error::other(format!(
84 "failed to become session leader: {e}"
85 )));
86 }
87
88 #[cfg(not(target_os = "macos"))]
89 let control = libc::TIOCSCTTY;
90 #[cfg(target_os = "macos")]
91 let control: u64 = libc::TIOCSCTTY.into();
92
93 let result = unsafe { libc::ioctl(0, control, 0) };
96 if result != 0 {
97 return Err(std::io::Error::other("failed to set controlling terminal"));
98 }
99
100 Ok(())
101}