add bug closure to changelog
[debian/amanda] / server-src / planner.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1999 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /*
27  * $Id: planner.c 10421 2008-03-06 18:48:30Z martineau $
28  *
29  * backup schedule planner for the Amanda backup system.
30  */
31 #include "amanda.h"
32 #include "arglist.h"
33 #include "find.h"
34 #include "conffile.h"
35 #include "diskfile.h"
36 #include "tapefile.h"
37 #include "infofile.h"
38 #include "logfile.h"
39 #include "clock.h"
40 #include "packet.h"
41 #include "security.h"
42 #include "protocol.h"
43 #include "version.h"
44 #include "amfeatures.h"
45 #include "server_util.h"
46 #include "holding.h"
47 #include "timestamp.h"
48 #include "amxml.h"
49
50 #define planner_debug(i,x) do {         \
51         if ((i) <= debug_planner) {     \
52             dbprintf(x);                \
53         }                               \
54 } while (0)
55
56 #define MAX_LEVELS                  3   /* max# of estimates per filesys */
57
58 #define RUNS_REDZONE                5   /* should be in conf file? */
59
60 #define PROMOTE_THRESHOLD        0.05   /* if <5% unbalanced, don't promote */
61 #define DEFAULT_DUMPRATE         1024.0 /* K/s */
62
63 /* configuration file stuff */
64
65 char *  conf_tapetype;
66 gint64  conf_maxdumpsize;
67 int     conf_runtapes;
68 int     conf_dumpcycle;
69 int     conf_runspercycle;
70 int     conf_tapecycle;
71 time_t  conf_etimeout;
72 int     conf_reserve;
73 int     conf_autoflush;
74 int     conf_usetimestamps;
75
76 #define HOST_READY                              ((void *)0)     /* must be 0 */
77 #define HOST_ACTIVE                             ((void *)1)
78 #define HOST_DONE                               ((void *)2)
79
80 #define DISK_READY                              0               /* must be 0 */
81 #define DISK_ACTIVE                             1
82 #define DISK_PARTIALY_DONE                      2
83 #define DISK_DONE                               3
84
85 typedef struct one_est_s {
86     int     level;
87     gint64  nsize;      /* native size     */
88     gint64  csize;      /* compressed size */
89     char   *dumpdate;
90     int     guessed;    /* If server guessed the estimate size */
91 } one_est_t;
92 static one_est_t default_one_est = {-1, -1, -1, "INVALID_DATE", 0};
93
94 typedef struct est_s {
95     int state;
96     int got_estimate;
97     int dump_priority;
98     one_est_t *dump_est;
99     one_est_t *degr_est;
100     one_est_t  estimate[MAX_LEVELS];
101     int last_level;
102     gint64 last_lev0size;
103     int next_level0;
104     int level_days;
105     int promote;
106     int post_dle;
107     double fullrate, incrrate;
108     double fullcomp, incrcomp;
109     char *errstr;
110     char *degr_mesg;
111     info_t *info;
112 } est_t;
113
114 #define est(dp) ((est_t *)(dp)->up)
115
116 /* pestq = partial estimate */
117 disklist_t startq, waitq, pestq, estq, failq, schedq;
118 gint64 total_size;
119 double total_lev0, balanced_size, balance_threshold;
120 gint64 tape_length;
121 size_t tape_mark;
122
123 tapetype_t *tape;
124 size_t tt_blocksize;
125 size_t tt_blocksize_kb;
126 int runs_per_cycle = 0;
127 time_t today;
128 char *planner_timestamp = NULL;
129
130 static am_feature_t *our_features = NULL;
131 static char *our_feature_string = NULL;
132
133 /* We keep a LIFO queue of before images for all modifications made
134  * to schedq in our attempt to make the schedule fit on the tape.
135  * Enough information is stored to reinstate a dump if it turns out
136  * that it shouldn't have been touched after all.
137  */
138 typedef struct bi_s {
139     struct bi_s *next;
140     struct bi_s *prev;
141     int deleted;                /* 0=modified, 1=deleted */
142     disk_t *dp;                 /* The disk that was changed */
143     int level;                  /* The original level */
144     gint64 nsize;               /* The original native size */
145     gint64 csize;               /* The original compressed size */
146     char *errstr;               /* A message describing why this disk is here */
147 } bi_t;
148
149 typedef struct bilist_s {
150     bi_t *head, *tail;
151 } bilist_t;
152
153 bilist_t biq;                   /* The BI queue itself */
154
155 /*
156  * ========================================================================
157  * MAIN PROGRAM
158  *
159  */
160
161 static void setup_estimate(disk_t *dp);
162 static void get_estimates(void);
163 static void analyze_estimate(disk_t *dp);
164 static void handle_failed(disk_t *dp);
165 static void delay_dumps(void);
166 static int promote_highest_priority_incremental(void);
167 static int promote_hills(void);
168 static void output_scheduleline(disk_t *dp);
169 static void server_estimate(disk_t *dp, int i, info_t *info, int level);
170 int main(int, char **);
171
172 int
173 main(
174     int         argc,
175     char **     argv)
176 {
177     disklist_t origq;
178     disk_t *dp;
179     int moved_one;
180     int diskarg_offset;
181     gint64 initial_size;
182     int i;
183     char *conf_diskfile;
184     char *conf_tapelist;
185     char *conf_infofile;
186     times_t section_start;
187     char *qname;
188     int    nb_disk;
189     char  *errstr = NULL;
190     config_overrides_t *cfg_ovr = NULL;
191     char *cfg_opt = NULL;
192     int    planner_setuid;
193     int exit_status = EXIT_SUCCESS;
194     gboolean no_taper = FALSE;
195     gboolean from_client = FALSE;
196
197     if (argc > 1 && argv && argv[1] && g_str_equal(argv[1], "--version")) {
198         printf("planner-%s\n", VERSION);
199         return (0);
200     }
201
202     /*
203      * Configure program for internationalization:
204      *   1) Only set the message locale for now.
205      *   2) Set textdomain for all amanda related programs to "amanda"
206      *      We don't want to be forced to support dozens of message catalogs.
207      */  
208     setlocale(LC_MESSAGES, "C");
209     textdomain("amanda"); 
210
211     /* drop root privileges */
212     planner_setuid = set_root_privs(0);
213
214     safe_fd(-1, 0);
215
216     set_pname("planner");
217
218     dbopen(DBG_SUBDIR_SERVER);
219
220     cfg_ovr = extract_commandline_config_overrides(&argc, &argv);
221     if (argc > 1) 
222         cfg_opt = argv[1];
223
224     set_config_overrides(cfg_ovr);
225     config_init(CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_USE_CWD, cfg_opt);
226
227     /* conf_diskfile is freed later, as it may be used in an error message */
228     conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
229     read_diskfile(conf_diskfile, &origq);
230     disable_skip_disk(&origq);
231
232     /* Don't die when child closes pipe */
233     signal(SIGPIPE, SIG_IGN);
234
235     setvbuf(stderr, (char *)NULL, (int)_IOLBF, 0);
236
237     add_amanda_log_handler(amanda_log_stderr);
238     add_amanda_log_handler(amanda_log_trace_log);
239
240     if (!planner_setuid) {
241         error(_("planner must be run setuid root"));
242     }
243
244     if (config_errors(NULL) >= CFGERR_ERRORS) {
245         g_critical(_("errors processing config file"));
246     }
247
248     safe_cd();
249
250     check_running_as(RUNNING_AS_ROOT | RUNNING_AS_UID_ONLY);
251
252     dbrename(get_config_name(), DBG_SUBDIR_SERVER);
253
254     startclock();
255     section_start = curclock();
256
257     our_features = am_init_feature_set();
258     our_feature_string = am_feature_to_string(our_features);
259
260     log_add(L_INFO, "%s pid %ld", get_pname(), (long)getpid());
261     g_fprintf(stderr, _("%s: pid %ld executable %s version %s\n"),
262             get_pname(), (long) getpid(), argv[0], VERSION);
263     for (i = 0; version_info[i] != NULL; i++)
264         g_fprintf(stderr, _("%s: %s"), get_pname(), version_info[i]);
265
266     diskarg_offset = 2;
267     if (argc - diskarg_offset > 1 && strcmp(argv[diskarg_offset], "--starttime") == 0) {
268         planner_timestamp = stralloc(argv[diskarg_offset+1]);
269         diskarg_offset += 2;
270     }
271     if (argc - diskarg_offset > 0 && strcmp(argv[diskarg_offset], "--no-taper") == 0) {
272         no_taper = TRUE;
273         diskarg_offset += 1;
274     }
275     if (argc - diskarg_offset > 0 && strcmp(argv[diskarg_offset], "--from-client") == 0) {
276         from_client = TRUE;
277         diskarg_offset += 1;
278     }
279
280
281     run_server_global_scripts(EXECUTE_ON_PRE_ESTIMATE, get_config_name());
282
283     /*
284      * 1. Networking Setup
285      *
286      */
287
288     protocol_init();
289
290     /*
291      * 2. Read in Configuration Information
292      *
293      * All the Amanda configuration files are loaded before we begin.
294      */
295
296     g_fprintf(stderr,_("READING CONF INFO...\n"));
297
298     if(origq.head == NULL) {
299         error(_("empty disklist \"%s\""), conf_diskfile);
300         /*NOTREACHED*/
301     }
302
303     amfree(conf_diskfile);
304
305     conf_tapelist = config_dir_relative(getconf_str(CNF_TAPELIST));
306     if(read_tapelist(conf_tapelist)) {
307         error(_("could not load tapelist \"%s\""), conf_tapelist);
308         /*NOTREACHED*/
309     }
310     amfree(conf_tapelist);
311
312     conf_infofile = config_dir_relative(getconf_str(CNF_INFOFILE));
313     if(open_infofile(conf_infofile)) {
314         error(_("could not open info db \"%s\""), conf_infofile);
315         /*NOTREACHED*/
316     }
317     if (check_infofile(conf_infofile, &origq, &errstr) == -1) {
318         log_add(L_WARNING, "problem copying infofile: %s", errstr);
319         amfree(errstr);
320     }
321     amfree(conf_infofile);
322
323     conf_tapetype = getconf_str(CNF_TAPETYPE);
324     conf_maxdumpsize = getconf_int64(CNF_MAXDUMPSIZE);
325     conf_runtapes = getconf_int(CNF_RUNTAPES);
326     conf_dumpcycle = getconf_int(CNF_DUMPCYCLE);
327     conf_runspercycle = getconf_int(CNF_RUNSPERCYCLE);
328     conf_tapecycle = getconf_int(CNF_TAPECYCLE);
329     conf_etimeout = (time_t)getconf_int(CNF_ETIMEOUT);
330     conf_reserve  = getconf_int(CNF_RESERVE);
331     conf_autoflush = getconf_no_yes_all(CNF_AUTOFLUSH);
332     conf_usetimestamps = getconf_boolean(CNF_USETIMESTAMPS);
333
334     today = time(0);
335     if (planner_timestamp) {
336         if (conf_usetimestamps == 0) {
337             planner_timestamp[8] = '\0';
338         }
339     } else if(conf_usetimestamps == 0) {
340         planner_timestamp = get_datestamp_from_time(0);
341     }
342     else {
343         planner_timestamp = get_timestamp_from_time(0);
344     }
345     log_add(L_START, _("date %s"), planner_timestamp);
346     g_printf("DATE %s\n", planner_timestamp);
347     fflush(stdout);
348     g_fprintf(stderr, _("%s: timestamp %s\n"),
349                     get_pname(), planner_timestamp);
350
351     errstr = match_disklist(&origq, argc-diskarg_offset,
352                                     argv+diskarg_offset);
353     if (errstr) {
354         g_fprintf(stderr,"%s",errstr);
355         exit_status = EXIT_FAILURE;
356     }
357
358     for (dp = origq.head; dp != NULL; dp = dp->next) {
359         if (dp->todo) {
360             if (from_client) {
361                 if (!dp->dump_limit || !dp->dump_limit->same_host)
362                     dp->todo = 0;
363             } else {
364                 if (dp->dump_limit && !dp->dump_limit->server)
365                     dp->todo = 0;
366             }
367         }
368     }
369
370     nb_disk = 0;
371     for (dp = origq.head; dp != NULL; dp = dp->next) {
372         if (dp->todo) {
373             qname = quote_string(dp->name);
374             log_add(L_DISK, "%s %s", dp->host->hostname, qname);
375             amfree(qname);
376             nb_disk++;
377         }
378     }
379
380     if (nb_disk == 0) {
381         if (errstr) {
382             error(_("no DLE to backup; %s"), errstr);
383         } else {
384             error(_("no DLE to backup"));
385         }
386         /*NOTREACHED*/
387     } else if (errstr) {
388         log_add(L_WARNING, "WARNING: %s", errstr);
389     }
390     amfree(errstr);
391
392     /* some initializations */
393
394     if(conf_runspercycle == 0) {
395         runs_per_cycle = conf_dumpcycle;
396     } else if(conf_runspercycle == -1 ) {
397         runs_per_cycle = guess_runs_from_tapelist();
398     } else
399         runs_per_cycle = conf_runspercycle;
400
401     if (runs_per_cycle <= 0) {
402         runs_per_cycle = 1;
403     }
404
405     /*
406      * do some basic sanity checking
407      */
408      if(conf_tapecycle <= runs_per_cycle) {
409         log_add(L_WARNING, _("tapecycle (%d) <= runspercycle (%d)"),
410                 conf_tapecycle, runs_per_cycle);
411      }
412     
413     tape = lookup_tapetype(conf_tapetype);
414     if(conf_maxdumpsize > (gint64)0) {
415         tape_length = conf_maxdumpsize;
416         g_fprintf(stderr, "planner: tape_length is set from maxdumpsize (%jd KB)\n",
417                          (intmax_t)conf_maxdumpsize);
418     }
419     else {
420         tape_length = tapetype_get_length(tape) * (gint64)conf_runtapes;
421         g_fprintf(stderr, "planner: tape_length is set from tape length (%jd KB) * runtapes (%d) == %jd KB\n",
422                          (intmax_t)tapetype_get_length(tape),
423                          conf_runtapes,
424                          (intmax_t)tape_length);
425     }
426     tape_mark = (size_t)tapetype_get_filemark(tape);
427     tt_blocksize_kb = (size_t)tapetype_get_blocksize(tape);
428     tt_blocksize = tt_blocksize_kb * 1024;
429
430     g_fprintf(stderr, _("%s: time %s: startup took %s secs\n"),
431                     get_pname(),
432                     walltime_str(curclock()),
433                     walltime_str(timessub(curclock(), section_start)));
434
435     /*
436      * 3. Send autoflush dumps left on the holding disks
437      *
438      * This should give us something to do while we generate the new
439      * dump schedule.
440      */
441
442     g_fprintf(stderr,_("\nSENDING FLUSHES...\n"));
443
444     if(conf_autoflush && !no_taper) {
445         dumpfile_t  file;
446         GSList *holding_list, *holding_file;
447         char *qdisk, *qhname;
448
449         /* get *all* flushable files in holding, without checking against
450          * the disklist (which may not contain some of the dumps) */
451         holding_list = holding_get_files_for_flush(NULL);
452         for(holding_file=holding_list; holding_file != NULL;
453                                        holding_file = holding_file->next) {
454             holding_file_get_dumpfile((char *)holding_file->data, &file);
455
456             if (holding_file_size((char *)holding_file->data, 1) <= 0) {
457                 log_add(L_INFO, "%s: removing file with no data.",
458                         (char *)holding_file->data);
459                 holding_file_unlink((char *)holding_file->data);
460                 dumpfile_free_data(&file);
461                 continue;
462             }
463
464             /* see if this matches the command-line arguments */
465             if (conf_autoflush == 1 &&
466                 !match_dumpfile(&file, argc-diskarg_offset,
467                                        argv+diskarg_offset)) {
468                 continue;
469             }
470
471             qdisk = quote_string(file.disk);
472             qhname = quote_string((char *)holding_file->data);
473             log_add(L_DISK, "%s %s", file.name, qdisk);
474             g_fprintf(stderr,
475                     "FLUSH %s %s %s %d %s\n",
476                     file.name,
477                     qdisk,
478                     file.datestamp,
479                     file.dumplevel,
480                     qhname);
481             g_fprintf(stdout,
482                     "FLUSH %s %s %s %d %s\n",
483                     file.name,
484                     qdisk,
485                     file.datestamp,
486                     file.dumplevel,
487                     qhname);
488             amfree(qdisk);
489             amfree(qhname);
490             dumpfile_free_data(&file);
491         }
492         slist_free_full(holding_list, g_free);
493         holding_list = NULL;
494     }
495     g_fprintf(stderr, _("ENDFLUSH\n"));
496     g_fprintf(stdout, _("ENDFLUSH\n"));
497     fflush(stdout);
498
499     /*
500      * 4. Calculate Preliminary Dump Levels
501      *
502      * Before we can get estimates from the remote slave hosts, we make a
503      * first attempt at guessing what dump levels we will be dumping at
504      * based on the curinfo database.
505      */
506
507     g_fprintf(stderr,_("\nSETTING UP FOR ESTIMATES...\n"));
508     section_start = curclock();
509
510     startq.head = startq.tail = NULL;
511     while(!empty(origq)) {
512         disk_t *dp = dequeue_disk(&origq);
513         if(dp->todo == 1) {
514             setup_estimate(dp);
515         }
516     }
517
518     g_fprintf(stderr, _("%s: time %s: setting up estimates took %s secs\n"),
519                     get_pname(),
520                     walltime_str(curclock()),
521                     walltime_str(timessub(curclock(), section_start)));
522
523
524     /*
525      * 5. Get Dump Size Estimates from Remote Client Hosts
526      *
527      * Each host is queried (in parallel) for dump size information on all
528      * of its disks, and the results gathered as they come in.
529      */
530
531     /* go out and get the dump estimates */
532
533     g_fprintf(stderr,_("\nGETTING ESTIMATES...\n"));
534     section_start = curclock();
535
536     estq.head = estq.tail = NULL;
537     pestq.head = pestq.tail = NULL;
538     waitq.head = waitq.tail = NULL;
539     failq.head = failq.tail = NULL;
540
541     get_estimates();
542
543     g_fprintf(stderr, _("%s: time %s: getting estimates took %s secs\n"),
544                     get_pname(),
545                     walltime_str(curclock()),
546                     walltime_str(timessub(curclock(), section_start)));
547
548     /*
549      * At this point, all disks with estimates are in estq, and
550      * all the disks on hosts that didn't respond to our inquiry
551      * are in failq.
552      */
553
554     dump_queue("FAILED", failq, 15, stderr);
555     dump_queue("DONE", estq, 15, stderr);
556
557     if (!empty(failq)) {
558         exit_status = EXIT_FAILURE;
559     }
560
561     /*
562      * 6. Analyze Dump Estimates
563      *
564      * Each disk's estimates are looked at to determine what level it
565      * should dump at, and to calculate the expected size and time taking
566      * historical dump rates and compression ratios into account.  The
567      * total expected size is accumulated as well.
568      */
569
570     g_fprintf(stderr,_("\nANALYZING ESTIMATES...\n"));
571     section_start = curclock();
572
573                         /* an empty tape still has a label and an endmark */
574     total_size = ((gint64)tt_blocksize_kb + (gint64)tape_mark) * (gint64)2;
575     total_lev0 = 0.0;
576     balanced_size = 0.0;
577
578     schedq.head = schedq.tail = NULL;
579     while(!empty(estq)) analyze_estimate(dequeue_disk(&estq));
580     while(!empty(failq)) handle_failed(dequeue_disk(&failq));
581
582     run_server_global_scripts(EXECUTE_ON_POST_ESTIMATE, get_config_name());
583
584     /*
585      * At this point, all the disks are on schedq sorted by priority.
586      * The total estimated size of the backups is in total_size.
587      */
588
589     {
590         disk_t *dp;
591
592         g_fprintf(stderr, _("INITIAL SCHEDULE (size %lld):\n"),
593                 (long long)total_size);
594         for(dp = schedq.head; dp != NULL; dp = dp->next) {
595             qname = quote_string(dp->name);
596             g_fprintf(stderr, _("  %s %s pri %d lev %d nsize %lld csize %lld\n"),
597                     dp->host->hostname, qname, est(dp)->dump_priority,
598                     est(dp)->dump_est->level,
599                     (long long)est(dp)->dump_est->nsize,
600                     (long long)est(dp)->dump_est->csize);
601             amfree(qname);
602         }
603     }
604
605
606     /*
607      * 7. Delay Dumps if Schedule Too Big
608      *
609      * If the generated schedule is too big to fit on the tape, we need to
610      * delay some full dumps to make room.  Incrementals will be done
611      * instead (except for new or forced disks).
612      *
613      * In extreme cases, delaying all the full dumps is not even enough.
614      * If so, some low-priority incrementals will be skipped completely
615      * until the dumps fit on the tape.
616      */
617
618     g_fprintf(stderr, _("\nDELAYING DUMPS IF NEEDED, total_size %lld, tape length %lld mark %zu\n"),
619             (long long)total_size,
620             (long long)tape_length,
621             tape_mark);
622
623     initial_size = total_size;
624
625     delay_dumps();
626
627     /* XXX - why bother checking this? */
628     if(empty(schedq) && total_size < initial_size) {
629         error(_("cannot fit anything on tape, bailing out"));
630         /*NOTREACHED*/
631     }
632
633
634     /*
635      * 8. Promote Dumps if Schedule Too Small
636      *
637      * Amanda attempts to balance the full dumps over the length of the
638      * dump cycle.  If this night's full dumps are too small relative to
639      * the other nights, promote some high-priority full dumps that will be
640      * due for the next run, to full dumps for tonight, taking care not to
641      * overflow the tape size.
642      *
643      * This doesn't work too well for small sites.  For these we scan ahead
644      * looking for nights that have an excessive number of dumps and promote
645      * one of them.
646      *
647      * Amanda never delays full dumps just for the sake of balancing the
648      * schedule, so it can take a full cycle to balance the schedule after
649      * a big bump.
650      */
651
652     g_fprintf(stderr,
653      _("\nPROMOTING DUMPS IF NEEDED, total_lev0 %1.0lf, balanced_size %1.0lf...\n"),
654             total_lev0, balanced_size);
655
656     balance_threshold = balanced_size * PROMOTE_THRESHOLD;
657     moved_one = 1;
658     while((balanced_size - total_lev0) > balance_threshold && moved_one)
659         moved_one = promote_highest_priority_incremental();
660
661     moved_one = promote_hills();
662
663     g_fprintf(stderr, _("%s: time %s: analysis took %s secs\n"),
664                     get_pname(),
665                     walltime_str(curclock()),
666                     walltime_str(timessub(curclock(), section_start)));
667
668
669     /*
670      * 9. Output Schedule
671      *
672      * The schedule goes to stdout, presumably to driver.  A copy is written
673      * on stderr for the debug file.
674      */
675
676     g_fprintf(stderr,_("\nGENERATING SCHEDULE:\n--------\n"));
677     if (empty(schedq)) {
678         exit_status = EXIT_FAILURE;
679         g_fprintf(stderr, _("--> Generated empty schedule! <--\n"));
680     } else {
681         while(!empty(schedq)) output_scheduleline(dequeue_disk(&schedq));
682     }
683     g_fprintf(stderr, _("--------\n"));
684
685     close_infofile();
686     log_add(L_FINISH, _("date %s time %s"), planner_timestamp, walltime_str(curclock()));
687     log_add(L_INFO, "pid-done %ld", (long)getpid());
688
689     clear_tapelist();
690     amfree(planner_timestamp);
691     amfree(our_feature_string);
692     am_release_feature_set(our_features);
693     our_features = NULL;
694
695     dbclose();
696
697     return exit_status;
698 }
699
700
701 \f
702 /*
703  * ========================================================================
704  * SETUP FOR ESTIMATES
705  *
706  */
707
708 static void askfor(est_t *, int, int, info_t *);
709 static int last_level(info_t *info);              /* subroutines */
710 static one_est_t *est_for_level(disk_t *dp, int level);
711 static void est_csize(disk_t *dp, one_est_t *one_est);
712 static gint64 est_tape_size(disk_t *dp, int level);
713 static int next_level0(disk_t *dp, info_t *info);
714 static int runs_at(info_t *info, int lev);
715 static gint64 bump_thresh(int level, gint64 size_level_0, int bumppercent, gint64 bumpsize, double bumpmult);
716 static int when_overwrite(char *label);
717
718 static void askfor(
719     est_t *ep,  /* esimate data block */
720     int seq,    /* sequence number of request */
721     int lev,    /* dump level being requested */
722     info_t *info)       /* info block for disk */
723 {
724     if(seq < 0 || seq >= MAX_LEVELS) {
725         error(_("error [planner askfor: seq out of range 0..%d: %d]"),
726               MAX_LEVELS, seq);
727         /*NOTREACHED*/
728     }
729     if(lev < -1 || lev >= DUMP_LEVELS) {
730         error(_("error [planner askfor: lev out of range -1..%d: %d]"),
731               DUMP_LEVELS, lev);
732         /*NOTREACHED*/
733     }
734
735     if (lev == -1) {
736         ep->estimate[seq].level = -1;
737         ep->estimate[seq].dumpdate = (char *)0;
738         ep->estimate[seq].nsize = (gint64)-3;
739         ep->estimate[seq].csize = (gint64)-3;
740         ep->estimate[seq].guessed = 0;
741         return;
742     }
743
744     ep->estimate[seq].level = lev;
745
746     ep->estimate[seq].dumpdate = stralloc(get_dumpdate(info,lev));
747
748     ep->estimate[seq].nsize = (gint64)-3;
749     ep->estimate[seq].csize = (gint64)-3;
750     ep->estimate[seq].guessed = 0;
751
752     return;
753 }
754
755 static void
756 setup_estimate(
757      disk_t *dp)
758 {
759     est_t *ep;
760     info_t *info;
761     int i;
762     char *qname;
763     int overwrite_runs;
764
765     assert(dp && dp->host);
766
767     qname = quote_string(dp->name);
768     g_fprintf(stderr, _("%s: time %s: setting up estimates for %s:%s\n"),
769                     get_pname(), walltime_str(curclock()),
770                     dp->host->hostname, qname);
771
772     /* get current information about disk */
773
774     info = g_new0(info_t, 1);
775     if(get_info(dp->host->hostname, dp->name, info)) {
776         /* no record for this disk, make a note of it */
777         log_add(L_INFO, _("Adding new disk %s:%s."), dp->host->hostname, qname);
778     }
779
780     if (dp->data_path == DATA_PATH_DIRECTTCP) {
781         if (dp->compress != COMP_NONE) {
782             log_add(L_FAIL, _("%s %s %s 0 [Can't compress directtcp data-path]"),
783                     dp->host->hostname, qname, planner_timestamp);
784             g_fprintf(stderr,_("%s:%s lev 0 skipped can't compress directtcp data-path\n"),
785                       dp->host->hostname, qname);
786             amfree(qname);
787             return;
788         }
789         if (dp->encrypt != ENCRYPT_NONE) {
790             log_add(L_FAIL, _("%s %s %s 0 [Can't encrypt directtcp data-path]"),
791                     dp->host->hostname, qname, planner_timestamp);
792             g_fprintf(stderr,_("%s:%s lev 0 skipped can't encrypt directtcp data-path\n"),
793                       dp->host->hostname, qname);
794             amfree(qname);
795             return;
796         }
797         if (dp->to_holdingdisk == HOLD_REQUIRED) {
798             log_add(L_FAIL, _("%s %s %s 0 [Holding disk can't be use for directtcp data-path]"),
799                     dp->host->hostname, qname, planner_timestamp);
800             g_fprintf(stderr,_("%s:%s lev 0 skipped Holding disk can't be use for directtcp data-path\n"),
801                       dp->host->hostname, qname);
802             amfree(qname);
803             return;
804         } else if (dp->to_holdingdisk == HOLD_AUTO) {
805             g_fprintf(stderr,_("%s:%s Disabling holding disk\n"),
806                       dp->host->hostname, qname);
807             dp->to_holdingdisk = HOLD_NEVER;
808         }
809     }
810
811     /* setup working data struct for disk */
812
813     ep = alloc(SIZEOF(est_t));
814     dp->up = (void *) ep;
815     ep->info = info;
816     ep->state = DISK_READY;
817     ep->dump_priority = dp->priority;
818     ep->errstr = 0;
819     ep->promote = 0;
820     ep->post_dle = 0;
821     ep->degr_mesg = NULL;
822     ep->dump_est = &default_one_est;
823     ep->degr_est = &default_one_est;
824
825     /* calculated fields */
826
827     if (ISSET(info->command, FORCE_FULL)) {
828         /* force a level 0, kind of like a new disk */
829         if(dp->strategy == DS_NOFULL) {
830             /*
831              * XXX - Not sure what it means to force a no-full disk.  The
832              * purpose of no-full is to just dump changes relative to a
833              * stable base, for example root partitions that vary only
834              * slightly from a site-wide prototype.  Only the variations
835              * are dumped.
836              *
837              * If we allow a level 0 onto the Amanda cycle, then we are
838              * hosed when that tape gets re-used next.  Disallow this for
839              * now.
840              */
841             log_add(L_WARNING,
842                     _("Cannot force full dump of %s:%s with no-full option."),
843                     dp->host->hostname, qname);
844
845             /* clear force command */
846             CLR(info->command, FORCE_FULL);
847             ep->last_level = last_level(info);
848             ep->next_level0 = next_level0(dp, info);
849         } else if (dp->strategy == DS_INCRONLY) {
850             log_add(L_WARNING,
851                     _("Cannot force full dump of %s:%s with incronly option."),
852                     dp->host->hostname, qname);
853
854             /* clear force command */
855             CLR(info->command, FORCE_FULL);
856             ep->last_level = last_level(info);
857             ep->next_level0 = next_level0(dp, info);
858         } else {
859             ep->degr_mesg = _("Skipping: force-full disk can't be dumped in degraded mode");
860             ep->last_level = -1;
861             ep->next_level0 = -conf_dumpcycle;
862             log_add(L_INFO, _("Forcing full dump of %s:%s as directed."),
863                     dp->host->hostname, qname);
864         }
865     }
866     else if(dp->strategy == DS_NOFULL) {
867         /* force estimate of level 1 */
868         ep->last_level = 1;
869         ep->next_level0 = next_level0(dp, info);
870     }
871     else {
872         ep->last_level = last_level(info);
873         ep->next_level0 = next_level0(dp, info);
874     }
875
876     /* adjust priority levels */
877
878     /* warn if dump will be overwritten */
879     if (ep->last_level > -1 && strlen(info->inf[0].label) > 0) {
880         overwrite_runs = when_overwrite(info->inf[0].label);
881         if(overwrite_runs == 0) {
882             log_add(L_WARNING, _("Last full dump of %s:%s "
883                     "on tape %s overwritten on this run."),
884                     dp->host->hostname, qname, info->inf[0].label);
885         } else if(overwrite_runs <= RUNS_REDZONE) {
886             log_add(L_WARNING,
887                     plural(_("Last full dump of %s:%s on tape %s overwritten in %d run."),
888                            _("Last full dump of %s:%s on tape %s overwritten in %d runs."), overwrite_runs),
889                     dp->host->hostname, qname, info->inf[0].label,
890                     overwrite_runs);
891         }
892     }
893
894     /* warn if last level 1 will be overwritten */
895     if (ep->last_level > 1 && strlen(info->inf[1].label) > 0) {
896         overwrite_runs = when_overwrite(info->inf[1].label);
897         if(overwrite_runs == 0) {
898             log_add(L_WARNING, _("Last level 1 dump of %s:%s "
899                     "on tape %s overwritten on this run, resetting to level 1"),
900                     dp->host->hostname, qname, info->inf[1].label);
901             ep->last_level = 0;
902         } else if(overwrite_runs <= RUNS_REDZONE) {
903             log_add(L_WARNING,
904                     plural(_("Last level 1 dump of %s:%s on tape %s overwritten in %d run."),
905                            _("Last level 1 dump of %s:%s on tape %s overwritten in %d runs."), overwrite_runs),
906                     dp->host->hostname, qname, info->inf[1].label,
907                     overwrite_runs);
908         }
909     }
910
911     if(ep->next_level0 < 0) {
912         g_fprintf(stderr,plural(_("%s:%s overdue %d day for level 0\n"),
913                               _("%s:%s overdue %d days for level 0\n"),
914                               (-ep->next_level0)),
915                 dp->host->hostname, qname, (-ep->next_level0));
916         ep->dump_priority -= ep->next_level0;
917     }
918     else if (ISSET(info->command, FORCE_FULL))
919         ep->dump_priority += 1;
920     /* else XXX bump up the priority of incrementals that failed last night */
921
922     /* handle external level 0 dumps */
923
924     if(dp->skip_full && dp->strategy != DS_NOINC) {
925         if(ep->next_level0 <= 0) {
926             /* update the date field */
927             info->inf[0].date = today;
928             CLR(info->command, FORCE_FULL);
929             ep->next_level0 += conf_dumpcycle;
930             ep->last_level = 0;
931             if(put_info(dp->host->hostname, dp->name, info)) {
932                 error(_("could not put info record for %s:%s: %s"),
933                       dp->host->hostname, qname, strerror(errno));
934                 /*NOTREACHED*/
935             }
936             log_add(L_INFO, _("Skipping full dump of %s:%s today."),
937                     dp->host->hostname, qname);
938             g_fprintf(stderr,_("%s:%s lev 0 skipped due to skip-full flag\n"),
939                     dp->host->hostname, qname);
940             /* don't enqueue the disk */
941             askfor(ep, 0, -1, info);
942             askfor(ep, 1, -1, info);
943             askfor(ep, 2, -1, info);
944             g_fprintf(stderr, _("%s: SKIPPED %s %s 0 [skip-full]\n"),
945                     get_pname(), dp->host->hostname, qname);
946             log_add(L_SUCCESS, _("%s %s %s 0 [skipped: skip-full]"),
947                     dp->host->hostname, qname, planner_timestamp);
948             amfree(qname);
949             return;
950         }
951
952         if(ep->last_level == -1) {
953             /* probably a new disk, but skip-full means no full! */
954             ep->last_level = 0;
955         }
956
957         if(ep->next_level0 == 1) {
958             log_add(L_WARNING, _("Skipping full dump of %s:%s tomorrow."),
959                     dp->host->hostname, qname);
960         }
961     }
962
963     if(dp->strategy == DS_INCRONLY && ep->last_level == -1 && !ISSET(info->command, FORCE_FULL)) {
964         /* don't enqueue the disk */
965         askfor(ep, 0, -1, info);
966         askfor(ep, 1, -1, info);
967         askfor(ep, 2, -1, info);
968         log_add(L_FAIL, _("%s %s 19000101 1 [Skipping incronly because no full dump were done]"),
969                 dp->host->hostname, qname);
970         g_fprintf(stderr,_("%s:%s lev 1 skipped due to strategy incronly and no full dump were done\n"),
971                 dp->host->hostname, qname);
972         amfree(qname);
973         return;
974     }
975
976     /* handle "skip-incr" type archives */
977
978     if(dp->skip_incr && ep->next_level0 > 0) {
979         g_fprintf(stderr,_("%s:%s lev 1 skipped due to skip-incr flag\n"),
980                 dp->host->hostname, qname);
981         /* don't enqueue the disk */
982         askfor(ep, 0, -1, info);
983         askfor(ep, 1, -1, info);
984         askfor(ep, 2, -1, info);
985
986         g_fprintf(stderr, _("%s: SKIPPED %s %s 1 [skip-incr]\n"),
987                 get_pname(), dp->host->hostname, qname);
988
989         log_add(L_SUCCESS, _("%s %s %s 1 [skipped: skip-incr]"),
990                 dp->host->hostname, qname, planner_timestamp);
991         amfree(qname);
992         return;
993     }
994
995     if( ep->last_level == -1 && ep->next_level0 > 0 && 
996         dp->strategy != DS_NOFULL && dp->strategy != DS_INCRONLY &&
997         conf_reserve == 100) {
998         log_add(L_WARNING, _("%s:%s mismatch: no tapelist record, "
999                 "but curinfo next_level0: %d."),
1000                 dp->host->hostname, qname, ep->next_level0);
1001         ep->next_level0 = 0;
1002     }
1003
1004     //if(ep->last_level == 0) ep->level_days = 0;
1005     //else ep->level_days = runs_at(info, ep->last_level);
1006     ep->level_days = runs_at(info, ep->last_level);
1007     ep->last_lev0size = info->inf[0].csize;
1008
1009     ep->fullrate = perf_average(info->full.rate, 0.0);
1010     ep->incrrate = perf_average(info->incr.rate, 0.0);
1011
1012     ep->fullcomp = perf_average(info->full.comp, dp->comprate[0]);
1013     ep->incrcomp = perf_average(info->incr.comp, dp->comprate[1]);
1014
1015     /* determine which estimates to get */
1016
1017     i = 0;
1018
1019     if (dp->strategy == DS_NOINC ||
1020         (!dp->skip_full &&
1021          (!ISSET(info->command, FORCE_BUMP) ||
1022           dp->skip_incr ||
1023           ep->last_level == -1))) {
1024         if(ISSET(info->command, FORCE_BUMP) && ep->last_level == -1) {
1025             log_add(L_INFO,
1026                   _("Remove force-bump command of %s:%s because it's a new disk."),
1027                     dp->host->hostname, qname);
1028         }
1029         switch (dp->strategy) {
1030         case DS_STANDARD: 
1031         case DS_NOINC:
1032             askfor(ep, i++, 0, info);
1033             if (ep->last_level == -1)
1034                 ep->degr_mesg = _("Skipping: new disk can't be dumped in degraded mode");
1035             else
1036                 ep->degr_mesg = _("Skipping: strategy NOINC can't be dumped in degraded mode");
1037             if(dp->skip_full) {
1038                 log_add(L_INFO, _("Ignoring skip-full for %s:%s "
1039                         "because the strategy is NOINC."),
1040                         dp->host->hostname, qname);
1041             }
1042             if(ISSET(info->command, FORCE_BUMP)) {
1043                 log_add(L_INFO,
1044                  _("Ignoring FORCE_BUMP for %s:%s because the strategy is NOINC."),
1045                         dp->host->hostname, qname);
1046             }
1047             
1048             break;
1049
1050         case DS_NOFULL:
1051             break;
1052
1053         case DS_INCRONLY:
1054             if (ISSET(info->command, FORCE_FULL))
1055                 ep->last_level = 0;
1056             break;
1057         }
1058     }
1059
1060     if(!dp->skip_incr && !(dp->strategy == DS_NOINC)) {
1061         if(ep->last_level == -1) {              /* a new disk */
1062             if (ep->degr_mesg == NULL)
1063                 ep->degr_mesg = _("Skipping: new disk can't be dumped in degraded mode");
1064             if(dp->strategy == DS_NOFULL || dp->strategy == DS_INCRONLY) {
1065                 askfor(ep, i++, 1, info);
1066             } else {
1067                 assert(!dp->skip_full);         /* should be handled above */
1068             }
1069         } else {                                /* not new, pick normally */
1070             int curr_level;
1071
1072             curr_level = ep->last_level;
1073
1074             if (ISSET(info->command, FORCE_NO_BUMP)) {
1075                 if(curr_level > 0) { /* level 0 already asked for */
1076                     askfor(ep, i++, curr_level, info);
1077                 }
1078                 log_add(L_INFO,_("Preventing bump of %s:%s as directed."),
1079                         dp->host->hostname, qname);
1080                 ep->degr_mesg = _("Skipping: force-no-bump disk can't be dumped in degraded mode");
1081             } else if (ISSET(info->command, FORCE_BUMP)
1082                        && curr_level + 1 < DUMP_LEVELS) {
1083                 askfor(ep, i++, curr_level+1, info);
1084                 log_add(L_INFO,_("Bumping of %s:%s at level %d as directed."),
1085                         dp->host->hostname, qname, curr_level+1);
1086                 ep->degr_mesg = _("Skipping: force-bump disk can't be dumped in degraded mode");
1087             } else if (curr_level == 0) {
1088                 askfor(ep, i++, 1, info);
1089             } else {
1090                 askfor(ep, i++, curr_level, info);
1091                 /*
1092                  * If last time we dumped less than the threshold, then this
1093                  * time we will too, OR the extra size will be charged to both
1094                  * cur_level and cur_level + 1, so we will never bump.  Also,
1095                  * if we haven't been at this level 2 days, or the dump failed
1096                  * last night, we can't bump.
1097                  */
1098                 if((info->inf[curr_level].size == (gint64)0 || /* no data, try it anyway */
1099                     (((info->inf[curr_level].size > bump_thresh(curr_level, info->inf[0].size,dp->bumppercent, dp->bumpsize, dp->bumpmult)))
1100                      && ep->level_days >= dp->bumpdays))
1101                    && curr_level + 1 < DUMP_LEVELS) {
1102                     askfor(ep, i++, curr_level+1, info);
1103                 }
1104             }
1105         }
1106     }
1107
1108     while(i < MAX_LEVELS)       /* mark end of estimates */
1109         askfor(ep, i++, -1, info);
1110
1111     /* debug output */
1112
1113     g_fprintf(stderr, _("setup_estimate: %s:%s: command %u, options: %s    "
1114             "last_level %d next_level0 %d level_days %d    getting estimates "
1115             "%d (%lld) %d (%lld) %d (%lld)\n"),
1116             dp->host->hostname, qname, info->command,
1117             dp->strategy == DS_NOFULL ? "no-full" :
1118                  dp->strategy == DS_INCRONLY ? "incr-only" :
1119                  dp->skip_full ? "skip-full" :
1120                  dp->skip_incr ? "skip-incr" : "none",
1121             ep->last_level, ep->next_level0, ep->level_days,
1122             ep->estimate[0].level, (long long)ep->estimate[0].nsize,
1123             ep->estimate[1].level, (long long)ep->estimate[1].nsize,
1124             ep->estimate[2].level, (long long)ep->estimate[2].nsize);
1125
1126     assert(ep->estimate[0].level != -1);
1127     enqueue_disk(&startq, dp);
1128     amfree(qname);
1129 }
1130
1131 static int when_overwrite(
1132     char *label)
1133 {
1134     tape_t *tp;
1135     int runtapes;
1136
1137     runtapes = conf_runtapes;
1138     if(runtapes == 0) runtapes = 1;
1139
1140     if((tp = lookup_tapelabel(label)) == NULL)
1141         return 1;       /* "shouldn't happen", but trigger warning message */
1142     else if(tp->reuse == 0)
1143         return 1024;
1144     else if(lookup_nb_tape() > conf_tapecycle)
1145         return (lookup_nb_tape() - tp->position) / runtapes;
1146     else
1147         return (conf_tapecycle - tp->position) / runtapes;
1148 }
1149
1150 /* Return the estimated size for a particular dump */
1151 static one_est_t *
1152 est_for_level(
1153     disk_t *dp,
1154     int level)
1155 {
1156     int i;
1157
1158     if (level < 0 || level >= DUMP_LEVELS)
1159         return &default_one_est;
1160
1161     for (i = 0; i < MAX_LEVELS; i++) {
1162         if (level == est(dp)->estimate[i].level) {
1163             if (est(dp)->estimate[i].csize <= -1) {
1164                 est_csize(dp, &est(dp)->estimate[i]);
1165             }
1166             return &est(dp)->estimate[i];
1167         }
1168     }
1169     return &default_one_est;
1170 }
1171
1172 /* Return the estimated on-tape size of a particular dump */
1173 static void
1174 est_csize(
1175     disk_t    *dp,
1176     one_est_t *one_est)
1177 {
1178     gint64 size = one_est->nsize;
1179     double ratio;
1180
1181     if (dp->compress == COMP_NONE) {
1182         one_est->csize = one_est->nsize;
1183         return;
1184     }
1185
1186     if (one_est->level == 0) ratio = est(dp)->fullcomp;
1187     else ratio = est(dp)->incrcomp;
1188
1189     /*
1190      * make sure over-inflated compression ratios don't throw off the
1191      * estimates, this is mostly for when you have a small dump getting
1192      * compressed which takes up alot more disk/tape space relatively due
1193      * to the overhead of the compression.  This is specifically for
1194      * Digital Unix vdump.  This patch is courtesy of Rudolf Gabler
1195      * (RUG@USM.Uni-Muenchen.DE)
1196      */
1197
1198     if (ratio > 1.1) ratio = 1.1;
1199
1200     size = (gint64)((double)size * ratio);
1201
1202     /*
1203      * Ratio can be very small in some error situations, so make sure
1204      * size goes back greater than zero.  It may not be right, but
1205      * indicates we did get an estimate.
1206      */
1207     if (size <= (gint64)0) {
1208         size = (gint64)1;
1209     }
1210
1211     one_est->csize = size;
1212 }
1213
1214 static gint64 est_tape_size(
1215     disk_t *dp,
1216     int level)
1217 {
1218     one_est_t *dump_est;
1219
1220     dump_est = est_for_level(dp, level);
1221     if (dump_est->level >= 0 && dump_est->csize <= -1)
1222         est_csize(dp, dump_est);
1223     return dump_est->csize;
1224 }
1225
1226
1227 /* what was the level of the last successful dump to tape? */
1228 static int last_level(
1229     info_t *info)
1230 {
1231     int min_pos, min_level, i;
1232     time_t lev0_date, last_date;
1233     tape_t *tp;
1234
1235     if(info->last_level != -1)
1236         return info->last_level;
1237
1238     /* to keep compatibility with old infofile */
1239     min_pos = 1000000000;
1240     min_level = -1;
1241     lev0_date = EPOCH;
1242     last_date = EPOCH;
1243     for(i = 0; i < 9; i++) {
1244         if(conf_reserve < 100) {
1245             if(i == 0) lev0_date = info->inf[0].date;
1246             else if(info->inf[i].date < lev0_date) continue;
1247             if(info->inf[i].date > last_date) {
1248                 last_date = info->inf[i].date;
1249                 min_level = i;
1250             }
1251         }
1252         else {
1253             if((tp = lookup_tapelabel(info->inf[i].label)) == NULL) continue;
1254             /* cull any entries from previous cycles */
1255             if(i == 0) lev0_date = info->inf[0].date;
1256             else if(info->inf[i].date < lev0_date) continue;
1257
1258             if(tp->position < min_pos) {
1259                 min_pos = tp->position;
1260                 min_level = i;
1261             }
1262         }
1263     }
1264     info->last_level = i;
1265     return min_level;
1266 }
1267
1268 /* when is next level 0 due? 0 = today, 1 = tomorrow, etc*/
1269 static int
1270 next_level0(
1271     disk_t *dp,
1272     info_t *info)
1273 {
1274     if(dp->strategy == DS_NOFULL || dp->strategy == DS_INCRONLY)
1275         return 1;               /* fake it */
1276     else if (dp->strategy == DS_NOINC)
1277         return 0;
1278     else if(info->inf[0].date < (time_t)0)
1279         return -days_diff(EPOCH, today);        /* new disk */
1280     else
1281         return dp->dumpcycle - days_diff(info->inf[0].date, today);
1282 }
1283
1284 /* how many runs at current level? */
1285 static int runs_at(
1286     info_t *info,
1287     int lev)
1288 {
1289     tape_t *cur_tape, *old_tape;
1290     int last, nb_runs;
1291
1292     last = last_level(info);
1293     if(lev != last) return 0;
1294     if(info->consecutive_runs != -1)
1295         return info->consecutive_runs;
1296     if(lev == 0) return 1;
1297
1298     /* to keep compatibility with old infofile */
1299     cur_tape = lookup_tapelabel(info->inf[lev].label);
1300     old_tape = lookup_tapelabel(info->inf[lev-1].label);
1301     if(cur_tape == NULL || old_tape == NULL) return 0;
1302
1303     if(conf_runtapes == 0)
1304         nb_runs = (old_tape->position - cur_tape->position) / 1;
1305     else
1306         nb_runs = (old_tape->position - cur_tape->position) / conf_runtapes;
1307     info->consecutive_runs = nb_runs;
1308
1309     return nb_runs;
1310 }
1311
1312
1313 static gint64 bump_thresh(
1314     int level,
1315     gint64 size_level_0,
1316     int bumppercent,
1317     gint64 bumpsize,
1318     double bumpmult)
1319 {
1320     double bump;
1321
1322     if ((bumppercent != 0) && (size_level_0 > (gint64)1024)) {
1323         bump = ((double)size_level_0 * (double)bumppercent) / 100.0;
1324     }
1325     else {
1326         bump = (double)bumpsize;
1327     }
1328     while(--level) bump = bump * bumpmult;
1329
1330     return (gint64)bump;
1331 }
1332
1333
1334 \f
1335 /*
1336  * ========================================================================
1337  * GET REMOTE DUMP SIZE ESTIMATES
1338  *
1339  */
1340
1341 static void getsize(am_host_t *hostp);
1342 static disk_t *lookup_hostdisk(am_host_t *hp, char *str);
1343 static void handle_result(void *datap, pkt_t *pkt, security_handle_t *sech);
1344
1345
1346 static void get_estimates(void)
1347 {
1348     am_host_t *hostp;
1349     disk_t *dp, *dp1;
1350     int something_started;
1351
1352     something_started = 1;
1353     while(something_started) {
1354         something_started = 0;
1355         for(dp = startq.head; dp != NULL; dp = dp->next) {
1356             hostp = dp->host;
1357             if(hostp->up == HOST_READY) {
1358                 something_started = 1;
1359                 run_server_host_scripts(EXECUTE_ON_PRE_HOST_ESTIMATE,
1360                                         get_config_name(), hostp);
1361                 for(dp1 = hostp->disks; dp1 != NULL; dp1 = dp1->hostnext) {
1362                     if (dp1->todo)
1363                         run_server_dle_scripts(EXECUTE_ON_PRE_DLE_ESTIMATE,
1364                                            get_config_name(), dp1,
1365                                            est(dp1)->estimate[0].level);
1366                 }
1367                 getsize(hostp);
1368                 protocol_check();
1369                 /*
1370                  * dp is no longer on startq, so dp->next is not valid
1371                  * and we have to start all over.
1372                  */
1373                 break;
1374             }
1375         }
1376     }
1377     protocol_run();
1378
1379     while(!empty(waitq)) {
1380         disk_t *dp = dequeue_disk(&waitq);
1381         est(dp)->errstr = _("hmm, disk was stranded on waitq");
1382         enqueue_disk(&failq, dp);
1383     }
1384
1385     while(!empty(pestq)) {
1386         disk_t *dp = dequeue_disk(&pestq);
1387         char *  qname = quote_string(dp->name);
1388         int i;
1389
1390         for (i=0; i < MAX_LEVELS; i++) {
1391             if (est(dp)->estimate[i].level != -1 &&
1392                 est(dp)->estimate[i].nsize < (gint64)0) {
1393                 if (est(dp)->estimate[i].nsize == (gint64)-3) {
1394                     log_add(L_WARNING,
1395                             _("disk %s:%s, estimate of level %d timed out."),
1396                             dp->host->hostname, qname, est(dp)->estimate[i].level);
1397                 }
1398                 est(dp)->estimate[i].level = -1;
1399             }
1400         }
1401
1402         if ((est(dp)->estimate[0].level != -1 &&
1403              est(dp)->estimate[0].nsize > (gint64)0) ||
1404             (est(dp)->estimate[1].level != -1 &&
1405              est(dp)->estimate[1].nsize > (gint64)0) ||
1406             (est(dp)->estimate[2].level != -1 &&
1407              est(dp)->estimate[2].nsize > (gint64)0)) {
1408             enqueue_disk(&estq, dp);
1409         }
1410         else {
1411            est(dp)->errstr = vstralloc("disk ", qname,
1412                                        _(", all estimate timed out"), NULL);
1413            enqueue_disk(&failq, dp);
1414         }
1415         amfree(qname);
1416     }
1417 }
1418
1419 static void getsize(
1420     am_host_t *hostp)
1421 {
1422     char        number[NUM_STR_SIZE], *req;
1423     disk_t *    dp;
1424     int         i;
1425     time_t      estimates, timeout;
1426     size_t      req_len;
1427     const       security_driver_t *secdrv;
1428     char *      calcsize;
1429     char *      qname, *b64disk = NULL;
1430     char *      qdevice, *b64device = NULL;
1431     estimate_t     estimate;
1432     estimatelist_t el;
1433
1434     assert(hostp->disks != NULL);
1435
1436     if(hostp->up != HOST_READY) {
1437         return;
1438     }
1439
1440     /*
1441      * The first time through here we send a "noop" request.  This will
1442      * return the feature list from the client if it supports that.
1443      * If it does not, handle_result() will set the feature list to an
1444      * empty structure.  In either case, we do the disks on the second
1445      * (and subsequent) pass(es).
1446      */
1447     if(hostp->features != NULL) { /* sendsize service */
1448         int nb_client = 0;
1449         int nb_server = 0;
1450
1451         int has_features = am_has_feature(hostp->features,
1452                                           fe_req_options_features);
1453         int has_hostname = am_has_feature(hostp->features,
1454                                           fe_req_options_hostname);
1455         int has_maxdumps = am_has_feature(hostp->features,
1456                                           fe_req_options_maxdumps);
1457         int has_config   = am_has_feature(hostp->features,
1458                                           fe_req_options_config);
1459
1460         g_snprintf(number, SIZEOF(number), "%d", hostp->maxdumps);
1461         req = vstralloc("SERVICE ", "sendsize", "\n",
1462                         "OPTIONS ",
1463                         has_features ? "features=" : "",
1464                         has_features ? our_feature_string : "",
1465                         has_features ? ";" : "",
1466                         has_maxdumps ? "maxdumps=" : "",
1467                         has_maxdumps ? number : "",
1468                         has_maxdumps ? ";" : "",
1469                         has_hostname ? "hostname=" : "",
1470                         has_hostname ? hostp->hostname : "",
1471                         has_hostname ? ";" : "",
1472                         has_config   ? "config=" : "",
1473                         has_config   ? get_config_name() : "",
1474                         has_config   ? ";" : "",
1475                         "\n",
1476                         NULL);
1477         req_len = strlen(req);
1478         req_len += 128;                 /* room for SECURITY ... */
1479         estimates = 0;
1480         for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1481             char *s = NULL;
1482             char *es;
1483             size_t s_len = 0;
1484             GPtrArray *errarray;
1485
1486             if(dp->todo == 0) continue;
1487
1488             if(est(dp)->state != DISK_READY) continue;
1489
1490             est(dp)->got_estimate = 0;
1491             if (est(dp)->estimate[0].level == -1) {
1492                 est(dp)->state = DISK_DONE;
1493                 continue;
1494             }
1495
1496             qname = quote_string(dp->name);
1497
1498             errarray = validate_optionstr(dp);
1499             if (errarray->len > 0) {
1500                 guint i;
1501                 for (i=0; i < errarray->len; i++) {
1502                     log_add(L_FAIL, _("%s %s %s 0 [%s]"),
1503                             dp->host->hostname, qname,
1504                             planner_timestamp,
1505                             (char *)g_ptr_array_index(errarray, i));
1506                 }
1507                 amfree(qname);
1508                 est(dp)->state = DISK_DONE;
1509                 continue;
1510             }
1511                     
1512             b64disk = amxml_format_tag("disk", dp->name);
1513             qdevice = quote_string(dp->device);
1514             estimate = (estimate_t)GPOINTER_TO_INT(dp->estimatelist->data);
1515             if (dp->device)
1516                 b64device = amxml_format_tag("diskdevice", dp->device);
1517
1518             estimate = ES_CLIENT;
1519             for (el = dp->estimatelist; el != NULL; el = el->next) {
1520                 estimate = (estimate_t)GPOINTER_TO_INT(el->data);
1521                 if (estimate == ES_SERVER)
1522                     break;
1523             }
1524             if (estimate == ES_SERVER) {
1525                 info_t info;
1526                 nb_server++;
1527                 get_info(dp->host->hostname, dp->name, &info);
1528                 for(i = 0; i < MAX_LEVELS; i++) {
1529                     int lev = est(dp)->estimate[i].level;
1530
1531                     if(lev == -1) break;
1532                     server_estimate(dp, i, &info, lev);
1533                 }
1534                 g_fprintf(stderr,_("%s time %s: got result for host %s disk %s:"),
1535                         get_pname(), walltime_str(curclock()),
1536                         dp->host->hostname, qname);
1537                 g_fprintf(stderr,_(" %d -> %lldK, %d -> %lldK, %d -> %lldK\n"),
1538                           est(dp)->estimate[0].level,
1539                           (long long)est(dp)->estimate[0].nsize,
1540                           est(dp)->estimate[1].level,
1541                           (long long)est(dp)->estimate[1].nsize,
1542                           est(dp)->estimate[2].level,
1543                           (long long)est(dp)->estimate[2].nsize);
1544                 if (!am_has_feature(hostp->features, fe_xml_estimate)) {
1545                     est(dp)->state = DISK_DONE;
1546                     remove_disk(&startq, dp);
1547                     enqueue_disk(&estq, dp);
1548                 }
1549             }
1550
1551             estimate = ES_SERVER;
1552             for (el = dp->estimatelist; el != NULL; el = el->next) {
1553                 estimate = (estimate_t)GPOINTER_TO_INT(el->data);
1554                 if (estimate == ES_CLIENT || estimate == ES_CALCSIZE)
1555                     break;
1556             }
1557             if (estimate == ES_CLIENT ||
1558                 estimate == ES_CALCSIZE ||
1559                 (am_has_feature(hostp->features, fe_req_xml) &&
1560                  am_has_feature(hostp->features, fe_xml_estimate))) {
1561                 nb_client++;
1562                 i = 0;
1563
1564                 if (am_has_feature(hostp->features, fe_req_xml)) {
1565                     char *levelstr = NULL;
1566                     char *spindlestr = NULL;
1567                     char level[NUM_STR_SIZE];
1568                     char spindle[NUM_STR_SIZE];
1569                     char *o;
1570                     char *l;
1571                     info_t info;
1572
1573                     get_info(dp->host->hostname, dp->name, &info);
1574                     for(i = 0; i < MAX_LEVELS; i++) {
1575                         char *server;
1576                         int lev = est(dp)->estimate[i].level;
1577                         if (lev == -1) break;
1578                         g_snprintf(level, SIZEOF(level), "%d", lev);
1579                         if (am_has_feature(hostp->features, fe_xml_level_server) &&
1580                             server_can_do_estimate(dp, &info, lev)) {
1581                             server = "<server>YES</server>";
1582                         } else {
1583                             server = "";
1584                         }
1585                         vstrextend(&levelstr, "  <level>",
1586                                    level, server,
1587                                    "</level>\n", NULL);
1588                     }
1589                     g_snprintf(spindle, SIZEOF(spindle), "%d", dp->spindle);
1590                     spindlestr = vstralloc("  <spindle>",
1591                                            spindle,
1592                                            "</spindle>\n", NULL);
1593                     o = xml_optionstr(dp, 0);
1594                     
1595                     if (strcmp(dp->program,"DUMP") == 0 ||
1596                         strcmp(dp->program,"GNUTAR") == 0) {
1597                         l = vstralloc("<dle>\n",
1598                                       "  <program>",
1599                                       dp->program,
1600                                       "</program>\n", NULL);
1601                     } else {
1602                         l = vstralloc("<dle>\n",
1603                                       "  <program>APPLICATION</program>\n",
1604                                       NULL);
1605                         if (dp->application) {
1606                             application_t *application;
1607                             char *xml_app;
1608
1609                             application = lookup_application(dp->application);
1610                             g_assert(application != NULL);
1611                             xml_app = xml_application(dp, application,
1612                                                       hostp->features);
1613                             vstrextend(&l, xml_app, NULL);
1614                             amfree(xml_app);
1615                         }
1616                     }
1617
1618                     es = xml_estimate(dp->estimatelist, hostp->features);
1619                     vstrextend(&l, es, "\n", NULL);
1620                     amfree(es);
1621                     vstrextend(&l, "  ", b64disk, "\n", NULL);
1622                     if (dp->device)
1623                         vstrextend(&l, "  ", b64device, "\n", NULL);
1624                     vstrextend(&l, levelstr, spindlestr, o, "</dle>\n", NULL);
1625                     strappend(s, l);
1626                     s_len += strlen(l);
1627                     amfree(l);
1628                     amfree(levelstr);
1629                     amfree(spindlestr);
1630                     amfree(o);
1631                 } else if (strcmp(dp->program,"DUMP") != 0 &&
1632                            strcmp(dp->program,"GNUTAR") != 0) {
1633                     est(dp)->errstr = newvstrallocf(est(dp)->errstr,
1634                                         _("does not support application-api"));
1635                 } else {
1636                     for(i = 0; i < MAX_LEVELS; i++) {
1637                         char *l;
1638                         char *exclude1 = "";
1639                         char *exclude2 = "";
1640                         char *excludefree = NULL;
1641                         char *include1 = "";
1642                         char *include2 = "";
1643                         char *includefree = NULL;
1644                         char spindle[NUM_STR_SIZE];
1645                         char level[NUM_STR_SIZE];
1646                         int lev = est(dp)->estimate[i].level;
1647
1648                         if(lev == -1) break;
1649
1650                         g_snprintf(level, SIZEOF(level), "%d", lev);
1651                         g_snprintf(spindle, SIZEOF(spindle), "%d", dp->spindle);
1652                         if (am_has_feature(hostp->features,
1653                                            fe_sendsize_req_options)){
1654                             exclude1 = " OPTIONS |";
1655                             exclude2 = optionstr(dp);
1656                             if ( exclude2 == NULL ) {
1657                                 error(_("problem with option string, check the dumptype definition.\n"));
1658                             }
1659                             excludefree = exclude2;
1660                             includefree = NULL;
1661                         } else {
1662                             if (dp->exclude_file &&
1663                                 dp->exclude_file->nb_element == 1) {
1664                                 exclude1 = " exclude-file=";
1665                                 exclude2 = quote_string(
1666                                                 dp->exclude_file->first->name);
1667                                 excludefree = exclude2;
1668                             }
1669                             else if (dp->exclude_list &&
1670                                      dp->exclude_list->nb_element == 1) {
1671                                 exclude1 = " exclude-list=";
1672                                 exclude2 = quote_string(
1673                                                 dp->exclude_list->first->name);
1674                                 excludefree = exclude2;
1675                             }
1676                             if (dp->include_file &&
1677                                 dp->include_file->nb_element == 1) {
1678                                 include1 = " include-file=";
1679                                 include2 = quote_string(
1680                                                 dp->include_file->first->name);
1681                                 includefree = include2;
1682                             }
1683                             else if (dp->include_list &&
1684                                      dp->include_list->nb_element == 1) {
1685                                 include1 = " include-list=";
1686                                 include2 = quote_string(
1687                                                 dp->include_list->first->name);
1688                                 includefree = include2;
1689                             }
1690                         }
1691
1692                         if (estimate == ES_CALCSIZE &&
1693                             !am_has_feature(hostp->features,
1694                                             fe_calcsize_estimate)) {
1695                             log_add(L_WARNING,
1696                                     _("%s:%s does not support CALCSIZE for estimate, using CLIENT.\n"),
1697                                     hostp->hostname, qname);
1698                             estimate = ES_CLIENT;
1699                         }
1700                         if(estimate == ES_CLIENT)
1701                             calcsize = "";
1702                         else
1703                             calcsize = "CALCSIZE ";
1704
1705                         l = vstralloc(calcsize,
1706                                       dp->program,
1707                                       " ", qname,
1708                                       " ", dp->device ? qdevice : "",
1709                                       " ", level,
1710                                       " ", est(dp)->estimate[i].dumpdate,
1711                                       " ", spindle,
1712                                       " ", exclude1, exclude2,
1713                                       ((includefree != NULL) ? " " : ""),
1714                                         include1, include2,
1715                                       "\n",
1716                                       NULL);
1717                         strappend(s, l);
1718                         s_len += strlen(l);
1719                         amfree(l);
1720                         amfree(includefree);
1721                         amfree(excludefree);
1722                     }
1723                 }
1724                 if (s != NULL) {
1725                     estimates += i;
1726                     strappend(req, s);
1727                     req_len += s_len;
1728                     amfree(s);
1729                     if (est(dp)->state == DISK_DONE) {
1730                         remove_disk(&estq, dp);
1731                         est(dp)->state = DISK_PARTIALY_DONE;
1732                         enqueue_disk(&pestq, dp);
1733                     } else {
1734                         remove_disk(&startq, dp);
1735                         est(dp)->state = DISK_ACTIVE;
1736                     }
1737                 } else if (est(dp)->state != DISK_DONE) {
1738                     remove_disk(&startq, dp);
1739                     est(dp)->state = DISK_DONE;
1740                     if (est(dp)->errstr == NULL) {
1741                         est(dp)->errstr = vstrallocf(
1742                                                 _("Can't request estimate"));
1743                     }
1744                     enqueue_disk(&failq, dp);
1745                 }
1746             }
1747             amfree(b64disk);
1748             amfree(b64device);
1749             amfree(qname);
1750             amfree(qdevice);
1751         }
1752
1753         if(estimates == 0) {
1754             amfree(req);
1755             hostp->up = HOST_DONE;
1756             return;
1757         }
1758
1759         if (conf_etimeout < 0) {
1760             timeout = - conf_etimeout;
1761         } else {
1762             timeout = estimates * conf_etimeout;
1763         }
1764     } else { /* noop service */
1765         req = vstralloc("SERVICE ", "noop", "\n",
1766                         "OPTIONS ",
1767                         "features=", our_feature_string, ";",
1768                         "\n",
1769                         NULL);
1770         /*
1771          * We use ctimeout for the "noop" request because it should be
1772          * very fast and etimeout has other side effects.
1773          */
1774         timeout = (time_t)getconf_int(CNF_CTIMEOUT);
1775     }
1776
1777     dbprintf(_("send request:\n----\n%s\n----\n\n"), req);
1778     secdrv = security_getdriver(hostp->disks->auth);
1779     if (secdrv == NULL) {
1780         hostp->up = HOST_DONE;
1781         log_add(L_ERROR,
1782                 _("Could not find security driver '%s' for host '%s'"),
1783                 hostp->disks->auth, hostp->hostname);
1784         amfree(req);
1785         return;
1786     }
1787     hostp->up = HOST_ACTIVE;
1788
1789     for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
1790         if(dp->todo == 0) {
1791             continue;
1792         }
1793         if(est(dp)->state == DISK_ACTIVE) {
1794             est(dp)->errstr = NULL;
1795             enqueue_disk(&waitq, dp);
1796         }
1797     }
1798
1799     protocol_sendreq(hostp->hostname, secdrv, amhost_get_security_conf, 
1800         req, timeout, handle_result, hostp);
1801
1802     amfree(req);
1803 }
1804
1805 static disk_t *lookup_hostdisk(
1806     /*@keep@*/ am_host_t *hp,
1807     char *str)
1808 {
1809     disk_t *dp;
1810
1811     for(dp = hp->disks; dp != NULL; dp = dp->hostnext)
1812         if(strcmp(str, dp->name) == 0) return dp;
1813
1814     return NULL;
1815 }
1816
1817
1818 static void handle_result(
1819     void *datap,
1820     pkt_t *pkt,
1821     security_handle_t *sech)
1822 {
1823     int level, i;
1824     gint64 size;
1825     disk_t *dp;
1826     am_host_t *hostp;
1827     char *msg, msg_undo;
1828     char *remoterr, *errbuf = NULL;
1829     char *s;
1830     char *t;
1831     char *fp;
1832     char *line;
1833     int ch;
1834     int tch;
1835     char *qname;
1836     char *disk;
1837     long long size_;
1838
1839     hostp = (am_host_t *)datap;
1840     hostp->up = HOST_READY;
1841
1842     if (pkt == NULL) {
1843         if (strcmp(security_geterror(sech), "timeout waiting for REP") == 0) {
1844             errbuf = vstrallocf("Some estimate timeout on %s, using server estimate if possible", hostp->hostname);
1845         } else {
1846             errbuf = vstrallocf(_("Request to %s failed: %s"),
1847                         hostp->hostname, security_geterror(sech));
1848         }
1849         goto error_return;
1850     }
1851     if (pkt->type == P_NAK) {
1852         s = pkt->body;
1853         if(strncmp_const_skip(s, "ERROR ", s, ch) == 0) {
1854             ch = *s++;
1855         } else {
1856             goto NAK_parse_failed;
1857         }
1858         skip_whitespace(s, ch);
1859         if(ch == '\0') goto NAK_parse_failed;
1860         remoterr = s - 1;
1861         if((s = strchr(remoterr, '\n')) != NULL) {
1862             if(s == remoterr) goto NAK_parse_failed;
1863                 *s = '\0';
1864         }
1865         if (strcmp(remoterr, "unknown service: noop") != 0
1866                 && strcmp(remoterr, "noop: invalid service") != 0) {
1867             errbuf = vstralloc(hostp->hostname, " NAK: ", remoterr, NULL);
1868             if(s) *s = '\n';
1869             goto error_return;
1870         }
1871     }
1872
1873     dbprintf(_("got reply:\n----\n%s\n----\n\n"), pkt->body);
1874     s = pkt->body;
1875     ch = *s++;
1876     while(ch) {
1877         line = s - 1;
1878
1879         if(strncmp_const(line, "OPTIONS ") == 0) {
1880             t = strstr(line, "features=");
1881             if(t != NULL && (g_ascii_isspace((int)t[-1]) || t[-1] == ';')) {
1882                 char *u = strchr(t, ';');
1883                 if (u)
1884                    *u = '\0';
1885                 t += SIZEOF("features=")-1;
1886                 am_release_feature_set(hostp->features);
1887                 if((hostp->features = am_string_to_feature(t)) == NULL) {
1888                     errbuf = vstrallocf(hostp->hostname,
1889                                        _(": bad features value: %s\n"), line);
1890                     goto error_return;
1891                 }
1892                 if (u)
1893                    *u = ';';
1894             }
1895             skip_quoted_line(s, ch);
1896             continue;
1897         }
1898
1899         t = line;
1900         if ((strncmp_const_skip(t, "ERROR ", t, tch) == 0) ||
1901             (strncmp_const_skip(t, "WARNING ", t, tch) == 0)) { 
1902             fp = t - 1;
1903             skip_whitespace(t, tch);
1904             if (tch == '\n') {
1905                 t[-1] = '\0';
1906             }
1907
1908             /*
1909              * If the "error" is that the "noop" service is unknown, it
1910              * just means the client is "old" (does not support the servie).
1911              * We can ignore this.
1912              */
1913             if(hostp->features == NULL
1914                && pkt->type == P_NAK
1915                && (strcmp(t - 1, "unknown service: noop") == 0
1916                    || strcmp(t - 1, "noop: invalid service") == 0)) {
1917                 skip_quoted_line(s, ch);
1918                 continue;
1919             }
1920             t = strchr(t,'\n');
1921             if (t) /* truncate after the first line */
1922                  *t = '\0';
1923             errbuf = vstralloc(hostp->hostname,
1924                                    (pkt->type == P_NAK) ? "NAK " : "",
1925                                    ": ",
1926                                    fp,
1927                                    NULL);
1928             goto error_return;
1929         }
1930
1931         msg = t = line;
1932         tch = *(t++);
1933         skip_quoted_string(t, tch);
1934         t[-1] = '\0';
1935         disk = unquote_string(msg);
1936
1937         skip_whitespace(t, tch);
1938
1939         if (sscanf(t - 1, "%d", &level) != 1) {
1940             goto bad_msg;
1941         }
1942
1943         skip_integer(t, tch);
1944         skip_whitespace(t, tch);
1945
1946         dp = lookup_hostdisk(hostp, disk);
1947         if(dp == NULL) {
1948             log_add(L_ERROR, _("%s: invalid reply from sendsize: `%s'\n"),
1949                     hostp->hostname, line);
1950             goto bad_msg;
1951         }
1952
1953         size = (gint64)-1;
1954         if (strncmp_const(t-1,"SIZE ") == 0) {
1955             if (sscanf(t - 1, "SIZE %lld", &size_) != 1) {
1956                 goto bad_msg;
1957             }
1958             size = (gint64)size_;
1959         } else if ((strncmp_const(t-1,"ERROR ") == 0) ||
1960                    (strncmp_const(t-1,"WARNING ") == 0)) {
1961             skip_non_whitespace(t, tch);
1962             skip_whitespace(t, tch);
1963             msg = t-1;
1964             skip_quoted_string(t,tch);
1965             msg_undo = t[-1];
1966             t[-1] = '\0';
1967             if (pkt->type == P_REP && !est(dp)->errstr) {
1968                 est(dp)->errstr = unquote_string(msg);
1969             }
1970             t[-1] = msg_undo;
1971         } else {
1972             goto bad_msg;
1973         }
1974
1975         amfree(disk);
1976
1977         for (i = 0; i < MAX_LEVELS; i++) {
1978             if (est(dp)->estimate[i].level == level) {
1979                 if (size == (gint64)-2) {
1980                     est(dp)->estimate[i].nsize = -1; /* remove estimate */
1981                     est(dp)->estimate[i].guessed = 0;
1982                 } else if (size > (gint64)-1) {
1983                     /* take the size returned by the client */
1984                     est(dp)->estimate[i].nsize = size;
1985                     est(dp)->estimate[i].guessed = 0;
1986                 }
1987                 break;
1988             }
1989         }
1990         if (i == MAX_LEVELS && level > 0) {
1991                         /* client always report level 0 for some error */
1992             goto bad_msg;               /* this est wasn't requested */
1993         }
1994         est(dp)->got_estimate++;
1995
1996         s = t;
1997         ch = tch;
1998         skip_quoted_line(s, ch);
1999     }
2000
2001     if(hostp->up == HOST_READY && hostp->features == NULL) {
2002         /*
2003          * The client does not support the features list, so give it an
2004          * empty one.
2005          */
2006         dbprintf(_("no feature set from host %s\n"), hostp->hostname);
2007         hostp->features = am_set_default_feature_set();
2008     }
2009
2010     security_close_connection(sech, hostp->hostname);
2011
2012     /* XXX what about disks that only got some estimates...  do we care? */
2013     /* XXX amanda 2.1 treated that case as a bad msg */
2014
2015     for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
2016         if(dp->todo == 0) continue;
2017         if(est(dp)->state != DISK_ACTIVE &&
2018            est(dp)->state != DISK_PARTIALY_DONE) continue;
2019
2020         if(est(dp)->state == DISK_ACTIVE) {
2021             remove_disk(&waitq, dp);
2022         }
2023         else if(est(dp)->state == DISK_PARTIALY_DONE) {
2024             remove_disk(&pestq, dp);
2025         }
2026
2027         if(pkt->type == P_REP) {
2028             est(dp)->state = DISK_DONE;
2029         }
2030         else if(pkt->type == P_PREP) {
2031             est(dp)->state = DISK_PARTIALY_DONE;
2032         }
2033
2034         if (est(dp)->estimate[0].level == -1) continue;   /* ignore this disk */
2035
2036
2037         qname = quote_string(dp->name);
2038         if(pkt->type == P_PREP) {
2039                 g_fprintf(stderr,_("%s: time %s: got partial result for host %s disk %s:"),
2040                         get_pname(), walltime_str(curclock()),
2041                         dp->host->hostname, qname);
2042                 g_fprintf(stderr,_(" %d -> %lldK, %d -> %lldK, %d -> %lldK\n"),
2043                           est(dp)->estimate[0].level,
2044                           (long long)est(dp)->estimate[0].nsize,
2045                           est(dp)->estimate[1].level,
2046                           (long long)est(dp)->estimate[1].nsize,
2047                           est(dp)->estimate[2].level,
2048                           (long long)est(dp)->estimate[2].nsize);
2049             enqueue_disk(&pestq, dp);
2050         }
2051         else if(pkt->type == P_REP) {
2052                 g_fprintf(stderr,_("%s: time %s: got result for host %s disk %s:"),
2053                         get_pname(), walltime_str(curclock()),
2054                         dp->host->hostname, qname);
2055                 g_fprintf(stderr,_(" %d -> %lldK, %d -> %lldK, %d -> %lldK\n"),
2056                           est(dp)->estimate[0].level,
2057                           (long long)est(dp)->estimate[0].nsize,
2058                           est(dp)->estimate[1].level,
2059                           (long long)est(dp)->estimate[1].nsize,
2060                           est(dp)->estimate[2].level,
2061                           (long long)est(dp)->estimate[2].nsize);
2062                 if ((est(dp)->estimate[0].level != -1 &&
2063                      est(dp)->estimate[0].nsize > (gint64)0) ||
2064                     (est(dp)->estimate[1].level != -1 &&
2065                      est(dp)->estimate[1].nsize > (gint64)0) ||
2066                     (est(dp)->estimate[2].level != -1 &&
2067                      est(dp)->estimate[2].nsize > (gint64)0)) {
2068
2069                     for (i=MAX_LEVELS-1; i >=0; i--) {
2070                         if (est(dp)->estimate[i].level != -1 &&
2071                             est(dp)->estimate[i].nsize < (gint64)0) {
2072                             est(dp)->estimate[i].level = -1;
2073                         }
2074                     }
2075                     enqueue_disk(&estq, dp);
2076             }
2077             else {
2078                 enqueue_disk(&failq, dp);
2079                 if(est(dp)->got_estimate && !est(dp)->errstr) {
2080                     est(dp)->errstr = vstrallocf("disk %s, all estimate failed",
2081                                                  qname);
2082                 }
2083                 else {
2084                     g_fprintf(stderr,
2085                          _("error result for host %s disk %s: missing estimate\n"),
2086                          dp->host->hostname, qname);
2087                     if (est(dp)->errstr == NULL) {
2088                         est(dp)->errstr = vstrallocf(_("missing result for %s in %s response"),
2089                                                     qname, dp->host->hostname);
2090                     }
2091                 }
2092             }
2093             hostp->up = HOST_DONE;
2094         }
2095         if (est(dp)->post_dle == 0 &&
2096             (pkt->type == P_REP ||
2097              ((est(dp)->estimate[0].level == -1 ||
2098                est(dp)->estimate[0].nsize > (gint64)0) &&
2099               (est(dp)->estimate[1].level == -1 ||
2100                est(dp)->estimate[1].nsize > (gint64)0) &&
2101               (est(dp)->estimate[2].level == -1 ||
2102                est(dp)->estimate[2].nsize > (gint64)0)))) {
2103             run_server_dle_scripts(EXECUTE_ON_POST_DLE_ESTIMATE,
2104                                get_config_name(), dp,
2105                                est(dp)->estimate[0].level);
2106             est(dp)->post_dle = 1;
2107         }
2108         amfree(qname);
2109     }
2110
2111     if(hostp->up == HOST_DONE) {
2112         if (pkt->type == P_REP) {
2113             run_server_host_scripts(EXECUTE_ON_POST_HOST_ESTIMATE,
2114                                     get_config_name(), hostp);
2115         }
2116     }
2117
2118     getsize(hostp);
2119     /* try to clean up any defunct processes, since Amanda doesn't wait() for
2120        them explicitly */
2121     while(waitpid(-1, NULL, WNOHANG)> 0);
2122     return;
2123
2124  NAK_parse_failed:
2125
2126     errbuf = vstrallocf(_("%s NAK: [NAK parse failed]"), hostp->hostname);
2127     g_fprintf(stderr, _("got strange nak from %s:\n----\n%s----\n\n"),
2128             hostp->hostname, pkt->body);
2129     goto error_return;
2130
2131  bad_msg:
2132     g_fprintf(stderr,_("got a bad message, stopped at:\n"));
2133     /*@ignore@*/
2134     g_fprintf(stderr,_("----\n%s----\n\n"), line);
2135     errbuf = stralloc2(_("badly formatted response from "), hostp->hostname);
2136     /*@end@*/
2137
2138  error_return:
2139     i = 0;
2140     for(dp = hostp->disks; dp != NULL; dp = dp->hostnext) {
2141         if (dp->todo) {
2142             if(est(dp)->state == DISK_ACTIVE) {
2143                 qname = quote_string(dp->name);
2144                 est(dp)->state = DISK_DONE;
2145                 remove_disk(&waitq, dp);
2146                 enqueue_disk(&failq, dp);
2147                 i++;
2148
2149                 est(dp)->errstr = stralloc(errbuf);
2150                 g_fprintf(stderr, _("error result for host %s disk %s: %s\n"),
2151                           dp->host->hostname, qname, errbuf);
2152                 amfree(qname);
2153             }
2154         }
2155     }
2156     if(i == 0) {
2157         /*
2158          * If there were no disks involved, make sure the error gets
2159          * reported.
2160          */
2161         log_add(L_ERROR, "%s", errbuf);
2162     }
2163     hostp->up = HOST_DONE;
2164     amfree(errbuf);
2165     /* try to clean up any defunct processes, since Amanda doesn't wait() for
2166        them explicitly */
2167     while(waitpid(-1, NULL, WNOHANG)> 0);
2168 }
2169
2170
2171
2172 \f
2173 /*
2174  * ========================================================================
2175  * ANALYSE ESTIMATES
2176  *
2177  */
2178
2179 static int schedule_order(disk_t *a, disk_t *b);          /* subroutines */
2180 static one_est_t *pick_inclevel(disk_t *dp);
2181
2182 static void analyze_estimate(
2183     disk_t *dp)
2184 {
2185     est_t *ep;
2186     info_t info;
2187     int have_info = 0;
2188     char *qname = quote_string(dp->name);
2189
2190     ep = est(dp);
2191
2192     g_fprintf(stderr, _("pondering %s:%s... "),
2193             dp->host->hostname, qname);
2194     g_fprintf(stderr, _("next_level0 %d last_level %d "),
2195             ep->next_level0, ep->last_level);
2196
2197     if(get_info(dp->host->hostname, dp->name, &info) == 0) {
2198         have_info = 1;
2199     }
2200
2201     ep->degr_est = &default_one_est;
2202
2203     if (ep->next_level0 <= 0 || (have_info && ep->last_level == 0
2204        && (ISSET(info.command, FORCE_NO_BUMP)))) {
2205         if (ep->next_level0 <= 0) {
2206             g_fprintf(stderr,_("(due for level 0) "));
2207         }
2208         ep->dump_est = est_for_level(dp, 0);
2209         if (ep->dump_est->csize <= (gint64)0) {
2210             g_fprintf(stderr,
2211                     _("(no estimate for level 0, picking an incr level)\n"));
2212             ep->dump_est = pick_inclevel(dp);
2213
2214             if (ep->dump_est->nsize == (gint64)-1) {
2215                 ep->dump_est = est_for_level(dp, ep->dump_est->level + 1);
2216             }
2217         }
2218         else {
2219             total_lev0 += (double) ep->dump_est->csize;
2220             if(ep->last_level == -1 || dp->skip_incr) {
2221                 g_fprintf(stderr,_("(%s disk, can't switch to degraded mode)\n"),
2222                         dp->skip_incr? "skip-incr":_("new"));
2223                 if (dp->skip_incr  && ep->degr_mesg == NULL) {
2224                     ep->degr_mesg = _("Skpping: skip-incr disk can't be dumped in degraded mode");
2225                 }
2226                 ep->degr_est = &default_one_est;
2227             }
2228             else {
2229                 /* fill in degraded mode info */
2230                 g_fprintf(stderr,_("(picking inclevel for degraded mode)"));
2231                 ep->degr_est = pick_inclevel(dp);
2232                 if (ep->degr_est->level >= 0 &&
2233                     ep->degr_est->csize == (gint64)-1) {
2234                     ep->degr_est = est_for_level(dp, ep->degr_est->level + 1);
2235                 }
2236                 if (ep->degr_est->csize == (gint64)-1) {
2237                     g_fprintf(stderr,_("(no inc estimate)"));
2238                     if (ep->degr_mesg == NULL)
2239                         ep->degr_mesg = _("Skipping: an incremental estimate could not be performed, so disk cannot be dumped in degraded mode");
2240                     ep->degr_est = &default_one_est;
2241                 }
2242                 g_fprintf(stderr,"\n");
2243             }
2244         }
2245     }
2246     else {
2247         g_fprintf(stderr,_("(not due for a full dump, picking an incr level)\n"));
2248         /* XXX - if this returns -1 may be we should force a total? */
2249         ep->dump_est = pick_inclevel(dp);
2250
2251         if (ep->dump_est->csize == (gint64)-1) {
2252             ep->dump_est = est_for_level(dp, ep->last_level);
2253         }
2254         if (ep->dump_est->csize == (gint64)-1) {
2255             ep->dump_est = est_for_level(dp, ep->last_level + 1);
2256         }
2257         if (ep->dump_est->csize == (gint64)-1) {
2258             ep->dump_est = est_for_level(dp, 0);
2259         }
2260         if (ep->degr_mesg == NULL) {
2261             ep->degr_mesg = _("Skipping: a full is not planned, so can't dump in degraded mode");
2262         }
2263     }
2264
2265     if (ep->dump_est->level < 0) {
2266         int   i;
2267         char *q = quote_string("no estimate");
2268
2269         g_fprintf(stderr,_("  no valid estimate\n"));
2270         for(i=0; i<MAX_LEVELS; i++) {
2271             if (est(dp)->estimate[i].level >= 0) {
2272                 g_fprintf(stderr,("    level: %d  nsize: %lld csize: %lld\n"),
2273                           est(dp)->estimate[i].level,
2274                           (long long)est(dp)->estimate[i].nsize,
2275                           (long long)est(dp)->estimate[i].csize);
2276             }
2277         }
2278         log_add(L_WARNING, _("%s %s %s 0 %s"), dp->host->hostname, qname,
2279                 planner_timestamp, q);
2280         amfree(q);
2281     }
2282
2283     g_fprintf(stderr,_("  curr level %d nsize %lld csize %lld "),
2284               ep->dump_est->level, (long long)ep->dump_est->nsize,
2285               (long long)ep->dump_est->csize);
2286
2287     insert_disk(&schedq, dp, schedule_order);
2288
2289     total_size += (gint64)tt_blocksize_kb + ep->dump_est->csize + tape_mark;
2290
2291     /* update the balanced size */
2292     if(!(dp->skip_full || dp->strategy == DS_NOFULL || 
2293          dp->strategy == DS_INCRONLY)) {
2294         gint64 lev0size;
2295
2296         lev0size = est_tape_size(dp, 0);
2297         if(lev0size == (gint64)-1) lev0size = ep->last_lev0size;
2298
2299         if (dp->strategy == DS_NOINC) {
2300             balanced_size += (double)lev0size;
2301         } else if (dp->dumpcycle == 0) {
2302             balanced_size += (double)(lev0size * conf_dumpcycle / (gint64)runs_per_cycle);
2303         } else if (dp->dumpcycle != conf_dumpcycle) {
2304             balanced_size += (double)(lev0size * (conf_dumpcycle / dp->dumpcycle) / (gint64)runs_per_cycle);
2305         } else {
2306             balanced_size += (double)(lev0size / (gint64)runs_per_cycle);
2307         }
2308     }
2309
2310     g_fprintf(stderr,_("total size %lld total_lev0 %1.0lf balanced-lev0size %1.0lf\n"),
2311             (long long)total_size, total_lev0, balanced_size);
2312
2313     /* Log errstr even if the estimate succeeded */
2314     /* It can be an error from a script          */
2315     if (est(dp)->errstr) {
2316         char *qerrstr = quote_string(est(dp)->errstr);
2317         /* Log only a warning if a server estimate is available */
2318         if (est(dp)->estimate[0].nsize > 0 ||
2319             est(dp)->estimate[1].nsize > 0 ||
2320             est(dp)->estimate[2].nsize > 0) {
2321             log_add(L_WARNING, _("%s %s %s 0 %s"), dp->host->hostname, qname, 
2322                     planner_timestamp, qerrstr);
2323         } else {
2324             log_add(L_FAIL, _("%s %s %s 0 %s"), dp->host->hostname, qname, 
2325                     planner_timestamp, qerrstr);
2326         }
2327         amfree(qerrstr);
2328     }
2329
2330     amfree(qname);
2331 }
2332
2333 static void handle_failed(
2334     disk_t *dp)
2335 {
2336     char *errstr, *errstr1, *qerrstr;
2337     char *qname = quote_string(dp->name);
2338
2339     errstr = est(dp)->errstr? est(dp)->errstr : _("hmm, no error indicator!");
2340     errstr1 = vstralloc("[",errstr,"]", NULL);
2341     qerrstr = quote_string(errstr1);
2342     amfree(errstr1);
2343
2344     g_fprintf(stderr, _("%s: FAILED %s %s %s 0 %s\n"),
2345         get_pname(), dp->host->hostname, qname, planner_timestamp, qerrstr);
2346
2347     log_add(L_FAIL, _("%s %s %s 0 %s"), dp->host->hostname, qname, 
2348             planner_timestamp, qerrstr);
2349
2350     amfree(qerrstr);
2351     amfree(qname);
2352     /* XXX - memory leak with *dp */
2353 }
2354
2355
2356 /*
2357  * insert-sort by decreasing priority, then
2358  * by decreasing size within priority levels.
2359  */
2360
2361 static int schedule_order(
2362     disk_t *a,
2363     disk_t *b)
2364 {
2365     int diff;
2366     gint64 ldiff;
2367
2368     diff = est(b)->dump_priority - est(a)->dump_priority;
2369     if(diff != 0) return diff;
2370
2371     ldiff = est(b)->dump_est->csize - est(a)->dump_est->csize;
2372     if(ldiff < (gint64)0) return -1; /* XXX - there has to be a better way to dothis */
2373     if(ldiff > (gint64)0) return 1;
2374     return 0;
2375 }
2376
2377
2378 static one_est_t *pick_inclevel(
2379     disk_t *dp)
2380 {
2381     one_est_t *level0_est, *base_est, *bump_est;
2382     gint64 thresh;
2383     char *qname;
2384
2385     level0_est = est_for_level(dp, 0);
2386     base_est = est_for_level(dp, est(dp)->last_level);
2387
2388     /* if last night was level 0, do level 1 tonight, no ifs or buts */
2389     if (base_est->level == 0) {
2390         g_fprintf(stderr,_("   picklev: last night 0, so tonight level 1\n"));
2391         return est_for_level(dp, 1);
2392     }
2393
2394     /* if no-full option set, always do level 1 */
2395     if(dp->strategy == DS_NOFULL) {
2396         g_fprintf(stderr,_("   picklev: no-full set, so always level 1\n"));
2397         return est_for_level(dp, 1);
2398     }
2399
2400     /* if we didn't get an estimate, we can't do an inc */
2401     if (base_est->nsize == (gint64)-1) {
2402         bump_est = est_for_level(dp, est(dp)->last_level + 1);
2403         if (bump_est->nsize > (gint64)0) { /* FORCE_BUMP */
2404             g_fprintf(stderr,_("   picklev: bumping to level %d\n"), bump_est->level);
2405             return bump_est;
2406         }
2407         g_fprintf(stderr,_("   picklev: no estimate for level %d, so no incs\n"), base_est->level);
2408         return base_est;
2409     }
2410
2411     thresh = bump_thresh(base_est->level, level0_est->nsize, dp->bumppercent,
2412                          dp->bumpsize, dp->bumpmult);
2413
2414     g_fprintf(stderr,
2415             _("   pick: size %lld level %d days %d (thresh %lldK, %d days)\n"),
2416             (long long)base_est->nsize, base_est->level, est(dp)->level_days,
2417             (long long)thresh, dp->bumpdays);
2418
2419     if(base_est->level == (DUMP_LEVELS - 1)
2420        || est(dp)->level_days < dp->bumpdays
2421        || base_est->nsize <= thresh)
2422             return base_est;
2423
2424     bump_est = est_for_level(dp, base_est->level + 1);
2425
2426     if (bump_est->nsize == (gint64)-1)
2427         return base_est;
2428
2429     g_fprintf(stderr, _("   pick: next size %lld... "),
2430               (long long)bump_est->nsize);
2431
2432     if (base_est->nsize - bump_est->nsize < thresh) {
2433         g_fprintf(stderr, _("not bumped\n"));
2434         return base_est;
2435     }
2436
2437     qname = quote_string(dp->name);
2438     g_fprintf(stderr, _("BUMPED\n"));
2439     log_add(L_INFO, _("Incremental of %s:%s bumped to level %d."),
2440             dp->host->hostname, qname, bump_est->level);
2441     amfree(qname);
2442
2443     return bump_est;
2444 }
2445
2446
2447
2448 \f
2449 /*
2450 ** ========================================================================
2451 ** ADJUST SCHEDULE
2452 **
2453 ** We have two strategies here:
2454 **
2455 ** 1. Delay dumps
2456 **
2457 ** If we are trying to fit too much on the tape something has to go.  We
2458 ** try to delay totals until tomorrow by converting them into incrementals
2459 ** and, if that is not effective enough, dropping incrementals altogether.
2460 ** While we are searching for the guilty dump (the one that is really
2461 ** causing the schedule to be oversize) we have probably trampled on a lot of
2462 ** innocent dumps, so we maintain a "before image" list and use this to
2463 ** put back what we can.
2464 **
2465 ** 2. Promote dumps.
2466 **
2467 ** We try to keep the amount of tape used by total dumps the same each night.
2468 ** If there is some spare tape in this run we have a look to see if any of
2469 ** tonights incrementals could be promoted to totals and leave us with a
2470 ** more balanced cycle.
2471 */
2472
2473 static void delay_one_dump(disk_t *dp, int delete, ...);
2474 static int promote_highest_priority_incremental(void);
2475 static int promote_hills(void);
2476
2477 /* delay any dumps that will not fit */
2478 static void delay_dumps(void)
2479 {
2480     disk_t *    dp;
2481     disk_t *    ndp;
2482     disk_t *    preserve;
2483     disk_t *    delayed_dp;
2484     bi_t *      bi;
2485     bi_t  *     nbi;
2486     gint64      new_total;              /* New total_size */
2487     char        est_kb[20];             /* Text formatted dump size */
2488     int         nb_forced_level_0;
2489     info_t      info;
2490     int         delete;
2491     char *      message;
2492     gint64      full_size;
2493     time_t      timestamps;
2494     int         priority;
2495
2496     biq.head = biq.tail = NULL;
2497
2498     /*
2499     ** 1. Delay dumps that are way oversize.
2500     **
2501     ** Dumps larger that the size of the tapes we are using are just plain
2502     ** not going to fit no matter how many other dumps we drop.  Delay
2503     ** oversize totals until tomorrow (by which time my owner will have
2504     ** resolved the problem!) and drop incrementals altogether.  Naturally
2505     ** a large total might be delayed into a large incremental so these
2506     ** need to be checked for separately.
2507     */
2508
2509     for(dp = schedq.head; dp != NULL; dp = ndp) {
2510         int avail_tapes = 1;
2511         if (dp->splitsize > (gint64)0 || dp->allow_split)
2512             avail_tapes = conf_runtapes;
2513
2514         ndp = dp->next; /* remove_disk zaps this */
2515
2516         full_size = est_tape_size(dp, 0);
2517         if (full_size > tapetype_get_length(tape) * (gint64)avail_tapes) {
2518             char *qname = quote_string(dp->name);
2519             if (conf_runtapes > 1 && dp->splitsize == (gint64)0) {
2520                 log_add(L_WARNING, _("disk %s:%s, full dump (%lldKB) will be larger than available tape space"
2521                         ", you could define a splitsize"),
2522                         dp->host->hostname, qname,
2523                         (long long)full_size);
2524             } else {
2525                 log_add(L_WARNING, _("disk %s:%s, full dump (%lldKB) will be larger than available tape space"),
2526                         dp->host->hostname, qname,
2527                         (long long)full_size);
2528             }
2529             amfree(qname);
2530         }
2531
2532         if (est(dp)->dump_est->csize == (gint64)-1 ||
2533             est(dp)->dump_est->csize <= tapetype_get_length(tape) * (gint64)avail_tapes) {
2534             continue;
2535         }
2536
2537         /* Format dumpsize for messages */
2538         g_snprintf(est_kb, 20, "%lld KB,",
2539                    (long long)est(dp)->dump_est->csize);
2540
2541         if(est(dp)->dump_est->level == 0) {
2542             if(dp->skip_incr) {
2543                 delete = 1;
2544                 message = _("but cannot incremental dump skip-incr disk");
2545             }
2546             else if(est(dp)->last_level < 0) {
2547                 delete = 1;
2548                 message = _("but cannot incremental dump new disk");
2549             }
2550             else if(est(dp)->degr_est->level < 0) {
2551                 delete = 1;
2552                 message = _("but no incremental estimate");
2553             }
2554             else if (est(dp)->degr_est->csize > tapetype_get_length(tape)) {
2555                 delete = 1;
2556                 message = _("incremental dump also larger than tape");
2557             }
2558             else {
2559                 delete = 0;
2560                 message = _("full dump delayed, doing incremental");
2561             }
2562         }
2563         else {
2564             delete = 1;
2565             message = _("skipping incremental");
2566         }
2567         delay_one_dump(dp, delete, _("dump larger than available tape space,"),
2568                        est_kb, message, NULL);
2569     }
2570
2571     /*
2572     ** 2. Delay total dumps.
2573     **
2574     ** Delay total dumps until tomorrow (or the day after!).  We start with
2575     ** the lowest priority (most dispensable) and work forwards.  We take
2576     ** care not to delay *all* the dumps since this could lead to a stale
2577     ** mate [for any one disk there are only three ways tomorrows dump will
2578     ** be smaller than todays: 1. we do a level 0 today so tomorows dump
2579     ** will be a level 1; 2. the disk gets more data so that it is bumped
2580     ** tomorrow (this can be a slow process); and, 3. the disk looses some
2581     ** data (when does that ever happen?)].
2582     */
2583
2584     nb_forced_level_0 = 0;
2585     preserve = NULL;
2586     timestamps = 2147483647;
2587     priority = 0;
2588     for(dp = schedq.head; dp != NULL; dp = dp->next) {
2589         if (est(dp)->dump_est->level == 0) {
2590             if (!preserve ||
2591                 est(dp)->dump_priority > priority ||
2592                 (est(dp)->dump_priority == priority &&
2593                  est(dp)->info->inf[0].date < timestamps)) {
2594                 priority = est(dp)->dump_priority;
2595                 timestamps = est(dp)->info->inf[0].date;
2596                 preserve = dp;
2597             }
2598         }
2599     }
2600
2601     /* 2.a. Do not delay forced full */
2602     delayed_dp = NULL;
2603     do {
2604         delayed_dp = 0;
2605         timestamps = 0;
2606         for(dp = schedq.tail;
2607                 dp != NULL && total_size > tape_length;
2608                 dp = ndp) {
2609             ndp = dp->prev;
2610
2611             if(est(dp)->dump_est->level != 0) continue;
2612
2613             get_info(dp->host->hostname, dp->name, &info);
2614             if(ISSET(info.command, FORCE_FULL)) {
2615                 nb_forced_level_0 += 1;
2616                 preserve = dp;
2617                 continue;
2618             }
2619
2620             if (dp != preserve &&
2621                 est(dp)->info->inf[0].date > timestamps) {
2622                 delayed_dp = dp;
2623                 timestamps = est(dp)->info->inf[0].date;
2624             }
2625         }
2626         if (delayed_dp) {
2627             /* Format dumpsize for messages */
2628             g_snprintf(est_kb, 20, "%lld KB,",
2629                        (long long)est(delayed_dp)->dump_est->csize);
2630
2631             if(delayed_dp->skip_incr) {
2632                 delete = 1;
2633                 message = _("but cannot incremental dump skip-incr disk");
2634             }
2635             else if(est(delayed_dp)->last_level < 0) {
2636                 delete = 1;
2637                 message = _("but cannot incremental dump new disk");
2638             }
2639             else if(est(delayed_dp)->degr_est->level < 0) {
2640                 delete = 1;
2641                 message = _("but no incremental estimate");
2642             }
2643             else {
2644                 delete = 0;
2645                 message = _("full dump delayed, doing incremental");
2646             }
2647             delay_one_dump(delayed_dp, delete, _("dumps too big,"), est_kb,
2648                            message, NULL);
2649         }
2650     } while (delayed_dp);
2651
2652     /* 2.b. Delay forced full if needed */
2653     if(nb_forced_level_0 > 0 && total_size > tape_length) {
2654         for(dp = schedq.tail;
2655                 dp != NULL && total_size > tape_length;
2656                 dp = ndp) {
2657             ndp = dp->prev;
2658
2659             if(est(dp)->dump_est->level == 0 && dp != preserve) {
2660
2661                 /* Format dumpsize for messages */
2662                 g_snprintf(est_kb, 20, "%lld KB,",
2663                            (long long)est(dp)->dump_est->csize);
2664
2665                 if(dp->skip_incr) {
2666                     delete = 1;
2667                     message = _("but cannot incremental dump skip-incr disk");
2668                 }
2669                 else if(est(dp)->last_level < 0) {
2670                     delete = 1;
2671                     message = _("but cannot incremental dump new disk");
2672                 }
2673                 else if(est(dp)->degr_est->level < 0) {
2674                     delete = 1;
2675                     message = _("but no incremental estimate");
2676                 }
2677                 else {
2678                     delete = 0;
2679                     message = _("full dump delayed");
2680                 }
2681                 delay_one_dump(dp, delete, _("dumps too big,"), est_kb,
2682                                message, NULL);
2683             }
2684         }
2685     }
2686
2687     /*
2688     ** 3. Delay incremental dumps.
2689     **
2690     ** Delay incremental dumps until tomorrow.  This is a last ditch attempt
2691     ** at making things fit.  Again, we start with the lowest priority (most
2692     ** dispensable) and work forwards.
2693     */
2694
2695     for(dp = schedq.tail;
2696             dp != NULL && total_size > tape_length;
2697             dp = ndp) {
2698         ndp = dp->prev;
2699
2700         if(est(dp)->dump_est->level != 0) {
2701
2702             /* Format dumpsize for messages */
2703             g_snprintf(est_kb, 20, "%lld KB,",
2704                        (long long)est(dp)->dump_est->csize);
2705
2706             delay_one_dump(dp, 1,
2707                            _("dumps way too big,"),
2708                            est_kb,
2709                            _("must skip incremental dumps"),
2710                            NULL);
2711         }
2712     }
2713
2714     /*
2715     ** 4. Reinstate delayed dumps.
2716     **
2717     ** We might not have needed to stomp on all of the dumps we have just
2718     ** delayed above.  Try to reinstate them all starting with the last one
2719     ** and working forwards.  It is unlikely that the last one will fit back
2720     ** in but why complicate the code?
2721     */
2722
2723 /*@i@*/ for(bi = biq.tail; bi != NULL; bi = nbi) {
2724         int avail_tapes = 1;
2725         nbi = bi->prev;
2726         dp = bi->dp;
2727         if(dp->splitsize > (gint64)0)
2728             avail_tapes = conf_runtapes;
2729
2730         if(bi->deleted) {
2731             new_total = total_size + (gint64)tt_blocksize_kb +
2732                         bi->csize + (gint64)tape_mark;
2733         } else {
2734             new_total = total_size - est(dp)->dump_est->csize + bi->csize;
2735         }
2736         if((new_total <= tape_length) &&
2737           (bi->csize < (tapetype_get_length(tape) * (gint64)avail_tapes))) {
2738             /* reinstate it */
2739             total_size = new_total;
2740             if(bi->deleted) {
2741                 if(bi->level == 0) {
2742                     total_lev0 += (double) bi->csize;
2743                 }
2744                 insert_disk(&schedq, dp, schedule_order);
2745             }
2746             else {
2747                 est(dp)->dump_est = est_for_level(dp, bi->level);
2748             }
2749
2750             /* Keep it clean */
2751             if(bi->next == NULL)
2752                 biq.tail = bi->prev;
2753             else
2754                 (bi->next)->prev = bi->prev;
2755             if(bi->prev == NULL)
2756                 biq.head = bi->next;
2757             else
2758                 (bi->prev)->next = bi->next;
2759
2760             amfree(bi->errstr);
2761             amfree(bi);
2762         }
2763     }
2764
2765     /*
2766     ** 5. Output messages about what we have done.
2767     **
2768     ** We can't output messages while we are delaying dumps because we might
2769     ** reinstate them later.  We remember all the messages and output them
2770     ** now.
2771     */
2772
2773 /*@i@*/ for(bi = biq.head; bi != NULL; bi = nbi) {
2774         nbi = bi->next;
2775         if(bi->deleted) {
2776             g_fprintf(stderr, "%s: FAILED %s\n", get_pname(), bi->errstr);
2777             log_add(L_FAIL, "%s", bi->errstr);
2778         }
2779         else {
2780             dp = bi->dp;
2781             g_fprintf(stderr, _("  delay: %s now at level %d\n"),
2782                 bi->errstr, est(dp)->dump_est->level);
2783             log_add(L_INFO, "%s", bi->errstr);
2784         }
2785         /*@ignore@*/
2786         amfree(bi->errstr);
2787         amfree(bi);
2788         /*@end@*/
2789     }
2790
2791     g_fprintf(stderr, _("  delay: Total size now %lld.\n"),
2792              (long long)total_size);
2793
2794     return;
2795 }
2796
2797
2798 /*
2799  * Remove a dump or modify it from full to incremental.
2800  * Keep track of it on the bi q in case we can add it back later.
2801  */
2802 arglist_function1(
2803     static void delay_one_dump,
2804     disk_t *, dp,
2805     int, delete)
2806 {
2807     bi_t *bi;
2808     va_list argp;
2809     char level_str[NUM_STR_SIZE];
2810     char *sep;
2811     char *next;
2812     char *qname = quote_string(dp->name);
2813     char *errstr, *qerrstr;
2814
2815     arglist_start(argp, delete);
2816
2817     total_size -= (gint64)tt_blocksize_kb + est(dp)->dump_est->csize + (gint64)tape_mark;
2818     if(est(dp)->dump_est->level == 0) {
2819         total_lev0 -= (double) est(dp)->dump_est->csize;
2820     }
2821
2822     bi = alloc(SIZEOF(bi_t));
2823     bi->next = NULL;
2824     bi->prev = biq.tail;
2825     if(biq.tail == NULL)
2826         biq.head = bi;
2827     else
2828         biq.tail->next = bi;
2829     biq.tail = bi;
2830
2831     bi->deleted = delete;
2832     bi->dp = dp;
2833     bi->level = est(dp)->dump_est->level;
2834     bi->nsize = est(dp)->dump_est->nsize;
2835     bi->csize = est(dp)->dump_est->csize;
2836
2837     g_snprintf(level_str, SIZEOF(level_str), "%d", est(dp)->dump_est->level);
2838     bi->errstr = vstralloc(dp->host->hostname,
2839                            " ", qname,
2840                            " ", planner_timestamp ? planner_timestamp : "?",
2841                            " ", level_str,
2842                            NULL);
2843     errstr = NULL;
2844     sep = "[";
2845     while ((next = arglist_val(argp, char *)) != NULL) {
2846         vstrextend(&errstr, sep, next, NULL);
2847         sep = " ";
2848     }
2849     strappend(errstr, "]");
2850     qerrstr = quote_string(errstr);
2851     vstrextend(&bi->errstr, " ", qerrstr, NULL);
2852     amfree(errstr);
2853     amfree(qerrstr);
2854     arglist_end(argp);
2855
2856     if (delete) {
2857         remove_disk(&schedq, dp);
2858     } else {
2859         est(dp)->dump_est = est(dp)->degr_est;
2860         total_size += (gint64)tt_blocksize_kb + est(dp)->dump_est->csize + (gint64)tape_mark;
2861     }
2862     amfree(qname);
2863     return;
2864 }
2865
2866
2867 static int promote_highest_priority_incremental(void)
2868 {
2869     disk_t *dp, *dp1, *dp_promote;
2870     gint64 new_total, new_lev0;
2871     int check_days;
2872     int nb_today, nb_same_day, nb_today2;
2873     int nb_disk_today, nb_disk_same_day;
2874     char *qname;
2875
2876     /*
2877      * return 1 if did so; must update total_size correctly; must not
2878      * cause total_size to exceed tape_length
2879      */
2880
2881     dp_promote = NULL;
2882     for(dp = schedq.head; dp != NULL; dp = dp->next) {
2883         one_est_t *level0_est = est_for_level(dp, 0);
2884         est(dp)->promote = -1000;
2885
2886         if (level0_est->nsize <= (gint64)0)
2887             continue;
2888
2889         if(est(dp)->next_level0 <= 0)
2890             continue;
2891
2892         if(est(dp)->next_level0 > dp->maxpromoteday)
2893             continue;
2894
2895         new_total = total_size - est(dp)->dump_est->csize + level0_est->csize;
2896         new_lev0 = (gint64)total_lev0 + level0_est->csize;
2897
2898         nb_today = 0;
2899         nb_same_day = 0;
2900         nb_disk_today = 0;
2901         nb_disk_same_day = 0;
2902         for(dp1 = schedq.head; dp1 != NULL; dp1 = dp1->next) {
2903             if(est(dp1)->dump_est->level == 0)
2904                 nb_disk_today++;
2905             else if(est(dp1)->next_level0 == est(dp)->next_level0)
2906                 nb_disk_same_day++;
2907             if(strcmp(dp->host->hostname, dp1->host->hostname) == 0) {
2908                 if(est(dp1)->dump_est->level == 0)
2909                     nb_today++;
2910                 else if(est(dp1)->next_level0 == est(dp)->next_level0)
2911                     nb_same_day++;
2912             }
2913         }
2914
2915         /* do not promote if overflow tape */
2916         if(new_total > tape_length)
2917             continue;
2918
2919         /* do not promote if overflow balanced size and something today */
2920         /* promote if nothing today */
2921         if((new_lev0 > (gint64)(balanced_size + balance_threshold)) &&
2922                 (nb_disk_today > 0))
2923             continue;
2924
2925         /* do not promote if only one disk due that day and nothing today */
2926         if(nb_disk_same_day == 1 && nb_disk_today == 0)
2927             continue;
2928
2929         nb_today2 = nb_today*nb_today;
2930         if(nb_today == 0 && nb_same_day > 1)
2931             nb_same_day++;
2932
2933         if(nb_same_day >= nb_today2) {
2934             est(dp)->promote = ((nb_same_day - nb_today2)*(nb_same_day - nb_today2)) + 
2935                                conf_dumpcycle - est(dp)->next_level0;
2936         }
2937         else {
2938             est(dp)->promote = -nb_today2 +
2939                                conf_dumpcycle - est(dp)->next_level0;
2940         }
2941
2942         qname = quote_string(dp->name);
2943         if(!dp_promote || est(dp_promote)->promote < est(dp)->promote) {
2944             dp_promote = dp;
2945             g_fprintf(stderr,"   try %s:%s %d %d %d = %d\n",
2946                     dp->host->hostname, qname, nb_same_day, nb_today, est(dp)->next_level0, est(dp)->promote);
2947         }
2948         else {
2949             g_fprintf(stderr,"no try %s:%s %d %d %d = %d\n",
2950                     dp->host->hostname, qname, nb_same_day, nb_today, est(dp)->next_level0, est(dp)->promote);
2951         }
2952         amfree(qname);
2953     }
2954
2955     if(dp_promote) {
2956         one_est_t *level0_est;
2957         dp = dp_promote;
2958         level0_est = est_for_level(dp, 0);
2959
2960         qname = quote_string(dp->name);
2961         new_total = total_size - est(dp)->dump_est->csize + level0_est->csize;
2962         new_lev0 = (gint64)total_lev0 + level0_est->csize;
2963
2964         total_size = new_total;
2965         total_lev0 = (double)new_lev0;
2966         check_days = est(dp)->next_level0;
2967         est(dp)->degr_est = est(dp)->dump_est;
2968         est(dp)->dump_est = level0_est;
2969         est(dp)->next_level0 = 0;
2970
2971         g_fprintf(stderr,
2972               _("   promote: moving %s:%s up, total_lev0 %1.0lf, total_size %lld\n"),
2973                 dp->host->hostname, qname,
2974                 total_lev0, (long long)total_size);
2975
2976         log_add(L_INFO,
2977                 plural(_("Full dump of %s:%s promoted from %d day ahead."),
2978                        _("Full dump of %s:%s promoted from %d days ahead."),
2979                       check_days),
2980                 dp->host->hostname, qname, check_days);
2981         amfree(qname);
2982         return 1;
2983     }
2984     return 0;
2985 }
2986
2987
2988 static int promote_hills(void)
2989 {
2990     disk_t *dp;
2991     struct balance_stats {
2992         int disks;
2993         gint64 size;
2994     } *sp = NULL;
2995     int days;
2996     int hill_days = 0;
2997     gint64 hill_size;
2998     gint64 new_total;
2999     int my_dumpcycle;
3000     char *qname;
3001
3002     /* If we are already doing a level 0 don't bother */
3003     if(total_lev0 > 0)
3004         return 0;
3005
3006     /* Do the guts of an "amadmin balance" */
3007     my_dumpcycle = conf_dumpcycle;
3008     if(my_dumpcycle > 10000) my_dumpcycle = 10000;
3009
3010     sp = (struct balance_stats *)
3011         alloc(SIZEOF(struct balance_stats) * my_dumpcycle);
3012
3013     for(days = 0; days < my_dumpcycle; days++) {
3014         sp[days].disks = 0;
3015         sp[days].size = (gint64)0;
3016     }
3017
3018     for(dp = schedq.head; dp != NULL; dp = dp->next) {
3019         days = est(dp)->next_level0;
3020         if (days < 0) days = 0;
3021         if(days<my_dumpcycle && !dp->skip_full && dp->strategy != DS_NOFULL &&
3022            dp->strategy != DS_INCRONLY) {
3023             sp[days].disks++;
3024             sp[days].size += est(dp)->last_lev0size;
3025         }
3026     }
3027
3028     /* Search for a suitable big hill and cut it down */
3029     while(1) {
3030         /* Find the tallest hill */
3031         hill_size = (gint64)0;
3032         for(days = 0; days < my_dumpcycle; days++) {
3033             if(sp[days].disks > 1 && sp[days].size > hill_size) {
3034                 hill_size = sp[days].size;
3035                 hill_days = days;
3036             }
3037         }
3038
3039         if(hill_size <= (gint64)0) break;       /* no suitable hills */
3040
3041         /* Find all the dumps in that hill and try and remove one */
3042         for(dp = schedq.head; dp != NULL; dp = dp->next) {
3043             one_est_t *level0_est;
3044             if(est(dp)->next_level0 != hill_days ||
3045                est(dp)->next_level0 > dp->maxpromoteday ||
3046                dp->skip_full ||
3047                dp->strategy == DS_NOFULL ||
3048                dp->strategy == DS_INCRONLY)
3049                 continue;
3050             level0_est = est_for_level(dp, 0);
3051             if (level0_est->nsize <= (gint64)0)
3052                 continue;
3053             new_total = total_size - est(dp)->dump_est->csize + level0_est->csize;
3054             if(new_total > tape_length)
3055                 continue;
3056             /* We found a disk we can promote */
3057             qname = quote_string(dp->name);
3058             total_size = new_total;
3059             total_lev0 += (double)level0_est->csize;
3060             est(dp)->degr_est = est(dp)->dump_est;
3061             est(dp)->dump_est = level0_est;
3062             est(dp)->next_level0 = 0;
3063
3064             g_fprintf(stderr,
3065                     _("   promote: moving %s:%s up, total_lev0 %1.0lf, total_size %lld\n"),
3066                     dp->host->hostname, qname,
3067                     total_lev0, (long long)total_size);
3068
3069             log_add(L_INFO,
3070                     plural(_("Full dump of %s:%s specially promoted from %d day ahead."),
3071                            _("Full dump of %s:%s specially promoted from %d days ahead."),
3072                            hill_days),
3073                     dp->host->hostname, qname, hill_days);
3074
3075             amfree(qname);
3076             amfree(sp);
3077             return 1;
3078         }
3079         /* All the disks in that hill were unsuitable. */
3080         sp[hill_days].disks = 0;        /* Don't get tricked again */
3081     }
3082
3083     amfree(sp);
3084     return 0;
3085 }
3086
3087 /*
3088  * ========================================================================
3089  * OUTPUT SCHEDULE
3090  *
3091  * XXX - memory leak - we shouldn't just throw away *dp
3092  */
3093 static void output_scheduleline(
3094     disk_t *dp)
3095 {
3096     est_t *ep;
3097     time_t dump_time = 0, degr_time = 0;
3098     double dump_kps = 0, degr_kps = 0;
3099     char *schedline = NULL, *degr_str = NULL;
3100     char dump_priority_str[NUM_STR_SIZE];
3101     char dump_level_str[NUM_STR_SIZE];
3102     char dump_nsize_str[NUM_STR_SIZE];
3103     char dump_csize_str[NUM_STR_SIZE];
3104     char dump_time_str[NUM_STR_SIZE];
3105     char dump_kps_str[NUM_STR_SIZE];
3106     char degr_level_str[NUM_STR_SIZE];
3107     char degr_nsize_str[NUM_STR_SIZE];
3108     char degr_csize_str[NUM_STR_SIZE];
3109     char degr_time_str[NUM_STR_SIZE];
3110     char degr_kps_str[NUM_STR_SIZE];
3111     char *dump_date, *degr_date;
3112     char *features;
3113     char *qname = quote_string(dp->name);
3114
3115     ep = est(dp);
3116
3117     if(ep->dump_est->csize == (gint64)-1) {
3118         /* no estimate, fail the disk */
3119         g_fprintf(stderr,
3120                 _("%s: FAILED %s %s %s %d \"[no estimate]\"\n"),
3121                 get_pname(),
3122                 dp->host->hostname, qname, planner_timestamp, ep->dump_est->level);
3123         log_add(L_FAIL, _("%s %s %s %d [no estimate]"),
3124                 dp->host->hostname, qname, planner_timestamp, ep->dump_est->level);
3125         amfree(qname);
3126         return;
3127     }
3128
3129     dump_date = ep->dump_est->dumpdate;
3130     degr_date = ep->degr_est->dumpdate;
3131
3132 #define fix_rate(rate) (rate < 1.0 ? DEFAULT_DUMPRATE : rate)
3133
3134     if(ep->dump_est->level == 0) {
3135         dump_kps = fix_rate(ep->fullrate);
3136         dump_time = (time_t)((double)ep->dump_est->csize / dump_kps);
3137
3138         if(ep->degr_est->csize != (gint64)-1) {
3139             degr_kps = fix_rate(ep->incrrate);
3140             degr_time = (time_t)((double)ep->degr_est->csize / degr_kps);
3141         }
3142     }
3143     else {
3144         dump_kps = fix_rate(ep->incrrate);
3145         dump_time = (time_t)((double)ep->dump_est->csize / dump_kps);
3146     }
3147
3148     if(ep->dump_est->level == 0 && ep->degr_est->csize != (gint64)-1) {
3149         g_snprintf(degr_level_str, sizeof(degr_level_str),
3150                     "%d", ep->degr_est->level);
3151         g_snprintf(degr_nsize_str, sizeof(degr_nsize_str),
3152                     "%lld", (long long)ep->degr_est->nsize);
3153         g_snprintf(degr_csize_str, sizeof(degr_csize_str),
3154                     "%lld", (long long)ep->degr_est->csize);
3155         g_snprintf(degr_time_str, sizeof(degr_time_str),
3156                     "%lld", (long long)degr_time);
3157         g_snprintf(degr_kps_str, sizeof(degr_kps_str),
3158                     "%.0lf", degr_kps);
3159         degr_str = vstralloc(" ", degr_level_str,
3160                              " ", degr_date,
3161                              " ", degr_nsize_str,
3162                              " ", degr_csize_str,
3163                              " ", degr_time_str,
3164                              " ", degr_kps_str,
3165                              NULL);
3166     } else {
3167         char *degr_mesg;
3168         if (ep->degr_mesg) {
3169             degr_mesg = quote_string(ep->degr_mesg);
3170         } else {
3171             degr_mesg = quote_string(_("Skipping: cannot dump in degraded mode for unknown reason"));
3172         }
3173         degr_str = vstralloc(" ", degr_mesg, NULL);
3174         amfree(degr_mesg);
3175     }
3176     g_snprintf(dump_priority_str, SIZEOF(dump_priority_str),
3177                 "%d", ep->dump_priority);
3178     g_snprintf(dump_level_str, SIZEOF(dump_level_str),
3179                 "%d", ep->dump_est->level);
3180     g_snprintf(dump_nsize_str, sizeof(dump_nsize_str),
3181                 "%lld", (long long)ep->dump_est->nsize);
3182     g_snprintf(dump_csize_str, sizeof(dump_csize_str),
3183                 "%lld", (long long)ep->dump_est->csize);
3184     g_snprintf(dump_time_str, sizeof(dump_time_str),
3185                 "%lld", (long long)dump_time);
3186     g_snprintf(dump_kps_str, sizeof(dump_kps_str),
3187                 "%.0lf", dump_kps);
3188     features = am_feature_to_string(dp->host->features);
3189     schedline = vstralloc("DUMP ",dp->host->hostname,
3190                           " ", features,
3191                           " ", qname,
3192                           " ", planner_timestamp,
3193                           " ", dump_priority_str,
3194                           " ", dump_level_str,
3195                           " ", dump_date,
3196                           " ", dump_nsize_str,
3197                           " ", dump_csize_str,
3198                           " ", dump_time_str,
3199                           " ", dump_kps_str,
3200                           degr_str ? degr_str : "",
3201                           "\n", NULL);
3202
3203     if (est(dp)->dump_est->guessed == 1) {
3204         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);
3205     }
3206     fputs(schedline, stdout);
3207     fputs(schedline, stderr);
3208     amfree(features);
3209     amfree(schedline);
3210     amfree(degr_str);
3211     amfree(qname);
3212 }
3213
3214 static void
3215 server_estimate(
3216     disk_t *dp,
3217     int     i,
3218     info_t *info,
3219     int     level)
3220 {
3221     int    stats;
3222     gint64 size;
3223
3224     size = internal_server_estimate(dp, info, level, &stats);
3225
3226     est(dp)->dump_est = &est(dp)->estimate[i];
3227     est(dp)->estimate[i].nsize = size;
3228     if (stats == 0) {
3229         est(dp)->estimate[i].guessed = 1;
3230     }
3231 }