Imported Upstream version 2.6.0p1
[debian/amanda] / server-src / driver.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 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 6512 2007-05-24 17:00:24Z ian $
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 "event.h"
42 #include "holding.h"
43 #include "infofile.h"
44 #include "logfile.h"
45 #include "fsusage.h"
46 #include "version.h"
47 #include "driverio.h"
48 #include "server_util.h"
49 #include "timestamp.h"
50
51 #define driver_debug(i, ...) do {       \
52         if ((i) <= debug_driver) {      \
53             dbprintf(__VA_ARGS__);      \
54         }                               \
55 } while (0)
56
57 #define hold_debug(i, ...) do {         \
58         if ((i) <= debug_holding) {     \
59             dbprintf(__VA_ARGS__);      \
60         }                               \
61 } while (0)
62
63 static disklist_t waitq;        // dle waiting estimate result
64 static disklist_t runq;         // dle waiting to be dumped to holding disk
65 static disklist_t directq;      // dle waiting to be dumped directly to tape
66 static disklist_t tapeq;        // dle on holding disk waiting to be written
67                                 //   to tape
68 static disklist_t roomq;        // dle waiting for more space on holding disk
69 static int pending_aborts;
70 static disk_t *taper_disk;
71 static int degraded_mode;
72 static off_t reserved_space;
73 static off_t total_disksize;
74 static char *dumper_program;
75 static char *chunker_program;
76 static int  inparallel;
77 static int nodump = 0;
78 static off_t tape_length = (off_t)0;
79 static off_t tape_left = (off_t)0;
80 static int current_tape = 0;
81 static int conf_taperalgo;
82 static int conf_runtapes;
83 static time_t sleep_time;
84 static int idle_reason;
85 static char *driver_timestamp;
86 static char *hd_driver_timestamp;
87 static am_host_t *flushhost = NULL;
88 static int need_degraded=0;
89 static holdalloc_t *holdalloc;
90 static int num_holdalloc;
91 static event_handle_t *dumpers_ev_time = NULL;
92 static event_handle_t *schedule_ev_read = NULL;
93 static int   conf_flush_threshold_dumped;
94 static int   conf_flush_threshold_scheduled;
95 static int   conf_taperflush;
96 static off_t flush_threshold_dumped;
97 static off_t flush_threshold_scheduled;
98 static off_t taperflush;
99 static int   schedule_done;                     // 1 if we don't wait for a
100                                                 //   schedule from the planner
101 static int   force_flush;                       // All dump are terminated, we
102                                                 // must now respect taper_flush
103
104 static int wait_children(int count);
105 static void wait_for_children(void);
106 static void allocate_bandwidth(netif_t *ip, unsigned long kps);
107 static int assign_holdingdisk(assignedhd_t **holdp, disk_t *diskp);
108 static void adjust_diskspace(disk_t *diskp, cmd_t cmd);
109 static void delete_diskspace(disk_t *diskp);
110 static assignedhd_t **build_diskspace(char *destname);
111 static int client_constrained(disk_t *dp);
112 static void deallocate_bandwidth(netif_t *ip, unsigned long kps);
113 static void dump_schedule(disklist_t *qp, char *str);
114 static void dump_to_tape(disk_t *dp);
115 static assignedhd_t **find_diskspace(off_t size, int *cur_idle,
116                                         assignedhd_t *preferred);
117 static unsigned long free_kps(netif_t *ip);
118 static off_t free_space(void);
119 static void dumper_chunker_result(disk_t *dp);
120 static void dumper_taper_result(disk_t *dp);
121 static void file_taper_result(disk_t *dp);
122 static void handle_dumper_result(void *);
123 static void handle_chunker_result(void *);
124 static void handle_dumpers_time(void *);
125 static void handle_taper_result(void *);
126
127 static void holdingdisk_state(char *time_str);
128 static dumper_t *idle_dumper(void);
129 static void interface_state(char *time_str);
130 static int queue_length(disklist_t q);
131 static disklist_t read_flush(void);
132 static void read_schedule(void *cookie);
133 static void short_dump_state(void);
134 static void startaflush(void);
135 static void start_degraded_mode(disklist_t *queuep);
136 static void start_some_dumps(disklist_t *rq);
137 static void continue_port_dumps(void);
138 static void update_failed_dump_to_tape(disk_t *);
139
140 typedef enum {
141     TAPE_ACTION_NO_ACTION     = 0,
142     TAPE_ACTION_NEW_TAPE      = (1 << 0),
143     TAPE_ACTION_NO_NEW_TAPE   = (1 << 1),
144     TAPE_ACTION_START_A_FLUSH = (1 << 2)
145 } TapeAction;
146
147 static TapeAction tape_action(void);
148
149 static const char *idle_strings[] = {
150 #define NOT_IDLE                0
151     T_("not-idle"),
152 #define IDLE_NO_DUMPERS         1
153     T_("no-dumpers"),
154 #define IDLE_START_WAIT         2
155     T_("start-wait"),
156 #define IDLE_NO_HOLD            3
157     T_("no-hold"),
158 #define IDLE_CLIENT_CONSTRAINED 4
159     T_("client-constrained"),
160 #define IDLE_NO_BANDWIDTH       5
161     T_("no-bandwidth"),
162 #define IDLE_NO_DISKSPACE       6
163     T_("no-diskspace")
164 };
165
166 int
167 main(
168     int         argc,
169     char **     argv)
170 {
171     disklist_t origq;
172     disk_t *diskp;
173     int dsk;
174     dumper_t *dumper;
175     char *newdir = NULL;
176     struct fs_usage fsusage;
177     holdingdisk_t *hdp;
178     unsigned long reserve = 100;
179     char *conf_diskfile;
180     cmd_t cmd;
181     int result_argc;
182     char *result_argv[MAX_ARGS+1];
183     char *taper_program;
184     char *conf_tapetype;
185     tapetype_t *tape;
186     char *line;
187     char hostname[1025];
188     intmax_t kb_avail;
189     config_overwrites_t *cfg_ovr = NULL;
190     char *cfg_opt = NULL;
191     holdalloc_t *ha, *ha_last;
192
193     /*
194      * Configure program for internationalization:
195      *   1) Only set the message locale for now.
196      *   2) Set textdomain for all amanda related programs to "amanda"
197      *      We don't want to be forced to support dozens of message catalogs.
198      */  
199     setlocale(LC_MESSAGES, "C");
200     textdomain("amanda");
201
202     safe_fd(-1, 0);
203
204     setvbuf(stdout, (char *)NULL, (int)_IOLBF, 0);
205     setvbuf(stderr, (char *)NULL, (int)_IOLBF, 0);
206
207     set_pname("driver");
208
209     dbopen(DBG_SUBDIR_SERVER);
210
211     atexit(wait_for_children);
212
213     /* Don't die when child closes pipe */
214     signal(SIGPIPE, SIG_IGN);
215
216     erroutput_type = (ERR_AMANDALOG|ERR_INTERACTIVE);
217     set_logerror(logerror);
218
219     startclock();
220
221     cfg_ovr = extract_commandline_config_overwrites(&argc, &argv);
222
223     if (argc > 1)
224         cfg_opt = argv[1];
225     config_init(CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_USE_CWD | CONFIG_INIT_FATAL,
226                 cfg_opt);
227     apply_config_overwrites(cfg_ovr);
228
229     g_printf(_("%s: pid %ld executable %s version %s\n"),
230            get_pname(), (long) getpid(), argv[0], version());
231
232     if(argc > 2) {
233         if(strncmp(argv[2], "nodump", 6) == 0) {
234             nodump = 1;
235         }
236     }
237
238     safe_cd(); /* do this *after* config_init */
239
240     check_running_as(RUNNING_AS_DUMPUSER);
241
242     dbrename(config_name, DBG_SUBDIR_SERVER);
243
244     amfree(driver_timestamp);
245     /* read timestamp from stdin */
246     while ((line = agets(stdin)) != NULL) {
247         if (line[0] != '\0')
248             break;
249         amfree(line);
250     }
251     if ( line == NULL ) {
252       error(_("Did not get DATE line from planner"));
253       /*NOTREACHED*/
254     }
255     driver_timestamp = alloc(15);
256     strncpy(driver_timestamp, &line[5], 14);
257     driver_timestamp[14] = '\0';
258     amfree(line);
259     log_add(L_START,_("date %s"), driver_timestamp);
260
261     gethostname(hostname, SIZEOF(hostname));
262     log_add(L_STATS,_("hostname %s"), hostname);
263
264     /* check that we don't do many dump in a day and usetimestamps is off */
265     if(strlen(driver_timestamp) == 8) {
266         if (!nodump) {
267             char *conf_logdir = getconf_str(CNF_LOGDIR);
268             char *logfile    = vstralloc(conf_logdir, "/log.",
269                                          driver_timestamp, ".0", NULL);
270             char *oldlogfile = vstralloc(conf_logdir, "/oldlog/log.",
271                                          driver_timestamp, ".0", NULL);
272             if(access(logfile, F_OK) == 0 || access(oldlogfile, F_OK) == 0) {
273                 log_add(L_WARNING, _("WARNING: This is not the first amdump run today. Enable the usetimestamps option in the configuration file if you want to run amdump more than once per calendar day."));
274             }
275             amfree(oldlogfile);
276             amfree(logfile);
277         }
278         hd_driver_timestamp = get_timestamp_from_time(0);
279     }
280     else {
281         hd_driver_timestamp = stralloc(driver_timestamp);
282     }
283
284     taper_program = vstralloc(amlibexecdir, "/", "taper", versionsuffix(), NULL);
285     dumper_program = vstralloc(amlibexecdir, "/", "dumper", versionsuffix(),
286                                NULL);
287     chunker_program = vstralloc(amlibexecdir, "/", "chunker", versionsuffix(),
288                                NULL);
289
290     conf_taperalgo = getconf_taperalgo(CNF_TAPERALGO);
291     conf_tapetype = getconf_str(CNF_TAPETYPE);
292     conf_runtapes = getconf_int(CNF_RUNTAPES);
293     tape = lookup_tapetype(conf_tapetype);
294     tape_length = tapetype_get_length(tape);
295     conf_flush_threshold_dumped = getconf_int(CNF_FLUSH_THRESHOLD_DUMPED);
296     conf_flush_threshold_scheduled = getconf_int(CNF_FLUSH_THRESHOLD_SCHEDULED);
297     conf_taperflush = getconf_int(CNF_TAPERFLUSH);
298
299     flush_threshold_dumped = (conf_flush_threshold_dumped * tape_length) / 100;
300     flush_threshold_scheduled = (conf_flush_threshold_scheduled * tape_length) / 100;
301     taperflush = (conf_taperflush *tape_length) / 100;
302
303     driver_debug(1, _("flush_threshold_dumped: %lld\n"), (long long)flush_threshold_dumped);
304     driver_debug(1, _("flush_threshold_scheduled: %lld\n"), (long long)flush_threshold_scheduled);
305     driver_debug(1, _("taperflush: %lld\n"), (long long)taperflush);
306
307     /* start initializing: read in databases */
308
309     conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
310     if (read_diskfile(conf_diskfile, &origq) < 0) {
311         error(_("could not load disklist \"%s\""), conf_diskfile);
312         /*NOTREACHED*/
313     }
314     amfree(conf_diskfile);
315
316     /* set up any configuration-dependent variables */
317
318     inparallel  = getconf_int(CNF_INPARALLEL);
319
320     reserve = (unsigned long)getconf_int(CNF_RESERVE);
321
322     total_disksize = (off_t)0;
323     ha_last = NULL;
324     num_holdalloc = 0;
325     for(hdp = getconf_holdingdisks(), dsk = 0; hdp != NULL; hdp = holdingdisk_next(hdp), dsk++) {
326         ha = alloc(SIZEOF(holdalloc_t));
327         num_holdalloc++;
328
329         /* link the list in the same order as getconf_holdingdisks's results */
330         ha->next = NULL;
331         if (ha_last == NULL)
332             holdalloc = ha;
333         else
334             ha_last->next = ha;
335         ha_last = ha;
336
337         ha->hdisk = hdp;
338         ha->allocated_dumpers = 0;
339         ha->allocated_space = (off_t)0;
340         ha->disksize = holdingdisk_get_disksize(hdp);
341
342         /* get disk size */
343         if(get_fs_usage(holdingdisk_get_diskdir(hdp), NULL, &fsusage) == -1
344            || access(holdingdisk_get_diskdir(hdp), W_OK) == -1) {
345             log_add(L_WARNING, _("WARNING: ignoring holding disk %s: %s\n"),
346                     holdingdisk_get_diskdir(hdp), strerror(errno));
347             ha->disksize = 0L;
348             continue;
349         }
350
351         /* do the division first to avoid potential integer overflow */
352         if (fsusage.fsu_bavail_top_bit_set)
353             kb_avail = 0;
354         else
355             kb_avail = fsusage.fsu_bavail / 1024 * fsusage.fsu_blocksize;
356
357         if(ha->disksize > (off_t)0) {
358             if(ha->disksize > kb_avail) {
359                 log_add(L_WARNING,
360                         _("WARNING: %s: %lld KB requested, "
361                         "but only %lld KB available."),
362                         holdingdisk_get_diskdir(hdp),
363                         (long long)ha->disksize,
364                         (long long)kb_avail);
365                         ha->disksize = kb_avail;
366             }
367         }
368         /* ha->disksize is negative; use all but that amount */
369         else if(kb_avail < -ha->disksize) {
370             log_add(L_WARNING,
371                     _("WARNING: %s: not %lld KB free."),
372                     holdingdisk_get_diskdir(hdp),
373                     (long long)-ha->disksize);
374             ha->disksize = (off_t)0;
375             continue;
376         }
377         else
378             ha->disksize += kb_avail;
379
380         g_printf(_("driver: adding holding disk %d dir %s size %lld chunksize %lld\n"),
381                dsk, holdingdisk_get_diskdir(hdp),
382                (long long)ha->disksize,
383                (long long)(holdingdisk_get_chunksize(hdp)));
384
385         newdir = newvstralloc(newdir,
386                               holdingdisk_get_diskdir(hdp), "/", hd_driver_timestamp,
387                               NULL);
388         if(!mkholdingdir(newdir)) {
389             ha->disksize = (off_t)0;
390         }
391         total_disksize += ha->disksize;
392     }
393
394     reserved_space = total_disksize * (off_t)(reserve / 100);
395
396     g_printf(_("reserving %lld out of %lld for degraded-mode dumps\n"),
397            (long long)reserved_space, (long long)free_space());
398
399     amfree(newdir);
400
401     if(inparallel > MAX_DUMPERS) inparallel = MAX_DUMPERS;
402
403     /* taper takes a while to get going, so start it up right away */
404
405     init_driverio();
406     if(conf_runtapes > 0) {
407         startup_tape_process(taper_program);
408         taper_cmd(START_TAPER, driver_timestamp, NULL, 0, NULL);
409     }
410
411     /* fire up the dumpers now while we are waiting */
412     if(!nodump) startup_dump_processes(dumper_program, inparallel, driver_timestamp);
413
414     /*
415      * Read schedule from stdin.  Usually, this is a pipe from planner,
416      * so the effect is that we wait here for the planner to
417      * finish, but meanwhile the taper is rewinding the tape, reading
418      * the label, checking it, writing a new label and all that jazz
419      * in parallel with the planner.
420      */
421
422     runq.head = NULL;
423     runq.tail = NULL;
424     directq.head = NULL;
425     directq.tail = NULL;
426     waitq = origq;
427     taper_state = TAPER_STATE_DEFAULT;
428     tapeq = read_flush();
429
430     roomq.head = roomq.tail = NULL;
431
432     log_add(L_STATS, _("startup time %s"), walltime_str(curclock()));
433
434     g_printf(_("driver: start time %s inparallel %d bandwidth %lu diskspace %lld "), walltime_str(curclock()), inparallel,
435            free_kps(NULL), (long long)free_space());
436     g_printf(_(" dir %s datestamp %s driver: drain-ends tapeq %s big-dumpers %s\n"),
437            "OBSOLETE", driver_timestamp, taperalgo2str(conf_taperalgo),
438            getconf_str(CNF_DUMPORDER));
439     fflush(stdout);
440
441     /* ok, planner is done, now lets see if the tape is ready */
442
443     if (conf_runtapes > 0) {
444         cmd = getresult(taper, 1, &result_argc, result_argv, MAX_ARGS+1);
445         if (cmd != TAPER_OK) {
446             /* no tape, go into degraded mode: dump to holding disk */
447             need_degraded = 1;
448         }
449     } else {
450         need_degraded = 1;
451     }
452
453     tape_left = tape_length;
454     taper_busy = 0;
455     taper_input_error = NULL;
456     taper_tape_error = NULL;
457     taper_disk = NULL;
458     taper_ev_read = NULL;
459
460     schedule_done = nodump;
461     force_flush = 0;
462
463     if(!need_degraded) startaflush();
464
465     if(!nodump)
466         schedule_ev_read = event_register((event_id_t)0, EV_READFD, read_schedule, NULL);
467
468     short_dump_state();
469     event_loop(0);
470
471     force_flush = 1;
472
473     /* mv runq to directq */
474     while (!empty(runq)) {
475         diskp = dequeue_disk(&runq);
476         headqueue_disk(&directq, diskp);
477     }
478
479     /* handle any remaining dumps by dumping directly to tape, if possible */
480     while(!empty(directq) && taper > 0) {
481         diskp = dequeue_disk(&directq);
482         if (diskp->to_holdingdisk == HOLD_REQUIRED) {
483             log_add(L_FAIL, _("%s %s %s %d [%s]"),
484                 diskp->host->hostname, diskp->name, sched(diskp)->datestamp,
485                 sched(diskp)->level,
486                 _("can't dump required holdingdisk"));
487         }
488         else if (!degraded_mode) {
489             taper_state |= TAPER_STATE_DUMP_TO_TAPE;
490             dump_to_tape(diskp);
491             event_loop(0);
492             taper_state &= !TAPER_STATE_DUMP_TO_TAPE;
493         }
494         else
495             log_add(L_FAIL, _("%s %s %s %d [%s]"),
496                 diskp->host->hostname, diskp->name, sched(diskp)->datestamp,
497                 sched(diskp)->level,
498                 diskp->to_holdingdisk == HOLD_AUTO ?
499                     _("no more holding disk space") :
500                     _("can't dump no-hold disk in degraded mode"));
501     }
502
503     /* fill up the tape or start new one for taperflush */
504     startaflush();
505     event_loop(0);
506
507     short_dump_state();                         /* for amstatus */
508
509     g_printf(_("driver: QUITTING time %s telling children to quit\n"),
510            walltime_str(curclock()));
511     fflush(stdout);
512
513     if(!nodump) {
514         for(dumper = dmptable; dumper < dmptable + inparallel; dumper++) {
515             if(dumper->fd >= 0)
516                 dumper_cmd(dumper, QUIT, NULL);
517         }
518     }
519
520     if(taper >= 0) {
521         taper_cmd(QUIT, NULL, NULL, 0, NULL);
522     }
523
524     /* wait for all to die */
525     wait_children(600);
526
527     /* cleanup */
528     holding_cleanup(NULL, NULL);
529
530     amfree(newdir);
531
532     check_unfree_serial();
533     g_printf(_("driver: FINISHED time %s\n"), walltime_str(curclock()));
534     fflush(stdout);
535     log_add(L_FINISH,_("date %s time %s"), driver_timestamp, walltime_str(curclock()));
536     amfree(driver_timestamp);
537
538     amfree(dumper_program);
539     amfree(taper_program);
540
541     dbclose();
542
543     return 0;
544 }
545
546 /* sleep up to count seconds, and wait for terminating child process */
547 /* if sleep is negative, this function will not timeout              */
548 /* exit once all child process are finished or the timout expired    */
549 /* return 0 if no more children to wait                              */
550 /* return 1 if some children are still alive                         */
551 static int
552 wait_children(int count)
553 {
554     pid_t     pid;
555     amwait_t  retstat;
556     char     *who;
557     char     *what;
558     int       code=0;
559     dumper_t *dumper;
560     int       wait_errno;
561
562     do {
563         do {
564             pid = waitpid((pid_t)-1, &retstat, WNOHANG);
565             wait_errno = errno;
566             if (pid > 0) {
567                 what = NULL;
568                 if (! WIFEXITED(retstat)) {
569                     what = _("signal");
570                     code = WTERMSIG(retstat);
571                 } else if (WEXITSTATUS(retstat) != 0) {
572                     what = _("code");
573                     code = WEXITSTATUS(retstat);
574                 }
575                 who = NULL;
576                 for (dumper = dmptable; dumper < dmptable + inparallel;
577                      dumper++) {
578                     if (pid == dumper->pid) {
579                         who = stralloc(dumper->name);
580                         dumper->pid = -1;
581                         break;
582                     }
583                     if (dumper->chunker && pid == dumper->chunker->pid) {
584                         who = stralloc(dumper->chunker->name);
585                         dumper->chunker->pid = -1;
586                         break;
587                     }
588                 }
589                 if (who == NULL && pid == taper_pid) {
590                     who = stralloc("taper");
591                     taper_pid = -1;
592                 }
593                 if(what != NULL && who == NULL) {
594                     who = stralloc("unknown");
595                 }
596                 if(who && what) {
597                     log_add(L_WARNING, _("%s pid %u exited with %s %d\n"), who, 
598                             (unsigned)pid, what, code);
599                     g_printf(_("driver: %s pid %u exited with %s %d\n"), who,
600                            (unsigned)pid, what, code);
601                 }
602                 amfree(who);
603             }
604         } while (pid > 0 || wait_errno == EINTR);
605         if (errno != ECHILD)
606             sleep(1);
607         if (count > 0)
608             count--;
609     } while ((errno != ECHILD) && (count != 0));
610     return (errno != ECHILD);
611 }
612
613 static void
614 kill_children(int signal)
615 {
616     dumper_t *dumper;
617
618     if(!nodump) {
619         for(dumper = dmptable; dumper < dmptable + inparallel; dumper++) {
620             if (!dumper->down && dumper->pid > 1) {
621                 g_printf(_("driver: sending signal %d to %s pid %u\n"), signal,
622                        dumper->name, (unsigned)dumper->pid);
623                 if (kill(dumper->pid, signal) == -1 && errno == ESRCH) {
624                     if (dumper->chunker)
625                         dumper->chunker->pid = 0;
626                 }
627                 if (dumper->chunker && dumper->chunker->pid > 1) {
628                     g_printf(_("driver: sending signal %d to %s pid %u\n"), signal,
629                            dumper->chunker->name,
630                            (unsigned)dumper->chunker->pid);
631                     if (kill(dumper->chunker->pid, signal) == -1 &&
632                         errno == ESRCH)
633                         dumper->chunker->pid = 0;
634                 }
635             }
636         }
637     }
638
639     if(taper_pid > 1)
640         g_printf(_("driver: sending signal %d to %s pid %u\n"), signal,
641                "taper", (unsigned)taper_pid);
642         if (kill(taper_pid, signal) == -1 && errno == ESRCH)
643             taper_pid = 0;
644 }
645
646 static void
647 wait_for_children(void)
648 {
649     dumper_t *dumper;
650
651     if(!nodump) {
652         for(dumper = dmptable; dumper < dmptable + inparallel; dumper++) {
653             if (dumper->pid > 1 && dumper->fd >= 0) {
654                 dumper_cmd(dumper, QUIT, NULL);
655                 if (dumper->chunker && dumper->chunker->pid > 1 &&
656                     dumper->chunker->fd >= 0)
657                     chunker_cmd(dumper->chunker, QUIT, NULL);
658             }
659         }
660     }
661
662     if(taper_pid > 1 && taper > 0) {
663         taper_cmd(QUIT, NULL, NULL, 0, NULL);
664     }
665
666     if(wait_children(60) == 0)
667         return;
668
669     kill_children(SIGHUP);
670     if(wait_children(60) == 0)
671         return;
672
673     kill_children(SIGKILL);
674     if(wait_children(-1) == 0)
675         return;
676
677 }
678
679 static void
680 startaflush(void)
681 {
682     disk_t *dp = NULL;
683     disk_t *fit = NULL;
684     char *datestamp;
685     int extra_tapes = 0;
686     char *qname;
687     TapeAction result_tape_action;
688
689     result_tape_action = tape_action();
690
691     if (result_tape_action & TAPE_ACTION_NEW_TAPE) {
692         taper_state &= !TAPER_STATE_WAIT_FOR_TAPE;
693         taper_cmd(NEW_TAPE, NULL, NULL, 0, NULL);
694     } else if (result_tape_action & TAPE_ACTION_NO_NEW_TAPE) {
695         taper_state &= !TAPER_STATE_WAIT_FOR_TAPE;
696         taper_cmd(NO_NEW_TAPE, NULL, NULL, 0, NULL);
697     }
698
699     if (!degraded_mode && !taper_busy && !empty(tapeq) &&
700         (result_tape_action & TAPE_ACTION_START_A_FLUSH)) {
701         
702         datestamp = sched(tapeq.head)->datestamp;
703         switch(conf_taperalgo) {
704         case ALGO_FIRST:
705                 dp = dequeue_disk(&tapeq);
706                 break;
707         case ALGO_FIRSTFIT:
708                 fit = tapeq.head;
709                 while (fit != NULL) {
710                     extra_tapes = (fit->tape_splitsize > (off_t)0) ? 
711                                         conf_runtapes - current_tape : 0;
712                     if(sched(fit)->act_size <= (tape_left +
713                              tape_length * (off_t)extra_tapes) &&
714                              strcmp(sched(fit)->datestamp, datestamp) <= 0) {
715                         dp = fit;
716                         fit = NULL;
717                     }
718                     else {
719                         fit = fit->next;
720                     }
721                 }
722                 if(dp) remove_disk(&tapeq, dp);
723                 break;
724         case ALGO_LARGEST:
725                 fit = dp = tapeq.head;
726                 while (fit != NULL) {
727                     if(sched(fit)->act_size > sched(dp)->act_size &&
728                        strcmp(sched(fit)->datestamp, datestamp) <= 0) {
729                         dp = fit;
730                     }
731                     fit = fit->next;
732                 }
733                 if(dp) remove_disk(&tapeq, dp);
734                 break;
735         case ALGO_LARGESTFIT:
736                 fit = tapeq.head;
737                 while (fit != NULL) {
738                     extra_tapes = (fit->tape_splitsize > (off_t)0) ? 
739                                         conf_runtapes - current_tape : 0;
740                     if(sched(fit)->act_size <=
741                        (tape_left + tape_length * (off_t)extra_tapes) &&
742                        (!dp || sched(fit)->act_size > sched(dp)->act_size) &&
743                        strcmp(sched(fit)->datestamp, datestamp) <= 0) {
744                         dp = fit;
745                     }
746                     fit = fit->next;
747                 }
748                 if(dp) remove_disk(&tapeq, dp);
749                 break;
750         case ALGO_SMALLEST:
751                 break;
752         case ALGO_LAST:
753                 dp = tapeq.tail;
754                 remove_disk(&tapeq, dp);
755                 break;
756         }
757         if(!dp) { /* ALGO_SMALLEST, or default if nothing fit. */
758             if(conf_taperalgo != ALGO_SMALLEST)  {
759                 g_fprintf(stderr,
760                    _("driver: startaflush: Using SMALLEST because nothing fit\n"));
761             }
762             fit = dp = tapeq.head;
763             while (fit != NULL) {
764                 if(sched(fit)->act_size < sched(dp)->act_size &&
765                    strcmp(sched(fit)->datestamp, datestamp) <= 0) {
766                     dp = fit;
767                 }
768                 fit = fit->next;
769             }
770             if(dp) remove_disk(&tapeq, dp);
771         }
772         if(taper_ev_read == NULL) {
773             taper_ev_read = event_register((event_id_t)taper, EV_READFD,
774                                            handle_taper_result, NULL);
775         }
776         if (dp) {
777             taper_disk = dp;
778             taper_busy = 1;
779             taper_input_error = NULL;
780             taper_tape_error = NULL;
781             taper_result = LAST_TOK;
782             taper_sendresult = 0;
783             taper_first_label = NULL;
784             taper_written = 0;
785             taper_state &= !TAPER_STATE_DUMP_TO_TAPE;
786             taper_dumper = NULL;
787             qname = quote_string(dp->name);
788             taper_cmd(FILE_WRITE, dp, sched(dp)->destname, sched(dp)->level,
789                       sched(dp)->datestamp);
790             g_fprintf(stderr,_("driver: startaflush: %s %s %s %lld %lld\n"),
791                     taperalgo2str(conf_taperalgo), dp->host->hostname, qname,
792                     (long long)sched(taper_disk)->act_size,
793                     (long long)tape_left);
794             if(sched(dp)->act_size <= tape_left)
795                 tape_left -= sched(dp)->act_size;
796             else
797                 tape_left = (off_t)0;
798             amfree(qname);
799         } else {
800             error(_("FATAL: Taper marked busy and no work found."));
801             /*NOTREACHED*/
802         }
803     } else if(!taper_busy && taper_ev_read != NULL) {
804         event_release(taper_ev_read);
805         taper_ev_read = NULL;
806     }
807 }
808
809 static int
810 client_constrained(
811     disk_t *    dp)
812 {
813     disk_t *dp2;
814
815     /* first, check if host is too busy */
816
817     if(dp->host->inprogress >= dp->host->maxdumps) {
818         return 1;
819     }
820
821     /* next, check conflict with other dumps on same spindle */
822
823     if(dp->spindle == -1) {     /* but spindle -1 never conflicts by def. */
824         return 0;
825     }
826
827     for(dp2 = dp->host->disks; dp2 != NULL; dp2 = dp2->hostnext)
828         if(dp2->inprogress && dp2->spindle == dp->spindle) {
829             return 1;
830         }
831
832     return 0;
833 }
834
835 static void
836 start_some_dumps(
837     disklist_t *        rq)
838 {
839     int cur_idle;
840     disk_t *diskp, *delayed_diskp, *diskp_accept;
841     assignedhd_t **holdp=NULL, **holdp_accept;
842     const time_t now = time(NULL);
843     cmd_t cmd;
844     int result_argc;
845     char *result_argv[MAX_ARGS+1];
846     chunker_t *chunker;
847     dumper_t *dumper;
848     char dumptype;
849     char *dumporder;
850     int  busy_dumpers = 0;
851
852     idle_reason = IDLE_NO_DUMPERS;
853     sleep_time = 0;
854
855     if(dumpers_ev_time != NULL) {
856         event_release(dumpers_ev_time);
857         dumpers_ev_time = NULL;
858     }
859
860     for(dumper = dmptable; dumper < (dmptable+inparallel); dumper++) {
861         if( dumper->busy ) {
862             busy_dumpers++;
863         }
864     }
865
866     for (dumper = dmptable; dumper < dmptable+inparallel; dumper++) {
867
868         if( dumper->busy || dumper->down) {
869             continue;
870         }
871
872         if (dumper->ev_read != NULL) {
873             event_release(dumper->ev_read);
874             dumper->ev_read = NULL;
875         }
876
877         /*
878          * A potential problem with starting from the bottom of the dump time
879          * distribution is that a slave host will have both one of the shortest
880          * and one of the longest disks, so starting its shortest disk first will
881          * tie up the host and eliminate its longest disk from consideration the
882          * first pass through.  This could cause a big delay in starting that long
883          * disk, which could drag out the whole night's dumps.
884          *
885          * While starting from the top of the dump time distribution solves the
886          * above problem, this turns out to be a bad idea, because the big dumps
887          * will almost certainly pack the holding disk completely, leaving no
888          * room for even one small dump to start.  This ends up shutting out the
889          * small-end dumpers completely (they stay idle).
890          *
891          * The introduction of multiple simultaneous dumps to one host alleviates
892          * the biggest&smallest dumps problem: both can be started at the
893          * beginning.
894          */
895
896         diskp_accept = NULL;
897         holdp_accept = NULL;
898         delayed_diskp = NULL;
899
900         cur_idle = NOT_IDLE;
901
902         dumporder = getconf_str(CNF_DUMPORDER);
903         if(strlen(dumporder) > (size_t)(dumper-dmptable)) {
904             dumptype = dumporder[dumper-dmptable];
905         }
906         else {
907             if(dumper-dmptable < 3)
908                 dumptype = 't';
909             else
910                 dumptype = 'T';
911         }
912
913         for(diskp = rq->head; diskp != NULL; diskp = diskp->next) {
914             assert(diskp->host != NULL && sched(diskp) != NULL);
915
916             if (diskp->host->start_t > now) {
917                 cur_idle = max(cur_idle, IDLE_START_WAIT);
918                 if (delayed_diskp == NULL || sleep_time > diskp->host->start_t) {
919                     delayed_diskp = diskp;
920                     sleep_time = diskp->host->start_t;
921                 }
922             } else if(diskp->start_t > now) {
923                 cur_idle = max(cur_idle, IDLE_START_WAIT);
924                 if (delayed_diskp == NULL || sleep_time > diskp->start_t) {
925                     delayed_diskp = diskp;
926                     sleep_time = diskp->start_t;
927                 }
928             } else if (diskp->host->netif->curusage > 0 &&
929                        sched(diskp)->est_kps > free_kps(diskp->host->netif)) {
930                 cur_idle = max(cur_idle, IDLE_NO_BANDWIDTH);
931             } else if(sched(diskp)->no_space) {
932                 cur_idle = max(cur_idle, IDLE_NO_DISKSPACE);
933             } else if (diskp->to_holdingdisk == HOLD_NEVER) {
934                 cur_idle = max(cur_idle, IDLE_NO_HOLD);
935             } else if ((holdp =
936                 find_diskspace(sched(diskp)->est_size, &cur_idle, NULL)) == NULL) {
937                 cur_idle = max(cur_idle, IDLE_NO_DISKSPACE);
938                 if (empty(tapeq) && busy_dumpers == 0) {
939                     remove_disk(rq, diskp);
940                     enqueue_disk(&directq, diskp);
941                 }
942             } else if (client_constrained(diskp)) {
943                 free_assignedhd(holdp);
944                 cur_idle = max(cur_idle, IDLE_CLIENT_CONSTRAINED);
945             } else {
946
947                 /* disk fits, dump it */
948                 int accept = !diskp_accept;
949                 if(!accept) {
950                     switch(dumptype) {
951                       case 's': accept = (sched(diskp)->est_size < sched(diskp_accept)->est_size);
952                                 break;
953                       case 'S': accept = (sched(diskp)->est_size > sched(diskp_accept)->est_size);
954                                 break;
955                       case 't': accept = (sched(diskp)->est_time < sched(diskp_accept)->est_time);
956                                 break;
957                       case 'T': accept = (sched(diskp)->est_time > sched(diskp_accept)->est_time);
958                                 break;
959                       case 'b': accept = (sched(diskp)->est_kps < sched(diskp_accept)->est_kps);
960                                 break;
961                       case 'B': accept = (sched(diskp)->est_kps > sched(diskp_accept)->est_kps);
962                                 break;
963                       default:  log_add(L_WARNING, _("Unknown dumporder character \'%c\', using 's'.\n"),
964                                         dumptype);
965                                 accept = (sched(diskp)->est_size < sched(diskp_accept)->est_size);
966                                 break;
967                     }
968                 }
969                 if(accept) {
970                     if( !diskp_accept || !degraded_mode || diskp->priority >= diskp_accept->priority) {
971                         if(holdp_accept) free_assignedhd(holdp_accept);
972                         diskp_accept = diskp;
973                         holdp_accept = holdp;
974                     }
975                     else {
976                         free_assignedhd(holdp);
977                     }
978                 }
979                 else {
980                     free_assignedhd(holdp);
981                 }
982             }
983         }
984
985         diskp = diskp_accept;
986         holdp = holdp_accept;
987
988         idle_reason = max(idle_reason, cur_idle);
989
990         /*
991          * If we have no disk at this point, and there are disks that
992          * are delayed, then schedule a time event to call this dumper
993          * with the disk with the shortest delay.
994          */
995         if (diskp == NULL && delayed_diskp != NULL) {
996             assert(sleep_time > now);
997             sleep_time -= now;
998             dumpers_ev_time = event_register((event_id_t)sleep_time, EV_TIME,
999                 handle_dumpers_time, &runq);
1000             return;
1001         } else if (diskp != NULL) {
1002             sched(diskp)->act_size = (off_t)0;
1003             allocate_bandwidth(diskp->host->netif, sched(diskp)->est_kps);
1004             sched(diskp)->activehd = assign_holdingdisk(holdp, diskp);
1005             amfree(holdp);
1006             sched(diskp)->destname = newstralloc(sched(diskp)->destname,
1007                                                  sched(diskp)->holdp[0]->destname);
1008             diskp->host->inprogress++;  /* host is now busy */
1009             diskp->inprogress = 1;
1010             sched(diskp)->dumper = dumper;
1011             sched(diskp)->timestamp = now;
1012
1013             dumper->busy = 1;           /* dumper is now busy */
1014             dumper->dp = diskp;         /* link disk to dumper */
1015             remove_disk(rq, diskp);             /* take it off the run queue */
1016
1017             sched(diskp)->origsize = (off_t)-1;
1018             sched(diskp)->dumpsize = (off_t)-1;
1019             sched(diskp)->dumptime = (time_t)0;
1020             sched(diskp)->tapetime = (time_t)0;
1021             chunker = dumper->chunker;
1022             chunker->result = LAST_TOK;
1023             dumper->result = LAST_TOK;
1024             startup_chunk_process(chunker,chunker_program);
1025             chunker_cmd(chunker, START, (void *)driver_timestamp);
1026             chunker->dumper = dumper;
1027             chunker_cmd(chunker, PORT_WRITE, diskp);
1028             cmd = getresult(chunker->fd, 1, &result_argc, result_argv, MAX_ARGS+1);
1029             if(cmd != PORT) {
1030                 assignedhd_t **h=NULL;
1031                 int activehd;
1032
1033                 g_printf(_("driver: did not get PORT from %s for %s:%s\n"),
1034                        chunker->name, diskp->host->hostname, diskp->name);
1035                 fflush(stdout);
1036
1037                 deallocate_bandwidth(diskp->host->netif, sched(diskp)->est_kps);
1038                 h = sched(diskp)->holdp;
1039                 activehd = sched(diskp)->activehd;
1040                 h[activehd]->used = 0;
1041                 h[activehd]->disk->allocated_dumpers--;
1042                 adjust_diskspace(diskp, DONE);
1043                 delete_diskspace(diskp);
1044                 diskp->host->inprogress--;
1045                 diskp->inprogress = 0;
1046                 sched(diskp)->dumper = NULL;
1047                 dumper->busy = 0;
1048                 dumper->dp = NULL;
1049                 sched(diskp)->dump_attempted++;
1050                 free_serial_dp(diskp);
1051                 if(sched(diskp)->dump_attempted < 2)
1052                     enqueue_disk(rq, diskp);
1053             }
1054             else {
1055                 dumper->ev_read = event_register((event_id_t)dumper->fd, EV_READFD,
1056                                                  handle_dumper_result, dumper);
1057                 chunker->ev_read = event_register((event_id_t)chunker->fd, EV_READFD,
1058                                                    handle_chunker_result, chunker);
1059                 dumper->output_port = atoi(result_argv[2]);
1060
1061                 dumper_cmd(dumper, PORT_DUMP, diskp);
1062             }
1063             diskp->host->start_t = now + 15;
1064         }
1065     }
1066 }
1067
1068 /*
1069  * This gets called when a dumper is delayed for some reason.  It may
1070  * be because a disk has a delayed start, or amanda is constrained
1071  * by network or disk limits.
1072  */
1073
1074 static void
1075 handle_dumpers_time(
1076     void *      cookie)
1077 {
1078     disklist_t *runq = cookie;
1079     event_release(dumpers_ev_time);
1080     dumpers_ev_time = NULL; 
1081     start_some_dumps(runq);
1082 }
1083
1084 static void
1085 dump_schedule(
1086     disklist_t *qp,
1087     char *      str)
1088 {
1089     disk_t *dp;
1090     char *qname;
1091
1092     g_printf(_("dump of driver schedule %s:\n--------\n"), str);
1093
1094     for(dp = qp->head; dp != NULL; dp = dp->next) {
1095         qname = quote_string(dp->name);
1096         g_printf("  %-20s %-25s lv %d t %5lu s %lld p %d\n",
1097                dp->host->hostname, qname, sched(dp)->level,
1098                sched(dp)->est_time,
1099                (long long)sched(dp)->est_size, sched(dp)->priority);
1100         amfree(qname);
1101     }
1102     g_printf("--------\n");
1103 }
1104
1105 static void
1106 start_degraded_mode(
1107     /*@keep@*/ disklist_t *queuep)
1108 {
1109     disk_t *dp;
1110     disklist_t newq;
1111     off_t est_full_size;
1112     char *qname;
1113
1114     newq.head = newq.tail = 0;
1115
1116     dump_schedule(queuep, _("before start degraded mode"));
1117
1118     est_full_size = (off_t)0;
1119     while(!empty(*queuep)) {
1120         dp = dequeue_disk(queuep);
1121
1122         qname = quote_string(dp->name);
1123         if(sched(dp)->level != 0)
1124             /* go ahead and do the disk as-is */
1125             enqueue_disk(&newq, dp);
1126         else {
1127             if (reserved_space + est_full_size + sched(dp)->est_size
1128                 <= total_disksize) {
1129                 enqueue_disk(&newq, dp);
1130                 est_full_size += sched(dp)->est_size;
1131             }
1132             else if(sched(dp)->degr_level != -1) {
1133                 sched(dp)->level = sched(dp)->degr_level;
1134                 sched(dp)->dumpdate = sched(dp)->degr_dumpdate;
1135                 sched(dp)->est_nsize = sched(dp)->degr_nsize;
1136                 sched(dp)->est_csize = sched(dp)->degr_csize;
1137                 sched(dp)->est_time = sched(dp)->degr_time;
1138                 sched(dp)->est_kps  = sched(dp)->degr_kps;
1139                 enqueue_disk(&newq, dp);
1140             }
1141             else {
1142                 log_add(L_FAIL,_("%s %s %s %d [can't switch to incremental dump]"),
1143                         dp->host->hostname, qname, sched(dp)->datestamp,
1144                         sched(dp)->level);
1145             }
1146         }
1147         amfree(qname);
1148     }
1149
1150     /*@i@*/ *queuep = newq;
1151     degraded_mode = 1;
1152
1153     dump_schedule(queuep, _("after start degraded mode"));
1154 }
1155
1156
1157 static void
1158 continue_port_dumps(void)
1159 {
1160     disk_t *dp, *ndp;
1161     assignedhd_t **h;
1162     int active_dumpers=0, busy_dumpers=0, i;
1163     dumper_t *dumper;
1164
1165     /* First we try to grant diskspace to some dumps waiting for it. */
1166     for( dp = roomq.head; dp; dp = ndp ) {
1167         ndp = dp->next;
1168         /* find last holdingdisk used by this dump */
1169         for( i = 0, h = sched(dp)->holdp; h[i+1]; i++ ) {
1170             (void)h; /* Quiet lint */
1171         }
1172         /* find more space */
1173         h = find_diskspace( sched(dp)->est_size - sched(dp)->act_size,
1174                             &active_dumpers, h[i] );
1175         if( h ) {
1176             for(dumper = dmptable; dumper < dmptable + inparallel &&
1177                                    dumper->dp != dp; dumper++) {
1178                 (void)dp; /* Quiet lint */
1179             }
1180             assert( dumper < dmptable + inparallel );
1181             sched(dp)->activehd = assign_holdingdisk( h, dp );
1182             chunker_cmd( dumper->chunker, CONTINUE, dp );
1183             amfree(h);
1184             remove_disk( &roomq, dp );
1185         }
1186     }
1187
1188     /* So for some disks there is less holding diskspace available than
1189      * was asked for. Possible reasons are
1190      * a) diskspace has been allocated for other dumps which are
1191      *    still running or already being written to tape
1192      * b) all other dumps have been suspended due to lack of diskspace
1193      * c) this dump doesn't fit on all the holding disks
1194      * Case a) is not a problem. We just wait for the diskspace to
1195      * be freed by moving the current disk to a queue.
1196      * If case b) occurs, we have a deadlock situation. We select
1197      * a dump from the queue to be aborted and abort it. It will
1198      * be retried later dumping to disk.
1199      * If case c) is detected, the dump is aborted. Next time
1200      * it will be dumped directly to tape. Actually, case c is a special
1201      * manifestation of case b) where only one dumper is busy.
1202      */
1203     for(dp=NULL, dumper = dmptable; dumper < (dmptable+inparallel); dumper++) {
1204         if( dumper->busy ) {
1205             busy_dumpers++;
1206             if( !find_disk(&roomq, dumper->dp) ) {
1207                 active_dumpers++;
1208             } else if( !dp || 
1209                        sched(dp)->est_size > sched(dumper->dp)->est_size ) {
1210                 dp = dumper->dp;
1211             }
1212         }
1213     }
1214     if((dp != NULL) && (active_dumpers == 0) && (busy_dumpers > 0) && 
1215         ((!taper_busy && empty(tapeq)) || degraded_mode) &&
1216         pending_aborts == 0 ) { /* not case a */
1217         if( busy_dumpers == 1 ) { /* case c */
1218             sched(dp)->no_space = 1;
1219         }
1220         /* case b */
1221         /* At this time, dp points to the dump with the smallest est_size.
1222          * We abort that dump, hopefully not wasting too much time retrying it.
1223          */
1224         remove_disk( &roomq, dp );
1225         chunker_cmd( sched(dp)->dumper->chunker, ABORT, NULL);
1226         dumper_cmd( sched(dp)->dumper, ABORT, NULL );
1227         pending_aborts++;
1228     }
1229 }
1230
1231
1232 static void
1233 handle_taper_result(
1234     void *cookie)
1235 {
1236     disk_t *dp;
1237     cmd_t cmd;
1238     int result_argc;
1239     char *result_argv[MAX_ARGS+1];
1240     char *qname;
1241
1242     (void)cookie;       /* Quiet unused parameter warning */
1243
1244     assert(cookie == NULL);
1245     
1246     do {
1247         
1248         short_dump_state();
1249         
1250         cmd = getresult(taper, 1, &result_argc, result_argv, MAX_ARGS+1);
1251         
1252         switch(cmd) {
1253             
1254         case FAILED:    /* FAILED <handle> INPUT-* TAPE-* <input err mesg> <tape err mesg> */
1255             if(result_argc != 6) {
1256                 error(_("error: [taper FAILED result_argc != 6: %d"), result_argc);
1257                 /*NOTREACHED*/
1258             }
1259             
1260             dp = serial2disk(result_argv[2]);
1261             assert(dp == taper_disk);
1262             if (!taper_dumper)
1263                 free_serial(result_argv[2]);
1264             
1265             qname = quote_string(dp->name);
1266             g_printf(_("driver: finished-cmd time %s taper wrote %s:%s\n"),
1267                    walltime_str(curclock()), dp->host->hostname, qname);
1268             fflush(stdout);
1269             amfree(qname);
1270
1271             if (strcmp(result_argv[3], "INPUT-ERROR") == 0) {
1272                 taper_input_error = stralloc(result_argv[5]);
1273             }
1274             if (strcmp(result_argv[4], "TAPE-ERROR") == 0) {
1275                 taper_tape_error = stralloc(result_argv[6]);
1276             }
1277
1278             taper_result = cmd;
1279
1280             break;
1281             
1282         case PARTIAL:   /* PARTIAL <handle> INPUT-* TAPE-* <stat mess> <input err mesg> <tape err mesg>*/
1283         case DONE:      /* DONE <handle> INPUT-GOOD TAPE-GOOD <stat mess> <input err mesg> <tape err mesg> */
1284             if(result_argc != 7) {
1285                 error(_("error: [taper PARTIAL result_argc != 7: %d"), result_argc);
1286                 /*NOTREACHED*/
1287             }
1288             
1289             dp = serial2disk(result_argv[2]);
1290             assert(dp == taper_disk);
1291             if (!taper_dumper)
1292                 free_serial(result_argv[2]);
1293
1294             g_printf(_("driver: finished-cmd time %s taper wrote %s:%s\n"),
1295                    walltime_str(curclock()), dp->host->hostname, dp->name);
1296             fflush(stdout);
1297
1298             if (strcmp(result_argv[3], "INPUT-ERROR") == 0) {
1299                 taper_input_error = stralloc(result_argv[5]);
1300             }
1301             if (strcmp(result_argv[4], "TAPE-ERROR") == 0) {
1302                 taper_tape_error = stralloc(result_argv[6]);
1303             }
1304
1305             taper_result = cmd;
1306
1307             break;
1308             
1309         case PARTDONE:  /* PARTDONE <handle> <label> <fileno> <stat> */
1310             dp = serial2disk(result_argv[2]);
1311             assert(dp == taper_disk);
1312             if (result_argc != 6) {
1313                 error(_("error [taper PARTDONE result_argc != 5: %d]"),
1314                       result_argc);
1315                 /*NOTREACHED*/
1316             }
1317             if (!taper_first_label) {
1318                 taper_first_label = stralloc(result_argv[3]);
1319                 taper_first_fileno = OFF_T_ATOI(result_argv[4]);
1320             }
1321             taper_written = OFF_T_ATOI(result_argv[5]);
1322             if (taper_written > sched(taper_disk)->act_size)
1323                 sched(taper_disk)->act_size = taper_written;
1324             
1325             break;
1326
1327         case REQUEST_NEW_TAPE:  /* REQUEST-NEW-TAPE */
1328             if (result_argc != 2) {
1329                 error(_("error [taper REQUEST_NEW_TAPE result_argc != 2: %d]"),
1330                       result_argc);
1331                 /*NOTREACHED*/
1332             }
1333             taper_state &= !TAPER_STATE_TAPE_STARTED;
1334
1335             if (current_tape >= conf_runtapes) {
1336                 taper_cmd(NO_NEW_TAPE, NULL, NULL, 0, NULL);
1337                 log_add(L_WARNING,
1338                         _("Out of tapes; going into degraded mode."));
1339                 start_degraded_mode(&runq);
1340             } else {
1341                 TapeAction result_tape_action;
1342
1343                 taper_state |= TAPER_STATE_WAIT_FOR_TAPE;
1344                 result_tape_action = tape_action();
1345                 if (result_tape_action & TAPE_ACTION_NEW_TAPE) {
1346                     taper_cmd(NEW_TAPE, NULL, NULL, 0, NULL);
1347                     taper_state &= !TAPER_STATE_WAIT_FOR_TAPE;
1348                 } else if (result_tape_action & TAPE_ACTION_NO_NEW_TAPE) {
1349                     taper_cmd(NO_NEW_TAPE, NULL, NULL, 0, NULL);
1350                     taper_state &= !TAPER_STATE_WAIT_FOR_TAPE;
1351                 }
1352             }
1353             break;
1354
1355         case NEW_TAPE: /* NEW-TAPE <handle> <label> */
1356             if (result_argc != 3) {
1357                 error(_("error [taper NEW_TAPE result_argc != 3: %d]"),
1358                       result_argc);
1359                 /*NOTREACHED*/
1360             }
1361
1362             /* Update our tape counter and reset tape_left */
1363             current_tape++;
1364             tape_left = tape_length;
1365             taper_state |= TAPER_STATE_TAPE_STARTED;
1366             break;
1367
1368         case NO_NEW_TAPE:  /* NO-NEW-TAPE <handle> */
1369             if (result_argc != 2) {
1370                 error(_("error [taper NO_NEW_TAPE result_argc != 2: %d]"),
1371                       result_argc);
1372                 /*NOTREACHED*/
1373             }
1374             break;
1375
1376         case DUMPER_STATUS:  /* DUMPER-STATUS <handle> */
1377             if (result_argc != 2) {
1378                 error(_("error [taper NO_NEW_TAPE result_argc != 2: %d]"),
1379                       result_argc);
1380                 /*NOTREACHED*/
1381             }
1382             if (taper_dumper->result == LAST_TOK) {
1383                 taper_sendresult = 1;
1384             } else {
1385                 if( taper_dumper->result == DONE) {
1386                     taper_cmd(DONE, NULL, NULL, 0, NULL);
1387                 } else {
1388                     taper_cmd(FAILED, NULL, NULL, 0, NULL);
1389                 }
1390             }
1391             break;
1392
1393         case TAPE_ERROR: /* TAPE-ERROR <handle> <err mess> */
1394             dp = serial2disk(result_argv[2]);
1395             if (!taper_dumper)
1396                 free_serial(result_argv[2]);
1397             g_printf(_("driver: finished-cmd time %s taper wrote %s:%s\n"),
1398                    walltime_str(curclock()), dp->host->hostname, dp->name);
1399             fflush(stdout);
1400             log_add(L_WARNING, _("Taper  error: %s"), result_argv[3]);
1401             taper_tape_error = stralloc(result_argv[3]);
1402             /*FALLTHROUGH*/
1403
1404         case BOGUS:
1405             if (cmd == BOGUS) {
1406                 log_add(L_WARNING, _("Taper protocol error"));
1407                 taper_tape_error = stralloc("BOGUS");
1408             }
1409             /*
1410              * Since we received a taper error, we can't send anything more
1411              * to the taper.  Go into degraded mode to try to get everthing
1412              * onto disk.  Later, these dumps can be flushed to a new tape.
1413              * The tape queue is zapped so that it appears empty in future
1414              * checks. If there are dumps waiting for diskspace to be freed,
1415              * cancel one.
1416              */
1417             if(!nodump) {
1418                 log_add(L_WARNING,
1419                         _("going into degraded mode because of taper component error."));
1420                 start_degraded_mode(&runq);
1421             }
1422             tapeq.head = tapeq.tail = NULL;
1423             taper_busy = 0;
1424             if(taper_ev_read != NULL) {
1425                 event_release(taper_ev_read);
1426                 taper_ev_read = NULL;
1427             }
1428             if(cmd != TAPE_ERROR) aclose(taper);
1429             taper_result = cmd;
1430
1431             break;
1432
1433         default:
1434             error(_("driver received unexpected token (%s) from taper"),
1435                   cmdstr[cmd]);
1436             /*NOTREACHED*/
1437         }
1438
1439         if (taper_result != LAST_TOK) {
1440             if(taper_dumper) {
1441                 if (taper_dumper->result != LAST_TOK) {
1442                     // Dumper already returned it's result
1443                     dumper_taper_result(taper_disk);
1444                 }
1445             } else {
1446                 file_taper_result(taper_disk);
1447             }
1448         }
1449         
1450     } while(areads_dataready(taper));
1451 }
1452
1453
1454 static void
1455 file_taper_result(
1456     disk_t *dp)
1457 {
1458     if (taper_result == DONE) {
1459         update_info_taper(dp, taper_first_label, taper_first_fileno,
1460                           sched(dp)->level);
1461     }
1462
1463     sched(dp)->taper_attempted += 1;
1464
1465     if (taper_input_error) {
1466         g_printf("driver: taper failed %s %s: %s\n",
1467                    dp->host->hostname, dp->name, taper_input_error);
1468         if (strcmp(sched(dp)->datestamp, driver_timestamp) == 0) {
1469             if(sched(dp)->taper_attempted >= 2) {
1470                 log_add(L_FAIL, _("%s %s %s %d [too many taper retries after holding disk error: %s]"),
1471                     dp->host->hostname, dp->name, sched(dp)->datestamp,
1472                     sched(dp)->level, taper_input_error);
1473                 g_printf("driver: taper failed %s %s, too many taper retry after holding disk error\n",
1474                    dp->host->hostname, dp->name);
1475                 amfree(sched(dp)->destname);
1476                 amfree(sched(dp)->dumpdate);
1477                 amfree(sched(dp)->degr_dumpdate);
1478                 amfree(sched(dp)->datestamp);
1479                 amfree(dp->up);
1480             } else {
1481                 log_add(L_INFO, _("%s %s %s %d [Will retry dump because of holding disk error: %s]"),
1482                         dp->host->hostname, dp->name, sched(dp)->datestamp,
1483                         sched(dp)->level, taper_input_error);
1484                 g_printf("driver: taper will retry %s %s because of holding disk error\n",
1485                         dp->host->hostname, dp->name);
1486                 if (dp->to_holdingdisk != HOLD_REQUIRED) {
1487                     dp->to_holdingdisk = HOLD_NEVER;
1488                     sched(dp)->dump_attempted -= 1;
1489                     headqueue_disk(&directq, dp);
1490                 } else {
1491                     amfree(sched(dp)->destname);
1492                     amfree(sched(dp)->dumpdate);
1493                     amfree(sched(dp)->degr_dumpdate);
1494                     amfree(sched(dp)->datestamp);
1495                     amfree(dp->up);
1496                 }
1497             }
1498         } else {
1499             amfree(sched(dp)->destname);
1500             amfree(sched(dp)->dumpdate);
1501             amfree(sched(dp)->degr_dumpdate);
1502             amfree(sched(dp)->datestamp);
1503             amfree(dp->up);
1504         }
1505     } else if (taper_tape_error) {
1506         if(sched(dp)->taper_attempted >= 2) {
1507             log_add(L_FAIL, _("%s %s %s %d [too many taper retries]"),
1508                     dp->host->hostname, dp->name, sched(dp)->datestamp,
1509                     sched(dp)->level);
1510             g_printf("driver: taper failed %s %s, too many taper retry\n",
1511                    dp->host->hostname, dp->name);
1512             amfree(sched(dp)->destname);
1513             amfree(sched(dp)->dumpdate);
1514             amfree(sched(dp)->degr_dumpdate);
1515             amfree(sched(dp)->datestamp);
1516             amfree(dp->up);
1517         } else {
1518             g_printf("driver: taper will retry %s %s\n",
1519                    dp->host->hostname, dp->name);
1520             /* Re-insert into taper queue. */
1521             headqueue_disk(&tapeq, dp);
1522         }
1523     } else {
1524         delete_diskspace(dp);
1525         amfree(sched(dp)->destname);
1526         amfree(sched(dp)->dumpdate);
1527         amfree(sched(dp)->degr_dumpdate);
1528         amfree(sched(dp)->datestamp);
1529         amfree(dp->up);
1530     }
1531
1532     taper_busy = 0;
1533     taper_input_error = NULL;
1534     taper_tape_error = NULL;
1535     taper_disk = NULL;
1536             
1537     /* continue with those dumps waiting for diskspace */
1538     continue_port_dumps();
1539     start_some_dumps(&runq);
1540     startaflush();
1541 }
1542
1543 static void
1544 dumper_taper_result(
1545     disk_t *dp)
1546 {
1547     dumper_t *dumper;
1548     int is_partial;
1549     char *qname;
1550
1551     dumper = sched(dp)->dumper;
1552
1553     free_serial_dp(dp);
1554     if(dumper->result == DONE && taper_result == DONE) {
1555         update_info_dumper(dp, sched(dp)->origsize,
1556                            sched(dp)->dumpsize, sched(dp)->dumptime);
1557         update_info_taper(dp, taper_first_label, taper_first_fileno,
1558                           sched(dp)->level);
1559         qname = quote_string(dp->name); /*quote to take care of spaces*/
1560
1561         log_add(L_STATS, _("estimate %s %s %s %d [sec %ld nkb %lld ckb %lld kps %lu]"),
1562                 dp->host->hostname, qname, sched(dp)->datestamp,
1563                 sched(dp)->level,
1564                 sched(dp)->est_time, (long long)sched(dp)->est_nsize,
1565                 (long long)sched(dp)->est_csize,
1566                 sched(dp)->est_kps);
1567         amfree(qname);
1568     } else {
1569         update_failed_dump_to_tape(dp);
1570     }
1571
1572     is_partial = dumper->result != DONE || taper_result != DONE;
1573
1574     sched(dp)->dump_attempted += 1;
1575     sched(dp)->taper_attempted += 1;
1576
1577     if((dumper->result != DONE || taper_result != DONE) &&
1578         sched(dp)->dump_attempted <= 1 &&
1579         sched(dp)->taper_attempted <= 1) {
1580         enqueue_disk(&directq, dp);
1581     }
1582
1583     if(dumper->ev_read != NULL) {
1584         event_release(dumper->ev_read);
1585         dumper->ev_read = NULL;
1586     }
1587     if(taper_ev_read != NULL) {
1588         event_release(taper_ev_read);
1589         taper_ev_read = NULL;
1590     }
1591     taper_busy = 0;
1592     taper_input_error = NULL;
1593     taper_tape_error = NULL;
1594     dumper->busy = 0;
1595     dp->host->inprogress -= 1;
1596     dp->inprogress = 0;
1597     deallocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
1598 }
1599
1600
1601 static dumper_t *
1602 idle_dumper(void)
1603 {
1604     dumper_t *dumper;
1605
1606     for(dumper = dmptable; dumper < dmptable+inparallel; dumper++)
1607         if(!dumper->busy && !dumper->down) return dumper;
1608
1609     return NULL;
1610 }
1611
1612 static void
1613 dumper_chunker_result(
1614     disk_t *    dp)
1615 {
1616     dumper_t *dumper;
1617     chunker_t *chunker;
1618     assignedhd_t **h=NULL;
1619     int activehd, i;
1620     off_t dummy;
1621     off_t size;
1622     int is_partial;
1623     char *qname;
1624
1625     dumper = sched(dp)->dumper;
1626     chunker = dumper->chunker;
1627
1628     free_serial_dp(dp);
1629
1630     h = sched(dp)->holdp;
1631     activehd = sched(dp)->activehd;
1632
1633     if(dumper->result == DONE && chunker->result == DONE) {
1634         update_info_dumper(dp, sched(dp)->origsize,
1635                            sched(dp)->dumpsize, sched(dp)->dumptime);
1636         qname = quote_string(dp->name);/*quote to take care of spaces*/
1637
1638         log_add(L_STATS, _("estimate %s %s %s %d [sec %ld nkb %lld ckb %lld kps %lu]"),
1639                 dp->host->hostname, qname, sched(dp)->datestamp,
1640                 sched(dp)->level,
1641                 sched(dp)->est_time, (long long)sched(dp)->est_nsize, 
1642                 (long long)sched(dp)->est_csize,
1643                 sched(dp)->est_kps);
1644         amfree(qname);
1645     }
1646
1647     deallocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
1648
1649     is_partial = dumper->result != DONE || chunker->result != DONE;
1650     rename_tmp_holding(sched(dp)->destname, !is_partial);
1651
1652     dummy = (off_t)0;
1653     for( i = 0, h = sched(dp)->holdp; i < activehd; i++ ) {
1654         dummy += h[i]->used;
1655     }
1656
1657     size = holding_file_size(sched(dp)->destname, 0);
1658     h[activehd]->used = size - dummy;
1659     h[activehd]->disk->allocated_dumpers--;
1660     adjust_diskspace(dp, DONE);
1661
1662     sched(dp)->dump_attempted += 1;
1663
1664     if((dumper->result != DONE || chunker->result != DONE) &&
1665        sched(dp)->dump_attempted <= 1) {
1666         delete_diskspace(dp);
1667         if (sched(dp)->no_space) {
1668             enqueue_disk(&directq, dp);
1669         } else {
1670             enqueue_disk(&runq, dp);
1671         }
1672     }
1673     else if(size > (off_t)DISK_BLOCK_KB) {
1674         enqueue_disk(&tapeq, dp);
1675     }
1676     else {
1677         delete_diskspace(dp);
1678     }
1679
1680     dumper->busy = 0;
1681     dp->host->inprogress -= 1;
1682     dp->inprogress = 0;
1683
1684     waitpid(chunker->pid, NULL, 0 );
1685     aclose(chunker->fd);
1686     chunker->fd = -1;
1687     chunker->down = 1;
1688     
1689     dp = NULL;
1690     if (chunker->result == ABORT_FINISHED)
1691         pending_aborts--;
1692     continue_port_dumps();
1693     /*
1694      * Wakeup any dumpers that are sleeping because of network
1695      * or disk constraints.
1696      */
1697     start_some_dumps(&runq);
1698     startaflush();
1699 }
1700
1701
1702 static void
1703 handle_dumper_result(
1704     void *      cookie)
1705 {
1706     /*static int pending_aborts = 0;*/
1707     dumper_t *dumper = cookie;
1708     disk_t *dp, *sdp;
1709     cmd_t cmd;
1710     int result_argc;
1711     char *qname;
1712     char *result_argv[MAX_ARGS+1];
1713
1714     assert(dumper != NULL);
1715     dp = dumper->dp;
1716     assert(dp != NULL);
1717     assert(sched(dp) != NULL);
1718     do {
1719
1720         short_dump_state();
1721
1722         cmd = getresult(dumper->fd, 1, &result_argc, result_argv, MAX_ARGS+1);
1723
1724         if(cmd != BOGUS) {
1725             /* result_argv[2] always contains the serial number */
1726             sdp = serial2disk(result_argv[2]);
1727             if (sdp != dp) {
1728                 error(_("Invalid serial number %s"), result_argv[2]);
1729                 g_assert_not_reached();
1730             }
1731         }
1732
1733         qname = quote_string(dp->name);
1734         switch(cmd) {
1735
1736         case DONE: /* DONE <handle> <origsize> <dumpsize> <dumptime> <errstr> */
1737             if(result_argc != 6) {
1738                 error(_("error [dumper DONE result_argc != 6: %d]"), result_argc);
1739                 /*NOTREACHED*/
1740             }
1741
1742             sched(dp)->origsize = OFF_T_ATOI(result_argv[3]);
1743             sched(dp)->dumptime = TIME_T_ATOI(result_argv[5]);
1744
1745             g_printf(_("driver: finished-cmd time %s %s dumped %s:%s\n"),
1746                    walltime_str(curclock()), dumper->name,
1747                    dp->host->hostname, qname);
1748             fflush(stdout);
1749
1750             dumper->result = cmd;
1751
1752             break;
1753
1754         case TRYAGAIN: /* TRY-AGAIN <handle> <errstr> */
1755             /*
1756              * Requeue this disk, and fall through to the FAILED
1757              * case for cleanup.
1758              */
1759             if(sched(dp)->dump_attempted) {
1760                 log_add(L_FAIL, _("%s %s %s %d [too many dumper retry: %s]"),
1761                     dp->host->hostname, dp->name, sched(dp)->datestamp,
1762                     sched(dp)->level, result_argv[3]);
1763                 g_printf(_("driver: dump failed %s %s %s, too many dumper retry: %s\n"),
1764                         result_argv[2], dp->host->hostname, dp->name,
1765                         result_argv[3]);
1766             }
1767             /* FALLTHROUGH */
1768         case FAILED: /* FAILED <handle> <errstr> */
1769             /*free_serial(result_argv[2]);*/
1770             dumper->result = cmd;
1771             break;
1772
1773         case ABORT_FINISHED: /* ABORT-FINISHED <handle> */
1774             /*
1775              * We sent an ABORT from the NO-ROOM case because this dump
1776              * wasn't going to fit onto the holding disk.  We now need to
1777              * clean up the remains of this image, and try to finish
1778              * other dumps that are waiting on disk space.
1779              */
1780             assert(pending_aborts);
1781             /*free_serial(result_argv[2]);*/
1782             dumper->result = cmd;
1783             break;
1784
1785         case BOGUS:
1786             /* either EOF or garbage from dumper.  Turn it off */
1787             log_add(L_WARNING, _("%s pid %ld is messed up, ignoring it.\n"),
1788                     dumper->name, (long)dumper->pid);
1789             if (dumper->ev_read) {
1790                 event_release(dumper->ev_read);
1791                 dumper->ev_read = NULL;
1792             }
1793             aclose(dumper->fd);
1794             dumper->busy = 0;
1795             dumper->down = 1;   /* mark it down so it isn't used again */
1796             if(dp) {
1797                 /* if it was dumping something, zap it and try again */
1798                 if(sched(dp)->dump_attempted) {
1799                 log_add(L_FAIL, _("%s %s %s %d [%s died]"),
1800                         dp->host->hostname, qname, sched(dp)->datestamp,
1801                         sched(dp)->level, dumper->name);
1802                 }
1803                 else {
1804                 log_add(L_WARNING, _("%s died while dumping %s:%s lev %d."),
1805                         dumper->name, dp->host->hostname, qname,
1806                         sched(dp)->level);
1807                 }
1808             }
1809             dumper->result = cmd;
1810             break;
1811
1812         default:
1813             assert(0);
1814         }
1815         amfree(qname);
1816
1817         /* send the dumper result to the chunker */
1818         if (dumper->chunker) {
1819             if (dumper->chunker->down == 0 && dumper->chunker->fd != -1 &&
1820                 dumper->chunker->result == LAST_TOK) {
1821                 if (cmd == DONE) {
1822                     chunker_cmd(dumper->chunker, DONE, dp);
1823                 }
1824                 else {
1825                     chunker_cmd(dumper->chunker, FAILED, dp);
1826                 }
1827             }
1828             if( dumper->result != LAST_TOK &&
1829                 dumper->chunker->result != LAST_TOK)
1830                 dumper_chunker_result(dp);
1831         } else { /* send the dumper result to the taper */
1832             if (taper_sendresult) {
1833                 if (cmd == DONE) {
1834                     taper_cmd(DONE, driver_timestamp, NULL, 0, NULL);
1835                 } else {
1836                     taper_cmd(FAILED, driver_timestamp, NULL, 0, NULL);
1837                 }
1838                 taper_sendresult = 0;
1839             }
1840         }
1841         if (taper_dumper && taper_result != LAST_TOK) {
1842             dumper_taper_result(dp);
1843         }
1844     } while(areads_dataready(dumper->fd));
1845 }
1846
1847
1848 static void
1849 handle_chunker_result(
1850     void *      cookie)
1851 {
1852     /*static int pending_aborts = 0;*/
1853     chunker_t *chunker = cookie;
1854     assignedhd_t **h=NULL;
1855     dumper_t *dumper;
1856     disk_t *dp, *sdp;
1857     cmd_t cmd;
1858     int result_argc;
1859     char *result_argv[MAX_ARGS+1];
1860     int dummy;
1861     int activehd = -1;
1862     char *qname;
1863
1864     assert(chunker != NULL);
1865     dumper = chunker->dumper;
1866     assert(dumper != NULL);
1867     dp = dumper->dp;
1868     assert(dp != NULL);
1869     assert(sched(dp) != NULL);
1870     assert(sched(dp)->destname != NULL);
1871     assert(dp != NULL && sched(dp) != NULL && sched(dp)->destname);
1872
1873     if(dp && sched(dp) && sched(dp)->holdp) {
1874         h = sched(dp)->holdp;
1875         activehd = sched(dp)->activehd;
1876     }
1877
1878     do {
1879
1880         short_dump_state();
1881
1882         cmd = getresult(chunker->fd, 1, &result_argc, result_argv, MAX_ARGS+1);
1883
1884         if(cmd != BOGUS) {
1885             /* result_argv[2] always contains the serial number */
1886             sdp = serial2disk(result_argv[2]);
1887             if (sdp != dp) {
1888                 error(_("Invalid serial number %s"), result_argv[2]);
1889                 g_assert_not_reached();
1890             }
1891         }
1892
1893         switch(cmd) {
1894
1895         case PARTIAL: /* PARTIAL <handle> <dumpsize> <errstr> */
1896         case DONE: /* DONE <handle> <dumpsize> <errstr> */
1897             if(result_argc != 4) {
1898                 error(_("error [chunker %s result_argc != 4: %d]"), cmdstr[cmd],
1899                       result_argc);
1900                 /*NOTREACHED*/
1901             }
1902             /*free_serial(result_argv[2]);*/
1903
1904             sched(dp)->dumpsize = (off_t)atof(result_argv[3]);
1905
1906             qname = quote_string(dp->name);
1907             g_printf(_("driver: finished-cmd time %s %s chunked %s:%s\n"),
1908                    walltime_str(curclock()), chunker->name,
1909                    dp->host->hostname, qname);
1910             fflush(stdout);
1911             amfree(qname);
1912
1913             event_release(chunker->ev_read);
1914
1915             chunker->result = cmd;
1916
1917             break;
1918
1919         case TRYAGAIN: /* TRY-AGAIN <handle> <errstr> */
1920             event_release(chunker->ev_read);
1921
1922             chunker->result = cmd;
1923
1924             break;
1925         case FAILED: /* FAILED <handle> <errstr> */
1926             /*free_serial(result_argv[2]);*/
1927
1928             event_release(chunker->ev_read);
1929
1930             chunker->result = cmd;
1931
1932             break;
1933
1934         case NO_ROOM: /* NO-ROOM <handle> <missing_size> */
1935             if (!h || activehd < 0) { /* should never happen */
1936                 error(_("!h || activehd < 0"));
1937                 /*NOTREACHED*/
1938             }
1939             h[activehd]->used -= OFF_T_ATOI(result_argv[3]);
1940             h[activehd]->reserved -= OFF_T_ATOI(result_argv[3]);
1941             h[activehd]->disk->allocated_space -= OFF_T_ATOI(result_argv[3]);
1942             h[activehd]->disk->disksize -= OFF_T_ATOI(result_argv[3]);
1943             break;
1944
1945         case RQ_MORE_DISK: /* RQ-MORE-DISK <handle> */
1946             if (!h || activehd < 0) { /* should never happen */
1947                 error(_("!h || activehd < 0"));
1948                 /*NOTREACHED*/
1949             }
1950             h[activehd]->disk->allocated_dumpers--;
1951             h[activehd]->used = h[activehd]->reserved;
1952             if( h[++activehd] ) { /* There's still some allocated space left.
1953                                    * Tell the dumper about it. */
1954                 sched(dp)->activehd++;
1955                 chunker_cmd( chunker, CONTINUE, dp );
1956             } else { /* !h[++activehd] - must allocate more space */
1957                 sched(dp)->act_size = sched(dp)->est_size; /* not quite true */
1958                 sched(dp)->est_size = (sched(dp)->act_size/(off_t)20) * (off_t)21; /* +5% */
1959                 sched(dp)->est_size = am_round(sched(dp)->est_size, (off_t)DISK_BLOCK_KB);
1960                 if (sched(dp)->est_size < sched(dp)->act_size + 2*DISK_BLOCK_KB)
1961                     sched(dp)->est_size += 2 * DISK_BLOCK_KB;
1962                 h = find_diskspace( sched(dp)->est_size - sched(dp)->act_size,
1963                                     &dummy,
1964                                     h[activehd-1] );
1965                 if( !h ) {
1966                     /* No diskspace available. The reason for this will be
1967                      * determined in continue_port_dumps(). */
1968                     enqueue_disk( &roomq, dp );
1969                     continue_port_dumps();
1970                 } else {
1971                     /* OK, allocate space for disk and have chunker continue */
1972                     sched(dp)->activehd = assign_holdingdisk( h, dp );
1973                     chunker_cmd( chunker, CONTINUE, dp );
1974                     amfree(h);
1975                 }
1976             }
1977             break;
1978
1979         case ABORT_FINISHED: /* ABORT-FINISHED <handle> */
1980             /*
1981              * We sent an ABORT from the NO-ROOM case because this dump
1982              * wasn't going to fit onto the holding disk.  We now need to
1983              * clean up the remains of this image, and try to finish
1984              * other dumps that are waiting on disk space.
1985              */
1986             /*assert(pending_aborts);*/
1987
1988             /*free_serial(result_argv[2]);*/
1989
1990             event_release(chunker->ev_read);
1991
1992             chunker->result = cmd;
1993
1994             break;
1995
1996         case BOGUS:
1997             /* either EOF or garbage from chunker.  Turn it off */
1998             log_add(L_WARNING, _("%s pid %ld is messed up, ignoring it.\n"),
1999                     chunker->name, (long)chunker->pid);
2000
2001             if(dp) {
2002                 /* if it was dumping something, zap it and try again */
2003                 if (!h || activehd < 0) { /* should never happen */
2004                     error(_("!h || activehd < 0"));
2005                     /*NOTREACHED*/
2006                 }
2007                 qname = quote_string(dp->name);
2008                 if(sched(dp)->dump_attempted) {
2009                     log_add(L_FAIL, _("%s %s %s %d [%s died]"),
2010                             dp->host->hostname, qname, sched(dp)->datestamp,
2011                             sched(dp)->level, chunker->name);
2012                 }
2013                 else {
2014                     log_add(L_WARNING, _("%s died while dumping %s:%s lev %d."),
2015                             chunker->name, dp->host->hostname, qname,
2016                             sched(dp)->level);
2017                 }
2018                 amfree(qname);
2019                 dp = NULL;
2020             }
2021
2022             event_release(chunker->ev_read);
2023
2024             chunker->result = cmd;
2025
2026             break;
2027
2028         default:
2029             assert(0);
2030         }
2031
2032         if(chunker->result != LAST_TOK && chunker->dumper->result != LAST_TOK)
2033             dumper_chunker_result(dp);
2034
2035     } while(areads_dataready(chunker->fd));
2036 }
2037
2038
2039 static disklist_t
2040 read_flush(void)
2041 {
2042     sched_t *sp;
2043     disk_t *dp;
2044     int line;
2045     dumpfile_t file;
2046     char *hostname, *diskname, *datestamp;
2047     int level;
2048     char *destname;
2049     disk_t *dp1;
2050     char *inpline = NULL;
2051     char *command;
2052     char *s;
2053     int ch;
2054     disklist_t tq;
2055     char *qname = NULL;
2056     char *qdestname = NULL;
2057
2058     tq.head = tq.tail = NULL;
2059
2060     for(line = 0; (inpline = agets(stdin)) != NULL; free(inpline)) {
2061         line++;
2062         if (inpline[0] == '\0')
2063             continue;
2064
2065         s = inpline;
2066         ch = *s++;
2067
2068         skip_whitespace(s, ch);                 /* find the command */
2069         if(ch == '\0') {
2070             error(_("flush line %d: syntax error (no command)"), line);
2071             /*NOTREACHED*/
2072         }
2073         command = s - 1;
2074         skip_non_whitespace(s, ch);
2075         s[-1] = '\0';
2076
2077         if(strcmp(command,"ENDFLUSH") == 0) {
2078             break;
2079         }
2080
2081         if(strcmp(command,"FLUSH") != 0) {
2082             error(_("flush line %d: syntax error (%s != FLUSH)"), line, command);
2083             /*NOTREACHED*/
2084         }
2085
2086         skip_whitespace(s, ch);                 /* find the hostname */
2087         if(ch == '\0') {
2088             error(_("flush line %d: syntax error (no hostname)"), line);
2089             /*NOTREACHED*/
2090         }
2091         hostname = s - 1;
2092         skip_non_whitespace(s, ch);
2093         s[-1] = '\0';
2094
2095         skip_whitespace(s, ch);                 /* find the diskname */
2096         if(ch == '\0') {
2097             error(_("flush line %d: syntax error (no diskname)"), line);
2098             /*NOTREACHED*/
2099         }
2100         qname = s - 1;
2101         skip_quoted_string(s, ch);
2102         s[-1] = '\0';                           /* terminate the disk name */
2103         diskname = unquote_string(qname);
2104
2105         skip_whitespace(s, ch);                 /* find the datestamp */
2106         if(ch == '\0') {
2107             error(_("flush line %d: syntax error (no datestamp)"), line);
2108             /*NOTREACHED*/
2109         }
2110         datestamp = s - 1;
2111         skip_non_whitespace(s, ch);
2112         s[-1] = '\0';
2113
2114         skip_whitespace(s, ch);                 /* find the level number */
2115         if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
2116             error(_("flush line %d: syntax error (bad level)"), line);
2117             /*NOTREACHED*/
2118         }
2119         skip_integer(s, ch);
2120
2121         skip_whitespace(s, ch);                 /* find the filename */
2122         if(ch == '\0') {
2123             error(_("flush line %d: syntax error (no filename)"), line);
2124             /*NOTREACHED*/
2125         }
2126         qdestname = s - 1;
2127         skip_quoted_string(s, ch);
2128         s[-1] = '\0';
2129         destname = unquote_string(qdestname);
2130
2131         holding_file_get_dumpfile(destname, &file);
2132         if( file.type != F_DUMPFILE) {
2133             if( file.type != F_CONT_DUMPFILE )
2134                 log_add(L_INFO, _("%s: ignoring cruft file."), destname);
2135             amfree(diskname);
2136             amfree(destname);
2137             continue;
2138         }
2139
2140         if(strcmp(hostname, file.name) != 0 ||
2141            strcmp(diskname, file.disk) != 0 ||
2142            strcmp(datestamp, file.datestamp) != 0) {
2143             log_add(L_INFO, _("disk %s:%s not consistent with file %s"),
2144                     hostname, diskname, destname);
2145             amfree(diskname);
2146             amfree(destname);
2147             continue;
2148         }
2149         amfree(diskname);
2150
2151         dp = lookup_disk(file.name, file.disk);
2152
2153         if (dp == NULL) {
2154             log_add(L_INFO, _("%s: disk %s:%s not in database, skipping it."),
2155                     destname, file.name, file.disk);
2156             amfree(destname);
2157             continue;
2158         }
2159
2160         if(file.dumplevel < 0 || file.dumplevel > 9) {
2161             log_add(L_INFO, _("%s: ignoring file with bogus dump level %d."),
2162                     destname, file.dumplevel);
2163             amfree(destname);
2164             continue;
2165         }
2166
2167         if (holding_file_size(destname,1) <= 0) {
2168             log_add(L_INFO, "%s: removing file with no data.", destname);
2169             holding_file_unlink(destname);
2170             amfree(destname);
2171             continue;
2172         }
2173
2174         dp1 = (disk_t *)alloc(SIZEOF(disk_t));
2175         *dp1 = *dp;
2176         dp1->next = dp1->prev = NULL;
2177
2178         /* add it to the flushhost list */
2179         if(!flushhost) {
2180             flushhost = alloc(SIZEOF(am_host_t));
2181             flushhost->next = NULL;
2182             flushhost->hostname = stralloc("FLUSHHOST");
2183             flushhost->up = NULL;
2184             flushhost->features = NULL;
2185         }
2186         dp1->hostnext = flushhost->disks;
2187         flushhost->disks = dp1;
2188
2189         sp = (sched_t *) alloc(SIZEOF(sched_t));
2190         sp->destname = destname;
2191         sp->level = file.dumplevel;
2192         sp->dumpdate = NULL;
2193         sp->degr_dumpdate = NULL;
2194         sp->datestamp = stralloc(file.datestamp);
2195         sp->est_nsize = (off_t)0;
2196         sp->est_csize = (off_t)0;
2197         sp->est_time = 0;
2198         sp->est_kps = 10;
2199         sp->priority = 0;
2200         sp->degr_level = -1;
2201         sp->dump_attempted = 0;
2202         sp->taper_attempted = 0;
2203         sp->act_size = holding_file_size(destname, 0);
2204         sp->holdp = build_diskspace(destname);
2205         if(sp->holdp == NULL) continue;
2206         sp->dumper = NULL;
2207         sp->timestamp = (time_t)0;
2208
2209         dp1->up = (char *)sp;
2210
2211         enqueue_disk(&tq, dp1);
2212     }
2213     amfree(inpline);
2214
2215     /*@i@*/ return tq;
2216 }
2217
2218 static void
2219 read_schedule(
2220     void *      cookie)
2221 {
2222     sched_t *sp;
2223     disk_t *dp;
2224     int level, line, priority;
2225     char *dumpdate, *degr_dumpdate;
2226     int degr_level;
2227     time_t time, degr_time;
2228     time_t *time_p = &time;
2229     time_t *degr_time_p = &degr_time;
2230     off_t nsize, csize, degr_nsize, degr_csize;
2231     unsigned long kps, degr_kps;
2232     char *hostname, *features, *diskname, *datestamp, *inpline = NULL;
2233     char *command;
2234     char *s;
2235     int ch;
2236     off_t flush_size = (off_t)0;
2237     char *qname = NULL;
2238     long long time_;
2239     long long nsize_;
2240     long long csize_;
2241     long long degr_nsize_;
2242     long long degr_csize_;
2243
2244     (void)cookie;       /* Quiet unused parameter warning */
2245
2246     event_release(schedule_ev_read);
2247
2248     /* read schedule from stdin */
2249
2250     for(line = 0; (inpline = agets(stdin)) != NULL; free(inpline)) {
2251         if (inpline[0] == '\0')
2252             continue;
2253         line++;
2254
2255         s = inpline;
2256         ch = *s++;
2257
2258         skip_whitespace(s, ch);                 /* find the command */
2259         if(ch == '\0') {
2260             error(_("schedule line %d: syntax error (no command)"), line);
2261             /*NOTREACHED*/
2262         }
2263         command = s - 1;
2264         skip_non_whitespace(s, ch);
2265         s[-1] = '\0';
2266
2267         if(strcmp(command,"DUMP") != 0) {
2268             error(_("schedule line %d: syntax error (%s != DUMP)"), line, command);
2269             /*NOTREACHED*/
2270         }
2271
2272         skip_whitespace(s, ch);                 /* find the host name */
2273         if(ch == '\0') {
2274             error(_("schedule line %d: syntax error (no host name)"), line);
2275             /*NOTREACHED*/
2276         }
2277         hostname = s - 1;
2278         skip_non_whitespace(s, ch);
2279         s[-1] = '\0';
2280
2281         skip_whitespace(s, ch);                 /* find the feature list */
2282         if(ch == '\0') {
2283             error(_("schedule line %d: syntax error (no feature list)"), line);
2284             /*NOTREACHED*/
2285         }
2286         features = s - 1;
2287         skip_non_whitespace(s, ch);
2288         s[-1] = '\0';
2289
2290         skip_whitespace(s, ch);                 /* find the disk name */
2291         if(ch == '\0') {
2292             error(_("schedule line %d: syntax error (no disk name)"), line);
2293             /*NOTREACHED*/
2294         }
2295         qname = s - 1;
2296         skip_quoted_string(s, ch);
2297         s[-1] = '\0';                           /* terminate the disk name */
2298         diskname = unquote_string(qname);
2299
2300         skip_whitespace(s, ch);                 /* find the datestamp */
2301         if(ch == '\0') {
2302             error(_("schedule line %d: syntax error (no datestamp)"), line);
2303             /*NOTREACHED*/
2304         }
2305         datestamp = s - 1;
2306         skip_non_whitespace(s, ch);
2307         s[-1] = '\0';
2308
2309         skip_whitespace(s, ch);                 /* find the priority number */
2310         if(ch == '\0' || sscanf(s - 1, "%d", &priority) != 1) {
2311             error(_("schedule line %d: syntax error (bad priority)"), line);
2312             /*NOTREACHED*/
2313         }
2314         skip_integer(s, ch);
2315
2316         skip_whitespace(s, ch);                 /* find the level number */
2317         if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
2318             error(_("schedule line %d: syntax error (bad level)"), line);
2319             /*NOTREACHED*/
2320         }
2321         skip_integer(s, ch);
2322
2323         skip_whitespace(s, ch);                 /* find the dump date */
2324         if(ch == '\0') {
2325             error(_("schedule line %d: syntax error (bad dump date)"), line);
2326             /*NOTREACHED*/
2327         }
2328         dumpdate = s - 1;
2329         skip_non_whitespace(s, ch);
2330         s[-1] = '\0';
2331
2332         skip_whitespace(s, ch);                 /* find the native size */
2333         nsize_ = (off_t)0;
2334         if(ch == '\0' || sscanf(s - 1, "%lld", &nsize_) != 1) {
2335             error(_("schedule line %d: syntax error (bad nsize)"), line);
2336             /*NOTREACHED*/
2337         }
2338         nsize = (off_t)nsize_;
2339         skip_integer(s, ch);
2340
2341         skip_whitespace(s, ch);                 /* find the compressed size */
2342         csize_ = (off_t)0;
2343         if(ch == '\0' || sscanf(s - 1, "%lld", &csize_) != 1) {
2344             error(_("schedule line %d: syntax error (bad csize)"), line);
2345             /*NOTREACHED*/
2346         }
2347         csize = (off_t)csize_;
2348         skip_integer(s, ch);
2349
2350         skip_whitespace(s, ch);                 /* find the time number */
2351         if(ch == '\0' || sscanf(s - 1, "%lld", &time_) != 1) {
2352             error(_("schedule line %d: syntax error (bad estimated time)"), line);
2353             /*NOTREACHED*/
2354         }
2355         *time_p = (time_t)time_;
2356         skip_integer(s, ch);
2357
2358         skip_whitespace(s, ch);                 /* find the kps number */
2359         if(ch == '\0' || sscanf(s - 1, "%lu", &kps) != 1) {
2360             error(_("schedule line %d: syntax error (bad kps)"), line);
2361             continue;
2362         }
2363         skip_integer(s, ch);
2364
2365         degr_dumpdate = NULL;                   /* flag if degr fields found */
2366         skip_whitespace(s, ch);                 /* find the degr level number */
2367         if(ch != '\0') {
2368             if(sscanf(s - 1, "%d", &degr_level) != 1) {
2369                 error(_("schedule line %d: syntax error (bad degr level)"), line);
2370                 /*NOTREACHED*/
2371             }
2372             skip_integer(s, ch);
2373
2374             skip_whitespace(s, ch);             /* find the degr dump date */
2375             if(ch == '\0') {
2376                 error(_("schedule line %d: syntax error (bad degr dump date)"), line);
2377                 /*NOTREACHED*/
2378             }
2379             degr_dumpdate = s - 1;
2380             skip_non_whitespace(s, ch);
2381             s[-1] = '\0';
2382
2383             skip_whitespace(s, ch);             /* find the degr native size */
2384             degr_nsize_ = (off_t)0;
2385             if(ch == '\0'  || sscanf(s - 1, "%lld", &degr_nsize_) != 1) {
2386                 error(_("schedule line %d: syntax error (bad degr nsize)"), line);
2387                 /*NOTREACHED*/
2388             }
2389             degr_nsize = (off_t)degr_nsize_;
2390             skip_integer(s, ch);
2391
2392             skip_whitespace(s, ch);             /* find the degr compressed size */
2393             degr_csize_ = (off_t)0;
2394             if(ch == '\0'  || sscanf(s - 1, "%lld", &degr_csize_) != 1) {
2395                 error(_("schedule line %d: syntax error (bad degr csize)"), line);
2396                 /*NOTREACHED*/
2397             }
2398             degr_csize = (off_t)degr_csize_;
2399             skip_integer(s, ch);
2400
2401             skip_whitespace(s, ch);             /* find the degr time number */
2402             if(ch == '\0' || sscanf(s - 1, "%lld", &time_) != 1) {
2403                 error(_("schedule line %d: syntax error (bad degr estimated time)"), line);
2404                 /*NOTREACHED*/
2405             }
2406             *degr_time_p = (time_t)time_;
2407             skip_integer(s, ch);
2408
2409             skip_whitespace(s, ch);             /* find the degr kps number */
2410             if(ch == '\0' || sscanf(s - 1, "%lu", &degr_kps) != 1) {
2411                 error(_("schedule line %d: syntax error (bad degr kps)"), line);
2412                 /*NOTREACHED*/
2413             }
2414             skip_integer(s, ch);
2415         } else {
2416             degr_level = -1;
2417             degr_nsize = (off_t)0;
2418             degr_csize = (off_t)0;
2419             degr_time = (time_t)0;
2420             degr_kps = 0;
2421         }
2422
2423         dp = lookup_disk(hostname, diskname);
2424         if(dp == NULL) {
2425             log_add(L_WARNING,
2426                     _("schedule line %d: %s:'%s' not in disklist, ignored"),
2427                     line, hostname, qname);
2428             amfree(diskname);
2429             continue;
2430         }
2431
2432         sp = (sched_t *) alloc(SIZEOF(sched_t));
2433         /*@ignore@*/
2434         sp->level = level;
2435         sp->dumpdate = stralloc(dumpdate);
2436         sp->est_nsize = DISK_BLOCK_KB + nsize; /* include header */
2437         sp->est_csize = DISK_BLOCK_KB + csize; /* include header */
2438         /* round estimate to next multiple of DISK_BLOCK_KB */
2439         sp->est_csize = am_round(sp->est_csize, DISK_BLOCK_KB);
2440         sp->est_size = sp->est_csize;
2441         sp->est_time = time;
2442         sp->est_kps = kps;
2443         sp->priority = priority;
2444         sp->datestamp = stralloc(datestamp);
2445
2446         if(degr_dumpdate) {
2447             sp->degr_level = degr_level;
2448             sp->degr_dumpdate = stralloc(degr_dumpdate);
2449             sp->degr_nsize = DISK_BLOCK_KB + degr_nsize;
2450             sp->degr_csize = DISK_BLOCK_KB + degr_csize;
2451             /* round estimate to next multiple of DISK_BLOCK_KB */
2452             sp->degr_csize = am_round(sp->degr_csize, DISK_BLOCK_KB);
2453             sp->degr_time = degr_time;
2454             sp->degr_kps = degr_kps;
2455         } else {
2456             sp->degr_level = -1;
2457             sp->degr_dumpdate = NULL;
2458         }
2459         /*@end@*/
2460
2461         sp->dump_attempted = 0;
2462         sp->taper_attempted = 0;
2463         sp->act_size = 0;
2464         sp->holdp = NULL;
2465         sp->activehd = -1;
2466         sp->dumper = NULL;
2467         sp->timestamp = (time_t)0;
2468         sp->destname = NULL;
2469         sp->no_space = 0;
2470
2471         dp->up = (char *) sp;
2472         if(dp->host->features == NULL) {
2473             dp->host->features = am_string_to_feature(features);
2474         }
2475         remove_disk(&waitq, dp);
2476         if (dp->to_holdingdisk == HOLD_NEVER) {
2477             enqueue_disk(&directq, dp);
2478         } else {
2479             enqueue_disk(&runq, dp);
2480         }
2481         flush_size += sp->act_size;
2482         amfree(diskname);
2483     }
2484     g_printf(_("driver: flush size %lld\n"), (long long)flush_size);
2485     amfree(inpline);
2486     if(line == 0)
2487         log_add(L_WARNING, _("WARNING: got empty schedule from planner"));
2488     if(need_degraded==1) start_degraded_mode(&runq);
2489     schedule_done = 1;
2490     start_some_dumps(&runq);
2491 }
2492
2493 static unsigned long
2494 free_kps(
2495     netif_t *ip)
2496 {
2497     unsigned long res;
2498
2499     if (ip == NULL) {
2500         netif_t *p;
2501         unsigned long maxusage=0;
2502         unsigned long curusage=0;
2503         for(p = disklist_netifs(); p != NULL; p = p->next) {
2504             maxusage += interface_get_maxusage(p->config);
2505             curusage += p->curusage;
2506         }
2507         if (maxusage >= curusage)
2508             res = maxusage - curusage;
2509         else
2510             res = 0;
2511 #ifndef __lint
2512     } else {
2513         if ((unsigned long)interface_get_maxusage(ip->config) >= ip->curusage)
2514             res = interface_get_maxusage(ip->config) - ip->curusage;
2515         else
2516             res = 0;
2517 #endif
2518     }
2519
2520     return res;
2521 }
2522
2523 static void
2524 interface_state(
2525     char *time_str)
2526 {
2527     netif_t *ip;
2528
2529     g_printf(_("driver: interface-state time %s"), time_str);
2530
2531     for(ip = disklist_netifs(); ip != NULL; ip = ip->next) {
2532         g_printf(_(" if %s: free %lu"), interface_name(ip->config), free_kps(ip));
2533     }
2534     g_printf("\n");
2535 }
2536
2537 static void
2538 allocate_bandwidth(
2539     netif_t *           ip,
2540     unsigned long       kps)
2541 {
2542     ip->curusage += kps;
2543 }
2544
2545 static void
2546 deallocate_bandwidth(
2547     netif_t *           ip,
2548     unsigned long       kps)
2549 {
2550     assert(kps <= ip->curusage);
2551     ip->curusage -= kps;
2552 }
2553
2554 /* ------------ */
2555 static off_t
2556 free_space(void)
2557 {
2558     holdalloc_t *ha;
2559     off_t total_free;
2560     off_t diff;
2561
2562     total_free = (off_t)0;
2563     for(ha = holdalloc; ha != NULL; ha = ha->next) {
2564         diff = ha->disksize - ha->allocated_space;
2565         if(diff > (off_t)0)
2566             total_free += diff;
2567     }
2568     return total_free;
2569 }
2570
2571 /*
2572  * We return an array of pointers to assignedhd_t. The array contains at
2573  * most one entry per holding disk. The list of pointers is terminated by
2574  * a NULL pointer. Each entry contains a pointer to a holdingdisk and
2575  * how much diskspace to use on that disk. Later on, assign_holdingdisk
2576  * will allocate the given amount of space.
2577  * If there is not enough room on the holdingdisks, NULL is returned.
2578  */
2579
2580 static assignedhd_t **
2581 find_diskspace(
2582     off_t               size,
2583     int *               cur_idle,
2584     assignedhd_t *      pref)
2585 {
2586     assignedhd_t **result = NULL;
2587     holdalloc_t *ha, *minp;
2588     int i=0;
2589     int j, minj;
2590     char *used;
2591     off_t halloc, dalloc, hfree, dfree;
2592
2593     (void)cur_idle;     /* Quiet unused parameter warning */
2594
2595     if (size < 2*DISK_BLOCK_KB)
2596         size = 2*DISK_BLOCK_KB;
2597     size = am_round(size, (off_t)DISK_BLOCK_KB);
2598
2599     hold_debug(1, _("find_diskspace: want %lld K\n"),
2600                    (long long)size);
2601
2602     used = alloc(SIZEOF(*used) * num_holdalloc);/*disks used during this run*/
2603     memset( used, 0, (size_t)num_holdalloc );
2604     result = alloc(SIZEOF(assignedhd_t *) * (num_holdalloc + 1));
2605     result[0] = NULL;
2606
2607     while( i < num_holdalloc && size > (off_t)0 ) {
2608         /* find the holdingdisk with the fewest active dumpers and among
2609          * those the one with the biggest free space
2610          */
2611         minp = NULL; minj = -1;
2612         for(j = 0, ha = holdalloc; ha != NULL; ha = ha->next, j++ ) {
2613             if( pref && pref->disk == ha && !used[j] &&
2614                 ha->allocated_space <= ha->disksize - (off_t)DISK_BLOCK_KB) {
2615                 minp = ha;
2616                 minj = j;
2617                 break;
2618             }
2619             else if( ha->allocated_space <= ha->disksize - (off_t)(2*DISK_BLOCK_KB) &&
2620                 !used[j] &&
2621                 (!minp ||
2622                  ha->allocated_dumpers < minp->allocated_dumpers ||
2623                  (ha->allocated_dumpers == minp->allocated_dumpers &&
2624                   ha->disksize-ha->allocated_space > minp->disksize-minp->allocated_space)) ) {
2625                 minp = ha;
2626                 minj = j;
2627             }
2628         }
2629
2630         pref = NULL;
2631         if( !minp ) { break; } /* all holding disks are full */
2632         used[minj] = 1;
2633
2634         /* hfree = free space on the disk */
2635         hfree = minp->disksize - minp->allocated_space;
2636
2637         /* dfree = free space for data, remove 1 header for each chunksize */
2638         dfree = hfree - (((hfree-(off_t)1)/holdingdisk_get_chunksize(minp->hdisk))+(off_t)1) * (off_t)DISK_BLOCK_KB;
2639
2640         /* dalloc = space I can allocate for data */
2641         dalloc = ( dfree < size ) ? dfree : size;
2642
2643         /* halloc = space to allocate, including 1 header for each chunksize */
2644         halloc = dalloc + (((dalloc-(off_t)1)/holdingdisk_get_chunksize(minp->hdisk))+(off_t)1) * (off_t)DISK_BLOCK_KB;
2645
2646         hold_debug(1, _("find_diskspace: find diskspace: size %lld hf %lld df %lld da %lld ha %lld\n"),
2647                        (long long)size,
2648                        (long long)hfree,
2649                        (long long)dfree,
2650                        (long long)dalloc,
2651                        (long long)halloc);
2652         size -= dalloc;
2653         result[i] = alloc(SIZEOF(assignedhd_t));
2654         result[i]->disk = minp;
2655         result[i]->reserved = halloc;
2656         result[i]->used = (off_t)0;
2657         result[i]->destname = NULL;
2658         result[i+1] = NULL;
2659         i++;
2660     }
2661     amfree(used);
2662
2663     if(size != (off_t)0) { /* not enough space available */
2664         g_printf(_("find diskspace: not enough diskspace. Left with %lld K\n"), (long long)size);
2665         fflush(stdout);
2666         free_assignedhd(result);
2667         result = NULL;
2668     }
2669
2670     if (debug_holding > 1) {
2671         for( i = 0; result && result[i]; i++ ) {
2672             hold_debug(1, _("find_diskspace: find diskspace: selected %s free %lld reserved %lld dumpers %d\n"),
2673                            holdingdisk_get_diskdir(result[i]->disk->hdisk),
2674                            (long long)(result[i]->disk->disksize -
2675                              result[i]->disk->allocated_space),
2676                            (long long)result[i]->reserved,
2677                            result[i]->disk->allocated_dumpers);
2678         }
2679     }
2680
2681     return result;
2682 }
2683
2684 static int
2685 assign_holdingdisk(
2686     assignedhd_t **     holdp,
2687     disk_t *            diskp)
2688 {
2689     int i, j, c, l=0;
2690     off_t size;
2691     char *sfn = sanitise_filename(diskp->name);
2692     char lvl[64];
2693     assignedhd_t **new_holdp;
2694     char *qname;
2695
2696     g_snprintf( lvl, SIZEOF(lvl), "%d", sched(diskp)->level );
2697
2698     size = am_round(sched(diskp)->est_size - sched(diskp)->act_size,
2699                     (off_t)DISK_BLOCK_KB);
2700
2701     for( c = 0; holdp[c]; c++ )
2702         (void)c; /* count number of disks */
2703
2704     /* allocate memory for sched(diskp)->holdp */
2705     for(j = 0; sched(diskp)->holdp && sched(diskp)->holdp[j]; j++)
2706         (void)j;        /* Quiet lint */
2707     new_holdp = (assignedhd_t **)alloc(SIZEOF(assignedhd_t*)*(j+c+1));
2708     if (sched(diskp)->holdp) {
2709         memcpy(new_holdp, sched(diskp)->holdp, j * SIZEOF(*new_holdp));
2710         amfree(sched(diskp)->holdp);
2711     }
2712     sched(diskp)->holdp = new_holdp;
2713     new_holdp = NULL;
2714
2715     i = 0;
2716     if( j > 0 ) { /* This is a request for additional diskspace. See if we can
2717                    * merge assignedhd_t's */
2718         l=j;
2719         if( sched(diskp)->holdp[j-1]->disk == holdp[0]->disk ) { /* Yes! */
2720             sched(diskp)->holdp[j-1]->reserved += holdp[0]->reserved;
2721             holdp[0]->disk->allocated_space += holdp[0]->reserved;
2722             size = (holdp[0]->reserved>size) ? (off_t)0 : size-holdp[0]->reserved;
2723             qname = quote_string(diskp->name);
2724             hold_debug(1, _("assign_holdingdisk: merging holding disk %s to disk %s:%s, add %lld for reserved %lld, left %lld\n"),
2725                            holdingdisk_get_diskdir(
2726                                                sched(diskp)->holdp[j-1]->disk->hdisk),
2727                            diskp->host->hostname, qname,
2728                            (long long)holdp[0]->reserved,
2729                            (long long)sched(diskp)->holdp[j-1]->reserved,
2730                            (long long)size);
2731             i++;
2732             amfree(qname);
2733             amfree(holdp[0]);
2734             l=j-1;
2735         }
2736     }
2737
2738     /* copy assignedhd_s to sched(diskp), adjust allocated_space */
2739     for( ; holdp[i]; i++ ) {
2740         holdp[i]->destname = newvstralloc( holdp[i]->destname,
2741                                            holdingdisk_get_diskdir(holdp[i]->disk->hdisk), "/",
2742                                            hd_driver_timestamp, "/",
2743                                            diskp->host->hostname, ".",
2744                                            sfn, ".",
2745                                            lvl, NULL );
2746         sched(diskp)->holdp[j++] = holdp[i];
2747         holdp[i]->disk->allocated_space += holdp[i]->reserved;
2748         size = (holdp[i]->reserved > size) ? (off_t)0 :
2749                   (size - holdp[i]->reserved);
2750         qname = quote_string(diskp->name);
2751         hold_debug(1,
2752                    _("assign_holdingdisk: %d assigning holding disk %s to disk %s:%s, reserved %lld, left %lld\n"),
2753                     i, holdingdisk_get_diskdir(holdp[i]->disk->hdisk),
2754                     diskp->host->hostname, qname,
2755                     (long long)holdp[i]->reserved,
2756                     (long long)size);
2757         amfree(qname);
2758         holdp[i] = NULL; /* so it doesn't get free()d... */
2759     }
2760     sched(diskp)->holdp[j] = NULL;
2761     amfree(sfn);
2762
2763     return l;
2764 }
2765
2766 static void
2767 adjust_diskspace(
2768     disk_t *    diskp,
2769     cmd_t       cmd)
2770 {
2771     assignedhd_t **holdp;
2772     off_t total = (off_t)0;
2773     off_t diff;
2774     int i;
2775     char *qname, *hqname, *qdest;
2776
2777     (void)cmd;  /* Quiet unused parameter warning */
2778
2779     qname = quote_string(diskp->name);
2780     qdest = quote_string(sched(diskp)->destname);
2781     hold_debug(1, _("adjust_diskspace: %s:%s %s\n"),
2782                    diskp->host->hostname, qname, qdest);
2783
2784     holdp = sched(diskp)->holdp;
2785
2786     assert(holdp != NULL);
2787
2788     for( i = 0; holdp[i]; i++ ) { /* for each allocated disk */
2789         diff = holdp[i]->used - holdp[i]->reserved;
2790         total += holdp[i]->used;
2791         holdp[i]->disk->allocated_space += diff;
2792         hqname = quote_string(holdingdisk_name(holdp[i]->disk->hdisk));
2793         hold_debug(1, _("adjust_diskspace: hdisk %s done, reserved %lld used %lld diff %lld alloc %lld dumpers %d\n"),
2794                        holdingdisk_name(holdp[i]->disk->hdisk),
2795                        (long long)holdp[i]->reserved,
2796                        (long long)holdp[i]->used,
2797                        (long long)diff,
2798                        (long long)holdp[i]->disk->allocated_space,
2799                        holdp[i]->disk->allocated_dumpers );
2800         holdp[i]->reserved += diff;
2801         amfree(hqname);
2802     }
2803
2804     sched(diskp)->act_size = total;
2805
2806     hold_debug(1, _("adjust_diskspace: after: disk %s:%s used %lld\n"),
2807                    diskp->host->hostname, qname,
2808                    (long long)sched(diskp)->act_size);
2809     amfree(qdest);
2810     amfree(qname);
2811 }
2812
2813 static void
2814 delete_diskspace(
2815     disk_t *diskp)
2816 {
2817     assignedhd_t **holdp;
2818     int i;
2819
2820     holdp = sched(diskp)->holdp;
2821
2822     assert(holdp != NULL);
2823
2824     for( i = 0; holdp[i]; i++ ) { /* for each disk */
2825         /* find all files of this dump on that disk, and subtract their
2826          * reserved sizes from the disk's allocated space
2827          */
2828         holdp[i]->disk->allocated_space -= holdp[i]->used;
2829     }
2830
2831     holding_file_unlink(holdp[0]->destname);    /* no need for the entire list,
2832                                                  * because holding_file_unlink
2833                                                  * will walk through all files
2834                                                  * using cont_filename */
2835     free_assignedhd(sched(diskp)->holdp);
2836     sched(diskp)->holdp = NULL;
2837     sched(diskp)->act_size = (off_t)0;
2838 }
2839
2840 static assignedhd_t **
2841 build_diskspace(
2842     char *      destname)
2843 {
2844     int i, j;
2845     int fd;
2846     ssize_t buflen;
2847     char buffer[DISK_BLOCK_BYTES];
2848     dumpfile_t file;
2849     assignedhd_t **result;
2850     holdalloc_t *ha;
2851     off_t *used;
2852     char dirname[1000], *ch;
2853     struct stat finfo;
2854     char *filename = destname;
2855
2856     memset(buffer, 0, sizeof(buffer));
2857     used = alloc(SIZEOF(off_t) * num_holdalloc);
2858     for(i=0;i<num_holdalloc;i++)
2859         used[i] = (off_t)0;
2860     result = alloc(SIZEOF(assignedhd_t *) * (num_holdalloc + 1));
2861     result[0] = NULL;
2862     while(filename != NULL && filename[0] != '\0') {
2863         strncpy(dirname, filename, 999);
2864         dirname[999]='\0';
2865         ch = strrchr(dirname,'/');
2866         *ch = '\0';
2867         ch = strrchr(dirname,'/');
2868         *ch = '\0';
2869
2870         for(j = 0, ha = holdalloc; ha != NULL; ha = ha->next, j++ ) {
2871             if(strcmp(dirname, holdingdisk_get_diskdir(ha->hdisk))==0) {
2872                 break;
2873             }
2874         }
2875
2876         if(stat(filename, &finfo) == -1) {
2877             g_fprintf(stderr, _("stat %s: %s\n"), filename, strerror(errno));
2878             finfo.st_size = (off_t)0;
2879         }
2880         used[j] += ((off_t)finfo.st_size+(off_t)1023)/(off_t)1024;
2881         if((fd = open(filename,O_RDONLY)) == -1) {
2882             g_fprintf(stderr,_("build_diskspace: open of %s failed: %s\n"),
2883                     filename, strerror(errno));
2884             return NULL;
2885         }
2886         if ((buflen = fullread(fd, buffer, SIZEOF(buffer))) > 0) {;
2887                 parse_file_header(buffer, &file, (size_t)buflen);
2888         }
2889         close(fd);
2890         filename = file.cont_filename;
2891     }
2892
2893     for(j = 0, i=0, ha = holdalloc; ha != NULL; ha = ha->next, j++ ) {
2894         if(used[j] != (off_t)0) {
2895             result[i] = alloc(SIZEOF(assignedhd_t));
2896             result[i]->disk = ha;
2897             result[i]->reserved = used[j];
2898             result[i]->used = used[j];
2899             result[i]->destname = stralloc(destname);
2900             result[i+1] = NULL;
2901             i++;
2902         }
2903     }
2904
2905     amfree(used);
2906     return result;
2907 }
2908
2909 static void
2910 holdingdisk_state(
2911     char *      time_str)
2912 {
2913     holdalloc_t *ha;
2914     int dsk;
2915     off_t diff;
2916
2917     g_printf(_("driver: hdisk-state time %s"), time_str);
2918
2919     for(ha = holdalloc, dsk = 0; ha != NULL; ha = ha->next, dsk++) {
2920         diff = ha->disksize - ha->allocated_space;
2921         g_printf(_(" hdisk %d: free %lld dumpers %d"), dsk,
2922                (long long)diff, ha->allocated_dumpers);
2923     }
2924     g_printf("\n");
2925 }
2926
2927 static void
2928 update_failed_dump_to_tape(
2929     disk_t *    dp)
2930 {
2931 /* JLM
2932  * should simply set no_bump
2933  */
2934
2935     time_t save_timestamp = sched(dp)->timestamp;
2936     /* setting timestamp to 0 removes the current level from the
2937      * database, so that we ensure that it will not be bumped to the
2938      * next level on the next run.  If we didn't do this, dumpdates or
2939      * gnutar-lists might have been updated already, and a bumped
2940      * incremental might be created.  */
2941     sched(dp)->timestamp = 0;
2942     update_info_dumper(dp, (off_t)-1, (off_t)-1, (time_t)-1);
2943     sched(dp)->timestamp = save_timestamp;
2944 }
2945
2946 /* ------------------- */
2947 static void
2948 dump_to_tape(
2949     disk_t *    dp)
2950 {
2951     dumper_t *dumper;
2952     cmd_t cmd;
2953     int result_argc;
2954     char *result_argv[MAX_ARGS+1];
2955     char *qname;
2956
2957     qname = quote_string(dp->name);
2958     g_printf(_("driver: dumping %s:%s directly to tape\n"),
2959            dp->host->hostname, qname);
2960     fflush(stdout);
2961
2962     /* pick a dumper and fail if there are no idle dumpers */
2963
2964     dumper = idle_dumper();
2965     if (!dumper) {
2966         g_printf(_("driver: no idle dumpers for %s:%s.\n"), 
2967                 dp->host->hostname, qname);
2968         fflush(stdout);
2969         log_add(L_WARNING, _("no idle dumpers for %s:%s.\n"),
2970                 dp->host->hostname, qname);
2971         amfree(qname);
2972         return; /* fatal problem */
2973     }
2974
2975     /* tell the taper to read from a port number of its choice */
2976
2977     taper_cmd(PORT_WRITE, dp, NULL, sched(dp)->level, sched(dp)->datestamp);
2978     cmd = getresult(taper, 1, &result_argc, result_argv, MAX_ARGS+1);
2979     if(cmd != PORT) {
2980         g_printf(_("driver: did not get PORT from taper for %s:%s\n"),
2981                 dp->host->hostname, qname);
2982         fflush(stdout);
2983         log_add(L_WARNING, _("driver: did not get PORT from taper for %s:%s.\n"),
2984                 dp->host->hostname, qname);
2985         amfree(qname);
2986         return; /* fatal problem */
2987     }
2988     amfree(qname);
2989
2990     /* copy port number */
2991     dumper->output_port = atoi(result_argv[2]);
2992
2993     dumper->dp = dp;
2994     dumper->chunker = NULL;
2995     dumper->result = LAST_TOK;
2996     taper_result = LAST_TOK;
2997     sched(dp)->dumper = dumper;
2998
2999     /* tell the dumper to dump to a port */
3000     dumper_cmd(dumper, PORT_DUMP, dp);
3001     dp->host->start_t = time(NULL) + 15;
3002
3003     /* update statistics & print state */
3004
3005     taper_busy = dumper->busy = 1;
3006     taper_input_error = NULL;
3007     taper_tape_error = NULL;
3008     taper_dumper = dumper;
3009     taper_disk = dp;
3010     taper_input_error = NULL;
3011     taper_tape_error = NULL;
3012     taper_first_label = NULL;
3013     taper_written = 0;
3014     taper_state |= TAPER_STATE_DUMP_TO_TAPE;
3015     sched(dp)->act_size = sched(dp)->est_size;
3016     dp->host->inprogress += 1;
3017     dp->inprogress = 1;
3018     sched(dp)->timestamp = time((time_t *)0);
3019     allocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
3020     idle_reason = NOT_IDLE;
3021
3022     short_dump_state();
3023
3024     dumper->ev_read = event_register(dumper->fd, EV_READFD,
3025                                      handle_dumper_result, dumper);
3026     taper_ev_read = event_register(taper, EV_READFD,
3027                                    handle_taper_result, NULL);
3028 }
3029
3030 static int
3031 queue_length(
3032     disklist_t  q)
3033 {
3034     disk_t *p;
3035     int len;
3036
3037     for(len = 0, p = q.head; p != NULL; len++, p = p->next)
3038         (void)len;      /* Quiet lint */
3039     return len;
3040 }
3041
3042 static void
3043 short_dump_state(void)
3044 {
3045     int i, nidle;
3046     char *wall_time;
3047
3048     wall_time = walltime_str(curclock());
3049
3050     g_printf(_("driver: state time %s "), wall_time);
3051     g_printf(_("free kps: %lu space: %lld taper: "),
3052            free_kps(NULL),
3053            (long long)free_space());
3054     if(degraded_mode) g_printf(_("DOWN"));
3055     else if(!taper_busy) g_printf(_("idle"));
3056     else g_printf(_("writing"));
3057     nidle = 0;
3058     for(i = 0; i < inparallel; i++) if(!dmptable[i].busy) nidle++;
3059     g_printf(_(" idle-dumpers: %d"), nidle);
3060     g_printf(_(" qlen tapeq: %d"), queue_length(tapeq));
3061     g_printf(_(" runq: %d"), queue_length(runq));
3062     g_printf(_(" roomq: %d"), queue_length(roomq));
3063     g_printf(_(" wakeup: %d"), (int)sleep_time);
3064     g_printf(_(" driver-idle: %s\n"), _(idle_strings[idle_reason]));
3065     interface_state(wall_time);
3066     holdingdisk_state(wall_time);
3067     fflush(stdout);
3068 }
3069
3070 static TapeAction tape_action(void)
3071 {
3072     TapeAction result = TAPE_ACTION_NO_ACTION;
3073     dumper_t *dumper;
3074     disk_t   *dp;
3075     off_t dumpers_size;
3076     off_t runq_size;
3077     off_t directq_size;
3078     off_t tapeq_size;
3079     off_t sched_size;
3080     off_t dump_to_disk_size;
3081     int   dump_to_disk_terminated;
3082
3083     dumpers_size = 0;
3084     for(dumper = dmptable; dumper < (dmptable+inparallel); dumper++) {
3085         if (dumper->busy)
3086             dumpers_size += sched(dumper->dp)->est_size;
3087     }
3088     driver_debug(1, _("dumpers_size: %lld\n"), (long long)dumpers_size);
3089
3090     runq_size = 0;
3091     for(dp = runq.head; dp != NULL; dp = dp->next) {
3092         runq_size += sched(dp)->est_size;
3093     }
3094     driver_debug(1, _("runq_size: %lld\n"), (long long)runq_size);
3095
3096     directq_size = 0;
3097     for(dp = directq.head; dp != NULL; dp = dp->next) {
3098         directq_size += sched(dp)->est_size;
3099     }
3100     driver_debug(1, _("directq_size: %lld\n"), (long long)directq_size);
3101
3102     tapeq_size = 0;
3103     for(dp = tapeq.head; dp != NULL; dp = dp->next) {
3104         tapeq_size += sched(dp)->act_size;
3105     }
3106     if (taper_disk) {
3107         tapeq_size += sched(taper_disk)->act_size - taper_written;
3108     }
3109     driver_debug(1, _("tapeq_size: %lld\n"), (long long)tapeq_size);
3110
3111     sched_size = runq_size + tapeq_size + dumpers_size;
3112     driver_debug(1, _("sched_size: %lld\n"), (long long)sched_size);
3113
3114     dump_to_disk_size = dumpers_size + runq_size;
3115     driver_debug(1, _("dump_to_disk_size: %lld\n"), (long long)dump_to_disk_size);
3116
3117     dump_to_disk_terminated = schedule_done && dump_to_disk_size == 0;
3118
3119     // Changing conditionals can produce a driver hang, take care.
3120     // 
3121     // when to start writting to a new tape
3122     if ((taper_state & TAPER_STATE_WAIT_FOR_TAPE) &&
3123         ((taper_state & TAPER_STATE_DUMP_TO_TAPE) ||    // for dump to tape
3124          !empty(directq) ||                             // if a dle is waiting for a dump to tape
3125          !empty(roomq) ||                               // holding disk constraint
3126          idle_reason == IDLE_NO_DISKSPACE ||            // holding disk constraint
3127          (flush_threshold_dumped < tapeq_size &&        // flush-threshold-dumped &&
3128           flush_threshold_scheduled < sched_size) ||    //  flush-threshold-scheduled
3129          (taperflush < tapeq_size &&                    // taperflush
3130           (force_flush == 1 ||                          //  if force_flush
3131            dump_to_disk_terminated))                    //  or all dump to disk terminated
3132         )) {
3133         result |= TAPE_ACTION_NEW_TAPE;
3134     // when to stop using new tape
3135     } else if ((taper_state & TAPER_STATE_WAIT_FOR_TAPE) &&
3136                (taperflush >= tapeq_size &&             // taperflush criteria not meet
3137                 (force_flush == 1 ||                    //  if force_flush
3138                  dump_to_disk_terminated))              //  or all dump to disk terminated
3139               ) {
3140         result |= TAPE_ACTION_NO_NEW_TAPE;
3141     }
3142
3143     // when to start a flush
3144     // We don't start a flush if taper_tape_started == 1 && dump_to_disk_terminated && force_flush == 0,
3145     // it is a criteria need to exit the first event_loop without flushing everything to tape,
3146     // they will be flush in another event_loop.
3147     if (!degraded_mode && !taper_busy && !empty(tapeq) &&
3148         (!((taper_state & TAPER_STATE_TAPE_STARTED) &&
3149             dump_to_disk_terminated && force_flush == 0) ||     // if tape already started and dump to disk not terminated
3150          ((taper_state & TAPER_STATE_TAPE_STARTED) &&
3151           force_flush == 1) ||                                  // if tape already started and force_flush
3152          !empty(roomq) ||                                       // holding disk constraint
3153          idle_reason == IDLE_NO_DISKSPACE ||                    // holding disk constraint
3154          (flush_threshold_dumped < tapeq_size &&                // flush-threshold-dumped &&
3155          flush_threshold_scheduled < sched_size) ||             //  flush-threshold-scheduled
3156          (force_flush == 1 && taperflush < tapeq_size))) {      // taperflush if force_flush
3157         result |= TAPE_ACTION_START_A_FLUSH;
3158     }
3159     return result;
3160 }
3161
3162 #if 0
3163 static void
3164 dump_state(
3165     const char *str)
3166 {
3167     int i;
3168     disk_t *dp;
3169     char *qname;
3170
3171     g_printf("================\n");
3172     g_printf(_("driver state at time %s: %s\n"), walltime_str(curclock()), str);
3173     g_printf(_("free kps: %lu, space: %lld\n"),
3174            free_kps(NULL),
3175            (long long)free_space());
3176     if(degraded_mode) g_printf(_("taper: DOWN\n"));
3177     else if(!taper_busy) g_printf(_("taper: idle\n"));
3178     else g_printf(_("taper: writing %s:%s.%d est size %lld\n"),
3179                 taper_disk->host->hostname, taper_disk->name,
3180                 sched(taper_disk)->level,
3181                 (long long)sched(taper_disk)->est_size);
3182     for(i = 0; i < inparallel; i++) {
3183         dp = dmptable[i].dp;
3184         if(!dmptable[i].busy)
3185           g_printf(_("%s: idle\n"), dmptable[i].name);
3186         else
3187           qname = quote_string(dp->name);
3188           g_printf(_("%s: dumping %s:%s.%d est kps %d size %lld time %lu\n"),
3189                 dmptable[i].name, dp->host->hostname, qname, sched(dp)->level,
3190                 sched(dp)->est_kps, (long long)sched(dp)->est_size, sched(dp)->est_time);
3191           amfree(qname);
3192     }
3193     dump_queue("TAPE", tapeq, 5, stdout);
3194     dump_queue("ROOM", roomq, 5, stdout);
3195     dump_queue("RUN ", runq, 5, stdout);
3196     g_printf("================\n");
3197     fflush(stdout);
3198 }
3199 #endif