aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJesse Luehrs <doy@tozt.net>2020-07-15 02:17:57 -0400
committerJesse Luehrs <doy@tozt.net>2020-07-15 02:17:57 -0400
commit3d54d1f4cb7274280f4e01e101137e91f336bc5c (patch)
tree896ecd9b9bd8696c9818c0779cd1d35876adec84
parent489a8198828be02c92da0c771ae0864915b08e7f (diff)
downloadpty-process-3d54d1f4cb7274280f4e01e101137e91f336bc5c.tar.gz
pty-process-3d54d1f4cb7274280f4e01e101137e91f336bc5c.zip
start of an implementation
-rw-r--r--.rustfmt.toml1
-rw-r--r--Cargo.toml4
-rw-r--r--examples/basic.rs14
-rw-r--r--src/command.rs41
-rw-r--r--src/error.rs10
-rw-r--r--src/lib.rs13
-rw-r--r--src/pty.rs32
7 files changed, 106 insertions, 9 deletions
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 <doy@tozt.net>"]
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<S: std::convert::AsRef<std::ffi::OsStr>>(
+ program: S,
+ ) -> Result<Self> {
+ 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<T> = std::result::Result<T, Error>;
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<Self> {
+ 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
+ }
+}