summaryrefslogtreecommitdiffstats
path: root/usr.bin/tar
diff options
context:
space:
mode:
authorcperciva <cperciva@FreeBSD.org>2008-05-18 06:24:47 +0000
committercperciva <cperciva@FreeBSD.org>2008-05-18 06:24:47 +0000
commit62fbb839584ccf0862199f020dd0e780e05a486e (patch)
tree1b5dbb3aaefee767b02f45a0a1c021a1d1841cf0 /usr.bin/tar
parenta8f81206ad84b1ab64280e9138a55223f038fc54 (diff)
downloadFreeBSD-src-62fbb839584ccf0862199f020dd0e780e05a486e.zip
FreeBSD-src-62fbb839584ccf0862199f020dd0e780e05a486e.tar.gz
Add SIGINFO (and for portability to SIGINFO-lacking systems, SIGUSR1)
handling to bsdtar. When writing archives (including copying via the @archive directive) a line is output to stderr indicating what is being done (adding or copying), the path, and how far through the file we are; extracting currently does not report progress within each file, but this is likely to happen eventually. Discussed with: kientzle Obtained from: tarsnap
Diffstat (limited to 'usr.bin/tar')
-rw-r--r--usr.bin/tar/Makefile2
-rw-r--r--usr.bin/tar/bsdtar.h6
-rw-r--r--usr.bin/tar/read.c26
-rw-r--r--usr.bin/tar/siginfo.c147
-rw-r--r--usr.bin/tar/write.c25
5 files changed, 205 insertions, 1 deletions
diff --git a/usr.bin/tar/Makefile b/usr.bin/tar/Makefile
index fb13745..b4fe8b5 100644
--- a/usr.bin/tar/Makefile
+++ b/usr.bin/tar/Makefile
@@ -2,7 +2,7 @@
PROG= bsdtar
BSDTAR_VERSION_STRING=2.5.0b
-SRCS= bsdtar.c getdate.y matching.c read.c tree.c util.c write.c
+SRCS= bsdtar.c getdate.y matching.c read.c siginfo.c tree.c util.c write.c
WARNS?= 5
DPADD= ${LIBARCHIVE} ${LIBBZ2} ${LIBZ}
LDADD= -larchive -lbz2 -lz
diff --git a/usr.bin/tar/bsdtar.h b/usr.bin/tar/bsdtar.h
index 6748560..62be3cc 100644
--- a/usr.bin/tar/bsdtar.h
+++ b/usr.bin/tar/bsdtar.h
@@ -96,6 +96,7 @@ struct bsdtar {
struct matching *matching; /* for matching.c */
struct security *security; /* for read.c */
struct name_cache *uname_cache; /* for write.c */
+ struct siginfo *siginfo; /* for siginfo.c */
};
void bsdtar_errc(struct bsdtar *, int _eval, int _code,
@@ -114,6 +115,11 @@ int process_lines(struct bsdtar *bsdtar, const char *pathname,
int (*process)(struct bsdtar *, const char *));
void safe_fprintf(FILE *, const char *fmt, ...);
void set_chdir(struct bsdtar *, const char *newdir);
+void siginfo_init(struct bsdtar *);
+void siginfo_setinfo(struct bsdtar *, const char * oper,
+ const char * path, int64_t size);
+void siginfo_printinfo(struct bsdtar *, off_t progress);
+void siginfo_done(struct bsdtar *);
void tar_mode_c(struct bsdtar *bsdtar);
void tar_mode_r(struct bsdtar *bsdtar);
void tar_mode_t(struct bsdtar *bsdtar);
diff --git a/usr.bin/tar/read.c b/usr.bin/tar/read.c
index a0267a3..5c14a4e 100644
--- a/usr.bin/tar/read.c
+++ b/usr.bin/tar/read.c
@@ -82,7 +82,21 @@ tar_mode_t(struct bsdtar *bsdtar)
void
tar_mode_x(struct bsdtar *bsdtar)
{
+ /* We want to catch SIGINFO and SIGUSR1. */
+ siginfo_init(bsdtar);
+
read_archive(bsdtar, 'x');
+
+ /* Restore old SIGINFO + SIGUSR1 handlers. */
+ siginfo_done(bsdtar);
+}
+
+static void
+progress_func(void * cookie)
+{
+ struct bsdtar * bsdtar = cookie;
+
+ siginfo_printinfo(bsdtar, 0);
}
/*
@@ -119,6 +133,12 @@ read_archive(struct bsdtar *bsdtar, char mode)
do_chdir(bsdtar);
+ if (mode == 'x') {
+ /* Set an extract callback so that we can handle SIGINFO. */
+ archive_read_extract_set_progress_callback(a, progress_func,
+ bsdtar);
+ }
+
if (mode == 'x' && bsdtar->option_chroot) {
#if HAVE_CHROOT
if (chroot(".") != 0)
@@ -238,6 +258,12 @@ read_archive(struct bsdtar *bsdtar, char mode)
archive_entry_pathname(entry));
fflush(stderr);
}
+
+ /* Tell the SIGINFO-handler code what we're doing. */
+ siginfo_setinfo(bsdtar, "extracting",
+ archive_entry_pathname(entry), 0);
+ siginfo_printinfo(bsdtar, 0);
+
if (bsdtar->option_stdout)
r = archive_read_data_into_fd(a, 1);
else
diff --git a/usr.bin/tar/siginfo.c b/usr.bin/tar/siginfo.c
new file mode 100644
index 0000000..e55308b
--- /dev/null
+++ b/usr.bin/tar/siginfo.c
@@ -0,0 +1,147 @@
+/*-
+ * Copyright 2008 Colin Percival
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "bsdtar_platform.h"
+__FBSDID("$FreeBSD$");
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "bsdtar.h"
+
+/* Is there a pending SIGINFO or SIGUSR1? */
+static volatile sig_atomic_t siginfo_received = 0;
+
+struct siginfo {
+ /* What sort of operation are we doing? */
+ char * oper;
+
+ /* What path are we handling? */
+ char * path;
+
+ /* How large is the archive entry? */
+ int64_t size;
+
+ /* Old signal handlers. */
+#ifdef SIGINFO
+ void (*siginfo_old)(int);
+#endif
+ void (*sigusr1_old)(int);
+};
+
+static void siginfo_handler(int sig);
+
+/* Handler for SIGINFO / SIGUSR1. */
+static void
+siginfo_handler(int sig)
+{
+
+ (void)sig; /* UNUSED */
+
+ /* Record that SIGINFO or SIGUSR1 has been received. */
+ siginfo_received = 1;
+}
+
+void
+siginfo_init(struct bsdtar *bsdtar)
+{
+
+ /* Allocate space for internal structure. */
+ if ((bsdtar->siginfo = malloc(sizeof(struct siginfo))) == NULL)
+ bsdtar_errc(bsdtar, 1, errno, "malloc failed");
+
+ /* Set the strings to NULL so that free() is safe. */
+ bsdtar->siginfo->path = bsdtar->siginfo->oper = NULL;
+
+#ifdef SIGINFO
+ /* We want to catch SIGINFO, if it exists. */
+ bsdtar->siginfo->siginfo_old = signal(SIGINFO, siginfo_handler);
+#endif
+ /* ... and treat SIGUSR1 the same way as SIGINFO. */
+ bsdtar->siginfo->sigusr1_old = signal(SIGUSR1, siginfo_handler);
+}
+
+void
+siginfo_setinfo(struct bsdtar *bsdtar, const char * oper, const char * path,
+ int64_t size)
+{
+
+ /* Free old operation and path strings. */
+ free(bsdtar->siginfo->oper);
+ free(bsdtar->siginfo->path);
+
+ /* Duplicate strings and store entry size. */
+ if ((bsdtar->siginfo->oper = strdup(oper)) == NULL)
+ bsdtar_errc(bsdtar, 1, errno, "Cannot strdup");
+ if ((bsdtar->siginfo->path = strdup(path)) == NULL)
+ bsdtar_errc(bsdtar, 1, errno, "Cannot strdup");
+ bsdtar->siginfo->size = size;
+}
+
+void
+siginfo_printinfo(struct bsdtar *bsdtar, off_t progress)
+{
+
+ /* If there's a signal to handle and we know what we're doing... */
+ if ((siginfo_received == 1) &&
+ (bsdtar->siginfo->path != NULL) &&
+ (bsdtar->siginfo->oper != NULL)) {
+ if (bsdtar->verbose)
+ fprintf(stderr, "\n");
+ if (bsdtar->siginfo->size > 0) {
+ safe_fprintf(stderr, "%s %s (%ju / %" PRId64 ")",
+ bsdtar->siginfo->oper, bsdtar->siginfo->path,
+ (uintmax_t)progress, bsdtar->siginfo->size);
+ } else {
+ safe_fprintf(stderr, "%s %s",
+ bsdtar->siginfo->oper, bsdtar->siginfo->path);
+ }
+ if (!bsdtar->verbose)
+ fprintf(stderr, "\n");
+ siginfo_received = 0;
+ }
+}
+
+void
+siginfo_done(struct bsdtar *bsdtar)
+{
+
+#ifdef SIGINFO
+ /* Restore old SIGINFO handler. */
+ signal(SIGINFO, bsdtar->siginfo->siginfo_old);
+#endif
+ /* And the old SIGUSR1 handler, too. */
+ signal(SIGUSR1, bsdtar->siginfo->sigusr1_old);
+
+ /* Free strings. */
+ free(bsdtar->siginfo->path);
+ free(bsdtar->siginfo->oper);
+
+ /* Free internal data structure. */
+ free(bsdtar->siginfo);
+}
diff --git a/usr.bin/tar/write.c b/usr.bin/tar/write.c
index 12a36a3..ec83b60 100644
--- a/usr.bin/tar/write.c
+++ b/usr.bin/tar/write.c
@@ -174,6 +174,9 @@ tar_mode_c(struct bsdtar *bsdtar)
if (*bsdtar->argv == NULL && bsdtar->names_from_file == NULL)
bsdtar_errc(bsdtar, 1, 0, "no files or directories specified");
+ /* We want to catch SIGINFO and SIGUSR1. */
+ siginfo_init(bsdtar);
+
a = archive_write_new();
/* Support any format that the library supports. */
@@ -242,6 +245,9 @@ tar_mode_c(struct bsdtar *bsdtar)
}
archive_write_finish(a);
+
+ /* Restore old SIGINFO + SIGUSR1 handlers. */
+ siginfo_done(bsdtar);
}
/*
@@ -574,6 +580,10 @@ append_archive(struct bsdtar *bsdtar, struct archive *a, struct archive *ina)
if (bsdtar->verbose)
safe_fprintf(stderr, "a %s",
archive_entry_pathname(in_entry));
+ siginfo_setinfo(bsdtar, "copying",
+ archive_entry_pathname(in_entry),
+ archive_entry_size(in_entry));
+ siginfo_printinfo(bsdtar, 0);
e = archive_write_header(a, in_entry);
if (e != ARCHIVE_OK) {
@@ -606,14 +616,18 @@ copy_file_data(struct bsdtar *bsdtar, struct archive *a, struct archive *ina)
char buff[64*1024];
ssize_t bytes_read;
ssize_t bytes_written;
+ off_t progress = 0;
bytes_read = archive_read_data(ina, buff, sizeof(buff));
while (bytes_read > 0) {
+ siginfo_printinfo(bsdtar, progress);
+
bytes_written = archive_write_data(a, buff, bytes_read);
if (bytes_written < bytes_read) {
bsdtar_warnc(bsdtar, 0, "%s", archive_error_string(a));
return (-1);
}
+ progress += bytes_written;
bytes_read = archive_read_data(ina, buff, sizeof(buff));
}
@@ -881,6 +895,13 @@ write_entry(struct bsdtar *bsdtar, struct archive *a, const struct stat *st,
if (!S_ISREG(st->st_mode))
archive_entry_set_size(entry, 0);
+ /* Record what we're doing, for the benefit of SIGINFO / SIGUSR1. */
+ siginfo_setinfo(bsdtar, "adding", archive_entry_pathname(entry),
+ archive_entry_size(entry));
+
+ /* Handle SIGINFO / SIGUSR1 request if one was made. */
+ siginfo_printinfo(bsdtar, 0);
+
e = archive_write_header(a, entry);
if (e != ARCHIVE_OK) {
if (!bsdtar->verbose)
@@ -923,12 +944,15 @@ write_file_data(struct bsdtar *bsdtar, struct archive *a, int fd)
char buff[64*1024];
ssize_t bytes_read;
ssize_t bytes_written;
+ off_t progress = 0;
/* XXX TODO: Allocate buffer on heap and store pointer to
* it in bsdtar structure; arrange cleanup as well. XXX */
bytes_read = read(fd, buff, sizeof(buff));
while (bytes_read > 0) {
+ siginfo_printinfo(bsdtar, progress);
+
bytes_written = archive_write_data(a, buff, bytes_read);
if (bytes_written < 0) {
/* Write failed; this is bad */
@@ -941,6 +965,7 @@ write_file_data(struct bsdtar *bsdtar, struct archive *a, int fd)
"Truncated write; file may have grown while being archived.");
return (0);
}
+ progress += bytes_written;
bytes_read = read(fd, buff, sizeof(buff));
}
return 0;
OpenPOWER on IntegriCloud