@@ -42,7 +42,24 @@ pub(crate) async fn run_to_completion(mut handle: Child) -> anyhow::Result<ExitS
42
42
use nix:: sys:: signal;
43
43
use nix:: unistd:: { getpgid, Pid } ;
44
44
use tokio:: select;
45
- use tokio:: signal:: unix:: { signal as handle_signal, SignalKind } ;
45
+ use tokio:: signal:: unix:: { signal as handle_signal, Signal , SignalKind } ;
46
+
47
+ /// A wrapper for a signal handler that is not available on all platforms, `recv` never
48
+ /// returns on unsupported platforms.
49
+ #[ allow( dead_code) ]
50
+ enum PlatformSpecificSignal {
51
+ Signal ( Signal ) ,
52
+ Dummy ,
53
+ }
54
+
55
+ impl PlatformSpecificSignal {
56
+ async fn recv ( & mut self ) -> Option < ( ) > {
57
+ match self {
58
+ PlatformSpecificSignal :: Signal ( ref mut signal) => signal. recv ( ) . await ,
59
+ PlatformSpecificSignal :: Dummy => std:: future:: pending ( ) . await ,
60
+ }
61
+ }
62
+ }
46
63
47
64
/// Simple new type for `Pid` allowing construction from [`Child`].
48
65
///
@@ -77,6 +94,44 @@ pub(crate) async fn run_to_completion(mut handle: Child) -> anyhow::Result<ExitS
77
94
let mut sigint_handle = handle_signal ( SignalKind :: interrupt ( ) ) ?;
78
95
let mut sigint_count = 0 ;
79
96
97
+ // The following signals are terminal by default, but can be have user defined handlers.
98
+ // Forward them to the child process for handling.
99
+ let mut sigusr1_handle = handle_signal ( SignalKind :: user_defined1 ( ) ) ?;
100
+ let mut sigusr2_handle = handle_signal ( SignalKind :: user_defined2 ( ) ) ?;
101
+ let mut sighup_handle = handle_signal ( SignalKind :: hangup ( ) ) ?;
102
+ let mut sigalrm_handle = handle_signal ( SignalKind :: alarm ( ) ) ?;
103
+ let mut sigquit_handle = handle_signal ( SignalKind :: quit ( ) ) ?;
104
+
105
+ // The following signals are ignored by default, but can be have user defined handlers.
106
+ // Forward them to the child process for handling.
107
+ let mut sigwinch_handle = handle_signal ( SignalKind :: window_change ( ) ) ?;
108
+ let mut sigpipe_handle = handle_signal ( SignalKind :: pipe ( ) ) ?;
109
+
110
+ // This signal is only available on some platforms, copied from `tokio::signal::unix`
111
+ #[ cfg( any(
112
+ target_os = "dragonfly" ,
113
+ target_os = "freebsd" ,
114
+ target_os = "macos" ,
115
+ target_os = "netbsd" ,
116
+ target_os = "openbsd" ,
117
+ target_os = "illumos" ,
118
+ ) ) ]
119
+ let mut siginfo_handle = PlatformSpecificSignal :: Signal ( handle_signal ( SignalKind :: info ( ) ) ?) ;
120
+
121
+ #[ cfg( not( any(
122
+ target_os = "dragonfly" ,
123
+ target_os = "freebsd" ,
124
+ target_os = "macos" ,
125
+ target_os = "netbsd" ,
126
+ target_os = "openbsd" ,
127
+ target_os = "illumos" ,
128
+ ) ) ) ]
129
+ let mut siginfo_handle = PlatformSpecificSignal :: Dummy ;
130
+
131
+ // Notably, we do not handle `SIGCHLD` (sent when a child process status changes) and
132
+ // `SIGIO` or `SIGPOLL` (sent when a file descriptor is ready for io) as they don't make
133
+ // sense for this parent / child relationship.
134
+
80
135
loop {
81
136
select ! {
82
137
result = handle. wait( ) => {
@@ -120,6 +175,86 @@ pub(crate) async fn run_to_completion(mut handle: Child) -> anyhow::Result<ExitS
120
175
debug!( "Received SIGTERM, forwarding to child at {child_pid}" ) ;
121
176
let _ = signal:: kill( child_pid, signal:: Signal :: SIGTERM ) ;
122
177
}
178
+ _ = sigusr1_handle. recv( ) => {
179
+ let Some ( child_pid) = * ChildPid :: from( & handle) else {
180
+ debug!( "Received SIGUSR1, but the child has already exited" ) ;
181
+ continue ;
182
+ } ;
183
+
184
+ // We unconditionally forward SIGUSR1 to the child process.
185
+ debug!( "Received SIGUSR1, forwarding to child at {child_pid}" ) ;
186
+ let _ = signal:: kill( child_pid, signal:: Signal :: SIGUSR1 ) ;
187
+ }
188
+ _ = sigusr2_handle. recv( ) => {
189
+ let Some ( child_pid) = * ChildPid :: from( & handle) else {
190
+ debug!( "Received SIGUSR2, but the child has already exited" ) ;
191
+ continue ;
192
+ } ;
193
+
194
+ // We unconditionally forward SIGUSR2 to the child process.
195
+ debug!( "Received SIGUSR2, forwarding to child at {child_pid}" ) ;
196
+ let _ = signal:: kill( child_pid, signal:: Signal :: SIGUSR2 ) ;
197
+ }
198
+ _ = sighup_handle. recv( ) => {
199
+ let Some ( child_pid) = * ChildPid :: from( & handle) else {
200
+ debug!( "Received SIGHUP, but the child has already exited" ) ;
201
+ continue ;
202
+ } ;
203
+
204
+ // We unconditionally forward SIGHUP to the child process.
205
+ debug!( "Received SIGHUP, forwarding to child at {child_pid}" ) ;
206
+ let _ = signal:: kill( child_pid, signal:: Signal :: SIGHUP ) ;
207
+ }
208
+ _ = sigalrm_handle. recv( ) => {
209
+ let Some ( child_pid) = * ChildPid :: from( & handle) else {
210
+ debug!( "Received SIGALRM, but the child has already exited" ) ;
211
+ continue ;
212
+ } ;
213
+
214
+ // We unconditionally forward SIGALRM to the child process.
215
+ debug!( "Received SIGALRM, forwarding to child at {child_pid}" ) ;
216
+ let _ = signal:: kill( child_pid, signal:: Signal :: SIGALRM ) ;
217
+ }
218
+ _ = sigquit_handle. recv( ) => {
219
+ let Some ( child_pid) = * ChildPid :: from( & handle) else {
220
+ debug!( "Received SIGQUIT, but the child has already exited" ) ;
221
+ continue ;
222
+ } ;
223
+
224
+ // We unconditionally forward SIGQUIT to the child process.
225
+ debug!( "Received SIGQUIT, forwarding to child at {child_pid}" ) ;
226
+ let _ = signal:: kill( child_pid, signal:: Signal :: SIGQUIT ) ;
227
+ }
228
+ _ = sigwinch_handle. recv( ) => {
229
+ let Some ( child_pid) = * ChildPid :: from( & handle) else {
230
+ debug!( "Received SIGWINCH, but the child has already exited" ) ;
231
+ continue ;
232
+ } ;
233
+
234
+ // We unconditionally forward SIGWINCH to the child process.
235
+ debug!( "Received SIGWINCH, forwarding to child at {child_pid}" ) ;
236
+ let _ = signal:: kill( child_pid, signal:: Signal :: SIGWINCH ) ;
237
+ }
238
+ _ = sigpipe_handle. recv( ) => {
239
+ let Some ( child_pid) = * ChildPid :: from( & handle) else {
240
+ debug!( "Received SIGPIPE, but the child has already exited" ) ;
241
+ continue ;
242
+ } ;
243
+
244
+ // We unconditionally forward SIGPIPE to the child process.
245
+ debug!( "Received SIGPIPE, forwarding to child at {child_pid}" ) ;
246
+ let _ = signal:: kill( child_pid, signal:: Signal :: SIGPIPE ) ;
247
+ }
248
+ _ = siginfo_handle. recv( ) => {
249
+ let Some ( child_pid) = * ChildPid :: from( & handle) else {
250
+ debug!( "Received SIGINFO, but the child has already exited" ) ;
251
+ continue ;
252
+ } ;
253
+
254
+ // We unconditionally forward SIGINFO to the child process.
255
+ debug!( "Received SIGINFO, forwarding to child at {child_pid}" ) ;
256
+ let _ = signal:: kill( child_pid, signal:: Signal :: SIGINFO ) ;
257
+ }
123
258
} ;
124
259
}
125
260
} ?;
0 commit comments