Imported Upstream version 3.2.0
[debian/amanda] / server-src / driver.c
index d5f265c0d664f084d16d3eccac23c29927acd6b6..9a0bb27bceba68fa9c80773cb6c6e0cbf4587458 100644 (file)
@@ -24,7 +24,7 @@
  * file named AUTHORS, in the root directory of this distribution.
  */
 /*
- * $Id: driver.c,v 1.198 2006/08/24 01:57:16 paddy_s Exp $
+ * $Id: driver.c 6512 2007-05-24 17:00:24Z ian $
  *
  * controlling process for the Amanda backup system
  */
@@ -35,6 +35,7 @@
  */
 
 #include "amanda.h"
+#include "find.h"
 #include "clock.h"
 #include "conffile.h"
 #include "diskfile.h"
 #include "holding.h"
 #include "infofile.h"
 #include "logfile.h"
-#include "statfs.h"
-#include "version.h"
+#include "fsusage.h"
 #include "driverio.h"
 #include "server_util.h"
+#include "timestamp.h"
 
-#define driver_debug(i,x) do {         \
+#define driver_debug(i, ...) do {      \
        if ((i) <= debug_driver) {      \
-           dbprintf(x);                \
+           dbprintf(__VA_ARGS__);      \
        }                               \
 } while (0)
 
-#define hold_debug(i,x) do {           \
+#define hold_debug(i, ...) do {                \
        if ((i) <= debug_holding) {     \
-           dbprintf(x);                \
+           dbprintf(__VA_ARGS__);      \
        }                               \
 } while (0)
 
-static disklist_t waitq, runq, tapeq, roomq;
+static disklist_t waitq;       // dle waiting estimate result
+static disklist_t runq;                // dle waiting to be dumped to holding disk
+static disklist_t directq;     // dle waiting to be dumped directly to tape
+static disklist_t tapeq;       // dle on holding disk waiting to be written
+                               //   to tape
+static disklist_t roomq;       // dle waiting for more space on holding disk
 static int pending_aborts;
-static disk_t *taper_disk;
 static int degraded_mode;
 static off_t reserved_space;
 static off_t total_disksize;
@@ -70,9 +75,9 @@ static char *chunker_program;
 static int  inparallel;
 static int nodump = 0;
 static off_t tape_length = (off_t)0;
-static off_t tape_left = (off_t)0;
-static int current_tape = 1;
+static int current_tape = 0;
 static int conf_taperalgo;
+static int conf_taper_parallel_write;
 static int conf_runtapes;
 static time_t sleep_time;
 static int idle_reason;
@@ -80,95 +85,129 @@ static char *driver_timestamp;
 static char *hd_driver_timestamp;
 static am_host_t *flushhost = NULL;
 static int need_degraded=0;
-
+static holdalloc_t *holdalloc;
+static int num_holdalloc;
 static event_handle_t *dumpers_ev_time = NULL;
+static event_handle_t *flush_ev_read = NULL;
 static event_handle_t *schedule_ev_read = NULL;
+static int   conf_flush_threshold_dumped;
+static int   conf_flush_threshold_scheduled;
+static int   conf_taperflush;
+static off_t flush_threshold_dumped;
+static off_t flush_threshold_scheduled;
+static off_t taperflush;
+static int   schedule_done;                    // 1 if we don't wait for a
+                                               //   schedule from the planner
+static int   force_flush;                      // All dump are terminated, we
+                                               // must now respect taper_flush
+static int taper_nb_scan_volume = 0;
+static int nb_sent_new_tape = 0;
+static int taper_started = 0;
+static taper_t *last_started_taper;
 
 static int wait_children(int count);
 static void wait_for_children(void);
-static void allocate_bandwidth(interface_t *ip, unsigned long kps);
+static void allocate_bandwidth(netif_t *ip, unsigned long kps);
 static int assign_holdingdisk(assignedhd_t **holdp, disk_t *diskp);
 static void adjust_diskspace(disk_t *diskp, cmd_t cmd);
 static void delete_diskspace(disk_t *diskp);
 static assignedhd_t **build_diskspace(char *destname);
 static int client_constrained(disk_t *dp);
-static void deallocate_bandwidth(interface_t *ip, unsigned long kps);
+static void deallocate_bandwidth(netif_t *ip, unsigned long kps);
 static void dump_schedule(disklist_t *qp, char *str);
-static int dump_to_tape(disk_t *dp);
 static assignedhd_t **find_diskspace(off_t size, int *cur_idle,
                                        assignedhd_t *preferred);
-static unsigned long free_kps(interface_t *ip);
+static unsigned long free_kps(netif_t *ip);
 static off_t free_space(void);
-static void dumper_result(disk_t *dp);
+static void dumper_chunker_result(disk_t *dp);
+static void dumper_taper_result(disk_t *dp);
+static void file_taper_result(disk_t *dp);
 static void handle_dumper_result(void *);
 static void handle_chunker_result(void *);
 static void handle_dumpers_time(void *);
 static void handle_taper_result(void *);
+
 static void holdingdisk_state(char *time_str);
-static dumper_t *idle_dumper(void);
+static taper_t *idle_taper(void);
+static taper_t *taper_from_name(char *name);
 static void interface_state(char *time_str);
 static int queue_length(disklist_t q);
-static disklist_t read_flush(void);
+static void read_flush(void *cookie);
 static void read_schedule(void *cookie);
 static void short_dump_state(void);
 static void startaflush(void);
 static void start_degraded_mode(disklist_t *queuep);
 static void start_some_dumps(disklist_t *rq);
 static void continue_port_dumps(void);
