lintian doesn't like orphan packages with uploaders...
[debian/amanda] / server-src / planner.c
index 031e36b6fe0a0ae7a6353fd947560a84c7343b3a..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
@@ -30,6 +31,7 @@
  */
 #include "amanda.h"
 #include "arglist.h"
+#include "find.h"
 #include "conffile.h"
 #include "diskfile.h"
 #include "tapefile.h"
@@ -107,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)
@@ -189,6 +192,14 @@ main(
     char *cfg_opt = NULL;
     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:
@@ -255,11 +266,26 @@ main(
        g_fprintf(stderr, _("%s: %s"), get_pname(), version_info[i]);
 
     diskarg_offset = 2;
-    if (argc > 3 && strcmp(argv[2], "--starttime") == 0) {
-       planner_timestamp = stralloc(argv[3]);
+    if (argc - diskarg_offset > 1 && strcmp(argv[diskarg_offset], "--starttime") == 0) {
+       planner_timestamp = stralloc(argv[diskarg_offset+1]);
        diskarg_offset += 2;
     }
+    if (argc - diskarg_offset > 0 && strcmp(argv[diskarg_offset], "--no-taper") == 0) {
+       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
@@ -309,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);
@@ -329,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) {
@@ -409,12 +448,13 @@ main(
 
     g_fprintf(stderr,_("\nSENDING FLUSHES...\n"));
 
-    if(conf_autoflush) {
+    if(conf_autoflush && !no_taper) {
        dumpfile_t  file;
        GSList *holding_list, *holding_file;
        char *qdisk, *qhname;
 
-       /* get *all* flushable files in holding */
+       /* get *all* flushable files in holding, without checking against
+        * the disklist (which may not contain some of the dumps) */
        holding_list = holding_get_files_for_flush(NULL);
        for(holding_file=holding_list; holding_file != NULL;
                                       holding_file = holding_file->next) {
@@ -428,6 +468,13 @@ main(
                continue;
            }
 
+           /* see if this matches the command-line arguments */
+           if (conf_autoflush == 1 &&
+               !match_dumpfile(&file, exact_match, argc-diskarg_offset,
+                                      argv+diskarg_offset)) {
+               continue;
+           }
+
            qdisk = quote_string(file.disk);
            qhname = quote_string((char *)holding_file->data);
            log_add(L_DISK, "%s %s", file.name, qdisk);
@@ -449,7 +496,7 @@ main(
            amfree(qhname);
            dumpfile_free_data(&file);
        }
-       g_slist_free_full(holding_list);
+       slist_free_full(holding_list, g_free);
        holding_list = NULL;
     }
     g_fprintf(stderr, _("ENDFLUSH\n"));
@@ -539,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.
@@ -715,7 +764,7 @@ setup_estimate(
      disk_t *dp)
 {
     est_t *ep;
-    info_t info;
+    info_t *info;
     int i;
     char *qname;
     int overwrite_runs;
@@ -729,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);
     }
@@ -759,8 +809,6 @@ setup_estimate(
            amfree(qname);
            return;
        } else if (dp->to_holdingdisk == HOLD_AUTO) {
-           log_add(L_INFO, _("Disabling holding disk for %s:%s."),
-                   dp->host->hostname, qname);
            g_fprintf(stderr,_("%s:%s Disabling holding disk\n"),
                      dp->host->hostname, qname);
            dp->to_holdingdisk = HOLD_NEVER;
@@ -771,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;
@@ -782,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) {
            /*
@@ -796,21 +845,24 @@ setup_estimate(
             * hosed when that tape gets re-used next.  Disallow this for
             * now.
             */
-           log_add(L_ERROR,
+           log_add(L_WARNING,
                    _("Cannot force full dump of %s:%s with no-full option."),
                    dp->host->hostname, qname);
 
            /* clear force command */
-           CLR(info.command, FORCE_FULL);
-           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*/
-           }
-           ep->last_level = last_level(&info);
-           ep->next_level0 = next_level0(dp, &info);
-       }
-       else {
+           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);
+       } else {
            ep->degr_mesg = _("Skipping: force-full disk can't be dumped in degraded mode");
            ep->last_level = -1;
            ep->next_level0 = -conf_dumpcycle;
@@ -821,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);
        }
     }
@@ -870,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 */
 
@@ -879,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*/
@@ -893,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]"),
@@ -915,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"),
@@ -934,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);
@@ -956,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 */
 
@@ -972,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(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);
@@ -983,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 "
+               log_add(L_INFO, _("Ignoring skip-full for %s:%s "
                        "because the strategy is NOINC."),
                        dp->host->hostname, qname);
            }
-           if(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);
@@ -1001,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;
        }
@@ -1012,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 */
            }
@@ -1021,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
@@ -1045,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" :
@@ -1168,7 +1225,7 @@ static gint64 est_tape_size(
     one_est_t *dump_est;
 
     dump_est = est_for_level(dp, level);
-    if (dump_est->csize <= -1)
+    if (dump_est->level >= 0 && dump_est->csize <= -1)
        est_csize(dp, dump_est);
     return dump_est->csize;
 }
@@ -1241,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);
@@ -1307,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);
                }
