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