From f3e8046eff473aa9bf940b7fbd156cf3dfbfa352 Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Tue, 23 Feb 2021 00:17:01 -0500 Subject: also create separate pty structs per backend for now, they are identical, but i will change them to be specialized next --- Cargo.toml | 2 ++ examples/basic.rs | 2 +- src/async_std.rs | 2 ++ src/command.rs | 41 +++++++++++++++--------- src/command/async_process.rs | 1 + src/command/std.rs | 1 + src/command/tokio.rs | 1 + src/lib.rs | 9 +++++- src/pty.rs | 74 ++++++++++++++++++-------------------------- src/pty/async_io.rs | 36 +++++++++++++++++++++ src/pty/std.rs | 36 +++++++++++++++++++++ src/pty/tokio.rs | 36 +++++++++++++++++++++ src/std.rs | 2 ++ src/tokio.rs | 2 ++ 14 files changed, 184 insertions(+), 61 deletions(-) create mode 100644 src/async_std.rs create mode 100644 src/pty/async_io.rs create mode 100644 src/pty/std.rs create mode 100644 src/pty/tokio.rs create mode 100644 src/std.rs create mode 100644 src/tokio.rs diff --git a/Cargo.toml b/Cargo.toml index 27e5bf1..6357d07 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ libc = "*" nix = "0.17" thiserror = "1.0" +async-io = { version = "1.3", optional = true } async-process = { version = "1.0", optional = true } tokio = { version = "1.2", optional = true, features = ["process"] } @@ -16,3 +17,4 @@ tokio = { version = "1.2", optional = true, features = ["process"] } default = ["std"] std = [] +async-std = ["async-io", "async-process"] diff --git a/examples/basic.rs b/examples/basic.rs index d7f99d0..bc9389c 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -35,7 +35,7 @@ impl Drop for RawGuard { } } -fn run(child: &pty_process::Child) { +fn run(child: &pty_process::std::Child) { let _raw = RawGuard::new(); let mut buf = [0_u8; 4096]; let pty = child.pty().as_raw_fd(); diff --git a/src/async_std.rs b/src/async_std.rs new file mode 100644 index 0000000..e91a18d --- /dev/null +++ b/src/async_std.rs @@ -0,0 +1,2 @@ +pub type Child = + crate::command::Child; diff --git a/src/command.rs b/src/command.rs index 0b53626..4679579 100644 --- a/src/command.rs +++ b/src/command.rs @@ -1,34 +1,38 @@ use crate::error::*; +use crate::pty::Pty as _; use ::std::os::unix::io::AsRawFd as _; mod std; -#[cfg(feature = "async-process")] +#[cfg(feature = "async-std")] mod async_process; #[cfg(feature = "tokio")] mod tokio; pub trait Command { type Child; + type Pty; fn spawn_pty( &mut self, size: Option<&crate::pty::Size>, - ) -> Result>; + ) -> Result>; } impl Command for T where T: CommandImpl, + T::Pty: crate::pty::Pty, { type Child = T::Child; + type Pty = T::Pty; fn spawn_pty( &mut self, size: Option<&crate::pty::Size>, - ) -> Result> { - let (pty, pts, stdin, stdout, stderr) = setup_pty(size)?; + ) -> Result> { + let (pty, pts, stdin, stdout, stderr) = setup_pty::(size)?; let pt_fd = pty.pt().as_raw_fd(); let pts_fd = pts.as_raw_fd(); @@ -69,12 +73,15 @@ where } } -pub struct Child { - child: T, - pty: crate::pty::Pty, +pub struct Child { + child: C, + pty: P, } -impl Child { +impl Child +where + P: crate::pty::Pty, +{ pub fn pty(&self) -> &::std::fs::File { self.pty.pt() } @@ -84,15 +91,15 @@ impl Child { } } -impl ::std::ops::Deref for Child { - type Target = T; +impl ::std::ops::Deref for Child { + type Target = C; fn deref(&self) -> &Self::Target { &self.child } } -impl ::std::ops::DerefMut for Child { +impl ::std::ops::DerefMut for Child { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.child } @@ -101,6 +108,7 @@ impl ::std::ops::DerefMut for Child { // XXX shouldn't be pub? pub trait CommandImpl { type Child; + type Pty; fn std_fds( &mut self, @@ -114,16 +122,19 @@ pub trait CommandImpl { fn spawn_impl(&mut self) -> ::std::io::Result; } -fn setup_pty( +fn setup_pty

( size: Option<&crate::pty::Size>, ) -> Result<( - crate::pty::Pty, + P, ::std::fs::File, ::std::os::unix::io::RawFd, ::std::os::unix::io::RawFd, ::std::os::unix::io::RawFd, -)> { - let pty = crate::pty::Pty::new()?; +)> +where + P: crate::pty::Pty, +{ + let pty = P::new()?; if let Some(size) = size { pty.resize(size)?; } diff --git a/src/command/async_process.rs b/src/command/async_process.rs index 4c453e6..4248402 100644 --- a/src/command/async_process.rs +++ b/src/command/async_process.rs @@ -3,6 +3,7 @@ use std::os::unix::io::FromRawFd as _; impl super::CommandImpl for async_process::Command { type Child = async_process::Child; + type Pty = crate::pty::async_io::Pty; fn std_fds( &mut self, diff --git a/src/command/std.rs b/src/command/std.rs index 3287834..f1c5418 100644 --- a/src/command/std.rs +++ b/src/command/std.rs @@ -3,6 +3,7 @@ use std::os::unix::process::CommandExt as _; impl super::CommandImpl for std::process::Command { type Child = std::process::Child; + type Pty = crate::pty::std::Pty; fn std_fds( &mut self, diff --git a/src/command/tokio.rs b/src/command/tokio.rs index 60dfb56..67baf74 100644 --- a/src/command/tokio.rs +++ b/src/command/tokio.rs @@ -2,6 +2,7 @@ use std::os::unix::io::FromRawFd as _; impl super::CommandImpl for tokio::process::Command { type Child = tokio::process::Child; + type Pty = crate::pty::tokio::Pty; fn std_fds( &mut self, diff --git a/src/lib.rs b/src/lib.rs index 112bec1..76bbf9c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,13 @@ mod command; -pub use command::{Child, Command}; +pub use command::Command; mod error; pub use error::{Error, Result}; mod pty; pub use pty::{Pty, Size}; + +pub mod std; + +#[cfg(feature = "async-std")] +pub mod async_std; +#[cfg(feature = "tokio")] +pub mod tokio; diff --git a/src/pty.rs b/src/pty.rs index ad4dfcf..f192298 100644 --- a/src/pty.rs +++ b/src/pty.rs @@ -1,6 +1,24 @@ use crate::error::*; -use std::os::unix::io::{AsRawFd as _, FromRawFd as _, IntoRawFd as _}; +use ::std::os::unix::io::{AsRawFd as _, IntoRawFd as _}; + +pub mod std; + +#[cfg(feature = "async-std")] +pub mod async_io; +#[cfg(feature = "tokio")] +pub mod tokio; + +pub trait Pty { + fn new() -> Result + where + Self: Sized; + fn pt(&self) -> &::std::fs::File; + fn pts(&self) -> Result<::std::fs::File>; + fn resize(&self, size: &super::Size) -> Result<()> { + set_term_size(self.pt(), size).map_err(Error::SetTermSize) + } +} pub struct Size { row: u16, @@ -45,51 +63,19 @@ impl From<&Size> for nix::pty::Winsize { } } -pub struct Pty { - pt: std::fs::File, - ptsname: std::path::PathBuf, -} - -impl Pty { - pub fn new() -> Result { - let pt = nix::pty::posix_openpt( - nix::fcntl::OFlag::O_RDWR | nix::fcntl::OFlag::O_NOCTTY, - ) - .map_err(Error::CreatePty)?; - nix::pty::grantpt(&pt).map_err(Error::CreatePty)?; - nix::pty::unlockpt(&pt).map_err(Error::CreatePty)?; - - let ptsname = - nix::pty::ptsname_r(&pt).map_err(Error::CreatePty)?.into(); - - let pt_fd = pt.into_raw_fd(); +fn create_pt() -> Result<(::std::os::unix::io::RawFd, ::std::path::PathBuf)> { + let pt = nix::pty::posix_openpt( + nix::fcntl::OFlag::O_RDWR | nix::fcntl::OFlag::O_NOCTTY, + ) + .map_err(Error::CreatePty)?; + nix::pty::grantpt(&pt).map_err(Error::CreatePty)?; + nix::pty::unlockpt(&pt).map_err(Error::CreatePty)?; - // safe because posix_openpt (or the previous functions operating on - // the result) would have returned an Err (causing us to return early) - // if the file descriptor was invalid. additionally, into_raw_fd gives - // up ownership over the file descriptor, allowing the newly created - // File object to take full ownership. - let pt = unsafe { std::fs::File::from_raw_fd(pt_fd) }; + let ptsname = nix::pty::ptsname_r(&pt).map_err(Error::CreatePty)?.into(); - Ok(Self { pt, ptsname }) - } - - pub fn pt(&self) -> &std::fs::File { - &self.pt - } + let pt_fd = pt.into_raw_fd(); - pub fn pts(&self) -> Result { - let fh = std::fs::OpenOptions::new() - .read(true) - .write(true) - .open(&self.ptsname) - .map_err(|e| Error::OpenPts(self.ptsname.clone(), e))?; - Ok(fh) - } - - pub fn resize(&self, size: &Size) -> Result<()> { - set_term_size(self.pt(), size).map_err(Error::SetTermSize) - } + Ok((pt_fd, ptsname)) } nix::ioctl_write_ptr_bad!( @@ -98,7 +84,7 @@ nix::ioctl_write_ptr_bad!( nix::pty::Winsize ); -fn set_term_size(file: &std::fs::File, size: &Size) -> nix::Result<()> { +fn set_term_size(file: &::std::fs::File, size: &Size) -> nix::Result<()> { let size = size.into(); let fd = file.as_raw_fd(); // safe because std::fs::File is required to contain a valid file diff --git a/src/pty/async_io.rs b/src/pty/async_io.rs new file mode 100644 index 0000000..84097f9 --- /dev/null +++ b/src/pty/async_io.rs @@ -0,0 +1,36 @@ +use crate::error::*; + +use std::os::unix::io::FromRawFd as _; + +pub struct Pty { + pt: std::fs::File, + ptsname: std::path::PathBuf, +} + +impl super::Pty for Pty { + fn new() -> Result { + let (pt_fd, ptsname) = super::create_pt()?; + + // safe because posix_openpt (or the previous functions operating on + // the result) would have returned an Err (causing us to return early) + // if the file descriptor was invalid. additionally, into_raw_fd gives + // up ownership over the file descriptor, allowing the newly created + // File object to take full ownership. + let pt = unsafe { std::fs::File::from_raw_fd(pt_fd) }; + + Ok(Self { pt, ptsname }) + } + + fn pt(&self) -> &std::fs::File { + &self.pt + } + + fn pts(&self) -> Result { + let fh = std::fs::OpenOptions::new() + .read(true) + .write(true) + .open(&self.ptsname) + .map_err(|e| Error::OpenPts(self.ptsname.clone(), e))?; + Ok(fh) + } +} diff --git a/src/pty/std.rs b/src/pty/std.rs new file mode 100644 index 0000000..84097f9 --- /dev/null +++ b/src/pty/std.rs @@ -0,0 +1,36 @@ +use crate::error::*; + +use std::os::unix::io::FromRawFd as _; + +pub struct Pty { + pt: std::fs::File, + ptsname: std::path::PathBuf, +} + +impl super::Pty for Pty { + fn new() -> Result { + let (pt_fd, ptsname) = super::create_pt()?; + + // safe because posix_openpt (or the previous functions operating on + // the result) would have returned an Err (causing us to return early) + // if the file descriptor was invalid. additionally, into_raw_fd gives + // up ownership over the file descriptor, allowing the newly created + // File object to take full ownership. + let pt = unsafe { std::fs::File::from_raw_fd(pt_fd) }; + + Ok(Self { pt, ptsname }) + } + + fn pt(&self) -> &std::fs::File { + &self.pt + } + + fn pts(&self) -> Result { + let fh = std::fs::OpenOptions::new() + .read(true) + .write(true) + .open(&self.ptsname) + .map_err(|e| Error::OpenPts(self.ptsname.clone(), e))?; + Ok(fh) + } +} diff --git a/src/pty/tokio.rs b/src/pty/tokio.rs new file mode 100644 index 0000000..84097f9 --- /dev/null +++ b/src/pty/tokio.rs @@ -0,0 +1,36 @@ +use crate::error::*; + +use std::os::unix::io::FromRawFd as _; + +pub struct Pty { + pt: std::fs::File, + ptsname: std::path::PathBuf, +} + +impl super::Pty for Pty { + fn new() -> Result { + let (pt_fd, ptsname) = super::create_pt()?; + + // safe because posix_openpt (or the previous functions operating on + // the result) would have returned an Err (causing us to return early) + // if the file descriptor was invalid. additionally, into_raw_fd gives + // up ownership over the file descriptor, allowing the newly created + // File object to take full ownership. + let pt = unsafe { std::fs::File::from_raw_fd(pt_fd) }; + + Ok(Self { pt, ptsname }) + } + + fn pt(&self) -> &std::fs::File { + &self.pt + } + + fn pts(&self) -> Result { + let fh = std::fs::OpenOptions::new() + .read(true) + .write(true) + .open(&self.ptsname) + .map_err(|e| Error::OpenPts(self.ptsname.clone(), e))?; + Ok(fh) + } +} diff --git a/src/std.rs b/src/std.rs new file mode 100644 index 0000000..fa07499 --- /dev/null +++ b/src/std.rs @@ -0,0 +1,2 @@ +pub type Child = + crate::command::Child; diff --git a/src/tokio.rs b/src/tokio.rs new file mode 100644 index 0000000..289d659 --- /dev/null +++ b/src/tokio.rs @@ -0,0 +1,2 @@ +pub type Child = + crate::command::Child; -- cgit v1.2.3