147082Smckusick /*-
261484Sbostic * Copyright (c) 1980, 1991, 1993
361484Sbostic * The Regents of the University of California. All rights reserved.
447082Smckusick *
547082Smckusick * %sccs.include.redist.c%
622040Sdist */
722040Sdist
817527Ssam #ifndef lint
9*69158Smckusick static char sccsid[] = "@(#)tape.c 8.4 (Berkeley) 05/01/95";
1046585Storek #endif /* not lint */
1117527Ssam
1257725Smckusick #include <sys/param.h>
1357725Smckusick #include <sys/socket.h>
1457725Smckusick #include <sys/time.h>
1557725Smckusick #include <sys/wait.h>
1650505Smckusick #ifdef sunos
1757725Smckusick #include <sys/vnode.h>
1857725Smckusick
1951605Sbostic #include <ufs/fs.h>
2057725Smckusick #include <ufs/inode.h>
2150505Smckusick #else
22*69158Smckusick #include <ufs/ufs/dinode.h>
2351605Sbostic #include <ufs/ffs/fs.h>
2450505Smckusick #endif
2557725Smckusick
2646795Sbostic #include <protocols/dumprestore.h>
2757725Smckusick
2846585Storek #include <errno.h>
2957725Smckusick #include <fcntl.h>
3050504Smckusick #include <setjmp.h>
3157725Smckusick #include <signal.h>
3257725Smckusick #include <stdio.h>
3346795Sbostic #ifdef __STDC__
3446795Sbostic #include <stdlib.h>
3546795Sbostic #include <string.h>
3657725Smckusick #include <unistd.h>
3757725Smckusick #else
3857725Smckusick int write(), read();
3946795Sbostic #endif
4057725Smckusick
4146795Sbostic #include "dump.h"
4239128Smckusick #include "pathnames.h"
431425Sroot
4429899Smckusick int writesize; /* size of malloc()ed buffer for tape */
4529899Smckusick long lastspclrec = -1; /* tape block number of last written header */
4629899Smckusick int trecno = 0; /* next record to write in current block */
4746614Smckusick extern long blocksperfile; /* number of blocks per output file */
4848621Skarels long blocksthisvol; /* number of blocks on current output file */
4948621Skarels extern int ntrec; /* blocking factor on tape */
5048621Skarels extern int cartridge;
5150504Smckusick extern char *host;
5247056Skarels char *nexttape;
5346585Storek
5457725Smckusick static int atomic __P((int (*)(), int, char *, int));
5557725Smckusick static void doslave __P((int, int));
5657725Smckusick static void enslave __P((void));
5757725Smckusick static void flushtape __P((void));
5857725Smckusick static void killall __P((void));
5957725Smckusick static void rollforward __P((void));
6057725Smckusick
6110911Ssam /*
6224181Smckusick * Concurrent dump mods (Caltech) - disk block reading and tape writing
6318012Smckusick * are exported to several slave processes. While one slave writes the
6418012Smckusick * tape, the others read disk blocks; they pass control of the tape in
6550504Smckusick * a ring via signals. The parent process traverses the filesystem and
6646789Smckusick * sends writeheader()'s and lists of daddr's to the slaves via pipes.
6750504Smckusick * The following structure defines the instruction packets sent to slaves.
6810911Ssam */
6950504Smckusick struct req {
7018012Smckusick daddr_t dblk;
7118012Smckusick int count;
7250504Smckusick };
7318012Smckusick int reqsiz;
7418012Smckusick
7524181Smckusick #define SLAVES 3 /* 1 slave writing, 1 reading, 1 for slack */
7650504Smckusick struct slave {
7750504Smckusick int tapea; /* header number at start of this chunk */
7850504Smckusick int count; /* count to next header (used for TS_TAPE */
7950504Smckusick /* after EOT) */
8050504Smckusick int inode; /* inode that we are currently dealing with */
8150504Smckusick int fd; /* FD for this slave */
8250504Smckusick int pid; /* PID for this slave */
8350504Smckusick int sent; /* 1 == we've sent this slave requests */
8450504Smckusick int firstrec; /* record number of this block */
8550504Smckusick char (*tblock)[TP_BSIZE]; /* buffer for data blocks */
8650504Smckusick struct req *req; /* buffer for requests */
8750504Smckusick } slaves[SLAVES+1];
8850504Smckusick struct slave *slp;
8918012Smckusick
9050504Smckusick char (*nextblock)[TP_BSIZE];
9150504Smckusick
9250504Smckusick int master; /* pid of master, for sending error signals */
9350504Smckusick int tenths; /* length of tape used per block written */
9450504Smckusick static int caught; /* have we caught the signal to proceed? */
9550504Smckusick static int ready; /* have we reached the lock point without having */
9650504Smckusick /* received the SIGUSR2 signal from the prev slave? */
9750504Smckusick static jmp_buf jmpbuf; /* where to jump to if we are ready when the */
9850504Smckusick /* SIGUSR2 arrives from the previous slave */
9950504Smckusick
10046585Storek int
alloctape()10110911Ssam alloctape()
10210911Ssam {
10325219Smckusick int pgoff = getpagesize() - 1;
10450504Smckusick char *buf;
10550504Smckusick int i;
10610911Ssam
10710911Ssam writesize = ntrec * TP_BSIZE;
10850504Smckusick reqsiz = (ntrec + 1) * sizeof(struct req);
10924181Smckusick /*
11025219Smckusick * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode
11125219Smckusick * (see DEC TU80 User's Guide). The shorter gaps of 6250-bpi require
11225219Smckusick * repositioning after stopping, i.e, streaming mode, where the gap is
11325219Smckusick * variable, 0.30" to 0.45". The gap is maximal when the tape stops.
11424181Smckusick */
11547056Skarels if (blocksperfile == 0)
11647056Skarels tenths = writesize / density +
11747056Skarels (cartridge ? 16 : density == 625 ? 5 : 8);
11825219Smckusick /*
11925219Smckusick * Allocate tape buffer contiguous with the array of instruction
12046789Smckusick * packets, so flushtape() can write them together with one write().
12125219Smckusick * Align tape buffer on page boundary to speed up tape write().
12225219Smckusick */
12350504Smckusick for (i = 0; i <= SLAVES; i++) {
12454047Smckusick buf = (char *)
12554047Smckusick malloc((unsigned)(reqsiz + writesize + pgoff + TP_BSIZE));
12650504Smckusick if (buf == NULL)
12754047Smckusick return(0);
12850504Smckusick slaves[i].tblock = (char (*)[TP_BSIZE])
12950504Smckusick (((long)&buf[ntrec + 1] + pgoff) &~ pgoff);
13050504Smckusick slaves[i].req = (struct req *)slaves[i].tblock - ntrec - 1;
13150504Smckusick }
13250504Smckusick slp = &slaves[0];
13350504Smckusick slp->count = 1;
13450504Smckusick slp->tapea = 0;
13550504Smckusick slp->firstrec = 0;
13650504Smckusick nextblock = slp->tblock;
13724181Smckusick return(1);
13810911Ssam }
13910911Ssam
14046585Storek void
writerec(dp,isspcl)14154595Smckusick writerec(dp, isspcl)
1425329Smckusic char *dp;
14354595Smckusick int isspcl;
1441425Sroot {
14550504Smckusick
14650504Smckusick slp->req[trecno].dblk = (daddr_t)0;
14750504Smckusick slp->req[trecno].count = 1;
14850504Smckusick *(union u_spcl *)(*(nextblock)++) = *(union u_spcl *)dp;
14954595Smckusick if (isspcl)
15054595Smckusick lastspclrec = spcl.c_tapea;
15124181Smckusick trecno++;
1521425Sroot spcl.c_tapea++;
15346585Storek if (trecno >= ntrec)
15446789Smckusick flushtape();
1551425Sroot }
1561425Sroot
15746585Storek void
dumpblock(blkno,size)15846789Smckusick dumpblock(blkno, size)
1594774Smckusic daddr_t blkno;
1604774Smckusic int size;
1611425Sroot {
16225219Smckusick int avail, tpblks, dblkno;
1631425Sroot
1645329Smckusic dblkno = fsbtodb(sblock, blkno);
16546585Storek tpblks = size >> tp_bshift;
16618012Smckusick while ((avail = MIN(tpblks, ntrec - trecno)) > 0) {
16750504Smckusick slp->req[trecno].dblk = dblkno;
16850504Smckusick slp->req[trecno].count = avail;
16925219Smckusick trecno += avail;
1704774Smckusic spcl.c_tapea += avail;
17125219Smckusick if (trecno >= ntrec)
17246789Smckusick flushtape();
17346585Storek dblkno += avail << (tp_bshift - dev_bshift);
1745329Smckusic tpblks -= avail;
1754774Smckusic }
1761425Sroot }
1771425Sroot
1781425Sroot int nogripe = 0;
1791425Sroot
18046585Storek void
tperror(signo)18155288Sbostic tperror(signo)
18255288Sbostic int signo;
18346585Storek {
18450504Smckusick
18518012Smckusick if (pipeout) {
18646614Smckusick msg("write error on %s\n", tape);
18746585Storek quit("Cannot recover\n");
18818012Smckusick /* NOTREACHED */
18918012Smckusick }
19048621Skarels msg("write error %d blocks into volume %d\n", blocksthisvol, tapeno);
19146614Smckusick broadcast("DUMP WRITE ERROR!\n");
19218012Smckusick if (!query("Do you want to restart?"))
19355288Sbostic dumpabort(0);
19446614Smckusick msg("Closing this volume. Prepare to restart with new media;\n");
19518012Smckusick msg("this dump volume will be rewritten.\n");
19624181Smckusick killall();
19718012Smckusick nogripe = 1;
19818012Smckusick close_rewind();
19918012Smckusick Exit(X_REWRITE);
20018012Smckusick }
20118012Smckusick
20246585Storek void
sigpipe(signo)20355288Sbostic sigpipe(signo)
20455288Sbostic int signo;
20525219Smckusick {
20625219Smckusick
20746585Storek quit("Broken pipe\n");
20825219Smckusick }
20925219Smckusick
21057725Smckusick static void
flushtape()21146789Smckusick flushtape()
21218012Smckusick {
21350504Smckusick int i, blks, got;
21450504Smckusick long lastfirstrec;
21546795Sbostic
21650504Smckusick int siz = (char *)nextblock - (char *)slp->req;
2171425Sroot
21850504Smckusick slp->req[trecno].count = 0; /* Sentinel */
21950504Smckusick
22054047Smckusick if (atomic(write, slp->fd, (char *)slp->req, siz) != siz)
22146585Storek quit("error writing command pipe: %s\n", strerror(errno));
22250504Smckusick slp->sent = 1; /* we sent a request, read the response later */
22350504Smckusick
22450504Smckusick lastfirstrec = slp->firstrec;
22550504Smckusick
22650504Smckusick if (++slp >= &slaves[SLAVES])
22750504Smckusick slp = &slaves[0];
22850504Smckusick
22950504Smckusick /* Read results back from next slave */
23050504Smckusick if (slp->sent) {
23154047Smckusick if (atomic(read, slp->fd, (char *)&got, sizeof got)
23254047Smckusick != sizeof got) {
23350504Smckusick perror(" DUMP: error reading command pipe in master");
23455288Sbostic dumpabort(0);
23550504Smckusick }
23650504Smckusick slp->sent = 0;
23750504Smckusick
23850504Smckusick /* Check for end of tape */
23950504Smckusick if (got < writesize) {
24050504Smckusick msg("End of tape detected\n");
24150504Smckusick
24250504Smckusick /*
24350504Smckusick * Drain the results, don't care what the values were.
24450504Smckusick * If we read them here then trewind won't...
24550504Smckusick */
24650504Smckusick for (i = 0; i < SLAVES; i++) {
24750504Smckusick if (slaves[i].sent) {
24854047Smckusick if (atomic(read, slaves[i].fd,
24954047Smckusick (char *)&got, sizeof got)
25054047Smckusick != sizeof got) {
25150504Smckusick perror(" DUMP: error reading command pipe in master");
25255288Sbostic dumpabort(0);
25350504Smckusick }
25450504Smckusick slaves[i].sent = 0;
25550504Smckusick }
25650504Smckusick }
25750504Smckusick
25850504Smckusick close_rewind();
25950504Smckusick rollforward();
26050504Smckusick return;
26150504Smckusick }
26250504Smckusick }
26350504Smckusick
26450504Smckusick blks = 0;
26550504Smckusick if (spcl.c_type != TS_END) {
26650504Smckusick for (i = 0; i < spcl.c_count; i++)
26750504Smckusick if (spcl.c_addr[i] != 0)
26850504Smckusick blks++;
26950504Smckusick }
27050504Smckusick slp->count = lastspclrec + blks + 1 - spcl.c_tapea;
27150504Smckusick slp->tapea = spcl.c_tapea;
27250504Smckusick slp->firstrec = lastfirstrec + ntrec;
27350504Smckusick slp->inode = curino;
27450504Smckusick nextblock = slp->tblock;
2751425Sroot trecno = 0;
27624181Smckusick asize += tenths;
27710911Ssam blockswritten += ntrec;
27848621Skarels blocksthisvol += ntrec;
27946614Smckusick if (!pipeout && (blocksperfile ?
28048621Skarels (blocksthisvol >= blocksperfile) : (asize > tsize))) {
2811425Sroot close_rewind();
28250504Smckusick startnewtape(0);
2831425Sroot }
2841425Sroot timeest();
2851425Sroot }
2861425Sroot
28746585Storek void
trewind()28846239Storek trewind()
2891425Sroot {
29024181Smckusick int f;
29150504Smckusick int got;
29212331Smckusick
29350504Smckusick for (f = 0; f < SLAVES; f++) {
29450504Smckusick /*
29550504Smckusick * Drain the results, but unlike EOT we DO (or should) care
29650504Smckusick * what the return values were, since if we detect EOT after
29750504Smckusick * we think we've written the last blocks to the tape anyway,
29850504Smckusick * we have to replay those blocks with rollforward.
29950504Smckusick *
30050504Smckusick * fixme: punt for now.
30150504Smckusick */
30250504Smckusick if (slaves[f].sent) {
30354047Smckusick if (atomic(read, slaves[f].fd, (char *)&got, sizeof got)
30450504Smckusick != sizeof got) {
30550504Smckusick perror(" DUMP: error reading command pipe in master");
30655288Sbostic dumpabort(0);
30750504Smckusick }
30850504Smckusick slaves[f].sent = 0;
30950504Smckusick if (got != writesize) {
31050504Smckusick msg("EOT detected in last 2 tape records!\n");
31150504Smckusick msg("Use a longer tape, decrease the size estimate\n");
31250504Smckusick quit("or use no size estimate at all.\n");
31350504Smckusick }
31450504Smckusick }
31554047Smckusick (void) close(slaves[f].fd);
31650504Smckusick }
31746585Storek while (wait((int *)NULL) >= 0) /* wait for any signals from slaves */
31846585Storek /* void */;
31954047Smckusick
32054047Smckusick if (pipeout)
32154047Smckusick return;
32254047Smckusick
32348621Skarels msg("Closing %s\n", tape);
32450504Smckusick
32518012Smckusick #ifdef RDUMP
32625219Smckusick if (host) {
32725219Smckusick rmtclose();
32825219Smckusick while (rmtopen(tape, 0) < 0)
32925219Smckusick sleep(10);
33025219Smckusick rmtclose();
33125219Smckusick return;
33225219Smckusick }
33350504Smckusick #endif
33454047Smckusick (void) close(tapefd);
3353214Swnj while ((f = open(tape, 0)) < 0)
3363214Swnj sleep (10);
33754047Smckusick (void) close(f);
3381425Sroot }
3391425Sroot
34046585Storek void
close_rewind()3411425Sroot close_rewind()
3421425Sroot {
34346239Storek trewind();
34448621Skarels if (nexttape)
34548621Skarels return;
34618012Smckusick if (!nogripe) {
34746614Smckusick msg("Change Volumes: Mount volume #%d\n", tapeno+1);
34846614Smckusick broadcast("CHANGE DUMP VOLUMES!\7\7\n");
3491425Sroot }
35048621Skarels while (!query("Is the new volume mounted and ready to go?"))
35125219Smckusick if (query("Do you want to abort?")) {
35255288Sbostic dumpabort(0);
35325219Smckusick /*NOTREACHED*/
35425219Smckusick }
3551425Sroot }
3561425Sroot
35750504Smckusick void
rollforward()35850504Smckusick rollforward()
35950504Smckusick {
36050504Smckusick register struct req *p, *q, *prev;
36150504Smckusick register struct slave *tslp;
36254047Smckusick int i, size, savedtapea, got;
36350504Smckusick union u_spcl *ntb, *otb;
36450504Smckusick tslp = &slaves[SLAVES];
36550504Smckusick ntb = (union u_spcl *)tslp->tblock[1];
36650504Smckusick
36750504Smckusick /*
36850504Smckusick * Each of the N slaves should have requests that need to
36950504Smckusick * be replayed on the next tape. Use the extra slave buffers
37050504Smckusick * (slaves[SLAVES]) to construct request lists to be sent to
37150504Smckusick * each slave in turn.
37250504Smckusick */
37350504Smckusick for (i = 0; i < SLAVES; i++) {
37450504Smckusick q = &tslp->req[1];
37550504Smckusick otb = (union u_spcl *)slp->tblock;
37650504Smckusick
37750504Smckusick /*
37850504Smckusick * For each request in the current slave, copy it to tslp.
37950504Smckusick */
38050504Smckusick
38159917Smckusick prev = NULL;
38250504Smckusick for (p = slp->req; p->count > 0; p += p->count) {
38350504Smckusick *q = *p;
38450504Smckusick if (p->dblk == 0)
38550504Smckusick *ntb++ = *otb++; /* copy the datablock also */
38650504Smckusick prev = q;
38750504Smckusick q += q->count;
38850504Smckusick }
38959917Smckusick if (prev == NULL)
39059917Smckusick quit("rollforward: protocol botch");
39150504Smckusick if (prev->dblk != 0)
39250504Smckusick prev->count -= 1;
39350504Smckusick else
39450504Smckusick ntb--;
39550504Smckusick q -= 1;
39650504Smckusick q->count = 0;
39750504Smckusick q = &tslp->req[0];
39850504Smckusick if (i == 0) {
39950504Smckusick q->dblk = 0;
40050504Smckusick q->count = 1;
40150504Smckusick trecno = 0;
40250504Smckusick nextblock = tslp->tblock;
40350504Smckusick savedtapea = spcl.c_tapea;
40450504Smckusick spcl.c_tapea = slp->tapea;
40550504Smckusick startnewtape(0);
40650504Smckusick spcl.c_tapea = savedtapea;
40750504Smckusick lastspclrec = savedtapea - 1;
40850504Smckusick }
40950504Smckusick size = (char *)ntb - (char *)q;
41054047Smckusick if (atomic(write, slp->fd, (char *)q, size) != size) {
41150504Smckusick perror(" DUMP: error writing command pipe");
41255288Sbostic dumpabort(0);
41350504Smckusick }
41450504Smckusick slp->sent = 1;
41550504Smckusick if (++slp >= &slaves[SLAVES])
41650504Smckusick slp = &slaves[0];
41750504Smckusick
41850504Smckusick q->count = 1;
41950504Smckusick
42050504Smckusick if (prev->dblk != 0) {
42150504Smckusick /*
42250504Smckusick * If the last one was a disk block, make the
42350504Smckusick * first of this one be the last bit of that disk
42450504Smckusick * block...
42550504Smckusick */
42650504Smckusick q->dblk = prev->dblk +
42750504Smckusick prev->count * (TP_BSIZE / DEV_BSIZE);
42850504Smckusick ntb = (union u_spcl *)tslp->tblock;
42950504Smckusick } else {
43050504Smckusick /*
43150504Smckusick * It wasn't a disk block. Copy the data to its
43250504Smckusick * new location in the buffer.
43350504Smckusick */
43450504Smckusick q->dblk = 0;
43550504Smckusick *((union u_spcl *)tslp->tblock) = *ntb;
43650504Smckusick ntb = (union u_spcl *)tslp->tblock[1];
43750504Smckusick }
43850504Smckusick }
43950504Smckusick slp->req[0] = *q;
44050504Smckusick nextblock = slp->tblock;
44150504Smckusick if (q->dblk == 0)
44250504Smckusick nextblock++;
44350504Smckusick trecno = 1;
44450504Smckusick
44550504Smckusick /*
44650504Smckusick * Clear the first slaves' response. One hopes that it
44750504Smckusick * worked ok, otherwise the tape is much too short!
44850504Smckusick */
44950504Smckusick if (slp->sent) {
45054047Smckusick if (atomic(read, slp->fd, (char *)&got, sizeof got)
45154047Smckusick != sizeof got) {
45250504Smckusick perror(" DUMP: error reading command pipe in master");
45355288Sbostic dumpabort(0);
45450504Smckusick }
45550504Smckusick slp->sent = 0;
45650504Smckusick
45750504Smckusick if (got != writesize) {
45850504Smckusick quit("EOT detected at start of the tape!\n");
45950504Smckusick }
46050504Smckusick }
46150504Smckusick }
46250504Smckusick
4631425Sroot /*
46450504Smckusick * We implement taking and restoring checkpoints on the tape level.
46550504Smckusick * When each tape is opened, a new process is created by forking; this
46650504Smckusick * saves all of the necessary context in the parent. The child
46750504Smckusick * continues the dump; the parent waits around, saving the context.
46850504Smckusick * If the child returns X_REWRITE, then it had problems writing that tape;
46950504Smckusick * this causes the parent to fork again, duplicating the context, and
47050504Smckusick * everything continues as if nothing had happened.
4711425Sroot */
47246585Storek void
startnewtape(top)47350504Smckusick startnewtape(top)
47450504Smckusick int top;
4751425Sroot {
4761425Sroot int parentpid;
4771425Sroot int childpid;
4781425Sroot int status;
4791425Sroot int waitpid;
48047056Skarels char *p;
48150574Smckusick #ifdef sunos
48254047Smckusick void (*interrupt_save)();
48350574Smckusick #else
48454047Smckusick sig_t interrupt_save;
48550574Smckusick #endif
4861425Sroot
48754047Smckusick interrupt_save = signal(SIGINT, SIG_IGN);
4881425Sroot parentpid = getpid();
4891425Sroot
49057725Smckusick restore_check_point:
49154047Smckusick (void)signal(SIGINT, interrupt_save);
49225219Smckusick /*
49325219Smckusick * All signals are inherited...
49425219Smckusick */
4951425Sroot childpid = fork();
49618012Smckusick if (childpid < 0) {
4971425Sroot msg("Context save fork fails in parent %d\n", parentpid);
4981425Sroot Exit(X_ABORT);
4991425Sroot }
50018012Smckusick if (childpid != 0) {
5011425Sroot /*
5021425Sroot * PARENT:
5031425Sroot * save the context by waiting
5041425Sroot * until the child doing all of the work returns.
50518012Smckusick * don't catch the interrupt
5061425Sroot */
50725219Smckusick signal(SIGINT, SIG_IGN);
5081425Sroot #ifdef TDEBUG
5091425Sroot msg("Tape: %d; parent process: %d child process %d\n",
5101425Sroot tapeno+1, parentpid, childpid);
51157725Smckusick #endif /* TDEBUG */
51218012Smckusick while ((waitpid = wait(&status)) != childpid)
51318012Smckusick msg("Parent %d waiting for child %d has another child %d return\n",
51418012Smckusick parentpid, childpid, waitpid);
51518012Smckusick if (status & 0xFF) {
5161425Sroot msg("Child %d returns LOB status %o\n",
5171425Sroot childpid, status&0xFF);
5181425Sroot }
5191425Sroot status = (status >> 8) & 0xFF;
5201425Sroot #ifdef TDEBUG
52118012Smckusick switch(status) {
5221425Sroot case X_FINOK:
5231425Sroot msg("Child %d finishes X_FINOK\n", childpid);
5241425Sroot break;
52550504Smckusick case X_ABORT:
5261425Sroot msg("Child %d finishes X_ABORT\n", childpid);
5271425Sroot break;
5281425Sroot case X_REWRITE:
5291425Sroot msg("Child %d finishes X_REWRITE\n", childpid);
5301425Sroot break;
5311425Sroot default:
53218012Smckusick msg("Child %d finishes unknown %d\n",
53325219Smckusick childpid, status);
5341425Sroot break;
5351425Sroot }
53657725Smckusick #endif /* TDEBUG */
53718012Smckusick switch(status) {
5381425Sroot case X_FINOK:
5391425Sroot Exit(X_FINOK);
5401425Sroot case X_ABORT:
5411425Sroot Exit(X_ABORT);
5421425Sroot case X_REWRITE:
5431425Sroot goto restore_check_point;
5441425Sroot default:
5451425Sroot msg("Bad return code from dump: %d\n", status);
5461425Sroot Exit(X_ABORT);
5471425Sroot }
5481425Sroot /*NOTREACHED*/
5491425Sroot } else { /* we are the child; just continue */
5501425Sroot #ifdef TDEBUG
5511425Sroot sleep(4); /* allow time for parent's message to get out */
5521425Sroot msg("Child on Tape %d has parent %d, my pid = %d\n",
5531425Sroot tapeno+1, parentpid, getpid());
55457725Smckusick #endif /* TDEBUG */
55547056Skarels /*
55647056Skarels * If we have a name like "/dev/rmt0,/dev/rmt1",
55747056Skarels * use the name before the comma first, and save
55848621Skarels * the remaining names for subsequent volumes.
55947056Skarels */
56050504Smckusick tapeno++; /* current tape sequence */
56168991Sbostic if (nexttape || strchr(tape, ',')) {
56248621Skarels if (nexttape && *nexttape)
56348621Skarels tape = nexttape;
56468991Sbostic if ((p = strchr(tape, ',')) != NULL) {
56548621Skarels *p = '\0';
56648621Skarels nexttape = p + 1;
56748621Skarels } else
56848621Skarels nexttape = NULL;
56948621Skarels msg("Dumping volume %d on %s\n", tapeno, tape);
57048621Skarels }
57118012Smckusick #ifdef RDUMP
57246789Smckusick while ((tapefd = (host ? rmtopen(tape, 2) :
57346789Smckusick pipeout ? 1 : open(tape, O_WRONLY|O_CREAT, 0666))) < 0)
57450504Smckusick #else
57550504Smckusick while ((tapefd = (pipeout ? 1 :
57650504Smckusick open(tape, O_WRONLY|O_CREAT, 0666))) < 0)
57750504Smckusick #endif
57839128Smckusick {
57946614Smckusick msg("Cannot open output \"%s\".\n", tape);
58039128Smckusick if (!query("Do you want to retry the open?"))
58155288Sbostic dumpabort(0);
58239128Smckusick }
5831425Sroot
58418012Smckusick enslave(); /* Share open tape file descriptor with slaves */
58518012Smckusick
5861425Sroot asize = 0;
58748621Skarels blocksthisvol = 0;
58850504Smckusick if (top)
58950504Smckusick newtape++; /* new tape signal */
59050504Smckusick spcl.c_count = slp->count;
59150504Smckusick /*
59250504Smckusick * measure firstrec in TP_BSIZE units since restore doesn't
59350504Smckusick * know the correct ntrec value...
59450504Smckusick */
59550504Smckusick spcl.c_firstrec = slp->firstrec;
5961425Sroot spcl.c_volume++;
5971425Sroot spcl.c_type = TS_TAPE;
59830432Smckusick spcl.c_flags |= DR_NEWHEADER;
59954047Smckusick writeheader((ino_t)slp->inode);
60030432Smckusick spcl.c_flags &=~ DR_NEWHEADER;
6011425Sroot if (tapeno > 1)
60248621Skarels msg("Volume %d begins with blocks from inode %d\n",
60350504Smckusick tapeno, slp->inode);
6041425Sroot }
6051425Sroot }
6061425Sroot
60746585Storek void
dumpabort(signo)60855288Sbostic dumpabort(signo)
60955288Sbostic int signo;
6101425Sroot {
61150504Smckusick
61218012Smckusick if (master != 0 && master != getpid())
61354047Smckusick /* Signals master to call dumpabort */
61454047Smckusick (void) kill(master, SIGTERM);
61524181Smckusick else {
61624181Smckusick killall();
61724181Smckusick msg("The ENTIRE dump is aborted.\n");
61824181Smckusick }
61966364Sbostic #ifdef RDUMP
62066364Sbostic rmtclose();
62166364Sbostic #endif
6221425Sroot Exit(X_ABORT);
6231425Sroot }
6241425Sroot
62557725Smckusick __dead void
Exit(status)6261425Sroot Exit(status)
62746239Storek int status;
6281425Sroot {
62950504Smckusick
6301425Sroot #ifdef TDEBUG
6311425Sroot msg("pid = %d exits with status %d\n", getpid(), status);
63257725Smckusick #endif /* TDEBUG */
63357725Smckusick exit(status);
6341425Sroot }
63518012Smckusick
63624181Smckusick /*
63750504Smckusick * proceed - handler for SIGUSR2, used to synchronize IO between the slaves.
63824181Smckusick */
63946585Storek void
proceed(signo)64055288Sbostic proceed(signo)
64155288Sbostic int signo;
64224181Smckusick {
64318012Smckusick
64450504Smckusick if (ready)
64550504Smckusick longjmp(jmpbuf, 1);
64650504Smckusick caught++;
64724181Smckusick }
64824181Smckusick
64946585Storek void
enslave()65018012Smckusick enslave()
65118012Smckusick {
65250504Smckusick int cmd[2];
65324181Smckusick register int i, j;
65418012Smckusick
65518012Smckusick master = getpid();
65650504Smckusick
65750504Smckusick signal(SIGTERM, dumpabort); /* Slave sends SIGTERM on dumpabort() */
65825219Smckusick signal(SIGPIPE, sigpipe);
65925219Smckusick signal(SIGUSR1, tperror); /* Slave sends SIGUSR1 on tape errors */
66050504Smckusick signal(SIGUSR2, proceed); /* Slave sends SIGUSR2 to next slave */
66150504Smckusick
66224181Smckusick for (i = 0; i < SLAVES; i++) {
66350504Smckusick if (i == slp - &slaves[0]) {
66450504Smckusick caught = 1;
66524181Smckusick } else {
66650504Smckusick caught = 0;
66724181Smckusick }
66850504Smckusick
66950504Smckusick if (socketpair(AF_UNIX, SOCK_STREAM, 0, cmd) < 0 ||
67050504Smckusick (slaves[i].pid = fork()) < 0)
67146585Storek quit("too many slaves, %d (recompile smaller): %s\n",
67246585Storek i, strerror(errno));
67350504Smckusick
67450504Smckusick slaves[i].fd = cmd[1];
67550504Smckusick slaves[i].sent = 0;
67650504Smckusick if (slaves[i].pid == 0) { /* Slave starts up here */
67718012Smckusick for (j = 0; j <= i; j++)
67854047Smckusick (void) close(slaves[j].fd);
67925219Smckusick signal(SIGINT, SIG_IGN); /* Master handles this */
68050504Smckusick doslave(cmd[0], i);
68118012Smckusick Exit(X_FINOK);
68218012Smckusick }
68318012Smckusick }
68450504Smckusick
68550504Smckusick for (i = 0; i < SLAVES; i++)
68654047Smckusick (void) atomic(write, slaves[i].fd,
68754047Smckusick (char *) &slaves[(i + 1) % SLAVES].pid,
68854047Smckusick sizeof slaves[0].pid);
68950504Smckusick
69050504Smckusick master = 0;
69118012Smckusick }
69218012Smckusick
69346585Storek void
killall()69424181Smckusick killall()
69518012Smckusick {
69624181Smckusick register int i;
69719982Smckusick
69824181Smckusick for (i = 0; i < SLAVES; i++)
69950504Smckusick if (slaves[i].pid > 0)
70054047Smckusick (void) kill(slaves[i].pid, SIGKILL);
70118012Smckusick }
70218012Smckusick
70324181Smckusick /*
70424181Smckusick * Synchronization - each process has a lockfile, and shares file
70524181Smckusick * descriptors to the following process's lockfile. When our write
70624181Smckusick * completes, we release our lock on the following process's lock-
70724181Smckusick * file, allowing the following process to lock it and proceed. We
70824181Smckusick * get the lock back for the next cycle by swapping descriptors.
70924181Smckusick */
71057725Smckusick static void
doslave(cmd,slave_number)71150504Smckusick doslave(cmd, slave_number)
71250504Smckusick register int cmd;
71350504Smckusick int slave_number;
71419982Smckusick {
71550504Smckusick register int nread;
71650504Smckusick int nextslave, size, wrote, eot_count;
71719982Smckusick
71846789Smckusick /*
71946789Smckusick * Need our own seek pointer.
72046789Smckusick */
72154047Smckusick (void) close(diskfd);
72246789Smckusick if ((diskfd = open(disk, O_RDONLY)) < 0)
72346585Storek quit("slave couldn't reopen disk: %s\n", strerror(errno));
72450504Smckusick
72524181Smckusick /*
72650504Smckusick * Need the pid of the next slave in the loop...
72750504Smckusick */
72854047Smckusick if ((nread = atomic(read, cmd, (char *)&nextslave, sizeof nextslave))
72950504Smckusick != sizeof nextslave) {
73050504Smckusick quit("master/slave protocol botched - didn't get pid of next slave.\n");
73150504Smckusick }
73250504Smckusick
73350504Smckusick /*
73425219Smckusick * Get list of blocks to dump, read the blocks into tape buffer
73524181Smckusick */
73654047Smckusick while ((nread = atomic(read, cmd, (char *)slp->req, reqsiz)) == reqsiz) {
73750504Smckusick register struct req *p = slp->req;
73850504Smckusick
73950504Smckusick for (trecno = 0; trecno < ntrec;
74050504Smckusick trecno += p->count, p += p->count) {
74118012Smckusick if (p->dblk) {
74250504Smckusick bread(p->dblk, slp->tblock[trecno],
74325219Smckusick p->count * TP_BSIZE);
74418012Smckusick } else {
74525219Smckusick if (p->count != 1 || atomic(read, cmd,
74654047Smckusick (char *)slp->tblock[trecno],
74754047Smckusick TP_BSIZE) != TP_BSIZE)
74854047Smckusick quit("master/slave protocol botched.\n");
74918012Smckusick }
75018012Smckusick }
75150504Smckusick if (setjmp(jmpbuf) == 0) {
75250504Smckusick ready = 1;
75350504Smckusick if (!caught)
75454047Smckusick (void) pause();
75550504Smckusick }
75650504Smckusick ready = 0;
75750504Smckusick caught = 0;
75825219Smckusick
75950504Smckusick /* Try to write the data... */
76050504Smckusick eot_count = 0;
76150504Smckusick size = 0;
76250504Smckusick
76350504Smckusick while (eot_count < 10 && size < writesize) {
76418012Smckusick #ifdef RDUMP
76550504Smckusick if (host)
76650504Smckusick wrote = rmtwrite(slp->tblock[0]+size,
76750504Smckusick writesize-size);
76848621Skarels else
76950504Smckusick #endif
77050504Smckusick wrote = write(tapefd, slp->tblock[0]+size,
77150504Smckusick writesize-size);
77250504Smckusick #ifdef WRITEDEBUG
77350504Smckusick printf("slave %d wrote %d\n", slave_number, wrote);
77450504Smckusick #endif
77550504Smckusick if (wrote < 0)
77650504Smckusick break;
77750504Smckusick if (wrote == 0)
77850504Smckusick eot_count++;
77950504Smckusick size += wrote;
78050504Smckusick }
78150504Smckusick
78250504Smckusick #ifdef WRITEDEBUG
78350504Smckusick if (size != writesize)
78450504Smckusick printf("slave %d only wrote %d out of %d bytes and gave up.\n",
78550504Smckusick slave_number, size, writesize);
78650504Smckusick #endif
78750504Smckusick
78850504Smckusick if (eot_count > 0)
78950504Smckusick size = 0;
79050504Smckusick
79150504Smckusick /*
79250504Smckusick * fixme: Pyramids running OSx return ENOSPC
79350504Smckusick * at EOT on 1/2 inch drives.
79450504Smckusick */
79550504Smckusick if (size < 0) {
79654047Smckusick (void) kill(master, SIGUSR1);
79725219Smckusick for (;;)
79854047Smckusick (void) sigpause(0);
79950504Smckusick } else {
80050504Smckusick /*
80150504Smckusick * pass size of write back to master
80250504Smckusick * (for EOT handling)
80350504Smckusick */
80454047Smckusick (void) atomic(write, cmd, (char *)&size, sizeof size);
80550504Smckusick }
80650504Smckusick
80750504Smckusick /*
80850504Smckusick * If partial write, don't want next slave to go.
80950504Smckusick * Also jolts him awake.
81050504Smckusick */
81154047Smckusick (void) kill(nextslave, SIGUSR2);
81250504Smckusick }
81346585Storek if (nread != 0)
81446585Storek quit("error reading command pipe: %s\n", strerror(errno));
81518012Smckusick }
81619947Smckusick
81719947Smckusick /*
81825219Smckusick * Since a read from a pipe may not return all we asked for,
81925219Smckusick * or a write may not write all we ask if we get a signal,
82025219Smckusick * loop until the count is satisfied (or error).
82119947Smckusick */
82257725Smckusick static int
82325219Smckusick atomic(func, fd, buf, count)
82457725Smckusick int (*func)(), fd;
82519947Smckusick char *buf;
82657725Smckusick int count;
82719947Smckusick {
82825219Smckusick int got, need = count;
82919947Smckusick
83025219Smckusick while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0)
83119947Smckusick buf += got;
83225219Smckusick return (got < 0 ? got : count - need);
83319947Smckusick }
834