Blame SOURCES/coreutils-8.22-dd-progress.patch

fc4c3c
From af2a4ed22594badd2719c0123441d69b17bd8328 Mon Sep 17 00:00:00 2001
fc4c3c
From: Federico Simoncelli <fsimonce@redhat.com>
fc4c3c
Date: Fri, 26 Sep 2014 17:12:32 +0000
fc4c3c
Subject: dd: new status=progress level to print stats periodically
fc4c3c
fc4c3c
* src/dd.c: Report the transfer progress every second when the
fc4c3c
new status=progress level is used.  Adjust the handling and
fc4c3c
description of the status= option so that they're treated as
fc4c3c
mutually exclusive levels, rather than flags with implicit precedence.
fc4c3c
* doc/coreutils.texi (dd invocation): Document the new progress
fc4c3c
status level.  Reference the new level in the description of SIGUSR1.
fc4c3c
* tests/dd/stats.sh: Add new test for status=progress.
fc4c3c
* tests/dd/misc.sh: Change so status=none only takes precedence
fc4c3c
if it's the last level specified.
fc4c3c
---
fc4c3c
diff --git a/doc/coreutils.texi b/doc/coreutils.texi
fc4c3c
index 7d32af5..03bb710 100644
fc4c3c
--- a/doc/coreutils.texi
fc4c3c
+++ b/doc/coreutils.texi
fc4c3c
@@ -8631,24 +8631,32 @@ will ensure that @samp{count=} corresponds to complete input blocks
fc4c3c
 rather than the traditional POSIX specified behavior of counting
fc4c3c
 input read operations.
fc4c3c
 
fc4c3c
-@item status=@var{which}
fc4c3c
+@item status=@var{level}
fc4c3c
 @opindex status
fc4c3c
 Transfer information is normally output to stderr upon
fc4c3c
 receipt of the @samp{INFO} signal or when @command{dd} exits.
fc4c3c
-Specifying @var{which} will identify which information to suppress.
fc4c3c
+Specifying @var{level} will adjust the amount of information printed,
fc4c3c
+with the last @var{level} specified taking precedence.
fc4c3c
 
fc4c3c
 @table @samp
fc4c3c
 
fc4c3c
-@item noxfer
fc4c3c
-@opindex noxfer @r{dd status=}
fc4c3c
-Do not print the transfer rate and volume statistics
fc4c3c
-that normally make up the last status line.
fc4c3c
-
fc4c3c
 @item none
fc4c3c
 @opindex none @r{dd status=}
fc4c3c
 Do not print any informational or warning messages to stderr.
fc4c3c
 Error messages are output as normal.
fc4c3c
 
fc4c3c
+@item noxfer
fc4c3c
+@opindex noxfer @r{dd status=}
fc4c3c
+Do not print the final transfer rate and volume statistics
fc4c3c
+that normally make up the last status line.
fc4c3c
+
fc4c3c
+@item progress
fc4c3c
+@opindex progress @r{dd status=}
fc4c3c
+Print the transfer rate and volume statistics on stderr,
fc4c3c
+when processing each input block.  Statistics are output
fc4c3c
+on a single line at most once every second, but updates
fc4c3c
+can be delayed when waiting on I/O.
fc4c3c
+
fc4c3c
 @end table
fc4c3c
 
fc4c3c
 @item conv=@var{conversion}[,@var{conversion}]@dots{}
fc4c3c
@@ -9033,6 +9041,9 @@ The above script will output in the following format
fc4c3c
 5120000000 bytes (5.1 GB) copied, 18.913 seconds, 271 MB/s
fc4c3c
 @end example
fc4c3c
 
fc4c3c
+Note also the @samp{status=progress} option which periodically updates
fc4c3c
+the last line of the transfer statistics above.
fc4c3c
+
fc4c3c
 @vindex POSIXLY_CORRECT
fc4c3c
 On systems lacking the @samp{INFO} signal @command{dd} responds to the
fc4c3c
 @samp{USR1} signal instead, unless the @env{POSIXLY_CORRECT}
fc4c3c
diff --git a/src/dd.c b/src/dd.c
fc4c3c
index d22ec59..4018190 100644
fc4c3c
--- a/src/dd.c
fc4c3c
+++ b/src/dd.c
fc4c3c
@@ -34,6 +34,7 @@
fc4c3c
 #include "long-options.h"
fc4c3c
 #include "quote.h"
fc4c3c
 #include "quotearg.h"
fc4c3c
+#include "verror.h"
fc4c3c
 #include "xstrtol.h"
