/* timeout -- run a command with bounded time                                   This is the timeout utility
   Copyright (C) 2008-2018 Free Software Foundation, Inc.                       
                                                                                
   This program is free software: you can redistribute it and/or modify         
   it under the terms of the GNU General Public License as published by         
   the Free Software Foundation, either version 3 of the License, or            
   (at your option) any later version.                                          
                                                                                
   This program is distributed in the hope that it will be useful,              
   but WITHOUT ANY WARRANTY; without even the implied warranty of               
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the                
   GNU General Public License for more details.                                 
                                                                                
   You should have received a copy of the GNU General Public License            
   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */   The GNUv3 license
                                                                                
                                                                                
/* timeout - Start a command, and kill it if the specified timeout expires      
                                                                                
   We try to behave like a shell starting a single (foreground) job,            
   and will kill the job if we receive the alarm signal we setup.               
   The exit status of the job is returned, or one of these errors:              
     EXIT_TIMEDOUT      124      job timed out                                  
     EXIT_CANCELED      125      internal error                                 
     EXIT_CANNOT_INVOKE 126      error executing job                            
     EXIT_ENOENT        127      couldn't find job to exec                      
                                                                                
   Caveats:                                                                     
     If user specifies the KILL (9) signal is to be sent on timeout,            
     the monitor is killed and so exits with 128+9 rather than 124.             
                                                                                
     If you start a command in the background, which reads from the tty         
     and so is immediately sent SIGTTIN to stop, then the timeout               
     process will ignore this so it can timeout the command as expected.        
     This can be seen with 'timeout 10 dd&' for example.                        
     However if one brings this group to the foreground with the 'fg'           
     command before the timer expires, the command will remain                  
     in the stop state as the shell doesn't send a SIGCONT                      
     because the timeout process (group leader) is already running.             
     To get the command running again one can Ctrl-Z, and do fg again.          
     Note one can Ctrl-C the whole job when in this state.                      
     I think this could be fixed but I'm not sure the extra                     
     complication is justified for this scenario.                               
                                                                                
   Written by Pádraig Brady.  */                                                
                                                                                
#include <config.h>                                                             Provides system specific information
#include <getopt.h>                                                             ...!includes auto-comment...
#include <stdio.h>                                                              Provides standard I/O capability
#include <sys/types.h>                                                          Provides system data types
#include <signal.h>                                                             ...!includes auto-comment...
#if HAVE_PRCTL                                                                  Line 52
# include <sys/prctl.h>                                                         Line 53
#endif                                                                          Line 54
#include <sys/wait.h>                                                           ...!includes auto-comment...
                                                                                
#include "system.h"                                                             ...!includes auto-comment...
#include "c-strtod.h"                                                           ...!includes auto-comment...
#include "xstrtod.h"                                                            ...!includes auto-comment...
#include "sig2str.h"                                                            ...!includes auto-comment...
#include "operand2sig.h"                                                        ...!includes auto-comment...
#include "error.h"                                                              ...!includes auto-comment...
#include "quote.h"                                                              ...!includes auto-comment...
                                                                                
#if HAVE_SETRLIMIT                                                              Line 65
/* FreeBSD 5.0 at least needs <sys/types.h> and <sys/time.h> included           
   before <sys/resource.h>.  Currently "system.h" includes <sys/time.h>.  */    
# include <sys/resource.h>                                                      ...!includes auto-comment...
#endif                                                                          Line 69
                                                                                
/* NonStop circa 2011 lacks both SA_RESTART and siginterrupt.  */               
#ifndef SA_RESTART                                                              Line 72
# define SA_RESTART 0                                                           Line 73
#endif                                                                          Line 74
                                                                                
#define PROGRAM_NAME "timeout"                                                  Line 76
                                                                                
#define AUTHORS proper_name ("Padraig Brady")                                   Line 78
                                                                                
