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