xref: /csrg-svn/sbin/dump/tape.c (revision 50504)
147082Smckusick /*-
247082Smckusick  * Copyright (c) 1980, 1991 The Regents of the University of California.
347082Smckusick  * All rights reserved.
447082Smckusick  *
547082Smckusick  * %sccs.include.redist.c%
622040Sdist  */
722040Sdist 
817527Ssam #ifndef lint
9*50504Smckusick static char sccsid[] = "@(#)tape.c	5.19 (Berkeley) 07/23/91";
1046585Storek #endif /* not lint */
1117527Ssam 
1246795Sbostic #include <sys/param.h>
1346585Storek #include <sys/wait.h>
1446795Sbostic #include <ufs/dinode.h>
1546795Sbostic #include <ufs/fs.h>
1646795Sbostic #include <signal.h>
1746795Sbostic #include <fcntl.h>
1846795Sbostic #include <protocols/dumprestore.h>
1946585Storek #include <errno.h>
20*50504Smckusick #include <setjmp.h>
2146795Sbostic #ifdef __STDC__
2246795Sbostic #include <unistd.h>
2346795Sbostic #include <stdlib.h>
2446795Sbostic #include <string.h>
2546795Sbostic #endif
26*50504Smckusick #include <sys/socket.h>
2746795Sbostic #include "dump.h"
2839128Smckusick #include "pathnames.h"
291425Sroot 
3029899Smckusick int	writesize;		/* size of malloc()ed buffer for tape */
3129899Smckusick long	lastspclrec = -1;	/* tape block number of last written header */
3229899Smckusick int	trecno = 0;		/* next record to write in current block */
3346614Smckusick extern	long blocksperfile;	/* number of blocks per output file */
3448621Skarels long	blocksthisvol;		/* number of blocks on current output file */
3548621Skarels extern	int ntrec;		/* blocking factor on tape */
3648621Skarels extern	int cartridge;
37*50504Smckusick extern	char *host;
3847056Skarels char	*nexttape;
3925219Smckusick #ifdef RDUMP
4046585Storek int	rmtopen(), rmtwrite();
4146585Storek void	rmtclose();
4225219Smckusick #endif RDUMP
43*50504Smckusick void	rollforward();
4446585Storek int	atomic();
4546789Smckusick void	doslave(), enslave(), flushtape(), killall();
4646585Storek 
4710911Ssam /*
4824181Smckusick  * Concurrent dump mods (Caltech) - disk block reading and tape writing
4918012Smckusick  * are exported to several slave processes.  While one slave writes the
5018012Smckusick  * tape, the others read disk blocks; they pass control of the tape in
51*50504Smckusick  * a ring via signals. The parent process traverses the filesystem and
5246789Smckusick  * sends writeheader()'s and lists of daddr's to the slaves via pipes.
53*50504Smckusick  * The following structure defines the instruction packets sent to slaves.
5410911Ssam  */
55*50504Smckusick struct req {
5618012Smckusick 	daddr_t dblk;
5718012Smckusick 	int count;
58*50504Smckusick };
5918012Smckusick int reqsiz;
6018012Smckusick 
6124181Smckusick #define SLAVES 3		/* 1 slave writing, 1 reading, 1 for slack */
62*50504Smckusick struct slave {
63*50504Smckusick 	int tapea;		/* header number at start of this chunk */
64*50504Smckusick 	int count;		/* count to next header (used for TS_TAPE */
65*50504Smckusick 				/* after EOT) */
66*50504Smckusick 	int inode;		/* inode that we are currently dealing with */
67*50504Smckusick 	int fd;			/* FD for this slave */
68*50504Smckusick 	int pid;		/* PID for this slave */
69*50504Smckusick 	int sent;		/* 1 == we've sent this slave requests */
70*50504Smckusick 	int firstrec;		/* record number of this block */
71*50504Smckusick 	char (*tblock)[TP_BSIZE]; /* buffer for data blocks */
72*50504Smckusick 	struct req *req;	/* buffer for requests */
73*50504Smckusick } slaves[SLAVES+1];
74*50504Smckusick struct slave *slp;
7518012Smckusick 
76*50504Smckusick char	(*nextblock)[TP_BSIZE];
77*50504Smckusick 
78*50504Smckusick int master;		/* pid of master, for sending error signals */
79*50504Smckusick int tenths;		/* length of tape used per block written */
80*50504Smckusick static int caught;	/* have we caught the signal to proceed? */
81*50504Smckusick static int ready;	/* have we reached the lock point without having */
82*50504Smckusick 			/* received the SIGUSR2 signal from the prev slave? */
83*50504Smckusick static jmp_buf jmpbuf;	/* where to jump to if we are ready when the */
84*50504Smckusick 			/* SIGUSR2 arrives from the previous slave */
85*50504Smckusick 
8646585Storek int
8710911Ssam alloctape()
8810911Ssam {
8925219Smckusick 	int pgoff = getpagesize() - 1;
90*50504Smckusick 	char *buf;
91*50504Smckusick 	int i;
9210911Ssam 
9310911Ssam 	writesize = ntrec * TP_BSIZE;
94*50504Smckusick 	reqsiz = (ntrec + 1) * sizeof(struct req);
9524181Smckusick 	/*
9625219Smckusick 	 * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode
9725219Smckusick 	 * (see DEC TU80 User's Guide).  The shorter gaps of 6250-bpi require
9825219Smckusick 	 * repositioning after stopping, i.e, streaming mode, where the gap is
9925219Smckusick 	 * variable, 0.30" to 0.45".  The gap is maximal when the tape stops.
10024181Smckusick 	 */
10147056Skarels 	if (blocksperfile == 0)
10247056Skarels 		tenths = writesize / density +
10347056Skarels 		    (cartridge ? 16 : density == 625 ? 5 : 8);
10425219Smckusick 	/*
10525219Smckusick 	 * Allocate tape buffer contiguous with the array of instruction
10646789Smckusick 	 * packets, so flushtape() can write them together with one write().
10725219Smckusick 	 * Align tape buffer on page boundary to speed up tape write().
10825219Smckusick 	 */
109*50504Smckusick 	for (i = 0; i <= SLAVES; i++) {
110*50504Smckusick 		buf = (char *) malloc(reqsiz + writesize + pgoff + TP_BSIZE);
111*50504Smckusick 		if (buf == NULL)
112*50504Smckusick 		  return(0);
113*50504Smckusick 		slaves[i].tblock = (char (*)[TP_BSIZE])
114*50504Smckusick 		    (((long)&buf[ntrec + 1] + pgoff) &~ pgoff);
115*50504Smckusick 		slaves[i].req = (struct req *)slaves[i].tblock - ntrec - 1;
116*50504Smckusick 	}
117*50504Smckusick 	slp = &slaves[0];
118*50504Smckusick 	slp->count = 1;
119*50504Smckusick 	slp->tapea = 0;
120*50504Smckusick 	slp->firstrec = 0;
121*50504Smckusick 	nextblock = slp->tblock;
12224181Smckusick 	return(1);
12310911Ssam }
12410911Ssam 
12546585Storek void
12646789Smckusick writerec(dp)
1275329Smckusic 	char *dp;
1281425Sroot {
129*50504Smckusick 
130*50504Smckusick 	slp->req[trecno].dblk = (daddr_t)0;
131*50504Smckusick 	slp->req[trecno].count = 1;
132*50504Smckusick 	*(union u_spcl *)(*(nextblock)++) = *(union u_spcl *)dp;
13329899Smckusick 	lastspclrec = spcl.c_tapea;
13424181Smckusick 	trecno++;
1351425Sroot 	spcl.c_tapea++;
13646585Storek 	if (trecno >= ntrec)
13746789Smckusick 		flushtape();
1381425Sroot }
1391425Sroot 
14046585Storek void
14146789Smckusick dumpblock(blkno, size)
1424774Smckusic 	daddr_t blkno;
1434774Smckusic 	int size;
1441425Sroot {
14525219Smckusick 	int avail, tpblks, dblkno;
1461425Sroot 
1475329Smckusic 	dblkno = fsbtodb(sblock, blkno);
14846585Storek 	tpblks = size >> tp_bshift;
14918012Smckusick 	while ((avail = MIN(tpblks, ntrec - trecno)) > 0) {
150*50504Smckusick 		slp->req[trecno].dblk = dblkno;
151*50504Smckusick 		slp->req[trecno].count = avail;
15225219Smckusick 		trecno += avail;
1534774Smckusic 		spcl.c_tapea += avail;
15425219Smckusick 		if (trecno >= ntrec)
15546789Smckusick 			flushtape();
15646585Storek 		dblkno += avail << (tp_bshift - dev_bshift);
1575329Smckusic 		tpblks -= avail;
1584774Smckusic 	}
1591425Sroot }
1601425Sroot 
1611425Sroot int	nogripe = 0;
1621425Sroot 
16346585Storek void
16446585Storek tperror()
16546585Storek {
166*50504Smckusick 
16718012Smckusick 	if (pipeout) {
16846614Smckusick 		msg("write error on %s\n", tape);
16946585Storek 		quit("Cannot recover\n");
17018012Smckusick 		/* NOTREACHED */
17118012Smckusick 	}
17248621Skarels 	msg("write error %d blocks into volume %d\n", blocksthisvol, tapeno);
17346614Smckusick 	broadcast("DUMP WRITE ERROR!\n");
17418012Smckusick 	if (!query("Do you want to restart?"))
17518012Smckusick 		dumpabort();
17646614Smckusick 	msg("Closing this volume.  Prepare to restart with new media;\n");
17718012Smckusick 	msg("this dump volume will be rewritten.\n");
17824181Smckusick 	killall();
17918012Smckusick 	nogripe = 1;
18018012Smckusick 	close_rewind();
18118012Smckusick 	Exit(X_REWRITE);
18218012Smckusick }
18318012Smckusick 
18446585Storek void
18525219Smckusick sigpipe()
18625219Smckusick {
18725219Smckusick 
18846585Storek 	quit("Broken pipe\n");
18925219Smckusick }
19025219Smckusick 
19146585Storek void
19246789Smckusick flushtape()
19318012Smckusick {
194*50504Smckusick 	int i, blks, got;
195*50504Smckusick 	long lastfirstrec;
19646795Sbostic #ifndef __STDC__
197*50504Smckusick 	int write(), read();
19846795Sbostic #endif
19946795Sbostic 
200*50504Smckusick 	int siz = (char *)nextblock - (char *)slp->req;
2011425Sroot 
202*50504Smckusick 	slp->req[trecno].count = 0;			/* Sentinel */
203*50504Smckusick 
204*50504Smckusick 	if (atomic(write, slp->fd, slp->req, siz) != siz)
20546585Storek 		quit("error writing command pipe: %s\n", strerror(errno));
206*50504Smckusick 	slp->sent = 1; /* we sent a request, read the response later */
207*50504Smckusick 
208*50504Smckusick 	lastfirstrec = slp->firstrec;
209*50504Smckusick 
210*50504Smckusick 	if (++slp >= &slaves[SLAVES])
211*50504Smckusick 		slp = &slaves[0];
212*50504Smckusick 
213*50504Smckusick 	/* Read results back from next slave */
214*50504Smckusick 	if (slp->sent) {
215*50504Smckusick 		if (atomic(read, slp->fd, &got, sizeof got) != sizeof got) {
216*50504Smckusick 			perror("  DUMP: error reading command pipe in master");
217*50504Smckusick 			dumpabort();
218*50504Smckusick 		}
219*50504Smckusick 		slp->sent = 0;
220*50504Smckusick 
221*50504Smckusick 		/* Check for end of tape */
222*50504Smckusick 		if (got < writesize) {
223*50504Smckusick 			msg("End of tape detected\n");
224*50504Smckusick 
225*50504Smckusick 			/*
226*50504Smckusick 			 * Drain the results, don't care what the values were.
227*50504Smckusick 			 * If we read them here then trewind won't...
228*50504Smckusick 			 */
229*50504Smckusick 			for (i = 0; i < SLAVES; i++) {
230*50504Smckusick 				if (slaves[i].sent) {
231*50504Smckusick 					if (atomic(read, slaves[i].fd, &got,
232*50504Smckusick 					    sizeof got) != sizeof got) {
233*50504Smckusick 						perror("  DUMP: error reading command pipe in master");
234*50504Smckusick 						dumpabort();
235*50504Smckusick 					}
236*50504Smckusick 					slaves[i].sent = 0;
237*50504Smckusick 				}
238*50504Smckusick 			}
239*50504Smckusick 
240*50504Smckusick 			close_rewind();
241*50504Smckusick 			rollforward();
242*50504Smckusick 			return;
243*50504Smckusick 		}
244*50504Smckusick 	}
245*50504Smckusick 
246*50504Smckusick 	blks = 0;
247*50504Smckusick 	if (spcl.c_type != TS_END) {
248*50504Smckusick 		for (i = 0; i < spcl.c_count; i++)
249*50504Smckusick 			if (spcl.c_addr[i] != 0)
250*50504Smckusick 				blks++;
251*50504Smckusick 	}
252*50504Smckusick 	slp->count = lastspclrec + blks + 1 - spcl.c_tapea;
253*50504Smckusick 	slp->tapea = spcl.c_tapea;
254*50504Smckusick 	slp->firstrec = lastfirstrec + ntrec;
255*50504Smckusick 	slp->inode = curino;
256*50504Smckusick 	nextblock = slp->tblock;
2571425Sroot 	trecno = 0;
25824181Smckusick 	asize += tenths;
25910911Ssam 	blockswritten += ntrec;
26048621Skarels 	blocksthisvol += ntrec;
26146614Smckusick 	if (!pipeout && (blocksperfile ?
26248621Skarels 	    (blocksthisvol >= blocksperfile) : (asize > tsize))) {
2631425Sroot 		close_rewind();
264*50504Smckusick 		startnewtape(0);
2651425Sroot 	}
2661425Sroot 	timeest();
2671425Sroot }
2681425Sroot 
26946585Storek void
27046239Storek trewind()
2711425Sroot {
27224181Smckusick 	int f;
273*50504Smckusick 	int got;
27412331Smckusick 
27512331Smckusick 	if (pipeout)
27612331Smckusick 		return;
277*50504Smckusick 	for (f = 0; f < SLAVES; f++) {
278*50504Smckusick 		/*
279*50504Smckusick 		 * Drain the results, but unlike EOT we DO (or should) care
280*50504Smckusick 		 * what the return values were, since if we detect EOT after
281*50504Smckusick 		 * we think we've written the last blocks to the tape anyway,
282*50504Smckusick 		 * we have to replay those blocks with rollforward.
283*50504Smckusick 		 *
284*50504Smckusick 		 * fixme: punt for now.
285*50504Smckusick 		 */
286*50504Smckusick 		if (slaves[f].sent) {
287*50504Smckusick 			if (atomic(read, slaves[f].fd, &got, sizeof got)
288*50504Smckusick 			    != sizeof got) {
289*50504Smckusick 				perror("  DUMP: error reading command pipe in master");
290*50504Smckusick 				dumpabort();
291*50504Smckusick 			}
292*50504Smckusick 			slaves[f].sent = 0;
293*50504Smckusick 			if (got != writesize) {
294*50504Smckusick 				msg("EOT detected in last 2 tape records!\n");
295*50504Smckusick 				msg("Use a longer tape, decrease the size estimate\n");
296*50504Smckusick 				quit("or use no size estimate at all.\n");
297*50504Smckusick 			}
298*50504Smckusick 		}
299*50504Smckusick 		close(slaves[f].fd);
300*50504Smckusick 	}
30146585Storek 	while (wait((int *)NULL) >= 0)	/* wait for any signals from slaves */
30246585Storek 		/* void */;
30348621Skarels 	msg("Closing %s\n", tape);
304*50504Smckusick 
30518012Smckusick #ifdef RDUMP
30625219Smckusick 	if (host) {
30725219Smckusick 		rmtclose();
30825219Smckusick 		while (rmtopen(tape, 0) < 0)
30925219Smckusick 			sleep(10);
31025219Smckusick 		rmtclose();
31125219Smckusick 		return;
31225219Smckusick 	}
313*50504Smckusick #endif
31446789Smckusick 	close(tapefd);
3153214Swnj 	while ((f = open(tape, 0)) < 0)
3163214Swnj 		sleep (10);
3173214Swnj 	close(f);
3181425Sroot }
3191425Sroot 
32046585Storek void
3211425Sroot close_rewind()
3221425Sroot {
32346239Storek 	trewind();
32448621Skarels 	if (nexttape)
32548621Skarels 		return;
32618012Smckusick 	if (!nogripe) {
32746614Smckusick 		msg("Change Volumes: Mount volume #%d\n", tapeno+1);
32846614Smckusick 		broadcast("CHANGE DUMP VOLUMES!\7\7\n");
3291425Sroot 	}
33048621Skarels 	while (!query("Is the new volume mounted and ready to go?"))
33125219Smckusick 		if (query("Do you want to abort?")) {
3321425Sroot 			dumpabort();
33325219Smckusick 			/*NOTREACHED*/
33425219Smckusick 		}
3351425Sroot }
3361425Sroot 
337*50504Smckusick #ifdef ROLLDEBUG
338*50504Smckusick int do_sum(block)
339*50504Smckusick      union u_spcl *block;
340*50504Smckusick 
341*50504Smckusick {
342*50504Smckusick 	char sum = 0;
343*50504Smckusick 	int i;
344*50504Smckusick 
345*50504Smckusick 	for (i = 0; i < TP_BSIZE; i++) {
346*50504Smckusick 		sum = sum ^ block->dummy[i];
347*50504Smckusick 	}
348*50504Smckusick 	return(sum);
349*50504Smckusick }
350*50504Smckusick #endif
351*50504Smckusick 
352*50504Smckusick void
353*50504Smckusick rollforward()
354*50504Smckusick {
355*50504Smckusick 	register struct req *p, *q, *prev;
356*50504Smckusick 	register struct slave *tslp;
357*50504Smckusick 	int i, next, size, savedtapea, got;
358*50504Smckusick 	union u_spcl *ntb, *otb;
359*50504Smckusick #ifdef ROLLDEBUG
360*50504Smckusick 	int j;
361*50504Smckusick #endif
362*50504Smckusick 	tslp = &slaves[SLAVES];
363*50504Smckusick 	ntb = (union u_spcl *)tslp->tblock[1];
364*50504Smckusick 
365*50504Smckusick 	/*
366*50504Smckusick 	 * Each of the N slaves should have requests that need to
367*50504Smckusick 	 * be replayed on the next tape.  Use the extra slave buffers
368*50504Smckusick 	 * (slaves[SLAVES]) to construct request lists to be sent to
369*50504Smckusick 	 * each slave in turn.
370*50504Smckusick 	 */
371*50504Smckusick 	for (i = 0; i < SLAVES; i++) {
372*50504Smckusick 		q = &tslp->req[1];
373*50504Smckusick 		otb = (union u_spcl *)slp->tblock;
374*50504Smckusick 
375*50504Smckusick 		/*
376*50504Smckusick 		 * For each request in the current slave, copy it to tslp.
377*50504Smckusick 		 */
378*50504Smckusick #ifdef ROLLDEBUG
379*50504Smckusick 		printf("replaying reqs to slave %d (%d)\n", slp - &slaves[0],
380*50504Smckusick 		    slp->pid);
381*50504Smckusick 		j = 0;
382*50504Smckusick #endif
383*50504Smckusick 
384*50504Smckusick 		for (p = slp->req; p->count > 0; p += p->count) {
385*50504Smckusick #ifdef ROLLDEBUG
386*50504Smckusick 			printf("    req %d count %d dblk %d\n",
387*50504Smckusick 			       j++, p->count, p->dblk);
388*50504Smckusick 			if (p->dblk == 0)
389*50504Smckusick 				printf("\tsum %x\n", do_sum(otb));
390*50504Smckusick #endif
391*50504Smckusick 			*q = *p;
392*50504Smckusick 			if (p->dblk == 0)
393*50504Smckusick 				*ntb++ = *otb++; /* copy the datablock also */
394*50504Smckusick 			prev = q;
395*50504Smckusick 			q += q->count;
396*50504Smckusick 		}
397*50504Smckusick 		if (prev->dblk != 0)
398*50504Smckusick 			prev->count -= 1;
399*50504Smckusick 		else
400*50504Smckusick 			ntb--;
401*50504Smckusick 		q -= 1;
402*50504Smckusick 		q->count = 0;
403*50504Smckusick 		q = &tslp->req[0];
404*50504Smckusick 		if (i == 0) {
405*50504Smckusick 			q->dblk = 0;
406*50504Smckusick 			q->count = 1;
407*50504Smckusick 			trecno = 0;
408*50504Smckusick 			nextblock = tslp->tblock;
409*50504Smckusick 			savedtapea = spcl.c_tapea;
410*50504Smckusick 			spcl.c_tapea = slp->tapea;
411*50504Smckusick 			startnewtape(0);
412*50504Smckusick 			spcl.c_tapea = savedtapea;
413*50504Smckusick 			lastspclrec = savedtapea - 1;
414*50504Smckusick 		}
415*50504Smckusick 		size = (char *)ntb - (char *)q;
416*50504Smckusick 		if (atomic(write, slp->fd, q, size) != size) {
417*50504Smckusick 			perror("  DUMP: error writing command pipe");
418*50504Smckusick 			dumpabort();
419*50504Smckusick 		}
420*50504Smckusick 		slp->sent = 1;
421*50504Smckusick #ifdef ROLLDEBUG
422*50504Smckusick 		printf("after the shift:\n");
423*50504Smckusick 		j = 0;
424*50504Smckusick 		for (p = tslp->req; p->count > 0; p += p->count) {
425*50504Smckusick 			printf("    req %d count %d dblk %d\n",
426*50504Smckusick 			       j++, p->count, p->dblk);
427*50504Smckusick 			if (p->dblk == 0) {
428*50504Smckusick 				/* dump block also */
429*50504Smckusick 			}
430*50504Smckusick 		}
431*50504Smckusick #endif
432*50504Smckusick 		if (++slp >= &slaves[SLAVES])
433*50504Smckusick 			slp = &slaves[0];
434*50504Smckusick 
435*50504Smckusick 		q->count = 1;
436*50504Smckusick 
437*50504Smckusick 		if (prev->dblk != 0) {
438*50504Smckusick 			/*
439*50504Smckusick 			 * If the last one was a disk block, make the
440*50504Smckusick 			 * first of this one be the last bit of that disk
441*50504Smckusick 			 * block...
442*50504Smckusick 			 */
443*50504Smckusick 			q->dblk = prev->dblk +
444*50504Smckusick 				prev->count * (TP_BSIZE / DEV_BSIZE);
445*50504Smckusick 			ntb = (union u_spcl *)tslp->tblock;
446*50504Smckusick 		} else {
447*50504Smckusick 			/*
448*50504Smckusick 			 * It wasn't a disk block.  Copy the data to its
449*50504Smckusick 			 * new location in the buffer.
450*50504Smckusick 			 */
451*50504Smckusick 			q->dblk = 0;
452*50504Smckusick 			*((union u_spcl *)tslp->tblock) = *ntb;
453*50504Smckusick 			ntb = (union u_spcl *)tslp->tblock[1];
454*50504Smckusick 		}
455*50504Smckusick 	}
456*50504Smckusick 	slp->req[0] = *q;
457*50504Smckusick 	nextblock = slp->tblock;
458*50504Smckusick 	if (q->dblk == 0)
459*50504Smckusick 		nextblock++;
460*50504Smckusick 	trecno = 1;
461*50504Smckusick 
462*50504Smckusick 	/*
463*50504Smckusick 	 * Clear the first slaves' response.  One hopes that it
464*50504Smckusick 	 * worked ok, otherwise the tape is much too short!
465*50504Smckusick 	 */
466*50504Smckusick 	if (slp->sent) {
467*50504Smckusick 		if (atomic(read, slp->fd, &got, sizeof got) != sizeof got) {
468*50504Smckusick 			perror("  DUMP: error reading command pipe in master");
469*50504Smckusick 			dumpabort();
470*50504Smckusick 		}
471*50504Smckusick 		slp->sent = 0;
472*50504Smckusick 
473*50504Smckusick 		if (got != writesize) {
474*50504Smckusick 			quit("EOT detected at start of the tape!\n");
475*50504Smckusick 		}
476*50504Smckusick 	}
477*50504Smckusick }
478*50504Smckusick 
4791425Sroot /*
480*50504Smckusick  * We implement taking and restoring checkpoints on the tape level.
481*50504Smckusick  * When each tape is opened, a new process is created by forking; this
482*50504Smckusick  * saves all of the necessary context in the parent.  The child
483*50504Smckusick  * continues the dump; the parent waits around, saving the context.
484*50504Smckusick  * If the child returns X_REWRITE, then it had problems writing that tape;
485*50504Smckusick  * this causes the parent to fork again, duplicating the context, and
486*50504Smckusick  * everything continues as if nothing had happened.
4871425Sroot  */
48846585Storek void
489*50504Smckusick startnewtape(top)
490*50504Smckusick 	int top;
4911425Sroot {
4921425Sroot 	int	parentpid;
4931425Sroot 	int	childpid;
4941425Sroot 	int	status;
4951425Sroot 	int	waitpid;
49639164Sbostic 	sig_t	interrupt;
497*50504Smckusick 	int	i;
49847056Skarels 	char	*p;
4991425Sroot 
50039164Sbostic 	interrupt = signal(SIGINT, SIG_IGN);
5011425Sroot 	parentpid = getpid();
5021425Sroot 
5031425Sroot     restore_check_point:
50439164Sbostic 	(void)signal(SIGINT, interrupt);
50525219Smckusick 	/*
50625219Smckusick 	 *	All signals are inherited...
50725219Smckusick 	 */
5081425Sroot 	childpid = fork();
50918012Smckusick 	if (childpid < 0) {
5101425Sroot 		msg("Context save fork fails in parent %d\n", parentpid);
5111425Sroot 		Exit(X_ABORT);
5121425Sroot 	}
51318012Smckusick 	if (childpid != 0) {
5141425Sroot 		/*
5151425Sroot 		 *	PARENT:
5161425Sroot 		 *	save the context by waiting
5171425Sroot 		 *	until the child doing all of the work returns.
51818012Smckusick 		 *	don't catch the interrupt
5191425Sroot 		 */
52025219Smckusick 		signal(SIGINT, SIG_IGN);
5211425Sroot #ifdef TDEBUG
5221425Sroot 		msg("Tape: %d; parent process: %d child process %d\n",
5231425Sroot 			tapeno+1, parentpid, childpid);
5241425Sroot #endif TDEBUG
52518012Smckusick 		while ((waitpid = wait(&status)) != childpid)
52618012Smckusick 			msg("Parent %d waiting for child %d has another child %d return\n",
52718012Smckusick 				parentpid, childpid, waitpid);
52818012Smckusick 		if (status & 0xFF) {
5291425Sroot 			msg("Child %d returns LOB status %o\n",
5301425Sroot 				childpid, status&0xFF);
5311425Sroot 		}
5321425Sroot 		status = (status >> 8) & 0xFF;
5331425Sroot #ifdef TDEBUG
53418012Smckusick 		switch(status) {
5351425Sroot 			case X_FINOK:
5361425Sroot 				msg("Child %d finishes X_FINOK\n", childpid);
5371425Sroot 				break;
538*50504Smckusick 			case X_ABORT:
5391425Sroot 				msg("Child %d finishes X_ABORT\n", childpid);
5401425Sroot 				break;
5411425Sroot 			case X_REWRITE:
5421425Sroot 				msg("Child %d finishes X_REWRITE\n", childpid);
5431425Sroot 				break;
5441425Sroot 			default:
54518012Smckusick 				msg("Child %d finishes unknown %d\n",
54625219Smckusick 					childpid, status);
5471425Sroot 				break;
5481425Sroot 		}
5491425Sroot #endif TDEBUG
55018012Smckusick 		switch(status) {
5511425Sroot 			case X_FINOK:
5521425Sroot 				Exit(X_FINOK);
5531425Sroot 			case X_ABORT:
5541425Sroot 				Exit(X_ABORT);
5551425Sroot 			case X_REWRITE:
5561425Sroot 				goto restore_check_point;
5571425Sroot 			default:
5581425Sroot 				msg("Bad return code from dump: %d\n", status);
5591425Sroot 				Exit(X_ABORT);
5601425Sroot 		}
5611425Sroot 		/*NOTREACHED*/
5621425Sroot 	} else {	/* we are the child; just continue */
5631425Sroot #ifdef TDEBUG
5641425Sroot 		sleep(4);	/* allow time for parent's message to get out */
5651425Sroot 		msg("Child on Tape %d has parent %d, my pid = %d\n",
5661425Sroot 			tapeno+1, parentpid, getpid());
56725219Smckusick #endif TDEBUG
56847056Skarels 		/*
56947056Skarels 		 * If we have a name like "/dev/rmt0,/dev/rmt1",
57047056Skarels 		 * use the name before the comma first, and save
57148621Skarels 		 * the remaining names for subsequent volumes.
57247056Skarels 		 */
573*50504Smckusick 		tapeno++;               /* current tape sequence */
57448621Skarels 		if (nexttape || index(tape, ',')) {
57548621Skarels 			if (nexttape && *nexttape)
57648621Skarels 				tape = nexttape;
57748621Skarels 			if (p = index(tape, ',')) {
57848621Skarels 				*p = '\0';
57948621Skarels 				nexttape = p + 1;
58048621Skarels 			} else
58148621Skarels 				nexttape = NULL;
58248621Skarels 			msg("Dumping volume %d on %s\n", tapeno, tape);
58348621Skarels 		}
58418012Smckusick #ifdef RDUMP
58546789Smckusick 		while ((tapefd = (host ? rmtopen(tape, 2) :
58646789Smckusick 			pipeout ? 1 : open(tape, O_WRONLY|O_CREAT, 0666))) < 0)
587*50504Smckusick #else
588*50504Smckusick 		while ((tapefd = (pipeout ? 1 :
589*50504Smckusick 				  open(tape, O_WRONLY|O_CREAT, 0666))) < 0)
590*50504Smckusick #endif
59139128Smckusick 		    {
59246614Smckusick 			msg("Cannot open output \"%s\".\n", tape);
59339128Smckusick 			if (!query("Do you want to retry the open?"))
59418012Smckusick 				dumpabort();
59539128Smckusick 		}
5961425Sroot 
59718012Smckusick 		enslave();  /* Share open tape file descriptor with slaves */
59818012Smckusick 
5991425Sroot 		asize = 0;
60048621Skarels 		blocksthisvol = 0;
601*50504Smckusick 		if (top)
602*50504Smckusick 			newtape++;		/* new tape signal */
603*50504Smckusick 		spcl.c_count = slp->count;
604*50504Smckusick 		/*
605*50504Smckusick 		 * measure firstrec in TP_BSIZE units since restore doesn't
606*50504Smckusick 		 * know the correct ntrec value...
607*50504Smckusick 		 */
608*50504Smckusick 		spcl.c_firstrec = slp->firstrec;
6091425Sroot 		spcl.c_volume++;
6101425Sroot 		spcl.c_type = TS_TAPE;
61130432Smckusick 		spcl.c_flags |= DR_NEWHEADER;
612*50504Smckusick 		writeheader(slp->inode);
61330432Smckusick 		spcl.c_flags &=~ DR_NEWHEADER;
6141425Sroot 		if (tapeno > 1)
61548621Skarels 			msg("Volume %d begins with blocks from inode %d\n",
616*50504Smckusick 				tapeno, slp->inode);
6171425Sroot 	}
6181425Sroot }
6191425Sroot 
62046585Storek void
6211425Sroot dumpabort()
6221425Sroot {
623*50504Smckusick 
62418012Smckusick 	if (master != 0 && master != getpid())
62525219Smckusick 		kill(master, SIGTERM);	/* Signals master to call dumpabort */
62624181Smckusick 	else {
62724181Smckusick 		killall();
62824181Smckusick 		msg("The ENTIRE dump is aborted.\n");
62924181Smckusick 	}
6301425Sroot 	Exit(X_ABORT);
6311425Sroot }
6321425Sroot 
63346585Storek void
6341425Sroot Exit(status)
63546239Storek 	int status;
6361425Sroot {
637*50504Smckusick 
6381425Sroot #ifdef TDEBUG
6391425Sroot 	msg("pid = %d exits with status %d\n", getpid(), status);
6401425Sroot #endif TDEBUG
6411925Swnj 	exit(status);
6421425Sroot }
64318012Smckusick 
64424181Smckusick /*
645*50504Smckusick  * proceed - handler for SIGUSR2, used to synchronize IO between the slaves.
64624181Smckusick  */
64746585Storek void
648*50504Smckusick proceed()
64924181Smckusick {
65018012Smckusick 
651*50504Smckusick 	if (ready)
652*50504Smckusick 		longjmp(jmpbuf, 1);
653*50504Smckusick 	caught++;
65424181Smckusick }
65524181Smckusick 
65646585Storek void
65718012Smckusick enslave()
65818012Smckusick {
659*50504Smckusick 	int cmd[2];
66024181Smckusick 	register int i, j;
66118012Smckusick 
66218012Smckusick 	master = getpid();
663*50504Smckusick 
664*50504Smckusick 	signal(SIGTERM, dumpabort);  /* Slave sends SIGTERM on dumpabort() */
66525219Smckusick 	signal(SIGPIPE, sigpipe);
66625219Smckusick 	signal(SIGUSR1, tperror);    /* Slave sends SIGUSR1 on tape errors */
667*50504Smckusick 	signal(SIGUSR2, proceed);    /* Slave sends SIGUSR2 to next slave */
668*50504Smckusick 
66924181Smckusick 	for (i = 0; i < SLAVES; i++) {
670*50504Smckusick 		if (i == slp - &slaves[0]) {
671*50504Smckusick 			caught = 1;
67224181Smckusick 		} else {
673*50504Smckusick 			caught = 0;
67424181Smckusick 		}
675*50504Smckusick 
676*50504Smckusick 		if (socketpair(AF_UNIX, SOCK_STREAM, 0, cmd) < 0 ||
677*50504Smckusick 		    (slaves[i].pid = fork()) < 0)
67846585Storek 			quit("too many slaves, %d (recompile smaller): %s\n",
67946585Storek 			    i, strerror(errno));
680*50504Smckusick 
681*50504Smckusick 		slaves[i].fd = cmd[1];
682*50504Smckusick 		slaves[i].sent = 0;
683*50504Smckusick 		if (slaves[i].pid == 0) { 	    /* Slave starts up here */
68418012Smckusick 			for (j = 0; j <= i; j++)
685*50504Smckusick 			        close(slaves[j].fd);
68625219Smckusick 			signal(SIGINT, SIG_IGN);    /* Master handles this */
687*50504Smckusick 			doslave(cmd[0], i);
68818012Smckusick 			Exit(X_FINOK);
68918012Smckusick 		}
69018012Smckusick 	}
691*50504Smckusick 
692*50504Smckusick 	for (i = 0; i < SLAVES; i++)
693*50504Smckusick 		atomic(write, slaves[i].fd, &slaves[(i + 1) % SLAVES].pid,
694*50504Smckusick 		       sizeof slaves[0].pid);
695*50504Smckusick 
696*50504Smckusick 	master = 0;
69718012Smckusick }
69818012Smckusick 
69946585Storek void
70024181Smckusick killall()
70118012Smckusick {
70224181Smckusick 	register int i;
70319982Smckusick 
70424181Smckusick 	for (i = 0; i < SLAVES; i++)
705*50504Smckusick 		if (slaves[i].pid > 0)
706*50504Smckusick 			kill(slaves[i].pid, SIGKILL);
70718012Smckusick }
70818012Smckusick 
70924181Smckusick /*
71024181Smckusick  * Synchronization - each process has a lockfile, and shares file
71124181Smckusick  * descriptors to the following process's lockfile.  When our write
71224181Smckusick  * completes, we release our lock on the following process's lock-
71324181Smckusick  * file, allowing the following process to lock it and proceed. We
71424181Smckusick  * get the lock back for the next cycle by swapping descriptors.
71524181Smckusick  */
71646585Storek void
717*50504Smckusick doslave(cmd, slave_number)
718*50504Smckusick 	register int cmd;
719*50504Smckusick         int slave_number;
72019982Smckusick {
721*50504Smckusick 	register int nread;
722*50504Smckusick 	int nextslave, size, wrote, eot_count;
72346795Sbostic #ifndef __STDC__
72446795Sbostic 	int read();
72546795Sbostic #endif
726*50504Smckusick #ifdef ROLLDEBUG
727*50504Smckusick 	int dodump = 2;
728*50504Smckusick 	FILE *out;
729*50504Smckusick 	char name[64];
730*50504Smckusick #endif
73119982Smckusick 
73246789Smckusick 	/*
73346789Smckusick 	 * Need our own seek pointer.
73446789Smckusick 	 */
73546789Smckusick 	close(diskfd);
73646789Smckusick 	if ((diskfd = open(disk, O_RDONLY)) < 0)
73746585Storek 		quit("slave couldn't reopen disk: %s\n", strerror(errno));
738*50504Smckusick 
73924181Smckusick 	/*
740*50504Smckusick 	 * Need the pid of the next slave in the loop...
741*50504Smckusick 	 */
742*50504Smckusick 	if ((nread = atomic(read, cmd, &nextslave, sizeof nextslave))
743*50504Smckusick 	    != sizeof nextslave) {
744*50504Smckusick 		quit("master/slave protocol botched - didn't get pid of next slave.\n");
745*50504Smckusick 	}
746*50504Smckusick 
747*50504Smckusick #ifdef ROLLDEBUG
748*50504Smckusick 	sprintf(name, "slave.%d", slave_number);
749*50504Smckusick 	out = fopen(name, "w");
750*50504Smckusick #endif
751*50504Smckusick 	/*
75225219Smckusick 	 * Get list of blocks to dump, read the blocks into tape buffer
75324181Smckusick 	 */
754*50504Smckusick 	while ((nread = atomic(read, cmd, slp->req, reqsiz)) == reqsiz) {
755*50504Smckusick 		register struct req *p = slp->req;
756*50504Smckusick 		int j;
757*50504Smckusick 		struct req *rover;
758*50504Smckusick 		char (*orover)[TP_BSIZE];
759*50504Smckusick 
760*50504Smckusick 		j = 0;
761*50504Smckusick 		for (trecno = 0; trecno < ntrec;
762*50504Smckusick 		     trecno += p->count, p += p->count) {
76318012Smckusick 			if (p->dblk) {
764*50504Smckusick 				bread(p->dblk, slp->tblock[trecno],
76525219Smckusick 					p->count * TP_BSIZE);
76618012Smckusick 			} else {
76725219Smckusick 				if (p->count != 1 || atomic(read, cmd,
768*50504Smckusick 				    slp->tblock[trecno], TP_BSIZE) != TP_BSIZE)
76946585Storek 					quit("master/slave protocol botched.\n");
77018012Smckusick 			}
771*50504Smckusick #ifdef ROLLDEBUG
772*50504Smckusick 			if (dodump) {
773*50504Smckusick 				fprintf(out, "    req %d count %d dblk %d\n",
774*50504Smckusick 					j++, p->count, p->dblk);
775*50504Smckusick 				if (p->dblk == 0) {
776*50504Smckusick 					fprintf(out, "\tsum %x\n",
777*50504Smckusick 						do_sum(slp->tblock[trecno]));
778*50504Smckusick 				}
779*50504Smckusick 			}
780*50504Smckusick #endif
78118012Smckusick 		}
782*50504Smckusick #ifdef ROLLDEBUG
783*50504Smckusick 		if (dodump) {
784*50504Smckusick 			fprintf(out, "\n");
785*50504Smckusick 		}
786*50504Smckusick 		if (--dodump == 0) {
787*50504Smckusick 			fclose(out);
788*50504Smckusick 		}
789*50504Smckusick #endif
790*50504Smckusick 		if (setjmp(jmpbuf) == 0) {
791*50504Smckusick 			ready = 1;
792*50504Smckusick 			if (!caught)
793*50504Smckusick 				pause();
794*50504Smckusick 		}
795*50504Smckusick 		ready = 0;
796*50504Smckusick 		caught = 0;
79725219Smckusick 
798*50504Smckusick 		/* Try to write the data... */
799*50504Smckusick 		eot_count = 0;
800*50504Smckusick 		size = 0;
801*50504Smckusick 
802*50504Smckusick 		while (eot_count < 10 && size < writesize) {
80318012Smckusick #ifdef RDUMP
804*50504Smckusick 			if (host)
805*50504Smckusick 				wrote = rmtwrite(slp->tblock[0]+size,
806*50504Smckusick 				    writesize-size);
80748621Skarels 			else
808*50504Smckusick #endif
809*50504Smckusick 				wrote = write(tapefd, slp->tblock[0]+size,
810*50504Smckusick 				    writesize-size);
811*50504Smckusick #ifdef WRITEDEBUG
812*50504Smckusick 			printf("slave %d wrote %d\n", slave_number, wrote);
813*50504Smckusick #endif
814*50504Smckusick 			if (wrote < 0)
815*50504Smckusick 				break;
816*50504Smckusick 			if (wrote == 0)
817*50504Smckusick 				eot_count++;
818*50504Smckusick 			size += wrote;
819*50504Smckusick 		}
820*50504Smckusick 
821*50504Smckusick #ifdef WRITEDEBUG
822*50504Smckusick 		if (size != writesize)
823*50504Smckusick 		 printf("slave %d only wrote %d out of %d bytes and gave up.\n",
824*50504Smckusick 		     slave_number, size, writesize);
825*50504Smckusick #endif
826*50504Smckusick 
827*50504Smckusick 		if (eot_count > 0)
828*50504Smckusick 			size = 0;
829*50504Smckusick 
830*50504Smckusick 		/*
831*50504Smckusick 		 * fixme: Pyramids running OSx return ENOSPC
832*50504Smckusick 		 * at EOT on 1/2 inch drives.
833*50504Smckusick 		 */
834*50504Smckusick 		if (size < 0) {
83525219Smckusick 			kill(master, SIGUSR1);
83625219Smckusick 			for (;;)
83725219Smckusick 				sigpause(0);
838*50504Smckusick 		} else {
839*50504Smckusick 			/*
840*50504Smckusick 			 * pass size of write back to master
841*50504Smckusick 			 * (for EOT handling)
842*50504Smckusick 			 */
843*50504Smckusick 			atomic(write, cmd, &size, sizeof size);
844*50504Smckusick 		}
845*50504Smckusick 
846*50504Smckusick 		/*
847*50504Smckusick 		 * If partial write, don't want next slave to go.
848*50504Smckusick 		 * Also jolts him awake.
849*50504Smckusick 		 */
850*50504Smckusick 		kill(nextslave, SIGUSR2);
851*50504Smckusick 	}
85246585Storek 	if (nread != 0)
85346585Storek 		quit("error reading command pipe: %s\n", strerror(errno));
85418012Smckusick }
85519947Smckusick 
85619947Smckusick /*
85725219Smckusick  * Since a read from a pipe may not return all we asked for,
85825219Smckusick  * or a write may not write all we ask if we get a signal,
85925219Smckusick  * loop until the count is satisfied (or error).
86019947Smckusick  */
86146585Storek int
86225219Smckusick atomic(func, fd, buf, count)
86325219Smckusick 	int (*func)(), fd, count;
86419947Smckusick 	char *buf;
86519947Smckusick {
86625219Smckusick 	int got, need = count;
86719947Smckusick 
86825219Smckusick 	while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0)
86919947Smckusick 		buf += got;
87025219Smckusick 	return (got < 0 ? got : count - need);
87119947Smckusick }
872