-static void update_failed_dump_to_tape(disk_t *);
-#if 0
-static void dump_state(const char *str);
-#endif
-int main(int main_argc, char **main_argv);
+static void update_failed_dump(disk_t *);
+static int all_taper_idle(void);
+
+typedef enum {
+    TAPE_ACTION_NO_ACTION         = 0,
+    TAPE_ACTION_SCAN              = (1 << 0),
+    TAPE_ACTION_NEW_TAPE          = (1 << 1),
+    TAPE_ACTION_NO_NEW_TAPE       = (1 << 2),
+    TAPE_ACTION_START_A_FLUSH     = (1 << 3),
+    TAPE_ACTION_START_A_FLUSH_FIT = (1 << 4),
+    TAPE_ACTION_MOVE              = (1 << 5)
+} TapeAction;
+
+static TapeAction tape_action(taper_t *taper, char **why_no_new_tape);
 
 static const char *idle_strings[] = {
 #define NOT_IDLE               0
-    "not-idle",
+    T_("not-idle"),
 #define IDLE_NO_DUMPERS                1
-    "no-dumpers",
+    T_("no-dumpers"),
 #define IDLE_START_WAIT                2
-    "start-wait",
+    T_("start-wait"),
 #define IDLE_NO_HOLD           3
-    "no-hold",
+    T_("no-hold"),
 #define IDLE_CLIENT_CONSTRAINED        4
-    "client-constrained",
-#define IDLE_NO_DISKSPACE      5
-    "no-diskspace",
-#define IDLE_TOO_LARGE         6
-    "file-too-large",
-#define IDLE_NO_BANDWIDTH      7
-    "no-bandwidth",
-#define IDLE_TAPER_WAIT                8
-    "taper-wait",
+    T_("client-constrained"),
+#define IDLE_NO_BANDWIDTH      5
+    T_("no-bandwidth"),
+#define IDLE_NO_DISKSPACE      6
+    T_("no-diskspace")
 };
 
 int
 main(
-    int                main_argc,
-    char **    main_argv)
+    int                argc,
+    char **    argv)
 {
     disklist_t origq;
     disk_t *diskp;
     int dsk;
     dumper_t *dumper;
     char *newdir = NULL;
-    generic_fs_stats_t fs;
+    struct fs_usage fsusage;
     holdingdisk_t *hdp;
-    unsigned long malloc_hist_1, malloc_size_1;
-    unsigned long malloc_hist_2, malloc_size_2;
+    identlist_t    il;
     unsigned long reserve = 100;
-    char *conffile;
     char *conf_diskfile;
-    cmd_t cmd;
-    int result_argc;
-    char *result_argv[MAX_ARGS+1];
+    char **result_argv = NULL;
     char *taper_program;
     char *conf_tapetype;
     tapetype_t *tape;
     char *line;
-    int    new_argc,   my_argc;
-    char **new_argv, **my_argv;
     char hostname[1025];
+    intmax_t kb_avail;
+    config_overrides_t *cfg_ovr = NULL;
+    char *cfg_opt = NULL;
+    holdalloc_t *ha, *ha_last;
+    find_result_t *holding_files;
+    disklist_t holding_disklist = { NULL, NULL };
+    int no_taper = FALSE;
+
+    /*
+     * 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);
 
@@ -184,55 +223,60 @@ main(
     /* Don't die when child closes pipe */
     signal(SIGPIPE, SIG_IGN);
 
-    malloc_size_1 = malloc_inuse(&malloc_hist_1);
-
-    erroutput_type = (ERR_AMANDALOG|ERR_INTERACTIVE);
-    set_logerror(logerror);
+    add_amanda_log_handler(amanda_log_stderr);
+    add_amanda_log_handler(amanda_log_trace_log);
 
     startclock();
 
-    parse_conf(main_argc, main_argv, &new_argc, &new_argv);
-    my_argc = new_argc;
-    my_argv = new_argv;
+    cfg_ovr = extract_commandline_config_overrides(&argc, &argv);
 
-    printf("%s: pid %ld executable %s version %s\n",
-          get_pname(), (long) getpid(), my_argv[0], version());
+    if (argc > 1)
+       cfg_opt = argv[1];
+    set_config_overrides(cfg_ovr);
+    config_init(CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_USE_CWD, cfg_opt);
 
-    if (my_argc > 1) {
-       config_name = stralloc(my_argv[1]);
-       config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
-       if(my_argc > 2) {
-           if(strncmp(my_argv[2], "nodump", 6) == 0) {
-               nodump = 1;
-           }
+    conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
+    read_diskfile(conf_diskfile, &origq);
+    disable_skip_disk(&origq);
+    amfree(conf_diskfile);
+
+    if (config_errors(NULL) >= CFGERR_WARNINGS) {
+       config_print_errors();
+       if (config_errors(NULL) >= CFGERR_ERRORS) {
+           g_critical(_("errors processing config file"));
        }
+    }
 
-    } else {
+    log_add(L_INFO, "%s pid %ld", get_pname(), (long)getpid());
+    g_printf(_("%s: pid %ld executable %s version %s\n"),
+          get_pname(), (long) getpid(), argv[0], VERSION);
 
-       char my_cwd[STR_SIZE];
+    if(argc > 2) {
+        if(strcmp(argv[2], "nodump") == 0) {
+            nodump = 1;
+           argv++;
+           argc--;
+        }
+    }
 
-       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);
+    if (argc > 2) {
+       if (strcmp(argv[2], "--no-taper") == 0) {
+           no_taper = TRUE;
+           argv++;
+           argc--;
        }
     }
 
-    safe_cd();
+    safe_cd(); /* do this *after* config_init */
 
-    conffile = stralloc2(config_dir, CONFFILE_NAME);
-    if(read_conffile(conffile)) {
-       error("errors processing config file \"%s\"", conffile);
-       /*NOTREACHED*/
-    }
-    amfree(conffile);
+    check_running_as(RUNNING_AS_DUMPUSER);
 
-    dbrename(config_name, DBG_SUBDIR_SERVER);
+    dbrename(get_config_name(), DBG_SUBDIR_SERVER);
 
-    report_bad_conf_arg();
+    /* load DLEs from the holding disk, in case there's anything to flush there */
+    search_holding_disk(&holding_files, &holding_disklist);
+    /* note that the dumps are added to the global disklist, so we need not consult
+     * holding_files or holding_disklist after this */
 
     amfree(driver_timestamp);
     /* read timestamp from stdin */
@@ -242,17 +286,17 @@ main(
        amfree(line);
     }
     if ( line == NULL ) {
-      error("Did not get DATE line from planner");
+      error(_("Did not get DATE line from planner"));
       /*NOTREACHED*/
     }
     driver_timestamp = alloc(15);
     strncpy(driver_timestamp, &line[5], 14);
     driver_timestamp[14] = '\0';
     amfree(line);
-    log_add(L_START,"date %s", driver_timestamp);
+    log_add(L_START,_("date %s"), driver_timestamp);
 
     gethostname(hostname, SIZEOF(hostname));
-    log_add(L_STATS,"hostname %s", hostname);
+    log_add(L_STATS,_("hostname %s"), hostname);
 
     /* check that we don't do many dump in a day and usetimestamps is off */
     if(strlen(driver_timestamp) == 8) {
@@ -263,43 +307,42 @@ main(
            char *oldlogfile = vstralloc(conf_logdir, "/oldlog/log.",
                                         driver_timestamp, ".0", NULL);
            if(access(logfile, F_OK) == 0 || access(oldlogfile, F_OK) == 0) {
-               log_add(L_WARNING, "WARNING: This is not the first amdump run today. Enable the usetimestamps option in the configuration file if you want to run amdump more than once per calendar day.");
+               log_add(L_WARNING, _("WARNING: This is not the first amdump run today. Enable the usetimestamps option in the configuration file if you want to run amdump more than once per calendar day."));
            }
            amfree(oldlogfile);
            amfree(logfile);
        }
-       hd_driver_timestamp = construct_timestamp(NULL);
+       hd_driver_timestamp = get_timestamp_from_time(0);
     }
     else {
        hd_driver_timestamp = stralloc(driver_timestamp);
     }
 
-    taper_program = vstralloc(libexecdir, "/", "taper", versionsuffix(), NULL);
-    dumper_program = vstralloc(libexecdir, "/", "dumper", versionsuffix(),
-                              NULL);
-    chunker_program = vstralloc(libexecdir, "/", "chunker", versionsuffix(),
-                              NULL);
+    taper_program = vstralloc(amlibexecdir, "/", "taper", NULL);
+    dumper_program = vstralloc(amlibexecdir, "/", "dumper", NULL);
+    chunker_program = vstralloc(amlibexecdir, "/", "chunker", NULL);
 
     conf_taperalgo = getconf_taperalgo(CNF_TAPERALGO);
+    conf_taper_parallel_write = getconf_int(CNF_TAPER_PARALLEL_WRITE);
     conf_tapetype = getconf_str(CNF_TAPETYPE);
     conf_runtapes = getconf_int(CNF_RUNTAPES);
+    if (conf_taper_parallel_write > conf_runtapes) {
+       conf_taper_parallel_write = conf_runtapes;
+    }
     tape = lookup_tapetype(conf_tapetype);
     tape_length = tapetype_get_length(tape);
-    printf("driver: tape size " OFF_T_FMT "\n", (OFF_T_FMT_TYPE)tape_length);
+    g_printf("driver: tape size %lld\n", (long long)tape_length);
+    conf_flush_threshold_dumped = getconf_int(CNF_FLUSH_THRESHOLD_DUMPED);
+    conf_flush_threshold_scheduled = getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED);
+    conf_taperflush = getconf_int(CNF_TAPERFLUSH);
 
-    /* start initializing: read in databases */
+    flush_threshold_dumped = (conf_flush_threshold_dumped * tape_length) / 100;
+    flush_threshold_scheduled = (conf_flush_threshold_scheduled * tape_length) / 100;
+    taperflush = (conf_taperflush *tape_length) / 100;
 
-    conf_diskfile = getconf_str(CNF_DISKFILE);
-    if (*conf_diskfile == '/') {
-       conf_diskfile = stralloc(conf_diskfile);
-    } else {
-       conf_diskfile = stralloc2(config_dir, conf_diskfile);
-    }
-    if (read_diskfile(conf_diskfile, &origq) < 0) {
-       error("could not load disklist \"%s\"", conf_diskfile);
-       /*NOTREACHED*/
-    }
-    amfree(conf_diskfile);
+    driver_debug(1, _("flush-threshold-dumped: %lld\n"), (long long)flush_threshold_dumped);
+    driver_debug(1, _("flush-threshold-scheduled: %lld\n"), (long long)flush_threshold_scheduled);
+    driver_debug(1, _("taperflush: %lld\n"), (long long)taperflush);
 
     /* set up any configuration-dependent variables */
 
@@ -308,62 +351,84 @@ main(
     reserve = (unsigned long)getconf_int(CNF_RESERVE);
 
     total_disksize = (off_t)0;
-    for(hdp = getconf_holdingdisks(), dsk = 0; hdp != NULL; hdp = hdp->next, dsk++) {
-       hdp->up = (void *)alloc(SIZEOF(holdalloc_t));
-       holdalloc(hdp)->allocated_dumpers = 0;
-       holdalloc(hdp)->allocated_space = (off_t)0;
+    ha_last = NULL;
+    num_holdalloc = 0;
+    for (il = getconf_identlist(CNF_HOLDINGDISK), dsk = 0;
+        il != NULL;
+        il = il->next, dsk++) {
+       hdp = lookup_holdingdisk(il->data);
+       ha = alloc(SIZEOF(holdalloc_t));
+       num_holdalloc++;
+
+       /* link the list in the same order as getconf_holdingdisks's results */
+       ha->next = NULL;
+       if (ha_last == NULL)
+           holdalloc = ha;
+       else
+           ha_last->next = ha;
+       ha_last = ha;
+
+       ha->hdisk = hdp;
+       ha->allocated_dumpers = 0;
+       ha->allocated_space = (off_t)0;
+       ha->disksize = holdingdisk_get_disksize(hdp);
 
-       if(get_fs_stats(holdingdisk_get_diskdir(hdp), &fs) == -1
+       /* get disk size */
+       if(get_fs_usage(holdingdisk_get_diskdir(hdp), NULL, &fsusage) == -1
           || access(holdingdisk_get_diskdir(hdp), W_OK) == -1) {
-           log_add(L_WARNING, "WARNING: ignoring holding disk %s: %s\n",
+           log_add(L_WARNING, _("WARNING: ignoring holding disk %s: %s\n"),
                    holdingdisk_get_diskdir(hdp), strerror(errno));
-           hdp->disksize = 0L;
+           ha->disksize = 0L;
            continue;
        }
 
-       if(fs.avail != (off_t)-1) {
-           if(hdp->disksize > (off_t)0) {
-               if(hdp->disksize > fs.avail) {
-                   log_add(L_WARNING,
-                           "WARNING: %s: " OFF_T_FMT " KB requested, "
-                           "but only " OFF_T_FMT " KB available.",
-                           holdingdisk_get_diskdir(hdp),
-                           (OFF_T_FMT_TYPE)hdp->disksize,
-                           (OFF_T_FMT_TYPE)fs.avail);
-                           hdp->disksize = fs.avail;
-               }
-           }
-           else if((fs.avail + hdp->disksize) < (off_t)0) {
+       /* do the division first to avoid potential integer overflow */
+       if (fsusage.fsu_bavail_top_bit_set)
+           kb_avail = 0;
+       else
+           kb_avail = fsusage.fsu_bavail / 1024 * fsusage.fsu_blocksize;
+
+       if(ha->disksize > (off_t)0) {
+           if(ha->disksize > kb_avail) {
                log_add(L_WARNING,
-                       "WARNING: %s: not " OFF_T_FMT " KB free.",
-                       holdingdisk_get_diskdir(hdp), -hdp->disksize);
-               hdp->disksize = (off_t)0;
-               continue;
+                       _("WARNING: %s: %lld KB requested, "
+                       "but only %lld KB available."),
+                       holdingdisk_get_diskdir(hdp),
+                       (long long)ha->disksize,
+                       (long long)kb_avail);
+                       ha->disksize = kb_avail;
            }
-           else
-               hdp->disksize += fs.avail;
        }
+       /* ha->disksize is negative; use all but that amount */
+       else if(kb_avail < -ha->disksize) {
+           log_add(L_WARNING,
+                   _("WARNING: %s: not %lld KB free."),
+                   holdingdisk_get_diskdir(hdp),
+                   (long long)-ha->disksize);
+           ha->disksize = (off_t)0;
+           continue;
+       }
+       else
+           ha->disksize += kb_avail;
 
-       printf("driver: adding holding disk %d dir %s size "
-               OFF_T_FMT " chunksize " OFF_T_FMT "\n",
+       g_printf(_("driver: adding holding disk %d dir %s size %lld chunksize %lld\n"),
               dsk, holdingdisk_get_diskdir(hdp),
-              (OFF_T_FMT_TYPE)hdp->disksize,
-              (OFF_T_FMT_TYPE)(holdingdisk_get_chunksize(hdp)));
+              (long long)ha->disksize,
+              (long long)(holdingdisk_get_chunksize(hdp)));
 
        newdir = newvstralloc(newdir,
                              holdingdisk_get_diskdir(hdp), "/", hd_driver_timestamp,
                              NULL);
        if(!mkholdingdir(newdir)) {
-           hdp->disksize = (off_t)0;
+           ha->disksize = (off_t)0;
        }
-       total_disksize += hdp->disksize;
+       total_disksize += ha->disksize;
     }
 
     reserved_space = total_disksize * (off_t)(reserve / 100);
 
-    printf("reserving " OFF_T_FMT " out of " OFF_T_FMT
-          " for degraded-mode dumps\n",
-          (OFF_T_FMT_TYPE)reserved_space, (OFF_T_FMT_TYPE)free_space());
+    g_printf(_("reserving %lld out of %lld for degraded-mode dumps\n"),
+          (long long)reserved_space, (long long)free_space());
 
     amfree(newdir);
 
@@ -372,10 +437,7 @@ main(
     /* taper takes a while to get going, so start it up right away */
 
     init_driverio();
-    if(conf_runtapes > 0) {
-        startup_tape_process(taper_program);
-        taper_cmd(START_TAPER, driver_timestamp, NULL, 0, NULL);
-    }
+    startup_tape_process(taper_program, conf_taper_parallel_write, no_taper);
 
     /* fire up the dumpers now while we are waiting */
     if(!nodump) startup_dump_processes(dumper_program, inparallel, driver_timestamp);
@@ -390,124 +452,149 @@ main(
 
     runq.head = NULL;
     runq.tail = NULL;
+    directq.head = NULL;
+    directq.tail = NULL;
     waitq = origq;
-    tapeq = read_flush();
+    tapeq.head = NULL;
+    tapeq.tail = NULL;
+    roomq.head = NULL;
+    roomq.tail = NULL;
+    taper_nb_wait_reply = 0;
+
+    need_degraded = 0;
+    if (no_taper || conf_runtapes <= 0) {
+       taper_started = 1; /* we'll pretend the taper started and failed immediately */
+       need_degraded = 1;
+    } else {
+       tapetable[0].state = TAPER_STATE_INIT;
+       taper_nb_wait_reply++;
+       taper_nb_scan_volume++;
+       taper_ev_read = event_register(taper_fd, EV_READFD,
+                                      handle_taper_result, NULL);
+        taper_cmd(START_TAPER, NULL, tapetable[0].name, 0, driver_timestamp);
+    }
 
-    roomq.head = roomq.tail = NULL;
+    flush_ev_read = event_register((event_id_t)0, EV_READFD, read_flush, NULL);
 
-    log_add(L_STATS, "startup time %s", walltime_str(curclock()));
+    log_add(L_STATS, _("startup time %s"), walltime_str(curclock()));
 
-    printf("driver: start time %s inparallel %d bandwidth %lu diskspace "
-          OFF_T_FMT " ", walltime_str(curclock()), inparallel,
-          free_kps((interface_t *)0), (OFF_T_FMT_TYPE)free_space());
-    printf(" dir %s datestamp %s driver: drain-ends tapeq %s big-dumpers %s\n",
+    g_printf(_("driver: start time %s inparallel %d bandwidth %lu diskspace %lld "), walltime_str(curclock()), inparallel,
+          free_kps(NULL), (long long)free_space());
+    g_printf(_(" dir %s datestamp %s driver: drain-ends tapeq %s big-dumpers %s\n"),
           "OBSOLETE", driver_timestamp, taperalgo2str(conf_taperalgo),
           getconf_str(CNF_DUMPORDER));
     fflush(stdout);
 
-    /* ok, planner is done, now lets see if the tape is ready */
-
-    if(conf_runtapes > 0) {
-       cmd = getresult(taper, 1, &result_argc, result_argv, MAX_ARGS+1);
-
-       if(cmd != TAPER_OK) {
-           /* no tape, go into degraded mode: dump to holding disk */
-           need_degraded=1;
-       }
-    }
-    else {
-       need_degraded=1;
-    }
-
-    tape_left = tape_length;
-    taper_busy = 0;
-    taper_disk = NULL;
-    taper_ev_read = NULL;
-    if(!need_degraded) startaflush();
-
-    if(!nodump)
-       schedule_ev_read = event_register((event_id_t)0, EV_READFD, read_schedule, NULL);
+    schedule_done = nodump;
+    force_flush = 0;
 
     short_dump_state();
     event_loop(0);
 
-    /* handle any remaining dumps by dumping directly to tape, if possible */
+    force_flush = 1;
 
-    while(!empty(runq) && taper > 0) {
+    /* mv runq to directq */
+    while (!empty(runq)) {
        diskp = dequeue_disk(&runq);
+       headqueue_disk(&directq, diskp);
+    }
+
+    /* handle any remaining dumps by dumping directly to tape, if possible */
+    while(!empty(directq) && taper_fd > 0) {
+       time_t  sleep_time  = 100000000;
+       disk_t *sleep_diskp = NULL;
+       time_t  now         = time(0);
+
+       /* Find one we can do immediately or the sonner */
+       for (diskp = directq.head; diskp != NULL; diskp = diskp->next) {
+           if (diskp->to_holdingdisk == HOLD_REQUIRED ||
+               degraded_mode) {
+               sleep_time = 0;
+               sleep_diskp = diskp;
+           } else if (diskp->host->start_t - now < sleep_time &&
+                      diskp->start_t -now < sleep_time) {
+               if (diskp->host->start_t > diskp->start_t)
+                   sleep_time = diskp->host->start_t - now;
+               else
+                   sleep_time = diskp->start_t - now;
+               sleep_diskp = diskp;
+           }
+       }
+       diskp = sleep_diskp;
+       if (sleep_time > 0)
+           sleep(sleep_time);
+       remove_disk(&directq, diskp);
+
        if (diskp->to_holdingdisk == HOLD_REQUIRED) {
+           char *qname = quote_string(diskp->name);
            log_add(L_FAIL, "%s %s %s %d [%s]",
-               diskp->host->hostname, diskp->name, sched(diskp)->datestamp,
+               diskp->host->hostname, qname, sched(diskp)->datestamp,
                sched(diskp)->level,
-               "can't dump required holdingdisk");
+               _("can't dump required holdingdisk"));
+           amfree(qname);
        }
        else if (!degraded_mode) {
-           int rc = dump_to_tape(diskp);
-           if(rc == 1)
-               log_add(L_INFO,
-                       "%s %s %d [dump to tape failed, will try again]",
-                       diskp->host->hostname,
-                       diskp->name,
-                       sched(diskp)->level);
-           else if(rc == 2)
-               log_add(L_FAIL, "%s %s %s %d [dump to tape failed]",
-                       diskp->host->hostname,
-                       diskp->name,
-                       sched(diskp)->datestamp,
-                       sched(diskp)->level);
+           char *qname = quote_string(diskp->name);
+           log_add(L_FAIL, "%s %s %s %d [%s]",
+               diskp->host->hostname, qname, sched(diskp)->datestamp,
+               sched(diskp)->level,
+               _("can't dump in degraded mode"));
+           amfree(qname);
        }
-       else
+       else {
+           char *qname = quote_string(diskp->name);
            log_add(L_FAIL, "%s %s %s %d [%s]",
-               diskp->host->hostname, diskp->name, sched(diskp)->datestamp,
+               diskp->host->hostname, qname, sched(diskp)->datestamp,
                sched(diskp)->level,
-               diskp->to_holdingdisk == HOLD_AUTO ?
-                   "no more holding disk space" :
-                   "can't dump no-hold disk in degraded mode");
+               num_holdalloc == 0 ?
+                   _("can't do degraded dump without holding disk") :
+               diskp->to_holdingdisk != HOLD_NEVER ?
+                   _("out of holding space in degraded mode") :
+                   _("can't dump 'holdingdisk never' dle in degraded mode"));
+           amfree(qname);
+       }
     }
 
+    /* fill up the tape or start new one for taperflush */
+    startaflush();
+    event_loop(0);
+
     short_dump_state();                                /* for amstatus */
 
-    printf("driver: QUITTING time %s telling children to quit\n",
+    g_printf(_("driver: QUITTING time %s telling children to quit\n"),
            walltime_str(curclock()));
     fflush(stdout);
 
     if(!nodump) {
        for(dumper = dmptable; dumper < dmptable + inparallel; dumper++) {
            if(dumper->fd >= 0)
-               dumper_cmd(dumper, QUIT, NULL);
+               dumper_cmd(dumper, QUIT, NULL, NULL);
        }
     }
 
-    if(taper >= 0) {
+    if(taper_fd >= 0) {
        taper_cmd(QUIT, NULL, NULL, 0, NULL);
     }
 
     /* wait for all to die */
     wait_children(600);
 
-    for(hdp = getconf_holdingdisks(); hdp != NULL; hdp = hdp->next) {
-       cleanup_holdingdisk(holdingdisk_get_diskdir(hdp), 0);
-       amfree(hdp->up);
-    }
+    /* cleanup */
+    holding_cleanup(NULL, NULL);
+
     amfree(newdir);
 
     check_unfree_serial();
-    printf("driver: FINISHED time %s\n", walltime_str(curclock()));
+    g_printf(_("driver: FINISHED time %s\n"), walltime_str(curclock()));
     fflush(stdout);
-    log_add(L_FINISH,"date %s time %s", driver_timestamp, walltime_str(curclock()));
+    log_add(L_FINISH,_("date %s time %s"), driver_timestamp, walltime_str(curclock()));
+    log_add(L_INFO, "pid-done %ld", (long)getpid());
     amfree(driver_timestamp);
 
-    free_new_argv(new_argc, new_argv);
     amfree(dumper_program);
     amfree(taper_program);
-    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);
-    }
+    if (result_argv)
+       g_strfreev(result_argv);
 
     dbclose();
 
@@ -537,10 +624,10 @@ wait_children(int count)
            if (pid > 0) {
                what = NULL;
                if (! WIFEXITED(retstat)) {
-                   what = "signal";
+                   what = _("signal");
                    code = WTERMSIG(retstat);
                } else if (WEXITSTATUS(retstat) != 0) {
-                   what = "code";
+                   what = _("code");
                    code = WEXITSTATUS(retstat);
                }
                who = NULL;
@@ -565,9 +652,9 @@ wait_children(int count)
                    who = stralloc("unknown");
                }
                if(who && what) {
-                   log_add(L_WARNING, "%s pid %u exited with %s %d\n", who, 
+                   log_add(L_WARNING, _("%s pid %u exited with %s %d\n"), who, 
                            (unsigned)pid, what, code);
-                   printf("driver: %s pid %u exited with %s %d\n", who,
+                   g_printf(_("driver: %s pid %u exited with %s %d\n"), who,
                           (unsigned)pid, what, code);
                }
                amfree(who);
@@ -589,14 +676,14 @@ kill_children(int signal)
     if(!nodump) {
         for(dumper = dmptable; dumper < dmptable + inparallel; dumper++) {
            if (!dumper->down && dumper->pid > 1) {
-               printf("driver: sending signal %d to %s pid %u\n", signal,
+               g_printf(_("driver: sending signal %d to %s pid %u\n"), signal,
                       dumper->name, (unsigned)dumper->pid);
                if (kill(dumper->pid, signal) == -1 && errno == ESRCH) {
                    if (dumper->chunker)
                        dumper->chunker->pid = 0;
                }
                if (dumper->chunker && dumper->chunker->pid > 1) {
-                   printf("driver: sending signal %d to %s pid %u\n", signal,
+                   g_printf(_("driver: sending signal %d to %s pid %u\n"), signal,
                           dumper->chunker->name,
                           (unsigned)dumper->chunker->pid);
                    if (kill(dumper->chunker->pid, signal) == -1 &&
@@ -607,11 +694,12 @@ kill_children(int signal)
         }
     }
 
-    if(taper_pid > 1)
-       printf("driver: sending signal %d to %s pid %u\n", signal,
+    if(taper_pid > 1) {
+       g_printf(_("driver: sending signal %d to %s pid %u\n"), signal,
               "taper", (unsigned)taper_pid);
        if (kill(taper_pid, signal) == -1 && errno == ESRCH)
            taper_pid = 0;
+    }
 }
 
 static void
@@ -622,15 +710,15 @@ wait_for_children(void)
     if(!nodump) {
        for(dumper = dmptable; dumper < dmptable + inparallel; dumper++) {
            if (dumper->pid > 1 && dumper->fd >= 0) {
-               dumper_cmd(dumper, QUIT, NULL);
+               dumper_cmd(dumper, QUIT, NULL, NULL);
                if (dumper->chunker && dumper->chunker->pid > 1 &&
                    dumper->chunker->fd >= 0)
-                   chunker_cmd(dumper->chunker, QUIT, NULL);
+                   chunker_cmd(dumper->chunker, QUIT, NULL, NULL);
            }
        }
     }
 
-    if(taper_pid > 1 && taper > 0) {
+    if(taper_pid > 1 && taper_fd > 0) {
        taper_cmd(QUIT, NULL, NULL, 0, NULL);
     }
 
@@ -647,29 +735,135 @@ wait_for_children(void)
 
 }
 
+static void startaflush_tape(taper_t *taper);
+
 static void
 startaflush(void)
+{
+    taper_t *taper;
+
+    for(taper = tapetable; taper <= tapetable+conf_taper_parallel_write;
+       taper++) {
+       if (!(taper->state & TAPER_STATE_DONE) &&
+           taper->state & TAPER_STATE_WAIT_FOR_TAPE) {
+           startaflush_tape(taper);
+       }
+    }
+    for(taper = tapetable; taper <= tapetable+conf_taper_parallel_write;
+       taper++) {
+       if (!(taper->state & TAPER_STATE_DONE) &&
+           taper->state & TAPER_STATE_TAPE_REQUESTED) {
+           startaflush_tape(taper);
+       }
+    }
+    for(taper = tapetable; taper <= tapetable+conf_taper_parallel_write;
+       taper++) {
+       if (!(taper->state & TAPER_STATE_DONE) &&
+           taper->state & TAPER_STATE_INIT) {
+           startaflush_tape(taper);
+       }
+    }
+    for(taper = tapetable; taper <= tapetable+conf_taper_parallel_write;
+       taper++) {
+       if (!(taper->state & TAPER_STATE_DONE) &&
+           taper->state & TAPER_STATE_IDLE) {
+           startaflush_tape(taper);
+       }
+    }
+}
+
+static void
+startaflush_tape(
+    taper_t *taper)
 {
     disk_t *dp = NULL;
     disk_t *fit = NULL;
     char *datestamp;
-    int extra_tapes = 0;
+    off_t extra_tapes_size = 0;
+    off_t taper_left;
     char *qname;
+    TapeAction result_tape_action;
+    char *why_no_new_tape = NULL;
+    taper_t *taper1;
+
+    result_tape_action = tape_action(taper, &why_no_new_tape);
+
+    if (result_tape_action & TAPE_ACTION_SCAN) {
+       taper->state &= ~TAPER_STATE_TAPE_REQUESTED;
+       taper->state |= TAPER_STATE_WAIT_FOR_TAPE;
+       taper_nb_scan_volume++;
+       taper_cmd(START_SCAN, taper->disk, NULL, 0, NULL);
+    } else if (result_tape_action & TAPE_ACTION_NEW_TAPE) {
+       taper->state &= ~TAPER_STATE_WAIT_FOR_TAPE;
+       taper->state |= TAPER_STATE_WAIT_NEW_TAPE;
+       nb_sent_new_tape++;
+       taper_cmd(NEW_TAPE, taper->disk, NULL, 0, NULL);
+    } else if (result_tape_action & TAPE_ACTION_NO_NEW_TAPE) {
+       taper->state &= ~TAPER_STATE_WAIT_FOR_TAPE;
+       taper_cmd(NO_NEW_TAPE, taper->disk, why_no_new_tape, 0, NULL);
+       taper->state |= TAPER_STATE_DONE;
+       start_degraded_mode(&runq);
+    } else if (result_tape_action & TAPE_ACTION_MOVE) {
+       taper_t *taper1 = idle_taper();
+       if (taper1) {
+           taper->state &= ~TAPER_STATE_TAPE_REQUESTED;
+           taper->state &= ~TAPER_STATE_WAIT_FOR_TAPE;
+           taper_cmd(TAKE_SCRIBE_FROM, taper->disk, taper1->name, 0 , NULL);
+           taper1->state = TAPER_STATE_DEFAULT;
+           taper->state |= TAPER_STATE_TAPE_STARTED;
+           taper->left = taper1->left;
+           if (last_started_taper == taper1) {
+               last_started_taper = taper;
+           }
+       }
+    }
 
-    if(!degraded_mode && !taper_busy && !empty(tapeq)) {
-       
+    if (!degraded_mode &&
+        taper->state & TAPER_STATE_IDLE &&
+       !empty(tapeq) &&
+       (result_tape_action & TAPE_ACTION_START_A_FLUSH ||
+        result_tape_action & TAPE_ACTION_START_A_FLUSH_FIT)) {
+
+       int taperalgo = conf_taperalgo;
+       if (result_tape_action & TAPE_ACTION_START_A_FLUSH_FIT) {
+           if (taperalgo == ALGO_FIRST)
+               taperalgo = ALGO_FIRSTFIT;
+           else if (taperalgo == ALGO_LARGEST)
+               taperalgo = ALGO_LARGESTFIT;
+           else if (taperalgo == ALGO_SMALLEST)
+               taperalgo = ALGO_SMALLESTFIT;
+           else if (taperalgo == ALGO_LAST)
+               taperalgo = ALGO_LASTFIT;
+       }
+
+       extra_tapes_size = tape_length * (off_t)(conf_runtapes - current_tape);
+       for (taper1 = tapetable; taper1 < tapetable + conf_taper_parallel_write;
+                                 taper1++) {
+           if (taper1->state & TAPER_STATE_TAPE_STARTED) {
+               extra_tapes_size += taper1->left;
+           }
+           dp = taper1->disk;
+           if (dp) {
+               extra_tapes_size -= (sched(dp)->act_size - taper1->written);
+           }
+       }
+
+       if (taper->state & TAPER_STATE_TAPE_STARTED) {
+           taper_left = taper->left;
+       } else {
+           taper_left = tape_length;
+       }
+       dp = NULL;
        datestamp = sched(tapeq.head)->datestamp;
-       switch(conf_taperalgo) {
+       switch(taperalgo) {
        case ALGO_FIRST:
                dp = dequeue_disk(&tapeq);
                break;
        case ALGO_FIRSTFIT:
                fit = tapeq.head;
                while (fit != NULL) {
-                   extra_tapes = (fit->tape_splitsize > (off_t)0) ? 
-                                       conf_runtapes - current_tape : 0;
-                   if(sched(fit)->act_size <= (tape_left +
-                            tape_length * (off_t)extra_tapes) &&
+                   if (sched(fit)->act_size <=
+                            (fit->splitsize ? extra_tapes_size : taper_left) &&
                             strcmp(sched(fit)->datestamp, datestamp) <= 0) {
                        dp = fit;
                        fit = NULL;
@@ -694,10 +888,8 @@ startaflush(void)
        case ALGO_LARGESTFIT:
                fit = tapeq.head;
                while (fit != NULL) {
-                   extra_tapes = (fit->tape_splitsize > (off_t)0) ? 
-                                       conf_runtapes - current_tape : 0;
                    if(sched(fit)->act_size <=
-                      (tape_left + tape_length * (off_t)extra_tapes) &&
+                      (fit->splitsize ? extra_tapes_size : taper_left) &&
                       (!dp || sched(fit)->act_size > sched(dp)->act_size) &&
                       strcmp(sched(fit)->datestamp, datestamp) <= 0) {
                        dp = fit;
@@ -707,58 +899,78 @@ startaflush(void)
                if(dp) remove_disk(&tapeq, dp);
                break;
        case ALGO_SMALLEST:
+               fit = dp = tapeq.head;
+               while (fit != NULL) {
+                   if (sched(fit)->act_size < sched(dp)->act_size &&
+                       strcmp(sched(fit)->datestamp, datestamp) <= 0) {
+                       dp = fit;
+                   }
+                   fit = fit->next;
+               }
+               if(dp) remove_disk(&tapeq, dp);
+               break;
+       case ALGO_SMALLESTFIT:
+               fit = dp = tapeq.head;
+               while (fit != NULL) {
+                   if (sched(fit)->act_size <=
+                       (fit->splitsize ? extra_tapes_size : taper_left) &&
+                       (!dp || sched(fit)->act_size < sched(dp)->act_size) &&
+                       strcmp(sched(fit)->datestamp, datestamp) <= 0) {
+                       dp = fit;
+                   }
+                   fit = fit->next;
+               }
+               if(dp) remove_disk(&tapeq, dp);
                break;
        case ALGO_LAST:
                dp = tapeq.tail;
                remove_disk(&tapeq, dp);
                break;
-       }
-       if(!dp) { /* ALGO_SMALLEST, or default if nothing fit. */
-           if(conf_taperalgo != ALGO_SMALLEST)  {
-               fprintf(stderr,
-                  "driver: startaflush: Using SMALLEST because nothing fit\n");
-           }
-           fit = dp = tapeq.head;
-           while (fit != NULL) {
-               if(sched(fit)->act_size < sched(dp)->act_size &&
-                  strcmp(sched(fit)->datestamp, datestamp) <= 0) {
-                   dp = fit;
+       case ALGO_LASTFIT:
+               fit = tapeq.tail;
+               while (fit != NULL) {
+                   if (sched(fit)->act_size <=
+                       (fit->splitsize ? extra_tapes_size : taper_left) &&
+                       (!dp || sched(fit)->act_size < sched(dp)->act_size) &&
+                       strcmp(sched(fit)->datestamp, datestamp) <= 0) {
+                       dp = fit;
+                   }
+                   fit = fit->prev;
                }
-               fit = fit->next;
-           }
-           if(dp) remove_disk(&tapeq, dp);
-       }
-       if(taper_ev_read == NULL) {
-           taper_ev_read = event_register((event_id_t)taper, EV_READFD,
-                                          handle_taper_result, NULL);
+               if(dp) remove_disk(&tapeq, dp);
+               break;
        }
        if (dp) {
-           taper_disk = dp;
-           taper_busy = 1;
+           taper->disk = dp;
+           taper->dumper = NULL;
+           amfree(taper->input_error);
+           amfree(taper->tape_error);
+           taper->result = LAST_TOK;
+           taper->sendresult = 0;
+           amfree(taper->first_label);
+           taper->written = 0;
+           taper->state &= ~TAPER_STATE_IDLE;
+           taper->state |= TAPER_STATE_FILE_TO_TAPE;
+           taper->dumper = NULL;
            qname = quote_string(dp->name);
+           if (taper_nb_wait_reply == 0) {
+               taper_ev_read = event_register(taper_fd, EV_READFD,
+                                              handle_taper_result, NULL);
+           }
+           taper_nb_wait_reply++;
+           sched(dp)->taper = taper;
            taper_cmd(FILE_WRITE, dp, sched(dp)->destname, sched(dp)->level,
                      sched(dp)->datestamp);
-           fprintf(stderr,"driver: startaflush: %s %s %s "
-                   OFF_T_FMT " " OFF_T_FMT "\n",
-                   taperalgo2str(conf_taperalgo), dp->host->hostname, qname,
-                   (OFF_T_FMT_TYPE)sched(taper_disk)->act_size,
-                   (OFF_T_FMT_TYPE)tape_left);
-           if(sched(dp)->act_size <= tape_left)
-               tape_left -= sched(dp)->act_size;
-           else
-               tape_left = (off_t)0;
+           g_fprintf(stderr,_("driver: startaflush: %s %s %s %lld %lld\n"),
+                   taperalgo2str(taperalgo), dp->host->hostname, qname,
+                   (long long)sched(taper->disk)->act_size,
+                   (long long)taper->left);
            amfree(qname);
-       } else {
-           error("FATAL: Taper marked busy and no work found.");
-           /*NOTREACHED*/
        }
-    } else if(!taper_busy && taper_ev_read != NULL) {
-       event_release(taper_ev_read);
-       taper_ev_read = NULL;
+       short_dump_state();
     }
 }
 
-
 static int
 client_constrained(
     disk_t *   dp)
@@ -785,21 +997,118 @@ client_constrained(
     return 0;
 }
 
+static void
+allow_dump_dle(
+    disk_t         *diskp,
+    taper_t        *taper,
+    char            dumptype,
+    disklist_t     *rq,
+    const time_t    now,
+    int             dumper_to_holding,
+    int            *cur_idle,
+    disk_t        **delayed_diskp,
+    disk_t        **diskp_accept,
+    assignedhd_t ***holdp_accept,
+    off_t           extra_tapes_size)
+{
+    assignedhd_t **holdp=NULL;
+
+    if (diskp->host->start_t > now) {
+       *cur_idle = max(*cur_idle, IDLE_START_WAIT);
+       if (*delayed_diskp == NULL || sleep_time > diskp->host->start_t) {
+           *delayed_diskp = diskp;
+           sleep_time = diskp->host->start_t;
+       }
+    } else if(diskp->start_t > now) {
+       *cur_idle = max(*cur_idle, IDLE_START_WAIT);
+       if (*delayed_diskp == NULL || sleep_time > diskp->start_t) {
+           *delayed_diskp = diskp;
+           sleep_time = diskp->start_t;
+       }
+    } else if (diskp->host->netif->curusage > 0 &&
+              sched(diskp)->est_kps > free_kps(diskp->host->netif)) {
+       *cur_idle = max(*cur_idle, IDLE_NO_BANDWIDTH);
+    } else if (!taper && sched(diskp)->no_space) {
+       *cur_idle = max(*cur_idle, IDLE_NO_DISKSPACE);
+    } else if (!taper && diskp->to_holdingdisk == HOLD_NEVER) {
+       *cur_idle = max(*cur_idle, IDLE_NO_HOLD);
+    } else if (extra_tapes_size && sched(diskp)->est_size > extra_tapes_size) {
+       *cur_idle = max(*cur_idle, IDLE_NO_DISKSPACE);
+       /* no tape space */
+    } else if (!taper && (holdp =
+       find_diskspace(sched(diskp)->est_size, cur_idle, NULL)) == NULL) {
+       *cur_idle = max(*cur_idle, IDLE_NO_DISKSPACE);
+       if (empty(tapeq) && dumper_to_holding == 0 && rq != &directq) {
+           remove_disk(rq, diskp);
+           if (diskp->to_holdingdisk != HOLD_REQUIRED) {
+               enqueue_disk(&directq, diskp);
+               diskp->to_holdingdisk = HOLD_NEVER;
+           }
+       }
+    } else if (client_constrained(diskp)) {
+       free_assignedhd(holdp);
+       *cur_idle = max(*cur_idle, IDLE_CLIENT_CONSTRAINED);
+    } else {
+
+       /* disk fits, dump it */
+       int accept = !*diskp_accept;
+       if(!accept) {
+           switch(dumptype) {
+             case 's': accept = (sched(diskp)->est_size < sched(*diskp_accept)->est_size);
+                       break;
+             case 'S': accept = (sched(diskp)->est_size > sched(*diskp_accept)->est_size);
+                       break;
+             case 't': accept = (sched(diskp)->est_time < sched(*diskp_accept)->est_time);
+                       break;
+             case 'T': accept = (sched(diskp)->est_time > sched(*diskp_accept)->est_time);
+                       break;
+             case 'b': accept = (sched(diskp)->est_kps < sched(*diskp_accept)->est_kps);
+                       break;
+             case 'B': accept = (sched(diskp)->est_kps > sched(*diskp_accept)->est_kps);
+                       break;
+             default:  log_add(L_WARNING, _("Unknown dumporder character \'%c\', using 's'.\n"),
+                               dumptype);
+                       accept = (sched(diskp)->est_size < sched(*diskp_accept)->est_size);
+                       break;
+           }
+       }
+       if(accept) {
+           if( !*diskp_accept || !degraded_mode || diskp->priority >= (*diskp_accept)->priority) {
+               if(*holdp_accept) free_assignedhd(*holdp_accept);
+               *diskp_accept = diskp;
+               *holdp_accept = holdp;
+           }
+           else {
+               free_assignedhd(holdp);
+           }
+       }
+       else {
+           free_assignedhd(holdp);
+       }
+    }
+}
+
 static void
 start_some_dumps(
-    disklist_t *       rq)
+    disklist_t *rq)
 {
+    const time_t now = time(NULL);
     int cur_idle;
     disk_t *diskp, *delayed_diskp, *diskp_accept;
+    disk_t *dp;
     assignedhd_t **holdp=NULL, **holdp_accept;
-    const time_t now = time(NULL);
     cmd_t cmd;
     int result_argc;
-    char *result_argv[MAX_ARGS+1];
+    char **result_argv;
     chunker_t *chunker;
     dumper_t *dumper;
+    taper_t  *taper;
     char dumptype;
     char *dumporder;
+    int  dumper_to_holding = 0;
+
+    /* don't start any actual dumps until the taper is started */
+    if (!taper_started) return;
 
     idle_reason = IDLE_NO_DUMPERS;
     sleep_time = 0;
@@ -809,6 +1118,11 @@ start_some_dumps(
        dumpers_ev_time = NULL;
     }
 
+    for(dumper = dmptable; dumper < (dmptable+inparallel); dumper++) {
+       if (dumper->busy && dumper->dp->to_holdingdisk != HOLD_NEVER) {
+           dumper_to_holding++;
+       }
+    }
     for (dumper = dmptable; dumper < dmptable+inparallel; dumper++) {
 
        if( dumper->busy || dumper->down) {
@@ -856,78 +1170,72 @@ start_some_dumps(
                dumptype = 'T';
        }
 
-       for(diskp = rq->head; diskp != NULL; diskp = diskp->next) {
-           assert(diskp->host != NULL && sched(diskp) != NULL);
-
-           if (diskp->host->start_t > now) {
-               cur_idle = max(cur_idle, IDLE_START_WAIT);
-               if (delayed_diskp == NULL || sleep_time > diskp->host->start_t) {
-                   delayed_diskp = diskp;
-                   sleep_time = diskp->host->start_t;
-               }
-           } else if(diskp->start_t > now) {
-               cur_idle = max(cur_idle, IDLE_START_WAIT);
-               if (delayed_diskp == NULL || sleep_time > diskp->start_t) {
-                   delayed_diskp = diskp;
-                   sleep_time = diskp->start_t;
-               }
-           } else if (diskp->host->netif->curusage > 0 &&
-                      sched(diskp)->est_kps > free_kps(diskp->host->netif)) {
-               cur_idle = max(cur_idle, IDLE_NO_BANDWIDTH);
-           } else if(sched(diskp)->no_space) {
-               cur_idle = max(cur_idle, IDLE_NO_DISKSPACE);
-           } else if (diskp->to_holdingdisk == HOLD_NEVER) {
-               cur_idle = max(cur_idle, IDLE_NO_HOLD);
-           } else if ((holdp =
-               find_diskspace(sched(diskp)->est_size, &cur_idle, NULL)) == NULL) {
-               cur_idle = max(cur_idle, IDLE_NO_DISKSPACE);
-           } else if (client_constrained(diskp)) {
-               free_assignedhd(holdp);
-               cur_idle = max(cur_idle, IDLE_CLIENT_CONSTRAINED);
-           } else {
-
-               /* disk fits, dump it */
-               int accept = !diskp_accept;
-               if(!accept) {
-                   switch(dumptype) {
-                     case 's': accept = (sched(diskp)->est_size < sched(diskp_accept)->est_size);
-                               break;
-                     case 'S': accept = (sched(diskp)->est_size > sched(diskp_accept)->est_size);
-                               break;
-                     case 't': accept = (sched(diskp)->est_time < sched(diskp_accept)->est_time);
-                               break;
-                     case 'T': accept = (sched(diskp)->est_time > sched(diskp_accept)->est_time);
-                               break;
-                     case 'b': accept = (sched(diskp)->est_kps < sched(diskp_accept)->est_kps);
-                               break;
-                     case 'B': accept = (sched(diskp)->est_kps > sched(diskp_accept)->est_kps);
-                               break;
-                     default:  log_add(L_WARNING, "Unknown dumporder character \'%c\', using 's'.\n",
-                                       dumptype);
-                               accept = (sched(diskp)->est_size < sched(diskp_accept)->est_size);
-                               break;
+       diskp = NULL;
+       taper = NULL;
+       if (!empty(directq)) {
+           taper = idle_taper();
+           if (taper) {
+               TapeAction result_tape_action;
+               char *why_no_new_tape = NULL;
+               result_tape_action = tape_action(taper, &why_no_new_tape);
+               if (result_tape_action & TAPE_ACTION_START_A_FLUSH ||
+                   result_tape_action & TAPE_ACTION_START_A_FLUSH_FIT) {
+                   off_t extra_tapes_size = 0;
+                   taper_t *taper1;
+
+                   if (result_tape_action & TAPE_ACTION_START_A_FLUSH_FIT) {
+                       extra_tapes_size = tape_length *
+                                         (off_t)(conf_runtapes - current_tape);
+                       for (taper1 = tapetable;
+                            taper1 < tapetable + conf_taper_parallel_write;
+                            taper1++) {
+                           if (taper1->state & TAPER_STATE_TAPE_STARTED) {
+                               extra_tapes_size += taper1->left;
+                           }
+                           dp = taper1->disk;
+                           if (dp) {
+                               extra_tapes_size -= (sched(dp)->est_size -
+                                                   taper1->written);
+                           }
+                       }
                    }
-               }
-               if(accept) {
-                   if( !diskp_accept || !degraded_mode || diskp->priority >= diskp_accept->priority) {
-                       if(holdp_accept) free_assignedhd(holdp_accept);
-                       diskp_accept = diskp;
-                       holdp_accept = holdp;
+
+                   for (diskp = directq.head; diskp != NULL;
+                                              diskp = diskp->next) {
+                       allow_dump_dle(diskp, taper, dumptype, &directq, now,
+                                      dumper_to_holding, &cur_idle,
+                                      &delayed_diskp, &diskp_accept,
+                                      &holdp_accept, extra_tapes_size);
                    }
-                   else {
-                       free_assignedhd(holdp);
+                   if (diskp_accept) {
+                       diskp = diskp_accept;
+                       holdp = holdp_accept;
+                   } else {
+                       taper = NULL;
                    }
-               }
-               else {
-                   free_assignedhd(holdp);
+               } else {
+                   taper = NULL;
                }
            }
        }
 
-       diskp = diskp_accept;
-       holdp = holdp_accept;
+       if (diskp == NULL) {
+           for(diskp = rq->head; diskp != NULL; diskp = diskp->next) {
+               assert(diskp->host != NULL && sched(diskp) != NULL);
+
+               allow_dump_dle(diskp, NULL, dumptype, rq, now,
+                              dumper_to_holding, &cur_idle, &delayed_diskp,
+                              &diskp_accept, &holdp_accept, 0);
+           }
+           diskp = diskp_accept;
+           holdp = holdp_accept;
+       }
 
        idle_reason = max(idle_reason, cur_idle);
+       if (diskp == NULL && idle_reason == IDLE_NO_DISKSPACE) {
+           /* continue flush waiting for new tape */
+           startaflush();
+       }
 
        /*
         * If we have no disk at this point, and there are disks that
@@ -940,7 +1248,7 @@ start_some_dumps(
            dumpers_ev_time = event_register((event_id_t)sleep_time, EV_TIME,
                handle_dumpers_time, &runq);
            return;
-       } else if (diskp != NULL) {
+       } else if (diskp != NULL && taper == NULL) {
            sched(diskp)->act_size = (off_t)0;
            allocate_bandwidth(diskp->host->netif, sched(diskp)->est_kps);
            sched(diskp)->activehd = assign_holdingdisk(holdp, diskp);
@@ -951,6 +1259,7 @@ start_some_dumps(
            diskp->inprogress = 1;
            sched(diskp)->dumper = dumper;
            sched(diskp)->timestamp = now;
+           amfree(diskp->dataport_list);
 
            dumper->busy = 1;           /* dumper is now busy */
            dumper->dp = diskp;         /* link disk to dumper */
@@ -960,27 +1269,29 @@ start_some_dumps(
            sched(diskp)->dumpsize = (off_t)-1;
            sched(diskp)->dumptime = (time_t)0;
            sched(diskp)->tapetime = (time_t)0;
-           chunker = dumper->chunker;
+           chunker = dumper->chunker = &chktable[dumper - dmptable];
            chunker->result = LAST_TOK;
            dumper->result = LAST_TOK;
            startup_chunk_process(chunker,chunker_program);
-           chunker_cmd(chunker, START, (void *)driver_timestamp);
+           chunker_cmd(chunker, START, NULL, driver_timestamp);
            chunker->dumper = dumper;
-           chunker_cmd(chunker, PORT_WRITE, diskp);
-           cmd = getresult(chunker->fd, 1, &result_argc, result_argv, MAX_ARGS+1);
+           chunker_cmd(chunker, PORT_WRITE, diskp, NULL);
+           cmd = getresult(chunker->fd, 1, &result_argc, &result_argv);
            if(cmd != PORT) {
                assignedhd_t **h=NULL;
                int activehd;
+               char *qname = quote_string(diskp->name);
 
-               printf("driver: did not get PORT from %s for %s:%s\n",
-                      chunker->name, diskp->host->hostname, diskp->name);
+               g_printf(_("driver: did not get PORT from %s for %s:%s\n"),
+                      chunker->name, diskp->host->hostname, qname);
+               amfree(qname);
                fflush(stdout);
 
                deallocate_bandwidth(diskp->host->netif, sched(diskp)->est_kps);
                h = sched(diskp)->holdp;
                activehd = sched(diskp)->activehd;
                h[activehd]->used = 0;
-               holdalloc(h[activehd]->disk)->allocated_dumpers--;
+               h[activehd]->disk->allocated_dumpers--;
                adjust_diskspace(diskp, DONE);
                delete_diskspace(diskp);
                diskp->host->inprogress--;
@@ -988,9 +1299,9 @@ start_some_dumps(
                sched(diskp)->dumper = NULL;
                dumper->busy = 0;
                dumper->dp = NULL;
-               sched(diskp)->attempted++;
+               sched(diskp)->dump_attempted++;
                free_serial_dp(diskp);
-               if(sched(diskp)->attempted < 2)
+               if(sched(diskp)->dump_attempted < 2)
                    enqueue_disk(rq, diskp);
            }
            else {
@@ -998,11 +1309,67 @@ start_some_dumps(
                                                 handle_dumper_result, dumper);
                chunker->ev_read = event_register((event_id_t)chunker->fd, EV_READFD,
                                                   handle_chunker_result, chunker);
-               dumper->output_port = atoi(result_argv[2]);
+               dumper->output_port = atoi(result_argv[1]);
+               amfree(diskp->dataport_list);
+               diskp->dataport_list = stralloc(result_argv[2]);
+
+               if (diskp->host->pre_script == 0) {
+                   for (dp=diskp->host->disks; dp != NULL; dp = dp->hostnext) {
+                       run_server_scripts(EXECUTE_ON_PRE_HOST_BACKUP,
+                                          get_config_name(), dp, -1);
+                   }
+                   diskp->host->pre_script = 1;
+               }
+               run_server_scripts(EXECUTE_ON_PRE_DLE_BACKUP,
+                                  get_config_name(), diskp,
+                                  sched(diskp)->level);
+               dumper_cmd(dumper, PORT_DUMP, diskp, NULL);
+           }
+           diskp->host->start_t = now + 15;
+
+           if (result_argv)
+               g_strfreev(result_argv);
+           short_dump_state();
+       } else if (diskp != NULL && taper != NULL) { /* dump to tape */
+           sched(diskp)->act_size = (off_t)0;
+           allocate_bandwidth(diskp->host->netif, sched(diskp)->est_kps);
+           diskp->host->inprogress++;  /* host is now busy */
+           diskp->inprogress = 1;
+           sched(diskp)->dumper = dumper;
+           sched(diskp)->taper = taper;
+           sched(diskp)->timestamp = now;
+           dumper->chunker = NULL;
+           amfree(diskp->dataport_list);
+
+           dumper->busy = 1;           /* dumper is now busy */
+           dumper->dp = diskp;         /* link disk to dumper */
+           remove_disk(&directq, diskp);  /* take it off the direct queue */
 
-               dumper_cmd(dumper, PORT_DUMP, diskp);
+           sched(diskp)->origsize = (off_t)-1;
+           sched(diskp)->dumpsize = (off_t)-1;
+           sched(diskp)->dumptime = (time_t)0;
+           sched(diskp)->tapetime = (time_t)0;
+           dumper->result = LAST_TOK;
+           taper->result = LAST_TOK;
+           taper->input_error = NULL;
+           taper->tape_error = NULL;
+           taper->disk = diskp;
+           taper->first_label = NULL;
+           taper->written = 0;
+           taper->dumper = dumper;
+           taper->state |= TAPER_STATE_DUMP_TO_TAPE;
+           taper->state &= ~TAPER_STATE_IDLE;
+           if (taper_nb_wait_reply == 0) {
+               taper_ev_read = event_register(taper_fd, EV_READFD,
+                                              handle_taper_result, NULL);
            }
+
+           taper_nb_wait_reply++;
+           taper_cmd(PORT_WRITE, diskp, NULL, sched(diskp)->level,
+                     sched(diskp)->datestamp);
            diskp->host->start_t = now + 15;
+
+           short_dump_state();
        }
     }
 }
@@ -1031,17 +1398,17 @@ dump_schedule(
     disk_t *dp;
     char *qname;
 
-    printf("dump of driver schedule %s:\n--------\n", str);
+    g_printf(_("dump of driver schedule %s:\n--------\n"), str);
 
     for(dp = qp->head; dp != NULL; dp = dp->next) {
         qname = quote_string(dp->name);
-       printf("  %-20s %-25s lv %d t %5lu s " OFF_T_FMT " p %d\n",
+       g_printf("  %-20s %-25s lv %d t %5lu s %lld p %d\n",
               dp->host->hostname, qname, sched(dp)->level,
               sched(dp)->est_time,
-              (OFF_T_FMT_TYPE)sched(dp)->est_size, sched(dp)->priority);
+              (long long)sched(dp)->est_size, sched(dp)->priority);
         amfree(qname);
     }
-    printf("--------\n");
+    g_printf("--------\n");
 }
 
 static void
@@ -1053,14 +1420,9 @@ start_degraded_mode(
     off_t est_full_size;
     char *qname;
 
-    if (taper_ev_read != NULL) {
-       event_release(taper_ev_read);
-       taper_ev_read = NULL;
-    }
-
     newq.head = newq.tail = 0;
 
-    dump_schedule(queuep, "before start degraded mode");
+    dump_schedule(queuep, _("before start degraded mode"));
 
     est_full_size = (off_t)0;
     while(!empty(*queuep)) {
@@ -1086,9 +1448,9 @@ start_degraded_mode(
                enqueue_disk(&newq, dp);
            }
            else {
-               log_add(L_FAIL,"%s %s %s %d [can't switch to incremental dump]",
+               log_add(L_FAIL, "%s %s %s %d [%s]",
                        dp->host->hostname, qname, sched(dp)->datestamp,
-                       sched(dp)->level);
+                       sched(dp)->level, sched(dp)->degr_mesg);
            }
        }
         amfree(qname);
@@ -1097,7 +1459,7 @@ start_degraded_mode(
     /*@i@*/ *queuep = newq;
     degraded_mode = 1;
 
-    dump_schedule(queuep, "after start degraded mode");
+    dump_schedule(queuep, _("after start degraded mode"));
 }
 
 
@@ -1126,7 +1488,7 @@ continue_port_dumps(void)
            }
            assert( dumper < dmptable + inparallel );
            sched(dp)->activehd = assign_holdingdisk( h, dp );
-           chunker_cmd( dumper->chunker, CONTINUE, dp );
+           chunker_cmd( dumper->chunker, CONTINUE, dp, NULL );
            amfree(h);
            remove_disk( &roomq, dp );
        }
@@ -1137,15 +1499,11 @@ continue_port_dumps(void)
      * a) diskspace has been allocated for other dumps which are
      *    still running or already being written to tape
      * b) all other dumps have been suspended due to lack of diskspace
-     * c) this dump doesn't fit on all the holding disks
      * Case a) is not a problem. We just wait for the diskspace to
      * be freed by moving the current disk to a queue.
      * If case b) occurs, we have a deadlock situation. We select
      * a dump from the queue to be aborted and abort it. It will
-     * be retried later dumping to disk.
-     * If case c) is detected, the dump is aborted. Next time
-     * it will be dumped directly to tape. Actually, case c is a special
-     * manifestation of case b) where only one dumper is busy.
+     * be retried directly to tape.
      */
     for(dp=NULL, dumper = dmptable; dumper < (dmptable+inparallel); dumper++) {
        if( dumper->busy ) {
@@ -1159,18 +1517,15 @@ continue_port_dumps(void)
        }
     }
     if((dp != NULL) && (active_dumpers == 0) && (busy_dumpers > 0) && 
-        ((!taper_busy && empty(tapeq)) || degraded_mode) &&
-       pending_aborts == 0 ) { /* not case a */
-       if( busy_dumpers == 1 ) { /* case c */
-           sched(dp)->no_space = 1;
-       }
-       /* case b */
+        ((all_taper_idle() && empty(tapeq)) || degraded_mode) &&
+       pending_aborts == 0 ) { /* case b */
+       sched(dp)->no_space = 1;
        /* At this time, dp points to the dump with the smallest est_size.
         * We abort that dump, hopefully not wasting too much time retrying it.
         */
        remove_disk( &roomq, dp );
-       chunker_cmd( sched(dp)->dumper->chunker, ABORT, NULL);
-       dumper_cmd( sched(dp)->dumper, ABORT, NULL );
+       chunker_cmd(sched(dp)->dumper->chunker, ABORT, NULL, _("Not enough holding disk space"));
+       dumper_cmd( sched(dp)->dumper, ABORT, NULL, _("Not enough holding disk space"));
        pending_aborts++;
     }
 }
@@ -1178,149 +1533,349 @@ continue_port_dumps(void)
 
 static void
 handle_taper_result(
-    void *     cookie)
+       void *cookie G_GNUC_UNUSED)
 {
-    disk_t *dp;
-    off_t filenum;
+    disk_t *dp = NULL, *dp1;
+    dumper_t *dumper;
     cmd_t cmd;
     int result_argc;
-    char *result_argv[MAX_ARGS+1];
-    int avail_tapes = 0;
-    
-    (void)cookie;      /* Quiet unused parameter warning */
+    char **result_argv;
+    char *qname, *q;
+    char *s;
+    taper_t *taper = NULL;
+    taper_t *taper1;
+    int      i;
+    off_t    partsize;
 
     assert(cookie == NULL);
-    
+
     do {
-        
+
        short_dump_state();
-        
-       cmd = getresult(taper, 1, &result_argc, result_argv, MAX_ARGS+1);
-        
+       taper = NULL;
+
+       cmd = getresult(taper_fd, 1, &result_argc, &result_argv);
+
        switch(cmd) {
-            
-       case PARTIAL:
-       case DONE:      /* DONE <handle> <label> <tape file> <err mess> */
-           if(result_argc != 5) {
-               error("error: [taper DONE result_argc != 5: %d", result_argc);
+
+       case TAPER_OK:
+           if(result_argc != 2) {
+               error(_("error: [taper FAILED result_argc != 2: %d"), result_argc);
                /*NOTREACHED*/
            }
-            
-           dp = serial2disk(result_argv[2]);
-           free_serial(result_argv[2]);
-            
-           filenum = OFF_T_ATOI(result_argv[4]);
-           if(cmd == DONE) {
-               update_info_taper(dp, result_argv[3], filenum,
-                                  sched(dp)->level);
-           }
-            
-           delete_diskspace(dp);
-            
-           printf("driver: finished-cmd time %s taper wrote %s:%s\n",
-                  walltime_str(curclock()), dp->host->hostname, dp->name);
-           fflush(stdout);
-            
-           amfree(sched(dp)->destname);
-           amfree(sched(dp)->dumpdate);
-           amfree(sched(dp)->degr_dumpdate);
-           amfree(sched(dp)->datestamp);
-           amfree(dp->up);
-            
-           taper_busy = 0;
-           taper_disk = NULL;
+
+           taper = NULL;
+           taper_started = 1;
+           for (i=0; i < conf_taper_parallel_write; i++) {
+               if (strcmp(tapetable[i].name, result_argv[1]) == 0) {
+                   taper= &tapetable[i];
+               }
+           }
+           assert(taper != NULL);
+           taper->left = 0;
+           taper->state &= ~TAPER_STATE_INIT;
+           taper->state |= TAPER_STATE_RESERVATION;
+           taper->state |= TAPER_STATE_IDLE;
+           amfree(taper->first_label);
+           taper_nb_wait_reply--;
+           taper_nb_scan_volume--;
+           last_started_taper = taper;
+           if (taper_nb_wait_reply == 0) {
+               event_release(taper_ev_read);
+               taper_ev_read = NULL;
+           }
+           start_some_dumps(&runq);
            startaflush();
-            
-           /* continue with those dumps waiting for diskspace */
-           continue_port_dumps();
            break;
-            
-       case TRYAGAIN:  /* TRY-AGAIN <handle> <err mess> */
-           if (result_argc < 2) {
-               error("error [taper TRYAGAIN result_argc < 2: %d]",
-                     result_argc);
-               /*NOTREACHED*/
+
+       case FAILED:    /* FAILED <handle> INPUT-* TAPE-* <input err mesg> <tape err mesg> */
+           if(result_argc != 6) {
+               error(_("error: [taper FAILED result_argc != 6: %d"), result_argc);
+               /*NOTREACHED*/
            }
-           dp = serial2disk(result_argv[2]);
-           free_serial(result_argv[2]);
-           printf("driver: taper-tryagain time %s disk %s:%s\n",
-                  walltime_str(curclock()), dp->host->hostname, dp->name);
+
+           dp = serial2disk(result_argv[1]);
+           taper = sched(dp)->taper;
+           assert(dp == taper->disk);
+           if (!taper->dumper)
+               free_serial(result_argv[1]);
+
+           qname = quote_string(dp->name);
+           g_printf(_("driver: finished-cmd time %s taper wrote %s:%s\n"),
+                  walltime_str(curclock()), dp->host->hostname, qname);
            fflush(stdout);
-            
-           /* See how many tapes we have left, but we alwyays
-              retry once (why?) */
-           current_tape++;
-           if(dp->tape_splitsize > (off_t)0)
-               avail_tapes = conf_runtapes - current_tape;
-           else
-               avail_tapes = 0;
-            
-           if(sched(dp)->attempted > avail_tapes) {
-               log_add(L_FAIL, "%s %s %s %d [too many taper retries]",
-                        dp->host->hostname, dp->name, sched(dp)->datestamp,
-                        sched(dp)->level);
-               printf("driver: taper failed %s %s %s, too many taper retry\n",
-                       result_argv[2], dp->host->hostname, dp->name);
+
+           if (strcmp(result_argv[2], "INPUT-ERROR") == 0) {
+               taper->input_error = newstralloc(taper->input_error, result_argv[4]);
+           } else if (strcmp(result_argv[2], "INPUT-GOOD") != 0) {
+               taper->tape_error = newstralloc(taper->tape_error,
+                                              _("Taper protocol error"));
+               taper->result = FAILED;
+               log_add(L_FAIL, _("%s %s %s %d [%s]"),
+                       dp->host->hostname, qname, sched(dp)->datestamp,
+                       sched(dp)->level, taper->tape_error);
+               amfree(qname);
+               break;
            }
-           else {
-               /* Re-insert into taper queue. */
-               sched(dp)->attempted++;
-               headqueue_disk(&tapeq, dp);
-           }
-            
-           tape_left = tape_length;
-            
-           /* run next thing from queue */
-            
-           taper_busy = 0;
-           taper_disk = NULL;
-           startaflush();
-           continue_port_dumps();
+           if (strcmp(result_argv[3], "TAPE-ERROR") == 0) {
+               taper->state &= ~TAPER_STATE_TAPE_STARTED;
+               taper->tape_error = newstralloc(taper->tape_error, result_argv[5]);
+           } else if (strcmp(result_argv[3], "TAPE-GOOD") != 0) {
+               taper->state &= ~TAPER_STATE_TAPE_STARTED;
+               taper->tape_error = newstralloc(taper->tape_error,
+                                              _("Taper protocol error"));
+               taper->result = FAILED;
+               log_add(L_FAIL, _("%s %s %s %d [%s]"),
+                       dp->host->hostname, qname, sched(dp)->datestamp,
+                       sched(dp)->level, taper->tape_error);
+               amfree(qname);
+               break;
+           }
+
+           amfree(qname);
+           taper->result = cmd;
+
            break;
-            
-        case SPLIT_CONTINUE:  /* SPLIT_CONTINUE <handle> <new_label> */
-            if (result_argc != 3) {
-                error("error [taper SPLIT_CONTINUE result_argc != 3: %d]",
+
+       case PARTIAL:   /* PARTIAL <handle> INPUT-* TAPE-* <stat mess> <input err mesg> <tape err mesg>*/
+       case DONE:      /* DONE <handle> INPUT-GOOD TAPE-GOOD <stat mess> <input err mesg> <tape err mesg> */
+           if(result_argc != 7) {
+               error(_("error: [taper PARTIAL result_argc != 7: %d"), result_argc);
+               /*NOTREACHED*/
+           }
+
+           dp = serial2disk(result_argv[1]);
+           taper = sched(dp)->taper;
+           assert(dp == taper->disk);
+            if (!taper->dumper)
+                free_serial(result_argv[1]);
+
+           qname = quote_string(dp->name);
+           g_printf(_("driver: finished-cmd time %s taper wrote %s:%s\n"),
+                  walltime_str(curclock()), dp->host->hostname, qname);
+           fflush(stdout);
+
+           if (strcmp(result_argv[2], "INPUT-ERROR") == 0) {
+               taper->input_error = newstralloc(taper->input_error, result_argv[5]);
+           } else if (strcmp(result_argv[2], "INPUT-GOOD") != 0) {
+               taper->tape_error = newstralloc(taper->tape_error,
+                                              _("Taper protocol error"));
+               taper->result = FAILED;
+               log_add(L_FAIL, _("%s %s %s %d [%s]"),
+                       dp->host->hostname, qname, sched(dp)->datestamp,
+                       sched(dp)->level, taper->tape_error);
+               amfree(qname);
+               break;
+           }
+           if (strcmp(result_argv[3], "TAPE-ERROR") == 0) {
+               taper->state &= ~TAPER_STATE_TAPE_STARTED;
+               taper->tape_error = newstralloc(taper->tape_error, result_argv[6]);
+           } else if (strcmp(result_argv[3], "TAPE-GOOD") != 0) {
+               taper->state &= ~TAPER_STATE_TAPE_STARTED;
+               taper->tape_error = newstralloc(taper->tape_error,
+                                              _("Taper protocol error"));
+               taper->result = FAILED;
+               log_add(L_FAIL, _("%s %s %s %d [%s]"),
+                       dp->host->hostname, qname, sched(dp)->datestamp,
+                       sched(dp)->level, taper->tape_error);
+               amfree(qname);
+               break;
+           }
+
+           s = strstr(result_argv[4], " kb ");
+           if (s) {
+               s += 4;
+               sched(dp)->dumpsize = atol(s);
+           }
+
+           taper->result = cmd;
+           amfree(qname);
+
+           break;
+
+        case PARTDONE:  /* PARTDONE <handle> <label> <fileno> <kbytes> <stat> */
+           dp = serial2disk(result_argv[1]);
+           taper = sched(dp)->taper;
+           assert(dp == taper->disk);
+            if (result_argc != 6) {
+                error(_("error [taper PARTDONE result_argc != 6: %d]"),
                       result_argc);
                /*NOTREACHED*/
             }
-            
+           if (!taper->first_label) {
+               amfree(taper->first_label);
+               taper->first_label = stralloc(result_argv[2]);
+               taper->first_fileno = OFF_T_ATOI(result_argv[3]);
+           }
+           taper->written += OFF_T_ATOI(result_argv[4]);
+           if (taper->written > sched(taper->disk)->act_size)
+               sched(taper->disk)->act_size = taper->written;
+
+           partsize = 0;
+           s = strstr(result_argv[5], " kb ");
+           if (s) {
+               s += 4;
+               partsize = atol(s);
+           }
+           taper->left -= partsize;
+
             break;
-        case SPLIT_NEEDNEXT:  /* SPLIT-NEEDNEXT <handle> <kb written> */
+
+        case REQUEST_NEW_TAPE:  /* REQUEST-NEW-TAPE <handle> */
+            if (result_argc != 2) {
+                error(_("error [taper REQUEST_NEW_TAPE result_argc != 2: %d]"),
+                      result_argc);
+               /*NOTREACHED*/
+            }
+
+           dp = serial2disk(result_argv[1]);
+           taper = sched(dp)->taper;
+           taper->state &= ~TAPER_STATE_TAPE_STARTED;
+           taper->state |= TAPER_STATE_TAPE_REQUESTED;
+
+           start_some_dumps(&runq);
+           startaflush();
+           break;
+
+       case NEW_TAPE: /* NEW-TAPE <handle> <label> */
             if (result_argc != 3) {
-                error("error [taper SPLIT_NEEDNEXT result_argc != 3: %d]",
+                error(_("error [taper NEW_TAPE result_argc != 3: %d]"),
                       result_argc);
                /*NOTREACHED*/
             }
-            
-            /* Update our tape counter and reset tape_left */
-            current_tape++;
-            tape_left = tape_length;
-            
-            /* Reduce the size of the dump by amount written and reduce
-               tape_left by the amount left over */
-            dp = serial2disk(result_argv[2]);
-            sched(dp)->act_size -= OFF_T_ATOI(result_argv[3]);
-            if (sched(dp)->act_size < tape_left)
-                tape_left -= sched(dp)->act_size;
-            else
-                tape_length = 0;
-            
-            break;
-            
-        case TAPE_ERROR: /* TAPE-ERROR <handle> <err mess> */
+
+           nb_sent_new_tape--;
+           taper_nb_scan_volume--;
+           dp = serial2disk(result_argv[1]);
+           taper = sched(dp)->taper;
+            /* Update our tape counter and reset taper->left */
+           current_tape++;
+           taper->left = tape_length;
+           taper->state &= ~TAPER_STATE_WAIT_NEW_TAPE;
+           taper->state |= TAPER_STATE_TAPE_STARTED;
+           last_started_taper = NULL;
+
+           /* start a new worker */
+           for (i = 0; i < conf_taper_parallel_write ; i++) {
+               taper1 = &tapetable[i];
+               if (need_degraded == 0 &&
+                   taper1->state == TAPER_STATE_DEFAULT) {
+                   taper1->state = TAPER_STATE_INIT;
+                   if (taper_nb_wait_reply == 0) {
+                       taper_ev_read = event_register(taper_fd, EV_READFD,
+                                               handle_taper_result, NULL);
+                   }
+                   taper_nb_wait_reply++;
+                   taper_nb_scan_volume++;
+                   taper_cmd(START_TAPER, NULL, taper1->name, 0,
+                             driver_timestamp);
+                   break;
+               }
+           }
+           break;
+
+       case NO_NEW_TAPE:  /* NO-NEW-TAPE <handle> */
+            if (result_argc != 2) {
+                error(_("error [taper NO_NEW_TAPE result_argc != 2: %d]"),
+                      result_argc);
+               /*NOTREACHED*/
+            }
+           nb_sent_new_tape--;
+           taper_nb_scan_volume--;
+           dp = serial2disk(result_argv[1]);
+           taper = sched(dp)->taper;
+           taper->state |= TAPER_STATE_DONE;
+           last_started_taper = NULL;
+           start_degraded_mode(&runq);
+           break;
+
+       case DUMPER_STATUS:  /* DUMPER-STATUS <handle> */
+            if (result_argc != 2) {
+                error(_("error [taper DUMPER_STATUS result_argc != 2: %d]"),
+                      result_argc);
+               /*NOTREACHED*/
+            }
+            dp = serial2disk(result_argv[1]);
+           taper = sched(dp)->taper;
+           if (taper->dumper->result == LAST_TOK) {
+               taper->sendresult = 1;
+           } else {
+               if( taper->dumper->result == DONE) {
+                   taper_cmd(DONE, dp, NULL, 0, NULL);
+               } else {
+                   taper_cmd(FAILED, dp, NULL, 0, NULL);
+               }
+           }
+           break;
+
+        case TAPE_ERROR: /* TAPE-ERROR <name> <err mess> */
+           taper_started = 1;
+           if (strcmp(result_argv[1], "SETUP") == 0) {
+               taper_nb_wait_reply = 0;
+               taper_nb_scan_volume = 0;
+           } else {
+               taper = taper_from_name(result_argv[1]);
+               taper->state = TAPER_STATE_DONE;
+               fflush(stdout);
+               q = quote_string(result_argv[2]);
+               log_add(L_WARNING, _("Taper error: %s"), q);
+               amfree(q);
+               if (taper) {
+                   taper->tape_error = newstralloc(taper->tape_error,
+                                                   result_argv[2]);
+               }
+
+               taper_nb_wait_reply--;
+               taper_nb_scan_volume--;
+           }
+           if (taper_nb_wait_reply == 0) {
+               event_release(taper_ev_read);
+               taper_ev_read = NULL;
+           }
+           need_degraded = 1;
+           if (schedule_done && !degraded_mode) {
+               start_degraded_mode(&runq);
+           }
+           start_some_dumps(&runq);
+           break;
+
+       case PORT: /* PORT <name> <handle> <port> <dataport_list> */
             dp = serial2disk(result_argv[2]);
-            free_serial(result_argv[2]);
-            printf("driver: finished-cmd time %s taper wrote %s:%s\n",
-                   walltime_str(curclock()), dp->host->hostname, dp->name);
-            fflush(stdout);
-            log_add(L_WARNING, "Taper  error: %s", result_argv[3]);
-            /*FALLTHROUGH*/
+           taper = sched(dp)->taper;
+           dumper = sched(dp)->dumper;
+           dumper->output_port = atoi(result_argv[3]);
+           amfree(dp->dataport_list);
+           dp->dataport_list = stralloc(result_argv[4]);
+
+           amfree(taper->input_error);
+           amfree(taper->tape_error);
+           amfree(taper->first_label);
+           taper->written = 0;
+           taper->state |= TAPER_STATE_DUMP_TO_TAPE;
+
+           if (dp->host->pre_script == 0) {
+               for (dp1=dp->host->disks; dp1 != NULL; dp1 = dp1->hostnext) {
+                   run_server_scripts(EXECUTE_ON_PRE_HOST_BACKUP,
+                                      get_config_name(), dp1, -1);
+               }
+               dp->host->pre_script = 1;
+           }
+           run_server_scripts(EXECUTE_ON_PRE_DLE_BACKUP,
+                              get_config_name(), dp,
+                              sched(dp)->level);
+           /* tell the dumper to dump to a port */
+           dumper_cmd(dumper, PORT_DUMP, dp, NULL);
+           dp->host->start_t = time(NULL) + 15;
+           amfree(dp->dataport_list);
+
+           taper->state |= TAPER_STATE_DUMP_TO_TAPE;
+
+           dumper->ev_read = event_register(dumper->fd, EV_READFD,
+                                            handle_dumper_result, dumper);
+           break;
 
         case BOGUS:
-            if (cmd == BOGUS) {
-               log_add(L_WARNING, "Taper protocol error");
-            }
+            log_add(L_WARNING, _("Taper protocol error"));
             /*
              * Since we received a taper error, we can't send anything more
              * to the taper.  Go into degraded mode to try to get everthing
@@ -1331,47 +1886,282 @@ handle_taper_result(
              */
             if(!nodump) {
                 log_add(L_WARNING,
-                        "going into degraded mode because of taper component error.");
-                start_degraded_mode(&runq);
-            }
-            tapeq.head = tapeq.tail = NULL;
-            taper_busy = 0;
-            taper_disk = NULL;
+                        _("going into degraded mode because of taper component error."));
+           }
+
+           for (taper = tapetable;
+                taper < tapetable + conf_taper_parallel_write;
+                 taper++) {
+               if (taper && taper->disk && taper->result != LAST_TOK) {
+                   taper->tape_error = newstralloc(taper->tape_error,"BOGUS");
+                   taper->result = cmd;
+                   if (taper->dumper) {
+                       if (taper->dumper->result != LAST_TOK) {
+                           // Dumper already returned it's result
+                           dumper_taper_result(taper->disk);
+                       }
+                   } else {
+                       file_taper_result(taper->disk);
+                   }
+               }
+
+           }
+           taper = NULL;
+
             if(taper_ev_read != NULL) {
                 event_release(taper_ev_read);
                 taper_ev_read = NULL;
+               taper_nb_wait_reply = 0;
             }
-            if(cmd != TAPE_ERROR) aclose(taper);
-            continue_port_dumps();
+           start_degraded_mode(&runq);
+            tapeq.head = tapeq.tail = NULL;
+            aclose(taper_fd);
+
             break;
 
        default:
-            error("driver received unexpected token (%s) from taper",
+            error(_("driver received unexpected token (%s) from taper"),
                   cmdstr[cmd]);
            /*NOTREACHED*/
        }
-       /*
-        * Wakeup any dumpers that are sleeping because of network
-        * or disk constraints.
-        */
-       start_some_dumps(&runq);
-        
-    } while(areads_dataready(taper));
+
+       g_strfreev(result_argv);
+
+       if (taper && taper->disk && taper->result != LAST_TOK) {
+           if(taper->dumper) {
+               if (taper->dumper->result != LAST_TOK) {
+                   // Dumper already returned it's result
+                   dumper_taper_result(taper->disk);
+               }
+           } else {
+               file_taper_result(taper->disk);
+           }
+       }
+
+    } while(areads_dataready(taper_fd));
+    startaflush();
+}
+
+
+static void
+file_taper_result(
+    disk_t *dp)
+{
+    taper_t *taper;
+    char *qname = quote_string(dp->name);
+
+    taper = sched(dp)->taper;
+    if (taper->result == DONE) {
+       update_info_taper(dp, taper->first_label, taper->first_fileno,
+                         sched(dp)->level);
+    }
+
+    sched(dp)->taper_attempted += 1;
+
+    if (taper->input_error) {
+       g_printf("driver: taper failed %s %s: %s\n",
+                  dp->host->hostname, qname, taper->input_error);
+       if (strcmp(sched(dp)->datestamp, driver_timestamp) == 0) {
+           if(sched(dp)->taper_attempted >= 2) {
+               log_add(L_FAIL, _("%s %s %s %d [too many taper retries after holding disk error: %s]"),
+                   dp->host->hostname, qname, sched(dp)->datestamp,
+                   sched(dp)->level, taper->input_error);
+               g_printf("driver: taper failed %s %s, too many taper retry after holding disk error\n",
+                  dp->host->hostname, qname);
+               amfree(sched(dp)->destname);
+               amfree(sched(dp)->dumpdate);
+               amfree(sched(dp)->degr_dumpdate);
+               amfree(sched(dp)->degr_mesg);
+               amfree(sched(dp)->datestamp);
+               amfree(dp->up);
+           } else {
+               log_add(L_INFO, _("%s %s %s %d [Will retry dump because of holding disk error: %s]"),
+                       dp->host->hostname, qname, sched(dp)->datestamp,
+                       sched(dp)->level, taper->input_error);
+               g_printf("driver: taper will retry %s %s because of holding disk error\n",
+                       dp->host->hostname, qname);
+               if (dp->to_holdingdisk != HOLD_REQUIRED) {
+                   dp->to_holdingdisk = HOLD_NEVER;
+                   sched(dp)->dump_attempted -= 1;
+                   headqueue_disk(&directq, dp);
+               } else {
+                   amfree(sched(dp)->destname);
+                   amfree(sched(dp)->dumpdate);
+                   amfree(sched(dp)->degr_dumpdate);
+                   amfree(sched(dp)->degr_mesg);
+                   amfree(sched(dp)->datestamp);
+                   amfree(dp->up);
+               }
+           }
+       } else {
+           amfree(sched(dp)->destname);
+           amfree(sched(dp)->dumpdate);
+           amfree(sched(dp)->degr_dumpdate);
+           amfree(sched(dp)->degr_mesg);
+           amfree(sched(dp)->datestamp);
+           amfree(dp->up);
+       }
+    } else if (taper->tape_error) {
+       g_printf("driver: taper failed %s %s with tape error: %s\n",
+                  dp->host->hostname, qname, taper->tape_error);
+       if(sched(dp)->taper_attempted >= 2) {
+           log_add(L_FAIL, _("%s %s %s %d [too many taper retries]"),
+                   dp->host->hostname, qname, sched(dp)->datestamp,
+                   sched(dp)->level);
+           g_printf("driver: taper failed %s %s, too many taper retry\n",
+                  dp->host->hostname, qname);
+           amfree(sched(dp)->destname);
+           amfree(sched(dp)->dumpdate);
+           amfree(sched(dp)->degr_dumpdate);
+           amfree(sched(dp)->degr_mesg);
+           amfree(sched(dp)->datestamp);
+           amfree(dp->up);
+       } else {
+           g_printf("driver: taper will retry %s %s\n",
+                  dp->host->hostname, qname);
+           /* Re-insert into taper queue. */
+           headqueue_disk(&tapeq, dp);
+       }
+    } else if (taper->result != DONE) {
+       g_printf("driver: taper failed %s %s without error\n",
+                  dp->host->hostname, qname);
+    } else {
+       delete_diskspace(dp);
+       amfree(sched(dp)->destname);
+       amfree(sched(dp)->dumpdate);
+       amfree(sched(dp)->degr_dumpdate);
+       amfree(sched(dp)->degr_mesg);
+       amfree(sched(dp)->datestamp);
+       amfree(dp->up);
+    }
+
+    amfree(qname);
+
+    taper->state &= ~TAPER_STATE_FILE_TO_TAPE;
+    taper->state |= TAPER_STATE_IDLE;
+    amfree(taper->input_error);
+    amfree(taper->tape_error);
+    taper->disk = NULL;
+    taper_nb_wait_reply--;
+    if (taper_nb_wait_reply == 0) {
+       event_release(taper_ev_read);
+       taper_ev_read = NULL;
+    }
+
+    /* continue with those dumps waiting for diskspace */
+    continue_port_dumps();
+    start_some_dumps(&runq);
+    startaflush();
 }
 
-static dumper_t *
-idle_dumper(void)
+static void
+dumper_taper_result(
+    disk_t *dp)
 {
     dumper_t *dumper;
+    taper_t  *taper;
+    int is_partial;
+    char *qname;
+
+    dumper = sched(dp)->dumper;
+    taper  = sched(dp)->taper;
+
+    free_serial_dp(dp);
+    if(dumper->result == DONE && taper->result == DONE) {
+       update_info_dumper(dp, sched(dp)->origsize,
+                          sched(dp)->dumpsize, sched(dp)->dumptime);
+       update_info_taper(dp, taper->first_label, taper->first_fileno,
+                         sched(dp)->level);
+       qname = quote_string(dp->name); /*quote to take care of spaces*/
+
+       log_add(L_STATS, _("estimate %s %s %s %d [sec %ld nkb %lld ckb %lld kps %lu]"),
+               dp->host->hostname, qname, sched(dp)->datestamp,
+               sched(dp)->level,
+               sched(dp)->est_time, (long long)sched(dp)->est_nsize,
+               (long long)sched(dp)->est_csize,
+               sched(dp)->est_kps);
+       amfree(qname);
+    } else {
+       update_failed_dump(dp);
+    }
+
+    is_partial = dumper->result != DONE || taper->result != DONE;
+
+    sched(dp)->dump_attempted += 1;
+    sched(dp)->taper_attempted += 1;
+
+    if((dumper->result != DONE || taper->result != DONE) &&
+       sched(dp)->dump_attempted <= 1 &&
+       sched(dp)->taper_attempted <= 1) {
+       enqueue_disk(&directq, dp);
+    }
+
+    if(dumper->ev_read != NULL) {
+       event_release(dumper->ev_read);
+       dumper->ev_read = NULL;
+    }
+    taper_nb_wait_reply--;
+    if (taper_nb_wait_reply == 0 && taper_ev_read != NULL) {
+       event_release(taper_ev_read);
+       taper_ev_read = NULL;
+    }
+    taper->state &= ~TAPER_STATE_DUMP_TO_TAPE;
+    taper->state |= TAPER_STATE_IDLE;
+    amfree(taper->input_error);
+    amfree(taper->tape_error);
+    dumper->busy = 0;
+    dp->host->inprogress -= 1;
+    dp->inprogress = 0;
+    deallocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
+    taper->dumper = NULL;
+    sched(dp)->dumper = NULL;
+    sched(dp)->taper = NULL;
+    start_some_dumps(&runq);
+}
+
+
+static taper_t *
+idle_taper(void)
+{
+    taper_t *taper;
+
+    /* Use an already started taper first */
+    for (taper = tapetable; taper < tapetable + conf_taper_parallel_write;
+                           taper++) {
+       if ((taper->state & TAPER_STATE_IDLE) &&
+           (taper->state & TAPER_STATE_TAPE_STARTED) &&
+           !(taper->state & TAPER_STATE_DONE) &&
+           !(taper->state & TAPER_STATE_FILE_TO_TAPE) &&
+           !(taper->state & TAPER_STATE_FILE_TO_TAPE))
+           return taper;
+    }
+    for (taper = tapetable; taper < tapetable + conf_taper_parallel_write;
+                           taper++) {
+       if ((taper->state & TAPER_STATE_IDLE) &&
+           (taper->state & TAPER_STATE_RESERVATION) &&
+           !(taper->state & TAPER_STATE_DONE) &&
+           !(taper->state & TAPER_STATE_FILE_TO_TAPE) &&
+           !(taper->state & TAPER_STATE_FILE_TO_TAPE))
+           return taper;
+    }
+    return NULL;
+}
+
+static taper_t *
+taper_from_name(
+    char *name)
+{
+    taper_t *taper;
 
-    for(dumper = dmptable; dumper < dmptable+inparallel; dumper++)
-       if(!dumper->busy && !dumper->down) return dumper;
+    for (taper = tapetable; taper < tapetable+conf_taper_parallel_write;
+                           taper++)
+       if (strcmp(taper->name, name) == 0) return taper;
 
     return NULL;
 }
 
 static void
-dumper_result(
+dumper_chunker_result(
     disk_t *   dp)
 {
     dumper_t *dumper;
@@ -1381,6 +2171,7 @@ dumper_result(
     off_t dummy;
     off_t size;
     int is_partial;
+    char *qname;
 
     dumper = sched(dp)->dumper;
     chunker = dumper->chunker;
@@ -1393,19 +2184,24 @@ dumper_result(
     if(dumper->result == DONE && chunker->result == DONE) {
        update_info_dumper(dp, sched(dp)->origsize,
                           sched(dp)->dumpsize, sched(dp)->dumptime);
-       log_add(L_STATS, "estimate %s %s %s %d [sec %ld nkb " OFF_T_FMT
-               " ckb " OFF_T_FMT " kps %lu]",
-               dp->host->hostname, dp->name, sched(dp)->datestamp,
+       qname = quote_string(dp->name);/*quote to take care of spaces*/
+
+       log_add(L_STATS, _("estimate %s %s %s %d [sec %ld nkb %lld ckb %lld kps %lu]"),
+               dp->host->hostname, qname, sched(dp)->datestamp,
                sched(dp)->level,
-               sched(dp)->est_time, (OFF_T_FMT_TYPE)sched(dp)->est_nsize, 
-                (OFF_T_FMT_TYPE)sched(dp)->est_csize,
+               sched(dp)->est_time, (long long)sched(dp)->est_nsize, 
+                (long long)sched(dp)->est_csize,
                sched(dp)->est_kps);
+       amfree(qname);
+    } else {
+       update_failed_dump(dp);
     }
 
     deallocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
 
     is_partial = dumper->result != DONE || chunker->result != DONE;
     rename_tmp_holding(sched(dp)->destname, !is_partial);
+    holding_set_origsize(sched(dp)->destname, sched(dp)->origsize);
 
     dummy = (off_t)0;
     for( i = 0, h = sched(dp)->holdp; i < activehd; i++ ) {
@@ -1414,20 +2210,22 @@ dumper_result(
 
     size = holding_file_size(sched(dp)->destname, 0);
     h[activehd]->used = size - dummy;
-    holdalloc(h[activehd]->disk)->allocated_dumpers--;
+    h[activehd]->disk->allocated_dumpers--;
     adjust_diskspace(dp, DONE);
 
-    sched(dp)->attempted += 1;
+    sched(dp)->dump_attempted += 1;
 
     if((dumper->result != DONE || chunker->result != DONE) &&
-       sched(dp)->attempted <= 1) {
+       sched(dp)->dump_attempted <= 1) {
        delete_diskspace(dp);
-       enqueue_disk(&runq, dp);
+       if (sched(dp)->no_space) {
+           enqueue_disk(&directq, dp);
+       } else {
+           enqueue_disk(&runq, dp);
+       }
     }
     else if(size > (off_t)DISK_BLOCK_KB) {
-       sched(dp)->attempted = 0;
        enqueue_disk(&tapeq, dp);
-       startaflush();
     }
     else {
        delete_diskspace(dp);
@@ -1441,7 +2239,7 @@ dumper_result(
     aclose(chunker->fd);
     chunker->fd = -1;
     chunker->down = 1;
-    
+
     dp = NULL;
     if (chunker->result == ABORT_FINISHED)
        pending_aborts--;
@@ -1451,37 +2249,39 @@ dumper_result(
      * or disk constraints.
      */
     start_some_dumps(&runq);
+    startaflush();
 }
 
 
 static void
 handle_dumper_result(
-    void *     cookie)
+       void * cookie)
 {
-    /*static int pending_aborts = 0;*/
+    /* uses global pending_aborts */
     dumper_t *dumper = cookie;
-    disk_t *dp, *sdp;
+    taper_t  *taper;
+    disk_t *dp, *sdp, *dp1;
     cmd_t cmd;
     int result_argc;
     char *qname;
-    char *result_argv[MAX_ARGS+1];
+    char **result_argv;
 
     assert(dumper != NULL);
     dp = dumper->dp;
-    assert(dp != NULL && sched(dp) != NULL);
-
+    assert(dp != NULL);
+    assert(sched(dp) != NULL);
     do {
 
        short_dump_state();
 
-       cmd = getresult(dumper->fd, 1, &result_argc, result_argv, MAX_ARGS+1);
+       cmd = getresult(dumper->fd, 1, &result_argc, &result_argv);
 
        if(cmd != BOGUS) {
-           /* result_argv[2] always contains the serial number */
-           sdp = serial2disk(result_argv[2]);
+           /* result_argv[1] always contains the serial number */
+           sdp = serial2disk(result_argv[1]);
            if (sdp != dp) {
-               error("%s: Invalid serial number: %s", get_pname(), result_argv[2]);
-               /*NOTREACHED*/
+               error(_("Invalid serial number %s"), result_argv[1]);
+                g_assert_not_reached();
            }
        }
 
@@ -1490,16 +2290,14 @@ handle_dumper_result(
 
        case DONE: /* DONE <handle> <origsize> <dumpsize> <dumptime> <errstr> */
            if(result_argc != 6) {
-               error("error [dumper DONE result_argc != 6: %d]", result_argc);
+               error(_("error [dumper DONE result_argc != 6: %d]"), result_argc);
                /*NOTREACHED*/
            }
 
-           /*free_serial(result_argv[2]);*/
+           sched(dp)->origsize = OFF_T_ATOI(result_argv[2]);
+           sched(dp)->dumptime = TIME_T_ATOI(result_argv[4]);
 
-           sched(dp)->origsize = OFF_T_ATOI(result_argv[3]);
-           sched(dp)->dumptime = TIME_T_ATOI(result_argv[5]);
-
-           printf("driver: finished-cmd time %s %s dumped %s:%s\n",
+           g_printf(_("driver: finished-cmd time %s %s dumped %s:%s\n"),
                   walltime_str(curclock()), dumper->name,
                   dp->host->hostname, qname);
            fflush(stdout);
@@ -1513,17 +2311,20 @@ handle_dumper_result(
             * Requeue this disk, and fall through to the FAILED
             * case for cleanup.
             */
-           if(sched(dp)->attempted) {
-               log_add(L_FAIL, "%s %s %s %d [too many dumper retry: %s]",
-                   dp->host->hostname, dp->name, sched(dp)->datestamp,
-                   sched(dp)->level, result_argv[3]);
-               printf("driver: dump failed %s %s %s, too many dumper retry: %s\n",
-                       result_argv[2], dp->host->hostname, dp->name,
-                       result_argv[3]);
+           if(sched(dp)->dump_attempted) {
+               char *qname = quote_string(dp->name);
+               char *qerr = quote_string(result_argv[2]);
+               log_add(L_FAIL, _("%s %s %s %d [too many dumper retry: %s]"),
+                   dp->host->hostname, qname, sched(dp)->datestamp,
+                   sched(dp)->level, qerr);
+               g_printf(_("driver: dump failed %s %s %s, too many dumper retry: %s\n"),
+                       result_argv[1], dp->host->hostname, qname, qerr);
+               amfree(qname);
+               amfree(qerr);
            }
            /* FALLTHROUGH */
        case FAILED: /* FAILED <handle> <errstr> */
-           /*free_serial(result_argv[2]);*/
+           /*free_serial(result_argv[1]);*/
            dumper->result = cmd;
            break;
 
@@ -1535,13 +2336,13 @@ handle_dumper_result(
             * other dumps that are waiting on disk space.
             */
            assert(pending_aborts);
-           /*free_serial(result_argv[2]);*/
+           /*free_serial(result_argv[1]);*/
            dumper->result = cmd;
            break;
 
        case BOGUS:
            /* either EOF or garbage from dumper.  Turn it off */
-           log_add(L_WARNING, "%s pid %ld is messed up, ignoring it.\n",
+           log_add(L_WARNING, _("%s pid %ld is messed up, ignoring it.\n"),
                    dumper->name, (long)dumper->pid);
            if (dumper->ev_read) {
                event_release(dumper->ev_read);
@@ -1550,19 +2351,17 @@ handle_dumper_result(
            aclose(dumper->fd);
            dumper->busy = 0;
            dumper->down = 1;   /* mark it down so it isn't used again */
-           if(dp) {
-               /* if it was dumping something, zap it and try again */
-               if(sched(dp)->attempted) {
-               log_add(L_FAIL, "%s %s %s %d [%s died]",
+
+            /* if it was dumping something, zap it and try again */
+            if(sched(dp)->dump_attempted) {
+               log_add(L_FAIL, _("%s %s %s %d [%s died]"),
                        dp->host->hostname, qname, sched(dp)->datestamp,
                        sched(dp)->level, dumper->name);
-               }
-               else {
-               log_add(L_WARNING, "%s died while dumping %s:%s lev %d.",
+            } else {
+               log_add(L_WARNING, _("%s died while dumping %s:%s lev %d."),
                        dumper->name, dp->host->hostname, qname,
                        sched(dp)->level);
-               }
-           }
+            }
            dumper->result = cmd;
            break;
 
@@ -1570,21 +2369,69 @@ handle_dumper_result(
            assert(0);
        }
         amfree(qname);
+       g_strfreev(result_argv);
+
+       if (cmd != BOGUS) {
+           int last_dump = 1;
+           dumper_t *dumper;
+
+           run_server_scripts(EXECUTE_ON_POST_DLE_BACKUP,
+                              get_config_name(), dp, sched(dp)->level);
+           /* check dump not yet started */
+           for (dp1=runq.head; dp1 != NULL; dp1 = dp1->next) {
+               if (dp1->host == dp->host)
+                   last_dump = 0;
+           }
+           /* check direct to tape dump */
+           for (dp1=directq.head; dp1 != NULL; dp1 = dp1->next) {
+               if (dp1->host == dp->host)
+                   last_dump = 0;
+           }
+           /* check dumping dle */
+           for (dumper = dmptable; dumper < dmptable + inparallel; dumper++) {
+               if (dumper->busy && dumper->dp != dp &&
+                   dumper->dp->host == dp->host)
+                last_dump = 0;
+           }
+           if (last_dump && dp->host->post_script == 0) {
+               if (dp->host->post_script == 0) {
+                   for (dp1=dp->host->disks; dp1 != NULL; dp1 = dp1->hostnext) {
+                       run_server_scripts(EXECUTE_ON_POST_HOST_BACKUP,
+                                          get_config_name(), dp1, -1);
+                   }
+                   dp->host->post_script = 1;
+               }
+           }
+       }
 
+       taper = sched(dp)->taper;
        /* send the dumper result to the chunker */
-       if(dumper->chunker->down == 0 && dumper->chunker->fd != -1 &&
-          dumper->chunker->result == LAST_TOK) {
-           if(cmd == DONE) {
-               chunker_cmd(dumper->chunker, DONE, dp);
+       if (dumper->chunker) {
+           if (dumper->chunker->down == 0 && dumper->chunker->fd != -1 &&
+               dumper->chunker->result == LAST_TOK) {
+               if (cmd == DONE) {
+                   chunker_cmd(dumper->chunker, DONE, dp, NULL);
+               }
+               else {
+                   chunker_cmd(dumper->chunker, FAILED, dp, NULL);
+               }
            }
-           else {
-               chunker_cmd(dumper->chunker, FAILED, dp);
+           if( dumper->result != LAST_TOK &&
+               dumper->chunker->result != LAST_TOK)
+               dumper_chunker_result(dp);
+       } else { /* send the dumper result to the taper */
+           if (taper->sendresult) {
+               if (cmd == DONE) {
+                   taper_cmd(DONE, dp, NULL, 0, NULL);
+               } else {
+                   taper_cmd(FAILED, dp, NULL, 0, NULL);
+               }
+               taper->sendresult = 0;
+           }
+           if (taper->dumper && taper->result != LAST_TOK) {
+               dumper_taper_result(dp);
            }
        }
-
-       if(dumper->result != LAST_TOK && dumper->chunker->result != LAST_TOK)
-           dumper_result(dp);
-
     } while(areads_dataready(dumper->fd));
 }
 
@@ -1593,14 +2440,13 @@ static void
 handle_chunker_result(
     void *     cookie)
 {
-    /*static int pending_aborts = 0;*/
     chunker_t *chunker = cookie;
     assignedhd_t **h=NULL;
     dumper_t *dumper;
     disk_t *dp, *sdp;
     cmd_t cmd;
     int result_argc;
-    char *result_argv[MAX_ARGS+1];
+    char **result_argv;
     int dummy;
     int activehd = -1;
     char *qname;
@@ -1614,23 +2460,22 @@ handle_chunker_result(
     assert(sched(dp)->destname != NULL);
     assert(dp != NULL && sched(dp) != NULL && sched(dp)->destname);
 
-    if(dp && sched(dp) && sched(dp)->holdp) {
+    if(sched(dp)->holdp) {
        h = sched(dp)->holdp;
        activehd = sched(dp)->activehd;
     }
 
     do {
-
        short_dump_state();
 
-       cmd = getresult(chunker->fd, 1, &result_argc, result_argv, MAX_ARGS+1);
+       cmd = getresult(chunker->fd, 1, &result_argc, &result_argv);
 
        if(cmd != BOGUS) {
-           /* result_argv[2] always contains the serial number */
-           sdp = serial2disk(result_argv[2]);
+           /* result_argv[1] always contains the serial number */
+           sdp = serial2disk(result_argv[1]);
            if (sdp != dp) {
-               error("%s: Invalid serial number: %s", get_pname(), result_argv[2]);
-               /*NOTREACHED*/
+               error(_("Invalid serial number %s"), result_argv[1]);
+                g_assert_not_reached();
            }
        }
 
@@ -1639,16 +2484,16 @@ handle_chunker_result(
        case PARTIAL: /* PARTIAL <handle> <dumpsize> <errstr> */
        case DONE: /* DONE <handle> <dumpsize> <errstr> */
            if(result_argc != 4) {
-               error("error [chunker %s result_argc != 4: %d]", cmdstr[cmd],
+               error(_("error [chunker %s result_argc != 4: %d]"), cmdstr[cmd],
                      result_argc);
                /*NOTREACHED*/
            }
-           /*free_serial(result_argv[2]);*/
+           /*free_serial(result_argv[1]);*/
 
-           sched(dp)->dumpsize = (off_t)atof(result_argv[3]);
+           sched(dp)->dumpsize = (off_t)atof(result_argv[2]);
 
            qname = quote_string(dp->name);
-           printf("driver: finished-cmd time %s %s chunked %s:%s\n",
+           g_printf(_("driver: finished-cmd time %s %s chunked %s:%s\n"),
                   walltime_str(curclock()), chunker->name,
                   dp->host->hostname, qname);
            fflush(stdout);
@@ -1667,7 +2512,7 @@ handle_chunker_result(
 
            break;
        case FAILED: /* FAILED <handle> <errstr> */
-           /*free_serial(result_argv[2]);*/
+           /*free_serial(result_argv[1]);*/
 
            event_release(chunker->ev_read);
 
@@ -1677,26 +2522,26 @@ handle_chunker_result(
 
        case NO_ROOM: /* NO-ROOM <handle> <missing_size> */
            if (!h || activehd < 0) { /* should never happen */
-               error("!h || activehd < 0");
+               error(_("!h || activehd < 0"));
                /*NOTREACHED*/
            }
-           h[activehd]->used -= OFF_T_ATOI(result_argv[3]);
-           h[activehd]->reserved -= OFF_T_ATOI(result_argv[3]);
-           holdalloc(h[activehd]->disk)->allocated_space -= OFF_T_ATOI(result_argv[3]);
-           h[activehd]->disk->disksize -= OFF_T_ATOI(result_argv[3]);
+           h[activehd]->used -= OFF_T_ATOI(result_argv[2]);
+           h[activehd]->reserved -= OFF_T_ATOI(result_argv[2]);
+           h[activehd]->disk->allocated_space -= OFF_T_ATOI(result_argv[2]);
+           h[activehd]->disk->disksize -= OFF_T_ATOI(result_argv[2]);
            break;
 
        case RQ_MORE_DISK: /* RQ-MORE-DISK <handle> */
            if (!h || activehd < 0) { /* should never happen */
-               error("!h || activehd < 0");
+               error(_("!h || activehd < 0"));
                /*NOTREACHED*/
            }
-           holdalloc(h[activehd]->disk)->allocated_dumpers--;
+           h[activehd]->disk->allocated_dumpers--;
            h[activehd]->used = h[activehd]->reserved;
            if( h[++activehd] ) { /* There's still some allocated space left.
                                   * Tell the dumper about it. */
                sched(dp)->activehd++;
-               chunker_cmd( chunker, CONTINUE, dp );
+               chunker_cmd( chunker, CONTINUE, dp, NULL );
            } else { /* !h[++activehd] - must allocate more space */
                sched(dp)->act_size = sched(dp)->est_size; /* not quite true */
                sched(dp)->est_size = (sched(dp)->act_size/(off_t)20) * (off_t)21; /* +5% */
@@ -1711,10 +2556,12 @@ handle_chunker_result(
                     * determined in continue_port_dumps(). */
                    enqueue_disk( &roomq, dp );
                    continue_port_dumps();
+                   /* continue flush waiting for new tape */
+                   startaflush();
                } else {
                    /* OK, allocate space for disk and have chunker continue */
                    sched(dp)->activehd = assign_holdingdisk( h, dp );
-                   chunker_cmd( chunker, CONTINUE, dp );
+                   chunker_cmd( chunker, CONTINUE, dp, NULL );
                    amfree(h);
                }
            }
@@ -1729,7 +2576,7 @@ handle_chunker_result(
             */
            /*assert(pending_aborts);*/
 
-           /*free_serial(result_argv[2]);*/
+           /*free_serial(result_argv[1]);*/
 
            event_release(chunker->ev_read);
 
@@ -1739,29 +2586,23 @@ handle_chunker_result(
 
        case BOGUS:
            /* either EOF or garbage from chunker.  Turn it off */
-           log_add(L_WARNING, "%s pid %ld is messed up, ignoring it.\n",
+           log_add(L_WARNING, _("%s pid %ld is messed up, ignoring it.\n"),
                    chunker->name, (long)chunker->pid);
 
-           if(dp) {
-               /* if it was dumping something, zap it and try again */
-               if (!h || activehd < 0) { /* should never happen */
-                   error("!h || activehd < 0");
-                   /*NOTREACHED*/
-               }
-               qname = quote_string(dp->name);
-               if(sched(dp)->attempted) {
-                   log_add(L_FAIL, "%s %s %s %d [%s died]",
-                           dp->host->hostname, qname, sched(dp)->datestamp,
-                           sched(dp)->level, chunker->name);
-               }
-               else {
-                   log_add(L_WARNING, "%s died while dumping %s:%s lev %d.",
-                           chunker->name, dp->host->hostname, qname,
-                           sched(dp)->level);
-               }
-               amfree(qname);
-               dp = NULL;
-           }
+            /* if it was dumping something, zap it and try again */
+            g_assert(h && activehd >= 0);
+            qname = quote_string(dp->name);
+            if(sched(dp)->dump_attempted) {
+                log_add(L_FAIL, _("%s %s %s %d [%s died]"),
+                        dp->host->hostname, qname, sched(dp)->datestamp,
+                        sched(dp)->level, chunker->name);
+            } else {
+                log_add(L_WARNING, _("%s died while dumping %s:%s lev %d."),
+                        chunker->name, dp->host->hostname, qname,
+                        sched(dp)->level);
+            }
+            amfree(qname);
+            dp = NULL;
 
            event_release(chunker->ev_read);
 
@@ -1772,21 +2613,22 @@ handle_chunker_result(
        default:
            assert(0);
        }
+       g_strfreev(result_argv);
 
        if(chunker->result != LAST_TOK && chunker->dumper->result != LAST_TOK)
-           dumper_result(dp);
+           dumper_chunker_result(dp);
 
     } while(areads_dataready(chunker->fd));
 }
 
 
-static disklist_t
-read_flush(void)
+static void
+read_flush(
+    void *     cookie)
 {
     sched_t *sp;
     disk_t *dp;
     int line;
-    dumpfile_t file;
     char *hostname, *diskname, *datestamp;
     int level;
     char *destname;
@@ -1795,13 +2637,26 @@ read_flush(void)
     char *command;
     char *s;
     int ch;
-    disklist_t tq;
     char *qname = NULL;
     char *qdestname = NULL;
+    char *conf_infofile;
 
-    tq.head = tq.tail = NULL;
+    (void)cookie;      /* Quiet unused parameter warning */
+
+    event_release(flush_ev_read);
+    flush_ev_read = NULL;
+
+    /* read schedule from stdin */
+    conf_infofile = config_dir_relative(getconf_str(CNF_INFOFILE));
+    if (open_infofile(conf_infofile)) {
+       error(_("could not open info db \"%s\""), conf_infofile);
+       /*NOTREACHED*/
+    }
+    amfree(conf_infofile);
 
     for(line = 0; (inpline = agets(stdin)) != NULL; free(inpline)) {
+       dumpfile_t file;
+
        line++;
        if (inpline[0] == '\0')
            continue;
@@ -1811,7 +2666,7 @@ read_flush(void)
 
        skip_whitespace(s, ch);                 /* find the command */
        if(ch == '\0') {
-           error("flush line %d: syntax error (no command)", line);
+           error(_("flush line %d: syntax error (no command)"), line);
            /*NOTREACHED*/
        }
        command = s - 1;
@@ -1823,13 +2678,13 @@ read_flush(void)
        }
 
        if(strcmp(command,"FLUSH") != 0) {
-           error("flush line %d: syntax error (%s != FLUSH)", line, command);
+           error(_("flush line %d: syntax error (%s != FLUSH)"), line, command);
            /*NOTREACHED*/
        }
 
        skip_whitespace(s, ch);                 /* find the hostname */
        if(ch == '\0') {
-           error("flush line %d: syntax error (no hostname)", line);
+           error(_("flush line %d: syntax error (no hostname)"), line);
            /*NOTREACHED*/
        }
        hostname = s - 1;
@@ -1838,7 +2693,7 @@ read_flush(void)
 
        skip_whitespace(s, ch);                 /* find the diskname */
        if(ch == '\0') {
-           error("flush line %d: syntax error (no diskname)", line);
+           error(_("flush line %d: syntax error (no diskname)"), line);
            /*NOTREACHED*/
        }
        qname = s - 1;
@@ -1848,7 +2703,7 @@ read_flush(void)
 
        skip_whitespace(s, ch);                 /* find the datestamp */
        if(ch == '\0') {
-           error("flush line %d: syntax error (no datestamp)", line);
+           error(_("flush line %d: syntax error (no datestamp)"), line);
            /*NOTREACHED*/
        }
        datestamp = s - 1;
@@ -1857,14 +2712,14 @@ read_flush(void)
 
        skip_whitespace(s, ch);                 /* find the level number */
        if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
-           error("flush line %d: syntax error (bad level)", line);
+           error(_("flush line %d: syntax error (bad level)"), line);
            /*NOTREACHED*/
        }
        skip_integer(s, ch);
 
        skip_whitespace(s, ch);                 /* find the filename */
        if(ch == '\0') {
-           error("flush line %d: syntax error (no filename)", line);
+           error(_("flush line %d: syntax error (no filename)"), line);
            /*NOTREACHED*/
        }
        qdestname = s - 1;
@@ -1875,17 +2730,21 @@ read_flush(void)
        holding_file_get_dumpfile(destname, &file);
        if( file.type != F_DUMPFILE) {
            if( file.type != F_CONT_DUMPFILE )
-               log_add(L_INFO, "%s: ignoring cruft file.", destname);
+               log_add(L_INFO, _("%s: ignoring cruft file."), destname);
            amfree(diskname);
+           amfree(destname);
+           dumpfile_free_data(&file);
            continue;
        }
 
        if(strcmp(hostname, file.name) != 0 ||
           strcmp(diskname, file.disk) != 0 ||
           strcmp(datestamp, file.datestamp) != 0) {
-           log_add(L_INFO, "disk %s:%s not consistent with file %s",
+           log_add(L_INFO, _("disk %s:%s not consistent with file %s"),
                    hostname, diskname, destname);
            amfree(diskname);
+           amfree(destname);
+           dumpfile_free_data(&file);
            continue;
        }
        amfree(diskname);
@@ -1893,20 +2752,26 @@ read_flush(void)
        dp = lookup_disk(file.name, file.disk);
 
        if (dp == NULL) {
-           log_add(L_INFO, "%s: disk %s:%s not in database, skipping it.",
+           log_add(L_INFO, _("%s: disk %s:%s not in database, skipping it."),
                    destname, file.name, file.disk);
+           amfree(destname);
+           dumpfile_free_data(&file);
            continue;
        }
 
        if(file.dumplevel < 0 || file.dumplevel > 9) {
-           log_add(L_INFO, "%s: ignoring file with bogus dump level %d.",
+           log_add(L_INFO, _("%s: ignoring file with bogus dump level %d."),
                    destname, file.dumplevel);
+           amfree(destname);
+           dumpfile_free_data(&file);
            continue;
        }
 
        if (holding_file_size(destname,1) <= 0) {
            log_add(L_INFO, "%s: removing file with no data.", destname);
            holding_file_unlink(destname);
+           amfree(destname);
+           dumpfile_free_data(&file);
            continue;
        }
 
@@ -1926,31 +2791,41 @@ read_flush(void)
        flushhost->disks = dp1;
 
        sp = (sched_t *) alloc(SIZEOF(sched_t));
-       sp->destname = stralloc(destname);
+       sp->destname = destname;
        sp->level = file.dumplevel;
        sp->dumpdate = NULL;
        sp->degr_dumpdate = NULL;
+       sp->degr_mesg = NULL;
        sp->datestamp = stralloc(file.datestamp);
        sp->est_nsize = (off_t)0;
        sp->est_csize = (off_t)0;
        sp->est_time = 0;
        sp->est_kps = 10;
+       sp->origsize = file.orig_size;
        sp->priority = 0;
        sp->degr_level = -1;
-       sp->attempted = 0;
+       sp->dump_attempted = 0;
+       sp->taper_attempted = 0;
        sp->act_size = holding_file_size(destname, 0);
        sp->holdp = build_diskspace(destname);
        if(sp->holdp == NULL) continue;
        sp->dumper = NULL;
+       sp->taper = NULL;
        sp->timestamp = (time_t)0;
 
        dp1->up = (char *)sp;
 
-       enqueue_disk(&tq, dp1);
+       enqueue_disk(&tapeq, dp1);
+       dumpfile_free_data(&file);
     }
     amfree(inpline);
+    close_infofile();
 
-    /*@i@*/ return tq;
+    startaflush();
+    if (!nodump) {
+       schedule_ev_read = event_register((event_id_t)0, EV_READFD,
+                                         read_schedule, NULL);
+    }
 }
 
 static void
@@ -1960,7 +2835,7 @@ read_schedule(
     sched_t *sp;
     disk_t *dp;
     int level, line, priority;
-    char *dumpdate, *degr_dumpdate;
+    char *dumpdate, *degr_dumpdate, *degr_mesg;
     int degr_level;
     time_t time, degr_time;
     time_t *time_p = &time;
@@ -1973,14 +2848,17 @@ read_schedule(
     int ch;
     off_t flush_size = (off_t)0;
     char *qname = NULL;
-    OFF_T_FMT_TYPE nsize_;
-    OFF_T_FMT_TYPE csize_;
-    OFF_T_FMT_TYPE degr_nsize_;
-    OFF_T_FMT_TYPE degr_csize_;
+    long long time_;
+    long long nsize_;
+    long long csize_;
+    long long degr_nsize_;
+    long long degr_csize_;
+    GPtrArray *errarray;
 
     (void)cookie;      /* Quiet unused parameter warning */
 
     event_release(schedule_ev_read);
+    schedule_ev_read = NULL;
 
     /* read schedule from stdin */
 
@@ -1994,7 +2872,7 @@ read_schedule(
 
        skip_whitespace(s, ch);                 /* find the command */
        if(ch == '\0') {
-           error("schedule line %d: syntax error (no command)", line);
+           error(_("schedule line %d: syntax error (no command)"), line);
            /*NOTREACHED*/
        }
        command = s - 1;
@@ -2002,13 +2880,13 @@ read_schedule(
        s[-1] = '\0';
 
        if(strcmp(command,"DUMP") != 0) {
-           error("schedule line %d: syntax error (%s != DUMP)", line, command);
+           error(_("schedule line %d: syntax error (%s != DUMP)"), line, command);
            /*NOTREACHED*/
        }
 
        skip_whitespace(s, ch);                 /* find the host name */
        if(ch == '\0') {
-           error("schedule line %d: syntax error (no host name)", line);
+           error(_("schedule line %d: syntax error (no host name)"), line);
            /*NOTREACHED*/
        }
        hostname = s - 1;
@@ -2017,7 +2895,7 @@ read_schedule(
 
        skip_whitespace(s, ch);                 /* find the feature list */
        if(ch == '\0') {
-           error("schedule line %d: syntax error (no feature list)", line);
+           error(_("schedule line %d: syntax error (no feature list)"), line);
            /*NOTREACHED*/
        }
        features = s - 1;
@@ -2026,7 +2904,7 @@ read_schedule(
 
        skip_whitespace(s, ch);                 /* find the disk name */
        if(ch == '\0') {
-           error("schedule line %d: syntax error (no disk name)", line);
+           error(_("schedule line %d: syntax error (no disk name)"), line);
            /*NOTREACHED*/
        }
        qname = s - 1;
@@ -2036,7 +2914,7 @@ read_schedule(
 
        skip_whitespace(s, ch);                 /* find the datestamp */
        if(ch == '\0') {
-           error("schedule line %d: syntax error (no datestamp)", line);
+           error(_("schedule line %d: syntax error (no datestamp)"), line);
            /*NOTREACHED*/
        }
        datestamp = s - 1;
@@ -2045,21 +2923,21 @@ read_schedule(
 
        skip_whitespace(s, ch);                 /* find the priority number */
        if(ch == '\0' || sscanf(s - 1, "%d", &priority) != 1) {
-           error("schedule line %d: syntax error (bad priority)", line);
+           error(_("schedule line %d: syntax error (bad priority)"), line);
            /*NOTREACHED*/
        }
        skip_integer(s, ch);
 
        skip_whitespace(s, ch);                 /* find the level number */
        if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
-           error("schedule line %d: syntax error (bad level)", line);
+           error(_("schedule line %d: syntax error (bad level)"), line);
            /*NOTREACHED*/
        }
        skip_integer(s, ch);
 
        skip_whitespace(s, ch);                 /* find the dump date */
        if(ch == '\0') {
-           error("schedule line %d: syntax error (bad dump date)", line);
+           error(_("schedule line %d: syntax error (bad dump date)"), line);
            /*NOTREACHED*/
        }
        dumpdate = s - 1;
@@ -2067,50 +2945,61 @@ read_schedule(
        s[-1] = '\0';
 
        skip_whitespace(s, ch);                 /* find the native size */
-       nsize_ = (OFF_T_FMT_TYPE)0;
-       if(ch == '\0' || sscanf(s - 1, OFF_T_FMT, &nsize_) != 1) {
-           error("schedule line %d: syntax error (bad nsize)", line);
+       nsize_ = (off_t)0;
+       if(ch == '\0' || sscanf(s - 1, "%lld", &nsize_) != 1) {
+           error(_("schedule line %d: syntax error (bad nsize)"), line);
            /*NOTREACHED*/
        }
        nsize = (off_t)nsize_;
        skip_integer(s, ch);
 
        skip_whitespace(s, ch);                 /* find the compressed size */
-       csize_ = (OFF_T_FMT_TYPE)0;
-       if(ch == '\0' || sscanf(s - 1, OFF_T_FMT, &csize_) != 1) {
-           error("schedule line %d: syntax error (bad csize)", line);
+       csize_ = (off_t)0;
+       if(ch == '\0' || sscanf(s - 1, "%lld", &csize_) != 1) {
+           error(_("schedule line %d: syntax error (bad csize)"), line);
            /*NOTREACHED*/
        }
        csize = (off_t)csize_;
        skip_integer(s, ch);
 
        skip_whitespace(s, ch);                 /* find the time number */
-       if(ch == '\0' || sscanf(s - 1, TIME_T_FMT,
-                               (TIME_T_FMT_TYPE *)time_p) != 1) {
-           error("schedule line %d: syntax error (bad estimated time)", line);
+       if(ch == '\0' || sscanf(s - 1, "%lld", &time_) != 1) {
+           error(_("schedule line %d: syntax error (bad estimated time)"), line);
            /*NOTREACHED*/
        }
+       *time_p = (time_t)time_;
        skip_integer(s, ch);
 
        skip_whitespace(s, ch);                 /* find the kps number */
        if(ch == '\0' || sscanf(s - 1, "%lu", &kps) != 1) {
-           error("schedule line %d: syntax error (bad kps)", line);
+           error(_("schedule line %d: syntax error (bad kps)"), line);
            continue;
        }
        skip_integer(s, ch);
 
        degr_dumpdate = NULL;                   /* flag if degr fields found */
        skip_whitespace(s, ch);                 /* find the degr level number */
-       if(ch != '\0') {
+       degr_mesg = NULL;
+       if (ch == '"') {
+           qname = s - 1;
+           skip_quoted_string(s, ch);
+           s[-1] = '\0';                       /* terminate degr mesg */
+           degr_mesg = unquote_string(qname);
+           degr_level = -1;
+           degr_nsize = (off_t)0;
+           degr_csize = (off_t)0;
+           degr_time = (time_t)0;
+           degr_kps = 0;
+       } else if (ch != '\0') {
            if(sscanf(s - 1, "%d", &degr_level) != 1) {
-               error("schedule line %d: syntax error (bad degr level)", line);
+               error(_("schedule line %d: syntax error (bad degr level)"), line);
                /*NOTREACHED*/
            }
            skip_integer(s, ch);
 
            skip_whitespace(s, ch);             /* find the degr dump date */
            if(ch == '\0') {
-               error("schedule line %d: syntax error (bad degr dump date)", line);
+               error(_("schedule line %d: syntax error (bad degr dump date)"), line);
                /*NOTREACHED*/
            }
            degr_dumpdate = s - 1;
@@ -2118,49 +3007,45 @@ read_schedule(
            s[-1] = '\0';
 
            skip_whitespace(s, ch);             /* find the degr native size */
-           degr_nsize_ = (OFF_T_FMT_TYPE)0;
-           if(ch == '\0'  || sscanf(s - 1, OFF_T_FMT, &degr_nsize_) != 1) {
-               error("schedule line %d: syntax error (bad degr nsize)", line);
+           degr_nsize_ = (off_t)0;
+           if(ch == '\0'  || sscanf(s - 1, "%lld", &degr_nsize_) != 1) {
+               error(_("schedule line %d: syntax error (bad degr nsize)"), line);
                /*NOTREACHED*/
            }
            degr_nsize = (off_t)degr_nsize_;
            skip_integer(s, ch);
 
            skip_whitespace(s, ch);             /* find the degr compressed size */
-           degr_csize_ = (OFF_T_FMT_TYPE)0;
-           if(ch == '\0'  || sscanf(s - 1, OFF_T_FMT, &degr_csize_) != 1) {
-               error("schedule line %d: syntax error (bad degr csize)", line);
+           degr_csize_ = (off_t)0;
+           if(ch == '\0'  || sscanf(s - 1, "%lld", &degr_csize_) != 1) {
+               error(_("schedule line %d: syntax error (bad degr csize)"), line);
                /*NOTREACHED*/
            }
            degr_csize = (off_t)degr_csize_;
            skip_integer(s, ch);
 
            skip_whitespace(s, ch);             /* find the degr time number */
-           if(ch == '\0' || sscanf(s - 1, TIME_T_FMT,
-                               (TIME_T_FMT_TYPE *)degr_time_p) != 1) {
-               error("schedule line %d: syntax error (bad degr estimated time)", line);
+           if(ch == '\0' || sscanf(s - 1, "%lld", &time_) != 1) {
+               error(_("schedule line %d: syntax error (bad degr estimated time)"), line);
                /*NOTREACHED*/
            }
+           *degr_time_p = (time_t)time_;
            skip_integer(s, ch);
 
            skip_whitespace(s, ch);             /* find the degr kps number */
            if(ch == '\0' || sscanf(s - 1, "%lu", &degr_kps) != 1) {
-               error("schedule line %d: syntax error (bad degr kps)", line);
+               error(_("schedule line %d: syntax error (bad degr kps)"), line);
                /*NOTREACHED*/
            }
            skip_integer(s, ch);
        } else {
-           degr_level = -1;
-           degr_nsize = (off_t)0;
-           degr_csize = (off_t)0;
-           degr_time = (time_t)0;
-           degr_kps = 0;
+           error(_("schedule line %d: no degraded estimate or message"), line);
        }
 
        dp = lookup_disk(hostname, diskname);
        if(dp == NULL) {
            log_add(L_WARNING,
-                   "schedule line %d: %s:'%s' not in disklist, ignored",
+                   _("schedule line %d: %s:'%s' not in disklist, ignored"),
                    line, hostname, qname);
            amfree(diskname);
            continue;
@@ -2189,17 +3074,21 @@ read_schedule(
            sp->degr_csize = am_round(sp->degr_csize, DISK_BLOCK_KB);
            sp->degr_time = degr_time;
            sp->degr_kps = degr_kps;
+           sp->degr_mesg = NULL;
        } else {
            sp->degr_level = -1;
            sp->degr_dumpdate = NULL;
+           sp->degr_mesg = degr_mesg;
        }
        /*@end@*/
 
-       sp->attempted = 0;
-       sp->act_size = (off_t)0;
+       sp->dump_attempted = 0;
+       sp->taper_attempted = 0;
+       sp->act_size = 0;
        sp->holdp = NULL;
        sp->activehd = -1;
        sp->dumper = NULL;
+       sp->taper = NULL;
        sp->timestamp = (time_t)0;
        sp->destname = NULL;
        sp->no_space = 0;
@@ -2207,32 +3096,63 @@ read_schedule(
        dp->up = (char *) sp;
        if(dp->host->features == NULL) {
            dp->host->features = am_string_to_feature(features);
+           if (!dp->host->features) {
+               log_add(L_WARNING,
+                   _("Invalid feature string from client '%s'"),
+                   features);
+               dp->host->features = am_set_default_feature_set();
+           }
        }
        remove_disk(&waitq, dp);
-       enqueue_disk(&runq, dp);
-       flush_size += sp->act_size;
+
+       errarray = validate_optionstr(dp);
+       if (errarray->len > 0) {
+           guint i;
+           for (i=0; i < errarray->len; i++) {
+               log_add(L_FAIL, _("%s %s %s 0 [%s]"),
+                       dp->host->hostname, qname,
+                       sp->datestamp,
+                       (char *)g_ptr_array_index(errarray, i));
+           }
+           amfree(qname);
+       } else {
+
+           if (dp->data_path == DATA_PATH_DIRECTTCP &&
+               dp->to_holdingdisk == HOLD_AUTO) {
+               dp->to_holdingdisk = HOLD_NEVER;
+           }
+
+           if (dp->to_holdingdisk == HOLD_NEVER) {
+               enqueue_disk(&directq, dp);
+           } else {
+               enqueue_disk(&runq, dp);
+           }
+           flush_size += sp->act_size;
+       }
        amfree(diskname);
     }
-    printf("driver: flush size " OFF_T_FMT "\n", (OFF_T_FMT_TYPE)flush_size);
+    g_printf(_("driver: flush size %lld\n"), (long long)flush_size);
     amfree(inpline);
     if(line == 0)
-       log_add(L_WARNING, "WARNING: got empty schedule from planner");
+       log_add(L_WARNING, _("WARNING: got empty schedule from planner"));
     if(need_degraded==1) start_degraded_mode(&runq);
+    schedule_done = 1;
     start_some_dumps(&runq);
+    startaflush();
 }
 
 static unsigned long
 free_kps(
-    interface_t *ip)
+    netif_t *ip)
 {
     unsigned long res;
 
-    if (ip == (interface_t *)0) {
-       interface_t *p;
+    if (ip == NULL) {
+       netif_t *p;
        unsigned long maxusage=0;
        unsigned long curusage=0;
-       for(p = lookup_interface(NULL); p != NULL; p = p->next) {
-           maxusage += interface_get_maxusage(p);
+       for(p = disklist_netifs(); p != NULL; p = p->next) {
+           maxusage += interface_get_maxusage(p->config);
            curusage += p->curusage;
        }
        if (maxusage >= curusage)
@@ -2241,8 +3161,8 @@ free_kps(
            res = 0;
 #ifndef __lint
     } else {
-       if ((unsigned long)interface_get_maxusage(ip) >= ip->curusage)
-           res = interface_get_maxusage(ip) - ip->curusage;
+       if ((unsigned long)interface_get_maxusage(ip->config) >= ip->curusage)
+           res = interface_get_maxusage(ip->config) - ip->curusage;
        else
            res = 0;
 #endif
@@ -2255,19 +3175,19 @@ static void
 interface_state(
     char *time_str)
 {
-    interface_t *ip;
+    netif_t *ip;
 
-    printf("driver: interface-state time %s", time_str);
+    g_printf(_("driver: interface-state time %s"), time_str);
 
-    for(ip = lookup_interface(NULL); ip != NULL; ip = ip->next) {
-       printf(" if %s: free %lu", ip->name, free_kps(ip));
+    for(ip = disklist_netifs(); ip != NULL; ip = ip->next) {
+       g_printf(_(" if %s: free %lu"), interface_name(ip->config), free_kps(ip));
     }
-    printf("\n");
+    g_printf("\n");
 }
 
 static void
 allocate_bandwidth(
-    interface_t *      ip,
+    netif_t *          ip,
     unsigned long      kps)
 {
     ip->curusage += kps;
@@ -2275,7 +3195,7 @@ allocate_bandwidth(
 
 static void
 deallocate_bandwidth(
-    interface_t *      ip,
+    netif_t *          ip,
     unsigned long      kps)
 {
     assert(kps <= ip->curusage);
@@ -2286,13 +3206,13 @@ deallocate_bandwidth(
 static off_t
 free_space(void)
 {
-    holdingdisk_t *hdp;
+    holdalloc_t *ha;
     off_t total_free;
     off_t diff;
 
     total_free = (off_t)0;
-    for(hdp = getconf_holdingdisks(); hdp != NULL; hdp = hdp->next) {
-       diff = hdp->disksize - holdalloc(hdp)->allocated_space;
+    for(ha = holdalloc; ha != NULL; ha = ha->next) {
+       diff = ha->disksize - ha->allocated_space;
        if(diff > (off_t)0)
            total_free += diff;
     }
@@ -2315,8 +3235,8 @@ find_diskspace(
     assignedhd_t *     pref)
 {
     assignedhd_t **result = NULL;
-    holdingdisk_t *minp, *hdp;
-    int i=0, num_holdingdisks=0; /* are we allowed to use the global thing? */
+    holdalloc_t *ha, *minp;
+    int i=0;
     int j, minj;
     char *used;
     off_t halloc, dalloc, hfree, dfree;
@@ -2327,38 +3247,33 @@ find_diskspace(
        size = 2*DISK_BLOCK_KB;
     size = am_round(size, (off_t)DISK_BLOCK_KB);
 
-    hold_debug(1, ("%s: want " OFF_T_FMT " K\n",
-                  debug_prefix_time(": find_diskspace"),
-                  (OFF_T_FMT_TYPE)size));
-
-    for(hdp = getconf_holdingdisks(); hdp != NULL; hdp = hdp->next) {
-       num_holdingdisks++;
-    }
+    hold_debug(1, _("find_diskspace: want %lld K\n"),
+                  (long long)size);
 
-    used = alloc(SIZEOF(*used) * num_holdingdisks);/*disks used during this run*/
-    memset( used, 0, (size_t)num_holdingdisks );
-    result = alloc(SIZEOF(assignedhd_t *) * (num_holdingdisks + 1));
+    used = alloc(SIZEOF(*used) * num_holdalloc);/*disks used during this run*/
+    memset( used, 0, (size_t)num_holdalloc );
+    result = alloc(SIZEOF(assignedhd_t *) * (num_holdalloc + 1));
     result[0] = NULL;
 
-    while( i < num_holdingdisks && size > (off_t)0 ) {
+    while( i < num_holdalloc && size > (off_t)0 ) {
        /* find the holdingdisk with the fewest active dumpers and among
         * those the one with the biggest free space
         */
        minp = NULL; minj = -1;
-       for(j = 0, hdp = getconf_holdingdisks(); hdp != NULL; hdp = hdp->next, j++ ) {
-           if( pref && pref->disk == hdp && !used[j] &&
-               holdalloc(hdp)->allocated_space <= hdp->disksize - (off_t)DISK_BLOCK_KB) {
-               minp = hdp;
+       for(j = 0, ha = holdalloc; ha != NULL; ha = ha->next, j++ ) {
+           if( pref && pref->disk == ha && !used[j] &&
+               ha->allocated_space <= ha->disksize - (off_t)DISK_BLOCK_KB) {
+               minp = ha;
                minj = j;
                break;
            }
-           else if( holdalloc(hdp)->allocated_space <= hdp->disksize - (off_t)(2*DISK_BLOCK_KB) &&
+           else if( ha->allocated_space <= ha->disksize - (off_t)(2*DISK_BLOCK_KB) &&
                !used[j] &&
                (!minp ||
-                holdalloc(hdp)->allocated_dumpers < holdalloc(minp)->allocated_dumpers ||
-                (holdalloc(hdp)->allocated_dumpers == holdalloc(minp)->allocated_dumpers &&
-                 hdp->disksize-holdalloc(hdp)->allocated_space > minp->disksize-holdalloc(minp)->allocated_space)) ) {
-               minp = hdp;
+                ha->allocated_dumpers < minp->allocated_dumpers ||
+                (ha->allocated_dumpers == minp->allocated_dumpers &&
+                 ha->disksize-ha->allocated_space > minp->disksize-minp->allocated_space)) ) {
+               minp = ha;
                minj = j;
            }
        }
@@ -2368,25 +3283,23 @@ find_diskspace(
        used[minj] = 1;
 
        /* hfree = free space on the disk */
-       hfree = minp->disksize - holdalloc(minp)->allocated_space;
+       hfree = minp->disksize - minp->allocated_space;
 
        /* dfree = free space for data, remove 1 header for each chunksize */
-       dfree = hfree - (((hfree-(off_t)1)/holdingdisk_get_chunksize(minp))+(off_t)1) * (off_t)DISK_BLOCK_KB;
+       dfree = hfree - (((hfree-(off_t)1)/holdingdisk_get_chunksize(minp->hdisk))+(off_t)1) * (off_t)DISK_BLOCK_KB;
 
        /* dalloc = space I can allocate for data */
        dalloc = ( dfree < size ) ? dfree : size;
 
        /* halloc = space to allocate, including 1 header for each chunksize */
-       halloc = dalloc + (((dalloc-(off_t)1)/holdingdisk_get_chunksize(minp))+(off_t)1) * (off_t)DISK_BLOCK_KB;
-
-       hold_debug(1, ("%s: find diskspace: size " OFF_T_FMT " hf " OFF_T_FMT
-                      " df " OFF_T_FMT " da " OFF_T_FMT " ha " OFF_T_FMT"\n",
-                      debug_prefix_time(": find_diskspace"),
-                      (OFF_T_FMT_TYPE)size,
-                      (OFF_T_FMT_TYPE)hfree,
-                      (OFF_T_FMT_TYPE)dfree,
-                      (OFF_T_FMT_TYPE)dalloc,
-                      (OFF_T_FMT_TYPE)halloc));
+       halloc = dalloc + (((dalloc-(off_t)1)/holdingdisk_get_chunksize(minp->hdisk))+(off_t)1) * (off_t)DISK_BLOCK_KB;
+
+       hold_debug(1, _("find_diskspace: find diskspace: size %lld hf %lld df %lld da %lld ha %lld\n"),
+                      (long long)size,
+                      (long long)hfree,
+                      (long long)dfree,
+                      (long long)dalloc,
+                      (long long)halloc);
        size -= dalloc;
        result[i] = alloc(SIZEOF(assignedhd_t));
        result[i]->disk = minp;
@@ -2395,12 +3308,11 @@ find_diskspace(
        result[i]->destname = NULL;
        result[i+1] = NULL;
        i++;
-    } /* while i < num_holdingdisks && size > 0 */
+    }
     amfree(used);
 
     if(size != (off_t)0) { /* not enough space available */
-       printf("find diskspace: not enough diskspace. Left with "
-              OFF_T_FMT " K\n", (OFF_T_FMT_TYPE)size);
+       g_printf(_("find diskspace: not enough diskspace. Left with %lld K\n"), (long long)size);
        fflush(stdout);
        free_assignedhd(result);
        result = NULL;
@@ -2408,14 +3320,12 @@ find_diskspace(
 
     if (debug_holding > 1) {
        for( i = 0; result && result[i]; i++ ) {
-           hold_debug(1, ("%s: find diskspace: selected %s free " OFF_T_FMT
-                          " reserved " OFF_T_FMT " dumpers %d\n",
-                          debug_prefix_time(": find_diskspace"),
-                          holdingdisk_get_diskdir(result[i]->disk),
-                          (OFF_T_FMT_TYPE)(result[i]->disk->disksize -
-                            holdalloc(result[i]->disk)->allocated_space),
-                          (OFF_T_FMT_TYPE)result[i]->reserved,
-                          holdalloc(result[i]->disk)->allocated_dumpers));
+           hold_debug(1, _("find_diskspace: find diskspace: selected %s free %lld reserved %lld dumpers %d\n"),
+                          holdingdisk_get_diskdir(result[i]->disk->hdisk),
+                          (long long)(result[i]->disk->disksize -
+                            result[i]->disk->allocated_space),
+                          (long long)result[i]->reserved,
+                          result[i]->disk->allocated_dumpers);
        }
     }
 
@@ -2434,7 +3344,7 @@ assign_holdingdisk(
     assignedhd_t **new_holdp;
     char *qname;
 
-    snprintf( lvl, SIZEOF(lvl), "%d", sched(diskp)->level );
+    g_snprintf( lvl, SIZEOF(lvl), "%d", sched(diskp)->level );
 
     size = am_round(sched(diskp)->est_size - sched(diskp)->act_size,
                    (off_t)DISK_BLOCK_KB);
@@ -2459,19 +3369,16 @@ assign_holdingdisk(
        l=j;
        if( sched(diskp)->holdp[j-1]->disk == holdp[0]->disk ) { /* Yes! */
            sched(diskp)->holdp[j-1]->reserved += holdp[0]->reserved;
-           holdalloc(holdp[0]->disk)->allocated_space += holdp[0]->reserved;
+           holdp[0]->disk->allocated_space += holdp[0]->reserved;
            size = (holdp[0]->reserved>size) ? (off_t)0 : size-holdp[0]->reserved;
            qname = quote_string(diskp->name);
-           hold_debug(1, ("%s: merging holding disk %s to disk %s:%s, add "
-                          OFF_T_FMT " for reserved " OFF_T_FMT ", left "
-                          OFF_T_FMT "\n",
-                          debug_prefix_time(": assign_holdingdisk"),
+           hold_debug(1, _("assign_holdingdisk: merging holding disk %s to disk %s:%s, add %lld for reserved %lld, left %lld\n"),
                           holdingdisk_get_diskdir(
-                                              sched(diskp)->holdp[j-1]->disk),
+                                              sched(diskp)->holdp[j-1]->disk->hdisk),
                           diskp->host->hostname, qname,
-                          (OFF_T_FMT_TYPE)holdp[0]->reserved,
-                          (OFF_T_FMT_TYPE)sched(diskp)->holdp[j-1]->reserved,
-                          (OFF_T_FMT_TYPE)size));
+                          (long long)holdp[0]->reserved,
+                          (long long)sched(diskp)->holdp[j-1]->reserved,
+                          (long long)size);
            i++;
            amfree(qname);
            amfree(holdp[0]);
@@ -2482,24 +3389,22 @@ assign_holdingdisk(
     /* copy assignedhd_s to sched(diskp), adjust allocated_space */
     for( ; holdp[i]; i++ ) {
        holdp[i]->destname = newvstralloc( holdp[i]->destname,
-                                          holdingdisk_get_diskdir(holdp[i]->disk), "/",
+                                          holdingdisk_get_diskdir(holdp[i]->disk->hdisk), "/",
                                           hd_driver_timestamp, "/",
                                           diskp->host->hostname, ".",
                                           sfn, ".",
                                           lvl, NULL );
        sched(diskp)->holdp[j++] = holdp[i];
-       holdalloc(holdp[i]->disk)->allocated_space += holdp[i]->reserved;
+       holdp[i]->disk->allocated_space += holdp[i]->reserved;
        size = (holdp[i]->reserved > size) ? (off_t)0 :
                  (size - holdp[i]->reserved);
        qname = quote_string(diskp->name);
        hold_debug(1,
-                  ("%s: %d assigning holding disk %s to disk %s:%s, reserved "
-                    OFF_T_FMT ", left " OFF_T_FMT "\n",
-                   debug_prefix_time(": assign_holdingdisk"),
-                   i, holdingdisk_get_diskdir(holdp[i]->disk),
+                  _("assign_holdingdisk: %d assigning holding disk %s to disk %s:%s, reserved %lld, left %lld\n"),
+                   i, holdingdisk_get_diskdir(holdp[i]->disk->hdisk),
                    diskp->host->hostname, qname,
-                   (OFF_T_FMT_TYPE)holdp[i]->reserved,
-                   (OFF_T_FMT_TYPE)size));
+                   (long long)holdp[i]->reserved,
+                   (long long)size);
        amfree(qname);
        holdp[i] = NULL; /* so it doesn't get free()d... */
     }
@@ -2524,9 +3429,8 @@ adjust_diskspace(
 
     qname = quote_string(diskp->name);
     qdest = quote_string(sched(diskp)->destname);
-    hold_debug(1, ("%s: %s:%s %s\n",
-                  debug_prefix_time(": adjust_diskspace"),
-                  diskp->host->hostname, qname, qdest));
+    hold_debug(1, _("adjust_diskspace: %s:%s %s\n"),
+                  diskp->host->hostname, qname, qdest);
 
     holdp = sched(diskp)->holdp;
 
@@ -2535,29 +3439,24 @@ adjust_diskspace(
     for( i = 0; holdp[i]; i++ ) { /* for each allocated disk */
        diff = holdp[i]->used - holdp[i]->reserved;
        total += holdp[i]->used;
-       holdalloc(holdp[i]->disk)->allocated_space += diff;
-       hqname = quote_string(holdp[i]->disk->name);
-       hold_debug(1, ("%s: hdisk %s done, reserved " OFF_T_FMT " used "
-                      OFF_T_FMT " diff " OFF_T_FMT " alloc " OFF_T_FMT
-                      " dumpers %d\n",
-                      debug_prefix_time(": adjust_diskspace"),
-                      holdp[i]->disk->name,
-                      (OFF_T_FMT_TYPE)holdp[i]->reserved,
-                      (OFF_T_FMT_TYPE)holdp[i]->used,
-                      (OFF_T_FMT_TYPE)diff,
-                      (OFF_T_FMT_TYPE)holdalloc(holdp[i]->disk)
-                                                            ->allocated_space,
-                      holdalloc(holdp[i]->disk)->allocated_dumpers ));
+       holdp[i]->disk->allocated_space += diff;
+       hqname = quote_string(holdingdisk_name(holdp[i]->disk->hdisk));
+       hold_debug(1, _("adjust_diskspace: hdisk %s done, reserved %lld used %lld diff %lld alloc %lld dumpers %d\n"),
+                      holdingdisk_name(holdp[i]->disk->hdisk),
+                      (long long)holdp[i]->reserved,
+                      (long long)holdp[i]->used,
+                      (long long)diff,
+                      (long long)holdp[i]->disk->allocated_space,
+                      holdp[i]->disk->allocated_dumpers );
        holdp[i]->reserved += diff;
        amfree(hqname);
     }
 
     sched(diskp)->act_size = total;
 
-    hold_debug(1, ("%s: after: disk %s:%s used " OFF_T_FMT "\n",
-                  debug_prefix_time(": adjust_diskspace"),
+    hold_debug(1, _("adjust_diskspace: after: disk %s:%s used %lld\n"),
                   diskp->host->hostname, qname,
-                  (OFF_T_FMT_TYPE)sched(diskp)->act_size));
+                  (long long)sched(diskp)->act_size);
     amfree(qdest);
     amfree(qname);
 }
@@ -2577,7 +3476,7 @@ delete_diskspace(
        /* find all files of this dump on that disk, and subtract their
         * reserved sizes from the disk's allocated space
         */
-       holdalloc(holdp[i]->disk)->allocated_space -= holdp[i]->used;
+       holdp[i]->disk->allocated_space -= holdp[i]->used;
     }
 
     holding_file_unlink(holdp[0]->destname);   /* no need for the entire list,
@@ -2595,63 +3494,70 @@ build_diskspace(
 {
     int i, j;
     int fd;
-    ssize_t buflen;
+    size_t buflen;
     char buffer[DISK_BLOCK_BYTES];
     dumpfile_t file;
     assignedhd_t **result;
-    holdingdisk_t *hdp;
+    holdalloc_t *ha;
     off_t *used;
-    int num_holdingdisks=0;
     char dirname[1000], *ch;
     struct stat finfo;
     char *filename = destname;
 
     memset(buffer, 0, sizeof(buffer));
-    for(hdp = getconf_holdingdisks(); hdp != NULL; hdp = hdp->next) {
-        num_holdingdisks++;
-    }
-    used = alloc(SIZEOF(off_t) * num_holdingdisks);
-    for(i=0;i<num_holdingdisks;i++)
+    used = alloc(SIZEOF(off_t) * num_holdalloc);
+    for(i=0;i<num_holdalloc;i++)
        used[i] = (off_t)0;
-    result = alloc(SIZEOF(assignedhd_t *) * (num_holdingdisks + 1));
+    result = alloc(SIZEOF(assignedhd_t *) * (num_holdalloc + 1));
     result[0] = NULL;
     while(filename != NULL && filename[0] != '\0') {
        strncpy(dirname, filename, 999);
        dirname[999]='\0';
        ch = strrchr(dirname,'/');
-        *ch = '\0';
-       ch = strrchr(dirname,'/');
-        *ch = '\0';
+       if (ch) {
+           *ch = '\0';
+           ch = strrchr(dirname,'/');
+           if (ch) {
+               *ch = '\0';
+           }
+       }
+
+       if (!ch) {
+           g_fprintf(stderr,_("build_diskspace: bogus filename '%s'\n"), filename);
+           amfree(used);
+           amfree(result);
+           return NULL;
+       }
 
-       for(j = 0, hdp = getconf_holdingdisks(); hdp != NULL;
-                                                hdp = hdp->next, j++ ) {
-           if(strcmp(dirname, holdingdisk_get_diskdir(hdp))==0) {
+       for(j = 0, ha = holdalloc; ha != NULL; ha = ha->next, j++ ) {
+           if(strcmp(dirname, holdingdisk_get_diskdir(ha->hdisk))==0) {
                break;
            }
        }
 
        if(stat(filename, &finfo) == -1) {
-           fprintf(stderr, "stat %s: %s\n", filename, strerror(errno));
+           g_fprintf(stderr, _("stat %s: %s\n"), filename, strerror(errno));
            finfo.st_size = (off_t)0;
        }
        used[j] += ((off_t)finfo.st_size+(off_t)1023)/(off_t)1024;
        if((fd = open(filename,O_RDONLY)) == -1) {
-           fprintf(stderr,"build_diskspace: open of %s failed: %s\n",
+           g_fprintf(stderr,_("build_diskspace: open of %s failed: %s\n"),
                    filename, strerror(errno));
+           amfree(used);
+           amfree(result);
            return NULL;
        }
-       if ((buflen = fullread(fd, buffer, SIZEOF(buffer))) > 0) {;
-               parse_file_header(buffer, &file, (size_t)buflen);
+       if ((buflen = full_read(fd, buffer, SIZEOF(buffer))) > 0) {;
+               parse_file_header(buffer, &file, buflen);
        }
        close(fd);
        filename = file.cont_filename;
     }
 
-    for(j = 0, i=0, hdp = getconf_holdingdisks(); hdp != NULL;
-                                                 hdp = hdp->next, j++ ) {
+    for(j = 0, i=0, ha = holdalloc; ha != NULL; ha = ha->next, j++ ) {
        if(used[j] != (off_t)0) {
            result[i] = alloc(SIZEOF(assignedhd_t));
-           result[i]->disk = hdp;
+           result[i]->disk = ha;
            result[i]->reserved = used[j];
            result[i]->used = used[j];
            result[i]->destname = stralloc(destname);
@@ -2668,28 +3574,24 @@ static void
 holdingdisk_state(
     char *     time_str)
 {
-    holdingdisk_t *hdp;
+    holdalloc_t *ha;
     int dsk;
     off_t diff;
 
-    printf("driver: hdisk-state time %s", time_str);
+    g_printf(_("driver: hdisk-state time %s"), time_str);
 
-    for(hdp = getconf_holdingdisks(), dsk = 0; hdp != NULL; hdp = hdp->next, dsk++) {
-       diff = hdp->disksize - holdalloc(hdp)->allocated_space;
-       printf(" hdisk %d: free " OFF_T_FMT " dumpers %d", dsk,
-              (OFF_T_FMT_TYPE)diff, holdalloc(hdp)->allocated_dumpers);
+    for(ha = holdalloc, dsk = 0; ha != NULL; ha = ha->next, dsk++) {
+       diff = ha->disksize - ha->allocated_space;
+       g_printf(_(" hdisk %d: free %lld dumpers %d"), dsk,
+              (long long)diff, ha->allocated_dumpers);
     }
-    printf("\n");
+    g_printf("\n");
 }
 
 static void
-update_failed_dump_to_tape(
+update_failed_dump(
     disk_t *   dp)
 {
-/* JLM
- * should simply set no_bump
- */
-
     time_t save_timestamp = sched(dp)->timestamp;
     /* setting timestamp to 0 removes the current level from the
      * database, so that we ensure that it will not be bumped to the
@@ -2702,257 +3604,242 @@ update_failed_dump_to_tape(
 }
 
 /* ------------------- */
+
 static int
-dump_to_tape(
-    disk_t *   dp)
+queue_length(
+    disklist_t q)
 {
-    dumper_t *dumper;
-    int failed = 0;
-    off_t filenum;
-    off_t origsize = (off_t)0;
-    off_t dumpsize = (off_t)0;
-    time_t dumptime = (time_t)0;
-    double tapetime = 0.0;
-    cmd_t cmd;
-    int result_argc, rc;
-    char *result_argv[MAX_ARGS+1];
-    int dumper_tryagain = 0;
-    char *qname;
-
-    qname = quote_string(dp->name);
-    printf("driver: dumping %s:%s directly to tape\n",
-          dp->host->hostname, qname);
-    fflush(stdout);
+    disk_t *p;
+    int len;
 
-    /* pick a dumper and fail if there are no idle dumpers */
+    for(len = 0, p = q.head; p != NULL; len++, p = p->next)
+       (void)len;      /* Quiet lint */
+    return len;
+}
 
-    dumper = idle_dumper();
-    if (!dumper) {
-       printf("driver: no idle dumpers for %s:%s.\n", 
-               dp->host->hostname, qname);
-       fflush(stdout);
-       log_add(L_WARNING, "no idle dumpers for %s:%s.\n",
-               dp->host->hostname, qname);
-        amfree(qname);
-       return 2;       /* fatal problem */
-    }
+static void
+short_dump_state(void)
+{
+    int i, nidle;
+    char *wall_time;
 
-    /* tell the taper to read from a port number of its choice */
+    wall_time = walltime_str(curclock());
 
-    taper_cmd(PORT_WRITE, dp, NULL, sched(dp)->level, sched(dp)->datestamp);
-    cmd = getresult(taper, 1, &result_argc, result_argv, MAX_ARGS+1);
-    if(cmd != PORT) {
-       printf("driver: did not get PORT from taper for %s:%s\n",
-               dp->host->hostname, qname);
-       fflush(stdout);
-        amfree(qname);
-       return 2;       /* fatal problem */
+    g_printf(_("driver: state time %s "), wall_time);
+    g_printf(_("free kps: %lu space: %lld taper: "),
+          free_kps(NULL),
+          (long long)free_space());
+    if(degraded_mode) g_printf(_("DOWN"));
+    else {
+       taper_t *taper;
+       int writing = 0;
+       for(taper = tapetable; taper < tapetable+conf_taper_parallel_write;
+                              taper++) {
+           if (taper->state & TAPER_STATE_DUMP_TO_TAPE ||
+               taper->state & TAPER_STATE_FILE_TO_TAPE)
+               writing = 1;
+       }
+       if(writing)
+           g_printf(_("writing"));
+       else
+           g_printf(_("idle"));
     }
-    /* copy port number */
-    dumper->output_port = atoi(result_argv[2]);
+    nidle = 0;
+    for(i = 0; i < inparallel; i++) if(!dmptable[i].busy) nidle++;
+    g_printf(_(" idle-dumpers: %d"), nidle);
+    g_printf(_(" qlen tapeq: %d"), queue_length(tapeq));
+    g_printf(_(" runq: %d"), queue_length(runq));
+    g_printf(_(" roomq: %d"), queue_length(roomq));
+    g_printf(_(" wakeup: %d"), (int)sleep_time);
+    g_printf(_(" driver-idle: %s\n"), _(idle_strings[idle_reason]));
+    interface_state(wall_time);
+    holdingdisk_state(wall_time);
+    fflush(stdout);
+}
 
-    /* tell the dumper to dump to a port */
+static TapeAction
+tape_action(
+    taper_t  *taper,
+    char    **why_no_new_tape)
+{
+    TapeAction result = TAPE_ACTION_NO_ACTION;
+    dumper_t *dumper;
+    taper_t  *taper1;
+    disk_t   *dp;
+    off_t dumpers_size;
+    off_t runq_size;
+    off_t directq_size;
+    off_t tapeq_size;
+    off_t sched_size;
+    off_t dump_to_disk_size;
+    int   dump_to_disk_terminated;
+    off_t my_flush_threshold_dumped;
+    off_t my_flush_threshold_scheduled;
+    off_t my_taperflush;
+    int   nb_taper_active = nb_sent_new_tape;
+    int   nb_taper_flushing = 0;
+
+    dumpers_size = 0;
+    for(dumper = dmptable; dumper < (dmptable+inparallel); dumper++) {
+       if (dumper->busy && !sched(dumper->dp)->taper)
+           dumpers_size += sched(dumper->dp)->est_size;
+    }
+    driver_debug(1, _("dumpers_size: %lld\n"), (long long)dumpers_size);
 
-    dumper_cmd(dumper, PORT_DUMP, dp);
-    dp->host->start_t = time(NULL) + 15;
+    runq_size = 0;
+    for(dp = runq.head; dp != NULL; dp = dp->next) {
+       runq_size += sched(dp)->est_size;
+    }
+    driver_debug(1, _("runq_size: %lld\n"), (long long)runq_size);
 
-    /* update statistics & print state */
+    directq_size = 0;
+    for(dp = directq.head; dp != NULL; dp = dp->next) {
+       directq_size += sched(dp)->est_size;
+    }
+    driver_debug(1, _("directq_size: %lld\n"), (long long)directq_size);
 
-    taper_busy = dumper->busy = 1;
-    dp->host->inprogress += 1;
-    dp->inprogress = 1;
-    sched(dp)->timestamp = time((time_t *)0);
-    allocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
-    idle_reason = NOT_IDLE;
+    tapeq_size = directq_size;
+    for(dp = tapeq.head; dp != NULL; dp = dp->next) {
+       tapeq_size += sched(dp)->act_size;
+    }
 
-    short_dump_state();
+    for (taper1 = tapetable; taper1 < tapetable+conf_taper_parallel_write;
+        taper1++) {
+       if (taper1->state & TAPER_STATE_FILE_TO_TAPE ||
+           taper1->state & TAPER_STATE_DUMP_TO_TAPE) {
+           nb_taper_flushing++;
+       }
+    }
 
-    /* wait for result from dumper */
-
-    cmd = getresult(dumper->fd, 1, &result_argc, result_argv, MAX_ARGS+1);
-
-    switch(cmd) {
-    case BOGUS:
-       /* either eof or garbage from dumper */
-       log_add(L_WARNING, "%s pid %ld is messed up, ignoring it.\n",
-               dumper->name, (long)dumper->pid);
-       dumper->down = 1;       /* mark it down so it isn't used again */
-       failed = 1;     /* dump failed, must still finish up with taper */
-       break;
-
-    case DONE: /* DONE <handle> <origsize> <dumpsize> <dumptime> <errstr> */
-       /* everything went fine */
-       origsize = (off_t)atof(result_argv[3]);
-       /*dumpsize = (off_t)atof(result_argv[4]);*/
-       dumptime = (time_t)atof(result_argv[5]);
-       break;
-
-    case NO_ROOM: /* NO-ROOM <handle> */
-       dumper_cmd(dumper, ABORT, dp);
-       cmd = getresult(dumper->fd, 1, &result_argc, result_argv, MAX_ARGS+1);
-       assert(cmd == ABORT_FINISHED);
-
-    case TRYAGAIN: /* TRY-AGAIN <handle> <errstr> */
-    default:
-       /* dump failed, but we must still finish up with taper */
-       /* problem with dump, possibly nonfatal, retry one time */
-       sched(dp)->attempted++;
-       failed = sched(dp)->attempted;
-       dumper_tryagain = 1;
-       break;
-       
-    case FAILED: /* FAILED <handle> <errstr> */
-       /* dump failed, but we must still finish up with taper */
-       failed = 2;     /* fatal problem with dump */
-       break;
+    /* Add what is currently written to tape and in the go. */
+    for (taper1 = tapetable; taper1 < tapetable+conf_taper_parallel_write;
+        taper1++) {
+       if (taper1->state & TAPER_STATE_TAPE_STARTED) {
+           tapeq_size -= taper1->left;
+       }
+       if (taper1->disk) {
+           if (taper1->dumper) {
+               tapeq_size += sched(taper1->disk)->est_size - taper1->written;
+           } else {
+               tapeq_size += sched(taper1->disk)->act_size - taper1->written;
+           }
+       }
     }
+    driver_debug(1, _("tapeq_size: %lld\n"), (long long)tapeq_size);
 
-    /*
-     * Note that at this point, even if the dump above failed, it may
-     * not be a fatal failure if taper below says we can try again.
-     * E.g. a dumper failure above may actually be the result of a
-     * tape overflow, which in turn causes dump to see "broken pipe",
-     * "no space on device", etc., since taper closed the port first.
-     */
+    sched_size = runq_size + directq_size + tapeq_size + dumpers_size;
+    driver_debug(1, _("sched_size: %lld\n"), (long long)sched_size);
 
-    continue_port_dump:
+    dump_to_disk_size = dumpers_size + runq_size + directq_size;
+    driver_debug(1, _("dump_to_disk_size: %lld\n"), (long long)dump_to_disk_size);
 
-    cmd = getresult(taper, 1, &result_argc, result_argv, MAX_ARGS+1);
+    dump_to_disk_terminated = schedule_done && dump_to_disk_size == 0;
 
-    switch(cmd) {
-    case PARTIAL:
-    case DONE: /* DONE <handle> <label> <tape file> <err mess> */
-       if(result_argc != 5) {
-           error("error [dump to tape DONE result_argc != 5: %d]", result_argc);
-           /*NOTREACHED*/
+    for (taper1 = tapetable; taper1 < tapetable + conf_taper_parallel_write;
+                            taper1++) {
+       if (taper1->state & TAPER_STATE_TAPE_STARTED) {
+           nb_taper_active++;
        }
+    }
+    if (nb_taper_active >= 1) {
+    my_flush_threshold_dumped = flush_threshold_dumped +
+                               (nb_taper_active-nb_taper_active) * tape_length;
+    my_flush_threshold_scheduled = flush_threshold_scheduled +
+                                  (nb_taper_active-nb_taper_active) * tape_length;
+    my_taperflush = taperflush + (nb_taper_active-nb_taper_active) * tape_length;
+    } else {
+    my_flush_threshold_dumped = flush_threshold_dumped +
+                               nb_taper_active * tape_length;
+    my_flush_threshold_scheduled = flush_threshold_scheduled +
+                                  nb_taper_active * tape_length;
+    my_taperflush = taperflush + nb_taper_active * tape_length;
+    }
 
-       if(failed == 1) goto tryagain;  /* dump didn't work */
-       else if(failed == 2) goto failed_dumper;
-
-       free_serial(result_argv[2]);
-
-       dumpsize = (off_t)0;
-       if (*result_argv[5] == '"') {
-           /* String was quoted */
-           OFF_T_FMT_TYPE dumpsize_ = (OFF_T_FMT_TYPE)0;
-           rc = sscanf(result_argv[5],"\"[sec %lf kb " OFF_T_FMT " ",
-                       &tapetime, &dumpsize_);
-           dumpsize = (off_t)dumpsize_;
+    // Changing conditionals can produce a driver hang, take care.
+    // 
+    // when to start writting to a new tape
+    if (taper->state & TAPER_STATE_TAPE_REQUESTED) {
+       if (current_tape >= conf_runtapes && taper_nb_scan_volume == 0 &&
+           nb_taper_active == 0) {
+           *why_no_new_tape = g_strdup_printf(_("%d tapes filled; runtapes=%d "
+               "does not allow additional tapes"), current_tape, conf_runtapes);
+           result |= TAPE_ACTION_NO_NEW_TAPE;
+       } else if (current_tape < conf_runtapes &&
+                  taper_nb_scan_volume == 0 &&
+                  ((my_flush_threshold_dumped < tapeq_size &&
+                    my_flush_threshold_scheduled < sched_size) ||
+                   nb_taper_active == 0) &&
+                  (last_started_taper == NULL ||
+                   last_started_taper == taper)) {
+           result |= TAPE_ACTION_SCAN;
        } else {
-           /* String was not quoted */
-           OFF_T_FMT_TYPE dumpsize_ = (OFF_T_FMT_TYPE)0;
-           rc = sscanf(result_argv[5],"[sec %lf kb " OFF_T_FMT " ",
-                       &tapetime, &dumpsize_);
-           dumpsize = (off_t)dumpsize_;
-       }
-       if (rc < 2) {
-           error("error [malformed result: %d items matched in '%s']",
-                 rc, result_argv[5]);
-           /*NOTREACHED*/
+           result |= TAPE_ACTION_MOVE;
+       }
+    } else if ((taper->state & TAPER_STATE_WAIT_FOR_TAPE) &&
+        ((taper->state & TAPER_STATE_DUMP_TO_TAPE) ||  // for dump to tape
+        !empty(directq) ||                             // if a dle is waiting for a dump to tape
+         !empty(roomq) ||                              // holding disk constraint
+         idle_reason == IDLE_NO_DISKSPACE ||           // holding disk constraint
+         (my_flush_threshold_dumped < tapeq_size &&    // flush-threshold-dumped &&
+         my_flush_threshold_scheduled < sched_size) || //  flush-threshold-scheduled
+        (my_taperflush < tapeq_size &&                 // taperflush
+         (force_flush == 1 ||                          //  if force_flush
+          dump_to_disk_terminated))                    //  or all dump to disk terminated
+       )) {
+       result |= TAPE_ACTION_NEW_TAPE;
+    // when to stop using new tape
+    } else if ((taper->state & TAPER_STATE_WAIT_FOR_TAPE) &&
+              (my_taperflush >= tapeq_size &&          // taperflush criteria
+              (force_flush == 1 ||                     //  if force_flush
+               dump_to_disk_terminated))               //  or all dump to disk
+             ) {
+       if (nb_taper_active <= 0) {
+           if (current_tape >= conf_runtapes) {
+               *why_no_new_tape = g_strdup_printf(_("%d tapes filled; runtapes=%d "
+                     "does not allow additional tapes"), current_tape, conf_runtapes);
+           } else {
+               *why_no_new_tape = _("taperflush criteria not met");
+           }
+           result |= TAPE_ACTION_NO_NEW_TAPE;
        }
-
-       if(cmd == DONE) {
-           /* every thing went fine */
-           update_info_dumper(dp, origsize, dumpsize, dumptime);
-           filenum = OFF_T_ATOI(result_argv[4]);
-           update_info_taper(dp, result_argv[3], filenum, sched(dp)->level);
-           /* note that update_info_dumper() must be run before
-              update_info_taper(), since update_info_dumper overwrites
-              tape information.  */
-       }
-
-       break;
-
-    case TRYAGAIN: /* TRY-AGAIN <handle> <err mess> */
-       tape_left = tape_length;
-       current_tape++;
-       if(dumper_tryagain == 0) {
-           sched(dp)->attempted++;
-           if(sched(dp)->attempted > failed)
-               failed = sched(dp)->attempted;
-       }
-    tryagain:
-       if(failed <= 1)
-           headqueue_disk(&runq, dp);
-    failed_dumper:
-       update_failed_dump_to_tape(dp);
-       free_serial(result_argv[2]);
-       break;
-
-    case SPLIT_CONTINUE:  /* SPLIT_CONTINUE <handle> <new_label> */
-        if (result_argc != 3) {
-            error("error [taper SPLIT_CONTINUE result_argc != 3: %d]", result_argc);
-           /*NOTREACHED*/
-        }
-        fprintf(stderr, "driver: Got SPLIT_CONTINUE %s %s\n",
-               result_argv[2], result_argv[3]);
-        goto continue_port_dump;
-
-    case SPLIT_NEEDNEXT:
-        fprintf(stderr, "driver: Got SPLIT_NEEDNEXT %s %s\n", result_argv[2], result_argv[3]);
-
-        goto continue_port_dump;
-
-    case TAPE_ERROR: /* TAPE-ERROR <handle> <err mess> */
-    case BOGUS:
-    default:
-       update_failed_dump_to_tape(dp);
-       free_serial(result_argv[2]);
-       failed = 2;     /* fatal problem */
-       start_degraded_mode(&runq);
-       break;
     }
 
-    /* reset statistics & return */
-
-    taper_busy = dumper->busy = 0;
-    dp->host->inprogress -= 1;
-    dp->inprogress = 0;
-    deallocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
-    amfree(qname);
-
-    return failed;
+    // when to start a flush
+    // We don't start a flush if taper_tape_started == 1 && dump_to_disk_terminated && force_flush == 0,
+    // it is a criteria need to exit the first event_loop without flushing everything to tape,
+    // they will be flush in another event_loop.
+    if (taper->state & TAPER_STATE_IDLE) {
+       if (!degraded_mode && (!empty(tapeq) || !empty(directq)) &&
+           (((taper->state & TAPER_STATE_TAPE_STARTED) &&
+             force_flush == 1) ||                              // if tape already started and force_flush
+             !empty(roomq) ||                                  // holding disk constraint
+             idle_reason == IDLE_NO_DISKSPACE ||               // holding disk constraint
+             (my_flush_threshold_dumped < tapeq_size &&                // flush-threshold-dumped &&
+             my_flush_threshold_scheduled < sched_size) ||     //  flush-threshold-scheduled
+             (force_flush == 1 && my_taperflush < tapeq_size))) {      // taperflush if force_flush
+
+           if (nb_taper_flushing == 0) {
+               result |= TAPE_ACTION_START_A_FLUSH;
+           } else {
+               result |= TAPE_ACTION_START_A_FLUSH_FIT;
+           }
+       }
+    }
+    return result;
 }
 
 static int
-queue_length(
-    disklist_t q)
-{
-    disk_t *p;
-    int len;
-
-    for(len = 0, p = q.head; p != NULL; len++, p = p->next)
-       (void)len;      /* Quiet lint */
-    return len;
-}
-
-static void
-short_dump_state(void)
+all_taper_idle(void)
 {
-    int i, nidle;
-    char *wall_time;
+    taper_t *taper;
 
-    wall_time = walltime_str(curclock());
-
-    printf("driver: state time %s ", wall_time);
-    printf("free kps: %lu space: " OFF_T_FMT " taper: ",
-          free_kps((interface_t *)0),
-          (OFF_T_FMT_TYPE)free_space());
-    if(degraded_mode) printf("DOWN");
-    else if(!taper_busy) printf("idle");
-    else printf("writing");
-    nidle = 0;
-    for(i = 0; i < inparallel; i++) if(!dmptable[i].busy) nidle++;
-    printf(" idle-dumpers: %d", nidle);
-    printf(" qlen tapeq: %d", queue_length(tapeq));
-    printf(" runq: %d", queue_length(runq));
-    printf(" roomq: %d", queue_length(roomq));
-    printf(" wakeup: %d", (int)sleep_time);
-    printf(" driver-idle: %s\n", idle_strings[idle_reason]);
-    interface_state(wall_time);
-    holdingdisk_state(wall_time);
-    fflush(stdout);
+    for (taper = tapetable; taper < tapetable + conf_taper_parallel_write;
+        taper++) {
+       if (taper->state & TAPER_STATE_DUMP_TO_TAPE ||
+           taper->state & TAPER_STATE_FILE_TO_TAPE)
+           return 0;
+    }
+    return 1;
 }
 
 #if 0
@@ -2964,32 +3851,32 @@ dump_state(
     disk_t *dp;
     char *qname;
 
-    printf("================\n");
-    printf("driver state at time %s: %s\n", walltime_str(curclock()), str);
-    printf("free kps: %lu, space: " OFF_T_FMT "\n",
-          free_kps((interface_t *)0),
-          (OFF_T_FMT_TYPE)free_space());
-    if(degraded_mode) printf("taper: DOWN\n");
-    else if(!taper_busy) printf("taper: idle\n");
-    else printf("taper: writing %s:%s.%d est size " OFF_T_FMT "\n",
-               taper_disk->host->hostname, taper_disk->name,
-               sched(taper_disk)->level,
-               sched(taper_disk)->est_size);
+    g_printf("================\n");
+    g_printf(_("driver state at time %s: %s\n"), walltime_str(curclock()), str);
+    g_printf(_("free kps: %lu, space: %lld\n"),
+          free_kps(NULL),
+          (long long)free_space());
+    if(degraded_mode) g_printf(_("taper: DOWN\n"));
+    else if(taper->status == TAPER_IDLE) g_printf(_("taper: idle\n"));
+    else g_printf(_("taper: writing %s:%s.%d est size %lld\n"),
+               taper->disk->host->hostname, taper->disk->name,
+               sched(taper->disk)->level,
+               (long long)sched(taper->disk)->est_size);
     for(i = 0; i < inparallel; i++) {
        dp = dmptable[i].dp;
        if(!dmptable[i].busy)
-         printf("%s: idle\n", dmptable[i].name);
+         g_printf(_("%s: idle\n"), dmptable[i].name);
        else
          qname = quote_string(dp->name);
-         printf("%s: dumping %s:%s.%d est kps %d size " OFF_T_FMT " time %lu\n",
+         g_printf(_("%s: dumping %s:%s.%d est kps %d size %lld time %lu\n"),
                dmptable[i].name, dp->host->hostname, qname, sched(dp)->level,
-               sched(dp)->est_kps, sched(dp)->est_size, sched(dp)->est_time);
+               sched(dp)->est_kps, (long long)sched(dp)->est_size, sched(dp)->est_time);
           amfree(qname);
     }
     dump_queue("TAPE", tapeq, 5, stdout);
     dump_queue("ROOM", roomq, 5, stdout);
     dump_queue("RUN ", runq, 5, stdout);
-    printf("================\n");
+    g_printf("================\n");
     fflush(stdout);
 }
 #endif