11558Srgrimes/*-
21558Srgrimes * Copyright (c) 1980, 1991, 1993
31558Srgrimes *	The Regents of the University of California.  All rights reserved.
41558Srgrimes *
51558Srgrimes * Redistribution and use in source and binary forms, with or without
61558Srgrimes * modification, are permitted provided that the following conditions
71558Srgrimes * are met:
81558Srgrimes * 1. Redistributions of source code must retain the above copyright
91558Srgrimes *    notice, this list of conditions and the following disclaimer.
101558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright
111558Srgrimes *    notice, this list of conditions and the following disclaimer in the
121558Srgrimes *    documentation and/or other materials provided with the distribution.
131558Srgrimes * 4. Neither the name of the University nor the names of its contributors
141558Srgrimes *    may be used to endorse or promote products derived from this software
151558Srgrimes *    without specific prior written permission.
161558Srgrimes *
171558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
181558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
191558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
201558Srgrimes * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
211558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
221558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
231558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
241558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
251558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
261558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
271558Srgrimes * SUCH DAMAGE.
281558Srgrimes */
291558Srgrimes
301558Srgrimes#ifndef lint
3136997Scharnier#if 0
3223672Speterstatic char sccsid[] = "@(#)tape.c	8.4 (Berkeley) 5/1/95";
3336997Scharnier#endif
3436997Scharnierstatic const char rcsid[] =
3550476Speter  "$FreeBSD$";
361558Srgrimes#endif /* not lint */
371558Srgrimes
381558Srgrimes#include <sys/param.h>
391558Srgrimes#include <sys/socket.h>
401558Srgrimes#include <sys/wait.h>
4190496Siedowse#include <sys/stat.h>
421558Srgrimes
4323672Speter#include <ufs/ufs/dinode.h>
441558Srgrimes#include <ufs/ffs/fs.h>
451558Srgrimes
461558Srgrimes#include <protocols/dumprestore.h>
471558Srgrimes
481558Srgrimes#include <errno.h>
491558Srgrimes#include <fcntl.h>
50103949Smike#include <limits.h>
511558Srgrimes#include <setjmp.h>
521558Srgrimes#include <signal.h>
531558Srgrimes#include <stdio.h>
541558Srgrimes#include <stdlib.h>
551558Srgrimes#include <string.h>
56217769Smckusick#include <time.h>
571558Srgrimes#include <unistd.h>
581558Srgrimes
591558Srgrimes#include "dump.h"
601558Srgrimes
611558Srgrimesint	writesize;		/* size of malloc()ed buffer for tape */
6298542Smckusickint64_t	lastspclrec = -1;	/* tape block number of last written header */
631558Srgrimesint	trecno = 0;		/* next record to write in current block */
641558Srgrimesextern	long blocksperfile;	/* number of blocks per output file */
651558Srgrimeslong	blocksthisvol;		/* number of blocks on current output file */
661558Srgrimesextern	int ntrec;		/* blocking factor on tape */
671558Srgrimesextern	int cartridge;
681558Srgrimesextern	char *host;
691558Srgrimeschar	*nexttape;
70128175SgreenFILE	*popenfp = NULL;
711558Srgrimes
7292837Simpstatic	int atomic(ssize_t (*)(), int, char *, int);
7392837Simpstatic	void doslave(int, int);
7492837Simpstatic	void enslave(void);
7592837Simpstatic	void flushtape(void);
7692837Simpstatic	void killall(void);
7792837Simpstatic	void rollforward(void);
781558Srgrimes
791558Srgrimes/*
801558Srgrimes * Concurrent dump mods (Caltech) - disk block reading and tape writing
811558Srgrimes * are exported to several slave processes.  While one slave writes the
821558Srgrimes * tape, the others read disk blocks; they pass control of the tape in
83102231Strhodes * a ring via signals. The parent process traverses the file system and
841558Srgrimes * sends writeheader()'s and lists of daddr's to the slaves via pipes.
851558Srgrimes * The following structure defines the instruction packets sent to slaves.
861558Srgrimes */
871558Srgrimesstruct req {
8898542Smckusick	ufs2_daddr_t dblk;
891558Srgrimes	int count;
901558Srgrimes};
911558Srgrimesint reqsiz;
921558Srgrimes
931558Srgrimes#define SLAVES 3		/* 1 slave writing, 1 reading, 1 for slack */
941558Srgrimesstruct slave {
9598542Smckusick	int64_t tapea;		/* header number at start of this chunk */
9698542Smckusick	int64_t firstrec;	/* record number of this block */
971558Srgrimes	int count;		/* count to next header (used for TS_TAPE */
981558Srgrimes				/* after EOT) */
991558Srgrimes	int inode;		/* inode that we are currently dealing with */
1001558Srgrimes	int fd;			/* FD for this slave */
1011558Srgrimes	int pid;		/* PID for this slave */
1021558Srgrimes	int sent;		/* 1 == we've sent this slave requests */
1031558Srgrimes	char (*tblock)[TP_BSIZE]; /* buffer for data blocks */
1041558Srgrimes	struct req *req;	/* buffer for requests */
1051558Srgrimes} slaves[SLAVES+1];
1061558Srgrimesstruct slave *slp;
1071558Srgrimes
1081558Srgrimeschar	(*nextblock)[TP_BSIZE];
1091558Srgrimes
1101558Srgrimesint master;		/* pid of master, for sending error signals */
1111558Srgrimesint tenths;		/* length of tape used per block written */
112142968Siedowsestatic volatile sig_atomic_t caught; /* have we caught the signal to proceed? */
113142968Siedowsestatic volatile sig_atomic_t ready; /* reached the lock point without having */
1141558Srgrimes			/* received the SIGUSR2 signal from the prev slave? */
1151558Srgrimesstatic jmp_buf jmpbuf;	/* where to jump to if we are ready when the */
1161558Srgrimes			/* SIGUSR2 arrives from the previous slave */
1171558Srgrimes
1181558Srgrimesint
11992837Simpalloctape(void)
1201558Srgrimes{
1211558Srgrimes	int pgoff = getpagesize() - 1;
1221558Srgrimes	char *buf;
1231558Srgrimes	int i;
1241558Srgrimes
1251558Srgrimes	writesize = ntrec * TP_BSIZE;
1261558Srgrimes	reqsiz = (ntrec + 1) * sizeof(struct req);
1271558Srgrimes	/*
1281558Srgrimes	 * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode
1291558Srgrimes	 * (see DEC TU80 User's Guide).  The shorter gaps of 6250-bpi require
1301558Srgrimes	 * repositioning after stopping, i.e, streaming mode, where the gap is
1311558Srgrimes	 * variable, 0.30" to 0.45".  The gap is maximal when the tape stops.
1321558Srgrimes	 */
13322192Sjoerg	if (blocksperfile == 0 && !unlimited)
1341558Srgrimes		tenths = writesize / density +
1351558Srgrimes		    (cartridge ? 16 : density == 625 ? 5 : 8);
1361558Srgrimes	/*
1371558Srgrimes	 * Allocate tape buffer contiguous with the array of instruction
1381558Srgrimes	 * packets, so flushtape() can write them together with one write().
1391558Srgrimes	 * Align tape buffer on page boundary to speed up tape write().
1401558Srgrimes	 */
1411558Srgrimes	for (i = 0; i <= SLAVES; i++) {
1421558Srgrimes		buf = (char *)
1431558Srgrimes		    malloc((unsigned)(reqsiz + writesize + pgoff + TP_BSIZE));
1441558Srgrimes		if (buf == NULL)
1451558Srgrimes			return(0);
1461558Srgrimes		slaves[i].tblock = (char (*)[TP_BSIZE])
1471558Srgrimes		    (((long)&buf[ntrec + 1] + pgoff) &~ pgoff);
1481558Srgrimes		slaves[i].req = (struct req *)slaves[i].tblock - ntrec - 1;
1491558Srgrimes	}
1501558Srgrimes	slp = &slaves[0];
1511558Srgrimes	slp->count = 1;
1521558Srgrimes	slp->tapea = 0;
1531558Srgrimes	slp->firstrec = 0;
1541558Srgrimes	nextblock = slp->tblock;
1551558Srgrimes	return(1);
1561558Srgrimes}
1571558Srgrimes
1581558Srgrimesvoid
15992837Simpwriterec(char *dp, int isspcl)
1601558Srgrimes{
1611558Srgrimes
16298542Smckusick	slp->req[trecno].dblk = (ufs2_daddr_t)0;
1631558Srgrimes	slp->req[trecno].count = 1;
16492837Simp	/* Can't do a structure assignment due to alignment problems */
16546360Smjacob	bcopy(dp, *(nextblock)++, sizeof (union u_spcl));
1661558Srgrimes	if (isspcl)
1671558Srgrimes		lastspclrec = spcl.c_tapea;
1681558Srgrimes	trecno++;
1691558Srgrimes	spcl.c_tapea++;
1701558Srgrimes	if (trecno >= ntrec)
1711558Srgrimes		flushtape();
1721558Srgrimes}
1731558Srgrimes
1741558Srgrimesvoid
17598542Smckusickdumpblock(ufs2_daddr_t blkno, int size)
1761558Srgrimes{
17798542Smckusick	int avail, tpblks;
17898542Smckusick	ufs2_daddr_t dblkno;
1791558Srgrimes
1801558Srgrimes	dblkno = fsbtodb(sblock, blkno);
1811558Srgrimes	tpblks = size >> tp_bshift;
1821558Srgrimes	while ((avail = MIN(tpblks, ntrec - trecno)) > 0) {
1831558Srgrimes		slp->req[trecno].dblk = dblkno;
1841558Srgrimes		slp->req[trecno].count = avail;
1851558Srgrimes		trecno += avail;
1861558Srgrimes		spcl.c_tapea += avail;
1871558Srgrimes		if (trecno >= ntrec)
1881558Srgrimes			flushtape();
1891558Srgrimes		dblkno += avail << (tp_bshift - dev_bshift);
1901558Srgrimes		tpblks -= avail;
1911558Srgrimes	}
1921558Srgrimes}
1931558Srgrimes
1941558Srgrimesint	nogripe = 0;
1951558Srgrimes
1961558Srgrimesvoid
19792837Simptperror(int signo __unused)
1981558Srgrimes{
1991558Srgrimes
2001558Srgrimes	if (pipeout) {
2011558Srgrimes		msg("write error on %s\n", tape);
2021558Srgrimes		quit("Cannot recover\n");
2031558Srgrimes		/* NOTREACHED */
2041558Srgrimes	}
20599530Siedowse	msg("write error %ld blocks into volume %d\n", blocksthisvol, tapeno);
2061558Srgrimes	broadcast("DUMP WRITE ERROR!\n");
2071558Srgrimes	if (!query("Do you want to restart?"))
2081558Srgrimes		dumpabort(0);
2091558Srgrimes	msg("Closing this volume.  Prepare to restart with new media;\n");
2101558Srgrimes	msg("this dump volume will be rewritten.\n");
2111558Srgrimes	killall();
2121558Srgrimes	nogripe = 1;
2131558Srgrimes	close_rewind();
2141558Srgrimes	Exit(X_REWRITE);
2151558Srgrimes}
2161558Srgrimes
2171558Srgrimesvoid
21892837Simpsigpipe(int signo __unused)
2191558Srgrimes{
2201558Srgrimes
2211558Srgrimes	quit("Broken pipe\n");
2221558Srgrimes}
2231558Srgrimes
2241558Srgrimesstatic void
22592837Simpflushtape(void)
2261558Srgrimes{
2271558Srgrimes	int i, blks, got;
22898542Smckusick	int64_t lastfirstrec;
2291558Srgrimes
2301558Srgrimes	int siz = (char *)nextblock - (char *)slp->req;
2311558Srgrimes
2321558Srgrimes	slp->req[trecno].count = 0;			/* Sentinel */
2331558Srgrimes
2341558Srgrimes	if (atomic(write, slp->fd, (char *)slp->req, siz) != siz)
2351558Srgrimes		quit("error writing command pipe: %s\n", strerror(errno));
2361558Srgrimes	slp->sent = 1; /* we sent a request, read the response later */
2371558Srgrimes
2381558Srgrimes	lastfirstrec = slp->firstrec;
2391558Srgrimes
2401558Srgrimes	if (++slp >= &slaves[SLAVES])
2411558Srgrimes		slp = &slaves[0];
2421558Srgrimes
2431558Srgrimes	/* Read results back from next slave */
2441558Srgrimes	if (slp->sent) {
2451558Srgrimes		if (atomic(read, slp->fd, (char *)&got, sizeof got)
2461558Srgrimes		    != sizeof got) {
2471558Srgrimes			perror("  DUMP: error reading command pipe in master");
2481558Srgrimes			dumpabort(0);
2491558Srgrimes		}
2501558Srgrimes		slp->sent = 0;
2511558Srgrimes
2521558Srgrimes		/* Check for end of tape */
2531558Srgrimes		if (got < writesize) {
2541558Srgrimes			msg("End of tape detected\n");
2551558Srgrimes
2561558Srgrimes			/*
2571558Srgrimes			 * Drain the results, don't care what the values were.
2581558Srgrimes			 * If we read them here then trewind won't...
2591558Srgrimes			 */
2601558Srgrimes			for (i = 0; i < SLAVES; i++) {
2611558Srgrimes				if (slaves[i].sent) {
2621558Srgrimes					if (atomic(read, slaves[i].fd,
2631558Srgrimes					    (char *)&got, sizeof got)
2641558Srgrimes					    != sizeof got) {
2651558Srgrimes						perror("  DUMP: error reading command pipe in master");
2661558Srgrimes						dumpabort(0);
2671558Srgrimes					}
2681558Srgrimes					slaves[i].sent = 0;
2691558Srgrimes				}
2701558Srgrimes			}
2711558Srgrimes
2721558Srgrimes			close_rewind();
2731558Srgrimes			rollforward();
2741558Srgrimes			return;
2751558Srgrimes		}
2761558Srgrimes	}
2771558Srgrimes
2781558Srgrimes	blks = 0;
2791558Srgrimes	if (spcl.c_type != TS_END) {
2801558Srgrimes		for (i = 0; i < spcl.c_count; i++)
2811558Srgrimes			if (spcl.c_addr[i] != 0)
2821558Srgrimes				blks++;
2831558Srgrimes	}
2841558Srgrimes	slp->count = lastspclrec + blks + 1 - spcl.c_tapea;
2851558Srgrimes	slp->tapea = spcl.c_tapea;
2861558Srgrimes	slp->firstrec = lastfirstrec + ntrec;
2871558Srgrimes	slp->inode = curino;
2881558Srgrimes	nextblock = slp->tblock;
2891558Srgrimes	trecno = 0;
2901558Srgrimes	asize += tenths;
2911558Srgrimes	blockswritten += ntrec;
2921558Srgrimes	blocksthisvol += ntrec;
29322192Sjoerg	if (!pipeout && !unlimited && (blocksperfile ?
2941558Srgrimes	    (blocksthisvol >= blocksperfile) : (asize > tsize))) {
2951558Srgrimes		close_rewind();
2961558Srgrimes		startnewtape(0);
2971558Srgrimes	}
2981558Srgrimes	timeest();
2991558Srgrimes}
3001558Srgrimes
3011558Srgrimesvoid
30292837Simptrewind(void)
3031558Srgrimes{
30490496Siedowse	struct stat sb;
3051558Srgrimes	int f;
3061558Srgrimes	int got;
3071558Srgrimes
3081558Srgrimes	for (f = 0; f < SLAVES; f++) {
3091558Srgrimes		/*
3108871Srgrimes		 * Drain the results, but unlike EOT we DO (or should) care
3118871Srgrimes		 * what the return values were, since if we detect EOT after
3128871Srgrimes		 * we think we've written the last blocks to the tape anyway,
3131558Srgrimes		 * we have to replay those blocks with rollforward.
3141558Srgrimes		 *
3158871Srgrimes		 * fixme: punt for now.
3161558Srgrimes		 */
3171558Srgrimes		if (slaves[f].sent) {
3181558Srgrimes			if (atomic(read, slaves[f].fd, (char *)&got, sizeof got)
3191558Srgrimes			    != sizeof got) {
3201558Srgrimes				perror("  DUMP: error reading command pipe in master");
3211558Srgrimes				dumpabort(0);
3221558Srgrimes			}
3231558Srgrimes			slaves[f].sent = 0;
3241558Srgrimes			if (got != writesize) {
3251558Srgrimes				msg("EOT detected in last 2 tape records!\n");
3261558Srgrimes				msg("Use a longer tape, decrease the size estimate\n");
3271558Srgrimes				quit("or use no size estimate at all.\n");
3281558Srgrimes			}
3291558Srgrimes		}
3301558Srgrimes		(void) close(slaves[f].fd);
3311558Srgrimes	}
3321558Srgrimes	while (wait((int *)NULL) >= 0)	/* wait for any signals from slaves */
3331558Srgrimes		/* void */;
3341558Srgrimes
3351558Srgrimes	if (pipeout)
3361558Srgrimes		return;
3371558Srgrimes
3381558Srgrimes	msg("Closing %s\n", tape);
3391558Srgrimes
340128175Sgreen	if (popenout) {
341128175Sgreen		tapefd = -1;
342128175Sgreen		(void)pclose(popenfp);
343128175Sgreen		popenfp = NULL;
344128175Sgreen		return;
345128175Sgreen	}
3461558Srgrimes#ifdef RDUMP
3471558Srgrimes	if (host) {
3481558Srgrimes		rmtclose();
3491558Srgrimes		while (rmtopen(tape, 0) < 0)
3501558Srgrimes			sleep(10);
3511558Srgrimes		rmtclose();
3521558Srgrimes		return;
3531558Srgrimes	}
3541558Srgrimes#endif
35590496Siedowse	if (fstat(tapefd, &sb) == 0 && S_ISFIFO(sb.st_mode)) {
35690496Siedowse		(void)close(tapefd);
35790496Siedowse		return;
35890496Siedowse	}
3591558Srgrimes	(void) close(tapefd);
3601558Srgrimes	while ((f = open(tape, 0)) < 0)
3611558Srgrimes		sleep (10);
3621558Srgrimes	(void) close(f);
3631558Srgrimes}
3641558Srgrimes
3651558Srgrimesvoid
3661558Srgrimesclose_rewind()
3671558Srgrimes{
36830341Sjoerg	time_t tstart_changevol, tend_changevol;
36930341Sjoerg
3701558Srgrimes	trewind();
3711558Srgrimes	if (nexttape)
3721558Srgrimes		return;
37330341Sjoerg	(void)time((time_t *)&(tstart_changevol));
3741558Srgrimes	if (!nogripe) {
3751558Srgrimes		msg("Change Volumes: Mount volume #%d\n", tapeno+1);
37671750Sphk		broadcast("CHANGE DUMP VOLUMES!\a\a\n");
3771558Srgrimes	}
3781558Srgrimes	while (!query("Is the new volume mounted and ready to go?"))
3791558Srgrimes		if (query("Do you want to abort?")) {
3801558Srgrimes			dumpabort(0);
3811558Srgrimes			/*NOTREACHED*/
3821558Srgrimes		}
38330341Sjoerg	(void)time((time_t *)&(tend_changevol));
38430341Sjoerg	if ((tstart_changevol != (time_t)-1) && (tend_changevol != (time_t)-1))
38530341Sjoerg		tstart_writing += (tend_changevol - tstart_changevol);
3861558Srgrimes}
3871558Srgrimes
3881558Srgrimesvoid
38992837Simprollforward(void)
3901558Srgrimes{
39186473Siedowse	struct req *p, *q, *prev;
39286473Siedowse	struct slave *tslp;
39398542Smckusick	int i, size, got;
39498542Smckusick	int64_t savedtapea;
3951558Srgrimes	union u_spcl *ntb, *otb;
3961558Srgrimes	tslp = &slaves[SLAVES];
3971558Srgrimes	ntb = (union u_spcl *)tslp->tblock[1];
3981558Srgrimes
3991558Srgrimes	/*
4008871Srgrimes	 * Each of the N slaves should have requests that need to
4018871Srgrimes	 * be replayed on the next tape.  Use the extra slave buffers
4028871Srgrimes	 * (slaves[SLAVES]) to construct request lists to be sent to
4031558Srgrimes	 * each slave in turn.
4041558Srgrimes	 */
4051558Srgrimes	for (i = 0; i < SLAVES; i++) {
4061558Srgrimes		q = &tslp->req[1];
4071558Srgrimes		otb = (union u_spcl *)slp->tblock;
4081558Srgrimes
4091558Srgrimes		/*
4108871Srgrimes		 * For each request in the current slave, copy it to tslp.
4111558Srgrimes		 */
4121558Srgrimes
4131558Srgrimes		prev = NULL;
4141558Srgrimes		for (p = slp->req; p->count > 0; p += p->count) {
4151558Srgrimes			*q = *p;
4161558Srgrimes			if (p->dblk == 0)
4171558Srgrimes				*ntb++ = *otb++; /* copy the datablock also */
4181558Srgrimes			prev = q;
4191558Srgrimes			q += q->count;
4201558Srgrimes		}
4211558Srgrimes		if (prev == NULL)
4221558Srgrimes			quit("rollforward: protocol botch");
4231558Srgrimes		if (prev->dblk != 0)
4241558Srgrimes			prev->count -= 1;
4251558Srgrimes		else
4261558Srgrimes			ntb--;
4271558Srgrimes		q -= 1;
4281558Srgrimes		q->count = 0;
4291558Srgrimes		q = &tslp->req[0];
4301558Srgrimes		if (i == 0) {
4311558Srgrimes			q->dblk = 0;
4321558Srgrimes			q->count = 1;
4331558Srgrimes			trecno = 0;
4341558Srgrimes			nextblock = tslp->tblock;
4351558Srgrimes			savedtapea = spcl.c_tapea;
4361558Srgrimes			spcl.c_tapea = slp->tapea;
4371558Srgrimes			startnewtape(0);
4381558Srgrimes			spcl.c_tapea = savedtapea;
4391558Srgrimes			lastspclrec = savedtapea - 1;
4401558Srgrimes		}
4411558Srgrimes		size = (char *)ntb - (char *)q;
4421558Srgrimes		if (atomic(write, slp->fd, (char *)q, size) != size) {
4431558Srgrimes			perror("  DUMP: error writing command pipe");
4441558Srgrimes			dumpabort(0);
4451558Srgrimes		}
4461558Srgrimes		slp->sent = 1;
4471558Srgrimes		if (++slp >= &slaves[SLAVES])
4481558Srgrimes			slp = &slaves[0];
4491558Srgrimes
4501558Srgrimes		q->count = 1;
4511558Srgrimes
4521558Srgrimes		if (prev->dblk != 0) {
4531558Srgrimes			/*
4548871Srgrimes			 * If the last one was a disk block, make the
4558871Srgrimes			 * first of this one be the last bit of that disk
4561558Srgrimes			 * block...
4571558Srgrimes			 */
4581558Srgrimes			q->dblk = prev->dblk +
4591558Srgrimes				prev->count * (TP_BSIZE / DEV_BSIZE);
4601558Srgrimes			ntb = (union u_spcl *)tslp->tblock;
4611558Srgrimes		} else {
4621558Srgrimes			/*
4638871Srgrimes			 * It wasn't a disk block.  Copy the data to its
4641558Srgrimes			 * new location in the buffer.
4651558Srgrimes			 */
4661558Srgrimes			q->dblk = 0;
4671558Srgrimes			*((union u_spcl *)tslp->tblock) = *ntb;
4681558Srgrimes			ntb = (union u_spcl *)tslp->tblock[1];
4691558Srgrimes		}
4701558Srgrimes	}
4711558Srgrimes	slp->req[0] = *q;
4721558Srgrimes	nextblock = slp->tblock;
4731558Srgrimes	if (q->dblk == 0)
4741558Srgrimes		nextblock++;
4751558Srgrimes	trecno = 1;
4761558Srgrimes
4771558Srgrimes	/*
4781558Srgrimes	 * Clear the first slaves' response.  One hopes that it
4791558Srgrimes	 * worked ok, otherwise the tape is much too short!
4801558Srgrimes	 */
4811558Srgrimes	if (slp->sent) {
4821558Srgrimes		if (atomic(read, slp->fd, (char *)&got, sizeof got)
4831558Srgrimes		    != sizeof got) {
4841558Srgrimes			perror("  DUMP: error reading command pipe in master");
4851558Srgrimes			dumpabort(0);
4861558Srgrimes		}
4871558Srgrimes		slp->sent = 0;
4881558Srgrimes
4891558Srgrimes		if (got != writesize) {
4901558Srgrimes			quit("EOT detected at start of the tape!\n");
4911558Srgrimes		}
4921558Srgrimes	}
4931558Srgrimes}
4941558Srgrimes
4951558Srgrimes/*
4961558Srgrimes * We implement taking and restoring checkpoints on the tape level.
4971558Srgrimes * When each tape is opened, a new process is created by forking; this
4981558Srgrimes * saves all of the necessary context in the parent.  The child
4991558Srgrimes * continues the dump; the parent waits around, saving the context.
5001558Srgrimes * If the child returns X_REWRITE, then it had problems writing that tape;
5011558Srgrimes * this causes the parent to fork again, duplicating the context, and
5021558Srgrimes * everything continues as if nothing had happened.
5031558Srgrimes */
5041558Srgrimesvoid
50592837Simpstartnewtape(int top)
5061558Srgrimes{
5071558Srgrimes	int	parentpid;
5081558Srgrimes	int	childpid;
5091558Srgrimes	int	status;
5101558Srgrimes	char	*p;
5111558Srgrimes	sig_t	interrupt_save;
5121558Srgrimes
5131558Srgrimes	interrupt_save = signal(SIGINT, SIG_IGN);
5141558Srgrimes	parentpid = getpid();
5151558Srgrimes
5161558Srgrimesrestore_check_point:
5171558Srgrimes	(void)signal(SIGINT, interrupt_save);
5181558Srgrimes	/*
5191558Srgrimes	 *	All signals are inherited...
5201558Srgrimes	 */
52190743Siedowse	setproctitle(NULL);	/* Restore the proctitle. */
5221558Srgrimes	childpid = fork();
5231558Srgrimes	if (childpid < 0) {
5241558Srgrimes		msg("Context save fork fails in parent %d\n", parentpid);
5251558Srgrimes		Exit(X_ABORT);
5261558Srgrimes	}
5271558Srgrimes	if (childpid != 0) {
5281558Srgrimes		/*
5291558Srgrimes		 *	PARENT:
5301558Srgrimes		 *	save the context by waiting
5311558Srgrimes		 *	until the child doing all of the work returns.
5321558Srgrimes		 *	don't catch the interrupt
5331558Srgrimes		 */
5341558Srgrimes		signal(SIGINT, SIG_IGN);
5351558Srgrimes#ifdef TDEBUG
5361558Srgrimes		msg("Tape: %d; parent process: %d child process %d\n",
5371558Srgrimes			tapeno+1, parentpid, childpid);
5381558Srgrimes#endif /* TDEBUG */
539128175Sgreen		if (waitpid(childpid, &status, 0) == -1)
540128175Sgreen			msg("Waiting for child %d: %s\n", childpid,
541128175Sgreen			    strerror(errno));
5421558Srgrimes		if (status & 0xFF) {
5431558Srgrimes			msg("Child %d returns LOB status %o\n",
5441558Srgrimes				childpid, status&0xFF);
5451558Srgrimes		}
5461558Srgrimes		status = (status >> 8) & 0xFF;
5471558Srgrimes#ifdef TDEBUG
5481558Srgrimes		switch(status) {
5491558Srgrimes			case X_FINOK:
5501558Srgrimes				msg("Child %d finishes X_FINOK\n", childpid);
5511558Srgrimes				break;
5528871Srgrimes			case X_ABORT:
5531558Srgrimes				msg("Child %d finishes X_ABORT\n", childpid);
5541558Srgrimes				break;
5551558Srgrimes			case X_REWRITE:
5561558Srgrimes				msg("Child %d finishes X_REWRITE\n", childpid);
5571558Srgrimes				break;
5581558Srgrimes			default:
5591558Srgrimes				msg("Child %d finishes unknown %d\n",
5601558Srgrimes					childpid, status);
5611558Srgrimes				break;
5621558Srgrimes		}
5631558Srgrimes#endif /* TDEBUG */
5641558Srgrimes		switch(status) {
5651558Srgrimes			case X_FINOK:
5661558Srgrimes				Exit(X_FINOK);
5671558Srgrimes			case X_ABORT:
5681558Srgrimes				Exit(X_ABORT);
5691558Srgrimes			case X_REWRITE:
5701558Srgrimes				goto restore_check_point;
5711558Srgrimes			default:
5721558Srgrimes				msg("Bad return code from dump: %d\n", status);
5731558Srgrimes				Exit(X_ABORT);
5741558Srgrimes		}
5751558Srgrimes		/*NOTREACHED*/
5761558Srgrimes	} else {	/* we are the child; just continue */
5771558Srgrimes#ifdef TDEBUG
5781558Srgrimes		sleep(4);	/* allow time for parent's message to get out */
5791558Srgrimes		msg("Child on Tape %d has parent %d, my pid = %d\n",
5801558Srgrimes			tapeno+1, parentpid, getpid());
5811558Srgrimes#endif /* TDEBUG */
5821558Srgrimes		/*
5831558Srgrimes		 * If we have a name like "/dev/rmt0,/dev/rmt1",
5841558Srgrimes		 * use the name before the comma first, and save
5851558Srgrimes		 * the remaining names for subsequent volumes.
5861558Srgrimes		 */
5871558Srgrimes		tapeno++;               /* current tape sequence */
58823672Speter		if (nexttape || strchr(tape, ',')) {
5891558Srgrimes			if (nexttape && *nexttape)
5901558Srgrimes				tape = nexttape;
59123672Speter			if ((p = strchr(tape, ',')) != NULL) {
5921558Srgrimes				*p = '\0';
5931558Srgrimes				nexttape = p + 1;
5941558Srgrimes			} else
5951558Srgrimes				nexttape = NULL;
5961558Srgrimes			msg("Dumping volume %d on %s\n", tapeno, tape);
5971558Srgrimes		}
598128175Sgreen		if (pipeout) {
599128175Sgreen			tapefd = STDOUT_FILENO;
600128175Sgreen		} else if (popenout) {
601128175Sgreen			char volno[sizeof("2147483647")];
602128175Sgreen
603128175Sgreen			(void)sprintf(volno, "%d", spcl.c_volume + 1);
604128175Sgreen			if (setenv("DUMP_VOLUME", volno, 1) == -1) {
605128175Sgreen				msg("Cannot set $DUMP_VOLUME.\n");
606128175Sgreen				dumpabort(0);
607128175Sgreen			}
608128175Sgreen			popenfp = popen(popenout, "w");
609128175Sgreen			if (popenfp == NULL) {
610128175Sgreen				msg("Cannot open output pipeline \"%s\".\n",
611128175Sgreen				    popenout);
612128175Sgreen				dumpabort(0);
613128175Sgreen			}
614128175Sgreen			tapefd = fileno(popenfp);
615128175Sgreen		} else {
6161558Srgrimes#ifdef RDUMP
617128175Sgreen			while ((tapefd = (host ? rmtopen(tape, 2) :
618128175Sgreen				open(tape, O_WRONLY|O_CREAT, 0666))) < 0)
6191558Srgrimes#else
620128175Sgreen			while ((tapefd =
621128175Sgreen			    open(tape, O_WRONLY|O_CREAT, 0666)) < 0)
6221558Srgrimes#endif
623128175Sgreen			    {
624128175Sgreen				msg("Cannot open output \"%s\".\n", tape);
625128175Sgreen				if (!query("Do you want to retry the open?"))
626128175Sgreen					dumpabort(0);
627128175Sgreen			}
6281558Srgrimes		}
6291558Srgrimes
6301558Srgrimes		enslave();  /* Share open tape file descriptor with slaves */
631128175Sgreen		if (popenout)
632128175Sgreen			close(tapefd);	/* Give up our copy of it. */
63390742Siedowse		signal(SIGINFO, infosch);
6341558Srgrimes
6351558Srgrimes		asize = 0;
6361558Srgrimes		blocksthisvol = 0;
6371558Srgrimes		if (top)
6381558Srgrimes			newtape++;		/* new tape signal */
6398871Srgrimes		spcl.c_count = slp->count;
6401558Srgrimes		/*
6411558Srgrimes		 * measure firstrec in TP_BSIZE units since restore doesn't
6421558Srgrimes		 * know the correct ntrec value...
6431558Srgrimes		 */
6441558Srgrimes		spcl.c_firstrec = slp->firstrec;
6451558Srgrimes		spcl.c_volume++;
6461558Srgrimes		spcl.c_type = TS_TAPE;
6471558Srgrimes		writeheader((ino_t)slp->inode);
6481558Srgrimes		if (tapeno > 1)
6491558Srgrimes			msg("Volume %d begins with blocks from inode %d\n",
6501558Srgrimes				tapeno, slp->inode);
6511558Srgrimes	}
6521558Srgrimes}
6531558Srgrimes
6541558Srgrimesvoid
65592837Simpdumpabort(int signo __unused)
6561558Srgrimes{
6571558Srgrimes
6581558Srgrimes	if (master != 0 && master != getpid())
6591558Srgrimes		/* Signals master to call dumpabort */
6601558Srgrimes		(void) kill(master, SIGTERM);
6611558Srgrimes	else {
6621558Srgrimes		killall();
6631558Srgrimes		msg("The ENTIRE dump is aborted.\n");
6641558Srgrimes	}
6651558Srgrimes#ifdef RDUMP
6661558Srgrimes	rmtclose();
6671558Srgrimes#endif
6681558Srgrimes	Exit(X_ABORT);
6691558Srgrimes}
6701558Srgrimes
67118286Sbdevoid
6721558SrgrimesExit(status)
6731558Srgrimes	int status;
6741558Srgrimes{
6751558Srgrimes
6761558Srgrimes#ifdef TDEBUG
6771558Srgrimes	msg("pid = %d exits with status %d\n", getpid(), status);
6781558Srgrimes#endif /* TDEBUG */
6791558Srgrimes	exit(status);
6801558Srgrimes}
6811558Srgrimes
6821558Srgrimes/*
6831558Srgrimes * proceed - handler for SIGUSR2, used to synchronize IO between the slaves.
6841558Srgrimes */
6851558Srgrimesvoid
68692837Simpproceed(int signo __unused)
6871558Srgrimes{
6881558Srgrimes
6891558Srgrimes	if (ready)
6901558Srgrimes		longjmp(jmpbuf, 1);
6911558Srgrimes	caught++;
6921558Srgrimes}
6931558Srgrimes
6941558Srgrimesvoid
69592837Simpenslave(void)
6961558Srgrimes{
6971558Srgrimes	int cmd[2];
69886473Siedowse	int i, j;
6991558Srgrimes
7001558Srgrimes	master = getpid();
7011558Srgrimes
7021558Srgrimes	signal(SIGTERM, dumpabort);  /* Slave sends SIGTERM on dumpabort() */
7031558Srgrimes	signal(SIGPIPE, sigpipe);
7041558Srgrimes	signal(SIGUSR1, tperror);    /* Slave sends SIGUSR1 on tape errors */
7051558Srgrimes	signal(SIGUSR2, proceed);    /* Slave sends SIGUSR2 to next slave */
7061558Srgrimes
7071558Srgrimes	for (i = 0; i < SLAVES; i++) {
7081558Srgrimes		if (i == slp - &slaves[0]) {
7091558Srgrimes			caught = 1;
7101558Srgrimes		} else {
7111558Srgrimes			caught = 0;
7121558Srgrimes		}
7131558Srgrimes
7141558Srgrimes		if (socketpair(AF_UNIX, SOCK_STREAM, 0, cmd) < 0 ||
7151558Srgrimes		    (slaves[i].pid = fork()) < 0)
7161558Srgrimes			quit("too many slaves, %d (recompile smaller): %s\n",
7171558Srgrimes			    i, strerror(errno));
7181558Srgrimes
7191558Srgrimes		slaves[i].fd = cmd[1];
7201558Srgrimes		slaves[i].sent = 0;
7211558Srgrimes		if (slaves[i].pid == 0) { 	    /* Slave starts up here */
7221558Srgrimes			for (j = 0; j <= i; j++)
7231558Srgrimes			        (void) close(slaves[j].fd);
7241558Srgrimes			signal(SIGINT, SIG_IGN);    /* Master handles this */
7251558Srgrimes			doslave(cmd[0], i);
7261558Srgrimes			Exit(X_FINOK);
7271558Srgrimes		}
7281558Srgrimes	}
7298871Srgrimes
7301558Srgrimes	for (i = 0; i < SLAVES; i++)
7318871Srgrimes		(void) atomic(write, slaves[i].fd,
7328871Srgrimes			      (char *) &slaves[(i + 1) % SLAVES].pid,
7331558Srgrimes		              sizeof slaves[0].pid);
7348871Srgrimes
7358871Srgrimes	master = 0;
7361558Srgrimes}
7371558Srgrimes
7381558Srgrimesvoid
73992837Simpkillall(void)
7401558Srgrimes{
74186473Siedowse	int i;
7421558Srgrimes
7431558Srgrimes	for (i = 0; i < SLAVES; i++)
74425514Sjoerg		if (slaves[i].pid > 0) {
7451558Srgrimes			(void) kill(slaves[i].pid, SIGKILL);
74625514Sjoerg			slaves[i].sent = 0;
74725514Sjoerg		}
7481558Srgrimes}
7491558Srgrimes
7501558Srgrimes/*
7511558Srgrimes * Synchronization - each process has a lockfile, and shares file
7521558Srgrimes * descriptors to the following process's lockfile.  When our write
7531558Srgrimes * completes, we release our lock on the following process's lock-
7541558Srgrimes * file, allowing the following process to lock it and proceed. We
7551558Srgrimes * get the lock back for the next cycle by swapping descriptors.
7561558Srgrimes */
7571558Srgrimesstatic void
75892837Simpdoslave(int cmd, int slave_number)
7591558Srgrimes{
76086473Siedowse	int nread;
7611558Srgrimes	int nextslave, size, wrote, eot_count;
7621558Srgrimes
7631558Srgrimes	/*
7641558Srgrimes	 * Need our own seek pointer.
7651558Srgrimes	 */
7661558Srgrimes	(void) close(diskfd);
7671558Srgrimes	if ((diskfd = open(disk, O_RDONLY)) < 0)
7681558Srgrimes		quit("slave couldn't reopen disk: %s\n", strerror(errno));
7691558Srgrimes
7701558Srgrimes	/*
7711558Srgrimes	 * Need the pid of the next slave in the loop...
7721558Srgrimes	 */
7731558Srgrimes	if ((nread = atomic(read, cmd, (char *)&nextslave, sizeof nextslave))
7741558Srgrimes	    != sizeof nextslave) {
7751558Srgrimes		quit("master/slave protocol botched - didn't get pid of next slave.\n");
7761558Srgrimes	}
7771558Srgrimes
7781558Srgrimes	/*
7791558Srgrimes	 * Get list of blocks to dump, read the blocks into tape buffer
7801558Srgrimes	 */
7811558Srgrimes	while ((nread = atomic(read, cmd, (char *)slp->req, reqsiz)) == reqsiz) {
78286473Siedowse		struct req *p = slp->req;
7831558Srgrimes
7841558Srgrimes		for (trecno = 0; trecno < ntrec;
7851558Srgrimes		     trecno += p->count, p += p->count) {
7861558Srgrimes			if (p->dblk) {
7871558Srgrimes				bread(p->dblk, slp->tblock[trecno],
7881558Srgrimes					p->count * TP_BSIZE);
7891558Srgrimes			} else {
7901558Srgrimes				if (p->count != 1 || atomic(read, cmd,
7918871Srgrimes				    (char *)slp->tblock[trecno],
7921558Srgrimes				    TP_BSIZE) != TP_BSIZE)
7931558Srgrimes				       quit("master/slave protocol botched.\n");
7941558Srgrimes			}
7951558Srgrimes		}
7961558Srgrimes		if (setjmp(jmpbuf) == 0) {
7971558Srgrimes			ready = 1;
7981558Srgrimes			if (!caught)
7991558Srgrimes				(void) pause();
8001558Srgrimes		}
8011558Srgrimes		ready = 0;
8021558Srgrimes		caught = 0;
8031558Srgrimes
8041558Srgrimes		/* Try to write the data... */
8051558Srgrimes		eot_count = 0;
8061558Srgrimes		size = 0;
8071558Srgrimes
80899562Siedowse		wrote = 0;
8091558Srgrimes		while (eot_count < 10 && size < writesize) {
8101558Srgrimes#ifdef RDUMP
8111558Srgrimes			if (host)
8121558Srgrimes				wrote = rmtwrite(slp->tblock[0]+size,
8131558Srgrimes				    writesize-size);
8141558Srgrimes			else
8151558Srgrimes#endif
8161558Srgrimes				wrote = write(tapefd, slp->tblock[0]+size,
8171558Srgrimes				    writesize-size);
8181558Srgrimes#ifdef WRITEDEBUG
8191558Srgrimes			printf("slave %d wrote %d\n", slave_number, wrote);
8201558Srgrimes#endif
8218871Srgrimes			if (wrote < 0)
8221558Srgrimes				break;
8231558Srgrimes			if (wrote == 0)
8241558Srgrimes				eot_count++;
8251558Srgrimes			size += wrote;
8261558Srgrimes		}
8271558Srgrimes
8281558Srgrimes#ifdef WRITEDEBUG
8298871Srgrimes		if (size != writesize)
8301558Srgrimes		 printf("slave %d only wrote %d out of %d bytes and gave up.\n",
8311558Srgrimes		     slave_number, size, writesize);
8321558Srgrimes#endif
8331558Srgrimes
83439256Sgibbs		/*
83539256Sgibbs		 * Handle ENOSPC as an EOT condition.
83639256Sgibbs		 */
83739256Sgibbs		if (wrote < 0 && errno == ENOSPC) {
83839256Sgibbs			wrote = 0;
83939256Sgibbs			eot_count++;
84039256Sgibbs		}
84139256Sgibbs
8421558Srgrimes		if (eot_count > 0)
8431558Srgrimes			size = 0;
8441558Srgrimes
84525514Sjoerg		if (wrote < 0) {
8461558Srgrimes			(void) kill(master, SIGUSR1);
8471558Srgrimes			for (;;)
8481558Srgrimes				(void) sigpause(0);
8491558Srgrimes		} else {
8501558Srgrimes			/*
8511558Srgrimes			 * pass size of write back to master
8521558Srgrimes			 * (for EOT handling)
8531558Srgrimes			 */
8541558Srgrimes			(void) atomic(write, cmd, (char *)&size, sizeof size);
8558871Srgrimes		}
8561558Srgrimes
8571558Srgrimes		/*
8581558Srgrimes		 * If partial write, don't want next slave to go.
8591558Srgrimes		 * Also jolts him awake.
8601558Srgrimes		 */
8611558Srgrimes		(void) kill(nextslave, SIGUSR2);
8621558Srgrimes	}
8631558Srgrimes	if (nread != 0)
8641558Srgrimes		quit("error reading command pipe: %s\n", strerror(errno));
8651558Srgrimes}
8661558Srgrimes
8671558Srgrimes/*
8681558Srgrimes * Since a read from a pipe may not return all we asked for,
8691558Srgrimes * or a write may not write all we ask if we get a signal,
8701558Srgrimes * loop until the count is satisfied (or error).
8711558Srgrimes */
8721558Srgrimesstatic int
87392837Simpatomic(ssize_t (*func)(), int fd, char *buf, int count)
8741558Srgrimes{
8751558Srgrimes	int got, need = count;
8761558Srgrimes
8771558Srgrimes	while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0)
8781558Srgrimes		buf += got;
8791558Srgrimes	return (got < 0 ? got : count - need);
8801558Srgrimes}
881