@@ -84,6 +84,9 @@ mod options {
8484 pub const SPLIT_STRING : & str = "split-string" ;
8585 pub const ARGV0 : & str = "argv0" ;
8686 pub const IGNORE_SIGNAL : & str = "ignore-signal" ;
87+ pub const DEFAULT_SIGNAL : & str = "default-signal" ;
88+ pub const BLOCK_SIGNAL : & str = "block-signal" ;
89+ pub const LIST_SIGNAL_HANDLING : & str = "list-signal-handling" ;
8790}
8891
8992struct Options < ' a > {
@@ -97,6 +100,12 @@ struct Options<'a> {
97100 argv0 : Option < & ' a OsStr > ,
98101 #[ cfg( unix) ]
99102 ignore_signal : Vec < usize > ,
103+ #[ cfg( unix) ]
104+ default_signal : Vec < usize > ,
105+ #[ cfg( unix) ]
106+ block_signal : Vec < usize > ,
107+ #[ cfg( unix) ]
108+ list_signal_handling : bool ,
100109}
101110
102111/// print `name=value` env pairs on screen
@@ -155,11 +164,11 @@ fn parse_signal_value(signal_name: &str) -> UResult<usize> {
155164}
156165
157166#[ cfg( unix) ]
158- fn parse_signal_opt < ' a > ( opts : & mut Options < ' a > , opt : & ' a OsStr ) -> UResult < ( ) > {
167+ fn parse_signal_opt ( signal_vec : & mut Vec < usize > , opt : & OsStr ) -> UResult < ( ) > {
159168 if opt. is_empty ( ) {
160169 return Ok ( ( ) ) ;
161170 }
162- let signals: Vec < & ' a OsStr > = opt
171+ let signals: Vec < & OsStr > = opt
163172 . as_bytes ( )
164173 . split ( |& b| b == b',' )
165174 . map ( OsStr :: from_bytes)
@@ -179,14 +188,33 @@ fn parse_signal_opt<'a>(opts: &mut Options<'a>, opt: &'a OsStr) -> UResult<()> {
179188 ) ) ;
180189 } ;
181190 let sig_val = parse_signal_value ( sig_str) ?;
182- if !opts . ignore_signal . contains ( & sig_val) {
183- opts . ignore_signal . push ( sig_val) ;
191+ if !signal_vec . contains ( & sig_val) {
192+ signal_vec . push ( sig_val) ;
184193 }
185194 }
186195
187196 Ok ( ( ) )
188197}
189198
199+ /// Parse signal option that can be empty (meaning all signals)
200+ #[ cfg( unix) ]
201+ fn parse_signal_opt_or_all ( signal_vec : & mut Vec < usize > , opt : & OsStr ) -> UResult < ( ) > {
202+ if opt. is_empty ( ) {
203+ // Empty means all signals - add all valid signal numbers (1-31 typically)
204+ // Skip SIGKILL (9) and SIGSTOP (19) which cannot be caught or ignored
205+ for sig_val in 1 ..32 {
206+ if sig_val == 9 || sig_val == 19 {
207+ continue ; // SIGKILL and SIGSTOP cannot be modified
208+ }
209+ if !signal_vec. contains ( & sig_val) {
210+ signal_vec. push ( sig_val) ;
211+ }
212+ }
213+ return Ok ( ( ) ) ;
214+ }
215+ parse_signal_opt ( signal_vec, opt)
216+ }
217+
190218fn load_config_file ( opts : & mut Options ) -> UResult < ( ) > {
191219 // NOTE: config files are parsed using an INI parser b/c it's available and compatible with ".env"-style files
192220 // ... * but support for actual INI files, although working, is not intended, nor claimed
@@ -307,9 +335,40 @@ pub fn uu_app() -> Command {
307335 . long ( options:: IGNORE_SIGNAL )
308336 . value_name ( "SIG" )
309337 . action ( ArgAction :: Append )
338+ . num_args ( 0 ..=1 )
339+ . default_missing_value ( "" )
340+ . require_equals ( true )
310341 . value_parser ( ValueParser :: os_string ( ) )
311342 . help ( translate ! ( "env-help-ignore-signal" ) ) ,
312343 )
344+ . arg (
345+ Arg :: new ( options:: DEFAULT_SIGNAL )
346+ . long ( options:: DEFAULT_SIGNAL )
347+ . value_name ( "SIG" )
348+ . action ( ArgAction :: Append )
349+ . num_args ( 0 ..=1 )
350+ . default_missing_value ( "" )
351+ . require_equals ( true )
352+ . value_parser ( ValueParser :: os_string ( ) )
353+ . help ( "set handling of SIG signal(s) to the default" ) ,
354+ )
355+ . arg (
356+ Arg :: new ( options:: BLOCK_SIGNAL )
357+ . long ( options:: BLOCK_SIGNAL )
358+ . value_name ( "SIG" )
359+ . action ( ArgAction :: Append )
360+ . num_args ( 0 ..=1 )
361+ . default_missing_value ( "" )
362+ . require_equals ( true )
363+ . value_parser ( ValueParser :: os_string ( ) )
364+ . help ( "block delivery of SIG signal(s) to COMMAND" ) ,
365+ )
366+ . arg (
367+ Arg :: new ( options:: LIST_SIGNAL_HANDLING )
368+ . long ( options:: LIST_SIGNAL_HANDLING )
369+ . action ( ArgAction :: SetTrue )
370+ . help ( "list non default signal handling to stderr" ) ,
371+ )
313372}
314373
315374pub fn parse_args_from_str ( text : & NativeIntStr ) -> UResult < Vec < NativeIntString > > {
@@ -543,9 +602,23 @@ impl EnvAppData {
543602
544603 apply_specified_env_vars ( & opts) ;
545604
605+ // Apply signal handling in the correct order:
606+ // 1. Reset signals to default
607+ // 2. Set signals to ignore
608+ // 3. Block signals
609+ // 4. List signal handling (if requested)
610+ #[ cfg( unix) ]
611+ apply_default_signal ( & opts) ?;
612+
546613 #[ cfg( unix) ]
547614 apply_ignore_signal ( & opts) ?;
548615
616+ #[ cfg( unix) ]
617+ apply_block_signal ( & opts) ?;
618+
619+ #[ cfg( unix) ]
620+ list_signal_handling ( & opts) ?;
621+
549622 if opts. program . is_empty ( ) {
550623 // no program provided, so just dump all env vars to stdout
551624 print_env ( opts. line_ending ) ;
@@ -705,12 +778,32 @@ fn make_options(matches: &clap::ArgMatches) -> UResult<Options<'_>> {
705778 argv0,
706779 #[ cfg( unix) ]
707780 ignore_signal : vec ! [ ] ,
781+ #[ cfg( unix) ]
782+ default_signal : vec ! [ ] ,
783+ #[ cfg( unix) ]
784+ block_signal : vec ! [ ] ,
785+ #[ cfg( unix) ]
786+ list_signal_handling : matches. get_flag ( options:: LIST_SIGNAL_HANDLING ) ,
708787 } ;
709788
710789 #[ cfg( unix) ]
711- if let Some ( iter) = matches. get_many :: < OsString > ( "ignore-signal" ) {
790+ if let Some ( iter) = matches. get_many :: < OsString > ( options :: IGNORE_SIGNAL ) {
712791 for opt in iter {
713- parse_signal_opt ( & mut opts, opt) ?;
792+ parse_signal_opt_or_all ( & mut opts. ignore_signal , opt) ?;
793+ }
794+ }
795+
796+ #[ cfg( unix) ]
797+ if let Some ( iter) = matches. get_many :: < OsString > ( options:: DEFAULT_SIGNAL ) {
798+ for opt in iter {
799+ parse_signal_opt_or_all ( & mut opts. default_signal , opt) ?;
800+ }
801+ }
802+
803+ #[ cfg( unix) ]
804+ if let Some ( iter) = matches. get_many :: < OsString > ( options:: BLOCK_SIGNAL ) {
805+ for opt in iter {
806+ parse_signal_opt_or_all ( & mut opts. block_signal , opt) ?;
714807 }
715808 }
716809
@@ -843,6 +936,86 @@ fn ignore_signal(sig: Signal) -> UResult<()> {
843936 Ok ( ( ) )
844937}
845938
939+ #[ cfg( unix) ]
940+ fn apply_default_signal ( opts : & Options < ' _ > ) -> UResult < ( ) > {
941+ use nix:: sys:: signal:: { SigHandler :: SigDfl , signal} ;
942+
943+ for & sig_value in & opts. default_signal {
944+ let sig: Signal = ( sig_value as i32 )
945+ . try_into ( )
946+ . map_err ( |e| io:: Error :: from_raw_os_error ( e as i32 ) ) ?;
947+
948+ // SAFETY: Setting signal handler to default is safe
949+ let result = unsafe { signal ( sig, SigDfl ) } ;
950+ if let Err ( err) = result {
951+ return Err ( USimpleError :: new (
952+ 125 ,
953+ translate ! ( "env-error-failed-set-signal-action" , "signal" => ( sig as i32 ) , "error" => err. desc( ) ) ,
954+ ) ) ;
955+ }
956+ }
957+ Ok ( ( ) )
958+ }
959+
960+ #[ cfg( unix) ]
961+ fn apply_block_signal ( opts : & Options < ' _ > ) -> UResult < ( ) > {
962+ use nix:: sys:: signal:: { SigSet , SigmaskHow , sigprocmask} ;
963+
964+ if opts. block_signal . is_empty ( ) {
965+ return Ok ( ( ) ) ;
966+ }
967+
968+ let mut sigset = SigSet :: empty ( ) ;
969+ for & sig_value in & opts. block_signal {
970+ let sig: Signal = ( sig_value as i32 )
971+ . try_into ( )
972+ . map_err ( |e| io:: Error :: from_raw_os_error ( e as i32 ) ) ?;
973+ sigset. add ( sig) ;
974+ }
975+
976+ sigprocmask ( SigmaskHow :: SIG_BLOCK , Some ( & sigset) , None ) . map_err ( |err| {
977+ USimpleError :: new ( 125 , format ! ( "failed to block signals: {}" , err. desc( ) ) )
978+ } ) ?;
979+
980+ Ok ( ( ) )
981+ }
982+
983+ #[ cfg( unix) ]
984+ fn list_signal_handling ( opts : & Options < ' _ > ) -> UResult < ( ) > {
985+ use std:: mem:: MaybeUninit ;
986+ use uucore:: signals:: signal_name_by_value;
987+
988+ if !opts. list_signal_handling {
989+ return Ok ( ( ) ) ;
990+ }
991+
992+ let stderr = io:: stderr ( ) ;
993+ let mut stderr = stderr. lock ( ) ;
994+
995+ // Check each signal that was modified
996+ for & sig_value in & opts. ignore_signal {
997+ if let Some ( name) = signal_name_by_value ( sig_value) {
998+ // Get current signal handler
999+ let mut current = MaybeUninit :: < libc:: sigaction > :: uninit ( ) ;
1000+ if unsafe { libc:: sigaction ( sig_value as i32 , std:: ptr:: null ( ) , current. as_mut_ptr ( ) ) }
1001+ == 0
1002+ {
1003+ let handler = unsafe { current. assume_init ( ) } . sa_sigaction ;
1004+ let handler_str = if handler == libc:: SIG_IGN {
1005+ "IGNORE"
1006+ } else if handler == libc:: SIG_DFL {
1007+ "DEFAULT"
1008+ } else {
1009+ "HANDLER"
1010+ } ;
1011+ writeln ! ( stderr, "{name:<10} (): {handler_str}" ) . ok ( ) ;
1012+ }
1013+ }
1014+ }
1015+
1016+ Ok ( ( ) )
1017+ }
1018+
8461019#[ uucore:: main]
8471020pub fn uumain ( args : impl uucore:: Args ) -> UResult < ( ) > {
8481021 // Rust ignores SIGPIPE (see https://github.com/rust-lang/rust/issues/62569).
0 commit comments