summaryrefslogtreecommitdiffstats
path: root/src/runner/sys.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/runner/sys.rs')
-rw-r--r--src/runner/sys.rs79
1 files changed, 79 insertions, 0 deletions
diff --git a/src/runner/sys.rs b/src/runner/sys.rs
new file mode 100644
index 0000000..b6a9428
--- /dev/null
+++ b/src/runner/sys.rs
@@ -0,0 +1,79 @@
+use crate::runner::prelude::*;
+
+const PID0: nix::unistd::Pid = nix::unistd::Pid::from_raw(0);
+
+pub fn pipe() -> Result<(std::fs::File, std::fs::File)> {
+ let (r, w) = nix::unistd::pipe2(nix::fcntl::OFlag::O_CLOEXEC)?;
+ // Safety: these file descriptors were just returned by pipe2 above, and
+ // are only available in this function, so nothing else can be accessing
+ // them
+ Ok((unsafe { std::fs::File::from_raw_fd(r) }, unsafe {
+ std::fs::File::from_raw_fd(w)
+ }))
+}
+
+pub fn set_foreground_pg(pg: nix::unistd::Pid) -> Result<()> {
+ let pty = nix::fcntl::open(
+ "/dev/tty",
+ nix::fcntl::OFlag::empty(),
+ nix::sys::stat::Mode::empty(),
+ )?;
+
+ // if a background process calls tcsetpgrp, the kernel will send it
+ // SIGTTOU which suspends it. if that background process is the session
+ // leader and doesn't have SIGTTOU blocked, the kernel will instead just
+ // return ENOTTY from the tcsetpgrp call rather than sending a signal to
+ // avoid deadlocking the process. therefore, we need to ensure that
+ // SIGTTOU is blocked here.
+
+ // Safety: setting a signal handler to SigIgn is always safe
+ unsafe {
+ nix::sys::signal::signal(
+ nix::sys::signal::Signal::SIGTTOU,
+ nix::sys::signal::SigHandler::SigIgn,
+ )?;
+ }
+ let res = nix::unistd::tcsetpgrp(pty, pg);
+ // Safety: setting a signal handler to SigDfl is always safe
+ unsafe {
+ nix::sys::signal::signal(
+ nix::sys::signal::Signal::SIGTTOU,
+ nix::sys::signal::SigHandler::SigDfl,
+ )?;
+ }
+ res?;
+
+ nix::unistd::close(pty)?;
+
+ nix::sys::signal::kill(neg_pid(pg), nix::sys::signal::Signal::SIGCONT)
+ // the process group has already exited
+ .allow(nix::errno::Errno::ESRCH)?;
+
+ Ok(())
+}
+
+pub fn setpgid_child(pg: Option<nix::unistd::Pid>) -> std::io::Result<()> {
+ nix::unistd::setpgid(PID0, pg.unwrap_or(PID0))?;
+ Ok(())
+}
+
+pub fn setpgid_parent(
+ pid: nix::unistd::Pid,
+ pg: Option<nix::unistd::Pid>,
+) -> Result<()> {
+ nix::unistd::setpgid(pid, pg.unwrap_or(PID0))
+ // the child already called exec, so it must have already called
+ // setpgid itself
+ .allow(nix::errno::Errno::EACCES)
+ // the child already exited, so we don't care
+ .allow(nix::errno::Errno::ESRCH)?;
+ Ok(())
+}
+
+pub fn id_to_pid(id: u32) -> nix::unistd::Pid {
+ nix::unistd::Pid::from_raw(id.try_into().unwrap())
+}
+
+pub fn neg_pid(pid: nix::unistd::Pid) -> nix::unistd::Pid {
+ nix::unistd::Pid::from_raw(-pid.as_raw())
+}