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