static int timed_out;                                                           Line 80
static int term_signal = SIGTERM;  /* same default as kill command.  */         Line 81
static pid_t monitored_pid;                                                     Line 82
static double kill_after;                                                       Line 83
static bool foreground;      /* whether to use another program group.  */       Line 84
static bool preserve_status; /* whether to use a timeout status or not.  */     Line 85
static bool verbose;         /* whether to diagnose timeouts or not.  */        Line 86
static char const* command;                                                     Line 87
                                                                                
/* for long options with no corresponding short option, use enum */             
enum                                                                            Line 90
{                                                                               
      FOREGROUND_OPTION = CHAR_MAX + 1,                                         Line 92
      PRESERVE_STATUS_OPTION                                                    Line 93
};                                                                              Block 1
                                                                                
static struct option const long_options[] =                                     Line 96
{                                                                               
  {"kill-after", required_argument, NULL, 'k'},                                 Line 98
  {"signal", required_argument, NULL, 's'},                                     Line 99
  {"verbose", no_argument, NULL, 'v'},                                          Line 100
  {"foreground", no_argument, NULL, FOREGROUND_OPTION},                         Line 101
  {"preserve-status", no_argument, NULL, PRESERVE_STATUS_OPTION},               Line 102
  {GETOPT_HELP_OPTION_DECL},                                                    Line 103
  {GETOPT_VERSION_OPTION_DECL},                                                 Line 104
  {NULL, 0, NULL, 0}                                                            Line 105
};                                                                              Block 2
                                                                                
/* Start the timeout after which we'll receive a SIGALRM.                       
   Round DURATION up to the next representable value.                           
   Treat out-of-range values as if they were maximal,                           
   as that's more useful in practice than reporting an error.                   
   '0' means don't timeout.  */                                                 
static void                                                                     Line 113
settimeout (double duration, bool warn)                                         Line 114
{                                                                               
                                                                                
/* timer_settime() provides potentially nanosecond resolution.                  
   setitimer() is more portable (to Darwin for example),                        
   but only provides microsecond resolution and thus is                         
   a little more awkward to use with timespecs, as well as being                
   deprecated by POSIX.  Instead we fallback to single second                   
   resolution provided by alarm().  */                                          
                                                                                
#if HAVE_TIMER_SETTIME                                                          Line 124
  struct timespec ts = dtotimespec (duration);                                  Line 125
  struct itimerspec its = { {0, 0}, ts };                                       Line 126
  timer_t timerid;                                                              Line 127
  if (timer_create (CLOCK_REALTIME, NULL, &timerid) == 0)                       Line 128
    {                                                                           
      if (timer_settime (timerid, 0, &its, NULL) == 0)                          Line 130
        return;                                                                 Line 131
      else                                                                      Line 132
        {                                                                       
          if (warn)                                                             Line 134
            error (0, errno, _("warning: timer_settime"));                      Line 135
          timer_delete (timerid);                                               Line 136
        }                                                                       
    }                                                                           
  else if (warn && errno != ENOSYS)                                             Line 139
    error (0, errno, _("warning: timer_create"));                               Line 140
#endif                                                                          Line 141
                                                                                
  unsigned int timeint;                                                         Line 143
  if (UINT_MAX <= duration)                                                     Line 144
    timeint = UINT_MAX;                                                         Line 145
  else                                                                          Line 146
    {                                                                           
      unsigned int duration_floor = duration;                                   Line 148
      timeint = duration_floor + (duration_floor < duration);                   Line 149
    }                                                                           
  alarm (timeint);                                                              Line 151
}                                                                               
                                                                                
/* send SIG avoiding the current process.  */                                   
                                                                                
static int                                                                      Line 156
send_sig (pid_t where, int sig)                                                 Line 157
{                                                                               
  /* If sending to the group, then ignore the signal,                           
     so we don't go into a signal loop.  Note that this will ignore any of the  
     signals registered in install_cleanup(), that are sent after we            
     propagate the first one, which hopefully won't be an issue.  Note this     
     process can be implicitly multithreaded due to some timer_settime()        
     implementations, therefore a signal sent to the group, can be sent         
     multiple times to this process.  */                                        
  if (where == 0)                                                               Line 166
    signal (sig, SIG_IGN);                                                      Line 167
  return kill (where, sig);                                                     Line 168
}                                                                               
                                                                                
