tape.c revision 217769
1251881Speter/*- 2251881Speter * Copyright (c) 1980, 1991, 1993 3251881Speter * The Regents of the University of California. All rights reserved. 4251881Speter * 5251881Speter * Redistribution and use in source and binary forms, with or without 6251881Speter * modification, are permitted provided that the following conditions 7251881Speter * are met: 8251881Speter * 1. Redistributions of source code must retain the above copyright 9251881Speter * notice, this list of conditions and the following disclaimer. 10251881Speter * 2. Redistributions in binary form must reproduce the above copyright 11251881Speter * notice, this list of conditions and the following disclaimer in the 12251881Speter * documentation and/or other materials provided with the distribution. 13251881Speter * 4. Neither the name of the University nor the names of its contributors 14251881Speter * may be used to endorse or promote products derived from this software 15251881Speter * without specific prior written permission. 16251881Speter * 17251881Speter * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18251881Speter * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19251881Speter * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20251881Speter * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21251881Speter * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22251881Speter * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23251881Speter * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24251881Speter * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25251881Speter * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26251881Speter * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27251881Speter * SUCH DAMAGE. 28251881Speter */ 29251881Speter 30251881Speter#ifndef lint 31251881Speter#if 0 32251881Speterstatic char sccsid[] = "@(#)tape.c 8.4 (Berkeley) 5/1/95"; 33251881Speter#endif 34251881Speterstatic const char rcsid[] = 35251881Speter "$FreeBSD: head/sbin/dump/tape.c 217769 2011-01-24 06:17:05Z mckusick $"; 36251881Speter#endif /* not lint */ 37251881Speter 38251881Speter#include <sys/param.h> 39251881Speter#include <sys/socket.h> 40251881Speter#include <sys/wait.h> 41251881Speter#include <sys/stat.h> 42251881Speter 43251881Speter#include <ufs/ufs/dinode.h> 44251881Speter#include <ufs/ffs/fs.h> 45251881Speter 46251881Speter#include <protocols/dumprestore.h> 47251881Speter 48251881Speter#include <errno.h> 49251881Speter#include <fcntl.h> 50251881Speter#include <limits.h> 51251881Speter#include <setjmp.h> 52251881Speter#include <signal.h> 53251881Speter#include <stdio.h> 54251881Speter#include <stdlib.h> 55251881Speter#include <string.h> 56251881Speter#include <time.h> 57251881Speter#include <unistd.h> 58251881Speter 59251881Speter#include "dump.h" 60251881Speter 61251881Speterint writesize; /* size of malloc()ed buffer for tape */ 62251881Speterint64_t lastspclrec = -1; /* tape block number of last written header */ 63251881Speterint trecno = 0; /* next record to write in current block */ 64251881Speterextern long blocksperfile; /* number of blocks per output file */ 65251881Speterlong blocksthisvol; /* number of blocks on current output file */ 66251881Speterextern int ntrec; /* blocking factor on tape */ 67251881Speterextern int cartridge; 68251881Speterextern char *host; 69251881Speterchar *nexttape; 70251881SpeterFILE *popenfp = NULL; 71251881Speter 72251881Speterstatic int atomic(ssize_t (*)(), int, char *, int); 73251881Speterstatic void doslave(int, int); 74251881Speterstatic void enslave(void); 75251881Speterstatic void flushtape(void); 76251881Speterstatic void killall(void); 77251881Speterstatic void rollforward(void); 78251881Speter 79251881Speter/* 80251881Speter * Concurrent dump mods (Caltech) - disk block reading and tape writing 81251881Speter * are exported to several slave processes. While one slave writes the 82251881Speter * tape, the others read disk blocks; they pass control of the tape in 83251881Speter * a ring via signals. The parent process traverses the file system and 84251881Speter * sends writeheader()'s and lists of daddr's to the slaves via pipes. 85251881Speter * The following structure defines the instruction packets sent to slaves. 86251881Speter */ 87251881Speterstruct req { 88251881Speter ufs2_daddr_t dblk; 89251881Speter int count; 90251881Speter}; 91251881Speterint reqsiz; 92251881Speter 93251881Speter#define SLAVES 3 /* 1 slave writing, 1 reading, 1 for slack */ 94251881Speterstruct slave { 95251881Speter int64_t tapea; /* header number at start of this chunk */ 96251881Speter int64_t firstrec; /* record number of this block */ 97251881Speter int count; /* count to next header (used for TS_TAPE */ 98251881Speter /* after EOT) */ 99251881Speter int inode; /* inode that we are currently dealing with */ 100251881Speter int fd; /* FD for this slave */ 101251881Speter int pid; /* PID for this slave */ 102251881Speter int sent; /* 1 == we've sent this slave requests */ 103251881Speter char (*tblock)[TP_BSIZE]; /* buffer for data blocks */ 104251881Speter struct req *req; /* buffer for requests */ 105251881Speter} slaves[SLAVES+1]; 106251881Speterstruct slave *slp; 107251881Speter 108251881Speterchar (*nextblock)[TP_BSIZE]; 109251881Speter 110251881Speterint master; /* pid of master, for sending error signals */ 111251881Speterint tenths; /* length of tape used per block written */ 112251881Speterstatic volatile sig_atomic_t caught; /* have we caught the signal to proceed? */ 113251881Speterstatic volatile sig_atomic_t ready; /* reached the lock point without having */ 114251881Speter /* received the SIGUSR2 signal from the prev slave? */ 115251881Speterstatic jmp_buf jmpbuf; /* where to jump to if we are ready when the */ 116251881Speter /* SIGUSR2 arrives from the previous slave */ 117251881Speter 118251881Speterint 119251881Speteralloctape(void) 120251881Speter{ 121251881Speter int pgoff = getpagesize() - 1; 122251881Speter char *buf; 123251881Speter int i; 124251881Speter 125251881Speter writesize = ntrec * TP_BSIZE; 126251881Speter reqsiz = (ntrec + 1) * sizeof(struct req); 127251881Speter /* 128251881Speter * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode 129251881Speter * (see DEC TU80 User's Guide). The shorter gaps of 6250-bpi require 130251881Speter * repositioning after stopping, i.e, streaming mode, where the gap is 131251881Speter * variable, 0.30" to 0.45". The gap is maximal when the tape stops. 132251881Speter */ 133251881Speter if (blocksperfile == 0 && !unlimited) 134251881Speter tenths = writesize / density + 135251881Speter (cartridge ? 16 : density == 625 ? 5 : 8); 136251881Speter /* 137251881Speter * Allocate tape buffer contiguous with the array of instruction 138251881Speter * packets, so flushtape() can write them together with one write(). 139251881Speter * Align tape buffer on page boundary to speed up tape write(). 140251881Speter */ 141251881Speter for (i = 0; i <= SLAVES; i++) { 142251881Speter buf = (char *) 143251881Speter malloc((unsigned)(reqsiz + writesize + pgoff + TP_BSIZE)); 144251881Speter if (buf == NULL) 145251881Speter return(0); 146251881Speter slaves[i].tblock = (char (*)[TP_BSIZE]) 147251881Speter (((long)&buf[ntrec + 1] + pgoff) &~ pgoff); 148251881Speter slaves[i].req = (struct req *)slaves[i].tblock - ntrec - 1; 149251881Speter } 150251881Speter slp = &slaves[0]; 151251881Speter slp->count = 1; 152251881Speter slp->tapea = 0; 153251881Speter slp->firstrec = 0; 154251881Speter nextblock = slp->tblock; 155251881Speter return(1); 156251881Speter} 157251881Speter 158251881Spetervoid 159251881Speterwriterec(char *dp, int isspcl) 160251881Speter{ 161251881Speter 162251881Speter slp->req[trecno].dblk = (ufs2_daddr_t)0; 163251881Speter slp->req[trecno].count = 1; 164251881Speter /* Can't do a structure assignment due to alignment problems */ 165251881Speter bcopy(dp, *(nextblock)++, sizeof (union u_spcl)); 166251881Speter if (isspcl) 167251881Speter lastspclrec = spcl.c_tapea; 168251881Speter trecno++; 169251881Speter spcl.c_tapea++; 170251881Speter if (trecno >= ntrec) 171251881Speter flushtape(); 172251881Speter} 173251881Speter 174251881Spetervoid 175251881Speterdumpblock(ufs2_daddr_t blkno, int size) 176251881Speter{ 177251881Speter int avail, tpblks; 178251881Speter ufs2_daddr_t dblkno; 179251881Speter 180251881Speter dblkno = fsbtodb(sblock, blkno); 181251881Speter tpblks = size >> tp_bshift; 182251881Speter while ((avail = MIN(tpblks, ntrec - trecno)) > 0) { 183251881Speter slp->req[trecno].dblk = dblkno; 184251881Speter slp->req[trecno].count = avail; 185251881Speter trecno += avail; 186251881Speter spcl.c_tapea += avail; 187251881Speter if (trecno >= ntrec) 188251881Speter flushtape(); 189251881Speter dblkno += avail << (tp_bshift - dev_bshift); 190251881Speter tpblks -= avail; 191251881Speter } 192251881Speter} 193251881Speter 194251881Speterint nogripe = 0; 195251881Speter 196251881Spetervoid 197251881Spetertperror(int signo __unused) 198251881Speter{ 199251881Speter 200251881Speter if (pipeout) { 201251881Speter msg("write error on %s\n", tape); 202251881Speter quit("Cannot recover\n"); 203251881Speter /* NOTREACHED */ 204251881Speter } 205251881Speter msg("write error %ld blocks into volume %d\n", blocksthisvol, tapeno); 206251881Speter broadcast("DUMP WRITE ERROR!\n"); 207251881Speter if (!query("Do you want to restart?")) 208251881Speter dumpabort(0); 209251881Speter msg("Closing this volume. Prepare to restart with new media;\n"); 210251881Speter msg("this dump volume will be rewritten.\n"); 211251881Speter killall(); 212251881Speter nogripe = 1; 213251881Speter close_rewind(); 214251881Speter Exit(X_REWRITE); 215251881Speter} 216251881Speter 217251881Spetervoid 218251881Spetersigpipe(int signo __unused) 219251881Speter{ 220251881Speter 221251881Speter quit("Broken pipe\n"); 222251881Speter} 223251881Speter 224251881Speterstatic void 225251881Speterflushtape(void) 226251881Speter{ 227251881Speter int i, blks, got; 228251881Speter int64_t lastfirstrec; 229251881Speter 230251881Speter int siz = (char *)nextblock - (char *)slp->req; 231251881Speter 232251881Speter slp->req[trecno].count = 0; /* Sentinel */ 233251881Speter 234251881Speter if (atomic(write, slp->fd, (char *)slp->req, siz) != siz) 235251881Speter quit("error writing command pipe: %s\n", strerror(errno)); 236251881Speter slp->sent = 1; /* we sent a request, read the response later */ 237251881Speter 238251881Speter lastfirstrec = slp->firstrec; 239251881Speter 240251881Speter if (++slp >= &slaves[SLAVES]) 241251881Speter slp = &slaves[0]; 242251881Speter 243251881Speter /* Read results back from next slave */ 244251881Speter if (slp->sent) { 245251881Speter if (atomic(read, slp->fd, (char *)&got, sizeof got) 246251881Speter != sizeof got) { 247251881Speter perror(" DUMP: error reading command pipe in master"); 248251881Speter dumpabort(0); 249251881Speter } 250251881Speter slp->sent = 0; 251251881Speter 252251881Speter /* Check for end of tape */ 253251881Speter if (got < writesize) { 254251881Speter msg("End of tape detected\n"); 255251881Speter 256251881Speter /* 257251881Speter * Drain the results, don't care what the values were. 258251881Speter * If we read them here then trewind won't... 259251881Speter */ 260251881Speter for (i = 0; i < SLAVES; i++) { 261251881Speter if (slaves[i].sent) { 262251881Speter if (atomic(read, slaves[i].fd, 263251881Speter (char *)&got, sizeof got) 264251881Speter != sizeof got) { 265251881Speter perror(" DUMP: error reading command pipe in master"); 266251881Speter dumpabort(0); 267251881Speter } 268251881Speter slaves[i].sent = 0; 269251881Speter } 270251881Speter } 271251881Speter 272251881Speter close_rewind(); 273251881Speter rollforward(); 274251881Speter return; 275251881Speter } 276251881Speter } 277251881Speter 278251881Speter blks = 0; 279251881Speter if (spcl.c_type != TS_END) { 280251881Speter for (i = 0; i < spcl.c_count; i++) 281251881Speter if (spcl.c_addr[i] != 0) 282251881Speter blks++; 283251881Speter } 284251881Speter slp->count = lastspclrec + blks + 1 - spcl.c_tapea; 285251881Speter slp->tapea = spcl.c_tapea; 286251881Speter slp->firstrec = lastfirstrec + ntrec; 287251881Speter slp->inode = curino; 288251881Speter nextblock = slp->tblock; 289251881Speter trecno = 0; 290251881Speter asize += tenths; 291251881Speter blockswritten += ntrec; 292251881Speter blocksthisvol += ntrec; 293251881Speter if (!pipeout && !unlimited && (blocksperfile ? 294251881Speter (blocksthisvol >= blocksperfile) : (asize > tsize))) { 295251881Speter close_rewind(); 296251881Speter startnewtape(0); 297251881Speter } 298251881Speter timeest(); 299251881Speter} 300251881Speter 301251881Spetervoid 302251881Spetertrewind(void) 303251881Speter{ 304251881Speter struct stat sb; 305251881Speter int f; 306251881Speter int got; 307251881Speter 308251881Speter for (f = 0; f < SLAVES; f++) { 309251881Speter /* 310251881Speter * Drain the results, but unlike EOT we DO (or should) care 311251881Speter * what the return values were, since if we detect EOT after 312251881Speter * we think we've written the last blocks to the tape anyway, 313251881Speter * we have to replay those blocks with rollforward. 314251881Speter * 315251881Speter * fixme: punt for now. 316251881Speter */ 317251881Speter if (slaves[f].sent) { 318251881Speter if (atomic(read, slaves[f].fd, (char *)&got, sizeof got) 319251881Speter != sizeof got) { 320251881Speter perror(" DUMP: error reading command pipe in master"); 321251881Speter dumpabort(0); 322251881Speter } 323251881Speter slaves[f].sent = 0; 324251881Speter if (got != writesize) { 325251881Speter msg("EOT detected in last 2 tape records!\n"); 326251881Speter msg("Use a longer tape, decrease the size estimate\n"); 327251881Speter quit("or use no size estimate at all.\n"); 328251881Speter } 329251881Speter } 330251881Speter (void) close(slaves[f].fd); 331251881Speter } 332251881Speter while (wait((int *)NULL) >= 0) /* wait for any signals from slaves */ 333251881Speter /* void */; 334251881Speter 335251881Speter if (pipeout) 336251881Speter return; 337251881Speter 338251881Speter msg("Closing %s\n", tape); 339251881Speter 340251881Speter if (popenout) { 341251881Speter tapefd = -1; 342251881Speter (void)pclose(popenfp); 343251881Speter popenfp = NULL; 344251881Speter return; 345251881Speter } 346251881Speter#ifdef RDUMP 347251881Speter if (host) { 348251881Speter rmtclose(); 349251881Speter while (rmtopen(tape, 0) < 0) 350251881Speter sleep(10); 351251881Speter rmtclose(); 352251881Speter return; 353251881Speter } 354251881Speter#endif 355251881Speter if (fstat(tapefd, &sb) == 0 && S_ISFIFO(sb.st_mode)) { 356251881Speter (void)close(tapefd); 357251881Speter return; 358251881Speter } 359251881Speter (void) close(tapefd); 360251881Speter while ((f = open(tape, 0)) < 0) 361251881Speter sleep (10); 362251881Speter (void) close(f); 363251881Speter} 364251881Speter 365251881Spetervoid 366251881Speterclose_rewind() 367251881Speter{ 368251881Speter time_t tstart_changevol, tend_changevol; 369251881Speter 370251881Speter trewind(); 371251881Speter if (nexttape) 372251881Speter return; 373251881Speter (void)time((time_t *)&(tstart_changevol)); 374251881Speter if (!nogripe) { 375251881Speter msg("Change Volumes: Mount volume #%d\n", tapeno+1); 376251881Speter broadcast("CHANGE DUMP VOLUMES!\a\a\n"); 377251881Speter } 378251881Speter while (!query("Is the new volume mounted and ready to go?")) 379251881Speter if (query("Do you want to abort?")) { 380251881Speter dumpabort(0); 381251881Speter /*NOTREACHED*/ 382251881Speter } 383251881Speter (void)time((time_t *)&(tend_changevol)); 384251881Speter if ((tstart_changevol != (time_t)-1) && (tend_changevol != (time_t)-1)) 385251881Speter tstart_writing += (tend_changevol - tstart_changevol); 386251881Speter} 387251881Speter 388251881Spetervoid 389251881Speterrollforward(void) 390251881Speter{ 391251881Speter struct req *p, *q, *prev; 392251881Speter struct slave *tslp; 393251881Speter int i, size, got; 394251881Speter int64_t savedtapea; 395251881Speter union u_spcl *ntb, *otb; 396251881Speter tslp = &slaves[SLAVES]; 397251881Speter ntb = (union u_spcl *)tslp->tblock[1]; 398251881Speter 399251881Speter /* 400251881Speter * Each of the N slaves should have requests that need to 401251881Speter * be replayed on the next tape. Use the extra slave buffers 402251881Speter * (slaves[SLAVES]) to construct request lists to be sent to 403251881Speter * each slave in turn. 404251881Speter */ 405251881Speter for (i = 0; i < SLAVES; i++) { 406251881Speter q = &tslp->req[1]; 407251881Speter otb = (union u_spcl *)slp->tblock; 408251881Speter 409251881Speter /* 410251881Speter * For each request in the current slave, copy it to tslp. 411251881Speter */ 412251881Speter 413251881Speter prev = NULL; 414251881Speter for (p = slp->req; p->count > 0; p += p->count) { 415251881Speter *q = *p; 416251881Speter if (p->dblk == 0) 417251881Speter *ntb++ = *otb++; /* copy the datablock also */ 418251881Speter prev = q; 419251881Speter q += q->count; 420 } 421 if (prev == NULL) 422 quit("rollforward: protocol botch"); 423 if (prev->dblk != 0) 424 prev->count -= 1; 425 else 426 ntb--; 427 q -= 1; 428 q->count = 0; 429 q = &tslp->req[0]; 430 if (i == 0) { 431 q->dblk = 0; 432 q->count = 1; 433 trecno = 0; 434 nextblock = tslp->tblock; 435 savedtapea = spcl.c_tapea; 436 spcl.c_tapea = slp->tapea; 437 startnewtape(0); 438 spcl.c_tapea = savedtapea; 439 lastspclrec = savedtapea - 1; 440 } 441 size = (char *)ntb - (char *)q; 442 if (atomic(write, slp->fd, (char *)q, size) != size) { 443 perror(" DUMP: error writing command pipe"); 444 dumpabort(0); 445 } 446 slp->sent = 1; 447 if (++slp >= &slaves[SLAVES]) 448 slp = &slaves[0]; 449 450 q->count = 1; 451 452 if (prev->dblk != 0) { 453 /* 454 * If the last one was a disk block, make the 455 * first of this one be the last bit of that disk 456 * block... 457 */ 458 q->dblk = prev->dblk + 459 prev->count * (TP_BSIZE / DEV_BSIZE); 460 ntb = (union u_spcl *)tslp->tblock; 461 } else { 462 /* 463 * It wasn't a disk block. Copy the data to its 464 * new location in the buffer. 465 */ 466 q->dblk = 0; 467 *((union u_spcl *)tslp->tblock) = *ntb; 468 ntb = (union u_spcl *)tslp->tblock[1]; 469 } 470 } 471 slp->req[0] = *q; 472 nextblock = slp->tblock; 473 if (q->dblk == 0) 474 nextblock++; 475 trecno = 1; 476 477 /* 478 * Clear the first slaves' response. One hopes that it 479 * worked ok, otherwise the tape is much too short! 480 */ 481 if (slp->sent) { 482 if (atomic(read, slp->fd, (char *)&got, sizeof got) 483 != sizeof got) { 484 perror(" DUMP: error reading command pipe in master"); 485 dumpabort(0); 486 } 487 slp->sent = 0; 488 489 if (got != writesize) { 490 quit("EOT detected at start of the tape!\n"); 491 } 492 } 493} 494 495/* 496 * We implement taking and restoring checkpoints on the tape level. 497 * When each tape is opened, a new process is created by forking; this 498 * saves all of the necessary context in the parent. The child 499 * continues the dump; the parent waits around, saving the context. 500 * If the child returns X_REWRITE, then it had problems writing that tape; 501 * this causes the parent to fork again, duplicating the context, and 502 * everything continues as if nothing had happened. 503 */ 504void 505startnewtape(int top) 506{ 507 int parentpid; 508 int childpid; 509 int status; 510 char *p; 511 sig_t interrupt_save; 512 513 interrupt_save = signal(SIGINT, SIG_IGN); 514 parentpid = getpid(); 515 516restore_check_point: 517 (void)signal(SIGINT, interrupt_save); 518 /* 519 * All signals are inherited... 520 */ 521 setproctitle(NULL); /* Restore the proctitle. */ 522 childpid = fork(); 523 if (childpid < 0) { 524 msg("Context save fork fails in parent %d\n", parentpid); 525 Exit(X_ABORT); 526 } 527 if (childpid != 0) { 528 /* 529 * PARENT: 530 * save the context by waiting 531 * until the child doing all of the work returns. 532 * don't catch the interrupt 533 */ 534 signal(SIGINT, SIG_IGN); 535#ifdef TDEBUG 536 msg("Tape: %d; parent process: %d child process %d\n", 537 tapeno+1, parentpid, childpid); 538#endif /* TDEBUG */ 539 if (waitpid(childpid, &status, 0) == -1) 540 msg("Waiting for child %d: %s\n", childpid, 541 strerror(errno)); 542 if (status & 0xFF) { 543 msg("Child %d returns LOB status %o\n", 544 childpid, status&0xFF); 545 } 546 status = (status >> 8) & 0xFF; 547#ifdef TDEBUG 548 switch(status) { 549 case X_FINOK: 550 msg("Child %d finishes X_FINOK\n", childpid); 551 break; 552 case X_ABORT: 553 msg("Child %d finishes X_ABORT\n", childpid); 554 break; 555 case X_REWRITE: 556 msg("Child %d finishes X_REWRITE\n", childpid); 557 break; 558 default: 559 msg("Child %d finishes unknown %d\n", 560 childpid, status); 561 break; 562 } 563#endif /* TDEBUG */ 564 switch(status) { 565 case X_FINOK: 566 Exit(X_FINOK); 567 case X_ABORT: 568 Exit(X_ABORT); 569 case X_REWRITE: 570 goto restore_check_point; 571 default: 572 msg("Bad return code from dump: %d\n", status); 573 Exit(X_ABORT); 574 } 575 /*NOTREACHED*/ 576 } else { /* we are the child; just continue */ 577#ifdef TDEBUG 578 sleep(4); /* allow time for parent's message to get out */ 579 msg("Child on Tape %d has parent %d, my pid = %d\n", 580 tapeno+1, parentpid, getpid()); 581#endif /* TDEBUG */ 582 /* 583 * If we have a name like "/dev/rmt0,/dev/rmt1", 584 * use the name before the comma first, and save 585 * the remaining names for subsequent volumes. 586 */ 587 tapeno++; /* current tape sequence */ 588 if (nexttape || strchr(tape, ',')) { 589 if (nexttape && *nexttape) 590 tape = nexttape; 591 if ((p = strchr(tape, ',')) != NULL) { 592 *p = '\0'; 593 nexttape = p + 1; 594 } else 595 nexttape = NULL; 596 msg("Dumping volume %d on %s\n", tapeno, tape); 597 } 598 if (pipeout) { 599 tapefd = STDOUT_FILENO; 600 } else if (popenout) { 601 char volno[sizeof("2147483647")]; 602 603 (void)sprintf(volno, "%d", spcl.c_volume + 1); 604 if (setenv("DUMP_VOLUME", volno, 1) == -1) { 605 msg("Cannot set $DUMP_VOLUME.\n"); 606 dumpabort(0); 607 } 608 popenfp = popen(popenout, "w"); 609 if (popenfp == NULL) { 610 msg("Cannot open output pipeline \"%s\".\n", 611 popenout); 612 dumpabort(0); 613 } 614 tapefd = fileno(popenfp); 615 } else { 616#ifdef RDUMP 617 while ((tapefd = (host ? rmtopen(tape, 2) : 618 open(tape, O_WRONLY|O_CREAT, 0666))) < 0) 619#else 620 while ((tapefd = 621 open(tape, O_WRONLY|O_CREAT, 0666)) < 0) 622#endif 623 { 624 msg("Cannot open output \"%s\".\n", tape); 625 if (!query("Do you want to retry the open?")) 626 dumpabort(0); 627 } 628 } 629 630 enslave(); /* Share open tape file descriptor with slaves */ 631 if (popenout) 632 close(tapefd); /* Give up our copy of it. */ 633 signal(SIGINFO, infosch); 634 635 asize = 0; 636 blocksthisvol = 0; 637 if (top) 638 newtape++; /* new tape signal */ 639 spcl.c_count = slp->count; 640 /* 641 * measure firstrec in TP_BSIZE units since restore doesn't 642 * know the correct ntrec value... 643 */ 644 spcl.c_firstrec = slp->firstrec; 645 spcl.c_volume++; 646 spcl.c_type = TS_TAPE; 647 writeheader((ino_t)slp->inode); 648 if (tapeno > 1) 649 msg("Volume %d begins with blocks from inode %d\n", 650 tapeno, slp->inode); 651 } 652} 653 654void 655dumpabort(int signo __unused) 656{ 657 658 if (master != 0 && master != getpid()) 659 /* Signals master to call dumpabort */ 660 (void) kill(master, SIGTERM); 661 else { 662 killall(); 663 msg("The ENTIRE dump is aborted.\n"); 664 } 665#ifdef RDUMP 666 rmtclose(); 667#endif 668 Exit(X_ABORT); 669} 670 671void 672Exit(status) 673 int status; 674{ 675 676#ifdef TDEBUG 677 msg("pid = %d exits with status %d\n", getpid(), status); 678#endif /* TDEBUG */ 679 exit(status); 680} 681 682/* 683 * proceed - handler for SIGUSR2, used to synchronize IO between the slaves. 684 */ 685void 686proceed(int signo __unused) 687{ 688 689 if (ready) 690 longjmp(jmpbuf, 1); 691 caught++; 692} 693 694void 695enslave(void) 696{ 697 int cmd[2]; 698 int i, j; 699 700 master = getpid(); 701 702 signal(SIGTERM, dumpabort); /* Slave sends SIGTERM on dumpabort() */ 703 signal(SIGPIPE, sigpipe); 704 signal(SIGUSR1, tperror); /* Slave sends SIGUSR1 on tape errors */ 705 signal(SIGUSR2, proceed); /* Slave sends SIGUSR2 to next slave */ 706 707 for (i = 0; i < SLAVES; i++) { 708 if (i == slp - &slaves[0]) { 709 caught = 1; 710 } else { 711 caught = 0; 712 } 713 714 if (socketpair(AF_UNIX, SOCK_STREAM, 0, cmd) < 0 || 715 (slaves[i].pid = fork()) < 0) 716 quit("too many slaves, %d (recompile smaller): %s\n", 717 i, strerror(errno)); 718 719 slaves[i].fd = cmd[1]; 720 slaves[i].sent = 0; 721 if (slaves[i].pid == 0) { /* Slave starts up here */ 722 for (j = 0; j <= i; j++) 723 (void) close(slaves[j].fd); 724 signal(SIGINT, SIG_IGN); /* Master handles this */ 725 doslave(cmd[0], i); 726 Exit(X_FINOK); 727 } 728 } 729 730 for (i = 0; i < SLAVES; i++) 731 (void) atomic(write, slaves[i].fd, 732 (char *) &slaves[(i + 1) % SLAVES].pid, 733 sizeof slaves[0].pid); 734 735 master = 0; 736} 737 738void 739killall(void) 740{ 741 int i; 742 743 for (i = 0; i < SLAVES; i++) 744 if (slaves[i].pid > 0) { 745 (void) kill(slaves[i].pid, SIGKILL); 746 slaves[i].sent = 0; 747 } 748} 749 750/* 751 * Synchronization - each process has a lockfile, and shares file 752 * descriptors to the following process's lockfile. When our write 753 * completes, we release our lock on the following process's lock- 754 * file, allowing the following process to lock it and proceed. We 755 * get the lock back for the next cycle by swapping descriptors. 756 */ 757static void 758doslave(int cmd, int slave_number) 759{ 760 int nread; 761 int nextslave, size, wrote, eot_count; 762 763 /* 764 * Need our own seek pointer. 765 */ 766 (void) close(diskfd); 767 if ((diskfd = open(disk, O_RDONLY)) < 0) 768 quit("slave couldn't reopen disk: %s\n", strerror(errno)); 769 770 /* 771 * Need the pid of the next slave in the loop... 772 */ 773 if ((nread = atomic(read, cmd, (char *)&nextslave, sizeof nextslave)) 774 != sizeof nextslave) { 775 quit("master/slave protocol botched - didn't get pid of next slave.\n"); 776 } 777 778 /* 779 * Get list of blocks to dump, read the blocks into tape buffer 780 */ 781 while ((nread = atomic(read, cmd, (char *)slp->req, reqsiz)) == reqsiz) { 782 struct req *p = slp->req; 783 784 for (trecno = 0; trecno < ntrec; 785 trecno += p->count, p += p->count) { 786 if (p->dblk) { 787 bread(p->dblk, slp->tblock[trecno], 788 p->count * TP_BSIZE); 789 } else { 790 if (p->count != 1 || atomic(read, cmd, 791 (char *)slp->tblock[trecno], 792 TP_BSIZE) != TP_BSIZE) 793 quit("master/slave protocol botched.\n"); 794 } 795 } 796 if (setjmp(jmpbuf) == 0) { 797 ready = 1; 798 if (!caught) 799 (void) pause(); 800 } 801 ready = 0; 802 caught = 0; 803 804 /* Try to write the data... */ 805 eot_count = 0; 806 size = 0; 807 808 wrote = 0; 809 while (eot_count < 10 && size < writesize) { 810#ifdef RDUMP 811 if (host) 812 wrote = rmtwrite(slp->tblock[0]+size, 813 writesize-size); 814 else 815#endif 816 wrote = write(tapefd, slp->tblock[0]+size, 817 writesize-size); 818#ifdef WRITEDEBUG 819 printf("slave %d wrote %d\n", slave_number, wrote); 820#endif 821 if (wrote < 0) 822 break; 823 if (wrote == 0) 824 eot_count++; 825 size += wrote; 826 } 827 828#ifdef WRITEDEBUG 829 if (size != writesize) 830 printf("slave %d only wrote %d out of %d bytes and gave up.\n", 831 slave_number, size, writesize); 832#endif 833 834 /* 835 * Handle ENOSPC as an EOT condition. 836 */ 837 if (wrote < 0 && errno == ENOSPC) { 838 wrote = 0; 839 eot_count++; 840 } 841 842 if (eot_count > 0) 843 size = 0; 844 845 if (wrote < 0) { 846 (void) kill(master, SIGUSR1); 847 for (;;) 848 (void) sigpause(0); 849 } else { 850 /* 851 * pass size of write back to master 852 * (for EOT handling) 853 */ 854 (void) atomic(write, cmd, (char *)&size, sizeof size); 855 } 856 857 /* 858 * If partial write, don't want next slave to go. 859 * Also jolts him awake. 860 */ 861 (void) kill(nextslave, SIGUSR2); 862 } 863 if (nread != 0) 864 quit("error reading command pipe: %s\n", strerror(errno)); 865} 866 867/* 868 * Since a read from a pipe may not return all we asked for, 869 * or a write may not write all we ask if we get a signal, 870 * loop until the count is satisfied (or error). 871 */ 872static int 873atomic(ssize_t (*func)(), int fd, char *buf, int count) 874{ 875 int got, need = count; 876 877 while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0) 878 buf += got; 879 return (got < 0 ? got : count - need); 880} 881