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