1 /* $NetBSD: tape.c,v 1.53 2013/06/15 01:27:19 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.53 2013/06/15 01:27:19 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\n"); 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\n"); 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 quit("error writing command pipe: %s\n", strerror(errno)); 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 - iswap32(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.\n"); 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, savedtapea, got; 461 union u_spcl *ntb, *otb; 462 tslp = &slaves[SLAVES]; 463 ntb = (union u_spcl *)tslp->tblock[1]; 464 465 /* 466 * Each of the N slaves should have requests that need to 467 * be replayed on the next tape. Use the extra slave buffers 468 * (slaves[SLAVES]) to construct request lists to be sent to 469 * each slave in turn. 470 */ 471 for (i = 0; i < SLAVES; i++) { 472 q = &tslp->req[1]; 473 otb = (union u_spcl *)slp->tblock; 474 475 /* 476 * For each request in the current slave, copy it to tslp. 477 */ 478 479 prev = NULL; 480 for (p = slp->req; p->count > 0; p += p->count) { 481 *q = *p; 482 if (p->dblk == 0) 483 *ntb++ = *otb++; /* copy the datablock also */ 484 prev = q; 485 q += q->count; 486 } 487 if (prev == NULL) 488 quit("rollforward: protocol botch"); 489 if (prev->dblk != 0) 490 prev->count -= 1; 491 else 492 ntb--; 493 q -= 1; 494 q->count = 0; 495 q = &tslp->req[0]; 496 if (i == 0) { 497 q->dblk = 0; 498 q->count = 1; 499 trecno = 0; 500 nextblock = tslp->tblock; 501 savedtapea = iswap32(spcl.c_tapea); 502 spcl.c_tapea = iswap32(slp->tapea); 503 startnewtape(0); 504 spcl.c_tapea = iswap32(savedtapea); 505 lastspclrec = savedtapea - 1; 506 } 507 size = (char *)ntb - (char *)q; 508 if (atomic_write(slp->fd, q, size) != size) { 509 perror(" DUMP: error writing command pipe"); 510 dumpabort(0); 511 } 512 slp->sent = 1; 513 if (++slp >= &slaves[SLAVES]) 514 slp = &slaves[0]; 515 516 q->count = 1; 517 518 if (prev->dblk != 0) { 519 /* 520 * If the last one was a disk block, make the 521 * first of this one be the last bit of that disk 522 * block... 523 */ 524 q->dblk = prev->dblk + 525 prev->count * (TP_BSIZE / DEV_BSIZE); 526 ntb = (union u_spcl *)tslp->tblock; 527 } else { 528 /* 529 * It wasn't a disk block. Copy the data to its 530 * new location in the buffer. 531 */ 532 q->dblk = 0; 533 *((union u_spcl *)tslp->tblock) = *ntb; 534 ntb = (union u_spcl *)tslp->tblock[1]; 535 } 536 } 537 slp->req[0] = *q; 538 nextblock = slp->tblock; 539 if (q->dblk == 0) 540 nextblock++; 541 trecno = 1; 542 543 /* 544 * Clear the first slaves' response. One hopes that it 545 * worked ok, otherwise the tape is much too short! 546 */ 547 if (slp->sent) { 548 if (atomic_read(slp->fd, &got, sizeof got) 549 != sizeof got) { 550 perror(" DUMP: error reading command pipe in master"); 551 dumpabort(0); 552 } 553 slp->sent = 0; 554 555 if (got != writesize) { 556 quit("EOT detected at start of the tape!\n"); 557 } 558 } 559 } 560 561 /* 562 * We implement taking and restoring checkpoints on the tape level. 563 * When each tape is opened, a new process is created by forking; this 564 * saves all of the necessary context in the parent. The child 565 * continues the dump; the parent waits around, saving the context. 566 * If the child returns X_REWRITE, then it had problems writing that tape; 567 * this causes the parent to fork again, duplicating the context, and 568 * everything continues as if nothing had happened. 569 */ 570 void 571 startnewtape(int top) 572 { 573 int parentpid; 574 int childpid; 575 int status; 576 int waitforpid; 577 char *p; 578 sig_t interrupt_save; 579 580 interrupt_save = signal(SIGINT, SIG_IGN); 581 parentpid = getpid(); 582 tapea_volume = iswap32(spcl.c_tapea); 583 (void)time(&tstart_volume); 584 585 restore_check_point: 586 (void)signal(SIGINT, interrupt_save); 587 /* 588 * All signals are inherited... 589 */ 590 childpid = fork(); 591 if (childpid < 0) { 592 msg("Context save fork fails in parent %d\n", parentpid); 593 Exit(X_ABORT); 594 } 595 if (childpid != 0) { 596 /* 597 * PARENT: 598 * save the context by waiting 599 * until the child doing all of the work returns. 600 * don't catch the interrupt 601 */ 602 signal(SIGINT, SIG_IGN); 603 signal(SIGINFO, SIG_IGN); /* only want child's stats */ 604 #ifdef TDEBUG 605 msg("Tape: %d; parent process: %d child process %d\n", 606 tapeno+1, parentpid, childpid); 607 #endif /* TDEBUG */ 608 while ((waitforpid = wait(&status)) != childpid) 609 msg("Parent %d waiting for child %d has another child %d return\n", 610 parentpid, childpid, waitforpid); 611 if (status & 0xFF) { 612 msg("Child %d returns LOB status %o\n", 613 childpid, status&0xFF); 614 } 615 status = (status >> 8) & 0xFF; 616 #ifdef TDEBUG 617 switch(status) { 618 case X_FINOK: 619 msg("Child %d finishes X_FINOK\n", childpid); 620 break; 621 case X_ABORT: 622 msg("Child %d finishes X_ABORT\n", childpid); 623 break; 624 case X_REWRITE: 625 msg("Child %d finishes X_REWRITE\n", childpid); 626 break; 627 default: 628 msg("Child %d finishes unknown %d\n", 629 childpid, status); 630 break; 631 } 632 #endif /* TDEBUG */ 633 switch(status) { 634 case X_FINOK: 635 Exit(X_FINOK); 636 case X_ABORT: 637 Exit(X_ABORT); 638 case X_REWRITE: 639 goto restore_check_point; 640 default: 641 msg("Bad return code from dump: %d\n", status); 642 Exit(X_ABORT); 643 } 644 /*NOTREACHED*/ 645 } else { /* we are the child; just continue */ 646 signal(SIGINFO, statussig); /* now want child's stats */ 647 #ifdef TDEBUG 648 sleep(4); /* allow time for parent's message to get out */ 649 msg("Child on Tape %d has parent %d, my pid = %d\n", 650 tapeno+1, parentpid, getpid()); 651 #endif /* TDEBUG */ 652 /* 653 * If we have a name like "/dev/rst0,/dev/rst1", 654 * use the name before the comma first, and save 655 * the remaining names for subsequent volumes. 656 */ 657 tapeno++; /* current tape sequence */ 658 if (nexttape || strchr(tape, ',')) { 659 if (nexttape && *nexttape) 660 tape = nexttape; 661 if ((p = strchr(tape, ',')) != NULL) { 662 *p = '\0'; 663 nexttape = p + 1; 664 } else 665 nexttape = NULL; 666 msg("Dumping volume %d on %s\n", tapeno, tape); 667 } 668 #ifdef RDUMP 669 while ((tapefd = (host ? rmtopen(tape, 2, 1) : 670 pipeout ? 1 : open(tape, O_WRONLY|O_CREAT, 0666))) < 0) 671 #else 672 while ((tapefd = (pipeout ? 1 : 673 open(tape, O_WRONLY|O_CREAT, 0666))) < 0) 674 #endif 675 { 676 msg("Cannot open output \"%s\".\n", tape); 677 if (!query("Do you want to retry the open?")) 678 dumpabort(0); 679 } 680 681 enslave(); /* Share open tape file descriptor with slaves */ 682 683 asize = 0; 684 blocksthisvol = 0; 685 if (top) 686 newtape++; /* new tape signal */ 687 spcl.c_count = iswap32(slp->count); 688 /* 689 * measure firstrec in TP_BSIZE units since restore doesn't 690 * know the correct ntrec value... 691 */ 692 spcl.c_firstrec = iswap32(slp->firstrec); 693 spcl.c_volume = iswap32(iswap32(spcl.c_volume) + 1); 694 spcl.c_type = iswap32(TS_TAPE); 695 if (!is_ufs2) 696 spcl.c_flags = iswap32(iswap32(spcl.c_flags) 697 | DR_NEWHEADER); 698 writeheader((ino_t)slp->inode); 699 if (!is_ufs2) 700 spcl.c_flags = iswap32(iswap32(spcl.c_flags) & 701 ~ DR_NEWHEADER); 702 msg("Volume %d started at: %s", tapeno, ctime(&tstart_volume)); 703 if (tapeno > 1) 704 msg("Volume %d begins with blocks from inode %d\n", 705 tapeno, slp->inode); 706 } 707 } 708 709 void 710 dumpabort(int signo __unused) 711 { 712 713 if (master != 0 && master != getpid()) 714 /* Signals master to call dumpabort */ 715 (void) kill(master, SIGTERM); 716 else { 717 #ifdef DUMP_LFS 718 lfs_wrap_go(); 719 #endif 720 killall(); 721 msg("The ENTIRE dump is aborted.\n"); 722 } 723 #ifdef RDUMP 724 rmtclose(); 725 #endif 726 Exit(X_ABORT); 727 } 728 729 void 730 Exit(int status) 731 { 732 733 #ifdef TDEBUG 734 msg("pid = %d exits with status %d\n", getpid(), status); 735 #endif /* TDEBUG */ 736 exit(status); 737 } 738 739 /* 740 * proceed - handler for SIGUSR2, used to synchronize IO between the slaves. 741 */ 742 static void 743 proceed(int signo __unused) 744 { 745 caught++; 746 } 747 748 void 749 enslave(void) 750 { 751 int cmd[2]; 752 int i, j; 753 754 master = getpid(); 755 756 signal(SIGTERM, dumpabort); /* Slave sends SIGTERM on dumpabort() */ 757 signal(SIGPIPE, sigpipe); 758 signal(SIGUSR1, tperror); /* Slave sends SIGUSR1 on tape errors */ 759 signal(SIGUSR2, proceed); /* Slave sends SIGUSR2 to next slave */ 760 761 for (i = 0; i < SLAVES; i++) { 762 if (i == slp - &slaves[0]) { 763 caught = 1; 764 } else { 765 caught = 0; 766 } 767 768 if (socketpair(AF_LOCAL, SOCK_STREAM, 0, cmd) < 0 || 769 (slaves[i].pid = fork()) < 0) 770 quit("too many slaves, %d (recompile smaller): %s\n", 771 i, strerror(errno)); 772 773 slaves[i].fd = cmd[1]; 774 slaves[i].sent = 0; 775 if (slaves[i].pid == 0) { /* Slave starts up here */ 776 for (j = 0; j <= i; j++) 777 (void) close(slaves[j].fd); 778 signal(SIGINT, SIG_IGN); /* Master handles this */ 779 signal(SIGINFO, SIG_IGN); 780 doslave(cmd[0], i); 781 Exit(X_FINOK); 782 } 783 } 784 785 for (i = 0; i < SLAVES; i++) 786 (void) atomic_write(slaves[i].fd, 787 &slaves[(i + 1) % SLAVES].pid, 788 sizeof slaves[0].pid); 789 790 master = 0; 791 } 792 793 void 794 killall(void) 795 { 796 int i; 797 798 for (i = 0; i < SLAVES; i++) 799 if (slaves[i].pid > 0) { 800 (void) kill(slaves[i].pid, SIGKILL); 801 slaves[i].sent = 0; 802 } 803 } 804 805 /* 806 * Synchronization - each process has a lockfile, and shares file 807 * descriptors to the following process's lockfile. When our write 808 * completes, we release our lock on the following process's lock- 809 * file, allowing the following process to lock it and proceed. We 810 * get the lock back for the next cycle by swapping descriptors. 811 */ 812 static void 813 doslave(int cmd, int slave_number __unused) 814 { 815 int nread, nextslave, size, wrote, eot_count, werror; 816 sigset_t nsigset, osigset; 817 818 wrote = 0; 819 /* 820 * Need our own seek pointer. 821 */ 822 (void) close(diskfd); 823 if ((diskfd = open(disk_dev, O_RDONLY)) < 0) 824 quit("slave couldn't reopen disk: %s\n", strerror(errno)); 825 826 /* 827 * Need the pid of the next slave in the loop... 828 */ 829 if ((nread = atomic_read(cmd, &nextslave, sizeof nextslave)) 830 != sizeof nextslave) { 831 quit("master/slave protocol botched - didn't get pid of next slave.\n"); 832 } 833 834 /* 835 * Get list of blocks to dump, read the blocks into tape buffer 836 */ 837 while ((nread = atomic_read(cmd, slp->req, reqsiz)) == reqsiz) { 838 struct req *p = slp->req; 839 840 for (trecno = 0; trecno < ntrec; 841 trecno += p->count, p += p->count) { 842 if (p->dblk) { 843 bread(p->dblk, slp->tblock[trecno], 844 p->count * TP_BSIZE); 845 } else { 846 if (p->count != 1 || atomic_read(cmd, 847 slp->tblock[trecno], 848 TP_BSIZE) != TP_BSIZE) 849 quit("master/slave protocol botched.\n"); 850 } 851 } 852 853 sigemptyset(&nsigset); 854 sigaddset(&nsigset, SIGUSR2); 855 sigprocmask(SIG_BLOCK, &nsigset, &osigset); 856 while (!caught) 857 sigsuspend(&osigset); 858 caught = 0; 859 sigprocmask(SIG_SETMASK, &osigset, NULL); 860 861 /* Try to write the data... */ 862 eot_count = 0; 863 size = 0; 864 werror = 0; 865 866 while (eot_count < 10 && size < writesize) { 867 #ifdef RDUMP 868 if (host) 869 wrote = rmtwrite(slp->tblock[0]+size, 870 writesize-size); 871 else 872 #endif 873 wrote = write(tapefd, slp->tblock[0]+size, 874 writesize-size); 875 werror = errno; 876 #ifdef WRITEDEBUG 877 fprintf(stderr, "slave %d wrote %d werror %d\n", 878 slave_number, wrote, werror); 879 #endif 880 if (wrote < 0) 881 break; 882 if (wrote == 0) 883 eot_count++; 884 size += wrote; 885 } 886 887 #ifdef WRITEDEBUG 888 if (size != writesize) 889 fprintf(stderr, 890 "slave %d only wrote %d out of %d bytes and gave up.\n", 891 slave_number, size, writesize); 892 #endif 893 894 /* 895 * Handle ENOSPC as an EOT condition. 896 */ 897 if (wrote < 0 && werror == ENOSPC) { 898 wrote = 0; 899 eot_count++; 900 } 901 902 if (eot_count > 0) 903 size = 0; 904 905 if (wrote < 0) { 906 (void) kill(master, SIGUSR1); 907 sigemptyset(&nsigset); 908 for (;;) 909 sigsuspend(&nsigset); 910 } else { 911 /* 912 * pass size of write back to master 913 * (for EOT handling) 914 */ 915 (void) atomic_write(cmd, &size, sizeof size); 916 } 917 918 /* 919 * If partial write, don't want next slave to go. 920 * Also jolts him awake. 921 */ 922 (void) kill(nextslave, SIGUSR2); 923 } 924 printcachestats(); 925 if (nread != 0) 926 quit("error reading command pipe: %s\n", strerror(errno)); 927 } 928 929 /* 930 * Since a read from a pipe may not return all we asked for, 931 * loop until the count is satisfied (or error). 932 */ 933 static ssize_t 934 atomic_read(int fd, void *buf, int count) 935 { 936 ssize_t got, need = count; 937 938 while ((got = read(fd, buf, need)) > 0 && (need -= got) > 0) 939 buf = (char *)buf + got; 940 return (got < 0 ? got : count - need); 941 } 942 943 /* 944 * Since a write may not write all we ask if we get a signal, 945 * loop until the count is satisfied (or error). 946 */ 947 static ssize_t 948 atomic_write(int fd, const void *buf, int count) 949 { 950 ssize_t got, need = count; 951 952 while ((got = write(fd, buf, need)) > 0 && (need -= got) > 0) 953 buf = (const char *)buf + got; 954 return (got < 0 ? got : count - need); 955 } 956