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