1
1
mirror of https://github.com/wez/wezterm.git synced 2024-12-23 05:12:40 +03:00

pty: ExitStatus now understands signals and impl Display

Improves the fidelity of the information in ExitStatus and shows it
in the wezterm exit_behavior output to clarify the status.
This commit is contained in:
Wez Furlong 2022-04-09 06:14:03 -07:00
parent 317bfeef41
commit 0f6ee20b28
3 changed files with 63 additions and 12 deletions

View File

@ -164,8 +164,9 @@ impl Pane for LocalPane {
(ExitBehavior::Close, _, _) => *proc = ProcessState::Dead, (ExitBehavior::Close, _, _) => *proc = ProcessState::Dead,
(ExitBehavior::CloseOnCleanExit, false, false) => { (ExitBehavior::CloseOnCleanExit, false, false) => {
notify = Some(format!( 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, self.command_description,
status,
EXIT_BEHAVIOR EXIT_BEHAVIOR
)); ));
*proc = ProcessState::DeadPendingClose { killed: false } *proc = ProcessState::DeadPendingClose { killed: false }
@ -179,8 +180,10 @@ impl Pane for LocalPane {
)); ));
} else { } else {
notify = Some(format!( notify = Some(format!(
"\r\n⚠️ Process {} didn't exit cleanly.\r\n{}=\"Hold\"\r\n", "\r\n⚠️ Process {} didn't exit cleanly\r\n{}.\r\n{}=\"Hold\"\r\n",
self.command_description, EXIT_BEHAVIOR self.command_description,
status,
EXIT_BEHAVIOR
)); ));
} }
*proc = ProcessState::DeadPendingClose { killed: false } *proc = ProcessState::DeadPendingClose { killed: false }

View File

@ -154,30 +154,76 @@ pub trait SlavePty {
} }
/// Represents the exit status of a child process. /// 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)] #[derive(Debug, Clone)]
pub struct ExitStatus { pub struct ExitStatus {
successful: bool, code: u32,
signal: Option<String>,
} }
impl ExitStatus { impl ExitStatus {
/// Construct an ExitStatus from a process return code /// Construct an ExitStatus from a process return code
pub fn with_exit_code(code: u32) -> Self { 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 { Self {
successful: code == 0, code: 1,
signal: Some(signal.to_string()),
} }
} }
/// Returns true if the status indicates successful completion
pub fn success(&self) -> bool { pub fn success(&self) -> bool {
self.successful match self.signal {
None => self.code == 0,
Some(_) => false,
}
} }
} }
impl From<std::process::ExitStatus> for ExitStatus { impl From<std::process::ExitStatus> for ExitStatus {
fn from(status: std::process::ExitStatus) -> ExitStatus { fn from(status: std::process::ExitStatus) -> ExitStatus {
ExitStatus { #[cfg(unix)]
successful: status.success(), {
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),
}
} }
} }
} }

View File

@ -25,8 +25,10 @@ impl ChannelWrap {
#[cfg(feature = "ssh2")] #[cfg(feature = "ssh2")]
Self::Ssh2(chan) => { Self::Ssh2(chan) => {
if chan.eof() && chan.wait_close().is_ok() { if chan.eof() && chan.wait_close().is_ok() {
if let Some(_sig) = has_signal(chan) { if let Some(sig) = has_signal(chan) {
Some(ExitStatus::with_exit_code(1)) Some(ExitStatus::with_signal(
sig.exit_signal.as_deref().unwrap_or("Unknown signal"),
))
} else if let Ok(status) = chan.exit_status() { } else if let Ok(status) = chan.exit_status() {
Some(ExitStatus::with_exit_code(status as _)) Some(ExitStatus::with_exit_code(status as _))
} else { } else {