Subject: Re: bug#6308: Preserve command exit code in timeout(3)
From: Pádraig Brady <[email-masked]>
To: Jim Meyering <[email-masked]>
Cc: Ángel González <[email-masked]>, [email-masked], Thomas Krennwallner <[email-masked]>
Date: Sun, 04 Nov 2012 01:56:30 +0000
List-Id: <debbugs-submit.debbugs.gnu.org>

On 10/29/2012 11:10 AM, Pádraig Brady wrote:
> On 10/29/2012 10:07 AM, Jim Meyering wrote:
>> These days, I prefer more expressive option names.  As long as a short
>> prefix is unique, length doesn't really matter.  Here, --p is enough.
>> However, --preserve-status seems ok, too.  There is precedent for
>> using "status" to mean "exit status" with md5sum's --status option.
>> Your call.
>
> I've a slight preference for --preserve-status so.

I'm pushing the attached so marking this done.

cheers,
Pádraig.
From de2397fece839ce90f71e0624f5435755ad9ea01 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=C3=81ngel=20Gonz=C3=A1lez?= <[email-masked]>
Date: Mon, 29 Oct 2012 00:36:08 +0000
Subject: [PATCH] timeout: add --preserve-status to always propagate the exit
 status

It's useful for commands that support running for an indeterminite
amount of time, to not return a specific timeout exit status (124),
and instead let the command handle the timeout signal and return
a status for the work done so far.

* doc/coreutils.texi (timeout invocation): Describe the new option.
* src/timeout.c (preserve_status): A new global boolean to
enable the --preserve-status behavior.
(usage): Describe the new option.
(main): Don't return EXIT_TIMEOUT of preserve_status is set.
* tests/misc/timeout.sh: Add a test for the new option.
---
 doc/coreutils.texi    |    6 ++++++
 src/timeout.c         |   28 +++++++++++++++++++---------
 tests/misc/timeout.sh |    5 +++++
 3 files changed, 30 insertions(+), 9 deletions(-)

diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index e2b1059..767a31e 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -15890,6 +15890,12 @@ The program accepts the following options.  Also see @ref{Common options}.
 Options must precede operands.
 
 @table @samp
+@item --preserve-status
+@opindex --preserve-status
+Return the exit status of the managed @var{command} on timeout, rather than
+a specific exit status indicating a timeout.  This is useful if the
+managed @var{command} supports running for an indeterminite amount of time.
+
 @item --foreground
 @opindex --foreground
 Don't create a separate background program group, so that
diff --git a/src/timeout.c b/src/timeout.c
index 4ce18ad..02d21c5 100644
--- a/src/timeout.c
+++ b/src/timeout.c
@@ -81,12 +81,14 @@ static int timed_out;
 static int term_signal = SIGTERM;  /* same default as kill command.  */
 static int monitored_pid;
 static double kill_after;
-static bool foreground;            /* whether to use another program group.  */
+static bool foreground;      /* whether to use another program group.  */
+static bool preserve_status; /* whether to use a timeout status or not.  */
 
 /* for long options with no corresponding short option, use enum */
 enum
 {
-      FOREGROUND_OPTION = CHAR_MAX + 1
+      FOREGROUND_OPTION = CHAR_MAX + 1,
+      PRESERVE_STATUS_OPTION
 };
 
 static struct option const long_options[] =
@@ -94,6 +96,7 @@ static struct option const long_options[] =
   {"kill-after", required_argument, NULL, 'k'},
   {"signal", required_argument, NULL, 's'},
   {"foreground", no_argument, NULL, FOREGROUND_OPTION},
+  {"preserve-status", no_argument, NULL, PRESERVE_STATUS_OPTION},
   {GETOPT_HELP_OPTION_DECL},
   {GETOPT_VERSION_OPTION_DECL},
   {NULL, 0, NULL, 0}
@@ -214,6 +217,9 @@ 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 (_("\
+      --preserve-status\n\
+                 exit with the same status as COMMAND, even when the\n\
+                 command times out\n\
       --foreground\n\
                  When not running timeout directly from a shell prompt,\n\
                  allow COMMAND to read from the TTY and receive TTY signals.\n\
@@ -235,12 +241,12 @@ DURATION is a floating point number with an optional suffix:\n\
 or 'd' for days.\n"), stdout);
 
       fputs (_("\n\
-If the command times out, then exit with status 124.  Otherwise, exit\n\
-with the status of COMMAND.  If no signal is specified, send the TERM\n\
-signal upon timeout.  The TERM signal kills any process that does not\n\
-block or catch that signal.  For other processes, it may be necessary to\n\
-use the KILL (9) signal, since this signal cannot be caught.  If the\n\
-KILL (9) signal is sent, the exit status is 128+9 rather than 124.\n"), stdout);
+If the command times out, and --preserve-status is not set, then exit with\n\
+status 124.  Otherwise, exit with the status of COMMAND.  If no signal\n\
+is specified, send the TERM signal upon timeout.  The TERM signal kills\n\
+any process that does not block or catch that signal.  It may be necessary\n\
+to use the KILL (9) signal, since this signal cannot be caught, in which\n\
+case the exit status is 128+9 rather than 124.\n"), stdout);
       emit_ancillary_info ();
     }
   exit (status);
@@ -376,6 +382,10 @@ main (int argc, char **argv)
           foreground = true;
           break;
 
+        case PRESERVE_STATUS_OPTION:
+          preserve_status = true;
+          break;
+
         case_GETOPT_HELP_CHAR;
 
         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
@@ -470,7 +480,7 @@ main (int argc, char **argv)
             }
         }
 
-      if (timed_out)
+      if (timed_out && !preserve_status)
         return EXIT_TIMEDOUT;
       else
         return status;
diff --git a/tests/misc/timeout.sh b/tests/misc/timeout.sh
index 57a4e15..66d6956 100755
--- a/tests/misc/timeout.sh
+++ b/tests/misc/timeout.sh
@@ -36,6 +36,11 @@ test $? = 2 || fail=1
 timeout 1 sleep 10
 test $? = 124 || fail=1
 
+# exit status propagation even on timeout
+timeout --preserve-status 1 sleep 10
+# exit status should be 128+TERM
+test $? = 124 && fail=1
+
 # kill delay. Note once the initial timeout triggers,
 # the exit status will be 124 even if the command
 # exits on its own accord.
-- 
1.7.6.4