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