X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=server-src%2Ftaper.c;h=c1b7cc6286e99de42cf84ae1e6a2677211a214b9;hb=d74dc4d908fcbc1a4ef474edaf51e61ec90eab6b;hp=ec84069e708fe616c67b8cf9a122ebe076171d5a;hpb=3ab887b9bc819a846c75dd7f2ee5d41fac22b19f;p=debian%2Famanda diff --git a/server-src/taper.c b/server-src/taper.c index ec84069..c1b7cc6 100644 --- a/server-src/taper.c +++ b/server-src/taper.c @@ -23,2242 +23,1318 @@ * 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.47.2.14.4.8.2.18 2004/02/13 14:11:26 martinea Exp $ +/* $Id: taper.c 6512 2007-05-24 17:00:24Z ian $ * * moves files from holding disk to tape, or from a socket to tape */ -#include "amanda.h" -#include "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 "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" -#ifdef HAVE_LIBVTBLC -#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? - */ - -/* NBUFS replaced by conf_tapebufs */ -/* #define NBUFS 20 */ -int conf_tapebufs; - -/* 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)); - -/* shared-memory routines */ -char *attach_buffers P((unsigned int size)); -void detach_buffers P((char *bufp)); -void destroy_buffers P((void)); - -/* synchronization pipe routines */ -void syncpipe_init P((int rd, int wr)); -char syncpipe_get P((void)); -int syncpipe_getint P((void)); -char *syncpipe_getstr P((void)); -void syncpipe_put P((int ch)); -void syncpipe_putint P((int i)); -void syncpipe_putstr P((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)); - -/* - * ======================================================================== - * 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; - -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; - -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 */ +#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; +} -/* - * ======================================================================== - * 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; - int fd; - unsigned int size; - int i; - int j; - int page_size; - char *first_buffer; - - for(fd = 3; fd < FD_SETSIZE; fd++) { - /* - * Make sure nobody spoofs us with a lot of extra open files - * that would cause an open we do to get a very high file - * descriptor, which in turn might be used as an index into - * an array (e.g. an fd_set). - */ - close(fd); +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; } +} - set_pname("taper"); - - 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(); - - /* print prompts and debug messages if running interactive */ +/* 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); - interactive = (main_argc > 1 && strcmp(main_argv[1],"-t") == 0); - if(interactive) { - erroutput_type = ERR_INTERACTIVE; - } else { - erroutput_type = ERR_AMANDALOG; - set_logerror(logerror); + if (len > cmdargs->argc) { + error("error [taper %s: not enough args; first missing arg is %s]", + cmdstr[cmdargs->cmd], argnames[cmdargs->argc]); } - 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: Too many args: Got %d, expected %d.]", + cmdstr[cmdargs->cmd], cmdargs->argc, len); } - 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); - } +/* 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; - 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); + 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; + } - runtapes = getconf_int(CNF_RUNTAPES); - cur_tape = 0; + socket = stream_server(res->ai_family, &port, 0, STREAM_BUFSIZE, 0); + freeaddrinfo(res); - conf_tapebufs = getconf_int(CNF_TAPEBUFS); + if (socket < 0) { + char *m; + char *q; + int save_errno = errno; + char *qdiskname = quote_string(info->diskname); + + m = vstralloc("[port create failure: ", + strerror(save_errno), + "]", + NULL); + q = quote_string(m); + putresult(TAPE_ERROR, "%s %s\n", info->handle, q); + log_add(L_FAIL, "%s %s %s %d %s", + info->hostname, qdiskname, info->timestamp, + info->level, q); + amfree(qdiskname); + amfree(m); + amfree(q); + return FALSE; + } - tt_blocksize_kb = tt->blocksize; - tt_blocksize = tt_blocksize_kb * 1024; - tt_file_pad = tt->file_pad; + putresult(PORT, "%d\n", port); - if(interactive) { - fprintf(stderr,"taper: running in interactive test mode\n"); - fflush(stderr); - } + fd = stream_accept(socket, CONNECT_TIMEOUT, 0, STREAM_BUFSIZE); - /* 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; + 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 { - j = 3; + aclose(socket); } - 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]); + 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; +} - tape_writer_side(p2c[0], c2p[1]); - error("tape writer terminated unexpectedly"); +typedef struct { + ConsumerFunctor next_consumer; + gpointer next_consumer_data; + guint64 bytes_written; +} CountingConsumerData; - default: /* parent */ - aclose(p2c[0]); - aclose(c2p[1]); +/* 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; - file_reader_side(c2p[0], p2c[1]); - error("file reader terminated unexpectedly"); + result = data->next_consumer(data->next_consumer_data, buffer); + + if (result > 0) { + data->bytes_written += result; } - /* NOTREACHED */ - return 0; + return result; } - -/* - * ======================================================================== - * FILE READER SIDE - * - */ -void read_file P((int fd, char *handle, - char *host, char *disk, char *datestamp, - int level, int port_flag)); -int taper_fill_buffer P((int fd, buffer_t *bp, int buflen)); -void dumpbufs P((char *str1)); -void dumpstatus P((buffer_t *bp)); - -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 tok; - char *q = NULL; - int level, fd, data_port, data_socket, wpid; - struct stat stat_file; - int tape_started; - int a; - - 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'); - syncpipe_putstr(taper_datestamp); - - /* get result of start command */ - - tok = syncpipe_get(); - 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]", result); - amfree(result); - syncpipe_put('e'); /* ACK error */ - break; - default: - error("expected 'S' or 'E' for START-TAPER, got '%c'", tok); +static gboolean boolean_prolong(void * data) { + if (data == NULL) { + return TRUE; /* Do not interrupt. */ + } else { + return *(gboolean*)data; } +} - /* 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 - */ - 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 file_reader_side PORT-WRITE: too many args: %d != %d]", - cmdargs.argc, a); - } - - 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); - 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); - break; - } - read_file(fd, handle, hostname, diskname, datestamp, level, 1); - aclose(data_socket); - break; - - case FILE_WRITE: - /* - * FILE-WRITE - * handle - * filename - * hostname - * features - * diskname - * level - * datestamp - */ - 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_reader_side FILE-WRITE: too many args: %d != %d]", - cmdargs.argc, a); - } - - if(stat(filename,&stat_file)!=0) { - q = squotef("[%s]", strerror(errno)); - putresult(TAPE_ERROR, "%s %s\n", handle, q); - break; - } - if((fd = open(filename, O_RDONLY)) == -1) { - q = squotef("[%s]", strerror(errno)); - putresult(TAPE_ERROR, "%s %s\n", handle, q); - break; - } - read_file(fd, handle, hostname, diskname, datestamp, level, 0); - break; - - case QUIT: - putresult(QUITTING, "\n"); - fprintf(stderr,"taper: DONE [idle wait: %s secs]\n", - walltime_str(total_wait)); - fflush(stderr); - syncpipe_put('Q'); /* 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); - } - - detach_buffers(buffers); - destroy_buffers(); - amfree(datestamp); - amfree(label); - amfree(errstr); - amfree(changer_resultstr); - amfree(tapedev); - amfree(config_dir); - amfree(config_name); - - malloc_size_2 = malloc_inuse(&malloc_hist_2); - - if(malloc_size_1 != malloc_size_2) { - malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2); - } - - exit(0); +/* 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); + } - 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); - break; - } } - amfree(q); - amfree(handle); - amfree(filename); - amfree(hostname); - amfree(diskname); + 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'; - ap_snprintf(bt, sizeof(bt), "%d", (int)(bp-buftable)); - - switch(bp->status) { - case FULL: ap_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: - ap_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))); } - -void read_file(fd, handle, hostname, diskname, datestamp, level, port_flag) - int fd, level, port_flag; - char *handle, *hostname, *diskname, *datestamp; -{ - buffer_t *bp; - char tok; - int rc, err, opening, closing, bufnum, need_closing; - long filesize; - times_t runtime; - char *strclosing = NULL; - char *str; - int header_read = 0; - int buflen; - dumpfile_t file; - - 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; - err = 0; - fh_init(&file); - - if(bufdebug) { - fprintf(stderr, "taper: r: start file\n"); - fflush(stderr); - } - - for(bp = buftable; bp < buftable + conf_tapebufs; bp++) { - bp->status = EMPTY; +static void log_taper_scan_errmsg(char * errmsg) { + char *c, *c1; + if (errmsg == NULL) + return; + + c = c1 = errmsg; + while (*c != '\0') { + if (*c == '\n') { + *c = '\0'; + log_add(L_WARNING,"%s", c1); + c1 = c+1; + } + c++; } + if (strlen(c1) > 1 ) + log_add(L_WARNING,"%s", c1); + amfree(errmsg); +} - bp = buftable; - if(interactive || bufdebug) dumpstatus(bp); - - /* tell writer to open tape */ - - opening = 1; - syncpipe_put('O'); - syncpipe_putstr(datestamp); - syncpipe_putstr(hostname); - syncpipe_putstr(diskname); - syncpipe_putint(level); - - startclock(); - - /* read file in loop */ - - while(1) { - tok = syncpipe_get(); - switch(tok) { - - case 'O': - assert(opening); - opening = 0; - err = 0; - break; - - case 'R': - bufnum = syncpipe_getint(); - - if(bufdebug) { - fprintf(stderr, "taper: r: got R%d\n", bufnum); - fflush(stderr); - } - - if(need_closing) { - syncpipe_put('C'); - 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); - amfree(q); - log_add(L_INFO, "retrying %s:%s.%d on new tape: %s", - hostname, diskname, level, errstr); - closing = 1; - syncpipe_put('X'); /* X == buffer snafu, bail */ - do { - tok = syncpipe_get(); - if(tok == 'R') - bufnum = syncpipe_getint(); - } while(tok != 'x'); - aclose(fd); - return; - } - - bp->status = FILLING; - buflen = header_read ? tt_blocksize : DISK_BLOCK_BYTES; - if(interactive || bufdebug) dumpstatus(bp); - if((rc = taper_fill_buffer(fd, bp, buflen)) < 0) { - err = errno; - closing = 1; - strclosing = newvstralloc(strclosing,"Can't read data: ",NULL); - syncpipe_put('C'); - } else { - if(rc < buflen) { /* switch to next file */ - int save_fd; - struct stat stat_file; - - save_fd = fd; - close(fd); - if(file.cont_filename[0] == '\0') { /* no more file */ - err = 0; - need_closing = 1; - } else if(stat(file.cont_filename, &stat_file) != 0) { - err = errno; - need_closing = 1; - strclosing = newvstralloc(strclosing,"can't stat: ",file.cont_filename,NULL); - } else if((fd = open(file.cont_filename,O_RDONLY)) == -1) { - err = errno; - need_closing = 1; - strclosing = newvstralloc(strclosing,"can't open: ",file.cont_filename,NULL); - } else if((fd != save_fd) && dup2(fd, save_fd) == -1) { - err = errno; - need_closing = 1; - strclosing = newvstralloc(strclosing,"can't dup2: ",file.cont_filename,NULL); - } else { - buffer_t bp1; - int rc1; - - 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; - need_closing = 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; - need_closing = 1; - if(rc1 < 0) { - strclosing = newvstralloc(strclosing, - "Can't read data: ", - file.cont_filename, - NULL); - } - } - else { - rc += rc1; - bp->size = rc; - } - } - } - } - if(rc > 0) { - bp->status = FULL; - if(header_read == 0) { - char *cont_filename; - - parse_file_header(bp->buffer, &file, rc); - cont_filename = stralloc(file.cont_filename); - file.cont_filename[0] = '\0'; - file.blocksize = tt_blocksize; - build_header(bp->buffer, &file, tt_blocksize); - - /* 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 */ - header_read = 1; - amfree(cont_filename); - } - else { - filesize += am_round(rc, 1024) / 1024; - } - if(interactive || bufdebug) dumpstatus(bp); - if(bufdebug) { - fprintf(stderr,"taper: r: put W%d\n",(int)(bp-buftable)); - fflush(stderr); - } - syncpipe_put('W'); - syncpipe_putint(bp-buftable); - bp = nextbuf(bp); - } - if(need_closing && rc <= 0) { - syncpipe_put('C'); - need_closing = 0; - closing = 1; - } - } - break; - - case 'T': - case 'E': - syncpipe_put('e'); /* ACK error */ - - aclose(fd); - str = syncpipe_getstr(); - errstr = newvstralloc(errstr, "[", str ? str : "(null)", "]", NULL); - amfree(str); - - q = squote(errstr); - if(tok == 'T') { - putresult(TRYAGAIN, "%s %s\n", handle, q); - log_add(L_INFO, "retrying %s:%s.%d on new tape: %s", - hostname, diskname, level, errstr); - } else { - 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]", errstr); - } - amfree(q); - return; - - case 'C': - assert(!opening); - assert(closing); - - 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); - - aclose(fd); - runtime = stopclock(); - 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); - log_add(L_FAIL, "%s %s %s %d %s", - hostname, diskname, datestamp, level, errstr); - str = syncpipe_getstr(); /* reap stats */ - amfree(str); - } 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; - ap_snprintf(kb_str, sizeof(kb_str), "%ld", filesize); - ap_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 ? str : "(null)", - "]", - NULL); - amfree(str); - q = squote(errstr); - putresult(DONE, "%s %s %d %s\n", - handle, label, filenum, q); - amfree(q); - log_add(L_SUCCESS, "%s %s %s %d %s", - hostname, diskname, datestamp, level, 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; - } +/* 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; - strncpy(desc + offset, diskname, 20); + if (state->device != NULL) { + return TRUE; + } - if ((len = strlen(diskname)) <= 20) { - memset(desc + offset + len, ' ', 1); - offset = offset + len + 1; - } - else{ - memset(desc + offset + 20, ' ', 1); - offset = offset + 21; - } + /* We save the value here in case it changes while we're running. */ + use_threads = g_thread_supported(); - 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'); - syncpipe_putint(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'); - syncpipe_putint(filenum); - syncpipe_putstr(vol_date); - -#endif /* HAVE_LIBVTBLC */ - } - return; + 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; - default: - assert(0); - } + switch (cmd) { + default: + g_fprintf(stderr, "taper: Got odd message from driver, expected NEW-TAPE or NO-NEW-TAPE.\n"); + /* FALLTHROUGH. */ + case NEW_TAPE: { + gboolean search_result; + if (use_threads) { + search_result = GPOINTER_TO_INT(g_thread_join(tape_search)); + } else { + search_result = + GPOINTER_TO_INT(tape_search_thread(&search_request)); + } + if (search_result) { + /* We don't say NEW_TAPE until we actually write the label. */ + amfree(search_request.errmsg); + free_cmdargs(cmdargs); + return TRUE; + } else { + putresult(NO_NEW_TAPE, "%s\n", dump->handle); + log_taper_scan_errmsg(search_request.errmsg); + log_add(L_ERROR, "no-tape [%s]", "No more writable valid tape found"); + free_cmdargs(cmdargs); + return FALSE; + } + } + case NO_NEW_TAPE: + search_request.prolong = FALSE; + if (use_threads) { + g_thread_join(tape_search); + } + log_add(L_ERROR, "no-tape [%s]", cmdargs->argv[1]); + state->last_errmsg = stralloc(cmdargs->argv[1]); + free_cmdargs(cmdargs); + return FALSE; } + /* NOTREACHED */ } -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; - - do { - cnt = read(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: - spaceleft -= cnt; - curptr += cnt; - bp->size += cnt; - } - - } while(spaceleft > 0); - - 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; } - - -/* - * ======================================================================== - * TAPE WRITER SIDE - * - */ -times_t idlewait, rdwait, wrwait, fmwait; -long total_writes; -double total_tape_used; -int total_tape_fm; - -void write_file P((void)); -int write_buffer P((buffer_t *bp)); - -void tape_writer_side(getp, putp) -int getp, putp; +static void +update_tapelist( + taper_state_t *state) { - char tok; - int tape_started; - char *str; - char *hostname; - char *diskname; - char *datestamp; - int level; - -#ifdef HAVE_LIBVTBLC - char *vol_label; - char *vol_date; -#endif /* HAVE_LIBVTBLC */ - - procname = "writer"; - syncpipe_init(getp, putp); - - tape_started = 0; - idlewait = times_zero; - - while(1) { - startclock(); - tok = syncpipe_get(); - idlewait = timesadd(idlewait, stopclock()); - if(tok != 'S' && tok != 'Q' && !tape_started) { - error("writer: token '%c' before start", tok); - } - - 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'); - syncpipe_putstr(errstr); - /* wait for reader to acknowledge error */ - do { - tok = syncpipe_get(); - if(tok != 'e') { - error("writer: got '%c' unexpectedly after error", tok); - } - } while(tok != 'e'); - } else { - syncpipe_put('S'); - 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 = syncpipe_getint(); - 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 = syncpipe_getint(); - 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(config_dir); - amfree(config_name); - - malloc_size_2 = malloc_inuse(&malloc_hist_2); - - if(malloc_size_1 != malloc_size_2) { - malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2); - } - - exit(0); + char *tapelist_name = NULL; + char *tapelist_name_old = NULL; + tape_t *tp; + char *comment = NULL; - default: - assert(0); - } + 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); } -} -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; - - 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 (read_tapelist(tapelist_name) != 0) { + log_add(L_INFO, "pid-done %ld", (long)getpid()); + error("could not load tapelist \"%s\"", tapelist_name); + /*NOTREACHED*/ } - /* - * Tell the reader that the tape is open, and give it all the buffers. - */ - syncpipe_put('O'); - for(i = 0; i < conf_tapebufs; i++) { - if(bufdebug) { - fprintf(stderr, "taper: w: put R%d\n", i); - fflush(stderr); - } - syncpipe_put('R'); syncpipe_putint(i); + /* 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*/ } - - /* - * We write the filemark at the start of the file rather than at the end, - * so that it can proceed in parallel with the reader's initial filling - * up of the buffers. - */ - - startclock(); - if(!write_filemark()) - goto tape_error; - fmwait = stopclock(); - - filenum += 1; - - do { - - /* - * STOPPED MODE - * - * At the start of the file, or if the input can't keep up with the - * tape, we enter STOPPED mode, which waits for most of the buffers - * to fill up before writing to tape. This maximizes the amount of - * data written in chunks to the tape drive, minimizing the number - * of starts/stops, which in turn saves tape and time. - */ - - if(interactive) fputs("[WS]", stderr); - startclock(); - while(full_buffers < conf_tapebufs - THRESHOLD) { - tok = syncpipe_get(); - if(tok != 'W') break; - bufnum = syncpipe_getint(); - 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(); - if(tok == 'W') { - bufnum = syncpipe_getint(); - 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'); - 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'); - - /* tell reader the tape and file number */ - - syncpipe_putstr(label); - ap_snprintf(number, sizeof(number), "%d", filenum); - syncpipe_putstr(number); - - ap_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'); /* next tape in place, try again */ - else syncpipe_put('E'); /* no more tapes, fail */ - syncpipe_putstr(errstr); - - error_ack: - /* wait for reader to acknowledge error */ - do { - tok = syncpipe_get(); - if(tok != 'W' && tok != 'C' && tok != 'e') - error("writer: got '%c' unexpectedly after error", tok); - if(tok == 'W') - syncpipe_getint(); /* eat buffer number */ - } while(tok != 'e'); - return; - - reader_buffer_snafu: - syncpipe_put('x'); - return; + 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); } -int write_buffer(bp) -buffer_t *bp; -{ - int rc; - - if(bp->status != FULL) { - /* XXX buffer management snafu */ - assert(0); +/* 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; } - - 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'); syncpipe_putint(bp-buftable); - return 1; - } else { - errstr = newvstralloc(errstr, - "writing file: ", - (rc != -1) ? "short write" : strerror(errno), - NULL); - wrwait = timesadd(wrwait, stopclock()); - if(interactive) fputs("[WE]", stderr); - return 0; + state->total_bytes = 0; + + if (!find_new_tape(state, dump_info)) { + return FALSE; } -} - - -/* - * ======================================================================== - * SHARED-MEMORY BUFFER SUBSYSTEM - * - */ -#ifdef HAVE_SYSVSHM - -int shmid = -1; - -char *attach_buffers(size) - unsigned int size; -{ - char *result; + return label_new_tape(state, dump_info); +} - shmid = shmget(IPC_PRIVATE, size, IPC_CREAT|0700); - if(shmid == -1) { - return NULL; +static gboolean label_new_tape(taper_state_t * state, dump_info_t * dump_info) { + char *old_volume_name = NULL; + char *old_volume_time = NULL; + tape_search_request_t request; + gboolean search_result; + DeviceStatusFlags status; + + /* If we got here, it means that we have found a tape to label and + * have gotten permission from the driver to write it. But we + * still can say NO-NEW-TAPE if a problem shows up, and must still + * say NEW-TAPE if one doesn't. */ + + amfree(state->last_errmsg); + state->device = device_open(state->next_tape_device); + g_assert(state->device != NULL); + amfree(state->next_tape_device); + + if (state->device->status != DEVICE_STATUS_SUCCESS) + goto skip_volume; + + if (!device_configure(state->device, TRUE)) + goto skip_volume; + + /* if we have an error, and are sure it isn't just an unlabeled volume, + * then skip this volume */ + status = device_read_label(state->device); + if ((status & ~DEVICE_STATUS_VOLUME_UNLABELED) && + !(status & DEVICE_STATUS_VOLUME_UNLABELED)) + goto skip_volume; + + old_volume_name = g_strdup(state->device->volume_label); + old_volume_time = g_strdup(state->device->volume_time); + + if (!device_start(state->device, ACCESS_WRITE, state->next_tape_label, + state->driver_start_time)) { + gboolean tape_used; + + /* Something broke, see if we can tell if the volume was erased or + * not. */ + g_fprintf(stderr, "taper: Error writing label %s to device %s: %s.\n", + state->next_tape_label, state->device->device_name, + device_error_or_status(state->device)); + + if (!device_finish(state->device)) + goto request_new_volume; + + /* This time, if we can't read the label, assume we've overwritten + * the volume or otherwise corrupted it */ + status = device_read_label(state->device); + if ((status & ~DEVICE_STATUS_VOLUME_UNLABELED) && + !(status & DEVICE_STATUS_VOLUME_UNLABELED)) + goto request_new_volume; + + tape_used = check_volume_changed(state->device, old_volume_name, + old_volume_time); + + if (tape_used) + goto request_new_volume; + else + goto skip_volume; } - result = (char *)shmat(shmid, (SHM_ARG_TYPE *)NULL, 0); + amfree(old_volume_name); + amfree(old_volume_time); + amfree(state->next_tape_label); - if(result == (char *)-1) { - int save_errno = errno; + update_tapelist(state); + state->cur_tape++; - destroy_buffers(); - errno = save_errno; - error("shmat: %s", strerror(errno)); + if (state->have_changer && + changer_label("UNKNOWN", state->device->volume_label) != 0) { + error(_("couldn't update barcode database")); + /*NOTREACHED*/ } - return result; -} - - -void detach_buffers(bufp) - char *bufp; -{ - if(shmdt((SHM_ARG_TYPE *)bufp) == -1) { - error("shmdt: %s", strerror(errno)); + log_add(L_START, "datestamp %s label %s tape %d", + state->driver_start_time, state->device->volume_label, + state->cur_tape); + putresult(NEW_TAPE, "%s %s\n", dump_info->handle, + state->device->volume_label); + + return TRUE; + +request_new_volume: + /* Tell the driver we overwrote this volume, even if it was empty, and request + * a new volume. */ + if (state->device) + state->last_errmsg = newstralloc(state->last_errmsg, device_error_or_status(state->device)); + else + state->last_errmsg = newstralloc(state->last_errmsg, "(unknown)"); + + putresult(NEW_TAPE, "%s %s\n", dump_info->handle, + state->next_tape_label); + if (old_volume_name) { + log_add(L_WARNING, "Problem writing label '%s' to volume %s " + "(volume may be erased): %s\n", + state->next_tape_label, old_volume_name, + state->last_errmsg); + } else { + log_add(L_WARNING, "Problem writing label '%s' to new volume " + "(volume may be erased): %s\n", state->next_tape_label, + state->last_errmsg); } -} -void destroy_buffers() -{ - if(shmid == -1) return; /* nothing to destroy */ - if(shmctl(shmid, IPC_RMID, NULL) == -1) { - error("shmctl: %s", strerror(errno)); + if (state->device) { + g_object_unref(state->device); + state->device = NULL; } -} -#else -#ifdef HAVE_MMAP + amfree(state->next_tape_label); + amfree(old_volume_name); + amfree(old_volume_time); -#ifdef HAVE_SYS_MMAN_H -#include -#endif + return find_and_label_new_tape(state, dump_info); -#ifndef MAP_ANON -# ifdef MAP_ANONYMOUS /* OSF/1-style */ -# define MAP_ANON MAP_ANONYMOUS -# else /* SunOS4-style */ -# define MAP_ANON 0 -# define ZERO_FILE "/dev/zero" -# endif -#endif - -int shmfd = -1; -unsigned int saved_size; +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)"); -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)); + 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); } -#endif - - saved_size = size; - shmbuf = (char *) mmap((void *) 0, - size, - PROT_READ|PROT_WRITE, - MAP_ANON|MAP_SHARED, - shmfd, 0); - return shmbuf; -} - -void detach_buffers(bufp) -char *bufp; -{ - if(munmap((void *)bufp, saved_size) == -1) { - error("detach_buffers: munmap: %s", strerror(errno)); + if (state->device) { + g_object_unref(state->device); + state->device = NULL; } - aclose(shmfd); -} - -void destroy_buffers() -{ -} - -#else -error: must define either HAVE_SYSVSHM or HAVE_MMAP! -#endif -#endif - - - -/* - * ======================================================================== - * SYNC-PIPE SUBSYSTEM - * - */ - -int getpipe, putpipe; - -void syncpipe_init(rd, wr) -int rd, wr; -{ - getpipe = rd; - putpipe = wr; + amfree(state->next_tape_label); + amfree(old_volume_name); + amfree(old_volume_time); + + request.state = state; + request.prolong = TRUE; + request.errmsg = NULL; + search_result = GPOINTER_TO_INT(tape_search_thread(&request)); + if (search_result) { + amfree(request.errmsg); + return label_new_tape(state, dump_info); + } else { + /* Problem finding a new tape! */ + log_taper_scan_errmsg(request.errmsg); + putresult(NO_NEW_TAPE, "%s\n", dump_info->handle); + return FALSE; + } } -char syncpipe_get() -{ - int rc; - char buf[1]; - - rc = read(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)); - - if(bufdebug && *buf != 'R' && *buf != 'W') { - fprintf(stderr,"taper: %c: getc %c\n",*procname,*buf); - fflush(stderr); +/* Find out if the dump is PARTIAL or not, and set the proper driver + and logfile tags for the dump. */ +static void find_completion_tags(dump_info_t * dump_info, /* IN */ + cmd_t * result_cmd, /* OUT */ + logtype_t * result_log /* OUT */) { + /* result_cmd is always DONE because the taper wrote all the input to + * the output. driver need to know if the taper completed its job. + * result_log is set to L_PARTIAL if the image is partial, the log + * must tell if the image is partial or complete. + */ + + if (taper_source_is_partial(dump_info->source)) { + *result_cmd = DONE; + *result_log = L_PARTIAL; + } else { + *result_cmd = DONE; + *result_log = L_DONE; } +} - return buf[0]; +/* Put an L_PARTIAL message to the logfile. */ +static void put_partial_log(dump_info_t * dump_info, double dump_time, + guint64 dump_kbytes, char *errstr) { + char * qdiskname = quote_string(dump_info->diskname); + + log_add(L_PARTIAL, "%s %s %s %d %d [sec %f kb %ju kps %f] %s", + dump_info->hostname, qdiskname, dump_info->timestamp, + dump_info->current_part, dump_info->level, dump_time, + (uintmax_t)dump_kbytes, dump_kbytes / dump_time, + errstr); + amfree(qdiskname); } -int syncpipe_getint() -{ - int rc; - int i; - int len = sizeof(i); - char *p; - - for(p = (char *)&i; len > 0; len -= rc, p += rc) { - if ((rc = read(getpipe, p, len)) <= 0) { - error("syncpipe_getint: %s", - rc < 0 ? strerror(errno) : "short read"); +/* 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; + } + } + + dump_info->total_time = timesadd(run_time, dump_info->total_time); + dump_info->total_bytes += run_bytes; + dump_time = g_timeval_to_double(dump_info->total_time); + dump_kbytes = dump_info->total_bytes / 1024; + dump_kbps = dump_info->total_bytes / (1024 * dump_time); + + putresult(PARTIAL, + "%s INPUT-%s TAPE-%s " + "\"[sec %f kb %ju kps %f]\" %s %s\n", + dump_info->handle, + (queue_result & QUEUE_PRODUCER_ERROR) ? "ERROR" : "GOOD", + (queue_result & QUEUE_CONSUMER_ERROR) ? "ERROR" : "GOOD", + dump_time, (uintmax_t)dump_kbytes, dump_kbps, + producer_errstr, consumer_errstr); + if (queue_result & QUEUE_CONSUMER_ERROR) { + put_partial_log(dump_info, dump_time, dump_kbytes, + consumer_errstr); + } else { + put_partial_log(dump_info, dump_time, dump_kbytes, + producer_errstr); } + amfree(producer_errstr); + amfree(consumer_errstr); } - return i; + amfree(qdiskname); + return FALSE; } +/* Generate the actual header structure to write to tape. This means dropping + * bits related to the holding disk, and adding bits for split dumps. */ +static dumpfile_t * munge_headers(dump_info_t * dump_info) { + dumpfile_t * rval; + int expected_splits; + + rval = taper_source_get_first_header(dump_info->source); -char *syncpipe_getstr() -{ - int rc; - int len; - char *p; - char *str; - - if((len = syncpipe_getint()) <= 0) { - return NULL; - } - - str = alloc(len); - - for(p = str; len > 0; len -= rc, p += rc) { - if ((rc = read(getpipe, p, len)) <= 0) { - error("syncpipe_getstr: %s", - rc < 0 ? strerror(errno) : "short read"); - } + if (rval == NULL) { + return NULL; } - return str; -} - + rval->cont_filename[0] = '\0'; -void syncpipe_put(chi) -int chi; -{ - int l, n, s; - char ch = chi; - char *item = &ch; + expected_splits = taper_source_predict_parts(dump_info->source); - if(bufdebug && chi != 'R' && chi != 'W') { - fprintf(stderr,"taper: %c: putc %c\n",*procname,chi); - fflush(stderr); + if (expected_splits != 1) { + rval->type = F_SPLIT_DUMPFILE; + rval->partnum = dump_info->current_part; + rval->totalparts = expected_splits; } - for(l = 0, n = sizeof(ch); l < n; l += s) { - if((s = write(putpipe, item + l, n - l)) < 0) { - error("syncpipe_put: %s", strerror(errno)); - } - } + return rval; } -void syncpipe_putint(i) -int i; +/* We call this when we can't find a tape to write data to. This could + happen with the first (or only) part of a file, but it could also + happen with an intermediate part of a split dump. dump_bytes + is 0 if this is the first part of a dump. */ +static void bail_no_volume( + dump_info_t *dump_info, + char *errmsg) { - int l, n, s; - char *item = (char *)&i; - - for(l = 0, n = sizeof(i); l < n; l += s) { - if((s = write(putpipe, item + l, n - l)) < 0) { - error("syncpipe_putint: %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); } -void syncpipe_putstr(item) -char *item; -{ - int l, n, s; - - n = strlen(item)+1; /* send '\0' as well */ - syncpipe_putint(n); - for(l = 0, n = strlen(item)+1; l < n; l += s) { - if((s = write(putpipe, item + l, n - l)) < 0) { - error("syncpipe_putstr: %s", strerror(errno)); - } +/* Link up the TaperSource with the Device, including retries etc. */ +static void run_device_output(taper_state_t * taper_state, + dump_info_t * dump_info) { + GValue val; + guint file_number; + dump_info->current_part = 1; + dump_info->total_time.tv_sec = 0; + dump_info->total_time.tv_usec = 0; + dump_info->total_bytes = 0; + + for (;;) { + GTimeVal start_time, end_time, run_time; + StreamingRequirement streaming_mode; + queue_result_flags queue_result; + CountingConsumerData consumer_data; + dumpfile_t *this_header; + size_t max_memory; + + this_header = munge_headers(dump_info); + if (this_header == NULL) { + char * qdiskname = quote_string(dump_info->diskname); + char * errstr = taper_source_get_errmsg(dump_info->source); + if (!errstr) + errstr = "Failed reading dump header."; + errstr = quote_string(errstr); + putresult(FAILED, + "%s INPUT-ERROR TAPE-GOOD %s \"\"\n", + dump_info->handle, errstr); + log_add(L_FAIL, "%s %s %s %d %s", + dump_info->hostname, qdiskname, dump_info->timestamp, + dump_info->level, errstr); + amfree(qdiskname); + amfree(errstr); + return; + } + + if (!find_and_label_new_tape(taper_state, dump_info)) { + bail_no_volume(dump_info, taper_state->last_errmsg); + dumpfile_free(this_header); + return; + } + + while (!device_start_file(taper_state->device, this_header)) { + /* Close the device. */ + device_finish(taper_state->device); + g_object_unref(taper_state->device); + taper_state->device = NULL; + + if (!find_and_label_new_tape(taper_state, dump_info)) { + bail_no_volume(dump_info, taper_state->last_errmsg); + dumpfile_free(this_header); + return; + } + } + dumpfile_free(this_header); + + bzero(&val, sizeof(val)); + if (!device_property_get(taper_state->device, PROPERTY_STREAMING, &val) + || !G_VALUE_HOLDS(&val, STREAMING_REQUIREMENT_TYPE)) { + g_fprintf(stderr, "taper: Couldn't get streaming type!\n"); + streaming_mode = STREAMING_REQUIREMENT_REQUIRED; + } else { + streaming_mode = g_value_get_enum(&val); + } + + file_number = taper_state->device->file; + + consumer_data.next_consumer = device_write_consumer; + consumer_data.next_consumer_data = taper_state->device; + consumer_data.bytes_written = 0; + + g_get_current_time(&start_time); + + if (getconf_seen(CNF_DEVICE_OUTPUT_BUFFER_SIZE)) { + max_memory = getconf_size(CNF_DEVICE_OUTPUT_BUFFER_SIZE); + if (getconf_seen(CNF_TAPEBUFS)) { + g_fprintf(stderr, + "Configuration directives 'device_output_buffer_size' " + "and \n" + "'tapebufs' are incompatible; using former.\n"); + } + } else if (getconf_seen(CNF_TAPEBUFS)) { + max_memory = getconf_int(CNF_TAPEBUFS) * + taper_state->device->block_size; + } else { + /* Use default. */ + max_memory = getconf_size(CNF_DEVICE_OUTPUT_BUFFER_SIZE); + } + + queue_result = do_consumer_producer_queue_full + (taper_source_producer, + dump_info->source, + counting_consumer, + &consumer_data, + taper_state->device->block_size, max_memory, + streaming_mode); + + g_get_current_time(&end_time); + run_time = timesub(end_time, start_time); + + /* 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; + } + + if (!finish_part_attempt(taper_state, dump_info, queue_result, + run_time, consumer_data.bytes_written)) { + break; + } } } - -/* - * ======================================================================== - * TAPE MANIPULATION SUBSYSTEM - * - */ - -/* local functions */ -int scan_init P((int rc, int ns, int bk)); -int taperscan_slot P((int rc, char *slotstr, char *device)); -char *taper_scan P((void)); -int label_tape P((void)); - -int label_tape() -{ - char *conf_tapelist_old = NULL; - char *olddatestamp = NULL; - char *result; - tape_t *tp; - static int first_call = 1; - - if(have_changer) { - amfree(tapedev); - if ((tapedev = taper_scan()) == NULL) { - errstr = newstralloc(errstr, changer_resultstr); - return 0; - } - } - -#ifdef HAVE_LINUX_ZFTAPE_H - if (is_zftape(tapedev) == 1){ - if((tape_fd = tape_open(tapedev, O_RDONLY)) == -1) { - errstr = newstralloc2(errstr, "taper: ", - (errno == EACCES) ? "tape is write-protected" - : strerror(errno)); - return 0; - } - if((result = tapefd_rdlabel(tape_fd, &olddatestamp, &label)) != NULL) { - amfree(olddatestamp); - errstr = newstralloc(errstr, result); - return 0; - } - if(tapefd_rewind(tape_fd) == -1) { - return 0; - } - tapefd_close(tape_fd); - tape_fd = -1; +/* 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(); } - else -#endif /* !HAVE_LINUX_ZFTAPE_H */ - if((result = tape_rdlabel(tapedev, &olddatestamp, &label)) != NULL) { - amfree(olddatestamp); - errstr = newstralloc(errstr, result); - return 0; + + 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(); } - - fprintf(stderr, "taper: read label `%s' date `%s'\n", label, olddatestamp); - fflush(stderr); - amfree(olddatestamp); - - /* check against tape list */ - if (strcmp(label, FAKE_LABEL) != 0) { - tp = lookup_tapelabel(label); - if(tp == NULL) { - errstr = newvstralloc(errstr, - "label ", label, - " match labelstr but it not listed in the tapelist file", - NULL); - return 0; - } - else if(tp != NULL && !reusable_tape(tp)) { - errstr = newvstralloc(errstr, - "cannot overwrite active tape ", label, - NULL); - return 0; - } - - if(!match(labelstr, label)) { - errstr = newvstralloc(errstr, - "label ", label, - " doesn\'t match labelstr \"", labelstr, "\"", - NULL); - return 0; - } + + if (strcmp(cmdargs->argv[7], "NULL") == 0) { + split_diskbuffer = NULL; + } else { + split_diskbuffer = g_strdup(cmdargs->argv[7]); } - - 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; + + 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(); } - tapefd_setinfo_length(tape_fd, tt->length); - - 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; + 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); - fprintf(stderr, "taper: wrote label `%s' date `%s'\n", label, taper_datestamp); - fflush(stderr); - -#ifdef HAVE_LIBVTBLC - /* store time for the first volume entry */ - time(&raw_time); - tape_timep = localtime(&raw_time); - strftime(start_datestr, 20, "%T %D", tape_timep); - fprintf(stderr, "taper: got vtbl start time: %s\n", start_datestr); - fflush(stderr); -#endif /* HAVE_LIBVTBLC */ - - /* write tape list */ - - /* XXX add cur_tape number to tape list structure */ - if (strcmp(label, FAKE_LABEL) != 0) { - - if(cur_tape == 0) { - conf_tapelist_old = stralloc2(conf_tapelist, ".yesterday"); - } else { - char cur_str[NUM_STR_SIZE]; + run_device_output(state, &dump_state); - ap_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); + free_dump_info(&dump_state); +} - remove_tapelabel(label); - add_tapelabel(atoi(taper_datestamp), label); - if(write_tapelist(conf_tapelist)) { - error("could not write tapelist: %s", strerror(errno)); - } +/* 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(); } - - 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); + + dump_state.timestamp = strdup(cmdargs->argv[6]); + + errno = 0; + splitsize = g_ascii_strtoull(cmdargs->argv[7], NULL, 10); + if (errno != 0) { + error("error [taper FILE-WRITE: Invalid splitsize %s]", + cmdargs->argv[7]); + g_assert_not_reached(); } - total_tape_used=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; + 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); - taper_datestamp = newstralloc(taper_datestamp, new_datestamp); + run_device_output(state, &dump_state); - if(!label_tape()) - return 0; + free_dump_info(&dump_state); + amfree(holding_disk_file); +} - filenum = 0; - return 1; +/* Send QUITTING message to driver and associated logging. Always + returns false. */ +static gboolean send_quitting(taper_state_t * state) { + putresult(QUITTING, "\n"); + g_fprintf(stderr,"taper: DONE\n"); + cleanup(state); + return FALSE; } -int next_tape(writerror) -int writerror; -{ - end_tape(writerror); +/* 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; - if(++cur_tape >= runtapes) - return 0; + /* We save the value here in case it changes while we're running. */ + use_threads = g_thread_supported(); - if(!label_tape()) { - return 0; + search_request.state = state; + search_request.prolong = TRUE; + search_request.errmsg = NULL; + + if (use_threads) { + tape_search = g_thread_create(tape_search_thread, + &search_request, TRUE, NULL); } - 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; + 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); } -#ifdef HAVE_LINUX_ZFTAPE_H - if (tape_fd >= 0 && is_zftape(tapedev) == 1) { - /* rewind the tape */ + g_assert_not_reached(); +} - if(tapefd_rewind(tape_fd) == -1 ) { - errstr = newstralloc2(errstr, "rewinding tape: ", strerror(errno)); - rc = 1; - goto common_exit; +/* 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); } - /* close the tape */ + 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(tapefd_close(tape_fd) == -1) { - errstr = newstralloc2(errstr, "closing tape: ", strerror(errno)); - rc = 1; - goto common_exit; - } - tape_fd = -1; + return TRUE; +} -#ifdef HAVE_LIBVTBLC - /* update volume table */ - fprintf(stderr, "taper: updating volume table ...\n"); - fflush(stderr); - - if ((tape_fd = raw_tape_open(rawtapedev, O_RDWR)) == -1) { - if(errno == EACCES) { - errstr = newstralloc(errstr, - "updating volume table: tape is write protected"); - } else { - errstr = newstralloc2(errstr, - "updating volume table: ", - strerror(errno)); - } - rc = 1; - goto common_exit; - } - /* read volume table */ - if ((num_volumes = read_vtbl(tape_fd, volumes, vtbl_buffer, - &first_seg, &last_seg)) == -1 ) { - errstr = newstralloc2(errstr, - "reading volume table: ", - strerror(errno)); - rc = 1; - goto common_exit; - } - /* set volume label and date for first entry */ - vtbl_no = 0; - if(set_label(label, volumes, num_volumes, vtbl_no)){ - errstr = newstralloc2(errstr, - "setting label for entry 1: ", - strerror(errno)); - rc = 1; - goto common_exit; - } - /* date of start writing this tape */ - if (set_date(start_datestr, volumes, num_volumes, vtbl_no)){ - errstr = newstralloc2(errstr, - "setting date for entry 1: ", - strerror(errno)); - rc = 1; - goto common_exit; - } - /* set volume labels and dates for backup files */ - for (vtbl_no = 1; vtbl_no <= num_volumes - 2; vtbl_no++){ - fprintf(stderr,"taper: label %i: %s, date %s\n", - vtbl_no, - vtbl_entry[vtbl_no].label, - vtbl_entry[vtbl_no].date); - fflush(stderr); - if(set_label(vtbl_entry[vtbl_no].label, - volumes, num_volumes, vtbl_no)){ - errstr = newstralloc2(errstr, - "setting label for entry i: ", - strerror(errno)); - rc = 1; - goto common_exit; - } - if(set_date(vtbl_entry[vtbl_no].date, - volumes, num_volumes, vtbl_no)){ - errstr = newstralloc2(errstr, - "setting date for entry i: ", - strerror(errno)); - rc = 1; - goto common_exit; - } - } - /* set volume label and date for last entry */ - vtbl_no = num_volumes - 1; - if(set_label("AMANDA Tape End", volumes, num_volumes, vtbl_no)){ - errstr = newstralloc2(errstr, - "setting label for last entry: ", - strerror(errno)); - rc = 1; - goto common_exit; - } - datestr = NULL; /* take current time */ - if (set_date(datestr, volumes, num_volumes, vtbl_no)){ - errstr = newstralloc2(errstr, - "setting date for last entry 1: ", - strerror(errno)); - rc = 1; - goto common_exit; - } - /* write volume table back */ - if (write_vtbl(tape_fd, volumes, vtbl_buffer, num_volumes, first_seg, - op_mode == trunc)) { - errstr = newstralloc2(errstr, - "writing volume table: ", - strerror(errno)); - rc = 1; - goto common_exit; - } - - fprintf(stderr, "taper: updating volume table: done.\n"); - fflush(stderr); -#endif /* HAVE_LIBVTBLC */ - } -#endif /* !HAVE_LINUX_ZFTAPE_H */ +int main(int argc, char ** argv) { + char * tapelist_name; + taper_state_t state; + config_overwrites_t *cfg_ovr = NULL; + char *cfg_opt = NULL; - /* close the tape and let the OS write the final filemarks */ + /* + * Configure program for internationalization: + * 1) Only set the message locale for now. + * 2) Set textdomain for all amanda related programs to "amanda" + * We don't want to be forced to support dozens of message catalogs. + */ + setlocale(LC_MESSAGES, "C"); + textdomain("amanda"); + + safe_fd(-1, 0); + set_pname("taper"); -common_exit: + dbopen("server"); - if(tape_fd >= 0 && tapefd_close(tape_fd) == -1 && ! writerror) { - errstr = newstralloc2(errstr, "closing tape: ", strerror(errno)); - rc = 1; - } - tape_fd = -1; - amfree(label); + device_api_init(); + init_taper_state(&state); - return rc; -} + /* Don't die when child closes pipe */ + signal(SIGPIPE, SIG_IGN); + 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()); -int write_filemark() -{ - if(tapefd_weof(tape_fd, 1) == -1) { - errstr = newstralloc2(errstr, "writing filemark: ", strerror(errno)); - return 0; - } - total_tape_fm++; - return 1; -} + /* Process options */ + cfg_ovr = extract_commandline_config_overwrites(&argc, &argv); -/* - * ======================================================================== - * TAPE CHANGER SCAN - * - */ -int nslots, backwards, found, got_match, tapedays; -char *first_match_label = NULL, *first_match = NULL, *found_device = NULL; -char *searchlabel, *labelstr; -tape_t *tp; + 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); -int scan_init(rc, ns, bk) -int rc, ns, bk; -{ - if(rc) { - fprintf(stderr, "%s: could not get changer info: %s\n", - get_pname(), changer_resultstr); - return rc; + if (config_errors(NULL) >= CFGERR_ERRORS) { + g_critical(_("errors processing config file")); } - nslots = ns; - backwards = bk; + safe_cd(); - return 0; -} + set_logerror(logerror); -int taperscan_slot(rc, slotstr, device) - int rc; - char *slotstr; - char *device; -{ - char *t_errstr; - char *scan_datestamp = NULL; - - if(rc == 2) { - fprintf(stderr, "%s: fatal slot %s: %s\n", - get_pname(), slotstr, changer_resultstr); - fflush(stderr); - return 1; - } - else if(rc == 1) { - fprintf(stderr, "%s: slot %s: %s\n", get_pname(), - slotstr, changer_resultstr); - fflush(stderr); - return 0; - } - else { - if((t_errstr = tape_rdlabel(device, &scan_datestamp, &label)) != NULL) { - amfree(scan_datestamp); - fprintf(stderr, "%s: slot %s: %s\n", - get_pname(), slotstr, t_errstr); - fflush(stderr); - } - else { - /* got an amanda tape */ - fprintf(stderr, "%s: slot %s: date %-8s label %s", - get_pname(), slotstr, scan_datestamp, label); - fflush(stderr); - amfree(scan_datestamp); - if(searchlabel != NULL - && (strcmp(label, FAKE_LABEL) == 0 - || strcmp(label, searchlabel) == 0)) { - /* it's the one we are looking for, stop here */ - fprintf(stderr, " (exact label match)\n"); - fflush(stderr); - found_device = newstralloc(found_device, device); - found = 1; - return 1; - } - else if(!match(labelstr, label)) { - fprintf(stderr, " (no match)\n"); - fflush(stderr); - } - else { - /* not an exact label match, but a labelstr match */ - /* check against tape list */ - tp = lookup_tapelabel(label); - if(tp == NULL) { - fprintf(stderr, "(not in tapelist)\n"); - fflush(stderr); - } - else if(!reusable_tape(tp)) { - fprintf(stderr, " (active tape)\n"); - fflush(stderr); - } - else if(got_match == 0 && tp->datestamp == 0) { - got_match = 1; - first_match = newstralloc(first_match, slotstr); - first_match_label = newstralloc(first_match_label, label); - fprintf(stderr, " (new tape)\n"); - fflush(stderr); - found = 3; - found_device = newstralloc(found_device, device); - return 1; - } - else if(got_match) { - fprintf(stderr, " (labelstr match)\n"); - fflush(stderr); - } - else { - got_match = 1; - first_match = newstralloc(first_match, slotstr); - first_match_label = newstralloc(first_match_label, label); - fprintf(stderr, " (first labelstr match)\n"); - fflush(stderr); - if(!backwards || !searchlabel) { - found = 2; - found_device = newstralloc(found_device, device); - return 1; - } - } - } - } - } - return 0; -} + check_running_as(RUNNING_AS_DUMPUSER); -char *taper_scan() -{ - char *outslot = NULL; + dbrename(get_config_name(), DBG_SUBDIR_SERVER); - if((tp = lookup_last_reusable_tape(0)) == NULL) - searchlabel = NULL; - else - searchlabel = tp->label; + log_add(L_INFO, "%s pid %ld", get_pname(), (long)getpid()); - found = 0; - got_match = 0; + tapelist_name = config_dir_relative(getconf_str(CNF_TAPELIST)); - if (searchlabel != NULL) - changer_find(scan_init, taperscan_slot, searchlabel); - else - changer_scan(scan_init, taperscan_slot); - - if(found == 2 || found == 3) - searchlabel = first_match_label; - else if(!found && got_match) { - searchlabel = first_match_label; - amfree(found_device); - if(changer_loadslot(first_match, &outslot, &found_device) == 0) { - found = 1; - } - amfree(outslot); + 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(); } - else if(!found) { - if(searchlabel) { - changer_resultstr = newvstralloc(changer_resultstr, - "label ", searchlabel, - " or new tape not found in rack", - NULL); - } else { - changer_resultstr = newstralloc(changer_resultstr, - "new tape not found in rack"); - } + amfree(tapelist_name); + + state.have_changer = changer_init(); + if (state.have_changer < 0) { + log_add(L_INFO, "pid-done %ld", (long)getpid()); + error("changer initialization failed: %s", strerror(errno)); + g_assert_not_reached(); } - if(found) { - outslot = found_device; - found_device = NULL; /* forget about our copy */ - } else { - outslot = NULL; - amfree(found_device); + state.next_tape_label = NULL; + state.next_tape_device = NULL; + state.cur_tape = 0; + + if (!find_first_tape(&state)) { + log_add(L_INFO, "pid-done %ld", (long)getpid()); + return EXIT_SUCCESS; } - return outslot; + + while (process_driver_command(&state)); + log_add(L_INFO, "pid-done %ld", (long)getpid()); + return EXIT_SUCCESS; }