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