improve error handling in pty (#1840)

* improve error handling in pty

* improve error handling in pty

* format code

* attach context to remaining result
This commit is contained in:
哇呜哇呜呀咦耶 2022-10-26 14:51:51 +08:00 committed by GitHub
parent 5793af7655
commit bb9437ff6c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 127 additions and 71 deletions

View file

@ -674,7 +674,7 @@ fn init_session(
config_options.scrollback_editor.clone(), config_options.scrollback_editor.clone(),
); );
move || pty_thread_main(pty, layout) move || pty_thread_main(pty, layout).fatal()
}) })
.unwrap(); .unwrap();

View file

@ -12,6 +12,7 @@ use std::{collections::HashMap, env, os::unix::io::RawFd, path::PathBuf};
use zellij_utils::nix::unistd::Pid; use zellij_utils::nix::unistd::Pid;
use zellij_utils::{ use zellij_utils::{
async_std, async_std,
errors::prelude::*,
errors::{ContextType, PtyContext}, errors::{ContextType, PtyContext},
input::{ input::{
command::{RunCommand, TerminalAction}, command::{RunCommand, TerminalAction},
@ -86,7 +87,8 @@ pub(crate) struct Pty {
default_editor: Option<PathBuf>, default_editor: Option<PathBuf>,
} }
pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) { pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) -> Result<()> {
let err_context = || "failed in pty thread main".to_string();
loop { loop {
let (event, mut err_ctx) = pty.bus.recv().expect("failed to receive event on channel"); let (event, mut err_ctx) = pty.bus.recv().expect("failed to receive event on channel");
err_ctx.add_call(ContextType::Pty((&event).into())); err_ctx.add_call(ContextType::Pty((&event).into()));
@ -115,7 +117,7 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) {
should_float, should_float,
client_or_tab_index, client_or_tab_index,
)) ))
.unwrap(); .with_context(err_context)?;
}, },
Err(SpawnTerminalError::CommandNotFound(pid)) => { Err(SpawnTerminalError::CommandNotFound(pid)) => {
if hold_on_close { if hold_on_close {
@ -127,17 +129,19 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) {
should_float, should_float,
client_or_tab_index, client_or_tab_index,
)) ))
.unwrap(); .with_context(err_context)?;
if let Some(run_command) = run_command { if let Some(run_command) = run_command {
send_command_not_found_to_screen( send_command_not_found_to_screen(
pty.bus.senders.clone(), pty.bus.senders.clone(),
pid, pid,
run_command.clone(), run_command.clone(),
); )
.with_context(err_context)?;
} }
} else { } else {
log::error!("Failed to spawn terminal: command not found"); log::error!("Failed to spawn terminal: command not found");
pty.close_pane(PaneId::Terminal(pid)); pty.close_pane(PaneId::Terminal(pid))
.with_context(err_context)?;
} }
}, },
Err(e) => { Err(e) => {
@ -157,7 +161,7 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) {
PaneId::Terminal(pid), PaneId::Terminal(pid),
client_id, client_id,
)) ))
.unwrap(); .with_context(err_context)?;
}, },
Err(e) => { Err(e) => {
log::error!("Failed to open editor: {}", e); log::error!("Failed to open editor: {}", e);
@ -182,7 +186,7 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) {
pane_title, pane_title,
client_id, client_id,
)) ))
.unwrap(); .with_context(err_context)?;
}, },
Err(SpawnTerminalError::CommandNotFound(pid)) => { Err(SpawnTerminalError::CommandNotFound(pid)) => {
if hold_on_close { if hold_on_close {
@ -193,7 +197,7 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) {
pane_title, pane_title,
client_id, client_id,
)) ))
.unwrap(); .with_context(err_context)?;
if let Some(run_command) = run_command { if let Some(run_command) = run_command {
pty.bus pty.bus
.senders .senders
@ -206,7 +210,7 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) {
.as_bytes() .as_bytes()
.to_vec(), .to_vec(),
)) ))
.unwrap(); .with_context(err_context)?;
pty.bus pty.bus
.senders .senders
.send_to_screen(ScreenInstruction::HoldPane( .send_to_screen(ScreenInstruction::HoldPane(
@ -215,7 +219,7 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) {
run_command, run_command,
None, None,
)) ))
.unwrap(); .with_context(err_context)?;
} }
} }
}, },
@ -242,7 +246,7 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) {
pane_title, pane_title,
client_id, client_id,
)) ))
.unwrap(); .with_context(err_context)?;
}, },
Err(SpawnTerminalError::CommandNotFound(pid)) => { Err(SpawnTerminalError::CommandNotFound(pid)) => {
if hold_on_close { if hold_on_close {
@ -253,7 +257,7 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) {
pane_title, pane_title,
client_id, client_id,
)) ))
.unwrap(); .with_context(err_context)?;
if let Some(run_command) = run_command { if let Some(run_command) = run_command {
pty.bus pty.bus
.senders .senders
@ -266,7 +270,7 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) {
.as_bytes() .as_bytes()
.to_vec(), .to_vec(),
)) ))
.unwrap(); .with_context(err_context)?;
pty.bus pty.bus
.senders .senders
.send_to_screen(ScreenInstruction::HoldPane( .send_to_screen(ScreenInstruction::HoldPane(
@ -275,7 +279,7 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) {
run_command, run_command,
None, None,
)) ))
.unwrap(); .with_context(err_context)?;
} }
} }
}, },
@ -291,43 +295,44 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) {
pty.bus pty.bus
.senders .senders
.send_to_screen(ScreenInstruction::GoToTab(tab_index, Some(client_id))) .send_to_screen(ScreenInstruction::GoToTab(tab_index, Some(client_id)))
.unwrap(); .with_context(err_context)?;
}, },
PtyInstruction::NewTab(terminal_action, tab_layout, tab_name, client_id) => { PtyInstruction::NewTab(terminal_action, tab_layout, tab_name, client_id) => {
pty.spawn_terminals_for_layout( pty.spawn_terminals_for_layout(
tab_layout.unwrap_or_else(|| layout.new_tab()), tab_layout.unwrap_or_else(|| layout.new_tab()),
terminal_action.clone(), terminal_action.clone(),
client_id, client_id,
); )
.with_context(err_context)?;
if let Some(tab_name) = tab_name { if let Some(tab_name) = tab_name {
// clear current name at first // clear current name at first
pty.bus pty.bus
.senders .senders
.send_to_screen(ScreenInstruction::UpdateTabName(vec![0], client_id)) .send_to_screen(ScreenInstruction::UpdateTabName(vec![0], client_id))
.unwrap(); .with_context(err_context)?;
pty.bus pty.bus
.senders .senders
.send_to_screen(ScreenInstruction::UpdateTabName( .send_to_screen(ScreenInstruction::UpdateTabName(
tab_name.into_bytes(), tab_name.into_bytes(),
client_id, client_id,
)) ))
.unwrap(); .with_context(err_context)?;
} }
}, },
PtyInstruction::ClosePane(id) => { PtyInstruction::ClosePane(id) => {
pty.close_pane(id); pty.close_pane(id).with_context(err_context)?;
pty.bus pty.bus
.senders .senders
.send_to_server(ServerInstruction::UnblockInputThread) .send_to_server(ServerInstruction::UnblockInputThread)
.unwrap(); .with_context(err_context)?;
}, },
PtyInstruction::CloseTab(ids) => { PtyInstruction::CloseTab(ids) => {
pty.close_tab(ids); pty.close_tab(ids).with_context(err_context)?;
pty.bus pty.bus
.senders .senders
.send_to_server(ServerInstruction::UnblockInputThread) .send_to_server(ServerInstruction::UnblockInputThread)
.unwrap(); .with_context(err_context)?;
}, },
PtyInstruction::ReRunCommandInPane(pane_id, run_command) => { PtyInstruction::ReRunCommandInPane(pane_id, run_command) => {
match pty.rerun_command_in_pane(pane_id, run_command.clone()) { match pty.rerun_command_in_pane(pane_id, run_command.clone()) {
@ -342,7 +347,7 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) {
.as_bytes() .as_bytes()
.to_vec(), .to_vec(),
)) ))
.unwrap(); .with_context(err_context)?;
pty.bus pty.bus
.senders .senders
.send_to_screen(ScreenInstruction::HoldPane( .send_to_screen(ScreenInstruction::HoldPane(
@ -351,7 +356,7 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) {
run_command, run_command,
None, None,
)) ))
.unwrap(); .with_context(err_context)?;
} }
}, },
Err(e) => { Err(e) => {
@ -362,6 +367,7 @@ pub(crate) fn pty_thread_main(mut pty: Pty, layout: Box<Layout>) {
PtyInstruction::Exit => break, PtyInstruction::Exit => break,
} }
} }
Ok(())
} }
impl Pty { impl Pty {
@ -449,8 +455,15 @@ impl Pty {
.unwrap() .unwrap()
.spawn_terminal(terminal_action, quit_cb, self.default_editor.clone())?; .spawn_terminal(terminal_action, quit_cb, self.default_editor.clone())?;
let terminal_bytes = task::spawn({ let terminal_bytes = task::spawn({
let err_context = || format!("failed to run async task for terminal {terminal_id}");
let senders = self.bus.senders.clone(); let senders = self.bus.senders.clone();
let os_input = self.bus.os_input.as_ref().unwrap().clone(); let os_input = self
.bus
.os_input
.as_ref()
.with_context(err_context)
.fatal()
.clone();
let debug_to_file = self.debug_to_file; let debug_to_file = self.debug_to_file;
async move { async move {
TerminalBytes::new(pid_primary, senders, os_input, debug_to_file, terminal_id) TerminalBytes::new(pid_primary, senders, os_input, debug_to_file, terminal_id)
@ -468,7 +481,8 @@ impl Pty {
layout: PaneLayout, layout: PaneLayout,
default_shell: Option<TerminalAction>, default_shell: Option<TerminalAction>,
client_id: ClientId, client_id: ClientId,
) { ) -> Result<()> {
let err_context = || format!("failed to spawn terminals for layout for client {client_id}");
let mut default_shell = default_shell.unwrap_or_else(|| self.get_default_terminal(None)); let mut default_shell = default_shell.unwrap_or_else(|| self.get_default_terminal(None));
self.fill_cwd(&mut default_shell, client_id); self.fill_cwd(&mut default_shell, client_id);
let extracted_run_instructions = layout.extract_run_instructions(); let extracted_run_instructions = layout.extract_run_instructions();
@ -503,11 +517,13 @@ impl Pty {
} }
}); });
let cmd = TerminalAction::RunCommand(command.clone()); let cmd = TerminalAction::RunCommand(command.clone());
match self.bus.os_input.as_mut().unwrap().spawn_terminal( match self
cmd, .bus
quit_cb, .os_input
self.default_editor.clone(), .as_mut()
) { .with_context(err_context)?
.spawn_terminal(cmd, quit_cb, self.default_editor.clone())
{
Ok((terminal_id, pid_primary, child_fd)) => { Ok((terminal_id, pid_primary, child_fd)) => {
self.id_to_child_pid.insert(terminal_id, child_fd); self.id_to_child_pid.insert(terminal_id, child_fd);
new_pane_pids.push(( new_pane_pids.push((
@ -530,11 +546,13 @@ impl Pty {
}, },
Some(Run::Cwd(cwd)) => { Some(Run::Cwd(cwd)) => {
let shell = self.get_default_terminal(Some(cwd)); let shell = self.get_default_terminal(Some(cwd));
match self.bus.os_input.as_mut().unwrap().spawn_terminal( match self
shell, .bus
quit_cb, .os_input
self.default_editor.clone(), .as_mut()
) { .with_context(err_context)?
.spawn_terminal(shell, quit_cb, self.default_editor.clone())
{
Ok((terminal_id, pid_primary, child_fd)) => { Ok((terminal_id, pid_primary, child_fd)) => {
self.id_to_child_pid.insert(terminal_id, child_fd); self.id_to_child_pid.insert(terminal_id, child_fd);
new_pane_pids.push((terminal_id, None, Ok(pid_primary))); new_pane_pids.push((terminal_id, None, Ok(pid_primary)));
@ -552,7 +570,12 @@ impl Pty {
} }
}, },
Some(Run::EditFile(path_to_file, line_number)) => { Some(Run::EditFile(path_to_file, line_number)) => {
match self.bus.os_input.as_mut().unwrap().spawn_terminal( match self
.bus
.os_input
.as_mut()
.with_context(err_context)?
.spawn_terminal(
TerminalAction::OpenFile(path_to_file, line_number), TerminalAction::OpenFile(path_to_file, line_number),
quit_cb, quit_cb,
self.default_editor.clone(), self.default_editor.clone(),
@ -574,11 +597,13 @@ impl Pty {
} }
}, },
None => { None => {
match self.bus.os_input.as_mut().unwrap().spawn_terminal( match self
default_shell.clone(), .bus
quit_cb, .os_input
self.default_editor.clone(), .as_mut()
) { .with_context(err_context)?
.spawn_terminal(default_shell.clone(), quit_cb, self.default_editor.clone())
{
Ok((terminal_id, pid_primary, child_fd)) => { Ok((terminal_id, pid_primary, child_fd)) => {
self.id_to_child_pid.insert(terminal_id, child_fd); self.id_to_child_pid.insert(terminal_id, child_fd);
new_pane_pids.push((terminal_id, None, Ok(pid_primary))); new_pane_pids.push((terminal_id, None, Ok(pid_primary)));
@ -610,13 +635,18 @@ impl Pty {
new_tab_pane_ids, new_tab_pane_ids,
client_id, client_id,
)) ))
.unwrap(); .with_context(err_context)?;
for (terminal_id, run_command, pid_primary) in new_pane_pids { for (terminal_id, run_command, pid_primary) in new_pane_pids {
match pid_primary { match pid_primary {
Ok(pid_primary) => { Ok(pid_primary) => {
let terminal_bytes = task::spawn({ let terminal_bytes = task::spawn({
let senders = self.bus.senders.clone(); let senders = self.bus.senders.clone();
let os_input = self.bus.os_input.as_ref().unwrap().clone(); let os_input = self
.bus
.os_input
.as_ref()
.with_context(err_context)?
.clone();
let debug_to_file = self.debug_to_file; let debug_to_file = self.debug_to_file;
async move { async move {
TerminalBytes::new( TerminalBytes::new(
@ -639,33 +669,45 @@ impl Pty {
self.bus.senders.clone(), self.bus.senders.clone(),
terminal_id, terminal_id,
run_command.clone(), run_command.clone(),
); )
.with_context(err_context)?;
} else { } else {
self.close_pane(PaneId::Terminal(terminal_id)); self.close_pane(PaneId::Terminal(terminal_id))
.with_context(err_context)?;
} }
}, },
None => { None => {
self.close_pane(PaneId::Terminal(terminal_id)); self.close_pane(PaneId::Terminal(terminal_id))
.with_context(err_context)?;
}, },
}, },
} }
} }
Ok(())
} }
pub fn close_pane(&mut self, id: PaneId) { pub fn close_pane(&mut self, id: PaneId) -> Result<()> {
let err_context = || format!("failed to close for pane {id:?}");
match id { match id {
PaneId::Terminal(id) => { PaneId::Terminal(id) => {
self.task_handles.remove(&id); self.task_handles.remove(&id);
if let Some(child_fd) = self.id_to_child_pid.remove(&id) { if let Some(child_fd) = self.id_to_child_pid.remove(&id) {
task::block_on(async { task::block_on(async {
let err_context = || format!("failed to run async task for pane {id}");
self.bus self.bus
.os_input .os_input
.as_mut() .as_mut()
.unwrap() .with_context(err_context)
.fatal()
.kill(Pid::from_raw(child_fd)) .kill(Pid::from_raw(child_fd))
.unwrap(); .with_context(err_context)
.fatal();
}); });
} }
self.bus.os_input.as_ref().unwrap().clear_terminal_id(id); self.bus
.os_input
.as_ref()
.with_context(err_context)?
.clear_terminal_id(id);
}, },
PaneId::Plugin(pid) => drop( PaneId::Plugin(pid) => drop(
self.bus self.bus
@ -673,11 +715,14 @@ impl Pty {
.send_to_plugin(PluginInstruction::Unload(pid)), .send_to_plugin(PluginInstruction::Unload(pid)),
), ),
} }
Ok(())
} }
pub fn close_tab(&mut self, ids: Vec<PaneId>) { pub fn close_tab(&mut self, ids: Vec<PaneId>) -> Result<()> {
ids.iter().for_each(|&id| { for id in ids {
self.close_pane(id); self.close_pane(id)
}); .with_context(|| format!("failed to close tab for pane {id:?}"))?;
}
Ok(())
} }
pub fn set_active_pane(&mut self, pane_id: Option<PaneId>, client_id: ClientId) { pub fn set_active_pane(&mut self, pane_id: Option<PaneId>, client_id: ClientId) {
if let Some(pane_id) = pane_id { if let Some(pane_id) = pane_id {
@ -706,15 +751,22 @@ impl Pty {
)); ));
} }
}); });
let (pid_primary, child_fd): (RawFd, RawFd) = let (pid_primary, child_fd): (RawFd, RawFd) = self
self.bus .bus
.os_input .os_input
.as_mut() .as_mut()
.unwrap() .ok_or_else(|| SpawnTerminalError::GenericSpawnError("os input is none"))?
.re_run_command_in_terminal(id, run_command, quit_cb)?; .re_run_command_in_terminal(id, run_command, quit_cb)?;
let terminal_bytes = task::spawn({ let terminal_bytes = task::spawn({
let err_context = || format!("failed to run async task for pane {pane_id:?}");
let senders = self.bus.senders.clone(); let senders = self.bus.senders.clone();
let os_input = self.bus.os_input.as_ref().unwrap().clone(); let os_input = self
.bus
.os_input
.as_ref()
.with_context(err_context)
.fatal()
.clone();
let debug_to_file = self.debug_to_file; let debug_to_file = self.debug_to_file;
async move { async move {
TerminalBytes::new(pid_primary, senders, os_input, debug_to_file, id) TerminalBytes::new(pid_primary, senders, os_input, debug_to_file, id)
@ -738,7 +790,9 @@ impl Drop for Pty {
fn drop(&mut self) { fn drop(&mut self) {
let child_ids: Vec<u32> = self.id_to_child_pid.keys().copied().collect(); let child_ids: Vec<u32> = self.id_to_child_pid.keys().copied().collect();
for id in child_ids { for id in child_ids {
self.close_pane(PaneId::Terminal(id)); self.close_pane(PaneId::Terminal(id))
.with_context(|| format!("failed to close pane for pid {id}"))
.fatal();
} }
} }
} }
@ -747,7 +801,8 @@ fn send_command_not_found_to_screen(
senders: ThreadSenders, senders: ThreadSenders,
terminal_id: u32, terminal_id: u32,
run_command: RunCommand, run_command: RunCommand,
) { ) -> Result<()> {
let err_context = || format!("failed to send command_not_fount for terminal {terminal_id}");
senders senders
.send_to_screen(ScreenInstruction::PtyBytes( .send_to_screen(ScreenInstruction::PtyBytes(
terminal_id, terminal_id,
@ -755,7 +810,7 @@ fn send_command_not_found_to_screen(
.as_bytes() .as_bytes()
.to_vec(), .to_vec(),
)) ))
.unwrap(); .with_context(err_context)?;
senders senders
.send_to_screen(ScreenInstruction::HoldPane( .send_to_screen(ScreenInstruction::HoldPane(
PaneId::Terminal(terminal_id), PaneId::Terminal(terminal_id),
@ -763,5 +818,6 @@ fn send_command_not_found_to_screen(
run_command.clone(), run_command.clone(),
None, None,
)) ))
.unwrap(); .with_context(err_context)?;
Ok(())
} }