diff options
Diffstat (limited to 'src/command.rs')
-rw-r--r-- | src/command.rs | 172 |
1 files changed, 82 insertions, 90 deletions
diff --git a/src/command.rs b/src/command.rs index 303b885..b3d1104 100644 --- a/src/command.rs +++ b/src/command.rs @@ -1,6 +1,4 @@ -use crate::pty::Pty as _; - -use ::std::os::unix::io::AsRawFd as _; +use ::std::os::unix::io::{AsRawFd as _, FromRawFd as _}; #[cfg(any(feature = "backend-async-std", feature = "backend-smol"))] mod async_process; @@ -9,47 +7,59 @@ mod std; #[cfg(feature = "backend-tokio")] mod tokio; -/// Adds methods to the existing `Command` struct. -/// -/// This trait is automatically implemented for a backend's `Command` struct -/// when that backend's feature is enabled. -pub trait Command { - type Child; - type Pty; +pub struct Command<C: Impl, P: crate::pty::Impl> { + inner: C, + stdin_set: bool, + stdout_set: bool, + stderr_set: bool, - /// Creates a new pty, associates the command's stdin/stdout/stderr with - /// that pty, and then calls `spawn`. This will override any previous - /// calls to `stdin`/`stdout`/`stderr`. - /// - /// # Errors - /// * `Error::CreatePty`: error creating pty - /// * `Error::SetTermSize`: error setting terminal size - /// * `Error::Spawn`: error spawning subprocess - fn spawn_pty( - &mut self, - size: Option<&crate::pty::Size>, - ) -> crate::error::Result<Child<Self::Child, Self::Pty>>; + _phantom: ::std::marker::PhantomData<P>, } -impl<T> Command for T -where - T: Impl, - T::Pty: crate::pty::Pty, - <<T as Impl>::Pty as crate::pty::Pty>::Pt: ::std::os::unix::io::AsRawFd, -{ - type Child = T::Child; - type Pty = T::Pty; +impl<C: Impl, P: crate::pty::Impl> Command<C, P> { + 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, + } + } + + pub fn stdin<T: Into<::std::process::Stdio>>(&mut self, cfg: T) { + self.stdin_set = true; + self.inner.stdin_impl(cfg.into()); + } + + pub fn stdout<T: Into<::std::process::Stdio>>(&mut self, cfg: T) { + self.stdout_set = true; + self.inner.stdout_impl(cfg.into()); + } + + pub fn stderr<T: Into<::std::process::Stdio>>(&mut self, cfg: T) { + self.stderr_set = true; + self.inner.stderr_impl(cfg.into()); + } - fn spawn_pty( + pub fn spawn( &mut self, - size: Option<&crate::pty::Size>, - ) -> crate::error::Result<Child<Self::Child, Self::Pty>> { - let (pty, pts, stdin, stdout, stderr) = setup_pty::<Self::Pty>(size)?; + 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.std_fds(stdin, stdout, stderr); + 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()?; @@ -75,30 +85,44 @@ where // safe because setsid() and close() are async-signal-safe functions // and ioctl() is a raw syscall (which is inherently // async-signal-safe). - unsafe { self.pre_exec_impl(pre_exec) }; + unsafe { self.inner.pre_exec_impl(pre_exec) }; + + let child = self.inner.spawn_impl()?; + + Ok(Child::new(child, pty)) + } +} + +impl<C: Impl, P: crate::pty::Impl> ::std::ops::Deref for Command<C, P> { + type Target = C; - let child = self.spawn_impl().map_err(crate::error::spawn)?; + fn deref(&self) -> &Self::Target { + &self.inner + } +} - Ok(Child { child, pty }) +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 } } -/// Wrapper struct adding pty methods to the normal `Child` struct. -pub struct Child<C, P> { - child: C, - pty: P, +pub struct Child<C, P: crate::pty::Impl> { + inner: C, + pty: crate::Pty<P>, } -impl<C, P> Child<C, P> -where - P: crate::pty::Pty, -{ +impl<C, P: crate::pty::Impl> Child<C, P> { + fn new(inner: C, pty: crate::Pty<P>) -> 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::Pt { + pub fn pty(&self) -> &P { self.pty.pt() } @@ -111,7 +135,7 @@ where /// 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::Pt { + pub fn pty_mut(&mut self) -> &mut P { self.pty.pt_mut() } @@ -124,70 +148,38 @@ where /// * `Error::SetTermSize`: error setting terminal size pub fn resize_pty( &self, - size: &crate::pty::Size, + size: crate::pty::Size, ) -> crate::error::Result<()> { self.pty.resize(size) } } -impl<C, P> ::std::ops::Deref for Child<C, P> { +impl<C, P: crate::pty::Impl> ::std::ops::Deref for Child<C, P> { type Target = C; fn deref(&self) -> &Self::Target { - &self.child + &self.inner } } -impl<C, P> ::std::ops::DerefMut for Child<C, P> { +impl<C, P: crate::pty::Impl> ::std::ops::DerefMut for Child<C, P> { fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.child + &mut self.inner } } -// XXX shouldn't be pub? pub trait Impl { type Child; - type Pty; - fn std_fds( - &mut self, - stdin: ::std::os::unix::io::RawFd, - stdout: ::std::os::unix::io::RawFd, - stderr: ::std::os::unix::io::RawFd, - ); + 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) -> ::std::io::Result<Self::Child>; -} - -fn setup_pty<P>( - size: Option<&crate::pty::Size>, -) -> crate::error::Result<( - P, - ::std::fs::File, - ::std::os::unix::io::RawFd, - ::std::os::unix::io::RawFd, - ::std::os::unix::io::RawFd, -)> -where - P: crate::pty::Pty, -{ - let pty = P::new()?; - if let Some(size) = size { - pty.resize(size)?; - } - - let pts = pty.pts()?; - let pts_fd = pts.as_raw_fd(); - - let stdin = nix::unistd::dup(pts_fd).map_err(crate::error::create_pty)?; - let stdout = - nix::unistd::dup(pts_fd).map_err(crate::error::create_pty)?; - let stderr = - nix::unistd::dup(pts_fd).map_err(crate::error::create_pty)?; - Ok((pty, pts, stdin, stdout, stderr)) + fn spawn_impl(&mut self) -> crate::Result<Self::Child>; } fn set_controlling_terminal( |