fc4c3c
 #include "xtime.h"
fc4c3c
 
fc4c3c
@@ -132,11 +133,13 @@ enum
fc4c3c
     C_SPARSE = 0200000
fc4c3c
   };
fc4c3c
 
fc4c3c
-/* Status bit masks.  */
fc4c3c
+/* Status levels.  */
fc4c3c
 enum
fc4c3c
   {
fc4c3c
-    STATUS_NOXFER = 01,
fc4c3c
-    STATUS_NONE = 02
fc4c3c
+    STATUS_NONE = 1,
fc4c3c
+    STATUS_NOXFER = 2,
fc4c3c
+    STATUS_DEFAULT = 3,
fc4c3c
+    STATUS_PROGRESS = 4
fc4c3c
   };
fc4c3c
 
fc4c3c
 /* The name of the input file, or NULL for the standard input. */
fc4c3c
@@ -188,7 +191,7 @@ static int input_flags = 0;
fc4c3c
 static int output_flags = 0;
fc4c3c
 
fc4c3c
 /* Status flags for what is printed to stderr.  */
fc4c3c
-static int status_flags = 0;
fc4c3c
+static int status_level = STATUS_DEFAULT;
fc4c3c
 
fc4c3c
 /* If nonzero, filter characters through the translation table.  */
fc4c3c
 static bool translation_needed = false;
fc4c3c
@@ -211,6 +214,12 @@ static uintmax_t w_bytes = 0;
fc4c3c
 /* Time that dd started.  */
fc4c3c
 static xtime_t start_time;
fc4c3c
 
fc4c3c
+/* Previous time for periodic progress.  */
fc4c3c
+static xtime_t previous_time;
fc4c3c
+
fc4c3c
+/* Whether a '\n' is pending after writing progress.  */
fc4c3c
+static bool newline_pending;
fc4c3c
+
fc4c3c
 /* True if input is seekable.  */
fc4c3c
 static bool input_seekable;
fc4c3c
 
fc4c3c
@@ -373,8 +382,9 @@ static struct symbol_value const flags[] =
fc4c3c
 /* Status, for status="...".  */
fc4c3c
 static struct symbol_value const statuses[] =
fc4c3c
 {
fc4c3c
-  {"noxfer",	STATUS_NOXFER},
fc4c3c
   {"none",	STATUS_NONE},
fc4c3c
+  {"noxfer",	STATUS_NOXFER},
fc4c3c
+  {"progress",	STATUS_PROGRESS},
fc4c3c
   {"",		0}
fc4c3c
 };
fc4c3c
 
fc4c3c
@@ -517,6 +527,25 @@ maybe_close_stdout (void)
fc4c3c
     _exit (EXIT_FAILURE);
fc4c3c
 }
fc4c3c
 
fc4c3c
+/* Like error() but handle any pending newline.  */
fc4c3c
+
fc4c3c
+static void _GL_ATTRIBUTE_FORMAT ((__printf__, 3, 4))
fc4c3c
+nl_error (int status, int errnum, const char *fmt, ...)
fc4c3c
+{
fc4c3c
+  if (newline_pending)
fc4c3c
+    {
fc4c3c
+      fputc ('\n', stderr);
fc4c3c
+      newline_pending = false;
fc4c3c
+    }
fc4c3c
+
fc4c3c
+  va_list ap;
fc4c3c
+  va_start (ap, fmt);
fc4c3c
+  verror (status, errnum, fmt, ap);
fc4c3c
+  va_end (ap);
fc4c3c
+}
fc4c3c
+
fc4c3c
+#define error nl_error
fc4c3c
+
fc4c3c
 void
fc4c3c
 usage (int status)
