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