@@ -8,15 +8,15 @@ mod status;
88
99use crate :: status:: ExitStatus ;
1010use clap:: { Arg , ArgAction , Command } ;
11+ use nix:: sys:: signal:: Signal ;
1112use std:: io:: ErrorKind ;
1213use std:: os:: unix:: process:: ExitStatusExt ;
1314use std:: process:: { self , Child , Stdio } ;
14- use std:: sync:: atomic:: { self , AtomicBool } ;
1515use std:: time:: Duration ;
1616use uucore:: display:: Quotable ;
1717use uucore:: error:: { UResult , USimpleError , UUsageError } ;
1818use uucore:: parser:: parse_time;
19- use uucore:: process:: ChildExt ;
19+ use uucore:: process:: { ChildExt , CommandExt , SelfPipe , WaitOrTimeoutRet } ;
2020use uucore:: translate;
2121
2222#[ cfg( unix) ]
@@ -176,34 +176,6 @@ pub fn uu_app() -> Command {
176176 . after_help ( translate ! ( "timeout-after-help" ) )
177177}
178178
179- /// Remove pre-existing SIGCHLD handlers that would make waiting for the child's exit code fail.
180- fn unblock_sigchld ( ) {
181- unsafe {
182- nix:: sys:: signal:: signal (
183- nix:: sys:: signal:: Signal :: SIGCHLD ,
184- nix:: sys:: signal:: SigHandler :: SigDfl ,
185- )
186- . unwrap ( ) ;
187- }
188- }
189-
190- /// We should terminate child process when receiving TERM signal.
191- static SIGNALED : AtomicBool = AtomicBool :: new ( false ) ;
192-
193- fn catch_sigterm ( ) {
194- use nix:: sys:: signal;
195-
196- extern "C" fn handle_sigterm ( signal : libc:: c_int ) {
197- let signal = signal:: Signal :: try_from ( signal) . unwrap ( ) ;
198- if signal == signal:: Signal :: SIGTERM {
199- SIGNALED . store ( true , atomic:: Ordering :: Relaxed ) ;
200- }
201- }
202-
203- let handler = signal:: SigHandler :: Handler ( handle_sigterm) ;
204- unsafe { signal:: signal ( signal:: Signal :: SIGTERM , handler) } . unwrap ( ) ;
205- }
206-
207179/// Report that a signal is being sent if the verbose flag is set.
208180fn report_if_verbose ( signal : usize , cmd : & str , verbose : bool ) {
209181 if verbose {
@@ -258,23 +230,27 @@ fn wait_or_kill_process(
258230 preserve_status : bool ,
259231 foreground : bool ,
260232 verbose : bool ,
233+ self_pipe : & mut SelfPipe ,
261234) -> std:: io:: Result < i32 > {
262235 // ignore `SIGTERM` here
263- match process. wait_or_timeout ( duration, None ) {
264- Ok ( Some ( status) ) => {
236+ self_pipe. unset_other ( ) ?;
237+
238+ match process. wait_or_timeout ( duration, self_pipe) {
239+ Ok ( WaitOrTimeoutRet :: InTime ( status) ) => {
265240 if preserve_status {
266241 Ok ( status. code ( ) . unwrap_or_else ( || status. signal ( ) . unwrap ( ) ) )
267242 } else {
268243 Ok ( ExitStatus :: TimeoutFailed . into ( ) )
269244 }
270245 }
271- Ok ( None ) => {
246+ Ok ( WaitOrTimeoutRet :: TimedOut ) => {
272247 let signal = signal_by_name_or_value ( "KILL" ) . unwrap ( ) ;
273248 report_if_verbose ( signal, cmd, verbose) ;
274249 send_signal ( process, signal, foreground) ;
275250 process. wait ( ) ?;
276251 Ok ( ExitStatus :: SignalSent ( signal) . into ( ) )
277252 }
253+ Ok ( WaitOrTimeoutRet :: CustomSignaled ) => unreachable ! ( ) , // We did not set it up.
278254 Err ( _) => Ok ( ExitStatus :: WaitingFailed . into ( ) ) ,
279255 }
280256}
@@ -321,52 +297,49 @@ fn timeout(
321297 #[ cfg( unix) ]
322298 enable_pipe_errors ( ) ?;
323299
324- let process = & mut process:: Command :: new ( & cmd[ 0 ] )
300+ let mut command = process:: Command :: new ( & cmd[ 0 ] ) ;
301+ command
325302 . args ( & cmd[ 1 ..] )
326303 . stdin ( Stdio :: inherit ( ) )
327304 . stdout ( Stdio :: inherit ( ) )
328- . stderr ( Stdio :: inherit ( ) )
329- . spawn ( )
330- . map_err ( |err| {
331- let status_code = if err. kind ( ) == ErrorKind :: NotFound {
332- // FIXME: not sure which to use
333- 127
334- } else {
335- // FIXME: this may not be 100% correct...
336- 126
337- } ;
338- USimpleError :: new (
339- status_code,
340- translate ! ( "timeout-error-failed-to-execute-process" , "error" => err) ,
341- )
342- } ) ?;
343- unblock_sigchld ( ) ;
344- catch_sigterm ( ) ;
305+ . stderr ( Stdio :: inherit ( ) ) ;
306+ let mut self_pipe = command. set_up_timeout ( Some ( Signal :: SIGTERM ) ) ?;
307+ let process = & mut command. spawn ( ) . map_err ( |err| {
308+ let status_code = if err. kind ( ) == ErrorKind :: NotFound {
309+ // FIXME: not sure which to use
310+ 127
311+ } else {
312+ // FIXME: this may not be 100% correct...
313+ 126
314+ } ;
315+ USimpleError :: new (
316+ status_code,
317+ translate ! ( "timeout-error-failed-to-execute-process" , "error" => err) ,
318+ )
319+ } ) ?;
345320 // Wait for the child process for the specified time period.
346321 //
347- // If the process exits within the specified time period (the
348- // `Ok(Some(_))` arm), then return the appropriate status code.
349- //
350- // If the process does not exit within that time (the `Ok(None)`
351- // arm) and `kill_after` is specified, then try sending `SIGKILL`.
352- //
353322 // TODO The structure of this block is extremely similar to the
354323 // structure of `wait_or_kill_process()`. They can probably be
355324 // refactored into some common function.
356- match process. wait_or_timeout ( duration, Some ( & SIGNALED ) ) {
357- Ok ( Some ( status) ) => Err ( status
325+ match process. wait_or_timeout ( duration, & mut self_pipe ) {
326+ Ok ( WaitOrTimeoutRet :: InTime ( status) ) => Err ( status
358327 . code ( )
359328 . unwrap_or_else ( || preserve_signal_info ( status. signal ( ) . unwrap ( ) ) )
360329 . into ( ) ) ,
361- Ok ( None ) => {
330+ Ok ( WaitOrTimeoutRet :: CustomSignaled ) => {
331+ report_if_verbose ( signal, & cmd[ 0 ] , verbose) ;
332+ send_signal ( process, signal, foreground) ;
333+ process. wait ( ) ?;
334+ Err ( ExitStatus :: Terminated . into ( ) )
335+ }
336+ Ok ( WaitOrTimeoutRet :: TimedOut ) => {
362337 report_if_verbose ( signal, & cmd[ 0 ] , verbose) ;
363338 send_signal ( process, signal, foreground) ;
364339 match kill_after {
365340 None => {
366341 let status = process. wait ( ) ?;
367- if SIGNALED . load ( atomic:: Ordering :: Relaxed ) {
368- Err ( ExitStatus :: Terminated . into ( ) )
369- } else if preserve_status {
342+ if preserve_status {
370343 if let Some ( ec) = status. code ( ) {
371344 Err ( ec. into ( ) )
372345 } else if let Some ( sc) = status. signal ( ) {
@@ -386,6 +359,7 @@ fn timeout(
386359 preserve_status,
387360 foreground,
388361 verbose,
362+ & mut self_pipe,
389363 ) {
390364 Ok ( status) => Err ( status. into ( ) ) ,
391365 Err ( e) => Err ( USimpleError :: new (
0 commit comments