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