lintian doesn't like orphan packages with uploaders...
[debian/amanda] / server-src / planner.c
index e2c1497c76f221d5eedcb5aca31423de0c06d9a9..f7a38be691eeedbdf900a7043fc8dc3e9fbde377 100644 (file)
@@ -1,6 +1,7 @@
 /*
  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
  * Copyright (c) 1991-1999 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
@@ -108,6 +109,7 @@ typedef struct est_s {
     double fullcomp, incrcomp;
     char *errstr;
     char *degr_mesg;
+    info_t *info;
 } est_t;
 
 #define est(dp)        ((est_t *)(dp)->up)
@@ -191,6 +193,13 @@ main(
     int    planner_setuid;
     int exit_status = EXIT_SUCCESS;
     gboolean no_taper = FALSE;
+    gboolean from_client = FALSE;
+    gboolean exact_match = FALSE;
+
+    if (argc > 1 && argv && argv[1] && g_str_equal(argv[1], "--version")) {
+       printf("planner-%s\n", VERSION);
+       return (0);
+    }
 
     /*
      * Configure program for internationalization:
@@ -265,7 +274,18 @@ main(
        no_taper = TRUE;
        diskarg_offset += 1;
     }
+    if (argc - diskarg_offset > 0 && strcmp(argv[diskarg_offset], "--from-client") == 0) {
+       from_client = TRUE;
+       diskarg_offset += 1;
+    }
+    if (argc - diskarg_offset > 0 && g_str_equal(argv[diskarg_offset],
+                                                 "--exact_match")) {
+       exact_match = TRUE;
+       diskarg_offset += 1;
+    }
+
 
+    run_server_global_scripts(EXECUTE_ON_PRE_ESTIMATE, get_config_name());
 
     /*
      * 1. Networking Setup
@@ -315,7 +335,7 @@ main(
     conf_tapecycle = getconf_int(CNF_TAPECYCLE);
     conf_etimeout = (time_t)getconf_int(CNF_ETIMEOUT);
     conf_reserve  = getconf_int(CNF_RESERVE);
-    conf_autoflush = getconf_boolean(CNF_AUTOFLUSH);
+    conf_autoflush = getconf_no_yes_all(CNF_AUTOFLUSH);
     conf_usetimestamps = getconf_boolean(CNF_USETIMESTAMPS);
 
     today = time(0);
@@ -335,12 +355,25 @@ main(
     g_fprintf(stderr, _("%s: timestamp %s\n"),
                    get_pname(), planner_timestamp);
 
-    errstr = match_disklist(&origq, argc-diskarg_offset,
+    errstr = match_disklist(&origq, exact_match, argc-diskarg_offset,
                                    argv+diskarg_offset);
     if (errstr) {
        g_fprintf(stderr,"%s",errstr);
         exit_status = EXIT_FAILURE;
     }
+
+    for (dp = origq.head; dp != NULL; dp = dp->next) {
+       if (dp->todo) {
+           if (from_client) {
+               if (!dp->dump_limit || !dp->dump_limit->same_host)
+                   dp->todo = 0;
+           } else {
+               if (dp->dump_limit && !dp->dump_limit->server)
+                   dp->todo = 0;
+           }
+       }
+    }
+
     nb_disk = 0;
     for (dp = origq.head; dp != NULL; dp = dp->next) {
        if (dp->todo) {
@@ -436,7 +469,8 @@ main(
            }
 
            /* see if this matches the command-line arguments */
