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