Subject: bug#6308: closed (Re: bug#6308: Preserve command exit code in timeout(3))

From: [email-masked] (GNU bug Tracking System)
Date: Sun, 04 Nov 2012 02:00:02 +0000

Your bug report

#6308: Preserve command exit code in timeout(3)

which was filed against the coreutils package, has been closed.

The explanation is attached below, along with your original report.
If you require more details, please reply to [email-masked].

-- 
6308: http://debbugs.gnu.org/cgi/bugreport.cgi?bug=6308
GNU Bug Tracking System
Contact [email-masked] with problems

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

Subject: Preserve command exit code in timeout(3)
From: "Ángel González" <[email-masked]>
To: bug-coreutils <[email-masked]>
Date: Sun, 30 May 2010 02:15:20 +0200
List-Id: <debbugs-submit.debbugs.gnu.org>

I wanted to keep the original exit status of the command run by
timeout(3), even after sending a timeout signal.
Thus I added a --exit-status parameter to it. Here it is in case you
find that useful, too.

diff --git a/src/timeout.c b/src/timeout.c
index e2234c3..29f5bec 100644
--- a/src/timeout.c
+++ b/src/timeout.c
@@ -64,6 +64,7 @@
 #define AUTHORS proper_name_utf8 ("Padraig Brady", "P\303\241draig Brady")
 
 static int timed_out;
+static int command_exit_status = 0;
 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.  */
@@ -73,6 +74,7 @@ static struct option const long_options[] =
 {
   {"kill-after", required_argument, NULL, 'k'},
   {"signal", required_argument, NULL, 's'},
+  {"exit-status", no_argument, NULL, 'x'},
   {GETOPT_HELP_OPTION_DECL},
   {GETOPT_VERSION_OPTION_DECL},
   {NULL, 0, NULL, 0}
@@ -141,7 +143,10 @@ Mandatory arguments to long options are mandatory for short options too.\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);
+                   See `kill -l` for a list of signals\n\
+  -x, --exit-status\n\
+                   exit with the same status as COMMAND even if the \n\
+                   command timed out\n"), stdout);
 
       fputs (HELP_OPTION_DESCRIPTION, stdout);
       fputs (VERSION_OPTION_DESCRIPTION, stdout);
@@ -152,11 +157,12 @@ DURATION is an integer with an optional suffix:\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.\n"), stdout);
+If the command times out, and --exit-status was 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.  For other processes, \n\
+it may be necessary to use the KILL (9) signal, since this signal cannot \n\
+be caught.\n"), stdout);
       emit_ancillary_info ();
     }
   exit (status);
@@ -253,7 +259,7 @@ main (int argc, char **argv)
   initialize_exit_failure (EXIT_CANCELED);
   atexit (close_stdout);
 
-  while ((c = getopt_long (argc, argv, "+k:s:", long_options, NULL)) != -1)
+  while ((c = getopt_long (argc, argv, "+k:s:x", long_options, NULL)) != -1)
     {
       switch (c)
         {
@@ -266,6 +272,9 @@ main (int argc, char **argv)
           if (term_signal == -1)
             usage (EXIT_CANCELED);
           break;
+		case 'x':
+			command_exit_status = 1;
+			break;
 
         case_GETOPT_HELP_CHAR;
 
@@ -351,7 +360,7 @@ main (int argc, char **argv)
             }
         }
 
-      if (timed_out)
+      if (timed_out && !command_exit_status)
         return EXIT_TIMEDOUT;
       else
         return status;