/* Signal handler which is required for sigsuspend() to be interrupted          
   whenever SIGCHLD is received.  */                                            
static void                                                                     Line 173
chld (int sig)                                                                  Line 174
{                                                                               
}                                                                               
                                                                                
                                                                                
static void                                                                     Line 179
cleanup (int sig)                                                               Line 180
{                                                                               
  if (sig == SIGALRM)                                                           Line 182
    {                                                                           
      timed_out = 1;                                                            Line 184
      sig = term_signal;                                                        Line 185
    }                                                                           
  if (monitored_pid)                                                            Line 187
    {                                                                           
      if (kill_after)                                                           Line 189
        {                                                                       
          int saved_errno = errno; /* settimeout may reset.  */                 Line 191
          /* Start a new timeout after which we'll send SIGKILL.  */            
          term_signal = SIGKILL;                                                Line 193
          settimeout (kill_after, false);                                       Line 194
          kill_after = 0; /* Don't let later signals reset kill alarm.  */      Line 195
          errno = saved_errno;                                                  Line 196
        }                                                                       
                                                                                
      /* Send the signal directly to the monitored child,                       
         in case it has itself become group leader,                             
         or is not running in a separate group.  */                             
      if (verbose)                                                              Line 202
        {                                                                       
          char signame[MAX (SIG2STR_MAX, INT_BUFSIZE_BOUND (int))];             Line 204
          if (sig2str (sig, signame) != 0)                                      Line 205
            snprintf (signame, sizeof signame, "%d", sig);                      Line 206
          error (0, 0, _("sending signal %s to command %s"),                    Line 207
                 signame, quote (command));                                     Line 208
        }                                                                       
      send_sig (monitored_pid, sig);                                            Line 210
                                                                                
      /* The normal case is the job has remained in our                         
         newly created process group, so send to all processes in that.  */     
      if (!foreground)                                                          Line 214
        {                                                                       
          send_sig (0, sig);                                                    Line 216
          if (sig != SIGKILL && sig != SIGCONT)                                 Line 217
            {                                                                   
              send_sig (monitored_pid, SIGCONT);                                Line 219
              send_sig (0, SIGCONT);                                            Line 220
            }                                                                   
        }                                                                       
    }                                                                           
  else /* we're the child or the child is not exec'd yet.  */                   Line 224
    _exit (128 + sig);                                                          Line 225
}                                                                               Block 6
                                                                                
