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