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