shrub_core/sys/unix/
commands.rs1use std::ffi::{CStr, CString};
4pub use std::os::unix::process::{CommandExt, ExitStatusExt};
5use std::sync::Arc;
6
7use command_fds::{CommandFdExt, FdMapping};
8
9use crate::{ShellFd, error, openfiles};
10
11pub trait CommandFdInjectionExt {
13 fn inject_fds(
26 &mut self,
27 open_files: impl Iterator<Item = (ShellFd, openfiles::OpenFile)>,
28 ) -> Result<(), error::Error>;
29}
30
31impl CommandFdInjectionExt for std::process::Command {
32 fn inject_fds(
33 &mut self,
34 open_files: impl Iterator<Item = (ShellFd, openfiles::OpenFile)>,
35 ) -> Result<(), error::Error> {
36 let fd_mappings: Vec<FdMapping> = open_files
37 .map(|(child_fd, open_file)| -> Result<FdMapping, error::Error> {
38 let parent_fd = open_file.try_clone_to_owned()?;
39 Ok(FdMapping {
40 child_fd,
41 parent_fd,
42 })
43 })
44 .collect::<Result<Vec<_>, _>>()?;
45
46 self.fd_mappings(fd_mappings)
47 .map_err(|_e| error::ErrorKind::ChildCreationFailure)?;
48
49 Ok(())
50 }
51}
52
53pub trait CommandFgControlExt {
55 fn take_foreground(&mut self);
57 fn lead_session(&mut self);
59}
60
61impl CommandFgControlExt for std::process::Command {
62 fn take_foreground(&mut self) {
63 unsafe {
68 self.pre_exec(pre_exec_take_foreground);
69 }
70 }
71
72 fn lead_session(&mut self) {
73 unsafe {
78 self.pre_exec(pre_exec_lead_session);
79 }
80 }
81}
82
83fn pre_exec_take_foreground() -> Result<(), std::io::Error> {
84 use crate::sys;
85
86 sys::terminal::move_self_to_foreground()?;
87 Ok(())
88}
89
90fn pre_exec_lead_session() -> Result<(), std::io::Error> {
91 if let Err(e) = nix::unistd::setsid() {
92 return Err(std::io::Error::other(format!(
93 "failed to become session leader: {e}"
94 )));
95 }
96
97 #[cfg(not(target_os = "macos"))]
98 let control = libc::TIOCSCTTY;
99 #[cfg(target_os = "macos")]
100 let control: u64 = libc::TIOCSCTTY.into();
101
102 let result = unsafe { libc::ioctl(0, control, 0) };
105 if result != 0 {
106 return Err(std::io::Error::other("failed to set controlling terminal"));
107 }
108
109 Ok(())
110}
111
112pub trait CommandCgroupExt {
114 fn join_cgroup(&mut self, cgroup_procs: Arc<CString>) -> Result<(), error::Error>;
122}
123
124impl CommandCgroupExt for std::process::Command {
125 fn join_cgroup(&mut self, cgroup_procs: Arc<CString>) -> Result<(), error::Error> {
126 unsafe {
132 self.pre_exec(move || pre_exec_join_cgroup(&cgroup_procs));
133 }
134 Ok(())
135 }
136}
137
138fn pre_exec_join_cgroup(cgroup_procs: &CStr) -> Result<(), std::io::Error> {
139 use nix::fcntl::{OFlag, open};
140 use nix::sys::stat::Mode;
141
142 let file = open(
143 cgroup_procs,
144 OFlag::O_WRONLY | OFlag::O_CLOEXEC,
145 Mode::empty(),
146 )?;
147 nix::unistd::write(&file, b"0")?;
148 Ok(())
149}
150
151pub(crate) fn is_missing(path: &CStr) -> bool {
154 use std::os::unix::ffi::OsStrExt as _;
155
156 !std::path::Path::new(std::ffi::OsStr::from_bytes(path.to_bytes())).exists()
157}