From 8587de9d4191ec3de4aebe115d8a10830fe6eb49 Mon Sep 17 00:00:00 2001 From: mattsu Date: Fri, 7 Nov 2025 21:12:49 +0900 Subject: [PATCH 01/13] feat(seq): handle SIGPIPE signal properly on Unix systems Add dependency on 'nix' crate and implement sigpipe_is_ignored() function to detect if SIGPIPE is ignored. Modify error handling in uumain to only ignore BrokenPipe errors when SIGPIPE is not ignored, ensuring correct behavior when the signal is handled externally. This prevents improper exits on broken pipes in environments where SIGPIPE is masked. --- Cargo.lock | 1 + src/uu/seq/Cargo.toml | 1 + src/uu/seq/src/seq.rs | 35 ++++++++++++++++++++++++++++++++++- 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index 65f5b3f0734..12318995512 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3923,6 +3923,7 @@ dependencies = [ "clap", "codspeed-divan-compat", "fluent", + "nix", "num-bigint", "num-traits", "tempfile", diff --git a/src/uu/seq/Cargo.toml b/src/uu/seq/Cargo.toml index 6f74ce37a9e..ad9786eb8a5 100644 --- a/src/uu/seq/Cargo.toml +++ b/src/uu/seq/Cargo.toml @@ -21,6 +21,7 @@ path = "src/seq.rs" [dependencies] bigdecimal = { workspace = true } clap = { workspace = true } +nix = { workspace = true } num-bigint = { workspace = true } num-traits = { workspace = true } thiserror = { workspace = true } diff --git a/src/uu/seq/src/seq.rs b/src/uu/seq/src/seq.rs index 6741356606d..e0b3d29e438 100644 --- a/src/uu/seq/src/seq.rs +++ b/src/uu/seq/src/seq.rs @@ -209,9 +209,10 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { padding, ); + let sigpipe_ignored = sigpipe_is_ignored(); match result { Ok(()) => Ok(()), - Err(err) if err.kind() == ErrorKind::BrokenPipe => Ok(()), + Err(err) if err.kind() == ErrorKind::BrokenPipe && !sigpipe_ignored => Ok(()), Err(err) => Err(err.map_err_context(|| "write error".into())), } } @@ -327,6 +328,38 @@ fn fast_print_seq( Ok(()) } +#[cfg(unix)] +fn sigpipe_is_ignored() -> bool { + use nix::libc; + use std::{env, mem::MaybeUninit, ptr}; + + const DETECT_ENV: &str = "RUST_SIGPIPE"; + const DETECT_ENV_VALUE: &str = "inherit"; + + let detection_enabled = env::var_os(DETECT_ENV).is_some_and(|value| { + value + .to_string_lossy() + .eq_ignore_ascii_case(DETECT_ENV_VALUE) + }); + + if !detection_enabled { + return false; + } + + unsafe { + let mut current = MaybeUninit::::uninit(); + if libc::sigaction(libc::SIGPIPE, ptr::null(), current.as_mut_ptr()) != 0 { + return false; + } + current.assume_init().sa_sigaction == libc::SIG_IGN + } +} + +#[cfg(not(unix))] +const fn sigpipe_is_ignored() -> bool { + false +} + fn done_printing(next: &T, increment: &T, last: &T) -> bool { if increment >= &T::zero() { next > last From 0331118147fd246fbdc4ccd04dc0ce6eb87ee11b Mon Sep 17 00:00:00 2001 From: mattsu Date: Fri, 7 Nov 2025 21:37:36 +0900 Subject: [PATCH 02/13] chore: add sigaction to jargon wordlist Add the Unix signal handling system call "sigaction" to the VSCode spell check dictionary to avoid false positives in technical documentation and code comments. --- .vscode/cspell.dictionaries/jargon.wordlist.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/.vscode/cspell.dictionaries/jargon.wordlist.txt b/.vscode/cspell.dictionaries/jargon.wordlist.txt index 70cbd937933..ebbadc27f05 100644 --- a/.vscode/cspell.dictionaries/jargon.wordlist.txt +++ b/.vscode/cspell.dictionaries/jargon.wordlist.txt @@ -129,6 +129,7 @@ setfacl setfattr shortcode shortcodes +sigaction siginfo sigusr strcasecmp From 9890401d779670b2b4a958985ca923ce65764aaf Mon Sep 17 00:00:00 2001 From: mattsu Date: Sat, 8 Nov 2025 08:24:17 +0900 Subject: [PATCH 03/13] chore(fuzz): update Rust dependencies in Cargo.lock - Bump cc from 1.2.44 to 1.2.45 - Bump crc-fast from 1.6.0 to 1.7.0, removing rand and regex deps - Bump jiff and jiff-static from 0.2.15 to 0.2.16, updating serde to serde_core - Bump quote from 1.0.41 to 1.0.42 - Bump syn from 2.0.108 to 2.0.109 - Remove aho-corasick, regex, and related packages - Update windows-sys to 0.60.2 and add nix dependency These changes refresh dependencies for fuzzing components, potentially improving security and performance. --- fuzz/Cargo.lock | 154 ++++++++---------------------------------------- 1 file changed, 24 insertions(+), 130 deletions(-) diff --git a/fuzz/Cargo.lock b/fuzz/Cargo.lock index b8c49ad8011..7f08421c1c9 100644 --- a/fuzz/Cargo.lock +++ b/fuzz/Cargo.lock @@ -8,15 +8,6 @@ version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" -[[package]] -name = "aho-corasick" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" -dependencies = [ - "memchr", -] - [[package]] name = "android_system_properties" version = "0.1.5" @@ -205,9 +196,9 @@ checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" [[package]] name = "cc" -version = "1.2.44" +version = "1.2.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37521ac7aabe3d13122dc382493e20c9416f299d2ccd5b3a5340a2570cdeb0f3" +checksum = "35900b6c8d709fb1d854671ae27aeaa9eec2f8b01b364e1619a40da3e6fe2afe" dependencies = [ "find-msvc-tools", "jobserver", @@ -349,14 +340,12 @@ checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" [[package]] name = "crc-fast" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ddc2d09feefeee8bd78101665bd8645637828fa9317f9f292496dbbd8c65ff3" +checksum = "311eddc0ebdb918fb3f9ce10304736a8e94bfbe48e3dfd61c04754fdbb5a4d67" dependencies = [ "crc", "digest", - "rand", - "regex", "rustversion", ] @@ -834,24 +823,24 @@ dependencies = [ [[package]] name = "jiff" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be1f93b8b1eb69c77f24bbb0afdf66f54b632ee39af40ca21c4365a1d7347e49" +checksum = "49cce2b81f2098e7e3efc35bc2e0a6b7abec9d34128283d7a26fa8f32a6dbb35" dependencies = [ "jiff-static", "jiff-tzdb-platform", "log", "portable-atomic", "portable-atomic-util", - "serde", - "windows-sys 0.59.0", + "serde_core", + "windows-sys 0.61.2", ] [[package]] name = "jiff-static" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03343451ff899767262ec32146f6d559dd759fdadf42ff0e227c7c48f72594b4" +checksum = "980af8b43c3ad5d8d349ace167ec8170839f753a42d233ba19e08afe1850fa69" dependencies = [ "proc-macro2", "quote", @@ -1203,9 +1192,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.41" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce25767e7b499d1b604768e7cde645d14cc8584231ea6b295e9c9eb22c02e1d1" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] @@ -1265,34 +1254,11 @@ dependencies = [ "crossbeam-utils", ] -[[package]] -name = "regex" -version = "1.12.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - [[package]] name = "regex-automata" version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "rust-ini" @@ -1450,9 +1416,9 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" -version = "2.0.108" +version = "2.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da58917d35242480a05c2897064da0a80589a2a0476c9a3f2fdc83b53502e917" +checksum = "2f17c7e013e88258aa9543dcbe81aca68a667a9ac37cd69c9fbc07858bfe0e2f" dependencies = [ "proc-macro2", "quote", @@ -1680,6 +1646,7 @@ dependencies = [ "bigdecimal", "clap", "fluent", + "nix", "num-bigint", "num-traits", "thiserror", @@ -1993,22 +1960,13 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - [[package]] name = "windows-sys" version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" dependencies = [ - "windows-targets 0.53.5", + "windows-targets", ] [[package]] @@ -2020,22 +1978,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-targets" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" -dependencies = [ - "windows_aarch64_gnullvm 0.52.6", - "windows_aarch64_msvc 0.52.6", - "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm 0.52.6", - "windows_i686_msvc 0.52.6", - "windows_x86_64_gnu 0.52.6", - "windows_x86_64_gnullvm 0.52.6", - "windows_x86_64_msvc 0.52.6", -] - [[package]] name = "windows-targets" version = "0.53.5" @@ -2043,106 +1985,58 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ "windows-link", - "windows_aarch64_gnullvm 0.53.1", - "windows_aarch64_msvc 0.53.1", - "windows_i686_gnu 0.53.1", - "windows_i686_gnullvm 0.53.1", - "windows_i686_msvc 0.53.1", - "windows_x86_64_gnu 0.53.1", - "windows_x86_64_gnullvm 0.53.1", - "windows_x86_64_msvc 0.53.1", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" - [[package]] name = "windows_aarch64_gnullvm" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" -[[package]] -name = "windows_aarch64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" - [[package]] name = "windows_aarch64_msvc" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" -[[package]] -name = "windows_i686_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" - [[package]] name = "windows_i686_gnu" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" -[[package]] -name = "windows_i686_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" - [[package]] name = "windows_i686_gnullvm" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" -[[package]] -name = "windows_i686_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" - [[package]] name = "windows_i686_msvc" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" -[[package]] -name = "windows_x86_64_gnu" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" - [[package]] name = "windows_x86_64_gnu" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" - [[package]] name = "windows_x86_64_gnullvm" version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" -[[package]] -name = "windows_x86_64_msvc" -version = "0.52.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" - [[package]] name = "windows_x86_64_msvc" version = "0.53.1" From 53c11673cc5203ddd90ee9abfddbfcfd290a1446 Mon Sep 17 00:00:00 2001 From: mattsu Date: Sat, 8 Nov 2025 14:01:55 +0900 Subject: [PATCH 04/13] perf: cache SIGPIPE ignored state at startup in seq Previously, sigpipe_is_ignored() queried the signal handler on each call, which could be inefficient. Now, capture the state once at program initialization using platform-specific init arrays and store it in a static AtomicBool for fast access. --- src/uu/seq/src/seq.rs | 44 ++++++++++++++++++++++++------------------- 1 file changed, 25 insertions(+), 19 deletions(-) diff --git a/src/uu/seq/src/seq.rs b/src/uu/seq/src/seq.rs index e0b3d29e438..f88767b36a0 100644 --- a/src/uu/seq/src/seq.rs +++ b/src/uu/seq/src/seq.rs @@ -5,6 +5,8 @@ // spell-checker:ignore (ToDO) bigdecimal extendedbigdecimal numberparse hexadecimalfloat biguint use std::ffi::{OsStr, OsString}; use std::io::{BufWriter, ErrorKind, Write, stdout}; +#[cfg(unix)] +use std::sync::atomic::{AtomicBool, Ordering}; use clap::{Arg, ArgAction, Command}; use num_bigint::BigUint; @@ -329,30 +331,34 @@ fn fast_print_seq( } #[cfg(unix)] -fn sigpipe_is_ignored() -> bool { +static SIGPIPE_WAS_IGNORED: AtomicBool = AtomicBool::new(false); + +#[cfg(unix)] +unsafe extern "C" fn capture_sigpipe_state() { use nix::libc; - use std::{env, mem::MaybeUninit, ptr}; + use std::{mem::MaybeUninit, ptr}; - const DETECT_ENV: &str = "RUST_SIGPIPE"; - const DETECT_ENV_VALUE: &str = "inherit"; + let mut current = MaybeUninit::::uninit(); + if unsafe { libc::sigaction(libc::SIGPIPE, ptr::null(), current.as_mut_ptr()) } == 0 { + let ignored = + unsafe { current.assume_init() }.sa_sigaction == libc::SIG_IGN; + SIGPIPE_WAS_IGNORED.store(ignored, Ordering::Relaxed); + } +} - let detection_enabled = env::var_os(DETECT_ENV).is_some_and(|value| { - value - .to_string_lossy() - .eq_ignore_ascii_case(DETECT_ENV_VALUE) - }); +#[cfg(all(unix, not(target_os = "macos")))] +#[used] +#[unsafe(link_section = ".init_array")] +static CAPTURE_SIGPIPE_STATE: unsafe extern "C" fn() = capture_sigpipe_state; - if !detection_enabled { - return false; - } +#[cfg(all(unix, target_os = "macos"))] +#[used] +#[unsafe(link_section = "__DATA,__mod_init_func")] +static CAPTURE_SIGPIPE_STATE_APPLE: unsafe extern "C" fn() = capture_sigpipe_state; - unsafe { - let mut current = MaybeUninit::::uninit(); - if libc::sigaction(libc::SIGPIPE, ptr::null(), current.as_mut_ptr()) != 0 { - return false; - } - current.assume_init().sa_sigaction == libc::SIG_IGN - } +#[cfg(unix)] +fn sigpipe_is_ignored() -> bool { + SIGPIPE_WAS_IGNORED.load(Ordering::Relaxed) } #[cfg(not(unix))] From b449815a6b0dce4a878f926d1149a8bf4bb75313 Mon Sep 17 00:00:00 2001 From: mattsu Date: Sat, 8 Nov 2025 14:59:56 +0900 Subject: [PATCH 05/13] docs: add safety comment to capture_sigpipe_state function - Documents the safety invariants for the unsafe extern "C" function - Ensures clarity on why the function is safe to call during process initialization - Improves code maintainability and adheres to Rust documentation standards --- src/uu/seq/src/seq.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/uu/seq/src/seq.rs b/src/uu/seq/src/seq.rs index f88767b36a0..829f7a43838 100644 --- a/src/uu/seq/src/seq.rs +++ b/src/uu/seq/src/seq.rs @@ -334,6 +334,9 @@ fn fast_print_seq( static SIGPIPE_WAS_IGNORED: AtomicBool = AtomicBool::new(false); #[cfg(unix)] +/// # Safety +/// This function runs once at process initialization and only observes the +/// current `SIGPIPE` handler, so there are no extra safety requirements for callers. unsafe extern "C" fn capture_sigpipe_state() { use nix::libc; use std::{mem::MaybeUninit, ptr}; From 1915ad319de96b1654fea2ae620865f3e6bd9515 Mon Sep 17 00:00:00 2001 From: mattsu Date: Sat, 8 Nov 2025 15:03:29 +0900 Subject: [PATCH 06/13] refactor(seq): format variable assignment on single line Simplify code formatting by placing the 'ignored' variable assignment on a single line, improving readability without altering functionality. --- src/uu/seq/src/seq.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/uu/seq/src/seq.rs b/src/uu/seq/src/seq.rs index 829f7a43838..101ab9a99d2 100644 --- a/src/uu/seq/src/seq.rs +++ b/src/uu/seq/src/seq.rs @@ -343,8 +343,7 @@ unsafe extern "C" fn capture_sigpipe_state() { let mut current = MaybeUninit::::uninit(); if unsafe { libc::sigaction(libc::SIGPIPE, ptr::null(), current.as_mut_ptr()) } == 0 { - let ignored = - unsafe { current.assume_init() }.sa_sigaction == libc::SIG_IGN; + let ignored = unsafe { current.assume_init() }.sa_sigaction == libc::SIG_IGN; SIGPIPE_WAS_IGNORED.store(ignored, Ordering::Relaxed); } } From 68ef63a4d81788ad3f725e334903e932f9bda7fe Mon Sep 17 00:00:00 2001 From: mattsu Date: Sat, 8 Nov 2025 17:52:03 +0900 Subject: [PATCH 07/13] test(seq): add test for SIGPIPE handling in seq Add a new test to verify that on Unix systems, when SIGPIPE is ignored, seq properly reports a write error ("Broken pipe") and exits with code 1 when its output is piped to head. This ensures robust behavior in piping scenarios. --- tests/by-util/test_seq.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/tests/by-util/test_seq.rs b/tests/by-util/test_seq.rs index f82a6228fe1..0f91515cffd 100644 --- a/tests/by-util/test_seq.rs +++ b/tests/by-util/test_seq.rs @@ -4,6 +4,8 @@ // file that was distributed with this source code. // spell-checker:ignore lmnop xlmnop use uutests::new_ucmd; +use uutests::util::TestScenario; +use uutests::util_name; #[test] fn test_invalid_arg() { @@ -182,6 +184,28 @@ fn test_width_invalid_float() { .usage_error("invalid floating point argument: '1e2.3'"); } +#[test] +#[cfg(unix)] +fn test_sigpipe_ignored_reports_write_error() { + let scene = TestScenario::new(util_name!()); + let seq_bin = scene.bin_path.clone().into_os_string(); + let script = + "trap '' PIPE; { \"$SEQ_BIN\" seq inf 2>err; echo $? >code; } | head -n1"; + let result = scene + .cmd_shell(script) + .env("SEQ_BIN", &seq_bin) + .succeeds(); + + assert_eq!(result.stdout_str(), "1\n"); + + let err_contents = scene.fixtures.read("err"); + assert!( + err_contents.contains("seq: write error: Broken pipe"), + "stderr missing write error message: {err_contents:?}" + ); + assert_eq!(scene.fixtures.read("code"), "1\n"); +} + // ---- Tests for the big integer based path ---- #[test] From bfe30c8c13370c6e7ad0d401a72e1755e50fff4c Mon Sep 17 00:00:00 2001 From: mattsu Date: Sat, 8 Nov 2025 19:43:48 +0900 Subject: [PATCH 08/13] refactor(tests): format seq test script and add Unix-gated imports - Split long script string in test_sigpipe_ignored_reports_write_error using concat! for improved readability - Added #[cfg(unix)] attributes to TestScenario and util_name imports to restrict to Unix platforms, where SIGPIPE handling is applicable --- tests/by-util/test_seq.rs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/by-util/test_seq.rs b/tests/by-util/test_seq.rs index 0f91515cffd..6da870e46cc 100644 --- a/tests/by-util/test_seq.rs +++ b/tests/by-util/test_seq.rs @@ -4,7 +4,9 @@ // file that was distributed with this source code. // spell-checker:ignore lmnop xlmnop use uutests::new_ucmd; +#[cfg(unix)] use uutests::util::TestScenario; +#[cfg(unix)] use uutests::util_name; #[test] @@ -189,8 +191,10 @@ fn test_width_invalid_float() { fn test_sigpipe_ignored_reports_write_error() { let scene = TestScenario::new(util_name!()); let seq_bin = scene.bin_path.clone().into_os_string(); - let script = - "trap '' PIPE; { \"$SEQ_BIN\" seq inf 2>err; echo $? >code; } | head -n1"; + let script = concat!( + "trap '' PIPE; ", + "{ \"$SEQ_BIN\" seq inf 2>err; echo $? >code; } | head -n1", + ); let result = scene .cmd_shell(script) .env("SEQ_BIN", &seq_bin) From 5f3df286ac54646875521131ad7df7d406a3f62a Mon Sep 17 00:00:00 2001 From: mattsu Date: Sat, 8 Nov 2025 19:45:39 +0900 Subject: [PATCH 09/13] refactor(test): simplify script string in test_sigpipe_ignored_reports_write_error Remove the `concat!` macro from the script variable, as the string is short enough to be defined directly without concatenation. This improves code readability and reduces unnecessary macro usage. --- tests/by-util/test_seq.rs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tests/by-util/test_seq.rs b/tests/by-util/test_seq.rs index 6da870e46cc..72553f2a3e9 100644 --- a/tests/by-util/test_seq.rs +++ b/tests/by-util/test_seq.rs @@ -191,10 +191,8 @@ fn test_width_invalid_float() { fn test_sigpipe_ignored_reports_write_error() { let scene = TestScenario::new(util_name!()); let seq_bin = scene.bin_path.clone().into_os_string(); - let script = concat!( - "trap '' PIPE; ", - "{ \"$SEQ_BIN\" seq inf 2>err; echo $? >code; } | head -n1", - ); + let script = + "trap '' PIPE; { \"$SEQ_BIN\" seq inf 2>err; echo $? >code; } | head -n1"; let result = scene .cmd_shell(script) .env("SEQ_BIN", &seq_bin) From d7b534e182d0450829f36c1dc5c612835d4d1e06 Mon Sep 17 00:00:00 2001 From: mattsu Date: Sat, 8 Nov 2025 19:47:20 +0900 Subject: [PATCH 10/13] refactor(test): simplify code formatting in seq SIGPIPE test - Combined multi-line string assignment and method chaining into single lines for conciseness and improved readability in test_sigpipe_ignored_reports_write_error. --- tests/by-util/test_seq.rs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/tests/by-util/test_seq.rs b/tests/by-util/test_seq.rs index 72553f2a3e9..ca21d4abb91 100644 --- a/tests/by-util/test_seq.rs +++ b/tests/by-util/test_seq.rs @@ -191,12 +191,8 @@ fn test_width_invalid_float() { fn test_sigpipe_ignored_reports_write_error() { let scene = TestScenario::new(util_name!()); let seq_bin = scene.bin_path.clone().into_os_string(); - let script = - "trap '' PIPE; { \"$SEQ_BIN\" seq inf 2>err; echo $? >code; } | head -n1"; - let result = scene - .cmd_shell(script) - .env("SEQ_BIN", &seq_bin) - .succeeds(); + let script = "trap '' PIPE; { \"$SEQ_BIN\" seq inf 2>err; echo $? >code; } | head -n1"; + let result = scene.cmd_shell(script).env("SEQ_BIN", &seq_bin).succeeds(); assert_eq!(result.stdout_str(), "1\n"); From 5e272dea90077e4ab9b05aaf83fd3e062062fa35 Mon Sep 17 00:00:00 2001 From: mattsu Date: Sat, 29 Nov 2025 19:14:45 +0900 Subject: [PATCH 11/13] refactor(seq): remove unused ErrorKind import and prefix unused sigpipe_ignored variable Removed unused `ErrorKind` from std::io import to clean up imports. Prefixed `sigpipe_ignored` with underscore to suppress unused variable warnings, indicating it's not utilized in the current logic. --- src/uu/seq/src/seq.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/uu/seq/src/seq.rs b/src/uu/seq/src/seq.rs index 44fc3aa27da..9daa81f816e 100644 --- a/src/uu/seq/src/seq.rs +++ b/src/uu/seq/src/seq.rs @@ -4,7 +4,7 @@ // file that was distributed with this source code. // spell-checker:ignore (ToDO) bigdecimal extendedbigdecimal numberparse hexadecimalfloat biguint use std::ffi::{OsStr, OsString}; -use std::io::{BufWriter, ErrorKind, Write, stdout}; +use std::io::{BufWriter, Write, stdout}; #[cfg(unix)] use std::sync::atomic::{AtomicBool, Ordering}; @@ -211,7 +211,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { padding, ); - let sigpipe_ignored = sigpipe_is_ignored(); + let _sigpipe_ignored = sigpipe_is_ignored(); match result { Ok(()) => Ok(()), Err(err) if err.kind() == std::io::ErrorKind::BrokenPipe => { From 167070303c1829317569829079ecaf14a28fdc30 Mon Sep 17 00:00:00 2001 From: mattsu Date: Sat, 29 Nov 2025 19:23:58 +0900 Subject: [PATCH 12/13] fix(seq): properly handle SIGPIPE when ignored Adjust error handling for BrokenPipe in seq to match GNU seq behavior: - Exit with status 0 if SIGPIPE was not ignored (prints error message). - Fail with error if SIGPIPE was explicitly ignored. --- src/uu/seq/src/seq.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/uu/seq/src/seq.rs b/src/uu/seq/src/seq.rs index 9daa81f816e..683ec35b536 100644 --- a/src/uu/seq/src/seq.rs +++ b/src/uu/seq/src/seq.rs @@ -211,17 +211,19 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { padding, ); - let _sigpipe_ignored = sigpipe_is_ignored(); - match result { - Ok(()) => Ok(()), - Err(err) if err.kind() == std::io::ErrorKind::BrokenPipe => { + let sigpipe_ignored = sigpipe_is_ignored(); + if let Err(err) = result { + if err.kind() == std::io::ErrorKind::BrokenPipe { // GNU seq prints the Broken pipe message but still exits with status 0 + // unless SIGPIPE was explicitly ignored, in which case it should fail. let err = err.map_err_context(|| "write error".into()); uucore::show_error!("{err}"); - Ok(()) + return if sigpipe_ignored { Err(err) } else { Ok(()) }; + } else { + return Err(err.map_err_context(|| "write error".into())); } - Err(err) => Err(err.map_err_context(|| "write error".into())), } + Ok(()) } pub fn uu_app() -> Command { From 02875d0b98a684908e0e45319acd69f419155e0d Mon Sep 17 00:00:00 2001 From: mattsu Date: Sat, 29 Nov 2025 19:31:18 +0900 Subject: [PATCH 13/13] fix: simplify write error handling in seq tool Balance error checking logic to consistently return errors for write failures, ensuring proper propagation without conditional SIGPIPE handling. --- src/uu/seq/src/seq.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/uu/seq/src/seq.rs b/src/uu/seq/src/seq.rs index 683ec35b536..79e64f0acab 100644 --- a/src/uu/seq/src/seq.rs +++ b/src/uu/seq/src/seq.rs @@ -219,9 +219,8 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> { let err = err.map_err_context(|| "write error".into()); uucore::show_error!("{err}"); return if sigpipe_ignored { Err(err) } else { Ok(()) }; - } else { - return Err(err.map_err_context(|| "write error".into())); } + return Err(err.map_err_context(|| "write error".into())); } Ok(()) }