2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-2000 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.58.2.31.2.8.2.20.2.14 2005/02/09 18:12:31 martinea 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.
47 #include "server_util.h"
49 disklist_t waitq, runq, tapeq, roomq;
50 int pending_aborts, inside_dump_to_tape;
53 unsigned long reserved_space;
54 unsigned long total_disksize;
58 long tape_length, tape_left = 0;
60 am_host_t *flushhost = NULL;
62 int client_constrained P((disk_t *dp));
63 int sort_by_priority_reversed P((disk_t *a, disk_t *b));
64 int sort_by_time P((disk_t *a, disk_t *b));
65 int start_some_dumps P((disklist_t *rq));
66 void dump_schedule P((disklist_t *qp, char *str));
67 void start_degraded_mode P((disklist_t *queuep));
68 void handle_taper_result P((void));
69 dumper_t *idle_dumper P((void));
70 int some_dumps_in_progress P((void));
71 int num_busy_dumpers P((void));
72 dumper_t *lookup_dumper P((int fd));
73 void handle_dumper_result P((int fd));
74 void read_flush P((disklist_t *tapeqp));
75 void read_schedule P((disklist_t *waitqp, disklist_t *runqp));
76 int free_kps P((interface_t *ip));
77 void interface_state P((char *time_str));
78 void allocate_bandwidth P((interface_t *ip, int kps));
79 void deallocate_bandwidth P((interface_t *ip, int kps));
80 unsigned long free_space P((void));
81 assignedhd_t **find_diskspace P((unsigned long size, int *cur_idle, assignedhd_t *preferred));
82 char *diskname2filename P((char *dname));
83 int assign_holdingdisk P((assignedhd_t **holdp, disk_t *diskp));
84 static void adjust_diskspace P((disk_t *diskp, cmd_t cmd));
85 static void delete_diskspace P((disk_t *diskp));
86 assignedhd_t **build_diskspace P((char *destname));
87 void holdingdisk_state P((char *time_str));
88 int dump_to_tape P((disk_t *dp));
89 int queue_length P((disklist_t q));
90 void short_dump_state P((void));
91 void dump_state P((char *str));
92 void startaflush P((void));
93 int main P((int main_argc, char **main_argv));
95 static int idle_reason;
99 char *idle_strings[] = {
102 #define IDLE_START_WAIT 1
104 #define IDLE_NO_DUMPERS 2
106 #define IDLE_NO_HOLD 3
108 #define IDLE_CLIENT_CONSTRAINED 4
109 "client-constrained",
110 #define IDLE_NO_DISKSPACE 5
112 #define IDLE_TOO_LARGE 6
114 #define IDLE_NO_BANDWIDTH 7
116 #define IDLE_TAPER_WAIT 8
120 #define SLEEP_MAX (24*3600)
121 struct timeval sleep_time = { SLEEP_MAX, 0 };
122 /* enabled if any disks are in start-wait: */
123 int any_delayed_disk = 0;
125 int main(main_argc, main_argv)
135 generic_fs_stats_t fs;
137 unsigned long malloc_hist_1, malloc_size_1;
138 unsigned long malloc_hist_2, malloc_size_2;
139 unsigned long reserve = 100;
144 char *result_argv[MAX_ARGS+1];
150 for(fd = 3; fd < FD_SETSIZE; fd++) {
152 * Make sure nobody spoofs us with a lot of extra open files
153 * that would cause an open we do to get a very high file
154 * descriptor, which in turn might be used as an index into
155 * an array (e.g. an fd_set).
160 setvbuf(stdout, (char *)NULL, _IOLBF, 0);
161 setvbuf(stderr, (char *)NULL, _IOLBF, 0);
165 signal(SIGPIPE, SIG_IGN);
167 malloc_size_1 = malloc_inuse(&malloc_hist_1);
169 erroutput_type = (ERR_AMANDALOG|ERR_INTERACTIVE);
170 set_logerror(logerror);
175 printf("%s: pid %ld executable %s version %s\n",
176 get_pname(), (long) getpid(), main_argv[0], version());
179 config_name = stralloc(main_argv[1]);
180 config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
182 if(strncmp(main_argv[2], "nodump", 6) == 0) {
188 char my_cwd[STR_SIZE];
190 if (getcwd(my_cwd, sizeof(my_cwd)) == NULL) {
191 error("cannot determine current working directory");
193 config_dir = stralloc2(my_cwd, "/");
194 if ((config_name = strrchr(my_cwd, '/')) != NULL) {
195 config_name = stralloc(config_name + 1);
201 conffile = stralloc2(config_dir, CONFFILE_NAME);
202 if(read_conffile(conffile)) {
203 error("errors processing config file \"%s\"", conffile);
208 datestamp = construct_datestamp(NULL);
209 timestamp = construct_timestamp(NULL);
210 log_add(L_START,"date %s", datestamp);
212 taper_program = vstralloc(libexecdir, "/", "taper", versionsuffix(), NULL);
213 dumper_program = vstralloc(libexecdir, "/", "dumper", versionsuffix(),
216 conf_taperalgo = getconf_int(CNF_TAPERALGO);
217 conf_tapetype = getconf_str(CNF_TAPETYPE);
218 tape = lookup_tapetype(conf_tapetype);
219 tape_length = tape->length;
220 printf("driver: tape size %ld\n", tape_length);
222 /* taper takes a while to get going, so start it up right away */
225 startup_tape_process(taper_program);
226 taper_cmd(START_TAPER, datestamp, NULL, 0, NULL);
228 /* start initializing: read in databases */
230 conf_diskfile = getconf_str(CNF_DISKFILE);
231 if (*conf_diskfile == '/') {
232 conf_diskfile = stralloc(conf_diskfile);
234 conf_diskfile = stralloc2(config_dir, conf_diskfile);
236 if((origqp = read_diskfile(conf_diskfile)) == NULL) {
237 error("could not load disklist \"%s\"", conf_diskfile);
239 amfree(conf_diskfile);
241 /* set up any configuration-dependent variables */
243 inparallel = getconf_int(CNF_INPARALLEL);
245 reserve = getconf_int(CNF_RESERVE);
248 for(hdp = getconf_holdingdisks(), dsk = 0; hdp != NULL; hdp = hdp->next, dsk++) {
249 hdp->up = (void *)alloc(sizeof(holdalloc_t));
250 holdalloc(hdp)->allocated_dumpers = 0;
251 holdalloc(hdp)->allocated_space = 0L;
253 if(get_fs_stats(hdp->diskdir, &fs) == -1
254 || access(hdp->diskdir, W_OK) == -1) {
255 log_add(L_WARNING, "WARNING: ignoring holding disk %s: %s\n",
256 hdp->diskdir, strerror(errno));
262 if(hdp->disksize > 0) {
263 if(hdp->disksize > fs.avail) {
265 "WARNING: %s: %ld KB requested, but only %ld KB available.",
266 hdp->diskdir, hdp->disksize, fs.avail);
267 hdp->disksize = fs.avail;
270 else if(fs.avail + hdp->disksize < 0) {
272 "WARNING: %s: not %ld KB free.",
273 hdp->diskdir, -hdp->disksize);
278 hdp->disksize += fs.avail;
281 printf("driver: adding holding disk %d dir %s size %ld chunksize %ld\n",
282 dsk, hdp->diskdir, hdp->disksize, hdp->chunksize);
284 newdir = newvstralloc(newdir,
285 hdp->diskdir, "/", timestamp,
287 if(!mkholdingdir(newdir)) {
290 total_disksize += hdp->disksize;
293 reserved_space = total_disksize * (reserve / 100.0);
295 printf("reserving %ld out of %ld for degraded-mode dumps\n",
296 reserved_space, free_space());
300 if(inparallel > MAX_DUMPERS) inparallel = MAX_DUMPERS;
302 /* fire up the dumpers now while we are waiting */
304 if(!nodump) startup_dump_processes(dumper_program, inparallel);
307 * Read schedule from stdin. Usually, this is a pipe from planner,
308 * so the effect is that we wait here for the planner to
309 * finish, but meanwhile the taper is rewinding the tape, reading
310 * the label, checking it, writing a new label and all that jazz
311 * in parallel with the planner.
315 tapeq.head = tapeq.tail = NULL;
316 roomq.head = roomq.tail = NULL;
317 runq.head = runq.tail = NULL;
321 log_add(L_STATS, "startup time %s", walltime_str(curclock()));
323 printf("driver: start time %s inparallel %d bandwidth %d diskspace %lu",
324 walltime_str(curclock()), inparallel, free_kps((interface_t *)0),
326 printf(" dir %s datestamp %s driver: drain-ends tapeq %s big-dumpers %s\n",
327 "OBSOLETE", datestamp, taperalgo2str(conf_taperalgo),
328 getconf_str(CNF_DUMPORDER));
331 /* Let's see if the tape is ready */
333 cmd = getresult(taper, 1, &result_argc, result_argv, MAX_ARGS+1);
335 if(cmd != TAPER_OK) {
336 /* no tape, go into degraded mode: dump to holding disk */
337 start_degraded_mode(&runq);
338 FD_CLR(taper,&readset);
341 short_dump_state(); /* for amstatus */
343 tape_left = tape_length;
347 /* Start autoflush while waiting for dump schedule */
349 /* Start any autoflush tape writes */
352 short_dump_state(); /* for amstatus */
354 /* Process taper results until the schedule arrives */
357 FD_SET(0, &selectset);
358 FD_SET(taper, &selectset);
360 if(select(taper+1, (SELECT_ARG_TYPE *)(&selectset), NULL, NULL,
362 error("select: %s", strerror(errno));
363 if (FD_ISSET(0, &selectset)) break; /* schedule arrived */
364 if (FD_ISSET(taper, &selectset)) handle_taper_result();
365 short_dump_state(); /* for amstatus */
370 /* Read the dump schedule */
371 read_schedule(&waitq, &runq);
374 /* Start any needed flushes */
377 while(start_some_dumps(&runq) || some_dumps_in_progress() ||
381 /* wait for results */
383 memcpy(&selectset, &readset, sizeof(fd_set));
384 if(select(maxfd+1, (SELECT_ARG_TYPE *)(&selectset),
385 NULL, NULL, &sleep_time) == -1)
386 error("select: %s", strerror(errno));
388 /* handle any results that have come in */
390 for(fd = 0; fd <= maxfd; fd++) {
392 * The first pass through the following loop, we have
393 * data ready for areads (called by getresult, called by
394 * handle_.*_result). But that may read more than one record,
395 * so we need to keep processing as long as areads has data.
396 * We will get control back after each record and the buffer
397 * will go empty (indicated by areads_dataready(fd) == 0)
398 * after the last one available has been processed.
400 while(FD_ISSET(fd, &selectset) || areads_dataready(fd) > 0) {
401 if(fd == taper) handle_taper_result();
402 else handle_dumper_result(fd);
403 FD_CLR(fd, &selectset);
409 /* handle any remaining dumps by dumping directly to tape, if possible */
411 while(!empty(runq)) {
412 diskp = dequeue_disk(&runq);
414 int rc = dump_to_tape(diskp);
417 "%s %s %d [dump to tape failed, will try again]",
418 diskp->host->hostname,
420 sched(diskp)->level);
422 log_add(L_FAIL, "%s %s %s %d [dump to tape failed]",
423 diskp->host->hostname,
425 sched(diskp)->datestamp,
426 sched(diskp)->level);
429 log_add(L_FAIL, "%s %s %s %d [%s]",
430 diskp->host->hostname, diskp->name,
431 sched(diskp)->datestamp, sched(diskp)->level,
433 "can't dump no-hold disk in degraded mode" :
434 "no more holding disk space");
437 short_dump_state(); /* for amstatus */
439 printf("driver: QUITTING time %s telling children to quit\n",
440 walltime_str(curclock()));
444 for(dumper = dmptable; dumper < dmptable + inparallel; dumper++) {
445 dumper_cmd(dumper, QUIT, NULL);
450 taper_cmd(QUIT, NULL, NULL, 0, NULL);
453 /* wait for all to die */
456 char number[NUM_STR_SIZE];
462 if((pid = wait(&retstat)) == -1) {
463 if(errno == EINTR) continue;
467 if(! WIFEXITED(retstat)) {
469 code = WTERMSIG(retstat);
470 } else if(WEXITSTATUS(retstat) != 0) {
472 code = WEXITSTATUS(retstat);
475 for(dumper = dmptable; dumper < dmptable + inparallel; dumper++) {
476 if(pid == dumper->pid) {
477 who = stralloc(dumper->name);
481 if(who == NULL && pid == taper_pid) {
482 who = stralloc("taper");
484 if(what != NULL && who == NULL) {
485 ap_snprintf(number, sizeof(number), "%ld", (long)pid);
486 who = stralloc2("unknown pid ", number);
489 log_add(L_WARNING, "%s exited with %s %d\n", who, what, code);
490 printf("driver: %s exited with %s %d\n", who, what, code);
495 for(dumper = dmptable; dumper < dmptable + inparallel; dumper++) {
496 amfree(dumper->name);
499 for(hdp = getconf_holdingdisks(); hdp != NULL; hdp = hdp->next) {
500 cleanup_holdingdisk(hdp->diskdir, 0);
505 printf("driver: FINISHED time %s\n", walltime_str(curclock()));
507 log_add(L_FINISH,"date %s time %s", datestamp, walltime_str(curclock()));
511 amfree(dumper_program);
512 amfree(taper_program);
516 malloc_size_2 = malloc_inuse(&malloc_hist_2);
518 if(malloc_size_1 != malloc_size_2) {
519 malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
530 if(!degraded_mode && !taper_busy && !empty(tapeq)) {
531 datestamp = sched(tapeq.head)->datestamp;
532 switch(conf_taperalgo) {
534 dp = dequeue_disk(&tapeq);
538 while (fit != NULL) {
539 if(sched(fit)->act_size <= tape_left &&
540 strcmp(sched(fit)->datestamp, datestamp) <= 0) {
548 if(dp) remove_disk(&tapeq, dp);
551 fit = dp = tapeq.head;
552 while (fit != NULL) {
553 if(sched(fit)->act_size > sched(dp)->act_size &&
554 strcmp(sched(fit)->datestamp, datestamp) <= 0) {
559 if(dp) remove_disk(&tapeq, dp);
561 case ALGO_LARGESTFIT:
563 while (fit != NULL) {
564 if(sched(fit)->act_size <= tape_left &&
565 (!dp || sched(fit)->act_size > sched(dp)->act_size) &&
566 strcmp(sched(fit)->datestamp, datestamp) <= 0) {
571 if(dp) remove_disk(&tapeq, dp);
577 remove_disk(&tapeq, dp);
580 if(!dp) { /* ALGO_SMALLEST, or default if nothing fit. */
581 if(conf_taperalgo != ALGO_SMALLEST) {
583 "driver: startaflush: Using SMALLEST because nothing fit\n");
585 fit = dp = tapeq.head;
586 while (fit != NULL) {
587 if(sched(fit)->act_size < sched(dp)->act_size &&
588 strcmp(sched(fit)->datestamp, datestamp) <= 0) {
593 if(dp) remove_disk(&tapeq, dp);
597 taper_cmd(FILE_WRITE, dp, sched(dp)->destname, sched(dp)->level,
598 sched(dp)->datestamp);
599 fprintf(stderr,"driver: startaflush: %s %s %s %ld %ld\n",
600 taperalgo2str(conf_taperalgo), dp->host->hostname,
601 dp->name, sched(taper_disk)->act_size, tape_left);
602 if(sched(dp)->act_size <= tape_left)
603 tape_left -= sched(dp)->act_size;
610 int client_constrained(dp)
615 /* first, check if host is too busy */
617 if(dp->host->inprogress >= dp->host->maxdumps) {
621 /* next, check conflict with other dumps on same spindle */
623 if(dp->spindle == -1) { /* but spindle -1 never conflicts by def. */
627 for(dp2 = dp->host->disks; dp2 != NULL; dp2 = dp2->hostnext)
628 if(dp2->inprogress && dp2->spindle == dp->spindle) {
635 int start_some_dumps(rq)
639 disk_t *diskp, *diskp_accept;
641 assignedhd_t **holdp=NULL, **holdp_accept;
642 time_t now = time(NULL);
645 idle_reason = IDLE_NO_DUMPERS;
646 sleep_time.tv_sec = SLEEP_MAX;
647 sleep_time.tv_usec = 0;
648 any_delayed_disk = 0;
650 if(rq->head == NULL) {
656 * A potential problem with starting from the bottom of the dump time
657 * distribution is that a slave host will have both one of the shortest
658 * and one of the longest disks, so starting its shortest disk first will
659 * tie up the host and eliminate its longest disk from consideration the
660 * first pass through. This could cause a big delay in starting that long
661 * disk, which could drag out the whole night's dumps.
663 * While starting from the top of the dump time distribution solves the
664 * above problem, this turns out to be a bad idea, because the big dumps
665 * will almost certainly pack the holding disk completely, leaving no
666 * room for even one small dump to start. This ends up shutting out the
667 * small-end dumpers completely (they stay idle).
669 * The introduction of multiple simultaneous dumps to one host alleviates
670 * the biggest&smallest dumps problem: both can be started at the
673 for(dumper = dmptable; dumper < dmptable+inparallel; dumper++) {
674 if(dumper->busy || dumper->down) continue;
675 /* found an idle dumper, now find a disk for it */
680 if(idle_reason == IDLE_NO_DUMPERS)
681 idle_reason = NOT_IDLE;
686 assert(diskp->host != NULL && sched(diskp) != NULL);
688 /* round estimate to next multiple of DISK_BLOCK_KB */
689 sched(diskp)->est_size = am_round(sched(diskp)->est_size,
692 if(diskp->host->start_t > now) {
693 cur_idle = max(cur_idle, IDLE_START_WAIT);
694 sleep_time.tv_sec = min(diskp->host->start_t - now,
696 any_delayed_disk = 1;
698 else if(diskp->start_t > now) {
699 cur_idle = max(cur_idle, IDLE_START_WAIT);
700 sleep_time.tv_sec = min(diskp->start_t - now,
702 any_delayed_disk = 1;
704 else if(diskp->host->netif->curusage > 0 &&
705 sched(diskp)->est_kps > free_kps(diskp->host->netif))
706 cur_idle = max(cur_idle, IDLE_NO_BANDWIDTH);
707 else if(sched(diskp)->no_space)
708 cur_idle = max(cur_idle, IDLE_NO_DISKSPACE);
709 else if((holdp = find_diskspace(sched(diskp)->est_size,&cur_idle,NULL)) == NULL)
710 cur_idle = max(cur_idle, IDLE_NO_DISKSPACE);
711 else if(diskp->no_hold) {
712 free_assignedhd(holdp);
713 cur_idle = max(cur_idle, IDLE_NO_HOLD);
714 } else if(client_constrained(diskp)) {
715 free_assignedhd(holdp);
716 cur_idle = max(cur_idle, IDLE_CLIENT_CONSTRAINED);
719 /* disk fits, dump it */
720 int accept = !diskp_accept;
723 char *dumporder = getconf_str(CNF_DUMPORDER);
724 if(strlen(dumporder) <= (dumper-dmptable)) {
725 if(dumper-dmptable < 3)
731 dumptype = dumporder[dumper-dmptable];
734 case 's': accept = (sched(diskp)->est_size < sched(diskp_accept)->est_size);
736 case 'S': accept = (sched(diskp)->est_size > sched(diskp_accept)->est_size);
738 case 't': accept = (sched(diskp)->est_time < sched(diskp_accept)->est_time);
740 case 'T': accept = (sched(diskp)->est_time > sched(diskp_accept)->est_time);
742 case 'b': accept = (sched(diskp)->est_kps < sched(diskp_accept)->est_kps);
744 case 'B': accept = (sched(diskp)->est_kps > sched(diskp_accept)->est_kps);
746 default: log_add(L_WARNING, "Unknown dumporder character \'%c\', using 's'.\n",
748 accept = (sched(diskp)->est_size < sched(diskp_accept)->est_size);
753 if( !diskp_accept || !degraded_mode || diskp->priority >= diskp_accept->priority) {
754 if(holdp_accept) free_assignedhd(holdp_accept);
755 diskp_accept = diskp;
756 holdp_accept = holdp;
759 free_assignedhd(holdp);
763 free_assignedhd(holdp);
769 diskp = diskp_accept;
770 holdp = holdp_accept;
773 sched(diskp)->act_size = 0;
774 allocate_bandwidth(diskp->host->netif, sched(diskp)->est_kps);
775 sched(diskp)->activehd = assign_holdingdisk(holdp, diskp);
777 diskp->host->inprogress += 1; /* host is now busy */
778 diskp->inprogress = 1;
779 sched(diskp)->dumper = dumper;
780 sched(diskp)->timestamp = time((time_t *)0);
782 dumper->busy = 1; /* dumper is now busy */
783 dumper->dp = diskp; /* link disk to dumper */
785 remove_disk(rq, diskp); /* take it off the run queue */
786 dumper_cmd(dumper, FILE_DUMP, diskp);
787 diskp->host->start_t = time(NULL) + 15;
789 idle_reason = max(idle_reason, cur_idle);
794 int sort_by_priority_reversed(a, b)
797 if(sched(b)->priority - sched(a)->priority != 0)
798 return sched(b)->priority - sched(a)->priority;
800 return sort_by_time(a, b);
803 int sort_by_time(a, b)
808 if ((diff = sched(a)->est_time - sched(b)->est_time) < 0) {
810 } else if (diff > 0) {
817 void dump_schedule(qp, str)
823 printf("dump of driver schedule %s:\n--------\n", str);
825 for(dp = qp->head; dp != NULL; dp = dp->next) {
826 printf(" %-20s %-25s lv %d t %5ld s %8lu p %d\n",
827 dp->host->hostname, dp->name, sched(dp)->level,
828 sched(dp)->est_time, sched(dp)->est_size, sched(dp)->priority);
830 printf("--------\n");
834 void start_degraded_mode(queuep)
839 unsigned long est_full_size;
841 newq.head = newq.tail = 0;
843 dump_schedule(queuep, "before start degraded mode");
846 while(!empty(*queuep)) {
847 dp = dequeue_disk(queuep);
849 if(sched(dp)->level != 0)
850 /* go ahead and do the disk as-is */
851 insert_disk(&newq, dp, sort_by_priority_reversed);
853 if (reserved_space + est_full_size + sched(dp)->est_size
855 insert_disk(&newq, dp, sort_by_priority_reversed);
856 est_full_size += sched(dp)->est_size;
858 else if(sched(dp)->degr_level != -1) {
859 sched(dp)->level = sched(dp)->degr_level;
860 sched(dp)->dumpdate = sched(dp)->degr_dumpdate;
861 sched(dp)->est_size = sched(dp)->degr_size;
862 sched(dp)->est_time = sched(dp)->degr_time;
863 sched(dp)->est_kps = sched(dp)->degr_kps;
864 insert_disk(&newq, dp, sort_by_priority_reversed);
867 log_add(L_FAIL, "%s %s %s %d [can't switch to incremental dump]",
868 dp->host->hostname, dp->name,
869 sched(dp)->datestamp, sched(dp)->level);
877 dump_schedule(queuep, "after start degraded mode");
880 void continue_dumps()
884 int active_dumpers=0, busy_dumpers=0, i;
887 /* First we try to grant diskspace to some dumps waiting for it. */
888 for( dp = roomq.head; dp; dp = ndp ) {
890 /* find last holdingdisk used by this dump */
891 for( i = 0, h = sched(dp)->holdp; h[i+1]; i++ );
892 /* find more space */
893 h = find_diskspace( sched(dp)->est_size - sched(dp)->act_size, &active_dumpers, h[i] );
895 for(dumper = dmptable; dumper < dmptable + inparallel &&
896 dumper->dp != dp; dumper++);
897 assert( dumper < dmptable + inparallel );
898 sched(dp)->activehd = assign_holdingdisk( h, dp );
899 dumper_cmd( dumper, CONTINUE, dp );
901 remove_disk( &roomq, dp );
905 /* So for some disks there is less holding diskspace available than
906 * was asked for. Possible reasons are
907 * a) diskspace has been allocated for other dumps which are
908 * still running or already being written to tape
909 * b) all other dumps have been suspended due to lack of diskspace
910 * c) this dump doesn't fit on all the holding disks
911 * Case a) is not a problem. We just wait for the diskspace to
912 * be freed by moving the current disk to a queue.
913 * If case b) occurs, we have a deadlock situation. We select
914 * a dump from the queue to be aborted and abort it. It will
915 * be retried later dumping to disk.
916 * If case c) is detected, the dump is aborted. Next time
917 * it will be dumped directly to tape. Actually, case c is a special
918 * manifestation of case b) where only one dumper is busy.
920 for( dp=NULL, dumper = dmptable; dumper < dmptable + inparallel; dumper++) {
923 if( !find_disk(&roomq, dumper->dp) ) {
925 } else if( !dp || sched(dp)->est_size > sched(dumper->dp)->est_size ) {
930 if( !active_dumpers && busy_dumpers > 0 &&
931 ((!taper_busy && empty(tapeq)) || degraded_mode) &&
932 pending_aborts == 0 ) { /* not case a */
933 if( busy_dumpers == 1 ) { /* case c */
934 sched(dp)->no_space = 1;
937 /* At this time, dp points to the dump with the smallest est_size.
938 * We abort that dump, hopefully not wasting too much time retrying it.
940 remove_disk( &roomq, dp );
941 dumper_cmd( sched(dp)->dumper, ABORT, NULL );
946 void handle_taper_result()
952 char *result_argv[MAX_ARGS+1];
954 cmd = getresult(taper, 1, &result_argc, result_argv, MAX_ARGS+1);
958 case DONE: /* DONE <handle> <label> <tape file> <err mess> */
959 if(result_argc != 5) {
960 error("error: [taper DONE result_argc != 5: %d", result_argc);
963 dp = serial2disk(result_argv[2]);
964 free_serial(result_argv[2]);
966 filenum = atoi(result_argv[4]);
967 update_info_taper(dp, result_argv[3], filenum, sched(dp)->level);
969 delete_diskspace(dp);
971 printf("driver: finished-cmd time %s taper wrote %s:%s\n",
972 walltime_str(curclock()), dp->host->hostname, dp->name);
975 amfree(sched(dp)->dumpdate);
976 amfree(sched(dp)->degr_dumpdate);
977 amfree(sched(dp)->datestamp);
983 continue_dumps(); /* continue with those dumps waiting for diskspace */
986 case TRYAGAIN: /* TRY-AGAIN <handle> <err mess> */
987 if (result_argc < 2) {
988 error("error [taper TRYAGAIN result_argc < 2: %d]", result_argc);
990 dp = serial2disk(result_argv[2]);
991 free_serial(result_argv[2]);
992 printf("driver: taper-tryagain time %s disk %s:%s\n",
993 walltime_str(curclock()), dp->host->hostname, dp->name);
996 /* re-insert into taper queue */
998 if(sched(dp)->attempted) {
999 log_add(L_FAIL, "%s %s %d %s [too many taper retries]",
1000 dp->host->hostname, dp->name, sched(dp)->level,
1001 sched(dp)->datestamp);
1002 printf("driver: taper failed %s %s %s, too many taper retry\n", result_argv[2], dp->host->hostname, dp->name);
1005 sched(dp)->attempted++;
1006 headqueue_disk(&tapeq, dp);
1009 tape_left = tape_length;
1011 /* run next thing from queue */
1015 continue_dumps(); /* continue with those dumps waiting for diskspace */
1019 case TAPE_ERROR: /* TAPE-ERROR <handle> <err mess> */
1020 dp = serial2disk(result_argv[2]);
1021 free_serial(result_argv[2]);
1022 printf("driver: finished-cmd time %s taper wrote %s:%s\n",
1023 walltime_str(curclock()), dp->host->hostname, dp->name);
1025 /* Note: fall through code... */
1029 * Since we've gotten a tape error, we can't send anything more
1030 * to the taper. Go into degraded mode to try to get everthing
1031 * onto disk. Later, these dumps can be flushed to a new tape.
1032 * The tape queue is zapped so that it appears empty in future
1033 * checks. If there are dumps waiting for diskspace to be freed,
1038 "going into degraded mode because of tape error.");
1040 start_degraded_mode(&runq);
1043 tapeq.head = tapeq.tail = NULL;
1044 FD_CLR(taper,&readset);
1045 if(cmd != TAPE_ERROR) aclose(taper);
1049 error("driver received unexpected token (%d) from taper", cmd);
1054 dumper_t *idle_dumper()
1058 for(dumper = dmptable; dumper < dmptable+inparallel; dumper++)
1059 if(!dumper->busy && !dumper->down) return dumper;
1064 int some_dumps_in_progress()
1068 for(dumper = dmptable; dumper < dmptable+inparallel; dumper++)
1069 if(dumper->busy) return 1;
1074 int num_busy_dumpers()
1080 for(dumper = dmptable; dumper < dmptable+inparallel; dumper++)
1081 if(dumper->busy) n += 1;
1086 dumper_t *lookup_dumper(fd)
1091 for(dumper = dmptable; dumper < dmptable+inparallel; dumper++)
1092 if(dumper->outfd == fd) return dumper;
1098 void handle_dumper_result(fd)
1101 assignedhd_t **h=NULL;
1109 char *result_argv[MAX_ARGS+1];
1113 dumper = lookup_dumper(fd);
1115 assert(dp && sched(dp) && sched(dp)->destname);
1117 if(dp && sched(dp) && sched(dp)->holdp) {
1118 h = sched(dp)->holdp;
1119 activehd = sched(dp)->activehd;
1122 cmd = getresult(fd, 1, &result_argc, result_argv, MAX_ARGS+1);
1125 sdp = serial2disk(result_argv[2]); /* result_argv[2] always contains the serial number */
1131 case DONE: /* DONE <handle> <origsize> <dumpsize> <dumptime> <err str> */
1132 if(result_argc != 6) {
1133 error("error [dumper DONE result_argc != 6: %d]", result_argc);
1136 free_serial(result_argv[2]);
1138 origsize = (long)atof(result_argv[3]);
1139 dumpsize = (long)atof(result_argv[4]);
1140 dumptime = (long)atof(result_argv[5]);
1141 update_info_dumper(dp, origsize, dumpsize, dumptime);
1143 /* adjust holdp[active]->used using the real dumpsize and all other
1144 * holdp[i]->used as an estimate.
1148 for( i = 0, h = sched(dp)->holdp; i < activehd; i++ ) {
1149 dummy += h[i]->used;
1152 rename_tmp_holding(sched(dp)->destname, 1);
1153 assert( h && activehd >= 0 );
1154 h[activehd]->used = size_holding_files(sched(dp)->destname) - dummy;
1155 deallocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
1156 holdalloc(h[activehd]->disk)->allocated_dumpers--;
1157 adjust_diskspace(dp, DONE);
1159 dp->host->inprogress -= 1;
1161 sched(dp)->attempted = 0;
1162 printf("driver: finished-cmd time %s %s dumped %s:%s\n",
1163 walltime_str(curclock()), dumper->name,
1164 dp->host->hostname, dp->name);
1167 enqueue_disk(&tapeq, dp);
1175 case TRYAGAIN: /* TRY-AGAIN <handle> <err str> */
1176 case FATAL_TRYAGAIN:
1177 free_serial(result_argv[2]);
1179 rename_tmp_holding(sched(dp)->destname, 0);
1180 deallocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
1181 assert( h && activehd >= 0 );
1182 holdalloc(h[activehd]->disk)->allocated_dumpers--;
1183 /* Because we don't know how much was written to disk the
1184 * following functions *must* be called together!
1186 adjust_diskspace(dp, DONE);
1187 delete_diskspace(dp);
1189 dp->host->inprogress -= 1;
1192 if(sched(dp)->attempted) {
1193 log_add(L_FAIL, "%s %s %d %s [too many dumper retry]",
1194 dp->host->hostname, dp->name,
1195 sched(dp)->level, sched(dp)->datestamp);
1196 printf("driver: dump failed %s %s %s, too many dumper retry\n", result_argv[2], dp->host->hostname, dp->name);
1198 sched(dp)->attempted++;
1199 enqueue_disk(&runq, dp);
1203 if(cmd == FATAL_TRYAGAIN) {
1204 /* dumper is confused, start another */
1205 log_add(L_WARNING, "%s (pid %ld) confused, restarting it.",
1206 dumper->name, (long)dumper->pid);
1207 FD_CLR(fd,&readset);
1209 startup_dump_process(dumper, dumper_program);
1211 /* sleep in case the dumper failed because of a temporary network
1212 problem, as NIS or NFS... */
1216 case FAILED: /* FAILED <handle> <errstr> */
1217 free_serial(result_argv[2]);
1219 rename_tmp_holding(sched(dp)->destname, 0);
1220 deallocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
1221 assert( h && activehd >= 0 );
1222 holdalloc(h[activehd]->disk)->allocated_dumpers--;
1223 /* Because we don't know how much was written to disk the
1224 * following functions *must* be called together!
1226 adjust_diskspace(dp, DONE);
1227 delete_diskspace(dp);
1229 dp->host->inprogress -= 1;
1233 /* no need to log this, dumper will do it */
1234 /* sleep in case the dumper failed because of a temporary network
1235 problem, as NIS or NFS... */
1239 case NO_ROOM: /* NO-ROOM <handle> <missing_size> */
1240 assert( h && activehd >= 0 );
1241 h[activehd]->used -= atoi(result_argv[3]);
1242 h[activehd]->reserved -= atoi(result_argv[3]);
1243 holdalloc(h[activehd]->disk)->allocated_space -= atoi(result_argv[3]);
1244 h[activehd]->disk->disksize -= atoi(result_argv[3]);
1247 case RQ_MORE_DISK: /* RQ-MORE-DISK <handle> */
1248 assert( h && activehd >= 0 );
1249 holdalloc(h[activehd]->disk)->allocated_dumpers--;
1250 h[activehd]->used = h[activehd]->reserved;
1251 if( h[++activehd] ) { /* There's still some allocated space left. Tell
1252 * the dumper about it. */
1253 sched(dp)->activehd++;
1254 dumper_cmd( dumper, CONTINUE, dp );
1255 } else { /* !h[++activehd] - must allocate more space */
1256 sched(dp)->act_size = sched(dp)->est_size; /* not quite true */
1257 sched(dp)->est_size = sched(dp)->act_size * 21 / 20; /* +5% */
1258 sched(dp)->est_size = am_round(sched(dp)->est_size, DISK_BLOCK_KB);
1259 h = find_diskspace( sched(dp)->est_size - sched(dp)->act_size,
1263 /* cur_idle = max(cur_idle, IDLE_NO_DISKSPACE); */
1264 /* No diskspace available. The reason for this will be
1265 * determined in continue_dumps(). */
1266 enqueue_disk( &roomq, dp );
1269 /* OK, allocate space for disk and have dumper continue */
1270 sched(dp)->activehd = assign_holdingdisk( h, dp );
1271 dumper_cmd( dumper, CONTINUE, dp );
1277 case ABORT_FINISHED: /* ABORT-FINISHED <handle> */
1278 assert(pending_aborts);
1279 free_serial(result_argv[2]);
1281 rename_tmp_holding(sched(dp)->destname, 0);
1282 deallocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
1283 /* Because we don't know how much was written to disk the
1284 * following functions *must* be called together!
1286 adjust_diskspace(dp, DONE);
1287 delete_diskspace(dp);
1288 sched(dp)->attempted++;
1289 enqueue_disk(&runq, dp); /* we'll try again later */
1291 dp->host->inprogress -= 1;
1299 /* either EOF or garbage from dumper. Turn it off */
1300 log_add(L_WARNING, "%s pid %ld is messed up, ignoring it.\n",
1301 dumper->name, (long)dumper->pid);
1302 FD_CLR(fd,&readset);
1305 dumper->down = 1; /* mark it down so it isn't used again */
1307 /* if it was dumping something, zap it and try again */
1308 rename_tmp_holding(sched(dp)->destname, 0);
1309 deallocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
1310 assert( h && activehd >= 0 );
1311 holdalloc(h[activehd]->disk)->allocated_dumpers--;
1312 /* Because we don't know how much was written to disk the
1313 * following functions *must* be called together!
1315 adjust_diskspace(dp, DONE);
1316 delete_diskspace(dp);
1317 dp->host->inprogress -= 1;
1319 if(sched(dp)->attempted) {
1320 log_add(L_FAIL, "%s %s %d %s [%s died]",
1321 dp->host->hostname, dp->name,
1322 sched(dp)->level, sched(dp)->datestamp, dumper->name);
1325 log_add(L_WARNING, "%s died while dumping %s:%s lev %d.",
1326 dumper->name, dp->host->hostname, dp->name,
1328 sched(dp)->attempted++;
1329 enqueue_disk(&runq, dp);
1344 void read_flush(tapeqp)
1351 char *hostname, *diskname, *datestamp;
1355 char *inpline = NULL;
1359 long flush_size = 0;
1361 /* read schedule from stdin */
1363 for(line = 0; (inpline = agets(stdin)) != NULL; free(inpline)) {
1369 skip_whitespace(s, ch); /* find the command */
1371 error("Aflush line %d: syntax error", line);
1375 skip_non_whitespace(s, ch);
1378 if(strcmp(command,"ENDFLUSH") == 0) {
1382 if(strcmp(command,"FLUSH") != 0) {
1383 error("Bflush line %d: syntax error", line);
1387 skip_whitespace(s, ch); /* find the hostname */
1389 error("Cflush line %d: syntax error", line);
1393 skip_non_whitespace(s, ch);
1396 skip_whitespace(s, ch); /* find the diskname */
1398 error("Cflush line %d: syntax error", line);
1402 skip_non_whitespace(s, ch);
1405 skip_whitespace(s, ch); /* find the datestamp */
1407 error("Cflush line %d: syntax error", line);
1411 skip_non_whitespace(s, ch);
1414 skip_whitespace(s, ch); /* find the level number */
1415 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
1416 error("Cflush line %d: syntax error", line);
1419 skip_integer(s, ch);
1421 skip_whitespace(s, ch); /* find the filename */
1423 error("Cflush line %d: syntax error", line);
1427 skip_non_whitespace(s, ch);
1430 get_dumpfile(destname, &file);
1431 if( file.type != F_DUMPFILE) {
1432 if( file.type != F_CONT_DUMPFILE )
1433 log_add(L_INFO, "%s: ignoring cruft file.", destname);
1437 if(strcmp(hostname, file.name) != 0 ||
1438 strcmp(diskname, file.disk) != 0 ||
1439 strcmp(datestamp, file.datestamp) != 0) {
1440 log_add(L_INFO, "disk %s:%s not consistent with file %s",
1441 hostname, diskname, destname);
1445 dp = lookup_disk(file.name, file.disk);
1448 log_add(L_INFO, "%s: disk %s:%s not in database, skipping it.",
1449 destname, file.name, file.disk);
1453 if(file.dumplevel < 0 || file.dumplevel > 9) {
1454 log_add(L_INFO, "%s: ignoring file with bogus dump level %d.",
1455 destname, file.dumplevel);
1459 dp1 = (disk_t *)alloc(sizeof(disk_t));
1461 dp1->next = dp1->prev = NULL;
1463 /* add it to the flushhost list */
1465 flushhost = alloc(sizeof(am_host_t));
1466 flushhost->next = NULL;
1467 flushhost->hostname = stralloc("FLUSHHOST");
1468 flushhost->up = NULL;
1469 flushhost->features = NULL;
1471 dp1->hostnext = flushhost->disks;
1472 flushhost->disks = dp1;
1474 sp = (sched_t *) alloc(sizeof(sched_t));
1475 sp->destname = stralloc(destname);
1476 sp->level = file.dumplevel;
1477 sp->dumpdate = NULL;
1478 sp->degr_dumpdate = NULL;
1479 sp->datestamp = stralloc(file.datestamp);
1483 sp->degr_level = -1;
1486 sp->act_size = size_holding_files(destname);
1487 /*sp->holdp = NULL; JLM: must be build*/
1488 sp->holdp = build_diskspace(destname);
1489 if(sp->holdp == NULL) continue;
1491 sp->timestamp = (time_t)0;
1493 dp1->up = (char *)sp;
1495 enqueue_disk(tapeqp, dp1);
1496 flush_size += sp->act_size;
1498 printf("driver: flush size %ld\n", flush_size);
1503 void read_schedule(waitqp, runqp)
1504 disklist_t *waitqp, *runqp;
1508 int level, line, priority;
1509 char *dumpdate, *degr_dumpdate;
1511 long time, degr_time;
1512 unsigned long size, degr_size;
1513 char *hostname, *features, *diskname, *datestamp, *inpline = NULL;
1518 /* read schedule from stdin */
1520 for(line = 0; (inpline = agets(stdin)) != NULL; free(inpline)) {
1526 skip_whitespace(s, ch); /* find the command */
1528 error("schedule line %d: syntax error (no command)", line);
1532 skip_non_whitespace(s, ch);
1535 if(strcmp(command,"DUMP") != 0) {
1536 error("schedule line %d: syntax error (%s != DUMP)", line, command);
1540 skip_whitespace(s, ch); /* find the host name */
1542 error("schedule line %d: syntax error (no host name)", line);
1546 skip_non_whitespace(s, ch);
1549 skip_whitespace(s, ch); /* find the feature list */
1551 error("schedule line %d: syntax error (no feature list)", line);
1555 skip_non_whitespace(s, ch);
1558 skip_whitespace(s, ch); /* find the disk name */
1560 error("schedule line %d: syntax error (no disk name)", line);
1564 skip_non_whitespace(s, ch);
1567 skip_whitespace(s, ch); /* find the datestamp */
1569 error("schedule line %d: syntax error (no datestamp)", line);
1573 skip_non_whitespace(s, ch);
1576 skip_whitespace(s, ch); /* find the priority number */
1577 if(ch == '\0' || sscanf(s - 1, "%d", &priority) != 1) {
1578 error("schedule line %d: syntax error (bad priority)", line);
1581 skip_integer(s, ch);
1583 skip_whitespace(s, ch); /* find the level number */
1584 if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
1585 error("schedule line %d: syntax error (bad level)", line);
1588 skip_integer(s, ch);
1590 skip_whitespace(s, ch); /* find the dump date */
1592 error("schedule line %d: syntax error (bad dump date)", line);
1596 skip_non_whitespace(s, ch);
1599 skip_whitespace(s, ch); /* find the size number */
1600 if(ch == '\0' || sscanf(s - 1, "%lu", &size) != 1) {
1601 error("schedule line %d: syntax error (bad size)", line);
1604 skip_integer(s, ch);
1606 skip_whitespace(s, ch); /* find the time number */
1607 if(ch == '\0' || sscanf(s - 1, "%ld", &time) != 1) {
1608 error("schedule line %d: syntax error (bad estimated time)", line);
1611 skip_integer(s, ch);
1613 degr_dumpdate = NULL; /* flag if degr fields found */
1614 skip_whitespace(s, ch); /* find the degr level number */
1616 if(sscanf(s - 1, "%d", °r_level) != 1) {
1617 error("schedule line %d: syntax error (bad degr level)", line);
1620 skip_integer(s, ch);
1622 skip_whitespace(s, ch); /* find the degr dump date */
1624 error("schedule line %d: syntax error (bad degr dump date)", line);
1627 degr_dumpdate = s - 1;
1628 skip_non_whitespace(s, ch);
1631 skip_whitespace(s, ch); /* find the degr size number */
1632 if(ch == '\0' || sscanf(s - 1, "%lu", °r_size) != 1) {
1633 error("schedule line %d: syntax error (bad degr size)", line);
1636 skip_integer(s, ch);
1638 skip_whitespace(s, ch); /* find the degr time number */
1639 if(ch == '\0' || sscanf(s - 1, "%lu", °r_time) != 1) {
1640 error("schedule line %d: syntax error (bad degr estimated time)", line);
1643 skip_integer(s, ch);
1646 dp = lookup_disk(hostname, diskname);
1649 "schedule line %d: %s:%s not in disklist, ignored",
1650 line, hostname, diskname);
1654 sp = (sched_t *) alloc(sizeof(sched_t));
1656 sp->dumpdate = stralloc(dumpdate);
1657 sp->est_size = DISK_BLOCK_KB + size; /* include header */
1658 sp->est_time = time;
1659 sp->priority = priority;
1660 sp->datestamp = stralloc(datestamp);
1663 sp->degr_level = degr_level;
1664 sp->degr_dumpdate = stralloc(degr_dumpdate);
1665 sp->degr_size = DISK_BLOCK_KB + degr_size;
1666 sp->degr_time = degr_time;
1668 sp->degr_level = -1;
1669 sp->degr_dumpdate = NULL;
1675 sp->est_kps = size/time;
1677 if(sp->degr_level != -1) {
1681 sp->degr_kps = degr_size/degr_time;
1689 sp->timestamp = (time_t)0;
1690 sp->destname = NULL;
1693 dp->up = (char *) sp;
1694 if(dp->host->features == NULL) {
1695 dp->host->features = am_string_to_feature(features);
1697 remove_disk(waitqp, dp);
1698 insert_disk(&runq, dp, sort_by_time);
1702 log_add(L_WARNING, "WARNING: got empty schedule from planner");
1710 if (ip == (interface_t *)0) {
1714 for(p = lookup_interface(NULL); p != NULL; p = p->next) {
1715 maxusage += p->maxusage;
1716 curusage += p->curusage;
1718 res = maxusage - curusage;
1721 res = ip->maxusage - ip->curusage;
1727 void interface_state(time_str)
1732 printf("driver: interface-state time %s", time_str);
1734 for(ip = lookup_interface(NULL); ip != NULL; ip = ip->next) {
1735 printf(" if %s: free %d", ip->name, free_kps(ip));
1740 void allocate_bandwidth(ip, kps)
1744 ip->curusage += kps;
1747 void deallocate_bandwidth(ip, kps)
1751 assert(kps <= ip->curusage);
1752 ip->curusage -= kps;
1756 unsigned long free_space()
1759 unsigned long total_free;
1763 for(hdp = getconf_holdingdisks(); hdp != NULL; hdp = hdp->next) {
1764 diff = hdp->disksize - holdalloc(hdp)->allocated_space;
1771 assignedhd_t **find_diskspace(size, cur_idle, pref)
1775 /* Rewrite by Peter Conrad <conrad@opus5.de>, June '99:
1776 * - enable splitting a dump across several holding disks
1777 * - allocate only as much as size tells us, dumpers may request more later
1778 * We return an array of pointers to assignedhd_t. The array contains at
1779 * most one entry per holding disk. The list of pointers is terminated by
1780 * a NULL pointer. Each entry contains a pointer to a holdingdisk and
1781 * how much diskspace to use on that disk. Later on, assign_holdingdisk
1782 * will allocate the given amount of space.
1783 * If there is not enough room on the holdingdisks, NULL is returned.
1786 assignedhd_t **result = NULL;
1787 holdingdisk_t *minp, *hdp;
1788 int i=0, num_holdingdisks=0; /* are we allowed to use the global thing? */
1791 long halloc, dalloc, hfree, dfree;
1793 size = am_round(size, DISK_BLOCK_KB);
1796 printf("find diskspace: want %lu K\n", size );
1800 for(hdp = getconf_holdingdisks(); hdp != NULL; hdp = hdp->next) {
1804 used = alloc(sizeof(char) * num_holdingdisks);/*disks used during this run*/
1805 memset( used, 0, num_holdingdisks );
1806 result = alloc( sizeof(assignedhd_t *) * (num_holdingdisks+1) );
1809 while( i < num_holdingdisks && size > 0 ) {
1810 /* find the holdingdisk with the fewest active dumpers and among
1811 * those the one with the biggest free space
1813 minp = NULL; minj = -1;
1814 for(j = 0, hdp = getconf_holdingdisks(); hdp != NULL; hdp = hdp->next, j++ ) {
1815 if( pref && pref->disk == hdp && !used[j] &&
1816 holdalloc(hdp)->allocated_space <= hdp->disksize - DISK_BLOCK_KB) {
1821 else if( holdalloc(hdp)->allocated_space <= hdp->disksize - 2*DISK_BLOCK_KB &&
1824 holdalloc(hdp)->allocated_dumpers < holdalloc(minp)->allocated_dumpers ||
1825 (holdalloc(hdp)->allocated_dumpers == holdalloc(minp)->allocated_dumpers &&
1826 hdp->disksize-holdalloc(hdp)->allocated_space > minp->disksize-holdalloc(minp)->allocated_space)) ) {
1832 if( !minp ) { break; } /* all holding disks are full */
1835 /* hfree = free space on the disk */
1836 hfree = minp->disksize - holdalloc(minp)->allocated_space;
1838 /* dfree = free space for data, remove 1 header for each chunksize */
1839 dfree = hfree - (((hfree-1)/minp->chunksize)+1) * DISK_BLOCK_KB;
1841 /* dalloc = space I can allocate for data */
1842 dalloc = ( dfree < size ) ? dfree : size;
1844 /* halloc = space to allocate, including 1 header for each chunksize */
1845 halloc = dalloc + (((dalloc-1)/minp->chunksize)+1) * DISK_BLOCK_KB;
1848 fprintf(stdout,"find diskspace: size %ld hf %ld df %ld da %ld ha %ld\n", size, hfree, dfree, dalloc, halloc);
1852 result[i] = alloc(sizeof(assignedhd_t));
1853 result[i]->disk = minp;
1854 result[i]->reserved = halloc;
1855 result[i]->used = 0;
1856 result[i]->destname = NULL;
1859 } /* while i < num_holdingdisks && size > 0 */
1862 if( size ) { /* not enough space available */
1864 printf("find diskspace: not enough diskspace. Left with %lu K\n", size);
1867 free_assignedhd(result);
1872 for( i = 0; result && result[i]; i++ ) {
1873 printf("find diskspace: selected %s free %ld reserved %ld dumpers %d\n",
1874 result[i]->disk->diskdir,
1875 result[i]->disk->disksize - holdalloc(result[i]->disk)->allocated_space,
1876 result[i]->reserved,
1877 holdalloc(result[i]->disk)->allocated_dumpers);
1885 int assign_holdingdisk(holdp, diskp)
1886 assignedhd_t **holdp;
1889 /* Modified by Peter Conrad <conrad@opus5.de>, June '99
1890 * Modifications for splitting dumps across holding disks:
1891 * sched(diskp)->holdp now contains an array of pointers to assignedhd_t.
1895 char *sfn = sanitise_filename(diskp->name);
1897 assignedhd_t **new_holdp;
1899 ap_snprintf( lvl, sizeof(lvl), "%d", sched(diskp)->level );
1901 size = am_round(sched(diskp)->est_size - sched(diskp)->act_size,
1904 for( c = 0; holdp[c]; c++ ); /* count number of disks */
1906 /* allocate memory for sched(diskp)->holdp */
1907 for(j = 0; sched(diskp)->holdp && sched(diskp)->holdp[j]; j++) {}
1908 new_holdp = (assignedhd_t **)alloc(sizeof(assignedhd_t*)*(j+c+1));
1909 if (sched(diskp)->holdp) {
1910 memcpy(new_holdp, sched(diskp)->holdp, j * sizeof(*new_holdp));
1911 amfree(sched(diskp)->holdp);
1913 sched(diskp)->holdp = new_holdp;
1917 if( j > 0 ) { /* This is a request for additional diskspace. See if we can
1918 * merge assignedhd_t's */
1920 if( sched(diskp)->holdp[j-1]->disk == holdp[0]->disk ) { /* Yes! */
1921 sched(diskp)->holdp[j-1]->reserved += holdp[0]->reserved;
1922 holdalloc(holdp[0]->disk)->allocated_space += holdp[0]->reserved;
1923 size = (holdp[0]->reserved>size) ? 0 : size-holdp[0]->reserved;
1925 printf("merging holding disk %s to disk %s:%s, add %lu for reserved %lu, left %lu\n",
1926 sched(diskp)->holdp[j-1]->disk->diskdir,
1927 diskp->host->hostname, diskp->name,
1928 holdp[0]->reserved, sched(diskp)->holdp[j-1]->reserved,
1938 /* copy assignedhd_s to sched(diskp), adjust allocated_space */
1939 for( ; holdp[i]; i++ ) {
1940 holdp[i]->destname = newvstralloc( holdp[i]->destname,
1941 holdp[i]->disk->diskdir, "/",
1943 diskp->host->hostname, ".",
1946 sched(diskp)->holdp[j++] = holdp[i];
1947 holdalloc(holdp[i]->disk)->allocated_space += holdp[i]->reserved;
1948 size = (holdp[i]->reserved>size) ? 0 : size-holdp[i]->reserved;
1950 printf("assigning holding disk %s to disk %s:%s, reserved %lu, left %lu\n",
1951 holdp[i]->disk->diskdir, diskp->host->hostname, diskp->name,
1952 holdp[i]->reserved, size );
1955 holdp[i] = NULL; /* so it doesn't get free()d... */
1957 sched(diskp)->holdp[j] = NULL;
1958 sched(diskp)->destname = newstralloc(sched(diskp)->destname,sched(diskp)->holdp[0]->destname);
1964 static void adjust_diskspace(diskp, cmd)
1968 /* Re-write by Peter Conrad <conrad@opus5.de>, March '99
1969 * Modifications for splitting dumps across holding disks:
1970 * Dumpers no longer write more than they've allocated, therefore an
1971 * adjustment may only free some allocated space.
1972 * 08/99: Jean-Louis suggested that dumpers tell us how much they've written.
1973 * We just believe them and don't stat all the files but rely on the used
1977 assignedhd_t **holdp;
1978 unsigned long total=0;
1983 printf("adjust: %s:%s %s\n", diskp->host->hostname, diskp->name,
1984 sched(diskp)->destname );
1988 holdp = sched(diskp)->holdp;
1992 for( i = 0; holdp[i]; i++ ) { /* for each allocated disk */
1993 diff = holdp[i]->used - holdp[i]->reserved;
1994 total += holdp[i]->used;
1995 holdalloc(holdp[i]->disk)->allocated_space += diff;
1997 printf("adjust: hdisk %s done, reserved %ld used %ld diff %ld alloc %ld dumpers %d\n",
1998 holdp[i]->disk->name, holdp[i]->reserved, holdp[i]->used, diff,
1999 holdalloc(holdp[i]->disk)->allocated_space,
2000 holdalloc(holdp[i]->disk)->allocated_dumpers );
2003 holdp[i]->reserved += diff;
2006 sched(diskp)->act_size = total;
2008 printf("adjust: after: disk %s:%s used %ld\n", diskp->host->hostname,
2009 diskp->name, sched(diskp)->act_size );
2014 static void delete_diskspace(diskp)
2017 /* Re-write by Peter Conrad <conrad@opus5.de>, March '99
2018 * Modifications for splitting dumps across holding disks:
2019 * After implementing Jean-Louis' suggestion (see above) this looks much
2020 * simpler... again, we rely on assignedhd_s containing correct info
2022 assignedhd_t **holdp;
2025 holdp = sched(diskp)->holdp;
2029 for( i = 0; holdp[i]; i++ ) { /* for each disk */
2030 /* find all files of this dump on that disk, and subtract their
2031 * reserved sizes from the disk's allocated space
2033 holdalloc(holdp[i]->disk)->allocated_space -= holdp[i]->used;
2036 unlink_holding_files(holdp[0]->destname); /* no need for the entire list,
2037 because unlink_holding_files
2038 will walk through all files
2039 using cont_filename */
2041 free_assignedhd(sched(diskp)->holdp);
2042 sched(diskp)->holdp = NULL;
2043 sched(diskp)->act_size = 0;
2044 amfree(sched(diskp)->destname);
2047 assignedhd_t **build_diskspace(destname)
2053 char buffer[DISK_BLOCK_BYTES];
2055 assignedhd_t **result;
2058 int num_holdingdisks=0;
2059 char dirname[1000], *ch;
2061 char *filename = destname;
2063 for(hdp = getconf_holdingdisks(); hdp != NULL; hdp = hdp->next) {
2066 used = alloc(sizeof(int) * num_holdingdisks);
2067 for(i=0;i<num_holdingdisks;i++)
2069 result = alloc( sizeof(assignedhd_t *) * (num_holdingdisks+1) );
2071 while(filename != NULL && filename[0] != '\0') {
2072 strncpy(dirname, filename, 999);
2074 ch = strrchr(dirname,'/');
2076 ch = strrchr(dirname,'/');
2079 for(j = 0, hdp = getconf_holdingdisks(); hdp != NULL;
2080 hdp = hdp->next, j++ ) {
2081 if(strcmp(dirname,hdp->diskdir)==0) {
2086 if(stat(filename, &finfo) == -1) {
2087 fprintf(stderr, "stat %s: %s\n", filename, strerror(errno));
2090 used[j] += (finfo.st_size+1023)/1024;
2091 if((fd = open(filename,O_RDONLY)) == -1) {
2092 fprintf(stderr,"build_diskspace: open of %s failed: %s\n",
2093 filename, strerror(errno));
2096 buflen = fullread(fd, buffer, sizeof(buffer));
2097 parse_file_header(buffer, &file, buflen);
2099 filename = file.cont_filename;
2102 for(j = 0, i=0, hdp = getconf_holdingdisks(); hdp != NULL;
2103 hdp = hdp->next, j++ ) {
2105 result[i] = alloc(sizeof(assignedhd_t));
2106 result[i]->disk = hdp;
2107 result[i]->reserved = used[j];
2108 result[i]->used = used[j];
2109 result[i]->destname = stralloc(destname);
2120 void holdingdisk_state(time_str)
2127 printf("driver: hdisk-state time %s", time_str);
2129 for(hdp = getconf_holdingdisks(), dsk = 0; hdp != NULL; hdp = hdp->next, dsk++) {
2130 diff = hdp->disksize - holdalloc(hdp)->allocated_space;
2131 printf(" hdisk %d: free %ld dumpers %d", dsk, diff,
2132 holdalloc(hdp)->allocated_dumpers);
2137 static void update_failed_dump_to_tape(dp)
2140 time_t save_timestamp = sched(dp)->timestamp;
2141 /* setting timestamp to 0 removes the current level from the
2142 * database, so that we ensure that it will not be bumped to the
2143 * next level on the next run. If we didn't do this, dumpdates or
2144 * gnutar-lists might have been updated already, and a bumped
2145 * incremental might be created. */
2146 sched(dp)->timestamp = 0;
2147 update_info_dumper(dp, -1, -1, -1);
2148 sched(dp)->timestamp = save_timestamp;
2151 /* ------------------- */
2152 int dump_to_tape(dp)
2163 char *result_argv[MAX_ARGS+1];
2164 int dumper_tryagain = 0;
2166 inside_dump_to_tape = 1; /* for simulator */
2168 printf("driver: dumping %s:%s directly to tape\n",
2169 dp->host->hostname, dp->name);
2172 /* pick a dumper and fail if there are no idle dumpers */
2174 dumper = idle_dumper();
2176 printf("driver: no idle dumpers for %s:%s.\n",
2177 dp->host->hostname, dp->name);
2179 log_add(L_WARNING, "no idle dumpers for %s:%s.\n",
2180 dp->host->hostname, dp->name);
2181 inside_dump_to_tape = 0;
2182 return 2; /* fatal problem */
2185 /* tell the taper to read from a port number of its choice */
2187 taper_cmd(PORT_WRITE, dp, NULL, sched(dp)->level, sched(dp)->datestamp);
2188 cmd = getresult(taper, 1, &result_argc, result_argv, MAX_ARGS+1);
2190 printf("driver: did not get PORT from taper for %s:%s\n",
2191 dp->host->hostname, dp->name);
2193 inside_dump_to_tape = 0;
2194 return 2; /* fatal problem */
2196 /* copy port number */
2197 sched(dp)->destname = newvstralloc(sched(dp)->destname, result_argv[2], NULL );
2199 /* tell the dumper to dump to a port */
2201 dumper_cmd(dumper, PORT_DUMP, dp);
2202 dp->host->start_t = time(NULL) + 15;
2204 /* update statistics & print state */
2206 taper_busy = dumper->busy = 1;
2207 dp->host->inprogress += 1;
2209 sched(dp)->timestamp = time((time_t *)0);
2210 allocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
2215 /* wait for result from dumper */
2217 cmd = getresult(dumper->outfd, 1, &result_argc, result_argv, MAX_ARGS+1);
2220 free_serial(result_argv[2]);
2224 /* either eof or garbage from dumper */
2225 log_add(L_WARNING, "%s pid %ld is messed up, ignoring it.\n",
2226 dumper->name, (long)dumper->pid);
2227 dumper->down = 1; /* mark it down so it isn't used again */
2228 failed = 1; /* dump failed, must still finish up with taper */
2231 case DONE: /* DONE <handle> <origsize> <dumpsize> <dumptime> <err str> */
2232 /* everything went fine */
2233 origsize = (long)atof(result_argv[3]);
2234 dumpsize = (long)atof(result_argv[4]);
2235 dumptime = (long)atof(result_argv[5]);
2238 case NO_ROOM: /* NO-ROOM <handle> */
2239 dumper_cmd(dumper, ABORT, dp);
2240 cmd = getresult(dumper->outfd, 1, &result_argc, result_argv, MAX_ARGS+1);
2242 free_serial(result_argv[2]);
2243 assert(cmd == ABORT_FINISHED);
2245 case TRYAGAIN: /* TRY-AGAIN <handle> <err str> */
2247 /* dump failed, but we must still finish up with taper */
2248 /* problem with dump, possibly nonfatal, retry one time */
2249 sched(dp)->attempted++;
2250 failed = sched(dp)->attempted;
2251 dumper_tryagain = 1;
2254 case FAILED: /* FAILED <handle> <errstr> */
2255 /* dump failed, but we must still finish up with taper */
2256 failed = 2; /* fatal problem with dump */
2261 * Note that at this point, even if the dump above failed, it may
2262 * not be a fatal failure if taper below says we can try again.
2263 * E.g. a dumper failure above may actually be the result of a
2264 * tape overflow, which in turn causes dump to see "broken pipe",
2265 * "no space on device", etc., since taper closed the port first.
2268 cmd = getresult(taper, 1, &result_argc, result_argv, MAX_ARGS+1);
2271 case DONE: /* DONE <handle> <label> <tape file> <err mess> */
2272 if(result_argc != 5) {
2273 error("error [dump to tape DONE result_argc != 5: %d]", result_argc);
2276 if(failed == 1) goto tryagain; /* dump didn't work */
2277 else if(failed == 2) goto failed_dumper;
2279 free_serial(result_argv[2]);
2281 /* every thing went fine */
2282 update_info_dumper(dp, origsize, dumpsize, dumptime);
2283 filenum = atoi(result_argv[4]);
2284 update_info_taper(dp, result_argv[3], filenum, sched(dp)->level);
2285 /* note that update_info_dumper() must be run before
2286 update_info_taper(), since update_info_dumper overwrites
2287 tape information. */
2291 case TRYAGAIN: /* TRY-AGAIN <handle> <err mess> */
2292 if(dumper_tryagain == 0) {
2293 sched(dp)->attempted++;
2294 if(sched(dp)->attempted > failed)
2295 failed = sched(dp)->attempted;
2299 headqueue_disk(&runq, dp);
2301 update_failed_dump_to_tape(dp);
2302 free_serial(result_argv[2]);
2303 tape_left = tape_length;
2307 case TAPE_ERROR: /* TAPE-ERROR <handle> <err mess> */
2310 update_failed_dump_to_tape(dp);
2311 free_serial(result_argv[2]);
2312 failed = 2; /* fatal problem */
2313 start_degraded_mode(&runq);
2316 /* reset statistics & return */
2318 taper_busy = dumper->busy = 0;
2319 dp->host->inprogress -= 1;
2321 deallocate_bandwidth(dp->host->netif, sched(dp)->est_kps);
2323 inside_dump_to_tape = 0;
2333 for(len = 0, p = q.head; p != NULL; len++, p = p->next);
2338 void short_dump_state()
2343 wall_time = walltime_str(curclock());
2345 printf("driver: state time %s ", wall_time);
2346 printf("free kps: %d space: %lu taper: ",
2347 free_kps((interface_t *)0), free_space());
2348 if(degraded_mode) printf("DOWN");
2349 else if(!taper_busy) printf("idle");
2350 else printf("writing");
2352 for(i = 0; i < inparallel; i++) if(!dmptable[i].busy) nidle++;
2353 printf(" idle-dumpers: %d", nidle);
2354 printf(" qlen tapeq: %d", queue_length(tapeq));
2355 printf(" runq: %d", queue_length(runq));
2356 printf(" roomq: %d", queue_length(roomq));
2357 printf(" wakeup: %d", (int)sleep_time.tv_sec);
2358 printf(" driver-idle: %s\n", idle_strings[idle_reason]);
2359 interface_state(wall_time);
2360 holdingdisk_state(wall_time);
2364 void dump_state(str)
2370 printf("================\n");
2371 printf("driver state at time %s: %s\n", walltime_str(curclock()), str);
2372 printf("free kps: %d, space: %lu\n", free_kps((interface_t *)0), free_space());
2373 if(degraded_mode) printf("taper: DOWN\n");
2374 else if(!taper_busy) printf("taper: idle\n");
2375 else printf("taper: writing %s:%s.%d est size %lu\n",
2376 taper_disk->host->hostname, taper_disk->name,
2377 sched(taper_disk)->level,
2378 sched(taper_disk)->est_size);
2379 for(i = 0; i < inparallel; i++) {
2380 dp = dmptable[i].dp;
2381 if(!dmptable[i].busy)
2382 printf("%s: idle\n", dmptable[i].name);
2384 printf("%s: dumping %s:%s.%d est kps %d size %lu time %ld\n",
2385 dmptable[i].name, dp->host->hostname, dp->name, sched(dp)->level,
2386 sched(dp)->est_kps, sched(dp)->est_size, sched(dp)->est_time);
2388 dump_queue("TAPE", tapeq, 5, stdout);
2389 dump_queue("ROOM", roomq, 5, stdout);
2390 dump_queue("RUN ", runq, 5, stdout);
2391 printf("================\n");