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