diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index bc133e5e..7b4122bf 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,7 +26,7 @@ jobs: uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} - - uses: swatinem/rust-cache@v1 + - uses: swatinem/rust-cache@v2 - name: cargo-check uses: actions-rs/cargo@v1 with: @@ -51,7 +51,7 @@ jobs: uses: dtolnay/rust-toolchain@master with: toolchain: ${{ matrix.rust }} - - uses: swatinem/rust-cache@v1 + - uses: swatinem/rust-cache@v2 - name: cargo-test uses: actions-rs/cargo@v1 with: @@ -107,7 +107,7 @@ jobs: with: toolchain: 1.67.0 components: clippy - - uses: swatinem/rust-cache@v1 + - uses: swatinem/rust-cache@v2 - name: cargo-clippy run: cargo clippy --all --all-targets --all-features diff --git a/src/process.rs b/src/process.rs index 4b388bcc..08d27ba3 100644 --- a/src/process.rs +++ b/src/process.rs @@ -3,6 +3,7 @@ use crate::error::Error; use nix; use nix::fcntl::{open, OFlag}; +use nix::libc::{ioctl, TIOCSCTTY}; use nix::libc::{STDERR_FILENO, STDIN_FILENO, STDOUT_FILENO}; use nix::pty::{grantpt, posix_openpt, unlockpt, PtyMaster}; pub use nix::sys::{signal, wait}; @@ -86,7 +87,7 @@ impl PtyProcess { /// Start a process in a forked pty pub fn new(mut command: Command) -> Result { // Open a new PTY master - let master_fd = posix_openpt(OFlag::O_RDWR)?; + let master_fd = posix_openpt(OFlag::O_RDWR | OFlag::O_NOCTTY)?; // Allow a slave to be generated for it grantpt(&master_fd)?; @@ -95,6 +96,11 @@ impl PtyProcess { // on Linux this is the libc function, on OSX this is our implementation of ptsname_r let slave_name = ptsname_r(&master_fd)?; + // set echo off + let mut flags = termios::tcgetattr(master_fd.as_raw_fd())?; + flags.local_flags.remove(termios::LocalFlags::ECHO); + termios::tcsetattr(master_fd.as_raw_fd(), termios::SetArg::TCSANOW, &flags)?; + match unsafe { fork()? } { ForkResult::Child => { // Avoid leaking master fd @@ -112,6 +118,13 @@ impl PtyProcess { dup2(slave_fd, STDOUT_FILENO)?; dup2(slave_fd, STDERR_FILENO)?; + unsafe { + match ioctl(master_fd.as_raw_fd(), TIOCSCTTY) { + 0 => Ok(()), + _ => Err(nix::Error::last()), + }?; + } + // Avoid leaking slave fd if slave_fd > STDERR_FILENO { close(slave_fd)?; @@ -119,9 +132,17 @@ impl PtyProcess { // set echo off let mut flags = termios::tcgetattr(STDIN_FILENO)?; - flags.local_flags &= !termios::LocalFlags::ECHO; + flags.local_flags.remove(termios::LocalFlags::ECHO); termios::tcsetattr(STDIN_FILENO, termios::SetArg::TCSANOW, &flags)?; + loop { + flags = termios::tcgetattr(STDIN_FILENO)?; + if !flags.local_flags.contains(termios::LocalFlags::ECHO) { + break; + } + std::thread::sleep(std::time::Duration::from_millis(100)); + } + command.exec(); Err(Error::Nix(nix::Error::last())) } diff --git a/src/session.rs b/src/session.rs index 625f4d1b..3d2ceb5b 100644 --- a/src/session.rs +++ b/src/session.rs @@ -29,8 +29,9 @@ impl StreamSession { /// this is guaranteed to be flushed to the process /// returns number of written bytes pub fn send_line(&mut self, line: &str) -> Result { - let mut len = self.send(line)?; + let mut len = self.send_internal(line)?; len += self.writer.write(&[b'\n'])?; + std::thread::sleep(std::time::Duration::from_millis(10)); Ok(len) } @@ -39,6 +40,12 @@ impl StreamSession { /// /// Returns number of written bytes pub fn send(&mut self, s: &str) -> Result { + let len = self.send_internal(s)?; + std::thread::sleep(std::time::Duration::from_millis(10)); + Ok(len) + } + + fn send_internal(&mut self, s: &str) -> Result { self.writer.write(s.as_bytes()).map_err(Error::from) }