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