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