diff options
Diffstat (limited to 'src/command.rs')
-rw-r--r-- | src/command.rs | 269 |
1 files changed, 112 insertions, 157 deletions
diff --git a/src/command.rs b/src/command.rs index b3d1104..bbfc1e8 100644 --- a/src/command.rs +++ b/src/command.rs @@ -1,198 +1,153 @@ -use ::std::os::unix::io::{AsRawFd as _, FromRawFd as _}; - -#[cfg(any(feature = "backend-async-std", feature = "backend-smol"))] -mod async_process; -#[cfg(feature = "backend-std")] -mod std; -#[cfg(feature = "backend-tokio")] -mod tokio; - -pub struct Command<C: Impl, P: crate::pty::Impl> { - inner: C, - stdin_set: bool, - stdout_set: bool, - stderr_set: bool, - - _phantom: ::std::marker::PhantomData<P>, +use async_process::unix::CommandExt as _; + +pub struct Command { + inner: async_process::Command, + stdin: Option<std::process::Stdio>, + stdout: Option<std::process::Stdio>, + stderr: Option<std::process::Stdio>, } -impl<C: Impl, P: crate::pty::Impl> Command<C, P> { - pub fn new<S: AsRef<::std::ffi::OsStr>>(program: S) -> Self { +impl Command { + pub fn new<S: AsRef<std::ffi::OsStr>>(program: S) -> Self { Self { - inner: C::new_impl(program.as_ref()), - stdin_set: false, - stdout_set: false, - stderr_set: false, - - _phantom: ::std::marker::PhantomData, + inner: async_process::Command::new(program), + stdin: None, + stdout: None, + stderr: None, } } - pub fn stdin<T: Into<::std::process::Stdio>>(&mut self, cfg: T) { - self.stdin_set = true; - self.inner.stdin_impl(cfg.into()); + pub fn arg<S: AsRef<std::ffi::OsStr>>(&mut self, arg: S) -> &mut Self { + self.inner.arg(arg); + self } - pub fn stdout<T: Into<::std::process::Stdio>>(&mut self, cfg: T) { - self.stdout_set = true; - self.inner.stdout_impl(cfg.into()); + pub fn args<I, S>(&mut self, args: I) -> &mut Self + where + I: IntoIterator<Item = S>, + S: AsRef<std::ffi::OsStr>, + { + self.inner.args(args); + self } - pub fn stderr<T: Into<::std::process::Stdio>>(&mut self, cfg: T) { - self.stderr_set = true; - self.inner.stderr_impl(cfg.into()); + pub fn env<K, V>(&mut self, key: K, val: V) -> &mut Self + where + K: AsRef<std::ffi::OsStr>, + V: AsRef<std::ffi::OsStr>, + { + self.inner.env(key, val); + self } - pub fn spawn( + pub fn envs<I, K, V>(&mut self, vars: I) -> &mut Self + where + I: IntoIterator<Item = (K, V)>, + K: AsRef<std::ffi::OsStr>, + V: AsRef<std::ffi::OsStr>, + { + self.inner.envs(vars); + self + } + + pub fn env_remove<K: AsRef<std::ffi::OsStr>>( &mut self, - pty: crate::Pty<P>, - ) -> crate::Result<Child<<C as Impl>::Child, P>> { - let (pts, stdin, stdout, stderr) = pty.setup()?; - - let pt_fd = pty.pt().as_raw_fd(); - let pts_fd = pts.as_raw_fd(); - - self.inner - .stdin_impl(unsafe { ::std::process::Stdio::from_raw_fd(stdin) }); - self.inner.stdout_impl(unsafe { - ::std::process::Stdio::from_raw_fd(stdout) - }); - self.inner.stderr_impl(unsafe { - ::std::process::Stdio::from_raw_fd(stderr) - }); - - let pre_exec = move || { - nix::unistd::setsid()?; - set_controlling_terminal(pts_fd)?; - - // in the parent, destructors will handle closing these file - // descriptors (other than pt, used by the parent to - // communicate with the child) when the function ends, but in - // the child, we end by calling exec(), which doesn't call - // destructors. - - nix::unistd::close(pt_fd)?; - nix::unistd::close(pts_fd)?; - // at this point, stdin/stdout/stderr have already been - // reopened as fds 0/1/2 in the child, so we can (and should) - // close the originals - nix::unistd::close(stdin)?; - nix::unistd::close(stdout)?; - nix::unistd::close(stderr)?; - - Ok(()) - }; - // safe because setsid() and close() are async-signal-safe functions - // and ioctl() is a raw syscall (which is inherently - // async-signal-safe). - unsafe { self.inner.pre_exec_impl(pre_exec) }; + key: K, + ) -> &mut Self { + self.inner.env_remove(key); + self + } - let child = self.inner.spawn_impl()?; + pub fn env_clear(&mut self) -> &mut Self { + self.inner.env_clear(); + self + } - Ok(Child::new(child, pty)) + pub fn current_dir<P: AsRef<std::path::Path>>( + &mut self, + dir: P, + ) -> &mut Self { + self.inner.current_dir(dir); + self } -} -impl<C: Impl, P: crate::pty::Impl> ::std::ops::Deref for Command<C, P> { - type Target = C; + pub fn stdin<T: Into<std::process::Stdio>>( + &mut self, + cfg: Option<T>, + ) -> &mut Self { + self.stdin = cfg.map(Into::into); + self + } - fn deref(&self) -> &Self::Target { - &self.inner + pub fn stdout<T: Into<std::process::Stdio>>( + &mut self, + cfg: Option<T>, + ) -> &mut Self { + self.stdout = cfg.map(Into::into); + self } -} -impl<C: Impl, P: crate::pty::Impl> ::std::ops::DerefMut for Command<C, P> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.inner + pub fn stderr<T: Into<std::process::Stdio>>( + &mut self, + cfg: Option<T>, + ) -> &mut Self { + self.stderr = cfg.map(Into::into); + self + } + + pub fn spawn(&mut self, pty: crate::Pty) -> crate::Result<Child> { + let (stdin, stdout, stderr, pre_exec) = crate::sys::setup_subprocess( + &pty, + pty.pts().map_err(crate::error::spawn)?, + ) + .map_err(crate::error::spawn)?; + + self.inner.stdin(self.stdin.take().unwrap_or(stdin)); + self.inner.stdout(self.stdout.take().unwrap_or(stdout)); + self.inner.stderr(self.stderr.take().unwrap_or(stderr)); + + // safe because setsid() and close() are async-signal-safe functions + // and ioctl() is a raw syscall (which is inherently + // async-signal-safe). + unsafe { self.inner.pre_exec(pre_exec) }; + + let child = self.inner.spawn().map_err(crate::error::spawn)?; + + Ok(Child::new(child, pty)) } } -pub struct Child<C, P: crate::pty::Impl> { - inner: C, - pty: crate::Pty<P>, +pub struct Child { + inner: async_process::Child, + pty: crate::Pty, } -impl<C, P: crate::pty::Impl> Child<C, P> { - fn new(inner: C, pty: crate::Pty<P>) -> Self { +impl Child { + fn new(inner: async_process::Child, pty: crate::Pty) -> Self { Self { inner, pty } } - /// Returns a reference to the pty. - /// - /// The underlying pty instance is guaranteed to implement - /// [`AsRawFd`](::std::os::unix::io::AsRawFd), as well as the appropriate - /// `Read` and `Write` traits for the associated backend. - pub fn pty(&self) -> &P { - self.pty.pt() - } - - /// Returns a mutable reference to the pty. - /// - /// The underlying pty instance is guaranteed to implement - /// [`AsRawFd`](::std::os::unix::io::AsRawFd), as well as the appropriate - /// `Read` and `Write` traits for the associated backend. - /// - /// This method is primarily useful for the tokio backend, since tokio's - /// `AsyncRead` and `AsyncWrite` traits have methods which take mutable - /// references. - pub fn pty_mut(&mut self) -> &mut P { - self.pty.pt_mut() - } - - /// Causes the pty to change its size. - /// - /// This will additionally cause a `SIGWINCH` signal to be sent to the - /// running process. - /// - /// # Errors - /// * `Error::SetTermSize`: error setting terminal size - pub fn resize_pty( - &self, - size: crate::pty::Size, - ) -> crate::error::Result<()> { - self.pty.resize(size) + #[must_use] + pub fn pty(&self) -> &crate::Pty { + &self.pty + } + + #[must_use] + pub fn pty_mut(&mut self) -> &mut crate::Pty { + &mut self.pty } } -impl<C, P: crate::pty::Impl> ::std::ops::Deref for Child<C, P> { - type Target = C; +impl std::ops::Deref for Child { + type Target = async_process::Child; fn deref(&self) -> &Self::Target { &self.inner } } -impl<C, P: crate::pty::Impl> ::std::ops::DerefMut for Child<C, P> { +impl std::ops::DerefMut for Child { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.inner } } - -pub trait Impl { - type Child; - - fn new_impl(program: &::std::ffi::OsStr) -> Self; - fn stdin_impl(&mut self, cfg: ::std::process::Stdio); - fn stdout_impl(&mut self, cfg: ::std::process::Stdio); - fn stderr_impl(&mut self, cfg: ::std::process::Stdio); - unsafe fn pre_exec_impl<F>(&mut self, f: F) - where - F: FnMut() -> ::std::io::Result<()> + Send + Sync + 'static; - - fn spawn_impl(&mut self) -> crate::Result<Self::Child>; -} - -fn set_controlling_terminal( - fd: ::std::os::unix::io::RawFd, -) -> nix::Result<()> { - // safe because std::fs::File is required to contain a valid file - // descriptor - unsafe { set_controlling_terminal_unsafe(fd, ::std::ptr::null()) } - .map(|_| ()) -} - -nix::ioctl_write_ptr_bad!( - set_controlling_terminal_unsafe, - libc::TIOCSCTTY, - libc::c_int -); |