diff options
Diffstat (limited to 'src/pipe.rs')
-rw-r--r-- | src/pipe.rs | 149 |
1 files changed, 0 insertions, 149 deletions
diff --git a/src/pipe.rs b/src/pipe.rs deleted file mode 100644 index e75f010..0000000 --- a/src/pipe.rs +++ /dev/null @@ -1,149 +0,0 @@ -use async_std::io::ReadExt as _; -use async_std::os::unix::process::CommandExt as _; -use async_std::stream::StreamExt as _; -use std::os::unix::io::FromRawFd as _; -use std::os::unix::process::ExitStatusExt as _; - -const PID0: nix::unistd::Pid = nix::unistd::Pid::from_raw(0); - -pub async fn run() -> anyhow::Result<i32> { - let pipeline = read_pipeline().await?; - let mut cmds: Vec<_> = pipeline - .exes() - .iter() - .map(|exe| { - let mut cmd = async_std::process::Command::new(exe.exe()); - cmd.args(exe.args()); - cmd - }) - .collect(); - for i in 0..(cmds.len() - 1) { - let (r, w) = pipe()?; - cmds[i].stdout(w); - cmds[i + 1].stdin(r); - } - - let mut children = vec![]; - - // Safety: setpgid is an async-signal-safe function - unsafe { - cmds[0].pre_exec(|| { - setpgid_child(PID0)?; - Ok(()) - }); - } - let leader = cmds[0].spawn()?; - let pg_pid = id_to_pid(leader.id()); - setpgid_parent(pg_pid, PID0)?; - set_foreground_pg(pg_pid)?; - children.push(leader); - - for cmd in &mut cmds[1..] { - // Safety: setpgid is an async-signal-safe function - unsafe { - cmd.pre_exec(move || { - setpgid_child(pg_pid)?; - Ok(()) - }); - } - let child = cmd.spawn()?; - let child_pid = id_to_pid(child.id()); - children.push(child); - setpgid_parent(child_pid, pg_pid)?; - } - // ensure that we don't keep the pipes open past when the children exit - drop(cmds); - - let mut final_status = None; - - let count = children.len(); - let mut children: futures_util::stream::FuturesUnordered<_> = children - .into_iter() - .enumerate() - .map(|(i, child)| async move { - (child.status_no_drop().await, i == count - 1) - }) - .collect(); - while let Some((status, last)) = children.next().await { - let status = status.unwrap_or_else(|_| { - async_std::process::ExitStatus::from_raw(1 << 8) - }); - // this conversion is safe because the Signal enum is repr(i32) - #[allow(clippy::as_conversions)] - if status.signal() == Some(nix::sys::signal::Signal::SIGINT as i32) { - nix::sys::signal::raise(nix::sys::signal::Signal::SIGINT)?; - } - if last { - final_status = Some(status); - } - } - - let final_status = final_status.unwrap(); - if let Some(signal) = final_status.signal() { - nix::sys::signal::raise(signal.try_into().unwrap())?; - } - Ok(final_status.code().unwrap()) -} - -async fn read_pipeline() -> anyhow::Result<crate::parse::Pipeline> { - // Safety: this code is only called by crate::history::run_pipeline, which - // passes data through on fd 3, and which will not spawn this process - // unless the pipe was successfully opened on that fd - let mut fd3 = unsafe { async_std::fs::File::from_raw_fd(3) }; - let mut pipeline = String::new(); - fd3.read_to_string(&mut pipeline).await?; - let ast = crate::parse::Pipeline::parse(&pipeline)?; - Ok(ast) -} - -fn pipe() -> anyhow::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, which - // means they must be valid otherwise that call would have returned an - // error - Ok((unsafe { std::fs::File::from_raw_fd(r) }, unsafe { - std::fs::File::from_raw_fd(w) - })) -} - -fn set_foreground_pg(pg: nix::unistd::Pid) -> anyhow::Result<()> { - let pty = nix::fcntl::open( - "/dev/tty", - nix::fcntl::OFlag::empty(), - nix::sys::stat::Mode::empty(), - )?; - nix::unistd::tcsetpgrp(pty, pg)?; - nix::unistd::close(pty)?; - nix::sys::signal::kill(neg_pid(pg), nix::sys::signal::Signal::SIGCONT)?; - Ok(()) -} - -fn setpgid_child(pg: nix::unistd::Pid) -> std::io::Result<()> { - nix::unistd::setpgid(id_to_pid(0), pg)?; - Ok(()) -} - -fn setpgid_parent( - pid: nix::unistd::Pid, - pg: nix::unistd::Pid, -) -> anyhow::Result<()> { - nix::unistd::setpgid(pid, pg).or_else(|e| { - // EACCES means that the child already called exec, but if it did, - // then it also must have already called setpgid itself, so we don't - // care - if e == nix::errno::Errno::EACCES { - Ok(()) - } else { - Err(e) - } - })?; - Ok(()) -} - -fn id_to_pid(id: u32) -> nix::unistd::Pid { - nix::unistd::Pid::from_raw(id.try_into().unwrap()) -} - -fn neg_pid(pid: nix::unistd::Pid) -> nix::unistd::Pid { - nix::unistd::Pid::from_raw(-pid.as_raw()) -} |