Fix double panic lockup in clients panic handler (#1433)

* Fix possible lockup in the clients panic handler

When the pty the client was running in disappears, reading from stdin
causes a panic, which triggers the custom panic handler. This handler
attempts to print a backtrace to the terminal and tries to unset the raw
mode for that. Since the pty has already disappeared, the tcsetattr call
fails and causes a second panic, which locks everything up.

This commit fixes this by returning an Result from the unset_raw_mode
function, allowing the calling panic handler to handle any error
gracefully.

* Log any client panics to file

Since we are now aware of the fact that panics may happen / are handled
after the pty has disappeared, logging them to file seems useful: there
is no other other place to show them to the user.

* fix tests and improve function return type
This commit is contained in:
raphCode 2022-05-24 14:10:37 +02:00 committed by GitHub
parent 69ec7c7e3a
commit eab464b11a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 15 additions and 14 deletions

View file

@ -5,6 +5,7 @@ mod input_handler;
mod stdin_ansi_parser;
mod stdin_handler;
use log::error;
use log::info;
use std::env::current_exe;
use std::io::{self, Write};
@ -124,7 +125,7 @@ pub fn start_client(
let clear_client_terminal_attributes = "\u{1b}[?1l\u{1b}=\u{1b}[r\u{1b}12l\u{1b}[?1000l\u{1b}[?1002l\u{1b}[?1003l\u{1b}[?1005l\u{1b}[?1006l\u{1b}[?12l";
let take_snapshot = "\u{1b}[?1049h";
let bracketed_paste = "\u{1b}[?2004h";
os_input.unset_raw_mode(0);
os_input.unset_raw_mode(0).unwrap();
let _ = os_input
.get_stdout_writer()
@ -201,8 +202,10 @@ pub fn start_client(
let send_client_instructions = send_client_instructions.clone();
let os_input = os_input.clone();
Box::new(move |info| {
os_input.unset_raw_mode(0);
handle_panic(info, &send_client_instructions);
error!("Panic occured in client:\n{:?}", info);
if let Ok(()) = os_input.unset_raw_mode(0) {
handle_panic(info, &send_client_instructions);
}
})
});
@ -293,7 +296,7 @@ pub fn start_client(
.unwrap();
let handle_error = |backtrace: String| {
os_input.unset_raw_mode(0);
os_input.unset_raw_mode(0).unwrap();
let goto_start_of_last_line = format!("\u{1b}[{};{}H", full_screen_ws.rows, 1);
let restore_snapshot = "\u{1b}[?1049l";
os_input.disable_mouse();
@ -364,7 +367,7 @@ pub fn start_client(
os_input.disable_mouse();
info!("{}", exit_msg);
os_input.unset_raw_mode(0);
os_input.unset_raw_mode(0).unwrap();
let mut stdout = os_input.get_stdout_writer();
let _ = stdout.write(goodbye_message.as_bytes()).unwrap();
stdout.flush().unwrap();

View file

@ -32,11 +32,8 @@ fn into_raw_mode(pid: RawFd) {
};
}
fn unset_raw_mode(pid: RawFd, orig_termios: termios::Termios) {
match termios::tcsetattr(pid, termios::SetArg::TCSANOW, &orig_termios) {
Ok(_) => {}
Err(e) => panic!("error {:?}", e),
};
fn unset_raw_mode(pid: RawFd, orig_termios: termios::Termios) -> Result<(), nix::Error> {
termios::tcsetattr(pid, termios::SetArg::TCSANOW, &orig_termios)
}
pub(crate) fn get_terminal_size_using_fd(fd: RawFd) -> Size {
@ -81,7 +78,7 @@ pub trait ClientOsApi: Send + Sync {
fn set_raw_mode(&mut self, fd: RawFd);
/// Set the terminal associated to file descriptor `fd` to
/// [cooked mode](https://en.wikipedia.org/wiki/Terminal_mode).
fn unset_raw_mode(&self, fd: RawFd);
fn unset_raw_mode(&self, fd: RawFd) -> Result<(), nix::Error>;
/// Returns the writer that allows writing to standard output.
fn get_stdout_writer(&self) -> Box<dyn io::Write>;
fn get_stdin_reader(&self) -> Box<dyn io::Read>;
@ -111,9 +108,9 @@ impl ClientOsApi for ClientOsInputOutput {
fn set_raw_mode(&mut self, fd: RawFd) {
into_raw_mode(fd);
}
fn unset_raw_mode(&self, fd: RawFd) {
fn unset_raw_mode(&self, fd: RawFd) -> Result<(), nix::Error> {
let orig_termios = self.orig_termios.lock().unwrap();
unset_raw_mode(fd, orig_termios.clone());
unset_raw_mode(fd, orig_termios.clone())
}
fn box_clone(&self) -> Box<dyn ClientOsApi> {
Box::new((*self).clone())

View file

@ -2,6 +2,7 @@ use super::input_loop;
use zellij_utils::input::actions::{Action, Direction};
use zellij_utils::input::config::Config;
use zellij_utils::input::options::Options;
use zellij_utils::nix;
use zellij_utils::pane_size::{Size, SizeInPixels};
use zellij_utils::termwiz::input::{InputEvent, KeyCode, KeyEvent, Modifiers};
use zellij_utils::zellij_tile::data::Palette;
@ -125,7 +126,7 @@ impl ClientOsApi for FakeClientOsApi {
fn set_raw_mode(&mut self, _fd: RawFd) {
unimplemented!()
}
fn unset_raw_mode(&self, _fd: RawFd) {
fn unset_raw_mode(&self, _fd: RawFd) -> Result<(), nix::Error> {
unimplemented!()
}
fn get_stdout_writer(&self) -> Box<dyn io::Write> {