From 3d54d1f4cb7274280f4e01e101137e91f336bc5c Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Wed, 15 Jul 2020 02:17:57 -0400 Subject: start of an implementation --- .rustfmt.toml | 1 + Cargo.toml | 4 ++-- examples/basic.rs | 14 ++++++++++++++ src/command.rs | 41 +++++++++++++++++++++++++++++++++++++++++ src/error.rs | 10 ++++++++++ src/lib.rs | 13 ++++++------- src/pty.rs | 32 ++++++++++++++++++++++++++++++++ 7 files changed, 106 insertions(+), 9 deletions(-) create mode 100644 .rustfmt.toml create mode 100644 examples/basic.rs create mode 100644 src/command.rs create mode 100644 src/error.rs create mode 100644 src/pty.rs diff --git a/.rustfmt.toml b/.rustfmt.toml new file mode 100644 index 0000000..bcad605 --- /dev/null +++ b/.rustfmt.toml @@ -0,0 +1 @@ +max_width = 78 diff --git a/Cargo.toml b/Cargo.toml index 9ae4a09..a71c256 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,6 +4,6 @@ version = "0.1.0" authors = ["Jesse Luehrs "] edition = "2018" -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html - [dependencies] +nix = "0.17" +thiserror = "1.0" diff --git a/examples/basic.rs b/examples/basic.rs new file mode 100644 index 0000000..8240d12 --- /dev/null +++ b/examples/basic.rs @@ -0,0 +1,14 @@ +use std::io::{Read as _, Write as _}; + +fn main() { + let mut cmd = pty_process::Command::new("ls").unwrap(); + cmd.args(&["--color=auto"]); + let mut child = cmd.spawn().unwrap(); + let mut buf = [0_u8; 1]; + loop { + cmd.pty().read_exact(&mut buf).unwrap(); + print!("{}", buf[0] as char); + std::io::stdout().flush().unwrap(); + } + child.wait().unwrap(); +} diff --git a/src/command.rs b/src/command.rs new file mode 100644 index 0000000..b2d8ef7 --- /dev/null +++ b/src/command.rs @@ -0,0 +1,41 @@ +use crate::error::*; + +use std::os::unix::io::{AsRawFd as _, FromRawFd as _}; + +pub struct Command { + pty: crate::pty::Pty, + command: std::process::Command, +} + +impl Command { + pub fn new>( + program: S, + ) -> Result { + let pty = crate::pty::Pty::new()?; + let fd = pty.master().as_raw_fd(); + let stdin = unsafe { std::process::Stdio::from_raw_fd(fd) }; + let stdout = unsafe { std::process::Stdio::from_raw_fd(fd) }; + let stderr = unsafe { std::process::Stdio::from_raw_fd(fd) }; + let mut command = std::process::Command::new(program); + command.stdin(stdin).stdout(stdout).stderr(stderr); + Ok(Self { pty, command }) + } + + pub fn pty(&self) -> &std::fs::File { + self.pty.slave() + } +} + +impl std::ops::Deref for Command { + type Target = std::process::Command; + + fn deref(&self) -> &Self::Target { + &self.command + } +} + +impl std::ops::DerefMut for Command { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.command + } +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..5b836de --- /dev/null +++ b/src/error.rs @@ -0,0 +1,10 @@ +#[derive(thiserror::Error, Debug)] +pub enum Error { + #[error("error creating pty")] + CreatePtyMaster(#[from] nix::Error), + + #[error("error creating pty")] + CreatePtySlave(#[from] std::io::Error), +} + +pub type Result = std::result::Result; diff --git a/src/lib.rs b/src/lib.rs index 31e1bb2..2e6d894 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,7 +1,6 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { - assert_eq!(2 + 2, 4); - } -} +mod command; +pub use command::Command; +mod error; +pub use error::{Error, Result}; +mod pty; +pub use pty::Pty; diff --git a/src/pty.rs b/src/pty.rs new file mode 100644 index 0000000..03dd7c1 --- /dev/null +++ b/src/pty.rs @@ -0,0 +1,32 @@ +use crate::error::*; + +pub struct Pty { + master: nix::pty::PtyMaster, + slave: std::fs::File, +} + +impl Pty { + pub fn new() -> Result { + let master = nix::pty::posix_openpt( + nix::fcntl::OFlag::O_RDWR | nix::fcntl::OFlag::O_NOCTTY, + )?; + nix::pty::grantpt(&master)?; + nix::pty::unlockpt(&master)?; + + let name = nix::pty::ptsname_r(&master)?; + let slave = std::fs::OpenOptions::new() + .read(true) + .write(true) + .open(name)?; + + Ok(Self { master, slave }) + } + + pub fn master(&self) -> &nix::pty::PtyMaster { + &self.master + } + + pub fn slave(&self) -> &std::fs::File { + &self.slave + } +} -- cgit v1.2.3