diff options
Diffstat (limited to 'src/tty/unix.rs')
-rw-r--r-- | src/tty/unix.rs | 183 |
1 files changed, 92 insertions, 91 deletions
diff --git a/src/tty/unix.rs b/src/tty/unix.rs index 2a07fc58..d8392dcd 100644 --- a/src/tty/unix.rs +++ b/src/tty/unix.rs @@ -15,54 +15,33 @@ //! tty related functionality //! -use crate::tty::EventedReadWrite; +use crate::tty::{EventedReadWrite, EventedPty, ChildEvent}; use crate::term::SizeInfo; use crate::display::OnResize; use crate::config::{Config, Shell}; use crate::cli::Options; use mio; -use libc::{self, c_int, pid_t, winsize, SIGCHLD, TIOCSCTTY, WNOHANG}; +use libc::{self, c_int, pid_t, winsize, TIOCSCTTY}; +use nix::pty::openpty; +use signal_hook::{self as sighook, iterator::Signals}; -use std::os::unix::io::{FromRawFd, RawFd}; +use std::os::unix::{process::CommandExt, io::{FromRawFd, AsRawFd, RawFd}}; use std::fs::File; -use std::os::unix::process::CommandExt; -use std::process::{Command, Stdio}; +use std::process::{Command, Stdio, Child}; use std::ffi::CStr; use std::ptr; use mio::unix::EventedFd; use std::io; -use std::os::unix::io::AsRawFd; - +use std::sync::atomic::{AtomicUsize, Ordering}; /// Process ID of child process /// /// Necessary to put this in static storage for `sigchld` to have access -pub static mut PID: pid_t = 0; - -/// Exit flag -/// -/// Calling exit() in the SIGCHLD handler sometimes causes opengl to deadlock, -/// and the process hangs. Instead, this flag is set, and its status can be -/// checked via `process_should_exit`. -static mut SHOULD_EXIT: bool = false; - -extern "C" fn sigchld(_a: c_int) { - let mut status: c_int = 0; - unsafe { - let p = libc::waitpid(PID, &mut status, WNOHANG); - if p < 0 { - die!("Waiting for pid {} failed: {}\n", PID, errno()); - } - - if PID == p { - SHOULD_EXIT = true; - } - } -} +static PID: AtomicUsize = AtomicUsize::new(0); -pub fn process_should_exit() -> bool { - unsafe { SHOULD_EXIT } +pub fn child_pid() -> pid_t { + PID.load(Ordering::Relaxed) as pid_t } /// Get the current value of errno @@ -71,50 +50,15 @@ fn errno() -> c_int { } /// Get raw fds for master/slave ends of a new pty -#[cfg(target_os = "linux")] -fn openpty(rows: u8, cols: u8) -> (c_int, c_int) { - let mut master: c_int = 0; - let mut slave: c_int = 0; - - let win = winsize { - ws_row: libc::c_ushort::from(rows), - ws_col: libc::c_ushort::from(cols), - ws_xpixel: 0, - ws_ypixel: 0, - }; - - let res = unsafe { - libc::openpty(&mut master, &mut slave, ptr::null_mut(), ptr::null(), &win) - }; - - if res < 0 { - die!("openpty failed"); - } - - (master, slave) -} - -#[cfg(any(target_os = "macos",target_os = "freebsd",target_os = "openbsd"))] -fn openpty(rows: u8, cols: u8) -> (c_int, c_int) { - let mut master: c_int = 0; - let mut slave: c_int = 0; - - let mut win = winsize { - ws_row: libc::c_ushort::from(rows), - ws_col: libc::c_ushort::from(cols), - ws_xpixel: 0, - ws_ypixel: 0, - }; - - let res = unsafe { - libc::openpty(&mut master, &mut slave, ptr::null_mut(), ptr::null_mut(), &mut win) - }; +fn make_pty(size: winsize) -> (RawFd, RawFd) { + let mut win_size = size; + win_size.ws_xpixel = 0; + win_size.ws_ypixel = 0; - if res < 0 { - die!("openpty failed"); - } + let ends = openpty(Some(&win_size), None) + .expect("openpty failed"); - (master, slave) + (ends.master, ends.slave) } /// Really only needed on BSD, but should be fine elsewhere @@ -185,9 +129,12 @@ fn get_pw_entry(buf: &mut [i8; 1024]) -> Passwd<'_> { } pub struct Pty { + child: Child, pub fd: File, - pub raw_fd: RawFd, token: mio::Token, + signals: Signals, + signals_token: mio::Token, + } impl Pty { @@ -215,11 +162,11 @@ pub fn new<T: ToWinsize>( size: &T, window_id: Option<usize>, ) -> Pty { - let win = size.to_winsize(); + let win_size = size.to_winsize(); let mut buf = [0; 1024]; let pw = get_pw_entry(&mut buf); - let (master, slave) = openpty(win.ws_row as _, win.ws_col as _); + let (master, slave) = make_pty(win_size); let default_shell = if cfg!(target_os = "macos") { let shell_name = pw.shell.rsplit('/').next().unwrap(); @@ -292,15 +239,14 @@ pub fn new<T: ToWinsize>( builder.current_dir(dir.as_path()); } + // Prepare signal handling before spawning child + let signals = Signals::new(&[sighook::SIGCHLD]).expect("error preparing signal handling"); + match builder.spawn() { Ok(child) => { - unsafe { - // Set PID for SIGCHLD handler - PID = child.id() as _; + // Remember child PID so other modules can use it + PID.store(child.id() as usize, Ordering::Relaxed); - // Handle SIGCHLD - libc::signal(SIGCHLD, sigchld as _); - } unsafe { // Maybe this should be done outside of this function so nonblocking // isn't forced upon consumers. Although maybe it should be? @@ -308,9 +254,11 @@ pub fn new<T: ToWinsize>( } let pty = Pty { - fd: unsafe {File::from_raw_fd(master) }, - raw_fd: master, - token: mio::Token::from(0) + child, + fd: unsafe { File::from_raw_fd(master) }, + token: mio::Token::from(0), + signals, + signals_token: mio::Token::from(0), }; pty.resize(size); pty @@ -329,32 +277,53 @@ impl EventedReadWrite for Pty { fn register( &mut self, poll: &mio::Poll, - token: &mut dyn Iterator<Item = &usize>, + token: &mut dyn Iterator<Item = mio::Token>, interest: mio::Ready, poll_opts: mio::PollOpt, ) -> io::Result<()> { - self.token = (*token.next().unwrap()).into(); + self.token = token.next().unwrap(); poll.register( - &EventedFd(&self.raw_fd), + &EventedFd(&self.fd.as_raw_fd()), self.token, interest, poll_opts + )?; + + self.signals_token = token.next().unwrap(); + poll.register( + &self.signals, + self.signals_token, + mio::Ready::readable(), + mio::PollOpt::level() ) } #[inline] - fn reregister(&mut self, poll: &mio::Poll, interest: mio::Ready, poll_opts: mio::PollOpt) -> io::Result<()> { + fn reregister( + &mut self, + poll: &mio::Poll, + interest: mio::Ready, + poll_opts: mio::PollOpt + ) -> io::Result<()> { poll.reregister( - &EventedFd(&self.raw_fd), + &EventedFd(&self.fd.as_raw_fd()), self.token, interest, poll_opts + )?; + + poll.reregister( + &self.signals, + self.signals_token, + mio::Ready::readable(), + mio::PollOpt::level() ) } #[inline] fn deregister(&mut self, poll: &mio::Poll) -> io::Result<()> { - poll.deregister(&EventedFd(&self.raw_fd)) + poll.deregister(&EventedFd(&self.fd.as_raw_fd()))?; + poll.deregister(&self.signals) } #[inline] @@ -378,6 +347,38 @@ impl EventedReadWrite for Pty { } } +impl EventedPty for Pty { + #[inline] + fn next_child_event(&mut self) -> Option<ChildEvent> { + self.signals + .pending() + .next() + .and_then(|signal| { + if signal != sighook::SIGCHLD { + return None; + } + + match self.child.try_wait() { + Err(e) => { + error!("Error checking child process termination: {}", e); + None + }, + Ok(None) => None, + Ok(_) => Some(ChildEvent::Exited), + } + }) + } + + #[inline] + fn child_event_token(&self) -> mio::Token { + self.signals_token + } +} + +pub fn process_should_exit() -> bool { + false +} + /// Types that can produce a `libc::winsize` pub trait ToWinsize { /// Get a `libc::winsize` |