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