Skip to main content

brush_core/sys/unix/
commands.rs

1//! Command execution utilities.
2
3pub 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
12/// Extension trait for injecting file descriptors into commands.
13pub trait CommandFdInjectionExt {
14    /// Injects the given open files as file descriptors into the command.
15    ///
16    /// # Arguments
17    ///
18    /// * `open_files` - A mapping of child file descriptors to open files.
19    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
47/// Extension trait for arranging for commands to take the foreground.
48pub trait CommandFgControlExt {
49    /// Arranges for the command to take the foreground when it is executed.
50    fn take_foreground(&mut self);
51    /// Arranges for the command to become a session leader when it is executed.
52    fn lead_session(&mut self);
53}
54
55impl CommandFgControlExt for std::process::Command {
56    fn take_foreground(&mut self) {
57        // SAFETY:
58        // This arranges for a provided function to run in the context of
59        // the forked process before it exec's the target command. In general,
60        // rust can't guarantee safety of code running in such a context.
61        unsafe {
62            self.pre_exec(pre_exec_take_foreground);
63        }
64    }
65
66    fn lead_session(&mut self) {
67        // SAFETY:
68        // This arranges for a provided function to run in the context of
69        // the forked process before it exec's the target command. In general,
70        // rust can't guarantee safety of code running in such a context.
71        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    // SAFETY:
97    // This is calling a libc function to set the controlling terminal.
98    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}