From f664ac9d36c9fe1914745361d890cb40f878defc Mon Sep 17 00:00:00 2001 From: Jesse Luehrs Date: Thu, 16 Jul 2020 01:58:01 -0400 Subject: slightly better process exit handling --- examples/basic.rs | 52 ++++++++++++++++++++++++++++++++++++++++++++------ src/command.rs | 57 ++++++++++++++++++++++++++++++++++++------------------- src/pty.rs | 24 +++++++++++++---------- 3 files changed, 97 insertions(+), 36 deletions(-) diff --git a/examples/basic.rs b/examples/basic.rs index 8240d12..39ef088 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -1,14 +1,54 @@ use std::io::{Read as _, Write as _}; +use std::os::unix::io::AsRawFd as _; fn main() { - let mut cmd = pty_process::Command::new("ls").unwrap(); - cmd.args(&["--color=auto"]); + let mut cmd = pty_process::Command::new("cat").unwrap(); + // cmd.args(&["--color=auto"]); let mut child = cmd.spawn().unwrap(); - let mut buf = [0_u8; 1]; + let mut buf = [0_u8; 4096]; + let pty = cmd.pty().as_raw_fd(); + let stdin = std::io::stdin().as_raw_fd(); loop { - cmd.pty().read_exact(&mut buf).unwrap(); - print!("{}", buf[0] as char); - std::io::stdout().flush().unwrap(); + let mut set = nix::sys::select::FdSet::new(); + set.insert(pty); + set.insert(stdin); + match nix::sys::select::select(None, Some(&mut set), None, None, None) + { + Ok(n) => { + if n > 0 { + if set.contains(pty) { + match cmd.pty().read(&mut buf) { + Ok(bytes) => { + let buf = &buf[..bytes]; + print!( + "got {} bytes: '{}'", + bytes, + std::str::from_utf8(buf).unwrap() + ); + std::io::stdout().flush().unwrap(); + } + Err(e) => { + eprintln!("pty read failed: {:?}", e); + break; + } + }; + } + if set.contains(stdin) { + match std::io::stdin().read(&mut buf) { + Ok(bytes) => { + let buf = &buf[..bytes]; + cmd.pty().write_all(buf).unwrap(); + } + Err(e) => { + eprintln!("stdin read failed: {:?}", e); + break; + } + } + } + } + } + Err(e) => println!("select failed: {:?}", e), + } } child.wait().unwrap(); } diff --git a/src/command.rs b/src/command.rs index b2d8ef7..481804f 100644 --- a/src/command.rs +++ b/src/command.rs @@ -1,41 +1,58 @@ use crate::error::*; use std::os::unix::io::{AsRawFd as _, FromRawFd as _}; +use std::os::unix::process::CommandExt as _; pub struct Command { pty: crate::pty::Pty, command: std::process::Command, + slave_fh: Option, } impl Command { - pub fn new>( - program: S, - ) -> Result { + 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 master_fd = pty.master().as_raw_fd(); + let slave_fh = pty.slave()?; + let slave_fd = slave_fh.as_raw_fd(); let mut command = std::process::Command::new(program); - command.stdin(stdin).stdout(stdout).stderr(stderr); - Ok(Self { pty, command }) + command + .stdin(unsafe { std::process::Stdio::from_raw_fd(slave_fd) }) + .stdout(unsafe { std::process::Stdio::from_raw_fd(slave_fd) }) + .stderr(unsafe { std::process::Stdio::from_raw_fd(slave_fd) }); + unsafe { + command.pre_exec(move || { + // XXX unwrap + nix::unistd::close(master_fd) + .map_err(|e| e.as_errno().unwrap())?; + nix::unistd::close(slave_fd) + .map_err(|e| e.as_errno().unwrap())?; + Ok(()) + }); + } + Ok(Self { + pty, + command, + slave_fh: Some(slave_fh), + }) } pub fn pty(&self) -> &std::fs::File { - self.pty.slave() + self.pty.master() } -} - -impl std::ops::Deref for Command { - type Target = std::process::Command; - fn deref(&self) -> &Self::Target { - &self.command + pub fn args(&mut self, args: I) -> &mut Self + where + I: IntoIterator, + S: AsRef, + { + self.command.args(args); + self } -} -impl std::ops::DerefMut for Command { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.command + pub fn spawn(&mut self) -> Result { + let child = self.command.spawn()?; + self.slave_fh = None; + Ok(child) } } diff --git a/src/pty.rs b/src/pty.rs index 03dd7c1..450ea8c 100644 --- a/src/pty.rs +++ b/src/pty.rs @@ -1,8 +1,10 @@ use crate::error::*; +use std::os::unix::io::{FromRawFd as _, IntoRawFd as _}; + pub struct Pty { - master: nix::pty::PtyMaster, - slave: std::fs::File, + master: std::fs::File, + slave: std::path::PathBuf, } impl Pty { @@ -13,20 +15,22 @@ impl Pty { 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)?; + let slave = nix::pty::ptsname_r(&master)?.into(); + + let master_fd = master.into_raw_fd(); + let master = unsafe { std::fs::File::from_raw_fd(master_fd) }; Ok(Self { master, slave }) } - pub fn master(&self) -> &nix::pty::PtyMaster { + pub fn master(&self) -> &std::fs::File { &self.master } - pub fn slave(&self) -> &std::fs::File { - &self.slave + pub fn slave(&self) -> Result { + Ok(std::fs::OpenOptions::new() + .read(true) + .write(true) + .open(&self.slave)?) } } -- cgit v1.2.3-54-g00ecf