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