summaryrefslogtreecommitdiff
path: root/src/dd.c
diff options
context:
space:
mode:
authorPaul Eggert <eggert@cs.ucla.edu>2005-04-09 04:55:40 +0000
committerPaul Eggert <eggert@cs.ucla.edu>2005-04-09 04:55:40 +0000
commit4f0b9dbd173e09de31389156a4506fe77687bf5e (patch)
treea4cae3e7b5d367db3732c2900062de192805b398 /src/dd.c
parentf33b6dab82e7c953ae684a5abe9632a4d8745b0b (diff)
downloadcoreutils-4f0b9dbd173e09de31389156a4506fe77687bf5e.tar.xz
Do not include safe-read.h or full-write; no longer needed.
(process_signals): Add forward decl. (SA_NOCLDSTOP, sigprocmask, sigset_t) [!defined SA_NOCLDSTOP]: New macros. (siginterrupt) [! HAVE_SIGINTERRUPT]: New macro. (SA_NODEFER) [!defined SA_NODEFER]: New macro. (SA_RESETHAND) [!defined SA_RESETHAND]: New macro. (caught_signals, interrupt_signal, info_signal_count, catch_siginfo): New vars. (usage): Mention -USR1 versus -INFO. (cleanup): Don't invoke print_stats; the caller must do it now. All callers changed. (quit): Process signals just before exiting. (interrupt_handler): Simply record the signal and return. (siginfo_handler): Simply increment the signal counter and return. (install_handler): Remove, replacing with: (install_signal_handlers, process_signals, iread, iwrite): New functions. All callers to safe_read and full_write replaced by iread and iwrite. All callers to install_handler replaced by install_handlers. Do not include inttostr.h, no longer needed. (print_stats, main): Rewrite and simplify formats to use PRIuMAX instead of umaxtostr. (print_stats): Work even in languages that have special forms for two of things, for r_truncate and w_bytes. We can't fix delta_s in this way, since ngettext doesn't support floating-point. (main): Rewrite to avoid casts.
Diffstat (limited to 'src/dd.c')
-rw-r--r--src/dd.c298
1 files changed, 221 insertions, 77 deletions
diff --git a/src/dd.c b/src/dd.c
index 3ec9ef0f7..906dc513e 100644
--- a/src/dd.c
+++ b/src/dd.c
@@ -28,22 +28,38 @@
#include "system.h"
#include "error.h"
-#include "full-write.h"
#include "gethrxtime.h"
#include "getpagesize.h"
#include "human.h"
-#include "inttostr.h"
#include "long-options.h"
#include "quote.h"
-#include "safe-read.h"
#include "xstrtol.h"
#include "xtime.h"
+static void process_signals (void);
+
/* The official name of this program (e.g., no `g' prefix). */
#define PROGRAM_NAME "dd"
#define AUTHORS "Paul Rubin", "David MacKenzie", "Stuart Kemp"
+/* Use SA_NOCLDSTOP as a proxy for whether the sigaction machinery is
+ present. SA_NODEFER and SA_RESETHAND are XSI extensions. */
+#ifndef SA_NOCLDSTOP
+# define SA_NOCLDSTOP 0
+# define sigprocmask(How, Set, Oset) /* empty */
+# define sigset_t int
+# if ! HAVE_SIGINTERRUPT
+# define siginterrupt(sig, flag) /* empty */
+# endif
+#endif
+#ifndef SA_NODEFER
+# define SA_NODEFER 0
+#endif
+#ifndef SA_RESETHAND
+# define SA_RESETHAND 0
+#endif
+
#ifndef SIGINFO
# define SIGINFO SIGUSR1
#endif
@@ -195,6 +211,15 @@ static size_t oc = 0;
/* Index into current line, for `conv=block' and `conv=unblock'. */
static size_t col = 0;
+/* The set of signals that are caught. */
+static sigset_t caught_signals;
+
+/* If nonzero, the value of the pending fatal signal. */
+static sig_atomic_t volatile interrupt_signal;
+
+/* A count of the number of pending info signals that have been received. */
+static sig_atomic_t volatile info_signal_count;
+
/* A longest symbol in the struct symbol_values tables below. */
#define LONGEST_SYMBOL "fdatasync"
@@ -437,20 +462,26 @@ Each FLAG symbol may be:\n\
if (O_NOCTTY)
fputs (_(" noctty do not assign controlling terminal from file\n"),
stdout);
- fputs (_("\
+
+ {
+ char const *siginfo_name = (SIGINFO == SIGUSR1 ? "USR1" : "INFO");
+ printf (_("\
\n\
-Sending a SIGUSR1 signal to a running `dd' process makes it\n\
-print I/O statistics to standard error, then to resume copying.\n\
+Sending a %s signal to a running `dd' process makes it\n\
+print I/O statistics to standard error and then resume copying.\n\
\n\
$ dd if=/dev/zero of=/dev/null& pid=$!\n\
- $ kill -USR1 $pid; sleep 1; kill $pid\n\
+ $ kill -%s $pid; sleep 1; kill $pid\n\
18335302+0 records in\n\
18335302+0 records out\n\
9387674624 bytes (9.4 GB) copied, 34.6279 seconds, 271 MB/s\n\
\n\
Options are:\n\
\n\
-"), stdout);
+"),
+ siginfo_name, siginfo_name);
+ }
+
fputs (HELP_OPTION_DESCRIPTION, stdout);
fputs (VERSION_OPTION_DESCRIPTION, stdout);
printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
@@ -476,30 +507,30 @@ multiple_bits_set (int i)
return (i & (i - 1)) != 0;
}
+/* Print transfer statistics. */
+
static void
print_stats (void)
{
xtime_t now = gethrxtime ();
- char buf[2][MAX (INT_BUFSIZE_BOUND (uintmax_t), LONGEST_HUMAN_READABLE + 1)];
+ char hbuf[LONGEST_HUMAN_READABLE + 1];
int human_opts =
(human_autoscale | human_round_to_nearest
| human_space_before_unit | human_SI | human_B);
- double XTIME_PRECISIONe0 = XTIME_PRECISION;
double delta_s;
char const *bytes_per_second;
- fprintf (stderr, _("%s+%s records in\n"),
- umaxtostr (r_full, buf[0]), umaxtostr (r_partial, buf[1]));
- fprintf (stderr, _("%s+%s records out\n"),
- umaxtostr (w_full, buf[0]), umaxtostr (w_partial, buf[1]));
- if (r_truncate > 0)
- {
- fprintf (stderr, "%s %s\n",
- umaxtostr (r_truncate, buf[0]),
- (r_truncate == 1
- ? _("truncated record")
- : _("truncated records")));
- }
+ 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 ("1 truncated record\n",
+ "%"PRIuMAX" truncated records\n",
+ MIN (r_truncate, ULONG_MAX)),
+ r_truncate);
if (status_flags & STATUS_NOXFER)
return;
@@ -508,24 +539,25 @@ print_stats (void)
since that makes it easy to use SI abbreviations. */
fprintf (stderr,
- ngettext ("%s byte (%s) copied",
- "%s bytes (%s) copied", w_bytes == 1),
- umaxtostr (w_bytes, buf[0]),
- human_readable (w_bytes, buf[1], human_opts, 1, 1));
+ ngettext ("1 byte (1 B) copied",
+ "%"PRIuMAX" bytes (%s) copied",
+ MIN (w_bytes, ULONG_MAX)),
+ w_bytes,
+ human_readable (w_bytes, hbuf, human_opts, 1, 1));
if (start_time < now)
{
+ double XTIME_PRECISIONe0 = XTIME_PRECISION;
uintmax_t delta_xtime = now;
delta_xtime -= start_time;
delta_s = delta_xtime / XTIME_PRECISIONe0;
- bytes_per_second = human_readable (w_bytes, buf[1], human_opts,
+ bytes_per_second = human_readable (w_bytes, hbuf, human_opts,
XTIME_PRECISION, delta_xtime);
}
else
{
delta_s = 0;
- sprintf (buf[1], "%s B", _("Infinity"));
- bytes_per_second = buf[1];
+ bytes_per_second = _("Infinity B");
}
fprintf (stderr,
@@ -543,60 +575,177 @@ cleanup (void)
if (close (STDOUT_FILENO) < 0)
error (EXIT_FAILURE, errno,
_("closing output file %s"), quote (output_file));
- print_stats ();
}
static inline void
quit (int code)
{
cleanup ();
+ print_stats ();
+ process_signals ();
exit (code);
}
-static RETSIGTYPE
+/* An ordinary signal was received; arrange for the program to exit. */
+
+static void
interrupt_handler (int sig)
{
-#ifdef SA_NOCLDSTOP
- struct sigaction sigact;
-
- sigact.sa_handler = SIG_DFL;
- sigemptyset (&sigact.sa_mask);
- sigact.sa_flags = 0;
- sigaction (sig, &sigact, NULL);
-#else
- signal (sig, SIG_DFL);
-#endif
- cleanup ();
- raise (sig);
+ if (! SA_RESETHAND)
+ signal (sig, SIG_DFL);
+ interrupt_signal = sig;
}
-static RETSIGTYPE
-siginfo_handler (int sig ATTRIBUTE_UNUSED)
+/* An info signal was received; arrange for the program to print status. */
+
+static void
+siginfo_handler (int sig)
{
- print_stats ();
+ if (! SA_NOCLDSTOP)
+ signal (sig, siginfo_handler);
+ info_signal_count++;
}
-/* Encapsulate portability mess of establishing signal handlers. */
+/* Install the signal handlers. */
static void
-install_handler (int sig_num, RETSIGTYPE (*sig_handler) (int sig))
+install_signal_handlers (void)
{
-#ifdef SA_NOCLDSTOP
- struct sigaction sigact;
- sigaction (sig_num, NULL, &sigact);
- if (sigact.sa_handler != SIG_IGN)
+ bool catch_siginfo = ! (SIGINFO == SIGUSR1 && getenv ("POSIXLY_CORRECT"));
+
+#if SA_NOCLDSTOP
+
+ struct sigaction act;
+ sigemptyset (&caught_signals);
+ if (catch_siginfo)
{
- sigact.sa_handler = sig_handler;
- sigemptyset (&sigact.sa_mask);
- sigact.sa_flags = 0;
- sigaction (sig_num, &sigact, NULL);
+ sigaction (SIGINFO, NULL, &act);
+ if (act.sa_handler != SIG_IGN)
+ sigaddset (&caught_signals, SIGINFO);
}
+ sigaction (SIGINT, NULL, &act);
+ if (act.sa_handler != SIG_IGN)
+ sigaddset (&caught_signals, SIGINT);
+ act.sa_mask = caught_signals;
+
+ if (sigismember (&caught_signals, SIGINFO))
+ {
+ act.sa_handler = siginfo_handler;
+ act.sa_flags = 0;
+ sigaction (SIGINFO, &act, NULL);
+ }
+
+ if (sigismember (&caught_signals, SIGINT))
+ {
+ /* POSIX 1003.1-2001 says SA_RESETHAND implies SA_NODEFER,
+ but this is not true on Solaris 8 at least. It doesn't
+ hurt to use SA_NODEFER here, so leave it in. */
+ act.sa_handler = interrupt_handler;
+ act.sa_flags = SA_NODEFER | SA_RESETHAND;
+ sigaction (SIGINT, &act, NULL);
+ }
+
#else
- if (signal (sig_num, SIG_IGN) != SIG_IGN)
- signal (sig_num, sig_handler);
+
+ if (catch_siginfo && signal (SIGINFO, SIG_IGN) != SIG_IGN)
+ {
+ signal (SIGINFO, siginfo_handler);
+ siginterrupt (SIGINFO, 1);
+ }
+ if (signal (SIGINT, SIG_IGN) != SIG_IGN)
+ {
+ signal (SIGINT, interrupt_handler);
+ siginterrupt (SIGINT, 1);
+ }
#endif
}
+/* Process any pending signals. If signals are caught, this function
+ should be called periodically. Ideally there should never be an
+ unbounded amount of time when signals are not being processed. */
+
+static void
+process_signals (void)
+{
+ while (interrupt_signal | info_signal_count)
+ {
+ int interrupt;
+ int infos;
+ sigset_t oldset;
+
+ sigprocmask (SIG_BLOCK, &caught_signals, &oldset);
+
+ /* Reload interrupt_signal and info_signal_count, in case a new
+ signal was handled before sigprocmask took effect. */
+ interrupt = interrupt_signal;
+ infos = info_signal_count;
+
+ if (infos)
+ info_signal_count = infos - 1;
+
+ sigprocmask (SIG_SETMASK, &oldset, NULL);
+
+ if (interrupt)
+ cleanup ();
+ print_stats ();
+ if (interrupt)
+ raise (interrupt);
+ }
+}
+
+/* Read from FD into the buffer BUF of size SIZE, processing any
+ signals that arrive before bytes are read. Return the number of
+ bytes read if successful, -1 (setting errno) on failure. */
+
+static ssize_t
+iread (int fd, void *buf, size_t size)
+{
+ for (;;)
+ {
+ ssize_t nread;
+ process_signals ();
+ nread = read (fd, buf, size);
+ if (! (nread < 0 && errno == EINTR))
+ return nread;
+ }
+}
+
+/* Write to FD the buffer BUF of size SIZE, processing any signals
+ that arrive. Return the number of bytes written, setting errno if
+ this is less than SIZE. Keep trying if there are partial
+ writes. */
+
+static size_t
+iwrite (int fd, void const *buf, size_t size)
+{
+ size_t total_written = 0;
+
+ while (total_written < size)
+ {
+ ssize_t nwritten;
+ process_signals ();
+ nwritten = write (fd, buf + total_written, size - total_written);
+ if (nwritten < 0)
+ {
+ if (errno != EINTR)
+ break;
+ }
+ else if (nwritten == 0)
+ {
+ /* Some buggy drivers return 0 when one tries to write beyond
+ a device's end. (Example: Linux 1.2.13 on /dev/fd0.)
+ Set errno to ENOSPC so they get a sensible diagnostic. */
+ errno = ENOSPC;
+ nwritten = -1;
+ break;
+ }
+ else
+ total_written += nwritten;
+ }
+
+ return total_written;
+}
+
/* Open a file to a particular file descriptor. This is like standard
`open', except it always returns DESIRED_FD if successful. */
static int
@@ -624,7 +773,7 @@ open_fd (int desired_fd, char const *filename, int options, mode_t mode)
static void
write_output (void)
{
- size_t nwritten = full_write (STDOUT_FILENO, obuf, output_blocksize);
+ size_t nwritten = iwrite (STDOUT_FILENO, obuf, output_blocksize);
w_bytes += nwritten;
if (nwritten != output_blocksize)
{
@@ -999,8 +1148,8 @@ skip (int fdesc, char const *file, uintmax_t records, size_t blocksize,
int lseek_errno = errno;
while (records--)
{
- size_t nread = safe_read (fdesc, buf, blocksize);
- if (nread == SAFE_READ_ERROR)
+ ssize_t nread = iread (fdesc, buf, blocksize);
+ if (nread < 0)
{
if (fdesc == STDIN_FILENO)
{
@@ -1187,7 +1336,7 @@ dd_copy (void)
char *ibuf, *bufstart; /* Input buffer. */
char *real_buf; /* real buffer address before alignment */
char *real_obuf;
- size_t nread; /* Bytes read in the current block. */
+ ssize_t nread; /* Bytes read in the current block. */
/* If nonzero, then the previously read block was partial and
PARTREAD was its size. */
@@ -1256,12 +1405,12 @@ dd_copy (void)
(conversions_mask & (C_BLOCK | C_UNBLOCK)) ? ' ' : '\0',
input_blocksize);
- nread = safe_read (STDIN_FILENO, ibuf, input_blocksize);
+ nread = iread (STDIN_FILENO, ibuf, input_blocksize);
if (nread == 0)
break; /* EOF. */
- if (nread == SAFE_READ_ERROR)
+ if (nread < 0)
{
error (0, errno, _("reading %s"), quote (input_file));
if (conversions_mask & C_NOERROR)
@@ -1316,7 +1465,7 @@ dd_copy (void)
if (ibuf == obuf) /* If not C_TWOBUFS. */
{
- size_t nwritten = full_write (STDOUT_FILENO, obuf, n_bytes_read);
+ size_t nwritten = iwrite (STDOUT_FILENO, obuf, n_bytes_read);
w_bytes += nwritten;
if (nwritten != n_bytes_read)
{
@@ -1376,7 +1525,7 @@ dd_copy (void)
/* Write out the last block. */
if (oc != 0)
{
- size_t nwritten = full_write (STDOUT_FILENO, obuf, oc);
+ size_t nwritten = iwrite (STDOUT_FILENO, obuf, oc);
w_bytes += nwritten;
if (nwritten != 0)
w_partial++;
@@ -1499,7 +1648,8 @@ main (int argc, char **argv)
{
struct stat stdout_stat;
off_t o = seek_records * output_blocksize;
- if ((uintmax_t) o / output_blocksize != seek_records)
+ uintmax_t uo = o;
+ if (uo / output_blocksize != seek_records)
error (EXIT_FAILURE, 0, _("file offset out of range"));
if (fstat (STDOUT_FILENO, &stdout_stat) != 0)
@@ -1515,20 +1665,14 @@ main (int argc, char **argv)
&& (S_ISREG (stdout_stat.st_mode)
|| S_ISDIR (stdout_stat.st_mode)
|| S_TYPEISSHM (&stdout_stat)))
- {
- char buf[INT_BUFSIZE_BOUND (off_t)];
- error (EXIT_FAILURE, errno,
- _("advancing past %s bytes in output file %s"),
- offtostr (o, buf), quote (output_file));
- }
+ error (EXIT_FAILURE, errno,
+ _("advancing past %"PRIuMAX" bytes in output file %s"),
+ uo, quote (output_file));
}
#endif
}
- install_handler (SIGINT, interrupt_handler);
- install_handler (SIGQUIT, interrupt_handler);
- install_handler (SIGPIPE, interrupt_handler);
- install_handler (SIGINFO, siginfo_handler);
+ install_signal_handlers ();
start_time = gethrxtime ();