From 5d00ecef562252414a0653538dc35414acf04d9a Mon Sep 17 00:00:00 2001 From: nibon7 Date: Fri, 1 Jul 2022 21:58:21 +0800 Subject: [PATCH] Return error when external command core dumped (#5908) * Return error when external command core dumped Fixes #5903 Signed-off-by: nibon7 * Use signal-hook to get signal name Signed-off-by: nibon7 * Fix comment Signed-off-by: nibon7 --- Cargo.lock | 1 + crates/nu-command/Cargo.toml | 1 + crates/nu-command/src/system/run_external.rs | 26 ++++++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/Cargo.lock b/Cargo.lock index 0bcfabeaf8..0b11e93386 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2630,6 +2630,7 @@ dependencies = [ "serde_yaml", "sha2 0.10.2", "shadow-rs", + "signal-hook", "sqlparser", "strip-ansi-escapes", "sysinfo 0.23.13", diff --git a/crates/nu-command/Cargo.toml b/crates/nu-command/Cargo.toml index 02ecd8f921..8f85469d51 100644 --- a/crates/nu-command/Cargo.toml +++ b/crates/nu-command/Cargo.toml @@ -91,6 +91,7 @@ sqlparser = { version = "0.16.0", features = ["serde"], optional = true } [target.'cfg(unix)'.dependencies] umask = "2.0.0" users = "0.11.0" +signal-hook = { version = "0.3.14", default-features = false } [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies.trash] version = "2.1.3" diff --git a/crates/nu-command/src/system/run_external.rs b/crates/nu-command/src/system/run_external.rs index 355d52213a..35672f3001 100644 --- a/crates/nu-command/src/system/run_external.rs +++ b/crates/nu-command/src/system/run_external.rs @@ -194,6 +194,9 @@ impl ExternalCommand { let (stderr_tx, stderr_rx) = mpsc::sync_channel(OUTPUT_BUFFERS_IN_FLIGHT); let (exit_code_tx, exit_code_rx) = mpsc::channel(); + #[cfg(unix)] + let (exit_status_tx, exit_status_rx) = mpsc::channel(); + std::thread::spawn(move || { // If this external is not the last expression, then its output is piped to a channel // and we create a ListStream that can be consumed @@ -283,6 +286,9 @@ impl ExternalCommand { span, )), Ok(x) => { + #[cfg(unix)] + let _ = exit_status_tx.send(x); + if let Some(code) = x.code() { let _ = exit_code_tx.send(Value::Int { val: code as i64, @@ -304,6 +310,26 @@ impl ExternalCommand { let stderr_receiver = ChannelReceiver::new(stderr_rx); let exit_code_receiver = ValueReceiver::new(exit_code_rx); + #[cfg(unix)] + { + use signal_hook::low_level::signal_name; + use std::os::unix::process::ExitStatusExt; + use std::time::Duration; + + // The receiver will block 100ms if there's no sender + if let Ok(status) = exit_status_rx.recv_timeout(Duration::from_millis(100)) { + if status.core_dumped() { + if let Some(sig) = status.signal().and_then(signal_name) { + return Err(ShellError::ExternalCommand( + format!("{sig} (core dumped)"), + "Child process core dumped".to_string(), + span, + )); + } + } + } + } + Ok(PipelineData::ExternalStream { stdout: if redirect_stdout { Some(RawStream::new(