lintian doesn't like orphan packages with uploaders...
[debian/amanda] / server-src / driver.c
index 9a0bb27bceba68fa9c80773cb6c6e0cbf4587458..38ebb331538a02842760ae6ae9a7016d3d695767 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
  * Copyright (c) 1991-1998 University of Maryland at College Park
+ * Copyright (c) 2007-2012 Zmanda, Inc.  All Rights Reserved.
  * All Rights Reserved.
  *
  * Permission to use, copy, modify, distribute, and sell this software and its
@@ -76,6 +77,7 @@ static int  inparallel;
 static int nodump = 0;
 static off_t tape_length = (off_t)0;
 static int current_tape = 0;
+static int conf_max_dle_by_volume;
 static int conf_taperalgo;
 static int conf_taper_parallel_write;
 static int conf_runtapes;
@@ -140,7 +142,8 @@ 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(disk_t *);
-static int all_taper_idle(void);
+static int no_taper_flushing(void);
+static int active_dumper(void);
 
 typedef enum {
     TAPE_ACTION_NO_ACTION         = 0,
@@ -186,7 +189,6 @@ main(
     identlist_t    il;
     unsigned long reserve = 100;
     char *conf_diskfile;
-    char **result_argv = NULL;
     char *taper_program;
     char *conf_tapetype;
     tapetype_t *tape;
@@ -199,6 +201,12 @@ main(
     find_result_t *holding_files;
     disklist_t holding_disklist = { NULL, NULL };
     int no_taper = FALSE;
+    int from_client = FALSE;
+
+    if (argc > 1 && argv && argv[1] && g_str_equal(argv[1], "--version")) {
+       printf("driver-%s\n", VERSION);
+       return (0);
+    }
 
     /*
      * Configure program for internationalization:
@@ -267,6 +275,15 @@ main(
        }
     }
 
+    if (argc > 2) {
+       if (strcmp(argv[2], "--from-client") == 0) {
+           from_client = TRUE;
+           from_client = from_client;
+           argv++;
+           argc--;
+       }
+    }
+
     safe_cd(); /* do this *after* config_init */
 
     check_running_as(RUNNING_AS_DUMPUSER);
@@ -326,6 +343,7 @@ main(
     conf_taper_parallel_write = getconf_int(CNF_TAPER_PARALLEL_WRITE);
     conf_tapetype = getconf_str(CNF_TAPETYPE);
     conf_runtapes = getconf_int(CNF_RUNTAPES);
+    conf_max_dle_by_volume = getconf_int(CNF_MAX_DLE_BY_VOLUME);
     if (conf_taper_parallel_write > conf_runtapes) {
        conf_taper_parallel_write = conf_runtapes;
     }
@@ -335,7 +353,6 @@ main(
     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);
-
     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;
@@ -499,31 +516,11 @@ main(
        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);
+    run_server_global_scripts(EXECUTE_ON_POST_BACKUP, get_config_name());
+
+    /* log error for any remaining dumps */
+    while(!empty(directq)) {
+       diskp = dequeue_disk(&directq);
 
        if (diskp->to_holdingdisk == HOLD_REQUIRED) {
            char *qname = quote_string(diskp->name);
@@ -538,7 +535,7 @@ main(
            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"));
+               _("can't dump in non degraded mode"));
            amfree(qname);
        }
        else {
@@ -555,10 +552,6 @@ main(
        }
     }
 
-    /* fill up the tape or start new one for taperflush */
-    startaflush();
-    event_loop(0);
-
     short_dump_state();                                /* for amstatus */
 
     g_printf(_("driver: QUITTING time %s telling children to quit\n"),
@@ -593,8 +586,6 @@ main(
 
     amfree(dumper_program);
     amfree(taper_program);
-    if (result_argv)
-       g_strfreev(result_argv);
 
     dbclose();
 
@@ -735,46 +726,51 @@ wait_for_children(void)
 
 }
 
-static void startaflush_tape(taper_t *taper);
+static void startaflush_tape(taper_t *taper, gboolean *state_changed);
 
 static void
 startaflush(void)
 {
     taper_t *taper;
+    gboolean state_changed = FALSE;
 
     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);
+           startaflush_tape(taper, &state_changed);
        }
     }
     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);
+           startaflush_tape(taper, &state_changed);
        }
     }
     for(taper = tapetable; taper <= tapetable+conf_taper_parallel_write;
        taper++) {
        if (!(taper->state & TAPER_STATE_DONE) &&
            taper->state & TAPER_STATE_INIT) {
-           startaflush_tape(taper);
+           startaflush_tape(taper, &state_changed);
        }
     }
     for(taper = tapetable; taper <= tapetable+conf_taper_parallel_write;
        taper++) {
        if (!(taper->state & TAPER_STATE_DONE) &&
            taper->state & TAPER_STATE_IDLE) {
-           startaflush_tape(taper);
+           startaflush_tape(taper, &state_changed);
        }
     }
+    if (state_changed) {
+       short_dump_state();
+    }
 }
 
 static void
 startaflush_tape(
-    taper_t *taper)
+    taper_t  *taper,
+    gboolean *state_changed)
 {
     disk_t *dp = NULL;
     disk_t *fit = NULL;
@@ -803,6 +799,7 @@ startaflush_tape(
        taper_cmd(NO_NEW_TAPE, taper->disk, why_no_new_tape, 0, NULL);
        taper->state |= TAPER_STATE_DONE;
        start_degraded_mode(&runq);
+       *state_changed = TRUE;
     } else if (result_tape_action & TAPE_ACTION_MOVE) {
        taper_t *taper1 = idle_taper();
        if (taper1) {
@@ -812,9 +809,11 @@ startaflush_tape(
            taper1->state = TAPER_STATE_DEFAULT;
            taper->state |= TAPER_STATE_TAPE_STARTED;
            taper->left = taper1->left;
+           taper->nb_dle++;
            if (last_started_taper == taper1) {
                last_started_taper = taper;
            }
+           *state_changed = TRUE;
        }
     }
 
@@ -940,6 +939,24 @@ startaflush_tape(
                if(dp) remove_disk(&tapeq, dp);
                break;
        }
+       if (!dp) {
+           if (!(result_tape_action & TAPE_ACTION_START_A_FLUSH_FIT)) {
+               if(conf_taperalgo != ALGO_SMALLEST)  {
+                   g_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;
+                   }
+                   fit = fit->next;
+               }
+               if(dp) remove_disk(&tapeq, dp);
+           }
+       }
        if (dp) {
            taper->disk = dp;
            taper->dumper = NULL;
@@ -958,6 +975,7 @@ startaflush_tape(
                                               handle_taper_result, NULL);
            }
            taper_nb_wait_reply++;
+           taper->nb_dle++;
            sched(dp)->taper = taper;
            taper_cmd(FILE_WRITE, dp, sched(dp)->destname, sched(dp)->level,
                      sched(dp)->datestamp);
@@ -966,8 +984,8 @@ startaflush_tape(
                    (long long)sched(taper->disk)->act_size,
                    (long long)taper->left);
            amfree(qname);
+           *state_changed = TRUE;
        }
