2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998 University of Maryland at College Park
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.
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.
23 * Authors: the Amanda Development Team. Its members are listed in a
24 * file named AUTHORS, in the root directory of this distribution.
27 * $Id: driver.c,v 1.198 2006/08/24 01:57:16 paddy_s Exp $
29 * controlling process for the Amanda backup system
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.
48 #include "server_util.h"
50 #define driver_debug(i,x) do { \
51 if ((i) <= debug_driver) { \
56 #define hold_debug(i,x) do { \
57 if ((i) <= debug_holding) { \
62 static disklist_t waitq, runq, tapeq, roomq;
63 static int pending_aborts;
64 static disk_t *taper_disk;
65 static int degraded_mode;
66 static off_t reserved_space;
67 static off_t total_disksize;
68 static char *dumper_program;
69 static char *chunker_program;
70 static int inparallel;
71 static int nodump = 0;
72 static off_t tape_length = (off_t)0;
73 static off_t tape_left = (off_t)0;
74 static int current_tape = 1;
75 static int conf_taperalgo;
76 static int conf_runtapes;
77 static time_t sleep_time;
78 static int idle_reason;
79 static char *driver_timestamp;
80 static char *hd_driver_timestamp;
81 static am_host_t *flushhost = NULL;
82 static int need_degraded=0;
84 static event_handle_t *dumpers_ev_time = NULL;
85 static event_handle_t *schedule_ev_read = NULL;
87 static int wait_children(int count);
88 static void wait_for_children(void);
89 static void allocate_bandwidth(interface_t *ip, unsigned long kps);
90 static int assign_holdingdisk(assignedhd_t **holdp, disk_t *diskp);
91 static void adjust_diskspace(disk_t *diskp, cmd_t cmd);
92 static void delete_diskspace(disk_t *diskp);
93 static assignedhd_t **build_diskspace(char *destname);
94 static int client_constrained(disk_t *dp);
95 static void deallocate_bandwidth(interface_t *ip, unsigned long kps);
96 static void dump_schedule(disklist_t *qp, char *str);
97 static int dump_to_tape(disk_t *dp);
98 static assignedhd_t **find_diskspace(off_t size, int *cur_idle,
99 assignedhd_t *preferred);
100 static unsigned long free_kps(interface_t *ip);
101 static off_t free_space(void);
102 static void dumper_result(disk_t *dp);
103 static void handle_dumper_result(void *);
104 static void handle_chunker_result(void *);
105 static void handle_dumpers_time(void *);
106 static void handle_taper_result(void *);
107 static void holdingdisk_state(char *time_str);
108 static dumper_t *idle_dumper(void);
109 static void interface_state(char *time_str);
110 static int queue_length(disklist_t q);
111 static disklist_t read_flush(void);
112 static void read_schedule(void *cookie);
113 static void short_dump_state(void);
114 static void startaflush(void);
115 static void start_degraded_mode(disklist_t *queuep);
116 static void start_some_dumps(disklist_t *rq);
117 static void continue_port_dumps(void);
118 static void update_failed_dump_to_tape(disk_t *);
120 static void dump_state(const char *str);
122 int main(int main_argc, char **main_argv);
124 static const char *idle_strings[] = {
127 #define IDLE_NO_DUMPERS 1
129 #define IDLE_START_WAIT 2
131 #define IDLE_NO_HOLD 3
133 #define IDLE_CLIENT_CONSTRAINED 4
134 "client-constrained",
135 #define IDLE_NO_DISKSPACE 5
137 #define IDLE_TOO_LARGE 6
139 #define IDLE_NO_BANDWIDTH 7
141 #define IDLE_TAPER_WAIT 8
155 generic_fs_stats_t fs;
157 unsigned long malloc_hist_1, malloc_size_1;
158 unsigned long malloc_hist_2, malloc_size_2;
159 unsigned long reserve = 100;
164 char *result_argv[MAX_ARGS+1];
169 int new_argc, my_argc;
170 char **new_argv, **my_argv;
175 setvbuf(stdout, (char *)NULL, (int)_IOLBF, 0);
176 setvbuf(stderr, (char *)NULL, (int)_IOLBF, 0);
180 dbopen(DBG_SUBDIR_SERVER);
182 atexit(wait_for_children);
184 /* Don't die when child closes pipe */
185 signal(SIGPIPE, SIG_IGN);
187 malloc_size_1 = malloc_inuse(&malloc_hist_1);
189 erroutput_type = (ERR_AMANDALOG|ERR_INTERACTIVE);
190 set_logerror(logerror);
194 parse_conf(main_argc, main_argv, &new_argc, &new_argv);
198 printf("%s: pid %ld executable %s version %s\n",
199 get_pname(), (long) getpid(), my_argv[0], version());
202 config_name = stralloc(my_argv[1]);
203 config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
205 if(strncmp(my_argv[2], "nodump", 6) == 0) {
212 char my_cwd[STR_SIZE];
214 if (getcwd(my_cwd, SIZEOF(my_cwd)) == NULL) {
215 error("cannot determine current working directory");
218 config_dir = stralloc2(my_cwd, "/");
219 if ((config_name = strrchr(my_cwd, '/')) != NULL) {
220 config_name = stralloc(config_name + 1);
226 conffile = stralloc2(config_dir, CONFFILE_NAME);
227 if(read_conffile(conffile)) {
228 error("errors processing config file \"%s\"", conffile);
233 dbrename(config_name, DBG_SUBDIR_SERVER);
235 report_bad_conf_arg();
237 amfree(driver_timestamp);
238 /* read timestamp from stdin */
239 while ((line = agets(stdin)) != NULL) {
244 if ( line == NULL ) {
245 error("Did not get DATE line from planner");
248 driver_timestamp = alloc(15);
249 strncpy(driver_timestamp, &line[5], 14);
250 driver_timestamp[14] = '\0';
252 log_add(L_START,"date %s", driver_timestamp);
254 gethostname(hostname, SIZEOF(hostname));
255 log_add(L_STATS,"hostname %s", hostname);
257 /* check that we don't do many dump in a day and usetimestamps is off */
258 if(strlen(driver_timestamp) == 8) {
260 char *conf_logdir = getconf_str(CNF_LOGDIR);
261 char *logfile = vstralloc(conf_logdir, "/log.",
262 driver_timestamp, ".0", NULL);
263 char *oldlogfile = vstralloc(conf_logdir, "/oldlog/log.",
264 driver_timestamp, ".0", NULL);
265 if(access(logfile, F_OK) == 0 || access(oldlogfile, F_OK) == 0) {
266 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.");
271 hd_driver_timestamp = construct_timestamp(NULL);
274 hd_driver_timestamp = stralloc(driver_timestamp);
277 taper_program = vstralloc(libexecdir, "/", "taper", versionsuffix(), NULL);
278 dumper_program = vstralloc(libexecdir, "/", "dumper", versionsuffix(),
280 chunker_program = vstralloc(libexecdir, "/", "chunker", versionsuffix(),
283 conf_taperalgo = getconf_taperalgo(CNF_TAPERALGO);
284 conf_tapetype = getconf_str(CNF_TAPETYPE);
285 conf_runtapes = getconf_int(CNF_RUNTAPES);
286 tape = lookup_tapetype(conf_tapetype);
287 tape_length = tapetype_get_length(tape);
288 printf("driver: tape size " OFF_T_FMT "\n", (OFF_T_FMT_TYPE)tape_length);
290 /* start initializing: read in databases */
292 conf_diskfile = getconf_str(CNF_DISKFILE);
293 if (*conf_diskfile == '/') {
294 conf_diskfile = stralloc(conf_diskfile);
296 conf_diskfile = stralloc2(config_dir, conf_diskfile);
298 if (read_diskfile(conf_diskfile, &origq) < 0) {
299 error("could not load disklist \"%s\"", conf_diskfile);
302 amfree(conf_diskfile);
304 /* set up any configuration-dependent variables */
306 inparallel = getconf_int(CNF_INPARALLEL);
308 reserve = (unsigned long)getconf_int(CNF_RESERVE);
310 total_disksize = (off_t)0;
311 for(hdp = getconf_holdingdisks(), dsk = 0; hdp != NULL; hdp = hdp->next, dsk++) {
312 hdp->up = (void *)alloc(SIZEOF(holdalloc_t));
313 holdalloc(hdp)->allocated_dumpers = 0;
314 holdalloc(hdp)->allocated_space = (off_t)0;
316 if(get_fs_stats(holdingdisk_get_diskdir(hdp), &fs) == -1
317 || access(holdingdisk_get_diskdir(hdp), W_OK) == -1) {
318 log_add(L_WARNING, "WARNING: ignoring holding disk %s: %s\n",
319 holdingdisk_get_diskdir(hdp), strerror(errno));
324 if(fs.avail != (off_t)-1) {
325 if(hdp->disksize > (off_t)0) {
326 if(hdp->disksize > fs.avail) {
328 "WARNING: %s: " OFF_T_FMT " KB requested, "
329 "but only " OFF_T_FMT " KB available.",
330 holdingdisk_get_diskdir(hdp),
331 (OFF_T_FMT_TYPE)hdp->disksize,
332 (OFF_T_FMT_TYPE)fs.avail);
333 hdp->disksize = fs.avail;
336 else if((fs.avail + hdp->disksize) < (off_t)0) {
338 "WARNING: %s: not " OFF_T_FMT " KB free.",
339 holdingdisk_get_diskdir(hdp), -hdp->disksize);
340 hdp->disksize = (off_t)0;
344 hdp->disksize += fs.avail;
347 printf("driver: adding holding disk %d dir %s size "
348 OFF_T_FMT " chunksize " OFF_T_FMT "\n",
349 dsk, holdingdisk_get_diskdir(hdp),
350 (OFF_T_FMT_TYPE)hdp->disksize,
351 (OFF_T_FMT_TYPE)(holdingdisk_get_chunksize(hdp)));
353 newdir = newvstralloc(newdir,
354 holdingdisk_get_diskdir(hdp), "/", hd_driver_timestamp,
356 if(!mkholdingdir(newdir)) {
357 hdp->disksize = (off_t)0;
359 total_disksize += hdp->disksize;
362 reserved_space = total_disksize * (off_t)(reserve / 100);
364 printf("reserving " OFF_T_FMT " out of " OFF_T_FMT
365 " for degraded-mode dumps\n",
366 (OFF_T_FMT_TYPE)reserved_space, (OFF_T_FMT_TYPE)free_space());
370 if(inparallel > MAX_DUMPERS) inparallel = MAX_DUMPERS;
372 /* taper takes a while to get going, so start it up right away */
375 if(conf_runtapes > 0) {
376 startup_tape_process(taper_program);
377 taper_cmd(START_TAPER, driver_timestamp, NULL, 0, NULL);
380 /* fire up the dumpers now while we are waiting */
381 if(!nodump) startup_dump_processes(dumper_program, inparallel, driver_timestamp);
384 * Read schedule from stdin. Usually, this is a pipe from planner,
385 * so the effect is that we wait here for the planner to
386 * finish, but meanwhile the taper is rewinding the tape, reading
387 * the label, checking it, writing a new label and all that jazz
388 * in parallel with the planner.
394 tapeq = read_flush();
396 roomq.head = roomq.tail = NULL;
398 log_add(L_STATS, "startup time %s", walltime_str(curclock()));
400 printf("driver: start time %s inparallel %d bandwidth %lu diskspace "
401 OFF_T_FMT " ", walltime_str(curclock()), inparallel,
402 free_kps((interface_t *)0), (OFF_T_FMT_TYPE)free_space());
403 printf(" dir %s datestamp %s driver: drain-ends tapeq %s big-dumpers %s\n",
404 "OBSOLETE", driver_timestamp, taperalgo2str(conf_taperalgo),
405 getconf_str(CNF_DUMPORDER));
408 /* ok, planner is done, now lets see if the tape is ready */
410 if(conf_runtapes > 0) {
411 cmd = getresult(taper, 1, &result_argc, result_argv, MAX_ARGS+1);
413 if(cmd != TAPER_OK) {
414 /* no tape, go into degraded mode: dump to holding disk */
422 tape_left = tape_length;
425 taper_ev_read = NULL;
426 if(!need_degraded) startaflush();
429 schedule_ev_read = event_register((event_id_t)0, EV_READFD, read_schedule, NULL);
434 /* handle any remaining dumps by dumping directly to tape, if possible */
436 while(!empty(runq) && taper > 0) {
437 diskp = dequeue_disk(&runq);
438 if (diskp->to_holdingdisk == HOLD_REQUIRED) {
439 log_add(L_FAIL, "%s %s %s %d [%s]",
440 diskp->host->hostname, diskp->name, sched(diskp)->datestamp,
442 "can't dump required holdingdisk");
444 else if (!degraded_mode) {
445 int rc = dump_to_tape(diskp);
448 "%s %s %d [dump to tape failed, will try again]",
449 diskp->host->hostname,
451 sched(diskp)->level);
453 log_add(L_FAIL, "%s %s %s %d [dump to tape failed]",
454 diskp->host->hostname,
456 sched(diskp)->datestamp,
457 sched(diskp)->level);
460 log_add(L_FAIL, "%s %s %s %d [%s]",
461 diskp->host->hostname, diskp->name, sched(diskp)->datestamp,
463 diskp->to_holdingdisk == HOLD_AUTO ?
464 "no more holding disk space" :
465 "can't dump no-hold disk in degraded mode");
468 short_dump_state(); /* for amstatus */
470 printf("driver: QUITTING time %s telling children to quit\n",
471 walltime_str(curclock()));
475 for(dumper = dmptable; dumper < dmptable + inparallel; dumper++) {
477 dumper_cmd(dumper, QUIT, NULL);
482 taper_cmd(QUIT, NULL, NULL, 0, NULL);
485 /* wait for all to die */
488 for(hdp = getconf_holdingdisks(); hdp != NULL; hdp = hdp->next) {
489 cleanup_holdingdisk(holdingdisk_get_diskdir(hdp), 0);
494 check_unfree_serial();
495 printf("driver: FINISHED time %s\n", walltime_str(curclock()));
497 log_add(L_FINISH,"date %s time %s", driver_timestamp, walltime_str(curclock()));
498 amfree(driver_timestamp);
500 free_new_argv(new_argc, new_argv);
501 amfree(dumper_program);
502 amfree(taper_program);
506 malloc_size_2 = malloc_inuse(&malloc_hist_2);
508 if(malloc_size_1 != malloc_size_2) {
509 malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
517 /* sleep up to count seconds, and wait for terminating child process */
518 /* if sleep is negative, this function will not timeout */
519 /* exit once all child process are finished or the timout expired */
520 /* return 0 if no more children to wait */
521 /* return 1 if some children are still alive */
523 wait_children(int count)
535 pid = waitpid((pid_t)-1, &retstat, WNOHANG);
539 if (! WIFEXITED(retstat)) {
541 code = WTERMSIG(retstat);
542 } else if (WEXITSTATUS(retstat) != 0) {
544 code = WEXITSTATUS(retstat);
547 for (dumper = dmptable; dumper < dmptable + inparallel;
549 if (pid == dumper->pid) {
550 who = stralloc(dumper->name);
554 if (dumper->chunker && pid == dumper->chunker->pid) {
555 who = stralloc(dumper->chunker->name);
556 dumper->chunker->pid = -1;
560 if (who == NULL && pid == taper_pid) {
561 who = stralloc("taper");
564 if(what != NULL && who == NULL) {
565 who = stralloc("unknown");
568 log_add(L_WARNING, "%s pid %u exited with %s %d\n", who,
569 (unsigned)pid, what, code);
570 printf("driver: %s pid %u exited with %s %d\n", who,
571 (unsigned)pid, what, code);
575 } while (pid > 0 || wait_errno == EINTR);
580 } while ((errno != ECHILD) && (count != 0));
581 return (errno != ECHILD);
585 kill_children(int signal)
590 for(dumper = dmptable; dumper < dmptable + inparallel; dumper++) {
591 if (!dumper->down && dumper->pid > 1) {
592 printf("driver: sending signal %d to %s pid %u\n", signal,
593 dumper->name, (unsigned)dumper->pid);
594 if (kill(dumper->pid, signal) == -1 && errno == ESRCH) {
596 dumper->chunker->pid = 0;
598 if (dumper->chunker && dumper->chunker->pid > 1) {
599 printf("driver: sending signal %d to %s pid %u\n", signal,
600 dumper->chunker->name,
601 (unsigned)dumper->chunker->pid);
602 if (kill(dumper->chunker->pid, signal) == -1 &&
604 dumper->chunker->pid = 0;
611 printf("driver: sending signal %d to %s pid %u\n", signal,
612 "taper", (unsigned)taper_pid);
613 if (kill(taper_pid, signal) == -1 && errno == ESRCH)
618 wait_for_children(void)
623 for(dumper = dmptable; dumper < dmptable + inparallel; dumper++) {
624 if (dumper->pid > 1 && dumper->fd >= 0) {
625 dumper_cmd(dumper, QUIT, NULL);
626 if (dumper->chunker && dumper->chunker->pid > 1 &&
627 dumper->chunker->fd >= 0)
628 chunker_cmd(dumper->chunker, QUIT, NULL);
633 if(taper_pid > 1 && taper > 0) {
634 taper_cmd(QUIT, NULL, NULL, 0, NULL);
637 if(wait_children(60) == 0)
640 kill_children(SIGHUP);
641 if(wait_children(60) == 0)
644 kill_children(SIGKILL);
645 if(wait_children(-1) == 0)
659 if(!degraded_mode && !taper_busy && !empty(tapeq)) {
661 datestamp = sched(tapeq.head)->datestamp;
662 switch(conf_taperalgo) {
664 dp = dequeue_disk(&tapeq);
668 while (fit != NULL) {
669 extra_tapes = (fit->tape_splitsize > (off_t)0) ?
670 conf_runtapes - current_tape : 0;
671 if(sched(fit)->act_size <= (tape_left +
672 tape_length * (off_t)extra_tapes) &&
673 strcmp(sched(fit)->datestamp, datestamp) <= 0) {
681 if(dp) remove_disk(&tapeq, dp);
684 fit = dp = tapeq.head;
685 while (fit != NULL) {
686 if(sched(fit)->act_size > sched(dp)->act_size &&
687 strcmp(sched(fit)->datestamp, datestamp) <= 0) {
692 if(dp) remove_disk(&tapeq, dp);
694 case ALGO_LARGESTFIT:
696 while (fit != NULL) {
697 extra_tapes = (fit->tape_splitsize > (off_t)0) ?
698 conf_runtapes - current_tape : 0;
699 if(sched(fit)->act_size <=
700 (tape_left + tape_length * (off_t)extra_tapes) &&
701 (!dp || sched(fit)->act_size > sched(dp)->act_size) &&
702 strcmp(sched(fit)->datestamp, datestamp) <= 0) {
707 if(dp) remove_disk(&tapeq, dp);
713 remove_disk(&tapeq, dp);
716 if(!dp) { /* ALGO_SMALLEST, or default if nothing fit. */
717 if(conf_taperalgo != ALGO_SMALLEST) {
719 "driver: startaflush: Using SMALLEST because nothing fit\n");
721 fit = dp = tapeq.head;
722 while (fit != NULL) {
723 if(sched(fit)->act_size < sched(dp)->act_size &&
724 strcmp(sched(fit)->datestamp, datestamp) <= 0) {
729 if(dp) remove_disk(&tapeq, dp);
731 if(taper_ev_read == NULL) {
732 taper_ev_read = event_register((event_id_t)taper, EV_READFD,
733 handle_taper_result, NULL);
738 qname = quote_string(dp->name);
739 taper_cmd(FILE_WRITE, dp, sched(dp)->destname, sched(dp)->level,
740 sched(dp)->datestamp);
741 fprintf(stderr,"driver: startaflush: %s %s %s "
742 OFF_T_FMT " " OFF_T_FMT "\n",
743 taperalgo2str(conf_taperalgo), dp->host->hostname, qname,
744 (OFF_T_FMT_TYPE)sched(taper_disk)->act_size,
745 (OFF_T_FMT_TYPE)tape_left);
746 if(sched(dp)->act_size <= tape_left)
747 tape_left -= sched(dp)->act_size;
749 tape_left = (off_t)0;
752 error("FATAL: Taper marked busy and no work found.");
755 } else if(!taper_busy && taper_ev_read != NULL) {
756 event_release(taper_ev_read);
757 taper_ev_read = NULL;
768 /* first, check if host is too busy */
770 if(dp->host->inprogress >= dp->host->maxdumps) {
774 /* next, check conflict with other dumps on same spindle */
776 if(dp->spindle == -1) { /* but spindle -1 never conflicts by def. */
780 for(dp2 = dp->host->disks; dp2 != NULL; dp2 = dp2->hostnext)
781 if(dp2->inprogress && dp2->spindle == dp->spindle) {
793 disk_t *diskp, *delayed_diskp, *diskp_accept;
794 assignedhd_t **holdp=NULL, **holdp_accept;
795 const time_t now = time(NULL);
798 char *result_argv[MAX_ARGS+1];
804 idle_reason = IDLE_NO_DUMPERS;
807 if(dumpers_ev_time != NULL) {
808 event_release(dumpers_ev_time);
809 dumpers_ev_time = NULL;
812 for (dumper = dmptable; dumper < dmptable+inparallel; dumper++) {
814 if( dumper->busy || dumper->down) {
818 if (dumper->ev_read != NULL) {
819 event_release(dumper->ev_read);
820 dumper->ev_read = NULL;
824 * A potential problem with starting from the bottom of the dump time
825 * distribution is that a slave host will have both one of the shortest
826 * and one of the longest disks, so starting its shortest disk first will
827 * tie up the host and eliminate its longest disk from consideration the
828 * first pass through. This could cause a big delay in starting that long
829 * disk, which could drag out the whole night's dumps.
831 * While starting from the top of the dump time distribution solves the
832 * above problem, this turns out to be a bad idea, because the big dumps
833 * will almost certainly pack the holding disk completely, leaving no
834 * room for even one small dump to start. This ends up shutting out the
835 * small-end dumpers completely (they stay idle).
837 * The introduction of multiple simultaneous dumps to one host alleviates
838 * the biggest&smallest dumps problem: both can be started at the
844 delayed_diskp = NULL;
848 dumporder = getconf_str(CNF_DUMPORDER);
849 if(strlen(dumporder) > (size_t)(dumper-dmptable)) {
850 dumptype = dumporder[dumper-dmptable];
853 if(dumper-dmptable < 3)
859 for(diskp = rq->head; diskp != NULL; diskp = diskp->next) {
860 assert(diskp->host != NULL && sched(diskp) != NULL);
862 if (diskp->host->start_t > now) {
863 cur_idle = max(cur_idle, IDLE_START_WAIT);
864 if (delayed_diskp == NULL || sleep_time > diskp->host->start_t) {
865 delayed_diskp = diskp;
866 sleep_time = diskp->host->start_t;
868 } else if(diskp->start_t > now) {
869 cur_idle = max(cur_idle, IDLE_START_WAIT);
870 if (delayed_diskp == NULL || sleep_time > diskp->start_t) {
871 delayed_diskp = diskp;
872 sleep_time = diskp->start_t;
874 } else if (diskp->host->netif->curusage > 0 &&
875 sched(diskp)->est_kps > free_kps(diskp->host->netif)) {
876 cur_idle = max(cur_idle, IDLE_NO_BANDWIDTH);
877 } else if(sched(diskp)->no_space) {
878 cur_idle = max(cur_idle, IDLE_NO_DISKSPACE);
879 } else if (diskp->to_holdingdisk == HOLD_NEVER) {
880 cur_idle = max(cur_idle, IDLE_NO_HOLD);
882 find_diskspace(sched(diskp)->est_size, &cur_idle, NULL)) == NULL) {
883 cur_idle = max(cur_idle, IDLE_NO_DISKSPACE);
884 } else if (client_constrained(diskp)) {
885 free_assignedhd(holdp);
886 cur_idle = max(cur_idle, IDLE_CLIENT_CONSTRAINED);
889 /* disk fits, dump it */
890 int accept = !diskp_accept;
893 case 's': accept = (sched(diskp)->est_size < sched(diskp_accept)->est_size);
895 case 'S': accept = (sched(diskp)->est_size > sched(diskp_accept)->est_size);
897 case 't': accept = (sched(diskp)->est_time < sched(diskp_accept)->est_time);
899 case 'T': accept = (sched(diskp)->est_time > sched(diskp_accept)->est_time);
901 case 'b': accept = (sched(diskp)->est_kps < sched(diskp_accept)->est_kps);
903 case 'B': accept = (sched(diskp)->est_kps > sched(diskp_accept)->est_kps);
905 default: log_add(L_WARNING, "Unknown dumporder character \'%c\', using 's'.\n",
907 accept = (sched(diskp)->est_size < sched(diskp_accept)->est_size);
912 if( !diskp_accept || !degraded_mode || diskp->priority >= diskp_accept->priority) {
913 if(holdp_accept) free_assignedhd(holdp_accept);
914 diskp_accept = diskp;
915 holdp_accept = holdp;
918 free_assignedhd(holdp);
922 free_assignedhd(holdp);
927 diskp = diskp_accept;
928 holdp = holdp_accept;
930 idle_reason = max(idle_reason, cur_idle);
933 * If we have no disk at this point, and there are disks that
934 * are delayed, then schedule a time event to call this dumper
935 * with the disk with the shortest delay.
937 if (diskp == NULL && delayed_diskp != NULL) {
938 assert(sleep_time > now);
940 dumpers_ev_time = event_register((event_id_t)sleep_time, EV_TIME,
941 handle_dumpers_time, &runq);
943 } else if (diskp != NULL) {
944 sched(diskp)->act_size = (off_t)0;
945 allocate_bandwidth(diskp->host->netif, sched(diskp)->est_kps);
946 sched(diskp)->activehd = assign_holdingdisk(holdp, diskp);
948 sched(diskp)->destname = newstralloc(sched(diskp)->destname,
949 sched(diskp)->holdp[0]->destname);
950 diskp->host->inprogress++; /* host is now busy */
951 diskp->inprogress = 1;
952 sched(diskp)->dumper = dumper;
953 sched(diskp)->timestamp = now;
955 dumper->busy = 1; /* dumper is now busy */
956 dumper->dp = diskp; /* link disk to dumper */
957 remove_disk(rq, diskp); /* take it off the run queue */
959 sched(diskp)->origsize = (off_t)-1;
960 sched(diskp)->dumpsize = (off_t)-1;
961 sched(diskp)->dumptime = (time_t)0;
962 sched(diskp)->tapetime = (time_t)0;
963 chunker = dumper->chunker;
964 chunker->result = LAST_TOK;
965 dumper->result = LAST_TOK;
966 startup_chunk_process(chunker,chunker_program);
967 chunker_cmd(chunker, START, (void *)driver_timestamp);
968 chunker->dumper = dumper;
969 chunker_cmd(chunker, PORT_WRITE, diskp);
970 cmd = getresult(chunker->fd, 1, &result_argc, result_argv, MAX_ARGS+1);
972 assignedhd_t **h=NULL;
975 printf("driver: did not get PORT from %s for %s:%s\n",
976 chunker->name, diskp->host->hostname, diskp->name);
979 deallocate_bandwidth(diskp->host->netif, sched(diskp)->est_kps);
980 h = sched(diskp)->holdp;
981 activehd = sched(diskp)->activehd;
982 h[activehd]->used = 0;
983 holdalloc(h[activehd]->disk)->allocated_dumpers--;
984 adjust_diskspace(diskp, DONE);
985 delete_diskspace(diskp);
986 diskp->host->inprogress--;
987 diskp->inprogress = 0;
988 sched(diskp)->dumper = NULL;
991 sched(diskp)->attempted++;
992 free_serial_dp(diskp);
993 if(sched(diskp)->attempted < 2)
994 enqueue_disk(rq, diskp);
997 dumper->ev_read = event_register((event_id_t)dumper->fd, EV_READFD,
998 handle_dumper_result, dumper);
999 chunker->ev_read = event_register((event_id_t)chunker->fd, EV_READFD,
1000 handle_chunker_result, chunker);
1001 dumper->output_port = atoi(result_argv[2]);
1003 dumper_cmd(dumper, PORT_DUMP, diskp);
1005 diskp->host->start_t = now + 15;
1011 * This gets called when a dumper is delayed for some reason. It may
1012 * be because a disk has a delayed start, or amanda is constrained
1013 * by network or disk limits.
1017 handle_dumpers_time(
1020 disklist_t *runq = cookie;
1021 event_release(dumpers_ev_time);
1022 dumpers_ev_time = NULL;
1023 start_some_dumps(runq);
1034 printf("dump of driver schedule %s:\n--------\n", str);
1036 for(dp = qp->head; dp != NULL; dp = dp->next) {
1037 qname = quote_string(dp->name);
1038 printf(" %-20s %-25s lv %d t %5lu s " OFF_T_FMT " p %d\n",
1039 dp->host->hostname, qname, sched(dp)->level,
1040 sched(dp)->est_time,
1041 (OFF_T_FMT_TYPE)sched(dp)->est_size, sched(dp)->priority);
1044 printf("--------\n");
1048 start_degraded_mode(
1049 /*@keep@*/ disklist_t *queuep)
1053 off_t est_full_size;
1056 if (taper_ev_read != NULL) {
1057 event_release(taper_ev_read);
1058 taper_ev_read = NULL;
1061 newq.head = newq.tail = 0;
1063 dump_schedule(queuep, "before start degraded mode");
1065 est_full_size = (off_t)0;
1066 while(!empty(*queuep)) {
1067 dp = dequeue_disk(queuep);
1069 qname = quote_string(dp->name);
1070 if(sched(dp)->level != 0)
1071 /* go ahead and do the disk as-is */
1072 enqueue_disk(&newq, dp);
1074 if (reserved_space + est_full_size + sched(dp)->est_size
1075 <= total_disksize) {
1076 enqueue_disk(&newq, dp);
1077 est_full_size += sched(dp)->est_size;
1079 else if(sched(dp)->degr_level != -1) {
1080 sched(dp)->level = sched(dp)->degr_level;
1081 sched(dp)->dumpdate = sched(dp)->degr_dumpdate;
1082 sched(dp)->est_nsize = sched(dp)->degr_nsize;
1083 sched(dp)->est_csize = sched(dp)->degr_csize;
1084 sched(dp)->est_time = sched(dp)->degr_time;
1085 sched(dp)->est_kps = sched(dp)->degr_kps;
1086 enqueue_disk(&newq, dp);
1089 log_add(L_FAIL,"%s %s %s %d [can't switch to incremental dump]",
1090 dp->host->hostname, qname, sched(dp)->datestamp,
1097 /*@i@*/ *queuep = newq;
1100 dump_schedule(queuep, "after start degraded mode");
1105 continue_port_dumps(void)
1109 int active_dumpers=0, busy_dumpers=0, i;
1112 /* First we try to grant diskspace to some dumps waiting for it. */
1113 for( dp = roomq.head; dp; dp = ndp ) {
1115 /* find last holdingdisk used by this dump */
1116 for( i = 0, h = sched(dp)->holdp; h[i+1]; i++ ) {
1117 (void)h; /* Quiet lint */
1119 /* find more space */
1120 h = find_diskspace( sched(dp)->est_size - sched(dp)->act_size,
1121 &active_dumpers, h[i] );
1123 for(dumper = dmptable; dumper < dmptable + inparallel &&
1124 dumper->dp != dp; dumper++) {
1125 (void)dp; /* Quiet lint */
1127 assert( dumper < dmptable + inparallel );
1128 sched(dp)->activehd = assign_holdingdisk( h, dp );
1129 chunker_cmd( dumper->chunker, CONTINUE, dp );
1131 remove_disk( &roomq, dp );
1135 /* So for some disks there is less holding diskspace available than
1136 * was asked for. Possible reasons are
1137 * a) diskspace has been allocated for other dumps which are
1138 * still running or already being written to tape
1139 * b) all other dumps have been suspended due to lack of diskspace
1140 * c) this dump doesn't fit on all the holding disks
1141 * Case a) is not a problem. We just wait for the diskspace to
1142 * be freed by moving the current disk to a queue.
1143 * If case b) occurs, we have a deadlock situation. We select
1144 * a dump from the queue to be aborted and abort it. It will
1145 * be retried later dumping to disk.
1146 * If case c) is detected, the dump is aborted. Next time
1147 * it will be dumped directly to tape. Actually, case c is a special
1148 * manifestation of case b) where only one dumper is busy.
1150 for(dp=NULL, dumper = dmptable; dumper < (dmptable+inparallel); dumper++) {
1151 if( dumper->busy ) {
1153 if( !find_disk(&roomq, dumper->dp) ) {
1156 sched(dp)->est_size > sched(dumper->dp)->est_size ) {
1161 if((dp != NULL) && (active_dumpers == 0) && (busy_dumpers > 0) &&
1162 ((!taper_busy && empty(tapeq)) || degraded_mode) &&
1163 pending_aborts == 0 ) { /* not case a */
1164 if( busy_dumpers == 1 ) { /* case c */
1165 sched(dp)->no_space = 1;
1168 /* At this time, dp points to the dump with the smallest est_size.
1169 * We abort that dump, hopefully not wasting too much time retrying it.
1171 remove_disk( &roomq, dp );
1172 chunker_cmd( sched(dp)->dumper->chunker, ABORT, NULL);
1173 dumper_cmd( sched(dp)->dumper, ABORT, NULL );
1180 handle_taper_result(
1187 char *result_argv[MAX_ARGS+1];
1188 int avail_tapes = 0;
1190 (void)cookie; /* Quiet unused parameter warning */
1192 assert(cookie == NULL);
1198 cmd = getresult(taper, 1, &result_argc, result_argv, MAX_ARGS+1);
1203 case DONE: /* DONE <handle> <label> <tape file> <err mess> */
1204 if(result_argc != 5) {
1205 error("error: [taper DONE result_argc != 5: %d", result_argc);
1209 dp = serial2disk(result_argv[2]);
1210 free_serial(result_argv[2]);
1212 filenum = OFF_T_ATOI(result_argv[4]);
1214 update_info_taper(dp, result_argv[3], filenum,
1218 delete_diskspace(dp);
1220 printf("driver: finished-cmd time %s taper wrote %s:%s\n",
1221 walltime_str(curclock()), dp->host->hostname, dp->name);
1224 amfree(sched(dp)->destname);
1225 amfree(sched(dp)->dumpdate);
1226 amfree(sched(dp)->degr_dumpdate);
1227 amfree(sched(dp)->datestamp);
1234 /* continue with those dumps waiting for diskspace */
1235 continue_port_dumps();
1238 case TRYAGAIN: /* TRY-AGAIN <handle> <err mess> */
1239 if (result_argc < 2) {
1240 error("error [taper TRYAGAIN result_argc < 2: %d]",
1244 dp = serial2disk(result_argv[2]);
1245 free_serial(result_argv[2]);
1246 printf("driver: taper-tryagain time %s disk %s:%s\n",
1247 walltime_str(curclock()), dp->host->hostname, dp->name);
1250 /* See how many tapes we have left, but we alwyays
1251 retry once (why?) */
1253 if(dp->tape_splitsize > (off_t)0)
1254 avail_tapes = conf_runtapes - current_tape;
1258 if(sched(dp)->attempted > avail_tapes) {
1259 log_add(L_FAIL, "%s %s %s %d [too many taper retries]",
1260 dp->host->hostname, dp->name, sched(dp)->datestamp,
1262 printf("driver: taper failed %s %s %s, too many taper retry\n",
1263 result_argv[2], dp->host->hostname, dp->name);
1266 /* Re-insert into taper queue. */
1267 sched(dp)->attempted++;
1268 headqueue_disk(&tapeq, dp);
1271 tape_left = tape_length;
1273 /* run next thing from queue */
1278 continue_port_dumps();
1281 case SPLIT_CONTINUE: /* SPLIT_CONTINUE <handle> <new_label> */
1282 if (result_argc != 3) {
1283 error("error [taper SPLIT_CONTINUE result_argc != 3: %d]",
1289 case SPLIT_NEEDNEXT: /* SPLIT-NEEDNEXT <handle> <kb written> */
1290 if (result_argc != 3) {
1291 error("error [taper SPLIT_NEEDNEXT result_argc != 3: %d]",
1296 /* Update our tape counter and reset tape_left */
1298 tape_left = tape_length;
1300 /* Reduce the size of the dump by amount written and reduce
1301 tape_left by the amount left over */
1302 dp = serial2disk(result_argv[2]);
1303 sched(dp)->act_size -= OFF_T_ATOI(result_argv[3]);
1304 if (sched(dp)->act_size < tape_left)
1305 tape_left -= sched(dp)->act_size;
1311 case TAPE_ERROR: /* TAPE-ERROR <handle> <err mess> */
1312 dp = serial2disk(result_argv[2]);
1313 free_serial(result_argv[2]);
1314 printf("driver: finished-cmd time %s taper wrote %s:%s\n",
1315 walltime_str(curclock()), dp->host->hostname, dp->name);
1317 log_add(L_WARNING, "Taper error: %s", result_argv[3]);
1322 log_add(L_WARNING, "Taper protocol error");
1325 * Since we received a taper error, we can't send anything more
1326 * to the taper. Go into degraded mode to try to get everthing
1327 * onto disk. Later, these dumps can be flushed to a new tape.
1328 * The tape queue is zapped so that it appears empty in future
1329 * checks. If there are dumps waiting for diskspace to be freed,
1334 "going into degraded mode because of taper component error.");
1335 start_degraded_mode(&runq);
1337 tapeq.head = tapeq.tail = NULL;
1340 if(taper_ev_read != NULL) {
1341 event_release(taper_ev_read);
1342 taper_ev_read = NULL;
1344 if(cmd != TAPE_ERROR) aclose(taper);
1345 continue_port_dumps();
1349 error("driver received unexpected token (%s) from taper",
1354 * Wakeup any dumpers that are sleeping because of network
1355 * or disk constraints.
1357 start_some_dumps(&runq);
1359 } while(areads_dataready(taper));
1367 for(dumper = dmptable; dumper < dmptable+inparallel; dumper++)
1368 if(!dumper->busy && !dumper->down) return dumper;
1379 assignedhd_t **h=NULL;
1385 dumper = sched(dp)->dumper;
1386 chunker = dumper->chunker;
1390 h = sched(dp)->holdp;
1391 activehd = sched(dp)->activehd;
1393 if(dumper->result == DONE && chunker->result == DONE) {
1394 update_info_dumper(dp, sched(dp)->origsize,
1395 sched(dp)->dumpsize, sched(dp)->dumptime);
1396 log_add(L_STATS, "estimate %s %s %s %d [sec %ld nkb " OFF_T_FMT
1397 " ckb " OFF_T_FMT " kps %lu]",
1398 dp->host->hostname, dp->name, sched(dp)->datestamp,
1400 sched(dp)->est_time, (OFF_T_FMT_TYPE)sched(dp)->est_nsize,
1401 (OFF_T_FMT_TYPE)sched(dp)->est_csize,
1402 sched(dp)->est_kps);
1405 deallocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
1407 is_partial = dumper->result != DONE || chunker->result != DONE;
1408 rename_tmp_holding(sched(dp)->destname, !is_partial);
1411 for( i = 0, h = sched(dp)->holdp; i < activehd; i++ ) {
1412 dummy += h[i]->used;
1415 size = holding_file_size(sched(dp)->destname, 0);
1416 h[activehd]->used = size - dummy;
1417 holdalloc(h[activehd]->disk)->allocated_dumpers--;
1418 adjust_diskspace(dp, DONE);
1420 sched(dp)->attempted += 1;
1422 if((dumper->result != DONE || chunker->result != DONE) &&
1423 sched(dp)->attempted <= 1) {
1424 delete_diskspace(dp);
1425 enqueue_disk(&runq, dp);
1427 else if(size > (off_t)DISK_BLOCK_KB) {
1428 sched(dp)->attempted = 0;
1429 enqueue_disk(&tapeq, dp);
1433 delete_diskspace(dp);
1437 dp->host->inprogress -= 1;
1440 waitpid(chunker->pid, NULL, 0 );
1441 aclose(chunker->fd);
1446 if (chunker->result == ABORT_FINISHED)
1448 continue_port_dumps();
1450 * Wakeup any dumpers that are sleeping because of network
1451 * or disk constraints.
1453 start_some_dumps(&runq);
1458 handle_dumper_result(
1461 /*static int pending_aborts = 0;*/
1462 dumper_t *dumper = cookie;
1467 char *result_argv[MAX_ARGS+1];
1469 assert(dumper != NULL);
1471 assert(dp != NULL && sched(dp) != NULL);
1477 cmd = getresult(dumper->fd, 1, &result_argc, result_argv, MAX_ARGS+1);
1480 /* result_argv[2] always contains the serial number */
1481 sdp = serial2disk(result_argv[2]);
1483 error("%s: Invalid serial number: %s", get_pname(), result_argv[2]);
1488 qname = quote_string(dp->name);
1491 case DONE: /* DONE <handle> <origsize> <dumpsize> <dumptime> <errstr> */
1492 if(result_argc != 6) {
1493 error("error [dumper DONE result_argc != 6: %d]", result_argc);
1497 /*free_serial(result_argv[2]);*/
1499 sched(dp)->origsize = OFF_T_ATOI(result_argv[3]);
1500 sched(dp)->dumptime = TIME_T_ATOI(result_argv[5]);
1502 printf("driver: finished-cmd time %s %s dumped %s:%s\n",
1503 walltime_str(curclock()), dumper->name,
1504 dp->host->hostname, qname);
1507 dumper->result = cmd;
1511 case TRYAGAIN: /* TRY-AGAIN <handle> <errstr> */
1513 * Requeue this disk, and fall through to the FAILED
1516 if(sched(dp)->attempted) {
1517 log_add(L_FAIL, "%s %s %s %d [too many dumper retry: %s]",
1518 dp->host->hostname, dp->name, sched(dp)->datestamp,
1519 sched(dp)->level, result_argv[3]);
1520 printf("driver: dump failed %s %s %s, too many dumper retry: %s\n",
1521 result_argv[2], dp->host->hostname, dp->name,
1525 case FAILED: /* FAILED <handle> <errstr> */
1526 /*free_serial(result_argv[2]);*/
1527 dumper->result = cmd;
1530 case ABORT_FINISHED: /* ABORT-FINISHED <handle> */
1532 * We sent an ABORT from the NO-ROOM case because this dump
1533 * wasn't going to fit onto the holding disk. We now need to
1534 * clean up the remains of this image, and try to finish
1535 * other dumps that are waiting on disk space.
1537 assert(pending_aborts);
1538 /*free_serial(result_argv[2]);*/
1539 dumper->result = cmd;
1543 /* either EOF or garbage from dumper. Turn it off */
1544 log_add(L_WARNING, "%s pid %ld is messed up, ignoring it.\n",
1545 dumper->name, (long)dumper->pid);
1546 if (dumper->ev_read) {
1547 event_release(dumper->ev_read);
1548 dumper->ev_read = NULL;
1552 dumper->down = 1; /* mark it down so it isn't used again */
1554 /* if it was dumping something, zap it and try again */
1555 if(sched(dp)->attempted) {
1556 log_add(L_FAIL, "%s %s %s %d [%s died]",
1557 dp->host->hostname, qname, sched(dp)->datestamp,
1558 sched(dp)->level, dumper->name);
1561 log_add(L_WARNING, "%s died while dumping %s:%s lev %d.",
1562 dumper->name, dp->host->hostname, qname,
1566 dumper->result = cmd;
1574 /* send the dumper result to the chunker */
1575 if(dumper->chunker->down == 0 && dumper->chunker->fd != -1 &&
1576 dumper->chunker->result == LAST_TOK) {
1578 chunker_cmd(dumper->chunker, DONE, dp);
1581 chunker_cmd(dumper->chunker, FAILED, dp);
1585 if(dumper->result != LAST_TOK && dumper->chunker->result != LAST_TOK)
1588 } while(areads_dataready(dumper->fd));
1593 handle_chunker_result(
1596 /*static int pending_aborts = 0;*/
1597 chunker_t *chunker = cookie;
1598 assignedhd_t **h=NULL;
1603 char *result_argv[MAX_ARGS+1];
1608 assert(chunker != NULL);
1609 dumper = chunker->dumper;
1610 assert(dumper != NULL);
1613 assert(sched(dp) != NULL);
1614 assert(sched(dp)->destname != NULL);
1615 assert(dp != NULL && sched(dp) != NULL && sched(dp)->destname);
1617 if(dp && sched(dp) && sched(dp)->holdp) {
1618 h = sched(dp)->holdp;
1619 activehd = sched(dp)->activehd;
1626 cmd = getresult(chunker->fd, 1, &result_argc, result_argv, MAX_ARGS+1);
1629 /* result_argv[2] always contains the serial number */
1630 sdp = serial2disk(result_argv[2]);
1632 error("%s: Invalid serial number: %s", get_pname(), result_argv[2]);
1639 case PARTIAL: /* PARTIAL <handle> <dumpsize> <errstr> */
1640 case DONE: /* DONE <handle> <dumpsize> <errstr> */
1641 if(result_argc != 4) {
1642 error("error [chunker %s result_argc != 4: %d]", cmdstr[cmd],
1646 /*free_serial(result_argv[2]);*/
1648 sched(dp)->dumpsize = (off_t)atof(result_argv[3]);
1650 qname = quote_string(dp->name);
1651 printf("driver: finished-cmd time %s %s chunked %s:%s\n",
1652 walltime_str(curclock()), chunker->name,
1653 dp->host->hostname, qname);
1657 event_release(chunker->ev_read);
1659 chunker->result = cmd;
1663 case TRYAGAIN: /* TRY-AGAIN <handle> <errstr> */
1664 event_release(chunker->ev_read);
1666 chunker->result = cmd;
1669 case FAILED: /* FAILED <handle> <errstr> */
1670 /*free_serial(result_argv[2]);*/
1672 event_release(chunker->ev_read);
1674 chunker->result = cmd;
1678 case NO_ROOM: /* NO-ROOM <handle> <missing_size> */
1679 if (!h || activehd < 0) { /* should never happen */
1680 error("!h || activehd < 0");
1683 h[activehd]->used -= OFF_T_ATOI(result_argv[3]);
1684 h[activehd]->reserved -= OFF_T_ATOI(result_argv[3]);
1685 holdalloc(h[activehd]->disk)->allocated_space -= OFF_T_ATOI(result_argv[3]);
1686 h[activehd]->disk->disksize -= OFF_T_ATOI(result_argv[3]);
1689 case RQ_MORE_DISK: /* RQ-MORE-DISK <handle> */
1690 if (!h || activehd < 0) { /* should never happen */
1691 error("!h || activehd < 0");
1694 holdalloc(h[activehd]->disk)->allocated_dumpers--;
1695 h[activehd]->used = h[activehd]->reserved;
1696 if( h[++activehd] ) { /* There's still some allocated space left.
1697 * Tell the dumper about it. */
1698 sched(dp)->activehd++;
1699 chunker_cmd( chunker, CONTINUE, dp );
1700 } else { /* !h[++activehd] - must allocate more space */
1701 sched(dp)->act_size = sched(dp)->est_size; /* not quite true */
1702 sched(dp)->est_size = (sched(dp)->act_size/(off_t)20) * (off_t)21; /* +5% */
1703 sched(dp)->est_size = am_round(sched(dp)->est_size, (off_t)DISK_BLOCK_KB);
1704 if (sched(dp)->est_size < sched(dp)->act_size + 2*DISK_BLOCK_KB)
1705 sched(dp)->est_size += 2 * DISK_BLOCK_KB;
1706 h = find_diskspace( sched(dp)->est_size - sched(dp)->act_size,
1710 /* No diskspace available. The reason for this will be
1711 * determined in continue_port_dumps(). */
1712 enqueue_disk( &roomq, dp );
1713 continue_port_dumps();
1715 /* OK, allocate space for disk and have chunker continue */
1716 sched(dp)->activehd = assign_holdingdisk( h, dp );
1717 chunker_cmd( chunker, CONTINUE, dp );
1723 case ABORT_FINISHED: /* ABORT-FINISHED <handle> */
1725 * We sent an ABORT from the NO-ROOM case because this dump
1726 * wasn't going to fit onto the holding disk. We now need to
1727 * clean up the remains of this image, and try to finish
1728 * other dumps that are waiting on disk space.
1730 /*assert(pending_aborts);*/
1732 /*free_serial(result_argv[2]);*/
1734 event_release(chunker->ev_read);
1736 chunker->result = cmd;
1741 /* either EOF or garbage from chunker. Turn it off */
1742 log_add(L_WARNING, "%s pid %ld is messed up, ignoring it.\n",
1743 chunker->name, (long)chunker->pid);
1746 /* if it was dumping something, zap it and try again */
1747 if (!h || activehd < 0) { /* should never happen */
1748 error("!h || activehd < 0");
1751 qname = quote_string(dp->name);
1752 if(sched(dp)->attempted) {
1753 log_add(L_FAIL, "%s %s %s %d [%s died]",
1754 dp->host->hostname, qname, sched(dp)->datestamp,
1755 sched(dp)->level, chunker->name);
1758 log_add(L_WARNING, "%s died while dumping %s:%s lev %d.",
1759 chunker->name, dp->host->hostname, qname,
1766 event_release(chunker->ev_read);
1768 chunker->result = cmd;
1776 if(chunker->result != LAST_TOK && chunker->dumper->result != LAST_TOK)
1779 } while(areads_dataready(chunker->fd));
1790 char *hostname, *diskname, *datestamp;
1794 char *inpline = NULL;
1800 char *qdestname = NULL;
1802 tq.head = tq.tail = NULL;
1804 for(line = 0; (inpline = agets(stdin)) != NULL; free(inpline)) {
1806 if (inpline[0] == '\0')
1812 skip_whitespace(s, ch); /* find the command */
1814 error("flush line %d: syntax error (no command)", line);
1818 skip_non_whitespace(s, ch);
1821 if(strcmp(command,"ENDFLUSH") == 0) {
1825 if(strcmp(command,"FLUSH") != 0) {
1826 error("flush line %d: syntax error (%s != FLUSH)", line, command);
1830 skip_whitespace(s, ch); /* find the hostname */
1832 error("flush line %d: syntax error (no hostname)", line);
1836 skip_non_whitespace(s, ch);
1839 skip_whitespace(s, ch); /* find the diskname */
1841 error("flush line %d: syntax error (no diskname)", line);
1845 skip_quoted_string(s, ch);
1846 s[-1] = '\0'; /* terminate the disk name */
1847 diskname = unquote_string(qname);
1849 skip_whitespace(s, ch); /* find the datestamp */
1851 error("flush line %d: syntax error (no datestamp)", line);
1855 skip_non_whitespace(s, ch);
1858 skip_whitespace(s, ch); /* find the level number */
1859 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
1860 error("flush line %d: syntax error (bad level)", line);
1863 skip_integer(s, ch);
1865 skip_whitespace(s, ch); /* find the filename */
1867 error("flush line %d: syntax error (no filename)", line);
1871 skip_quoted_string(s, ch);
1873 destname = unquote_string(qdestname);
1875 holding_file_get_dumpfile(destname, &file);
1876 if( file.type != F_DUMPFILE) {
1877 if( file.type != F_CONT_DUMPFILE )
1878 log_add(L_INFO, "%s: ignoring cruft file.", destname);
1883 if(strcmp(hostname, file.name) != 0 ||
1884 strcmp(diskname, file.disk) != 0 ||
1885 strcmp(datestamp, file.datestamp) != 0) {
1886 log_add(L_INFO, "disk %s:%s not consistent with file %s",
1887 hostname, diskname, destname);
1893 dp = lookup_disk(file.name, file.disk);
1896 log_add(L_INFO, "%s: disk %s:%s not in database, skipping it.",
1897 destname, file.name, file.disk);
1901 if(file.dumplevel < 0 || file.dumplevel > 9) {
1902 log_add(L_INFO, "%s: ignoring file with bogus dump level %d.",
1903 destname, file.dumplevel);
1907 if (holding_file_size(destname,1) <= 0) {
1908 log_add(L_INFO, "%s: removing file with no data.", destname);
1909 holding_file_unlink(destname);
1913 dp1 = (disk_t *)alloc(SIZEOF(disk_t));
1915 dp1->next = dp1->prev = NULL;
1917 /* add it to the flushhost list */
1919 flushhost = alloc(SIZEOF(am_host_t));
1920 flushhost->next = NULL;
1921 flushhost->hostname = stralloc("FLUSHHOST");
1922 flushhost->up = NULL;
1923 flushhost->features = NULL;
1925 dp1->hostnext = flushhost->disks;
1926 flushhost->disks = dp1;
1928 sp = (sched_t *) alloc(SIZEOF(sched_t));
1929 sp->destname = stralloc(destname);
1930 sp->level = file.dumplevel;
1931 sp->dumpdate = NULL;
1932 sp->degr_dumpdate = NULL;
1933 sp->datestamp = stralloc(file.datestamp);
1934 sp->est_nsize = (off_t)0;
1935 sp->est_csize = (off_t)0;
1939 sp->degr_level = -1;
1941 sp->act_size = holding_file_size(destname, 0);
1942 sp->holdp = build_diskspace(destname);
1943 if(sp->holdp == NULL) continue;
1945 sp->timestamp = (time_t)0;
1947 dp1->up = (char *)sp;
1949 enqueue_disk(&tq, dp1);
1962 int level, line, priority;
1963 char *dumpdate, *degr_dumpdate;
1965 time_t time, degr_time;
1966 time_t *time_p = &time;
1967 time_t *degr_time_p = °r_time;
1968 off_t nsize, csize, degr_nsize, degr_csize;
1969 unsigned long kps, degr_kps;
1970 char *hostname, *features, *diskname, *datestamp, *inpline = NULL;
1974 off_t flush_size = (off_t)0;
1976 OFF_T_FMT_TYPE nsize_;
1977 OFF_T_FMT_TYPE csize_;
1978 OFF_T_FMT_TYPE degr_nsize_;
1979 OFF_T_FMT_TYPE degr_csize_;
1981 (void)cookie; /* Quiet unused parameter warning */
1983 event_release(schedule_ev_read);
1985 /* read schedule from stdin */
1987 for(line = 0; (inpline = agets(stdin)) != NULL; free(inpline)) {
1988 if (inpline[0] == '\0')
1995 skip_whitespace(s, ch); /* find the command */
1997 error("schedule line %d: syntax error (no command)", line);
2001 skip_non_whitespace(s, ch);
2004 if(strcmp(command,"DUMP") != 0) {
2005 error("schedule line %d: syntax error (%s != DUMP)", line, command);
2009 skip_whitespace(s, ch); /* find the host name */
2011 error("schedule line %d: syntax error (no host name)", line);
2015 skip_non_whitespace(s, ch);
2018 skip_whitespace(s, ch); /* find the feature list */
2020 error("schedule line %d: syntax error (no feature list)", line);
2024 skip_non_whitespace(s, ch);
2027 skip_whitespace(s, ch); /* find the disk name */
2029 error("schedule line %d: syntax error (no disk name)", line);
2033 skip_quoted_string(s, ch);
2034 s[-1] = '\0'; /* terminate the disk name */
2035 diskname = unquote_string(qname);
2037 skip_whitespace(s, ch); /* find the datestamp */
2039 error("schedule line %d: syntax error (no datestamp)", line);
2043 skip_non_whitespace(s, ch);
2046 skip_whitespace(s, ch); /* find the priority number */
2047 if(ch == '\0' || sscanf(s - 1, "%d", &priority) != 1) {
2048 error("schedule line %d: syntax error (bad priority)", line);
2051 skip_integer(s, ch);
2053 skip_whitespace(s, ch); /* find the level number */
2054 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
2055 error("schedule line %d: syntax error (bad level)", line);
2058 skip_integer(s, ch);
2060 skip_whitespace(s, ch); /* find the dump date */
2062 error("schedule line %d: syntax error (bad dump date)", line);
2066 skip_non_whitespace(s, ch);
2069 skip_whitespace(s, ch); /* find the native size */
2070 nsize_ = (OFF_T_FMT_TYPE)0;
2071 if(ch == '\0' || sscanf(s - 1, OFF_T_FMT, &nsize_) != 1) {
2072 error("schedule line %d: syntax error (bad nsize)", line);
2075 nsize = (off_t)nsize_;
2076 skip_integer(s, ch);
2078 skip_whitespace(s, ch); /* find the compressed size */
2079 csize_ = (OFF_T_FMT_TYPE)0;
2080 if(ch == '\0' || sscanf(s - 1, OFF_T_FMT, &csize_) != 1) {
2081 error("schedule line %d: syntax error (bad csize)", line);
2084 csize = (off_t)csize_;
2085 skip_integer(s, ch);
2087 skip_whitespace(s, ch); /* find the time number */
2088 if(ch == '\0' || sscanf(s - 1, TIME_T_FMT,
2089 (TIME_T_FMT_TYPE *)time_p) != 1) {
2090 error("schedule line %d: syntax error (bad estimated time)", line);
2093 skip_integer(s, ch);
2095 skip_whitespace(s, ch); /* find the kps number */
2096 if(ch == '\0' || sscanf(s - 1, "%lu", &kps) != 1) {
2097 error("schedule line %d: syntax error (bad kps)", line);
2100 skip_integer(s, ch);
2102 degr_dumpdate = NULL; /* flag if degr fields found */
2103 skip_whitespace(s, ch); /* find the degr level number */
2105 if(sscanf(s - 1, "%d", °r_level) != 1) {
2106 error("schedule line %d: syntax error (bad degr level)", line);
2109 skip_integer(s, ch);
2111 skip_whitespace(s, ch); /* find the degr dump date */
2113 error("schedule line %d: syntax error (bad degr dump date)", line);
2116 degr_dumpdate = s - 1;
2117 skip_non_whitespace(s, ch);
2120 skip_whitespace(s, ch); /* find the degr native size */
2121 degr_nsize_ = (OFF_T_FMT_TYPE)0;
2122 if(ch == '\0' || sscanf(s - 1, OFF_T_FMT, °r_nsize_) != 1) {
2123 error("schedule line %d: syntax error (bad degr nsize)", line);
2126 degr_nsize = (off_t)degr_nsize_;
2127 skip_integer(s, ch);
2129 skip_whitespace(s, ch); /* find the degr compressed size */
2130 degr_csize_ = (OFF_T_FMT_TYPE)0;
2131 if(ch == '\0' || sscanf(s - 1, OFF_T_FMT, °r_csize_) != 1) {
2132 error("schedule line %d: syntax error (bad degr csize)", line);
2135 degr_csize = (off_t)degr_csize_;
2136 skip_integer(s, ch);
2138 skip_whitespace(s, ch); /* find the degr time number */
2139 if(ch == '\0' || sscanf(s - 1, TIME_T_FMT,
2140 (TIME_T_FMT_TYPE *)degr_time_p) != 1) {
2141 error("schedule line %d: syntax error (bad degr estimated time)", line);
2144 skip_integer(s, ch);
2146 skip_whitespace(s, ch); /* find the degr kps number */
2147 if(ch == '\0' || sscanf(s - 1, "%lu", °r_kps) != 1) {
2148 error("schedule line %d: syntax error (bad degr kps)", line);
2151 skip_integer(s, ch);
2154 degr_nsize = (off_t)0;
2155 degr_csize = (off_t)0;
2156 degr_time = (time_t)0;
2160 dp = lookup_disk(hostname, diskname);
2163 "schedule line %d: %s:'%s' not in disklist, ignored",
2164 line, hostname, qname);
2169 sp = (sched_t *) alloc(SIZEOF(sched_t));
2172 sp->dumpdate = stralloc(dumpdate);
2173 sp->est_nsize = DISK_BLOCK_KB + nsize; /* include header */
2174 sp->est_csize = DISK_BLOCK_KB + csize; /* include header */
2175 /* round estimate to next multiple of DISK_BLOCK_KB */
2176 sp->est_csize = am_round(sp->est_csize, DISK_BLOCK_KB);
2177 sp->est_size = sp->est_csize;
2178 sp->est_time = time;
2180 sp->priority = priority;
2181 sp->datestamp = stralloc(datestamp);
2184 sp->degr_level = degr_level;
2185 sp->degr_dumpdate = stralloc(degr_dumpdate);
2186 sp->degr_nsize = DISK_BLOCK_KB + degr_nsize;
2187 sp->degr_csize = DISK_BLOCK_KB + degr_csize;
2188 /* round estimate to next multiple of DISK_BLOCK_KB */
2189 sp->degr_csize = am_round(sp->degr_csize, DISK_BLOCK_KB);
2190 sp->degr_time = degr_time;
2191 sp->degr_kps = degr_kps;
2193 sp->degr_level = -1;
2194 sp->degr_dumpdate = NULL;
2199 sp->act_size = (off_t)0;
2203 sp->timestamp = (time_t)0;
2204 sp->destname = NULL;
2207 dp->up = (char *) sp;
2208 if(dp->host->features == NULL) {
2209 dp->host->features = am_string_to_feature(features);
2211 remove_disk(&waitq, dp);
2212 enqueue_disk(&runq, dp);
2213 flush_size += sp->act_size;
2216 printf("driver: flush size " OFF_T_FMT "\n", (OFF_T_FMT_TYPE)flush_size);
2219 log_add(L_WARNING, "WARNING: got empty schedule from planner");
2220 if(need_degraded==1) start_degraded_mode(&runq);
2221 start_some_dumps(&runq);
2224 static unsigned long
2230 if (ip == (interface_t *)0) {
2232 unsigned long maxusage=0;
2233 unsigned long curusage=0;
2234 for(p = lookup_interface(NULL); p != NULL; p = p->next) {
2235 maxusage += interface_get_maxusage(p);
2236 curusage += p->curusage;
2238 if (maxusage >= curusage)
2239 res = maxusage - curusage;
2244 if ((unsigned long)interface_get_maxusage(ip) >= ip->curusage)
2245 res = interface_get_maxusage(ip) - ip->curusage;
2260 printf("driver: interface-state time %s", time_str);
2262 for(ip = lookup_interface(NULL); ip != NULL; ip = ip->next) {
2263 printf(" if %s: free %lu", ip->name, free_kps(ip));
2273 ip->curusage += kps;
2277 deallocate_bandwidth(
2281 assert(kps <= ip->curusage);
2282 ip->curusage -= kps;
2293 total_free = (off_t)0;
2294 for(hdp = getconf_holdingdisks(); hdp != NULL; hdp = hdp->next) {
2295 diff = hdp->disksize - holdalloc(hdp)->allocated_space;
2303 * We return an array of pointers to assignedhd_t. The array contains at
2304 * most one entry per holding disk. The list of pointers is terminated by
2305 * a NULL pointer. Each entry contains a pointer to a holdingdisk and
2306 * how much diskspace to use on that disk. Later on, assign_holdingdisk
2307 * will allocate the given amount of space.
2308 * If there is not enough room on the holdingdisks, NULL is returned.
2311 static assignedhd_t **
2315 assignedhd_t * pref)
2317 assignedhd_t **result = NULL;
2318 holdingdisk_t *minp, *hdp;
2319 int i=0, num_holdingdisks=0; /* are we allowed to use the global thing? */
2322 off_t halloc, dalloc, hfree, dfree;
2324 (void)cur_idle; /* Quiet unused parameter warning */
2326 if (size < 2*DISK_BLOCK_KB)
2327 size = 2*DISK_BLOCK_KB;
2328 size = am_round(size, (off_t)DISK_BLOCK_KB);
2330 hold_debug(1, ("%s: want " OFF_T_FMT " K\n",
2331 debug_prefix_time(": find_diskspace"),
2332 (OFF_T_FMT_TYPE)size));
2334 for(hdp = getconf_holdingdisks(); hdp != NULL; hdp = hdp->next) {
2338 used = alloc(SIZEOF(*used) * num_holdingdisks);/*disks used during this run*/
2339 memset( used, 0, (size_t)num_holdingdisks );
2340 result = alloc(SIZEOF(assignedhd_t *) * (num_holdingdisks + 1));
2343 while( i < num_holdingdisks && size > (off_t)0 ) {
2344 /* find the holdingdisk with the fewest active dumpers and among
2345 * those the one with the biggest free space
2347 minp = NULL; minj = -1;
2348 for(j = 0, hdp = getconf_holdingdisks(); hdp != NULL; hdp = hdp->next, j++ ) {
2349 if( pref && pref->disk == hdp && !used[j] &&
2350 holdalloc(hdp)->allocated_space <= hdp->disksize - (off_t)DISK_BLOCK_KB) {
2355 else if( holdalloc(hdp)->allocated_space <= hdp->disksize - (off_t)(2*DISK_BLOCK_KB) &&
2358 holdalloc(hdp)->allocated_dumpers < holdalloc(minp)->allocated_dumpers ||
2359 (holdalloc(hdp)->allocated_dumpers == holdalloc(minp)->allocated_dumpers &&
2360 hdp->disksize-holdalloc(hdp)->allocated_space > minp->disksize-holdalloc(minp)->allocated_space)) ) {
2367 if( !minp ) { break; } /* all holding disks are full */
2370 /* hfree = free space on the disk */
2371 hfree = minp->disksize - holdalloc(minp)->allocated_space;
2373 /* dfree = free space for data, remove 1 header for each chunksize */
2374 dfree = hfree - (((hfree-(off_t)1)/holdingdisk_get_chunksize(minp))+(off_t)1) * (off_t)DISK_BLOCK_KB;
2376 /* dalloc = space I can allocate for data */
2377 dalloc = ( dfree < size ) ? dfree : size;
2379 /* halloc = space to allocate, including 1 header for each chunksize */
2380 halloc = dalloc + (((dalloc-(off_t)1)/holdingdisk_get_chunksize(minp))+(off_t)1) * (off_t)DISK_BLOCK_KB;
2382 hold_debug(1, ("%s: find diskspace: size " OFF_T_FMT " hf " OFF_T_FMT
2383 " df " OFF_T_FMT " da " OFF_T_FMT " ha " OFF_T_FMT"\n",
2384 debug_prefix_time(": find_diskspace"),
2385 (OFF_T_FMT_TYPE)size,
2386 (OFF_T_FMT_TYPE)hfree,
2387 (OFF_T_FMT_TYPE)dfree,
2388 (OFF_T_FMT_TYPE)dalloc,
2389 (OFF_T_FMT_TYPE)halloc));
2391 result[i] = alloc(SIZEOF(assignedhd_t));
2392 result[i]->disk = minp;
2393 result[i]->reserved = halloc;
2394 result[i]->used = (off_t)0;
2395 result[i]->destname = NULL;
2398 } /* while i < num_holdingdisks && size > 0 */
2401 if(size != (off_t)0) { /* not enough space available */
2402 printf("find diskspace: not enough diskspace. Left with "
2403 OFF_T_FMT " K\n", (OFF_T_FMT_TYPE)size);
2405 free_assignedhd(result);
2409 if (debug_holding > 1) {
2410 for( i = 0; result && result[i]; i++ ) {
2411 hold_debug(1, ("%s: find diskspace: selected %s free " OFF_T_FMT
2412 " reserved " OFF_T_FMT " dumpers %d\n",
2413 debug_prefix_time(": find_diskspace"),
2414 holdingdisk_get_diskdir(result[i]->disk),
2415 (OFF_T_FMT_TYPE)(result[i]->disk->disksize -
2416 holdalloc(result[i]->disk)->allocated_space),
2417 (OFF_T_FMT_TYPE)result[i]->reserved,
2418 holdalloc(result[i]->disk)->allocated_dumpers));
2427 assignedhd_t ** holdp,
2432 char *sfn = sanitise_filename(diskp->name);
2434 assignedhd_t **new_holdp;
2437 snprintf( lvl, SIZEOF(lvl), "%d", sched(diskp)->level );
2439 size = am_round(sched(diskp)->est_size - sched(diskp)->act_size,
2440 (off_t)DISK_BLOCK_KB);
2442 for( c = 0; holdp[c]; c++ )
2443 (void)c; /* count number of disks */
2445 /* allocate memory for sched(diskp)->holdp */
2446 for(j = 0; sched(diskp)->holdp && sched(diskp)->holdp[j]; j++)
2447 (void)j; /* Quiet lint */
2448 new_holdp = (assignedhd_t **)alloc(SIZEOF(assignedhd_t*)*(j+c+1));
2449 if (sched(diskp)->holdp) {
2450 memcpy(new_holdp, sched(diskp)->holdp, j * SIZEOF(*new_holdp));
2451 amfree(sched(diskp)->holdp);
2453 sched(diskp)->holdp = new_holdp;
2457 if( j > 0 ) { /* This is a request for additional diskspace. See if we can
2458 * merge assignedhd_t's */
2460 if( sched(diskp)->holdp[j-1]->disk == holdp[0]->disk ) { /* Yes! */
2461 sched(diskp)->holdp[j-1]->reserved += holdp[0]->reserved;
2462 holdalloc(holdp[0]->disk)->allocated_space += holdp[0]->reserved;
2463 size = (holdp[0]->reserved>size) ? (off_t)0 : size-holdp[0]->reserved;
2464 qname = quote_string(diskp->name);
2465 hold_debug(1, ("%s: merging holding disk %s to disk %s:%s, add "
2466 OFF_T_FMT " for reserved " OFF_T_FMT ", left "
2468 debug_prefix_time(": assign_holdingdisk"),
2469 holdingdisk_get_diskdir(
2470 sched(diskp)->holdp[j-1]->disk),
2471 diskp->host->hostname, qname,
2472 (OFF_T_FMT_TYPE)holdp[0]->reserved,
2473 (OFF_T_FMT_TYPE)sched(diskp)->holdp[j-1]->reserved,
2474 (OFF_T_FMT_TYPE)size));
2482 /* copy assignedhd_s to sched(diskp), adjust allocated_space */
2483 for( ; holdp[i]; i++ ) {
2484 holdp[i]->destname = newvstralloc( holdp[i]->destname,
2485 holdingdisk_get_diskdir(holdp[i]->disk), "/",
2486 hd_driver_timestamp, "/",
2487 diskp->host->hostname, ".",
2490 sched(diskp)->holdp[j++] = holdp[i];
2491 holdalloc(holdp[i]->disk)->allocated_space += holdp[i]->reserved;
2492 size = (holdp[i]->reserved > size) ? (off_t)0 :
2493 (size - holdp[i]->reserved);
2494 qname = quote_string(diskp->name);
2496 ("%s: %d assigning holding disk %s to disk %s:%s, reserved "
2497 OFF_T_FMT ", left " OFF_T_FMT "\n",
2498 debug_prefix_time(": assign_holdingdisk"),
2499 i, holdingdisk_get_diskdir(holdp[i]->disk),
2500 diskp->host->hostname, qname,
2501 (OFF_T_FMT_TYPE)holdp[i]->reserved,
2502 (OFF_T_FMT_TYPE)size));
2504 holdp[i] = NULL; /* so it doesn't get free()d... */
2506 sched(diskp)->holdp[j] = NULL;
2517 assignedhd_t **holdp;
2518 off_t total = (off_t)0;
2521 char *qname, *hqname, *qdest;
2523 (void)cmd; /* Quiet unused parameter warning */
2525 qname = quote_string(diskp->name);
2526 qdest = quote_string(sched(diskp)->destname);
2527 hold_debug(1, ("%s: %s:%s %s\n",
2528 debug_prefix_time(": adjust_diskspace"),
2529 diskp->host->hostname, qname, qdest));
2531 holdp = sched(diskp)->holdp;
2533 assert(holdp != NULL);
2535 for( i = 0; holdp[i]; i++ ) { /* for each allocated disk */
2536 diff = holdp[i]->used - holdp[i]->reserved;
2537 total += holdp[i]->used;
2538 holdalloc(holdp[i]->disk)->allocated_space += diff;
2539 hqname = quote_string(holdp[i]->disk->name);
2540 hold_debug(1, ("%s: hdisk %s done, reserved " OFF_T_FMT " used "
2541 OFF_T_FMT " diff " OFF_T_FMT " alloc " OFF_T_FMT
2543 debug_prefix_time(": adjust_diskspace"),
2544 holdp[i]->disk->name,
2545 (OFF_T_FMT_TYPE)holdp[i]->reserved,
2546 (OFF_T_FMT_TYPE)holdp[i]->used,
2547 (OFF_T_FMT_TYPE)diff,
2548 (OFF_T_FMT_TYPE)holdalloc(holdp[i]->disk)
2550 holdalloc(holdp[i]->disk)->allocated_dumpers ));
2551 holdp[i]->reserved += diff;
2555 sched(diskp)->act_size = total;
2557 hold_debug(1, ("%s: after: disk %s:%s used " OFF_T_FMT "\n",
2558 debug_prefix_time(": adjust_diskspace"),
2559 diskp->host->hostname, qname,
2560 (OFF_T_FMT_TYPE)sched(diskp)->act_size));
2569 assignedhd_t **holdp;
2572 holdp = sched(diskp)->holdp;
2574 assert(holdp != NULL);
2576 for( i = 0; holdp[i]; i++ ) { /* for each disk */
2577 /* find all files of this dump on that disk, and subtract their
2578 * reserved sizes from the disk's allocated space
2580 holdalloc(holdp[i]->disk)->allocated_space -= holdp[i]->used;
2583 holding_file_unlink(holdp[0]->destname); /* no need for the entire list,
2584 * because holding_file_unlink
2585 * will walk through all files
2586 * using cont_filename */
2587 free_assignedhd(sched(diskp)->holdp);
2588 sched(diskp)->holdp = NULL;
2589 sched(diskp)->act_size = (off_t)0;
2592 static assignedhd_t **
2599 char buffer[DISK_BLOCK_BYTES];
2601 assignedhd_t **result;
2604 int num_holdingdisks=0;
2605 char dirname[1000], *ch;
2607 char *filename = destname;
2609 memset(buffer, 0, sizeof(buffer));
2610 for(hdp = getconf_holdingdisks(); hdp != NULL; hdp = hdp->next) {
2613 used = alloc(SIZEOF(off_t) * num_holdingdisks);
2614 for(i=0;i<num_holdingdisks;i++)
2616 result = alloc(SIZEOF(assignedhd_t *) * (num_holdingdisks + 1));
2618 while(filename != NULL && filename[0] != '\0') {
2619 strncpy(dirname, filename, 999);
2621 ch = strrchr(dirname,'/');
2623 ch = strrchr(dirname,'/');
2626 for(j = 0, hdp = getconf_holdingdisks(); hdp != NULL;
2627 hdp = hdp->next, j++ ) {
2628 if(strcmp(dirname, holdingdisk_get_diskdir(hdp))==0) {
2633 if(stat(filename, &finfo) == -1) {
2634 fprintf(stderr, "stat %s: %s\n", filename, strerror(errno));
2635 finfo.st_size = (off_t)0;
2637 used[j] += ((off_t)finfo.st_size+(off_t)1023)/(off_t)1024;
2638 if((fd = open(filename,O_RDONLY)) == -1) {
2639 fprintf(stderr,"build_diskspace: open of %s failed: %s\n",
2640 filename, strerror(errno));
2643 if ((buflen = fullread(fd, buffer, SIZEOF(buffer))) > 0) {;
2644 parse_file_header(buffer, &file, (size_t)buflen);
2647 filename = file.cont_filename;
2650 for(j = 0, i=0, hdp = getconf_holdingdisks(); hdp != NULL;
2651 hdp = hdp->next, j++ ) {
2652 if(used[j] != (off_t)0) {
2653 result[i] = alloc(SIZEOF(assignedhd_t));
2654 result[i]->disk = hdp;
2655 result[i]->reserved = used[j];
2656 result[i]->used = used[j];
2657 result[i]->destname = stralloc(destname);
2675 printf("driver: hdisk-state time %s", time_str);
2677 for(hdp = getconf_holdingdisks(), dsk = 0; hdp != NULL; hdp = hdp->next, dsk++) {
2678 diff = hdp->disksize - holdalloc(hdp)->allocated_space;
2679 printf(" hdisk %d: free " OFF_T_FMT " dumpers %d", dsk,
2680 (OFF_T_FMT_TYPE)diff, holdalloc(hdp)->allocated_dumpers);
2686 update_failed_dump_to_tape(
2690 * should simply set no_bump
2693 time_t save_timestamp = sched(dp)->timestamp;
2694 /* setting timestamp to 0 removes the current level from the
2695 * database, so that we ensure that it will not be bumped to the
2696 * next level on the next run. If we didn't do this, dumpdates or
2697 * gnutar-lists might have been updated already, and a bumped
2698 * incremental might be created. */
2699 sched(dp)->timestamp = 0;
2700 update_info_dumper(dp, (off_t)-1, (off_t)-1, (time_t)-1);
2701 sched(dp)->timestamp = save_timestamp;
2704 /* ------------------- */
2712 off_t origsize = (off_t)0;
2713 off_t dumpsize = (off_t)0;
2714 time_t dumptime = (time_t)0;
2715 double tapetime = 0.0;
2717 int result_argc, rc;
2718 char *result_argv[MAX_ARGS+1];
2719 int dumper_tryagain = 0;
2722 qname = quote_string(dp->name);
2723 printf("driver: dumping %s:%s directly to tape\n",
2724 dp->host->hostname, qname);
2727 /* pick a dumper and fail if there are no idle dumpers */
2729 dumper = idle_dumper();
2731 printf("driver: no idle dumpers for %s:%s.\n",
2732 dp->host->hostname, qname);
2734 log_add(L_WARNING, "no idle dumpers for %s:%s.\n",
2735 dp->host->hostname, qname);
2737 return 2; /* fatal problem */
2740 /* tell the taper to read from a port number of its choice */
2742 taper_cmd(PORT_WRITE, dp, NULL, sched(dp)->level, sched(dp)->datestamp);
2743 cmd = getresult(taper, 1, &result_argc, result_argv, MAX_ARGS+1);
2745 printf("driver: did not get PORT from taper for %s:%s\n",
2746 dp->host->hostname, qname);
2749 return 2; /* fatal problem */
2751 /* copy port number */
2752 dumper->output_port = atoi(result_argv[2]);
2754 /* tell the dumper to dump to a port */
2756 dumper_cmd(dumper, PORT_DUMP, dp);
2757 dp->host->start_t = time(NULL) + 15;
2759 /* update statistics & print state */
2761 taper_busy = dumper->busy = 1;
2762 dp->host->inprogress += 1;
2764 sched(dp)->timestamp = time((time_t *)0);
2765 allocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
2766 idle_reason = NOT_IDLE;
2770 /* wait for result from dumper */
2772 cmd = getresult(dumper->fd, 1, &result_argc, result_argv, MAX_ARGS+1);
2776 /* either eof or garbage from dumper */
2777 log_add(L_WARNING, "%s pid %ld is messed up, ignoring it.\n",
2778 dumper->name, (long)dumper->pid);
2779 dumper->down = 1; /* mark it down so it isn't used again */
2780 failed = 1; /* dump failed, must still finish up with taper */
2783 case DONE: /* DONE <handle> <origsize> <dumpsize> <dumptime> <errstr> */
2784 /* everything went fine */
2785 origsize = (off_t)atof(result_argv[3]);
2786 /*dumpsize = (off_t)atof(result_argv[4]);*/
2787 dumptime = (time_t)atof(result_argv[5]);
2790 case NO_ROOM: /* NO-ROOM <handle> */
2791 dumper_cmd(dumper, ABORT, dp);
2792 cmd = getresult(dumper->fd, 1, &result_argc, result_argv, MAX_ARGS+1);
2793 assert(cmd == ABORT_FINISHED);
2795 case TRYAGAIN: /* TRY-AGAIN <handle> <errstr> */
2797 /* dump failed, but we must still finish up with taper */
2798 /* problem with dump, possibly nonfatal, retry one time */
2799 sched(dp)->attempted++;
2800 failed = sched(dp)->attempted;
2801 dumper_tryagain = 1;
2804 case FAILED: /* FAILED <handle> <errstr> */
2805 /* dump failed, but we must still finish up with taper */
2806 failed = 2; /* fatal problem with dump */
2811 * Note that at this point, even if the dump above failed, it may
2812 * not be a fatal failure if taper below says we can try again.
2813 * E.g. a dumper failure above may actually be the result of a
2814 * tape overflow, which in turn causes dump to see "broken pipe",
2815 * "no space on device", etc., since taper closed the port first.
2820 cmd = getresult(taper, 1, &result_argc, result_argv, MAX_ARGS+1);
2824 case DONE: /* DONE <handle> <label> <tape file> <err mess> */
2825 if(result_argc != 5) {
2826 error("error [dump to tape DONE result_argc != 5: %d]", result_argc);
2830 if(failed == 1) goto tryagain; /* dump didn't work */
2831 else if(failed == 2) goto failed_dumper;
2833 free_serial(result_argv[2]);
2835 dumpsize = (off_t)0;
2836 if (*result_argv[5] == '"') {
2837 /* String was quoted */
2838 OFF_T_FMT_TYPE dumpsize_ = (OFF_T_FMT_TYPE)0;
2839 rc = sscanf(result_argv[5],"\"[sec %lf kb " OFF_T_FMT " ",
2840 &tapetime, &dumpsize_);
2841 dumpsize = (off_t)dumpsize_;
2843 /* String was not quoted */
2844 OFF_T_FMT_TYPE dumpsize_ = (OFF_T_FMT_TYPE)0;
2845 rc = sscanf(result_argv[5],"[sec %lf kb " OFF_T_FMT " ",
2846 &tapetime, &dumpsize_);
2847 dumpsize = (off_t)dumpsize_;
2850 error("error [malformed result: %d items matched in '%s']",
2851 rc, result_argv[5]);
2856 /* every thing went fine */
2857 update_info_dumper(dp, origsize, dumpsize, dumptime);
2858 filenum = OFF_T_ATOI(result_argv[4]);
2859 update_info_taper(dp, result_argv[3], filenum, sched(dp)->level);
2860 /* note that update_info_dumper() must be run before
2861 update_info_taper(), since update_info_dumper overwrites
2862 tape information. */
2867 case TRYAGAIN: /* TRY-AGAIN <handle> <err mess> */
2868 tape_left = tape_length;
2870 if(dumper_tryagain == 0) {
2871 sched(dp)->attempted++;
2872 if(sched(dp)->attempted > failed)
2873 failed = sched(dp)->attempted;
2877 headqueue_disk(&runq, dp);
2879 update_failed_dump_to_tape(dp);
2880 free_serial(result_argv[2]);
2883 case SPLIT_CONTINUE: /* SPLIT_CONTINUE <handle> <new_label> */
2884 if (result_argc != 3) {
2885 error("error [taper SPLIT_CONTINUE result_argc != 3: %d]", result_argc);
2888 fprintf(stderr, "driver: Got SPLIT_CONTINUE %s %s\n",
2889 result_argv[2], result_argv[3]);
2890 goto continue_port_dump;
2892 case SPLIT_NEEDNEXT:
2893 fprintf(stderr, "driver: Got SPLIT_NEEDNEXT %s %s\n", result_argv[2], result_argv[3]);
2895 goto continue_port_dump;
2897 case TAPE_ERROR: /* TAPE-ERROR <handle> <err mess> */
2900 update_failed_dump_to_tape(dp);
2901 free_serial(result_argv[2]);
2902 failed = 2; /* fatal problem */
2903 start_degraded_mode(&runq);
2907 /* reset statistics & return */
2909 taper_busy = dumper->busy = 0;
2910 dp->host->inprogress -= 1;
2912 deallocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
2925 for(len = 0, p = q.head; p != NULL; len++, p = p->next)
2926 (void)len; /* Quiet lint */
2931 short_dump_state(void)
2936 wall_time = walltime_str(curclock());
2938 printf("driver: state time %s ", wall_time);
2939 printf("free kps: %lu space: " OFF_T_FMT " taper: ",
2940 free_kps((interface_t *)0),
2941 (OFF_T_FMT_TYPE)free_space());
2942 if(degraded_mode) printf("DOWN");
2943 else if(!taper_busy) printf("idle");
2944 else printf("writing");
2946 for(i = 0; i < inparallel; i++) if(!dmptable[i].busy) nidle++;
2947 printf(" idle-dumpers: %d", nidle);
2948 printf(" qlen tapeq: %d", queue_length(tapeq));
2949 printf(" runq: %d", queue_length(runq));
2950 printf(" roomq: %d", queue_length(roomq));
2951 printf(" wakeup: %d", (int)sleep_time);
2952 printf(" driver-idle: %s\n", idle_strings[idle_reason]);
2953 interface_state(wall_time);
2954 holdingdisk_state(wall_time);
2967 printf("================\n");
2968 printf("driver state at time %s: %s\n", walltime_str(curclock()), str);
2969 printf("free kps: %lu, space: " OFF_T_FMT "\n",
2970 free_kps((interface_t *)0),
2971 (OFF_T_FMT_TYPE)free_space());
2972 if(degraded_mode) printf("taper: DOWN\n");
2973 else if(!taper_busy) printf("taper: idle\n");
2974 else printf("taper: writing %s:%s.%d est size " OFF_T_FMT "\n",
2975 taper_disk->host->hostname, taper_disk->name,
2976 sched(taper_disk)->level,
2977 sched(taper_disk)->est_size);
2978 for(i = 0; i < inparallel; i++) {
2979 dp = dmptable[i].dp;
2980 if(!dmptable[i].busy)
2981 printf("%s: idle\n", dmptable[i].name);
2983 qname = quote_string(dp->name);
2984 printf("%s: dumping %s:%s.%d est kps %d size " OFF_T_FMT " time %lu\n",
2985 dmptable[i].name, dp->host->hostname, qname, sched(dp)->level,
2986 sched(dp)->est_kps, sched(dp)->est_size, sched(dp)->est_time);
2989 dump_queue("TAPE", tapeq, 5, stdout);
2990 dump_queue("ROOM", roomq, 5, stdout);
2991 dump_queue("RUN ", runq, 5, stdout);
2992 printf("================\n");