1
1
//! Providing auxiliary information for signals.
2
2
3
- use libc:: c_int;
3
+ use std:: io:: Error ;
4
+ use std:: mem;
5
+ use std:: ptr;
6
+
7
+ use libc:: { c_int, EINVAL } ;
4
8
5
9
use crate :: consts:: signal:: * ;
10
+ use crate :: low_level;
11
+
12
+ #[ derive( Clone , Copy , Debug ) ]
13
+ enum DefaultKind {
14
+ Ignore ,
15
+ #[ cfg( not( windows) ) ]
16
+ Stop ,
17
+ Term ,
18
+ }
6
19
7
20
struct Details {
8
21
signal : c_int ,
9
22
name : & ' static str ,
23
+ default_kind : DefaultKind ,
10
24
}
11
25
12
26
macro_rules! s {
13
- ( $name: expr) => {
27
+ ( $name: expr, $kind : ident ) => {
14
28
Details {
15
29
signal: $name,
16
30
name: stringify!( $name) ,
31
+ default_kind: DefaultKind :: $kind,
17
32
}
18
33
} ;
19
34
}
20
35
21
36
#[ cfg( not( windows) ) ]
22
37
const DETAILS : & [ Details ] = & [
23
- s ! ( SIGABRT ) ,
24
- s ! ( SIGALRM ) ,
25
- s ! ( SIGBUS ) ,
26
- s ! ( SIGCHLD ) ,
27
- s ! ( SIGCONT ) ,
28
- s ! ( SIGFPE ) ,
29
- s ! ( SIGHUP ) ,
30
- s ! ( SIGILL ) ,
31
- s ! ( SIGINT ) ,
32
- s ! ( SIGIO ) ,
33
- s ! ( SIGKILL ) ,
34
- s ! ( SIGPIPE ) ,
35
- s ! ( SIGPROF ) ,
36
- s ! ( SIGQUIT ) ,
37
- s ! ( SIGSEGV ) ,
38
- s ! ( SIGSTOP ) ,
39
- s ! ( SIGSYS ) ,
40
- s ! ( SIGTERM ) ,
41
- s ! ( SIGTRAP ) ,
42
- s ! ( SIGTSTP ) ,
43
- s ! ( SIGTTIN ) ,
44
- s ! ( SIGTTOU ) ,
45
- s ! ( SIGURG ) ,
46
- s ! ( SIGUSR1 ) ,
47
- s ! ( SIGUSR2 ) ,
48
- s ! ( SIGVTALRM ) ,
49
- s ! ( SIGWINCH ) ,
50
- s ! ( SIGXCPU ) ,
51
- s ! ( SIGXFSZ ) ,
38
+ s ! ( SIGABRT , Term ) ,
39
+ s ! ( SIGALRM , Term ) ,
40
+ s ! ( SIGBUS , Term ) ,
41
+ s ! ( SIGCHLD , Ignore ) ,
42
+ // Technically, continue the process... but this is not done *by* the process.
43
+ s ! ( SIGCONT , Ignore ) ,
44
+ s ! ( SIGFPE , Term ) ,
45
+ s ! ( SIGHUP , Term ) ,
46
+ s ! ( SIGILL , Term ) ,
47
+ s ! ( SIGINT , Term ) ,
48
+ s ! ( SIGIO , Ignore ) ,
49
+ // Can't override anyway, but...
50
+ s ! ( SIGKILL , Term ) ,
51
+ s ! ( SIGPIPE , Term ) ,
52
+ s ! ( SIGPROF , Term ) ,
53
+ s ! ( SIGQUIT , Term ) ,
54
+ s ! ( SIGSEGV , Term ) ,
55
+ // Can't override anyway, but...
56
+ s ! ( SIGSTOP , Stop ) ,
57
+ s ! ( SIGSYS , Term ) ,
58
+ s ! ( SIGTERM , Term ) ,
59
+ s ! ( SIGTRAP , Term ) ,
60
+ s ! ( SIGTSTP , Stop ) ,
61
+ s ! ( SIGTTIN , Stop ) ,
62
+ s ! ( SIGTTOU , Stop ) ,
63
+ s ! ( SIGURG , Ignore ) ,
64
+ s ! ( SIGUSR1 , Term ) ,
65
+ s ! ( SIGUSR2 , Term ) ,
66
+ s ! ( SIGVTALRM , Term ) ,
67
+ s ! ( SIGWINCH , Ignore ) ,
68
+ s ! ( SIGXCPU , Term ) ,
69
+ s ! ( SIGXFSZ , Term ) ,
52
70
] ;
53
71
54
72
#[ cfg( windows) ]
55
73
const DETAILS : & [ Details ] = & [
56
- s ! ( SIGABRT ) ,
57
- s ! ( SIGFPE ) ,
58
- s ! ( SIGILL ) ,
59
- s ! ( SIGINT ) ,
60
- s ! ( SIGSEGV ) ,
61
- s ! ( SIGTERM ) ,
74
+ s ! ( SIGABRT , Term ) ,
75
+ s ! ( SIGFPE , Term ) ,
76
+ s ! ( SIGILL , Term ) ,
77
+ s ! ( SIGINT , Term ) ,
78
+ s ! ( SIGSEGV , Term ) ,
79
+ s ! ( SIGTERM , Term ) ,
62
80
] ;
63
81
64
82
/// Provides a human-readable name of a signal.
@@ -77,6 +95,90 @@ pub fn signal_name(signal: c_int) -> Option<&'static str> {
77
95
DETAILS . iter ( ) . find ( |d| d. signal == signal) . map ( |d| d. name )
78
96
}
79
97
98
+ #[ cfg( not( windows) ) ]
99
+ fn restore_default ( signal : c_int ) -> Result < ( ) , Error > {
100
+ unsafe {
101
+ // A C structure, supposed to be memset to 0 before use.
102
+ let mut action: libc:: sigaction = mem:: zeroed ( ) ;
103
+ action. sa_sigaction = libc:: SIG_DFL as _ ;
104
+ if libc:: sigaction ( signal, & action, ptr:: null_mut ( ) ) == 0 {
105
+ Ok ( ( ) )
106
+ } else {
107
+ Err ( Error :: last_os_error ( ) )
108
+ }
109
+ }
110
+ }
111
+
112
+ #[ cfg( windows) ]
113
+ fn restore_default ( signal : c_int ) -> Result < ( ) , Error > {
114
+ unsafe {
115
+ // SIG_DFL = 0, but not in libc :-(
116
+ if libc:: signal ( signal, 0 ) == 0 {
117
+ Ok ( ( ) )
118
+ } else {
119
+ Err ( Error :: last_os_error ( ) )
120
+ }
121
+ }
122
+ }
123
+
124
+ /// Emulates the behaviour of a default handler for the provided signal.
125
+ ///
126
+ /// This function does its best to provide the same action as the default handler would do, without
127
+ /// disrupting the rest of the handling of such signal in the application. It is also
128
+ /// async-signal-safe.
129
+ ///
130
+ /// This function necessarily looks up the appropriate action in a table. That means it is possible
131
+ /// your system has a signal that is not known to this function. In such case an error is returned
132
+ /// (equivalent of `EINVAL`).
133
+ ///
134
+ /// See also the [`register_conditional_default`][crate::flag::register_conditional_default].
135
+ ///
136
+ /// # Warning
137
+ ///
138
+ /// There's a short race condition in case of signals that terminate (either with or without a core
139
+ /// dump). The emulation first resets the signal handler back to default (as the application is
140
+ /// going to end, it's not a problem) and invokes it. But if some other thread installs a signal
141
+ /// handler in the meantime (without assistance from `signal-hook`), it can happen this will be
142
+ /// invoked by the re-raised signal.
143
+ ///
144
+ /// This function will still terminate the application (there's a fallback on `abort`), the risk is
145
+ /// invoking the newly installed signal handler. Note that manipulating the low-level signals is
146
+ /// always racy in a multi-threaded program, therefore the described situation is already
147
+ /// discouraged.
148
+ ///
149
+ /// If you are uneasy about such race condition, the recommendation is to run relevant termination
150
+ /// routine manually ([`exit`][super::exit] or [`abort`][super::abort]); they always do what they
151
+ /// say, but slightly differ in externally observable behaviour from termination by a signal (the
152
+ /// exit code will specify that the application exited, not that it terminated with a signal in the
153
+ /// first case, and `abort` terminates on `SIGABRT`, so the detected termination signal may be
154
+ /// different).
155
+ pub fn emulate_default_handler ( signal : c_int ) -> Result < ( ) , Error > {
156
+ #[ cfg( not( windows) ) ]
157
+ {
158
+ if signal == SIGSTOP || signal == SIGKILL {
159
+ return low_level:: raise ( signal) ;
160
+ }
161
+ }
162
+ let kind = DETAILS
163
+ . iter ( )
164
+ . find ( |d| d. signal == signal)
165
+ . map ( |d| d. default_kind )
166
+ . ok_or_else ( || Error :: from_raw_os_error ( EINVAL ) ) ?;
167
+ match kind {
168
+ DefaultKind :: Ignore => Ok ( ( ) ) ,
169
+ #[ cfg( not( windows) ) ]
170
+ DefaultKind :: Stop => low_level:: raise ( SIGSTOP ) ,
171
+ DefaultKind :: Term => {
172
+ if let Ok ( ( ) ) = restore_default ( signal) {
173
+ let _ = low_level:: raise ( signal) ;
174
+ }
175
+ // Fallback if anything failed or someone managed to put some other action in in
176
+ // between.
177
+ unsafe { libc:: abort ( ) }
178
+ }
179
+ }
180
+ }
181
+
80
182
#[ cfg( test) ]
81
183
mod test {
82
184
use super :: * ;
0 commit comments