1 /*- 2 * Copyright (c) 1980, 1991 The Regents of the University of California. 3 * All rights reserved. 4 * 5 * %sccs.include.redist.c% 6 */ 7 8 #ifndef lint 9 static char sccsid[] = "@(#)tape.c 5.19 (Berkeley) 07/23/91"; 10 #endif /* not lint */ 11 12 #include <sys/param.h> 13 #include <sys/wait.h> 14 #include <ufs/dinode.h> 15 #include <ufs/fs.h> 16 #include <signal.h> 17 #include <fcntl.h> 18 #include <protocols/dumprestore.h> 19 #include <errno.h> 20 #include <setjmp.h> 21 #ifdef __STDC__ 22 #include <unistd.h> 23 #include <stdlib.h> 24 #include <string.h> 25 #endif 26 #include <sys/socket.h> 27 #include "dump.h" 28 #include "pathnames.h" 29 30 int writesize; /* size of malloc()ed buffer for tape */ 31 long lastspclrec = -1; /* tape block number of last written header */ 32 int trecno = 0; /* next record to write in current block */ 33 extern long blocksperfile; /* number of blocks per output file */ 34 long blocksthisvol; /* number of blocks on current output file */ 35 extern int ntrec; /* blocking factor on tape */ 36 extern int cartridge; 37 extern char *host; 38 char *nexttape; 39 #ifdef RDUMP 40 int rmtopen(), rmtwrite(); 41 void rmtclose(); 42 #endif RDUMP 43 void rollforward(); 44 int atomic(); 45 void doslave(), enslave(), flushtape(), killall(); 46 47 /* 48 * Concurrent dump mods (Caltech) - disk block reading and tape writing 49 * are exported to several slave processes. While one slave writes the 50 * tape, the others read disk blocks; they pass control of the tape in 51 * a ring via signals. The parent process traverses the filesystem and 52 * sends writeheader()'s and lists of daddr's to the slaves via pipes. 53 * The following structure defines the instruction packets sent to slaves. 54 */ 55 struct req { 56 daddr_t dblk; 57 int count; 58 }; 59 int reqsiz; 60 61 #define SLAVES 3 /* 1 slave writing, 1 reading, 1 for slack */ 62 struct slave { 63 int tapea; /* header number at start of this chunk */ 64 int count; /* count to next header (used for TS_TAPE */ 65 /* after EOT) */ 66 int inode; /* inode that we are currently dealing with */ 67 int fd; /* FD for this slave */ 68 int pid; /* PID for this slave */ 69 int sent; /* 1 == we've sent this slave requests */ 70 int firstrec; /* record number of this block */ 71 char (*tblock)[TP_BSIZE]; /* buffer for data blocks */ 72 struct req *req; /* buffer for requests */ 73 } slaves[SLAVES+1]; 74 struct slave *slp; 75 76 char (*nextblock)[TP_BSIZE]; 77 78 int master; /* pid of master, for sending error signals */ 79 int tenths; /* length of tape used per block written */ 80 static int caught; /* have we caught the signal to proceed? */ 81 static int ready; /* have we reached the lock point without having */ 82 /* received the SIGUSR2 signal from the prev slave? */ 83 static jmp_buf jmpbuf; /* where to jump to if we are ready when the */ 84 /* SIGUSR2 arrives from the previous slave */ 85 86 int 87 alloctape() 88 { 89 int pgoff = getpagesize() - 1; 90 char *buf; 91 int i; 92 93 writesize = ntrec * TP_BSIZE; 94 reqsiz = (ntrec + 1) * sizeof(struct req); 95 /* 96 * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode 97 * (see DEC TU80 User's Guide). The shorter gaps of 6250-bpi require 98 * repositioning after stopping, i.e, streaming mode, where the gap is 99 * variable, 0.30" to 0.45". The gap is maximal when the tape stops. 100 */ 101 if (blocksperfile == 0) 102 tenths = writesize / density + 103 (cartridge ? 16 : density == 625 ? 5 : 8); 104 /* 105 * Allocate tape buffer contiguous with the array of instruction 106 * packets, so flushtape() can write them together with one write(). 107 * Align tape buffer on page boundary to speed up tape write(). 108 */ 109 for (i = 0; i <= SLAVES; i++) { 110 buf = (char *) malloc(reqsiz + writesize + pgoff + TP_BSIZE); 111 if (buf == NULL) 112 return(0); 113 slaves[i].tblock = (char (*)[TP_BSIZE]) 114 (((long)&buf[ntrec + 1] + pgoff) &~ pgoff); 115 slaves[i].req = (struct req *)slaves[i].tblock - ntrec - 1; 116 } 117 slp = &slaves[0]; 118 slp->count = 1; 119 slp->tapea = 0; 120 slp->firstrec = 0; 121 nextblock = slp->tblock; 122 return(1); 123 } 124 125 void 126 writerec(dp) 127 char *dp; 128 { 129 130 slp->req[trecno].dblk = (daddr_t)0; 131 slp->req[trecno].count = 1; 132 *(union u_spcl *)(*(nextblock)++) = *(union u_spcl *)dp; 133 lastspclrec = spcl.c_tapea; 134 trecno++; 135 spcl.c_tapea++; 136 if (trecno >= ntrec) 137 flushtape(); 138 } 139 140 void 141 dumpblock(blkno, size) 142 daddr_t blkno; 143 int size; 144 { 145 int avail, tpblks, dblkno; 146 147 dblkno = fsbtodb(sblock, blkno); 148 tpblks = size >> tp_bshift; 149 while ((avail = MIN(tpblks, ntrec - trecno)) > 0) { 150 slp->req[trecno].dblk = dblkno; 151 slp->req[trecno].count = avail; 152 trecno += avail; 153 spcl.c_tapea += avail; 154 if (trecno >= ntrec) 155 flushtape(); 156 dblkno += avail << (tp_bshift - dev_bshift); 157 tpblks -= avail; 158 } 159 } 160 161 int nogripe = 0; 162 163 void 164 tperror() 165 { 166 167 if (pipeout) { 168 msg("write error on %s\n", tape); 169 quit("Cannot recover\n"); 170 /* NOTREACHED */ 171 } 172 msg("write error %d blocks into volume %d\n", blocksthisvol, tapeno); 173 broadcast("DUMP WRITE ERROR!\n"); 174 if (!query("Do you want to restart?")) 175 dumpabort(); 176 msg("Closing this volume. Prepare to restart with new media;\n"); 177 msg("this dump volume will be rewritten.\n"); 178 killall(); 179 nogripe = 1; 180 close_rewind(); 181 Exit(X_REWRITE); 182 } 183 184 void 185 sigpipe() 186 { 187 188 quit("Broken pipe\n"); 189 } 190 191 void 192 flushtape() 193 { 194 int i, blks, got; 195 long lastfirstrec; 196 #ifndef __STDC__ 197 int write(), read(); 198 #endif 199 200 int siz = (char *)nextblock - (char *)slp->req; 201 202 slp->req[trecno].count = 0; /* Sentinel */ 203 204 if (atomic(write, slp->fd, slp->req, siz) != siz) 205 quit("error writing command pipe: %s\n", strerror(errno)); 206 slp->sent = 1; /* we sent a request, read the response later */ 207 208 lastfirstrec = slp->firstrec; 209 210 if (++slp >= &slaves[SLAVES]) 211 slp = &slaves[0]; 212 213 /* Read results back from next slave */ 214 if (slp->sent) { 215 if (atomic(read, slp->fd, &got, sizeof got) != sizeof got) { 216 perror(" DUMP: error reading command pipe in master"); 217 dumpabort(); 218 } 219 slp->sent = 0; 220 221 /* Check for end of tape */ 222 if (got < writesize) { 223 msg("End of tape detected\n"); 224 225 /* 226 * Drain the results, don't care what the values were. 227 * If we read them here then trewind won't... 228 */ 229 for (i = 0; i < SLAVES; i++) { 230 if (slaves[i].sent) { 231 if (atomic(read, slaves[i].fd, &got, 232 sizeof got) != sizeof got) { 233 perror(" DUMP: error reading command pipe in master"); 234 dumpabort(); 235 } 236 slaves[i].sent = 0; 237 } 238 } 239 240 close_rewind(); 241 rollforward(); 242 return; 243 } 244 } 245 246 blks = 0; 247 if (spcl.c_type != TS_END) { 248 for (i = 0; i < spcl.c_count; i++) 249 if (spcl.c_addr[i] != 0) 250 blks++; 251 } 252 slp->count = lastspclrec + blks + 1 - spcl.c_tapea; 253 slp->tapea = spcl.c_tapea; 254 slp->firstrec = lastfirstrec + ntrec; 255 slp->inode = curino; 256 nextblock = slp->tblock; 257 trecno = 0; 258 asize += tenths; 259 blockswritten += ntrec; 260 blocksthisvol += ntrec; 261 if (!pipeout && (blocksperfile ? 262 (blocksthisvol >= blocksperfile) : (asize > tsize))) { 263 close_rewind(); 264 startnewtape(0); 265 } 266 timeest(); 267 } 268 269 void 270 trewind() 271 { 272 int f; 273 int got; 274 275 if (pipeout) 276 return; 277 for (f = 0; f < SLAVES; f++) { 278 /* 279 * Drain the results, but unlike EOT we DO (or should) care 280 * what the return values were, since if we detect EOT after 281 * we think we've written the last blocks to the tape anyway, 282 * we have to replay those blocks with rollforward. 283 * 284 * fixme: punt for now. 285 */ 286 if (slaves[f].sent) { 287 if (atomic(read, slaves[f].fd, &got, sizeof got) 288 != sizeof got) { 289 perror(" DUMP: error reading command pipe in master"); 290 dumpabort(); 291 } 292 slaves[f].sent = 0; 293 if (got != writesize) { 294 msg("EOT detected in last 2 tape records!\n"); 295 msg("Use a longer tape, decrease the size estimate\n"); 296 quit("or use no size estimate at all.\n"); 297 } 298 } 299 close(slaves[f].fd); 300 } 301 while (wait((int *)NULL) >= 0) /* wait for any signals from slaves */ 302 /* void */; 303 msg("Closing %s\n", tape); 304 305 #ifdef RDUMP 306 if (host) { 307 rmtclose(); 308 while (rmtopen(tape, 0) < 0) 309 sleep(10); 310 rmtclose(); 311 return; 312 } 313 #endif 314 close(tapefd); 315 while ((f = open(tape, 0)) < 0) 316 sleep (10); 317 close(f); 318 } 319 320 void 321 close_rewind() 322 { 323 trewind(); 324 if (nexttape) 325 return; 326 if (!nogripe) { 327 msg("Change Volumes: Mount volume #%d\n", tapeno+1); 328 broadcast("CHANGE DUMP VOLUMES!\7\7\n"); 329 } 330 while (!query("Is the new volume mounted and ready to go?")) 331 if (query("Do you want to abort?")) { 332 dumpabort(); 333 /*NOTREACHED*/ 334 } 335 } 336 337 #ifdef ROLLDEBUG 338 int do_sum(block) 339 union u_spcl *block; 340 341 { 342 char sum = 0; 343 int i; 344 345 for (i = 0; i < TP_BSIZE; i++) { 346 sum = sum ^ block->dummy[i]; 347 } 348 return(sum); 349 } 350 #endif 351 352 void 353 rollforward() 354 { 355 register struct req *p, *q, *prev; 356 register struct slave *tslp; 357 int i, next, size, savedtapea, got; 358 union u_spcl *ntb, *otb; 359 #ifdef ROLLDEBUG 360 int j; 361 #endif 362 tslp = &slaves[SLAVES]; 363 ntb = (union u_spcl *)tslp->tblock[1]; 364 365 /* 366 * Each of the N slaves should have requests that need to 367 * be replayed on the next tape. Use the extra slave buffers 368 * (slaves[SLAVES]) to construct request lists to be sent to 369 * each slave in turn. 370 */ 371 for (i = 0; i < SLAVES; i++) { 372 q = &tslp->req[1]; 373 otb = (union u_spcl *)slp->tblock; 374 375 /* 376 * For each request in the current slave, copy it to tslp. 377 */ 378 #ifdef ROLLDEBUG 379 printf("replaying reqs to slave %d (%d)\n", slp - &slaves[0], 380 slp->pid); 381 j = 0; 382 #endif 383 384 for (p = slp->req; p->count > 0; p += p->count) { 385 #ifdef ROLLDEBUG 386 printf(" req %d count %d dblk %d\n", 387 j++, p->count, p->dblk); 388 if (p->dblk == 0) 389 printf("\tsum %x\n", do_sum(otb)); 390 #endif 391 *q = *p; 392 if (p->dblk == 0) 393 *ntb++ = *otb++; /* copy the datablock also */ 394 prev = q; 395 q += q->count; 396 } 397 if (prev->dblk != 0) 398 prev->count -= 1; 399 else 400 ntb--; 401 q -= 1; 402 q->count = 0; 403 q = &tslp->req[0]; 404 if (i == 0) { 405 q->dblk = 0; 406 q->count = 1; 407 trecno = 0; 408 nextblock = tslp->tblock; 409 savedtapea = spcl.c_tapea; 410 spcl.c_tapea = slp->tapea; 411 startnewtape(0); 412 spcl.c_tapea = savedtapea; 413 lastspclrec = savedtapea - 1; 414 } 415 size = (char *)ntb - (char *)q; 416 if (atomic(write, slp->fd, q, size) != size) { 417 perror(" DUMP: error writing command pipe"); 418 dumpabort(); 419 } 420 slp->sent = 1; 421 #ifdef ROLLDEBUG 422 printf("after the shift:\n"); 423 j = 0; 424 for (p = tslp->req; p->count > 0; p += p->count) { 425 printf(" req %d count %d dblk %d\n", 426 j++, p->count, p->dblk); 427 if (p->dblk == 0) { 428 /* dump block also */ 429 } 430 } 431 #endif 432 if (++slp >= &slaves[SLAVES]) 433 slp = &slaves[0]; 434 435 q->count = 1; 436 437 if (prev->dblk != 0) { 438 /* 439 * If the last one was a disk block, make the 440 * first of this one be the last bit of that disk 441 * block... 442 */ 443 q->dblk = prev->dblk + 444 prev->count * (TP_BSIZE / DEV_BSIZE); 445 ntb = (union u_spcl *)tslp->tblock; 446 } else { 447 /* 448 * It wasn't a disk block. Copy the data to its 449 * new location in the buffer. 450 */ 451 q->dblk = 0; 452 *((union u_spcl *)tslp->tblock) = *ntb; 453 ntb = (union u_spcl *)tslp->tblock[1]; 454 } 455 } 456 slp->req[0] = *q; 457 nextblock = slp->tblock; 458 if (q->dblk == 0) 459 nextblock++; 460 trecno = 1; 461 462 /* 463 * Clear the first slaves' response. One hopes that it 464 * worked ok, otherwise the tape is much too short! 465 */ 466 if (slp->sent) { 467 if (atomic(read, slp->fd, &got, sizeof got) != sizeof got) { 468 perror(" DUMP: error reading command pipe in master"); 469 dumpabort(); 470 } 471 slp->sent = 0; 472 473 if (got != writesize) { 474 quit("EOT detected at start of the tape!\n"); 475 } 476 } 477 } 478 479 /* 480 * We implement taking and restoring checkpoints on the tape level. 481 * When each tape is opened, a new process is created by forking; this 482 * saves all of the necessary context in the parent. The child 483 * continues the dump; the parent waits around, saving the context. 484 * If the child returns X_REWRITE, then it had problems writing that tape; 485 * this causes the parent to fork again, duplicating the context, and 486 * everything continues as if nothing had happened. 487 */ 488 void 489 startnewtape(top) 490 int top; 491 { 492 int parentpid; 493 int childpid; 494 int status; 495 int waitpid; 496 sig_t interrupt; 497 int i; 498 char *p; 499 500 interrupt = signal(SIGINT, SIG_IGN); 501 parentpid = getpid(); 502 503 restore_check_point: 504 (void)signal(SIGINT, interrupt); 505 /* 506 * All signals are inherited... 507 */ 508 childpid = fork(); 509 if (childpid < 0) { 510 msg("Context save fork fails in parent %d\n", parentpid); 511 Exit(X_ABORT); 512 } 513 if (childpid != 0) { 514 /* 515 * PARENT: 516 * save the context by waiting 517 * until the child doing all of the work returns. 518 * don't catch the interrupt 519 */ 520 signal(SIGINT, SIG_IGN); 521 #ifdef TDEBUG 522 msg("Tape: %d; parent process: %d child process %d\n", 523 tapeno+1, parentpid, childpid); 524 #endif TDEBUG 525 while ((waitpid = wait(&status)) != childpid) 526 msg("Parent %d waiting for child %d has another child %d return\n", 527 parentpid, childpid, waitpid); 528 if (status & 0xFF) { 529 msg("Child %d returns LOB status %o\n", 530 childpid, status&0xFF); 531 } 532 status = (status >> 8) & 0xFF; 533 #ifdef TDEBUG 534 switch(status) { 535 case X_FINOK: 536 msg("Child %d finishes X_FINOK\n", childpid); 537 break; 538 case X_ABORT: 539 msg("Child %d finishes X_ABORT\n", childpid); 540 break; 541 case X_REWRITE: 542 msg("Child %d finishes X_REWRITE\n", childpid); 543 break; 544 default: 545 msg("Child %d finishes unknown %d\n", 546 childpid, status); 547 break; 548 } 549 #endif TDEBUG 550 switch(status) { 551 case X_FINOK: 552 Exit(X_FINOK); 553 case X_ABORT: 554 Exit(X_ABORT); 555 case X_REWRITE: 556 goto restore_check_point; 557 default: 558 msg("Bad return code from dump: %d\n", status); 559 Exit(X_ABORT); 560 } 561 /*NOTREACHED*/ 562 } else { /* we are the child; just continue */ 563 #ifdef TDEBUG 564 sleep(4); /* allow time for parent's message to get out */ 565 msg("Child on Tape %d has parent %d, my pid = %d\n", 566 tapeno+1, parentpid, getpid()); 567 #endif TDEBUG 568 /* 569 * If we have a name like "/dev/rmt0,/dev/rmt1", 570 * use the name before the comma first, and save 571 * the remaining names for subsequent volumes. 572 */ 573 tapeno++; /* current tape sequence */ 574 if (nexttape || index(tape, ',')) { 575 if (nexttape && *nexttape) 576 tape = nexttape; 577 if (p = index(tape, ',')) { 578 *p = '\0'; 579 nexttape = p + 1; 580 } else 581 nexttape = NULL; 582 msg("Dumping volume %d on %s\n", tapeno, tape); 583 } 584 #ifdef RDUMP 585 while ((tapefd = (host ? rmtopen(tape, 2) : 586 pipeout ? 1 : open(tape, O_WRONLY|O_CREAT, 0666))) < 0) 587 #else 588 while ((tapefd = (pipeout ? 1 : 589 open(tape, O_WRONLY|O_CREAT, 0666))) < 0) 590 #endif 591 { 592 msg("Cannot open output \"%s\".\n", tape); 593 if (!query("Do you want to retry the open?")) 594 dumpabort(); 595 } 596 597 enslave(); /* Share open tape file descriptor with slaves */ 598 599 asize = 0; 600 blocksthisvol = 0; 601 if (top) 602 newtape++; /* new tape signal */ 603 spcl.c_count = slp->count; 604 /* 605 * measure firstrec in TP_BSIZE units since restore doesn't 606 * know the correct ntrec value... 607 */ 608 spcl.c_firstrec = slp->firstrec; 609 spcl.c_volume++; 610 spcl.c_type = TS_TAPE; 611 spcl.c_flags |= DR_NEWHEADER; 612 writeheader(slp->inode); 613 spcl.c_flags &=~ DR_NEWHEADER; 614 if (tapeno > 1) 615 msg("Volume %d begins with blocks from inode %d\n", 616 tapeno, slp->inode); 617 } 618 } 619 620 void 621 dumpabort() 622 { 623 624 if (master != 0 && master != getpid()) 625 kill(master, SIGTERM); /* Signals master to call dumpabort */ 626 else { 627 killall(); 628 msg("The ENTIRE dump is aborted.\n"); 629 } 630 Exit(X_ABORT); 631 } 632 633 void 634 Exit(status) 635 int status; 636 { 637 638 #ifdef TDEBUG 639 msg("pid = %d exits with status %d\n", getpid(), status); 640 #endif TDEBUG 641 exit(status); 642 } 643 644 /* 645 * proceed - handler for SIGUSR2, used to synchronize IO between the slaves. 646 */ 647 void 648 proceed() 649 { 650 651 if (ready) 652 longjmp(jmpbuf, 1); 653 caught++; 654 } 655 656 void 657 enslave() 658 { 659 int cmd[2]; 660 register int i, j; 661 662 master = getpid(); 663 664 signal(SIGTERM, dumpabort); /* Slave sends SIGTERM on dumpabort() */ 665 signal(SIGPIPE, sigpipe); 666 signal(SIGUSR1, tperror); /* Slave sends SIGUSR1 on tape errors */ 667 signal(SIGUSR2, proceed); /* Slave sends SIGUSR2 to next slave */ 668 669 for (i = 0; i < SLAVES; i++) { 670 if (i == slp - &slaves[0]) { 671 caught = 1; 672 } else { 673 caught = 0; 674 } 675 676 if (socketpair(AF_UNIX, SOCK_STREAM, 0, cmd) < 0 || 677 (slaves[i].pid = fork()) < 0) 678 quit("too many slaves, %d (recompile smaller): %s\n", 679 i, strerror(errno)); 680 681 slaves[i].fd = cmd[1]; 682 slaves[i].sent = 0; 683 if (slaves[i].pid == 0) { /* Slave starts up here */ 684 for (j = 0; j <= i; j++) 685 close(slaves[j].fd); 686 signal(SIGINT, SIG_IGN); /* Master handles this */ 687 doslave(cmd[0], i); 688 Exit(X_FINOK); 689 } 690 } 691 692 for (i = 0; i < SLAVES; i++) 693 atomic(write, slaves[i].fd, &slaves[(i + 1) % SLAVES].pid, 694 sizeof slaves[0].pid); 695 696 master = 0; 697 } 698 699 void 700 killall() 701 { 702 register int i; 703 704 for (i = 0; i < SLAVES; i++) 705 if (slaves[i].pid > 0) 706 kill(slaves[i].pid, SIGKILL); 707 } 708 709 /* 710 * Synchronization - each process has a lockfile, and shares file 711 * descriptors to the following process's lockfile. When our write 712 * completes, we release our lock on the following process's lock- 713 * file, allowing the following process to lock it and proceed. We 714 * get the lock back for the next cycle by swapping descriptors. 715 */ 716 void 717 doslave(cmd, slave_number) 718 register int cmd; 719 int slave_number; 720 { 721 register int nread; 722 int nextslave, size, wrote, eot_count; 723 #ifndef __STDC__ 724 int read(); 725 #endif 726 #ifdef ROLLDEBUG 727 int dodump = 2; 728 FILE *out; 729 char name[64]; 730 #endif 731 732 /* 733 * Need our own seek pointer. 734 */ 735 close(diskfd); 736 if ((diskfd = open(disk, O_RDONLY)) < 0) 737 quit("slave couldn't reopen disk: %s\n", strerror(errno)); 738 739 /* 740 * Need the pid of the next slave in the loop... 741 */ 742 if ((nread = atomic(read, cmd, &nextslave, sizeof nextslave)) 743 != sizeof nextslave) { 744 quit("master/slave protocol botched - didn't get pid of next slave.\n"); 745 } 746 747 #ifdef ROLLDEBUG 748 sprintf(name, "slave.%d", slave_number); 749 out = fopen(name, "w"); 750 #endif 751 /* 752 * Get list of blocks to dump, read the blocks into tape buffer 753 */ 754 while ((nread = atomic(read, cmd, slp->req, reqsiz)) == reqsiz) { 755 register struct req *p = slp->req; 756 int j; 757 struct req *rover; 758 char (*orover)[TP_BSIZE]; 759 760 j = 0; 761 for (trecno = 0; trecno < ntrec; 762 trecno += p->count, p += p->count) { 763 if (p->dblk) { 764 bread(p->dblk, slp->tblock[trecno], 765 p->count * TP_BSIZE); 766 } else { 767 if (p->count != 1 || atomic(read, cmd, 768 slp->tblock[trecno], TP_BSIZE) != TP_BSIZE) 769 quit("master/slave protocol botched.\n"); 770 } 771 #ifdef ROLLDEBUG 772 if (dodump) { 773 fprintf(out, " req %d count %d dblk %d\n", 774 j++, p->count, p->dblk); 775 if (p->dblk == 0) { 776 fprintf(out, "\tsum %x\n", 777 do_sum(slp->tblock[trecno])); 778 } 779 } 780 #endif 781 } 782 #ifdef ROLLDEBUG 783 if (dodump) { 784 fprintf(out, "\n"); 785 } 786 if (--dodump == 0) { 787 fclose(out); 788 } 789 #endif 790 if (setjmp(jmpbuf) == 0) { 791 ready = 1; 792 if (!caught) 793 pause(); 794 } 795 ready = 0; 796 caught = 0; 797 798 /* Try to write the data... */ 799 eot_count = 0; 800 size = 0; 801 802 while (eot_count < 10 && size < writesize) { 803 #ifdef RDUMP 804 if (host) 805 wrote = rmtwrite(slp->tblock[0]+size, 806 writesize-size); 807 else 808 #endif 809 wrote = write(tapefd, slp->tblock[0]+size, 810 writesize-size); 811 #ifdef WRITEDEBUG 812 printf("slave %d wrote %d\n", slave_number, wrote); 813 #endif 814 if (wrote < 0) 815 break; 816 if (wrote == 0) 817 eot_count++; 818 size += wrote; 819 } 820 821 #ifdef WRITEDEBUG 822 if (size != writesize) 823 printf("slave %d only wrote %d out of %d bytes and gave up.\n", 824 slave_number, size, writesize); 825 #endif 826 827 if (eot_count > 0) 828 size = 0; 829 830 /* 831 * fixme: Pyramids running OSx return ENOSPC 832 * at EOT on 1/2 inch drives. 833 */ 834 if (size < 0) { 835 kill(master, SIGUSR1); 836 for (;;) 837 sigpause(0); 838 } else { 839 /* 840 * pass size of write back to master 841 * (for EOT handling) 842 */ 843 atomic(write, cmd, &size, sizeof size); 844 } 845 846 /* 847 * If partial write, don't want next slave to go. 848 * Also jolts him awake. 849 */ 850 kill(nextslave, SIGUSR2); 851 } 852 if (nread != 0) 853 quit("error reading command pipe: %s\n", strerror(errno)); 854 } 855 856 /* 857 * Since a read from a pipe may not return all we asked for, 858 * or a write may not write all we ask if we get a signal, 859 * loop until the count is satisfied (or error). 860 */ 861 int 862 atomic(func, fd, buf, count) 863 int (*func)(), fd, count; 864 char *buf; 865 { 866 int got, need = count; 867 868 while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0) 869 buf += got; 870 return (got < 0 ? got : count - need); 871 } 872