fc4c3c
 {
fc4c3c
@@ -546,8 +575,10 @@ Copy a file, converting and formatting according to the operands.\n\
fc4c3c
   oflag=FLAGS     write as per the comma separated symbol list\n\
fc4c3c
   seek=N          skip N obs-sized blocks at start of output\n\
fc4c3c
   skip=N          skip N ibs-sized blocks at start of input\n\
fc4c3c
-  status=WHICH    WHICH info to suppress outputting to stderr;\n\
fc4c3c
-                  'noxfer' suppresses transfer stats, 'none' suppresses all\n\
fc4c3c
+  status=LEVEL    The LEVEL of information to print to stderr;\n\
fc4c3c
+                  'none' suppresses everything but error messages,\n\
fc4c3c
+                  'noxfer' suppresses the final transfer statistics,\n\
fc4c3c
+                  'progress' shows periodic transfer statistics\n\
fc4c3c
 "), stdout);
fc4c3c
       fputs (_("\
fc4c3c
 \n\
fc4c3c
@@ -724,8 +755,7 @@ multiple_bits_set (int i)
fc4c3c
 /* Print transfer statistics.  */
fc4c3c
 
fc4c3c
 static void
fc4c3c
-print_stats (void)
fc4c3c
-{
fc4c3c
+print_xfer_stats (xtime_t progress_time) {
fc4c3c
   char hbuf[LONGEST_HUMAN_READABLE + 1];
fc4c3c
   int human_opts =
fc4c3c
     (human_autoscale | human_round_to_nearest
fc4c3c
@@ -733,23 +763,8 @@ print_stats (void)
fc4c3c
   double delta_s;
fc4c3c
   char const *bytes_per_second;
fc4c3c
 
fc4c3c
-  if (status_flags & STATUS_NONE)
fc4c3c
-    return;
fc4c3c
-
fc4c3c
-  fprintf (stderr,
fc4c3c
-           _("%"PRIuMAX"+%"PRIuMAX" records in\n"
fc4c3c
-             "%"PRIuMAX"+%"PRIuMAX" records out\n"),
fc4c3c
-           r_full, r_partial, w_full, w_partial);
fc4c3c
-
fc4c3c
-  if (r_truncate != 0)
fc4c3c
-    fprintf (stderr,
fc4c3c
-             ngettext ("%"PRIuMAX" truncated record\n",
fc4c3c
-                       "%"PRIuMAX" truncated records\n",
fc4c3c
-                       select_plural (r_truncate)),
fc4c3c
-             r_truncate);
fc4c3c
-
fc4c3c
-  if (status_flags & STATUS_NOXFER)
fc4c3c
-    return;
fc4c3c
+  if (progress_time)
fc4c3c
+    fputc ('\r', stderr);
fc4c3c
 
fc4c3c
   /* Use integer arithmetic to compute the transfer rate,
fc4c3c
      since that makes it easy to use SI abbreviations.  */
fc4c3c
@@ -761,7 +776,8 @@ print_stats (void)
fc4c3c
            w_bytes,
fc4c3c
            human_readable (w_bytes, hbuf, human_opts, 1, 1));
fc4c3c
 
fc4c3c
-  xtime_t now = gethrxtime ();
fc4c3c
+  xtime_t now = progress_time ? progress_time : gethrxtime ();
fc4c3c
+
fc4c3c
   if (start_time < now)
fc4c3c
     {
fc4c3c
       double XTIME_PRECISIONe0 = XTIME_PRECISION;
fc4c3c
@@ -787,7 +803,42 @@ print_stats (void)
fc4c3c
      but that was incorrect for languages like Polish.  To fix this
fc4c3c
      bug we now use SI symbols even though they're a bit more
fc4c3c
      confusing in English.  */
fc4c3c
-  fprintf (stderr, _(", %g s, %s/s\n"), delta_s, bytes_per_second);
fc4c3c
+  char const *time_fmt = _(", %g s, %s/s\n");;
fc4c3c
+  if (progress_time)
fc4c3c
+    time_fmt = _(", %.6f s, %s/s");  /* OK with '\r' as increasing width.  */
fc4c3c
+  fprintf (stderr, time_fmt, delta_s, bytes_per_second);
fc4c3c
+
fc4c3c
+  newline_pending = !!progress_time;
fc4c3c
+}
fc4c3c
+
fc4c3c
+static void
fc4c3c
+print_stats (void)
fc4c3c
+{
fc4c3c
+  if (status_level == STATUS_NONE)
fc4c3c
+    return;
fc4c3c
+
fc4c3c
+  if (newline_pending)
fc4c3c
+    {
fc4c3c
+      fputc ('\n', stderr);
fc4c3c
+      newline_pending = false;
fc4c3c
+    }
fc4c3c
+
fc4c3c
+  fprintf (stderr,
fc4c3c
+           _("%"PRIuMAX"+%"PRIuMAX" records in\n"
fc4c3c
+             "%"PRIuMAX"+%"PRIuMAX" records out\n"),
fc4c3c
+           r_full, r_partial, w_full, w_partial);
fc4c3c
+
fc4c3c
+  if (r_truncate != 0)
fc4c3c
+    fprintf (stderr,
fc4c3c
+             ngettext ("%"PRIuMAX" truncated record\n",
fc4c3c
+                       "%"PRIuMAX" truncated records\n",
fc4c3c
+                       select_plural (r_truncate)),
fc4c3c
+             r_truncate);
fc4c3c
+
fc4c3c
+  if (status_level == STATUS_NOXFER)
fc4c3c
+    return;
fc4c3c
+
fc4c3c
+  print_xfer_stats (0);
fc4c3c
 }
fc4c3c
 
fc4c3c
 /* An ordinary signal was received; arrange for the program to exit.  */
fc4c3c
@@ -1035,7 +1086,7 @@ iread (int fd, char *buf, size_t size)
fc4c3c
       if (0 < prev_nread && prev_nread < size)
fc4c3c
         {
fc4c3c
           uintmax_t prev = prev_nread;
fc4c3c
-          if (!(status_flags & STATUS_NONE))
fc4c3c
+          if (status_level != STATUS_NONE)
fc4c3c
             error (0, 0, ngettext (("warning: partial read (%"PRIuMAX" byte); "
fc4c3c
                                     "suggest iflag=fullblock"),
fc4c3c
                                    ("warning: partial read (%"PRIuMAX" bytes); "
fc4c3c
@@ -1086,7 +1137,7 @@ iwrite (int fd, char const *buf, size_t size)
fc4c3c
     {
fc4c3c
       int old_flags = fcntl (STDOUT_FILENO, F_GETFL);
fc4c3c
       if (fcntl (STDOUT_FILENO, F_SETFL, old_flags & ~O_DIRECT) != 0
fc4c3c
-          && !(status_flags & STATUS_NONE))
fc4c3c
+          && status_level != STATUS_NONE)
fc4c3c
         error (0, errno, _("failed to turn off O_DIRECT: %s"),
fc4c3c
                quote (output_file));
fc4c3c
 
fc4c3c
@@ -1219,7 +1270,7 @@ operand_matches (char const *str, char const *pattern, char delim)
fc4c3c
 
fc4c3c
 static int
fc4c3c
 parse_symbols (char const *str, struct symbol_value const *table,
fc4c3c
-               char const *error_msgid)
fc4c3c
+               bool exclusive, char const *error_msgid)
fc4c3c
 {
fc4c3c
   int value = 0;
fc4c3c
 
fc4c3c
@@ -1241,7 +1292,10 @@ parse_symbols (char const *str, struct symbol_value const *table,
fc4c3c
             }
fc4c3c
         }
fc4c3c
 
fc4c3c
-      value |= entry->value;
fc4c3c
+      if (exclusive)
fc4c3c
+        value = entry->value;
fc4c3c
+      else
fc4c3c
+        value |= entry->value;
fc4c3c
       if (!strcomma)
fc4c3c
         break;
fc4c3c
       str = strcomma + 1;
fc4c3c
@@ -1316,17 +1370,17 @@ scanargs (int argc, char *const *argv)
fc4c3c
       else if (operand_is (name, "of"))
fc4c3c
         output_file = val;
fc4c3c
       else if (operand_is (name, "conv"))
fc4c3c
-        conversions_mask |= parse_symbols (val, conversions,
fc4c3c
+        conversions_mask |= parse_symbols (val, conversions, false,
fc4c3c
                                            N_("invalid conversion"));
fc4c3c
       else if (operand_is (name, "iflag"))
fc4c3c
-        input_flags |= parse_symbols (val, flags,
fc4c3c
+        input_flags |= parse_symbols (val, flags, false,
fc4c3c
                                       N_("invalid input flag"));
fc4c3c
       else if (operand_is (name, "oflag"))
fc4c3c
-        output_flags |= parse_symbols (val, flags,
fc4c3c
+        output_flags |= parse_symbols (val, flags, false,
fc4c3c
                                        N_("invalid output flag"));
fc4c3c
       else if (operand_is (name, "status"))
fc4c3c
-        status_flags |= parse_symbols (val, statuses,
fc4c3c
-                                       N_("invalid status flag"));
fc4c3c
+        status_level = parse_symbols (val, statuses, true,
fc4c3c
+                                      N_("invalid status level"));
fc4c3c
       else
fc4c3c
         {
fc4c3c
           bool invalid = false;
fc4c3c
@@ -1613,7 +1667,7 @@ skip_via_lseek (char const *filename, int fdesc, off_t offset, int whence)
fc4c3c
       && ioctl (fdesc, MTIOCGET, &s2) == 0
fc4c3c
       && MT_SAME_POSITION (s1, s2))
fc4c3c
     {
fc4c3c
-      if (!(status_flags & STATUS_NONE))
fc4c3c
+      if (status_level != STATUS_NONE)
fc4c3c
         error (0, 0, _("warning: working around lseek kernel bug for file "
fc4c3c
                        "(%s)\n  of mt_type=0x%0lx -- "
fc4c3c
                        "see <sys/mtio.h> for the list of types"),
fc4c3c
@@ -1787,7 +1841,7 @@ advance_input_after_read_error (size_t nbytes)
fc4c3c
           if (offset == input_offset)
fc4c3c
             return true;
fc4c3c
           diff = input_offset - offset;
fc4c3c
-          if (! (0 <= diff && diff <= nbytes) && !(status_flags & STATUS_NONE))
fc4c3c
+          if (! (0 <= diff && diff <= nbytes) && status_level != STATUS_NONE)
fc4c3c
             error (0, 0, _("warning: invalid file offset after failed read"));
fc4c3c
           if (0 <= skip_via_lseek (input_file, STDIN_FILENO, diff, SEEK_CUR))
fc4c3c
             return true;
fc4c3c
@@ -1986,7 +2040,7 @@ dd_copy (void)
fc4c3c
              2. pipe has not enough data
fc4c3c
              3. partial reads  */
fc4c3c
       if ((us_blocks || (!input_offset_overflow && us_bytes))
fc4c3c
-          && !(status_flags & STATUS_NONE))
fc4c3c
+          && status_level != STATUS_NONE)
fc4c3c
         {
fc4c3c
           error (0, 0,
fc4c3c
                  _("%s: cannot skip to specified offset"), quote (input_file));
fc4c3c
@@ -2029,6 +2083,19 @@ dd_copy (void)
fc4c3c
 
fc4c3c
   while (1)
fc4c3c
     {
fc4c3c
+      if (status_level == STATUS_PROGRESS)
fc4c3c
+        {
fc4c3c
+          xtime_t progress_time = gethrxtime ();
fc4c3c
+          uintmax_t delta_xtime = progress_time;
fc4c3c
+          delta_xtime -= previous_time;
fc4c3c
+          double XTIME_PRECISIONe0 = XTIME_PRECISION;
fc4c3c
+          if (delta_xtime / XTIME_PRECISIONe0 > 1)
fc4c3c
+            {
fc4c3c
+              print_xfer_stats (progress_time);
fc4c3c
+              previous_time = progress_time;
fc4c3c
+            }
fc4c3c
+        }
fc4c3c
+
fc4c3c
       if (r_partial + r_full >= max_records + !!max_bytes)
fc4c3c
         break;
fc4c3c
 
fc4c3c
@@ -2053,7 +2120,7 @@ dd_copy (void)
fc4c3c
 
fc4c3c
       if (nread < 0)
fc4c3c
         {
fc4c3c
-          if (!(conversions_mask & C_NOERROR) || !(status_flags & STATUS_NONE))
fc4c3c
+          if (!(conversions_mask & C_NOERROR) || status_level != STATUS_NONE)
fc4c3c
             error (0, errno, _("error reading %s"), quote (input_file));
fc4c3c
 
fc4c3c
           if (conversions_mask & C_NOERROR)
fc4c3c
@@ -2345,7 +2412,7 @@ main (int argc, char **argv)
fc4c3c
         }
fc4c3c
     }
fc4c3c
 
fc4c3c
-  start_time = gethrxtime ();
fc4c3c
+  start_time = previous_time = gethrxtime ();
fc4c3c
 
fc4c3c
   exit_status = dd_copy ();
fc4c3c
 
fc4c3c
diff --git a/tests/dd/misc.sh b/tests/dd/misc.sh
fc4c3c
index f877fdd..34dfba7 100755
fc4c3c
--- a/tests/dd/misc.sh
fc4c3c
+++ b/tests/dd/misc.sh
fc4c3c
@@ -35,9 +35,12 @@ dd status=none if=$tmp_in of=/dev/null 2> err || fail=1
fc4c3c
 test -s err && { cat err; fail=1; }
fc4c3c
 dd status=none if=$tmp_in skip=2 of=/dev/null 2> err || fail=1
fc4c3c
 test -s err && { cat err; fail=1; }
fc4c3c
-# check status=none is cumulative with status=noxfer
fc4c3c
-dd status=none status=noxfer if=$tmp_in of=/dev/null 2> err || fail=1
fc4c3c
+# check later status=none overrides earlier status=noxfer
fc4c3c
+dd status=noxfer status=none if=$tmp_in of=/dev/null 2> err || fail=1
fc4c3c
 test -s err && { cat err; fail=1; }
fc4c3c
+# check later status=noxfer overrides earlier status=none
fc4c3c
+dd status=none status=noxfer if=$tmp_in of=/dev/null 2> err || fail=1
fc4c3c
+compare /dev/null err && fail=1
fc4c3c
 
fc4c3c
 dd if=$tmp_in of=$tmp_out 2> /dev/null || fail=1
fc4c3c
 compare $tmp_in $tmp_out || fail=1
fc4c3c
diff --git a/tests/dd/stats.sh b/tests/dd/stats.sh
fc4c3c
new file mode 100755
fc4c3c
index 0000000..24b8c49`
fc4c3c
--- a/dev/null
fc4c3c
+++ b/tests/dd/stats.sh
fc4c3c
@@ -0,0 +1,65 @@
fc4c3c
+#!/bin/sh
fc4c3c
+# Check stats output for SIG{INFO,USR1} and status=progress
fc4c3c
+
fc4c3c
+# Copyright (C) 2014 Free Software Foundation, Inc.
fc4c3c
+
fc4c3c
+# This program is free software: you can redistribute it and/or modify
fc4c3c
+# it under the terms of the GNU General Public License as published by
fc4c3c
+# the Free Software Foundation, either version 3 of the License, or
fc4c3c
+# (at your option) any later version.
fc4c3c
+
fc4c3c
+# This program is distributed in the hope that it will be useful,
fc4c3c
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
fc4c3c
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
fc4c3c
+# GNU General Public License for more details.
fc4c3c
+
fc4c3c
+# You should have received a copy of the GNU General Public License
fc4c3c
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
fc4c3c
+
fc4c3c
+. "${srcdir=.}/tests/init.sh"; path_prepend_ ./src
fc4c3c
+print_ver_ dd
fc4c3c
+
fc4c3c
+env kill -l | grep '^INFO$' && SIGINFO='INFO' || SIGINFO='USR1'
fc4c3c
+
fc4c3c
+# This to avoid races in the USR1 case
fc4c3c
+# as the dd process will terminate by default until
fc4c3c
+# it has its handler enabled.
fc4c3c
+trap '' $SIGINFO
fc4c3c
+
fc4c3c
+mkfifo_or_skip_ fifo
fc4c3c
+
fc4c3c
+for open in '' '1'; do
fc4c3c
+  # Run dd with the fullblock iflag to avoid short reads
fc4c3c
+  # which can be triggered by reception of signals
fc4c3c
+  dd iflag=fullblock if=/dev/zero of=fifo count=100 bs=5000000 2>err & pid=$!
fc4c3c
+
fc4c3c
+  # Note if we sleep here we give dd a chance to exec and block on open.
fc4c3c
+  # Otherwise we're probably testing SIG_IGN in the forked shell or early dd.
fc4c3c
+  test "$open" && sleep .1
fc4c3c
+
fc4c3c
+  # dd will block on open until fifo is opened for reading.
fc4c3c
+  # Timeout in case dd goes away erroneously which we check for below.
fc4c3c
+  timeout 10 sh -c 'wc -c < fifo > nwritten' &
fc4c3c
+
fc4c3c
+  # Send lots of signals immediately to ensure dd not killed due
fc4c3c
+  # to race setting handler, or blocking on open of fifo.
fc4c3c
+  # Many signals also check that short reads are handled.
fc4c3c
+  until ! kill -s $SIGINFO $pid 2>/dev/null; do
fc4c3c
+    sleep .01
fc4c3c
+  done
fc4c3c
+
fc4c3c
+  wait
fc4c3c
+
fc4c3c
+  # Ensure all data processed and at least last status written
fc4c3c
+  grep '500000000 bytes .* copied' err || { cat err; fail=1; }
fc4c3c
+done
fc4c3c
+
fc4c3c
+progress_output()
fc4c3c
+{
fc4c3c
+  { sleep "$1"; echo 1; } | dd bs=1 status=progress of=/dev/null 2>err
fc4c3c
+  # Progress output should be for "byte ... copied", while final is "bytes ..."
fc4c3c
+  grep 'byte .* copied' err
fc4c3c
+}
fc4c3c
+retry_delay_ progress_output 1 4 || { cat err; fail=1; }
fc4c3c
+
fc4c3c
+Exit $fail
fc4c3c
--
fc4c3c
cgit v0.9.0.2