X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=server-src%2Ftaper.c;h=4b54430c2ee4add5766afc24ae42360d268eebba;hb=94a044f90357edefa6f4ae9f0b1d5885b0e34aee;hp=3a10efe8e5367e224e55aa48576c450a4c83652a;hpb=d3b2175e084f88c8736ad7073eacbf4670147aec;p=debian%2Famanda diff --git a/server-src/taper.c b/server-src/taper.c index 3a10efe..4b54430 100644 --- a/server-src/taper.c +++ b/server-src/taper.c @@ -23,3276 +23,1151 @@ * 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 -#endif - -#define taper_debug(i,x) do { \ - if ((i) <= debug_taper) { \ - dbprintf(x); \ - } \ -} while (0) - -#ifdef HAVE_LIBVTBLC -#include -#include -#include - -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; - -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_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 = getconf_str(CNF_TAPEDEV); - if (tapedev != NULL) - tapedev = stralloc(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: " SIZE_T_FMT " 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*/ +/* FIXME: This file needs to use gettext. */ - case 0: /* child */ - aclose(p2c[1]); - aclose(c2p[0]); +#include +#include "physmem.h" - 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 " OFF_T_FMT "kb to buffer %s in-memory", - buff_err, (OFF_T_FMT_TYPE)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 (mmap_splitbuf != NULL) { - munmap(mmap_splitbuf, (size_t)mmap_splitsize); - mmap_splitbuf = NULL; - } -#endif -#endif - aclose(mmap_splitbuffer_fd); - amfree(mmap_filename); - mmap_splitsize = 0; - } - if (mem_splitbuf) { - amfree(mem_splitbuf); - mem_splitsize = 0; - } -} - -void -put_syncpipe_fault_result( - char * handle) -{ - char *q; - - if (handle == NULL) - handle = ""; - - 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]); - - if (tapedev == NULL) { - if (getconf_str(CNF_TPCHANGER) == NULL) { - putresult(TAPE_ERROR, "[No tapedev or tpchanger defined]\n"); - log_add(L_ERROR, "No tapedev or tpchanger defined"); - dbprintf(("taper: No tapedev or tpchanger defined\n")); - exit(1); - } - } else { - tapedev = stralloc(tapedev); - } - - 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, " %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, " %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 (SIZEOF_OFF_T == 4 && splitsize > 1048576) { /* 1G in 32 bits */ - splitsize = 1048576; - } - - 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 (SIZEOF_OFF_T == 4 && fallback_splitsize > 1048576) { /* 1G */ - fallback_splitsize = 1048576; - } - - 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(); - 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)); - } - - taper_debug(1, ("taper: r: start file\n")); - - for (bp = buftable; bp < buftable + conf_tapebufs; bp++) { - bp->status = EMPTY; - } - - bp = buftable; - if (interactive || debug_taper >= 1) - 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': - taper_debug(1, ("taper: r: got R%d\n", bufnum)); - - 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 || debug_taper >= 1) - 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 || debug_taper >= 1) - 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); - - 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 || debug_taper >= 1) - 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; - } - - taper_debug(1, ("taper: r: put W%d\n", - (int)(bp-buftable))); - 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; - } +#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 "token.h" +#include "version.h" - if (interactive) - fputs("R", stderr); - return ((ssize_t)bp->size); +/* 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; +} 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(); } -/* 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*/ +static void cleanup(taper_state_t * state) { + amfree(state->driver_start_time); + amfree(state->next_tape_label); + amfree(state->next_tape_device); + taper_scan_tracker_free(state->taper_scan_tracker); + if (state->device != NULL) { + g_object_unref(state->device); + state->device = NULL; } +} - /* should only calculuate this once, not on retries etc */ - if (expected_splits != 0) - return(expected_splits); - - total_kb = holding_file_size(filename, 1); - - if (total_kb <= (off_t)0) { - fprintf(stderr, "taper: r: " OFF_T_FMT - " kb holding file makes no sense, setting splitsize to 0\n", - (OFF_T_FMT_TYPE)total_kb); - fflush(stderr); - splitsize = 0; /* disabling split */ - return(0); +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; } - - 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; - if (tapedev != NULL) { - tapedev = stralloc(tapedev); +/* 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(cmd_t cmd, struct cmdargs * args, + char ** argnames) { + int i; + + for (i = 0; argnames[i] != NULL; i ++) { + if (i > args->argc) { + error("error [taper %s: not enough args: %s]", + cmdstr[cmd], argnames[i]); + } } - - 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(); - 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); - } + if (i < args->argc) { + error("error [taper %s: Too many args: Got %d, expected %d.]", + cmdstr[cmd], args->argc, i); } } -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; +/* 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 = squote(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); - rdwait = wrwait = times_zero; - total_writes = 0; + m = vstralloc("[port create failure: ", + strerror(save_errno), + "]", + NULL); + q = squote(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; + } - bp = buftable; - full_buffers = 0; - tok = '?'; + putresult(PORT, "%d\n", port); - taper_debug(1, ("taper: w: start file\n")); + fd = stream_accept(socket, CONNECT_TIMEOUT, 0, STREAM_BUFSIZE); - /* - * 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++) { - taper_debug(1, ("taper: w: put R%d\n", i)); - if (syncpipe_put('R', i) == -1) { - error("writer: Syncpipe failure readying write buffers"); - /*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 = squote(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 { + aclose(socket); } - /* - * 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; - taper_debug(1, ("taper: w: got W%d\n",bufnum)); - 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') { - taper_debug(1, ("taper: w: got W%d\n",bufnum)); - 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'); + 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; +} - /* got close signal from reader, acknowledge it */ +typedef struct { + ConsumerFunctor next_consumer; + gpointer next_consumer_data; + guint64 bytes_written; +} CountingConsumerData; - if (tok == 'X') - goto reader_buffer_snafu; +/* A ConsumerFunctor. This consumer just passes its arguments on to a + second consumer, but counts the number of bytes successfully + written. */ +static int counting_consumer(gpointer user_data, queue_buffer_t * buffer) { + int result; + CountingConsumerData * data = user_data; - assert(tok == 'C'); - if (syncpipe_put('C', 0) == -1) { - error("writer: Syncpipe failure putting close"); - /*NOTREACHED*/ + result = data->next_consumer(data->next_consumer_data, buffer); + + if (result > 0) { + data->bytes_written += result; } - /* tell reader the tape and file number */ + return result; +} - if (syncpipe_putstr(label) == -1) { - error("writer: Syncpipe failure putting label"); - /*NOTREACHED*/ - } - snprintf(number, SIZEOF(number), "%d", filenum); - if (syncpipe_putstr(number) == -1) { - error("writer: Syncpipe failure putting filenum"); - /*NOTREACHED*/ +static gboolean boolean_prolong(void * data) { + if (data == NULL) { + return TRUE; /* Do not interrupt. */ + } else { + 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 (previously non-amanda) tape"), + *label); + } - 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*/ } + amfree(timestamp); + return TRUE; +} - /* XXX go to next tape if past tape size? */ +typedef struct { + taper_state_t * state; + gboolean prolong; /* scan stops when this is FALSE. */ + char *errmsg; +} tape_search_request_t; - return; +/* A GThread that runs taper_scan. */ +static gpointer tape_search_thread(gpointer data) { + tape_search_request_t * request = data; - tape_error: - if (errstr) - dbprintf(("tape_error: %s\n", errstr)); - /* 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 (request->state->next_tape_label != NULL && + request->state->next_tape_device != NULL) { + return GINT_TO_POINTER(TRUE); } 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*/ + amfree(request->state->next_tape_label); + amfree(request->state->next_tape_device); } - 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; - - reader_buffer_snafu: - if (syncpipe_put('x', 0) == -1) { - error("writer: syncpipe failure putting buffer snafu"); - /*NOTREACHED*/ - } - return; + return GINT_TO_POINTER + (simple_taper_scan(request->state, + &(request->prolong), + &(request->errmsg))); } -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 || debug_taper >= 1) - dumpstatus(bp); - if (interactive) - fputs("W", stderr); - - taper_debug(1, ("taper: w: put R%d\n", (int)(bp-buftable))); - if (syncpipe_put('R', (int)(bp-buftable)) == -1) { - error("writer: Syncpipe failure during advancing write bufffer"); - /*NOTREACHED*/ - } - return 1; - } else { - errstr = newvstralloc(errstr, - "writing file: ", - (rc != -1) ? "short write" : strerror(errno), - NULL); - wrwait = timesadd(wrwait, stopclock()); - if (interactive) - fputs("[WE]", stderr); - return 0; +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); } +/* 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; + cmd_t cmd; + struct cmdargs args; -static void -cleanup(void) -{ - REMOVE_SHARED_MEMORY(); -} - + if (state->device != NULL) { + return TRUE; + } -/* - * Cleanup shared memory segments - */ -static void -signal_handler( - int signum) -{ - log_add(L_INFO, "Received signal %d", signum); + /* We save the value here in case it changes while we're running. */ + use_threads = g_thread_supported(); - exit(1); + 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); + cmd = getcmd(&args); + 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); + return TRUE; + } else { + putresult(NO_NEW_TAPE, "%s\n", dump->handle); + log_taper_scan_errmsg(search_request.errmsg); + return FALSE; + } + } + case NO_NEW_TAPE: + search_request.prolong = FALSE; + if (use_threads) { + g_thread_join(tape_search); + } + return FALSE; + } } +/* 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; +} -/* - * 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; +/* 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; + } + + if (!find_new_tape(state, dump_info)) { + return FALSE; + } - act.sa_handler = signal_handler; - act.sa_flags = 0; - sigemptyset(&act.sa_mask); + return label_new_tape(state, dump_info); +} - signal(SIGPIPE, SIG_IGN); +static gboolean label_new_tape(taper_state_t * state, dump_info_t * dump_info) { + char *tapelist_name; + char *tapelist_name_old; + char * old_volume_name; + char * old_volume_time; - if (sigaction(SIGINT, &act, NULL) != 0) { - error("taper: couldn't install SIGINT handler [%s]", strerror(errno)); - /*NOTREACHED*/ - } + /* 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. */ - if (sigaction(SIGHUP, &act, NULL) != 0) { - error("taper: couldn't install SIGHUP handler [%s]", strerror(errno)); - /*NOTREACHED*/ + state->device = device_open(state->next_tape_device); + amfree(state->next_tape_device); + if (state->device == NULL) { + amfree(state->next_tape_label); + return FALSE; } - - if (sigaction(SIGTERM, &act, NULL) != 0) { - error("taper: couldn't install SIGTERM handler [%s]", strerror(errno)); - /*NOTREACHED*/ + + device_set_startup_properties_from_config(state->device); + device_read_label(state->device); + 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.\n", + state->next_tape_label, state->device->device_name); + device_finish(state->device); + device_read_label(state->device); + tape_used = check_volume_changed(state->device, old_volume_name, + old_volume_time); + g_object_unref(state->device); + state->device = NULL; + amfree(state->next_tape_label); + /* If the volume was written, we tell the driver and then immediately + * try again. */ + if (tape_used) { + putresult(NEW_TAPE, "%s %s\n", dump_info->handle, + state->device->volume_label); + if (old_volume_name) { + log_add(L_WARNING, "Problem writing label to volume %s, " + "volume may be erased.\n", old_volume_name); + } else { + log_add(L_WARNING, "Problem writing label %s to new volume, " + "volume may be erased.\n", state->next_tape_label); + } + amfree(old_volume_name); + amfree(old_volume_time); + return find_and_label_new_tape(state, dump_info); + } else { + /* Otherwise, we grab a new tape without talking to the driver + * again. */ + tape_search_request_t request; + gboolean search_result; + if (old_volume_name) { + log_add(L_WARNING, "Problem writing label to volume %s, " + "old volume data intact\n", old_volume_name); + } else { + log_add(L_WARNING, "Problem writing label %s to new volume, " + "old volume data intact\n", state->next_tape_label); + } + amfree(old_volume_name); + amfree(old_volume_time); + 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 { + /* Problem finding a new tape! */ + log_taper_scan_errmsg(request.errmsg); + putresult(NO_NEW_TAPE, "%s\n", dump_info->handle); + return FALSE; + } + } + } else { + amfree(old_volume_name); + amfree(old_volume_time); } - if (sigaction(SIGUSR1, &act, NULL) != 0) { - error("taper: couldn't install SIGUSR1 handler [%s]", strerror(errno)); - /*NOTREACHED*/ + amfree(state->next_tape_label); + + 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); } - if (sigaction(SIGUSR2, &act, NULL) != 0) { - error("taper: couldn't install SIGUSR2 handler [%s]", strerror(errno)); + if (write_tapelist(tapelist_name_old)) { + error("could not write tapelist: %s", strerror(errno)); /*NOTREACHED*/ } + amfree(tapelist_name_old); - if (sigaction(SIGALRM, &act, NULL) != 0) { - error("taper: couldn't install SIGALRM handler [%s]", strerror(errno)); + remove_tapelabel(state->device->volume_label); + add_tapelabel(state->driver_start_time, + state->device->volume_label); + if (write_tapelist(tapelist_name)) { + error("could not write tapelist: %s", strerror(errno)); /*NOTREACHED*/ } -} - - -/* - * ======================================================================== - * SHARED-MEMORY BUFFER SUBSYSTEM - * - */ + amfree(tapelist_name); + state->cur_tape++; -#ifdef HAVE_SYSVSHM - -int shmid = -1; + 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); -char * -attach_buffers( - size_t size) -{ - char *result; + return TRUE; +} - shmid = shmget(IPC_PRIVATE, size, IPC_CREAT|0700); - if (shmid == -1) { - return NULL; +/* 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 */) { + if (taper_source_is_partial(dump_info->source)) { + *result_cmd = PARTIAL; + *result_log = L_PARTIAL; + } else { + *result_cmd = DONE; + *result_log = L_DONE; } +} - result = (char *)shmat(shmid, (SHM_ARG_TYPE *)NULL, 0); - - if (result == (char *)-1) { - int save_errno = errno; - - destroy_buffers(); - errno = save_errno; - error("shmat: %s", strerror(errno)); - /*NOTREACHED*/ - } +/* 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 * qdiskname = quote_string(dump_info->diskname); - return result; + log_add(L_PARTIAL, "%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_kbytes / dump_time); + amfree(qdiskname); } +/* 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); + + 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; + + /* A problem occured. */ + if (queue_result & QUEUE_CONSUMER_ERROR) { + /* Close the device. */ + device_finish(taper_state->device); + g_object_unref(taper_state->device); + taper_state->device = NULL; + } + + log_add(L_PARTPARTIAL, + "%s %d %s %s %s %d/%d %d [sec %f kb %ju kps %f] \"\"", + 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); + 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; + } + } -void -detach_buffers( - char *bufp) -{ - if ((bufp != NULL) && - (shmdt((SHM_ARG_TYPE *)bufp) == -1)) { - error("shmdt: %s", strerror(errno)); - /*NOTREACHED*/ - } + 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]\" \"\" \"\"\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); + put_partial_log(dump_info, dump_time, dump_kbytes); + } + + amfree(qdiskname); + return FALSE; } -void -destroy_buffers(void) -{ - if (shmid == -1) - return; /* nothing to destroy */ - if (shmctl(shmid, IPC_RMID, NULL) == -1) { - error("shmctl: %s", strerror(errno)); - /*NOTREACHED*/ - } -} +/* 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); -#else -#ifdef HAVE_MMAP - -#ifdef HAVE_SYS_MMAN_H -#include -#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*/ + if (rval == NULL) { + return NULL; } -#endif - saved_size = size; - shmbuf = (char *) mmap((void *) 0, - size, - PROT_READ|PROT_WRITE, - MAP_ANON|MAP_SHARED, - shmfd, 0); + rval->cont_filename[0] = '\0'; - return shmbuf; -} + expected_splits = taper_source_predict_parts(dump_info->source); -void -detach_buffers( - char *bufp) -{ - if ((bufp != NULL) && - (munmap((void *)bufp, saved_size) == -1)) { - error("detach_buffers: munmap: %s", strerror(errno)); - /*NOTREACHED*/ + if (expected_splits != 1) { + rval->type = F_SPLIT_DUMPFILE; + rval->partnum = dump_info->current_part; + rval->totalparts = expected_splits; } - if (shmfd != -1) - aclose(shmfd); + return rval; } -void -destroy_buffers(void) -{ +/* 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) { + 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]\" \"\" \"no new tape\"\n", + dump_info->handle, + dump_time, (uintmax_t)dump_kbytes, dump_kbps); + put_partial_log(dump_info, dump_time, dump_kbytes); + } else { + char * qdiskname = quote_string(dump_info->diskname); + putresult(FAILED, + "%s INPUT-GOOD TAPE-ERROR \"\" \"No new tape.\"\n", + dump_info->handle); + log_add(L_FAIL, "%s %s %s %d \"No new tape.\"", + dump_info->hostname, qdiskname, dump_info->timestamp, + dump_info->level); + amfree(qdiskname); + } } -#else -#error: must define either HAVE_SYSVSHM or HAVE_MMAP! -#endif -#endif - - +/* 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); + putresult(FAILED, + "%s INPUT-ERROR TAPE-GOOD \"Failed reading dump header.\" \"\"\n", + dump_info->handle); + log_add(L_FAIL, "%s %s %s %d \"Failed reading dump header.\"", + dump_info->hostname, qdiskname, dump_info->timestamp, + dump_info->level); + amfree(qdiskname); + return; + } + + if (!find_and_label_new_tape(taper_state, dump_info)) { + bail_no_volume(dump_info); + amfree(this_header); + return; + } -/* - * ======================================================================== - * SYNC-PIPE SUBSYSTEM - * - */ + if (!device_start_file(taper_state->device, this_header)) { + bail_no_volume(dump_info); + amfree(this_header); + return; + } + amfree(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) * + device_write_max_size(taper_state->device); + } else { + /* Use default. */ + max_memory = getconf_size(CNF_DEVICE_OUTPUT_BUFFER_SIZE); + } -int getpipe, putpipe; + queue_result = do_consumer_producer_queue_full + (taper_source_producer, + dump_info->source, + counting_consumer, + &consumer_data, + device_write_max_size(taper_state->device), max_memory, + streaming_mode); + + g_get_current_time(&end_time); + run_time = timesub(end_time, start_time); + + /* The device_write_consumer may have closed the file with a short + * write, so we only finish here if it needs it. */ + if (taper_state->device->in_file && + !device_finish_file(taper_state->device)) { + queue_result = queue_result | QUEUE_CONSUMER_ERROR; + } -void -syncpipe_init( - int rd, - int wr) -{ - getpipe = rd; - putpipe = wr; + if (!finish_part_attempt(taper_state, dump_info, queue_result, + run_time, consumer_data.bytes_written)) { + break; + } + } } -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)); - } 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); +/* 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", /* 1 */ + "handle", /* 2 */ + "hostname", /* 3 */ + "diskname", /* 4 */ + "level", /* 5 */ + "datestamp", /* 6 */ + "splitsize", /* 7 */ + "split_diskbuffer", /* 8 */ + "fallback_splitsize", /* 9 */ + NULL }; + + validate_args(PORT_WRITE, cmdargs, argnames); + + dump_state.handle = g_strdup(cmdargs->argv[2]); + dump_state.hostname = g_strdup(cmdargs->argv[3]); + dump_state.diskname = unquote_string(cmdargs->argv[4]); + + errno = 0; + dump_state.level = strtol(cmdargs->argv[5], NULL, 10); + if (errno != 0) { + error("error [taper PORT-WRITE: Invalid dump level %s]", + cmdargs->argv[5]); + g_assert_not_reached(); } - /* 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; -} + + dump_state.timestamp = strdup(cmdargs->argv[6]); -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)); + errno = 0; + splitsize = g_ascii_strtoull(cmdargs->argv[7], NULL, 10); + if (errno != 0) { + error("error [taper PORT-WRITE: Invalid splitsize %s]", + cmdargs->argv[7]); + g_assert_not_reached(); + } + + if (strcmp(cmdargs->argv[8], "NULL") == 0) { + split_diskbuffer = NULL; } 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); + split_diskbuffer = g_strdup(cmdargs->argv[8]); } - /* 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); + + errno = 0; + fallback_splitsize = g_ascii_strtoull(cmdargs->argv[9], NULL, 10); + if (errno != 0) { + error("error [taper PORT-WRITE: Invalid fallback_splitsize %s]", + cmdargs->argv[9]); + g_assert_not_reached(); } - if (debug_taper >= 1 && *buf != 'R' && *buf != 'W') { - taper_debug(1, ("taper: %c: getc %c\n", *procname, *buf)); + 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; } + free(split_diskbuffer); - memcpy(intp, &buf[1], SIZEOF(int)); - return (int)buf[0]; -} - -int -syncpipe_getint(void) -{ - ssize_t rc; - int i = 0; + run_device_output(state, &dump_state); - rc = fullread(getpipe, &i, SIZEOF(i)); - if (rc != (ssize_t)sizeof(i)) { - syncpipe_read_error(rc, (ssize_t)sizeof(i)); - return (-1); - } - - return (i); + free_dump_info(&dump_state); } - -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*/ +/* 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", /* 1 */ + "handle", /* 2 */ + "filename", /* 3 */ + "hostname", /* 4 */ + "diskname", /* 5 */ + "level", /* 6 */ + "datestamp", /* 7 */ + "splitsize", /* 8 */ + NULL }; + + validate_args(FILE_WRITE, cmdargs, argnames); + + dump_state.handle = g_strdup(cmdargs->argv[2]); + holding_disk_file = unquote_string(cmdargs->argv[3]); + dump_state.hostname = g_strdup(cmdargs->argv[4]); + dump_state.diskname = unquote_string(cmdargs->argv[5]); + + errno = 0; + dump_state.level = strtol(cmdargs->argv[6], 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[7]); - 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); + errno = 0; + splitsize = g_ascii_strtoull(cmdargs->argv[8], NULL, 10); + if (errno != 0) { + error("error [taper FILE-WRITE: Invalid splitsize %s]", + cmdargs->argv[8]); + g_assert_not_reached(); } - return (str); -} - -int -syncpipe_put( - int chi, - int intval) -{ - char buf[sizeof(char) + sizeof(int)]; - ssize_t rc; + 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); - buf[0] = (char)chi; - memcpy(&buf[1], &intval, SIZEOF(int)); - if (debug_taper >= 1 && buf[0] != 'R' && buf[0] != 'W') { - taper_debug(1, ("taper: %c: putc %c\n",*procname,buf[0])); - } + run_device_output(state, &dump_state); - rc = fullwrite(putpipe, buf, SIZEOF(buf)); - if (rc != (ssize_t)sizeof(buf)) { - syncpipe_write_error(rc, (ssize_t)sizeof(buf)); - return (-1); - } - return (0); + free_dump_info(&dump_state); + amfree(holding_disk_file); } -int -syncpipe_putint( - int i) -{ - 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); +/* 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; } -int -syncpipe_putstr( - const char *str) -{ - ssize_t n, rc; - - if(!str) - str = "UNKNOWN syncpipe_putstr STRING"; +/* This function recieves the START_TAPER command from driver, and + returns the attached timestamp. */ +static gboolean find_first_tape(taper_state_t * state) { + cmd_t cmd; + /* Note: cmdargs.argv is never freed. In the entire Amanda codebase. */ + struct cmdargs cmdargs; + tape_search_request_t search_request; + GThread * tape_search = NULL; + gboolean use_threads; - n = (ssize_t)strlen(str) + 1; /* send '\0' as well */ - syncpipe_putint((int)n); + /* We save the value here in case it changes while we're running. */ + use_threads = g_thread_supported(); - rc = fullwrite(putpipe, str, (size_t)n); - if (rc != n) { - syncpipe_write_error(rc, n); - return (-1); - } - return (0); -} - -/* - * ======================================================================== - * 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; - int scan_result; - - amfree(label); - amfree(tapedev); - if ((scan_result = 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; + 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); } - 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); + cmd = getcmd(&cmdargs); + + switch (cmd) { + case START_TAPER: { + gboolean search_result; + state->driver_start_time = strdup(cmdargs.argv[2]); + 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); + return TRUE; } - if ((tape_fd = tape_open(tapedev, O_WRONLY)) == -1) { - if (errno == EACCES) { - errstr = newstralloc2(errstr, - "writing label: tape is write protected or I don't have write permission on ", tapedev); - } else { - errstr = newstralloc2(errstr, - "writing label: ", strerror(errno)); - } - return 0; + case QUIT: + search_request.prolong = FALSE; + if (use_threads) { + g_thread_join(tape_search); + } + return send_quitting(state); + default: + error("error [file_reader_side cmd %d argc %d]", cmd, cmdargs.argc); } - 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; - } + g_assert_not_reached(); +} - /* Output a description of what we just did. A result of '3' from taper_scan - * means that a new tape was found and will be labeled. */ - if (slot > -1) { - fprintf(stderr, _("taper: slot: %d wrote label `%s' date `%s'\n"), slot, label, taper_timestamp); - if (scan_result == 3) - log_add(L_INFO, _("Wrote new label `%s' to new (non-amanda) tape in slot %d"), label, slot); - } else { - fprintf(stderr, _("taper: wrote label `%s' date `%s'\n"), label, taper_timestamp); - if (scan_result == 3) - log_add(L_INFO, _("Wrote new label `%s' to new (non-amanda) tape"), label); - } - 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]; - - snprintf(cur_str, SIZEOF(cur_str), "%d", cur_tape - 1); - conf_tapelist_old = vstralloc(conf_tapelist, - ".today.", cur_str, NULL); - } - - if (write_tapelist(conf_tapelist_old)) { - error("could not write tapelist: %s", strerror(errno)); - /*NOTREACHED*/ - } - amfree(conf_tapelist_old); - - remove_tapelabel(label); - add_tapelabel(taper_timestamp, label); - if (write_tapelist(conf_tapelist)) { - error("could not write tapelist: %s", strerror(errno)); - /*NOTREACHED*/ - } - } +/* In running mode (not startup mode), get a command from driver and + deal with it. */ +static gboolean process_driver_command(taper_state_t * state) { + cmd_t cmd; + struct cmdargs cmdargs; + char * q; - 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); + /* This will return QUIT if driver has died. */ + cmd = getcmd(&cmdargs); + switch (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: + return send_quitting(state); + 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; } - total_tape_used=(off_t)0; - total_tape_fm = 0; - - return 1; + return TRUE; } -/* 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*/ - } - changer_debug = 1; +int main(int argc, char ** argv) { + char * tapelist_name; + int have_changer; + taper_state_t state; + config_overwrites_t *cfg_ovr = NULL; + char *cfg_opt = NULL; - taper_timestamp = newstralloc(taper_timestamp, new_datestamp); + /* + * 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); -int -next_tape( - int writerror) -{ - end_tape(writerror); + /* Don't die when child closes pipe */ + signal(SIGPIPE, SIG_IGN); - if (++cur_tape >= runtapes) - return 0; + 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()); - if (!label_tape()) { - return 0; - } + /* Process options */ - filenum = 0; - return 1; -} + 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 | CONFIG_INIT_FATAL, + cfg_opt); + apply_config_overwrites(cfg_ovr); -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; - } + safe_cd(); - result = tapefd_wrendmark(tape_fd, taper_timestamp, tt_blocksize); - if (result != NULL) { - errstr = newstralloc(errstr, result); - rc = 1; - goto common_exit; - } - } - } + set_logerror(logerror); -#ifdef HAVE_LINUX_ZFTAPE_H - if (tape_fd >= 0 && is_zftape(tapedev) == 1) { - /* rewind the tape */ - - if (tapefd_rewind(tape_fd) == -1 ) { - errstr = newstralloc2(errstr, "rewinding tape: ", strerror(errno)); - rc = 1; - goto common_exit; - } - /* close the tape */ - - if (tapefd_close(tape_fd) == -1) { - errstr = newstralloc2(errstr, "closing tape: ", strerror(errno)); - rc = 1; - goto common_exit; - } - tape_fd = -1; - -#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(config_name, DBG_SUBDIR_SERVER); -common_exit: + tapelist_name = config_dir_relative(getconf_str(CNF_TAPELIST)); - if (tape_fd >= 0 && tapefd_close(tape_fd) == -1 && ! writerror) { - errstr = newstralloc2(errstr, "closing tape: ", strerror(errno)); - rc = 1; + if (read_tapelist(tapelist_name) != 0) { + error("could not load tapelist \"%s\"", tapelist_name); + g_assert_not_reached(); } - tape_fd = -1; - amfree(label); - - return rc; -} + amfree(tapelist_name); + have_changer = changer_init(); + if (have_changer < 0) { + 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)) { + return EXIT_SUCCESS; } - total_tape_fm++; - return 1; + + while (process_driver_command(&state)); + return EXIT_SUCCESS; }