* Authors: the Amanda Development Team. Its members are listed in a
* file named AUTHORS, in the root directory of this distribution.
*/
-/* $Id: taper.c,v 1.144 2006/08/24 11:23:32 martinea Exp $
+/* $Id: taper.c 6512 2007-05-24 17:00:24Z ian $
*
* moves files from holding disk to tape, or from a socket to tape
*/
-#include "amanda.h"
-#include "util.h"
-#include "conffile.h"
-#include "tapefile.h"
-#include "clock.h"
-#include "stream.h"
-#include "holding.h"
-#include "logfile.h"
-#include "tapeio.h"
-#include "changer.h"
-#include "version.h"
-#include "arglist.h"
-#include "token.h"
-#include "amfeatures.h"
-#include "fileheader.h"
-#include "server_util.h"
-#include "taperscan.c"
-
-#ifdef HAVE_SYS_MMAN_H
-#include <sys/mman.h>
-#endif
-
-#ifdef HAVE_LIBVTBLC
-#include <vtblc.h>
-#include <strings.h>
-#include <math.h>
-
-
-static int vtbl_no = -1;
-static int len = 0;
-static int offset = 0;
-static char *datestr = NULL;
-static char start_datestr[20];
-static time_t raw_time;
-static struct tm tape_time;
-static struct tm backup_time;
-static struct tm *tape_timep = &tape_time;
-typedef struct vtbl_lbls {
- u_int8_t label[45];
- u_int8_t date[20];
-} vtbl_lbls;
-static vtbl_lbls vtbl_entry[MAX_VOLUMES];
-#endif /* HAVE_LIBVTBLC */
-/*
- * XXX update stat collection/printing
- * XXX advance to next tape first in next_tape
- * XXX label is being read twice?
- */
-static off_t splitsize = (off_t)0; /* max size of dumpfile before split (Kb) */
-static off_t mmap_splitsize = (off_t)0;
-static char *mmap_filename = NULL;
-static char *mmap_splitbuf = NULL;
-static char *mem_splitbuf = NULL;
-static char *splitbuf = NULL;
-static off_t mem_splitsize = (off_t)0;
-static char *splitbuf_wr_ptr = NULL; /* the number of Kb we've written into splitbuf */
-int orig_holdfile = -1;
-
-/* NBUFS replaced by conf_tapebufs */
-/* #define NBUFS 20 */
-static int conf_tapebufs;
-
-static off_t maxseek = (off_t)1 << ((SIZEOF(off_t) * 8) - 11);
-
-static char *holdfile_path = NULL;
-static char *holdfile_path_thischunk = NULL;
-static int num_holdfile_chunks = 0;
-static off_t holdfile_offset_thischunk = (off_t)0;
-static int mmap_splitbuffer_fd = -1;
-
-#define MODE_NONE 0
-#define MODE_FILE_WRITE 1
-#define MODE_PORT_WRITE 2
-
-static mode_t mode = MODE_NONE;
-
-/* This is now the number of empties, not full bufs */
-#define THRESHOLD 1
-
-#define CONNECT_TIMEOUT 2*60
-
-#define EMPTY 1
-#define FILLING 2
-#define FULL 3
-
-typedef struct buffer_s {
- long status;
- ssize_t size;
- char *buffer;
-} buffer_t;
-
-#define nextbuf(p) ((p) == buftable+conf_tapebufs-1? buftable : (p)+1)
-#define prevbuf(p) ((p) == buftable? buftable+conf_tapebufs-1 : (p)-1)
-
-/* major modules */
-int main(int main_argc, char **main_argv);
-void file_reader_side(int rdpipe, int wrpipe);
-void tape_writer_side(int rdpipe, int wrpipe);
-void put_syncpipe_fault_result(char *handle);
-
-/* shared-memory routines */
-char *attach_buffers(size_t size);
-void detach_buffers(char *bufp);
-void destroy_buffers(void);
-#define REMOVE_SHARED_MEMORY() \
- detach_buffers(buffers); \
- if (strcmp(procname, "reader") == 0) { \
- destroy_buffers(); \
- }
-
-/* synchronization pipe routines */
-void syncpipe_init(int rd, int wr);
-void syncpipe_read_error(ssize_t rc, ssize_t expected);
-void syncpipe_write_error(ssize_t rc, ssize_t expected);
-int syncpipe_get(int *intp);
-int syncpipe_getint(void);
-char *syncpipe_getstr(void);
-int syncpipe_put(int ch, int intval);
-int syncpipe_putint(int i);
-int syncpipe_putstr(const char *str);
-
-/* tape manipulation subsystem */
-int first_tape(char *new_datestamp);
-int next_tape(int writerr);
-int end_tape(int writerr);
-int write_filemark(void);
-
-/* support crap */
-int seek_holdfile(int fd, buffer_t *bp, off_t kbytes);
-
-/* signal handling */
-static void install_signal_handlers(void);
-static void signal_handler(int);
-
-/* exit routine */
-static void cleanup(void);
-
-/*
- * ========================================================================
- * GLOBAL STATE
- *
- */
-int interactive;
-pid_t writerpid;
-times_t total_wait;
-#ifdef TAPER_DEBUG
-int bufdebug = 1;
-#else
-int bufdebug = 0;
-#endif
-
-char *buffers = NULL;
-buffer_t *buftable = NULL;
-int err;
-
-char *procname = "parent";
-
-char *taper_timestamp = NULL;
-char *label = NULL;
-int filenum;
-char *errstr = NULL;
-int tape_fd = -1;
-char *tapedev = NULL;
-char *tapetype = NULL;
-tapetype_t *tt = NULL;
-size_t tt_blocksize;
-size_t tt_blocksize_kb;
-size_t buffer_size;
-int tt_file_pad;
-static unsigned long malloc_hist_1, malloc_size_1;
-static unsigned long malloc_hist_2, malloc_size_2;
-dumpfile_t file;
-dumpfile_t *save_holdfile = NULL;
-off_t cur_span_chunkstart = (off_t)0; /* start of current split dump chunk (Kb) */
-char *holdfile_name;
-int num_splits = 0;
-int expected_splits = 0;
-int num_holdfiles = 0;
-times_t curdump_rt;
-
-am_feature_t *their_features = NULL;
-
-int runtapes, cur_tape, have_changer, tapedays;
-char *labelstr, *conf_tapelist;
-#ifdef HAVE_LIBVTBLC
-char *rawtapedev;
-int first_seg, last_seg;
-#endif /* HAVE_LIBVTBLC */
-
-/*
- * ========================================================================
- * MAIN PROGRAM
- *
- */
-int
-main(
- int main_argc,
- char **main_argv)
-{
- int p2c[2], c2p[2]; /* parent-to-child, child-to-parent pipes */
- char *conffile;
- size_t size;
- int i;
- size_t j;
- size_t page_size;
- char *first_buffer;
- int new_argc, my_argc;
- char **new_argv, **my_argv;
-
- safe_fd(-1, 0);
-
- set_pname("taper");
-
- dbopen("server");
-
- /* Don't die when child closes pipe */
- signal(SIGPIPE, SIG_IGN);
-
- malloc_size_1 = malloc_inuse(&malloc_hist_1);
-
- parse_server_conf(main_argc, main_argv, &new_argc, &new_argv);
- my_argc = new_argc;
- my_argv = new_argv;
-
- fprintf(stderr, "%s: pid %ld executable %s version %s\n",
- get_pname(), (long) getpid(), my_argv[0], version());
- dbprintf(("%s: pid %ld executable %s version %s\n",
- get_pname(), (long) getpid(), my_argv[0], version()));
- fflush(stderr);
-
- if (my_argc > 1 && my_argv[1][0] != '-') {
- config_name = stralloc(my_argv[1]);
- config_dir = vstralloc(CONFIG_DIR, "/", my_argv[1], "/", NULL);
- my_argc--;
- my_argv++;
- } else {
- char my_cwd[STR_SIZE];
-
- if (getcwd(my_cwd, SIZEOF(my_cwd)) == NULL) {
- error("cannot determine current working directory");
- /*NOTREACHED*/
- }
- config_dir = stralloc2(my_cwd, "/");
- if ((config_name = strrchr(my_cwd, '/')) != NULL) {
- config_name = stralloc(config_name + 1);
- }
- }
-
- safe_cd();
-
- install_signal_handlers();
- atexit(cleanup);
-
- /* print prompts and debug messages if running interactive */
-
- interactive = (my_argc > 1 && strcmp(my_argv[1],"-t") == 0);
- if (interactive) {
- erroutput_type = ERR_INTERACTIVE;
- } else {
- erroutput_type = ERR_AMANDALOG;
- set_logerror(logerror);
- }
-
- free_new_argv(new_argc, new_argv);
-
- conffile = stralloc2(config_dir, CONFFILE_NAME);
- if (read_conffile(conffile)) {
- error("errors processing config file \"%s\"", conffile);
- /*NOTREACHED*/
- }
- amfree(conffile);
-
- dbrename(config_name, DBG_SUBDIR_SERVER);
-
- report_bad_conf_arg();
-
- conf_tapelist = getconf_str(CNF_TAPELIST);
- if (*conf_tapelist == '/') {
- conf_tapelist = stralloc(conf_tapelist);
- } else {
- conf_tapelist = stralloc2(config_dir, conf_tapelist);
- }
- if (read_tapelist(conf_tapelist)) {
- error("could not load tapelist \"%s\"", conf_tapelist);
- /*NOTREACHED*/
- }
-
- tapedev = stralloc(getconf_str(CNF_TAPEDEV));
- tapetype = getconf_str(CNF_TAPETYPE);
- tt = lookup_tapetype(tapetype);
-#ifdef HAVE_LIBVTBLC
- rawtapedev = stralloc(getconf_str(CNF_RAWTAPEDEV));
-#endif /* HAVE_LIBVTBLC */
- tapedays = getconf_int(CNF_TAPECYCLE);
- labelstr = getconf_str(CNF_LABELSTR);
-
- runtapes = getconf_int(CNF_RUNTAPES);
- cur_tape = 0;
-
- conf_tapebufs = getconf_int(CNF_TAPEBUFS);
-
- tt_blocksize_kb = (size_t)tapetype_get_blocksize(tt);
- tt_blocksize = tt_blocksize_kb * 1024;
- tt_file_pad = tapetype_get_file_pad(tt);
-
- if (interactive) {
- fprintf(stderr,"taper: running in interactive test mode\n");
- dbprintf(("taper: running in interactive test mode\n"));
- fflush(stderr);
- }
-
- /* create read/write syncronization pipes */
-
- if (pipe(p2c)) {
- error("creating sync pipes: %s", strerror(errno));
- /*NOTREACHED*/
- }
- if (pipe(c2p)) {
- error("creating sync pipes: %s", strerror(errno));
- /*NOTREACHED*/
- }
-
- /* create shared memory segment */
-
-#if defined(HAVE_GETPAGESIZE)
- page_size = (size_t)getpagesize();
- fprintf(stderr, "%s: page size = " SIZE_T_FMT "\n",
- get_pname(), (SIZE_T_FMT_TYPE)page_size);
- dbprintf(("%s: page size = " SIZE_T_FMT "\n", get_pname(),
- (SIZE_T_FMT_TYPE)page_size));
-#else
- page_size = 1024;
- fprintf(stderr, "%s: getpagesize() not available, using " SIZE_T_FMT "\n",
- get_pname(), page_size);
- dbprintf((stderr, "%s: getpagesize() not available, using " SIZE_T_FMT "\n",
- get_pname(), page_size));
-#endif
- buffer_size = am_round(tt_blocksize, page_size);
- fprintf(stderr, "%s: buffer size is " SIZE_T_FMT "\n",
- get_pname(), (SIZE_T_FMT_TYPE)buffer_size);
- dbprintf(("%s: buffer size is " SIZE_T_FMT "\n",
- get_pname(), (SIZE_T_FMT_TYPE)buffer_size));
- while (conf_tapebufs > 0) {
- size = page_size;
- size += conf_tapebufs * buffer_size;
- size += conf_tapebufs * SIZEOF(buffer_t);
- if ((buffers = attach_buffers(size)) != NULL) {
- break;
- }
- log_add(L_INFO, "attach_buffers: (%d tapebuf%s: %d bytes) %s",
- conf_tapebufs,
- (conf_tapebufs == 1) ? "" : "s",
- size,
- strerror(errno));
- conf_tapebufs--;
- }
- if (buffers == NULL) {
- error("cannot allocate shared memory");
- /*NOTREACHED*/
- }
-
- /* page boundary offset */
- i = (int)((buffers - (char *)0) & (page_size - 1));
- if (i != 0) {
- first_buffer = buffers + page_size - i;
- dbprintf(("%s: shared memory at %p, first buffer at %p\n",
- get_pname(),
- (void *)buffers,
- (void *)first_buffer));
- } else {
- first_buffer = buffers;
- }
-
- /*LINTED first_buffer, conf_tapebufs and buffer size are all * pagesize */
- buftable = (buffer_t *)(first_buffer + (conf_tapebufs * buffer_size));
- memset(buftable, 0, conf_tapebufs * SIZEOF(buffer_t));
- if (conf_tapebufs < 10) {
- j = 1;
- } else if (conf_tapebufs < 100) {
- j = 2;
- } else {
- j = 3;
- }
- for (i = 0; i < conf_tapebufs; i++) {
- buftable[i].buffer = first_buffer + i * buffer_size;
- dbprintf(("%s: buffer[%0*d] at %p\n",
- get_pname(),
- (int)j, i,
- (void *)buftable[i].buffer));
- }
- dbprintf(("%s: buffer structures at %p for %d bytes\n",
- get_pname(),
- (void *)buftable,
- (int)(conf_tapebufs * SIZEOF(buffer_t))));
-
- /* fork off child writer process, parent becomes reader process */
- switch(writerpid = fork()) {
- case -1:
- error("fork: %s", strerror(errno));
- /*NOTREACHED*/
-
- case 0: /* child */
- aclose(p2c[1]);
- aclose(c2p[0]);
-
- tape_writer_side(p2c[0], c2p[1]);
- error("tape writer terminated unexpectedly");
- /*NOTREACHED*/
-
- default: /* parent */
- aclose(p2c[0]);
- aclose(c2p[1]);
-
- file_reader_side(c2p[0], p2c[1]);
- error("file reader terminated unexpectedly");
- /*NOTREACHED*/
- }
-
- /*NOTREACHED*/
- return 0;
-}
-
-
-/*
- * ========================================================================
- * FILE READER SIDE
- *
- */
-int read_file(int fd, char *handle,
- char *host, char *disk, char *datestamp,
- int level);
-ssize_t taper_fill_buffer(int fd, buffer_t *bp, size_t buflen);
-void dumpbufs(char *str1);
-void dumpstatus(buffer_t *bp);
-ssize_t get_next_holding_file(int fd, buffer_t *bp, char **strclosing, size_t rc);
-int predict_splits(char *filename);
-void create_split_buffer(char *split_diskbuffer, size_t fallback_splitsize, char *id_string);
-void free_split_buffer(void);
-
-
-/*
- * Create a buffer, either in an mmapped file or in memory, where PORT-WRITE
- * dumps can buffer the current split chunk in case of retry.
- */
-void
-create_split_buffer(
- char *split_diskbuffer,
- size_t fallback_splitsize,
- char *id_string)
-{
- char *buff_err = NULL;
- off_t offset;
- char *splitbuffer_path = NULL;
-
- /* don't bother if we're not actually splitting */
- if (splitsize <= (off_t)0) {
- splitbuf = NULL;
- splitbuf_wr_ptr = NULL;
- return;
- }
-
-#ifdef HAVE_MMAP
-#ifdef HAVE_SYS_MMAN_H
- if (strcmp(split_diskbuffer, "NULL")) {
- void *nulls = NULL;
- char *quoted;
- off_t c;
-
- splitbuffer_path = vstralloc(split_diskbuffer,
- "/splitdump_buffer",
- NULL);
- /* different file, munmap the previous */
- if (mmap_filename && strcmp(mmap_filename, splitbuffer_path) != 0) {
- dbprintf(("create_split_buffer: new file %s\n", splitbuffer_path));
- munmap(splitbuf, (size_t)mmap_splitsize);
- aclose(mmap_splitbuffer_fd);
- mmap_splitbuf = NULL;
- amfree(mmap_filename);
- mmap_splitsize = 0;
- }
- if (!mmap_filename) {
- dbprintf(("create_split_buffer: open file %s\n",
- splitbuffer_path));
- mmap_splitbuffer_fd = open(splitbuffer_path, O_RDWR|O_CREAT, 0600);
- if (mmap_splitbuffer_fd == -1) {
- buff_err = newvstralloc(buff_err, "open of ",
- splitbuffer_path, "failed (",
- strerror(errno), ")", NULL);
- goto fallback;
- }
- }
- offset = lseek(mmap_splitbuffer_fd, (off_t)0, SEEK_END) / 1024;
- if (offset < splitsize) { /* Increase file size */
- dbprintf(("create_split_buffer: increase file size of %s to "
- OFF_T_FMT "kb\n",
- splitbuffer_path, (OFF_T_FMT_TYPE)splitsize));
- if (mmap_filename) {
- dbprintf(("create_split_buffer: munmap old file %s\n",
- mmap_filename));
- munmap(splitbuf, (size_t)mmap_splitsize);
- mmap_splitsize = 0;
- mmap_splitbuf = NULL;
- }
- nulls = alloc(1024); /* lame */
- memset(nulls, 0, 1024);
- for (c = offset; c < splitsize ; c += (off_t)1) {
- if (fullwrite(mmap_splitbuffer_fd, nulls, 1024) < 1024) {
- buff_err = newvstralloc(buff_err, "write to ",
- splitbuffer_path,
- "failed (", strerror(errno),
- ")", NULL);
- c -= 1;
- if (c <= (off_t)fallback_splitsize) {
- goto fallback;
- }
- splitsize = c;
- break;
- }
- }
- }
- amfree(nulls);
-
- if (mmap_splitsize < splitsize*1024) {
- mmap_splitsize = splitsize*1024;
- mmap_filename = stralloc(splitbuffer_path);
- dbprintf(("create_split_buffer: mmap file %s for " OFF_T_FMT "kb\n",
- mmap_filename,(OFF_T_FMT_TYPE)splitsize));
- mmap_splitbuf = mmap(NULL, (size_t)mmap_splitsize,
- PROT_READ|PROT_WRITE,
- MAP_SHARED, mmap_splitbuffer_fd, (off_t)0);
- if (mmap_splitbuf == (char*)-1) {
- buff_err = newvstralloc(buff_err, "mmap failed (",
- strerror(errno), ")", NULL);
- aclose(mmap_splitbuffer_fd);
- amfree(mmap_filename);
- mmap_splitsize = 0;
- mmap_splitbuf = NULL;
- goto fallback;
- }
- }
- quoted = quote_string(splitbuffer_path);
- fprintf(stderr,
- "taper: r: buffering " OFF_T_FMT
- "kb split chunks in mmapped file %s\n",
- (OFF_T_FMT_TYPE)splitsize, quoted);
- dbprintf(("taper: r: buffering " OFF_T_FMT
- "kb split chunks in mmapped file %s\n",
- (OFF_T_FMT_TYPE)splitsize, quoted));
- amfree(splitbuffer_path);
- amfree(quoted);
- amfree(buff_err);
- splitbuf = mmap_splitbuf;
- splitbuf_wr_ptr = splitbuf;
- return;
- } else {
- buff_err = stralloc("no split_diskbuffer specified");
- }
-#else
- (void)split_diskbuffer; /* Quite unused parameter warning */
- buff_err = stralloc("mman.h not available");
- goto fallback;
-#endif
-#else
- (void)split_diskbuffer; /* Quite unused parameter warning */
- buff_err = stralloc("mmap not available");
- goto fallback;
-#endif
-
- /*
- Buffer split dumps in memory, if we can't use a file.
- */
- fallback:
- amfree(splitbuffer_path);
- splitsize = (off_t)fallback_splitsize;
- dbprintf(("create_split_buffer: fallback size " OFF_T_FMT "\n",
- (OFF_T_FMT_TYPE)splitsize));
- log_add(L_INFO,
- "%s: using fallback split size of %dkb to buffer %s in-memory",
- buff_err, splitsize, id_string);
- amfree(buff_err);
- if (splitsize > mem_splitsize) {
- amfree(mem_splitbuf);
- mem_splitbuf = alloc(fallback_splitsize * 1024);
- mem_splitsize = fallback_splitsize;
- dbprintf(("create_split_buffer: alloc buffer size " OFF_T_FMT "\n",
- (OFF_T_FMT_TYPE)splitsize *1024));
- }
- splitbuf = mem_splitbuf;
- splitbuf_wr_ptr = splitbuf;
-}
-
-/*
- * Free up resources that create_split_buffer eats.
- */
-void
-free_split_buffer(void)
-{
- if (mmap_splitbuffer_fd != -1) {
-#ifdef HAVE_MMAP
-#ifdef HAVE_SYS_MMAN_H
- if (splitbuf != NULL)
- munmap(splitbuf, (size_t)mmap_splitsize);
-#endif
-#endif
- aclose(mmap_splitbuffer_fd);
- amfree(mmap_filename);
- mmap_splitsize = 0;
- }
- if (mem_splitbuf) {
- amfree(splitbuf);
- mem_splitsize = 0;
- }
-}
-
-void
-put_syncpipe_fault_result(
- char * handle)
-{
- char *q;
-
- if (handle == NULL)
- handle = "<nohandle>";
-
- q = squotef("[Taper syncpipe fault]");
- putresult(TAPE_ERROR, "%s %s\n", handle, q);
- log_add(L_ERROR, "tape-error %s %s", handle, q);
- amfree(q);
-}
-
-void
-file_reader_side(
- int rdpipe,
- int wrpipe)
-{
- cmd_t cmd;
- struct cmdargs cmdargs;
- char *handle = NULL;
- char *filename = NULL;
- char *qfilename = NULL;
- char *hostname = NULL;
- char *diskname = NULL;
- char *qdiskname = NULL;
- char *result = NULL;
- char *datestamp = NULL;
- char *split_diskbuffer = NULL;
- char *id_string = NULL;
- int tok;
- char *q = NULL;
- int level, fd;
- in_port_t data_port;
- int data_socket;
- pid_t wpid;
- char level_str[64];
- struct stat stat_file;
- int tape_started;
- int a;
- size_t fallback_splitsize = 0;
- int tmpint;
- char *c, *c1;
-
- procname = "reader";
- syncpipe_init(rdpipe, wrpipe);
-
- /* must get START_TAPER before beginning */
-
- startclock();
- cmd = getcmd(&cmdargs);
- total_wait = stopclock();
-
- if (cmd != START_TAPER || cmdargs.argc != 2) {
- error("error [file_reader_side cmd %d argc %d]", cmd, cmdargs.argc);
- /*NOTREACHED*/
- }
-
- /* pass start command on to tape writer */
-
- taper_timestamp = newstralloc(taper_timestamp, cmdargs.argv[2]);
-
- tape_started = 0;
- if (syncpipe_put('S', 0) == -1) {
- put_syncpipe_fault_result(NULL);
- }
-
- if (syncpipe_putstr(taper_timestamp) == -1) {
- put_syncpipe_fault_result(NULL);
- }
-
- /* get result of start command */
-
- tok = syncpipe_get(&tmpint);
- switch(tok) {
- case -1:
- put_syncpipe_fault_result(NULL);
- break;
-
- case 'S':
- putresult(TAPER_OK, "\n");
- tape_started = 1;
- /* start is logged in writer */
- break;
-
- case 'E':
- /* no tape, bail out */
- if ((result = syncpipe_getstr()) == NULL) {
- put_syncpipe_fault_result(NULL);
- } else {
- q = squotef("[%s]", result);
- putresult(TAPE_ERROR, "<nohandle> %s\n", q);
- amfree(q);
- log_add(L_ERROR,"no-tape [%s]", "No writable valid tape found");
- c = c1 = result;
- while (*c != '\0') {
- if (*c == '\n') {
- *c = '\0';
- log_add(L_WARNING,"%s", c1);
- c1 = c+1;
- }
- c++;
- }
- if (strlen(c1) > 1 )
- log_add(L_WARNING,"%s", c1);
- amfree(result);
- (void)syncpipe_put('e', 0); /* ACK error */
- }
- break;
-
- case 'H': /* Syncpipe I/O error */
- /* No ACK syncpipe is down just exit */
- put_syncpipe_fault_result(handle);
- break;
-
- case 'X':
- /*
- * Pipe read error: Communications is severed at least
- * back to us. We send a blind 'Q' (quit) and we don't
- * wait for a response...
- */
- syncpipe_put('Q', 0); /* ACK error */
- error("error [communications pipe from writer severed]");
- /*NOTREACHED*/
-
- default:
- q = squotef("[syncpipe sequence fault: Expected 'S' or 'E']");
- putresult(TAPE_ERROR, "<nohandle> %s\n", q);
- log_add(L_ERROR, "no-tape %s]", q);
- amfree(q);
- }
-
- /* process further driver commands */
- while (1) {
- startclock();
- cmd = getcmd(&cmdargs);
- if (cmd != QUIT && !tape_started) {
- error("error [file_reader_side cmd %d without tape ready]", cmd);
- /*NOTREACHED*/
- }
- total_wait = timesadd(total_wait, stopclock());
-
- switch(cmd) {
- case PORT_WRITE:
- /*
- * PORT-WRITE
- * handle
- * hostname
- * features
- * diskname
- * level
- * datestamp
- * splitsize
- * split_diskbuffer
- */
- mode = MODE_PORT_WRITE;
- cmdargs.argc++; /* true count of args */
- a = 2;
-
- if (a >= cmdargs.argc) {
- error("error [taper PORT-WRITE: not enough args: handle]");
- /*NOTREACHED*/
- }
- handle = newstralloc(handle, cmdargs.argv[a++]);
-
- if (a >= cmdargs.argc) {
- error("error [taper PORT-WRITE: not enough args: hostname]");
- /*NOTREACHED*/
- }
- hostname = newstralloc(hostname, cmdargs.argv[a++]);
-
- if (a >= cmdargs.argc) {
- error("error [taper PORT-WRITE: not enough args: features]");
- /*NOTREACHED*/
- }
- am_release_feature_set(their_features);
- their_features = am_string_to_feature(cmdargs.argv[a++]);
-
- if (a >= cmdargs.argc) {
- error("error [taper PORT-WRITE: not enough args: diskname]");
- /*NOTREACHED*/
- }
- qdiskname = newstralloc(qdiskname, cmdargs.argv[a++]);
- if (diskname != NULL)
- amfree(diskname);
- diskname = unquote_string(qdiskname);
-
- if (a >= cmdargs.argc) {
- error("error [taper PORT-WRITE: not enough args: level]");
- /*NOTREACHED*/
- }
- level = atoi(cmdargs.argv[a++]);
-
- if (a >= cmdargs.argc) {
- error("error [taper PORT-WRITE: not enough args: datestamp]");
- /*NOTREACHED*/
- }
- datestamp = newstralloc(datestamp, cmdargs.argv[a++]);
-
- if (a >= cmdargs.argc) {
- error("error [taper PORT-WRITE: not enough args: splitsize]");
- /*NOTREACHED*/
- }
- splitsize = OFF_T_ATOI(cmdargs.argv[a++]);
-
- if (a >= cmdargs.argc) {
- error("error [taper PORT-WRITE: not enough args: split_diskbuffer]");
- /*NOTREACHED*/
- }
- split_diskbuffer = newstralloc(split_diskbuffer, cmdargs.argv[a++]);
-
- if (a >= cmdargs.argc) {
- error("error [taper PORT-WRITE: not enough args: fallback_splitsize]");
- /*NOTREACHED*/
- }
- /* Must fit in memory... */
- fallback_splitsize = (size_t)atoi(cmdargs.argv[a++]);
-
- if (a != cmdargs.argc) {
- error("error [taper file_reader_side PORT-WRITE: too many args: %d != %d]",
- cmdargs.argc, a);
- /*NOTREACHED*/
- }
-
- if (fallback_splitsize < 128 ||
- fallback_splitsize > 64 * 1024 * 1024) {
- error("error [bad value for fallback_splitsize]");
- /*NOTREACHED*/
- }
- snprintf(level_str, SIZEOF(level_str), "%d", level);
- id_string = newvstralloc(id_string, hostname, ":", qdiskname, ".",
- level_str, NULL);
-
- create_split_buffer(split_diskbuffer, fallback_splitsize, id_string);
- amfree(id_string);
-
- data_port = 0;
- data_socket = stream_server(&data_port, 0, STREAM_BUFSIZE, 0);
- if (data_socket < 0) {
- char *m;
-
- m = vstralloc("[port create failure: ",
- strerror(errno),
- "]",
- NULL);
- q = squote(m);
- putresult(TAPE_ERROR, "%s %s\n", handle, q);
- amfree(m);
- amfree(q);
- break;
- }
- putresult(PORT, "%d\n", data_port);
-
- if ((fd = stream_accept(data_socket, CONNECT_TIMEOUT,
- 0, STREAM_BUFSIZE)) == -1) {
- q = squote("[port connect timeout]");
- putresult(TAPE_ERROR, "%s %s\n", handle, q);
- aclose(data_socket);
- amfree(q);
- break;
- }
- expected_splits = -1;
-
- while(read_file(fd, handle, hostname, qdiskname, datestamp, level))
- (void)fd; /* Quiet lint */
-
- aclose(data_socket);
- break;
-
- case FILE_WRITE:
- /*
- * FILE-WRITE
- * handle
- * filename
- * hostname
- * features
- * diskname
- * level
- * datestamp
- * splitsize
- */
- mode = MODE_FILE_WRITE;
- cmdargs.argc++; /* true count of args */
- a = 2;
-
- if (a >= cmdargs.argc) {
- error("error [taper FILE-WRITE: not enough args: handle]");
- /*NOTREACHED*/
- }
- handle = newstralloc(handle, cmdargs.argv[a++]);
-
- if (a >= cmdargs.argc) {
- error("error [taper FILE-WRITE: not enough args: filename]");
- /*NOTREACHED*/
- }
- qfilename = newstralloc(qfilename, cmdargs.argv[a++]);
- if (filename != NULL)
- amfree(filename);
- filename = unquote_string(qfilename);
-
- if (a >= cmdargs.argc) {
- error("error [taper FILE-WRITE: not enough args: hostname]");
- /*NOTREACHED*/
- }
- hostname = newstralloc(hostname, cmdargs.argv[a++]);
-
- if (a >= cmdargs.argc) {
- error("error [taper FILE-WRITE: not enough args: features]");
- /*NOTREACHED*/
- }
- am_release_feature_set(their_features);
- their_features = am_string_to_feature(cmdargs.argv[a++]);
-
- if (a >= cmdargs.argc) {
- error("error [taper FILE-WRITE: not enough args: diskname]");
- /*NOTREACHED*/
- }
- qdiskname = newstralloc(qdiskname, cmdargs.argv[a++]);
- if (diskname != NULL)
- amfree(diskname);
- diskname = unquote_string(qdiskname);
-
- if (a >= cmdargs.argc) {
- error("error [taper FILE-WRITE: not enough args: level]");
- /*NOTREACHED*/
- }
- level = atoi(cmdargs.argv[a++]);
-
- if (a >= cmdargs.argc) {
- error("error [taper FILE-WRITE: not enough args: datestamp]");
- /*NOTREACHED*/
- }
- datestamp = newstralloc(datestamp, cmdargs.argv[a++]);
-
- if (a >= cmdargs.argc) {
- error("error [taper FILE-WRITE: not enough args: splitsize]");
- /*NOTREACHED*/
- }
- splitsize = OFF_T_ATOI(cmdargs.argv[a++]);
-
- if (a != cmdargs.argc) {
- error("error [taper file_reader_side FILE-WRITE: too many args: %d != %d]",
- cmdargs.argc, a);
- /*NOTREACHED*/
- }
- if (holdfile_name != NULL) {
- filename = newstralloc(filename, holdfile_name);
- }
-
- if ((expected_splits = predict_splits(filename)) < 0) {
- break;
- }
- if (stat(filename, &stat_file)!=0) {
- q = squotef("[%s]", strerror(errno));
- putresult(TAPE_ERROR, "%s %s\n", handle, q);
- amfree(q);
- break;
- }
- if ((fd = open(filename, O_RDONLY)) == -1) {
- q = squotef("[%s]", strerror(errno));
- putresult(TAPE_ERROR, "%s %s\n", handle, q);
- amfree(q);
- break;
- }
- holdfile_path = stralloc(filename);
- holdfile_path_thischunk = stralloc(filename);
- holdfile_offset_thischunk = (off_t)0;
-
- while (read_file(fd,handle,hostname,qdiskname,datestamp,level)) {
- if (splitsize > (off_t)0 && holdfile_path_thischunk)
- filename = newstralloc(filename, holdfile_path_thischunk);
- if ((fd = open(filename, O_RDONLY)) == -1) {
- q = squotef("[%s]", strerror(errno));
- putresult(TAPE_ERROR, "%s %s\n", handle, q);
- amfree(q);
- break;
- }
- }
- break;
-
- case QUIT:
- putresult(QUITTING, "\n");
- fprintf(stderr,"taper: DONE [idle wait: %s secs]\n",
- walltime_str(total_wait));
- fflush(stderr);
- (void)syncpipe_put('Q', 0); /* tell writer we're exiting gracefully */
- aclose(wrpipe);
-
- if ((wpid = wait(NULL)) != writerpid) {
- dbprintf(("taper: writer wait returned %u instead of %u: %s\n",
- (unsigned)wpid, (unsigned)writerpid, strerror(errno)));
- fprintf(stderr,
- "taper: writer wait returned %u instead of %u: %s\n",
- (unsigned)wpid, (unsigned)writerpid, strerror(errno));
- fflush(stderr);
- }
-
- free_split_buffer();
- amfree(datestamp);
- clear_tapelist();
- free_server_config();
- amfree(taper_timestamp);
- amfree(label);
- amfree(errstr);
- amfree(changer_resultstr);
- amfree(tapedev);
- amfree(filename);
- amfree(conf_tapelist);
- amfree(config_dir);
- amfree(config_name);
- amfree(holdfile_name);
-
- malloc_size_2 = malloc_inuse(&malloc_hist_2);
-
- if (malloc_size_1 != malloc_size_2) {
- malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
- }
- exit(0);
- /*NOTREACHED*/
-
- default:
- if (cmdargs.argc >= 1) {
- q = squote(cmdargs.argv[1]);
- } else if (cmdargs.argc >= 0) {
- q = squote(cmdargs.argv[0]);
- } else {
- q = stralloc("(no input?)");
- }
- putresult(BAD_COMMAND, "%s\n", q);
- amfree(q);
- break;
- }
- }
- /* NOTREACHED */
-}
-
-void
-dumpbufs(
- char *str1)
-{
- int i,j;
- long v;
-
- fprintf(stderr, "%s: state", str1);
- for (i = j = 0; i < conf_tapebufs; i = j+1) {
- v = buftable[i].status;
- for(j = i; j < conf_tapebufs && buftable[j].status == v; j++)
- (void)j; /* Quiet lint */
- j--;
- if (i == j) {
- fprintf(stderr, " %d:", i);
- } else {
- fprintf(stderr, " %d-%d:", i, j);
- }
- switch(v) {
- case FULL:
- fputc('F', stderr);
- break;
-
- case FILLING:
- fputc('f', stderr);
- break;
-
- case EMPTY:
- fputc('E', stderr);
- break;
-
- default:
- fprintf(stderr, "%ld", v);
- break;
- }
- }
- fputc('\n', stderr);
- fflush(stderr);
-}
-
-void
-dumpstatus(
- buffer_t *bp)
-{
- char pn[2];
- char bt[NUM_STR_SIZE];
- char status[NUM_STR_SIZE + 1];
- char *str = NULL;
-
- pn[0] = procname[0];
- pn[1] = '\0';
- snprintf(bt, SIZEOF(bt), "%d", (int)(bp-buftable));
-
- switch(bp->status) {
- case FULL:
- snprintf(status, SIZEOF(status), "F" SIZE_T_FMT,
- (SIZE_T_FMT_TYPE)bp->size);
- break;
-
- case FILLING:
- snprintf(status, SIZEOF(status), "f");
- break;
-
- case EMPTY:
- snprintf(status, SIZEOF(status), "E");
- break;
-
- default:
- snprintf(status, SIZEOF(status), "%ld", bp->status);
- break;
- }
-
- str = vstralloc("taper: ", pn, ": [buf ", bt, ":=", status, "]", NULL);
- dumpbufs(str);
- amfree(str);
-}
-
-/*
- Handle moving to the next chunk of holding file, if any. Returns -1 for
- errors, 0 if there's no more file, or a positive integer for the amount of
- stuff read that'll go into 'rc' (XXX That's fugly, maybe that should just
- be another global. What is rc anyway, 'read count?' I keep thinking it
- should be 'return code')
-*/
-ssize_t
-get_next_holding_file(
- int fd,
- buffer_t *bp,
- char **strclosing,
- size_t rc)
-{
- int save_fd;
- ssize_t rc1;
- struct stat stat_file;
- ssize_t ret = -1;
-
- save_fd = fd;
- close(fd);
-
- /* see if we're fresh out of file */
- if (file.cont_filename[0] == '\0') {
- err = 0;
- ret = 0;
- } else if (stat(file.cont_filename, &stat_file) != 0) {
- err = errno;
- ret = -1;
- *strclosing = newvstralloc(*strclosing, "can't stat: ",
- file.cont_filename, NULL);
- } else if ((fd = open(file.cont_filename,O_RDONLY)) == -1) {
- err = errno;
- ret = -1;
- *strclosing = newvstralloc(*strclosing, "can't open: ",
- file.cont_filename, NULL);
- } else if ((fd != save_fd) && dup2(fd, save_fd) == -1) {
- err = errno;
- ret = -1;
- *strclosing = newvstralloc(*strclosing, "can't dup2: ",
- file.cont_filename, NULL);
- } else {
- buffer_t bp1;
- char *quoted;
-
- holdfile_path = stralloc(file.cont_filename);
- quoted = quote_string(holdfile_path);
- fprintf(stderr, "taper: r: switching to next holding chunk '%s'\n",
- quoted);
- amfree(quoted);
- num_holdfile_chunks++;
-
- bp1.status = EMPTY;
- bp1.size = DISK_BLOCK_BYTES;
- bp1.buffer = alloc(DISK_BLOCK_BYTES);
-
- if (fd != save_fd) {
- close(fd);
- fd = save_fd;
- }
-
- rc1 = taper_fill_buffer(fd, &bp1, DISK_BLOCK_BYTES);
- if (rc1 <= 0) {
- amfree(bp1.buffer);
- err = (rc1 < 0) ? errno : 0;
- ret = -1;
- *strclosing = newvstralloc(*strclosing,
- "Can't read header: ",
- file.cont_filename,
- NULL);
- } else {
- parse_file_header(bp1.buffer, &file, (size_t)rc1);
-
- amfree(bp1.buffer);
- bp1.buffer = bp->buffer + rc;
-
- rc1 = taper_fill_buffer(fd, &bp1, (size_t)tt_blocksize - rc);
- if (rc1 <= 0) {
- err = (rc1 < 0) ? errno : 0;
- ret = -1;
- if (rc1 < 0) {
- *strclosing = newvstralloc(*strclosing,
- "Can't read data: ",
- file.cont_filename,
- NULL);
- }
- } else {
- ret = rc1;
- num_holdfiles++;
- }
- }
- }
-
- return(ret);
-}
-
-
-int
-read_file(
- int fd,
- char * handle,
- char * hostname,
- char * qdiskname,
- char * datestamp,
- int level)
-{
- buffer_t *bp;
- int tok;
- ssize_t rc;
-#ifdef ASSERTIONS
- int opening;
-#endif
- int closing, bufnum, need_closing, nexting;
- off_t filesize;
- times_t runtime;
- char *strclosing = NULL;
- char seekerrstr[STR_SIZE];
- char *str;
- int header_written = 0;
- size_t buflen;
- dumpfile_t first_file;
- dumpfile_t cur_holdfile;
- off_t kbytesread = (off_t)0;
- int header_read = 0;
- char *cur_filename = NULL;
- int retry_from_splitbuf = 0;
- char *splitbuf_rd_ptr = NULL;
- char *q = NULL;
-
-#ifdef HAVE_LIBVTBLC
- static char desc[45];
- static char vol_date[20];
- static char vol_label[45];
-#endif /* HAVE_LIBVTBLC */
-
-
- /* initialize */
- memset(&first_file, 0, SIZEOF(first_file));
- memset(&cur_holdfile, 0, SIZEOF(cur_holdfile));
-
- filesize = (off_t)0;
- closing = 0;
- need_closing = 0;
- nexting = 0;
- err = 0;
-
- /* don't break this if we're still on the same file as a previous init */
- if (cur_span_chunkstart <= (off_t)0) {
- fh_init(&file);
- header_read = 0;
- } else if(mode == MODE_FILE_WRITE){
- memcpy(&file, save_holdfile, SIZEOF(dumpfile_t));
- memcpy(&cur_holdfile, save_holdfile, SIZEOF(dumpfile_t));
- }
-
- if (bufdebug) {
- fprintf(stderr, "taper: r: start file\n");
- fflush(stderr);
- }
-
- for (bp = buftable; bp < buftable + conf_tapebufs; bp++) {
- bp->status = EMPTY;
- }
-
- bp = buftable;
- if (interactive || bufdebug)
- dumpstatus(bp);
-
- if ((cur_span_chunkstart >= (off_t)0) && (splitsize > (off_t)0)) {
- /* We're supposed to start at some later part of the file, not read the
- whole thing. "Seek" forward to where we want to be. */
- if (label)
- putresult(SPLIT_CONTINUE, "%s %s\n", handle, label);
- if ((mode == MODE_FILE_WRITE) && (cur_span_chunkstart > (off_t)0)) {
- char *quoted = quote_string(holdfile_path_thischunk);
- fprintf(stderr, "taper: r: seeking %s to " OFF_T_FMT " kb\n",
- quoted,
- (OFF_T_FMT_TYPE)holdfile_offset_thischunk);
- fflush(stderr);
-
- if (holdfile_offset_thischunk > maxseek) {
- snprintf(seekerrstr, SIZEOF(seekerrstr), "Can't seek by "
- OFF_T_FMT " kb (compiled for %d-bit file offsets), "
- "recompile with large file support or "
- "set holdingdisk chunksize to <" OFF_T_FMT " Mb",
- (OFF_T_FMT_TYPE)holdfile_offset_thischunk,
- (int)(sizeof(off_t) * 8),
- (OFF_T_FMT_TYPE)(maxseek/(off_t)1024));
- log_add(L_ERROR, "%s", seekerrstr);
- fprintf(stderr, "taper: r: FATAL: %s\n", seekerrstr);
- fflush(stderr);
- if (syncpipe_put('X', 0) == -1) {
- put_syncpipe_fault_result(handle);
- }
- amfree(quoted);
- return -1;
- }
- if (lseek(fd, holdfile_offset_thischunk*(off_t)1024, SEEK_SET) == (off_t)-1) {
- fprintf(stderr, "taper: r: FATAL: seek_holdfile lseek error "
- "while seeking into %s by "
- OFF_T_FMT "kb: %s\n", quoted,
- (OFF_T_FMT_TYPE)holdfile_offset_thischunk,
- strerror(errno));
- fflush(stderr);
- if (syncpipe_put('X', 0) == -1) {
- put_syncpipe_fault_result(handle);
- }
- amfree(quoted);
- return -1;
- }
- amfree(quoted);
- } else if (mode == MODE_PORT_WRITE) {
- fprintf(stderr, "taper: r: re-reading split dump piece from buffer\n");
- fflush(stderr);
- retry_from_splitbuf = 1;
- splitbuf_rd_ptr = splitbuf;
- if (splitbuf_rd_ptr >= splitbuf_wr_ptr)
- retry_from_splitbuf = 0;
- }
- if (cur_span_chunkstart > (off_t)0)
- header_read = 1; /* really initialized in prior run */
- }
-
- /* tell writer to open tape */
-
-#ifdef ASSERTIONS
- opening = 1;
-#endif
-
- if (syncpipe_put('O', 0) == -1) {
- put_syncpipe_fault_result(handle);
- return -1;
- }
- if (syncpipe_putstr(datestamp) == -1) {
- put_syncpipe_fault_result(handle);
- return -1;
- }
- if (syncpipe_putstr(hostname) == -1) {
- put_syncpipe_fault_result(handle);
- return -1;
- }
- if (syncpipe_putstr(qdiskname) == -1) {
- put_syncpipe_fault_result(handle);
- return -1;
- }
- if (syncpipe_putint(level) == -1) {
- put_syncpipe_fault_result(handle);
- return -1;
- }
-
- startclock();
-
- /* read file in loop */
-
- while (1) {
- if ((tok = syncpipe_get(&bufnum)) == -1) {
- put_syncpipe_fault_result(handle);
- return -1;
- }
-
- switch(tok) {
- case 'O':
-#ifdef ASSERTIONS
- assert(opening);
- opening = 0;
-#endif
- err = 0;
- break;
-
- case 'R':
- if (bufdebug) {
- fprintf(stderr, "taper: r: got R%d\n", bufnum);
- fflush(stderr);
- }
-
- if (need_closing) {
- if (syncpipe_put('C', 0) == -1) {
- put_syncpipe_fault_result(handle);
- return (-1);
- }
- closing = 1;
- need_closing = 0;
- break;
- }
-
- if (closing)
- break; /* ignore extra read tokens */
-
-#ifdef ASSERTIONS
- assert(!opening);
-#endif
- if(bp->status != EMPTY || bufnum != (int)(bp - buftable)) {
- /* XXX this SHOULD NOT HAPPEN. Famous last words. */
- fprintf(stderr,"taper: panic: buffer mismatch at ofs "
- OFF_T_FMT ":\n", (OFF_T_FMT_TYPE)filesize);
- if(bufnum != (int)(bp - buftable)) {
- fprintf(stderr, " my buf %d but writer buf %d\n",
- (int)(bp-buftable), bufnum);
- } else {
- fprintf(stderr,"buf %d state %s (%ld) instead of EMPTY\n",
- (int)(bp-buftable),
- bp->status == FILLING? "FILLING" :
- bp->status == FULL? "FULL" : "EMPTY!?!?",
- (long)bp->status);
- }
- dumpbufs("taper");
- sleep(1);
- dumpbufs("taper: after 1 sec");
- if (bp->status == EMPTY)
- fprintf(stderr, "taper: result now correct!\n");
- fflush(stderr);
-
- errstr = newstralloc(errstr,
- "[fatal buffer mismanagement bug]");
- q = squote(errstr);
- putresult(TRYAGAIN, "%s %s\n", handle, q);
- cur_span_chunkstart = (off_t)0;
- amfree(q);
- log_add(L_INFO, "retrying %s:%s.%d on new tape due to: %s",
- hostname, qdiskname, level, errstr);
- closing = 1;
- if (syncpipe_put('X', 0) == -1) {/* X == buffer snafu, bail */
- put_syncpipe_fault_result(handle);
- return (-1);
- }
- do {
- if ((tok = syncpipe_get(&bufnum)) == -1) {
- put_syncpipe_fault_result(handle);
- return (-1);
- }
- } while (tok != 'x');
- aclose(fd);
- return -1;
- } /* end 'if (bf->status != EMPTY || bufnum != (int)(bp-buftable))' */
-
- bp->status = FILLING;
- buflen = header_read ? (size_t)tt_blocksize : DISK_BLOCK_BYTES;
- if (interactive || bufdebug)
- dumpstatus(bp);
- if (header_written == 0 &&
- (header_read == 1 || cur_span_chunkstart > (off_t)0)) {
- /* for split dumpfiles, modify headers for the second - nth
- pieces that signify that they're continuations of the last
- normal one */
- char *cont_filename;
- file.type = F_SPLIT_DUMPFILE;
- file.partnum = num_splits + 1;
- file.totalparts = expected_splits;
- cont_filename = stralloc(file.cont_filename);
- file.cont_filename[0] = '\0';
- build_header(bp->buffer, &file, tt_blocksize);
-
- if (cont_filename[0] != '\0') {
- file.type = F_CONT_DUMPFILE;
- strncpy(file.cont_filename, cont_filename,
- SIZEOF(file.cont_filename));
- }
- memcpy(&cur_holdfile, &file, SIZEOF(dumpfile_t));
-
- if (interactive || bufdebug)
- dumpstatus(bp);
- bp->size = (ssize_t)tt_blocksize;
- rc = (ssize_t)tt_blocksize;
- header_written = 1;
- amfree(cont_filename);
- } else if (retry_from_splitbuf) {
- /* quietly pull dump data from our in-memory cache, and the
- writer side need never know the wiser */
- memcpy(bp->buffer, splitbuf_rd_ptr, tt_blocksize);
- bp->size = (ssize_t)tt_blocksize;
- rc = (ssize_t)tt_blocksize;
-
- splitbuf_rd_ptr += tt_blocksize;
- if (splitbuf_rd_ptr >= splitbuf_wr_ptr)
- retry_from_splitbuf = 0;
- } else if ((rc = taper_fill_buffer(fd, bp, buflen)) < 0) {
- err = errno;
- closing = 1;
- strclosing = newvstralloc(strclosing,"Can't read data: ",
- NULL);
- if (syncpipe_put('C', 0) == -1) {
- put_syncpipe_fault_result(handle);
- return (-1);
- }
- }
-
- if (!closing) {
- if (rc < (ssize_t)buflen) { /* switch to next holding file */
- ssize_t ret;
-
- if (file.cont_filename[0] != '\0') {
- cur_filename = newvstralloc(cur_filename, file.cont_filename, NULL);
- }
- ret = get_next_holding_file(fd, bp, &strclosing, (size_t)rc);
- if (ret <= 0) {
- need_closing = 1;
- } else {
- memcpy(&cur_holdfile, &file, SIZEOF(dumpfile_t));
- rc += ret;
- bp->size = rc;
- }
- }
- if (rc > 0) {
- bp->status = FULL;
- /* rebuild the header block, which might have CONT junk */
- if (header_read == 0) {
- char *cont_filename;
- /* write the "real" filename if the holding-file
- is a partial one */
- parse_file_header(bp->buffer, &file, (size_t)rc);
- parse_file_header(bp->buffer, &first_file, (size_t)rc);
- cont_filename = stralloc(file.cont_filename);
- file.cont_filename[0] = '\0';
- if (splitsize > (off_t)0) {
- file.type = F_SPLIT_DUMPFILE;
- file.partnum = 1;
- file.totalparts = expected_splits;
- }
- file.blocksize = tt_blocksize;
- build_header(bp->buffer, &file, tt_blocksize);
- kbytesread += (off_t)(tt_blocksize/1024); /* XXX shady */
-
- file.type = F_CONT_DUMPFILE;
-
- /* add CONT_FILENAME back to in-memory header */
- strncpy(file.cont_filename, cont_filename,
- SIZEOF(file.cont_filename));
- if (interactive || bufdebug)
- dumpstatus(bp);
- bp->size = (ssize_t)tt_blocksize; /* output a full tape block */
- /* save the header, we'll need it if we jump tapes */
- memcpy(&cur_holdfile, &file, SIZEOF(dumpfile_t));
- header_read = 1;
- header_written = 1;
- amfree(cont_filename);
- } else {
- filesize = kbytesread;
- }
-
- if (bufdebug) {
- fprintf(stderr,"taper: r: put W%d\n",(int)(bp-buftable));
- fflush(stderr);
- }
- if (syncpipe_put('W', (int)(bp-buftable)) == -1) {
- put_syncpipe_fault_result(handle);
- return (-1);
- }
- bp = nextbuf(bp);
- }
-
- if (((kbytesread + (off_t)(DISK_BLOCK_BYTES/1024)) >= splitsize)
- && (splitsize > (off_t)0) && !need_closing) {
-
- if (mode == MODE_PORT_WRITE) {
- splitbuf_wr_ptr = splitbuf;
- splitbuf_rd_ptr = splitbuf;
- memset(splitbuf, 0, SIZEOF(splitbuf));
- retry_from_splitbuf = 0;
- }
-
- fprintf(stderr,"taper: r: end %s.%s.%s.%d part %d, "
- "splitting chunk that started at "
- OFF_T_FMT "kb after " OFF_T_FMT
- "kb (next chunk will start at "
- OFF_T_FMT "kb)\n",
- hostname, qdiskname, datestamp, level,
- num_splits+1,
- (OFF_T_FMT_TYPE)cur_span_chunkstart,
- (OFF_T_FMT_TYPE)kbytesread,
- (OFF_T_FMT_TYPE)(cur_span_chunkstart+kbytesread));
- fflush(stderr);
-
- nexting = 1;
- need_closing = 1;
- } /* end '(kbytesread >= splitsize && splitsize > 0)' */
- if (need_closing && rc <= 0) {
- if (syncpipe_put('C', 0) == -1) {
- put_syncpipe_fault_result(handle);
- return (-1);
- }
- need_closing = 0;
- closing = 1;
- }
- kbytesread += (off_t)(rc / 1024);
- } /* end the 'if (!closing)' (successful buffer fill) */
- break;
-
- case 'T':
- case 'E':
- case 'H':
- if (syncpipe_put('e', 0) == -1) { /* ACK error */
- put_syncpipe_fault_result(handle);
- return (-1);
- }
-
- if ((str = syncpipe_getstr()) == NULL) {
- put_syncpipe_fault_result(handle);
- return (-1);
- }
-
- errstr = newvstralloc(errstr, "[", str, "]", NULL);
- amfree(str);
-
- q = squote(errstr);
- if (tok == 'T') {
- if (splitsize > (off_t)0) {
- /* we'll be restarting this chunk on the next tape */
- if (mode == MODE_FILE_WRITE) {
- aclose(fd);
- }
-
- putresult(SPLIT_NEEDNEXT, "%s " OFF_T_FMT "\n", handle,
- (OFF_T_FMT_TYPE)cur_span_chunkstart);
- log_add(L_INFO, "continuing %s:%s.%d on new tape from "
- OFF_T_FMT "kb mark: %s",
- hostname, qdiskname, level,
- (OFF_T_FMT_TYPE)cur_span_chunkstart, errstr);
- return 1;
- } else {
- /* restart the entire dump (failure propagates to driver) */
- aclose(fd);
- putresult(TRYAGAIN, "%s %s\n", handle, q);
- cur_span_chunkstart = (off_t)0;
- log_add(L_INFO, "retrying %s:%s.%d on new tape due to: %s",
- hostname, qdiskname, level, errstr);
- }
- } else {
- aclose(fd);
- putresult(TAPE_ERROR, "%s %s\n", handle, q);
- log_add(L_FAIL, "%s %s %s %d [out of tape]",
- hostname, qdiskname, datestamp, level);
- log_add(L_ERROR,"no-tape [%s]", "No more writable valid tape found");
- }
- amfree(q);
- return 0;
-
- case 'C':
-#ifdef ASSERTIONS
- assert(!opening);
-#endif
- assert(closing);
-
- if (nexting) {
- cur_span_chunkstart += kbytesread; /* XXX possibly wrong */
- if (cur_filename)
- holdfile_name = newvstralloc(holdfile_name, cur_filename,
- NULL);
- else
- amfree(holdfile_name);
-
- kbytesread = (off_t)0;
- amfree(cur_filename);
- }
-
- if ((str = syncpipe_getstr()) == NULL) {
- put_syncpipe_fault_result(handle);
- return (-1);
- }
-
- label = newstralloc(label, str ? str : "(null)");
- amfree(str);
- if ((str = syncpipe_getstr()) == NULL) {
- put_syncpipe_fault_result(handle);
- return (-1);
- }
-
- filenum = atoi(str ? str : "-9876"); /* ??? */
- amfree(str);
- fprintf(stderr, "taper: reader-side: got label %s filenum %d\n",
- label, filenum);
- fflush(stderr);
-
- /* we'll need that file descriptor if we're gonna write more */
- if (!nexting) {
- aclose(fd);
- }
-
- runtime = stopclock();
- if (nexting)
- startclock();
- if (err) {
- if (strclosing) {
- errstr = newvstralloc(errstr,
- "[input: ", strclosing, ": ",
- strerror(err), "]", NULL);
- amfree(strclosing);
- } else
- errstr = newvstralloc(errstr,
- "[input: ", strerror(err), "]",
- NULL);
- q = squote(errstr);
- putresult(TAPE_ERROR, "%s %s\n", handle, q);
-
- amfree(q);
- if (splitsize != (off_t)0) {
- log_add(L_FAIL, "%s %s %s.%d %d %s", hostname, qdiskname,
- datestamp, num_splits, level, errstr);
- } else {
- log_add(L_FAIL, "%s %s %s %d %s",
- hostname, qdiskname, datestamp, level, errstr);
- }
- if ((str = syncpipe_getstr()) == NULL) { /* reap stats */
- put_syncpipe_fault_result(handle);
- return (-1);
- }
- amfree(str);
- amfree(errstr);
- } else {
- char kb_str[NUM_STR_SIZE];
- char kps_str[NUM_STR_SIZE];
- double rt;
-
- rt = (double)(runtime.r.tv_sec) +
- ((double)(runtime.r.tv_usec) / 1000000.0);
- curdump_rt = timesadd(runtime, curdump_rt);
- snprintf(kb_str, SIZEOF(kb_str), OFF_T_FMT,
- (OFF_T_FMT_TYPE)filesize);
- snprintf(kps_str, SIZEOF(kps_str), "%3.1lf",
- (isnormal(rt) ? (double)filesize / rt : 0.0));
- if ((str = syncpipe_getstr()) == NULL) {
- put_syncpipe_fault_result(handle);
- return (-1);
- }
- errstr = newvstralloc(errstr,
- "[sec ", walltime_str(runtime),
- " kb ", kb_str,
- " kps ", kps_str,
- " ", str,
- "]",
- NULL);
- if (splitsize == (off_t)0) { /* Ordinary dump */
- q = squote(errstr);
-/*@i@*/ if (first_file.is_partial) {
- putresult(PARTIAL, "%s %s %d %s\n",
- handle, label, filenum, q);
- log_add(L_PARTIAL, "%s %s %s %d %s",
- hostname, qdiskname, datestamp, level, errstr);
- } else {
- putresult(DONE, "%s %s %d %s\n",
- handle, label, filenum, q);
- log_add(L_SUCCESS, "%s %s %s %d %s",
- hostname, qdiskname, datestamp, level, errstr);
- }
- amfree(q);
- } else { /* Chunked dump */
- num_splits++;
- if (mode == MODE_FILE_WRITE) {
- holdfile_path_thischunk = stralloc(holdfile_path);
- holdfile_offset_thischunk = (lseek(fd, (off_t)0, SEEK_CUR))/(off_t)1024;
- if(!save_holdfile){
- save_holdfile = alloc(SIZEOF(dumpfile_t));
- }
- memcpy(save_holdfile, &cur_holdfile,SIZEOF(dumpfile_t));
- }
- log_add(L_CHUNK, "%s %s %s %d %d %s", hostname, qdiskname,
- datestamp, num_splits, level, errstr);
- if (!nexting) { /* split dump complete */
- rt = (double)(curdump_rt.r.tv_sec) +
- ((double)(curdump_rt.r.tv_usec) / 1000000.0);
- snprintf(kb_str, SIZEOF(kb_str), OFF_T_FMT,
- (OFF_T_FMT_TYPE)(filesize + cur_span_chunkstart));
- snprintf(kps_str, SIZEOF(kps_str), "%3.1lf",
- isnormal(rt) ?
- ((double)(filesize+cur_span_chunkstart)) / rt :
- 0.0);
- amfree(errstr);
- errstr = newvstralloc(errstr,
- "[sec ", walltime_str(curdump_rt),
- " kb ", kb_str,
- " kps ", kps_str,
- " ", str,
- "]",
- NULL);
- q = squote(errstr);
- putresult(DONE, "%s %s %d %s\n", handle, label,
- filenum, q);
- log_add(L_CHUNKSUCCESS, "%s %s %s %d %s",
- hostname, qdiskname, datestamp, level, errstr);
- amfree(save_holdfile);
- amfree(holdfile_path_thischunk);
- amfree(q);
- }
- }
- amfree(str);
-
- if (!nexting) {
- num_splits = 0;
- expected_splits = 0;
- amfree(holdfile_name);
- num_holdfiles = 0;
- cur_span_chunkstart = (off_t)0;
- curdump_rt = times_zero;
- }
- amfree(errstr);
-
-#ifdef HAVE_LIBVTBLC
- /*
- * We have 44 characters available for the label string:
- * use max 20 characters for hostname
- * max 20 characters for diskname
- * (it could contain a samba share or dos path)
- * 2 for level
- */
- memset(desc, '\0', 45);
-
- strncpy(desc, hostname, 20);
-
- if ((len = strlen(hostname)) <= 20) {
- memset(desc + len, ' ', 1);
- offset = len + 1;
- } else {
- memset(desc + 20, ' ', 1);
- offset = 21;
- }
-
- strncpy(desc + offset, qdiskname, 20);
-
- if ((len = strlen(qdiskname)) <= 20) {
- memset(desc + offset + len, ' ', 1);
- offset = offset + len + 1;
- } else {
- memset(desc + offset + 20, ' ', 1);
- offset = offset + 21;
- }
-
- sprintf(desc + offset, "%i", level);
-
- strncpy(vol_label, desc, 44);
- fprintf(stderr, "taper: added vtbl label string %i: \"%s\"\n",
- filenum, vol_label);
- fflush(stderr);
-
- /* pass label string on to tape writer */
- if (syncpipe_put('L', filenum) == -1) {
- put_syncpipe_fault_result(handle);
- return (-1);
- }
- if (syncpipe_putstr(vol_label) == -1) {
- put_syncpipe_fault_result(handle);
- return (-1);
- }
-
- /*
- * reformat datestamp for later use with set_date from vtblc
- */
- strptime(datestamp, "%Y%m%d", &backup_time);
- strftime(vol_date, 20, "%T %D", &backup_time);
- fprintf(stderr,
- "taper: reformatted vtbl date string: \"%s\"->\"%s\"\n",
- datestamp,
- vol_date);
-
- /* pass date string on to tape writer */
- if (syncpipe_put('D', filenum) == -1) {
- put_syncpipe_fault_result(handle);
- return (-1);
- }
- if (syncpipe_putstr(vol_date) == -1) {
- put_syncpipe_fault_result(handle);
- return (-1);
- }
-
-#endif /* HAVE_LIBVTBLC */
- }
- /* reset stuff that assumes we're on a new file */
-
- if (!nexting)
- return 0;
-
-#ifdef ASSERTIONS
- opening = 1;
-#endif
- nexting = 0;
- closing = 0;
- filesize = (off_t)0;
- if (syncpipe_put('O', 0) == -1) {
- put_syncpipe_fault_result(handle);
- return -1;
- }
- if (syncpipe_putstr(datestamp) == -1) {
- put_syncpipe_fault_result(handle);
- return -1;
- }
- if (syncpipe_putstr(hostname) == -1) {
- put_syncpipe_fault_result(handle);
- return -1;
- }
- if (syncpipe_putstr(qdiskname) == -1) {
- put_syncpipe_fault_result(handle);
- return -1;
- }
- if (syncpipe_putint(level) == -1) {
- put_syncpipe_fault_result(handle);
- return -1;
- }
- for (bp = buftable; bp < buftable + conf_tapebufs; bp++) {
- bp->status = EMPTY;
- }
- bp = buftable;
- header_written = 0;
- break;
-
- case 'X':
- /*
- * Pipe read error: Communications is severed at least
- * back to us. We send a blind 'Q' (quit) and we don't
- * wait for a response...
- */
- syncpipe_put('Q', 0); /* ACK error */
- fprintf(stderr, "taper: communications pipe from reader severed\n");
- return -1;
-
- default:
- q = squotef("[Taper syncpipe protocol error]");
- putresult(TAPE_ERROR, "%s %s\n", handle, q);
- log_add(L_ERROR, "tape-error %s %s", handle, q);
- amfree(q);
- return -1;
- }
- }
- return 0;
-}
-
-ssize_t
-taper_fill_buffer(
- int fd,
- buffer_t *bp,
- size_t buflen)
-{
- char *curptr;
- ssize_t cnt;
-
- curptr = bp->buffer;
-
- cnt = fullread(fd, curptr, buflen);
- switch(cnt) {
- case 0: /* eof */
- if (interactive)
- fputs("r0", stderr);
- bp->size = 0;
- return (ssize_t)0;
- /*NOTREACHED*/
-
- case -1: /* error on read, punt */
- if (interactive)
- fputs("rE", stderr);
- bp->size = 0;
- return -1;
- /*NOTREACHED*/
-
- default:
- if ((mode == MODE_PORT_WRITE) && (splitsize > (off_t)0)) {
- memcpy(splitbuf_wr_ptr, curptr, (size_t)cnt);
- splitbuf_wr_ptr += cnt;
- }
- bp->size = cnt;
- break;
- }
-
- if (interactive)
- fputs("R", stderr);
- return ((ssize_t)bp->size);
-}
-
-/* Given a dumpfile in holding, determine its size and figure out how many
- * times we'd have to split it.
- */
-int
-predict_splits(
- char *filename)
-{
- int splits = 0;
- off_t total_kb = (off_t)0;
- off_t adj_splitsize = splitsize - (off_t)(DISK_BLOCK_BYTES / 1024);
-
- if (splitsize <= (off_t)0)
- return(0);
-
- if (adj_splitsize <= (off_t)0) {
- error("Split size must be > " OFF_T_FMT "k",
- (OFF_T_FMT_TYPE)(DISK_BLOCK_BYTES/1024));
- /*NOTREACHED*/
- }
-
- /* should only calculuate this once, not on retries etc */
- if (expected_splits != 0)
- return(expected_splits);
-
- total_kb = size_holding_files(filename, 1);
-
- if (total_kb <= (off_t)0) {
- fprintf(stderr, "taper: r: " OFF_T_FMT
- " kb holding file makes no sense, not precalculating splits\n",
- (OFF_T_FMT_TYPE)total_kb);
- fflush(stderr);
- return(0);
- }
-
- fprintf(stderr, "taper: r: Total dump size should be " OFF_T_FMT
- "kb, chunk size is " OFF_T_FMT "kb\n",
- (OFF_T_FMT_TYPE)total_kb,
- (OFF_T_FMT_TYPE)splitsize);
- fflush(stderr);
-
- splits = (int)(total_kb / adj_splitsize);
- if ((splits == 0) || (total_kb % adj_splitsize))
- splits++;
-
-
- fprintf(stderr, "taper: r: Expecting to split into %d parts \n", splits);
- fflush(stderr);
-
- return(splits);
-}
-
-/*
- * ========================================================================
- * TAPE WRITER SIDE
- *
- */
-times_t idlewait, rdwait, wrwait, fmwait;
-unsigned long total_writes;
-off_t total_tape_used;
-int total_tape_fm;
-
-void write_file(void);
-int write_buffer(buffer_t *bp);
-
-void
-tape_writer_side(
- int getp,
- int putp)
-{
- int tok;
- int tape_started;
- char *str;
- char *hostname;
- char *diskname;
- char *datestamp;
- int level;
- int tmpint;
-
-#ifdef HAVE_LIBVTBLC
- char *vol_label;
- char *vol_date;
-#endif /* HAVE_LIBVTBLC */
-
- procname = "writer";
- syncpipe_init(getp, putp);
- tape_started = 0;
- idlewait = times_zero;
-
- while (1) {
- startclock();
- if ((tok = syncpipe_get(&tmpint)) == -1) {
- error("writer: Syncpipe failure before start");
- /*NOTREACHED*/
- }
-
- idlewait = timesadd(idlewait, stopclock());
- if (tok != 'S' && tok != 'Q' && !tape_started) {
- error("writer: token '%c' before start", tok);
- /*NOTREACHED*/
- }
-
- switch(tok) {
- case 'H': /* Reader read pipe side is down */
- dbprintf(("writer: Communications with reader is down"));
- error("writer: Communications with reader is down");
- /*NOTREACHED*/
-
- case 'S': /* start-tape */
- if (tape_started) {
- error("writer: multiple start requests");
- /*NOTREACHED*/
- }
- if ((str = syncpipe_getstr()) == NULL) {
- error("writer: Syncpipe failure");
- /*NOTREACHED*/
- }
- if (!first_tape(str ? str : "bad-datestamp")) {
- if (tape_fd >= 0) {
- tapefd_close(tape_fd);
- tape_fd = -1;
- }
- if (syncpipe_put('E', 0) == -1) {
- error("writer: Syncpipe failure passing exit code");
- /*NOTREACHED*/
- }
- if (syncpipe_putstr(errstr) == -1) {
- error("writer: Syncpipe failure passing exit string");
- /*NOTREACHED*/
- }
- /* wait for reader to acknowledge error */
- do {
- if ((tok = syncpipe_get(&tmpint)) == -1) {
- error("writer: Syncpipe failure waiting for error ack");
- /*NOTREACHED*/
- }
- if (tok != 'e') {
- error("writer: got '%c' unexpectedly after error", tok);
- /*NOTREACHED*/
- }
- } while (tok != 'e');
- } else {
- if (syncpipe_put('S', 0) == -1) {
- error("writer: syncpipe failure while starting tape");
- /*NOTREACHED*/
- }
- tape_started = 1;
- }
- amfree(str);
- break;
-
- case 'O': /* open-output */
- if ((datestamp = syncpipe_getstr()) == NULL) {
- error("writer: Syncpipe failure during open");
- /*NOTREACHED*/
- }
- tapefd_setinfo_datestamp(tape_fd, datestamp);
- amfree(datestamp);
-
- if ((hostname = syncpipe_getstr()) == NULL) {
- error("writer: Syncpipe failure fetching hostname");
- /*NOTREACHED*/
- }
- tapefd_setinfo_host(tape_fd, hostname);
- amfree(hostname);
-
- if ((diskname = syncpipe_getstr()) == NULL) {
- error("writer: Syncpipe failure fetching diskname");
- /*NOTREACHED*/
- }
- tapefd_setinfo_disk(tape_fd, diskname);
- amfree(diskname);
- if ((level = syncpipe_getint()) == -1) {
- error("writer: Syncpipe failure fetching level");
- /*NOTREACHED*/
- }
- tapefd_setinfo_level(tape_fd, level);
- write_file();
- break;
-
-#ifdef HAVE_LIBVTBLC
- case 'L': /* read vtbl label */
- vtbl_no = tmpint;
- if ((vol_label = syncpipe_getstr()) == NULL) {
- error("writer: Syncpipe failure fetching vrbl label");
- /*NOTREACHED*/
- }
- fprintf(stderr, "taper: read label string \"%s\" from pipe\n",
- vol_label);
- strncpy(vtbl_entry[vtbl_no].label, vol_label, 45);
- break;
-
- case 'D': /* read vtbl date */
- vtbl_no = tmpint;
- if ((vol_date = syncpipe_getstr()) == NULL) {
- error("writer: Syncpipe failure fetching vrbl date");
- /*NOTREACHED*/
- }
- fprintf(stderr, "taper: read date string \"%s\" from pipe\n",
- vol_date);
- strncpy(vtbl_entry[vtbl_no].date, vol_date, 20);
- break;
-#endif /* HAVE_LIBVTBLC */
-
- case 'Q':
- end_tape(0); /* XXX check results of end tape ?? */
- clear_tapelist();
- free_server_config();
- amfree(taper_timestamp);
- amfree(label);
- amfree(errstr);
- amfree(changer_resultstr);
- amfree(tapedev);
- amfree(conf_tapelist);
- amfree(config_dir);
- amfree(config_name);
-
- malloc_size_2 = malloc_inuse(&malloc_hist_2);
-
- if (malloc_size_1 != malloc_size_2) {
- malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
- }
- exit(0);
- /*NOTREACHED*/
-
- default:
- assert(0);
- }
- }
-}
-
-void
-write_file(void)
-{
- buffer_t *bp;
- int full_buffers, i, bufnum;
- int tok;
- char number[NUM_STR_SIZE];
- char *rdwait_str, *wrwait_str, *fmwait_str;
- int tmpint;
-
- rdwait = wrwait = times_zero;
- total_writes = 0;
-
- bp = buftable;
- full_buffers = 0;
- tok = '?';
-
- if (bufdebug) {
- fprintf(stderr, "taper: w: start file\n");
- fflush(stderr);
- }
-
- /*
- * Tell the reader that the tape is open, and give it all the buffers.
- */
- if (syncpipe_put('O', 0) == -1) {
- error("writer: Syncpipe failure starting write sequence");
- /*NOTREACHED*/
- }
- for (i = 0; i < conf_tapebufs; i++) {
- if (bufdebug) {
- fprintf(stderr, "taper: w: put R%d\n", i);
- fflush(stderr);
- }
- if (syncpipe_put('R', i) == -1) {
- error("writer: Syncpipe failure readying write buffers");
- /*NOTREACHED*/
- }
- }
-
- /*
- * We write the filemark at the start of the file rather than at the end,
- * so that it can proceed in parallel with the reader's initial filling
- * up of the buffers.
- */
-
- startclock();
- if (!write_filemark())
- goto tape_error;
- fmwait = stopclock();
-
- filenum += 1;
-
- do {
-
- /*
- * STOPPED MODE
- *
- * At the start of the file, or if the input can't keep up with the
- * tape, we enter STOPPED mode, which waits for most of the buffers
- * to fill up before writing to tape. This maximizes the amount of
- * data written in chunks to the tape drive, minimizing the number
- * of starts/stops, which in turn saves tape and time.
- */
-
- if (interactive)
- fputs("[WS]", stderr);
- startclock();
- while (full_buffers < conf_tapebufs - THRESHOLD) {
- if ((tok = syncpipe_get(&bufnum)) == -1) {
- error("writer: Syncpipe failure during buffer advance");
- /*NOTREACHED*/
- }
- if (tok != 'W')
- break;
- if (bufdebug) {
- fprintf(stderr,"taper: w: got W%d\n",bufnum);
- fflush(stderr);
- }
- full_buffers++;
- }
- rdwait = timesadd(rdwait, stopclock());
-
- /*
- * STARTING MODE
- *
- * We start output when sufficient buffers have filled up, or at
- * end-of-file, whichever comes first. Here we drain all the buffers
- * that were waited on in STOPPED mode. If more full buffers come
- * in, then we will be STREAMING.
- */
-
- while (full_buffers) {
- if (tt_file_pad && bp->size < (ssize_t)tt_blocksize) {
- memset(bp->buffer+bp->size, 0, tt_blocksize - bp->size);
- bp->size = (ssize_t)tt_blocksize;
- }
- if (!write_buffer(bp))
- goto tape_error;
- full_buffers--;
- bp = nextbuf(bp);
- }
-
- /*
- * STREAMING MODE
- *
- * With any luck, the input source is faster than the tape drive. In
- * this case, full buffers will appear in the circular queue faster
- * than we can write them, so the next buffer in the queue will always
- * be marked FULL by the time we get to it. If so, we'll stay in
- * STREAMING mode.
- *
- * On the other hand, if we catch up to the input and thus would have
- * to wait for buffers to fill, we are then STOPPED again.
- */
-
- while (tok == 'W' && bp->status == FULL) {
- if ((tok = syncpipe_get(&bufnum)) == -1) {
- error("writer: Syncpipe failure advancing buffer");
- /*NOTREACHED*/
- }
-
- if (tok == 'W') {
- if (bufdebug) {
- fprintf(stderr,"taper: w: got W%d\n",bufnum);
- fflush(stderr);
- }
- if(bufnum != (int)(bp - buftable)) {
- fprintf(stderr,
- "taper: tape-writer: my buf %d reader buf %d\n",
- (int)(bp-buftable), bufnum);
- fflush(stderr);
- if (syncpipe_put('E', 0) == -1) {
- error("writer: Syncpipe failure putting error token");
- /*NOTREACHED*/
- }
- if (syncpipe_putstr("writer-side buffer mismatch") == -1) {
- error("writer: Syncpipe failure putting error messgae");
- /*NOTREACHED*/
- }
- goto error_ack;
- }
- if (tt_file_pad && bp->size < (ssize_t)tt_blocksize) {
- memset(bp->buffer+bp->size, 0, tt_blocksize - bp->size);
- bp->size = (ssize_t)tt_blocksize;
- }
- if (!write_buffer(bp))
- goto tape_error;
- bp = nextbuf(bp);
- } else if (tok == 'Q') {
- return;
- } else if (tok == 'X') {
- goto reader_buffer_snafu;
- } else {
- error("writer-side not expecting token: %c", tok);
- /*NOTREACHED*/
- }
- }
- } while (tok == 'W');
+/* FIXME: This file needs to use gettext. */
- /* got close signal from reader, acknowledge it */
+#include <glib.h>
+#include "physmem.h"
- if (tok == 'X')
- goto reader_buffer_snafu;
+#include "changer.h"
+#include "clock.h"
+#include "conffile.h"
+#include "device.h"
+#include "logfile.h"
+#include "server_util.h"
+#include "stream.h"
+#include "tapefile.h"
+#include "taperscan.h"
+#include "taper-source.h"
+#include "timestamp.h"
+#include "util.h"
+#include "version.h"
+#include "queueing.h"
+#include "device-queueing.h"
+
+/* FIXME: This should not be here. */
+#define CONNECT_TIMEOUT (2*60)
+
+/* Use this instead of global variables, so that we are reentrant. */
+typedef struct {
+ Device * device;
+ char * driver_start_time;
+ int cur_tape;
+ char * next_tape_label;
+ char * next_tape_device;
+ taper_scan_tracker_t * taper_scan_tracker;
+ char * last_errmsg;
+ off_t total_bytes;
+ int have_changer;
+} taper_state_t;
+
+typedef struct {
+ char * handle;
+ char * hostname;
+ char * diskname;
+ int level;
+ char * timestamp;
+ char * id_string;
+ TaperSource * source;
+ int current_part;
+ GTimeVal total_time;
+ guint64 total_bytes;
+} dump_info_t;
+
+static gboolean label_new_tape(taper_state_t * state, dump_info_t * dump_info);
+
+static void init_taper_state(taper_state_t* state) {
+ state->device = NULL;
+ state->driver_start_time = NULL;
+ state->taper_scan_tracker = taper_scan_tracker_new();
+ state->last_errmsg = NULL;
+ state->total_bytes = 0;
+}
- assert(tok == 'C');
- if (syncpipe_put('C', 0) == -1) {
- error("writer: Syncpipe failure putting close");
- /*NOTREACHED*/
+static void cleanup(taper_state_t * state) {
+ amfree(state->driver_start_time);
+ amfree(state->next_tape_label);
+ amfree(state->next_tape_device);
+ amfree(state->last_errmsg);
+ taper_scan_tracker_free(state->taper_scan_tracker);
+ if (state->device != NULL) {
+ g_object_unref(state->device);
+ state->device = NULL;
+ }
+}
+
+static void free_dump_info(dump_info_t * info) {
+ amfree(info->handle);
+ amfree(info->hostname);
+ amfree(info->diskname);
+ amfree(info->timestamp);
+ amfree(info->id_string);
+ if (info->source != NULL) {
+ g_object_unref(info->source);
+ info->source = NULL;
}
+}
- /* tell reader the tape and file number */
+/* Validate that a command has the proper number of arguments, and
+ print a meaningful error message if not. It returns only if the
+ check is successful. */
+static void validate_args(struct cmdargs * cmdargs,
+ char ** argnames) {
+ int len = g_strv_length(argnames);
- if (syncpipe_putstr(label) == -1) {
- error("writer: Syncpipe failure putting label");
- /*NOTREACHED*/
+ if (len > cmdargs->argc) {
+ error("error [taper %s: not enough args; first missing arg is %s]",
+ cmdstr[cmdargs->cmd], argnames[cmdargs->argc]);
}
- snprintf(number, SIZEOF(number), "%d", filenum);
- if (syncpipe_putstr(number) == -1) {
- error("writer: Syncpipe failure putting filenum");
- /*NOTREACHED*/
+
+ if (len < cmdargs->argc) {
+ error("error [taper %s: Too many args: Got %d, expected %d.]",
+ cmdstr[cmdargs->cmd], cmdargs->argc, len);
}
+}
- snprintf(number, SIZEOF(number), "%lu", total_writes);
- rdwait_str = stralloc(walltime_str(rdwait));
- wrwait_str = stralloc(walltime_str(wrwait));
- fmwait_str = stralloc(walltime_str(fmwait));
- errstr = newvstralloc(errstr,
- "{wr:",
- " writers ", number,
- " rdwait ", rdwait_str,
- " wrwait ", wrwait_str,
- " filemark ", fmwait_str,
- "}",
- NULL);
- amfree(rdwait_str);
- amfree(wrwait_str);
- amfree(fmwait_str);
- if (syncpipe_putstr(errstr) == -1) {
- error("writer: Syncpipe failure putting '%s'", errstr);
- /*NOTREACHED*/
+/* Open a socket to the dumper. Returns TRUE if everything is happy, FALSE
+ otherwise. */
+static gboolean open_read_socket(dump_info_t * info, char * split_diskbuffer,
+ guint64 splitsize, guint64 fallback_splitsize) {
+ in_port_t port = 0;
+ int socket;
+ int fd;
+ int result;
+ struct addrinfo *res;
+
+ if ((result = resolve_hostname("localhost", 0, &res, NULL) != 0)) {
+ char *m;
+ char *q;
+ int save_errno = errno;
+ char *qdiskname = quote_string(info->diskname);
+
+ m = vstralloc("[localhost resolve failure: ",
+ strerror(save_errno),
+ "]",
+ NULL);
+ q = quote_string(m);
+ putresult(TAPE_ERROR, "%s %s\n", info->handle, q);
+ log_add(L_FAIL, "%s %s %s %d %s",
+ info->hostname, qdiskname, info->timestamp,
+ info->level, q);
+ amfree(qdiskname);
+ amfree(m);
+ amfree(q);
+ return FALSE;
+ }
+
+ socket = stream_server(res->ai_family, &port, 0, STREAM_BUFSIZE, 0);
+ freeaddrinfo(res);
+
+ if (socket < 0) {
+ char *m;
+ char *q;
+ int save_errno = errno;
+ char *qdiskname = quote_string(info->diskname);
+
+ m = vstralloc("[port create failure: ",
+ strerror(save_errno),
+ "]",
+ NULL);
+ q = quote_string(m);
+ putresult(TAPE_ERROR, "%s %s\n", info->handle, q);
+ log_add(L_FAIL, "%s %s %s %d %s",
+ info->hostname, qdiskname, info->timestamp,
+ info->level, q);
+ amfree(qdiskname);
+ amfree(m);
+ amfree(q);
+ return FALSE;
}
- /* XXX go to next tape if past tape size? */
+ putresult(PORT, "%d\n", port);
- return;
+ fd = stream_accept(socket, CONNECT_TIMEOUT, 0, STREAM_BUFSIZE);
- tape_error:
- /* got tape error */
- if (next_tape(1)) {
- if (syncpipe_put('T', 0) == -1) { /* next tape in place, try again */
- error("writer: Syncpipe failure during tape advance");
- /*NOTREACHED*/
- }
+ if (fd < 0) {
+ char *m, *q;
+ int save_errno = errno;
+ char *qdiskname = quote_string(info->diskname);
+ m = vstralloc("[port connect failure: ",
+ strerror(save_errno),
+ "]",
+ NULL);
+ q = quote_string(m);
+ putresult(TAPE_ERROR, "%s %s\n", info->handle, q);
+ log_add(L_FAIL, "%s %s %s %d %s",
+ info->hostname, qdiskname, info->timestamp,
+ info->level, q);
+ amfree(qdiskname);
+ aclose(socket);
+ amfree(m);
+ amfree(q);
+ return FALSE;
} else {
- if (syncpipe_put('E', 0) == -1) { /* no more tapes, fail */
- error("writer: Syncpipe failure during tape error");
- /*NOTREACHED*/
- }
- }
- if (syncpipe_putstr(errstr) == -1) {
- error("writer: Syncpipe failure putting '%s'", errstr);
- /*NOTREACHED*/
+ aclose(socket);
}
- error_ack:
- /* wait for reader to acknowledge error */
- do {
- if ((tok = syncpipe_get(&tmpint)) == -1) {
- error("writer: syncpipe failure waiting for error ack");
- /*NOTREACHED*/
- }
-
- if (tok != 'W' && tok != 'C' && tok != 'e') {
- error("writer: got '%c' unexpectedly after error", tok);
- /*NOTREACHED*/
- }
- } while (tok != 'e');
- return;
+ info->source = taper_source_new(info->handle, PORT_WRITE, NULL, fd,
+ split_diskbuffer, splitsize,
+ fallback_splitsize);
+ /* FIXME: This should be handled properly. */
+ g_assert(info->source != NULL);
+ return TRUE;
+}
- reader_buffer_snafu:
- if (syncpipe_put('x', 0) == -1) {
- error("writer: syncpipe failure putting buffer snafu");
- /*NOTREACHED*/
+typedef struct {
+ ConsumerFunctor next_consumer;
+ gpointer next_consumer_data;
+ guint64 bytes_written;
+} CountingConsumerData;
+
+/* A ConsumerFunctor. This consumer just passes its arguments on to a
+ second consumer, but counts the number of bytes successfully
+ written. */
+static ssize_t counting_consumer(gpointer user_data, queue_buffer_t * buffer) {
+ ssize_t result;
+ CountingConsumerData * data = user_data;
+
+ result = data->next_consumer(data->next_consumer_data, buffer);
+
+ if (result > 0) {
+ data->bytes_written += result;
}
- return;
+
+ return result;
}
-int
-write_buffer(
- buffer_t *bp)
-{
- ssize_t rc;
-
- assert(bp->status == FULL);
-
- startclock();
- rc = tapefd_write(tape_fd, bp->buffer, (size_t)bp->size);
- if (rc == (ssize_t)bp->size) {
-#if defined(NEED_RESETOFS)
- static double tape_used_modulus_2gb = 0;
-
- /*
- * If the next write will go over the 2 GByte boundary, reset
- * the kernel concept of where we are to make sure it does not
- * go silly on us.
- */
- tape_used_modulus_2gb += (double)rc;
- if (tape_used_modulus_2gb + (double)rc > (double)0x7fffffff) {
- tape_used_modulus_2gb = 0;
- tapefd_resetofs(tape_fd);
- }
-#endif
- wrwait = timesadd(wrwait, stopclock());
- total_writes += 1;
- total_tape_used += (off_t)rc;
- bp->status = EMPTY;
- if (interactive || bufdebug)
- dumpstatus(bp);
- if (interactive)
- fputs("W", stderr);
-
- if (bufdebug) {
- fprintf(stderr, "taper: w: put R%d\n", (int)(bp-buftable));
- fflush(stderr);
- }
- if (syncpipe_put('R', (int)(bp-buftable)) == -1) {
- error("writer: Syncpipe failure during advancing write bufffer");
- /*NOTREACHED*/
- }
- return 1;
+static gboolean boolean_prolong(void * data) {
+ if (data == NULL) {
+ return TRUE; /* Do not interrupt. */
} else {
- errstr = newvstralloc(errstr,
- "writing file: ",
- (rc != -1) ? "short write" : strerror(errno),
- NULL);
- wrwait = timesadd(wrwait, stopclock());
- if (interactive)
- fputs("[WE]", stderr);
- return 0;
+ return *(gboolean*)data;
}
}
+/* A (simpler) wrapper around taper_scan(). */
+static gboolean simple_taper_scan(taper_state_t * state,
+ gboolean* prolong, char ** error_message) {
+ char ** label = &(state->next_tape_label);
+ char ** device = &(state->next_tape_device);
+ char *timestamp = NULL;
+ int result;
+ result = taper_scan(NULL, label, ×tamp, device,
+ state->taper_scan_tracker,
+ CHAR_taperscan_output_callback,
+ error_message, boolean_prolong, prolong);
+ if (prolong != NULL && !*prolong) {
+ g_fprintf(stderr, _("Cancelled taper scan.\n"));
+ return FALSE;
+ } else if (result < 0) {
+ g_fprintf(stderr, _("Failed taper scan: %s\n"), (*error_message)?(*error_message):_("(no error message)"));
+ amfree(timestamp);
+ return FALSE;
+ } else {
+ g_fprintf(stderr, _("taper: using label `%s' date `%s'\n"), *label,
+ state->driver_start_time);
+ if (result == 3) {
+ log_add(L_INFO,
+ _("Will write new label `%s' to new tape"),
+ *label);
+ }
-static void
-cleanup(void)
-{
- REMOVE_SHARED_MEMORY();
+ }
+ amfree(timestamp);
+ return TRUE;
}
+typedef struct {
+ taper_state_t * state;
+ gboolean prolong; /* scan stops when this is FALSE. */
+ char *errmsg;
+} tape_search_request_t;
-/*
- * Cleanup shared memory segments
- */
-static void
-signal_handler(
- int signum)
-{
- log_add(L_INFO, "Received signal %d", signum);
-
- exit(1);
-}
+/* A GThread that runs taper_scan. */
+static gpointer tape_search_thread(gpointer data) {
+ tape_search_request_t * request = data;
+ if (request->state->next_tape_label != NULL &&
+ request->state->next_tape_device != NULL) {
+ return GINT_TO_POINTER(TRUE);
+ } else {
+ amfree(request->state->next_tape_label);
+ amfree(request->state->next_tape_device);
+ }
-/*
- * Installing signal handlers for signal whose default action is
- * process termination so that we can clean up shared memory
- * segments
- */
-static void
-install_signal_handlers(void)
-{
- struct sigaction act;
+ return GINT_TO_POINTER
+ (simple_taper_scan(request->state,
+ &(request->prolong),
+ &(request->errmsg)));
+}
- act.sa_handler = signal_handler;
- act.sa_flags = 0;
- sigemptyset(&act.sa_mask);
+static void log_taper_scan_errmsg(char * errmsg) {
+ char *c, *c1;
+ if (errmsg == NULL)
+ return;
+
+ c = c1 = errmsg;
+ while (*c != '\0') {
+ if (*c == '\n') {
+ *c = '\0';
+ log_add(L_WARNING,"%s", c1);
+ c1 = c+1;
+ }
+ c++;
+ }
+ if (strlen(c1) > 1 )
+ log_add(L_WARNING,"%s", c1);
+ amfree(errmsg);
+}
- signal(SIGPIPE, SIG_IGN);
+/* If handle is NULL, then this function assumes that we are in startup mode.
+ * In that case it will wait for a command from driver. If handle is not NULL,
+ * this this function will ask for permission with REQUEST-NEW-TAPE. */
+static gboolean find_new_tape(taper_state_t * state, dump_info_t * dump) {
+ GThread * tape_search = NULL;
+ tape_search_request_t search_request;
+ gboolean use_threads;
+ struct cmdargs *cmdargs;
+ cmd_t cmd;
- if (sigaction(SIGINT, &act, NULL) != 0) {
- error("taper: couldn't install SIGINT handler [%s]", strerror(errno));
- /*NOTREACHED*/
+ if (state->device != NULL) {
+ return TRUE;
}
- if (sigaction(SIGHUP, &act, NULL) != 0) {
- error("taper: couldn't install SIGHUP handler [%s]", strerror(errno));
- /*NOTREACHED*/
- }
-
- if (sigaction(SIGTERM, &act, NULL) != 0) {
- error("taper: couldn't install SIGTERM handler [%s]", strerror(errno));
- /*NOTREACHED*/
- }
+ /* We save the value here in case it changes while we're running. */
+ use_threads = g_thread_supported();
- if (sigaction(SIGUSR1, &act, NULL) != 0) {
- error("taper: couldn't install SIGUSR1 handler [%s]", strerror(errno));
- /*NOTREACHED*/
+ search_request.state = state;
+ search_request.prolong = TRUE;
+ search_request.errmsg = NULL;
+ if (use_threads) {
+ tape_search = g_thread_create(tape_search_thread,
+ &search_request, TRUE, NULL);
}
+
+ putresult(REQUEST_NEW_TAPE, "%s\n", dump->handle);
+ cmdargs = getcmd();
+ cmd = cmdargs->cmd;
- if (sigaction(SIGUSR2, &act, NULL) != 0) {
- error("taper: couldn't install SIGUSR2 handler [%s]", strerror(errno));
- /*NOTREACHED*/
+ switch (cmd) {
+ default:
+ g_fprintf(stderr, "taper: Got odd message from driver, expected NEW-TAPE or NO-NEW-TAPE.\n");
+ /* FALLTHROUGH. */
+ case NEW_TAPE: {
+ gboolean search_result;
+ if (use_threads) {
+ search_result = GPOINTER_TO_INT(g_thread_join(tape_search));
+ } else {
+ search_result =
+ GPOINTER_TO_INT(tape_search_thread(&search_request));
+ }
+ if (search_result) {
+ /* We don't say NEW_TAPE until we actually write the label. */
+ amfree(search_request.errmsg);
+ free_cmdargs(cmdargs);
+ return TRUE;
+ } else {
+ putresult(NO_NEW_TAPE, "%s\n", dump->handle);
+ log_taper_scan_errmsg(search_request.errmsg);
+ log_add(L_ERROR, "no-tape [%s]", "No more writable valid tape found");
+ free_cmdargs(cmdargs);
+ return FALSE;
+ }
}
-
- if (sigaction(SIGALRM, &act, NULL) != 0) {
- error("taper: couldn't install SIGALRM handler [%s]", strerror(errno));
- /*NOTREACHED*/
+ case NO_NEW_TAPE:
+ search_request.prolong = FALSE;
+ if (use_threads) {
+ g_thread_join(tape_search);
+ }
+ log_add(L_ERROR, "no-tape [%s]", cmdargs->argv[1]);
+ state->last_errmsg = stralloc(cmdargs->argv[1]);
+ free_cmdargs(cmdargs);
+ return FALSE;
}
+ /* NOTREACHED */
}
+/* Returns TRUE if the old volume details are not the same as the new ones. */
+static gboolean check_volume_changed(Device * device,
+ char * old_label, char * old_timestamp) {
+ /* If one is NULL and the other is not, something changed. */
+ if ((old_label == NULL) != (device->volume_label == NULL))
+ return TRUE;
+ if ((old_timestamp == NULL) != (device->volume_time == NULL))
+ return TRUE;
+ /* If details were not NULL and is now different, we have a difference. */
+ if (old_label != NULL && strcmp(old_label, device->volume_label) != 0)
+ return TRUE;
+ if (old_timestamp != NULL &&
+ strcmp(old_timestamp, device->volume_time) != 0)
+ return TRUE;
+
+ /* If we got here, everything is cool. */
+ return FALSE;
+}
-/*
- * ========================================================================
- * SHARED-MEMORY BUFFER SUBSYSTEM
- *
- */
-
-#ifdef HAVE_SYSVSHM
-
-int shmid = -1;
-
-char *
-attach_buffers(
- size_t size)
+static void
+update_tapelist(
+ taper_state_t *state)
{
- char *result;
-
- shmid = shmget(IPC_PRIVATE, size, IPC_CREAT|0700);
- if (shmid == -1) {
- return NULL;
+ char *tapelist_name = NULL;
+ char *tapelist_name_old = NULL;
+ tape_t *tp;
+ char *comment = NULL;
+
+ tapelist_name = config_dir_relative(getconf_str(CNF_TAPELIST));
+ if (state->cur_tape == 0) {
+ tapelist_name_old = stralloc2(tapelist_name, ".yesterday");
+ } else {
+ char cur_str[NUM_STR_SIZE];
+ g_snprintf(cur_str, SIZEOF(cur_str), "%d", state->cur_tape - 1);
+ tapelist_name_old = vstralloc(tapelist_name,
+ ".today.", cur_str, NULL);
}
- result = (char *)shmat(shmid, (SHM_ARG_TYPE *)NULL, 0);
-
- if (result == (char *)-1) {
- int save_errno = errno;
+ if (read_tapelist(tapelist_name) != 0) {
+ log_add(L_INFO, "pid-done %ld", (long)getpid());
+ error("could not load tapelist \"%s\"", tapelist_name);
+ /*NOTREACHED*/
+ }
- destroy_buffers();
- errno = save_errno;
- error("shmat: %s", strerror(errno));
+ /* make a copy of the tapelist file */
+ if (write_tapelist(tapelist_name_old)) {
+ log_add(L_INFO, "pid-done %ld", (long)getpid());
+ error("could not write tapelist: %s", strerror(errno));
/*NOTREACHED*/
}
+ amfree(tapelist_name_old);
- return result;
-}
+ /* get a copy of the comment, before freeing the old record */
+ tp = lookup_tapelabel(state->device->volume_label);
+ if (tp && tp->comment)
+ comment = stralloc(tp->comment);
-
-void
-detach_buffers(
- char *bufp)
-{
- if ((bufp != NULL) &&
- (shmdt((SHM_ARG_TYPE *)bufp) == -1)) {
- error("shmdt: %s", strerror(errno));
+ /* edit the tapelist and rewrite it */
+ remove_tapelabel(state->device->volume_label);
+ add_tapelabel(state->driver_start_time,
+ state->device->volume_label,
+ comment);
+ if (write_tapelist(tapelist_name)) {
+ error("could not write tapelist: %s", strerror(errno));
/*NOTREACHED*/
}
+ amfree(tapelist_name);
+ amfree(comment);
}
-void
-destroy_buffers(void)
-{
- if (shmid == -1)
- return; /* nothing to destroy */
- if (shmctl(shmid, IPC_RMID, NULL) == -1) {
- error("shmctl: %s", strerror(errno));
- /*NOTREACHED*/
+/* Find and label a new tape, if one is not already open. Returns TRUE
+ * if a tape could be written. */
+static gboolean find_and_label_new_tape(taper_state_t * state,
+ dump_info_t * dump_info) {
+ if (state->device != NULL) {
+ return TRUE;
}
-}
-
-#else
-#ifdef HAVE_MMAP
-
-#ifdef HAVE_SYS_MMAN_H
-#include <sys/mman.h>
-#endif
-
-#ifndef MAP_ANON
-# ifdef MAP_ANONYMOUS /* OSF/1-style */
-# define MAP_ANON MAP_ANONYMOUS
-# else /* SunOS4-style */
-# define MAP_ANON 0
-# define ZERO_FILE "/dev/zero"
-# endif
-#endif
-
-int shmfd = -1;
-size_t saved_size;
-
-char *
-attach_buffers(
- size_t size)
-{
- char *shmbuf;
-
-#ifdef ZERO_FILE
- shmfd = open(ZERO_FILE, O_RDWR);
- if (shmfd == -1) {
- error("attach_buffers: could not open %s: %s",
- ZERO_FILE,
- strerror(errno));
- /*NOTREACHED*/
+ state->total_bytes = 0;
+
+ if (!find_new_tape(state, dump_info)) {
+ return FALSE;
}
-#endif
-
- saved_size = size;
- shmbuf = (char *) mmap((void *) 0,
- size,
- PROT_READ|PROT_WRITE,
- MAP_ANON|MAP_SHARED,
- shmfd, 0);
- return shmbuf;
+ return label_new_tape(state, dump_info);
}
-void
-detach_buffers(
- char *bufp)
-{
- if ((bufp != NULL) &&
- (munmap((void *)bufp, saved_size) == -1)) {
- error("detach_buffers: munmap: %s", strerror(errno));
+static gboolean label_new_tape(taper_state_t * state, dump_info_t * dump_info) {
+ char *old_volume_name = NULL;
+ char *old_volume_time = NULL;
+ tape_search_request_t request;
+ gboolean search_result;
+ DeviceStatusFlags status;
+
+ /* If we got here, it means that we have found a tape to label and
+ * have gotten permission from the driver to write it. But we
+ * still can say NO-NEW-TAPE if a problem shows up, and must still
+ * say NEW-TAPE if one doesn't. */
+
+ amfree(state->last_errmsg);
+ state->device = device_open(state->next_tape_device);
+ g_assert(state->device != NULL);
+ amfree(state->next_tape_device);
+
+ if (state->device->status != DEVICE_STATUS_SUCCESS)
+ goto skip_volume;
+
+ if (!device_configure(state->device, TRUE))
+ goto skip_volume;
+
+ /* if we have an error, and are sure it isn't just an unlabeled volume,
+ * then skip this volume */
+ status = device_read_label(state->device);
+ if ((status & ~DEVICE_STATUS_VOLUME_UNLABELED) &&
+ !(status & DEVICE_STATUS_VOLUME_UNLABELED))
+ goto skip_volume;
+
+ old_volume_name = g_strdup(state->device->volume_label);
+ old_volume_time = g_strdup(state->device->volume_time);
+
+ if (!device_start(state->device, ACCESS_WRITE, state->next_tape_label,
+ state->driver_start_time)) {
+ gboolean tape_used;
+
+ /* Something broke, see if we can tell if the volume was erased or
+ * not. */
+ g_fprintf(stderr, "taper: Error writing label %s to device %s: %s.\n",
+ state->next_tape_label, state->device->device_name,
+ device_error_or_status(state->device));
+
+ if (!device_finish(state->device))
+ goto request_new_volume;
+
+ /* This time, if we can't read the label, assume we've overwritten
+ * the volume or otherwise corrupted it */
+ status = device_read_label(state->device);
+ if ((status & ~DEVICE_STATUS_VOLUME_UNLABELED) &&
+ !(status & DEVICE_STATUS_VOLUME_UNLABELED))
+ goto request_new_volume;
+
+ tape_used = check_volume_changed(state->device, old_volume_name,
+ old_volume_time);
+
+ if (tape_used)
+ goto request_new_volume;
+ else
+ goto skip_volume;
+ }
+
+ amfree(old_volume_name);
+ amfree(old_volume_time);
+ amfree(state->next_tape_label);
+
+ update_tapelist(state);
+ state->cur_tape++;
+
+ if (state->have_changer &&
+ changer_label("UNKNOWN", state->device->volume_label) != 0) {
+ error(_("couldn't update barcode database"));
/*NOTREACHED*/
}
- if (shmfd != -1)
- aclose(shmfd);
-}
+ log_add(L_START, "datestamp %s label %s tape %d",
+ state->driver_start_time, state->device->volume_label,
+ state->cur_tape);
+ putresult(NEW_TAPE, "%s %s\n", dump_info->handle,
+ state->device->volume_label);
+
+ return TRUE;
+
+request_new_volume:
+ /* Tell the driver we overwrote this volume, even if it was empty, and request
+ * a new volume. */
+ if (state->device)
+ state->last_errmsg = newstralloc(state->last_errmsg, device_error_or_status(state->device));
+ else
+ state->last_errmsg = newstralloc(state->last_errmsg, "(unknown)");
+
+ putresult(NEW_TAPE, "%s %s\n", dump_info->handle,
+ state->next_tape_label);
+ if (old_volume_name) {
+ log_add(L_WARNING, "Problem writing label '%s' to volume %s "
+ "(volume may be erased): %s\n",
+ state->next_tape_label, old_volume_name,
+ state->last_errmsg);
+ } else {
+ log_add(L_WARNING, "Problem writing label '%s' to new volume "
+ "(volume may be erased): %s\n", state->next_tape_label,
+ state->last_errmsg);
+ }
-void
-destroy_buffers(void)
-{
-}
+ if (state->device) {
+ g_object_unref(state->device);
+ state->device = NULL;
+ }
-#else
-#error: must define either HAVE_SYSVSHM or HAVE_MMAP!
-#endif
-#endif
+ amfree(state->next_tape_label);
+ amfree(old_volume_name);
+ amfree(old_volume_time);
+ return find_and_label_new_tape(state, dump_info);
+skip_volume:
+ /* grab a new volume without talking to the driver again -- we do this if we're
+ * confident we didn't overwrite the last tape we got. */
+ if (state->device)
+ state->last_errmsg = newstralloc(state->last_errmsg, device_error_or_status(state->device));
+ else
+ state->last_errmsg = newstralloc(state->last_errmsg, "(unknown)");
-/*
- * ========================================================================
- * SYNC-PIPE SUBSYSTEM
- *
- */
+ if (old_volume_name) {
+ log_add(L_WARNING, "Problem writing label '%s' to volume '%s' "
+ "(old volume data intact): %s\n",
+ state->next_tape_label, old_volume_name, state->last_errmsg);
+ } else {
+ log_add(L_WARNING, "Problem writing label '%s' to new volume "
+ "(old volume data intact): %s\n", state->next_tape_label,
+ state->last_errmsg);
+ }
-int getpipe, putpipe;
+ if (state->device) {
+ g_object_unref(state->device);
+ state->device = NULL;
+ }
-void
-syncpipe_init(
- int rd,
- int wr)
-{
- getpipe = rd;
- putpipe = wr;
-}
+ amfree(state->next_tape_label);
+ amfree(old_volume_name);
+ amfree(old_volume_time);
-void
-syncpipe_read_error(
- ssize_t rc,
- ssize_t expected)
-{
- char buf[sizeof(char) + sizeof(int)];
-
- if (rc == 0) {
- dbprintf(("syncpipe_get %s halting: Unexpected read EOF\n", procname));
- fprintf(stderr, "syncpipe_get %s halting: Unexpected read EOF\n", procname);
- } else if (rc < 0) {
- dbprintf(("syncpipe_get %s halting: Read error - %s\n",
- procname, strerror(errno)));
- fprintf(stderr, "syncpipe_get %s halting: Read error - %s\n",
- procname, strerror(errno));
+ request.state = state;
+ request.prolong = TRUE;
+ request.errmsg = NULL;
+ search_result = GPOINTER_TO_INT(tape_search_thread(&request));
+ if (search_result) {
+ amfree(request.errmsg);
+ return label_new_tape(state, dump_info);
} else {
- dbprintf(("syncpipe_get %s halting: Read "
- SSIZE_T_FMT " bytes short of " SSIZE_T_FMT "\n",
- procname, (SSIZE_T_FMT_TYPE)(rc - expected),
- (SSIZE_T_FMT_TYPE)expected));
- fprintf(stderr, "syncpipe_get %s halting: Read "
- SSIZE_T_FMT " bytes short of " SSIZE_T_FMT "\n",
- procname, (SSIZE_T_FMT_TYPE)(rc - expected),
- (SSIZE_T_FMT_TYPE)expected);
+ /* Problem finding a new tape! */
+ log_taper_scan_errmsg(request.errmsg);
+ putresult(NO_NEW_TAPE, "%s\n", dump_info->handle);
+ return FALSE;
}
- /* Halt the other side if it's still alive */
- buf[0] = 'H';
- memset(&buf[1], 0, SIZEOF(int));
- if (write(putpipe, buf, SIZEOF(buf)))
- return;
}
-void
-syncpipe_write_error(
- ssize_t rc,
- ssize_t expected)
-{
- char buf[sizeof(char) + sizeof(int)];
-
- if (rc == 0) { /* EOF */
- dbprintf(("syncpipe %s halting: Write EOF\n", procname));
- fprintf(stderr, "syncpipe %s halting: Write EOF\n", procname);
- } else if (rc < 0) {
- dbprintf(("syncpipe %s halting: Write error - %s\n",
- procname, strerror(errno)));
- fprintf(stderr, "syncpipe %s halting: Write error - %s\n",
- procname, strerror(errno));
+/* Find out if the dump is PARTIAL or not, and set the proper driver
+ and logfile tags for the dump. */
+static void find_completion_tags(dump_info_t * dump_info, /* IN */
+ cmd_t * result_cmd, /* OUT */
+ logtype_t * result_log /* OUT */) {
+ /* result_cmd is always DONE because the taper wrote all the input to
+ * the output. driver need to know if the taper completed its job.
+ * result_log is set to L_PARTIAL if the image is partial, the log
+ * must tell if the image is partial or complete.
+ */
+
+ if (taper_source_is_partial(dump_info->source)) {
+ *result_cmd = DONE;
+ *result_log = L_PARTIAL;
} else {
- dbprintf(("syncpipe %s halting: Write "
- SSIZE_T_FMT " bytes short of " SSIZE_T_FMT "\n",
- procname, (SSIZE_T_FMT_TYPE)(rc - expected),
- (SSIZE_T_FMT_TYPE)expected));
- fprintf(stderr, "syncpipe %s halting: Write "
- SSIZE_T_FMT " bytes short of " SSIZE_T_FMT "\n",
- procname, (SSIZE_T_FMT_TYPE)(rc - expected),
- (SSIZE_T_FMT_TYPE)expected);
+ *result_cmd = DONE;
+ *result_log = L_DONE;
}
- /* Halt the other side if it's still alive */
- buf[0] = 'H';
- memset(&buf[1], 0, SIZEOF(int));
- if (write(putpipe, buf, SIZEOF(buf)))
- return;
}
-int
-syncpipe_get(
- int *intp)
-{
- ssize_t rc;
- char buf[SIZEOF(char) + SIZEOF(int)];
-
- memset(buf, 0, sizeof(buf));
- rc = fullread(getpipe, buf, SIZEOF(buf));
- if (rc != (ssize_t)sizeof(buf)) {
- syncpipe_read_error(rc, (ssize_t)sizeof(buf));
- return (-1);
- }
-
- if (bufdebug && *buf != 'R' && *buf != 'W') {
- fprintf(stderr,"taper: %c: getc %c\n", *procname, *buf);
- fflush(stderr);
- }
-
- memcpy(intp, &buf[1], SIZEOF(int));
- return (int)buf[0];
+/* Put an L_PARTIAL message to the logfile. */
+static void put_partial_log(dump_info_t * dump_info, double dump_time,
+ guint64 dump_kbytes, char *errstr) {
+ char * qdiskname = quote_string(dump_info->diskname);
+
+ log_add(L_PARTIAL, "%s %s %s %d %d [sec %f kb %ju kps %f] %s",
+ dump_info->hostname, qdiskname, dump_info->timestamp,
+ dump_info->current_part, dump_info->level, dump_time,
+ (uintmax_t)dump_kbytes, dump_kbytes / dump_time,
+ errstr);
+ amfree(qdiskname);
}
-int
-syncpipe_getint(void)
-{
- ssize_t rc;
- int i = 0;
+/* Figure out what to do after a part attempt. Returns TRUE if another
+ attempt should proceed for this dump; FALSE if we are done. */
+static gboolean finish_part_attempt(taper_state_t * taper_state,
+ dump_info_t * dump_info,
+ queue_result_flags queue_result,
+ GTimeVal run_time, guint64 run_bytes) {
+ double part_time = g_timeval_to_double(run_time);
+ guint64 part_kbytes = run_bytes / 1024;
+ double part_kbps = run_bytes / (1024 * part_time);
+
+ char * qdiskname = quote_string(dump_info->diskname);
+
+ if (queue_result == QUEUE_SUCCESS) {
+ dump_info->total_time = timesadd(run_time, dump_info->total_time);
+ dump_info->total_bytes += run_bytes;
+
+ log_add(L_PART, "%s %d %s %s %s %d/%d %d [sec %f kb %ju kps %f]",
+ taper_state->device->volume_label,
+ taper_state->device->file, dump_info->hostname, qdiskname,
+ dump_info->timestamp, dump_info->current_part,
+ taper_source_predict_parts(dump_info->source),
+ dump_info->level, part_time, (uintmax_t)part_kbytes, part_kbps);
+ putresult(PARTDONE, "%s %s %d %ju \"[sec %f kb %ju kps %f]\"\n",
+ dump_info->handle, taper_state->device->volume_label,
+ taper_state->device->file, (uintmax_t)part_kbytes, part_time,
+ (uintmax_t)part_kbytes, part_kbps);
+ taper_state->total_bytes += run_bytes;
+
+ if (taper_source_get_end_of_data(dump_info->source)) {
+ cmd_t result_cmd;
+ logtype_t result_log;
+ double dump_time = g_timeval_to_double(dump_info->total_time);
+ guint64 dump_kbytes = dump_info->total_bytes / 1024;
+ double dump_kbps = dump_info->total_bytes / (1024 * dump_time);
+
+ find_completion_tags(dump_info, &result_cmd, &result_log);
+
+ g_object_unref(dump_info->source);
+ dump_info->source = NULL;
+
+ log_add(result_log, "%s %s %s %d %d [sec %f kb %ju kps %f]",
+ dump_info->hostname, qdiskname, dump_info->timestamp,
+ dump_info->current_part, dump_info->level, dump_time,
+ (uintmax_t)dump_kbytes, dump_kbps);
+ putresult(result_cmd, "%s INPUT-GOOD TAPE-GOOD "
+ "\"[sec %f kb %ju kps %f]\" \"\" \"\"\n",
+ dump_info->handle, dump_time, (uintmax_t)dump_kbytes,
+ dump_kbps);
+
+ amfree(qdiskname);
+ return FALSE;
+ } else if (taper_source_get_end_of_part(dump_info->source)) {
+ taper_source_start_new_part(dump_info->source);
+ dump_info->current_part ++;
+ amfree(qdiskname);
+ return TRUE;
+ }
+ /* If we didn't read EOF or EOP, then an error
+ occured. But we read QUEUE_SUCCESS, so something is
+ b0rked. */
+ g_assert_not_reached();
+ } else {
+ char * volume_label = strdup(taper_state->device->volume_label);
+ int file_number = taper_state->device->file;
+ double dump_time, dump_kbps;
+ guint64 dump_kbytes;
+ char *producer_errstr = quote_string(
+ taper_source_get_errmsg(dump_info->source));
+ char *consumer_errstr = quote_string(
+ device_error(taper_state->device));
+
+ log_add(L_PARTPARTIAL,
+ "%s %d %s %s %s %d/%d %d [sec %f kb %ju kps %f] %s",
+ volume_label, file_number, dump_info->hostname, qdiskname,
+ dump_info->timestamp, dump_info->current_part,
+ taper_source_predict_parts(dump_info->source),
+ dump_info->level, part_time, (uintmax_t)part_kbytes, part_kbps,
+ consumer_errstr);
+ log_add(L_INFO, "tape %s kb %lld fm %d [OK]\n",
+ volume_label,
+ (long long)((taper_state->total_bytes+(off_t)1023) / (off_t)1024),
+ taper_state->device->file);
+
+ /* A problem occured. */
+ if (queue_result & QUEUE_CONSUMER_ERROR) {
+ /* Make a note if this was EOM (we treat EOM the same as any other error,
+ * so it's just for debugging purposes */
+ if (taper_state->device->is_eof)
+ g_debug("device %s ran out of space", taper_state->device->device_name);
+
+ /* Close the device. */
+ device_finish(taper_state->device);
+ g_object_unref(taper_state->device);
+ taper_state->device = NULL;
+ }
+
+ amfree(volume_label);
+
+ if ((queue_result & QUEUE_CONSUMER_ERROR) &&
+ (!(queue_result & QUEUE_PRODUCER_ERROR)) &&
+ taper_source_seek_to_part_start(dump_info->source)) {
+ /* It is recoverable. */
+ log_add(L_INFO, "Will request retry of failed split part.");
+ if (find_and_label_new_tape(taper_state, dump_info)) {
+ /* dump_info->current_part is unchanged. */
+ amfree(qdiskname);
+ return TRUE;
+ }
+ }
- rc = fullread(getpipe, &i, SIZEOF(i));
- if (rc != (ssize_t)sizeof(i)) {
- syncpipe_read_error(rc, (ssize_t)sizeof(i));
- return (-1);
+ dump_info->total_time = timesadd(run_time, dump_info->total_time);
+ dump_info->total_bytes += run_bytes;
+ dump_time = g_timeval_to_double(dump_info->total_time);
+ dump_kbytes = dump_info->total_bytes / 1024;
+ dump_kbps = dump_info->total_bytes / (1024 * dump_time);
+
+ putresult(PARTIAL,
+ "%s INPUT-%s TAPE-%s "
+ "\"[sec %f kb %ju kps %f]\" %s %s\n",
+ dump_info->handle,
+ (queue_result & QUEUE_PRODUCER_ERROR) ? "ERROR" : "GOOD",
+ (queue_result & QUEUE_CONSUMER_ERROR) ? "ERROR" : "GOOD",
+ dump_time, (uintmax_t)dump_kbytes, dump_kbps,
+ producer_errstr, consumer_errstr);
+ if (queue_result & QUEUE_CONSUMER_ERROR) {
+ put_partial_log(dump_info, dump_time, dump_kbytes,
+ consumer_errstr);
+ } else {
+ put_partial_log(dump_info, dump_time, dump_kbytes,
+ producer_errstr);
+ }
+ amfree(producer_errstr);
+ amfree(consumer_errstr);
}
- return (i);
+ amfree(qdiskname);
+ return FALSE;
}
+/* Generate the actual header structure to write to tape. This means dropping
+ * bits related to the holding disk, and adding bits for split dumps. */
+static dumpfile_t * munge_headers(dump_info_t * dump_info) {
+ dumpfile_t * rval;
+ int expected_splits;
+
+ rval = taper_source_get_first_header(dump_info->source);
-char *
-syncpipe_getstr(void)
-{
- ssize_t rc;
- int len;
- char *str;
-
- if ((len = syncpipe_getint()) <= 0) {
- fprintf(stderr, "syncpipe %s halting: Protocol error - "
- "Invalid string length (%d)\n", procname, len);
- syncpipe_put('H', 0); /* Halt the other side */
- exit(1);
- /*NOTREACHED*/
+ if (rval == NULL) {
+ return NULL;
}
- str = alloc((size_t)len);
-
- rc = fullread(getpipe, str, (size_t)len);
- if (rc != (ssize_t)len) {
- syncpipe_read_error(rc, (ssize_t)len);
- return (NULL);
- }
- return (str);
-}
+ rval->cont_filename[0] = '\0';
+ expected_splits = taper_source_predict_parts(dump_info->source);
-int
-syncpipe_put(
- int chi,
- int intval)
-{
- char buf[sizeof(char) + sizeof(int)];
- ssize_t rc;
-
- buf[0] = (char)chi;
- memcpy(&buf[1], &intval, SIZEOF(int));
- if (bufdebug && buf[0] != 'R' && buf[0] != 'W') {
- fprintf(stderr,"taper: %c: putc %c\n",*procname,buf[0]);
- fflush(stderr);
+ if (expected_splits != 1) {
+ rval->type = F_SPLIT_DUMPFILE;
+ rval->partnum = dump_info->current_part;
+ rval->totalparts = expected_splits;
}
- rc = fullwrite(putpipe, buf, SIZEOF(buf));
- if (rc != (ssize_t)sizeof(buf)) {
- syncpipe_write_error(rc, (ssize_t)sizeof(buf));
- return (-1);
- }
- return (0);
+ return rval;
}
-int
-syncpipe_putint(
- int i)
+/* We call this when we can't find a tape to write data to. This could
+ happen with the first (or only) part of a file, but it could also
+ happen with an intermediate part of a split dump. dump_bytes
+ is 0 if this is the first part of a dump. */
+static void bail_no_volume(
+ dump_info_t *dump_info,
+ char *errmsg)
{
- ssize_t rc;
-
- rc = fullwrite(putpipe, &i, SIZEOF(i));
- if (rc != (ssize_t)sizeof(i)) {
- syncpipe_write_error(rc, (ssize_t)sizeof(i));
- return (-1);
- /* NOTREACHED */
- }
- return (0);
+ char *errstr;
+ if (errmsg)
+ errstr = quote_string(errmsg);
+ else
+ errstr = quote_string("no new tape");
+ if (dump_info->total_bytes > 0) {
+ /* Second or later part of a split dump, so PARTIAL message. */
+ double dump_time = g_timeval_to_double(dump_info->total_time);
+ guint64 dump_kbytes = dump_info->total_bytes / 1024;
+ double dump_kbps = dump_kbytes / dump_time;
+ putresult(PARTIAL,
+ "%s INPUT-GOOD TAPE-ERROR "
+ "\"[sec %f kb %ju kps %f]\" \"\" %s\n",
+ dump_info->handle,
+ dump_time, (uintmax_t)dump_kbytes, dump_kbps, errstr);
+ put_partial_log(dump_info, dump_time, dump_kbytes, errstr);
+ } else {
+ char * qdiskname = quote_string(dump_info->diskname);
+ putresult(FAILED,
+ "%s INPUT-GOOD TAPE-ERROR \"\" %s\n",
+ dump_info->handle, errstr);
+ log_add(L_FAIL, "%s %s %s %d %s",
+ dump_info->hostname, qdiskname, dump_info->timestamp,
+ dump_info->level, errstr);
+ amfree(qdiskname);
+ }
+ amfree(errstr);
}
-int
-syncpipe_putstr(
- const char *str)
-{
- ssize_t n, rc;
+/* Link up the TaperSource with the Device, including retries etc. */
+static void run_device_output(taper_state_t * taper_state,
+ dump_info_t * dump_info) {
+ GValue val;
+ guint file_number;
+ dump_info->current_part = 1;
+ dump_info->total_time.tv_sec = 0;
+ dump_info->total_time.tv_usec = 0;
+ dump_info->total_bytes = 0;
+
+ for (;;) {
+ GTimeVal start_time, end_time, run_time;
+ StreamingRequirement streaming_mode;
+ queue_result_flags queue_result;
+ CountingConsumerData consumer_data;
+ dumpfile_t *this_header;
+ size_t max_memory;
+
+ this_header = munge_headers(dump_info);
+ if (this_header == NULL) {
+ char * qdiskname = quote_string(dump_info->diskname);
+ char * errstr = taper_source_get_errmsg(dump_info->source);
+ if (!errstr)
+ errstr = "Failed reading dump header.";
+ errstr = quote_string(errstr);
+ putresult(FAILED,
+ "%s INPUT-ERROR TAPE-GOOD %s \"\"\n",
+ dump_info->handle, errstr);
+ log_add(L_FAIL, "%s %s %s %d %s",
+ dump_info->hostname, qdiskname, dump_info->timestamp,
+ dump_info->level, errstr);
+ amfree(qdiskname);
+ amfree(errstr);
+ return;
+ }
+
+ if (!find_and_label_new_tape(taper_state, dump_info)) {
+ bail_no_volume(dump_info, taper_state->last_errmsg);
+ dumpfile_free(this_header);
+ return;
+ }
+
+ while (!device_start_file(taper_state->device, this_header)) {
+ /* Close the device. */
+ device_finish(taper_state->device);
+ g_object_unref(taper_state->device);
+ taper_state->device = NULL;
+
+ if (!find_and_label_new_tape(taper_state, dump_info)) {
+ bail_no_volume(dump_info, taper_state->last_errmsg);
+ dumpfile_free(this_header);
+ return;
+ }
+ }
+ dumpfile_free(this_header);
+
+ bzero(&val, sizeof(val));
+ if (!device_property_get(taper_state->device, PROPERTY_STREAMING, &val)
+ || !G_VALUE_HOLDS(&val, STREAMING_REQUIREMENT_TYPE)) {
+ g_fprintf(stderr, "taper: Couldn't get streaming type!\n");
+ streaming_mode = STREAMING_REQUIREMENT_REQUIRED;
+ } else {
+ streaming_mode = g_value_get_enum(&val);
+ }
+
+ file_number = taper_state->device->file;
+
+ consumer_data.next_consumer = device_write_consumer;
+ consumer_data.next_consumer_data = taper_state->device;
+ consumer_data.bytes_written = 0;
+
+ g_get_current_time(&start_time);
+
+ if (getconf_seen(CNF_DEVICE_OUTPUT_BUFFER_SIZE)) {
+ max_memory = getconf_size(CNF_DEVICE_OUTPUT_BUFFER_SIZE);
+ if (getconf_seen(CNF_TAPEBUFS)) {
+ g_fprintf(stderr,
+ "Configuration directives 'device_output_buffer_size' "
+ "and \n"
+ "'tapebufs' are incompatible; using former.\n");
+ }
+ } else if (getconf_seen(CNF_TAPEBUFS)) {
+ max_memory = getconf_int(CNF_TAPEBUFS) *
+ taper_state->device->block_size;
+ } else {
+ /* Use default. */
+ max_memory = getconf_size(CNF_DEVICE_OUTPUT_BUFFER_SIZE);
+ }
+
+ queue_result = do_consumer_producer_queue_full
+ (taper_source_producer,
+ dump_info->source,
+ counting_consumer,
+ &consumer_data,
+ taper_state->device->block_size, max_memory,
+ streaming_mode);
- if(!str)
- str = "UNKNOWN syncpipe_putstr STRING";
+ g_get_current_time(&end_time);
+ run_time = timesub(end_time, start_time);
- n = (ssize_t)strlen(str) + 1; /* send '\0' as well */
- syncpipe_putint((int)n);
+ /* The device_write_consumer leaves the file open, so close it now. */
+ if (!device_finish_file(taper_state->device)) {
+ queue_result = queue_result | QUEUE_CONSUMER_ERROR;
+ }
- rc = fullwrite(putpipe, str, (size_t)n);
- if (rc != n) {
- syncpipe_write_error(rc, n);
- return (-1);
+ if (!finish_part_attempt(taper_state, dump_info, queue_result,
+ run_time, consumer_data.bytes_written)) {
+ break;
+ }
}
- return (0);
}
-\f
-/*
- * ========================================================================
- * TAPE MANIPULATION SUBSYSTEM
- *
- */
-int label_tape(void);
-
-/* local functions */
-
-/* return 0 on success */
-/* return 1 on error and set errstr */
-int
-label_tape(void)
-{
- char *conf_tapelist_old = NULL;
- char *result;
- static int first_call = 1;
- char *timestamp;
- char *error_msg = NULL;
- char *s, *r;
- int slot = -1;
-
- amfree(label);
- amfree(tapedev);
- if (taper_scan(NULL, &label, ×tamp, &tapedev, CHAR_taperscan_output_callback, &error_msg) < 0) {
- fprintf(stderr, "%s\n", error_msg);
- errstr = error_msg;
- error_msg = NULL;
- amfree(timestamp);
- return 0;
+
+/* Handle a PORT_WRITE command. */
+static void process_port_write(taper_state_t * state,
+ struct cmdargs * cmdargs) {
+ dump_info_t dump_state;
+ guint64 splitsize;
+ guint64 fallback_splitsize;
+ char * split_diskbuffer;
+ char * argnames[] = {"command", /* 0 */
+ "handle", /* 1 */
+ "hostname", /* 2 */
+ "diskname", /* 3 */
+ "level", /* 4 */
+ "datestamp", /* 5 */
+ "splitsize", /* 6 */
+ "split_diskbuffer", /* 7 */
+ "fallback_splitsize", /* 8 */
+ NULL };
+
+ validate_args(cmdargs, argnames);
+
+ dump_state.handle = g_strdup(cmdargs->argv[1]);
+ dump_state.hostname = g_strdup(cmdargs->argv[2]);
+ dump_state.diskname = g_strdup(cmdargs->argv[3]);
+
+ errno = 0;
+ dump_state.level = strtol(cmdargs->argv[4], NULL, 10);
+ if (errno != 0) {
+ error("error [taper PORT-WRITE: Invalid dump level %s]",
+ cmdargs->argv[4]);
+ g_assert_not_reached();
}
- amfree(timestamp);
- if(error_msg) {
- s = error_msg; r = NULL;
- while((s=strstr(s,"slot "))) { s += 5; r=s; };
- if(r) {
- slot = atoi(r);
- }
- amfree(error_msg);
+
+ dump_state.timestamp = strdup(cmdargs->argv[5]);
+
+ errno = 0;
+ splitsize = g_ascii_strtoull(cmdargs->argv[6], NULL, 10);
+ if (errno != 0) {
+ error("error [taper PORT-WRITE: Invalid splitsize %s]",
+ cmdargs->argv[6]);
+ g_assert_not_reached();
}
- if ((tape_fd = tape_open(tapedev, O_WRONLY)) == -1) {
- if (errno == EACCES) {
- errstr = newstralloc(errstr,
- "writing label: tape is write protected");
- } else {
- errstr = newstralloc2(errstr,
- "writing label: ", strerror(errno));
- }
- return 0;
+
+ if (strcmp(cmdargs->argv[7], "NULL") == 0) {
+ split_diskbuffer = NULL;
+ } else {
+ split_diskbuffer = g_strdup(cmdargs->argv[7]);
}
-
- tapefd_setinfo_length(tape_fd, tapetype_get_length(tt));
-
- tapefd_setinfo_datestamp(tape_fd, taper_timestamp);
- tapefd_setinfo_disk(tape_fd, label);
- result = tapefd_wrlabel(tape_fd, taper_timestamp, label, tt_blocksize);
- if (result != NULL) {
- errstr = newstralloc(errstr, result);
- return 0;
+
+ errno = 0;
+ fallback_splitsize = g_ascii_strtoull(cmdargs->argv[8], NULL, 10);
+ if (errno != 0) {
+ error("error [taper PORT-WRITE: Invalid fallback_splitsize %s]",
+ cmdargs->argv[8]);
+ g_assert_not_reached();
}
- if(slot > -1) {
- fprintf(stderr, "taper: slot: %d wrote label `%s' date `%s'\n", slot,
- label, taper_timestamp);
- }
- else {
- fprintf(stderr, "taper: wrote label `%s' date `%s'\n", label,
- taper_timestamp);
+ dump_state.id_string = g_strdup_printf("%s:%s.%d", dump_state.hostname,
+ dump_state.diskname,
+ dump_state.level);
+
+ if (!open_read_socket(&dump_state, split_diskbuffer, splitsize,
+ fallback_splitsize)) {
+ free(split_diskbuffer);
+ return;
}
- fflush(stderr);
-
-#ifdef HAVE_LIBVTBLC
- /* store time for the first volume entry */
- time(&raw_time);
- tape_timep = localtime(&raw_time);
- strftime(start_datestr, 20, "%T %D", tape_timep);
- fprintf(stderr, "taper: got vtbl start time: %s\n", start_datestr);
- fflush(stderr);
-#endif /* HAVE_LIBVTBLC */
-
- if (strcmp(label, FAKE_LABEL) != 0) {
- if (cur_tape == 0) {
- conf_tapelist_old = stralloc2(conf_tapelist, ".yesterday");
- } else {
- char cur_str[NUM_STR_SIZE];
+ free(split_diskbuffer);
- snprintf(cur_str, SIZEOF(cur_str), "%d", cur_tape - 1);
- conf_tapelist_old = vstralloc(conf_tapelist,
- ".today.", cur_str, NULL);
- }
+ run_device_output(state, &dump_state);
- if (write_tapelist(conf_tapelist_old)) {
- error("could not write tapelist: %s", strerror(errno));
- /*NOTREACHED*/
- }
- amfree(conf_tapelist_old);
+ free_dump_info(&dump_state);
+}
- remove_tapelabel(label);
- add_tapelabel(taper_timestamp, label);
- if (write_tapelist(conf_tapelist)) {
- error("could not write tapelist: %s", strerror(errno));
- /*NOTREACHED*/
- }
+/* Handle a FILE_WRITE command. */
+static void process_file_write(taper_state_t * state,
+ struct cmdargs * cmdargs) {
+ dump_info_t dump_state;
+ char * holding_disk_file;
+ guint64 splitsize;
+ char * argnames[] = {"command", /* 0 */
+ "handle", /* 1 */
+ "filename", /* 2 */
+ "hostname", /* 3 */
+ "diskname", /* 4 */
+ "level", /* 5 */
+ "datestamp", /* 6 */
+ "splitsize", /* 7 */
+ NULL };
+
+ validate_args(cmdargs, argnames);
+
+ dump_state.handle = g_strdup(cmdargs->argv[1]);
+ holding_disk_file = g_strdup(cmdargs->argv[2]);
+ dump_state.hostname = g_strdup(cmdargs->argv[3]);
+ dump_state.diskname = g_strdup(cmdargs->argv[4]);
+
+ errno = 0;
+ dump_state.level = strtol(cmdargs->argv[5], NULL, 10);
+ if (errno != 0) {
+ error("error [taper FILE-WRITE: Invalid dump level %s]",
+ cmdargs->argv[5]);
+ g_assert_not_reached();
}
+
+ dump_state.timestamp = strdup(cmdargs->argv[6]);
- log_add(L_START, "datestamp %s label %s tape %d",
- taper_timestamp, label, cur_tape);
- if (first_call && strcmp(label, FAKE_LABEL) == 0) {
- first_call = 0;
- log_add(L_WARNING, "tapedev is %s, dumps will be thrown away", tapedev);
+ errno = 0;
+ splitsize = g_ascii_strtoull(cmdargs->argv[7], NULL, 10);
+ if (errno != 0) {
+ error("error [taper FILE-WRITE: Invalid splitsize %s]",
+ cmdargs->argv[7]);
+ g_assert_not_reached();
}
- total_tape_used=(off_t)0;
- total_tape_fm = 0;
+ dump_state.id_string = g_strdup_printf("%s:%s.%d", dump_state.hostname,
+ dump_state.diskname,
+ dump_state.level);
+
+ dump_state.source = taper_source_new(dump_state.handle, FILE_WRITE,
+ holding_disk_file, -1,
+ NULL, splitsize, -1);
+ /* FIXME: This should be handled properly. */
+ g_assert(dump_state.source != NULL);
+
+ run_device_output(state, &dump_state);
- return 1;
+ free_dump_info(&dump_state);
+ amfree(holding_disk_file);
}
-/* return 0 on error and set errstr */
-/* return 1 on success */
-int
-first_tape(
- char *new_datestamp)
-{
- if ((have_changer = changer_init()) < 0) {
- error("changer initialization failed: %s", strerror(errno));
- /*NOTREACHED*/
+/* Send QUITTING message to driver and associated logging. Always
+ returns false. */
+static gboolean send_quitting(taper_state_t * state) {
+ putresult(QUITTING, "\n");
+ g_fprintf(stderr,"taper: DONE\n");
+ cleanup(state);
+ return FALSE;
+}
+
+/* This function recieves the START_TAPER command from driver, and
+ returns the attached timestamp. */
+static gboolean find_first_tape(taper_state_t * state) {
+ struct cmdargs *cmdargs;
+ tape_search_request_t search_request;
+ GThread * tape_search = NULL;
+ gboolean use_threads;
+
+ /* We save the value here in case it changes while we're running. */
+ use_threads = g_thread_supported();
+
+ search_request.state = state;
+ search_request.prolong = TRUE;
+ search_request.errmsg = NULL;
+
+ if (use_threads) {
+ tape_search = g_thread_create(tape_search_thread,
+ &search_request, TRUE, NULL);
+ }
+
+ cmdargs = getcmd();
+
+ switch (cmdargs->cmd) {
+ case START_TAPER: {
+ gboolean search_result;
+ state->driver_start_time = strdup(cmdargs->argv[1]);
+ if (use_threads) {
+ search_result = GPOINTER_TO_INT(g_thread_join(tape_search));
+ } else {
+ search_result =
+ GPOINTER_TO_INT(tape_search_thread(&search_request));
+ }
+ if (search_result) {
+ putresult(TAPER_OK, "\n");
+ } else {
+ putresult(TAPE_ERROR, "Could not find a tape to use.\n");
+ log_add(L_ERROR, "no-tape [%s]", "Could not find a tape to use");
+ if (search_request.errmsg != NULL) {
+ char *c, *c1;
+ c = c1 = search_request.errmsg;
+ while (*c != '\0') {
+ if (*c == '\n') {
+ *c = '\0';
+ log_add(L_WARNING,"%s", c1);
+ c1 = c+1;
+ }
+ c++;
+ }
+ if (strlen(c1) > 1 )
+ log_add(L_WARNING,"%s", c1);
+ }
+ }
+ amfree(search_request.errmsg);
+ free_cmdargs(cmdargs);
+ return TRUE;
+ }
+ case QUIT:
+ search_request.prolong = FALSE;
+ if (use_threads) {
+ g_thread_join(tape_search);
+ }
+ free_cmdargs(cmdargs);
+ return send_quitting(state);
+ default:
+ error("error [file_reader_side cmd %d argc %d]", cmdargs->cmd, cmdargs->argc);
}
- changer_debug = 1;
- taper_timestamp = newstralloc(taper_timestamp, new_datestamp);
+ g_assert_not_reached();
+}
- if (!label_tape())
- return 0;
+/* In running mode (not startup mode), get a command from driver and
+ deal with it. */
+static gboolean process_driver_command(taper_state_t * state) {
+ struct cmdargs *cmdargs;
+ char * q;
+
+ /* This will return QUIT if driver has died. */
+ cmdargs = getcmd();
+ switch (cmdargs->cmd) {
+ case PORT_WRITE:
+ /*
+ * PORT-WRITE
+ * handle
+ * hostname
+ * features
+ * diskname
+ * level
+ * datestamp
+ * splitsize
+ * split_diskbuffer
+ */
+ process_port_write(state, cmdargs);
+ break;
+
+ case FILE_WRITE:
+ /*
+ * FILE-WRITE
+ * handle
+ * filename
+ * hostname
+ * features
+ * diskname
+ * level
+ * datestamp
+ * splitsize
+ */
+ process_file_write(state, cmdargs);
+ break;
+
+ case QUIT:
+ free_cmdargs(cmdargs);
+ if (state->device && state->device->volume_label) {
+ log_add(L_INFO, "tape %s kb %lld fm %d [OK]\n",
+ state->device->volume_label,
+ (long long)((state->total_bytes+(off_t)1023) / (off_t)1024),
+ state->device->file);
+ }
+ return send_quitting(state);
+ default:
+ if (cmdargs->argc >= 1) {
+ q = quote_string(cmdargs->argv[0]);
+ } else {
+ q = stralloc("(no input?)");
+ }
+ putresult(BAD_COMMAND, "%s\n", q);
+ amfree(q);
+ break;
+ }
+ free_cmdargs(cmdargs);
- filenum = 0;
- return 1;
+ return TRUE;
}
-int
-next_tape(
- int writerror)
-{
- end_tape(writerror);
+int main(int argc, char ** argv) {
+ char * tapelist_name;
+ taper_state_t state;
+ config_overwrites_t *cfg_ovr = NULL;
+ char *cfg_opt = NULL;
- if (++cur_tape >= runtapes)
- return 0;
+ /*
+ * Configure program for internationalization:
+ * 1) Only set the message locale for now.
+ * 2) Set textdomain for all amanda related programs to "amanda"
+ * We don't want to be forced to support dozens of message catalogs.
+ */
+ setlocale(LC_MESSAGES, "C");
+ textdomain("amanda");
+
+ safe_fd(-1, 0);
+ set_pname("taper");
- if (!label_tape()) {
- return 0;
- }
+ dbopen("server");
- filenum = 0;
- return 1;
-}
+ device_api_init();
+ init_taper_state(&state);
+ /* Don't die when child closes pipe */
+ signal(SIGPIPE, SIG_IGN);
-int
-end_tape(
- int writerror)
-{
- char *result;
- int rc = 0;
-
- if (tape_fd >= 0) {
- log_add(L_INFO, "tape %s kb " OFF_T_FMT " fm %d %s",
- label,
- (OFF_T_FMT_TYPE)((total_tape_used+(off_t)1023) / (off_t)1024),
- total_tape_fm,
- writerror? errstr : "[OK]");
-
- fprintf(stderr, "taper: writing end marker. [%s %s kb "
- OFF_T_FMT " fm %d]\n", label,
- writerror? "ERR" : "OK",
- (OFF_T_FMT_TYPE)((total_tape_used+(off_t)1023) / (off_t)1024),
- total_tape_fm);
- fflush(stderr);
- if (! writerror) {
- if (! write_filemark()) {
- rc = 1;
- goto common_exit;
- }
+ g_fprintf(stderr, _("%s: pid %ld executable %s version %s\n"),
+ get_pname(), (long) getpid(), argv[0], version());
+ dbprintf(_("%s: pid %ld executable %s version %s\n"),
+ get_pname(), (long) getpid(), argv[0], version());
- result = tapefd_wrendmark(tape_fd, taper_timestamp, tt_blocksize);
- if (result != NULL) {
- errstr = newstralloc(errstr, result);
- rc = 1;
- goto common_exit;
- }
- }
+ /* Process options */
+
+ cfg_ovr = extract_commandline_config_overwrites(&argc, &argv);
+
+ if(argc > 2) {
+ error("Too many arguments!\n");
+ g_assert_not_reached();
}
+ if (argc > 1)
+ cfg_opt = argv[1];
+ config_init(CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_USE_CWD, cfg_opt);
+ apply_config_overwrites(cfg_ovr);
-#ifdef HAVE_LINUX_ZFTAPE_H
- if (tape_fd >= 0 && is_zftape(tapedev) == 1) {
- /* rewind the tape */
+ if (config_errors(NULL) >= CFGERR_ERRORS) {
+ g_critical(_("errors processing config file"));
+ }
- if (tapefd_rewind(tape_fd) == -1 ) {
- errstr = newstralloc2(errstr, "rewinding tape: ", strerror(errno));
- rc = 1;
- goto common_exit;
- }
- /* close the tape */
+ safe_cd();
- if (tapefd_close(tape_fd) == -1) {
- errstr = newstralloc2(errstr, "closing tape: ", strerror(errno));
- rc = 1;
- goto common_exit;
- }
- tape_fd = -1;
+ set_logerror(logerror);
-#ifdef HAVE_LIBVTBLC
- /* update volume table */
- fprintf(stderr, "taper: updating volume table ...\n");
- fflush(stderr);
-
- if ((tape_fd = raw_tape_open(rawtapedev, O_RDWR)) == -1) {
- if (errno == EACCES) {
- errstr = newstralloc(errstr,
- "updating volume table: tape is write protected");
- } else {
- errstr = newstralloc2(errstr,
- "updating volume table: ",
- strerror(errno));
- }
- rc = 1;
- goto common_exit;
- }
- /* read volume table */
- if ((num_volumes = read_vtbl(tape_fd, volumes, vtbl_buffer,
- &first_seg, &last_seg)) == -1 ) {
- errstr = newstralloc2(errstr,
- "reading volume table: ",
- strerror(errno));
- rc = 1;
- goto common_exit;
- }
- /* set volume label and date for first entry */
- vtbl_no = 0;
- if (set_label(label, volumes, num_volumes, vtbl_no)) {
- errstr = newstralloc2(errstr,
- "setting label for entry 1: ",
- strerror(errno));
- rc = 1;
- goto common_exit;
- }
- /* date of start writing this tape */
- if (set_date(start_datestr, volumes, num_volumes, vtbl_no)) {
- errstr = newstralloc2(errstr,
- "setting date for entry 1: ",
- strerror(errno));
- rc = 1;
- goto common_exit;
- }
- /* set volume labels and dates for backup files */
- for (vtbl_no = 1; vtbl_no <= num_volumes - 2; vtbl_no++) {
- fprintf(stderr,"taper: label %i: %s, date %s\n",
- vtbl_no,
- vtbl_entry[vtbl_no].label,
- vtbl_entry[vtbl_no].date);
- fflush(stderr);
- if (set_label(vtbl_entry[vtbl_no].label,
- volumes, num_volumes, vtbl_no)) {
- errstr = newstralloc2(errstr,
- "setting label for entry i: ",
- strerror(errno));
- rc = 1;
- goto common_exit;
- }
- if (set_date(vtbl_entry[vtbl_no].date,
- volumes, num_volumes, vtbl_no)) {
- errstr = newstralloc2(errstr,
- "setting date for entry i: ",
- strerror(errno));
- rc = 1;
- goto common_exit;
- }
- }
- /* set volume label and date for last entry */
- vtbl_no = num_volumes - 1;
- if (set_label("AMANDA Tape End", volumes, num_volumes, vtbl_no)) {
- errstr = newstralloc2(errstr,
- "setting label for last entry: ",
- strerror(errno));
- rc = 1;
- goto common_exit;
- }
- datestr = NULL; /* take current time */
- if (set_date(datestr, volumes, num_volumes, vtbl_no)) {
- errstr = newstralloc2(errstr,
- "setting date for last entry 1: ",
- strerror(errno));
- rc = 1;
- goto common_exit;
- }
- /* write volume table back */
- if (write_vtbl(tape_fd, volumes, vtbl_buffer, num_volumes, first_seg,
- op_mode == trunc)) {
- errstr = newstralloc2(errstr,
- "writing volume table: ",
- strerror(errno));
- rc = 1;
- goto common_exit;
- }
-
- fprintf(stderr, "taper: updating volume table: done.\n");
- fflush(stderr);
-#endif /* HAVE_LIBVTBLC */
- }
-#endif /* !HAVE_LINUX_ZFTAPE_H */
+ check_running_as(RUNNING_AS_DUMPUSER);
- /* close the tape and let the OS write the final filemarks */
+ dbrename(get_config_name(), DBG_SUBDIR_SERVER);
-common_exit:
+ log_add(L_INFO, "%s pid %ld", get_pname(), (long)getpid());
- if (tape_fd >= 0 && tapefd_close(tape_fd) == -1 && ! writerror) {
- errstr = newstralloc2(errstr, "closing tape: ", strerror(errno));
- rc = 1;
- }
- tape_fd = -1;
- amfree(label);
+ tapelist_name = config_dir_relative(getconf_str(CNF_TAPELIST));
- return rc;
-}
+ if (read_tapelist(tapelist_name) != 0) {
+ log_add(L_INFO, "pid-done %ld", (long)getpid());
+ error("could not load tapelist \"%s\"", tapelist_name);
+ g_assert_not_reached();
+ }
+ amfree(tapelist_name);
+ state.have_changer = changer_init();
+ if (state.have_changer < 0) {
+ log_add(L_INFO, "pid-done %ld", (long)getpid());
+ error("changer initialization failed: %s", strerror(errno));
+ g_assert_not_reached();
+ }
-int
-write_filemark(void)
-{
- if (tapefd_weof(tape_fd, (off_t)1) == -1) {
- errstr = newstralloc2(errstr, "writing filemark: ", strerror(errno));
- return 0;
+ state.next_tape_label = NULL;
+ state.next_tape_device = NULL;
+ state.cur_tape = 0;
+
+ if (!find_first_tape(&state)) {
+ log_add(L_INFO, "pid-done %ld", (long)getpid());
+ return EXIT_SUCCESS;
}
- total_tape_fm++;
- return 1;
+
+ while (process_driver_command(&state));
+ log_add(L_INFO, "pid-done %ld", (long)getpid());
+ return EXIT_SUCCESS;
}