-       short_dump_state();
     }
 }
 
@@ -1038,12 +1056,13 @@ allow_dump_dle(
     } 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) {
+       if (empty(tapeq) && dumper_to_holding == 0 && rq != &directq && no_taper_flushing()) {
            remove_disk(rq, diskp);
            if (diskp->to_holdingdisk != HOLD_REQUIRED) {
                enqueue_disk(&directq, diskp);
                diskp->to_holdingdisk = HOLD_NEVER;
            }
+           if (empty(*rq) && active_dumper() == 0) { force_flush = 1;}
        }
     } else if (client_constrained(diskp)) {
        free_assignedhd(holdp);
@@ -1094,7 +1113,7 @@ start_some_dumps(
 {
     const time_t now = time(NULL);
     int cur_idle;
-    disk_t *diskp, *delayed_diskp, *diskp_accept;
+    disk_t *diskp, *delayed_diskp, *diskp_accept, *diskp_next;
     disk_t *dp;
     assignedhd_t **holdp=NULL, **holdp_accept;
     cmd_t cmd;
@@ -1106,6 +1125,7 @@ start_some_dumps(
     char dumptype;
     char *dumporder;
     int  dumper_to_holding = 0;
+    gboolean state_changed = FALSE;
 
     /* don't start any actual dumps until the taper is started */
     if (!taper_started) return;
@@ -1201,7 +1221,8 @@ start_some_dumps(
                    }
 
                    for (diskp = directq.head; diskp != NULL;
-                                              diskp = diskp->next) {
+                                              diskp = diskp_next) {
+                       diskp_next = diskp->next;
                        allow_dump_dle(diskp, taper, dumptype, &directq, now,
                                       dumper_to_holding, &cur_idle,
                                       &delayed_diskp, &diskp_accept,
@@ -1220,7 +1241,8 @@ start_some_dumps(
        }
 
        if (diskp == NULL) {
-           for(diskp = rq->head; diskp != NULL; diskp = diskp->next) {
+           for(diskp = rq->head; diskp != NULL; diskp = diskp_next) {
+               diskp_next = diskp->next;
                assert(diskp->host != NULL && sched(diskp) != NULL);
 
                allow_dump_dle(diskp, NULL, dumptype, rq, now,
@@ -1314,18 +1336,17 @@ start_some_dumps(
                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);
-                   }
+                   run_server_host_scripts(EXECUTE_ON_PRE_HOST_BACKUP,
+                                           get_config_name(), diskp->host);
                    diskp->host->pre_script = 1;
                }
-               run_server_scripts(EXECUTE_ON_PRE_DLE_BACKUP,
+               run_server_dle_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;
+           diskp->host->start_t = now + 5;
+           if (empty(*rq) && active_dumper() == 0) { force_flush = 1;}
 
            if (result_argv)
                g_strfreev(result_argv);
@@ -1359,6 +1380,7 @@ start_some_dumps(
            taper->dumper = dumper;
            taper->state |= TAPER_STATE_DUMP_TO_TAPE;
            taper->state &= ~TAPER_STATE_IDLE;
+           taper->nb_dle++;
            if (taper_nb_wait_reply == 0) {
                taper_ev_read = event_register(taper_fd, EV_READFD,
                                               handle_taper_result, NULL);
@@ -1367,11 +1389,14 @@ start_some_dumps(
            taper_nb_wait_reply++;
            taper_cmd(PORT_WRITE, diskp, NULL, sched(diskp)->level,
                      sched(diskp)->datestamp);
-           diskp->host->start_t = now + 15;
+           diskp->host->start_t = now + 5;
 
-           short_dump_state();
+           state_changed = TRUE;
        }
     }
+    if (state_changed) {
+       short_dump_state();
+    }
 }
 
 /*
@@ -1419,6 +1444,31 @@ start_degraded_mode(
     disklist_t newq;
     off_t est_full_size;
     char *qname;
+    taper_t *taper;
+
+    if (need_degraded == 0) {
+       for(taper = tapetable;
+           taper < tapetable+conf_taper_parallel_write;
+           taper++) {
+           if (!(taper->state & TAPER_STATE_DONE))
+               return;
+       }
+       need_degraded = 1;
+    }
+
+    if (!schedule_done || degraded_mode) {
+       return;
+    }
+
+    if (need_degraded == 0) {
+       for(taper = tapetable;
+           taper < tapetable+conf_taper_parallel_write;
+           taper++) {
+           if (!(taper->state & TAPER_STATE_DONE))
+               return;
+       }
+       need_degraded = 1;
+    }
 
     newq.head = newq.tail = 0;
 
@@ -1509,15 +1559,17 @@ continue_port_dumps(void)
        if( dumper->busy ) {
            busy_dumpers++;
            if( !find_disk(&roomq, dumper->dp) ) {
-               active_dumpers++;
-           } else if( !dp || 
+               if (dumper->chunker) {
+                   active_dumpers++;
+               }
+           } else if( !dp ||
                       sched(dp)->est_size > sched(dumper->dp)->est_size ) {
                dp = dumper->dp;
            }
        }
     }
-    if((dp != NULL) && (active_dumpers == 0) && (busy_dumpers > 0) && 
-        ((all_taper_idle() && empty(tapeq)) || degraded_mode) &&
+    if((dp != NULL) && (active_dumpers == 0) && (busy_dumpers > 0) &&
+        ((no_taper_flushing() && 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.
@@ -1535,7 +1587,7 @@ static void
 handle_taper_result(
        void *cookie G_GNUC_UNUSED)
 {
-    disk_t *dp = NULL, *dp1;
+    disk_t *dp = NULL;
     dumper_t *dumper;
     cmd_t cmd;
     int result_argc;
@@ -1573,6 +1625,7 @@ handle_taper_result(
            }
            assert(taper != NULL);
            taper->left = 0;
+           taper->nb_dle = 0;
            taper->state &= ~TAPER_STATE_INIT;
            taper->state |= TAPER_STATE_RESERVATION;
            taper->state |= TAPER_STATE_IDLE;
@@ -1607,6 +1660,9 @@ handle_taper_result(
 
            if (strcmp(result_argv[2], "INPUT-ERROR") == 0) {
                taper->input_error = newstralloc(taper->input_error, result_argv[4]);
+               taper->result = FAILED;
+               amfree(qname);
+               break;
            } else if (strcmp(result_argv[2], "INPUT-GOOD") != 0) {
                taper->tape_error = newstralloc(taper->tape_error,
                                               _("Taper protocol error"));
@@ -1617,9 +1673,13 @@ handle_taper_result(
                amfree(qname);
                break;
            }
-           if (strcmp(result_argv[3], "TAPE-ERROR") == 0) {
+           if (strcmp(result_argv[3], "TAPE-ERROR") == 0 ||
+               strcmp(result_argv[3], "TAPE-CONFIG") == 0) {
                taper->state &= ~TAPER_STATE_TAPE_STARTED;
                taper->tape_error = newstralloc(taper->tape_error, result_argv[5]);
+               taper->result = FAILED;
+               amfree(qname);
+               break;
            } else if (strcmp(result_argv[3], "TAPE-GOOD") != 0) {
                taper->state &= ~TAPER_STATE_TAPE_STARTED;
                taper->tape_error = newstralloc(taper->tape_error,
@@ -1657,6 +1717,9 @@ handle_taper_result(
 
            if (strcmp(result_argv[2], "INPUT-ERROR") == 0) {
                taper->input_error = newstralloc(taper->input_error, result_argv[5]);
+               taper->result = FAILED;
+               amfree(qname);
+               break;
            } else if (strcmp(result_argv[2], "INPUT-GOOD") != 0) {
                taper->tape_error = newstralloc(taper->tape_error,
                                               _("Taper protocol error"));
@@ -1667,9 +1730,13 @@ handle_taper_result(
                amfree(qname);
                break;
            }
-           if (strcmp(result_argv[3], "TAPE-ERROR") == 0) {
+           if (strcmp(result_argv[3], "TAPE-ERROR") == 0 ||
+               strcmp(result_argv[3], "TAPE-CONFIG") == 0) {
                taper->state &= ~TAPER_STATE_TAPE_STARTED;
                taper->tape_error = newstralloc(taper->tape_error, result_argv[6]);
+               taper->result = FAILED;
+               amfree(qname);
+               break;
            } else if (strcmp(result_argv[3], "TAPE-GOOD") != 0) {
                taper->state &= ~TAPER_STATE_TAPE_STARTED;
                taper->tape_error = newstralloc(taper->tape_error,
@@ -1685,7 +1752,13 @@ handle_taper_result(
            s = strstr(result_argv[4], " kb ");
            if (s) {
                s += 4;
-               sched(dp)->dumpsize = atol(s);
+               sched(dp)->dumpsize = OFF_T_ATOI(s);
+           } else {
+               s = strstr(result_argv[4], " bytes ");
+               if (s) {
+                   s += 7;
+                   sched(dp)->dumpsize = OFF_T_ATOI(s)/1024;
+               }
            }
 
            taper->result = cmd;
@@ -1715,7 +1788,13 @@ handle_taper_result(
            s = strstr(result_argv[5], " kb ");
            if (s) {
                s += 4;
-               partsize = atol(s);
+               partsize = OFF_T_ATOI(s);
+           } else {
+               s = strstr(result_argv[5], " bytes ");
+               if (s) {
+                   s += 7;
+                   partsize = OFF_T_ATOI(s)/1024;
+               }
            }
            taper->left -= partsize;
 
@@ -1730,11 +1809,15 @@ handle_taper_result(
 
            dp = serial2disk(result_argv[1]);
            taper = sched(dp)->taper;
-           taper->state &= ~TAPER_STATE_TAPE_STARTED;
-           taper->state |= TAPER_STATE_TAPE_REQUESTED;
+           if (taper->state & TAPER_STATE_DONE) {
+               taper_cmd(NO_NEW_TAPE, taper->disk, "taper found no tape", 0, NULL);
+           } else {
+               taper->state &= ~TAPER_STATE_TAPE_STARTED;
+               taper->state |= TAPER_STATE_TAPE_REQUESTED;
 
-           start_some_dumps(&runq);
-           startaflush();
+               start_some_dumps(&runq);
+               startaflush();
+           }
            break;
 
        case NEW_TAPE: /* NEW-TAPE <handle> <label> */
@@ -1750,6 +1833,7 @@ handle_taper_result(
            taper = sched(dp)->taper;
             /* Update our tape counter and reset taper->left */
            current_tape++;
+           taper->nb_dle = 1;
            taper->left = tape_length;
            taper->state &= ~TAPER_STATE_WAIT_NEW_TAPE;
            taper->state |= TAPER_STATE_TAPE_STARTED;
@@ -1813,6 +1897,7 @@ handle_taper_result(
            if (strcmp(result_argv[1], "SETUP") == 0) {
                taper_nb_wait_reply = 0;
                taper_nb_scan_volume = 0;
+               need_degraded = 1;
            } else {
                taper = taper_from_name(result_argv[1]);
                taper->state = TAPER_STATE_DONE;
@@ -1829,13 +1914,11 @@ handle_taper_result(
                taper_nb_scan_volume--;
            }
            if (taper_nb_wait_reply == 0) {
+               need_degraded = 1;
                event_release(taper_ev_read);
                taper_ev_read = NULL;
            }
-           need_degraded = 1;
-           if (schedule_done && !degraded_mode) {
-               start_degraded_mode(&runq);
-           }
+           start_degraded_mode(&runq);
            start_some_dumps(&runq);
            break;
 
@@ -1854,18 +1937,16 @@ handle_taper_result(
            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);
-               }
+               run_server_host_scripts(EXECUTE_ON_PRE_HOST_BACKUP,
+                                       get_config_name(), dp->host);
                dp->host->pre_script = 1;
            }
-           run_server_scripts(EXECUTE_ON_PRE_DLE_BACKUP,
+           run_server_dle_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;
+           dp->host->start_t = time(NULL) + 5;
            amfree(dp->dataport_list);
 
            taper->state |= TAPER_STATE_DUMP_TO_TAPE;
@@ -1884,6 +1965,7 @@ handle_taper_result(
              * checks. If there are dumps waiting for diskspace to be freed,
              * cancel one.
              */
+           taper_started = 1;
             if(!nodump) {
                 log_add(L_WARNING,
                         _("going into degraded mode because of taper component error."));
@@ -1892,7 +1974,7 @@ handle_taper_result(
            for (taper = tapetable;
                 taper < tapetable + conf_taper_parallel_write;
                  taper++) {
-               if (taper && taper->disk && taper->result != LAST_TOK) {
+               if (taper && taper->disk) {
                    taper->tape_error = newstralloc(taper->tape_error,"BOGUS");
                    taper->result = cmd;
                    if (taper->dumper) {
@@ -1904,7 +1986,6 @@ handle_taper_result(
                        file_taper_result(taper->disk);
                    }
                }
-
            }
            taper = NULL;
 
@@ -1913,6 +1994,7 @@ handle_taper_result(
                 taper_ev_read = NULL;
                taper_nb_wait_reply = 0;
             }
+           need_degraded = 1;
            start_degraded_mode(&runq);
             tapeq.head = tapeq.tail = NULL;
             aclose(taper_fd);
@@ -1928,6 +2010,9 @@ handle_taper_result(
        g_strfreev(result_argv);
 
        if (taper && taper->disk && taper->result != LAST_TOK) {
+           if (taper->nb_dle >= conf_max_dle_by_volume) {
+               taper_cmd(CLOSE_VOLUME, dp, NULL, 0, NULL);
+           }
            if(taper->dumper) {
                if (taper->dumper->result != LAST_TOK) {
                    // Dumper already returned it's result
@@ -1939,6 +2024,7 @@ handle_taper_result(
        }
 
     } while(areads_dataready(taper_fd));
+    start_some_dumps(&runq);
     startaflush();
 }
 
@@ -2060,7 +2146,6 @@ dumper_taper_result(
 {
     dumper_t *dumper;
     taper_t  *taper;
-    int is_partial;
     char *qname;
 
     dumper = sched(dp)->dumper;
@@ -2085,8 +2170,6 @@ dumper_taper_result(
        update_failed_dump(dp);
     }
 
-    is_partial = dumper->result != DONE || taper->result != DONE;
-
     sched(dp)->dump_attempted += 1;
     sched(dp)->taper_attempted += 1;
 
@@ -2114,6 +2197,7 @@ dumper_taper_result(
     dp->inprogress = 0;
     deallocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
     taper->dumper = NULL;
+    taper->disk = NULL;
     sched(dp)->dumper = NULL;
     sched(dp)->taper = NULL;
     start_some_dumps(&runq);
@@ -2375,7 +2459,7 @@ handle_dumper_result(
            int last_dump = 1;
            dumper_t *dumper;
 
-           run_server_scripts(EXECUTE_ON_POST_DLE_BACKUP,
+           run_server_dle_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) {
@@ -2395,10 +2479,8 @@ handle_dumper_result(
            }
            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);
-                   }
+                   run_server_host_scripts(EXECUTE_ON_POST_HOST_BACKUP,
+                                           get_config_name(), dp->host);
                    dp->host->post_script = 1;
                }
            }
@@ -2420,14 +2502,12 @@ handle_dumper_result(
                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 (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);
            }
@@ -2759,7 +2839,7 @@ read_flush(
            continue;
        }
 
-       if(file.dumplevel < 0 || file.dumplevel > 9) {
+       if (file.dumplevel < 0 || file.dumplevel > 399) {
            log_add(L_INFO, _("%s: ignoring file with bogus dump level %d."),
                    destname, file.dumplevel);
            amfree(destname);
@@ -2825,6 +2905,8 @@ read_flush(
     if (!nodump) {
        schedule_ev_read = event_register((event_id_t)0, EV_READFD,
                                          read_schedule, NULL);
+    } else {
+       force_flush = 1;
     }
 }
 
@@ -3135,8 +3217,10 @@ read_schedule(
     amfree(inpline);
     if(line == 0)
        log_add(L_WARNING, _("WARNING: got empty schedule from planner"));
-    if(need_degraded==1) start_degraded_mode(&runq);
     schedule_done = 1;
+    start_degraded_mode(&runq);
+    run_server_global_scripts(EXECUTE_ON_PRE_BACKUP, get_config_name());
+    if (empty(runq)) force_flush = 1;
     start_some_dumps(&runq);
     startaflush();
 }
@@ -3534,10 +3618,18 @@ build_diskspace(
                break;
            }
        }
-
+       if (!ha || j >= num_holdalloc) {
+           fprintf(stderr,_("build_diskspace: holding disk file '%s' is not in a holding disk directory.\n"), filename);
+           amfree(used);
+           amfree(result);
+           return NULL;
+       }
        if(stat(filename, &finfo) == -1) {
-           g_fprintf(stderr, _("stat %s: %s\n"), filename, strerror(errno));
-           finfo.st_size = (off_t)0;
+           g_fprintf(stderr, _("build_diskspace: can't stat %s: %s\n"),
+                     filename, strerror(errno));
+           amfree(used);
+           amfree(result);
+           return NULL;
        }
        used[j] += ((off_t)finfo.st_size+(off_t)1023)/(off_t)1024;
        if((fd = open(filename,O_RDONLY)) == -1) {
@@ -3673,30 +3765,37 @@ tape_action(
     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;
-
+    int   dle_free = 0;                /* number of dle that fit on started tape */
+    int   new_dle = 0;         /* number of dle that doesn't fit on started tape */
+    off_t new_data = 0;                /* size of dle that doesn't fit on started tape */
+    off_t data_next_tape = 0;
+    off_t data_free = 0;
+    off_t data_lost = 0;
+    off_t data_lost_next_tape = 0;
+    gboolean taperflush_criteria;
+    gboolean flush_criteria;
+
+    driver_debug(2, "tape_action: ENTER %p\n", taper);
     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);
+    driver_debug(2, _("dumpers_size: %lld\n"), (long long)dumpers_size);
 
     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);
+    driver_debug(2, _("runq_size: %lld\n"), (long long)runq_size);
 
     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);
+    driver_debug(2, _("directq_size: %lld\n"), (long long)directq_size);
 
     tapeq_size = directq_size;
     for(dp = tapeq.head; dp != NULL; dp = dp->next) {
@@ -3712,26 +3811,80 @@ tape_action(
     }
 
     /* Add what is currently written to tape and in the go. */
+    new_data = 0;
     for (taper1 = tapetable; taper1 < tapetable+conf_taper_parallel_write;
         taper1++) {
        if (taper1->state & TAPER_STATE_TAPE_STARTED) {
-           tapeq_size -= taper1->left;
+           if (taper1->nb_dle < conf_max_dle_by_volume) {
+               tapeq_size -= taper1->left;
+           }
+           dle_free += (conf_max_dle_by_volume - taper1->nb_dle);
        }
        if (taper1->disk) {
+           off_t data_to_go;
+           off_t t_size;
            if (taper1->dumper) {
-               tapeq_size += sched(taper1->disk)->est_size - taper1->written;
+               t_size = sched(taper1->disk)->est_size;
            } else {
-               tapeq_size += sched(taper1->disk)->act_size - taper1->written;
+               t_size = sched(taper1->disk)->act_size;
+           }
+           data_to_go =  t_size - taper1->written;
+           if (data_to_go > taper1->left) {
+               data_next_tape += data_to_go - taper1->left;
+               data_lost += taper1->written + taper1->left;
+               if (taper1->state & TAPER_STATE_TAPE_STARTED) {
+                   dle_free -= (conf_max_dle_by_volume - taper1->nb_dle) + 1;
+               } else {
+                   dle_free -= 2;
+                   new_data += t_size;
+               }
+           } else {
+               if (!(taper1->state & TAPER_STATE_TAPE_STARTED)) {
+                   dle_free--;
+                   new_data += t_size;
+               }
+               data_free += taper1->left - data_to_go;
+           }
+           tapeq_size += data_to_go;
+       }
+    }
+
+    new_dle = queue_length(tapeq) - dle_free;
+    driver_debug(2, _("dle_free: %d\n"), dle_free);
+    driver_debug(2, _("new_dle: %d\n"), new_dle);
+    if (new_dle > 0) {
+       if (taperflush == 0 &&
+           flush_threshold_dumped == 0 &&
+           flush_threshold_scheduled == 0) {
+           /* shortcut, will trigger taperflush_criteria and/or flush_criteria */
+           new_data += 1;
+       } else {
+           /* sum the size of the first new-dle in tapeq */
+           /* they should be the reverse taperalgo       */
+           for (dp = tapeq.head;
+                dp != NULL && new_dle > 0;
+                dp = dp->next, new_dle--) {
+               new_data += sched(dp)->act_size;
            }
        }
+       if (tapeq_size < new_data) {
+           tapeq_size = new_data;
+       }
     }
-    driver_debug(1, _("tapeq_size: %lld\n"), (long long)tapeq_size);
+    driver_debug(2, _("new_data: %lld\n"), (long long)new_data);
+    data_lost_next_tape = tape_length + data_free - data_next_tape - runq_size - directq_size - tapeq_size;
+    driver_debug(2, _("data_lost: %lld\n"), (long long)data_lost);
+    driver_debug(2, _("data_free: %lld\n"), (long long)data_free);
+    driver_debug(2, _("data_next_tape: %lld\n"), (long long)data_next_tape);
+    driver_debug(2, _("data_lost_next_tape: %lld\n"), (long long)data_lost_next_tape);
+;
+    driver_debug(2, _("tapeq_size: %lld\n"), (long long)tapeq_size);
 
     sched_size = runq_size + directq_size + tapeq_size + dumpers_size;
-    driver_debug(1, _("sched_size: %lld\n"), (long long)sched_size);
+    driver_debug(2, _("sched_size: %lld\n"), (long long)sched_size);
 
     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);
+    driver_debug(2, _("dump_to_disk_size: %lld\n"), (long long)dump_to_disk_size);
 
     dump_to_disk_terminated = schedule_done && dump_to_disk_size == 0;
 
@@ -3741,38 +3894,46 @@ tape_action(
            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;
-    }
+
+    taperflush_criteria = (taperflush < tapeq_size &&
+                          (force_flush == 1 || dump_to_disk_terminated));
+    flush_criteria = (flush_threshold_dumped < tapeq_size &&
+                     flush_threshold_scheduled < sched_size) ||
+                    taperflush_criteria;
+
+    driver_debug(2, "taperflush %lld\n", (long long)taperflush);
+    driver_debug(2, "flush_threshold_dumped %lld\n", (long long)flush_threshold_dumped);
+    driver_debug(2, "flush_threshold_scheduled %lld\n", (long long)flush_threshold_scheduled);
+    driver_debug(2, "force_flush %d\n", force_flush);
+    driver_debug(2, "dump_to_disk_terminated %d\n", dump_to_disk_terminated);
+    driver_debug(2, "queue_length(runq) %d\n", queue_length(runq));
+    driver_debug(2, "queue_length(directq) %d\n", queue_length(directq));
+    driver_debug(2, "queue_length(tapeq) %d\n", queue_length(tapeq));
+    driver_debug(2, "taperflush_criteria %d\n", taperflush_criteria);
+    driver_debug(2, "flush_criteria %d\n", flush_criteria);
 
     // Changing conditionals can produce a driver hang, take care.
-    // 
+    //
     // when to start writting to a new tape
     if (taper->state & TAPER_STATE_TAPE_REQUESTED) {
+       driver_debug(2, "tape_action: TAPER_STATE_TAPE_REQUESTED\n");
        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);
+           driver_debug(2, "tape_action: TAPER_STATE_TAPE_REQUESTED return TAPE_ACTION_NO_NEW_TAPE\n");
            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) ||
+                  (flush_criteria ||
+                   (data_lost > data_lost_next_tape) ||
                    nb_taper_active == 0) &&
                   (last_started_taper == NULL ||
                    last_started_taper == taper)) {
+           driver_debug(2, "tape_action: TAPER_STATE_TAPE_REQUESTED return TAPE_ACTION_SCAN\n");
            result |= TAPE_ACTION_SCAN;
        } else {
+           driver_debug(2, "tape_action: TAPER_STATE_TAPE_REQUESTED return TAPE_ACTION_MOVE\n");
            result |= TAPE_ACTION_MOVE;
        }
     } else if ((taper->state & TAPER_STATE_WAIT_FOR_TAPE) &&
@@ -3780,68 +3941,76 @@ tape_action(
         !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
+        flush_criteria ||                              // flush criteria
+        data_lost > data_lost_next_tape
        )) {
+       driver_debug(2, "tape_action: TAPER_STATE_WAIT_FOR_TAPE return TAPE_ACTION_NEW_TAPE\n");
        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
+              (taperflush >= tapeq_size &&             // taperflush criteria
               (force_flush == 1 ||                     //  if force_flush
                dump_to_disk_terminated))               //  or all dump to disk
              ) {
+       driver_debug(2, "tape_action: TAPER_STATE_WAIT_FOR_TAPE B\n");
        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 {
+               driver_debug(2, "tape_action: TAPER_STATE_WAIT_FOR_TAPE return TAPE_ACTION_NO_NEW_TAPE\n");
+               result |= TAPE_ACTION_NO_NEW_TAPE;
+           } else if (dumpers_size <= 0) {
                *why_no_new_tape = _("taperflush criteria not met");
+               driver_debug(2, "tape_action: TAPER_STATE_WAIT_FOR_TAPE return TAPE_ACTION_NO_NEW_TAPE\n");
+               result |= TAPE_ACTION_NO_NEW_TAPE;
            }
-           result |= TAPE_ACTION_NO_NEW_TAPE;
        }
     }
 
     // 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) {
+       driver_debug(2, "tape_action: TAPER_STATE_IDLE\n");
        if (!degraded_mode && (!empty(tapeq) || !empty(directq)) &&
-           (((taper->state & TAPER_STATE_TAPE_STARTED) &&
-             force_flush == 1) ||                              // if tape already started and force_flush
+           (taper->state & TAPER_STATE_TAPE_STARTED ||         // tape already started 
              !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
+            flush_criteria)) {                                 // flush
 
            if (nb_taper_flushing == 0) {
+               driver_debug(2, "tape_action: TAPER_STATE_IDLE return TAPE_ACTION_START_A_FLUSH\n");
                result |= TAPE_ACTION_START_A_FLUSH;
            } else {
+               driver_debug(2, "tape_action: TAPER_STATE_IDLE return TAPE_ACTION_START_A_FLUSH_FIT\n");
                result |= TAPE_ACTION_START_A_FLUSH_FIT;
            }
+       } else {
+           driver_debug(2, "tape_action: TAPER_STATE_IDLE return TAPE_ACTION_NO_ACTION\n");
        }
     }
     return result;
 }
 
 static int
-all_taper_idle(void)
+no_taper_flushing(void)
 {
     taper_t *taper;
 
     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)
+       if (taper->state & TAPER_STATE_FILE_TO_TAPE)
            return 0;
     }
     return 1;
 }
 
+static int
+active_dumper(void)
+{
+    int i, nidle=0;
+
+    for(i = 0; i < inparallel; i++) if(!dmptable[i].busy) nidle++;
+    return inparallel - nidle;
+}
 #if 0
 static void
 dump_state(