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