From cba75be9c673b846ad0c4f6f5f2e8461056582ad Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Sun, 9 Jan 2022 22:46:15 -0500 Subject: rename since this doesn't just run pipelines anymore --- src/runner/command.rs | 219 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 219 insertions(+) create mode 100644 src/runner/command.rs (limited to 'src/runner/command.rs') diff --git a/src/runner/command.rs b/src/runner/command.rs new file mode 100644 index 0000000..34b770e --- /dev/null +++ b/src/runner/command.rs @@ -0,0 +1,219 @@ +use crate::runner::prelude::*; + +pub struct Command { + inner: Inner, + exe: std::path::PathBuf, + redirects: Vec, + pre_exec: Option< + Box std::io::Result<()> + Send + Sync + 'static>, + >, +} +impl Command { + pub fn new(exe: crate::parse::Exe) -> Self { + let exe_path = exe.exe().to_path_buf(); + let redirects = exe.redirects().to_vec(); + Self { + inner: super::builtins::Command::new(exe).map_or_else( + |exe| Self::new_binary(exe).inner, + Inner::Builtin, + ), + exe: exe_path, + redirects, + pre_exec: None, + } + } + + pub fn new_with_io( + exe: crate::parse::Exe, + io: super::builtins::Io, + ) -> Self { + let exe_path = exe.exe().to_path_buf(); + let redirects = exe.redirects().to_vec(); + Self { + inner: super::builtins::Command::new_with_io(exe, io) + .map_or_else( + |exe| Self::new_binary(exe).inner, + Inner::Builtin, + ), + exe: exe_path, + redirects, + pre_exec: None, + } + } + + #[allow(clippy::needless_pass_by_value)] + pub fn new_binary(exe: crate::parse::Exe) -> Self { + let exe_path = exe.exe().to_path_buf(); + let redirects = exe.redirects().to_vec(); + let mut cmd = async_std::process::Command::new(exe.exe()); + cmd.args(exe.args()); + Self { + inner: Inner::Binary(cmd), + exe: exe_path, + redirects, + pre_exec: None, + } + } + + pub fn new_builtin(exe: crate::parse::Exe) -> Self { + let exe_path = exe.exe().to_path_buf(); + let redirects = exe.redirects().to_vec(); + Self { + inner: super::builtins::Command::new(exe) + .map_or_else(|_| todo!(), Inner::Builtin), + exe: exe_path, + redirects, + pre_exec: None, + } + } + + pub fn stdin(&mut self, fh: std::fs::File) { + match &mut self.inner { + Inner::Binary(cmd) => { + cmd.stdin(fh); + } + Inner::Builtin(cmd) => { + cmd.stdin(fh); + } + } + } + + pub fn stdout(&mut self, fh: std::fs::File) { + match &mut self.inner { + Inner::Binary(cmd) => { + cmd.stdout(fh); + } + Inner::Builtin(cmd) => { + cmd.stdout(fh); + } + } + } + + pub fn stderr(&mut self, fh: std::fs::File) { + match &mut self.inner { + Inner::Binary(cmd) => { + cmd.stderr(fh); + } + Inner::Builtin(cmd) => { + cmd.stderr(fh); + } + } + } + + // Safety: see pre_exec in async_std::os::unix::process::CommandExt (this + // is just a wrapper) + pub unsafe fn pre_exec(&mut self, f: F) + where + F: 'static + FnMut() -> std::io::Result<()> + Send + Sync, + { + self.pre_exec = Some(Box::new(f)); + } + + pub fn spawn(self, env: &Env) -> anyhow::Result { + let Self { + inner, + exe, + redirects, + pre_exec, + } = self; + + #[allow(clippy::as_conversions)] + let pre_exec = pre_exec.map_or_else( + || { + let redirects = redirects.clone(); + Box::new(move || { + apply_redirects(&redirects)?; + Ok(()) + }) + as Box std::io::Result<()> + Send + Sync> + }, + |mut pre_exec| { + let redirects = redirects.clone(); + Box::new(move || { + apply_redirects(&redirects)?; + pre_exec()?; + Ok(()) + }) + }, + ); + match inner { + Inner::Binary(mut cmd) => { + // Safety: open, dup2, and close are async-signal-safe + // functions + unsafe { cmd.pre_exec(pre_exec) }; + Ok(Child::Binary(cmd.spawn().map_err(|e| { + anyhow::anyhow!( + "{}: {}", + crate::format::io_error(&e), + exe.display() + ) + })?)) + } + Inner::Builtin(mut cmd) => { + // Safety: open, dup2, and close are async-signal-safe + // functions + unsafe { cmd.pre_exec(pre_exec) }; + cmd.apply_redirects(&redirects); + Ok(Child::Builtin(cmd.spawn(env)?)) + } + } + } +} + +pub enum Inner { + Binary(async_std::process::Command), + Builtin(super::builtins::Command), +} + +pub enum Child<'a> { + Binary(async_std::process::Child), + Builtin(super::builtins::Child<'a>), +} + +impl<'a> Child<'a> { + pub fn id(&self) -> Option { + match self { + Self::Binary(child) => Some(child.id()), + Self::Builtin(child) => child.id(), + } + } + + pub fn status( + self, + ) -> std::pin::Pin< + Box< + dyn std::future::Future< + Output = anyhow::Result, + > + Send + + Sync + + 'a, + >, + > { + Box::pin(async move { + match self { + Self::Binary(child) => Ok(child.status_no_drop().await?), + Self::Builtin(child) => Ok(child.status().await?), + } + }) + } +} + +fn apply_redirects( + redirects: &[crate::parse::Redirect], +) -> std::io::Result<()> { + for redirect in redirects { + match &redirect.to { + crate::parse::RedirectTarget::Fd(fd) => { + nix::unistd::dup2(*fd, redirect.from)?; + } + crate::parse::RedirectTarget::File(path) => { + let fd = redirect.dir.open(path)?; + if fd != redirect.from { + nix::unistd::dup2(fd, redirect.from)?; + nix::unistd::close(fd)?; + } + } + } + } + Ok(()) +} -- cgit v1.2.3-54-g00ecf