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