Imported Upstream version 3.3.1
[debian/amanda] / server-src / planner.c
index 1c612e4f53e73146d587991403f5aa30aa74efe5..c8501e2f35aced2ab93db61374600e0fd921c0ef 100644 (file)
  * file named AUTHORS, in the root directory of this distribution.
  */
 /*
- * $Id: planner.c,v 1.206 2006/08/10 23:57:27 paddy_s Exp $
+ * $Id: planner.c 10421 2008-03-06 18:48:30Z martineau $
  *
  * backup schedule planner for the Amanda backup system.
  */
 #include "amanda.h"
 #include "arglist.h"
+#include "find.h"
 #include "conffile.h"
 #include "diskfile.h"
 #include "tapefile.h"
@@ -44,6 +45,7 @@
 #include "server_util.h"
 #include "holding.h"
 #include "timestamp.h"
+#include "amxml.h"
 
 #define planner_debug(i,x) do {                \
        if ((i) <= debug_planner) {     \
@@ -61,7 +63,7 @@
 /* configuration file stuff */
 
 char * conf_tapetype;
-off_t  conf_maxdumpsize;
+gint64         conf_maxdumpsize;
 int    conf_runtapes;
 int    conf_dumpcycle;
 int    conf_runspercycle;
@@ -80,36 +82,41 @@ int conf_usetimestamps;
 #define DISK_PARTIALY_DONE                     2
 #define DISK_DONE                              3
 
+typedef struct one_est_s {
+    int     level;
+    gint64  nsize;     /* native size     */
+    gint64  csize;     /* compressed size */
+    char   *dumpdate;
+    int     guessed;    /* If server guessed the estimate size */
+} one_est_t;
+static one_est_t default_one_est = {-1, -1, -1, "INVALID_DATE", 0};
+
 typedef struct est_s {
     int state;
     int got_estimate;
     int dump_priority;
-    int dump_level;
-    off_t dump_nsize;  /* native size */
-    off_t dump_csize;  /* compressed size */
-    int degr_level;    /* if dump_level == 0, what would be the inc level */
-    off_t degr_nsize;  /* native degraded size */
-    off_t degr_csize;  /* compressed degraded size */
+    one_est_t *dump_est;
+    one_est_t *degr_est;
+    one_est_t  estimate[MAX_LEVELS];
     int last_level;
-    off_t last_lev0size;
+    gint64 last_lev0size;
     int next_level0;
     int level_days;
     int promote;
+    int post_dle;
     double fullrate, incrrate;
     double fullcomp, incrcomp;
     char *errstr;
-    int level[MAX_LEVELS];
-    char *dumpdate[MAX_LEVELS];
-    off_t est_size[MAX_LEVELS];
+    char *degr_mesg;
 } est_t;
 
 #define est(dp)        ((est_t *)(dp)->up)
 
 /* pestq = partial estimate */
 disklist_t startq, waitq, pestq, estq, failq, schedq;
-off_t total_size;
+gint64 total_size;
 double total_lev0, balanced_size, balance_threshold;
-off_t tape_length;
+gint64 tape_length;
 size_t tape_mark;
 
 tapetype_t *tape;
@@ -133,8 +140,8 @@ typedef struct bi_s {
     int deleted;               /* 0=modified, 1=deleted */
     disk_t *dp;                        /* The disk that was changed */
     int level;                 /* The original level */
-    off_t nsize;               /* The original native size */
-    off_t csize;               /* The original compressed size */
+    gint64 nsize;              /* The original native size */
+    gint64 csize;              /* The original compressed size */
     char *errstr;              /* A message describing why this disk is here */
 } bi_t;
 
@@ -158,6 +165,7 @@ static void delay_dumps(void);
 static int promote_highest_priority_incremental(void);
 static int promote_hills(void);
 static void output_scheduleline(disk_t *dp);
+static void server_estimate(disk_t *dp, int i, info_t *info, int level);
 int main(int, char **);
 
 int
@@ -169,7 +177,7 @@ main(
     disk_t *dp;
     int moved_one;
     int diskarg_offset;
-    off_t initial_size;
+    gint64 initial_size;
     int i;
     char *conf_diskfile;
     char *conf_tapelist;
@@ -178,8 +186,17 @@ main(
     char *qname;
     int    nb_disk;
     char  *errstr = NULL;
-    config_overwrites_t *cfg_ovr = NULL;
+    config_overrides_t *cfg_ovr = NULL;
     char *cfg_opt = NULL;
+    int    planner_setuid;
+    int exit_status = EXIT_SUCCESS;
+    gboolean no_taper = FALSE;
+    gboolean from_client = FALSE;
+
+    if (argc > 1 && argv && argv[1] && g_str_equal(argv[1], "--version")) {
+       printf("planner-%s\n", VERSION);
+       return (0);
+    }
 
     /*
      * Configure program for internationalization:
@@ -191,9 +208,7 @@ main(
     textdomain("amanda"); 
 
     /* drop root privileges */
-    if (!set_root_privs(0)) {
-       error(_("planner must be run setuid root"));
-    }
+    planner_setuid = set_root_privs(0);
 
     safe_fd(-1, 0);
 
@@ -201,45 +216,68 @@ main(
 
     dbopen(DBG_SUBDIR_SERVER);
 
-    cfg_ovr = extract_commandline_config_overwrites(&argc, &argv);
-
+    cfg_ovr = extract_commandline_config_overrides(&argc, &argv);
     if (argc > 1) 
        cfg_opt = argv[1];
 
-    config_init(CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_USE_CWD | CONFIG_INIT_FATAL,
-               cfg_opt);
-    apply_config_overwrites(cfg_ovr);
-
-    safe_cd();
-
-    check_running_as(RUNNING_AS_DUMPUSER);
+    set_config_overrides(cfg_ovr);
+    config_init(CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_USE_CWD, cfg_opt);
 
-    dbrename(config_name, DBG_SUBDIR_SERVER);
+    /* conf_diskfile is freed later, as it may be used in an error message */
+    conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
+    read_diskfile(conf_diskfile, &origq);
+    disable_skip_disk(&origq);
 
     /* Don't die when child closes pipe */
     signal(SIGPIPE, SIG_IGN);
 
     setvbuf(stderr, (char *)NULL, (int)_IOLBF, 0);
 
-    erroutput_type = (ERR_AMANDALOG|ERR_INTERACTIVE);
-    set_logerror(logerror);
+    add_amanda_log_handler(amanda_log_stderr);
+    add_amanda_log_handler(amanda_log_trace_log);
+
+    if (!planner_setuid) {
+       error(_("planner must be run setuid root"));
+    }
+
+    if (config_errors(NULL) >= CFGERR_ERRORS) {
+       g_critical(_("errors processing config file"));
+    }
+
+    safe_cd();
+
+    check_running_as(RUNNING_AS_ROOT | RUNNING_AS_UID_ONLY);
+
+    dbrename(get_config_name(), DBG_SUBDIR_SERVER);
+
     startclock();
     section_start = curclock();
 
     our_features = am_init_feature_set();
     our_feature_string = am_feature_to_string(our_features);
 
+    log_add(L_INFO, "%s pid %ld", get_pname(), (long)getpid());
     g_fprintf(stderr, _("%s: pid %ld executable %s version %s\n"),
-           get_pname(), (long) getpid(), argv[0], version());
+           get_pname(), (long) getpid(), argv[0], VERSION);
     for (i = 0; version_info[i] != NULL; i++)
        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;
+    }
+
 
+    run_server_global_scripts(EXECUTE_ON_PRE_ESTIMATE, get_config_name());
 
     /*
      * 1. Networking Setup
@@ -256,36 +294,11 @@ main(
 
     g_fprintf(stderr,_("READING CONF INFO...\n"));
 
-    conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
-    if (read_diskfile(conf_diskfile, &origq) < 0) {
-       error(_("could not load disklist \"%s\""), conf_diskfile);
-       /*NOTREACHED*/
-    }
     if(origq.head == NULL) {
        error(_("empty disklist \"%s\""), conf_diskfile);
        /*NOTREACHED*/
     }
 
-    errstr = match_disklist(&origq, argc-diskarg_offset,
-                                   argv+diskarg_offset);
-    if (errstr) {
-       g_fprintf(stderr,"%s",errstr);
-       amfree(errstr);
-    }
-    nb_disk = 0;
-    for(dp = origq.head; dp != NULL; dp = dp->next) {
-       if(dp->todo) {
-           qname = quote_string(dp->name);
-           log_add(L_DISK, "%s %s", dp->host->hostname, qname);
-           amfree(qname);
-           nb_disk++;
-       }
-    }
-
-    if(nb_disk == 0) {
-       error(_("no DLE to backup"));
-       /*NOTREACHED*/
-    }
     amfree(conf_diskfile);
 
     conf_tapelist = config_dir_relative(getconf_str(CNF_TAPELIST));
@@ -307,14 +320,14 @@ main(
     amfree(conf_infofile);
 
     conf_tapetype = getconf_str(CNF_TAPETYPE);
-    conf_maxdumpsize = getconf_am64(CNF_MAXDUMPSIZE);
+    conf_maxdumpsize = getconf_int64(CNF_MAXDUMPSIZE);
     conf_runtapes = getconf_int(CNF_RUNTAPES);
     conf_dumpcycle = getconf_int(CNF_DUMPCYCLE);
     conf_runspercycle = getconf_int(CNF_RUNSPERCYCLE);
     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);
@@ -334,6 +347,47 @@ main(
     g_fprintf(stderr, _("%s: timestamp %s\n"),
                    get_pname(), planner_timestamp);
 
+    errstr = match_disklist(&origq, 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) {
+           qname = quote_string(dp->name);
+           log_add(L_DISK, "%s %s", dp->host->hostname, qname);
+           amfree(qname);
+           nb_disk++;
+       }
+    }
+
+    if (nb_disk == 0) {
+       if (errstr) {
+           error(_("no DLE to backup; %s"), errstr);
+       } else {
+           error(_("no DLE to backup"));
+       }
+       /*NOTREACHED*/
+    } else if (errstr) {
+       log_add(L_WARNING, "WARNING: %s", errstr);
+    }
+    amfree(errstr);
+
     /* some initializations */
 
     if(conf_runspercycle == 0) {
@@ -356,11 +410,17 @@ main(
      }
     
     tape = lookup_tapetype(conf_tapetype);
-    if(conf_maxdumpsize > (off_t)0) {
-       tape_length = (off_t)conf_maxdumpsize;
+    if(conf_maxdumpsize > (gint64)0) {
+       tape_length = conf_maxdumpsize;
+       g_fprintf(stderr, "planner: tape_length is set from maxdumpsize (%jd KB)\n",
+                        (intmax_t)conf_maxdumpsize);
     }
     else {
-       tape_length = tapetype_get_length(tape) * (off_t)conf_runtapes;
+       tape_length = tapetype_get_length(tape) * (gint64)conf_runtapes;
+       g_fprintf(stderr, "planner: tape_length is set from tape length (%jd KB) * runtapes (%d) == %jd KB\n",
+                        (intmax_t)tapetype_get_length(tape),
+                        conf_runtapes,
+                        (intmax_t)tape_length);
     }
     tape_mark = (size_t)tapetype_get_filemark(tape);
     tt_blocksize_kb = (size_t)tapetype_get_blocksize(tape);
@@ -380,12 +440,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) {
@@ -395,6 +456,14 @@ main(
                log_add(L_INFO, "%s: removing file with no data.",
                        (char *)holding_file->data);
                holding_file_unlink((char *)holding_file->data);
+               dumpfile_free_data(&file);
+               continue;
+           }
+
+           /* see if this matches the command-line arguments */
+           if (conf_autoflush == 1 &&
+               !match_dumpfile(&file, argc-diskarg_offset,
+                                      argv+diskarg_offset)) {
                continue;
            }
 
@@ -417,8 +486,9 @@ main(
                    qhname);
            amfree(qdisk);
            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"));
@@ -483,6 +553,9 @@ main(
     dump_queue("FAILED", failq, 15, stderr);
     dump_queue("DONE", estq, 15, stderr);
 
+    if (!empty(failq)) {
+        exit_status = EXIT_FAILURE;
+    }
 
     /*
      * 6. Analyze Dump Estimates
@@ -497,7 +570,7 @@ main(
     section_start = curclock();
 
                        /* an empty tape still has a label and an endmark */
-    total_size = ((off_t)tt_blocksize_kb + (off_t)tape_mark) * (off_t)2;
+    total_size = ((gint64)tt_blocksize_kb + (gint64)tape_mark) * (gint64)2;
     total_lev0 = 0.0;
     balanced_size = 0.0;
 
@@ -505,6 +578,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.
@@ -519,9 +594,9 @@ main(
            qname = quote_string(dp->name);
            g_fprintf(stderr, _("  %s %s pri %d lev %d nsize %lld csize %lld\n"),
                    dp->host->hostname, qname, est(dp)->dump_priority,
-                   est(dp)->dump_level,
-                   (long long)est(dp)->dump_nsize,
-                    (long long)est(dp)->dump_csize);
+                   est(dp)->dump_est->level,
+                   (long long)est(dp)->dump_est->nsize,
+                    (long long)est(dp)->dump_est->csize);
            amfree(qname);
        }
     }
@@ -598,12 +673,17 @@ main(
      */
 
     g_fprintf(stderr,_("\nGENERATING SCHEDULE:\n--------\n"));
-
-    while(!empty(schedq)) output_scheduleline(dequeue_disk(&schedq));
+    if (empty(schedq)) {
+        exit_status = EXIT_FAILURE;
+        g_fprintf(stderr, _("--> Generated empty schedule! <--\n"));
+    } else {
+        while(!empty(schedq)) output_scheduleline(dequeue_disk(&schedq));
+    }
     g_fprintf(stderr, _("--------\n"));
 
     close_infofile();
     log_add(L_FINISH, _("date %s time %s"), planner_timestamp, walltime_str(curclock()));
+    log_add(L_INFO, "pid-done %ld", (long)getpid());
 
     clear_tapelist();
     amfree(planner_timestamp);
@@ -613,7 +693,7 @@ main(
 
     dbclose();
 
-    return 0;
+    return exit_status;
 }
 
 
@@ -626,11 +706,12 @@ main(
 
 static void askfor(est_t *, int, int, info_t *);
 static int last_level(info_t *info);             /* subroutines */
-static off_t est_size(disk_t *dp, int level);
-static off_t est_tape_size(disk_t *dp, int level);
+static one_est_t *est_for_level(disk_t *dp, int level);
+static void est_csize(disk_t *dp, one_est_t *one_est);
+static gint64 est_tape_size(disk_t *dp, int level);
 static int next_level0(disk_t *dp, info_t *info);
 static int runs_at(info_t *info, int lev);
-static off_t bump_thresh(int level, off_t size_level_0, int bumppercent, off_t bumpsize, double bumpmult);
+static gint64 bump_thresh(int level, gint64 size_level_0, int bumppercent, gint64 bumpsize, double bumpmult);
 static int when_overwrite(char *label);
 
 static void askfor(
@@ -651,17 +732,21 @@ static void askfor(
     }
 
     if (lev == -1) {
-       ep->level[seq] = -1;
-       ep->dumpdate[seq] = (char *)0;
-       ep->est_size[seq] = (off_t)-2;
+       ep->estimate[seq].level = -1;
+       ep->estimate[seq].dumpdate = (char *)0;
+       ep->estimate[seq].nsize = (gint64)-3;
+       ep->estimate[seq].csize = (gint64)-3;
+       ep->estimate[seq].guessed = 0;
        return;
     }
 
-    ep->level[seq] = lev;
+    ep->estimate[seq].level = lev;
 
-    ep->dumpdate[seq] = stralloc(get_dumpdate(info,lev));
+    ep->estimate[seq].dumpdate = stralloc(get_dumpdate(info,lev));
 
-    ep->est_size[seq] = (off_t)-2;
+    ep->estimate[seq].nsize = (gint64)-3;
+    ep->estimate[seq].csize = (gint64)-3;
+    ep->estimate[seq].guessed = 0;
 
     return;
 }
@@ -687,7 +772,38 @@ setup_estimate(
 
     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, dp->name);
+       log_add(L_INFO, _("Adding new disk %s:%s."), dp->host->hostname, qname);
+    }
+
+    if (dp->data_path == DATA_PATH_DIRECTTCP) {
+       if (dp->compress != COMP_NONE) {
+           log_add(L_FAIL, _("%s %s %s 0 [Can't compress directtcp data-path]"),
+                   dp->host->hostname, qname, planner_timestamp);
+           g_fprintf(stderr,_("%s:%s lev 0 skipped can't compress directtcp data-path\n"),
+                     dp->host->hostname, qname);
+           amfree(qname);
+           return;
+       }
+       if (dp->encrypt != ENCRYPT_NONE) {
+           log_add(L_FAIL, _("%s %s %s 0 [Can't encrypt directtcp data-path]"),
+                   dp->host->hostname, qname, planner_timestamp);
+           g_fprintf(stderr,_("%s:%s lev 0 skipped can't encrypt directtcp data-path\n"),
+                     dp->host->hostname, qname);
+           amfree(qname);
+           return;
+       }
+       if (dp->to_holdingdisk == HOLD_REQUIRED) {
+           log_add(L_FAIL, _("%s %s %s 0 [Holding disk can't be use for directtcp data-path]"),
+                   dp->host->hostname, qname, planner_timestamp);
+           g_fprintf(stderr,_("%s:%s lev 0 skipped Holding disk can't be use for directtcp data-path\n"),
+                     dp->host->hostname, qname);
+           amfree(qname);
+           return;
+       } else if (dp->to_holdingdisk == HOLD_AUTO) {
+           g_fprintf(stderr,_("%s:%s Disabling holding disk\n"),
+                     dp->host->hostname, qname);
+           dp->to_holdingdisk = HOLD_NEVER;
+       }
     }
 
     /* setup working data struct for disk */
@@ -695,11 +811,13 @@ setup_estimate(
     ep = alloc(SIZEOF(est_t));
     dp->up = (void *) ep;
     ep->state = DISK_READY;
-    ep->dump_nsize = (off_t)-1;
-    ep->dump_csize = (off_t)-1;
     ep->dump_priority = dp->priority;
     ep->errstr = 0;
     ep->promote = 0;
+    ep->post_dle = 0;
+    ep->degr_mesg = NULL;
+    ep->dump_est = &default_one_est;
+    ep->degr_est = &default_one_est;
 
     /* calculated fields */
 
@@ -717,21 +835,25 @@ 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 {
+       } 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;
            log_add(L_INFO, _("Forcing full dump of %s:%s as directed."),
@@ -895,7 +1017,7 @@ setup_estimate(
         (!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);
@@ -904,12 +1026,16 @@ setup_estimate(
        case DS_STANDARD: 
        case DS_NOINC:
            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);
@@ -929,6 +1055,8 @@ setup_estimate(
 
     if(!dp->skip_incr && !(dp->strategy == DS_NOINC)) {
        if(ep->last_level == -1) {              /* a new disk */
+           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);
            } else {
@@ -945,11 +1073,13 @@ setup_estimate(
                }
                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)
                       && curr_level + 1 < DUMP_LEVELS) {
                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);
            } else {
@@ -961,7 +1091,7 @@ 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 == (off_t)0 || /* no data, try it anyway */
+               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) {
@@ -985,11 +1115,11 @@ setup_estimate(
                 dp->skip_full ? "skip-full" :
                 dp->skip_incr ? "skip-incr" : "none",
            ep->last_level, ep->next_level0, ep->level_days,
-           ep->level[0], (long long)ep->est_size[0],
-           ep->level[1], (long long)ep->est_size[1],
-           ep->level[2], (long long)ep->est_size[2]);
+           ep->estimate[0].level, (long long)ep->estimate[0].nsize,
+           ep->estimate[1].level, (long long)ep->estimate[1].nsize,
+           ep->estimate[2].level, (long long)ep->estimate[2].nsize);
 
-    assert(ep->level[0] != -1);
+    assert(ep->estimate[0].level != -1);
     enqueue_disk(&startq, dp);
     amfree(qname);
 }
@@ -1014,35 +1144,42 @@ static int when_overwrite(
 }
 
 /* Return the estimated size for a particular dump */
-static off_t est_size(
+static one_est_t *
+est_for_level(
     disk_t *dp,
     int level)
 {
     int i;
 
-    for(i = 0; i < MAX_LEVELS; i++) {
-       if(level == est(dp)->level[i])
-           return est(dp)->est_size[i];
+    if (level < 0 || level >= DUMP_LEVELS)
+       return &default_one_est;
+
+    for (i = 0; i < MAX_LEVELS; i++) {
+        if (level == est(dp)->estimate[i].level) {
+            if (est(dp)->estimate[i].csize <= -1) {
+                est_csize(dp, &est(dp)->estimate[i]);
+            }
+           return &est(dp)->estimate[i];
+       }
     }
-    return (off_t)-1;
+    return &default_one_est;
 }
 
 /* Return the estimated on-tape size of a particular dump */
-static off_t est_tape_size(
-    disk_t *dp,
-    int level)
+static void
+est_csize(
+    disk_t    *dp,
+    one_est_t *one_est)
 {
-    off_t size;
+    gint64 size = one_est->nsize;
     double ratio;
 
-    size = est_size(dp, level);
-
-    if(size == (off_t)-1) return size;
-
-    if(dp->compress == COMP_NONE)
-       return size;
+    if (dp->compress == COMP_NONE) {
+        one_est->csize = one_est->nsize;
+        return;
+    }
 
-    if(level == 0) ratio = est(dp)->fullcomp;
+    if (one_est->level == 0) ratio = est(dp)->fullcomp;
     else ratio = est(dp)->incrcomp;
 
     /*
@@ -1054,20 +1191,32 @@ static off_t est_tape_size(
      * (RUG@USM.Uni-Muenchen.DE)
      */
 
-    if(ratio > 1.1) ratio = 1.1;
+    if (ratio > 1.1) ratio = 1.1;
 
-    size = (off_t)((double)size * ratio);
+    size = (gint64)((double)size * ratio);
 
     /*
      * Ratio can be very small in some error situations, so make sure
      * size goes back greater than zero.  It may not be right, but
      * indicates we did get an estimate.
      */
-    if(size <= (off_t)0) {
-       size = (off_t)1;
+    if (size <= (gint64)0) {
+       size = (gint64)1;
     }
 
-    return size;
+    one_est->csize = size;
+}
+
+static gint64 est_tape_size(
+    disk_t *dp,
+    int level)
+{
+    one_est_t *dump_est;
+
+    dump_est = est_for_level(dp, level);
+    if (dump_est->level >= 0 && dump_est->csize <= -1)
+       est_csize(dp, dump_est);
+    return dump_est->csize;
 }
 
 
@@ -1158,16 +1307,16 @@ static int runs_at(
 }
 
 
-static off_t bump_thresh(
+static gint64 bump_thresh(
     int level,
-    off_t size_level_0,
+    gint64 size_level_0,
     int bumppercent,
-    off_t bumpsize,
+    gint64 bumpsize,
     double bumpmult)
 {
     double bump;
 
-    if ((bumppercent != 0) && (size_level_0 > (off_t)1024)) {
+    if ((bumppercent != 0) && (size_level_0 > (gint64)1024)) {
        bump = ((double)size_level_0 * (double)bumppercent) / 100.0;
     }
     else {
@@ -1175,7 +1324,7 @@ static off_t bump_thresh(
     }
     while(--level) bump = bump * bumpmult;
 
-    return (off_t)bump;
+    return (gint64)bump;
 }
 
 
@@ -1194,7 +1343,7 @@ static void handle_result(void *datap, pkt_t *pkt, security_handle_t *sech);
 static void get_estimates(void)
 {
     am_host_t *hostp;
-    disk_t *dp;
+    disk_t *dp, *dp1;
     int something_started;
 
     something_started = 1;
@@ -1204,6 +1353,14 @@ 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_dle_scripts(EXECUTE_ON_PRE_DLE_ESTIMATE,
+                                          get_config_name(), dp1,
+                                          est(dp1)->estimate[0].level);
+               }
                getsize(hostp);
                protocol_check();
                /*
@@ -1225,51 +1382,26 @@ static void get_estimates(void)
     while(!empty(pestq)) {
        disk_t *dp = dequeue_disk(&pestq);
        char *  qname = quote_string(dp->name);
-       
-       if(est(dp)->level[0] != -1 && est(dp)->est_size[0] < (off_t)0) {
-           if(est(dp)->est_size[0] == (off_t)-1) {
-               log_add(L_WARNING, _("disk %s:%s, estimate of level %d failed."),
-                       dp->host->hostname, qname, est(dp)->level[0]);
-           }
-           else {
-               log_add(L_WARNING,
-                       _("disk %s:%s, estimate of level %d timed out."),
-                       dp->host->hostname, qname, est(dp)->level[0]);
-           }
-           est(dp)->level[0] = -1;
-       }
-
-       if(est(dp)->level[1] != -1 && est(dp)->est_size[1] < (off_t)0) {
-           if(est(dp)->est_size[1] == (off_t)-1) {
-               log_add(L_WARNING,
-                       _("disk %s:%s, estimate of level %d failed."),
-                       dp->host->hostname, qname, est(dp)->level[1]);
-           }
-           else {
-               log_add(L_WARNING,
-                       _("disk %s:%s, estimate of level %d timed out."),
-                       dp->host->hostname, qname, est(dp)->level[1]);
-           }
-           est(dp)->level[1] = -1;
-       }
-
-       if(est(dp)->level[2] != -1 && est(dp)->est_size[2] < (off_t)0) {
-           if(est(dp)->est_size[2] == (off_t)-1) {
-               log_add(L_WARNING,
-                       _("disk %s:%s, estimate of level %d failed."),
-                       dp->host->hostname, qname, est(dp)->level[2]);
-           }
-           else {
-               log_add(L_WARNING,
-                       _("disk %s:%s, estimate of level %d timed out."),
-                       dp->host->hostname, qname, est(dp)->level[2]);
-           }
-           est(dp)->level[2] = -1;
-       }
-
-       if((est(dp)->level[0] != -1 && est(dp)->est_size[0] > (off_t)0) ||
-          (est(dp)->level[1] != -1 && est(dp)->est_size[1] > (off_t)0) ||
-          (est(dp)->level[2] != -1 && est(dp)->est_size[2] > (off_t)0)) {
+       int i;
+
+       for (i=0; i < MAX_LEVELS; i++) {
+           if (est(dp)->estimate[i].level != -1 &&
+               est(dp)->estimate[i].nsize < (gint64)0) {
+               if (est(dp)->estimate[i].nsize == (gint64)-3) {
+                   log_add(L_WARNING,
+                           _("disk %s:%s, estimate of level %d timed out."),
+                           dp->host->hostname, qname, est(dp)->estimate[i].level);
+               }
+               est(dp)->estimate[i].level = -1;
+            }
+       }
+
+       if ((est(dp)->estimate[0].level != -1 &&
+            est(dp)->estimate[0].nsize > (gint64)0) ||
+           (est(dp)->estimate[1].level != -1 &&
+             est(dp)->estimate[1].nsize > (gint64)0) ||
+           (est(dp)->estimate[2].level != -1 &&
+             est(dp)->estimate[2].nsize > (gint64)0)) {
            enqueue_disk(&estq, dp);
        }
        else {
@@ -1290,10 +1422,11 @@ static void getsize(
     time_t     estimates, timeout;
     size_t     req_len;
     const      security_driver_t *secdrv;
-    char *     backup_api;
     char *     calcsize;
-    char *     qname;
-    char *     qdevice;
+    char *     qname, *b64disk = NULL;
+    char *     qdevice, *b64device = NULL;
+    estimate_t     estimate;
+    estimatelist_t el;
 
     assert(hostp->disks != NULL);
 
@@ -1334,7 +1467,7 @@ static void getsize(
                        has_hostname ? hostp->hostname : "",
                        has_hostname ? ";" : "",
                        has_config   ? "config=" : "",
-                       has_config   ? config_name : "",
+                       has_config   ? get_config_name() : "",
                        has_config   ? ";" : "",
                        "\n",
                        NULL);
@@ -1343,229 +1476,273 @@ static void getsize(
        estimates = 0;
        for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
            char *s = NULL;
+           char *es;
            size_t s_len = 0;
+           GPtrArray *errarray;
 
            if(dp->todo == 0) continue;
 
            if(est(dp)->state != DISK_READY) continue;
 
            est(dp)->got_estimate = 0;
-           if(est(dp)->level[0] == -1) {
+           if (est(dp)->estimate[0].level == -1) {
                est(dp)->state = DISK_DONE;
                continue;
            }
 
            qname = quote_string(dp->name);
-           qdevice = quote_string(dp->device);
-           if(dp->estimate == ES_CLIENT ||
-              dp->estimate == ES_CALCSIZE) {
-               nb_client++;
 
+           errarray = validate_optionstr(dp);
+           if (errarray->len > 0) {
+               guint i;
+               for (i=0; i < errarray->len; i++) {
+                   log_add(L_FAIL, _("%s %s %s 0 [%s]"),
+                           dp->host->hostname, qname,
+                           planner_timestamp,
+                           (char *)g_ptr_array_index(errarray, i));
+               }
+               amfree(qname);
+               est(dp)->state = DISK_DONE;
+               continue;
+           }
+                   
+           b64disk = amxml_format_tag("disk", dp->name);
+           qdevice = quote_string(dp->device);
+           estimate = (estimate_t)GPOINTER_TO_INT(dp->estimatelist->data);
+           if (dp->device)
+               b64device = amxml_format_tag("diskdevice", dp->device);
+
+           estimate = ES_CLIENT;
+           for (el = dp->estimatelist; el != NULL; el = el->next) {
+               estimate = (estimate_t)GPOINTER_TO_INT(el->data);
+               if (estimate == ES_SERVER)
+                   break;
+           }
+           if (estimate == ES_SERVER) {
+               info_t info;
+               nb_server++;
+               get_info(dp->host->hostname, dp->name, &info);
                for(i = 0; i < MAX_LEVELS; i++) {
-                   char *l;
-                   char *exclude1 = "";
-                   char *exclude2 = "";
-                   char *excludefree = NULL;
-                   char *include1 = "";
-                   char *include2 = "";
-                   char *includefree = NULL;
-                   char spindle[NUM_STR_SIZE];
-                   char level[NUM_STR_SIZE];
-                   int lev = est(dp)->level[i];
+                   int lev = est(dp)->estimate[i].level;
 
                    if(lev == -1) break;
+                   server_estimate(dp, i, &info, lev);
+               }
+               g_fprintf(stderr,_("%s time %s: got result for host %s disk %s:"),
+                       get_pname(), walltime_str(curclock()),
+                       dp->host->hostname, qname);
+               g_fprintf(stderr,_(" %d -> %lldK, %d -> %lldK, %d -> %lldK\n"),
+                         est(dp)->estimate[0].level,
+                         (long long)est(dp)->estimate[0].nsize,
+                         est(dp)->estimate[1].level,
+                          (long long)est(dp)->estimate[1].nsize,
+                         est(dp)->estimate[2].level,
+                          (long long)est(dp)->estimate[2].nsize);
+               if (!am_has_feature(hostp->features, fe_xml_estimate)) {
+                   est(dp)->state = DISK_DONE;
+                   remove_disk(&startq, dp);
+                   enqueue_disk(&estq, dp);
+               }
+           }
 
-                   g_snprintf(level, SIZEOF(level), "%d", lev);
-                   g_snprintf(spindle, SIZEOF(spindle), "%d", dp->spindle);
-                   if(am_has_feature(hostp->features,fe_sendsize_req_options)){
-                       exclude1 = " OPTIONS |";
-                       exclude2 = optionstr(dp, hostp->features, NULL);
-                       if ( exclude2 == NULL ) {
-                         error(_("problem with option string, check the dumptype definition.\n"));
+           estimate = ES_SERVER;
+           for (el = dp->estimatelist; el != NULL; el = el->next) {
+               estimate = (estimate_t)GPOINTER_TO_INT(el->data);
+               if (estimate == ES_CLIENT || estimate == ES_CALCSIZE)
+                   break;
+           }
+           if (estimate == ES_CLIENT ||
+               estimate == ES_CALCSIZE ||
+               (am_has_feature(hostp->features, fe_req_xml) &&
+                am_has_feature(hostp->features, fe_xml_estimate))) {
+               nb_client++;
+               i = 0;
+
+               if (am_has_feature(hostp->features, fe_req_xml)) {
+                   char *levelstr = NULL;
+                   char *spindlestr = NULL;
+                   char level[NUM_STR_SIZE];
+                   char spindle[NUM_STR_SIZE];
+                   char *o;
+                   char *l;
+                   info_t info;
+
+                   get_info(dp->host->hostname, dp->name, &info);
+                   for(i = 0; i < MAX_LEVELS; i++) {
+                       char *server;
+                       int lev = est(dp)->estimate[i].level;
+                       if (lev == -1) break;
+                       g_snprintf(level, SIZEOF(level), "%d", lev);
+                       if (am_has_feature(hostp->features, fe_xml_level_server) &&
+                           server_can_do_estimate(dp, &info, lev)) {
+                           server = "<server>YES</server>";
+                       } else {
+                           server = "";
                        }
-                       excludefree = exclude2;
-                       includefree = NULL;
+                       vstrextend(&levelstr, "  <level>",
+                                  level, server,
+                                  "</level>\n", NULL);
                    }
-                   else {
-                       if(dp->exclude_file &&
-                          dp->exclude_file->nb_element == 1) {
-                           exclude1 = " exclude-file=";
-                           exclude2 =
-                               quote_string(dp->exclude_file->first->name);
-                           excludefree = exclude2;
-                       }
-                       else if(dp->exclude_list &&
-                               dp->exclude_list->nb_element == 1) {
-                           exclude1 = " exclude-list=";
-                           exclude2 =
-                               quote_string(dp->exclude_list->first->name);
-                           excludefree = exclude2;
-                       }
-                       if(dp->include_file &&
-                          dp->include_file->nb_element == 1) {
-                           include1 = " include-file=";
-                           include2 =
-                               quote_string(dp->include_file->first->name);
-                           includefree = include2;
-                       }
-                       else if(dp->include_list &&
-                               dp->include_list->nb_element == 1) {
-                           include1 = " include-list=";
-                           include2 =
-                               quote_string(dp->include_list->first->name);
-                           includefree = include2;
+                   g_snprintf(spindle, SIZEOF(spindle), "%d", dp->spindle);
+                   spindlestr = vstralloc("  <spindle>",
+                                          spindle,
+                                          "</spindle>\n", NULL);
+                   o = xml_optionstr(dp, 0);
+                   
+                   if (strcmp(dp->program,"DUMP") == 0 ||
+                       strcmp(dp->program,"GNUTAR") == 0) {
+                       l = vstralloc("<dle>\n",
+                                     "  <program>",
+                                     dp->program,
+                                     "</program>\n", NULL);
+                   } else {
+                       l = vstralloc("<dle>\n",
+                                     "  <program>APPLICATION</program>\n",
+                                     NULL);
+                       if (dp->application) {
+                           application_t *application;
+                           char *xml_app;
+
+                           application = lookup_application(dp->application);
+                           g_assert(application != NULL);
+                           xml_app = xml_application(dp, application,
+                                                     hostp->features);
+                           vstrextend(&l, xml_app, NULL);
+                           amfree(xml_app);
                        }
                    }
 
-                   if(dp->estimate == ES_CALCSIZE &&
-                      !am_has_feature(hostp->features, fe_calcsize_estimate)) {
-                       log_add(L_WARNING,_("%s:%s does not support CALCSIZE for estimate, using CLIENT.\n"),
-                               hostp->hostname, qname);
-                       dp->estimate = ES_CLIENT;
-                   }
-                   if(dp->estimate == ES_CLIENT)
-                       calcsize = "";
-                   else
-                       calcsize = "CALCSIZE ";
-
-                   if(strcmp(dp->program,"DUMP") == 0 || 
-                      strcmp(dp->program,"GNUTAR") == 0) {
-                       backup_api = "";
-                   } else {
-                       backup_api = "BACKUP ";
-                   }
-                   l = vstralloc(calcsize,
-                                 backup_api,
-                                 dp->program,
-                                 " ", qname,
-                                 " ", dp->device ? qdevice : "",
-                                 " ", level,
-                                 " ", est(dp)->dumpdate[i],
-                                 " ", spindle,
-                                 " ", exclude1, exclude2,
-                                 ((includefree != NULL) ? " " : ""),
-                                   include1, include2,
-                                 "\n",
-                                 NULL);
+                   es = xml_estimate(dp->estimatelist, hostp->features);
+                   vstrextend(&l, es, "\n", NULL);
+                   amfree(es);
+                   vstrextend(&l, "  ", b64disk, "\n", NULL);
+                   if (dp->device)
+                       vstrextend(&l, "  ", b64device, "\n", NULL);
+                   vstrextend(&l, levelstr, spindlestr, o, "</dle>\n", NULL);
                    strappend(s, l);
                    s_len += strlen(l);
                    amfree(l);
-                   amfree(includefree);
-                   amfree(excludefree);
-               }
-               if (s != NULL) {
-                   estimates += i;
-                   strappend(req, s);
-                   req_len += s_len;
-                   amfree(s);
-               }
-               est(dp)->state = DISK_ACTIVE;
-               remove_disk(&startq, dp);
-           }
-           else if (dp->estimate == ES_SERVER) {
-               info_t info;
-               nb_server++;
-               get_info(dp->host->hostname, dp->name, &info);
-               for(i = 0; i < MAX_LEVELS; i++) {
-                   int j;
-                   int lev = est(dp)->level[i];
-
-                   if(lev == -1) break;
-                   if(lev == 0) { /* use latest level 0, should do extrapolation */
-                       off_t est_size = (off_t)0;
-                       int nb_est = 0;
-
-                       for(j=NB_HISTORY-2;j>=0;j--) {
-                           if(info.history[j].level == 0) {
-                               if(info.history[j].size < (off_t)0) continue;
-                               est_size = info.history[j].size;
-                               nb_est++;
+                   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,
+                                       _("does not support application-api"));
+               } else {
+                   for(i = 0; i < MAX_LEVELS; i++) {
+                       char *l;
+                       char *exclude1 = "";
+                       char *exclude2 = "";
+                       char *excludefree = NULL;
+                       char *include1 = "";
+                       char *include2 = "";
+                       char *includefree = NULL;
+                       char spindle[NUM_STR_SIZE];
+                       char level[NUM_STR_SIZE];
+                       int lev = est(dp)->estimate[i].level;
+
+                       if(lev == -1) break;
+
+                       g_snprintf(level, SIZEOF(level), "%d", lev);
+                       g_snprintf(spindle, SIZEOF(spindle), "%d", dp->spindle);
+                       if (am_has_feature(hostp->features,
+                                          fe_sendsize_req_options)){
+                           exclude1 = " OPTIONS |";
+                           exclude2 = optionstr(dp);
+                           if ( exclude2 == NULL ) {
+                               error(_("problem with option string, check the dumptype definition.\n"));
                            }
-                       }
-                       if(nb_est > 0) {
-                           est(dp)->est_size[i] = est_size;
-                       }
-                       else if(info.inf[lev].size > (off_t)1000) { /* stats */
-                           est(dp)->est_size[i] = info.inf[lev].size;
-                       }
-                       else {
-                           est(dp)->est_size[i] = (off_t)1000000;
-                       }
-                   }
-                   else if(lev == est(dp)->last_level) {
-                       /* means of all X day at the same level */
-                       #define NB_DAY 30
-                       int nb_day = 0;
-                       off_t est_size_day[NB_DAY];
-                       int nb_est_day[NB_DAY];
-                       for(j=0;j<NB_DAY;j++) {
-                           est_size_day[j]=(off_t)0;
-                           nb_est_day[j]=0;
-                       }
-
-                       for(j=NB_HISTORY-2;j>=0;j--) {
-                           if(info.history[j].level <= 0) continue;
-                           if(info.history[j].size < (off_t)0) continue;
-                           if(info.history[j].level==info.history[j+1].level) {
-                               if(nb_day <NB_DAY-1) nb_day++;
-                               est_size_day[nb_day] += info.history[j].size;
-                               nb_est_day[nb_day]++;
+                           excludefree = exclude2;
+                           includefree = NULL;
+                       } else {
+                           if (dp->exclude_file &&
+                               dp->exclude_file->nb_element == 1) {
+                               exclude1 = " exclude-file=";
+                               exclude2 = quote_string(
+                                               dp->exclude_file->first->name);
+                               excludefree = exclude2;
                            }
-                           else {
-                               nb_day=0;
+                           else if (dp->exclude_list &&
+                                    dp->exclude_list->nb_element == 1) {
+                               exclude1 = " exclude-list=";
+                               exclude2 = quote_string(
+                                               dp->exclude_list->first->name);
+                               excludefree = exclude2;
+                           }
+                           if (dp->include_file &&
+                               dp->include_file->nb_element == 1) {
+                               include1 = " include-file=";
+                               include2 = quote_string(
+                                               dp->include_file->first->name);
+                               includefree = include2;
+                           }
+                           else if (dp->include_list &&
+                                    dp->include_list->nb_element == 1) {
+                               include1 = " include-list=";
+                               include2 = quote_string(
+                                               dp->include_list->first->name);
+                               includefree = include2;
                            }
                        }
-                       nb_day = info.consecutive_runs + 1;
-                       if(nb_day > NB_DAY-1) nb_day = NB_DAY-1;
 
-                       while(nb_day > 0 && nb_est_day[nb_day] == 0) nb_day--;
-
-                       if(nb_est_day[nb_day] > 0) {
-                           est(dp)->est_size[i] = est_size_day[nb_day] /
-                                       (off_t)nb_est_day[nb_day];
-                       }
-                       else if(info.inf[lev].size > (off_t)1000) { /* stats */
-                           est(dp)->est_size[i] = info.inf[lev].size;
-                       }
-                       else {
-                           est(dp)->est_size[i] = (off_t)10000;
+                       if (estimate == ES_CALCSIZE &&
+                           !am_has_feature(hostp->features,
+                                           fe_calcsize_estimate)) {
+                           log_add(L_WARNING,
+                                   _("%s:%s does not support CALCSIZE for estimate, using CLIENT.\n"),
+                                   hostp->hostname, qname);
+                           estimate = ES_CLIENT;
                        }
+                       if(estimate == ES_CLIENT)
+                           calcsize = "";
+                       else
+                           calcsize = "CALCSIZE ";
+
+                       l = vstralloc(calcsize,
+                                     dp->program,
+                                     " ", qname,
+                                     " ", dp->device ? qdevice : "",
+                                     " ", level,
+                                     " ", est(dp)->estimate[i].dumpdate,
+                                     " ", spindle,
+                                     " ", exclude1, exclude2,
+                                     ((includefree != NULL) ? " " : ""),
+                                       include1, include2,
+                                     "\n",
+                                     NULL);
+                       strappend(s, l);
+                       s_len += strlen(l);
+                       amfree(l);
+                       amfree(includefree);
+                       amfree(excludefree);
                    }
-                   else if(lev == est(dp)->last_level + 1) {
-                       /* means of all first day at a new level */
-                       off_t est_size = (off_t)0;
-                       int nb_est = 0;
-
-                       for(j=NB_HISTORY-2;j>=0;j--) {
-                           if(info.history[j].level <= 0) continue;
-                           if(info.history[j].size < (off_t)0) continue;
-                           if(info.history[j].level == info.history[j+1].level + 1 ) {
-                               est_size += info.history[j].size;
-                               nb_est++;
-                           }
-                       }
-                       if(nb_est > 0) {
-                           est(dp)->est_size[i] = est_size / (off_t)nb_est;
-                       }
-                       else if(info.inf[lev].size > (off_t)1000) { /* stats */
-                           est(dp)->est_size[i] = info.inf[lev].size;
-                       }
-                       else {
-                           est(dp)->est_size[i] = (off_t)100000;
-                       }
+               }
+               if (s != NULL) {
+                   estimates += i;
+                   strappend(req, s);
+                   req_len += s_len;
+                   amfree(s);
+                   if (est(dp)->state == DISK_DONE) {
+                       remove_disk(&estq, dp);
+                       est(dp)->state = DISK_PARTIALY_DONE;
+                       enqueue_disk(&pestq, dp);
+                   } else {
+                       remove_disk(&startq, dp);
+                       est(dp)->state = DISK_ACTIVE;
                    }
+               } else if (est(dp)->state != DISK_DONE) {
+                   remove_disk(&startq, dp);
+                   est(dp)->state = DISK_DONE;
+                   if (est(dp)->errstr == NULL) {
+                       est(dp)->errstr = vstrallocf(
+                                               _("Can't request estimate"));
+                   }
+                   enqueue_disk(&failq, dp);
                }
-               g_fprintf(stderr,_("%s time %s: got result for host %s disk %s:"),
-                       get_pname(), walltime_str(curclock()),
-                       dp->host->hostname, qname);
-               g_fprintf(stderr,_(" %d -> %lldK, %d -> %lldK, %d -> %lldK\n"),
-                       est(dp)->level[0], (long long)est(dp)->est_size[0],
-                       est(dp)->level[1], (long long)est(dp)->est_size[1],
-                       est(dp)->level[2], (long long)est(dp)->est_size[2]);
-               est(dp)->state = DISK_DONE;
-               remove_disk(&startq, dp);
-               enqueue_disk(&estq, dp);
            }
+           amfree(b64disk);
+           amfree(b64device);
            amfree(qname);
            amfree(qdevice);
        }
@@ -1594,12 +1771,13 @@ static void getsize(
        timeout = (time_t)getconf_int(CNF_CTIMEOUT);
     }
 
-    secdrv = security_getdriver(hostp->disks->security_driver);
+    dbprintf(_("send request:\n----\n%s\n----\n\n"), req);
+    secdrv = security_getdriver(hostp->disks->auth);
     if (secdrv == NULL) {
        hostp->up = HOST_DONE;
        log_add(L_ERROR,
                _("Could not find security driver '%s' for host '%s'"),
-               hostp->disks->security_driver, hostp->hostname);
+               hostp->disks->auth, hostp->hostname);
        amfree(req);
        return;
     }
@@ -1640,7 +1818,7 @@ static void handle_result(
     security_handle_t *sech)
 {
     int level, i;
-    off_t size;
+    gint64 size;
     disk_t *dp;
     am_host_t *hostp;
     char *msg, msg_undo;
@@ -1659,8 +1837,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) {
@@ -1685,6 +1867,7 @@ static void handle_result(
        }
     }
 
+    dbprintf(_("got reply:\n----\n%s\n----\n\n"), pkt->body);
     s = pkt->body;
     ch = *s++;
     while(ch) {
@@ -1692,7 +1875,10 @@ static void handle_result(
 
        if(strncmp_const(line, "OPTIONS ") == 0) {
            t = strstr(line, "features=");
-           if(t != NULL && (isspace((int)t[-1]) || t[-1] == ';')) {
+           if(t != NULL && (g_ascii_isspace((int)t[-1]) || t[-1] == ';')) {
+               char *u = strchr(t, ';');
+               if (u)
+                  *u = '\0';
                t += SIZEOF("features=")-1;
                am_release_feature_set(hostp->features);
                if((hostp->features = am_string_to_feature(t)) == NULL) {
@@ -1700,13 +1886,16 @@ static void handle_result(
                                       _(": bad features value: %s\n"), line);
                    goto error_return;
                }
+               if (u)
+                  *u = ';';
            }
            skip_quoted_line(s, ch);
            continue;
        }
 
        t = line;
-       if(strncmp_const_skip(t, "ERROR ", t, tch) == 0) {
+       if ((strncmp_const_skip(t, "ERROR ", t, tch) == 0) ||
+           (strncmp_const_skip(t, "WARNING ", t, tch) == 0)) { 
            fp = t - 1;
            skip_whitespace(t, tch);
            if (tch == '\n') {
@@ -1725,6 +1914,9 @@ static void handle_result(
                skip_quoted_line(s, ch);
                continue;
            }
+           t = strchr(t,'\n');
+           if (t) /* truncate after the first line */
+                *t = '\0';
            errbuf = vstralloc(hostp->hostname,
                                   (pkt->type == P_NAK) ? "NAK " : "",
                                   ": ",
@@ -1748,7 +1940,6 @@ static void handle_result(
        skip_integer(t, tch);
        skip_whitespace(t, tch);
 
-       dp = lookup_hostdisk(hostp, disk);
        dp = lookup_hostdisk(hostp, disk);
        if(dp == NULL) {
            log_add(L_ERROR, _("%s: invalid reply from sendsize: `%s'\n"),
@@ -1756,20 +1947,21 @@ static void handle_result(
            goto bad_msg;
        }
 
-       size = (off_t)-1;
+       size = (gint64)-1;
        if (strncmp_const(t-1,"SIZE ") == 0) {
            if (sscanf(t - 1, "SIZE %lld", &size_) != 1) {
                goto bad_msg;
            }
-           size = (off_t)size_;
-       } else if (strncmp_const(t-1,"ERROR ") == 0) {
+           size = (gint64)size_;
+       } else if ((strncmp_const(t-1,"ERROR ") == 0) ||
+                  (strncmp_const(t-1,"WARNING ") == 0)) {
            skip_non_whitespace(t, tch);
            skip_whitespace(t, tch);
            msg = t-1;
            skip_quoted_string(t,tch);
            msg_undo = t[-1];
            t[-1] = '\0';
-           if (pkt->type == P_REP) {
+           if (pkt->type == P_REP && !est(dp)->errstr) {
                est(dp)->errstr = unquote_string(msg);
            }
            t[-1] = msg_undo;
@@ -1779,18 +1971,25 @@ static void handle_result(
 
        amfree(disk);
 
-       if (size > (off_t)-1) {
-           for(i = 0; i < MAX_LEVELS; i++) {
-               if(est(dp)->level[i] == level) {
-                   est(dp)->est_size[i] = size;
-                   break;
+       for (i = 0; i < MAX_LEVELS; i++) {
+           if (est(dp)->estimate[i].level == level) {
+               if (size == (gint64)-2) {
+                   est(dp)->estimate[i].nsize = -1; /* remove estimate */
+                   est(dp)->estimate[i].guessed = 0;
+               } else if (size > (gint64)-1) {
+                   /* take the size returned by the client */
+                   est(dp)->estimate[i].nsize = size;
+                   est(dp)->estimate[i].guessed = 0;
                }
+               break;
            }
-           if(i == MAX_LEVELS) {
-               goto bad_msg;                   /* this est wasn't requested */
-           }
-           est(dp)->got_estimate++;
        }
+       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++;
+
        s = t;
        ch = tch;
        skip_quoted_line(s, ch);
@@ -1829,7 +2028,7 @@ static void handle_result(
            est(dp)->state = DISK_PARTIALY_DONE;
        }
 
-       if(est(dp)->level[0] == -1) continue;   /* ignore this disk */
+       if (est(dp)->estimate[0].level == -1) continue;   /* ignore this disk */
 
 
        qname = quote_string(dp->name);
@@ -1838,9 +2037,12 @@ static void handle_result(
                        get_pname(), walltime_str(curclock()),
                        dp->host->hostname, qname);
                g_fprintf(stderr,_(" %d -> %lldK, %d -> %lldK, %d -> %lldK\n"),
-                       est(dp)->level[0], (long long)est(dp)->est_size[0],
-                       est(dp)->level[1], (long long)est(dp)->est_size[1],
-                       est(dp)->level[2], (long long)est(dp)->est_size[2]);
+                         est(dp)->estimate[0].level,
+                          (long long)est(dp)->estimate[0].nsize,
+                         est(dp)->estimate[1].level,
+                          (long long)est(dp)->estimate[1].nsize,
+                         est(dp)->estimate[2].level,
+                          (long long)est(dp)->estimate[2].nsize);
            enqueue_disk(&pestq, dp);
        }
        else if(pkt->type == P_REP) {
@@ -1848,37 +2050,30 @@ static void handle_result(
                        get_pname(), walltime_str(curclock()),
                        dp->host->hostname, qname);
                g_fprintf(stderr,_(" %d -> %lldK, %d -> %lldK, %d -> %lldK\n"),
-                       est(dp)->level[0], (long long)est(dp)->est_size[0],
-                       est(dp)->level[1], (long long)est(dp)->est_size[1],
-                       est(dp)->level[2], (long long)est(dp)->est_size[2]);
-               if((est(dp)->level[0] != -1 && est(dp)->est_size[0] > (off_t)0) ||
-                  (est(dp)->level[1] != -1 && est(dp)->est_size[1] > (off_t)0) ||
-                  (est(dp)->level[2] != -1 && est(dp)->est_size[2] > (off_t)0)) {
-
-                   if(est(dp)->level[2] != -1 && est(dp)->est_size[2] < (off_t)0) {
-                       log_add(L_WARNING,
-                               _("disk %s:%s, estimate of level %d failed."),
-                               dp->host->hostname, qname, est(dp)->level[2]);
-                       est(dp)->level[2] = -1;
-                   }
-                   if(est(dp)->level[1] != -1 && est(dp)->est_size[1] < (off_t)0) {
-                       log_add(L_WARNING,
-                               _("disk %s:%s, estimate of level %d failed."),
-                               dp->host->hostname, qname,
-                               est(dp)->level[1]);
-                       est(dp)->level[1] = -1;
-                   }
-                   if(est(dp)->level[0] != -1 && est(dp)->est_size[0] < (off_t)0) {
-                       log_add(L_WARNING,
-                               _("disk %s:%s, estimate of level %d failed."),
-                               dp->host->hostname, qname, est(dp)->level[0]);
-                       est(dp)->level[0] = -1;
+                         est(dp)->estimate[0].level,
+                          (long long)est(dp)->estimate[0].nsize,
+                         est(dp)->estimate[1].level,
+                          (long long)est(dp)->estimate[1].nsize,
+                         est(dp)->estimate[2].level,
+                          (long long)est(dp)->estimate[2].nsize);
+               if ((est(dp)->estimate[0].level != -1 &&
+                     est(dp)->estimate[0].nsize > (gint64)0) ||
+                   (est(dp)->estimate[1].level != -1 &&
+                     est(dp)->estimate[1].nsize > (gint64)0) ||
+                   (est(dp)->estimate[2].level != -1 &&
+                     est(dp)->estimate[2].nsize > (gint64)0)) {
+
+                    for (i=MAX_LEVELS-1; i >=0; i--) {
+                       if (est(dp)->estimate[i].level != -1 &&
+                            est(dp)->estimate[i].nsize < (gint64)0) {
+                           est(dp)->estimate[i].level = -1;
+                       }
                    }
                    enqueue_disk(&estq, dp);
            }
            else {
                enqueue_disk(&failq, dp);
-               if(est(dp)->got_estimate) {
+               if(est(dp)->got_estimate && !est(dp)->errstr) {
                    est(dp)->errstr = vstrallocf("disk %s, all estimate failed",
                                                 qname);
                }
@@ -1892,9 +2087,31 @@ static void handle_result(
                    }
                }
            }
+           hostp->up = HOST_DONE;
+       }
+       if (est(dp)->post_dle == 0 &&
+           (pkt->type == P_REP ||
+            ((est(dp)->estimate[0].level == -1 ||
+               est(dp)->estimate[0].nsize > (gint64)0) &&
+             (est(dp)->estimate[1].level == -1 ||
+               est(dp)->estimate[1].nsize > (gint64)0) &&
+             (est(dp)->estimate[2].level == -1 ||
+               est(dp)->estimate[2].nsize > (gint64)0)))) {
+           run_server_dle_scripts(EXECUTE_ON_POST_DLE_ESTIMATE,
+                              get_config_name(), dp,
+                               est(dp)->estimate[0].level);
+           est(dp)->post_dle = 1;
        }
        amfree(qname);
     }
+
+    if(hostp->up == HOST_DONE) {
+       if (pkt->type == P_REP) {
+           run_server_host_scripts(EXECUTE_ON_POST_HOST_ESTIMATE,
+                                   get_config_name(), hostp);
+       }
+    }
+
     getsize(hostp);
     /* try to clean up any defunct processes, since Amanda doesn't wait() for
        them explicitly */
@@ -1957,7 +2174,7 @@ static void handle_result(
  */
 
 static int schedule_order(disk_t *a, disk_t *b);         /* subroutines */
-static int pick_inclevel(disk_t *dp);
+static one_est_t *pick_inclevel(disk_t *dp);
 
 static void analyze_estimate(
     disk_t *dp)
@@ -1978,54 +2195,45 @@ static void analyze_estimate(
        have_info = 1;
     }
 
-    ep->degr_level = -1;
-    ep->degr_nsize = (off_t)-1;
-    ep->degr_csize = (off_t)-1;
+    ep->degr_est = &default_one_est;
 
-    if(ep->next_level0 <= 0 || (have_info && ep->last_level == 0
-       && (info.command & FORCE_NO_BUMP))) {
-       if(ep->next_level0 <= 0) {
+    if (ep->next_level0 <= 0 || (have_info && ep->last_level == 0
+       && (ISSET(info.command, FORCE_NO_BUMP)))) {
+       if (ep->next_level0 <= 0) {
            g_fprintf(stderr,_("(due for level 0) "));
        }
-       ep->dump_level = 0;
-       ep->dump_nsize = est_size(dp, 0);
-       ep->dump_csize = est_tape_size(dp, 0);
-       if(ep->dump_csize <= (off_t)0) {
+       ep->dump_est = est_for_level(dp, 0);
+       if (ep->dump_est->csize <= (gint64)0) {
            g_fprintf(stderr,
                    _("(no estimate for level 0, picking an incr level)\n"));
-           ep->dump_level = pick_inclevel(dp);
-           ep->dump_nsize = est_size(dp, ep->dump_level);
-           ep->dump_csize = est_tape_size(dp, ep->dump_level);
-
-           if(ep->dump_nsize == (off_t)-1) {
-               ep->dump_level = ep->dump_level + 1;
-               ep->dump_nsize = est_size(dp, ep->dump_level);
-               ep->dump_csize = est_tape_size(dp, ep->dump_level);
+           ep->dump_est = pick_inclevel(dp);
+
+           if (ep->dump_est->nsize == (gint64)-1) {
+               ep->dump_est = est_for_level(dp, ep->dump_est->level + 1);
            }
        }
        else {
-           total_lev0 += (double) ep->dump_csize;
+           total_lev0 += (double) ep->dump_est->csize;
            if(ep->last_level == -1 || dp->skip_incr) {
                g_fprintf(stderr,_("(%s disk, can't switch to degraded mode)\n"),
                        dp->skip_incr? "skip-incr":_("new"));
-               ep->degr_level = -1;
-               ep->degr_nsize = (off_t)-1;
-               ep->degr_csize = (off_t)-1;
+               if (dp->skip_incr  && ep->degr_mesg == NULL) {
+                   ep->degr_mesg = _("Skpping: skip-incr disk can't be dumped in degraded mode");
+               }
+               ep->degr_est = &default_one_est;
            }
            else {
                /* fill in degraded mode info */
                g_fprintf(stderr,_("(picking inclevel for degraded mode)"));
-               ep->degr_level = pick_inclevel(dp);
-               ep->degr_nsize = est_size(dp, ep->degr_level);
-               ep->degr_csize = est_tape_size(dp, ep->degr_level);
-               if(ep->degr_csize == (off_t)-1) {
-                   ep->degr_level = ep->degr_level + 1;
-                   ep->degr_nsize = est_size(dp, ep->degr_level);
-                   ep->degr_csize = est_tape_size(dp, ep->degr_level);
+               ep->degr_est = pick_inclevel(dp);
+               if (ep->degr_est->csize == (gint64)-1) {
+                    ep->degr_est = est_for_level(dp, ep->degr_est->level + 1);
                }
-               if(ep->degr_csize == (off_t)-1) {
+               if (ep->degr_est->csize == (gint64)-1) {
                    g_fprintf(stderr,_("(no inc estimate)"));
-                   ep->degr_level = -1;
+                   if (ep->degr_mesg == NULL)
+                       ep->degr_mesg = _("Skipping: an incremental estimate could not be performed, so disk cannot be dumped in degraded mode");
+                   ep->degr_est = &default_one_est;
                }
                g_fprintf(stderr,"\n");
            }
@@ -2034,48 +2242,87 @@ static void analyze_estimate(
     else {
        g_fprintf(stderr,_("(not due for a full dump, picking an incr level)\n"));
        /* XXX - if this returns -1 may be we should force a total? */
-       ep->dump_level = pick_inclevel(dp);
-       ep->dump_nsize = est_size(dp, ep->dump_level);
-       ep->dump_csize = est_tape_size(dp, ep->dump_level);
+       ep->dump_est = pick_inclevel(dp);
 
-       if(ep->dump_csize == (off_t)-1) {
-           ep->dump_level = ep->last_level;
-           ep->dump_nsize = est_size(dp, ep->dump_level);
-           ep->dump_csize = est_tape_size(dp, ep->dump_level);
+       if (ep->dump_est->csize == (gint64)-1) {
+           ep->dump_est = est_for_level(dp, ep->last_level);
        }
-       if(ep->dump_csize == (off_t)-1) {
-           ep->dump_level = ep->last_level + 1;
-           ep->dump_nsize = est_size(dp, ep->dump_level);
-           ep->dump_csize = est_tape_size(dp, ep->dump_level);
+       if (ep->dump_est->csize == (gint64)-1) {
+           ep->dump_est = est_for_level(dp, ep->last_level + 1);
        }
-       if(ep->dump_csize == (off_t)-1) {
-           ep->dump_level = 0;
-           ep->dump_nsize = est_size(dp, ep->dump_level);
-           ep->dump_csize = est_tape_size(dp, ep->dump_level);
+       if (ep->dump_est->csize == (gint64)-1) {
+           ep->dump_est = est_for_level(dp, 0);
+       }
+       if (ep->degr_mesg == NULL) {
+           ep->degr_mesg = _("Skipping: a full is not planned, so can't dump in degraded mode");
        }
     }
 
+    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_level, (long long)ep->dump_nsize, 
-            (long long)ep->dump_csize);
+              ep->dump_est->level, (long long)ep->dump_est->nsize,
+              (long long)ep->dump_est->csize);
 
     insert_disk(&schedq, dp, schedule_order);
 
-    total_size += (off_t)tt_blocksize_kb + ep->dump_csize + tape_mark;
+    total_size += (gint64)tt_blocksize_kb + ep->dump_est->csize + tape_mark;
 
     /* update the balanced size */
     if(!(dp->skip_full || dp->strategy == DS_NOFULL || 
         dp->strategy == DS_INCRONLY)) {
-       off_t lev0size;
+       gint64 lev0size;
 
        lev0size = est_tape_size(dp, 0);
-       if(lev0size == (off_t)-1) lev0size = ep->last_lev0size;
-
-       balanced_size += (double)(lev0size / (off_t)runs_per_cycle);
+       if(lev0size == (gint64)-1) lev0size = ep->last_lev0size;
+
+       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"),
            (long long)total_size, total_lev0, balanced_size);
+
+    /* Log errstr even if the estimate succeeded */
+    /* It can be an error from a script          */
+    if (est(dp)->errstr) {
+       char *qerrstr = quote_string(est(dp)->errstr);
+       /* Log only a warning if a server estimate is available */
+       if (est(dp)->estimate[0].nsize > 0 ||
+           est(dp)->estimate[1].nsize > 0 ||
+           est(dp)->estimate[2].nsize > 0) {
+           log_add(L_WARNING, _("%s %s %s 0 %s"), dp->host->hostname, qname, 
+                   planner_timestamp, qerrstr);
+       } else {
+           log_add(L_FAIL, _("%s %s %s 0 %s"), dp->host->hostname, qname, 
+                   planner_timestamp, qerrstr);
+       }
+       amfree(qerrstr);
+    }
+
     amfree(qname);
 }
 
@@ -2112,83 +2359,84 @@ static int schedule_order(
     disk_t *b)
 {
     int diff;
-    off_t ldiff;
+    gint64 ldiff;
 
     diff = est(b)->dump_priority - est(a)->dump_priority;
     if(diff != 0) return diff;
 
-    ldiff = est(b)->dump_csize - est(a)->dump_csize;
-    if(ldiff < (off_t)0) return -1; /* XXX - there has to be a better way to dothis */
-    if(ldiff > (off_t)0) return 1;
+    ldiff = est(b)->dump_est->csize - est(a)->dump_est->csize;
+    if(ldiff < (gint64)0) return -1; /* XXX - there has to be a better way to dothis */
+    if(ldiff > (gint64)0) return 1;
     return 0;
 }
 
 
-static int pick_inclevel(
+static one_est_t *pick_inclevel(
     disk_t *dp)
 {
-    int base_level, bump_level;
-    off_t base_size, bump_size;
-    off_t thresh;
+    one_est_t *level0_est, *base_est, *bump_est;
+    gint64 thresh;
     char *qname;
 
-    base_level = est(dp)->last_level;
+    level0_est = est_for_level(dp, 0);
+    base_est = est_for_level(dp, est(dp)->last_level);
 
     /* if last night was level 0, do level 1 tonight, no ifs or buts */
-    if(base_level == 0) {
+    if (base_est->level == 0) {
        g_fprintf(stderr,_("   picklev: last night 0, so tonight level 1\n"));
-       return 1;
+       return est_for_level(dp, 1);
     }
 
     /* if no-full option set, always do level 1 */
     if(dp->strategy == DS_NOFULL) {
        g_fprintf(stderr,_("   picklev: no-full set, so always level 1\n"));
-       return 1;
+       return est_for_level(dp, 1);
     }
 
-    base_size = est_size(dp, base_level);
-
     /* if we didn't get an estimate, we can't do an inc */
-    if(base_size == (off_t)-1) {
-       base_size = est_size(dp, base_level+1);
-       if(base_size > (off_t)0) /* FORCE_BUMP */
-           return base_level+1;
-       g_fprintf(stderr,_("   picklev: no estimate for level %d, so no incs\n"), base_level);
-       return base_level;
+    if (base_est->nsize == (gint64)-1) {
+       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;
     }
 
-    thresh = bump_thresh(base_level, est_size(dp, 0), dp->bumppercent, dp->bumpsize, dp->bumpmult);
+    thresh = bump_thresh(base_est->level, level0_est->nsize, dp->bumppercent,
+                         dp->bumpsize, dp->bumpmult);
 
     g_fprintf(stderr,
            _("   pick: size %lld level %d days %d (thresh %lldK, %d days)\n"),
-           (long long)base_size, base_level, est(dp)->level_days,
+           (long long)base_est->nsize, base_est->level, est(dp)->level_days,
            (long long)thresh, dp->bumpdays);
 
-    if(base_level == 9
+    if(base_est->level == (DUMP_LEVELS - 1)
        || est(dp)->level_days < dp->bumpdays
-       || base_size <= thresh)
-           return base_level;
+       || base_est->nsize <= thresh)
+           return base_est;
 
-    bump_level = base_level + 1;
-    bump_size = est_size(dp, bump_level);
+    bump_est = est_for_level(dp, base_est->level + 1);
 
-    if(bump_size == (off_t)-1) return base_level;
+    if (bump_est->nsize == (gint64)-1)
+        return base_est;
 
     g_fprintf(stderr, _("   pick: next size %lld... "),
-           (long long)bump_size);
+              (long long)bump_est->nsize);
 
-    if(base_size - bump_size < thresh) {
+    if (base_est->nsize - bump_est->nsize < thresh) {
        g_fprintf(stderr, _("not bumped\n"));
-       return base_level;
+       return base_est;
     }
 
     qname = quote_string(dp->name);
     g_fprintf(stderr, _("BUMPED\n"));
     log_add(L_INFO, _("Incremental of %s:%s bumped to level %d."),
-           dp->host->hostname, qname, bump_level);
+           dp->host->hostname, qname, bump_est->level);
     amfree(qname);
 
-    return bump_level;
+    return bump_est;
 }
 
 
@@ -2230,13 +2478,13 @@ static void delay_dumps(void)
     disk_t *   preserve;
     bi_t *     bi;
     bi_t  *    nbi;
-    off_t      new_total;              /* New total_size */
+    gint64     new_total;              /* New total_size */
     char       est_kb[20];             /* Text formatted dump size */
     int                nb_forced_level_0;
     info_t     info;
     int                delete;
     char *     message;
-    off_t      full_size;
+    gint64     full_size;
 
     biq.head = biq.tail = NULL;
 
@@ -2253,15 +2501,15 @@ static void delay_dumps(void)
 
     for(dp = schedq.head; dp != NULL; dp = ndp) {
        int avail_tapes = 1;
-       if (dp->tape_splitsize > (off_t)0)
+       if (dp->splitsize > (gint64)0 || dp->allow_split)
            avail_tapes = conf_runtapes;
 
        ndp = dp->next; /* remove_disk zaps this */
 
        full_size = est_tape_size(dp, 0);
-       if (full_size > tapetype_get_length(tape) * (off_t)avail_tapes) {
+       if (full_size > tapetype_get_length(tape) * (gint64)avail_tapes) {
            char *qname = quote_string(dp->name);
-           if (conf_runtapes > 1 && dp->tape_splitsize == (off_t)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,
@@ -2274,16 +2522,16 @@ static void delay_dumps(void)
            amfree(qname);
        }
 
-       if (est(dp)->dump_csize == (off_t)-1 ||
-           est(dp)->dump_csize <= tapetype_get_length(tape) * (off_t)avail_tapes) {
+       if (est(dp)->dump_est->csize == (gint64)-1 ||
+           est(dp)->dump_est->csize <= tapetype_get_length(tape) * (gint64)avail_tapes) {
            continue;
        }
 
        /* Format dumpsize for messages */
        g_snprintf(est_kb, 20, "%lld KB,",
-                (long long)est(dp)->dump_csize);
+                   (long long)est(dp)->dump_est->csize);
 
-       if(est(dp)->dump_level == 0) {
+       if(est(dp)->dump_est->level == 0) {
            if(dp->skip_incr) {
                delete = 1;
                message = _("but cannot incremental dump skip-incr disk");
@@ -2292,11 +2540,11 @@ static void delay_dumps(void)
                delete = 1;
                message = _("but cannot incremental dump new disk");
            }
-           else if(est(dp)->degr_level < 0) {
+           else if(est(dp)->degr_est->level < 0) {
                delete = 1;
                message = _("but no incremental estimate");
            }
-           else if (est(dp)->degr_csize > tapetype_get_length(tape)) {
+           else if (est(dp)->degr_est->csize > tapetype_get_length(tape)) {
                delete = 1;
                message = _("incremental dump also larger than tape");
            }
@@ -2329,7 +2577,7 @@ 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_level == 0)
+       if(est(dp)->dump_est->level == 0)
            preserve = dp;
 
     /* 2.a. Do not delay forced full */
@@ -2338,10 +2586,10 @@ static void delay_dumps(void)
                dp = ndp) {
        ndp = dp->prev;
 
-       if(est(dp)->dump_level != 0) continue;
+       if(est(dp)->dump_est->level != 0) continue;
 
        get_info(dp->host->hostname, dp->name, &info);
-       if(info.command & FORCE_FULL) {
+       if(ISSET(info.command, FORCE_FULL)) {
            nb_forced_level_0 += 1;
            preserve = dp;
            continue;
@@ -2351,7 +2599,7 @@ static void delay_dumps(void)
 
            /* Format dumpsize for messages */
            g_snprintf(est_kb, 20, "%lld KB,",
-                    (long long)est(dp)->dump_csize);
+                       (long long)est(dp)->dump_est->csize);
 
            if(dp->skip_incr) {
                delete = 1;
@@ -2361,7 +2609,7 @@ static void delay_dumps(void)
                delete = 1;
                message = _("but cannot incremental dump new disk");
            }
-           else if(est(dp)->degr_level < 0) {
+           else if(est(dp)->degr_est->level < 0) {
                delete = 1;
                message = _("but no incremental estimate");
            }
@@ -2381,11 +2629,11 @@ static void delay_dumps(void)
                dp = ndp) {
            ndp = dp->prev;
 
-           if(est(dp)->dump_level == 0 && dp != preserve) {
+           if(est(dp)->dump_est->level == 0 && dp != preserve) {
 
                /* Format dumpsize for messages */
                g_snprintf(est_kb, 20, "%lld KB,",
-                            (long long)est(dp)->dump_csize);
+                           (long long)est(dp)->dump_est->csize);
 
                if(dp->skip_incr) {
                    delete = 1;
@@ -2395,7 +2643,7 @@ static void delay_dumps(void)
                    delete = 1;
                    message = _("but cannot incremental dump new disk");
                }
-               else if(est(dp)->degr_level < 0) {
+               else if(est(dp)->degr_est->level < 0) {
                    delete = 1;
                    message = _("but no incremental estimate");
                }
@@ -2422,11 +2670,11 @@ static void delay_dumps(void)
            dp = ndp) {
        ndp = dp->prev;
 
-       if(est(dp)->dump_level != 0) {
+       if(est(dp)->dump_est->level != 0) {
 
            /* Format dumpsize for messages */
            g_snprintf(est_kb, 20, "%lld KB,",
-                    (long long)est(dp)->dump_csize);
+                       (long long)est(dp)->dump_est->csize);
 
            delay_one_dump(dp, 1,
                           _("dumps way too big,"),
@@ -2449,17 +2697,17 @@ static void delay_dumps(void)
        int avail_tapes = 1;
        nbi = bi->prev;
        dp = bi->dp;
-       if(dp->tape_splitsize > (off_t)0)
+       if(dp->splitsize > (gint64)0)
            avail_tapes = conf_runtapes;
 
        if(bi->deleted) {
-           new_total = total_size + (off_t)tt_blocksize_kb +
-                       bi->csize + (off_t)tape_mark;
+           new_total = total_size + (gint64)tt_blocksize_kb +
+                       bi->csize + (gint64)tape_mark;
        } else {
-           new_total = total_size - est(dp)->dump_csize + bi->csize;
+           new_total = total_size - est(dp)->dump_est->csize + bi->csize;
        }
        if((new_total <= tape_length) &&
-         (bi->csize < (tapetype_get_length(tape) * (off_t)avail_tapes))) {
+         (bi->csize < (tapetype_get_length(tape) * (gint64)avail_tapes))) {
            /* reinstate it */
            total_size = new_total;
            if(bi->deleted) {
@@ -2469,9 +2717,7 @@ static void delay_dumps(void)
                insert_disk(&schedq, dp, schedule_order);
            }
            else {
-               est(dp)->dump_level = bi->level;
-               est(dp)->dump_nsize = bi->nsize;
-               est(dp)->dump_csize = bi->csize;
+               est(dp)->dump_est = est_for_level(dp, bi->level);
            }
 
            /* Keep it clean */
@@ -2506,7 +2752,7 @@ static void delay_dumps(void)
        else {
            dp = bi->dp;
            g_fprintf(stderr, _("  delay: %s now at level %d\n"),
-               bi->errstr, est(dp)->dump_level);
+               bi->errstr, est(dp)->dump_est->level);
            log_add(L_INFO, "%s", bi->errstr);
        }
        /*@ignore@*/
@@ -2541,9 +2787,9 @@ arglist_function1(
 
     arglist_start(argp, delete);
 
-    total_size -= (off_t)tt_blocksize_kb + est(dp)->dump_csize + (off_t)tape_mark;
-    if(est(dp)->dump_level == 0) {
-       total_lev0 -= (double) est(dp)->dump_csize;
+    total_size -= (gint64)tt_blocksize_kb + est(dp)->dump_est->csize + (gint64)tape_mark;
+    if(est(dp)->dump_est->level == 0) {
+       total_lev0 -= (double) est(dp)->dump_est->csize;
     }
 
     bi = alloc(SIZEOF(bi_t));
@@ -2557,11 +2803,11 @@ arglist_function1(
 
     bi->deleted = delete;
     bi->dp = dp;
-    bi->level = est(dp)->dump_level;
-    bi->nsize = est(dp)->dump_nsize;
-    bi->csize = est(dp)->dump_csize;
+    bi->level = est(dp)->dump_est->level;
+    bi->nsize = est(dp)->dump_est->nsize;
+    bi->csize = est(dp)->dump_est->csize;
 
-    g_snprintf(level_str, SIZEOF(level_str), "%d", est(dp)->dump_level);
+    g_snprintf(level_str, SIZEOF(level_str), "%d", est(dp)->dump_est->level);
     bi->errstr = vstralloc(dp->host->hostname,
                           " ", qname,
                           " ", planner_timestamp ? planner_timestamp : "?",
@@ -2583,10 +2829,8 @@ arglist_function1(
     if (delete) {
        remove_disk(&schedq, dp);
     } else {
-       est(dp)->dump_level = est(dp)->degr_level;
-       est(dp)->dump_nsize = est(dp)->degr_nsize;
-       est(dp)->dump_csize = est(dp)->degr_csize;
-       total_size += (off_t)tt_blocksize_kb + est(dp)->dump_csize + (off_t)tape_mark;
+       est(dp)->dump_est = est(dp)->degr_est;
+       total_size += (gint64)tt_blocksize_kb + est(dp)->dump_est->csize + (gint64)tape_mark;
     }
     amfree(qname);
     return;
@@ -2596,7 +2840,7 @@ arglist_function1(
 static int promote_highest_priority_incremental(void)
 {
     disk_t *dp, *dp1, *dp_promote;
-    off_t new_size, new_total, new_lev0;
+    gint64 new_total, new_lev0;
     int check_days;
     int nb_today, nb_same_day, nb_today2;
     int nb_disk_today, nb_disk_same_day;
@@ -2609,10 +2853,10 @@ static int promote_highest_priority_incremental(void)
 
     dp_promote = NULL;
     for(dp = schedq.head; dp != NULL; dp = dp->next) {
-
+       one_est_t *level0_est = est_for_level(dp, 0);
        est(dp)->promote = -1000;
 
-       if(est_size(dp,0) <= (off_t)0)
+       if (level0_est->nsize <= (gint64)0)
            continue;
 
        if(est(dp)->next_level0 <= 0)
@@ -2621,21 +2865,20 @@ static int promote_highest_priority_incremental(void)
        if(est(dp)->next_level0 > dp->maxpromoteday)
            continue;
 
-       new_size = est_tape_size(dp, 0);
-       new_total = total_size - est(dp)->dump_csize + new_size;
-       new_lev0 = (off_t)total_lev0 + new_size;
+       new_total = total_size - est(dp)->dump_est->csize + level0_est->csize;
+       new_lev0 = (gint64)total_lev0 + level0_est->csize;
 
        nb_today = 0;
        nb_same_day = 0;
        nb_disk_today = 0;
        nb_disk_same_day = 0;
        for(dp1 = schedq.head; dp1 != NULL; dp1 = dp1->next) {
-           if(est(dp1)->dump_level == 0)
+           if(est(dp1)->dump_est->level == 0)
                nb_disk_today++;
            else if(est(dp1)->next_level0 == est(dp)->next_level0)
                nb_disk_same_day++;
            if(strcmp(dp->host->hostname, dp1->host->hostname) == 0) {
-               if(est(dp1)->dump_level == 0)
+               if(est(dp1)->dump_est->level == 0)
                    nb_today++;
                else if(est(dp1)->next_level0 == est(dp)->next_level0)
                    nb_same_day++;
@@ -2648,7 +2891,7 @@ static int promote_highest_priority_incremental(void)
 
        /* do not promote if overflow balanced size and something today */
        /* promote if nothing today */
-       if((new_lev0 > (off_t)(balanced_size + balance_threshold)) &&
+       if((new_lev0 > (gint64)(balanced_size + balance_threshold)) &&
                (nb_disk_today > 0))
            continue;
 
@@ -2683,22 +2926,19 @@ static int promote_highest_priority_incremental(void)
     }
 
     if(dp_promote) {
+       one_est_t *level0_est;
        dp = dp_promote;
+       level0_est = est_for_level(dp, 0);
 
        qname = quote_string(dp->name);
-       new_size = est_tape_size(dp, 0);
-       new_total = total_size - est(dp)->dump_csize + new_size;
-       new_lev0 = (off_t)total_lev0 + new_size;
+       new_total = total_size - est(dp)->dump_est->csize + level0_est->csize;
+       new_lev0 = (gint64)total_lev0 + level0_est->csize;
 
        total_size = new_total;
        total_lev0 = (double)new_lev0;
        check_days = est(dp)->next_level0;
-       est(dp)->degr_level = est(dp)->dump_level;
-       est(dp)->degr_nsize = est(dp)->dump_nsize;
-       est(dp)->degr_csize = est(dp)->dump_csize;
-       est(dp)->dump_level = 0;
-       est(dp)->dump_nsize = est_size(dp, 0);
-       est(dp)->dump_csize = new_size;
+       est(dp)->degr_est = est(dp)->dump_est;
+       est(dp)->dump_est = level0_est;
        est(dp)->next_level0 = 0;
 
        g_fprintf(stderr,
@@ -2723,13 +2963,12 @@ static int promote_hills(void)
     disk_t *dp;
     struct balance_stats {
        int disks;
-       off_t size;
+       gint64 size;
     } *sp = NULL;
     int days;
     int hill_days = 0;
-    off_t hill_size;
-    off_t new_size;
-    off_t new_total;
+    gint64 hill_size;
+    gint64 new_total;
     int my_dumpcycle;
     char *qname;
 
@@ -2746,11 +2985,12 @@ static int promote_hills(void)
 
     for(days = 0; days < my_dumpcycle; days++) {
        sp[days].disks = 0;
-       sp[days].size = (off_t)0;
+       sp[days].size = (gint64)0;
     }
 
     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++;
@@ -2761,7 +3001,7 @@ static int promote_hills(void)
     /* Search for a suitable big hill and cut it down */
     while(1) {
        /* Find the tallest hill */
-       hill_size = (off_t)0;
+       hill_size = (gint64)0;
        for(days = 0; days < my_dumpcycle; days++) {
            if(sp[days].disks > 1 && sp[days].size > hill_size) {
                hill_size = sp[days].size;
@@ -2769,31 +3009,30 @@ static int promote_hills(void)
            }
        }
 
-       if(hill_size <= (off_t)0) break;        /* no suitable hills */
+       if(hill_size <= (gint64)0) break;       /* no suitable hills */
 
        /* Find all the dumps in that hill and try and remove one */
        for(dp = schedq.head; dp != NULL; dp = dp->next) {
+           one_est_t *level0_est;
            if(est(dp)->next_level0 != hill_days ||
               est(dp)->next_level0 > dp->maxpromoteday ||
               dp->skip_full ||
               dp->strategy == DS_NOFULL ||
               dp->strategy == DS_INCRONLY)
                continue;
-           new_size = est_tape_size(dp, 0);
-           new_total = total_size - est(dp)->dump_csize + new_size;
+           level0_est = est_for_level(dp, 0);
+           if (level0_est->nsize <= (gint64)0)
+               continue;
+           new_total = total_size - est(dp)->dump_est->csize + level0_est->csize;
            if(new_total > tape_length)
                continue;
            /* We found a disk we can promote */
            qname = quote_string(dp->name);
            total_size = new_total;
-           total_lev0 += (double)new_size;
-           est(dp)->degr_level = est(dp)->dump_level;
-           est(dp)->degr_nsize = est(dp)->dump_nsize;
-           est(dp)->degr_csize = est(dp)->dump_csize;
-           est(dp)->dump_level = 0;
+           total_lev0 += (double)level0_est->csize;
+            est(dp)->degr_est = est(dp)->dump_est;
+            est(dp)->dump_est = level0_est;
            est(dp)->next_level0 = 0;
-           est(dp)->dump_nsize = est_size(dp, 0);
-           est(dp)->dump_csize = new_size;
 
            g_fprintf(stderr,
                    _("   promote: moving %s:%s up, total_lev0 %1.0lf, total_size %lld\n"),
@@ -2844,54 +3083,48 @@ static void output_scheduleline(
     char degr_kps_str[NUM_STR_SIZE];
     char *dump_date, *degr_date;
     char *features;
-    int i;
     char *qname = quote_string(dp->name);
 
     ep = est(dp);
 
-    if(ep->dump_csize == (off_t)-1) {
+    if(ep->dump_est->csize == (gint64)-1) {
        /* no estimate, fail the disk */
        g_fprintf(stderr,
                _("%s: FAILED %s %s %s %d \"[no estimate]\"\n"),
                get_pname(),
-               dp->host->hostname, qname, planner_timestamp, ep->dump_level);
+               dp->host->hostname, qname, planner_timestamp, ep->dump_est->level);
        log_add(L_FAIL, _("%s %s %s %d [no estimate]"),
-               dp->host->hostname, qname, planner_timestamp, ep->dump_level);
+               dp->host->hostname, qname, planner_timestamp, ep->dump_est->level);
        amfree(qname);
        return;
     }
 
-    dump_date = degr_date = (char *)0;
-    for(i = 0; i < MAX_LEVELS; i++) {
-       if(ep->dump_level == ep->level[i])
-           dump_date = ep->dumpdate[i];
-       if(ep->degr_level == ep->level[i])
-           degr_date = ep->dumpdate[i];
-    }
+    dump_date = ep->dump_est->dumpdate;
+    degr_date = ep->degr_est->dumpdate;
 
 #define fix_rate(rate) (rate < 1.0 ? DEFAULT_DUMPRATE : rate)
 
-    if(ep->dump_level == 0) {
+    if(ep->dump_est->level == 0) {
        dump_kps = fix_rate(ep->fullrate);
-       dump_time = (time_t)((double)ep->dump_csize / dump_kps);
+       dump_time = (time_t)((double)ep->dump_est->csize / dump_kps);
 
-       if(ep->degr_csize != (off_t)-1) {
+       if(ep->degr_est->csize != (gint64)-1) {
            degr_kps = fix_rate(ep->incrrate);
-           degr_time = (time_t)((double)ep->degr_csize / degr_kps);
+           degr_time = (time_t)((double)ep->degr_est->csize / degr_kps);
        }
     }
     else {
        dump_kps = fix_rate(ep->incrrate);
-       dump_time = (time_t)((double)ep->dump_csize / dump_kps);
+       dump_time = (time_t)((double)ep->dump_est->csize / dump_kps);
     }
 
-    if(ep->dump_level == 0 && ep->degr_csize != (off_t)-1) {
+    if(ep->dump_est->level == 0 && ep->degr_est->csize != (gint64)-1) {
        g_snprintf(degr_level_str, sizeof(degr_level_str),
-                   "%d", ep->degr_level);
+                   "%d", ep->degr_est->level);
        g_snprintf(degr_nsize_str, sizeof(degr_nsize_str),
-                   "%lld", (long long)ep->degr_nsize);
+                   "%lld", (long long)ep->degr_est->nsize);
        g_snprintf(degr_csize_str, sizeof(degr_csize_str),
-                   "%lld", (long long)ep->degr_csize);
+                   "%lld", (long long)ep->degr_est->csize);
        g_snprintf(degr_time_str, sizeof(degr_time_str),
                    "%lld", (long long)degr_time);
        g_snprintf(degr_kps_str, sizeof(degr_kps_str),
@@ -2903,15 +3136,24 @@ static void output_scheduleline(
                             " ", degr_time_str,
                             " ", degr_kps_str,
                             NULL);
+    } else {
+       char *degr_mesg;
+       if (ep->degr_mesg) {
+           degr_mesg = quote_string(ep->degr_mesg);
+       } else {
+           degr_mesg = quote_string(_("Skipping: cannot dump in degraded mode for unknown reason"));
+       }
+       degr_str = vstralloc(" ", degr_mesg, NULL);
+       amfree(degr_mesg);
     }
     g_snprintf(dump_priority_str, SIZEOF(dump_priority_str),
                "%d", ep->dump_priority);
     g_snprintf(dump_level_str, SIZEOF(dump_level_str),
-               "%d", ep->dump_level);
+               "%d", ep->dump_est->level);
     g_snprintf(dump_nsize_str, sizeof(dump_nsize_str),
-               "%lld", (long long)ep->dump_nsize);
+               "%lld", (long long)ep->dump_est->nsize);
     g_snprintf(dump_csize_str, sizeof(dump_csize_str),
-               "%lld", (long long)ep->dump_csize);
+               "%lld", (long long)ep->dump_est->csize);
     g_snprintf(dump_time_str, sizeof(dump_time_str),
                "%lld", (long long)dump_time);
     g_snprintf(dump_kps_str, sizeof(dump_kps_str),
@@ -2931,6 +3173,9 @@ static void output_scheduleline(
                          degr_str ? degr_str : "",
                          "\n", NULL);
 
+    if (est(dp)->dump_est->guessed == 1) {
+        log_add(L_WARNING, _("WARNING: no history available for %s:%s; guessing that size will be %lld KB\n"), dp->host->hostname, qname, (long long)est(dp)->dump_est->csize);
+    }
     fputs(schedline, stdout);
     fputs(schedline, stderr);
     amfree(features);
@@ -2938,3 +3183,22 @@ static void output_scheduleline(
     amfree(degr_str);
     amfree(qname);
 }
+
+static void
+server_estimate(
+    disk_t *dp,
+    int     i,
+    info_t *info,
+    int     level)
+{
+    int    stats;
+    gint64 size;
+
+    size = internal_server_estimate(dp, info, level, &stats);
+
+    est(dp)->dump_est = &est(dp)->estimate[i];
+    est(dp)->estimate[i].nsize = size;
+    if (stats == 0) {
+       est(dp)->estimate[i].guessed = 1;
+    }
+}