Subject: Re: bug#8938: make timeout and CTRL-C

From: Pádraig Brady <[email-masked]>
Date: Thu, 07 Jul 2011 11:05:21 +0100

On 06/07/11 23:37, Pádraig Brady wrote:
> OK I've added --foreground to support this.
> Note it still maintains a separate timeout
> monitor process to return 124 on timeout etc.

Updated wording and NEWS entry now included.
I'll push this later today.

cheers,
Pádraig.
>From b7326b5b54cfa44a022ec5e894a36985bf764525 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?P=C3=A1draig=20Brady?= <[email-masked]>
Date: Wed, 6 Jul 2011 23:17:10 +0100
Subject: [PATCH] timeout: add --foreground to support interactive commands

Or more accurately, commands not started from the shell prompt,
that are interactive, or need to receive Ctrl-C etc. from the terminal.

* doc/coreutils.texi (timeout invocation): Document --foreground.
* src/timeout.c (main): Set the foreground flag and don't create
a separate group.
(cleanup): Only send a signal directly to the monitored command
when the foreground flag is set.
(usage): Describe --foreground.
NEWS: Mention the new option.

Reported by Shay Shimony
Analysis by Alan Curry
Fix suggested by Paul Eggert
---
 NEWS               |    4 ++++
 doc/coreutils.texi |   17 +++++++++++++++++
 src/timeout.c      |   36 ++++++++++++++++++++++++++++--------
 3 files changed, 49 insertions(+), 8 deletions(-)

diff --git a/NEWS b/NEWS
index d58df26..cde68d3 100644
--- a/NEWS
+++ b/NEWS
@@ -29,6 +29,10 @@ GNU coreutils NEWS                                    -*- outline -*-
   Note the use of single quotes, not double quotes.
   That creates files named xaa.xz, xab.xz and xac.xz.
 
+  timeout accepts a new --foreground option, to support commands not started
+  directly from a shell prompt, where the command is interactive or needs to
+  receive signals initiated from the terminal.
+
 ** Improvements
 
   shuf outputs small subsets of large permutations much more efficiently.
diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index c59af2f..e04806d 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -15651,6 +15651,23 @@ The program accepts the following options.  Also see @ref{Common options}.
 Options must precede operands.
 
 @table @samp
+@itemx --foreground
+@opindex --foreground
+Don't create a separate background program group, so that
+the managed @var{COMMAND} can use the foreground TTY normally.
+This is needed to support timing out commands not started
+directly from an interactive shell, in two situations.
+@enumerate
+@item
+@var{command} is interactive and needs to read from the terminal for example
+@item
+the user wants to support sending signals directly to @var{command}
+from the terminal (like Ctrl-C for example)
+@end enumerate
+
+Note in this mode of operation, any children of @var{command}
+will not be timed out.
+
 @item -k @var{duration}
 @itemx --kill-after=@var{duration}
 @opindex -k
diff --git a/src/timeout.c b/src/timeout.c
index a686225..8f0980b 100644
--- a/src/timeout.c
+++ b/src/timeout.c
@@ -67,11 +67,19 @@ static int term_signal = SIGTERM;  /* same default as kill command.  */
 static int monitored_pid;
 static int sigs_to_ignore[NSIG];   /* so monitor can ignore sigs it resends.  */
 static unsigned long kill_after;
+static bool foreground;            /* whether to use another program group.  */
+
+/* for long options with no corresponding short option, use enum */
+enum
+{
+      FOREGROUND_OPTION = CHAR_MAX + 1
+};
 
 static struct option const long_options[] =
 {
   {"kill-after", required_argument, NULL, 'k'},
   {"signal", required_argument, NULL, 's'},
+  {"foreground", no_argument, NULL, FOREGROUND_OPTION},
   {GETOPT_HELP_OPTION_DECL},
   {GETOPT_VERSION_OPTION_DECL},
   {NULL, 0, NULL, 0}
@@ -96,6 +104,8 @@ cleanup (int sig)
     }
   if (monitored_pid)
     {
+      int where = foreground ? monitored_pid : 0;
+
       if (sigs_to_ignore[sig])
         {
           sigs_to_ignore[sig] = 0;
@@ -108,9 +118,10 @@ cleanup (int sig)
           alarm (kill_after);
           kill_after = 0; /* Don't let later signals reset kill alarm.  */
         }
-      send_sig (0, sig);
+
+      send_sig (where, sig);
       if (sig != SIGKILL && sig != SIGCONT)
-        send_sig (0, SIGCONT);
+        send_sig (where, SIGCONT);
     }
   else /* we're the child or the child is not exec'd yet.  */
     _exit (128 + sig);
@@ -134,13 +145,17 @@ Start COMMAND, and kill it if still running after DURATION.\n\
 Mandatory arguments to long options are mandatory for short options too.\n\
 "), stdout);
       fputs (_("\
+      --foreground\n\
+                 When not running timeout directly from a shell prompt,\n\
+                 allow COMMAND to read from the TTY and receive TTY signals.\n\
+                 In this mode, children of COMMAND will not be timed out.\n\
   -k, --kill-after=DURATION\n\
-                   also send a KILL signal if COMMAND is still running\n\
-                   this long after the initial signal was sent.\n\
+                 also send a KILL signal if COMMAND is still running\n\
+                 this long after the initial signal was sent.\n\
   -s, --signal=SIGNAL\n\
-                   specify the signal to be sent on timeout.\n\
-                   SIGNAL may be a name like `HUP' or a number.\n\
-                   See `kill -l` for a list of signals\n"), stdout);
+                 specify the signal to be sent on timeout.\n\
+                 SIGNAL may be a name like `HUP' or a number.\n\
+                 See `kill -l` for a list of signals\n"), stdout);
 
       fputs (HELP_OPTION_DESCRIPTION, stdout);
       fputs (VERSION_OPTION_DESCRIPTION, stdout);
@@ -266,6 +281,10 @@ main (int argc, char **argv)
             usage (EXIT_CANCELED);
           break;
 
+        case FOREGROUND_OPTION:
+          foreground = true;
+          break;
+
         case_GETOPT_HELP_CHAR;
 
         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
@@ -287,7 +306,8 @@ main (int argc, char **argv)
      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.  */
-  setpgid (0, 0);
+  if (!foreground)
+    setpgid (0, 0);
 
   /* Setup handlers before fork() so that we
      handle any signals caused by child, without races.  */
-- 
1.7.5.2