132002Spc /*
232002Spc * Copyright (c) 1980 Regents of the University of California.
332002Spc * All rights reserved. The Berkeley software License Agreement
432002Spc * specifies the terms and conditions for redistribution.
532002Spc */
632002Spc
732002Spc #ifndef lint
8*32047Spc static char sccsid[] = "@(#)dumptape.c 1.4 (UKC) 08/11/87 5.8 (Berkeley) 2/23/87";
932002Spc #endif not lint
1032002Spc
1132028Spc #include "dump.h"
1232002Spc #include <sys/file.h>
1332028Spc #include <sys/ioctl.h>
1432028Spc #include <sys/mtio.h>
1532002Spc
1632028Spc
1732003Spc char (*tblock)[TP_BSIZE]; /* pointer to malloc()ed buffer for tape */
1832003Spc int writesize; /* size of malloc()ed buffer for tape */
1932003Spc long lastspclrec = -1; /* tape block number of last written header */
2032003Spc int trecno = 0; /* next record to write in current block */
2132002Spc extern int ntrec; /* blocking factor on tape */
2232002Spc extern int cartridge;
2332028Spc extern int rewindoffline;
2432002Spc extern int read(), write();
2532002Spc #ifdef RDUMP
2632002Spc extern char *host;
2732002Spc #endif RDUMP
2832002Spc
2932002Spc /*
3032002Spc * Concurrent dump mods (Caltech) - disk block reading and tape writing
3132002Spc * are exported to several slave processes. While one slave writes the
3232002Spc * tape, the others read disk blocks; they pass control of the tape in
3332002Spc * a ring via flock(). The parent process traverses the filesystem and
3432002Spc * sends spclrec()'s and lists of daddr's to the slaves via pipes.
3532002Spc */
3632002Spc struct req { /* instruction packets sent to slaves */
3732002Spc daddr_t dblk;
3832002Spc int count;
3932002Spc } *req;
4032002Spc int reqsiz;
4132002Spc
4232002Spc #define SLAVES 3 /* 1 slave writing, 1 reading, 1 for slack */
4332002Spc int slavefd[SLAVES]; /* pipes from master to each slave */
4432002Spc int slavepid[SLAVES]; /* used by killall() */
4532002Spc int rotor; /* next slave to be instructed */
4632002Spc int master; /* pid of master, for sending error signals */
4732002Spc int tenths; /* length of tape used per block written */
4832002Spc
alloctape()4932002Spc alloctape()
5032002Spc {
5132002Spc int pgoff = getpagesize() - 1;
5232002Spc
5332002Spc writesize = ntrec * TP_BSIZE;
5432002Spc reqsiz = ntrec * sizeof(struct req);
5532002Spc /*
5632002Spc * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode
5732002Spc * (see DEC TU80 User's Guide). The shorter gaps of 6250-bpi require
5832002Spc * repositioning after stopping, i.e, streaming mode, where the gap is
5932002Spc * variable, 0.30" to 0.45". The gap is maximal when the tape stops.
6032002Spc */
6132002Spc tenths = writesize/density + (cartridge ? 16 : density == 625 ? 5 : 8);
6232002Spc /*
6332002Spc * Allocate tape buffer contiguous with the array of instruction
6432002Spc * packets, so flusht() can write them together with one write().
6532002Spc * Align tape buffer on page boundary to speed up tape write().
6632002Spc */
6732002Spc req = (struct req *)malloc(reqsiz + writesize + pgoff);
6832002Spc if (req == NULL)
6932002Spc return(0);
7032002Spc tblock = (char (*)[TP_BSIZE]) (((long)&req[ntrec] + pgoff) &~ pgoff);
7132002Spc req = (struct req *)tblock - ntrec;
7232002Spc return(1);
7332002Spc }
7432002Spc
7532002Spc
taprec(dp)7632002Spc taprec(dp)
7732002Spc char *dp;
7832002Spc {
7932002Spc req[trecno].dblk = (daddr_t)0;
8032002Spc req[trecno].count = 1;
8132002Spc *(union u_spcl *)(*tblock++) = *(union u_spcl *)dp; /* movc3 */
8232003Spc lastspclrec = spcl.c_tapea;
8332002Spc trecno++;
8432002Spc spcl.c_tapea++;
8532002Spc if(trecno >= ntrec)
8632002Spc flusht();
8732002Spc }
8832002Spc
dmpblk(blkno,size)8932002Spc dmpblk(blkno, size)
9032002Spc daddr_t blkno;
9132002Spc int size;
9232002Spc {
9332002Spc int avail, tpblks, dblkno;
9432002Spc
9532002Spc dblkno = fsbtodb(sblock, blkno);
9632002Spc tpblks = size / TP_BSIZE;
9732002Spc while ((avail = MIN(tpblks, ntrec - trecno)) > 0) {
9832002Spc req[trecno].dblk = dblkno;
9932002Spc req[trecno].count = avail;
10032002Spc trecno += avail;
10132002Spc spcl.c_tapea += avail;
10232002Spc if (trecno >= ntrec)
10332002Spc flusht();
10432002Spc dblkno += avail * (TP_BSIZE / DEV_BSIZE);
10532002Spc tpblks -= avail;
10632002Spc }
10732002Spc }
10832002Spc
10932002Spc int nogripe = 0;
11032002Spc
tperror()11132002Spc tperror() {
11232002Spc if (pipeout) {
11332002Spc msg("Tape write error on %s\n", tape);
11432002Spc msg("Cannot recover\n");
11532002Spc dumpabort();
11632002Spc /* NOTREACHED */
11732002Spc }
11832002Spc msg("Tape write error %d feet into tape %d\n", asize/120L, tapeno);
11932002Spc broadcast("TAPE ERROR!\n");
12032002Spc if (!query("Do you want to restart?"))
12132002Spc dumpabort();
12232002Spc msg("This tape will rewind. After it is rewound,\n");
12332002Spc msg("replace the faulty tape with a new one;\n");
12432002Spc msg("this dump volume will be rewritten.\n");
12532002Spc killall();
12632002Spc nogripe = 1;
12732002Spc close_rewind();
12832002Spc Exit(X_REWRITE);
12932002Spc }
13032002Spc
sigpipe()13132002Spc sigpipe()
13232002Spc {
13332002Spc
13432002Spc msg("Broken pipe\n");
13532002Spc dumpabort();
13632002Spc }
13732002Spc
13832002Spc #ifdef RDUMP
13932002Spc /*
14032002Spc * compatibility routine
14132002Spc */
tflush(i)14232002Spc tflush(i)
14332002Spc int i;
14432002Spc {
14532002Spc
14632002Spc for (i = 0; i < ntrec; i++)
14732002Spc spclrec();
14832002Spc }
14932002Spc #endif RDUMP
15032002Spc
flusht()15132002Spc flusht()
15232002Spc {
15332002Spc int siz = (char *)tblock - (char *)req;
15432002Spc
15532002Spc if (atomic(write, slavefd[rotor], req, siz) != siz) {
15632002Spc perror(" DUMP: error writing command pipe");
15732002Spc dumpabort();
15832002Spc }
15932002Spc if (++rotor >= SLAVES) rotor = 0;
16032002Spc tblock = (char (*)[TP_BSIZE]) &req[ntrec];
16132002Spc trecno = 0;
16232002Spc asize += tenths;
16332002Spc blockswritten += ntrec;
16432002Spc if (!pipeout && asize > tsize) {
16532002Spc close_rewind();
16632002Spc otape();
16732002Spc }
16832002Spc timeest();
16932002Spc }
17032002Spc
rewind()17132002Spc rewind()
17232002Spc {
17332002Spc int f;
17432002Spc
17532002Spc if (pipeout)
17632002Spc return;
17732002Spc for (f = 0; f < SLAVES; f++)
17832002Spc close(slavefd[f]);
17932002Spc while (wait(NULL) >= 0) ; /* wait for any signals from slaves */
18032002Spc msg("Tape rewinding\n");
18132002Spc #ifdef RDUMP
18232002Spc if (host) {
18332028Spc if (rewindoffline) {
18432028Spc rewind_offline(to);
18532028Spc rmtclose();
18632028Spc return;
18732028Spc }
18832002Spc rmtclose();
18932002Spc while (rmtopen(tape, 0) < 0)
19032002Spc sleep(10);
19132002Spc rmtclose();
19232002Spc return;
19332002Spc }
19432002Spc #endif RDUMP
19532028Spc if (rewindoffline) {
19632028Spc rewind_offline(to);
19732028Spc close(to);
19832028Spc return;
19932028Spc }
20032002Spc close(to);
20132002Spc while ((f = open(tape, 0)) < 0)
20232002Spc sleep (10);
20332002Spc close(f);
20432002Spc }
20532002Spc
close_rewind()20632002Spc close_rewind()
20732002Spc {
20832028Spc extern int userlabel;
20932028Spc char *label;
21032028Spc char *createlabel();
21132028Spc
21232002Spc rewind();
21332002Spc if (!nogripe) {
21432028Spc if (userlabel) {
21532028Spc label = createlabel(tapeno+1);
21632028Spc msg("Change Tapes: Mount tape labelled `%s' reel %d of this dump\n",
21732028Spc label, tapeno+1);
21832028Spc }
21932028Spc else
22032028Spc msg("Change Tapes: Mount tape #%d\n", tapeno+1);
22132002Spc broadcast("CHANGE TAPES!\7\7\n");
22232002Spc }
22332002Spc while (!query("Is the new tape mounted and ready to go?"))
22432002Spc if (query("Do you want to abort?")) {
22532002Spc dumpabort();
22632002Spc /*NOTREACHED*/
22732002Spc }
22832002Spc }
22932002Spc
23032002Spc /*
23132002Spc * We implement taking and restoring checkpoints on the tape level.
23232002Spc * When each tape is opened, a new process is created by forking; this
23332002Spc * saves all of the necessary context in the parent. The child
23432002Spc * continues the dump; the parent waits around, saving the context.
23532002Spc * If the child returns X_REWRITE, then it had problems writing that tape;
23632002Spc * this causes the parent to fork again, duplicating the context, and
23732002Spc * everything continues as if nothing had happened.
23832002Spc */
23932002Spc
otape()24032002Spc otape()
24132002Spc {
24232002Spc int parentpid;
24332002Spc int childpid;
24432002Spc int status;
24532002Spc int waitpid;
24632002Spc int (*interrupt)() = signal(SIGINT, SIG_IGN);
24732003Spc int blks, i;
24832028Spc
24932002Spc parentpid = getpid();
25032002Spc
25132002Spc restore_check_point:
25232002Spc signal(SIGINT, interrupt);
25332002Spc /*
25432002Spc * All signals are inherited...
25532002Spc */
25632002Spc childpid = fork();
25732002Spc if (childpid < 0) {
25832002Spc msg("Context save fork fails in parent %d\n", parentpid);
25932002Spc Exit(X_ABORT);
26032002Spc }
26132002Spc if (childpid != 0) {
26232002Spc /*
26332002Spc * PARENT:
26432002Spc * save the context by waiting
26532002Spc * until the child doing all of the work returns.
26632002Spc * don't catch the interrupt
26732002Spc */
26832002Spc signal(SIGINT, SIG_IGN);
26932002Spc #ifdef TDEBUG
27032002Spc msg("Tape: %d; parent process: %d child process %d\n",
27132002Spc tapeno+1, parentpid, childpid);
27232002Spc #endif TDEBUG
27332002Spc while ((waitpid = wait(&status)) != childpid)
27432002Spc msg("Parent %d waiting for child %d has another child %d return\n",
27532002Spc parentpid, childpid, waitpid);
27632002Spc if (status & 0xFF) {
27732002Spc msg("Child %d returns LOB status %o\n",
27832002Spc childpid, status&0xFF);
27932002Spc }
28032002Spc status = (status >> 8) & 0xFF;
28132002Spc #ifdef TDEBUG
28232002Spc switch(status) {
28332002Spc case X_FINOK:
28432002Spc msg("Child %d finishes X_FINOK\n", childpid);
28532002Spc break;
28632002Spc case X_ABORT:
28732002Spc msg("Child %d finishes X_ABORT\n", childpid);
28832002Spc break;
28932002Spc case X_REWRITE:
29032002Spc msg("Child %d finishes X_REWRITE\n", childpid);
29132002Spc break;
29232002Spc default:
29332002Spc msg("Child %d finishes unknown %d\n",
29432002Spc childpid, status);
29532002Spc break;
29632002Spc }
29732002Spc #endif TDEBUG
29832002Spc switch(status) {
29932002Spc case X_FINOK:
30032002Spc Exit(X_FINOK);
30132002Spc case X_ABORT:
30232002Spc Exit(X_ABORT);
30332002Spc case X_REWRITE:
30432002Spc goto restore_check_point;
30532002Spc default:
30632002Spc msg("Bad return code from dump: %d\n", status);
30732002Spc Exit(X_ABORT);
30832002Spc }
30932002Spc /*NOTREACHED*/
31032002Spc } else { /* we are the child; just continue */
31132002Spc #ifdef TDEBUG
31232002Spc sleep(4); /* allow time for parent's message to get out */
31332002Spc msg("Child on Tape %d has parent %d, my pid = %d\n",
31432002Spc tapeno+1, parentpid, getpid());
31532002Spc #endif TDEBUG
316*32047Spc while (labelcheck(tapeno+1) < 0)
317*32047Spc if (!query("Problem with tape label. Do you want to retry the open?"))
318*32047Spc dumpabort();
31932002Spc #ifdef RDUMP
32032002Spc while ((to = (host ? rmtopen(tape, 2) :
32132002Spc pipeout ? 1 : creat(tape, 0666))) < 0)
32232002Spc #else RDUMP
32332002Spc while ((to = pipeout ? 1 : creat(tape, 0666)) < 0)
32432002Spc #endif RDUMP
32532002Spc if (!query("Cannot open tape. Do you want to retry the open?"))
32632002Spc dumpabort();
32732002Spc
32832002Spc enslave(); /* Share open tape file descriptor with slaves */
32932002Spc
33032002Spc asize = 0;
33132002Spc tapeno++; /* current tape sequence */
33232002Spc newtape++; /* new tape signal */
33332003Spc blks = 0;
33432003Spc if (spcl.c_type != TS_END)
33532003Spc for (i = 0; i < spcl.c_count; i++)
33632003Spc if (spcl.c_addr[i] != 0)
33732003Spc blks++;
33832003Spc spcl.c_count = blks + 1 - spcl.c_tapea + lastspclrec;
33932002Spc spcl.c_volume++;
34032002Spc spcl.c_type = TS_TAPE;
34132003Spc spcl.c_flags |= DR_NEWHEADER;
34232002Spc spclrec();
34332003Spc spcl.c_flags &=~ DR_NEWHEADER;
34432028Spc /*
34532028Spc * It may seem that the logging ought to be done
34632028Spc * at the end of the write but
34732028Spc * a) for cyclic dumps the tape has been destroyed
34832028Spc * b) if this dump fails due to a bad tape
34932028Spc * then the new tape ought to be relabelled
35032028Spc * c) if this dump fails for other reasons
35132028Spc * then the successful dump will also be logged
35232028Spc */
35332028Spc log_volume(spcl.c_label);
35432028Spc
35532028Spc if (tapeno > 1){
35632028Spc if (userlabel)
35732028Spc msg("Tape `%s' (reel %d) begins with blocks from ino %d\n",
35832028Spc spcl.c_label, tapeno, ino);
35932028Spc else
36032028Spc msg("Tape %d begins with blocks from ino %d\n",
36132028Spc tapeno, ino);
36232028Spc }
36332002Spc }
36432002Spc }
36532002Spc
dumpabort()36632002Spc dumpabort()
36732002Spc {
36832002Spc if (master != 0 && master != getpid())
36932002Spc kill(master, SIGTERM); /* Signals master to call dumpabort */
37032002Spc else {
37132002Spc killall();
37232002Spc msg("The ENTIRE dump is aborted.\n");
37332002Spc }
37432002Spc Exit(X_ABORT);
37532002Spc }
37632002Spc
Exit(status)37732002Spc Exit(status)
37832002Spc {
37932002Spc #ifdef TDEBUG
38032002Spc msg("pid = %d exits with status %d\n", getpid(), status);
38132002Spc #endif TDEBUG
38232002Spc exit(status);
38332002Spc }
38432002Spc
38532002Spc /*
38632002Spc * could use pipe() for this if flock() worked on pipes
38732002Spc */
lockfile(fd)38832002Spc lockfile(fd)
38932002Spc int fd[2];
39032002Spc {
39132002Spc char tmpname[20];
39232002Spc
39332002Spc strcpy(tmpname, "/tmp/dumplockXXXXXX");
39432002Spc mktemp(tmpname);
39532002Spc if ((fd[1] = creat(tmpname, 0400)) < 0) {
39632002Spc msg("Could not create lockfile ");
39732002Spc perror(tmpname);
39832002Spc dumpabort();
39932002Spc }
40032002Spc if ((fd[0] = open(tmpname, 0)) < 0) {
40132002Spc msg("Could not reopen lockfile ");
40232002Spc perror(tmpname);
40332002Spc dumpabort();
40432002Spc }
40532002Spc unlink(tmpname);
40632002Spc }
40732002Spc
enslave()40832002Spc enslave()
40932002Spc {
41032002Spc int first[2], prev[2], next[2], cmd[2]; /* file descriptors */
41132002Spc register int i, j;
41232002Spc
41332002Spc master = getpid();
41432002Spc signal(SIGTERM, dumpabort); /* Slave sends SIGTERM on dumpabort() */
41532002Spc signal(SIGPIPE, sigpipe);
41632002Spc signal(SIGUSR1, tperror); /* Slave sends SIGUSR1 on tape errors */
41732002Spc lockfile(first);
41832002Spc for (i = 0; i < SLAVES; i++) {
41932002Spc if (i == 0) {
42032002Spc prev[0] = first[1];
42132002Spc prev[1] = first[0];
42232002Spc } else {
42332002Spc prev[0] = next[0];
42432002Spc prev[1] = next[1];
42532002Spc flock(prev[1], LOCK_EX);
42632002Spc }
42732002Spc if (i < SLAVES - 1) {
42832002Spc lockfile(next);
42932002Spc } else {
43032002Spc next[0] = first[0];
43132002Spc next[1] = first[1]; /* Last slave loops back */
43232002Spc }
43332002Spc if (pipe(cmd) < 0 || (slavepid[i] = fork()) < 0) {
43432002Spc msg("too many slaves, %d (recompile smaller) ", i);
43532002Spc perror("");
43632002Spc dumpabort();
43732002Spc }
43832002Spc slavefd[i] = cmd[1];
43932002Spc if (slavepid[i] == 0) { /* Slave starts up here */
44032002Spc for (j = 0; j <= i; j++)
44132002Spc close(slavefd[j]);
44232002Spc signal(SIGINT, SIG_IGN); /* Master handles this */
44332002Spc doslave(cmd[0], prev, next);
44432002Spc Exit(X_FINOK);
44532002Spc }
44632002Spc close(cmd[0]);
44732002Spc if (i > 0) {
44832002Spc close(prev[0]);
44932002Spc close(prev[1]);
45032002Spc }
45132002Spc }
45232002Spc close(first[0]);
45332002Spc close(first[1]);
45432002Spc master = 0; rotor = 0;
45532002Spc }
45632002Spc
killall()45732002Spc killall()
45832002Spc {
45932002Spc register int i;
46032002Spc
46132002Spc for (i = 0; i < SLAVES; i++)
46232002Spc if (slavepid[i] > 0)
46332002Spc kill(slavepid[i], SIGKILL);
46432002Spc }
46532002Spc
46632002Spc /*
46732002Spc * Synchronization - each process has a lockfile, and shares file
46832002Spc * descriptors to the following process's lockfile. When our write
46932002Spc * completes, we release our lock on the following process's lock-
47032002Spc * file, allowing the following process to lock it and proceed. We
47132002Spc * get the lock back for the next cycle by swapping descriptors.
47232002Spc */
doslave(cmd,prev,next)47332002Spc doslave(cmd, prev, next)
47432002Spc register int cmd, prev[2], next[2];
47532002Spc {
47632002Spc register int nread, toggle = 0;
47732002Spc
47832002Spc close(fi);
47932002Spc if ((fi = open(disk, 0)) < 0) { /* Need our own seek pointer */
48032002Spc perror(" DUMP: slave couldn't reopen disk");
48132002Spc dumpabort();
48232002Spc }
48332002Spc /*
48432002Spc * Get list of blocks to dump, read the blocks into tape buffer
48532002Spc */
48632002Spc while ((nread = atomic(read, cmd, req, reqsiz)) == reqsiz) {
48732002Spc register struct req *p = req;
48832002Spc for (trecno = 0; trecno < ntrec; trecno += p->count, p += p->count) {
48932002Spc if (p->dblk) {
49032002Spc bread(p->dblk, tblock[trecno],
49132002Spc p->count * TP_BSIZE);
49232002Spc } else {
49332002Spc if (p->count != 1 || atomic(read, cmd,
49432002Spc tblock[trecno], TP_BSIZE) != TP_BSIZE) {
49532002Spc msg("Master/slave protocol botched.\n");
49632002Spc dumpabort();
49732002Spc }
49832002Spc }
49932002Spc }
50032002Spc flock(prev[toggle], LOCK_EX); /* Wait our turn */
50132002Spc
50232002Spc #ifdef RDUMP
50332002Spc if ((host ? rmtwrite(tblock[0], writesize)
50432002Spc : write(to, tblock[0], writesize)) != writesize) {
50532002Spc #else RDUMP
50632002Spc if (write(to, tblock[0], writesize) != writesize) {
50732002Spc #endif RDUMP
50832002Spc kill(master, SIGUSR1);
50932002Spc for (;;)
51032002Spc sigpause(0);
51132002Spc }
51232002Spc toggle ^= 1;
51332002Spc flock(next[toggle], LOCK_UN); /* Next slave's turn */
51432002Spc } /* Also jolts him awake */
51532002Spc if (nread != 0) {
51632002Spc perror(" DUMP: error reading command pipe");
51732002Spc dumpabort();
51832002Spc }
51932002Spc }
52032002Spc
52132002Spc /*
52232002Spc * Since a read from a pipe may not return all we asked for,
52332002Spc * or a write may not write all we ask if we get a signal,
52432002Spc * loop until the count is satisfied (or error).
52532002Spc */
52632002Spc atomic(func, fd, buf, count)
52732002Spc int (*func)(), fd, count;
52832002Spc char *buf;
52932002Spc {
53032002Spc int got, need = count;
53132002Spc
53232002Spc while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0)
53332002Spc buf += got;
53432002Spc return (got < 0 ? got : count - need);
53532002Spc }
53632028Spc
53732028Spc /*
53832028Spc * Added by Peter C
53932028Spc * put a tape drive offline
54032028Spc */
rewind_offline(fd)54132028Spc rewind_offline(fd)
54232028Spc {
54332028Spc struct mtop mtop;
54432028Spc char *eofr;
54532028Spc char *eoff;
54632028Spc
54732028Spc eofr = "Cannot write end of file record";
54832028Spc eoff = "Cannot put the tape offline";
54932028Spc #ifdef RDUMP
55032028Spc if (host) {
55132028Spc mtop.mt_op = MTWEOF;
55232028Spc mtop.mt_count = 1;
55332028Spc if (rmtioctl(fd, MTIOCTOP, &mtop) < 0)
55432028Spc perror(eofr);
55532028Spc mtop.mt_op = MTWEOF;
55632028Spc mtop.mt_count = 1;
55732028Spc if (rmtioctl(fd, MTIOCTOP, &mtop) < 0)
55832028Spc perror(eofr);
55932028Spc mtop.mt_op = MTOFFL;
56032028Spc mtop.mt_count = 1;
56132028Spc if (rmtioctl(fd, MTIOCTOP, &mtop) < 0)
56232028Spc perror(eoff);
56332028Spc return;
56432028Spc }
56532028Spc #endif RDUMP
56632028Spc mtop.mt_op = MTWEOF;
56732028Spc mtop.mt_count = 1;
56832028Spc if (ioctl(fd, MTIOCTOP, &mtop) < 0)
56932028Spc perror(eofr);
57032028Spc mtop.mt_op = MTWEOF;
57132028Spc mtop.mt_count = 1;
57232028Spc if (ioctl(fd, MTIOCTOP, &mtop) < 0)
57332028Spc perror(eofr);
57432028Spc mtop.mt_op = MTOFFL;
57532028Spc mtop.mt_count = 1;
57632028Spc if (ioctl(fd, MTIOCTOP, &mtop) < 0)
57732028Spc perror(eoff);
57832028Spc
57932028Spc }
580