diff --git a/mux/src/localpane.rs b/mux/src/localpane.rs index f6a381c0b..f9afb094d 100644 --- a/mux/src/localpane.rs +++ b/mux/src/localpane.rs @@ -164,8 +164,9 @@ impl Pane for LocalPane { (ExitBehavior::Close, _, _) => *proc = ProcessState::Dead, (ExitBehavior::CloseOnCleanExit, false, false) => { notify = Some(format!( - "\r\n⚠️ Process {} didn't exit cleanly.\r\n{}=\"CloseOnCleanExit\"\r\n", + "\r\n⚠️ Process {} didn't exit cleanly\r\n{}.\r\n{}=\"CloseOnCleanExit\"\r\n", self.command_description, + status, EXIT_BEHAVIOR )); *proc = ProcessState::DeadPendingClose { killed: false } @@ -179,8 +180,10 @@ impl Pane for LocalPane { )); } else { notify = Some(format!( - "\r\n⚠️ Process {} didn't exit cleanly.\r\n{}=\"Hold\"\r\n", - self.command_description, EXIT_BEHAVIOR + "\r\n⚠️ Process {} didn't exit cleanly\r\n{}.\r\n{}=\"Hold\"\r\n", + self.command_description, + status, + EXIT_BEHAVIOR )); } *proc = ProcessState::DeadPendingClose { killed: false } diff --git a/pty/src/lib.rs b/pty/src/lib.rs index 08dca0572..b46cfe493 100644 --- a/pty/src/lib.rs +++ b/pty/src/lib.rs @@ -154,30 +154,76 @@ pub trait SlavePty { } /// Represents the exit status of a child process. -/// This is rather anemic in the current version of this crate, -/// holding only an indicator of success or failure. #[derive(Debug, Clone)] pub struct ExitStatus { - successful: bool, + code: u32, + signal: Option, } impl ExitStatus { /// Construct an ExitStatus from a process return code pub fn with_exit_code(code: u32) -> Self { + Self { code, signal: None } + } + + /// Construct an ExitStatus from a signal name + pub fn with_signal(signal: &str) -> Self { Self { - successful: code == 0, + code: 1, + signal: Some(signal.to_string()), } } + /// Returns true if the status indicates successful completion pub fn success(&self) -> bool { - self.successful + match self.signal { + None => self.code == 0, + Some(_) => false, + } } } impl From for ExitStatus { fn from(status: std::process::ExitStatus) -> ExitStatus { - ExitStatus { - successful: status.success(), + #[cfg(unix)] + { + use std::os::unix::process::ExitStatusExt; + + if let Some(signal) = status.signal() { + let signame = unsafe { libc::strsignal(signal) }; + let signal = if signame.is_null() { + format!("Signal {}", signal) + } else { + let signame = unsafe { std::ffi::CStr::from_ptr(signame) }; + signame.to_string_lossy().to_string() + }; + + return ExitStatus { + code: status.code().map(|c| c as u32).unwrap_or(1), + signal: Some(signal), + }; + } + } + + let code = + status + .code() + .map(|c| c as u32) + .unwrap_or_else(|| if status.success() { 0 } else { 1 }); + + ExitStatus { code, signal: None } + } +} + +impl std::fmt::Display for ExitStatus { + fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result { + if self.success() { + write!(fmt, "Success") + } else { + match &self.signal { + Some(sig) => write!(fmt, "Terminated by {}", sig), + None => write!(fmt, "Exited with code {}", self.code), + } } } } diff --git a/wezterm-ssh/src/channelwrap.rs b/wezterm-ssh/src/channelwrap.rs index 921b11d46..b3178c663 100644 --- a/wezterm-ssh/src/channelwrap.rs +++ b/wezterm-ssh/src/channelwrap.rs @@ -25,8 +25,10 @@ impl ChannelWrap { #[cfg(feature = "ssh2")] Self::Ssh2(chan) => { if chan.eof() && chan.wait_close().is_ok() { - if let Some(_sig) = has_signal(chan) { - Some(ExitStatus::with_exit_code(1)) + if let Some(sig) = has_signal(chan) { + Some(ExitStatus::with_signal( + sig.exit_signal.as_deref().unwrap_or("Unknown signal"), + )) } else if let Ok(status) = chan.exit_status() { Some(ExitStatus::with_exit_code(status as _)) } else {