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