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::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 }

View File

@ -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<String>,
}
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<std::process::ExitStatus> 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),
}
}
}
}

View File

@ -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 {