diff options
author | cperciva <cperciva@FreeBSD.org> | 2008-05-18 06:24:47 +0000 |
---|---|---|
committer | cperciva <cperciva@FreeBSD.org> | 2008-05-18 06:24:47 +0000 |
commit | 62fbb839584ccf0862199f020dd0e780e05a486e (patch) | |
tree | 1b5dbb3aaefee767b02f45a0a1c021a1d1841cf0 /usr.bin/tar | |
parent | a8f81206ad84b1ab64280e9138a55223f038fc54 (diff) | |
download | FreeBSD-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/Makefile | 2 | ||||
-rw-r--r-- | usr.bin/tar/bsdtar.h | 6 | ||||
-rw-r--r-- | usr.bin/tar/read.c | 26 | ||||
-rw-r--r-- | usr.bin/tar/siginfo.c | 147 | ||||
-rw-r--r-- | usr.bin/tar/write.c | 25 |
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; |