----- Original Message -----
> From: "Pádraig Brady" <[email protected]>
> To: "Federico Simoncelli" <[email protected]>
> Cc: "Bernhard Voelker" <[email protected]>, "Coreutils" 
> <[email protected]>
> Sent: Friday, September 26, 2014 6:05:33 PM
> Subject: Re: dd SIGUSR1 race
> 
> > Many other commands are providing an explicit flag: wget, curl,
> > qemu-img, etc.
> 
> Yes, I see qemu-img supports both.
> If not too invasive, we could accept a status=progress patch to output
> stats every approx 1s (noting the caveats of being blocked behind large
> reads/writes)

I'll try to investigate if I can easily use sigusr1 with your new patch,
meanwhile I came up with a patch adding status=progress.

It's a simple draft and I am open to hear all comments about it,
especially on what's the format to report the progress.

For example qemu-img uses '\r' so that the output is always on the same
line.

Patch in attachment.

-- 
Federico
From 1f271b8f7c9730d72e4ece558da49757dbbf9b7e Mon Sep 17 00:00:00 2001
From: Federico Simoncelli <[email protected]>
Date: Fri, 26 Sep 2014 17:12:32 +0000
Subject: [PATCH] dd: new status=progress operand

* src/dd.c: Report the transfer progress every second when the
new status=progress operand is used.
* doc/corutils.texi (dd invocation): Document the new progress
status operand.
* tests/dd/stats.sh: Add new test for status=progress.
* NEWS: Mention the feature.
---
 NEWS               |  2 ++
 doc/coreutils.texi |  5 +++++
 src/dd.c           | 63 ++++++++++++++++++++++++++++++++++++------------------
 tests/dd/misc.sh   |  3 +++
 tests/dd/stats.sh  |  4 ++++
 5 files changed, 56 insertions(+), 21 deletions(-)

diff --git a/NEWS b/NEWS
index 3e10ac4..1c16d66 100644
--- a/NEWS
+++ b/NEWS
@@ -19,6 +19,8 @@ GNU coreutils NEWS                                    -*- outline -*-
   king directory.  The new option is only permitted if the new root directory is
   the old "/", and therefore is useful with the --group and --userspec options.
 
+  dd status=progress now reports statistics about the data transfer.
+
 ** Changes in behavior
 
   chroot changes the current directory to "/" in again - unless the above new
diff --git a/doc/coreutils.texi b/doc/coreutils.texi
index 6e1a2fb..c93c8cf 100644
--- a/doc/coreutils.texi
+++ b/doc/coreutils.texi
@@ -8649,6 +8649,11 @@ that normally make up the last status line.
 Do not print any informational or warning messages to stderr.
 Error messages are output as normal.
 
+@item progress
+@opindex progress @r{dd status=}
+Print the the transfer rate and volume statistics during the
+operation.
+
 @end table
 
 @item conv=@var{conversion}[,@var{conversion}]@dots{}
diff --git a/src/dd.c b/src/dd.c
index a0c5bb1..b84e85e 100644
--- a/src/dd.c
+++ b/src/dd.c
@@ -136,7 +136,8 @@ enum
 enum
   {
     STATUS_NOXFER = 01,
-    STATUS_NONE = 02
+    STATUS_NONE = 02,
+    STATUS_PROGRESS = 04
   };
 
 /* The name of the input file, or NULL for the standard input. */
@@ -375,6 +376,7 @@ static struct symbol_value const statuses[] =
 {
   {"noxfer",	STATUS_NOXFER},
   {"none",	STATUS_NONE},
+  {"progress",	STATUS_PROGRESS},
   {"",		0}
 };
 
@@ -732,8 +734,7 @@ multiple_bits_set (int i)
 /* Print transfer statistics.  */
 
 static void
