]> git.gag.com Git - debian/amanda/blobdiff - server-src/taper.c
Merge branch 'master' into squeeze
[debian/amanda] / server-src / taper.c
diff --git a/server-src/taper.c b/server-src/taper.c
deleted file mode 100644 (file)
index 8908801..0000000
+++ /dev/null
@@ -1,1347 +0,0 @@
-/*
- * Amanda, The Advanced Maryland Automatic Network Disk Archiver
- * Copyright (c) 1991-1998, 2000 University of Maryland at College Park
- * All Rights Reserved.
- *
- * Permission to use, copy, modify, distribute, and sell this software and its
- * documentation for any purpose is hereby granted without fee, provided that
- * the above copyright notice appear in all copies and that both that
- * copyright notice and this permission notice appear in supporting
- * documentation, and that the name of U.M. not be used in advertising or
- * publicity pertaining to distribution of the software without specific,
- * written prior permission.  U.M. makes no representations about the
- * suitability of this software for any purpose.  It is provided "as is"
- * without express or implied warranty.
- *
- * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
- * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
- * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
- * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- *
- * Authors: the Amanda Development Team.  Its members are listed in a
- * file named AUTHORS, in the root directory of this distribution.
- */
-/* $Id: taper.c 6512 2007-05-24 17:00:24Z ian $
- *
- * moves files from holding disk to tape, or from a socket to tape
- */
-
-/* FIXME: This file needs to use gettext. */
-
-#include <glib.h>
-#include "physmem.h"
-
-#include "changer.h"
-#include "clock.h"
-#include "conffile.h"
-#include "device.h"
-#include "logfile.h"
-#include "server_util.h"
-#include "stream.h"
-#include "tapefile.h"
-#include "taperscan.h"
-#include "taper-source.h"
-#include "timestamp.h"
-#include "util.h"
-#include "version.h"
-#include "queueing.h"
-#include "device-queueing.h"
-
-/* FIXME: This should not be here. */
-#define CONNECT_TIMEOUT (2*60)
-
-/* Use this instead of global variables, so that we are reentrant. */
-typedef struct {
-    Device * device;
-    char * driver_start_time;
-    int    cur_tape;
-    char * next_tape_label;
-    char * next_tape_device;
-    taper_scan_tracker_t * taper_scan_tracker;
-    char * last_errmsg;
-    off_t  total_bytes;
-    int have_changer;
-} taper_state_t;
-
-typedef struct {
-    char * handle;
-    char * hostname;
-    char * diskname;
-    int level;
-    char * timestamp;
-    char * id_string;
-    TaperSource * source;
-    int current_part;
-    GTimeVal total_time;
-    guint64 total_bytes;
-} dump_info_t;
-
-static gboolean label_new_tape(taper_state_t * state, dump_info_t * dump_info);
-
-static void init_taper_state(taper_state_t* state) {
-    state->device = NULL;
-    state->driver_start_time = NULL;
-    state->taper_scan_tracker = taper_scan_tracker_new();
-    state->last_errmsg = NULL;
-    state->total_bytes = 0;
-}
-
-static void cleanup(taper_state_t * state) {
-    amfree(state->driver_start_time);
-    amfree(state->next_tape_label);
-    amfree(state->next_tape_device);
-    amfree(state->last_errmsg);
-    taper_scan_tracker_free(state->taper_scan_tracker);
-    if (state->device != NULL) {
-        g_object_unref(state->device);
-        state->device = NULL;
-    }
-}
-
-static void free_dump_info(dump_info_t * info) {
-    amfree(info->handle);
-    amfree(info->hostname);
-    amfree(info->diskname);
-    amfree(info->timestamp);
-    amfree(info->id_string);
-    if (info->source != NULL) {
-        g_object_unref(info->source);
-        info->source = NULL;
-    }
-}
-
-/* Validate that a command has the proper number of arguments, and
-   print a meaningful error message if not. It returns only if the
-   check is successful. */
-static void validate_args(struct cmdargs * cmdargs,
-                          char ** argnames) {
-    int len = g_strv_length(argnames);
-
-    if (len > cmdargs->argc) {
-       error("error [taper %s: not enough args; first missing arg is %s]",
-             cmdstr[cmdargs->cmd], argnames[cmdargs->argc]);
-    }
-
-    if (len < cmdargs->argc) {
-        error("error [taper %s: Too many args: Got %d, expected %d.]",
-              cmdstr[cmdargs->cmd], cmdargs->argc, len);
-    }
-}
-
-/* Open a socket to the dumper. Returns TRUE if everything is happy, FALSE
-   otherwise. */
-static gboolean open_read_socket(dump_info_t * info, char * split_diskbuffer,
-                             guint64 splitsize, guint64 fallback_splitsize) {
-    in_port_t port = 0;
-    int socket;
-    int fd;
-    int result;
-    struct addrinfo *res;
-
-    if ((result = resolve_hostname("localhost", 0, &res, NULL) != 0)) {
-        char *m;
-        char *q;
-       int save_errno = errno;
-        char *qdiskname = quote_string(info->diskname);
-
-        m = vstralloc("[localhost resolve failure: ",
-                      strerror(save_errno),
-                      "]",
-                      NULL);
-        q = quote_string(m);
-        putresult(TAPE_ERROR, "%s %s\n", info->handle, q);
-        log_add(L_FAIL, "%s %s %s %d %s",
-                info->hostname, qdiskname, info->timestamp,
-                info->level, q);
-        amfree(qdiskname);
-        amfree(m);
-        amfree(q);
-        return FALSE;
-    }
-
-    socket = stream_server(res->ai_family, &port, 0, STREAM_BUFSIZE, 0);
-    freeaddrinfo(res);
-
-    if (socket < 0) {
-        char *m;
-        char *q;
-       int save_errno = errno;
-        char *qdiskname = quote_string(info->diskname);
-
-        m = vstralloc("[port create failure: ",
-                      strerror(save_errno),
-                      "]",
-                      NULL);
-        q = quote_string(m);
-        putresult(TAPE_ERROR, "%s %s\n", info->handle, q);
-        log_add(L_FAIL, "%s %s %s %d %s",
-                info->hostname, qdiskname, info->timestamp,
-                info->level, q);
-        amfree(qdiskname);
-        amfree(m);
-        amfree(q);
-        return FALSE;
-    }
-
-    putresult(PORT, "%d\n", port);
-
-    fd = stream_accept(socket, CONNECT_TIMEOUT, 0, STREAM_BUFSIZE);
-
-    if (fd < 0) {
-        char *m, *q;
-       int save_errno = errno;
-        char *qdiskname = quote_string(info->diskname);
-        m = vstralloc("[port connect failure: ",
-                      strerror(save_errno),
-                      "]",
-                      NULL);
-        q = quote_string(m);
-        putresult(TAPE_ERROR, "%s %s\n", info->handle, q);
-        log_add(L_FAIL, "%s %s %s %d %s",
-                info->hostname, qdiskname, info->timestamp,
-                info->level, q);
-        amfree(qdiskname);
-        aclose(socket);
-        amfree(m);
-        amfree(q);
-        return FALSE;
-    } else {
-        aclose(socket);
-    }
-
-    info->source = taper_source_new(info->handle, PORT_WRITE, NULL, fd,
-                                    split_diskbuffer, splitsize,
-                                    fallback_splitsize);
-    /* FIXME: This should be handled properly. */
-    g_assert(info->source != NULL);
-    return TRUE;
-}
-
-typedef struct {
-    ConsumerFunctor next_consumer;
-    gpointer next_consumer_data;
-    guint64 bytes_written;
-} CountingConsumerData;
-
-/* A ConsumerFunctor. This consumer just passes its arguments on to a
-   second consumer, but counts the number of bytes successfully
-   written. */
-static ssize_t counting_consumer(gpointer user_data, queue_buffer_t * buffer) {
-    ssize_t result;
-    CountingConsumerData * data = user_data;
-
-    result = data->next_consumer(data->next_consumer_data, buffer);
-    
-    if (result > 0) {
-        data->bytes_written += result;
-    }
-
-    return result;
-}
-
-static gboolean boolean_prolong(void * data) {
-    if (data == NULL) {
-        return TRUE; /* Do not interrupt. */
-    } else {
-        return *(gboolean*)data;
-    }
-}
-
-static double get_kbps(double kb, double secs) {
-    /* avoid division by zero */
-    if (secs < 0.0001)
-       return 0.0;
-    return kb / secs;
-}
-
-/* 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 tape"),
-                    *label);
-        }
-
-    }
-    amfree(timestamp);
-    return TRUE;
-}
-
-typedef struct {
-    taper_state_t * state;
-    gboolean prolong; /* scan stops when this is FALSE. */
-    char *errmsg;
-} tape_search_request_t;
-
-/* A GThread that runs taper_scan. */
-static gpointer tape_search_thread(gpointer data) {
-    tape_search_request_t * request = data;
-
-    if (request->state->next_tape_label != NULL &&
-        request->state->next_tape_device != NULL) {
-        return GINT_TO_POINTER(TRUE);
-    } else {
-        amfree(request->state->next_tape_label);
-        amfree(request->state->next_tape_device);
-    }
-
-    return GINT_TO_POINTER
-        (simple_taper_scan(request->state,
-                           &(request->prolong),
-                          &(request->errmsg)));
-}
-
-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;
-    struct cmdargs *cmdargs;
-    cmd_t cmd;
-
-    if (state->device != NULL) {
-        return TRUE;
-    }
-
-    /* We save the value here in case it changes while we're running. */
-    use_threads = g_thread_supported();
-
-    search_request.state = state;
-    search_request.prolong = TRUE;
-    search_request.errmsg = NULL;
-    if (use_threads) {
-        tape_search = g_thread_create(tape_search_thread,
-                                      &search_request, TRUE, NULL);
-    }
-    
-    putresult(REQUEST_NEW_TAPE, "%s\n", dump->handle);
-    cmdargs = getcmd();
-    cmd = cmdargs->cmd;
-
-    switch (cmd) {
-    default:
-        g_fprintf(stderr, "taper: Got odd message from driver, expected NEW-TAPE or NO-NEW-TAPE.\n");
-        /* FALLTHROUGH. */
-    case NEW_TAPE: {
-        gboolean search_result;
-        if (use_threads) {
-            search_result = GPOINTER_TO_INT(g_thread_join(tape_search));
-        } else {
-            search_result =
-                GPOINTER_TO_INT(tape_search_thread(&search_request));
-        }
-        if (search_result) {
-            /* We don't say NEW_TAPE until we actually write the label. */
-           amfree(search_request.errmsg);
-           free_cmdargs(cmdargs);
-            return TRUE;
-        } else {
-            putresult(NO_NEW_TAPE, "%s\n", dump->handle);
-            log_taper_scan_errmsg(search_request.errmsg);
-           log_add(L_ERROR, "no-tape [%s]", "No more writable valid tape found");
-           free_cmdargs(cmdargs);
-            return FALSE;
-        }
-    }
-    case NO_NEW_TAPE:
-        search_request.prolong = FALSE;
-        if (use_threads) {
-            g_thread_join(tape_search);
-        }
-       log_add(L_ERROR, "no-tape [%s]", cmdargs->argv[1]);
-       state->last_errmsg = stralloc(cmdargs->argv[1]);
-       free_cmdargs(cmdargs);
-        return FALSE;
-    }
-    /* NOTREACHED */
-}
-
-/* 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;
-}
-
-static void
-update_tapelist(
-    taper_state_t *state)
-{
-    char *tapelist_name = NULL;
-    char *tapelist_name_old = NULL;
-    tape_t *tp;
-    char *comment = NULL;
-
-    tapelist_name = config_dir_relative(getconf_str(CNF_TAPELIST));
-    if (state->cur_tape == 0) {
-       tapelist_name_old = stralloc2(tapelist_name, ".yesterday");
-    } else {
-       char cur_str[NUM_STR_SIZE];
-       g_snprintf(cur_str, SIZEOF(cur_str), "%d", state->cur_tape - 1);
-       tapelist_name_old = vstralloc(tapelist_name,
-                                     ".today.", cur_str, NULL);
-    }
-
-   if (read_tapelist(tapelist_name) != 0) {
-        log_add(L_INFO, "pid-done %ld", (long)getpid());
-        error("could not load tapelist \"%s\"", tapelist_name);
-        /*NOTREACHED*/
-    }
-
-    /* make a copy of the tapelist file */
-    if (write_tapelist(tapelist_name_old)) {
-        log_add(L_INFO, "pid-done %ld", (long)getpid());
-       error("could not write tapelist: %s", strerror(errno));
-       /*NOTREACHED*/
-    }
-    amfree(tapelist_name_old);
-
-    /* get a copy of the comment, before freeing the old record */
-    tp = lookup_tapelabel(state->device->volume_label);
-    if (tp && tp->comment)
-       comment = stralloc(tp->comment);
-
-    /* edit the tapelist and rewrite it */
-    remove_tapelabel(state->device->volume_label);
-    add_tapelabel(state->driver_start_time,
-                  state->device->volume_label,
-                 comment);
-    if (write_tapelist(tapelist_name)) {
-       error("could not write tapelist: %s", strerror(errno));
-       /*NOTREACHED*/
-    }
-    amfree(tapelist_name);
-    amfree(comment);
-}
-
-/* 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;
-    }
-    state->total_bytes = 0;
-    if (!find_new_tape(state, dump_info)) {
-        return FALSE;
-    }
-
-    return label_new_tape(state, dump_info);
-}
-
-static gboolean label_new_tape(taper_state_t * state, dump_info_t * dump_info) {
-    char *old_volume_name = NULL;
-    char *old_volume_time = NULL;
-    tape_search_request_t request;
-    gboolean search_result;
-    DeviceStatusFlags status;
-
-    /* If we got here, it means that we have found a tape to label and
-     * have gotten permission from the driver to write it. But we
-     * still can say NO-NEW-TAPE if a problem shows up, and must still
-     * say NEW-TAPE if one doesn't. */
-
-    amfree(state->last_errmsg);
-    state->device = device_open(state->next_tape_device);
-    g_assert(state->device != NULL);
-    amfree(state->next_tape_device);
-
-    if (state->device->status != DEVICE_STATUS_SUCCESS)
-       goto skip_volume;
-
-    if (!device_configure(state->device, TRUE))
-       goto skip_volume;
-
-    /* if we have an error, and are sure it isn't just an unlabeled volume,
-     * then skip this volume */
-    status = device_read_label(state->device);
-    if ((status & ~DEVICE_STATUS_VOLUME_UNLABELED) &&
-       !(status & DEVICE_STATUS_VOLUME_UNLABELED))
-       goto skip_volume;
-
-    old_volume_name = g_strdup(state->device->volume_label);
-    old_volume_time = g_strdup(state->device->volume_time);
-
-    if (!device_start(state->device, ACCESS_WRITE, state->next_tape_label,
-                      state->driver_start_time)) {
-        gboolean tape_used;
-
-        /* Something broke, see if we can tell if the volume was erased or
-         * not. */
-        g_fprintf(stderr, "taper: Error writing label %s to device %s: %s.\n",
-                state->next_tape_label, state->device->device_name,
-               device_error_or_status(state->device));
-
-        if (!device_finish(state->device))
-           goto request_new_volume;
-
-       /* This time, if we can't read the label, assume we've overwritten
-        * the volume or otherwise corrupted it */
-       status = device_read_label(state->device);
-       if ((status & ~DEVICE_STATUS_VOLUME_UNLABELED) &&
-           !(status & DEVICE_STATUS_VOLUME_UNLABELED))
-           goto request_new_volume;
-
-        tape_used = check_volume_changed(state->device, old_volume_name, 
-                                         old_volume_time);
-
-        if (tape_used)
-           goto request_new_volume;
-        else
-           goto skip_volume;
-    }
-
-    amfree(old_volume_name);
-    amfree(old_volume_time);
-    amfree(state->next_tape_label);
-
-    update_tapelist(state);
-    state->cur_tape++;
-
-    if (state->have_changer &&
-       changer_label("UNKNOWN", state->device->volume_label) != 0) {
-       error(_("couldn't update barcode database"));
-       /*NOTREACHED*/
-    }
-
-    log_add(L_START, "datestamp %s label %s tape %d",
-            state->driver_start_time, state->device->volume_label,
-            state->cur_tape);
-    putresult(NEW_TAPE, "%s %s\n", dump_info->handle,
-             state->device->volume_label);
-
-    return TRUE;
-
-request_new_volume:
-    /* Tell the driver we overwrote this volume, even if it was empty, and request
-     * a new volume. */
-    if (state->device)
-       state->last_errmsg = newstralloc(state->last_errmsg, device_error_or_status(state->device));
-    else
-       state->last_errmsg = newstralloc(state->last_errmsg, "(unknown)");
-
-    putresult(NEW_TAPE, "%s %s\n", dump_info->handle,
-             state->next_tape_label);
-    if (old_volume_name) {
-       log_add(L_WARNING, "Problem writing label '%s' to volume %s "
-               "(volume may be erased): %s\n",
-               state->next_tape_label, old_volume_name,
-               state->last_errmsg);
-    } else {
-       log_add(L_WARNING, "Problem writing label '%s' to new volume "
-               "(volume may be erased): %s\n", state->next_tape_label,
-               state->last_errmsg);
-    }
-
-    if (state->device) {
-        g_object_unref(state->device);
-        state->device = NULL;
-    }
-
-    amfree(state->next_tape_label);
-    amfree(old_volume_name);
-    amfree(old_volume_time);
-
-    return find_and_label_new_tape(state, dump_info);
-
-skip_volume:
-    /* grab a new volume without talking to the driver again -- we do this if we're
-     * confident we didn't overwrite the last tape we got. */
-    if (state->device)
-       state->last_errmsg = newstralloc(state->last_errmsg, device_error_or_status(state->device));
-    else
-       state->last_errmsg = newstralloc(state->last_errmsg, "(unknown)");
-
-    if (old_volume_name) {
-       log_add(L_WARNING, "Problem writing label '%s' to volume '%s' "
-               "(old volume data intact): %s\n",
-               state->next_tape_label, old_volume_name, state->last_errmsg);
-    } else {
-       log_add(L_WARNING, "Problem writing label '%s' to new volume "
-               "(old volume data intact): %s\n", state->next_tape_label,
-               state->last_errmsg);
-    }
-
-    if (state->device) {
-        g_object_unref(state->device);
-        state->device = NULL;
-    }
-
-    amfree(state->next_tape_label);
-    amfree(old_volume_name);
-    amfree(old_volume_time);
-
-    request.state = state;
-    request.prolong = TRUE;
-    request.errmsg = NULL;
-    search_result = GPOINTER_TO_INT(tape_search_thread(&request));
-    if (search_result) {
-       amfree(request.errmsg);
-       return label_new_tape(state, dump_info);
-    } else {
-       /* Problem finding a new tape! */
-       log_taper_scan_errmsg(request.errmsg);
-       putresult(NO_NEW_TAPE, "%s\n", dump_info->handle);
-       return FALSE;
-    }
-}
-
-/* Find out if the dump is PARTIAL or not, and set the proper driver
-   and logfile tags for the dump. */
-static void find_completion_tags(dump_info_t * dump_info, /* IN */
-                                 cmd_t * result_cmd,      /* OUT */
-                                 logtype_t * result_log   /* OUT */) {
-    /* result_cmd is always DONE because the taper wrote all the input to
-     * the output. driver need to know if the taper completed its job.
-     * result_log is set to L_PARTIAL if the image is partial, the log
-     * must tell if the image is partial or complete.
-     */
-       
-    if (taper_source_is_partial(dump_info->source)) {
-        *result_cmd = DONE;
-        *result_log = L_PARTIAL;
-    } else {
-        *result_cmd = DONE;
-        *result_log = L_DONE;
-    }
-}
-
-/* Put an L_PARTIAL message to the logfile. */
-static void put_partial_log(dump_info_t * dump_info, double dump_time,
-                            guint64 dump_kbytes, char *errstr) {
-    char * qdiskname = quote_string(dump_info->diskname);
-
-    log_add(L_PARTIAL, "%s %s %s %d %d [sec %f kb %ju kps %f] %s",
-            dump_info->hostname, qdiskname, dump_info->timestamp,
-            dump_info->current_part, dump_info->level, dump_time,
-            (uintmax_t)dump_kbytes, get_kbps(dump_kbytes, dump_time),
-           errstr);
-    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 = get_kbps((double)run_bytes / 1024.0, part_time);
-        
-    char * qdiskname = quote_string(dump_info->diskname);
-
-    if (queue_result == QUEUE_SUCCESS) {
-        dump_info->total_time = timesadd(run_time, dump_info->total_time);
-        dump_info->total_bytes += run_bytes;
-
-        log_add(L_PART, "%s %d %s %s %s %d/%d %d [sec %f kb %ju kps %f]",
-                taper_state->device->volume_label,
-                taper_state->device->file, dump_info->hostname, qdiskname,
-                dump_info->timestamp, dump_info->current_part,
-                taper_source_predict_parts(dump_info->source),
-                dump_info->level, part_time, (uintmax_t)part_kbytes, part_kbps);
-        putresult(PARTDONE, "%s %s %d %ju \"[sec %f kb %ju kps %f]\"\n",
-                  dump_info->handle, taper_state->device->volume_label,
-                  taper_state->device->file, (uintmax_t)part_kbytes, part_time,
-                 (uintmax_t)part_kbytes, part_kbps);
-       taper_state->total_bytes += run_bytes;
-        
-        if (taper_source_get_end_of_data(dump_info->source)) {
-            cmd_t result_cmd;
-            logtype_t result_log;
-            double dump_time = g_timeval_to_double(dump_info->total_time);
-            guint64 dump_kbytes = dump_info->total_bytes / 1024;
-            double dump_kbps = get_kbps((double)dump_info->total_bytes / 1024.0, dump_time);
-
-            find_completion_tags(dump_info, &result_cmd, &result_log);
-
-            g_object_unref(dump_info->source);
-            dump_info->source = NULL;
-        
-            log_add(result_log, "%s %s %s %d %d [sec %f kb %ju kps %f]",
-                    dump_info->hostname, qdiskname, dump_info->timestamp,
-                    dump_info->current_part, dump_info->level, dump_time,
-                   (uintmax_t)dump_kbytes, dump_kbps);
-            putresult(result_cmd, "%s INPUT-GOOD TAPE-GOOD "
-                      "\"[sec %f kb %ju kps %f]\" \"\" \"\"\n",
-                      dump_info->handle, dump_time, (uintmax_t)dump_kbytes,
-                      dump_kbps);
-            
-            amfree(qdiskname);
-            return FALSE;
-        } else if (taper_source_get_end_of_part(dump_info->source)) {
-            taper_source_start_new_part(dump_info->source);
-            dump_info->current_part ++;
-            amfree(qdiskname);
-            return TRUE;
-        }
-        /* If we didn't read EOF or EOP, then an error
-           occured. But we read QUEUE_SUCCESS, so something is
-           b0rked. */
-        g_assert_not_reached();
-    } else {
-        char * volume_label = strdup(taper_state->device->volume_label);
-        int file_number = taper_state->device->file;
-        double dump_time, dump_kbps;
-        guint64 dump_kbytes;
-       char *producer_errstr = quote_string(
-                                  taper_source_get_errmsg(dump_info->source));
-       char *consumer_errstr = quote_string(
-                                  device_error(taper_state->device));
-
-        log_add(L_PARTPARTIAL,
-                "%s %d %s %s %s %d/%d %d [sec %f kb %ju kps %f] %s",
-                volume_label, file_number, dump_info->hostname, qdiskname,
-                dump_info->timestamp, dump_info->current_part,
-                taper_source_predict_parts(dump_info->source),
-                dump_info->level, part_time, (uintmax_t)part_kbytes, part_kbps,
-               consumer_errstr);
-       log_add(L_INFO, "tape %s kb %lld fm %d [OK]\n",
-               volume_label,
-               (long long)((taper_state->total_bytes+(off_t)1023) / (off_t)1024),
-               taper_state->device->file);
-
-        /* A problem occured. */
-        if (queue_result & QUEUE_CONSUMER_ERROR) {
-           /* Make a note if this was EOM (we treat EOM the same as any other error,
-            * so it's just for debugging purposes */
-           if (taper_state->device->is_eof)
-               g_debug("device %s ran out of space", taper_state->device->device_name);
-
-            /* Close the device. */
-            device_finish(taper_state->device);
-            g_object_unref(taper_state->device);
-            taper_state->device = NULL;
-        }
-        
-        amfree(volume_label);
-        
-        if ((queue_result & QUEUE_CONSUMER_ERROR) &&
-            (!(queue_result & QUEUE_PRODUCER_ERROR)) &&
-            taper_source_seek_to_part_start(dump_info->source)) {
-            /* It is recoverable. */
-            log_add(L_INFO, "Will request retry of failed split part.");
-            if (find_and_label_new_tape(taper_state, dump_info)) {
-                /* dump_info->current_part is unchanged. */
-                amfree(qdiskname);
-                return TRUE;
-            }
-        }
-
-        dump_info->total_time = timesadd(run_time, dump_info->total_time);
-        dump_info->total_bytes += run_bytes;
-        dump_time = g_timeval_to_double(dump_info->total_time);
-        dump_kbytes = dump_info->total_bytes / 1024;
-        dump_kbps = get_kbps((double)dump_info->total_bytes / 1024.0, dump_time);
-        
-        putresult(PARTIAL,
-                  "%s INPUT-%s TAPE-%s "
-                  "\"[sec %f kb %ju kps %f]\" %s %s\n",
-                  dump_info->handle,
-                  (queue_result & QUEUE_PRODUCER_ERROR) ? "ERROR" : "GOOD",
-                  (queue_result & QUEUE_CONSUMER_ERROR) ? "ERROR" : "GOOD",
-                  dump_time, (uintmax_t)dump_kbytes, dump_kbps,
-                 producer_errstr, consumer_errstr);
-       if (queue_result & QUEUE_CONSUMER_ERROR) {
-            put_partial_log(dump_info, dump_time, dump_kbytes,
-                           consumer_errstr);
-       } else {
-            put_partial_log(dump_info, dump_time, dump_kbytes,
-                           producer_errstr);
-       }
-       amfree(producer_errstr);
-       amfree(consumer_errstr);
-    }
-
-    amfree(qdiskname);
-    return FALSE;
-}
-
-/* Generate the actual header structure to write to tape. This means dropping
- * bits related to the holding disk, and adding bits for split dumps. */
-static dumpfile_t * munge_headers(dump_info_t * dump_info) {
-    dumpfile_t * rval;
-    int expected_splits;
-    
-    rval = taper_source_get_first_header(dump_info->source);
-
-    if (rval == NULL) {
-        return NULL;
-    }
-
-    rval->cont_filename[0] = '\0';
-
-    expected_splits = taper_source_predict_parts(dump_info->source);
-
-    if (expected_splits != 1) {
-        rval->type = F_SPLIT_DUMPFILE;
-        rval->partnum = dump_info->current_part;
-        rval->totalparts = expected_splits;
-    }
-
-    return rval;
-}
-
-/* We call this when we can't find a tape to write data to. This could
-   happen with the first (or only) part of a file, but it could also
-   happen with an intermediate part of a split dump. dump_bytes
-   is 0 if this is the first part of a dump. */
-static void bail_no_volume(
-    dump_info_t *dump_info,
-    char *errmsg)
-{
-    char *errstr;
-    if (errmsg)
-       errstr = quote_string(errmsg);
-    else
-       errstr = quote_string("no new tape");
-    if (dump_info->total_bytes > 0) {
-        /* Second or later part of a split dump, so PARTIAL message. */
-        double dump_time = g_timeval_to_double(dump_info->total_time);
-        guint64 dump_kbytes = dump_info->total_bytes / 1024;
-        double dump_kbps = get_kbps(dump_kbytes, dump_time);
-        putresult(PARTIAL,
-                  "%s INPUT-GOOD TAPE-ERROR "
-                  "\"[sec %f kb %ju kps %f]\" \"\" %s\n",
-                  dump_info->handle, 
-                  dump_time, (uintmax_t)dump_kbytes, dump_kbps, errstr);
-        put_partial_log(dump_info, dump_time, dump_kbytes, errstr);
-    } else {
-        char * qdiskname = quote_string(dump_info->diskname);
-        putresult(FAILED,
-                  "%s INPUT-GOOD TAPE-ERROR \"\" %s\n",
-                  dump_info->handle, errstr);
-        log_add(L_FAIL, "%s %s %s %d %s",
-                dump_info->hostname, qdiskname, dump_info->timestamp,
-                dump_info->level, errstr);
-       amfree(qdiskname);
-    }
-    amfree(errstr);
-}
-
-/* Link up the TaperSource with the Device, including retries etc. */
-static void run_device_output(taper_state_t * taper_state,
-                              dump_info_t * dump_info) {
-    GValue val;
-    guint file_number;
-    dump_info->current_part = 1;
-    dump_info->total_time.tv_sec = 0;
-    dump_info->total_time.tv_usec = 0;
-    dump_info->total_bytes = 0;
-
-    for (;;) {
-        GTimeVal start_time, end_time, run_time;
-        StreamingRequirement streaming_mode;
-        queue_result_flags queue_result;
-        CountingConsumerData consumer_data;
-        dumpfile_t *this_header;
-        size_t max_memory;
-        
-        this_header = munge_headers(dump_info);
-        if (this_header == NULL) {
-            char * qdiskname = quote_string(dump_info->diskname);
-           char * errstr = taper_source_get_errmsg(dump_info->source);
-           if (!errstr)
-               errstr = "Failed reading dump header.";
-           errstr = quote_string(errstr);
-            putresult(FAILED,
-             "%s INPUT-ERROR TAPE-GOOD %s \"\"\n",
-                      dump_info->handle, errstr);
-            log_add(L_FAIL, "%s %s %s %d %s",
-                    dump_info->hostname, qdiskname, dump_info->timestamp,
-                    dump_info->level, errstr);
-            amfree(qdiskname);
-           amfree(errstr);
-            return;
-        }            
-
-        if (!find_and_label_new_tape(taper_state, dump_info)) {
-            bail_no_volume(dump_info, taper_state->last_errmsg);
-           dumpfile_free(this_header);
-            return;
-        }
-
-       while (!device_start_file(taper_state->device, this_header)) {
-            /* Close the device. */
-            device_finish(taper_state->device);
-            g_object_unref(taper_state->device);
-            taper_state->device = NULL;
-
-            if (!find_and_label_new_tape(taper_state, dump_info)) {
-               bail_no_volume(dump_info, taper_state->last_errmsg);
-               dumpfile_free(this_header);
-               return;
-            }
-        }
-       dumpfile_free(this_header);
-
-        bzero(&val, sizeof(val));
-        if (!device_property_get(taper_state->device, PROPERTY_STREAMING, &val)
-            || !G_VALUE_HOLDS(&val, STREAMING_REQUIREMENT_TYPE)) {
-            g_fprintf(stderr, "taper: Couldn't get streaming type!\n");
-            streaming_mode = STREAMING_REQUIREMENT_REQUIRED;
-        } else {
-            streaming_mode = g_value_get_enum(&val);
-        }
-    
-        file_number = taper_state->device->file;
-
-        consumer_data.next_consumer = device_write_consumer;
-        consumer_data.next_consumer_data = taper_state->device;
-        consumer_data.bytes_written = 0;
-
-        g_get_current_time(&start_time);
-
-        if (getconf_seen(CNF_DEVICE_OUTPUT_BUFFER_SIZE)) {
-            max_memory = getconf_size(CNF_DEVICE_OUTPUT_BUFFER_SIZE);
-            if (getconf_seen(CNF_TAPEBUFS)) {
-                g_fprintf(stderr,
-                        "Configuration directives 'device_output_buffer_size' "
-                        "and \n"
-                        "'tapebufs' are incompatible; using former.\n");
-            }
-        } else if (getconf_seen(CNF_TAPEBUFS)) {
-            max_memory = getconf_int(CNF_TAPEBUFS) *
-                taper_state->device->block_size;
-        } else {
-            /* Use default. */
-            max_memory = getconf_size(CNF_DEVICE_OUTPUT_BUFFER_SIZE);
-        }
-
-        queue_result = do_consumer_producer_queue_full
-            (taper_source_producer,
-             dump_info->source,
-             counting_consumer,
-             &consumer_data,
-             taper_state->device->block_size, max_memory,
-             streaming_mode);
-
-        g_get_current_time(&end_time);
-        run_time = timesub(end_time, start_time);
-
-        /* The device_write_consumer leaves the file open, so close it now. */
-        if (!device_finish_file(taper_state->device)) {
-            queue_result = queue_result | QUEUE_CONSUMER_ERROR;
-        }
-
-        if (!finish_part_attempt(taper_state, dump_info, queue_result,
-                                 run_time, consumer_data.bytes_written)) {
-            break;
-        }
-    }
-}
-
-/* Handle a PORT_WRITE command. */
-static void process_port_write(taper_state_t * state,
-                               struct cmdargs * cmdargs) {
-    dump_info_t dump_state;
-    guint64 splitsize;
-    guint64 fallback_splitsize;
-    char * split_diskbuffer;
-    char * argnames[] = {"command",               /* 0 */
-                        "handle",                /* 1 */
-                         "hostname",              /* 2 */
-                         "diskname",              /* 3 */
-                         "level",                 /* 4 */
-                         "datestamp",             /* 5 */
-                         "splitsize",             /* 6 */
-                         "split_diskbuffer",      /* 7 */
-                         "fallback_splitsize",    /* 8 */
-                          NULL };
-
-    validate_args(cmdargs, argnames);
-
-    dump_state.handle = g_strdup(cmdargs->argv[1]);
-    dump_state.hostname = g_strdup(cmdargs->argv[2]);
-    dump_state.diskname = g_strdup(cmdargs->argv[3]);
-    
-    errno = 0;
-    dump_state.level = strtol(cmdargs->argv[4], NULL, 10);
-    if (errno != 0) {
-        error("error [taper PORT-WRITE: Invalid dump level %s]",
-              cmdargs->argv[4]);
-        g_assert_not_reached();
-    }
-    
-    dump_state.timestamp = strdup(cmdargs->argv[5]);
-
-    errno = 0;
-    splitsize = g_ascii_strtoull(cmdargs->argv[6], NULL, 10);
-    if (errno != 0) {
-        error("error [taper PORT-WRITE: Invalid splitsize %s]",
-              cmdargs->argv[6]);
-        g_assert_not_reached();
-    }
-    
-    if (strcmp(cmdargs->argv[7], "NULL") == 0) {
-        split_diskbuffer = NULL;
-    } else {
-        split_diskbuffer = g_strdup(cmdargs->argv[7]);
-    }
-    
-    errno = 0;
-    fallback_splitsize = g_ascii_strtoull(cmdargs->argv[8], NULL, 10);
-    if (errno != 0) {
-        error("error [taper PORT-WRITE: Invalid fallback_splitsize %s]",
-              cmdargs->argv[8]);
-        g_assert_not_reached();
-    }
-
-    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);
-
-    run_device_output(state, &dump_state);
-
-    free_dump_info(&dump_state);
-}
-
-/* Handle a FILE_WRITE command. */
-static void process_file_write(taper_state_t * state,
-                               struct cmdargs * cmdargs) {
-    dump_info_t dump_state;
-    char * holding_disk_file;
-    guint64 splitsize;
-    char * argnames[] = {"command",               /* 0 */
-                        "handle",                /* 1 */
-                         "filename",              /* 2 */
-                         "hostname",              /* 3 */
-                         "diskname",              /* 4 */
-                         "level",                 /* 5 */
-                         "datestamp",             /* 6 */
-                         "splitsize",             /* 7 */
-                          NULL };
-
-    validate_args(cmdargs, argnames);
-
-    dump_state.handle = g_strdup(cmdargs->argv[1]);
-    holding_disk_file = g_strdup(cmdargs->argv[2]);
-    dump_state.hostname = g_strdup(cmdargs->argv[3]);
-    dump_state.diskname = g_strdup(cmdargs->argv[4]);
-    
-    errno = 0;
-    dump_state.level = strtol(cmdargs->argv[5], NULL, 10);
-    if (errno != 0) {
-        error("error [taper FILE-WRITE: Invalid dump level %s]",
-              cmdargs->argv[5]);
-        g_assert_not_reached();
-    }
-    
-    dump_state.timestamp = strdup(cmdargs->argv[6]);
-
-    errno = 0;
-    splitsize = g_ascii_strtoull(cmdargs->argv[7], NULL, 10);
-    if (errno != 0) {
-        error("error [taper FILE-WRITE: Invalid splitsize %s]",
-              cmdargs->argv[7]);
-        g_assert_not_reached();
-    }
-
-    dump_state.id_string = g_strdup_printf("%s:%s.%d", dump_state.hostname,
-                                           dump_state.diskname,
-                                          dump_state.level);
-    
-    dump_state.source = taper_source_new(dump_state.handle, FILE_WRITE,
-                                         holding_disk_file, -1,
-                                         NULL, splitsize, -1);
-    /* FIXME: This should be handled properly. */
-    g_assert(dump_state.source != NULL);
-
-    run_device_output(state, &dump_state);
-
-    free_dump_info(&dump_state);
-    amfree(holding_disk_file);
-}
-
-/* Send QUITTING message to driver and associated logging. Always
-   returns false. */
-static gboolean send_quitting(taper_state_t * state) {
-    putresult(QUITTING, "\n");
-    g_fprintf(stderr,"taper: DONE\n");
-    cleanup(state);
-    return FALSE;
-}
-
-/* This function recieves the START_TAPER command from driver, and
-   returns the attached timestamp. */
-static gboolean find_first_tape(taper_state_t * state) {
-    struct cmdargs *cmdargs;
-    tape_search_request_t search_request;
-    GThread * tape_search = NULL;
-    gboolean use_threads;
-
-    /* We save the value here in case it changes while we're running. */
-    use_threads = g_thread_supported();
-
-    search_request.state = state;
-    search_request.prolong = TRUE;
-    search_request.errmsg = NULL;
-    
-    if (use_threads) {
-        tape_search = g_thread_create(tape_search_thread,
-                                      &search_request, TRUE, NULL);
-    }
-
-    cmdargs = getcmd();
-
-    switch (cmdargs->cmd) {
-    case START_TAPER: {
-        gboolean search_result;
-        state->driver_start_time = strdup(cmdargs->argv[1]);
-        if (use_threads) {
-            search_result = GPOINTER_TO_INT(g_thread_join(tape_search));
-        } else {
-            search_result =
-                GPOINTER_TO_INT(tape_search_thread(&search_request));
-        }
-        if (search_result) {
-            putresult(TAPER_OK, "\n");
-        } else {
-            putresult(TAPE_ERROR, "Could not find a tape to use.\n");
-           log_add(L_ERROR, "no-tape [%s]", "Could not find a tape to use");
-           if (search_request.errmsg != NULL) {
-               char *c, *c1;
-               c = c1 = search_request.errmsg;
-               while (*c != '\0') {
-                   if (*c == '\n') {
-                       *c = '\0';
-                       log_add(L_WARNING,"%s", c1);
-                       c1 = c+1;
-                   }
-                   c++;
-               }
-               if (strlen(c1) > 1 )
-                   log_add(L_WARNING,"%s", c1);
-           }
-        }
-       amfree(search_request.errmsg);
-       free_cmdargs(cmdargs);
-        return TRUE;
-    }
-    case QUIT:
-        search_request.prolong = FALSE;
-        if (use_threads) {
-            g_thread_join(tape_search);
-        }
-       free_cmdargs(cmdargs);
-        return send_quitting(state);
-    default:
-        error("error [file_reader_side cmd %d argc %d]", cmdargs->cmd, cmdargs->argc);
-    }
-
-    g_assert_not_reached();
-}
-
-/* In running mode (not startup mode), get a command from driver and
-   deal with it. */
-static gboolean process_driver_command(taper_state_t * state) {
-    struct cmdargs *cmdargs;
-    char * q;
-
-    /* This will return QUIT if driver has died. */
-    cmdargs = getcmd();
-    switch (cmdargs->cmd) {
-    case PORT_WRITE:
-        /*
-         * PORT-WRITE
-         *   handle
-         *   hostname
-         *   features
-         *   diskname
-         *   level
-         *   datestamp
-         *   splitsize
-         *   split_diskbuffer
-         */
-        process_port_write(state, cmdargs);
-        break;
-        
-    case FILE_WRITE:
-        /*
-         * FILE-WRITE
-         *   handle
-         *   filename
-         *   hostname
-         *   features
-         *   diskname
-         *   level
-         *   datestamp
-         *   splitsize
-         */
-        process_file_write(state, cmdargs);
-        break;
-        
-    case QUIT:
-       free_cmdargs(cmdargs);
-       if (state->device && state->device->volume_label) {
-           log_add(L_INFO, "tape %s kb %lld fm %d [OK]\n",
-                   state->device->volume_label,
-                   (long long)((state->total_bytes+(off_t)1023) / (off_t)1024),
-                   state->device->file);
-       }
-        return send_quitting(state);
-    default:
-        if (cmdargs->argc >= 1) {
-            q = quote_string(cmdargs->argv[0]);
-        } else {
-            q = stralloc("(no input?)");
-        }
-        putresult(BAD_COMMAND, "%s\n", q);
-        amfree(q);
-        break;
-    }
-    free_cmdargs(cmdargs);
-
-    return TRUE;
-}
-
-int main(int argc, char ** argv) {
-    char * tapelist_name;
-    taper_state_t state;
-    config_overwrites_t *cfg_ovr = NULL;
-    char *cfg_opt = NULL;
-
-    /*
-     * 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");
-
-    dbopen("server");
-
-    device_api_init();
-    init_taper_state(&state);
-
-    /* Don't die when child closes pipe */
-    signal(SIGPIPE, SIG_IGN);
-
-    g_fprintf(stderr, _("%s: pid %ld executable %s version %s\n"),
-           get_pname(), (long) getpid(), argv[0], version());
-    dbprintf(_("%s: pid %ld executable %s version %s\n"),
-              get_pname(), (long) getpid(), argv[0], version());
-
-    /* Process options */
-
-    cfg_ovr = extract_commandline_config_overwrites(&argc, &argv);
-
-    if(argc > 2) {
-        error("Too many arguments!\n");
-        g_assert_not_reached();
-    }
-    if (argc > 1)
-       cfg_opt = argv[1];
-    config_init(CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_USE_CWD, cfg_opt);
-    apply_config_overwrites(cfg_ovr);
-
-    if (config_errors(NULL) >= CFGERR_ERRORS) {
-       g_critical(_("errors processing config file"));
-    }
-
-    safe_cd();
-
-    set_logerror(logerror);
-
-    check_running_as(RUNNING_AS_DUMPUSER);
-
-    dbrename(get_config_name(), DBG_SUBDIR_SERVER);
-
-    log_add(L_INFO, "%s pid %ld", get_pname(), (long)getpid());
-
-    tapelist_name = config_dir_relative(getconf_str(CNF_TAPELIST));
-
-    if (read_tapelist(tapelist_name) != 0) {
-       log_add(L_INFO, "pid-done %ld", (long)getpid());
-        error("could not load tapelist \"%s\"", tapelist_name);
-        g_assert_not_reached();
-    }
-    amfree(tapelist_name);
-
-    state.have_changer = changer_init();
-    if (state.have_changer < 0) {
-       log_add(L_INFO, "pid-done %ld", (long)getpid());
-        error("changer initialization failed: %s", strerror(errno));
-        g_assert_not_reached();
-    }
-
-    state.next_tape_label = NULL;
-    state.next_tape_device = NULL;
-    state.cur_tape = 0;
-    
-    if (!find_first_tape(&state)) {
-       log_add(L_INFO, "pid-done %ld", (long)getpid());
-        return EXIT_SUCCESS;
-    }
-
-    while (process_driver_command(&state));
-    log_add(L_INFO, "pid-done %ld", (long)getpid());
-    return EXIT_SUCCESS;
-}