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