@@ -1580,6 +1632,9 @@ static void getsize(
                    strappend(s, l);
                    s_len += strlen(l);
                    amfree(l);
+                   amfree(levelstr);
+                   amfree(spindlestr);
+                   amfree(o);
                } else if (strcmp(dp->program,"DUMP") != 0 &&
                           strcmp(dp->program,"GNUTAR") != 0) {
                    est(dp)->errstr = newvstrallocf(est(dp)->errstr,
@@ -1696,6 +1751,8 @@ static void getsize(
                    enqueue_disk(&failq, dp);
                }
            }
+           amfree(b64disk);
+           amfree(b64device);
            amfree(qname);
            amfree(qdevice);
        }
@@ -1724,6 +1781,7 @@ static void getsize(
        timeout = (time_t)getconf_int(CNF_CTIMEOUT);
     }
 
+    dbprintf(_("send request:\n----\n%s\n----\n\n"), req);
     secdrv = security_getdriver(hostp->disks->auth);
     if (secdrv == NULL) {
        hostp->up = HOST_DONE;
@@ -1789,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) {
@@ -1815,6 +1877,7 @@ static void handle_result(
        }
     }
 
+    dbprintf(_("got reply:\n----\n%s\n----\n\n"), pkt->body);
     s = pkt->body;
     ch = *s++;
     while(ch) {
@@ -1931,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++;
@@ -2043,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;
@@ -2052,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);
        }
     }
 
@@ -2106,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);
@@ -2148,7 +2221,7 @@ static void analyze_estimate(
     ep->degr_est = &default_one_est;
 
     if (ep->next_level0 <= 0 || (have_info && ep->last_level == 0
-       && (info.command & FORCE_NO_BUMP))) {
+       && (ISSET(info.command, FORCE_NO_BUMP)))) {
        if (ep->next_level0 <= 0) {
            g_fprintf(stderr,_("(due for level 0) "));
        }
@@ -2176,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) {
@@ -2208,6 +2282,24 @@ static void analyze_estimate(
        }
     }
 
+    if (ep->dump_est->level < 0) {
+       int   i;
+       char *q = quote_string("no estimate");
+
+       g_fprintf(stderr,_("  no valid estimate\n"));
+       for(i=0; i<MAX_LEVELS; i++) {
+           if (est(dp)->estimate[i].level >= 0) {
+               g_fprintf(stderr,("    level: %d  nsize: %lld csize: %lld\n"),
+                         est(dp)->estimate[i].level,
+                         (long long)est(dp)->estimate[i].nsize,
+                         (long long)est(dp)->estimate[i].csize);
+           }
+       }
+       log_add(L_WARNING, _("%s %s %s 0 %s"), dp->host->hostname, qname,
+               planner_timestamp, q);
+       amfree(q);
+    }
+
     g_fprintf(stderr,_("  curr level %d nsize %lld csize %lld "),
               ep->dump_est->level, (long long)ep->dump_est->nsize,
               (long long)ep->dump_est->csize);
@@ -2224,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"),
@@ -2319,9 +2419,11 @@ static one_est_t *pick_inclevel(
 
     /* if we didn't get an estimate, we can't do an inc */
     if (base_est->nsize == (gint64)-1) {
-       bump_est = est_for_level(dp, base_est->level + 1);
-       if (bump_est->nsize > (gint64)0) /* FORCE_BUMP */
+       bump_est = est_for_level(dp, est(dp)->last_level + 1);
+       if (bump_est->nsize > (gint64)0) { /* FORCE_BUMP */
+           g_fprintf(stderr,_("   picklev: bumping to level %d\n"), bump_est->level);
            return bump_est;
+       }
        g_fprintf(stderr,_("   picklev: no estimate for level %d, so no incs\n"), base_est->level);
        return base_est;
     }
@@ -2398,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 */
@@ -2407,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;
 
@@ -2423,7 +2528,7 @@ static void delay_dumps(void)
 
     for(dp = schedq.head; dp != NULL; dp = ndp) {
        int avail_tapes = 1;
-       if (dp->tape_splitsize > (gint64)0)
+       if (dp->splitsize > (gint64)0 || dp->allow_split)
            avail_tapes = conf_runtapes;
 
        ndp = dp->next; /* remove_disk zaps this */
@@ -2431,7 +2536,7 @@ static void delay_dumps(void)
        full_size = est_tape_size(dp, 0);
        if (full_size > tapetype_get_length(tape) * (gint64)avail_tapes) {
            char *qname = quote_string(dp->name);
-           if (conf_runtapes > 1 && dp->tape_splitsize == (gint64)0) {
+           if (conf_runtapes > 1 && dp->splitsize == (gint64)0) {
                log_add(L_WARNING, _("disk %s:%s, full dump (%lldKB) will be larger than available tape space"
                        ", you could define a splitsize"),
                        dp->host->hostname, qname,
@@ -2472,7 +2577,7 @@ static void delay_dumps(void)
            }
            else {
                delete = 0;
-               message = _("full dump delayed");
+               message = _("full dump delayed, doing incremental");
            }
        }
        else {
@@ -2498,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;
+           ndp = dp->prev;
 
-       if(est(dp)->dump_est->level != 0) continue;
+           if(est(dp)->dump_est->level != 0) continue;
 
-       get_info(dp->host->hostname, dp->name, &info);
-       if(info.command & FORCE_FULL) {
-           nb_forced_level_0 += 1;
-           preserve = dp;
-           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) {
@@ -2619,7 +2744,7 @@ static void delay_dumps(void)
        int avail_tapes = 1;
        nbi = bi->prev;
        dp = bi->dp;
-       if(dp->tape_splitsize > (gint64)0)
+       if(dp->splitsize > (gint64)0)
            avail_tapes = conf_runtapes;
 
        if(bi->deleted) {
@@ -2911,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++;