Imported Upstream version 2.4.5p1
[debian/amanda] / server-src / driver.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-2000 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: driver.c,v 1.58.2.31.2.8.2.20.2.16 2005/09/20 21:31:52 jrjackson Exp $
28  *
29  * controlling process for the Amanda backup system
30  */
31
32 /*
33  * XXX possibly modify tape queue to be cognizant of how much room is left on
34  *     tape.  Probably not effective though, should do this in planner.
35  */
36
37 #include "amanda.h"
38 #include "clock.h"
39 #include "conffile.h"
40 #include "diskfile.h"
41 #include "holding.h"
42 #include "infofile.h"
43 #include "logfile.h"
44 #include "statfs.h"
45 #include "version.h"
46 #include "driverio.h"
47 #include "server_util.h"
48
49 disklist_t waitq, runq, tapeq, roomq;
50 int pending_aborts, inside_dump_to_tape;
51 disk_t *taper_disk;
52 int degraded_mode;
53 unsigned long reserved_space;
54 unsigned long total_disksize;
55 char *dumper_program;
56 int  inparallel;
57 int nodump = 0;
58 unsigned long tape_length, tape_left = 0;
59 int conf_taperalgo;
60 am_host_t *flushhost = NULL;
61
62 int client_constrained P((disk_t *dp));
63 int sort_by_priority_reversed P((disk_t *a, disk_t *b));
64 int sort_by_time P((disk_t *a, disk_t *b));
65 int start_some_dumps P((disklist_t *rq));
66 void dump_schedule P((disklist_t *qp, char *str));
67 void start_degraded_mode P((disklist_t *queuep));
68 void handle_taper_result P((void));
69 dumper_t *idle_dumper P((void));
70 int some_dumps_in_progress P((void));
71 int num_busy_dumpers P((void));
72 dumper_t *lookup_dumper P((int fd));
73 void handle_dumper_result P((int fd));
74 void read_flush P((disklist_t *tapeqp));
75 void read_schedule P((disklist_t *waitqp, disklist_t *runqp));
76 int free_kps P((interface_t *ip));
77 void interface_state P((char *time_str));
78 void allocate_bandwidth P((interface_t *ip, int kps));
79 void deallocate_bandwidth P((interface_t *ip, int kps));
80 unsigned long free_space P((void));
81 assignedhd_t **find_diskspace P((unsigned long size, int *cur_idle, assignedhd_t *preferred));
82 char *diskname2filename P((char *dname));
83 int assign_holdingdisk P((assignedhd_t **holdp, disk_t *diskp));
84 static void adjust_diskspace P((disk_t *diskp, cmd_t cmd));
85 static void delete_diskspace P((disk_t *diskp));
86 assignedhd_t **build_diskspace P((char *destname));
87 void holdingdisk_state P((char *time_str));
88 int dump_to_tape P((disk_t *dp));
89 int queue_length P((disklist_t q));
90 void short_dump_state P((void));
91 void dump_state P((char *str));
92 void startaflush P((void));
93 int main P((int main_argc, char **main_argv));
94
95 static int idle_reason;
96 char *datestamp;
97 char *timestamp;
98
99 char *idle_strings[] = {
100 #define NOT_IDLE                0
101     "not-idle",
102 #define IDLE_START_WAIT         1
103     "start-wait",
104 #define IDLE_NO_DUMPERS         2
105     "no-dumpers",
106 #define IDLE_NO_HOLD            3
107     "no-hold",
108 #define IDLE_CLIENT_CONSTRAINED 4
109     "client-constrained",
110 #define IDLE_NO_DISKSPACE       5
111     "no-diskspace",
112 #define IDLE_TOO_LARGE          6
113     "file-too-large",
114 #define IDLE_NO_BANDWIDTH       7
115     "no-bandwidth",
116 #define IDLE_TAPER_WAIT         8
117     "taper-wait",
118 };
119
120 #define SLEEP_MAX               (24*3600)
121 struct timeval sleep_time = { SLEEP_MAX, 0 };
122 /* enabled if any disks are in start-wait: */
123 int any_delayed_disk = 0;
124
125 int main(main_argc, main_argv)
126      int main_argc;
127      char **main_argv;
128 {
129     disklist_t *origqp;
130     disk_t *diskp;
131     fd_set selectset;
132     int fd, dsk;
133     dumper_t *dumper;
134     char *newdir = NULL;
135     generic_fs_stats_t fs;
136     holdingdisk_t *hdp;
137     unsigned long malloc_hist_1, malloc_size_1;
138     unsigned long malloc_hist_2, malloc_size_2;
139     unsigned long reserve = 100;
140     char *conffile;
141     char *conf_diskfile;
142     cmd_t cmd;
143     int result_argc;
144     char *result_argv[MAX_ARGS+1];
145     char *taper_program;
146     amwait_t retstat;
147     char *conf_tapetype;
148     tapetype_t *tape;
149
150     safe_fd(-1, 0);
151
152     setvbuf(stdout, (char *)NULL, _IOLBF, 0);
153     setvbuf(stderr, (char *)NULL, _IOLBF, 0);
154
155     set_pname("driver");
156
157     signal(SIGPIPE, SIG_IGN);
158
159     malloc_size_1 = malloc_inuse(&malloc_hist_1);
160
161     erroutput_type = (ERR_AMANDALOG|ERR_INTERACTIVE);
162     set_logerror(logerror);
163
164     startclock();
165     FD_ZERO(&readset);
166
167     printf("%s: pid %ld executable %s version %s\n",
168            get_pname(), (long) getpid(), main_argv[0], version());
169
170     if (main_argc > 1) {
171         config_name = stralloc(main_argv[1]);
172         config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
173         if(main_argc > 2) {
174             if(strncmp(main_argv[2], "nodump", 6) == 0) {
175                 nodump = 1;
176             }
177         }
178
179     } else {
180         char my_cwd[STR_SIZE];
181
182         if (getcwd(my_cwd, sizeof(my_cwd)) == NULL) {
183             error("cannot determine current working directory");
184         }
185         config_dir = stralloc2(my_cwd, "/");
186         if ((config_name = strrchr(my_cwd, '/')) != NULL) {
187             config_name = stralloc(config_name + 1);
188         }
189     }
190
191     safe_cd();
192
193     conffile = stralloc2(config_dir, CONFFILE_NAME);
194     if(read_conffile(conffile)) {
195         error("errors processing config file \"%s\"", conffile);
196     }
197     amfree(conffile);
198
199     amfree(datestamp);
200     datestamp = construct_datestamp(NULL);
201     timestamp = construct_timestamp(NULL);
202     log_add(L_START,"date %s", datestamp);
203
204     taper_program = vstralloc(libexecdir, "/", "taper", versionsuffix(), NULL);
205     dumper_program = vstralloc(libexecdir, "/", "dumper", versionsuffix(),
206                                NULL);
207
208     conf_taperalgo = getconf_int(CNF_TAPERALGO);
209     conf_tapetype = getconf_str(CNF_TAPETYPE);
210     tape = lookup_tapetype(conf_tapetype);
211     tape_length = tape->length;
212     printf("driver: tape size %ld\n", tape_length);
213
214     /* taper takes a while to get going, so start it up right away */
215
216     init_driverio();
217     startup_tape_process(taper_program);
218     taper_cmd(START_TAPER, datestamp, NULL, 0, NULL);
219
220     /* start initializing: read in databases */
221
222     conf_diskfile = getconf_str(CNF_DISKFILE);
223     if (*conf_diskfile == '/') {
224         conf_diskfile = stralloc(conf_diskfile);
225     } else {
226         conf_diskfile = stralloc2(config_dir, conf_diskfile);
227     }
228     if((origqp = read_diskfile(conf_diskfile)) == NULL) {
229         error("could not load disklist \"%s\"", conf_diskfile);
230     }
231     amfree(conf_diskfile);
232
233     /* set up any configuration-dependent variables */
234
235     inparallel  = getconf_int(CNF_INPARALLEL);
236
237     reserve = getconf_int(CNF_RESERVE);
238
239     total_disksize = 0;
240     for(hdp = getconf_holdingdisks(), dsk = 0; hdp != NULL; hdp = hdp->next, dsk++) {
241         hdp->up = (void *)alloc(sizeof(holdalloc_t));
242         holdalloc(hdp)->allocated_dumpers = 0;
243         holdalloc(hdp)->allocated_space = 0L;
244
245         if(get_fs_stats(hdp->diskdir, &fs) == -1
246            || access(hdp->diskdir, W_OK) == -1) {
247             log_add(L_WARNING, "WARNING: ignoring holding disk %s: %s\n",
248                     hdp->diskdir, strerror(errno));
249             hdp->disksize = 0L;
250             continue;
251         }
252
253         if(fs.avail != -1) {
254             if(hdp->disksize > 0) {
255                 if(hdp->disksize > fs.avail) {
256                     log_add(L_WARNING,
257                             "WARNING: %s: %ld KB requested, but only %ld KB available.",
258                             hdp->diskdir, hdp->disksize, fs.avail);
259                             hdp->disksize = fs.avail;
260                 }
261             }
262             else if(fs.avail + hdp->disksize < 0) {
263                 log_add(L_WARNING,
264                         "WARNING: %s: not %ld KB free.",
265                         hdp->diskdir, -hdp->disksize);
266                 hdp->disksize = 0L;
267                 continue;
268             }
269             else
270                 hdp->disksize += fs.avail;
271         }
272
273         printf("driver: adding holding disk %d dir %s size %ld chunksize %ld\n",
274                dsk, hdp->diskdir, hdp->disksize, hdp->chunksize);
275
276         newdir = newvstralloc(newdir,
277                               hdp->diskdir, "/", timestamp,
278                               NULL);
279         if(!mkholdingdir(newdir)) {
280             hdp->disksize = 0L;
281         }
282         total_disksize += hdp->disksize;
283     }
284
285     reserved_space = total_disksize * (reserve / 100.0);
286
287     printf("reserving %ld out of %ld for degraded-mode dumps\n",
288                 reserved_space, free_space());
289
290     amfree(newdir);
291
292     if(inparallel > MAX_DUMPERS) inparallel = MAX_DUMPERS;
293
294     /* fire up the dumpers now while we are waiting */
295
296     if(!nodump) startup_dump_processes(dumper_program, inparallel);
297
298     /*
299      * Read schedule from stdin.  Usually, this is a pipe from planner,
300      * so the effect is that we wait here for the planner to
301      * finish, but meanwhile the taper is rewinding the tape, reading
302      * the label, checking it, writing a new label and all that jazz
303      * in parallel with the planner.
304      */
305
306     waitq = *origqp;
307     tapeq.head = tapeq.tail = NULL;
308     roomq.head = roomq.tail = NULL;
309     runq.head = runq.tail = NULL;
310
311     read_flush(&tapeq);
312
313     log_add(L_STATS, "startup time %s", walltime_str(curclock()));
314
315     printf("driver: start time %s inparallel %d bandwidth %d diskspace %lu",
316            walltime_str(curclock()), inparallel, free_kps((interface_t *)0),
317            free_space());
318     printf(" dir %s datestamp %s driver: drain-ends tapeq %s big-dumpers %s\n",
319            "OBSOLETE", datestamp, taperalgo2str(conf_taperalgo),
320            getconf_str(CNF_DUMPORDER));
321     fflush(stdout);
322
323     /* Let's see if the tape is ready */
324
325     cmd = getresult(taper, 1, &result_argc, result_argv, MAX_ARGS+1);
326
327     if(cmd != TAPER_OK) {
328         /* no tape, go into degraded mode: dump to holding disk */
329         start_degraded_mode(&runq);
330         FD_CLR(taper,&readset);
331     }
332
333     short_dump_state();                                 /* for amstatus */
334
335     tape_left = tape_length;
336     taper_busy = 0;
337     taper_disk = NULL;
338
339     /* Start autoflush while waiting for dump schedule */
340     if(!nodump) {
341         /* Start any autoflush tape writes */
342         if (!empty(tapeq)) {
343             startaflush();
344             short_dump_state();                         /* for amstatus */
345
346             /* Process taper results until the schedule arrives */
347             while (1) {
348                 FD_ZERO(&selectset);
349                 FD_SET(0, &selectset);
350                 FD_SET(taper, &selectset);
351
352                 if(select(taper+1, (SELECT_ARG_TYPE *)(&selectset), NULL, NULL,
353                           &sleep_time) == -1)
354                     error("select: %s", strerror(errno));
355                 if (FD_ISSET(0, &selectset)) break;     /* schedule arrived */
356                 if (FD_ISSET(taper, &selectset)) handle_taper_result();
357                 short_dump_state();                     /* for amstatus */
358             }
359             
360         }
361
362         /* Read the dump schedule */
363         read_schedule(&waitq, &runq);
364     }
365
366     /* Start any needed flushes */
367     startaflush();
368
369     while(start_some_dumps(&runq) || some_dumps_in_progress() ||
370           any_delayed_disk) {
371         short_dump_state();
372
373         /* wait for results */
374
375         memcpy(&selectset, &readset, sizeof(fd_set));
376         if(select(maxfd+1, (SELECT_ARG_TYPE *)(&selectset),
377                   NULL, NULL, &sleep_time) == -1)
378             error("select: %s", strerror(errno));
379
380         /* handle any results that have come in */
381
382         for(fd = 0; fd <= maxfd; fd++) {
383             /*
384              * The first pass through the following loop, we have
385              * data ready for areads (called by getresult, called by
386              * handle_.*_result).  But that may read more than one record,
387              * so we need to keep processing as long as areads has data.
388              * We will get control back after each record and the buffer
389              * will go empty (indicated by areads_dataready(fd) == 0)
390              * after the last one available has been processed.
391              */
392             while(FD_ISSET(fd, &selectset) || areads_dataready(fd) > 0) {
393                 if(fd == taper) handle_taper_result();
394                 else handle_dumper_result(fd);
395                 FD_CLR(fd, &selectset);
396             }
397         }
398
399     }
400
401     /* handle any remaining dumps by dumping directly to tape, if possible */
402
403     while(!empty(runq)) {
404         diskp = dequeue_disk(&runq);
405         if(!degraded_mode) {
406             int rc = dump_to_tape(diskp);
407             if(rc == 1)
408                 log_add(L_INFO,
409                         "%s %s %d [dump to tape failed, will try again]",
410                         diskp->host->hostname,
411                         diskp->name,
412                         sched(diskp)->level);
413             else if(rc == 2)
414                 log_add(L_FAIL, "%s %s %s %d [dump to tape failed]",
415                         diskp->host->hostname,
416                         diskp->name,
417                         sched(diskp)->datestamp,
418                         sched(diskp)->level);
419         }
420         else
421             log_add(L_FAIL, "%s %s %s %d [%s]",
422                     diskp->host->hostname, diskp->name,
423                     sched(diskp)->datestamp, sched(diskp)->level,
424                 diskp->no_hold ?
425                     "can't dump no-hold disk in degraded mode" :
426                     "no more holding disk space");
427     }
428
429     short_dump_state();                         /* for amstatus */
430
431     printf("driver: QUITTING time %s telling children to quit\n",
432            walltime_str(curclock()));
433     fflush(stdout);
434
435     if(!nodump) {
436         for(dumper = dmptable; dumper < dmptable + inparallel; dumper++) {
437             dumper_cmd(dumper, QUIT, NULL);
438         }
439     }
440
441     if(taper >= 0) {
442         taper_cmd(QUIT, NULL, NULL, 0, NULL);
443     }
444
445     /* wait for all to die */
446
447     while(1) {
448         char number[NUM_STR_SIZE];
449         pid_t pid;
450         char *who;
451         char *what;
452         int code=0;
453
454         if((pid = wait(&retstat)) == -1) {
455             if(errno == EINTR) continue;
456             else break;
457         }
458         what = NULL;
459         if(! WIFEXITED(retstat)) {
460             what = "signal";
461             code = WTERMSIG(retstat);
462         } else if(WEXITSTATUS(retstat) != 0) {
463             what = "code";
464             code = WEXITSTATUS(retstat);
465         }
466         who = NULL;
467         for(dumper = dmptable; dumper < dmptable + inparallel; dumper++) {
468             if(pid == dumper->pid) {
469                 who = stralloc(dumper->name);
470                 break;
471             }
472         }
473         if(who == NULL && pid == taper_pid) {
474             who = stralloc("taper");
475         }
476         if(what != NULL && who == NULL) {
477             ap_snprintf(number, sizeof(number), "%ld", (long)pid);
478             who = stralloc2("unknown pid ", number);
479         }
480         if(who && what) {
481             log_add(L_WARNING, "%s exited with %s %d\n", who, what, code);
482             printf("driver: %s exited with %s %d\n", who, what, code);
483         }
484         amfree(who);
485     }
486
487     for(dumper = dmptable; dumper < dmptable + inparallel; dumper++) {
488         amfree(dumper->name);
489     }
490
491     for(hdp = getconf_holdingdisks(); hdp != NULL; hdp = hdp->next) {
492         cleanup_holdingdisk(hdp->diskdir, 0);
493         amfree(hdp->up);
494     }
495     amfree(newdir);
496
497     printf("driver: FINISHED time %s\n", walltime_str(curclock()));
498     fflush(stdout);
499     log_add(L_FINISH,"date %s time %s", datestamp, walltime_str(curclock()));
500     amfree(datestamp);
501     amfree(timestamp);
502
503     amfree(dumper_program);
504     amfree(taper_program);
505     amfree(config_dir);
506     amfree(config_name);
507
508     malloc_size_2 = malloc_inuse(&malloc_hist_2);
509
510     if(malloc_size_1 != malloc_size_2) {
511         malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
512     }
513
514     return 0;
515 }
516
517 void startaflush() {
518     disk_t *dp = NULL;
519     disk_t *fit = NULL;
520     char *datestamp;
521
522     if(!degraded_mode && !taper_busy && !empty(tapeq)) {
523         datestamp = sched(tapeq.head)->datestamp;
524         switch(conf_taperalgo) {
525         case ALGO_FIRST:
526                 dp = dequeue_disk(&tapeq);
527                 break;
528         case ALGO_FIRSTFIT: 
529                 fit = tapeq.head;
530                 while (fit != NULL) {
531                     if(sched(fit)->act_size <= tape_left &&
532                        strcmp(sched(fit)->datestamp, datestamp) <= 0) {
533                         dp = fit;
534                         fit = NULL;
535                     }
536                     else {
537                         fit = fit->next;
538                     }
539                 }
540                 if(dp) remove_disk(&tapeq, dp);
541                 break;
542         case ALGO_LARGEST: 
543                 fit = dp = tapeq.head;
544                 while (fit != NULL) {
545                     if(sched(fit)->act_size > sched(dp)->act_size &&
546                        strcmp(sched(fit)->datestamp, datestamp) <= 0) {
547                         dp = fit;
548                     }
549                     fit = fit->next;
550                 }
551                 if(dp) remove_disk(&tapeq, dp);
552                 break;
553         case ALGO_LARGESTFIT: 
554                 fit = tapeq.head;
555                 while (fit != NULL) {
556                     if(sched(fit)->act_size <= tape_left &&
557                        (!dp || sched(fit)->act_size > sched(dp)->act_size) &&
558                        strcmp(sched(fit)->datestamp, datestamp) <= 0) {
559                         dp = fit;
560                     }
561                     fit = fit->next;
562                 }
563                 if(dp) remove_disk(&tapeq, dp);
564                 break;
565         case ALGO_SMALLEST: 
566                 break;
567         case ALGO_LAST:
568                 dp = tapeq.tail;
569                 remove_disk(&tapeq, dp);
570                 break;
571         }
572         if(!dp) { /* ALGO_SMALLEST, or default if nothing fit. */
573             if(conf_taperalgo != ALGO_SMALLEST)  {
574                 fprintf(stderr,
575                    "driver: startaflush: Using SMALLEST because nothing fit\n");
576             }
577             fit = dp = tapeq.head;
578             while (fit != NULL) {
579                 if(sched(fit)->act_size < sched(dp)->act_size &&
580                    strcmp(sched(fit)->datestamp, datestamp) <= 0) {
581                     dp = fit;
582                 }
583                 fit = fit->next;
584             }
585             if(dp) remove_disk(&tapeq, dp);
586         }
587         taper_disk = dp;
588         taper_busy = 1;
589         taper_cmd(FILE_WRITE, dp, sched(dp)->destname, sched(dp)->level, 
590                   sched(dp)->datestamp);
591         fprintf(stderr,"driver: startaflush: %s %s %s %ld %ld\n",
592                 taperalgo2str(conf_taperalgo), dp->host->hostname,
593                 dp->name, sched(taper_disk)->act_size, tape_left);
594         if(sched(dp)->act_size <= tape_left)
595             tape_left -= sched(dp)->act_size;
596         else
597             tape_left = 0;
598     }
599 }
600
601
602 int client_constrained(dp)
603 disk_t *dp;
604 {
605     disk_t *dp2;
606
607     /* first, check if host is too busy */
608
609     if(dp->host->inprogress >= dp->host->maxdumps) {
610         return 1;
611     }
612
613     /* next, check conflict with other dumps on same spindle */
614
615     if(dp->spindle == -1) {     /* but spindle -1 never conflicts by def. */
616         return 0;
617     }
618
619     for(dp2 = dp->host->disks; dp2 != NULL; dp2 = dp2->hostnext)
620         if(dp2->inprogress && dp2->spindle == dp->spindle) {
621             return 1;
622         }
623
624     return 0;
625 }
626
627 int start_some_dumps(rq)
628 disklist_t *rq;
629 {
630     int total, cur_idle;
631     disk_t *diskp, *diskp_accept;
632     dumper_t *dumper;
633     assignedhd_t **holdp=NULL, **holdp_accept;
634     time_t now = time(NULL);
635
636     total = 0;
637     idle_reason = IDLE_NO_DUMPERS;
638     sleep_time.tv_sec = SLEEP_MAX;
639     sleep_time.tv_usec = 0;
640     any_delayed_disk = 0;
641
642     if(rq->head == NULL) {
643         idle_reason = 0;
644         return 0;
645     }
646
647     /*
648      * A potential problem with starting from the bottom of the dump time
649      * distribution is that a slave host will have both one of the shortest
650      * and one of the longest disks, so starting its shortest disk first will
651      * tie up the host and eliminate its longest disk from consideration the
652      * first pass through.  This could cause a big delay in starting that long
653      * disk, which could drag out the whole night's dumps.
654      *
655      * While starting from the top of the dump time distribution solves the
656      * above problem, this turns out to be a bad idea, because the big dumps
657      * will almost certainly pack the holding disk completely, leaving no
658      * room for even one small dump to start.  This ends up shutting out the
659      * small-end dumpers completely (they stay idle).
660      *
661      * The introduction of multiple simultaneous dumps to one host alleviates
662      * the biggest&smallest dumps problem: both can be started at the
663      * beginning.
664      */
665     for(dumper = dmptable; dumper < dmptable+inparallel; dumper++) {
666         if(dumper->busy || dumper->down) continue;
667         /* found an idle dumper, now find a disk for it */
668         diskp = rq->head;
669         diskp_accept = NULL;
670         holdp_accept = NULL;
671
672         if(idle_reason == IDLE_NO_DUMPERS)
673             idle_reason = NOT_IDLE;
674
675         cur_idle = NOT_IDLE;
676
677         while(diskp) {
678             assert(diskp->host != NULL && sched(diskp) != NULL);
679
680             /* round estimate to next multiple of DISK_BLOCK_KB */
681             sched(diskp)->est_size = am_round(sched(diskp)->est_size,
682                                               DISK_BLOCK_KB);
683
684             if(diskp->host->start_t > now) {
685                 cur_idle = max(cur_idle, IDLE_START_WAIT);
686                 sleep_time.tv_sec = min(diskp->host->start_t - now, 
687                                         sleep_time.tv_sec);
688                 any_delayed_disk = 1;
689             }
690             else if(diskp->start_t > now) {
691                 cur_idle = max(cur_idle, IDLE_START_WAIT);
692                 sleep_time.tv_sec = min(diskp->start_t - now, 
693                                         sleep_time.tv_sec);
694                 any_delayed_disk = 1;
695             }
696             else if(diskp->host->netif->curusage > 0 &&
697                     sched(diskp)->est_kps > free_kps(diskp->host->netif))
698                 cur_idle = max(cur_idle, IDLE_NO_BANDWIDTH);
699             else if(sched(diskp)->no_space)
700                 cur_idle = max(cur_idle, IDLE_NO_DISKSPACE);
701             else if((holdp = find_diskspace(sched(diskp)->est_size,&cur_idle,NULL)) == NULL)
702                 cur_idle = max(cur_idle, IDLE_NO_DISKSPACE);
703             else if(diskp->no_hold) {
704                 free_assignedhd(holdp);
705                 cur_idle = max(cur_idle, IDLE_NO_HOLD);
706             } else if(client_constrained(diskp)) {
707                 free_assignedhd(holdp);
708                 cur_idle = max(cur_idle, IDLE_CLIENT_CONSTRAINED);
709             } else {
710
711                 /* disk fits, dump it */
712                 int accept = !diskp_accept;
713                 if(!accept) {
714                     char dumptype;
715                     char *dumporder = getconf_str(CNF_DUMPORDER);
716                     if(strlen(dumporder) <= (dumper-dmptable)) {
717                         if(dumper-dmptable < 3)
718                             dumptype = 't';
719                         else
720                             dumptype = 'T';
721                     }
722                     else {
723                         dumptype = dumporder[dumper-dmptable];
724                     }
725                     switch(dumptype) {
726                       case 's': accept = (sched(diskp)->est_size < sched(diskp_accept)->est_size);
727                                 break;
728                       case 'S': accept = (sched(diskp)->est_size > sched(diskp_accept)->est_size);
729                                 break;
730                       case 't': accept = (sched(diskp)->est_time < sched(diskp_accept)->est_time);
731                                 break;
732                       case 'T': accept = (sched(diskp)->est_time > sched(diskp_accept)->est_time);
733                                 break;
734                       case 'b': accept = (sched(diskp)->est_kps < sched(diskp_accept)->est_kps);
735                                 break;
736                       case 'B': accept = (sched(diskp)->est_kps > sched(diskp_accept)->est_kps);
737                                 break;
738                       default:  log_add(L_WARNING, "Unknown dumporder character \'%c\', using 's'.\n",
739                                         dumptype);
740                                 accept = (sched(diskp)->est_size < sched(diskp_accept)->est_size);
741                                 break;
742                     }
743                 }
744                 if(accept) {
745                     if( !diskp_accept || !degraded_mode || diskp->priority >= diskp_accept->priority) {
746                         if(holdp_accept) free_assignedhd(holdp_accept);
747                         diskp_accept = diskp;
748                         holdp_accept = holdp;
749                     }
750                     else {
751                         free_assignedhd(holdp);
752                     }
753                 }
754                 else {
755                     free_assignedhd(holdp);
756                 }
757             }
758             diskp = diskp->next;
759         }
760
761         diskp = diskp_accept;
762         holdp = holdp_accept;
763         if(diskp) {
764             cur_idle = NOT_IDLE;
765             sched(diskp)->act_size = 0;
766             allocate_bandwidth(diskp->host->netif, sched(diskp)->est_kps);
767             sched(diskp)->activehd = assign_holdingdisk(holdp, diskp);
768             amfree(holdp);
769             diskp->host->inprogress += 1;       /* host is now busy */
770             diskp->inprogress = 1;
771             sched(diskp)->dumper = dumper;
772             sched(diskp)->timestamp = time((time_t *)0);
773
774             dumper->busy = 1;           /* dumper is now busy */
775             dumper->dp = diskp;         /* link disk to dumper */
776             total++;
777             remove_disk(rq, diskp);             /* take it off the run queue */
778             dumper_cmd(dumper, FILE_DUMP, diskp);
779             diskp->host->start_t = time(NULL) + 15;
780         }
781         idle_reason = max(idle_reason, cur_idle);
782     }
783     return total;
784 }
785
786 int sort_by_priority_reversed(a, b)
787 disk_t *a, *b;
788 {
789     if(sched(b)->priority - sched(a)->priority != 0)
790         return sched(b)->priority - sched(a)->priority;
791     else
792         return sort_by_time(a, b);
793 }
794
795 int sort_by_time(a, b)
796 disk_t *a, *b;
797 {
798     long diff;
799
800     if ((diff = sched(a)->est_time - sched(b)->est_time) < 0) {
801         return -1;
802     } else if (diff > 0) {
803         return 1;
804     } else {
805         return 0;
806     }
807 }
808
809 void dump_schedule(qp, str)
810 disklist_t *qp;
811 char *str;
812 {
813     disk_t *dp;
814
815     printf("dump of driver schedule %s:\n--------\n", str);
816
817     for(dp = qp->head; dp != NULL; dp = dp->next) {
818         printf("  %-20s %-25s lv %d t %5ld s %8lu p %d\n",
819                dp->host->hostname, dp->name, sched(dp)->level,
820                sched(dp)->est_time, sched(dp)->est_size, sched(dp)->priority);
821     }
822     printf("--------\n");
823 }
824
825
826 void start_degraded_mode(queuep)
827 disklist_t *queuep;
828 {
829     disk_t *dp;
830     disklist_t newq;
831     unsigned long est_full_size;
832
833     newq.head = newq.tail = 0;
834
835     dump_schedule(queuep, "before start degraded mode");
836
837     est_full_size = 0;
838     while(!empty(*queuep)) {
839         dp = dequeue_disk(queuep);
840
841         if(sched(dp)->level != 0)
842             /* go ahead and do the disk as-is */
843             insert_disk(&newq, dp, sort_by_priority_reversed);
844         else {
845             if (reserved_space + est_full_size + sched(dp)->est_size
846                 <= total_disksize) {
847                 insert_disk(&newq, dp, sort_by_priority_reversed);
848                 est_full_size += sched(dp)->est_size;
849             }
850             else if(sched(dp)->degr_level != -1) {
851                 sched(dp)->level = sched(dp)->degr_level;
852                 sched(dp)->dumpdate = sched(dp)->degr_dumpdate;
853                 sched(dp)->est_size = sched(dp)->degr_size;
854                 sched(dp)->est_time = sched(dp)->degr_time;
855                 sched(dp)->est_kps  = sched(dp)->degr_kps;
856                 insert_disk(&newq, dp, sort_by_priority_reversed);
857             }
858             else {
859                 log_add(L_FAIL, "%s %s %s %d [can't switch to incremental dump]",
860                         dp->host->hostname, dp->name,
861                         sched(dp)->datestamp, sched(dp)->level);
862             }
863         }
864     }
865
866     *queuep = newq;
867     degraded_mode = 1;
868
869     dump_schedule(queuep, "after start degraded mode");
870 }
871
872 void continue_dumps()
873 {
874 disk_t *dp, *ndp;
875 assignedhd_t **h;
876 int active_dumpers=0, busy_dumpers=0, i;
877 dumper_t *dumper;
878
879     /* First we try to grant diskspace to some dumps waiting for it. */
880     for( dp = roomq.head; dp; dp = ndp ) {
881         ndp = dp->next;
882         /* find last holdingdisk used by this dump */
883         for( i = 0, h = sched(dp)->holdp; h[i+1]; i++ );
884         /* find more space */
885         h = find_diskspace( sched(dp)->est_size - sched(dp)->act_size, &active_dumpers, h[i] );
886         if( h ) {
887             for(dumper = dmptable; dumper < dmptable + inparallel &&
888                                    dumper->dp != dp; dumper++);
889             assert( dumper < dmptable + inparallel );
890             sched(dp)->activehd = assign_holdingdisk( h, dp );
891             dumper_cmd( dumper, CONTINUE, dp );
892             amfree(h);
893             remove_disk( &roomq, dp );
894         }
895     }
896
897     /* So for some disks there is less holding diskspace available than
898      * was asked for. Possible reasons are
899      * a) diskspace has been allocated for other dumps which are
900      *    still running or already being written to tape
901      * b) all other dumps have been suspended due to lack of diskspace
902      * c) this dump doesn't fit on all the holding disks
903      * Case a) is not a problem. We just wait for the diskspace to
904      * be freed by moving the current disk to a queue.
905      * If case b) occurs, we have a deadlock situation. We select
906      * a dump from the queue to be aborted and abort it. It will
907      * be retried later dumping to disk.
908      * If case c) is detected, the dump is aborted. Next time
909      * it will be dumped directly to tape. Actually, case c is a special
910      * manifestation of case b) where only one dumper is busy.
911      */
912     for( dp=NULL, dumper = dmptable; dumper < dmptable + inparallel; dumper++) {
913         if( dumper->busy ) {
914             busy_dumpers++;
915             if( !find_disk(&roomq, dumper->dp) ) {
916                 active_dumpers++;
917             } else if( !dp || sched(dp)->est_size > sched(dumper->dp)->est_size ) {
918                 dp = dumper->dp;
919             }
920         }
921     }
922     if( !active_dumpers && busy_dumpers > 0 && 
923         ((!taper_busy && empty(tapeq)) || degraded_mode) &&
924         pending_aborts == 0 ) { /* not case a */
925         if( busy_dumpers == 1 ) { /* case c */
926             sched(dp)->no_space = 1;
927         }
928         /* case b */
929         /* At this time, dp points to the dump with the smallest est_size.
930          * We abort that dump, hopefully not wasting too much time retrying it.
931          */
932         remove_disk( &roomq, dp );
933         dumper_cmd( sched(dp)->dumper, ABORT, NULL );
934         pending_aborts++;
935     }
936 }
937
938 void handle_taper_result()
939 {
940     disk_t *dp;
941     int filenum;
942     cmd_t cmd;
943     int result_argc;
944     char *result_argv[MAX_ARGS+1];
945
946     cmd = getresult(taper, 1, &result_argc, result_argv, MAX_ARGS+1);
947
948     switch(cmd) {
949
950     case DONE:  /* DONE <handle> <label> <tape file> <err mess> */
951         if(result_argc != 5) {
952             error("error: [taper DONE result_argc != 5: %d", result_argc);
953         }
954
955         dp = serial2disk(result_argv[2]);
956         free_serial(result_argv[2]);
957
958         filenum = atoi(result_argv[4]);
959         update_info_taper(dp, result_argv[3], filenum, sched(dp)->level);
960
961         delete_diskspace(dp);
962
963         printf("driver: finished-cmd time %s taper wrote %s:%s\n",
964                walltime_str(curclock()), dp->host->hostname, dp->name);
965         fflush(stdout);
966
967         amfree(sched(dp)->dumpdate);
968         amfree(sched(dp)->degr_dumpdate);
969         amfree(sched(dp)->datestamp);
970         amfree(dp->up);
971
972         taper_busy = 0;
973         taper_disk = NULL;
974         startaflush();
975         continue_dumps(); /* continue with those dumps waiting for diskspace */
976         break;
977
978     case TRYAGAIN:  /* TRY-AGAIN <handle> <err mess> */
979         if (result_argc < 2) {
980             error("error [taper TRYAGAIN result_argc < 2: %d]", result_argc);
981         }
982         dp = serial2disk(result_argv[2]);
983         free_serial(result_argv[2]);
984         printf("driver: taper-tryagain time %s disk %s:%s\n",
985                walltime_str(curclock()), dp->host->hostname, dp->name);
986         fflush(stdout);
987
988         /* re-insert into taper queue */
989
990         if(sched(dp)->attempted) {
991             log_add(L_FAIL, "%s %s %d %s [too many taper retries]",
992                     dp->host->hostname, dp->name, sched(dp)->level,
993                     sched(dp)->datestamp);
994             printf("driver: taper failed %s %s %s, too many taper retry\n", result_argv[2], dp->host->hostname, dp->name);
995         }
996         else {
997             sched(dp)->attempted++;
998             headqueue_disk(&tapeq, dp);
999         }
1000
1001         tape_left = tape_length;
1002
1003         /* run next thing from queue */
1004         taper_busy = 0;
1005         taper_disk = NULL;
1006         startaflush();
1007         continue_dumps(); /* continue with those dumps waiting for diskspace */
1008
1009         break;
1010
1011     case TAPE_ERROR: /* TAPE-ERROR <handle> <err mess> */
1012         dp = serial2disk(result_argv[2]);
1013         free_serial(result_argv[2]);
1014         printf("driver: finished-cmd time %s taper wrote %s:%s\n",
1015                walltime_str(curclock()), dp->host->hostname, dp->name);
1016         fflush(stdout);
1017         /* Note: fall through code... */
1018
1019     case BOGUS:
1020         /*
1021          * Since we've gotten a tape error, we can't send anything more
1022          * to the taper.  Go into degraded mode to try to get everthing
1023          * onto disk.  Later, these dumps can be flushed to a new tape.
1024          * The tape queue is zapped so that it appears empty in future
1025          * checks. If there are dumps waiting for diskspace to be freed,
1026          * cancel one.
1027          */
1028         if(!nodump) {
1029             log_add(L_WARNING,
1030                     "going into degraded mode because of tape error.");
1031         }
1032         start_degraded_mode(&runq);
1033         taper_busy = 0;
1034         taper_disk = NULL;
1035         tapeq.head = tapeq.tail = NULL;
1036         FD_CLR(taper,&readset);
1037         if(cmd != TAPE_ERROR) aclose(taper);
1038         continue_dumps();
1039         break;
1040     default:
1041         error("driver received unexpected token (%d) from taper", cmd);
1042     }
1043 }
1044
1045
1046 dumper_t *idle_dumper()
1047 {
1048     dumper_t *dumper;
1049
1050     for(dumper = dmptable; dumper < dmptable+inparallel; dumper++)
1051         if(!dumper->busy && !dumper->down) return dumper;
1052
1053     return NULL;
1054 }
1055
1056 int some_dumps_in_progress()
1057 {
1058     dumper_t *dumper;
1059
1060     for(dumper = dmptable; dumper < dmptable+inparallel; dumper++)
1061         if(dumper->busy) return 1;
1062
1063     return taper_busy;
1064 }
1065
1066 int num_busy_dumpers()
1067 {
1068     dumper_t *dumper;
1069     int n;
1070
1071     n = 0;
1072     for(dumper = dmptable; dumper < dmptable+inparallel; dumper++)
1073         if(dumper->busy) n += 1;
1074
1075     return n;
1076 }
1077
1078 dumper_t *lookup_dumper(fd)
1079 int fd;
1080 {
1081     dumper_t *dumper;
1082
1083     for(dumper = dmptable; dumper < dmptable+inparallel; dumper++)
1084         if(dumper->outfd == fd) return dumper;
1085
1086     return NULL;
1087 }
1088
1089
1090 void handle_dumper_result(fd)
1091      int fd;
1092 {
1093     assignedhd_t **h=NULL;
1094     dumper_t *dumper;
1095     disk_t *dp, *sdp;
1096     long origsize;
1097     long dumpsize;
1098     long dumptime;
1099     cmd_t cmd;
1100     int result_argc;
1101     char *result_argv[MAX_ARGS+1];
1102     int i, dummy;
1103     int activehd = -1;
1104
1105     dumper = lookup_dumper(fd);
1106     dp = dumper->dp;
1107     assert(dp && sched(dp) && sched(dp)->destname);
1108
1109     if(dp && sched(dp) && sched(dp)->holdp) {
1110         h = sched(dp)->holdp;
1111         activehd = sched(dp)->activehd;
1112     }
1113
1114     cmd = getresult(fd, 1, &result_argc, result_argv, MAX_ARGS+1);
1115
1116     if(cmd != BOGUS) {
1117         sdp = serial2disk(result_argv[2]); /* result_argv[2] always contains the serial number */
1118         assert(sdp == dp);
1119     }
1120
1121     switch(cmd) {
1122
1123     case DONE: /* DONE <handle> <origsize> <dumpsize> <dumptime> <err str> */
1124         if(result_argc != 6) {
1125             error("error [dumper DONE result_argc != 6: %d]", result_argc);
1126         }
1127
1128         free_serial(result_argv[2]);
1129
1130         origsize = (long)atof(result_argv[3]);
1131         dumpsize = (long)atof(result_argv[4]);
1132         dumptime = (long)atof(result_argv[5]);
1133         update_info_dumper(dp, origsize, dumpsize, dumptime);
1134
1135         /* adjust holdp[active]->used using the real dumpsize and all other
1136          * holdp[i]->used as an estimate.
1137          */
1138
1139         dummy = 0;
1140         for( i = 0, h = sched(dp)->holdp; i < activehd; i++ ) {
1141             dummy += h[i]->used;
1142         }
1143
1144         rename_tmp_holding(sched(dp)->destname, 1);
1145         assert( h && activehd >= 0 );
1146         h[activehd]->used = size_holding_files(sched(dp)->destname) - dummy;
1147         deallocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
1148         holdalloc(h[activehd]->disk)->allocated_dumpers--;
1149         adjust_diskspace(dp, DONE);
1150         dumper->busy = 0;
1151         dp->host->inprogress -= 1;
1152         dp->inprogress = 0;
1153         sched(dp)->attempted = 0;
1154         printf("driver: finished-cmd time %s %s dumped %s:%s\n",
1155                walltime_str(curclock()), dumper->name,
1156                dp->host->hostname, dp->name);
1157         fflush(stdout);
1158
1159         enqueue_disk(&tapeq, dp);
1160         dp = NULL;
1161
1162         startaflush();
1163         continue_dumps();
1164
1165         break;
1166
1167     case TRYAGAIN: /* TRY-AGAIN <handle> <err str> */
1168     case FATAL_TRYAGAIN:
1169         free_serial(result_argv[2]);
1170
1171         rename_tmp_holding(sched(dp)->destname, 0);
1172         deallocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
1173         assert( h && activehd >= 0 );
1174         holdalloc(h[activehd]->disk)->allocated_dumpers--;
1175         /* Because we don't know how much was written to disk the
1176          * following functions *must* be called together!
1177          */
1178         adjust_diskspace(dp, DONE);
1179         delete_diskspace(dp);
1180         dumper->busy = 0;
1181         dp->host->inprogress -= 1;
1182         dp->inprogress = 0;
1183
1184         if(sched(dp)->attempted) {
1185             log_add(L_FAIL, "%s %s %d %s [too many dumper retry]",
1186                     dp->host->hostname, dp->name,
1187                     sched(dp)->level, sched(dp)->datestamp);
1188             printf("driver: dump failed %s %s %s, too many dumper retry\n", result_argv[2], dp->host->hostname, dp->name);
1189         } else {
1190             sched(dp)->attempted++;
1191             enqueue_disk(&runq, dp);
1192         }
1193         continue_dumps();
1194
1195         if(cmd == FATAL_TRYAGAIN) {
1196             /* dumper is confused, start another */
1197             log_add(L_WARNING, "%s (pid %ld) confused, restarting it.",
1198                     dumper->name, (long)dumper->pid);
1199             FD_CLR(fd,&readset);
1200             aclose(fd);
1201             startup_dump_process(dumper, dumper_program);
1202         }
1203         /* sleep in case the dumper failed because of a temporary network
1204            problem, as NIS or NFS... */
1205         sleep(15);
1206         break;
1207
1208     case FAILED: /* FAILED <handle> <errstr> */
1209         free_serial(result_argv[2]);
1210
1211         rename_tmp_holding(sched(dp)->destname, 0);
1212         deallocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
1213         assert( h && activehd >= 0 );
1214         holdalloc(h[activehd]->disk)->allocated_dumpers--;
1215         /* Because we don't know how much was written to disk the
1216          * following functions *must* be called together!
1217          */
1218         adjust_diskspace(dp, DONE);
1219         delete_diskspace(dp);
1220         dumper->busy = 0;
1221         dp->host->inprogress -= 1;
1222         dp->inprogress = 0;
1223         continue_dumps();
1224
1225         /* no need to log this, dumper will do it */
1226         /* sleep in case the dumper failed because of a temporary network
1227            problem, as NIS or NFS... */
1228         sleep(15);
1229         break;
1230
1231     case NO_ROOM: /* NO-ROOM <handle> <missing_size> */
1232         assert( h && activehd >= 0 );
1233         h[activehd]->used -= atoi(result_argv[3]);
1234         h[activehd]->reserved -= atoi(result_argv[3]);
1235         holdalloc(h[activehd]->disk)->allocated_space -= atoi(result_argv[3]);
1236         h[activehd]->disk->disksize -= atoi(result_argv[3]);
1237         break;
1238
1239     case RQ_MORE_DISK: /* RQ-MORE-DISK <handle> */
1240         assert( h && activehd >= 0 );
1241         holdalloc(h[activehd]->disk)->allocated_dumpers--;
1242         h[activehd]->used = h[activehd]->reserved;
1243         if( h[++activehd] ) { /* There's still some allocated space left. Tell
1244                                * the dumper about it. */
1245             sched(dp)->activehd++;
1246             dumper_cmd( dumper, CONTINUE, dp );
1247         } else { /* !h[++activehd] - must allocate more space */
1248             sched(dp)->act_size = sched(dp)->est_size; /* not quite true */
1249             sched(dp)->est_size = sched(dp)->act_size * 21 / 20; /* +5% */
1250             sched(dp)->est_size = am_round(sched(dp)->est_size, DISK_BLOCK_KB);
1251             h = find_diskspace( sched(dp)->est_size - sched(dp)->act_size,
1252                                 &dummy,
1253                                 h[activehd-1] );
1254             if( !h ) {
1255     /*      cur_idle = max(cur_idle, IDLE_NO_DISKSPACE); */
1256                 /* No diskspace available. The reason for this will be
1257                  * determined in continue_dumps(). */
1258                 enqueue_disk( &roomq, dp );
1259                 continue_dumps();
1260             } else {
1261                 /* OK, allocate space for disk and have dumper continue */
1262                 sched(dp)->activehd = assign_holdingdisk( h, dp );
1263                 dumper_cmd( dumper, CONTINUE, dp );
1264                 amfree(h);
1265             }
1266         }
1267         break;
1268
1269     case ABORT_FINISHED: /* ABORT-FINISHED <handle> */
1270         assert(pending_aborts);
1271         free_serial(result_argv[2]);
1272
1273         rename_tmp_holding(sched(dp)->destname, 0);
1274         deallocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
1275         /* Because we don't know how much was written to disk the
1276          * following functions *must* be called together!
1277          */
1278         adjust_diskspace(dp, DONE);
1279         delete_diskspace(dp);
1280         sched(dp)->attempted++;
1281         enqueue_disk(&runq, dp);        /* we'll try again later */
1282         dumper->busy = 0;
1283         dp->host->inprogress -= 1;
1284         dp->inprogress = 0;
1285         dp = NULL;
1286         pending_aborts--;
1287         continue_dumps();
1288         break;
1289
1290     case BOGUS:
1291         /* either EOF or garbage from dumper.  Turn it off */
1292         log_add(L_WARNING, "%s pid %ld is messed up, ignoring it.\n",
1293                 dumper->name, (long)dumper->pid);
1294         FD_CLR(fd,&readset);
1295         aclose(fd);
1296         dumper->busy = 0;
1297         dumper->down = 1;       /* mark it down so it isn't used again */
1298         if(dp) {
1299             /* if it was dumping something, zap it and try again */
1300             rename_tmp_holding(sched(dp)->destname, 0);
1301             deallocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
1302             assert( h && activehd >= 0 );
1303             holdalloc(h[activehd]->disk)->allocated_dumpers--;
1304             /* Because we don't know how much was written to disk the
1305              * following functions *must* be called together!
1306              */
1307             adjust_diskspace(dp, DONE);
1308             delete_diskspace(dp);
1309             dp->host->inprogress -= 1;
1310             dp->inprogress = 0;
1311             if(sched(dp)->attempted) {
1312                 log_add(L_FAIL, "%s %s %d %s [%s died]",
1313                         dp->host->hostname, dp->name,
1314                         sched(dp)->level, sched(dp)->datestamp, dumper->name);
1315             }
1316             else {
1317                 log_add(L_WARNING, "%s died while dumping %s:%s lev %d.",
1318                         dumper->name, dp->host->hostname, dp->name,
1319                         sched(dp)->level);
1320                 sched(dp)->attempted++;
1321                 enqueue_disk(&runq, dp);
1322             }
1323             dp = NULL;
1324             continue_dumps();
1325         }
1326         break;
1327
1328     default:
1329         assert(0);
1330     }
1331
1332     return;
1333 }
1334
1335
1336 void read_flush(tapeqp)
1337 disklist_t *tapeqp;
1338 {
1339     sched_t *sp;
1340     disk_t *dp;
1341     int line;
1342     dumpfile_t file;
1343     char *hostname, *diskname, *datestamp;
1344     int level;
1345     char *destname;
1346     disk_t *dp1;
1347     char *inpline = NULL;
1348     char *command;
1349     char *s;
1350     int ch;
1351     long flush_size = 0;
1352
1353     /* read schedule from stdin */
1354
1355     for(line = 0; (inpline = agets(stdin)) != NULL; free(inpline)) {
1356         line++;
1357
1358         s = inpline;
1359         ch = *s++;
1360
1361         skip_whitespace(s, ch);                 /* find the command */
1362         if(ch == '\0') {
1363             error("Aflush line %d: syntax error", line);
1364             continue;
1365         }
1366         command = s - 1;
1367         skip_non_whitespace(s, ch);
1368         s[-1] = '\0';
1369
1370         if(strcmp(command,"ENDFLUSH") == 0) {
1371             break;
1372         }
1373
1374         if(strcmp(command,"FLUSH") != 0) {
1375             error("Bflush line %d: syntax error", line);
1376             continue;
1377         }
1378
1379         skip_whitespace(s, ch);                 /* find the hostname */
1380         if(ch == '\0') {
1381             error("Cflush line %d: syntax error", line);
1382             continue;
1383         }
1384         hostname = s - 1;
1385         skip_non_whitespace(s, ch);
1386         s[-1] = '\0';
1387
1388         skip_whitespace(s, ch);                 /* find the diskname */
1389         if(ch == '\0') {
1390             error("Cflush line %d: syntax error", line);
1391             continue;
1392         }
1393         diskname = s - 1;
1394         skip_non_whitespace(s, ch);
1395         s[-1] = '\0';
1396
1397         skip_whitespace(s, ch);                 /* find the datestamp */
1398         if(ch == '\0') {
1399             error("Cflush line %d: syntax error", line);
1400             continue;
1401         }
1402         datestamp = s - 1;
1403         skip_non_whitespace(s, ch);
1404         s[-1] = '\0';
1405
1406         skip_whitespace(s, ch);                 /* find the level number */
1407         if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
1408             error("Cflush line %d: syntax error", line);
1409             continue;
1410         }
1411         skip_integer(s, ch);
1412
1413         skip_whitespace(s, ch);                 /* find the filename */
1414         if(ch == '\0') {
1415             error("Cflush line %d: syntax error", line);
1416             continue;
1417         }
1418         destname = s - 1;
1419         skip_non_whitespace(s, ch);
1420         s[-1] = '\0';
1421
1422         get_dumpfile(destname, &file);
1423         if( file.type != F_DUMPFILE) {
1424             if( file.type != F_CONT_DUMPFILE )
1425                 log_add(L_INFO, "%s: ignoring cruft file.", destname);
1426             continue;
1427         }
1428
1429         if(strcmp(hostname, file.name) != 0 ||
1430            strcmp(diskname, file.disk) != 0 ||
1431            strcmp(datestamp, file.datestamp) != 0) {
1432             log_add(L_INFO, "disk %s:%s not consistent with file %s",
1433                     hostname, diskname, destname);
1434             continue;
1435         }
1436
1437         dp = lookup_disk(file.name, file.disk);
1438
1439         if (dp == NULL) {
1440             log_add(L_INFO, "%s: disk %s:%s not in database, skipping it.",
1441                     destname, file.name, file.disk);
1442             continue;
1443         }
1444
1445         if(file.dumplevel < 0 || file.dumplevel > 9) {
1446             log_add(L_INFO, "%s: ignoring file with bogus dump level %d.",
1447                     destname, file.dumplevel);
1448             continue;
1449         }
1450
1451         dp1 = (disk_t *)alloc(sizeof(disk_t));
1452         *dp1 = *dp;
1453         dp1->next = dp1->prev = NULL;
1454
1455         /* add it to the flushhost list */
1456         if(!flushhost) {
1457             flushhost = alloc(sizeof(am_host_t));
1458             flushhost->next = NULL;
1459             flushhost->hostname = stralloc("FLUSHHOST");
1460             flushhost->up = NULL;
1461             flushhost->features = NULL;
1462         }
1463         dp1->hostnext = flushhost->disks;
1464         flushhost->disks = dp1;
1465
1466         sp = (sched_t *) alloc(sizeof(sched_t));
1467         sp->destname = stralloc(destname);
1468         sp->level = file.dumplevel;
1469         sp->dumpdate = NULL;
1470         sp->degr_dumpdate = NULL;
1471         sp->datestamp = stralloc(file.datestamp);
1472         sp->est_size = 0;
1473         sp->est_time = 0;
1474         sp->priority = 0;
1475         sp->degr_level = -1;
1476         sp->est_kps = 10;
1477         sp->attempted = 0;
1478         sp->act_size = size_holding_files(destname);
1479         /*sp->holdp = NULL; JLM: must be build*/
1480         sp->holdp = build_diskspace(destname);
1481         if(sp->holdp == NULL) continue;
1482         sp->dumper = NULL;
1483         sp->timestamp = (time_t)0;
1484
1485         dp1->up = (char *)sp;
1486
1487         enqueue_disk(tapeqp, dp1);
1488         flush_size += sp->act_size;
1489     }
1490     printf("driver: flush size %ld\n", flush_size);
1491     amfree(inpline);
1492 }
1493
1494
1495 void read_schedule(waitqp, runqp)
1496 disklist_t *waitqp, *runqp;
1497 {
1498     sched_t *sp;
1499     disk_t *dp;
1500     int level, line, priority;
1501     char *dumpdate, *degr_dumpdate;
1502     int degr_level;
1503     long time, degr_time;
1504     unsigned long size, degr_size;
1505     char *hostname, *features, *diskname, *datestamp, *inpline = NULL;
1506     char *command;
1507     char *s;
1508     int ch;
1509
1510     /* read schedule from stdin */
1511
1512     for(line = 0; (inpline = agets(stdin)) != NULL; free(inpline)) {
1513         line++;
1514
1515         s = inpline;
1516         ch = *s++;
1517
1518         skip_whitespace(s, ch);                 /* find the command */
1519         if(ch == '\0') {
1520             error("schedule line %d: syntax error (no command)", line);
1521             continue;
1522         }
1523         command = s - 1;
1524         skip_non_whitespace(s, ch);
1525         s[-1] = '\0';
1526
1527         if(strcmp(command,"DUMP") != 0) {
1528             error("schedule line %d: syntax error (%s != DUMP)", line, command);
1529             continue;
1530         }
1531
1532         skip_whitespace(s, ch);                 /* find the host name */
1533         if(ch == '\0') {
1534             error("schedule line %d: syntax error (no host name)", line);
1535             continue;
1536         }
1537         hostname = s - 1;
1538         skip_non_whitespace(s, ch);
1539         s[-1] = '\0';
1540
1541         skip_whitespace(s, ch);                 /* find the feature list */
1542         if(ch == '\0') {
1543             error("schedule line %d: syntax error (no feature list)", line);
1544             continue;
1545         }
1546         features = s - 1;
1547         skip_non_whitespace(s, ch);
1548         s[-1] = '\0';
1549
1550         skip_whitespace(s, ch);                 /* find the disk name */
1551         if(ch == '\0') {
1552             error("schedule line %d: syntax error (no disk name)", line);
1553             continue;
1554         }
1555         diskname = s - 1;
1556         skip_non_whitespace(s, ch);
1557         s[-1] = '\0';
1558
1559         skip_whitespace(s, ch);                 /* find the datestamp */
1560         if(ch == '\0') {
1561             error("schedule line %d: syntax error (no datestamp)", line);
1562             continue;
1563         }
1564         datestamp = s - 1;
1565         skip_non_whitespace(s, ch);
1566         s[-1] = '\0';
1567
1568         skip_whitespace(s, ch);                 /* find the priority number */
1569         if(ch == '\0' || sscanf(s - 1, "%d", &priority) != 1) {
1570             error("schedule line %d: syntax error (bad priority)", line);
1571             continue;
1572         }
1573         skip_integer(s, ch);
1574
1575         skip_whitespace(s, ch);                 /* find the level number */
1576         if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
1577             error("schedule line %d: syntax error (bad level)", line);
1578             continue;
1579         }
1580         skip_integer(s, ch);
1581
1582         skip_whitespace(s, ch);                 /* find the dump date */
1583         if(ch == '\0') {
1584             error("schedule line %d: syntax error (bad dump date)", line);
1585             continue;
1586         }
1587         dumpdate = s - 1;
1588         skip_non_whitespace(s, ch);
1589         s[-1] = '\0';
1590
1591         skip_whitespace(s, ch);                 /* find the size number */
1592         if(ch == '\0' || sscanf(s - 1, "%lu", &size) != 1) {
1593             error("schedule line %d: syntax error (bad size)", line);
1594             continue;
1595         }
1596         skip_integer(s, ch);
1597
1598         skip_whitespace(s, ch);                 /* find the time number */
1599         if(ch == '\0' || sscanf(s - 1, "%ld", &time) != 1) {
1600             error("schedule line %d: syntax error (bad estimated time)", line);
1601             continue;
1602         }
1603         skip_integer(s, ch);
1604
1605         degr_dumpdate = NULL;                   /* flag if degr fields found */
1606         skip_whitespace(s, ch);                 /* find the degr level number */
1607         if(ch != '\0') {
1608             if(sscanf(s - 1, "%d", &degr_level) != 1) {
1609                 error("schedule line %d: syntax error (bad degr level)", line);
1610                 continue;
1611             }
1612             skip_integer(s, ch);
1613
1614             skip_whitespace(s, ch);             /* find the degr dump date */
1615             if(ch == '\0') {
1616                 error("schedule line %d: syntax error (bad degr dump date)", line);
1617                 continue;
1618             }
1619             degr_dumpdate = s - 1;
1620             skip_non_whitespace(s, ch);
1621             s[-1] = '\0';
1622
1623             skip_whitespace(s, ch);             /* find the degr size number */
1624             if(ch == '\0'  || sscanf(s - 1, "%lu", &degr_size) != 1) {
1625                 error("schedule line %d: syntax error (bad degr size)", line);
1626                 continue;
1627             }
1628             skip_integer(s, ch);
1629
1630             skip_whitespace(s, ch);             /* find the degr time number */
1631             if(ch == '\0' || sscanf(s - 1, "%lu", &degr_time) != 1) {
1632                 error("schedule line %d: syntax error (bad degr estimated time)", line);
1633                 continue;
1634             }
1635             skip_integer(s, ch);
1636         }
1637
1638         dp = lookup_disk(hostname, diskname);
1639         if(dp == NULL) {
1640             log_add(L_WARNING,
1641                     "schedule line %d: %s:%s not in disklist, ignored",
1642                     line, hostname, diskname);
1643             continue;
1644         }
1645
1646         sp = (sched_t *) alloc(sizeof(sched_t));
1647         sp->level    = level;
1648         sp->dumpdate = stralloc(dumpdate);
1649         sp->est_size = DISK_BLOCK_KB + size; /* include header */
1650         sp->est_time = time;
1651         sp->priority = priority;
1652         sp->datestamp = stralloc(datestamp);
1653
1654         if(degr_dumpdate) {
1655             sp->degr_level = degr_level;
1656             sp->degr_dumpdate = stralloc(degr_dumpdate);
1657             sp->degr_size = DISK_BLOCK_KB + degr_size;
1658             sp->degr_time = degr_time;
1659         } else {
1660             sp->degr_level = -1;
1661             sp->degr_dumpdate = NULL;
1662         }
1663
1664         if(time <= 0)
1665             sp->est_kps = 10;
1666         else
1667             sp->est_kps = size/time;
1668
1669         if(sp->degr_level != -1) {
1670             if(degr_time <= 0)
1671                 sp->degr_kps = 10;
1672             else
1673                 sp->degr_kps = degr_size/degr_time;
1674         }
1675
1676         sp->attempted = 0;
1677         sp->act_size = 0;
1678         sp->holdp = NULL;
1679         sp->activehd = -1;
1680         sp->dumper = NULL;
1681         sp->timestamp = (time_t)0;
1682         sp->destname = NULL;
1683         sp->no_space = 0;
1684
1685         dp->up = (char *) sp;
1686         if(dp->host->features == NULL) {
1687             dp->host->features = am_string_to_feature(features);
1688         }
1689         remove_disk(waitqp, dp);
1690         insert_disk(&runq, dp, sort_by_time);
1691     }
1692     amfree(inpline);
1693     if(line == 0)
1694         log_add(L_WARNING, "WARNING: got empty schedule from planner");
1695 }
1696
1697 int free_kps(ip)
1698 interface_t *ip;
1699 {
1700     int res;
1701
1702     if (ip == (interface_t *)0) {
1703         interface_t *p;
1704         int maxusage=0;
1705         int curusage=0;
1706         for(p = lookup_interface(NULL); p != NULL; p = p->next) {
1707             maxusage += p->maxusage;
1708             curusage += p->curusage;
1709         }
1710         res = maxusage - curusage;
1711     }
1712     else {
1713         res = ip->maxusage - ip->curusage;
1714     }
1715
1716     return res;
1717 }
1718
1719 void interface_state(time_str)
1720 char *time_str;
1721 {
1722     interface_t *ip;
1723
1724     printf("driver: interface-state time %s", time_str);
1725
1726     for(ip = lookup_interface(NULL); ip != NULL; ip = ip->next) {
1727         printf(" if %s: free %d", ip->name, free_kps(ip));
1728     }
1729     printf("\n");
1730 }
1731
1732 void allocate_bandwidth(ip, kps)
1733 interface_t *ip;
1734 int kps;
1735 {
1736     ip->curusage += kps;
1737 }
1738
1739 void deallocate_bandwidth(ip, kps)
1740 interface_t *ip;
1741 int kps;
1742 {
1743     assert(kps <= ip->curusage);
1744     ip->curusage -= kps;
1745 }
1746
1747 /* ------------ */
1748 unsigned long free_space()
1749 {
1750     holdingdisk_t *hdp;
1751     unsigned long total_free;
1752     long diff;
1753
1754     total_free = 0L;
1755     for(hdp = getconf_holdingdisks(); hdp != NULL; hdp = hdp->next) {
1756         diff = hdp->disksize - holdalloc(hdp)->allocated_space;
1757         if(diff > 0)
1758             total_free += diff;
1759     }
1760     return total_free;
1761 }
1762
1763 assignedhd_t **find_diskspace(size, cur_idle, pref)
1764 unsigned long size;
1765 int *cur_idle;
1766 assignedhd_t *pref;
1767 /* Rewrite by Peter Conrad <conrad@opus5.de>, June '99:
1768  *  - enable splitting a dump across several holding disks
1769  *  - allocate only as much as size tells us, dumpers may request more later
1770  * We return an array of pointers to assignedhd_t. The array contains at
1771  * most one entry per holding disk. The list of pointers is terminated by
1772  * a NULL pointer. Each entry contains a pointer to a holdingdisk and
1773  * how much diskspace to use on that disk. Later on, assign_holdingdisk
1774  * will allocate the given amount of space.
1775  * If there is not enough room on the holdingdisks, NULL is returned.
1776  */
1777 {
1778 assignedhd_t **result = NULL;
1779     holdingdisk_t *minp, *hdp;
1780     int i=0, num_holdingdisks=0; /* are we allowed to use the global thing? */
1781     int j, minj;
1782     char *used;
1783     long halloc, dalloc, hfree, dfree;
1784
1785     size = am_round(size, DISK_BLOCK_KB);
1786
1787 #ifdef HOLD_DEBUG
1788     printf("find diskspace: want %lu K\n", size );
1789     fflush(stdout);
1790 #endif
1791
1792     for(hdp = getconf_holdingdisks(); hdp != NULL; hdp = hdp->next) {
1793         num_holdingdisks++;
1794     }
1795
1796     used = alloc(sizeof(char) * num_holdingdisks);/*disks used during this run*/
1797     memset( used, 0, num_holdingdisks );
1798     result = alloc( sizeof(assignedhd_t *) * (num_holdingdisks+1) );
1799     result[0] = NULL;
1800
1801     while( i < num_holdingdisks && size > 0 ) {
1802         /* find the holdingdisk with the fewest active dumpers and among
1803          * those the one with the biggest free space
1804          */
1805         minp = NULL; minj = -1;
1806         for(j = 0, hdp = getconf_holdingdisks(); hdp != NULL; hdp = hdp->next, j++ ) {
1807             if( pref && pref->disk == hdp && !used[j] &&
1808                 holdalloc(hdp)->allocated_space <= hdp->disksize - DISK_BLOCK_KB) {
1809                 minp = hdp;
1810                 minj = j;
1811                 break;
1812             }
1813             else if( holdalloc(hdp)->allocated_space <= hdp->disksize - 2*DISK_BLOCK_KB &&
1814                 !used[j] && 
1815                 (!minp ||
1816                  holdalloc(hdp)->allocated_dumpers < holdalloc(minp)->allocated_dumpers ||
1817                  (holdalloc(hdp)->allocated_dumpers == holdalloc(minp)->allocated_dumpers &&
1818                   hdp->disksize-holdalloc(hdp)->allocated_space > minp->disksize-holdalloc(minp)->allocated_space)) ) {
1819                 minp = hdp;
1820                 minj = j;
1821             }
1822         }
1823         pref = NULL;
1824         if( !minp ) { break; } /* all holding disks are full */
1825         used[minj] = 1;
1826
1827         /* hfree = free space on the disk */
1828         hfree = minp->disksize - holdalloc(minp)->allocated_space;
1829
1830         /* dfree = free space for data, remove 1 header for each chunksize */
1831         dfree = hfree - (((hfree-1)/minp->chunksize)+1) * DISK_BLOCK_KB;
1832
1833         /* dalloc = space I can allocate for data */
1834         dalloc = ( dfree < size ) ? dfree : size;
1835
1836         /* halloc = space to allocate, including 1 header for each chunksize */
1837         halloc = dalloc + (((dalloc-1)/minp->chunksize)+1) * DISK_BLOCK_KB;
1838
1839 #ifdef HOLD_DEBUG
1840         fprintf(stdout,"find diskspace: size %ld hf %ld df %ld da %ld ha %ld\n",                size, hfree, dfree, dalloc, halloc);
1841         fflush(stdout);
1842 #endif
1843         size -= dalloc;
1844         result[i] = alloc(sizeof(assignedhd_t));
1845         result[i]->disk = minp;
1846         result[i]->reserved = halloc;
1847         result[i]->used = 0;
1848         result[i]->destname = NULL;
1849         result[i+1] = NULL;
1850         i++;
1851     } /* while i < num_holdingdisks && size > 0 */
1852     amfree(used);
1853
1854     if( size ) { /* not enough space available */
1855 #ifdef HOLD_DEBUG
1856         printf("find diskspace: not enough diskspace. Left with %lu K\n", size);
1857         fflush(stdout);
1858 #endif
1859         free_assignedhd(result);
1860         result = NULL;
1861     }
1862
1863 #ifdef HOLD_DEBUG
1864     for( i = 0; result && result[i]; i++ ) {
1865     printf("find diskspace: selected %s free %ld reserved %ld dumpers %d\n",
1866            result[i]->disk->diskdir,
1867            result[i]->disk->disksize - holdalloc(result[i]->disk)->allocated_space,
1868            result[i]->reserved,
1869            holdalloc(result[i]->disk)->allocated_dumpers);
1870     }
1871     fflush(stdout);
1872 #endif
1873
1874     return result;
1875 }
1876
1877 int assign_holdingdisk(holdp, diskp)
1878 assignedhd_t **holdp;
1879 disk_t *diskp;
1880 {
1881 /* Modified by Peter Conrad <conrad@opus5.de>, June '99
1882  * Modifications for splitting dumps across holding disks:
1883  * sched(diskp)->holdp now contains an array of pointers to assignedhd_t.
1884  */
1885     int i, j, c, l=0;
1886     unsigned long size;
1887     char *sfn = sanitise_filename(diskp->name);
1888     char lvl[64];
1889     assignedhd_t **new_holdp;
1890
1891     ap_snprintf( lvl, sizeof(lvl), "%d", sched(diskp)->level );
1892
1893     size = am_round(sched(diskp)->est_size - sched(diskp)->act_size,
1894                     DISK_BLOCK_KB);
1895
1896     for( c = 0; holdp[c]; c++ ); /* count number of disks */
1897
1898     /* allocate memory for sched(diskp)->holdp */
1899     for(j = 0; sched(diskp)->holdp && sched(diskp)->holdp[j]; j++) {}
1900     new_holdp = (assignedhd_t **)alloc(sizeof(assignedhd_t*)*(j+c+1));
1901     if (sched(diskp)->holdp) {
1902         memcpy(new_holdp, sched(diskp)->holdp, j * sizeof(*new_holdp));
1903         amfree(sched(diskp)->holdp);
1904     }
1905     sched(diskp)->holdp = new_holdp;
1906     new_holdp = NULL;
1907
1908     i = 0;
1909     if( j > 0 ) { /* This is a request for additional diskspace. See if we can
1910                    * merge assignedhd_t's */
1911         l=j;
1912         if( sched(diskp)->holdp[j-1]->disk == holdp[0]->disk ) { /* Yes! */
1913             sched(diskp)->holdp[j-1]->reserved += holdp[0]->reserved;
1914             holdalloc(holdp[0]->disk)->allocated_space += holdp[0]->reserved;
1915             size = (holdp[0]->reserved>size) ? 0 : size-holdp[0]->reserved;
1916 #ifdef HOLD_DEBUG
1917             printf("merging holding disk %s to disk %s:%s, add %lu for reserved %lu, left %lu\n",
1918                    sched(diskp)->holdp[j-1]->disk->diskdir,
1919                    diskp->host->hostname, diskp->name,
1920                    holdp[0]->reserved, sched(diskp)->holdp[j-1]->reserved,
1921                    size );
1922             fflush(stdout);
1923 #endif
1924             i++;
1925             amfree(holdp[0]);
1926             l=j-1;
1927         }
1928     }
1929
1930     /* copy assignedhd_s to sched(diskp), adjust allocated_space */
1931     for( ; holdp[i]; i++ ) {
1932         holdp[i]->destname = newvstralloc( holdp[i]->destname,
1933                                            holdp[i]->disk->diskdir, "/",
1934                                            timestamp, "/",
1935                                            diskp->host->hostname, ".",
1936                                            sfn, ".",
1937                                            lvl, NULL );
1938         sched(diskp)->holdp[j++] = holdp[i];
1939         holdalloc(holdp[i]->disk)->allocated_space += holdp[i]->reserved;
1940         size = (holdp[i]->reserved>size) ? 0 : size-holdp[i]->reserved;
1941 #ifdef HOLD_DEBUG
1942         printf("assigning holding disk %s to disk %s:%s, reserved %lu, left %lu\n",
1943                 holdp[i]->disk->diskdir, diskp->host->hostname, diskp->name,
1944                 holdp[i]->reserved, size );
1945         fflush(stdout);
1946 #endif
1947         holdp[i] = NULL; /* so it doesn't get free()d... */
1948     }
1949     sched(diskp)->holdp[j] = NULL;
1950     sched(diskp)->destname = newstralloc(sched(diskp)->destname,sched(diskp)->holdp[0]->destname);
1951     amfree(sfn);
1952
1953     return l;
1954 }
1955
1956 static void adjust_diskspace(diskp, cmd)
1957 disk_t *diskp;
1958 cmd_t cmd;
1959 {
1960 /* Re-write by Peter Conrad <conrad@opus5.de>, March '99
1961  * Modifications for splitting dumps across holding disks:
1962  * Dumpers no longer write more than they've allocated, therefore an
1963  * adjustment may only free some allocated space.
1964  * 08/99: Jean-Louis suggested that dumpers tell us how much they've written.
1965  * We just believe them and don't stat all the files but rely on the used
1966  * field.
1967  */
1968
1969     assignedhd_t **holdp;
1970     unsigned long total=0;
1971     long diff;
1972     int i;
1973
1974 #ifdef HOLD_DEBUG
1975     printf("adjust: %s:%s %s\n", diskp->host->hostname, diskp->name,
1976            sched(diskp)->destname );
1977     fflush(stdout);
1978 #endif
1979
1980     holdp = sched(diskp)->holdp;
1981
1982     assert(holdp);
1983
1984     for( i = 0; holdp[i]; i++ ) { /* for each allocated disk */
1985         diff = holdp[i]->used - holdp[i]->reserved;
1986         total += holdp[i]->used;
1987         holdalloc(holdp[i]->disk)->allocated_space += diff;
1988 #ifdef HOLD_DEBUG
1989         printf("adjust: hdisk %s done, reserved %ld used %ld diff %ld alloc %ld dumpers %d\n",
1990                 holdp[i]->disk->name, holdp[i]->reserved, holdp[i]->used, diff,
1991                 holdalloc(holdp[i]->disk)->allocated_space,
1992                 holdalloc(holdp[i]->disk)->allocated_dumpers );
1993                 fflush(stdout);
1994 #endif
1995         holdp[i]->reserved += diff;
1996     }
1997
1998     sched(diskp)->act_size = total;
1999 #ifdef HOLD_DEBUG
2000     printf("adjust: after: disk %s:%s used %ld\n", diskp->host->hostname,
2001            diskp->name, sched(diskp)->act_size );
2002     fflush(stdout);
2003 #endif
2004 }
2005
2006 static void delete_diskspace(diskp)
2007 disk_t *diskp;
2008 {
2009 /* Re-write by Peter Conrad <conrad@opus5.de>, March '99
2010  * Modifications for splitting dumps across holding disks:
2011  * After implementing Jean-Louis' suggestion (see above) this looks much
2012  * simpler... again, we rely on assignedhd_s containing correct info
2013  */
2014     assignedhd_t **holdp;
2015     int i;
2016
2017     holdp = sched(diskp)->holdp;
2018
2019     assert(holdp);
2020
2021     for( i = 0; holdp[i]; i++ ) { /* for each disk */
2022         /* find all files of this dump on that disk, and subtract their
2023          * reserved sizes from the disk's allocated space
2024          */
2025         holdalloc(holdp[i]->disk)->allocated_space -= holdp[i]->used;
2026     }
2027
2028     unlink_holding_files(holdp[0]->destname); /* no need for the entire list, 
2029                                                  because unlink_holding_files
2030                                                  will walk through all files
2031                                                  using cont_filename */
2032
2033     free_assignedhd(sched(diskp)->holdp);
2034     sched(diskp)->holdp = NULL;
2035     sched(diskp)->act_size = 0;
2036     amfree(sched(diskp)->destname);
2037 }
2038
2039 assignedhd_t **build_diskspace(destname)
2040 char *destname;
2041 {
2042     int i, j;
2043     int fd;
2044     int buflen;
2045     char buffer[DISK_BLOCK_BYTES];
2046     dumpfile_t file;
2047     assignedhd_t **result;
2048     holdingdisk_t *hdp;
2049     int *used;
2050     int num_holdingdisks=0;
2051     char dirname[1000], *ch;
2052     struct stat finfo;
2053     char *filename = destname;
2054
2055     for(hdp = getconf_holdingdisks(); hdp != NULL; hdp = hdp->next) {
2056         num_holdingdisks++;
2057     }
2058     used = alloc(sizeof(int) * num_holdingdisks);
2059     for(i=0;i<num_holdingdisks;i++)
2060         used[i] = 0;
2061     result = alloc( sizeof(assignedhd_t *) * (num_holdingdisks+1) );
2062     result[0] = NULL;
2063     while(filename != NULL && filename[0] != '\0') {
2064         strncpy(dirname, filename, 999);
2065         dirname[999]='\0';
2066         ch = strrchr(dirname,'/');
2067         *ch = '\0';
2068         ch = strrchr(dirname,'/');
2069         *ch = '\0';
2070
2071         for(j = 0, hdp = getconf_holdingdisks(); hdp != NULL;
2072                                                  hdp = hdp->next, j++ ) {
2073             if(strcmp(dirname,hdp->diskdir)==0) {
2074                 break;
2075             }
2076         }
2077
2078         if(stat(filename, &finfo) == -1) {
2079             fprintf(stderr, "stat %s: %s\n", filename, strerror(errno));
2080             finfo.st_size = 0;
2081         }
2082         used[j] += (finfo.st_size+1023)/1024;
2083         if((fd = open(filename,O_RDONLY)) == -1) {
2084             fprintf(stderr,"build_diskspace: open of %s failed: %s\n",
2085                     filename, strerror(errno));
2086             return NULL;
2087         }
2088         buflen = fullread(fd, buffer, sizeof(buffer));
2089         parse_file_header(buffer, &file, buflen);
2090         close(fd);
2091         filename = file.cont_filename;
2092     }
2093
2094     for(j = 0, i=0, hdp = getconf_holdingdisks(); hdp != NULL;
2095                                                   hdp = hdp->next, j++ ) {
2096         if(used[j]) {
2097             result[i] = alloc(sizeof(assignedhd_t));
2098             result[i]->disk = hdp;
2099             result[i]->reserved = used[j];
2100             result[i]->used = used[j];
2101             result[i]->destname = stralloc(destname);
2102             result[i+1] = NULL;
2103             i++;
2104         }
2105     }
2106
2107     amfree(used);
2108     return result;
2109 }
2110
2111
2112 void holdingdisk_state(time_str)
2113 char *time_str;
2114 {
2115     holdingdisk_t *hdp;
2116     int dsk;
2117     long diff;
2118
2119     printf("driver: hdisk-state time %s", time_str);
2120
2121     for(hdp = getconf_holdingdisks(), dsk = 0; hdp != NULL; hdp = hdp->next, dsk++) {
2122         diff = hdp->disksize - holdalloc(hdp)->allocated_space;
2123         printf(" hdisk %d: free %ld dumpers %d", dsk, diff,
2124                holdalloc(hdp)->allocated_dumpers);
2125     }
2126     printf("\n");
2127 }
2128
2129 static void update_failed_dump_to_tape(dp)
2130 disk_t *dp;
2131 {
2132     time_t save_timestamp = sched(dp)->timestamp;
2133     /* setting timestamp to 0 removes the current level from the
2134      * database, so that we ensure that it will not be bumped to the
2135      * next level on the next run.  If we didn't do this, dumpdates or
2136      * gnutar-lists might have been updated already, and a bumped
2137      * incremental might be created.  */
2138     sched(dp)->timestamp = 0;
2139     update_info_dumper(dp, -1, -1, -1);
2140     sched(dp)->timestamp = save_timestamp;
2141 }
2142
2143 /* ------------------- */
2144 int dump_to_tape(dp)
2145      disk_t *dp;
2146 {
2147     dumper_t *dumper;
2148     int failed = 0;
2149     int filenum;
2150     long origsize = 0;
2151     long dumpsize = 0;
2152     long dumptime = 0;
2153     cmd_t cmd;
2154     int result_argc;
2155     char *result_argv[MAX_ARGS+1];
2156     int dumper_tryagain = 0;
2157
2158     inside_dump_to_tape = 1;    /* for simulator */
2159
2160     printf("driver: dumping %s:%s directly to tape\n",
2161            dp->host->hostname, dp->name);
2162     fflush(stdout);
2163
2164     /* pick a dumper and fail if there are no idle dumpers */
2165
2166     dumper = idle_dumper();
2167     if (!dumper) {
2168         printf("driver: no idle dumpers for %s:%s.\n", 
2169                 dp->host->hostname, dp->name);
2170         fflush(stdout);
2171         log_add(L_WARNING, "no idle dumpers for %s:%s.\n",
2172                 dp->host->hostname, dp->name);
2173         inside_dump_to_tape = 0;
2174         return 2;       /* fatal problem */
2175     }
2176
2177     /* tell the taper to read from a port number of its choice */
2178
2179     taper_cmd(PORT_WRITE, dp, NULL, sched(dp)->level, sched(dp)->datestamp);
2180     cmd = getresult(taper, 1, &result_argc, result_argv, MAX_ARGS+1);
2181     if(cmd != PORT) {
2182         printf("driver: did not get PORT from taper for %s:%s\n",
2183                 dp->host->hostname, dp->name);
2184         fflush(stdout);
2185         inside_dump_to_tape = 0;
2186         return 2;       /* fatal problem */
2187     }
2188     /* copy port number */
2189     sched(dp)->destname = newvstralloc(sched(dp)->destname, result_argv[2], NULL );
2190
2191     /* tell the dumper to dump to a port */
2192
2193     dumper_cmd(dumper, PORT_DUMP, dp);
2194     dp->host->start_t = time(NULL) + 15;
2195
2196     /* update statistics & print state */
2197
2198     taper_busy = dumper->busy = 1;
2199     dp->host->inprogress += 1;
2200     dp->inprogress = 1;
2201     sched(dp)->timestamp = time((time_t *)0);
2202     allocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
2203     idle_reason = 0;
2204
2205     short_dump_state();
2206
2207     /* wait for result from dumper */
2208
2209     cmd = getresult(dumper->outfd, 1, &result_argc, result_argv, MAX_ARGS+1);
2210
2211     if(cmd != BOGUS)
2212         free_serial(result_argv[2]);
2213
2214     switch(cmd) {
2215     case BOGUS:
2216         /* either eof or garbage from dumper */
2217         log_add(L_WARNING, "%s pid %ld is messed up, ignoring it.\n",
2218                 dumper->name, (long)dumper->pid);
2219         dumper->down = 1;       /* mark it down so it isn't used again */
2220         failed = 1;     /* dump failed, must still finish up with taper */
2221         break;
2222
2223     case DONE: /* DONE <handle> <origsize> <dumpsize> <dumptime> <err str> */
2224         /* everything went fine */
2225         origsize = (long)atof(result_argv[3]);
2226         dumpsize = (long)atof(result_argv[4]);
2227         dumptime = (long)atof(result_argv[5]);
2228         break;
2229
2230     case NO_ROOM: /* NO-ROOM <handle> */
2231         dumper_cmd(dumper, ABORT, dp);
2232         cmd = getresult(dumper->outfd, 1, &result_argc, result_argv, MAX_ARGS+1);
2233         if(cmd != BOGUS)
2234             free_serial(result_argv[2]);
2235         assert(cmd == ABORT_FINISHED);
2236
2237     case TRYAGAIN: /* TRY-AGAIN <handle> <err str> */
2238     default:
2239         /* dump failed, but we must still finish up with taper */
2240         /* problem with dump, possibly nonfatal, retry one time */
2241         sched(dp)->attempted++;
2242         failed = sched(dp)->attempted;
2243         dumper_tryagain = 1;
2244         break;
2245         
2246     case FAILED: /* FAILED <handle> <errstr> */
2247         /* dump failed, but we must still finish up with taper */
2248         failed = 2;     /* fatal problem with dump */
2249         break;
2250     }
2251
2252     /*
2253      * Note that at this point, even if the dump above failed, it may
2254      * not be a fatal failure if taper below says we can try again.
2255      * E.g. a dumper failure above may actually be the result of a
2256      * tape overflow, which in turn causes dump to see "broken pipe",
2257      * "no space on device", etc., since taper closed the port first.
2258      */
2259
2260     cmd = getresult(taper, 1, &result_argc, result_argv, MAX_ARGS+1);
2261
2262     switch(cmd) {
2263     case DONE: /* DONE <handle> <label> <tape file> <err mess> */
2264         if(result_argc != 5) {
2265             error("error [dump to tape DONE result_argc != 5: %d]", result_argc);
2266         }
2267
2268         if(failed == 1) goto tryagain;  /* dump didn't work */
2269         else if(failed == 2) goto failed_dumper;
2270
2271         free_serial(result_argv[2]);
2272
2273         /* every thing went fine */
2274         update_info_dumper(dp, origsize, dumpsize, dumptime);
2275         filenum = atoi(result_argv[4]);
2276         update_info_taper(dp, result_argv[3], filenum, sched(dp)->level);
2277         /* note that update_info_dumper() must be run before
2278            update_info_taper(), since update_info_dumper overwrites
2279            tape information.  */
2280
2281         break;
2282
2283     case TRYAGAIN: /* TRY-AGAIN <handle> <err mess> */
2284         if(dumper_tryagain == 0) {
2285             sched(dp)->attempted++;
2286             if(sched(dp)->attempted > failed)
2287                 failed = sched(dp)->attempted;
2288         }
2289     tryagain:
2290         if(failed <= 1)
2291             headqueue_disk(&runq, dp);
2292     failed_dumper:
2293         update_failed_dump_to_tape(dp);
2294         free_serial(result_argv[2]);
2295         tape_left = tape_length;
2296         break;
2297
2298
2299     case TAPE_ERROR: /* TAPE-ERROR <handle> <err mess> */
2300     case BOGUS:
2301     default:
2302         update_failed_dump_to_tape(dp);
2303         free_serial(result_argv[2]);
2304         failed = 2;     /* fatal problem */
2305         start_degraded_mode(&runq);
2306     }
2307
2308     /* reset statistics & return */
2309
2310     taper_busy = dumper->busy = 0;
2311     dp->host->inprogress -= 1;
2312     dp->inprogress = 0;
2313     deallocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
2314
2315     inside_dump_to_tape = 0;
2316     return failed;
2317 }
2318
2319 int queue_length(q)
2320 disklist_t q;
2321 {
2322     disk_t *p;
2323     int len;
2324
2325     for(len = 0, p = q.head; p != NULL; len++, p = p->next);
2326     return len;
2327 }
2328
2329
2330 void short_dump_state()
2331 {
2332     int i, nidle;
2333     char *wall_time;
2334
2335     wall_time = walltime_str(curclock());
2336
2337     printf("driver: state time %s ", wall_time);
2338     printf("free kps: %d space: %lu taper: ",
2339            free_kps((interface_t *)0), free_space());
2340     if(degraded_mode) printf("DOWN");
2341     else if(!taper_busy) printf("idle");
2342     else printf("writing");
2343     nidle = 0;
2344     for(i = 0; i < inparallel; i++) if(!dmptable[i].busy) nidle++;
2345     printf(" idle-dumpers: %d", nidle);
2346     printf(" qlen tapeq: %d", queue_length(tapeq));
2347     printf(" runq: %d", queue_length(runq));
2348     printf(" roomq: %d", queue_length(roomq));
2349     printf(" wakeup: %d", (int)sleep_time.tv_sec);
2350     printf(" driver-idle: %s\n", idle_strings[idle_reason]);
2351     interface_state(wall_time);
2352     holdingdisk_state(wall_time);
2353     fflush(stdout);
2354 }
2355
2356 void dump_state(str)
2357 char *str;
2358 {
2359     int i;
2360     disk_t *dp;
2361
2362     printf("================\n");
2363     printf("driver state at time %s: %s\n", walltime_str(curclock()), str);
2364     printf("free kps: %d, space: %lu\n", free_kps((interface_t *)0), free_space());
2365     if(degraded_mode) printf("taper: DOWN\n");
2366     else if(!taper_busy) printf("taper: idle\n");
2367     else printf("taper: writing %s:%s.%d est size %lu\n",
2368                 taper_disk->host->hostname, taper_disk->name,
2369                 sched(taper_disk)->level,
2370                 sched(taper_disk)->est_size);
2371     for(i = 0; i < inparallel; i++) {
2372         dp = dmptable[i].dp;
2373         if(!dmptable[i].busy)
2374           printf("%s: idle\n", dmptable[i].name);
2375         else
2376           printf("%s: dumping %s:%s.%d est kps %d size %lu time %ld\n",
2377                 dmptable[i].name, dp->host->hostname, dp->name, sched(dp)->level,
2378                 sched(dp)->est_kps, sched(dp)->est_size, sched(dp)->est_time);
2379     }
2380     dump_queue("TAPE", tapeq, 5, stdout);
2381     dump_queue("ROOM", roomq, 5, stdout);
2382     dump_queue("RUN ", runq, 5, stdout);
2383     printf("================\n");
2384     fflush(stdout);
2385 }