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 * @(#)tape.c 8.4 (Berkeley) 5/1/95 34 * $FreeBSD: src/sbin/dump/tape.c,v 1.12.2.3 2002/02/23 22:32:51 iedowse Exp $ 35 * $DragonFly: src/sbin/dump/tape.c,v 1.13 2005/04/13 16:07:15 joerg Exp $ 36 */ 37 38 #include <sys/param.h> 39 #include <sys/socket.h> 40 #include <sys/time.h> 41 #include <sys/wait.h> 42 #include <sys/stat.h> 43 #ifdef sunos 44 #include <sys/vnode.h> 45 46 #include <ufs/fs.h> 47 #include <ufs/inode.h> 48 #else 49 #include <vfs/ufs/dinode.h> 50 #include <vfs/ufs/fs.h> 51 #endif 52 53 #include <protocols/dumprestore.h> 54 55 #include <errno.h> 56 #include <fcntl.h> 57 #include <setjmp.h> 58 #include <signal.h> 59 #include <stdio.h> 60 #ifdef __STDC__ 61 #include <stdlib.h> 62 #include <string.h> 63 #include <unistd.h> 64 #else 65 int write(), read(); 66 #endif 67 68 #include "dump.h" 69 70 static int writesize; /* size of malloc()ed buffer for tape */ 71 static long lastspclrec = -1; /* tape block number of last written header */ 72 static int trecno = 0; /* next record to write in current block */ 73 static long blocksthisvol; /* number of blocks on current output file */ 74 static const char *nexttape; 75 static int tapeno = 0; /* current tape number */ 76 77 static int atomic_read(int, void *, int); 78 static int atomic_write(int, const void *, int); 79 #ifdef WRITEDEBUG 80 static void doslave(int, int); 81 #else 82 static void doslave(int); 83 #endif 84 static void enslave(void); 85 static void flushtape(void); 86 static void killall(void); 87 static void rollforward(void); 88 89 /* 90 * Concurrent dump mods (Caltech) - disk block reading and tape writing 91 * are exported to several slave processes. While one slave writes the 92 * tape, the others read disk blocks; they pass control of the tape in 93 * a ring via signals. The parent process traverses the filesystem and 94 * sends writeheader()'s and lists of daddr's to the slaves via pipes. 95 * The following structure defines the instruction packets sent to slaves. 96 */ 97 struct req { 98 daddr_t dblk; 99 int count; 100 }; 101 int reqsiz; 102 103 #define SLAVES 3 /* 1 slave writing, 1 reading, 1 for slack */ 104 struct slave { 105 int tapea; /* header number at start of this chunk */ 106 int count; /* count to next header (used for TS_TAPE */ 107 /* after EOT) */ 108 int inode; /* inode that we are currently dealing with */ 109 int fd; /* FD for this slave */ 110 int pid; /* PID for this slave */ 111 int sent; /* 1 == we've sent this slave requests */ 112 int firstrec; /* record number of this block */ 113 char (*tblock)[TP_BSIZE]; /* buffer for data blocks */ 114 struct req *req; /* buffer for requests */ 115 } slaves[SLAVES+1]; 116 struct slave *slp; 117 118 char (*nextblock)[TP_BSIZE]; 119 120 int master; /* pid of master, for sending error signals */ 121 int tenths; /* length of tape used per block written */ 122 static int caught; /* have we caught the signal to proceed? */ 123 static int ready; /* have we reached the lock point without having */ 124 /* received the SIGUSR2 signal from the prev slave? */ 125 static jmp_buf jmpbuf; /* where to jump to if we are ready when the */ 126 /* SIGUSR2 arrives from the previous slave */ 127 128 int 129 alloctape(void) 130 { 131 int pgoff = getpagesize() - 1; 132 char *buf; 133 int i; 134 135 writesize = ntrec * TP_BSIZE; 136 reqsiz = (ntrec + 1) * sizeof(struct req); 137 /* 138 * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode 139 * (see DEC TU80 User's Guide). The shorter gaps of 6250-bpi require 140 * repositioning after stopping, i.e, streaming mode, where the gap is 141 * variable, 0.30" to 0.45". The gap is maximal when the tape stops. 142 */ 143 if (blocksperfile == 0 && !unlimited) 144 tenths = writesize / density + 145 (cartridge ? 16 : density == 625 ? 5 : 8); 146 /* 147 * Allocate tape buffer contiguous with the array of instruction 148 * packets, so flushtape() can write them together with one write(). 149 * Align tape buffer on page boundary to speed up tape write(). 150 */ 151 for (i = 0; i <= SLAVES; i++) { 152 buf = (char *) 153 malloc((unsigned)(reqsiz + writesize + pgoff + TP_BSIZE)); 154 if (buf == NULL) 155 return(0); 156 slaves[i].tblock = (char (*)[TP_BSIZE]) 157 (((long)&buf[ntrec + 1] + pgoff) &~ pgoff); 158 slaves[i].req = (struct req *)slaves[i].tblock - ntrec - 1; 159 } 160 slp = &slaves[0]; 161 slp->count = 1; 162 slp->tapea = 0; 163 slp->firstrec = 0; 164 nextblock = slp->tblock; 165 return(1); 166 } 167 168 void 169 writerec(const void *dp, int isspcl) 170 { 171 172 slp->req[trecno].dblk = (daddr_t)0; 173 slp->req[trecno].count = 1; 174 bcopy(dp, *(nextblock)++, sizeof (union u_spcl)); 175 if (isspcl) 176 lastspclrec = spcl.c_tapea; 177 trecno++; 178 spcl.c_tapea++; 179 if (trecno >= ntrec) 180 flushtape(); 181 } 182 183 void 184 dumpblock(daddr_t blkno, int size) 185 { 186 int avail, tpblks, dblkno; 187 188 dblkno = fsbtodb(sblock, blkno); 189 tpblks = size >> tp_bshift; 190 while ((avail = MIN(tpblks, ntrec - trecno)) > 0) { 191 slp->req[trecno].dblk = dblkno; 192 slp->req[trecno].count = avail; 193 trecno += avail; 194 spcl.c_tapea += avail; 195 if (trecno >= ntrec) 196 flushtape(); 197 dblkno += avail << (tp_bshift - dev_bshift); 198 tpblks -= avail; 199 } 200 } 201 202 int nogripe = 0; 203 204 static void 205 tperror(int signo __unused) 206 { 207 208 if (pipeout) { 209 msg("write error on %s\n", tape); 210 quit("Cannot recover\n"); 211 /* NOTREACHED */ 212 } 213 msg("write error %ld blocks into volume %d\n", blocksthisvol, tapeno); 214 broadcast("DUMP WRITE ERROR!\n"); 215 if (!query("Do you want to restart?")) 216 dumpabort(0); 217 msg("Closing this volume. Prepare to restart with new media;\n"); 218 msg("this dump volume will be rewritten.\n"); 219 killall(); 220 nogripe = 1; 221 close_rewind(); 222 Exit(X_REWRITE); 223 } 224 225 static void 226 sigpipe(int signo __unused) 227 { 228 229 quit("Broken pipe\n"); 230 } 231 232 static void 233 flushtape(void) 234 { 235 int i, blks, got; 236 long lastfirstrec; 237 238 int siz = (char *)nextblock - (char *)slp->req; 239 240 slp->req[trecno].count = 0; /* Sentinel */ 241 242 if (atomic_write(slp->fd, slp->req, siz) != siz) 243 quit("error writing command pipe: %s\n", strerror(errno)); 244 slp->sent = 1; /* we sent a request, read the response later */ 245 246 lastfirstrec = slp->firstrec; 247 248 if (++slp >= &slaves[SLAVES]) 249 slp = &slaves[0]; 250 251 /* Read results back from next slave */ 252 if (slp->sent) { 253 if (atomic_read(slp->fd, &got, sizeof got) 254 != sizeof got) { 255 perror(" DUMP: error reading command pipe in master"); 256 dumpabort(0); 257 } 258 slp->sent = 0; 259 260 /* Check for end of tape */ 261 if (got < writesize) { 262 msg("End of tape detected\n"); 263 264 /* 265 * Drain the results, don't care what the values were. 266 * If we read them here then trewind won't... 267 */ 268 for (i = 0; i < SLAVES; i++) { 269 if (slaves[i].sent) { 270 if (atomic_read(slaves[i].fd, 271 &got, sizeof got) 272 != sizeof got) { 273 perror(" DUMP: error reading command pipe in master"); 274 dumpabort(0); 275 } 276 slaves[i].sent = 0; 277 } 278 } 279 280 close_rewind(); 281 rollforward(); 282 return; 283 } 284 } 285 286 blks = 0; 287 if (spcl.c_type != TS_END) { 288 for (i = 0; i < spcl.c_count; i++) 289 if (spcl.c_addr[i] != 0) 290 blks++; 291 } 292 slp->count = lastspclrec + blks + 1 - spcl.c_tapea; 293 slp->tapea = spcl.c_tapea; 294 slp->firstrec = lastfirstrec + ntrec; 295 slp->inode = curino; 296 nextblock = slp->tblock; 297 trecno = 0; 298 asize += tenths; 299 blockswritten += ntrec; 300 blocksthisvol += ntrec; 301 if (!pipeout && !unlimited && (blocksperfile ? 302 (blocksthisvol >= blocksperfile) : (asize > tsize))) { 303 close_rewind(); 304 startnewtape(0); 305 } 306 timeest(); 307 } 308 309 void 310 trewind(void) 311 { 312 struct stat sb; 313 int f; 314 int got; 315 316 for (f = 0; f < SLAVES; f++) { 317 /* 318 * Drain the results, but unlike EOT we DO (or should) care 319 * what the return values were, since if we detect EOT after 320 * we think we've written the last blocks to the tape anyway, 321 * we have to replay those blocks with rollforward. 322 * 323 * fixme: punt for now. 324 */ 325 if (slaves[f].sent) { 326 if (atomic_read(slaves[f].fd, &got, sizeof got) 327 != sizeof got) { 328 perror(" DUMP: error reading command pipe in master"); 329 dumpabort(0); 330 } 331 slaves[f].sent = 0; 332 if (got != writesize) { 333 msg("EOT detected in last 2 tape records!\n"); 334 msg("Use a longer tape, decrease the size estimate\n"); 335 quit("or use no size estimate at all.\n"); 336 } 337 } 338 close(slaves[f].fd); 339 } 340 while (wait((int *)NULL) >= 0) /* wait for any signals from slaves */ 341 /* void */; 342 343 if (pipeout) 344 return; 345 346 msg("Closing %s\n", tape); 347 348 #ifdef RDUMP 349 if (host) { 350 rmtclose(); 351 while (rmtopen(tape, 0) < 0) 352 sleep(10); 353 rmtclose(); 354 return; 355 } 356 #endif 357 if (fstat(tapefd, &sb) == 0 && S_ISFIFO(sb.st_mode)) { 358 close(tapefd); 359 return; 360 } 361 close(tapefd); 362 while ((f = open(tape, 0)) < 0) 363 sleep (10); 364 close(f); 365 } 366 367 void 368 close_rewind(void) 369 { 370 time_t tstart_changevol, tend_changevol; 371 372 trewind(); 373 if (nexttape) 374 return; 375 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 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(void) 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, 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, &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(int top) 507 { 508 int parentpid; 509 int childpid; 510 int status; 511 int wait_pid; 512 char *p; 513 #ifdef sunos 514 void (*interrupt_save)(); 515 #else 516 sig_t interrupt_save; 517 #endif 518 519 interrupt_save = signal(SIGINT, SIG_IGN); 520 parentpid = getpid(); 521 522 restore_check_point: 523 signal(SIGINT, interrupt_save); 524 /* 525 * All signals are inherited... 526 */ 527 setproctitle(NULL); /* Restore the proctitle. */ 528 childpid = fork(); 529 if (childpid < 0) { 530 msg("Context save fork fails in parent %d\n", parentpid); 531 Exit(X_ABORT); 532 } 533 if (childpid != 0) { 534 /* 535 * PARENT: 536 * save the context by waiting 537 * until the child doing all of the work returns. 538 * don't catch the interrupt 539 */ 540 signal(SIGINT, SIG_IGN); 541 #ifdef TDEBUG 542 msg("Tape: %d; parent process: %d child process %d\n", 543 tapeno+1, parentpid, childpid); 544 #endif /* TDEBUG */ 545 while ((wait_pid = wait(&status)) != childpid) 546 msg("Parent %d waiting for child %d has another child %d return\n", 547 parentpid, childpid, wait_pid); 548 if (status & 0xFF) { 549 msg("Child %d returns LOB status %o\n", 550 childpid, status&0xFF); 551 } 552 status = (status >> 8) & 0xFF; 553 #ifdef TDEBUG 554 switch(status) { 555 case X_FINOK: 556 msg("Child %d finishes X_FINOK\n", childpid); 557 break; 558 case X_ABORT: 559 msg("Child %d finishes X_ABORT\n", childpid); 560 break; 561 case X_REWRITE: 562 msg("Child %d finishes X_REWRITE\n", childpid); 563 break; 564 default: 565 msg("Child %d finishes unknown %d\n", 566 childpid, status); 567 break; 568 } 569 #endif /* TDEBUG */ 570 switch(status) { 571 case X_FINOK: 572 Exit(X_FINOK); 573 case X_ABORT: 574 Exit(X_ABORT); 575 case X_REWRITE: 576 goto restore_check_point; 577 default: 578 msg("Bad return code from dump: %d\n", status); 579 Exit(X_ABORT); 580 } 581 /*NOTREACHED*/ 582 } else { /* we are the child; just continue */ 583 #ifdef TDEBUG 584 sleep(4); /* allow time for parent's message to get out */ 585 msg("Child on Tape %d has parent %d, my pid = %d\n", 586 tapeno+1, parentpid, getpid()); 587 #endif /* TDEBUG */ 588 /* 589 * If we have a name like "/dev/rmt0,/dev/rmt1", 590 * use the name before the comma first, and save 591 * the remaining names for subsequent volumes. 592 */ 593 tapeno++; /* current tape sequence */ 594 if (nexttape || strchr(tape, ',')) { 595 if (nexttape && *nexttape) 596 tape = nexttape; 597 if ((p = strchr(tape, ',')) != NULL) { 598 *p = '\0'; 599 nexttape = p + 1; 600 } else 601 nexttape = NULL; 602 msg("Dumping volume %d on %s\n", tapeno, tape); 603 } 604 #ifdef RDUMP 605 while ((tapefd = (host ? rmtopen(tape, 2) : 606 pipeout ? 1 : open(tape, O_WRONLY|O_CREAT, 0666))) < 0) 607 #else 608 while ((tapefd = (pipeout ? 1 : 609 open(tape, O_WRONLY|O_CREAT, 0666))) < 0) 610 #endif 611 { 612 msg("Cannot open output \"%s\".\n", tape); 613 if (!query("Do you want to retry the open?")) 614 dumpabort(0); 615 } 616 617 enslave(); /* Share open tape file descriptor with slaves */ 618 signal(SIGINFO, infosch); 619 620 asize = 0; 621 blocksthisvol = 0; 622 if (top) 623 newtape++; /* new tape signal */ 624 spcl.c_count = slp->count; 625 /* 626 * measure firstrec in TP_BSIZE units since restore doesn't 627 * know the correct ntrec value... 628 */ 629 spcl.c_firstrec = slp->firstrec; 630 spcl.c_volume++; 631 spcl.c_type = TS_TAPE; 632 spcl.c_flags |= DR_NEWHEADER; 633 writeheader((ino_t)slp->inode); 634 spcl.c_flags &=~ DR_NEWHEADER; 635 if (tapeno > 1) 636 msg("Volume %d begins with blocks from inode %d\n", 637 tapeno, slp->inode); 638 } 639 } 640 641 void 642 dumpabort(int signo __unused) 643 { 644 645 if (master != 0 && master != getpid()) 646 /* Signals master to call dumpabort */ 647 kill(master, SIGTERM); 648 else { 649 killall(); 650 msg("The ENTIRE dump is aborted.\n"); 651 } 652 #ifdef RDUMP 653 rmtclose(); 654 #endif 655 Exit(X_ABORT); 656 } 657 658 void 659 Exit(int status) 660 { 661 662 #ifdef TDEBUG 663 msg("pid = %d exits with status %d\n", getpid(), status); 664 #endif /* TDEBUG */ 665 exit(status); 666 } 667 668 /* 669 * proceed - handler for SIGUSR2, used to synchronize IO between the slaves. 670 */ 671 static void 672 proceed(int signo __unused) 673 { 674 675 if (ready) 676 longjmp(jmpbuf, 1); 677 caught++; 678 } 679 680 static void 681 enslave(void) 682 { 683 int cmd[2]; 684 int i, j; 685 686 master = getpid(); 687 688 signal(SIGTERM, dumpabort); /* Slave sends SIGTERM on dumpabort() */ 689 signal(SIGPIPE, sigpipe); 690 signal(SIGUSR1, tperror); /* Slave sends SIGUSR1 on tape errors */ 691 signal(SIGUSR2, proceed); /* Slave sends SIGUSR2 to next slave */ 692 693 for (i = 0; i < SLAVES; i++) { 694 if (i == slp - &slaves[0]) { 695 caught = 1; 696 } else { 697 caught = 0; 698 } 699 700 if (socketpair(AF_UNIX, SOCK_STREAM, 0, cmd) < 0 || 701 (slaves[i].pid = fork()) < 0) 702 quit("too many slaves, %d (recompile smaller): %s\n", 703 i, strerror(errno)); 704 705 slaves[i].fd = cmd[1]; 706 slaves[i].sent = 0; 707 if (slaves[i].pid == 0) { /* Slave starts up here */ 708 for (j = 0; j <= i; j++) 709 close(slaves[j].fd); 710 signal(SIGINT, SIG_IGN); /* Master handles this */ 711 #ifdef WRITEDEBUG 712 doslave(cmd[0], i); 713 #else 714 doslave(cmd[0]); 715 #endif 716 Exit(X_FINOK); 717 } 718 } 719 720 for (i = 0; i < SLAVES; i++) 721 atomic_write(slaves[i].fd, &slaves[(i + 1) % SLAVES].pid, 722 sizeof slaves[0].pid); 723 724 master = 0; 725 } 726 727 void 728 killall(void) 729 { 730 int i; 731 732 for (i = 0; i < SLAVES; i++) 733 if (slaves[i].pid > 0) { 734 kill(slaves[i].pid, SIGKILL); 735 slaves[i].sent = 0; 736 } 737 } 738 739 /* 740 * Synchronization - each process has a lockfile, and shares file 741 * descriptors to the following process's lockfile. When our write 742 * completes, we release our lock on the following process's lock- 743 * file, allowing the following process to lock it and proceed. We 744 * get the lock back for the next cycle by swapping descriptors. 745 */ 746 static void 747 #ifdef WRITEDEBUG 748 doslave(int cmd, int slave_number) 749 #else 750 doslave(int cmd) 751 #endif 752 { 753 int nread; 754 int nextslave, size, wrote, eot_count; 755 756 /* 757 * Need our own seek pointer. 758 */ 759 close(diskfd); 760 if ((diskfd = open(disk, O_RDONLY)) < 0) 761 quit("slave couldn't reopen disk: %s\n", strerror(errno)); 762 763 /* 764 * Need the pid of the next slave in the loop... 765 */ 766 if ((nread = atomic_read(cmd, &nextslave, sizeof nextslave)) 767 != sizeof nextslave) { 768 quit("master/slave protocol botched - didn't get pid of next slave.\n"); 769 } 770 771 /* 772 * Get list of blocks to dump, read the blocks into tape buffer 773 */ 774 while ((nread = atomic_read(cmd, slp->req, reqsiz)) == reqsiz) { 775 struct req *p = slp->req; 776 777 for (trecno = 0; trecno < ntrec; 778 trecno += p->count, p += p->count) { 779 if (p->dblk) { 780 bread(p->dblk, slp->tblock[trecno], 781 p->count * TP_BSIZE); 782 } else { 783 if (p->count != 1 || 784 atomic_read(cmd, slp->tblock[trecno], 785 TP_BSIZE) != TP_BSIZE) 786 quit("master/slave protocol botched.\n"); 787 } 788 } 789 if (setjmp(jmpbuf) == 0) { 790 ready = 1; 791 if (!caught) 792 pause(); 793 } 794 ready = 0; 795 caught = 0; 796 797 /* Try to write the data... */ 798 eot_count = 0; 799 size = 0; 800 wrote = 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 /* 828 * Handle ENOSPC as an EOT condition. 829 */ 830 if (wrote < 0 && errno == ENOSPC) { 831 wrote = 0; 832 eot_count++; 833 } 834 835 if (eot_count > 0) 836 size = 0; 837 838 if (wrote < 0) { 839 kill(master, SIGUSR1); 840 for (;;) 841 sigpause(0); 842 } else { 843 /* 844 * pass size of write back to master 845 * (for EOT handling) 846 */ 847 atomic_write(cmd, &size, sizeof size); 848 } 849 850 /* 851 * If partial write, don't want next slave to go. 852 * Also jolts him awake. 853 */ 854 kill(nextslave, SIGUSR2); 855 } 856 if (nread != 0) 857 quit("error reading command pipe: %s\n", strerror(errno)); 858 } 859 860 static int 861 atomic_read(int fd, void *buf, int count) 862 { 863 int got, need = count; 864 865 while ((got = read(fd, buf, need)) > 0 && (need -= got) > 0) 866 buf = (uint8_t *)buf + got; 867 return (got < 0 ? got : count - need); 868 } 869 870 static int 871 atomic_write(int fd, const void *buf, int count) 872 { 873 int got, need = count; 874 875 while ((got = write(fd, buf, need)) > 0 && (need -= got) > 0) 876 buf = (const uint8_t *)buf + got; 877 return (got < 0 ? got : count - need); 878 } 879