X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=server-src%2Ftaper.c;h=fed6a33a114669e298fc29f75b2d5664e385c3a6;hb=2627875b7d18858bc1f9f7652811e4d8c15a23eb;hp=aa883eae66d3410a5872b004b790b3174bbf7149;hpb=1194fb66aa28d9929c3f2bef3cc6c1c3f40a60a4;p=debian%2Famanda diff --git a/server-src/taper.c b/server-src/taper.c index aa883ea..fed6a33 100644 --- a/server-src/taper.c +++ b/server-src/taper.c @@ -23,2602 +23,1312 @@ * 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.118 2006/03/17 15:34:12 vectro 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" +/* FIXME: This file needs to use gettext. */ + +#include +#include "physmem.h" + +#include "changer.h" #include "clock.h" -#include "stream.h" -#include "holding.h" +#include "conffile.h" +#include "device.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 - -#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]; -time_t raw_time; -struct tm tape_time; -struct tm backup_time; -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? - */ -long splitsize = 0; /* max size of dumpfile before split (Kb) */ -char *splitbuf = NULL; -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 */ -int conf_tapebufs; - -off_t maxseek = (off_t)1 << ((sizeof(off_t) * 8) - 11); - -char *holdfile_path = NULL; -char *holdfile_path_thischunk = NULL; -int num_holdfile_chunks = 0; -dumpfile_t holdfile_hdr; -dumpfile_t holdfile_hdr_thischunk; -off_t holdfile_offset_thischunk = (off_t)0; -int splitbuffer_fd = -1; -char *splitbuffer_path = NULL; - -#define MODE_NONE 0 -#define MODE_FILE_WRITE 1 -#define MODE_PORT_WRITE 2 - -int 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; - unsigned int 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 P((int main_argc, char **main_argv)); -void file_reader_side P((int rdpipe, int wrpipe)); -void tape_writer_side P((int rdpipe, int wrpipe)); +#include "stream.h" +#include "tapefile.h" +#include "taperscan.h" +#include "taper-source.h" +#include "timestamp.h" +#include "util.h" +#include "version.h" +#include "queueing.h" +#include "device-queueing.h" + +/* FIXME: This should not be here. */ +#define CONNECT_TIMEOUT (2*60) + +/* Use this instead of global variables, so that we are reentrant. */ +typedef struct { + Device * device; + char * driver_start_time; + int cur_tape; + char * next_tape_label; + char * next_tape_device; + taper_scan_tracker_t * taper_scan_tracker; + char * last_errmsg; + off_t total_bytes; + int have_changer; +} taper_state_t; + +typedef struct { + char * handle; + char * hostname; + char * diskname; + int level; + char * timestamp; + char * id_string; + TaperSource * source; + int current_part; + GTimeVal total_time; + guint64 total_bytes; +} dump_info_t; + +static gboolean label_new_tape(taper_state_t * state, dump_info_t * dump_info); + +static void init_taper_state(taper_state_t* state) { + state->device = NULL; + state->driver_start_time = NULL; + state->taper_scan_tracker = taper_scan_tracker_new(); + state->last_errmsg = NULL; + state->total_bytes = 0; +} -/* shared-memory routines */ -char *attach_buffers P((unsigned int size)); -void detach_buffers P((char *bufp)); -void destroy_buffers P((void)); -#define REMOVE_SHARED_MEMORY() \ - detach_buffers(buffers); \ - if (strcmp(procname, "reader") == 0) { \ - destroy_buffers(); \ +static void cleanup(taper_state_t * state) { + amfree(state->driver_start_time); + amfree(state->next_tape_label); + amfree(state->next_tape_device); + amfree(state->last_errmsg); + taper_scan_tracker_free(state->taper_scan_tracker); + if (state->device != NULL) { + g_object_unref(state->device); + state->device = NULL; } +} -/* synchronization pipe routines */ -void syncpipe_init P((int rd, int wr)); -char syncpipe_get P((int *intp)); -int syncpipe_getint P((void)); -char *syncpipe_getstr P((void)); -void syncpipe_put P((int ch, int intval)); -void syncpipe_putint P((int i)); -void syncpipe_putstr P((const char *str)); - -/* tape manipulation subsystem */ -int first_tape P((char *new_datestamp)); -int next_tape P((int writerr)); -int end_tape P((int writerr)); -int write_filemark P((void)); - -/* support crap */ -int seek_holdfile P((int fd, buffer_t *bp, long kbytes)); - -/* signal handling */ -static void install_signal_handlers P((void)); -static void signal_handler P((int)); - -/* exit routine */ -static void cleanup P((void)); - -/* - * ======================================================================== - * GLOBAL STATE - * - */ -int interactive; -int writerpid; -times_t total_wait; -#ifdef TAPER_DEBUG -int bufdebug = 1; -#else -int bufdebug = 0; -#endif - -char *buffers = NULL; -buffer_t *buftable = NULL; -int err; - -char *procname = "parent"; - -char *taper_datestamp = NULL; -char *label = NULL; -int filenum; -char *errstr = NULL; -int tape_fd = -1; -char *tapedev = NULL; -char *tapetype = NULL; -tapetype_t *tt = NULL; -long tt_blocksize; -long tt_blocksize_kb; -long 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; -long cur_span_chunkstart = 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(main_argc, main_argv) -int main_argc; -char **main_argv; -{ - int p2c[2], c2p[2]; /* parent-to-child, child-to-parent pipes */ - char *conffile; - unsigned int size; - int i; - int j; - int page_size; - char *first_buffer; - - safe_fd(-1, 0); - - set_pname("taper"); - - /* Don't die when child closes pipe */ - signal(SIGPIPE, SIG_IGN); - - malloc_size_1 = malloc_inuse(&malloc_hist_1); - - fprintf(stderr, "%s: pid %ld executable %s version %s\n", - get_pname(), (long) getpid(), main_argv[0], version()); - fflush(stderr); - - if (main_argc > 1 && main_argv[1][0] != '-') { - config_name = stralloc(main_argv[1]); - config_dir = vstralloc(CONFIG_DIR, "/", main_argv[1], "/", NULL); - main_argc--; - main_argv++; - } else { - char my_cwd[STR_SIZE]; - - if (getcwd(my_cwd, sizeof(my_cwd)) == NULL) { - error("cannot determine current working directory"); - } - config_dir = stralloc2(my_cwd, "/"); - if ((config_name = strrchr(my_cwd, '/')) != NULL) { - config_name = stralloc(config_name + 1); - } +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; } +} - safe_cd(); - - install_signal_handlers(); - atexit(cleanup); - - /* print prompts and debug messages if running interactive */ - - interactive = (main_argc > 1 && strcmp(main_argv[1],"-t") == 0); - if(interactive) { - erroutput_type = ERR_INTERACTIVE; - } else { - erroutput_type = ERR_AMANDALOG; - set_logerror(logerror); - } +/* Validate that a command has the proper number of arguments, and + print a meaningful error message if not. It returns only if the + check is successful. */ +static void validate_args(struct cmdargs * cmdargs, + char ** argnames) { + int len = g_strv_length(argnames); - conffile = stralloc2(config_dir, CONFFILE_NAME); - if(read_conffile(conffile)) { - error("errors processing config file \"%s\"", conffile); + if (len > cmdargs->argc) { + error("error [taper %s: not enough args; first missing arg is %s]", + cmdstr[cmdargs->cmd], argnames[cmdargs->argc]); } - amfree(conffile); - 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); + if (len < cmdargs->argc) { + error("error [taper %s: Too many args: Got %d, expected %d.]", + cmdstr[cmdargs->cmd], cmdargs->argc, len); } +} - tapedev = getconf_str(CNF_TAPEDEV); - tapetype = getconf_str(CNF_TAPETYPE); - tt = lookup_tapetype(tapetype); -#ifdef HAVE_LIBVTBLC - rawtapedev = 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 = tt->blocksize; - tt_blocksize = tt_blocksize_kb * 1024; - tt_file_pad = tt->file_pad; +/* Open a socket to the dumper. Returns TRUE if everything is happy, FALSE + otherwise. */ +static gboolean open_read_socket(dump_info_t * info, char * split_diskbuffer, + guint64 splitsize, guint64 fallback_splitsize) { + in_port_t port = 0; + int socket; + int fd; + int result; + struct addrinfo *res; + + if ((result = resolve_hostname("localhost", 0, &res, NULL) != 0)) { + char *m; + char *q; + int save_errno = errno; + char *qdiskname = quote_string(info->diskname); + + m = vstralloc("[localhost resolve failure: ", + strerror(save_errno), + "]", + NULL); + q = quote_string(m); + putresult(TAPE_ERROR, "%s %s\n", info->handle, q); + log_add(L_FAIL, "%s %s %s %d %s", + info->hostname, qdiskname, info->timestamp, + info->level, q); + amfree(qdiskname); + amfree(m); + amfree(q); + return FALSE; + } + + socket = stream_server(res->ai_family, &port, 0, STREAM_BUFSIZE, 0); + freeaddrinfo(res); + + if (socket < 0) { + char *m; + char *q; + int save_errno = errno; + char *qdiskname = quote_string(info->diskname); - if(interactive) { - fprintf(stderr,"taper: running in interactive test mode\n"); - fflush(stderr); + m = vstralloc("[port create failure: ", + strerror(save_errno), + "]", + NULL); + q = quote_string(m); + putresult(TAPE_ERROR, "%s %s\n", info->handle, q); + log_add(L_FAIL, "%s %s %s %d %s", + info->hostname, qdiskname, info->timestamp, + info->level, q); + amfree(qdiskname); + amfree(m); + amfree(q); + return FALSE; } - /* create read/write syncronization pipes */ - - if(pipe(p2c) || pipe(c2p)) - error("creating sync pipes: %s", strerror(errno)); - - /* create shared memory segment */ - -#if defined(HAVE_GETPAGESIZE) - page_size = getpagesize(); - fprintf(stderr, "%s: page size is %d\n", get_pname(), page_size); -#else - page_size = 1024; - fprintf(stderr, "%s: getpagesize() not available, using %d\n", - get_pname(), - page_size); -#endif - buffer_size = am_round(tt_blocksize, page_size); - fprintf(stderr, "%s: buffer size is %ld\n", get_pname(), buffer_size); - while(conf_tapebufs > 0) { - size = page_size; - size += conf_tapebufs * buffer_size; - size += conf_tapebufs * sizeof(buffer_t); - if((buffers = attach_buffers(size)) != NULL) { - break; - } - log_add(L_INFO, "attach_buffers: (%d tapebuf%s: %d bytes) %s", - conf_tapebufs, - (conf_tapebufs == 1) ? "" : "s", - size, - strerror(errno)); - conf_tapebufs--; - } - if(buffers == NULL) { - error("cannot allocate shared memory"); - } - i = (buffers - (char *)0) & (page_size - 1); /* page boundary offset */ - if(i != 0) { - first_buffer = buffers + page_size - i; - fprintf(stderr, "%s: shared memory at %p, first buffer at %p\n", - get_pname(), - buffers, - first_buffer); - } else { - first_buffer = buffers; - } - 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; - fprintf(stderr, "%s: buffer[%0*d] at %p\n", - get_pname(), - j, i, - buftable[i].buffer); - } - fprintf(stderr, "%s: buffer structures at %p for %d bytes\n", - get_pname(), - 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)); - - case 0: /* child */ - aclose(p2c[1]); - aclose(c2p[0]); - - tape_writer_side(p2c[0], c2p[1]); - error("tape writer terminated unexpectedly"); + putresult(PORT, "%d\n", port); - default: /* parent */ - aclose(p2c[0]); - aclose(c2p[1]); + fd = stream_accept(socket, CONNECT_TIMEOUT, 0, STREAM_BUFSIZE); - file_reader_side(c2p[0], p2c[1]); - error("file reader terminated unexpectedly"); + if (fd < 0) { + char *m, *q; + int save_errno = errno; + char *qdiskname = quote_string(info->diskname); + m = vstralloc("[port connect failure: ", + strerror(save_errno), + "]", + NULL); + q = quote_string(m); + putresult(TAPE_ERROR, "%s %s\n", info->handle, q); + log_add(L_FAIL, "%s %s %s %d %s", + info->hostname, qdiskname, info->timestamp, + info->level, q); + amfree(qdiskname); + aclose(socket); + amfree(m); + amfree(q); + return FALSE; + } else { + aclose(socket); } - /* NOTREACHED */ - return 0; + 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; } +typedef struct { + ConsumerFunctor next_consumer; + gpointer next_consumer_data; + guint64 bytes_written; +} CountingConsumerData; -/* - * ======================================================================== - * FILE READER SIDE - * - */ -int read_file P((int fd, char *handle, - char *host, char *disk, char *datestamp, - int level)); -int taper_fill_buffer P((int fd, buffer_t *bp, int buflen)); -void dumpbufs P((char *str1)); -void dumpstatus P((buffer_t *bp)); -int get_next_holding_file P((int fd, buffer_t *bp, char *strclosing, int rc)); -int predict_splits P((char *filename)); -void create_split_buffer P((char *split_diskbuffer, long fallback_splitsize, char *id_string)); -void free_split_buffer P(()); - +/* A ConsumerFunctor. This consumer just passes its arguments on to a + second consumer, but counts the number of bytes successfully + written. */ +static ssize_t counting_consumer(gpointer user_data, queue_buffer_t * buffer) { + ssize_t result; + CountingConsumerData * data = user_data; -/* - * 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(split_diskbuffer, fallback_splitsize, id_string) -char *split_diskbuffer; -long fallback_splitsize; -char *id_string; -{ - char *buff_err = NULL; - void *nulls = NULL; - int c; + result = data->next_consumer(data->next_consumer_data, buffer); - /* don't bother if we're not actually splitting */ - if(splitsize <= 0){ - splitbuf = NULL; - splitbuf_wr_ptr = NULL; - return; - } - -#ifdef HAVE_MMAP -#ifdef HAVE_SYS_MMAN_H - if(strcmp(split_diskbuffer, "NULL")){ - splitbuffer_path = vstralloc(split_diskbuffer, - "/splitdump_buffer_XXXXXX", - NULL); -#ifdef HAVE_MKSTEMP - splitbuffer_fd = mkstemp(splitbuffer_path); -#else - log_add(L_INFO, "mkstemp not available, using plain open() for split buffer- make sure %s has safe permissions", split_diskbuffer); - splitbuffer_fd = open(splitbuffer_path, O_RDWR|O_CREAT, 0600); -#endif - if(splitbuffer_fd == -1){ - buff_err = newvstralloc(buff_err, "mkstemp/open of ", - splitbuffer_path, "failed (", - strerror(errno), ")", NULL); - goto fallback; - } - nulls = alloc(1024); /* lame */ - memset(nulls, 0, 1024); - for(c = 0; c < splitsize ; c++) { - if(fullwrite(splitbuffer_fd, nulls, 1024) < 1024){ - buff_err = newvstralloc(buff_err, "write to ", splitbuffer_path, - "failed (", strerror(errno), ")", NULL); - free_split_buffer(); - goto fallback; - } - } - amfree(nulls); - - splitbuf = mmap(NULL, (size_t)splitsize*1024, PROT_READ|PROT_WRITE, - MAP_SHARED, splitbuffer_fd, (off_t)0); - if(splitbuf == (char*)-1){ - buff_err = newvstralloc(buff_err, "mmap failed (", strerror(errno), - ")", NULL); - free_split_buffer(); - goto fallback; - } - fprintf(stderr, - "taper: r: buffering %ldkb split chunks in mmapped file %s\n", - splitsize, splitbuffer_path); - splitbuf_wr_ptr = splitbuf; - return; - } - else{ - buff_err = stralloc("no split_diskbuffer specified"); + if (result > 0) { + data->bytes_written += result; } -#else - buff_err = stralloc("mman.h not available"); - goto fallback; -#endif -#else - buff_err = stralloc("mmap not available"); - goto fallback; -#endif - /* - Buffer split dumps in memory, if we can't use a file. - */ - fallback: - splitsize = fallback_splitsize; - log_add(L_INFO, - "%s: using fallback split size of %dkb to buffer %s in-memory", - buff_err, splitsize, id_string); - splitbuf = alloc(splitsize * 1024); - splitbuf_wr_ptr = splitbuf; + return result; } -/* - * Free up resources that create_split_buffer eats. - */ -void free_split_buffer() -{ - if(splitbuffer_fd != -1){ -#ifdef HAVE_MMAP -#ifdef HAVE_SYS_MMAN_H - if(splitbuf != NULL) munmap(splitbuf, splitsize); -#endif -#endif - aclose(splitbuffer_fd); - splitbuffer_fd = -1; - - if(unlink(splitbuffer_path) == -1){ - log_add(L_WARNING, "Failed to unlink %s: %s", - splitbuffer_path, strerror(errno)); - } - amfree(splitbuffer_path); - splitbuffer_path = NULL; - } - else if(splitbuf){ - amfree(splitbuf); - splitbuf = NULL; +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 tape"), + *label); + } -void file_reader_side(rdpipe, wrpipe) -int rdpipe, wrpipe; -{ - cmd_t cmd; - struct cmdargs cmdargs; - char *handle = NULL; - char *filename = NULL; - char *hostname = NULL; - char *diskname = NULL; - char *result = NULL; - char *datestamp = NULL; - char *split_diskbuffer = NULL; - char *id_string = NULL; - char tok; - char *q = NULL; - int level, fd, data_port, data_socket, wpid; - char level_str[64]; - struct stat stat_file; - int tape_started; - int a; - long fallback_splitsize = 0; - int tmpint; - - 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); - } - - /* pass start command on to tape writer */ - - taper_datestamp = newstralloc(taper_datestamp, cmdargs.argv[2]); - - tape_started = 0; - syncpipe_put('S', 0); - syncpipe_putstr(taper_datestamp); - - /* get result of start command */ - - tok = syncpipe_get(&tmpint); - switch(tok) { - case 'S': - putresult(TAPER_OK, "\n"); - tape_started = 1; - /* start is logged in writer */ - break; - case 'E': - /* no tape, bail out */ - result = syncpipe_getstr(); - q = squotef("[%s]", result ? result : "(null)"); - putresult(TAPE_ERROR, "%s\n", q); - amfree(q); - log_add(L_ERROR,"no-tape [%s]", "No writable valid tape found"); - amfree(result); - syncpipe_put('e', 0); /* ACK error */ - break; - default: - error("expected 'S' or 'E' for START-TAPER, got '%c'", tok); - } - - /* process further commands */ - - while(1) { - startclock(); - cmd = getcmd(&cmdargs); - if(cmd != QUIT && !tape_started) { - error("error [file_reader_side cmd %d without tape ready]", cmd); - } - 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]"); - } - handle = newstralloc(handle, cmdargs.argv[a++]); - - if(a >= cmdargs.argc) { - error("error [taper PORT-WRITE: not enough args: hostname]"); - } - hostname = newstralloc(hostname, cmdargs.argv[a++]); - - if(a >= cmdargs.argc) { - error("error [taper PORT-WRITE: not enough args: features]"); - } - 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]"); - } - diskname = newstralloc(diskname, cmdargs.argv[a++]); - - if(a >= cmdargs.argc) { - error("error [taper PORT-WRITE: not enough args: level]"); - } - level = atoi(cmdargs.argv[a++]); - - if(a >= cmdargs.argc) { - error("error [taper PORT-WRITE: not enough args: datestamp]"); - } - datestamp = newstralloc(datestamp, cmdargs.argv[a++]); - - if(a >= cmdargs.argc) { - error("error [taper PORT-WRITE: not enough args: splitsize]"); - } - splitsize = atoi(cmdargs.argv[a++]); - - if(a >= cmdargs.argc) { - error("error [taper PORT-WRITE: not enough args: split_diskbuffer]"); - } - split_diskbuffer = newstralloc(split_diskbuffer, cmdargs.argv[a++]); - - if(a >= cmdargs.argc) { - error("error [taper PORT-WRITE: not enough args: fallback_splitsize]"); - } - fallback_splitsize = atoi(cmdargs.argv[a++]); - - if(a != cmdargs.argc) { - error("error [taper file_reader_side PORT-WRITE: too many args: %d != %d]", - cmdargs.argc, a); - } - - snprintf(level_str, sizeof(level_str), "%d", level); - id_string = newvstralloc(id_string, hostname, ":", diskname, ".", - level_str, NULL); - - create_split_buffer(split_diskbuffer, fallback_splitsize, id_string); - amfree(id_string); - - data_port = 0; - data_socket = stream_server(&data_port, -1, STREAM_BUFSIZE); - 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, - -1, NETWORK_BLOCK_BYTES)) == -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,diskname,datestamp,level)); - - aclose(data_socket); - free_split_buffer(); - 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]"); - } - handle = newstralloc(handle, cmdargs.argv[a++]); - - if(a >= cmdargs.argc) { - error("error [taper FILE-WRITE: not enough args: filename]"); - } - filename = newstralloc(filename, cmdargs.argv[a++]); - - if(a >= cmdargs.argc) { - error("error [taper FILE-WRITE: not enough args: hostname]"); - } - hostname = newstralloc(hostname, cmdargs.argv[a++]); - - if(a >= cmdargs.argc) { - error("error [taper FILE-WRITE: not enough args: features]"); - } - 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]"); - } - diskname = newstralloc(diskname, cmdargs.argv[a++]); - - if(a >= cmdargs.argc) { - error("error [taper FILE-WRITE: not enough args: level]"); - } - level = atoi(cmdargs.argv[a++]); - - if(a >= cmdargs.argc) { - error("error [taper FILE-WRITE: not enough args: datestamp]"); - } - datestamp = newstralloc(datestamp, cmdargs.argv[a++]); - - if(a >= cmdargs.argc) { - error("error [taper FILE-WRITE: not enough args: splitsize]"); - } - splitsize = atoi(cmdargs.argv[a++]); - - if(a != cmdargs.argc) { - error("error [taper file_reader_side FILE-WRITE: too many args: %d != %d]", - cmdargs.argc, a); - } - 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,diskname,datestamp,level)){ - if(splitsize > 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); - syncpipe_put('Q', 0); /* tell writer we're exiting gracefully */ - aclose(wrpipe); - - if((wpid = wait(NULL)) != writerpid) { - fprintf(stderr, - "taper: writer wait returned %d instead of %d: %s\n", - wpid, writerpid, strerror(errno)); - fflush(stderr); - } - - if (datestamp != NULL) - amfree(datestamp); - amfree(label); - amfree(errstr); - amfree(changer_resultstr); - amfree(tapedev); - amfree(conf_tapelist); - amfree(filename); - amfree(config_dir); - amfree(config_name); - if(holdfile_name != NULL) 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); - - 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; - } } - amfree(handle); - am_release_feature_set(their_features); - amfree(hostname); - amfree(diskname); - fprintf(stderr, "TAPER AT END OF READER SIDE\n"); + amfree(timestamp); + return TRUE; } -void dumpbufs(str1) -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++); - 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; - } +typedef struct { + taper_state_t * state; + gboolean prolong; /* scan stops when this is FALSE. */ + char *errmsg; +} tape_search_request_t; - } - fputc('\n', stderr); - fflush(stderr); -} +/* A GThread that runs taper_scan. */ +static gpointer tape_search_thread(gpointer data) { + tape_search_request_t * request = data; -void dumpstatus(bp) -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%d", bp->size); - break; - case FILLING: status[0] = 'f'; status[1] = '\0'; break; - case EMPTY: status[0] = 'E'; status[1] = '\0'; break; - default: - snprintf(status, sizeof(status), "%ld", bp->status); - break; + if (request->state->next_tape_label != NULL && + request->state->next_tape_device != NULL) { + return GINT_TO_POINTER(TRUE); + } else { + amfree(request->state->next_tape_label); + amfree(request->state->next_tape_device); } - str = vstralloc("taper: ", pn, ": [buf ", bt, ":=", status, "]", NULL); - dumpbufs(str); - amfree(str); + return GINT_TO_POINTER + (simple_taper_scan(request->state, + &(request->prolong), + &(request->errmsg))); } -/* - 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') -*/ -int get_next_holding_file(fd, bp, strclosing, rc) - int fd; - buffer_t *bp; - char *strclosing; -{ - int save_fd, rc1; - struct stat stat_file; - int 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; - holdfile_path = stralloc(file.cont_filename); - - fprintf(stderr, "taper: r: switching to next holding chunk '%s'\n", file.cont_filename); - num_holdfile_chunks++; - - bp1.status = EMPTY; - bp1.size = DISK_BLOCK_BYTES; - bp1.buffer = malloc(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, rc1); - - amfree(bp1.buffer); - bp1.buffer = bp->buffer + rc; - - rc1 = taper_fill_buffer(fd, &bp1, 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++; - } - } +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++; } - - return(ret); + 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; + struct cmdargs *cmdargs; + cmd_t cmd; -int read_file(fd, handle, hostname, diskname, datestamp, level) - int fd, level; - char *handle, *hostname, *diskname, *datestamp; -{ - buffer_t *bp; - char tok; - int rc, opening, closing, bufnum, need_closing, nexting; - long filesize; - times_t runtime; - char *strclosing = NULL; - char seekerrstr[STR_SIZE]; - char *str; - int header_written = 0; - int buflen; - dumpfile_t first_file; - dumpfile_t cur_holdfile; - long kbytesread = 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 */ - - filesize = 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 <= 0){ - fh_init(&file); - header_read = 0; - } - else if(mode == MODE_FILE_WRITE){ - memcpy(&file, save_holdfile, sizeof(dumpfile_t)); - memcpy(&cur_holdfile, save_holdfile, sizeof(dumpfile_t)); + if (state->device != NULL) { + return TRUE; } - if(bufdebug) { - fprintf(stderr, "taper: r: start file\n"); - fflush(stderr); - } + /* We save the value here in case it changes while we're running. */ + use_threads = g_thread_supported(); - for(bp = buftable; bp < buftable + conf_tapebufs; bp++) { - bp->status = EMPTY; + search_request.state = state; + search_request.prolong = TRUE; + search_request.errmsg = NULL; + if (use_threads) { + tape_search = g_thread_create(tape_search_thread, + &search_request, TRUE, NULL); } + + putresult(REQUEST_NEW_TAPE, "%s\n", dump->handle); + cmdargs = getcmd(); + cmd = cmdargs->cmd; - bp = buftable; - if(interactive || bufdebug) dumpstatus(bp); - - if(cur_span_chunkstart >= 0 && splitsize > 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 > 0){ - fprintf(stderr, "taper: r: seeking %s to " OFF_T_FMT " kb\n", - holdfile_path_thischunk, 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 <%ld Mb", holdfile_offset_thischunk, (int)(sizeof(off_t) * 8), (long)(maxseek/1024)); - log_add(L_ERROR, "%s", seekerrstr); - fprintf(stderr, "taper: r: FATAL: %s\n", seekerrstr); - fflush(stderr); - syncpipe_put('X', 0); - return -1; - } - if(lseek(fd, holdfile_offset_thischunk*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", holdfile_path_thischunk, holdfile_offset_thischunk, strerror(errno)); - fflush(stderr); - syncpipe_put('X', 0); - return -1; - } + 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)); } - 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 (search_result) { + /* We don't say NEW_TAPE until we actually write the label. */ + amfree(search_request.errmsg); + free_cmdargs(cmdargs); + return TRUE; + } else { + putresult(NO_NEW_TAPE, "%s\n", dump->handle); + log_taper_scan_errmsg(search_request.errmsg); + log_add(L_ERROR, "no-tape [%s]", "No more writable valid tape found"); + free_cmdargs(cmdargs); + return FALSE; } - if(cur_span_chunkstart > 0) header_read = 1; /* really initialized in prior run */ } - - /* tell writer to open tape */ - - opening = 1; - syncpipe_put('O', 0); - syncpipe_putstr(datestamp); - syncpipe_putstr(hostname); - syncpipe_putstr(diskname); - syncpipe_putint(level); - - startclock(); - - /* read file in loop */ - - while(1) { - tok = syncpipe_get(&bufnum); - switch(tok) { - - case 'O': - assert(opening); - opening = 0; - err = 0; - break; - - case 'R': - if(bufdebug) { - fprintf(stderr, "taper: r: got R%d\n", bufnum); - fflush(stderr); - } - - if(need_closing) { - syncpipe_put('C', 0); - closing = 1; - need_closing = 0; - break; - } - - if(closing) break; /* ignore extra read tokens */ - - assert(!opening); - if(bp->status != EMPTY || bufnum != bp-buftable) { - /* XXX this SHOULD NOT HAPPEN. Famous last words. */ - fprintf(stderr,"taper: panic: buffer mismatch at ofs %ld:\n", - filesize); - if(bufnum != 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 = 0; - amfree(q); - log_add(L_INFO, "retrying %s:%s.%d on new tape due to: %s", - hostname, diskname, level, errstr); - closing = 1; - syncpipe_put('X', 0); /* X == buffer snafu, bail */ - do { - tok = syncpipe_get(&bufnum); - } while(tok != 'x'); - aclose(fd); - return -1; - } /* end 'if (bf->status != EMPTY || bufnum != bp-buftable)' */ - - bp->status = FILLING; - buflen = header_read ? tt_blocksize : DISK_BLOCK_BYTES; - if(interactive || bufdebug) dumpstatus(bp); - if(header_written == 0 && (header_read == 1 || cur_span_chunkstart > 0)){ - /* for split dumpfiles, modify headers for the second - nth - pieces that signify that they're continuations of the last - normal one */ - char *cont_filename; - file.type = F_SPLIT_DUMPFILE; - file.partnum = num_splits + 1; - file.totalparts = expected_splits; - cont_filename = stralloc(file.cont_filename); - file.cont_filename[0] = '\0'; - build_header(bp->buffer, &file, tt_blocksize); - - if(cont_filename[0] != '\0') { - file.type = F_CONT_DUMPFILE; - strncpy(file.cont_filename, cont_filename, - sizeof(file.cont_filename)); - } - memcpy(&cur_holdfile, &file, sizeof(dumpfile_t)); - - if(interactive || bufdebug) dumpstatus(bp); - bp->size = tt_blocksize; - rc = 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 = tt_blocksize; - rc = 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); - syncpipe_put('C', 0); - } - - if(!closing) { - if(rc < buflen) { /* switch to next holding file */ - int ret; - if(file.cont_filename[0] != '\0'){ - cur_filename = newvstralloc(cur_filename, file.cont_filename, NULL); - } - ret = get_next_holding_file(fd, bp, strclosing, 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, rc); - parse_file_header(bp->buffer, &first_file, rc); - cont_filename = stralloc(file.cont_filename); - file.cont_filename[0] = '\0'; - if(splitsize > 0){ - file.type = F_SPLIT_DUMPFILE; - file.partnum = 1; - file.totalparts = expected_splits; - } - file.blocksize = tt_blocksize; - build_header(bp->buffer, &file, tt_blocksize); - kbytesread += tt_blocksize/1024; /* XXX shady */ - - file.type = F_CONT_DUMPFILE; - - /* add CONT_FILENAME back to in-memory header */ - strncpy(file.cont_filename, cont_filename, - sizeof(file.cont_filename)); - if(interactive || bufdebug) dumpstatus(bp); - bp->size = tt_blocksize; /* output a full tape block */ - /* save the header, we'll need it if we jump tapes */ - memcpy(&cur_holdfile, &file, sizeof(dumpfile_t)); - header_read = 1; - header_written = 1; - amfree(cont_filename); - } - else { - filesize = kbytesread; - } - - if(bufdebug) { - fprintf(stderr,"taper: r: put W%d\n",(int)(bp-buftable)); - fflush(stderr); - } - syncpipe_put('W', bp-buftable); - bp = nextbuf(bp); - } - - if(kbytesread + DISK_BLOCK_BYTES/1024 >= splitsize && splitsize > 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 %ldkb after %ldkb (next chunk will start at %ldkb)\n", hostname, diskname, datestamp, level, num_splits+1, cur_span_chunkstart, kbytesread, cur_span_chunkstart+kbytesread); - fflush(stderr); - - nexting = 1; - need_closing = 1; - } /* end '(kbytesread >= splitsize && splitsize > 0)' */ - if(need_closing && rc <= 0) { - syncpipe_put('C', 0); - need_closing = 0; - closing = 1; - } - kbytesread += rc/1024; - } /* end the 'if(!closing)' (successful buffer fill) */ - break; - - case 'T': - case 'E': - syncpipe_put('e', 0); /* ACK error */ - - str = syncpipe_getstr(); - errstr = newvstralloc(errstr, "[", str ? str : "(null)", "]", NULL); - amfree(str); - - q = squote(errstr); - if(tok == 'T') { - if(splitsize > 0){ - /* we'll be restarting this chunk on the next tape */ - if(mode == MODE_FILE_WRITE){ - aclose(fd); - } - - putresult(SPLIT_NEEDNEXT, "%s %ld\n", handle, cur_span_chunkstart); - log_add(L_INFO, "continuing %s:%s.%d on new tape from %ldkb mark: %s", - hostname, diskname, level, 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 = 0; - log_add(L_INFO, "retrying %s:%s.%d on new tape due to: %s", - hostname, diskname, 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, diskname, datestamp, level); - log_add(L_ERROR,"no-tape [%s]", "No more writable valid tape found"); - } - amfree(q); - - return 0; - - case 'C': - assert(!opening); - assert(closing); - - if(nexting){ - cur_span_chunkstart += kbytesread; /* XXX possibly wrong */ - holdfile_name = newvstralloc(holdfile_name, cur_filename, NULL); - - kbytesread = 0; - if(cur_filename != NULL) amfree(cur_filename); - } - - - str = syncpipe_getstr(); - label = newstralloc(label, str ? str : "(null)"); - amfree(str); - str = syncpipe_getstr(); - 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){ - log_add(L_FAIL, "%s %s %s.%d %d %s", hostname, diskname, - datestamp, num_splits, level, errstr); - } - else{ - log_add(L_FAIL, "%s %s %s %d %s", - hostname, diskname, datestamp, level, errstr); - } - str = syncpipe_getstr(); /* reap stats */ - amfree(str); - amfree(errstr); - } else { - char kb_str[NUM_STR_SIZE]; - char kps_str[NUM_STR_SIZE]; - double rt; - - rt = runtime.r.tv_sec+runtime.r.tv_usec/1000000.0; - curdump_rt = timesadd(runtime, curdump_rt); - snprintf(kb_str, sizeof(kb_str), "%ld", filesize); - snprintf(kps_str, sizeof(kps_str), "%3.1f", - rt ? filesize / rt : 0.0); - str = syncpipe_getstr(); - errstr = newvstralloc(errstr, - "[sec ", walltime_str(runtime), - " kb ", kb_str, - " kps ", kps_str, - " ", str, - "]", - NULL); - q = squote(errstr); - if (splitsize == 0) { /* Ordinary dump */ - 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, diskname, 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, diskname, datestamp, level, errstr); - } - } 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))/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, diskname, - datestamp, num_splits, level, errstr); - if(!nexting){ /* split dump complete */ - rt =curdump_rt.r.tv_sec+curdump_rt.r.tv_usec/1000000.0; - snprintf(kb_str, sizeof(kb_str), "%ld", - filesize+cur_span_chunkstart); - snprintf(kps_str, sizeof(kps_str), "%3.1f", - rt ? (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, diskname, 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 = 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, diskname, 20); - - if ((len = strlen(diskname)) <= 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 */ - syncpipe_put('L', filenum); - syncpipe_putstr(vol_label); - - /* - * 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 */ - syncpipe_put('D', filenum); - syncpipe_putstr(vol_date); - -#endif /* HAVE_LIBVTBLC */ - } - /* reset stuff that assumes we're on a new file */ - - if(nexting){ - opening = 1; - nexting = 0; - closing = 0; - filesize = 0; - syncpipe_put('O', 0); - syncpipe_putstr(datestamp); - syncpipe_putstr(hostname); - syncpipe_putstr(diskname); - syncpipe_putint(level); - for(bp = buftable; bp < buftable + conf_tapebufs; bp++) { - bp->status = EMPTY; - } - bp = buftable; - header_written = 0; - break; - } - else return 0; - - default: - assert(0); - } + case NO_NEW_TAPE: + search_request.prolong = FALSE; + if (use_threads) { + g_thread_join(tape_search); + } + log_add(L_ERROR, "no-tape [%s]", cmdargs->argv[1]); + state->last_errmsg = stralloc(cmdargs->argv[1]); + free_cmdargs(cmdargs); + return FALSE; } - - return 0; + /* NOTREACHED */ } -int taper_fill_buffer(fd, bp, buflen) -int fd; -buffer_t *bp; -int buflen; -{ - char *curptr; - int spaceleft, cnt; - - curptr = bp->buffer; - bp->size = 0; - spaceleft = buflen; - - cnt = fullread(fd, curptr, spaceleft); - switch(cnt) { - case 0: /* eof */ - if(interactive) fputs("r0", stderr); - return bp->size; - case -1: /* error on read, punt */ - if(interactive) fputs("rE", stderr); - return -1; - default: - if(mode == MODE_PORT_WRITE && splitsize > 0){ - memcpy(splitbuf_wr_ptr, curptr, (size_t)cnt); - splitbuf_wr_ptr += cnt; - } - spaceleft -= cnt; - curptr += cnt; - bp->size += cnt; - } - - if(interactive) fputs("R", stderr); - return bp->size; +/* 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; } -/* Given a dumpfile in holding, determine its size and figure out how many - * times we'd have to split it. - */ -int predict_splits(filename) -char *filename; +static void +update_tapelist( + taper_state_t *state) { - int splits = 0; - long total_kb = 0; - long adj_splitsize = splitsize - DISK_BLOCK_BYTES/1024; - - if(splitsize <= 0) return(0); + char *tapelist_name = NULL; + char *tapelist_name_old = NULL; + tape_t *tp; + char *comment = NULL; + + tapelist_name = config_dir_relative(getconf_str(CNF_TAPELIST)); + if (state->cur_tape == 0) { + tapelist_name_old = stralloc2(tapelist_name, ".yesterday"); + } else { + char cur_str[NUM_STR_SIZE]; + g_snprintf(cur_str, SIZEOF(cur_str), "%d", state->cur_tape - 1); + tapelist_name_old = vstralloc(tapelist_name, + ".today.", cur_str, NULL); + } + + if (read_tapelist(tapelist_name) != 0) { + log_add(L_INFO, "pid-done %ld", (long)getpid()); + error("could not load tapelist \"%s\"", tapelist_name); + /*NOTREACHED*/ + } + + /* make a copy of the tapelist file */ + if (write_tapelist(tapelist_name_old)) { + log_add(L_INFO, "pid-done %ld", (long)getpid()); + error("could not write tapelist: %s", strerror(errno)); + /*NOTREACHED*/ + } + amfree(tapelist_name_old); + + /* get a copy of the comment, before freeing the old record */ + tp = lookup_tapelabel(state->device->volume_label); + if (tp && tp->comment) + comment = stralloc(tp->comment); + + /* edit the tapelist and rewrite it */ + remove_tapelabel(state->device->volume_label); + add_tapelabel(state->driver_start_time, + state->device->volume_label, + comment); + if (write_tapelist(tapelist_name)) { + error("could not write tapelist: %s", strerror(errno)); + /*NOTREACHED*/ + } + amfree(tapelist_name); + amfree(comment); +} - if(adj_splitsize <= 0){ - error("Split size must be > %ldk", DISK_BLOCK_BYTES/1024); +/* 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; } - - /* should only calculuate this once, not on retries etc */ - if(expected_splits != 0) return(expected_splits); - - total_kb = size_holding_files(filename, 1); - - if(total_kb <= 0){ - fprintf(stderr, "taper: r: %ld kb holding file makes no sense, not precalculating splits\n", total_kb); - fflush(stderr); - return(0); + state->total_bytes = 0; + + if (!find_new_tape(state, dump_info)) { + return FALSE; } - fprintf(stderr, "taper: r: Total dump size should be %ldkb, chunk size is %ldkb\n", total_kb, splitsize); - fflush(stderr); - - splits = total_kb/adj_splitsize; - if(total_kb % adj_splitsize) splits++; - - - fprintf(stderr, "taper: r: Expecting to split into %d parts \n", splits); - fflush(stderr); - - return(splits); + return label_new_tape(state, dump_info); } -/* - * ======================================================================== - * TAPE WRITER SIDE - * - */ -times_t idlewait, rdwait, wrwait, fmwait; -long total_writes; -double total_tape_used; -int total_tape_fm; +static gboolean label_new_tape(taper_state_t * state, dump_info_t * dump_info) { + char *old_volume_name = NULL; + char *old_volume_time = NULL; + tape_search_request_t request; + gboolean search_result; + DeviceStatusFlags status; -void write_file P((void)); -int write_buffer P((buffer_t *bp)); + /* 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. */ -void tape_writer_side(getp, putp) -int getp, putp; -{ - char tok; - int tape_started; - char *str; - char *hostname; - char *diskname; - char *datestamp; - int level; - int tmpint; + amfree(state->last_errmsg); + state->device = device_open(state->next_tape_device); + g_assert(state->device != NULL); + amfree(state->next_tape_device); -#ifdef HAVE_LIBVTBLC - char *vol_label; - char *vol_date; -#endif /* HAVE_LIBVTBLC */ + if (state->device->status != DEVICE_STATUS_SUCCESS) + goto skip_volume; - procname = "writer"; - syncpipe_init(getp, putp); + if (!device_configure(state->device, TRUE)) + goto skip_volume; - tape_started = 0; - idlewait = times_zero; + /* if we have an error, and are sure it isn't just an unlabeled volume, + * then skip this volume */ + status = device_read_label(state->device); + if ((status & ~DEVICE_STATUS_VOLUME_UNLABELED) && + !(status & DEVICE_STATUS_VOLUME_UNLABELED)) + goto skip_volume; - while(1) { - startclock(); - tok = syncpipe_get(&tmpint); - idlewait = timesadd(idlewait, stopclock()); - if(tok != 'S' && tok != 'Q' && !tape_started) { - error("writer: token '%c' before start", tok); - } + old_volume_name = g_strdup(state->device->volume_label); + old_volume_time = g_strdup(state->device->volume_time); - switch(tok) { - case 'S': /* start-tape */ - if(tape_started) { - error("writer: multiple start requests"); - } - str = syncpipe_getstr(); - if(!first_tape(str ? str : "bad-datestamp")) { - if(tape_fd >= 0) { - tapefd_close(tape_fd); - tape_fd = -1; - } - syncpipe_put('E', 0); - syncpipe_putstr(errstr); - /* wait for reader to acknowledge error */ - do { - tok = syncpipe_get(&tmpint); - if(tok != 'e') { - error("writer: got '%c' unexpectedly after error", tok); - } - } while(tok != 'e'); - } else { - syncpipe_put('S', 0); - tape_started = 1; - } - amfree(str); - - break; - - case 'O': /* open-output */ - datestamp = syncpipe_getstr(); - tapefd_setinfo_datestamp(tape_fd, datestamp); - amfree(datestamp); - hostname = syncpipe_getstr(); - tapefd_setinfo_host(tape_fd, hostname); - amfree(hostname); - diskname = syncpipe_getstr(); - tapefd_setinfo_disk(tape_fd, diskname); - amfree(diskname); - level = syncpipe_getint(); - tapefd_setinfo_level(tape_fd, level); - write_file(); - break; - -#ifdef HAVE_LIBVTBLC - case 'L': /* read vtbl label */ - vtbl_no = tmpint; - vol_label = syncpipe_getstr(); - 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; - vol_date = syncpipe_getstr(); - 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_datestamp); - amfree(label); - amfree(errstr); - amfree(changer_resultstr); - amfree(tapedev); - amfree(conf_tapelist); - amfree(config_dir); - amfree(config_name); + if (!device_start(state->device, ACCESS_WRITE, state->next_tape_label, + state->driver_start_time)) { + gboolean tape_used; - malloc_size_2 = malloc_inuse(&malloc_hist_2); + /* Something broke, see if we can tell if the volume was erased or + * not. */ + g_fprintf(stderr, "taper: Error writing label %s to device %s: %s.\n", + state->next_tape_label, state->device->device_name, + device_error_or_status(state->device)); - if(malloc_size_1 != malloc_size_2) { - malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2); - } + if (!device_finish(state->device)) + goto request_new_volume; - exit(0); + /* This time, if we can't read the label, assume we've overwritten + * the volume or otherwise corrupted it */ + status = device_read_label(state->device); + if ((status & ~DEVICE_STATUS_VOLUME_UNLABELED) && + !(status & DEVICE_STATUS_VOLUME_UNLABELED)) + goto request_new_volume; - default: - assert(0); - } - } -} + tape_used = check_volume_changed(state->device, old_volume_name, + old_volume_time); -void write_file() -{ - buffer_t *bp; - int full_buffers, i, bufnum; - char tok; - char number[NUM_STR_SIZE]; - char *rdwait_str, *wrwait_str, *fmwait_str; - int tmpint; - - rdwait = wrwait = times_zero; - total_writes = 0; - - bp = buftable; - full_buffers = 0; - tok = '?'; - - if(bufdebug) { - fprintf(stderr, "taper: w: start file\n"); - fflush(stderr); + if (tape_used) + goto request_new_volume; + else + goto skip_volume; } - /* - * Tell the reader that the tape is open, and give it all the buffers. - */ - syncpipe_put('O', 0); - for(i = 0; i < conf_tapebufs; i++) { - if(bufdebug) { - fprintf(stderr, "taper: w: put R%d\n", i); - fflush(stderr); - } - syncpipe_put('R', i); - } + amfree(old_volume_name); + amfree(old_volume_time); + amfree(state->next_tape_label); - /* - * 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. - */ + update_tapelist(state); + state->cur_tape++; - 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) { - tok = syncpipe_get(&bufnum); - if(tok != 'W') break; - if(bufdebug) { - fprintf(stderr,"taper: w: got W%d\n",bufnum); - fflush(stderr); - } - full_buffers++; - } - rdwait = timesadd(rdwait, stopclock()); - - /* - * STARTING MODE - * - * We start output when sufficient buffers have filled up, or at - * end-of-file, whichever comes first. Here we drain all the buffers - * that were waited on in STOPPED mode. If more full buffers come - * in, then we will be STREAMING. - */ - - while(full_buffers) { - if(tt_file_pad && bp->size < tt_blocksize) { - memset(bp->buffer+bp->size, 0, tt_blocksize - bp->size); - bp->size = 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) { - tok = syncpipe_get(&bufnum); - if(tok == 'W') { - if(bufdebug) { - fprintf(stderr,"taper: w: got W%d\n",bufnum); - fflush(stderr); - } - if(bufnum != bp-buftable) { - fprintf(stderr, - "taper: tape-writer: my buf %d reader buf %d\n", - (int)(bp-buftable), bufnum); - fflush(stderr); - syncpipe_put('E', 0); - syncpipe_putstr("writer-side buffer mismatch"); - goto error_ack; - } - if(tt_file_pad && bp->size < tt_blocksize) { - memset(bp->buffer+bp->size, 0, tt_blocksize - bp->size); - bp->size = 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); - } - } while(tok == 'W'); - - /* got close signal from reader, acknowledge it */ - - if(tok == 'X') - goto reader_buffer_snafu; - - assert(tok == 'C'); - syncpipe_put('C', 0); - - /* tell reader the tape and file number */ - - syncpipe_putstr(label); - snprintf(number, sizeof(number), "%d", filenum); - syncpipe_putstr(number); - - snprintf(number, sizeof(number), "%ld", 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); - syncpipe_putstr(errstr); - - /* XXX go to next tape if past tape size? */ - - return; - - tape_error: - /* got tape error */ - if(next_tape(1)) syncpipe_put('T', 0); /* next tape in place, try again */ - else syncpipe_put('E', 0); /* no more tapes, fail */ - syncpipe_putstr(errstr); - - error_ack: - /* wait for reader to acknowledge error */ - do { - tok = syncpipe_get(&tmpint); - if(tok != 'W' && tok != 'C' && tok != 'e') - error("writer: got '%c' unexpectedly after error", tok); - } while(tok != 'e'); - return; - - reader_buffer_snafu: - syncpipe_put('x', 0); - return; -} + if (state->have_changer && + changer_label("UNKNOWN", state->device->volume_label) != 0) { + error(_("couldn't update barcode database")); + /*NOTREACHED*/ + } -int write_buffer(bp) -buffer_t *bp; -{ - int rc; - - assert(bp->status == FULL); - - startclock(); - rc = tapefd_write(tape_fd, bp->buffer, bp->size); - if(rc == 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 += (double)rc; - bp->status = EMPTY; - if(interactive || bufdebug) dumpstatus(bp); - if(interactive) fputs("W", stderr); - - if(bufdebug) { - fprintf(stderr, "taper: w: put R%d\n", (int)(bp-buftable)); - fflush(stderr); - } - syncpipe_put('R', bp-buftable); - return 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); + + return TRUE; + +request_new_volume: + /* Tell the driver we overwrote this volume, even if it was empty, and request + * a new volume. */ + if (state->device) + state->last_errmsg = newstralloc(state->last_errmsg, device_error_or_status(state->device)); + else + state->last_errmsg = newstralloc(state->last_errmsg, "(unknown)"); + + putresult(NEW_TAPE, "%s %s\n", dump_info->handle, + state->next_tape_label); + if (old_volume_name) { + log_add(L_WARNING, "Problem writing label '%s' to volume %s " + "(volume may be erased): %s\n", + state->next_tape_label, old_volume_name, + state->last_errmsg); } else { - errstr = newvstralloc(errstr, - "writing file: ", - (rc != -1) ? "short write" : strerror(errno), - NULL); - wrwait = timesadd(wrwait, stopclock()); - if(interactive) fputs("[WE]", stderr); - return 0; + log_add(L_WARNING, "Problem writing label '%s' to new volume " + "(volume may be erased): %s\n", state->next_tape_label, + state->last_errmsg); } -} - - -static void -cleanup(void) -{ - REMOVE_SHARED_MEMORY(); -} + if (state->device) { + g_object_unref(state->device); + state->device = NULL; + } -/* - * Cleanup shared memory segments - */ -static void -signal_handler(int signum) -{ - log_add(L_INFO, "Received signal %d", signum); - - exit(1); -} + amfree(state->next_tape_label); + amfree(old_volume_name); + amfree(old_volume_time); + return find_and_label_new_tape(state, dump_info); -/* - * 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; +skip_volume: + /* grab a new volume without talking to the driver again -- we do this if we're + * confident we didn't overwrite the last tape we got. */ + if (state->device) + state->last_errmsg = newstralloc(state->last_errmsg, device_error_or_status(state->device)); + else + state->last_errmsg = newstralloc(state->last_errmsg, "(unknown)"); - act.sa_handler = signal_handler; - act.sa_flags = 0; - sigemptyset(&act.sa_mask); - - signal(SIGPIPE, SIG_IGN); - - if (sigaction(SIGINT, &act, NULL) != 0) { - error("taper: couldn't install SIGINT handler [%s]", strerror(errno)); + if (old_volume_name) { + log_add(L_WARNING, "Problem writing label '%s' to volume '%s' " + "(old volume data intact): %s\n", + state->next_tape_label, old_volume_name, state->last_errmsg); + } else { + log_add(L_WARNING, "Problem writing label '%s' to new volume " + "(old volume data intact): %s\n", state->next_tape_label, + state->last_errmsg); } - if (sigaction(SIGHUP, &act, NULL) != 0) { - error("taper: couldn't install SIGHUP handler [%s]", strerror(errno)); - } - - if (sigaction(SIGTERM, &act, NULL) != 0) { - error("taper: couldn't install SIGTERM handler [%s]", strerror(errno)); + if (state->device) { + g_object_unref(state->device); + state->device = NULL; } - if (sigaction(SIGUSR1, &act, NULL) != 0) { - error("taper: couldn't install SIGUSR1 handler [%s]", strerror(errno)); - } + amfree(state->next_tape_label); + amfree(old_volume_name); + amfree(old_volume_time); - if (sigaction(SIGUSR2, &act, NULL) != 0) { - error("taper: couldn't install SIGUSR2 handler [%s]", strerror(errno)); + request.state = state; + request.prolong = TRUE; + request.errmsg = NULL; + search_result = GPOINTER_TO_INT(tape_search_thread(&request)); + if (search_result) { + amfree(request.errmsg); + return label_new_tape(state, dump_info); + } else { + /* Problem finding a new tape! */ + log_taper_scan_errmsg(request.errmsg); + putresult(NO_NEW_TAPE, "%s\n", dump_info->handle); + return FALSE; } +} - if (sigaction(SIGALRM, &act, NULL) != 0) { - error("taper: couldn't install SIGALRM handler [%s]", strerror(errno)); +/* Find out if the dump is PARTIAL or not, and set the proper driver + and logfile tags for the dump. */ +static void find_completion_tags(dump_info_t * dump_info, /* IN */ + cmd_t * result_cmd, /* OUT */ + logtype_t * result_log /* OUT */) { + if (taper_source_is_partial(dump_info->source)) { + *result_cmd = PARTIAL; + *result_log = L_PARTIAL; + } else { + *result_cmd = DONE; + *result_log = L_DONE; } } +/* Put an L_PARTIAL message to the logfile. */ +static void put_partial_log(dump_info_t * dump_info, double dump_time, + guint64 dump_kbytes, char *errstr) { + char * qdiskname = quote_string(dump_info->diskname); + + log_add(L_PARTIAL, "%s %s %s %d %d [sec %f kb %ju kps %f] %s", + dump_info->hostname, qdiskname, dump_info->timestamp, + dump_info->current_part, dump_info->level, dump_time, + (uintmax_t)dump_kbytes, dump_kbytes / dump_time, + errstr); + amfree(qdiskname); +} -/* - * ======================================================================== - * SHARED-MEMORY BUFFER SUBSYSTEM - * - */ - -#ifdef HAVE_SYSVSHM - -int shmid = -1; - -char *attach_buffers(size) - unsigned int size; -{ - char *result; +/* Figure out what to do after a part attempt. Returns TRUE if another + attempt should proceed for this dump; FALSE if we are done. */ +static gboolean finish_part_attempt(taper_state_t * taper_state, + dump_info_t * dump_info, + queue_result_flags queue_result, + GTimeVal run_time, guint64 run_bytes) { + double part_time = g_timeval_to_double(run_time); + guint64 part_kbytes = run_bytes / 1024; + double part_kbps = run_bytes / (1024 * part_time); + + char * qdiskname = quote_string(dump_info->diskname); + + if (queue_result == QUEUE_SUCCESS) { + dump_info->total_time = timesadd(run_time, dump_info->total_time); + dump_info->total_bytes += run_bytes; + + log_add(L_PART, "%s %d %s %s %s %d/%d %d [sec %f kb %ju kps %f]", + taper_state->device->volume_label, + taper_state->device->file, dump_info->hostname, qdiskname, + dump_info->timestamp, dump_info->current_part, + taper_source_predict_parts(dump_info->source), + dump_info->level, part_time, (uintmax_t)part_kbytes, part_kbps); + putresult(PARTDONE, "%s %s %d %ju \"[sec %f kb %ju kps %f]\"\n", + dump_info->handle, taper_state->device->volume_label, + taper_state->device->file, (uintmax_t)part_kbytes, part_time, + (uintmax_t)part_kbytes, part_kbps); + taper_state->total_bytes += run_bytes; + + if (taper_source_get_end_of_data(dump_info->source)) { + cmd_t result_cmd; + logtype_t result_log; + double dump_time = g_timeval_to_double(dump_info->total_time); + guint64 dump_kbytes = dump_info->total_bytes / 1024; + double dump_kbps = dump_info->total_bytes / (1024 * dump_time); + + find_completion_tags(dump_info, &result_cmd, &result_log); + + g_object_unref(dump_info->source); + dump_info->source = NULL; + + log_add(result_log, "%s %s %s %d %d [sec %f kb %ju kps %f]", + dump_info->hostname, qdiskname, dump_info->timestamp, + dump_info->current_part, dump_info->level, dump_time, + (uintmax_t)dump_kbytes, dump_kbps); + putresult(result_cmd, "%s INPUT-GOOD TAPE-GOOD " + "\"[sec %f kb %ju kps %f]\" \"\" \"\"\n", + dump_info->handle, dump_time, (uintmax_t)dump_kbytes, + dump_kbps); + + amfree(qdiskname); + return FALSE; + } else if (taper_source_get_end_of_part(dump_info->source)) { + taper_source_start_new_part(dump_info->source); + dump_info->current_part ++; + amfree(qdiskname); + return TRUE; + } + /* If we didn't read EOF or EOP, then an error + occured. But we read QUEUE_SUCCESS, so something is + b0rked. */ + g_assert_not_reached(); + } else { + char * volume_label = strdup(taper_state->device->volume_label); + int file_number = taper_state->device->file; + double dump_time, dump_kbps; + guint64 dump_kbytes; + char *producer_errstr = quote_string( + taper_source_get_errmsg(dump_info->source)); + char *consumer_errstr = quote_string( + device_error(taper_state->device)); + + log_add(L_PARTPARTIAL, + "%s %d %s %s %s %d/%d %d [sec %f kb %ju kps %f] %s", + volume_label, file_number, dump_info->hostname, qdiskname, + dump_info->timestamp, dump_info->current_part, + taper_source_predict_parts(dump_info->source), + dump_info->level, part_time, (uintmax_t)part_kbytes, part_kbps, + consumer_errstr); + log_add(L_INFO, "tape %s kb %lld fm %d [OK]\n", + volume_label, + (long long)((taper_state->total_bytes+(off_t)1023) / (off_t)1024), + taper_state->device->file); + + /* A problem occured. */ + if (queue_result & QUEUE_CONSUMER_ERROR) { + /* Make a note if this was EOM (we treat EOM the same as any other error, + * so it's just for debugging purposes */ + if (taper_state->device->is_eof) + g_debug("device %s ran out of space", taper_state->device->device_name); + + /* Close the device. */ + device_finish(taper_state->device); + g_object_unref(taper_state->device); + taper_state->device = NULL; + } + + amfree(volume_label); + + if ((queue_result & QUEUE_CONSUMER_ERROR) && + (!(queue_result & QUEUE_PRODUCER_ERROR)) && + taper_source_seek_to_part_start(dump_info->source)) { + /* It is recoverable. */ + log_add(L_INFO, "Will request retry of failed split part."); + if (find_and_label_new_tape(taper_state, dump_info)) { + /* dump_info->current_part is unchanged. */ + amfree(qdiskname); + return TRUE; + } + } - shmid = shmget(IPC_PRIVATE, size, IPC_CREAT|0700); - if(shmid == -1) { - return NULL; + dump_info->total_time = timesadd(run_time, dump_info->total_time); + dump_info->total_bytes += run_bytes; + dump_time = g_timeval_to_double(dump_info->total_time); + dump_kbytes = dump_info->total_bytes / 1024; + dump_kbps = dump_info->total_bytes / (1024 * dump_time); + + putresult(PARTIAL, + "%s INPUT-%s TAPE-%s " + "\"[sec %f kb %ju kps %f]\" %s %s\n", + dump_info->handle, + (queue_result & QUEUE_PRODUCER_ERROR) ? "ERROR" : "GOOD", + (queue_result & QUEUE_CONSUMER_ERROR) ? "ERROR" : "GOOD", + dump_time, (uintmax_t)dump_kbytes, dump_kbps, + producer_errstr, consumer_errstr); + if (queue_result & QUEUE_CONSUMER_ERROR) { + put_partial_log(dump_info, dump_time, dump_kbytes, + consumer_errstr); + } else { + put_partial_log(dump_info, dump_time, dump_kbytes, + producer_errstr); + } + amfree(producer_errstr); + amfree(consumer_errstr); } - result = (char *)shmat(shmid, (SHM_ARG_TYPE *)NULL, 0); + amfree(qdiskname); + return FALSE; +} - if(result == (char *)-1) { - int save_errno = errno; +/* 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); - destroy_buffers(); - errno = save_errno; - error("shmat: %s", strerror(errno)); + if (rval == NULL) { + return NULL; } - return result; -} + rval->cont_filename[0] = '\0'; + expected_splits = taper_source_predict_parts(dump_info->source); -void detach_buffers(bufp) - char *bufp; -{ - if ((bufp != NULL) && - (shmdt((SHM_ARG_TYPE *)bufp) == -1)) { - error("shmdt: %s", strerror(errno)); + if (expected_splits != 1) { + rval->type = F_SPLIT_DUMPFILE; + rval->partnum = dump_info->current_part; + rval->totalparts = expected_splits; } + + return rval; } -void destroy_buffers() +/* We call this when we can't find a tape to write data to. This could + happen with the first (or only) part of a file, but it could also + happen with an intermediate part of a split dump. dump_bytes + is 0 if this is the first part of a dump. */ +static void bail_no_volume( + dump_info_t *dump_info, + char *errmsg) { - if(shmid == -1) return; /* nothing to destroy */ - if(shmctl(shmid, IPC_RMID, NULL) == -1) { - error("shmctl: %s", strerror(errno)); - } + char *errstr; + if (errmsg) + errstr = quote_string(errmsg); + else + errstr = quote_string("no new tape"); + if (dump_info->total_bytes > 0) { + /* Second or later part of a split dump, so PARTIAL message. */ + double dump_time = g_timeval_to_double(dump_info->total_time); + guint64 dump_kbytes = dump_info->total_bytes / 1024; + double dump_kbps = dump_kbytes / dump_time; + putresult(PARTIAL, + "%s INPUT-GOOD TAPE-ERROR " + "\"[sec %f kb %ju kps %f]\" \"\" %s\n", + dump_info->handle, + dump_time, (uintmax_t)dump_kbytes, dump_kbps, errstr); + put_partial_log(dump_info, dump_time, dump_kbytes, errstr); + } else { + char * qdiskname = quote_string(dump_info->diskname); + putresult(FAILED, + "%s INPUT-GOOD TAPE-ERROR \"\" %s\n", + dump_info->handle, errstr); + log_add(L_FAIL, "%s %s %s %d %s", + dump_info->hostname, qdiskname, dump_info->timestamp, + dump_info->level, errstr); + amfree(qdiskname); + } + amfree(errstr); } -#else -#ifdef HAVE_MMAP +/* Link up the TaperSource with the Device, including retries etc. */ +static void run_device_output(taper_state_t * taper_state, + dump_info_t * dump_info) { + GValue val; + guint file_number; + dump_info->current_part = 1; + dump_info->total_time.tv_sec = 0; + dump_info->total_time.tv_usec = 0; + dump_info->total_bytes = 0; + + for (;;) { + GTimeVal start_time, end_time, run_time; + StreamingRequirement streaming_mode; + queue_result_flags queue_result; + CountingConsumerData consumer_data; + dumpfile_t *this_header; + size_t max_memory; + + this_header = munge_headers(dump_info); + if (this_header == NULL) { + char * qdiskname = quote_string(dump_info->diskname); + char * errstr = taper_source_get_errmsg(dump_info->source); + if (!errstr) + errstr = "Failed reading dump header."; + errstr = quote_string(errstr); + putresult(FAILED, + "%s INPUT-ERROR TAPE-GOOD %s \"\"\n", + dump_info->handle, errstr); + log_add(L_FAIL, "%s %s %s %d %s", + dump_info->hostname, qdiskname, dump_info->timestamp, + dump_info->level, errstr); + amfree(qdiskname); + amfree(errstr); + return; + } -#ifdef HAVE_SYS_MMAN_H -#include -#endif + if (!find_and_label_new_tape(taper_state, dump_info)) { + bail_no_volume(dump_info, taper_state->last_errmsg); + dumpfile_free(this_header); + return; + } -#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 + while (!device_start_file(taper_state->device, this_header)) { + /* Close the device. */ + device_finish(taper_state->device); + g_object_unref(taper_state->device); + taper_state->device = NULL; -int shmfd = -1; -unsigned int saved_size; + if (!find_and_label_new_tape(taper_state, dump_info)) { + bail_no_volume(dump_info, taper_state->last_errmsg); + dumpfile_free(this_header); + return; + } + } + dumpfile_free(this_header); + + bzero(&val, sizeof(val)); + if (!device_property_get(taper_state->device, PROPERTY_STREAMING, &val) + || !G_VALUE_HOLDS(&val, STREAMING_REQUIREMENT_TYPE)) { + g_fprintf(stderr, "taper: Couldn't get streaming type!\n"); + streaming_mode = STREAMING_REQUIREMENT_REQUIRED; + } else { + streaming_mode = g_value_get_enum(&val); + } + + file_number = taper_state->device->file; + + consumer_data.next_consumer = device_write_consumer; + consumer_data.next_consumer_data = taper_state->device; + consumer_data.bytes_written = 0; + + g_get_current_time(&start_time); + + if (getconf_seen(CNF_DEVICE_OUTPUT_BUFFER_SIZE)) { + max_memory = getconf_size(CNF_DEVICE_OUTPUT_BUFFER_SIZE); + if (getconf_seen(CNF_TAPEBUFS)) { + g_fprintf(stderr, + "Configuration directives 'device_output_buffer_size' " + "and \n" + "'tapebufs' are incompatible; using former.\n"); + } + } else if (getconf_seen(CNF_TAPEBUFS)) { + max_memory = getconf_int(CNF_TAPEBUFS) * + taper_state->device->block_size; + } else { + /* Use default. */ + max_memory = getconf_size(CNF_DEVICE_OUTPUT_BUFFER_SIZE); + } -char *attach_buffers(size) - unsigned int 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)); - } -#endif + queue_result = do_consumer_producer_queue_full + (taper_source_producer, + dump_info->source, + counting_consumer, + &consumer_data, + taper_state->device->block_size, max_memory, + streaming_mode); - saved_size = size; - shmbuf = (char *) mmap((void *) 0, - size, - PROT_READ|PROT_WRITE, - MAP_ANON|MAP_SHARED, - shmfd, 0); + g_get_current_time(&end_time); + run_time = timesub(end_time, start_time); - return shmbuf; -} + /* The device_write_consumer leaves the file open, so close it now. */ + if (!device_finish_file(taper_state->device)) { + queue_result = queue_result | QUEUE_CONSUMER_ERROR; + } -void detach_buffers(bufp) -char *bufp; -{ - if ((bufp != NULL) && - (munmap((void *)bufp, saved_size) == -1)) { - error("detach_buffers: munmap: %s", strerror(errno)); + if (!finish_part_attempt(taper_state, dump_info, queue_result, + run_time, consumer_data.bytes_written)) { + break; + } } - - if (shmfd != -1) - aclose(shmfd); } -void destroy_buffers() -{ -} - -#else -#error: must define either HAVE_SYSVSHM or HAVE_MMAP! -#endif -#endif - +/* Handle a PORT_WRITE command. */ +static void process_port_write(taper_state_t * state, + struct cmdargs * cmdargs) { + dump_info_t dump_state; + guint64 splitsize; + guint64 fallback_splitsize; + char * split_diskbuffer; + char * argnames[] = {"command", /* 0 */ + "handle", /* 1 */ + "hostname", /* 2 */ + "diskname", /* 3 */ + "level", /* 4 */ + "datestamp", /* 5 */ + "splitsize", /* 6 */ + "split_diskbuffer", /* 7 */ + "fallback_splitsize", /* 8 */ + NULL }; + + validate_args(cmdargs, argnames); + + dump_state.handle = g_strdup(cmdargs->argv[1]); + dump_state.hostname = g_strdup(cmdargs->argv[2]); + dump_state.diskname = g_strdup(cmdargs->argv[3]); + + errno = 0; + dump_state.level = strtol(cmdargs->argv[4], NULL, 10); + if (errno != 0) { + error("error [taper PORT-WRITE: Invalid dump level %s]", + cmdargs->argv[4]); + g_assert_not_reached(); + } + + dump_state.timestamp = strdup(cmdargs->argv[5]); + errno = 0; + splitsize = g_ascii_strtoull(cmdargs->argv[6], NULL, 10); + if (errno != 0) { + error("error [taper PORT-WRITE: Invalid splitsize %s]", + cmdargs->argv[6]); + g_assert_not_reached(); + } + + if (strcmp(cmdargs->argv[7], "NULL") == 0) { + split_diskbuffer = NULL; + } else { + split_diskbuffer = g_strdup(cmdargs->argv[7]); + } + + errno = 0; + fallback_splitsize = g_ascii_strtoull(cmdargs->argv[8], NULL, 10); + if (errno != 0) { + error("error [taper PORT-WRITE: Invalid fallback_splitsize %s]", + cmdargs->argv[8]); + g_assert_not_reached(); + } -/* - * ======================================================================== - * SYNC-PIPE SUBSYSTEM - * - */ + 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); -int getpipe, putpipe; + run_device_output(state, &dump_state); -void syncpipe_init(rd, wr) -int rd, wr; -{ - getpipe = rd; - putpipe = wr; + free_dump_info(&dump_state); } -char syncpipe_get(intp) -int *intp; -{ - int rc; - char buf[sizeof(char) + sizeof(int)]; - - rc = fullread(getpipe, buf, sizeof(buf)); - if(rc == 0) /* EOF */ - error("syncpipe_get: %c: unexpected EOF", *procname); - else if(rc < 0) - error("syncpipe_get: %c: %s", *procname, strerror(errno)); - else if(rc != sizeof(buf)) - error("syncpipe_get: %s", "short read"); - - if(bufdebug && *buf != 'R' && *buf != 'W') { - fprintf(stderr,"taper: %c: getc %c\n",*procname,*buf); - fflush(stderr); +/* Handle a FILE_WRITE command. */ +static void process_file_write(taper_state_t * state, + struct cmdargs * cmdargs) { + dump_info_t dump_state; + char * holding_disk_file; + guint64 splitsize; + char * argnames[] = {"command", /* 0 */ + "handle", /* 1 */ + "filename", /* 2 */ + "hostname", /* 3 */ + "diskname", /* 4 */ + "level", /* 5 */ + "datestamp", /* 6 */ + "splitsize", /* 7 */ + NULL }; + + validate_args(cmdargs, argnames); + + dump_state.handle = g_strdup(cmdargs->argv[1]); + holding_disk_file = g_strdup(cmdargs->argv[2]); + dump_state.hostname = g_strdup(cmdargs->argv[3]); + dump_state.diskname = g_strdup(cmdargs->argv[4]); + + errno = 0; + dump_state.level = strtol(cmdargs->argv[5], NULL, 10); + if (errno != 0) { + error("error [taper FILE-WRITE: Invalid dump level %s]", + cmdargs->argv[5]); + g_assert_not_reached(); } + + dump_state.timestamp = strdup(cmdargs->argv[6]); - memcpy(intp, &buf[1], sizeof(int)); - return buf[0]; -} + errno = 0; + splitsize = g_ascii_strtoull(cmdargs->argv[7], NULL, 10); + if (errno != 0) { + error("error [taper FILE-WRITE: Invalid splitsize %s]", + cmdargs->argv[7]); + g_assert_not_reached(); + } -int syncpipe_getint() -{ - int rc, i; + 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); - if ((rc = fullread(getpipe, &i, sizeof(i))) != sizeof(i)) - error("syncpipe_getint: %s", rc < 0 ? strerror(errno) : "short read"); + run_device_output(state, &dump_state); - return (i); + free_dump_info(&dump_state); + amfree(holding_disk_file); } - -char *syncpipe_getstr() -{ - int rc, len; - char *str; - - if((len = syncpipe_getint()) <= 0) { - error("syncpipe_getstr: Protocol error - Invalid length (%d)", len); - /* NOTREACHED */ - } - - str = alloc(len); - - if ((rc = fullread(getpipe, str, len)) != len) { - error("syncpipe_getstr: %s", rc < 0 ? strerror(errno) : "short read"); - /* NOTREACHED */ - } - - return (str); +/* Send QUITTING message to driver and associated logging. Always + returns false. */ +static gboolean send_quitting(taper_state_t * state) { + putresult(QUITTING, "\n"); + g_fprintf(stderr,"taper: DONE\n"); + cleanup(state); + return FALSE; } +/* This function recieves the START_TAPER command from driver, and + returns the attached timestamp. */ +static gboolean find_first_tape(taper_state_t * state) { + struct cmdargs *cmdargs; + tape_search_request_t search_request; + GThread * tape_search = NULL; + gboolean use_threads; -void syncpipe_put(chi, intval) -int chi; -int intval; -{ - char buf[sizeof(char) + sizeof(int)]; + /* We save the value here in case it changes while we're running. */ + use_threads = g_thread_supported(); - buf[0] = (char)chi; - memcpy(&buf[1], &intval, sizeof(int)); - if(bufdebug && buf[0] != 'R' && buf[0] != 'W') { - fprintf(stderr,"taper: %c: putc %c\n",*procname,buf[0]); - fflush(stderr); + search_request.state = state; + search_request.prolong = TRUE; + search_request.errmsg = NULL; + + if (use_threads) { + tape_search = g_thread_create(tape_search_thread, + &search_request, TRUE, NULL); + } + + cmdargs = getcmd(); + + switch (cmdargs->cmd) { + case START_TAPER: { + gboolean search_result; + state->driver_start_time = strdup(cmdargs->argv[1]); + if (use_threads) { + search_result = GPOINTER_TO_INT(g_thread_join(tape_search)); + } else { + search_result = + GPOINTER_TO_INT(tape_search_thread(&search_request)); + } + if (search_result) { + putresult(TAPER_OK, "\n"); + } else { + putresult(TAPE_ERROR, "Could not find a tape to use.\n"); + log_add(L_ERROR, "no-tape [%s]", "Could not find a tape to use"); + if (search_request.errmsg != NULL) { + char *c, *c1; + c = c1 = search_request.errmsg; + while (*c != '\0') { + if (*c == '\n') { + *c = '\0'; + log_add(L_WARNING,"%s", c1); + c1 = c+1; + } + c++; + } + if (strlen(c1) > 1 ) + log_add(L_WARNING,"%s", c1); + } + } + amfree(search_request.errmsg); + free_cmdargs(cmdargs); + return TRUE; + } + case QUIT: + search_request.prolong = FALSE; + if (use_threads) { + g_thread_join(tape_search); + } + free_cmdargs(cmdargs); + return send_quitting(state); + default: + error("error [file_reader_side cmd %d argc %d]", cmdargs->cmd, cmdargs->argc); } - if (fullwrite(putpipe, buf, sizeof(buf)) < 0) - error("syncpipe_put: %s", strerror(errno)); + g_assert_not_reached(); } -void syncpipe_putint(i) -int i; -{ +/* In running mode (not startup mode), get a command from driver and + deal with it. */ +static gboolean process_driver_command(taper_state_t * state) { + struct cmdargs *cmdargs; + char * q; + + /* This will return QUIT if driver has died. */ + cmdargs = getcmd(); + switch (cmdargs->cmd) { + case PORT_WRITE: + /* + * PORT-WRITE + * handle + * hostname + * features + * diskname + * level + * datestamp + * splitsize + * split_diskbuffer + */ + process_port_write(state, cmdargs); + break; + + case FILE_WRITE: + /* + * FILE-WRITE + * handle + * filename + * hostname + * features + * diskname + * level + * datestamp + * splitsize + */ + process_file_write(state, cmdargs); + break; + + case QUIT: + free_cmdargs(cmdargs); + if (state->device && state->device->volume_label) { + log_add(L_INFO, "tape %s kb %lld fm %d [OK]\n", + state->device->volume_label, + (long long)((state->total_bytes+(off_t)1023) / (off_t)1024), + state->device->file); + } + return send_quitting(state); + default: + if (cmdargs->argc >= 1) { + q = quote_string(cmdargs->argv[0]); + } else { + q = stralloc("(no input?)"); + } + putresult(BAD_COMMAND, "%s\n", q); + amfree(q); + break; + } + free_cmdargs(cmdargs); - if (fullwrite(putpipe, &i, sizeof(i)) < 0) - error("syncpipe_putint: %s", strerror(errno)); + return TRUE; } -void syncpipe_putstr(str) -const char *str; -{ - int n; - - n = strlen(str)+1; /* send '\0' as well */ - syncpipe_putint(n); - if (fullwrite(putpipe, str, n) < 0) - error("syncpipe_putstr: %s", strerror(errno)); -} +int main(int argc, char ** argv) { + char * tapelist_name; + taper_state_t state; + config_overwrites_t *cfg_ovr = NULL; + char *cfg_opt = NULL; - -/* - * ======================================================================== - * TAPE MANIPULATION SUBSYSTEM - * - */ - -/* local functions */ -int label_tape P((void)); - -int label_tape() -{ - char *conf_tapelist_old = NULL; - char *result; - static int first_call = 1; - char *timestamp; - char *error_msg; - - if (taper_scan(NULL, &label, ×tamp, &error_msg, &tapedev) < 0) { - fprintf(stderr, "%s\n", error_msg); - errstr = newstralloc(errstr, error_msg); - amfree(error_msg); - amfree(timestamp); - return 0; - } + /* + * Configure program for internationalization: + * 1) Only set the message locale for now. + * 2) Set textdomain for all amanda related programs to "amanda" + * We don't want to be forced to support dozens of message catalogs. + */ + setlocale(LC_MESSAGES, "C"); + textdomain("amanda"); - if((tape_fd = tape_open(tapedev, O_WRONLY)) == -1) { - if(errno == EACCES) { - errstr = newstralloc(errstr, - "writing label: tape is write protected"); - } else { - errstr = newstralloc2(errstr, - "writing label: ", strerror(errno)); - } - return 0; - } + safe_fd(-1, 0); + set_pname("taper"); - tapefd_setinfo_length(tape_fd, tt->length); + dbopen("server"); - tapefd_setinfo_datestamp(tape_fd, taper_datestamp); - tapefd_setinfo_disk(tape_fd, label); - result = tapefd_wrlabel(tape_fd, taper_datestamp, label, tt_blocksize); - if(result != NULL) { - errstr = newstralloc(errstr, result); - return 0; - } + device_api_init(); + init_taper_state(&state); - fprintf(stderr, "taper: wrote label `%s' date `%s'\n", label, taper_datestamp); - fflush(stderr); + /* Don't die when child closes pipe */ + signal(SIGPIPE, SIG_IGN); -#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 */ + 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 (strcmp(label, FAKE_LABEL) != 0) { + /* Process options */ - if(cur_tape == 0) { - conf_tapelist_old = stralloc2(conf_tapelist, ".yesterday"); - } else { - char cur_str[NUM_STR_SIZE]; + cfg_ovr = extract_commandline_config_overwrites(&argc, &argv); - 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)); - } - amfree(conf_tapelist_old); - - remove_tapelabel(label); - add_tapelabel(atoi(taper_datestamp), label); - if(write_tapelist(conf_tapelist)) { - error("could not write tapelist: %s", strerror(errno)); - } + if(argc > 2) { + error("Too many arguments!\n"); + g_assert_not_reached(); } + if (argc > 1) + cfg_opt = argv[1]; + config_init(CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_USE_CWD, cfg_opt); + apply_config_overwrites(cfg_ovr); - log_add(L_START, "datestamp %s label %s tape %d", - taper_datestamp, 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); + if (config_errors(NULL) >= CFGERR_ERRORS) { + g_critical(_("errors processing config file")); } - total_tape_used=0.0; - total_tape_fm = 0; - - return 1; -} - -int first_tape(new_datestamp) -char *new_datestamp; -{ - if((have_changer = changer_init()) < 0) { - error("changer initialization failed: %s", strerror(errno)); - } - changer_debug = 1; + safe_cd(); - taper_datestamp = newstralloc(taper_datestamp, new_datestamp); + set_logerror(logerror); - if(!label_tape()) - return 0; + check_running_as(RUNNING_AS_DUMPUSER); - filenum = 0; - return 1; -} + dbrename(get_config_name(), DBG_SUBDIR_SERVER); -int next_tape(writerror) -int writerror; -{ - end_tape(writerror); + log_add(L_INFO, "%s pid %ld", get_pname(), (long)getpid()); - if(++cur_tape >= runtapes) - return 0; + tapelist_name = config_dir_relative(getconf_str(CNF_TAPELIST)); - if(!label_tape()) { - return 0; + if (read_tapelist(tapelist_name) != 0) { + log_add(L_INFO, "pid-done %ld", (long)getpid()); + error("could not load tapelist \"%s\"", tapelist_name); + g_assert_not_reached(); } + amfree(tapelist_name); - filenum = 0; - return 1; -} - - -int end_tape(writerror) -int writerror; -{ - char *result; - int rc = 0; - - if(tape_fd >= 0) { - log_add(L_INFO, "tape %s kb %ld fm %d %s", - label, - (long) ((total_tape_used+1023.0) / 1024.0), - total_tape_fm, - writerror? errstr : "[OK]"); - - fprintf(stderr, "taper: writing end marker. [%s %s kb %ld fm %d]\n", - label, - writerror? "ERR" : "OK", - (long) ((total_tape_used+1023.0) / 1024.0), - total_tape_fm); - fflush(stderr); - if(! writerror) { - if(! write_filemark()) { - rc = 1; - goto common_exit; - } - - result = tapefd_wrendmark(tape_fd, taper_datestamp, tt_blocksize); - if(result != NULL) { - errstr = newstralloc(errstr, result); - rc = 1; - goto common_exit; - } - } + state.have_changer = changer_init(); + if (state.have_changer < 0) { + log_add(L_INFO, "pid-done %ld", (long)getpid()); + error("changer initialization failed: %s", strerror(errno)); + g_assert_not_reached(); } -#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); + state.next_tape_label = NULL; + state.next_tape_device = NULL; + state.cur_tape = 0; - 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 */ - - /* close the tape and let the OS write the final filemarks */ - -common_exit: - - if(tape_fd >= 0 && tapefd_close(tape_fd) == -1 && ! writerror) { - errstr = newstralloc2(errstr, "closing tape: ", strerror(errno)); - rc = 1; + if (!find_first_tape(&state)) { + log_add(L_INFO, "pid-done %ld", (long)getpid()); + return EXIT_SUCCESS; } - tape_fd = -1; - amfree(label); - return rc; + while (process_driver_command(&state)); + log_add(L_INFO, "pid-done %ld", (long)getpid()); + return EXIT_SUCCESS; } - - -int write_filemark() -{ - if(tapefd_weof(tape_fd, 1) == -1) { - errstr = newstralloc2(errstr, "writing filemark: ", strerror(errno)); - return 0; - } - total_tape_fm++; - return 1; -} - -