-print_stats (void)
-{
+print_short_stats (void) {
   char hbuf[LONGEST_HUMAN_READABLE + 1];
   int human_opts =
     (human_autoscale | human_round_to_nearest
@@ -741,24 +742,6 @@ print_stats (void)
   double delta_s;
   char const *bytes_per_second;
 
-  if (status_flags & STATUS_NONE)
-    return;
-
-  fprintf (stderr,
-           _("%"PRIuMAX"+%"PRIuMAX" records in\n"
-             "%"PRIuMAX"+%"PRIuMAX" records out\n"),
-           r_full, r_partial, w_full, w_partial);
-
-  if (r_truncate != 0)
-    fprintf (stderr,
-             ngettext ("%"PRIuMAX" truncated record\n",
-                       "%"PRIuMAX" truncated records\n",
-                       select_plural (r_truncate)),
-             r_truncate);
-
-  if (status_flags & STATUS_NOXFER)
-    return;
-
   /* Use integer arithmetic to compute the transfer rate,
      since that makes it easy to use SI abbreviations.  */
 
@@ -798,6 +781,30 @@ print_stats (void)
   fprintf (stderr, _(", %g s, %s/s\n"), delta_s, bytes_per_second);
 }
 
+static void
+print_stats (void)
+{
+  if (status_flags & STATUS_NONE)
+    return;
+
+  fprintf (stderr,
+           _("%"PRIuMAX"+%"PRIuMAX" records in\n"
+             "%"PRIuMAX"+%"PRIuMAX" records out\n"),
+           r_full, r_partial, w_full, w_partial);
+
+  if (r_truncate != 0)
+    fprintf (stderr,
+             ngettext ("%"PRIuMAX" truncated record\n",
+                       "%"PRIuMAX" truncated records\n",
+                       select_plural (r_truncate)),
+             r_truncate);
+
+  if (status_flags & STATUS_NOXFER)
+    return;
+
+  print_short_stats ();
+}
+
 /* An ordinary signal was received; arrange for the program to exit.  */
 
 static void
@@ -1925,6 +1932,8 @@ dd_copy (void)
   int exit_status = EXIT_SUCCESS;
   size_t n_bytes_read;
 
+  time_t last_progress = 0;
+
   /* Leave at least one extra byte at the beginning and end of 'ibuf'
      for conv=swab, but keep the buffer address even.  But some peculiar
      device drivers work only with word-aligned buffers, so leave an
@@ -2000,6 +2009,18 @@ dd_copy (void)
 
   while (1)
     {
+      if ((status_flags & STATUS_PROGRESS) && !(status_flags & STATUS_NONE))
+        {
+          int rv;
+          struct timespec monotime;
+          rv = clock_gettime(CLOCK_MONOTONIC, &monotime);
+          if (rv == 0 && monotime.tv_sec - last_progress > 1)
+            {
+              last_progress = monotime.tv_sec;
+              print_short_stats ();
+            }
+        }
+
       if (r_partial + r_full >= max_records + !!max_bytes)
         break;
 
diff --git a/tests/dd/misc.sh b/tests/dd/misc.sh
index f877fdd..b141268 100755
--- a/tests/dd/misc.sh
+++ b/tests/dd/misc.sh
@@ -38,6 +38,9 @@ compare /dev/null err || fail=1
 # check status=none is cumulative with status=noxfer
 dd status=none status=noxfer if=$tmp_in of=/dev/null 2> err || fail=1
 compare /dev/null err || fail=1
+# check status=none has precedence over status=progress
+dd status=none status=progress if=$tmp_in of=/dev/null 2> err || fail=1
+compare /dev/null err || fail=1
 
 dd if=$tmp_in of=$tmp_out 2> /dev/null || fail=1
 compare $tmp_in $tmp_out || fail=1
diff --git a/tests/dd/stats.sh b/tests/dd/stats.sh
index 386752e..1fe5b1f 100755
--- a/tests/dd/stats.sh
+++ b/tests/dd/stats.sh
@@ -54,4 +54,8 @@ for open in '' '1'; do
   grep '500000000 bytes .* copied' err || { cat err; fail=1; }
 done
 
+# check status=progress output
+timeout 3 dd status=progress if=/dev/zero of=/dev/null 2> err
+test $(grep -c 'bytes.*copied' err) -gt 1 || { cat err; fail=1; }
+
 Exit $fail
-- 
1.9.3

Reply via email to