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