122040Sdist /* 222040Sdist * Copyright (c) 1980 Regents of the University of California. 322040Sdist * All rights reserved. The Berkeley software License Agreement 422040Sdist * specifies the terms and conditions for redistribution. 522040Sdist */ 622040Sdist 717527Ssam #ifndef lint 8*46789Smckusick static char sccsid[] = "@(#)tape.c 5.14 (Berkeley) 02/28/91"; 946585Storek #endif /* not lint */ 1017527Ssam 1146585Storek #include "dump.h" 1246239Storek #include <sys/types.h> 1346585Storek #include <sys/wait.h> 1446585Storek #include <errno.h> 1546239Storek #include <fcntl.h> 1639128Smckusick #include "pathnames.h" 171425Sroot 1829899Smckusick char (*tblock)[TP_BSIZE]; /* pointer to malloc()ed buffer for tape */ 1929899Smckusick int writesize; /* size of malloc()ed buffer for tape */ 2029899Smckusick long lastspclrec = -1; /* tape block number of last written header */ 2129899Smckusick int trecno = 0; /* next record to write in current block */ 2246614Smckusick extern long blocksperfile; /* number of blocks per output file */ 2325219Smckusick extern int ntrec; /* blocking factor on tape */ 2425219Smckusick extern int cartridge; 2525219Smckusick extern int read(), write(); 2625219Smckusick #ifdef RDUMP 2725219Smckusick extern char *host; 2846585Storek int rmtopen(), rmtwrite(); 2946585Storek void rmtclose(); 3025219Smckusick #endif RDUMP 311425Sroot 3246585Storek int atomic(); 33*46789Smckusick void doslave(), enslave(), flushtape(), killall(); 3446585Storek 3510911Ssam /* 3624181Smckusick * Concurrent dump mods (Caltech) - disk block reading and tape writing 3718012Smckusick * are exported to several slave processes. While one slave writes the 3818012Smckusick * tape, the others read disk blocks; they pass control of the tape in 3924181Smckusick * a ring via flock(). The parent process traverses the filesystem and 40*46789Smckusick * sends writeheader()'s and lists of daddr's to the slaves via pipes. 4110911Ssam */ 4218012Smckusick struct req { /* instruction packets sent to slaves */ 4318012Smckusick daddr_t dblk; 4418012Smckusick int count; 4518012Smckusick } *req; 4618012Smckusick int reqsiz; 4718012Smckusick 4824181Smckusick #define SLAVES 3 /* 1 slave writing, 1 reading, 1 for slack */ 4925219Smckusick int slavefd[SLAVES]; /* pipes from master to each slave */ 5025219Smckusick int slavepid[SLAVES]; /* used by killall() */ 5125219Smckusick int rotor; /* next slave to be instructed */ 5225219Smckusick int master; /* pid of master, for sending error signals */ 5325219Smckusick int tenths; /* length of tape used per block written */ 5418012Smckusick 5546585Storek int 5610911Ssam alloctape() 5710911Ssam { 5825219Smckusick int pgoff = getpagesize() - 1; 5910911Ssam 6010911Ssam writesize = ntrec * TP_BSIZE; 6125219Smckusick reqsiz = ntrec * sizeof(struct req); 6224181Smckusick /* 6325219Smckusick * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode 6425219Smckusick * (see DEC TU80 User's Guide). The shorter gaps of 6250-bpi require 6525219Smckusick * repositioning after stopping, i.e, streaming mode, where the gap is 6625219Smckusick * variable, 0.30" to 0.45". The gap is maximal when the tape stops. 6724181Smckusick */ 6825219Smckusick tenths = writesize/density + (cartridge ? 16 : density == 625 ? 5 : 8); 6925219Smckusick /* 7025219Smckusick * Allocate tape buffer contiguous with the array of instruction 71*46789Smckusick * packets, so flushtape() can write them together with one write(). 7225219Smckusick * Align tape buffer on page boundary to speed up tape write(). 7325219Smckusick */ 7424181Smckusick req = (struct req *)malloc(reqsiz + writesize + pgoff); 7524181Smckusick if (req == NULL) 7624181Smckusick return(0); 7724181Smckusick tblock = (char (*)[TP_BSIZE]) (((long)&req[ntrec] + pgoff) &~ pgoff); 7825219Smckusick req = (struct req *)tblock - ntrec; 7924181Smckusick return(1); 8010911Ssam } 8110911Ssam 8225219Smckusick 8346585Storek void 84*46789Smckusick writerec(dp) 855329Smckusic char *dp; 861425Sroot { 8718012Smckusick req[trecno].dblk = (daddr_t)0; 8818012Smckusick req[trecno].count = 1; 8924181Smckusick *(union u_spcl *)(*tblock++) = *(union u_spcl *)dp; /* movc3 */ 9029899Smckusick lastspclrec = spcl.c_tapea; 9124181Smckusick trecno++; 921425Sroot spcl.c_tapea++; 9346585Storek if (trecno >= ntrec) 94*46789Smckusick flushtape(); 951425Sroot } 961425Sroot 9746585Storek void 98*46789Smckusick dumpblock(blkno, size) 994774Smckusic daddr_t blkno; 1004774Smckusic int size; 1011425Sroot { 10225219Smckusick int avail, tpblks, dblkno; 1031425Sroot 1045329Smckusic dblkno = fsbtodb(sblock, blkno); 10546585Storek tpblks = size >> tp_bshift; 10618012Smckusick while ((avail = MIN(tpblks, ntrec - trecno)) > 0) { 10718012Smckusick req[trecno].dblk = dblkno; 10818012Smckusick req[trecno].count = avail; 10925219Smckusick trecno += avail; 1104774Smckusic spcl.c_tapea += avail; 11125219Smckusick if (trecno >= ntrec) 112*46789Smckusick flushtape(); 11346585Storek dblkno += avail << (tp_bshift - dev_bshift); 1145329Smckusic tpblks -= avail; 1154774Smckusic } 1161425Sroot } 1171425Sroot 1181425Sroot int nogripe = 0; 1191425Sroot 12046585Storek void 12146585Storek tperror() 12246585Storek { 12318012Smckusick if (pipeout) { 12446614Smckusick msg("write error on %s\n", tape); 12546585Storek quit("Cannot recover\n"); 12618012Smckusick /* NOTREACHED */ 12718012Smckusick } 12846614Smckusick msg("write error %d blocks into volume %d\n", blockswritten, tapeno); 12946614Smckusick broadcast("DUMP WRITE ERROR!\n"); 13018012Smckusick if (!query("Do you want to restart?")) 13118012Smckusick dumpabort(); 13246614Smckusick msg("Closing this volume. Prepare to restart with new media;\n"); 13318012Smckusick msg("this dump volume will be rewritten.\n"); 13424181Smckusick killall(); 13518012Smckusick nogripe = 1; 13618012Smckusick close_rewind(); 13718012Smckusick Exit(X_REWRITE); 13818012Smckusick } 13918012Smckusick 14046585Storek void 14125219Smckusick sigpipe() 14225219Smckusick { 14325219Smckusick 14446585Storek quit("Broken pipe\n"); 14525219Smckusick } 14625219Smckusick 14746585Storek void 148*46789Smckusick flushtape() 14918012Smckusick { 15025219Smckusick int siz = (char *)tblock - (char *)req; 1511425Sroot 15246585Storek if (atomic(write, slavefd[rotor], req, siz) != siz) 15346585Storek quit("error writing command pipe: %s\n", strerror(errno)); 15446585Storek if (++rotor >= SLAVES) 15546585Storek rotor = 0; 15618012Smckusick tblock = (char (*)[TP_BSIZE]) &req[ntrec]; 1571425Sroot trecno = 0; 15824181Smckusick asize += tenths; 15910911Ssam blockswritten += ntrec; 16046614Smckusick if (!pipeout && (blocksperfile ? 16146614Smckusick (blockswritten >= blocksperfile) : (asize > tsize))) { 1621425Sroot close_rewind(); 163*46789Smckusick startnewtape(); 1641425Sroot } 1651425Sroot timeest(); 1661425Sroot } 1671425Sroot 16846585Storek void 16946239Storek trewind() 1701425Sroot { 17124181Smckusick int f; 17212331Smckusick 17312331Smckusick if (pipeout) 17412331Smckusick return; 17518012Smckusick for (f = 0; f < SLAVES; f++) 17618012Smckusick close(slavefd[f]); 17746585Storek while (wait((int *)NULL) >= 0) /* wait for any signals from slaves */ 17846585Storek /* void */; 17918012Smckusick msg("Tape rewinding\n"); 18018012Smckusick #ifdef RDUMP 18125219Smckusick if (host) { 18225219Smckusick rmtclose(); 18325219Smckusick while (rmtopen(tape, 0) < 0) 18425219Smckusick sleep(10); 18525219Smckusick rmtclose(); 18625219Smckusick return; 18725219Smckusick } 18825219Smckusick #endif RDUMP 189*46789Smckusick close(tapefd); 1903214Swnj while ((f = open(tape, 0)) < 0) 1913214Swnj sleep (10); 1923214Swnj close(f); 1931425Sroot } 1941425Sroot 19546585Storek void 1961425Sroot close_rewind() 1971425Sroot { 19846239Storek trewind(); 19918012Smckusick if (!nogripe) { 20046614Smckusick msg("Change Volumes: Mount volume #%d\n", tapeno+1); 20146614Smckusick broadcast("CHANGE DUMP VOLUMES!\7\7\n"); 2021425Sroot } 20346614Smckusick while (!query("Is the new volume mounted and ready to go?")) 20425219Smckusick if (query("Do you want to abort?")) { 2051425Sroot dumpabort(); 20625219Smckusick /*NOTREACHED*/ 20725219Smckusick } 2081425Sroot } 2091425Sroot 2101425Sroot /* 21118012Smckusick * We implement taking and restoring checkpoints on the tape level. 2121425Sroot * When each tape is opened, a new process is created by forking; this 2131425Sroot * saves all of the necessary context in the parent. The child 2141425Sroot * continues the dump; the parent waits around, saving the context. 2151425Sroot * If the child returns X_REWRITE, then it had problems writing that tape; 2161425Sroot * this causes the parent to fork again, duplicating the context, and 2171425Sroot * everything continues as if nothing had happened. 2181425Sroot */ 2191425Sroot 22046585Storek void 221*46789Smckusick startnewtape() 2221425Sroot { 2231425Sroot int parentpid; 2241425Sroot int childpid; 2251425Sroot int status; 2261425Sroot int waitpid; 22739164Sbostic sig_t interrupt; 22829899Smckusick int blks, i; 2291425Sroot 23039164Sbostic interrupt = signal(SIGINT, SIG_IGN); 2311425Sroot parentpid = getpid(); 2321425Sroot 2331425Sroot restore_check_point: 23439164Sbostic (void)signal(SIGINT, interrupt); 23525219Smckusick /* 23625219Smckusick * All signals are inherited... 23725219Smckusick */ 2381425Sroot childpid = fork(); 23918012Smckusick if (childpid < 0) { 2401425Sroot msg("Context save fork fails in parent %d\n", parentpid); 2411425Sroot Exit(X_ABORT); 2421425Sroot } 24318012Smckusick if (childpid != 0) { 2441425Sroot /* 2451425Sroot * PARENT: 2461425Sroot * save the context by waiting 2471425Sroot * until the child doing all of the work returns. 24818012Smckusick * don't catch the interrupt 2491425Sroot */ 25025219Smckusick signal(SIGINT, SIG_IGN); 2511425Sroot #ifdef TDEBUG 2521425Sroot msg("Tape: %d; parent process: %d child process %d\n", 2531425Sroot tapeno+1, parentpid, childpid); 2541425Sroot #endif TDEBUG 25518012Smckusick while ((waitpid = wait(&status)) != childpid) 25618012Smckusick msg("Parent %d waiting for child %d has another child %d return\n", 25718012Smckusick parentpid, childpid, waitpid); 25818012Smckusick if (status & 0xFF) { 2591425Sroot msg("Child %d returns LOB status %o\n", 2601425Sroot childpid, status&0xFF); 2611425Sroot } 2621425Sroot status = (status >> 8) & 0xFF; 2631425Sroot #ifdef TDEBUG 26418012Smckusick switch(status) { 2651425Sroot case X_FINOK: 2661425Sroot msg("Child %d finishes X_FINOK\n", childpid); 2671425Sroot break; 2681425Sroot case X_ABORT: 2691425Sroot msg("Child %d finishes X_ABORT\n", childpid); 2701425Sroot break; 2711425Sroot case X_REWRITE: 2721425Sroot msg("Child %d finishes X_REWRITE\n", childpid); 2731425Sroot break; 2741425Sroot default: 27518012Smckusick msg("Child %d finishes unknown %d\n", 27625219Smckusick childpid, status); 2771425Sroot break; 2781425Sroot } 2791425Sroot #endif TDEBUG 28018012Smckusick switch(status) { 2811425Sroot case X_FINOK: 2821425Sroot Exit(X_FINOK); 2831425Sroot case X_ABORT: 2841425Sroot Exit(X_ABORT); 2851425Sroot case X_REWRITE: 2861425Sroot goto restore_check_point; 2871425Sroot default: 2881425Sroot msg("Bad return code from dump: %d\n", status); 2891425Sroot Exit(X_ABORT); 2901425Sroot } 2911425Sroot /*NOTREACHED*/ 2921425Sroot } else { /* we are the child; just continue */ 2931425Sroot #ifdef TDEBUG 2941425Sroot sleep(4); /* allow time for parent's message to get out */ 2951425Sroot msg("Child on Tape %d has parent %d, my pid = %d\n", 2961425Sroot tapeno+1, parentpid, getpid()); 29725219Smckusick #endif TDEBUG 29818012Smckusick #ifdef RDUMP 299*46789Smckusick while ((tapefd = (host ? rmtopen(tape, 2) : 300*46789Smckusick pipeout ? 1 : open(tape, O_WRONLY|O_CREAT, 0666))) < 0) 30125219Smckusick #else RDUMP 302*46789Smckusick while ((tapefd = 303*46789Smckusick pipeout ? 1 : open(tape, O_WRONLY|O_CREAT, 0666)) < 0) 30424181Smckusick #endif RDUMP 30539128Smckusick { 30646614Smckusick msg("Cannot open output \"%s\".\n", tape); 30739128Smckusick if (!query("Do you want to retry the open?")) 30818012Smckusick dumpabort(); 30939128Smckusick } 3101425Sroot 31118012Smckusick enslave(); /* Share open tape file descriptor with slaves */ 31218012Smckusick 3131425Sroot asize = 0; 3141425Sroot tapeno++; /* current tape sequence */ 3151425Sroot newtape++; /* new tape signal */ 31629899Smckusick blks = 0; 31729899Smckusick if (spcl.c_type != TS_END) 31829899Smckusick for (i = 0; i < spcl.c_count; i++) 31929899Smckusick if (spcl.c_addr[i] != 0) 32029899Smckusick blks++; 32129899Smckusick spcl.c_count = blks + 1 - spcl.c_tapea + lastspclrec; 3221425Sroot spcl.c_volume++; 3231425Sroot spcl.c_type = TS_TAPE; 32430432Smckusick spcl.c_flags |= DR_NEWHEADER; 325*46789Smckusick writeheader(curino); 32630432Smckusick spcl.c_flags &=~ DR_NEWHEADER; 3271425Sroot if (tapeno > 1) 328*46789Smckusick msg("Tape %d begins with blocks from inode %d\n", 329*46789Smckusick tapeno, curino); 3301425Sroot } 3311425Sroot } 3321425Sroot 33346585Storek void 3341425Sroot dumpabort() 3351425Sroot { 33618012Smckusick if (master != 0 && master != getpid()) 33725219Smckusick kill(master, SIGTERM); /* Signals master to call dumpabort */ 33824181Smckusick else { 33924181Smckusick killall(); 34024181Smckusick msg("The ENTIRE dump is aborted.\n"); 34124181Smckusick } 3421425Sroot Exit(X_ABORT); 3431425Sroot } 3441425Sroot 34546585Storek void 3461425Sroot Exit(status) 34746239Storek int status; 3481425Sroot { 3491425Sroot #ifdef TDEBUG 3501425Sroot msg("pid = %d exits with status %d\n", getpid(), status); 3511425Sroot #endif TDEBUG 3521925Swnj exit(status); 3531425Sroot } 35418012Smckusick 35524181Smckusick /* 35625219Smckusick * could use pipe() for this if flock() worked on pipes 35724181Smckusick */ 35846585Storek void 35924181Smckusick lockfile(fd) 36024181Smckusick int fd[2]; 36124181Smckusick { 36224181Smckusick char tmpname[20]; 36318012Smckusick 36439128Smckusick strcpy(tmpname, _PATH_LOCK); 36524181Smckusick mktemp(tmpname); 36646585Storek if ((fd[1] = creat(tmpname, 0400)) < 0) 36746585Storek quit("cannot create lockfile %s: %s\n", 36846585Storek tmpname, strerror(errno)); 36946585Storek if ((fd[0] = open(tmpname, 0)) < 0) 37046585Storek quit("cannot reopen lockfile %s: %s\n", 37146585Storek tmpname, strerror(errno)); 37246585Storek (void) unlink(tmpname); 37324181Smckusick } 37424181Smckusick 37546585Storek void 37618012Smckusick enslave() 37718012Smckusick { 37824181Smckusick int first[2], prev[2], next[2], cmd[2]; /* file descriptors */ 37924181Smckusick register int i, j; 38018012Smckusick 38118012Smckusick master = getpid(); 38225219Smckusick signal(SIGTERM, dumpabort); /* Slave sends SIGTERM on dumpabort() */ 38325219Smckusick signal(SIGPIPE, sigpipe); 38425219Smckusick signal(SIGUSR1, tperror); /* Slave sends SIGUSR1 on tape errors */ 38524181Smckusick lockfile(first); 38624181Smckusick for (i = 0; i < SLAVES; i++) { 38724181Smckusick if (i == 0) { 38824181Smckusick prev[0] = first[1]; 38924181Smckusick prev[1] = first[0]; 39024181Smckusick } else { 39124181Smckusick prev[0] = next[0]; 39224181Smckusick prev[1] = next[1]; 39324181Smckusick flock(prev[1], LOCK_EX); 39424181Smckusick } 39526485Smckusick if (i < SLAVES - 1) { 39626485Smckusick lockfile(next); 39726485Smckusick } else { 39826485Smckusick next[0] = first[0]; 39926485Smckusick next[1] = first[1]; /* Last slave loops back */ 40026485Smckusick } 40146585Storek if (pipe(cmd) < 0 || (slavepid[i] = fork()) < 0) 40246585Storek quit("too many slaves, %d (recompile smaller): %s\n", 40346585Storek i, strerror(errno)); 40418012Smckusick slavefd[i] = cmd[1]; 40525219Smckusick if (slavepid[i] == 0) { /* Slave starts up here */ 40618012Smckusick for (j = 0; j <= i; j++) 40718012Smckusick close(slavefd[j]); 40825219Smckusick signal(SIGINT, SIG_IGN); /* Master handles this */ 40925219Smckusick doslave(cmd[0], prev, next); 41018012Smckusick Exit(X_FINOK); 41118012Smckusick } 41218012Smckusick close(cmd[0]); 41324181Smckusick if (i > 0) { 41424181Smckusick close(prev[0]); 41524181Smckusick close(prev[1]); 41624181Smckusick } 41718012Smckusick } 41824181Smckusick close(first[0]); 41924181Smckusick close(first[1]); 42024181Smckusick master = 0; rotor = 0; 42118012Smckusick } 42218012Smckusick 42346585Storek void 42424181Smckusick killall() 42518012Smckusick { 42624181Smckusick register int i; 42719982Smckusick 42824181Smckusick for (i = 0; i < SLAVES; i++) 42924181Smckusick if (slavepid[i] > 0) 43024181Smckusick kill(slavepid[i], SIGKILL); 43118012Smckusick } 43218012Smckusick 43324181Smckusick /* 43424181Smckusick * Synchronization - each process has a lockfile, and shares file 43524181Smckusick * descriptors to the following process's lockfile. When our write 43624181Smckusick * completes, we release our lock on the following process's lock- 43724181Smckusick * file, allowing the following process to lock it and proceed. We 43824181Smckusick * get the lock back for the next cycle by swapping descriptors. 43924181Smckusick */ 44046585Storek void 44125219Smckusick doslave(cmd, prev, next) 44225219Smckusick register int cmd, prev[2], next[2]; 44319982Smckusick { 44425219Smckusick register int nread, toggle = 0; 44519982Smckusick 446*46789Smckusick /* 447*46789Smckusick * Need our own seek pointer. 448*46789Smckusick */ 449*46789Smckusick close(diskfd); 450*46789Smckusick if ((diskfd = open(disk, O_RDONLY)) < 0) 45146585Storek quit("slave couldn't reopen disk: %s\n", strerror(errno)); 45224181Smckusick /* 45325219Smckusick * Get list of blocks to dump, read the blocks into tape buffer 45424181Smckusick */ 45525219Smckusick while ((nread = atomic(read, cmd, req, reqsiz)) == reqsiz) { 45618012Smckusick register struct req *p = req; 45718012Smckusick for (trecno = 0; trecno < ntrec; trecno += p->count, p += p->count) { 45818012Smckusick if (p->dblk) { 45918012Smckusick bread(p->dblk, tblock[trecno], 46025219Smckusick p->count * TP_BSIZE); 46118012Smckusick } else { 46225219Smckusick if (p->count != 1 || atomic(read, cmd, 46346585Storek tblock[trecno], TP_BSIZE) != TP_BSIZE) 46446585Storek quit("master/slave protocol botched.\n"); 46518012Smckusick } 46618012Smckusick } 46724181Smckusick flock(prev[toggle], LOCK_EX); /* Wait our turn */ 46825219Smckusick 46918012Smckusick #ifdef RDUMP 47025219Smckusick if ((host ? rmtwrite(tblock[0], writesize) 471*46789Smckusick : write(tapefd, tblock[0], writesize)) != writesize) { 47225219Smckusick #else RDUMP 473*46789Smckusick if (write(tapefd, tblock[0], writesize) != writesize) { 47424181Smckusick #endif RDUMP 47525219Smckusick kill(master, SIGUSR1); 47625219Smckusick for (;;) 47725219Smckusick sigpause(0); 47818012Smckusick } 47924181Smckusick toggle ^= 1; 48024181Smckusick flock(next[toggle], LOCK_UN); /* Next slave's turn */ 48124181Smckusick } /* Also jolts him awake */ 48246585Storek if (nread != 0) 48346585Storek quit("error reading command pipe: %s\n", strerror(errno)); 48418012Smckusick } 48519947Smckusick 48619947Smckusick /* 48725219Smckusick * Since a read from a pipe may not return all we asked for, 48825219Smckusick * or a write may not write all we ask if we get a signal, 48925219Smckusick * loop until the count is satisfied (or error). 49019947Smckusick */ 49146585Storek int 49225219Smckusick atomic(func, fd, buf, count) 49325219Smckusick int (*func)(), fd, count; 49419947Smckusick char *buf; 49519947Smckusick { 49625219Smckusick int got, need = count; 49719947Smckusick 49825219Smckusick while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0) 49919947Smckusick buf += got; 50025219Smckusick return (got < 0 ? got : count - need); 50119947Smckusick } 502