@@ -17,10 +17,6 @@ use ini::Ini;
1717use native_int_str:: {
1818 Convert , NCvt , NativeIntStr , NativeIntString , NativeStr , from_native_int_representation_owned,
1919} ;
20- #[ cfg( unix) ]
21- use nix:: libc;
22- #[ cfg( unix) ]
23- use nix:: sys:: signal:: { SigHandler :: SigIgn , Signal , signal} ;
2420use std:: borrow:: Cow ;
2521use std:: env;
2622use std:: ffi:: { OsStr , OsString } ;
@@ -29,12 +25,15 @@ use std::io::{self, Write};
2925use std:: os:: unix:: ffi:: OsStrExt ;
3026#[ cfg( unix) ]
3127use std:: os:: unix:: process:: CommandExt ;
28+ #[ cfg( unix) ]
29+ use uucore:: signals:: {
30+ SignalStatus , block_signals, get_signal_status, ignore_signal, reset_signal,
31+ signal_by_name_or_value, signal_name_by_value,
32+ } ;
3233
3334use uucore:: display:: Quotable ;
3435use uucore:: error:: { ExitCode , UError , UResult , USimpleError , UUsageError } ;
3536use uucore:: line_ending:: LineEnding ;
36- #[ cfg( unix) ]
37- use uucore:: signals:: signal_by_name_or_value;
3837use uucore:: translate;
3938use uucore:: { format_usage, show_warning} ;
4039
@@ -84,6 +83,9 @@ mod options {
8483 pub const SPLIT_STRING : & str = "split-string" ;
8584 pub const ARGV0 : & str = "argv0" ;
8685 pub const IGNORE_SIGNAL : & str = "ignore-signal" ;
86+ pub const DEFAULT_SIGNAL : & str = "default-signal" ;
87+ pub const BLOCK_SIGNAL : & str = "block-signal" ;
88+ pub const LIST_SIGNAL_HANDLING : & str = "list-signal-handling" ;
8789}
8890
8991struct Options < ' a > {
@@ -97,6 +99,12 @@ struct Options<'a> {
9799 argv0 : Option < & ' a OsStr > ,
98100 #[ cfg( unix) ]
99101 ignore_signal : Vec < usize > ,
102+ #[ cfg( unix) ]
103+ default_signal : Vec < usize > ,
104+ #[ cfg( unix) ]
105+ block_signal : Vec < usize > ,
106+ #[ cfg( unix) ]
107+ list_signal_handling : bool ,
100108}
101109
102110/// print `name=value` env pairs on screen
@@ -155,11 +163,11 @@ fn parse_signal_value(signal_name: &str) -> UResult<usize> {
155163}
156164
157165#[ cfg( unix) ]
158- fn parse_signal_opt < ' a > ( opts : & mut Options < ' a > , opt : & ' a OsStr ) -> UResult < ( ) > {
166+ fn parse_signal_opt ( signal_vec : & mut Vec < usize > , opt : & OsStr ) -> UResult < ( ) > {
159167 if opt. is_empty ( ) {
160168 return Ok ( ( ) ) ;
161169 }
162- let signals: Vec < & ' a OsStr > = opt
170+ let signals: Vec < & OsStr > = opt
163171 . as_bytes ( )
164172 . split ( |& b| b == b',' )
165173 . map ( OsStr :: from_bytes)
@@ -179,14 +187,33 @@ fn parse_signal_opt<'a>(opts: &mut Options<'a>, opt: &'a OsStr) -> UResult<()> {
179187 ) ) ;
180188 } ;
181189 let sig_val = parse_signal_value ( sig_str) ?;
182- if !opts . ignore_signal . contains ( & sig_val) {
183- opts . ignore_signal . push ( sig_val) ;
190+ if !signal_vec . contains ( & sig_val) {
191+ signal_vec . push ( sig_val) ;
184192 }
185193 }
186194
187195 Ok ( ( ) )
188196}
189197
198+ /// Parse signal option that can be empty (meaning all signals)
199+ #[ cfg( unix) ]
200+ fn parse_signal_opt_or_all ( signal_vec : & mut Vec < usize > , opt : & OsStr ) -> UResult < ( ) > {
201+ if opt. is_empty ( ) {
202+ // Empty means all signals - add all valid signal numbers (1-31 typically)
203+ // Skip SIGKILL (9) and SIGSTOP (19) which cannot be caught or ignored
204+ for sig_val in 1 ..32 {
205+ if sig_val == 9 || sig_val == 19 {
206+ continue ; // SIGKILL and SIGSTOP cannot be modified
207+ }
208+ if !signal_vec. contains ( & sig_val) {
209+ signal_vec. push ( sig_val) ;
210+ }
211+ }
212+ return Ok ( ( ) ) ;
213+ }
214+ parse_signal_opt ( signal_vec, opt)
215+ }
216+
190217fn load_config_file ( opts : & mut Options ) -> UResult < ( ) > {
191218 // NOTE: config files are parsed using an INI parser b/c it's available and compatible with ".env"-style files
192219 // ... * but support for actual INI files, although working, is not intended, nor claimed
@@ -307,9 +334,40 @@ pub fn uu_app() -> Command {
307334 . long ( options:: IGNORE_SIGNAL )
308335 . value_name ( "SIG" )
309336 . action ( ArgAction :: Append )
337+ . num_args ( 0 ..=1 )
338+ . default_missing_value ( "" )
339+ . require_equals ( true )
310340 . value_parser ( ValueParser :: os_string ( ) )
311341 . help ( translate ! ( "env-help-ignore-signal" ) ) ,
312342 )
343+ . arg (
344+ Arg :: new ( options:: DEFAULT_SIGNAL )
345+ . long ( options:: DEFAULT_SIGNAL )
346+ . value_name ( "SIG" )
347+ . action ( ArgAction :: Append )
348+ . num_args ( 0 ..=1 )
349+ . default_missing_value ( "" )
350+ . require_equals ( true )
351+ . value_parser ( ValueParser :: os_string ( ) )
352+ . help ( translate ! ( "env-help-default-signal" ) ) ,
353+ )
354+ . arg (
355+ Arg :: new ( options:: BLOCK_SIGNAL )
356+ . long ( options:: BLOCK_SIGNAL )
357+ . value_name ( "SIG" )
358+ . action ( ArgAction :: Append )
359+ . num_args ( 0 ..=1 )
360+ . default_missing_value ( "" )
361+ . require_equals ( true )
362+ . value_parser ( ValueParser :: os_string ( ) )
363+ . help ( translate ! ( "env-help-block-signal" ) ) ,
364+ )
365+ . arg (
366+ Arg :: new ( options:: LIST_SIGNAL_HANDLING )
367+ . long ( options:: LIST_SIGNAL_HANDLING )
368+ . action ( ArgAction :: SetTrue )
369+ . help ( translate ! ( "env-help-list-signal-handling" ) ) ,
370+ )
313371}
314372
315373pub fn parse_args_from_str ( text : & NativeIntStr ) -> UResult < Vec < NativeIntString > > {
@@ -543,9 +601,23 @@ impl EnvAppData {
543601
544602 apply_specified_env_vars ( & opts) ;
545603
604+ // Apply signal handling in the correct order:
605+ // 1. Reset signals to default
606+ // 2. Set signals to ignore
607+ // 3. Block signals
608+ // 4. List signal handling (if requested)
609+ #[ cfg( unix) ]
610+ apply_default_signal ( & opts) ?;
611+
546612 #[ cfg( unix) ]
547613 apply_ignore_signal ( & opts) ?;
548614
615+ #[ cfg( unix) ]
616+ apply_block_signal ( & opts) ?;
617+
618+ #[ cfg( unix) ]
619+ list_signal_handling ( & opts) ?;
620+
549621 if opts. program . is_empty ( ) {
550622 // no program provided, so just dump all env vars to stdout
551623 print_env ( opts. line_ending ) ;
@@ -705,12 +777,32 @@ fn make_options(matches: &clap::ArgMatches) -> UResult<Options<'_>> {
705777 argv0,
706778 #[ cfg( unix) ]
707779 ignore_signal : vec ! [ ] ,
780+ #[ cfg( unix) ]
781+ default_signal : vec ! [ ] ,
782+ #[ cfg( unix) ]
783+ block_signal : vec ! [ ] ,
784+ #[ cfg( unix) ]
785+ list_signal_handling : matches. get_flag ( options:: LIST_SIGNAL_HANDLING ) ,
708786 } ;
709787
710788 #[ cfg( unix) ]
711- if let Some ( iter) = matches. get_many :: < OsString > ( "ignore-signal" ) {
789+ if let Some ( iter) = matches. get_many :: < OsString > ( options:: IGNORE_SIGNAL ) {
790+ for opt in iter {
791+ parse_signal_opt_or_all ( & mut opts. ignore_signal , opt) ?;
792+ }
793+ }
794+
795+ #[ cfg( unix) ]
796+ if let Some ( iter) = matches. get_many :: < OsString > ( options:: DEFAULT_SIGNAL ) {
712797 for opt in iter {
713- parse_signal_opt ( & mut opts, opt) ?;
798+ parse_signal_opt_or_all ( & mut opts. default_signal , opt) ?;
799+ }
800+ }
801+
802+ #[ cfg( unix) ]
803+ if let Some ( iter) = matches. get_many :: < OsString > ( options:: BLOCK_SIGNAL ) {
804+ for opt in iter {
805+ parse_signal_opt_or_all ( & mut opts. block_signal , opt) ?;
714806 }
715807 }
716808
@@ -821,25 +913,58 @@ fn apply_specified_env_vars(opts: &Options<'_>) {
821913#[ cfg( unix) ]
822914fn apply_ignore_signal ( opts : & Options < ' _ > ) -> UResult < ( ) > {
823915 for & sig_value in & opts. ignore_signal {
824- let sig: Signal = ( sig_value as i32 )
825- . try_into ( )
826- . map_err ( |e| io:: Error :: from_raw_os_error ( e as i32 ) ) ?;
916+ ignore_signal ( sig_value) . map_err ( |err| {
917+ USimpleError :: new (
918+ 125 ,
919+ translate ! ( "env-error-failed-set-signal-action" , "signal" => sig_value, "error" => err. desc( ) ) ,
920+ )
921+ } ) ?;
922+ }
923+ Ok ( ( ) )
924+ }
827925
828- ignore_signal ( sig) ?;
926+ #[ cfg( unix) ]
927+ fn apply_default_signal ( opts : & Options < ' _ > ) -> UResult < ( ) > {
928+ for & sig_value in & opts. default_signal {
929+ reset_signal ( sig_value) . map_err ( |err| {
930+ USimpleError :: new (
931+ 125 ,
932+ translate ! ( "env-error-failed-set-signal-action" , "signal" => sig_value, "error" => err. desc( ) ) ,
933+ )
934+ } ) ?;
829935 }
830936 Ok ( ( ) )
831937}
832938
833939#[ cfg( unix) ]
834- fn ignore_signal ( sig : Signal ) -> UResult < ( ) > {
835- // SAFETY: This is safe because we write the handler for each signal only once, and therefore "the current handler is the default", as the documentation requires it.
836- let result = unsafe { signal ( sig, SigIgn ) } ;
837- if let Err ( err) = result {
838- return Err ( USimpleError :: new (
839- 125 ,
840- translate ! ( "env-error-failed-set-signal-action" , "signal" => ( sig as i32 ) , "error" => err. desc( ) ) ,
841- ) ) ;
940+ fn apply_block_signal ( opts : & Options < ' _ > ) -> UResult < ( ) > {
941+ block_signals ( & opts. block_signal )
942+ . map_err ( |err| USimpleError :: new ( 125 , format ! ( "failed to block signals: {}" , err. desc( ) ) ) )
943+ }
944+
945+ #[ cfg( unix) ]
946+ fn list_signal_handling ( opts : & Options < ' _ > ) -> UResult < ( ) > {
947+ if !opts. list_signal_handling {
948+ return Ok ( ( ) ) ;
842949 }
950+
951+ let stderr = io:: stderr ( ) ;
952+ let mut stderr = stderr. lock ( ) ;
953+
954+ // Check each signal that was modified
955+ for & sig_value in & opts. ignore_signal {
956+ if let Some ( name) = signal_name_by_value ( sig_value) {
957+ if let Ok ( status) = get_signal_status ( sig_value) {
958+ let handler_str = match status {
959+ SignalStatus :: Ignore => "IGNORE" ,
960+ SignalStatus :: Default => "DEFAULT" ,
961+ SignalStatus :: Custom => "HANDLER" ,
962+ } ;
963+ writeln ! ( stderr, "{name:<10} (): {handler_str}" ) . ok ( ) ;
964+ }
965+ }
966+ }
967+
843968 Ok ( ( ) )
844969}
845970
@@ -848,9 +973,7 @@ pub fn uumain(args: impl uucore::Args) -> UResult<()> {
848973 // Rust ignores SIGPIPE (see https://github.com/rust-lang/rust/issues/62569).
849974 // We restore its default action here.
850975 #[ cfg( unix) ]
851- unsafe {
852- libc:: signal ( libc:: SIGPIPE , libc:: SIG_DFL ) ;
853- }
976+ uucore:: signals:: enable_pipe_errors ( ) . ok ( ) ;
854977 EnvAppData :: default ( ) . run_env ( args)
855978}
856979
0 commit comments