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_ansi_parser;
mod stdin_handler; mod stdin_handler;
use log::error;
use log::info; use log::info;
use std::env::current_exe; use std::env::current_exe;
use std::io::{self, Write}; 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 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 take_snapshot = "\u{1b}[?1049h";
let bracketed_paste = "\u{1b}[?2004h"; let bracketed_paste = "\u{1b}[?2004h";
os_input.unset_raw_mode(0); os_input.unset_raw_mode(0).unwrap();
let _ = os_input let _ = os_input
.get_stdout_writer() .get_stdout_writer()
@ -201,8 +202,10 @@ pub fn start_client(
let send_client_instructions = send_client_instructions.clone(); let send_client_instructions = send_client_instructions.clone();
let os_input = os_input.clone(); let os_input = os_input.clone();
Box::new(move |info| { Box::new(move |info| {
os_input.unset_raw_mode(0); error!("Panic occured in client:\n{:?}", info);
if let Ok(()) = os_input.unset_raw_mode(0) {
handle_panic(info, &send_client_instructions); handle_panic(info, &send_client_instructions);
}
}) })
}); });
@ -293,7 +296,7 @@ pub fn start_client(
.unwrap(); .unwrap();
let handle_error = |backtrace: String| { 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 goto_start_of_last_line = format!("\u{1b}[{};{}H", full_screen_ws.rows, 1);
let restore_snapshot = "\u{1b}[?1049l"; let restore_snapshot = "\u{1b}[?1049l";
os_input.disable_mouse(); os_input.disable_mouse();
@ -364,7 +367,7 @@ pub fn start_client(
os_input.disable_mouse(); os_input.disable_mouse();
info!("{}", exit_msg); 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 mut stdout = os_input.get_stdout_writer();
let _ = stdout.write(goodbye_message.as_bytes()).unwrap(); let _ = stdout.write(goodbye_message.as_bytes()).unwrap();
stdout.flush().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) { fn unset_raw_mode(pid: RawFd, orig_termios: termios::Termios) -> Result<(), nix::Error> {
match termios::tcsetattr(pid, termios::SetArg::TCSANOW, &orig_termios) { termios::tcsetattr(pid, termios::SetArg::TCSANOW, &orig_termios)
Ok(_) => {}
Err(e) => panic!("error {:?}", e),
};
} }
pub(crate) fn get_terminal_size_using_fd(fd: RawFd) -> Size { 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); fn set_raw_mode(&mut self, fd: RawFd);
/// Set the terminal associated to file descriptor `fd` to /// Set the terminal associated to file descriptor `fd` to
/// [cooked mode](https://en.wikipedia.org/wiki/Terminal_mode). /// [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. /// Returns the writer that allows writing to standard output.
fn get_stdout_writer(&self) -> Box<dyn io::Write>; fn get_stdout_writer(&self) -> Box<dyn io::Write>;
fn get_stdin_reader(&self) -> Box<dyn io::Read>; 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) { fn set_raw_mode(&mut self, fd: RawFd) {
into_raw_mode(fd); 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(); 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> { fn box_clone(&self) -> Box<dyn ClientOsApi> {
Box::new((*self).clone()) 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::actions::{Action, Direction};
use zellij_utils::input::config::Config; use zellij_utils::input::config::Config;
use zellij_utils::input::options::Options; use zellij_utils::input::options::Options;
use zellij_utils::nix;
use zellij_utils::pane_size::{Size, SizeInPixels}; use zellij_utils::pane_size::{Size, SizeInPixels};
use zellij_utils::termwiz::input::{InputEvent, KeyCode, KeyEvent, Modifiers}; use zellij_utils::termwiz::input::{InputEvent, KeyCode, KeyEvent, Modifiers};
use zellij_utils::zellij_tile::data::Palette; use zellij_utils::zellij_tile::data::Palette;
@ -125,7 +126,7 @@ impl ClientOsApi for FakeClientOsApi {
fn set_raw_mode(&mut self, _fd: RawFd) { fn set_raw_mode(&mut self, _fd: RawFd) {
unimplemented!() unimplemented!()
} }
fn unset_raw_mode(&self, _fd: RawFd) { fn unset_raw_mode(&self, _fd: RawFd) -> Result<(), nix::Error> {
unimplemented!() unimplemented!()
} }
fn get_stdout_writer(&self) -> Box<dyn io::Write> { fn get_stdout_writer(&self) -> Box<dyn io::Write> {