void                                                                            Line 228
usage (int status)                                                              Line 229
{                                                                               
  if (status != EXIT_SUCCESS)                                                   Line 231
    emit_try_help ();                                                           ...!common auto-comment...
  else                                                                          Line 233
    {                                                                           
      printf (_("\                                                              Line 235
Usage: %s [OPTION] DURATION COMMAND [ARG]...\n\                                 Line 236
  or:  %s [OPTION]\n"), program_name, program_name);                            Line 237
                                                                                
      fputs (_("\                                                               Line 239
Start COMMAND, and kill it if still running after DURATION.\n\                  Line 240
"), stdout);                                                                    Line 241
                                                                                
      emit_mandatory_arg_note ();                                               ...!common auto-comment...
                                                                                
      fputs (_("\                                                               Line 245
      --preserve-status\n\                                                      Line 246
                 exit with the same status as COMMAND, even when the\n\         Line 247
                   command times out\n\                                         Line 248
      --foreground\n\                                                           Line 249
                 when not running timeout directly from a shell prompt,\n\      Line 250
                   allow COMMAND to read from the TTY and get TTY signals;\n\   Line 251
                   in this mode, children of COMMAND will not be timed out\n\   Line 252
  -k, --kill-after=DURATION\n\                                                  Line 253
                 also send a KILL signal if COMMAND is still running\n\         Line 254
                   this long after the initial signal was sent\n\               Line 255
  -s, --signal=SIGNAL\n\                                                        Line 256
                 specify the signal to be sent on timeout;\n\                   Line 257
                   SIGNAL may be a name like 'HUP' or a number;\n\              Line 258
                   see 'kill -l' for a list of signals\n"), stdout);            Line 259
      fputs (_("\                                                               Line 260
  -v, --verbose  diagnose to stderr any signal sent upon timeout\n"), stdout);  Line 261
                                                                                
      fputs (HELP_OPTION_DESCRIPTION, stdout);                                  Line 263
      fputs (VERSION_OPTION_DESCRIPTION, stdout);                               Line 264
                                                                                
      fputs (_("\n\                                                             Line 266
DURATION is a floating point number with an optional suffix:\n\                 Line 267
's' for seconds (the default), 'm' for minutes, 'h' for hours or \              Line 268
'd' for days.\nA duration of 0 disables the associated timeout.\n"), stdout);   Line 269
                                                                                
      fputs (_("\n\                                                             Line 271
If the command times out, and --preserve-status is not set, then exit with\n\   Line 272
status 124.  Otherwise, exit with the status of COMMAND.  If no signal\n\       Line 273
is specified, send the TERM signal upon timeout.  The TERM signal kills\n\      Line 274
any process that does not block or catch that signal.  It may be necessary\n\   Line 275
to use the KILL (9) signal, since this signal cannot be caught, in which\n\     Line 276
case the exit status is 128+9 rather than 124.\n"), stdout);                    Line 277
      emit_ancillary_info (PROGRAM_NAME);                                       Line 278
    }                                                                           
  exit (status);                                                                Line 280
}                                                                               Block 7
                                                                                
/* Given a floating point value *X, and a suffix character, SUFFIX_CHAR,        
   scale *X by the multiplier implied by SUFFIX_CHAR.  SUFFIX_CHAR may          
   be the NUL byte or 's' to denote seconds, 'm' for minutes, 'h' for           
   hours, or 'd' for days.  If SUFFIX_CHAR is invalid, don't modify *X          
   and return false.  Otherwise return true.  */                                
                                                                                
static bool                                                                     Line 289
apply_time_suffix (double *x, char suffix_char)                                 Line 290
{                                                                               
  int multiplier;                                                               Line 292
                                                                                
  switch (suffix_char)                                                          Line 294
    {                                                                           
    case 0:                                                                     Line 296
    case 's':                                                                   Line 297
      multiplier = 1;                                                           Line 298
      break;                                                                    Line 299
    case 'm':                                                                   Line 300
      multiplier = 60;                                                          Line 301
      break;                                                                    Line 302
    case 'h':                                                                   Line 303
      multiplier = 60 * 60;                                                     Line 304
      break;                                                                    Line 305
    case 'd':                                                                   Line 306
      multiplier = 60 * 60 * 24;                                                Line 307
      break;                                                                    Line 308
    default:                                                                    Line 309
      return false;                                                             Line 310
    }                                                                           
                                                                                
  *x *= multiplier;                                                             Line 313
                                                                                
  return true;                                                                  Line 315
}                                                                               Block 8
                                                                                
static double                                                                   Line 318
parse_duration (const char* str)                                                Line 319
{                                                                               
  double duration;                                                              Line 321
  const char *ep;                                                               Line 322
                                                                                
  if (! (xstrtod (str, &ep, &duration, c_strtod) || errno == ERANGE)            Line 324
      /* Nonnegative interval.  */                                              
      || ! (0 <= duration)                                                      Line 326
      /* No extra chars after the number and an optional s,m,h,d char.  */      
      || (*ep && *(ep + 1))                                                     Line 328
      /* Check any suffix char and update timeout based on the suffix.  */      
      || !apply_time_suffix (&duration, *ep))                                   Line 330
    {                                                                           
      error (0, 0, _("invalid time interval %s"), quote (str));                 Line 332
      usage (EXIT_CANCELED);                                                    Line 333
    }                                                                           
                                                                                
  return duration;                                                              Line 336
}                                                                               Block 9
                                                                                
static void                                                                     Line 339
unblock_signal (int sig)                                                        Line 340
{                                                                               
  sigset_t unblock_set;                                                         Line 342
  sigemptyset (&unblock_set);                                                   Line 343
  sigaddset (&unblock_set, sig);                                                Line 344
  if (sigprocmask (SIG_UNBLOCK, &unblock_set, NULL) != 0)                       Line 345
    error (0, errno, _("warning: sigprocmask"));                                Line 346
}                                                                               Block 10
                                                                                
static void                                                                     Line 349
install_sigchld (void)                                                          Line 350
{                                                                               
  struct sigaction sa;                                                          Line 352
  sigemptyset (&sa.sa_mask);  /* Allow concurrent calls to handler */           Line 353
  sa.sa_handler = chld;                                                         Line 354
  sa.sa_flags = SA_RESTART;   /* Restart syscalls if possible, as that's        Line 355
                                 more likely to work cleanly.  */               
                                                                                
  sigaction (SIGCHLD, &sa, NULL);                                               Line 358
                                                                                
  /* We inherit the signal mask from our parent process,                        
     so ensure SIGCHLD is not blocked. */                                       
  unblock_signal (SIGCHLD);                                                     Line 362
}                                                                               Block 11
                                                                                
static void                                                                     Line 365
install_cleanup (int sigterm)                                                   Line 366
{                                                                               
  struct sigaction sa;                                                          Line 368
  sigemptyset (&sa.sa_mask);  /* Allow concurrent calls to handler */           Line 369
  sa.sa_handler = cleanup;                                                      Line 370
  sa.sa_flags = SA_RESTART;   /* Restart syscalls if possible, as that's        Line 371
                                 more likely to work cleanly.  */               
                                                                                
  sigaction (SIGALRM, &sa, NULL); /* our timeout.  */                           Line 374
  sigaction (SIGINT, &sa, NULL);  /* Ctrl-C at terminal for example.  */        Line 375
  sigaction (SIGQUIT, &sa, NULL); /* Ctrl-\ at terminal for example.  */        Line 376
  sigaction (SIGHUP, &sa, NULL);  /* terminal closed for example.  */           Line 377
  sigaction (SIGTERM, &sa, NULL); /* if we're killed, stop monitored proc.  */  Line 378
  sigaction (sigterm, &sa, NULL); /* user specified termination signal.  */     Line 379
}                                                                               Block 12
                                                                                
/* Block all signals which were registered with cleanup() as the signal         
   handler, so we never kill processes after waitpid() returns.                 
   Also block SIGCHLD to ensure it doesn't fire between                         
   waitpid() polling and sigsuspend() waiting for a signal.                     
   Return original mask in OLD_SET.  */                                         
static void                                                                     Line 387
block_cleanup_and_chld (int sigterm, sigset_t *old_set)                         Line 388
{                                                                               
  sigset_t block_set;                                                           Line 390
  sigemptyset (&block_set);                                                     Line 391
                                                                                
  sigaddset (&block_set, SIGALRM);                                              Line 393
  sigaddset (&block_set, SIGINT);                                               Line 394
  sigaddset (&block_set, SIGQUIT);                                              Line 395
  sigaddset (&block_set, SIGHUP);                                               Line 396
  sigaddset (&block_set, SIGTERM);                                              Line 397
  sigaddset (&block_set, sigterm);                                              Line 398
                                                                                
  sigaddset (&block_set, SIGCHLD);                                              Line 400
                                                                                
  if (sigprocmask (SIG_BLOCK, &block_set, old_set) != 0)                        Line 402
    error (0, errno, _("warning: sigprocmask"));                                Line 403
}                                                                               Block 13
                                                                                
/* Try to disable core dumps for this process.                                  
   Return TRUE if successful, FALSE otherwise.  */                              
static bool                                                                     Line 408
disable_core_dumps (void)                                                       Line 409
{                                                                               
#if HAVE_PRCTL && defined PR_SET_DUMPABLE                                       Line 411
  if (prctl (PR_SET_DUMPABLE, 0) == 0)                                          Line 412
    return true;                                                                Line 413
                                                                                
#elif HAVE_SETRLIMIT && defined RLIMIT_CORE                                     Line 415
  /* Note this doesn't disable processing by a filter in                        
     /proc/sys/kernel/core_pattern on Linux.  */                                
  if (setrlimit (RLIMIT_CORE, &(struct rlimit) {0,0}) == 0)                     Line 418
    return true;                                                                Line 419
                                                                                
#else                                                                           Line 421
  return false;                                                                 Line 422
#endif                                                                          Line 423
                                                                                
  error (0, errno, _("warning: disabling core dumps failed"));                  Line 425
  return false;                                                                 Line 426
}                                                                               Block 14
                                                                                
int                                                                             
main (int argc, char **argv)                                                    Line 430
{                                                                               
  double timeout;                                                               Line 432
  char signame[SIG2STR_MAX];                                                    Line 433
  int c;                                                                        Line 434
                                                                                
  initialize_main (&argc, &argv);                                               VMS-specific entry point handling wildcard expansion
  set_program_name (argv[0]);                                                   Retains program name and discards path
  setlocale (LC_ALL, "");                                                       Sets up internationalization (i18n)
  bindtextdomain (PACKAGE, LOCALEDIR);                                          Assigns i18n directorySets text domain for _() [gettext()] function
  textdomain (PACKAGE);                                                         Sets text domain for _() [gettext()] function
                                                                                
  initialize_exit_failure (EXIT_CANCELED);                                      Line 442
  atexit (close_stdout);                                                        Close stdout on exit (see gnulib)
                                                                                
  while ((c = getopt_long (argc, argv, "+k:s:v", long_options, NULL)) != -1)    Line 445
    {                                                                           
      switch (c)                                                                Line 447
        {                                                                       
        case 'k':                                                               Line 449
          kill_after = parse_duration (optarg);                                 Line 450
          break;                                                                Line 451
                                                                                
        case 's':                                                               Line 453
          term_signal = operand2sig (optarg, signame);                          Line 454
          if (term_signal == -1)                                                Line 455
            usage (EXIT_CANCELED);                                              Line 456
          break;                                                                Line 457
                                                                                
        case 'v':                                                               Line 459
          verbose = true;                                                       Line 460
          break;                                                                Line 461
                                                                                
        case FOREGROUND_OPTION:                                                 Line 463
          foreground = true;                                                    Line 464
          break;                                                                Line 465
                                                                                
        case PRESERVE_STATUS_OPTION:                                            Line 467
          preserve_status = true;                                               Line 468
          break;                                                                Line 469
                                                                                
        case_GETOPT_HELP_CHAR;                                                  Line 471
                                                                                
        case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);                       Line 473
                                                                                
        default:                                                                Line 475
          usage (EXIT_CANCELED);                                                Line 476
          break;                                                                Line 477
        }                                                                       
    }                                                                           
                                                                                
  if (argc - optind < 2)                                                        Line 481
    usage (EXIT_CANCELED);                                                      Line 482
                                                                                
  timeout = parse_duration (argv[optind++]);                                    Line 484
                                                                                
  argv += optind;                                                               Line 486
  command = argv[0];                                                            Line 487
                                                                                
  /* Ensure we're in our own group so all subprocesses can be killed.           
     Note we don't just put the child in a separate group as                    
     then we would need to worry about foreground and background groups         
     and propagating signals between them.  */                                  
  if (!foreground)                                                              Line 493
    setpgid (0, 0);                                                             Line 494
                                                                                
  /* Setup handlers before fork() so that we                                    
     handle any signals caused by child, without races.  */                     
  install_cleanup (term_signal);                                                Line 498
  signal (SIGTTIN, SIG_IGN);   /* Don't stop if background child needs tty.  */ Line 499
  signal (SIGTTOU, SIG_IGN);   /* Don't stop if background child needs tty.  */ Line 500
  install_sigchld ();          /* Interrupt sigsuspend() when child exits.   */ Line 501
                                                                                
  monitored_pid = fork ();                                                      Line 503...!syscalls auto-comment...
  if (monitored_pid == -1)                                                      Line 504
    {                                                                           
      error (0, errno, _("fork system call failed"));                           Line 506
      return EXIT_CANCELED;                                                     Line 507
    }                                                                           
  else if (monitored_pid == 0)                                                  Line 509
    {                           /* child */                                     Line 510
      /* exec doesn't reset SIG_IGN -> SIG_DFL.  */                             
      signal (SIGTTIN, SIG_DFL);                                                Line 512
      signal (SIGTTOU, SIG_DFL);                                                Line 513
                                                                                
      execvp (argv[0], argv);   /* FIXME: should we use "sh -c" ... here?  */   Line 515
                                                                                
      /* exit like sh, env, nohup, ...  */                                      
      int exit_status = errno == ENOENT ? EXIT_ENOENT : EXIT_CANNOT_INVOKE;     Line 518
      error (0, errno, _("failed to run command %s"), quote (command));         Line 519
      return exit_status;                                                       Line 520
    }                                                                           
  else                                                                          Line 522
    {                                                                           
      pid_t wait_result;                                                        Line 524
      int status;                                                               Line 525
                                                                                
      /* We configure timers so that SIGALRM is sent on expiry.                 
         Therefore ensure we don't inherit a mask blocking SIGALRM.  */         
      unblock_signal (SIGALRM);                                                 Line 529
                                                                                
      settimeout (timeout, true);                                               Line 531
                                                                                
      /* Ensure we don't cleanup() after waitpid() reaps the child,             
         to avoid sending signals to a possibly different process.  */          
      sigset_t cleanup_set;                                                     Line 535
      block_cleanup_and_chld (term_signal, &cleanup_set);                       Line 536
                                                                                
      while ((wait_result = waitpid (monitored_pid, &status, WNOHANG)) == 0)    Line 538
        sigsuspend (&cleanup_set);  /* Wait with cleanup signals unblocked.  */ Line 539
                                                                                
      if (wait_result < 0)                                                      Line 541
        {                                                                       
          /* shouldn't happen.  */                                              
          error (0, errno, _("error waiting for command"));                     Line 544
          status = EXIT_CANCELED;                                               Line 545
        }                                                                       
      else                                                                      Line 547
        {                                                                       
          if (WIFEXITED (status))                                               Line 549
            status = WEXITSTATUS (status);                                      Line 550
          else if (WIFSIGNALED (status))                                        Line 551
            {                                                                   
              int sig = WTERMSIG (status);                                      Line 553
              if (WCOREDUMP (status))                                           Line 554
                error (0, 0, _("the monitored command dumped core"));           Line 555
              if (!timed_out && disable_core_dumps ())                          Line 556
                {                                                               
                  /* exit with the signal flag set.  */                         
                  signal (sig, SIG_DFL);                                        Line 559
                  unblock_signal (sig);                                         Line 560
                  raise (sig);                                                  Line 561
                }                                                               
              status = sig + 128; /* what sh returns for signaled processes.  */Line 563
            }                                                                   
          else                                                                  Line 565
            {                                                                   
              /* shouldn't happen.  */                                          
              error (0, 0, _("unknown status from command (%d)"), status);      Line 568
              status = EXIT_FAILURE;                                            Line 569
            }                                                                   
        }                                                                       
                                                                                
      if (timed_out && !preserve_status)                                        Line 573
        status = EXIT_TIMEDOUT;                                                 Line 574
      return status;                                                            Line 575
    }                                                                           
}                                                                               Block 15