-           if (!match_dumpfile(&file, argc-diskarg_offset,
+           if (conf_autoflush == 1 &&
+               !match_dumpfile(&file, exact_match, argc-diskarg_offset,
                                       argv+diskarg_offset)) {
                continue;
            }
@@ -552,6 +586,8 @@ main(
     while(!empty(estq)) analyze_estimate(dequeue_disk(&estq));
     while(!empty(failq)) handle_failed(dequeue_disk(&failq));
 
+    run_server_global_scripts(EXECUTE_ON_POST_ESTIMATE, get_config_name());
+
     /*
      * At this point, all the disks are on schedq sorted by priority.
      * The total estimated size of the backups is in total_size.
@@ -728,7 +764,7 @@ setup_estimate(
      disk_t *dp)
 {
     est_t *ep;
-    info_t info;
+    info_t *info;
     int i;
     char *qname;
     int overwrite_runs;
@@ -742,7 +778,8 @@ setup_estimate(
 
     /* get current information about disk */
 
-    if(get_info(dp->host->hostname, dp->name, &info)) {
+    info = g_new0(info_t, 1);
+    if(get_info(dp->host->hostname, dp->name, info)) {
        /* no record for this disk, make a note of it */
        log_add(L_INFO, _("Adding new disk %s:%s."), dp->host->hostname, qname);
     }
@@ -782,6 +819,7 @@ setup_estimate(
 
     ep = alloc(SIZEOF(est_t));
     dp->up = (void *) ep;
+    ep->info = info;
     ep->state = DISK_READY;
     ep->dump_priority = dp->priority;
     ep->errstr = 0;
@@ -793,7 +831,7 @@ setup_estimate(
 
     /* calculated fields */
 
-    if (ISSET(info.command, FORCE_FULL)) {
+    if (ISSET(info->command, FORCE_FULL)) {
        /* force a level 0, kind of like a new disk */
        if(dp->strategy == DS_NOFULL) {
            /*
@@ -812,18 +850,18 @@ setup_estimate(
                    dp->host->hostname, qname);
 
            /* clear force command */
-           CLR(info.command, FORCE_FULL);
-           ep->last_level = last_level(&info);
-           ep->next_level0 = next_level0(dp, &info);
+           CLR(info->command, FORCE_FULL);
+           ep->last_level = last_level(info);
+           ep->next_level0 = next_level0(dp, info);
        } else if (dp->strategy == DS_INCRONLY) {
            log_add(L_WARNING,
                    _("Cannot force full dump of %s:%s with incronly option."),
                    dp->host->hostname, qname);
 
            /* clear force command */
-           CLR(info.command, FORCE_FULL);
-           ep->last_level = last_level(&info);
-           ep->next_level0 = next_level0(dp, &info);
+           CLR(info->command, FORCE_FULL);
+           ep->last_level = last_level(info);
+           ep->next_level0 = next_level0(dp, info);
        } else {
            ep->degr_mesg = _("Skipping: force-full disk can't be dumped in degraded mode");
            ep->last_level = -1;
@@ -835,44 +873,44 @@ setup_estimate(
     else if(dp->strategy == DS_NOFULL) {
        /* force estimate of level 1 */
        ep->last_level = 1;
-       ep->next_level0 = next_level0(dp, &info);
+       ep->next_level0 = next_level0(dp, info);
     }
     else {
-       ep->last_level = last_level(&info);
-       ep->next_level0 = next_level0(dp, &info);
+       ep->last_level = last_level(info);
+       ep->next_level0 = next_level0(dp, info);
     }
 
     /* adjust priority levels */
 
     /* warn if dump will be overwritten */
-    if (ep->last_level > -1 && strlen(info.inf[0].label) > 0) {
-       overwrite_runs = when_overwrite(info.inf[0].label);
+    if (ep->last_level > -1 && strlen(info->inf[0].label) > 0) {
+       overwrite_runs = when_overwrite(info->inf[0].label);
        if(overwrite_runs == 0) {
            log_add(L_WARNING, _("Last full dump of %s:%s "
                    "on tape %s overwritten on this run."),
-                   dp->host->hostname, qname, info.inf[0].label);
+                   dp->host->hostname, qname, info->inf[0].label);
        } else if(overwrite_runs <= RUNS_REDZONE) {
            log_add(L_WARNING,
                    plural(_("Last full dump of %s:%s on tape %s overwritten in %d run."),
                           _("Last full dump of %s:%s on tape %s overwritten in %d runs."), overwrite_runs),
-                   dp->host->hostname, qname, info.inf[0].label,
+                   dp->host->hostname, qname, info->inf[0].label,
                    overwrite_runs);
        }
     }
 
     /* warn if last level 1 will be overwritten */
-    if (ep->last_level > 1 && strlen(info.inf[1].label) > 0) {
-       overwrite_runs = when_overwrite(info.inf[1].label);
+    if (ep->last_level > 1 && strlen(info->inf[1].label) > 0) {
+       overwrite_runs = when_overwrite(info->inf[1].label);
        if(overwrite_runs == 0) {
            log_add(L_WARNING, _("Last level 1 dump of %s:%s "
                    "on tape %s overwritten on this run, resetting to level 1"),
-                   dp->host->hostname, qname, info.inf[1].label);
+                   dp->host->hostname, qname, info->inf[1].label);
            ep->last_level = 0;
        } else if(overwrite_runs <= RUNS_REDZONE) {
            log_add(L_WARNING,
                    plural(_("Last level 1 dump of %s:%s on tape %s overwritten in %d run."),
                           _("Last level 1 dump of %s:%s on tape %s overwritten in %d runs."), overwrite_runs),
-                   dp->host->hostname, qname, info.inf[1].label,
+                   dp->host->hostname, qname, info->inf[1].label,
                    overwrite_runs);
        }
     }
@@ -884,7 +922,7 @@ setup_estimate(
                dp->host->hostname, qname, (-ep->next_level0));
        ep->dump_priority -= ep->next_level0;
     }
-    else if (ISSET(info.command, FORCE_FULL))
+    else if (ISSET(info->command, FORCE_FULL))
        ep->dump_priority += 1;
     /* else XXX bump up the priority of incrementals that failed last night */
 
@@ -893,11 +931,11 @@ setup_estimate(
     if(dp->skip_full && dp->strategy != DS_NOINC) {
        if(ep->next_level0 <= 0) {
            /* update the date field */
-           info.inf[0].date = today;
-           CLR(info.command, FORCE_FULL);
+           info->inf[0].date = today;
+           CLR(info->command, FORCE_FULL);
            ep->next_level0 += conf_dumpcycle;
            ep->last_level = 0;
-           if(put_info(dp->host->hostname, dp->name, &info)) {
+           if(put_info(dp->host->hostname, dp->name, info)) {
                error(_("could not put info record for %s:%s: %s"),
                      dp->host->hostname, qname, strerror(errno));
                /*NOTREACHED*/
@@ -907,9 +945,9 @@ setup_estimate(
            g_fprintf(stderr,_("%s:%s lev 0 skipped due to skip-full flag\n"),
                    dp->host->hostname, qname);
            /* don't enqueue the disk */
-           askfor(ep, 0, -1, &info);
-           askfor(ep, 1, -1, &info);
-           askfor(ep, 2, -1, &info);
+           askfor(ep, 0, -1, info);
+           askfor(ep, 1, -1, info);
+           askfor(ep, 2, -1, info);
            g_fprintf(stderr, _("%s: SKIPPED %s %s 0 [skip-full]\n"),
                    get_pname(), dp->host->hostname, qname);
            log_add(L_SUCCESS, _("%s %s %s 0 [skipped: skip-full]"),
@@ -929,11 +967,11 @@ setup_estimate(
        }
     }
 
-    if(dp->strategy == DS_INCRONLY && ep->last_level == -1 && !ISSET(info.command, FORCE_FULL)) {
+    if(dp->strategy == DS_INCRONLY && ep->last_level == -1 && !ISSET(info->command, FORCE_FULL)) {
        /* don't enqueue the disk */
-       askfor(ep, 0, -1, &info);
-       askfor(ep, 1, -1, &info);
-       askfor(ep, 2, -1, &info);
+       askfor(ep, 0, -1, info);
+       askfor(ep, 1, -1, info);
+       askfor(ep, 2, -1, info);
        log_add(L_FAIL, _("%s %s 19000101 1 [Skipping incronly because no full dump were done]"),
                dp->host->hostname, qname);
        g_fprintf(stderr,_("%s:%s lev 1 skipped due to strategy incronly and no full dump were done\n"),
@@ -948,9 +986,9 @@ setup_estimate(
        g_fprintf(stderr,_("%s:%s lev 1 skipped due to skip-incr flag\n"),
                dp->host->hostname, qname);
        /* don't enqueue the disk */
-       askfor(ep, 0, -1, &info);
-       askfor(ep, 1, -1, &info);
-       askfor(ep, 2, -1, &info);
+       askfor(ep, 0, -1, info);
+       askfor(ep, 1, -1, info);
+       askfor(ep, 2, -1, info);
 
        g_fprintf(stderr, _("%s: SKIPPED %s %s 1 [skip-incr]\n"),
                get_pname(), dp->host->hostname, qname);
@@ -970,15 +1008,16 @@ setup_estimate(
        ep->next_level0 = 0;
     }
 
-    if(ep->last_level == 0) ep->level_days = 0;
-    else ep->level_days = runs_at(&info, ep->last_level);
-    ep->last_lev0size = info.inf[0].csize;
+    //if(ep->last_level == 0) ep->level_days = 0;
+    //else ep->level_days = runs_at(info, ep->last_level);
+    ep->level_days = runs_at(info, ep->last_level);
+    ep->last_lev0size = info->inf[0].csize;
 
-    ep->fullrate = perf_average(info.full.rate, 0.0);
-    ep->incrrate = perf_average(info.incr.rate, 0.0);
+    ep->fullrate = perf_average(info->full.rate, 0.0);
+    ep->incrrate = perf_average(info->incr.rate, 0.0);
 
-    ep->fullcomp = perf_average(info.full.comp, dp->comprate[0]);
-    ep->incrcomp = perf_average(info.incr.comp, dp->comprate[1]);
+    ep->fullcomp = perf_average(info->full.comp, dp->comprate[0]);
+    ep->incrcomp = perf_average(info->incr.comp, dp->comprate[1]);
 
     /* determine which estimates to get */
 
@@ -986,10 +1025,10 @@ setup_estimate(
 
     if (dp->strategy == DS_NOINC ||
        (!dp->skip_full &&
-        (!ISSET(info.command, FORCE_BUMP) ||
+        (!ISSET(info->command, FORCE_BUMP) ||
          dp->skip_incr ||
          ep->last_level == -1))) {
-       if(ISSET(info.command, FORCE_BUMP) && ep->last_level == -1) {
+       if(ISSET(info->command, FORCE_BUMP) && ep->last_level == -1) {
            log_add(L_INFO,
                  _("Remove force-bump command of %s:%s because it's a new disk."),
                    dp->host->hostname, qname);
@@ -997,13 +1036,17 @@ setup_estimate(
        switch (dp->strategy) {
        case DS_STANDARD: 
        case DS_NOINC:
-           askfor(ep, i++, 0, &info);
+           askfor(ep, i++, 0, info);
+           if (ep->last_level == -1)
+               ep->degr_mesg = _("Skipping: new disk can't be dumped in degraded mode");
+           else
+               ep->degr_mesg = _("Skipping: strategy NOINC can't be dumped in degraded mode");
            if(dp->skip_full) {
                log_add(L_INFO, _("Ignoring skip-full for %s:%s "
                        "because the strategy is NOINC."),
                        dp->host->hostname, qname);
            }
-           if(ISSET(info.command, FORCE_BUMP)) {
+           if(ISSET(info->command, FORCE_BUMP)) {
                log_add(L_INFO,
                 _("Ignoring FORCE_BUMP for %s:%s because the strategy is NOINC."),
                        dp->host->hostname, qname);
@@ -1015,7 +1058,7 @@ setup_estimate(
            break;
 
        case DS_INCRONLY:
-           if (ISSET(info.command, FORCE_FULL))
+           if (ISSET(info->command, FORCE_FULL))
                ep->last_level = 0;
            break;
        }
@@ -1026,7 +1069,7 @@ setup_estimate(
            if (ep->degr_mesg == NULL)
                ep->degr_mesg = _("Skipping: new disk can't be dumped in degraded mode");
            if(dp->strategy == DS_NOFULL || dp->strategy == DS_INCRONLY) {
-               askfor(ep, i++, 1, &info);
+               askfor(ep, i++, 1, info);
            } else {
                assert(!dp->skip_full);         /* should be handled above */
            }
@@ -1035,23 +1078,23 @@ setup_estimate(
 
            curr_level = ep->last_level;
 
-           if (ISSET(info.command, FORCE_NO_BUMP)) {
+           if (ISSET(info->command, FORCE_NO_BUMP)) {
                if(curr_level > 0) { /* level 0 already asked for */
-                   askfor(ep, i++, curr_level, &info);
+                   askfor(ep, i++, curr_level, info);
                }
                log_add(L_INFO,_("Preventing bump of %s:%s as directed."),
                        dp->host->hostname, qname);
                ep->degr_mesg = _("Skipping: force-no-bump disk can't be dumped in degraded mode");
-           } else if (ISSET(info.command, FORCE_BUMP)
+           } else if (ISSET(info->command, FORCE_BUMP)
                       && curr_level + 1 < DUMP_LEVELS) {
-               askfor(ep, i++, curr_level+1, &info);
+               askfor(ep, i++, curr_level+1, info);
                log_add(L_INFO,_("Bumping of %s:%s at level %d as directed."),
                        dp->host->hostname, qname, curr_level+1);
                ep->degr_mesg = _("Skipping: force-bump disk can't be dumped in degraded mode");
            } else if (curr_level == 0) {
-               askfor(ep, i++, 1, &info);
+               askfor(ep, i++, 1, info);
            } else {
-               askfor(ep, i++, curr_level, &info);
+               askfor(ep, i++, curr_level, info);
                /*
                 * If last time we dumped less than the threshold, then this
                 * time we will too, OR the extra size will be charged to both
@@ -1059,25 +1102,25 @@ setup_estimate(
                 * if we haven't been at this level 2 days, or the dump failed
                 * last night, we can't bump.
                 */
-               if((info.inf[curr_level].size == (gint64)0 || /* no data, try it anyway */
-                   (((info.inf[curr_level].size > bump_thresh(curr_level, info.inf[0].size,dp->bumppercent, dp->bumpsize, dp->bumpmult)))
+               if((info->inf[curr_level].size == (gint64)0 || /* no data, try it anyway */
+                   (((info->inf[curr_level].size > bump_thresh(curr_level, info->inf[0].size,dp->bumppercent, dp->bumpsize, dp->bumpmult)))
                     && ep->level_days >= dp->bumpdays))
                   && curr_level + 1 < DUMP_LEVELS) {
-                   askfor(ep, i++, curr_level+1, &info);
+                   askfor(ep, i++, curr_level+1, info);
                }
-           } 
+           }
        }
     }
 
-    while(i < MAX_LEVELS)      /* mark end of estimates */
-       askfor(ep, i++, -1, &info);
+    while(i < MAX_LEVELS)      /* mark end of estimates */
+       askfor(ep, i++, -1, info);
 
     /* debug output */
 
     g_fprintf(stderr, _("setup_estimate: %s:%s: command %u, options: %s    "
-           "last_level %d next_level0 %d level_days %d    getting estimates "
+           "last_level %d next_level0 %d level_days %d    getting estimates "
            "%d (%lld) %d (%lld) %d (%lld)\n"),
-           dp->host->hostname, qname, info.command,
+           dp->host->hostname, qname, info->command,
            dp->strategy == DS_NOFULL ? "no-full" :
                 dp->strategy == DS_INCRONLY ? "incr-only" :
                 dp->skip_full ? "skip-full" :
@@ -1255,10 +1298,9 @@ static int runs_at(
 
     last = last_level(info);
     if(lev != last) return 0;
-    if(lev == 0) return 1;
-
     if(info->consecutive_runs != -1)
        return info->consecutive_runs;
+    if(lev == 0) return 1;
 
     /* to keep compatibility with old infofile */
     cur_tape = lookup_tapelabel(info->inf[lev].label);
@@ -1321,15 +1363,11 @@ static void get_estimates(void)
            hostp = dp->host;
            if(hostp->up == HOST_READY) {
                something_started = 1;
+               run_server_host_scripts(EXECUTE_ON_PRE_HOST_ESTIMATE,
+                                       get_config_name(), hostp);
                for(dp1 = hostp->disks; dp1 != NULL; dp1 = dp1->hostnext) {
                    if (dp1->todo)
-                       run_server_scripts(EXECUTE_ON_PRE_HOST_ESTIMATE,
-                                          get_config_name(), dp1,
-                                          est(dp1)->estimate[0].level);
-               }
-               for(dp1 = hostp->disks; dp1 != NULL; dp1 = dp1->hostnext) {
-                   if (dp1->todo)
-                       run_server_scripts(EXECUTE_ON_PRE_DLE_ESTIMATE,
+                       run_server_dle_scripts(EXECUTE_ON_PRE_DLE_ESTIMATE,
                                           get_config_name(), dp1,
                                           est(dp1)->estimate[0].level);
                }
@@ -1809,8 +1847,12 @@ static void handle_result(
     hostp->up = HOST_READY;
 
     if (pkt == NULL) {
-       errbuf = vstrallocf(_("Request to %s failed: %s"),
+       if (strcmp(security_geterror(sech), "timeout waiting for REP") == 0) {
+           errbuf = vstrallocf("Some estimate timeout on %s, using server estimate if possible", hostp->hostname);
+       } else {
+           errbuf = vstrallocf(_("Request to %s failed: %s"),
                        hostp->hostname, security_geterror(sech));
+       }
        goto error_return;
     }
     if (pkt->type == P_NAK) {
@@ -1952,7 +1994,8 @@ static void handle_result(
                break;
            }
        }
-       if (i == MAX_LEVELS) {
+       if (i == MAX_LEVELS && level > 0) {
+                       /* client always report level 0 for some error */
            goto bad_msg;               /* this est wasn't requested */
        }
        est(dp)->got_estimate++;
@@ -2064,7 +2107,7 @@ static void handle_result(
                est(dp)->estimate[1].nsize > (gint64)0) &&
              (est(dp)->estimate[2].level == -1 ||
                est(dp)->estimate[2].nsize > (gint64)0)))) {
-           run_server_scripts(EXECUTE_ON_POST_DLE_ESTIMATE,
+           run_server_dle_scripts(EXECUTE_ON_POST_DLE_ESTIMATE,
                               get_config_name(), dp,
                                est(dp)->estimate[0].level);
            est(dp)->post_dle = 1;
@@ -2073,13 +2116,9 @@ static void handle_result(
     }
 
     if(hostp->up == HOST_DONE) {
-       for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
-           if (dp->todo)
-               if (pkt->type == P_REP) {
-                   run_server_scripts(EXECUTE_ON_POST_HOST_ESTIMATE,
-                                      get_config_name(), dp,
-                                       est(dp)->estimate[0].level);
-           }
+       if (pkt->type == P_REP) {
+           run_server_host_scripts(EXECUTE_ON_POST_HOST_ESTIMATE,
+                                   get_config_name(), hostp);
        }
     }
 
@@ -2127,6 +2166,19 @@ static void handle_result(
         * reported.
         */
        log_add(L_ERROR, "%s", errbuf);
+       for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
+           if (dp->todo) {
+               qname = quote_string(dp->name);
+               est(dp)->state = DISK_DONE;
+               remove_disk(&waitq, dp);
+               enqueue_disk(&failq, dp);
+
+               est(dp)->errstr = g_strdup(errbuf);
+               g_fprintf(stderr, _("error result for host %s disk %s: %s\n"),
+                         dp->host->hostname, qname, errbuf);
+               amfree(qname);
+           }
+       }
     }
     hostp->up = HOST_DONE;
     amfree(errbuf);
@@ -2197,7 +2249,8 @@ static void analyze_estimate(
                /* fill in degraded mode info */
                g_fprintf(stderr,_("(picking inclevel for degraded mode)"));
                ep->degr_est = pick_inclevel(dp);
-               if (ep->degr_est->csize == (gint64)-1) {
+               if (ep->degr_est->level >= 0 &&
+                   ep->degr_est->csize == (gint64)-1) {
                     ep->degr_est = est_for_level(dp, ep->degr_est->level + 1);
                }
                if (ep->degr_est->csize == (gint64)-1) {
@@ -2263,7 +2316,15 @@ static void analyze_estimate(
        lev0size = est_tape_size(dp, 0);
        if(lev0size == (gint64)-1) lev0size = ep->last_lev0size;
 
-       balanced_size += (double)(lev0size / (gint64)runs_per_cycle);
+       if (dp->strategy == DS_NOINC) {
+           balanced_size += (double)lev0size;
+       } else if (dp->dumpcycle == 0) {
+           balanced_size += (double)(lev0size * conf_dumpcycle / (gint64)runs_per_cycle);
+       } else if (dp->dumpcycle != conf_dumpcycle) {
+           balanced_size += (double)(lev0size * (conf_dumpcycle / dp->dumpcycle) / (gint64)runs_per_cycle);
+       } else {
+           balanced_size += (double)(lev0size / (gint64)runs_per_cycle);
+       }
     }
 
     g_fprintf(stderr,_("total size %lld total_lev0 %1.0lf balanced-lev0size %1.0lf\n"),
@@ -2439,6 +2500,7 @@ static void delay_dumps(void)
     disk_t *   dp;
     disk_t *   ndp;
     disk_t *   preserve;
+    disk_t *   delayed_dp;
     bi_t *     bi;
     bi_t  *    nbi;
     gint64     new_total;              /* New total_size */
@@ -2448,6 +2510,8 @@ static void delay_dumps(void)
     int                delete;
     char *     message;
     gint64     full_size;
+    time_t      timestamps;
+    int         priority;
 
     biq.head = biq.tail = NULL;
 
@@ -2464,7 +2528,7 @@ static void delay_dumps(void)
 
     for(dp = schedq.head; dp != NULL; dp = ndp) {
        int avail_tapes = 1;
-       if (dp->splitsize > (gint64)0)
+       if (dp->splitsize > (gint64)0 || dp->allow_split)
            avail_tapes = conf_runtapes;
 
        ndp = dp->next; /* remove_disk zaps this */
@@ -2513,7 +2577,7 @@ static void delay_dumps(void)
            }
            else {
                delete = 0;
-               message = _("full dump delayed");
+               message = _("full dump delayed, doing incremental");
            }
        }
        else {
@@ -2539,51 +2603,71 @@ static void delay_dumps(void)
 
     nb_forced_level_0 = 0;
     preserve = NULL;
-    for(dp = schedq.head; dp != NULL && preserve == NULL; dp = dp->next)
-       if(est(dp)->dump_est->level == 0)
-           preserve = dp;
+    timestamps = 2147483647;
+    priority = 0;
+    for(dp = schedq.head; dp != NULL; dp = dp->next) {
+       if (est(dp)->dump_est->level == 0) {
+           if (!preserve ||
+               est(dp)->dump_priority > priority ||
+               (est(dp)->dump_priority == priority &&
+                est(dp)->info->inf[0].date < timestamps)) {
+               priority = est(dp)->dump_priority;
+               timestamps = est(dp)->info->inf[0].date;
+               preserve = dp;
+           }
+       }
+    }
 
     /* 2.a. Do not delay forced full */
-    for(dp = schedq.tail;
+    delayed_dp = NULL;
+    do {
+       delayed_dp = 0;
+       timestamps = 0;
+       for(dp = schedq.tail;
                dp != NULL && total_size > tape_length;
                dp = ndp) {
-       ndp = dp->prev;
-
-       if(est(dp)->dump_est->level != 0) continue;
+           ndp = dp->prev;
 
-       get_info(dp->host->hostname, dp->name, &info);
-       if(ISSET(info.command, FORCE_FULL)) {
-           nb_forced_level_0 += 1;
-           preserve = dp;
-           continue;
-       }
+           if(est(dp)->dump_est->level != 0) continue;
 
-       if(dp != preserve) {
+           get_info(dp->host->hostname, dp->name, &info);
+           if(ISSET(info.command, FORCE_FULL)) {
+               nb_forced_level_0 += 1;
+               preserve = dp;
+               continue;
+           }
 
+           if (dp != preserve &&
+               est(dp)->info->inf[0].date > timestamps) {
+               delayed_dp = dp;
+               timestamps = est(dp)->info->inf[0].date;
+           }
+       }
+       if (delayed_dp) {
            /* Format dumpsize for messages */
            g_snprintf(est_kb, 20, "%lld KB,",
-                       (long long)est(dp)->dump_est->csize);
+                       (long long)est(delayed_dp)->dump_est->csize);
 
-           if(dp->skip_incr) {
+           if(delayed_dp->skip_incr) {
                delete = 1;
                message = _("but cannot incremental dump skip-incr disk");
            }
-           else if(est(dp)->last_level < 0) {
+           else if(est(delayed_dp)->last_level < 0) {
                delete = 1;
                message = _("but cannot incremental dump new disk");
            }
-           else if(est(dp)->degr_est->level < 0) {
+           else if(est(delayed_dp)->degr_est->level < 0) {
                delete = 1;
                message = _("but no incremental estimate");
            }
            else {
                delete = 0;
-               message = _("full dump delayed");
+               message = _("full dump delayed, doing incremental");
            }
-           delay_one_dump(dp, delete, _("dumps too big,"), est_kb,
+           delay_one_dump(delayed_dp, delete, _("dumps too big,"), est_kb,
                           message, NULL);
        }
-    }
+    } while (delayed_dp);
 
     /* 2.b. Delay forced full if needed */
     if(nb_forced_level_0 > 0 && total_size > tape_length) {
@@ -2952,7 +3036,8 @@ static int promote_hills(void)
     }
 
     for(dp = schedq.head; dp != NULL; dp = dp->next) {
-       days = est(dp)->next_level0;   /* This is > 0 by definition */
+       days = est(dp)->next_level0;
+       if (days < 0) days = 0;
        if(days<my_dumpcycle && !dp->skip_full && dp->strategy != DS_NOFULL &&
           dp->strategy != DS_INCRONLY) {
            sp[days].disks++;