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*46795Sbostic static char sccsid[] = "@(#)tape.c 5.15 (Berkeley) 02/28/91"; 946585Storek #endif /* not lint */ 1017527Ssam 11*46795Sbostic #include <sys/param.h> 1246585Storek #include <sys/wait.h> 13*46795Sbostic #include <ufs/dir.h> 14*46795Sbostic #include <ufs/dinode.h> 15*46795Sbostic #include <ufs/fs.h> 16*46795Sbostic #include <signal.h> 17*46795Sbostic #include <fcntl.h> 18*46795Sbostic #include <protocols/dumprestore.h> 1946585Storek #include <errno.h> 20*46795Sbostic #ifdef __STDC__ 21*46795Sbostic #include <unistd.h> 22*46795Sbostic #include <stdlib.h> 23*46795Sbostic #include <string.h> 24*46795Sbostic #endif 25*46795Sbostic #include "dump.h" 2639128Smckusick #include "pathnames.h" 271425Sroot 2829899Smckusick char (*tblock)[TP_BSIZE]; /* pointer to malloc()ed buffer for tape */ 2929899Smckusick int writesize; /* size of malloc()ed buffer for tape */ 3029899Smckusick long lastspclrec = -1; /* tape block number of last written header */ 3129899Smckusick int trecno = 0; /* next record to write in current block */ 3246614Smckusick extern long blocksperfile; /* number of blocks per output file */ 3325219Smckusick extern int ntrec; /* blocking factor on tape */ 3425219Smckusick extern int cartridge; 3525219Smckusick #ifdef RDUMP 3625219Smckusick extern char *host; 3746585Storek int rmtopen(), rmtwrite(); 3846585Storek void rmtclose(); 3925219Smckusick #endif RDUMP 401425Sroot 4146585Storek int atomic(); 4246789Smckusick void doslave(), enslave(), flushtape(), killall(); 4346585Storek 4410911Ssam /* 4524181Smckusick * Concurrent dump mods (Caltech) - disk block reading and tape writing 4618012Smckusick * are exported to several slave processes. While one slave writes the 4718012Smckusick * tape, the others read disk blocks; they pass control of the tape in 4824181Smckusick * a ring via flock(). The parent process traverses the filesystem and 4946789Smckusick * sends writeheader()'s and lists of daddr's to the slaves via pipes. 5010911Ssam */ 5118012Smckusick struct req { /* instruction packets sent to slaves */ 5218012Smckusick daddr_t dblk; 5318012Smckusick int count; 5418012Smckusick } *req; 5518012Smckusick int reqsiz; 5618012Smckusick 5724181Smckusick #define SLAVES 3 /* 1 slave writing, 1 reading, 1 for slack */ 5825219Smckusick int slavefd[SLAVES]; /* pipes from master to each slave */ 5925219Smckusick int slavepid[SLAVES]; /* used by killall() */ 6025219Smckusick int rotor; /* next slave to be instructed */ 6125219Smckusick int master; /* pid of master, for sending error signals */ 6225219Smckusick int tenths; /* length of tape used per block written */ 6318012Smckusick 6446585Storek int 6510911Ssam alloctape() 6610911Ssam { 6725219Smckusick int pgoff = getpagesize() - 1; 6810911Ssam 6910911Ssam writesize = ntrec * TP_BSIZE; 7025219Smckusick reqsiz = ntrec * sizeof(struct req); 7124181Smckusick /* 7225219Smckusick * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode 7325219Smckusick * (see DEC TU80 User's Guide). The shorter gaps of 6250-bpi require 7425219Smckusick * repositioning after stopping, i.e, streaming mode, where the gap is 7525219Smckusick * variable, 0.30" to 0.45". The gap is maximal when the tape stops. 7624181Smckusick */ 7725219Smckusick tenths = writesize/density + (cartridge ? 16 : density == 625 ? 5 : 8); 7825219Smckusick /* 7925219Smckusick * Allocate tape buffer contiguous with the array of instruction 8046789Smckusick * packets, so flushtape() can write them together with one write(). 8125219Smckusick * Align tape buffer on page boundary to speed up tape write(). 8225219Smckusick */ 8324181Smckusick req = (struct req *)malloc(reqsiz + writesize + pgoff); 8424181Smckusick if (req == NULL) 8524181Smckusick return(0); 8624181Smckusick tblock = (char (*)[TP_BSIZE]) (((long)&req[ntrec] + pgoff) &~ pgoff); 8725219Smckusick req = (struct req *)tblock - ntrec; 8824181Smckusick return(1); 8910911Ssam } 9010911Ssam 9125219Smckusick 9246585Storek void 9346789Smckusick writerec(dp) 945329Smckusic char *dp; 951425Sroot { 9618012Smckusick req[trecno].dblk = (daddr_t)0; 9718012Smckusick req[trecno].count = 1; 9824181Smckusick *(union u_spcl *)(*tblock++) = *(union u_spcl *)dp; /* movc3 */ 9929899Smckusick lastspclrec = spcl.c_tapea; 10024181Smckusick trecno++; 1011425Sroot spcl.c_tapea++; 10246585Storek if (trecno >= ntrec) 10346789Smckusick flushtape(); 1041425Sroot } 1051425Sroot 10646585Storek void 10746789Smckusick dumpblock(blkno, size) 1084774Smckusic daddr_t blkno; 1094774Smckusic int size; 1101425Sroot { 11125219Smckusick int avail, tpblks, dblkno; 1121425Sroot 1135329Smckusic dblkno = fsbtodb(sblock, blkno); 11446585Storek tpblks = size >> tp_bshift; 11518012Smckusick while ((avail = MIN(tpblks, ntrec - trecno)) > 0) { 11618012Smckusick req[trecno].dblk = dblkno; 11718012Smckusick req[trecno].count = avail; 11825219Smckusick trecno += avail; 1194774Smckusic spcl.c_tapea += avail; 12025219Smckusick if (trecno >= ntrec) 12146789Smckusick flushtape(); 12246585Storek dblkno += avail << (tp_bshift - dev_bshift); 1235329Smckusic tpblks -= avail; 1244774Smckusic } 1251425Sroot } 1261425Sroot 1271425Sroot int nogripe = 0; 1281425Sroot 12946585Storek void 13046585Storek tperror() 13146585Storek { 13218012Smckusick if (pipeout) { 13346614Smckusick msg("write error on %s\n", tape); 13446585Storek quit("Cannot recover\n"); 13518012Smckusick /* NOTREACHED */ 13618012Smckusick } 13746614Smckusick msg("write error %d blocks into volume %d\n", blockswritten, tapeno); 13846614Smckusick broadcast("DUMP WRITE ERROR!\n"); 13918012Smckusick if (!query("Do you want to restart?")) 14018012Smckusick dumpabort(); 14146614Smckusick msg("Closing this volume. Prepare to restart with new media;\n"); 14218012Smckusick msg("this dump volume will be rewritten.\n"); 14324181Smckusick killall(); 14418012Smckusick nogripe = 1; 14518012Smckusick close_rewind(); 14618012Smckusick Exit(X_REWRITE); 14718012Smckusick } 14818012Smckusick 14946585Storek void 15025219Smckusick sigpipe() 15125219Smckusick { 15225219Smckusick 15346585Storek quit("Broken pipe\n"); 15425219Smckusick } 15525219Smckusick 15646585Storek void 15746789Smckusick flushtape() 15818012Smckusick { 159*46795Sbostic #ifndef __STDC__ 160*46795Sbostic int write(); 161*46795Sbostic #endif 162*46795Sbostic 16325219Smckusick int siz = (char *)tblock - (char *)req; 1641425Sroot 16546585Storek if (atomic(write, slavefd[rotor], req, siz) != siz) 16646585Storek quit("error writing command pipe: %s\n", strerror(errno)); 16746585Storek if (++rotor >= SLAVES) 16846585Storek rotor = 0; 16918012Smckusick tblock = (char (*)[TP_BSIZE]) &req[ntrec]; 1701425Sroot trecno = 0; 17124181Smckusick asize += tenths; 17210911Ssam blockswritten += ntrec; 17346614Smckusick if (!pipeout && (blocksperfile ? 17446614Smckusick (blockswritten >= blocksperfile) : (asize > tsize))) { 1751425Sroot close_rewind(); 17646789Smckusick startnewtape(); 1771425Sroot } 1781425Sroot timeest(); 1791425Sroot } 1801425Sroot 18146585Storek void 18246239Storek trewind() 1831425Sroot { 18424181Smckusick int f; 18512331Smckusick 18612331Smckusick if (pipeout) 18712331Smckusick return; 18818012Smckusick for (f = 0; f < SLAVES; f++) 18918012Smckusick close(slavefd[f]); 19046585Storek while (wait((int *)NULL) >= 0) /* wait for any signals from slaves */ 19146585Storek /* void */; 19218012Smckusick msg("Tape rewinding\n"); 19318012Smckusick #ifdef RDUMP 19425219Smckusick if (host) { 19525219Smckusick rmtclose(); 19625219Smckusick while (rmtopen(tape, 0) < 0) 19725219Smckusick sleep(10); 19825219Smckusick rmtclose(); 19925219Smckusick return; 20025219Smckusick } 20125219Smckusick #endif RDUMP 20246789Smckusick close(tapefd); 2033214Swnj while ((f = open(tape, 0)) < 0) 2043214Swnj sleep (10); 2053214Swnj close(f); 2061425Sroot } 2071425Sroot 20846585Storek void 2091425Sroot close_rewind() 2101425Sroot { 21146239Storek trewind(); 21218012Smckusick if (!nogripe) { 21346614Smckusick msg("Change Volumes: Mount volume #%d\n", tapeno+1); 21446614Smckusick broadcast("CHANGE DUMP VOLUMES!\7\7\n"); 2151425Sroot } 21646614Smckusick while (!query("Is the new volume mounted and ready to go?")) 21725219Smckusick if (query("Do you want to abort?")) { 2181425Sroot dumpabort(); 21925219Smckusick /*NOTREACHED*/ 22025219Smckusick } 2211425Sroot } 2221425Sroot 2231425Sroot /* 22418012Smckusick * We implement taking and restoring checkpoints on the tape level. 2251425Sroot * When each tape is opened, a new process is created by forking; this 2261425Sroot * saves all of the necessary context in the parent. The child 2271425Sroot * continues the dump; the parent waits around, saving the context. 2281425Sroot * If the child returns X_REWRITE, then it had problems writing that tape; 2291425Sroot * this causes the parent to fork again, duplicating the context, and 2301425Sroot * everything continues as if nothing had happened. 2311425Sroot */ 2321425Sroot 23346585Storek void 23446789Smckusick startnewtape() 2351425Sroot { 2361425Sroot int parentpid; 2371425Sroot int childpid; 2381425Sroot int status; 2391425Sroot int waitpid; 24039164Sbostic sig_t interrupt; 24129899Smckusick int blks, i; 2421425Sroot 24339164Sbostic interrupt = signal(SIGINT, SIG_IGN); 2441425Sroot parentpid = getpid(); 2451425Sroot 2461425Sroot restore_check_point: 24739164Sbostic (void)signal(SIGINT, interrupt); 24825219Smckusick /* 24925219Smckusick * All signals are inherited... 25025219Smckusick */ 2511425Sroot childpid = fork(); 25218012Smckusick if (childpid < 0) { 2531425Sroot msg("Context save fork fails in parent %d\n", parentpid); 2541425Sroot Exit(X_ABORT); 2551425Sroot } 25618012Smckusick if (childpid != 0) { 2571425Sroot /* 2581425Sroot * PARENT: 2591425Sroot * save the context by waiting 2601425Sroot * until the child doing all of the work returns. 26118012Smckusick * don't catch the interrupt 2621425Sroot */ 26325219Smckusick signal(SIGINT, SIG_IGN); 2641425Sroot #ifdef TDEBUG 2651425Sroot msg("Tape: %d; parent process: %d child process %d\n", 2661425Sroot tapeno+1, parentpid, childpid); 2671425Sroot #endif TDEBUG 26818012Smckusick while ((waitpid = wait(&status)) != childpid) 26918012Smckusick msg("Parent %d waiting for child %d has another child %d return\n", 27018012Smckusick parentpid, childpid, waitpid); 27118012Smckusick if (status & 0xFF) { 2721425Sroot msg("Child %d returns LOB status %o\n", 2731425Sroot childpid, status&0xFF); 2741425Sroot } 2751425Sroot status = (status >> 8) & 0xFF; 2761425Sroot #ifdef TDEBUG 27718012Smckusick switch(status) { 2781425Sroot case X_FINOK: 2791425Sroot msg("Child %d finishes X_FINOK\n", childpid); 2801425Sroot break; 2811425Sroot case X_ABORT: 2821425Sroot msg("Child %d finishes X_ABORT\n", childpid); 2831425Sroot break; 2841425Sroot case X_REWRITE: 2851425Sroot msg("Child %d finishes X_REWRITE\n", childpid); 2861425Sroot break; 2871425Sroot default: 28818012Smckusick msg("Child %d finishes unknown %d\n", 28925219Smckusick childpid, status); 2901425Sroot break; 2911425Sroot } 2921425Sroot #endif TDEBUG 29318012Smckusick switch(status) { 2941425Sroot case X_FINOK: 2951425Sroot Exit(X_FINOK); 2961425Sroot case X_ABORT: 2971425Sroot Exit(X_ABORT); 2981425Sroot case X_REWRITE: 2991425Sroot goto restore_check_point; 3001425Sroot default: 3011425Sroot msg("Bad return code from dump: %d\n", status); 3021425Sroot Exit(X_ABORT); 3031425Sroot } 3041425Sroot /*NOTREACHED*/ 3051425Sroot } else { /* we are the child; just continue */ 3061425Sroot #ifdef TDEBUG 3071425Sroot sleep(4); /* allow time for parent's message to get out */ 3081425Sroot msg("Child on Tape %d has parent %d, my pid = %d\n", 3091425Sroot tapeno+1, parentpid, getpid()); 31025219Smckusick #endif TDEBUG 31118012Smckusick #ifdef RDUMP 31246789Smckusick while ((tapefd = (host ? rmtopen(tape, 2) : 31346789Smckusick pipeout ? 1 : open(tape, O_WRONLY|O_CREAT, 0666))) < 0) 31425219Smckusick #else RDUMP 31546789Smckusick while ((tapefd = 31646789Smckusick pipeout ? 1 : open(tape, O_WRONLY|O_CREAT, 0666)) < 0) 31724181Smckusick #endif RDUMP 31839128Smckusick { 31946614Smckusick msg("Cannot open output \"%s\".\n", tape); 32039128Smckusick if (!query("Do you want to retry the open?")) 32118012Smckusick dumpabort(); 32239128Smckusick } 3231425Sroot 32418012Smckusick enslave(); /* Share open tape file descriptor with slaves */ 32518012Smckusick 3261425Sroot asize = 0; 3271425Sroot tapeno++; /* current tape sequence */ 3281425Sroot newtape++; /* new tape signal */ 32929899Smckusick blks = 0; 33029899Smckusick if (spcl.c_type != TS_END) 33129899Smckusick for (i = 0; i < spcl.c_count; i++) 33229899Smckusick if (spcl.c_addr[i] != 0) 33329899Smckusick blks++; 33429899Smckusick spcl.c_count = blks + 1 - spcl.c_tapea + lastspclrec; 3351425Sroot spcl.c_volume++; 3361425Sroot spcl.c_type = TS_TAPE; 33730432Smckusick spcl.c_flags |= DR_NEWHEADER; 33846789Smckusick writeheader(curino); 33930432Smckusick spcl.c_flags &=~ DR_NEWHEADER; 3401425Sroot if (tapeno > 1) 34146789Smckusick msg("Tape %d begins with blocks from inode %d\n", 34246789Smckusick tapeno, curino); 3431425Sroot } 3441425Sroot } 3451425Sroot 34646585Storek void 3471425Sroot dumpabort() 3481425Sroot { 34918012Smckusick if (master != 0 && master != getpid()) 35025219Smckusick kill(master, SIGTERM); /* Signals master to call dumpabort */ 35124181Smckusick else { 35224181Smckusick killall(); 35324181Smckusick msg("The ENTIRE dump is aborted.\n"); 35424181Smckusick } 3551425Sroot Exit(X_ABORT); 3561425Sroot } 3571425Sroot 35846585Storek void 3591425Sroot Exit(status) 36046239Storek int status; 3611425Sroot { 3621425Sroot #ifdef TDEBUG 3631425Sroot msg("pid = %d exits with status %d\n", getpid(), status); 3641425Sroot #endif TDEBUG 3651925Swnj exit(status); 3661425Sroot } 36718012Smckusick 36824181Smckusick /* 36925219Smckusick * could use pipe() for this if flock() worked on pipes 37024181Smckusick */ 37146585Storek void 37224181Smckusick lockfile(fd) 37324181Smckusick int fd[2]; 37424181Smckusick { 37524181Smckusick char tmpname[20]; 37618012Smckusick 37739128Smckusick strcpy(tmpname, _PATH_LOCK); 37824181Smckusick mktemp(tmpname); 37946585Storek if ((fd[1] = creat(tmpname, 0400)) < 0) 38046585Storek quit("cannot create lockfile %s: %s\n", 38146585Storek tmpname, strerror(errno)); 38246585Storek if ((fd[0] = open(tmpname, 0)) < 0) 38346585Storek quit("cannot reopen lockfile %s: %s\n", 38446585Storek tmpname, strerror(errno)); 38546585Storek (void) unlink(tmpname); 38624181Smckusick } 38724181Smckusick 38846585Storek void 38918012Smckusick enslave() 39018012Smckusick { 39124181Smckusick int first[2], prev[2], next[2], cmd[2]; /* file descriptors */ 39224181Smckusick register int i, j; 39318012Smckusick 39418012Smckusick master = getpid(); 39525219Smckusick signal(SIGTERM, dumpabort); /* Slave sends SIGTERM on dumpabort() */ 39625219Smckusick signal(SIGPIPE, sigpipe); 39725219Smckusick signal(SIGUSR1, tperror); /* Slave sends SIGUSR1 on tape errors */ 39824181Smckusick lockfile(first); 39924181Smckusick for (i = 0; i < SLAVES; i++) { 40024181Smckusick if (i == 0) { 40124181Smckusick prev[0] = first[1]; 40224181Smckusick prev[1] = first[0]; 40324181Smckusick } else { 40424181Smckusick prev[0] = next[0]; 40524181Smckusick prev[1] = next[1]; 40624181Smckusick flock(prev[1], LOCK_EX); 40724181Smckusick } 40826485Smckusick if (i < SLAVES - 1) { 40926485Smckusick lockfile(next); 41026485Smckusick } else { 41126485Smckusick next[0] = first[0]; 41226485Smckusick next[1] = first[1]; /* Last slave loops back */ 41326485Smckusick } 41446585Storek if (pipe(cmd) < 0 || (slavepid[i] = fork()) < 0) 41546585Storek quit("too many slaves, %d (recompile smaller): %s\n", 41646585Storek i, strerror(errno)); 41718012Smckusick slavefd[i] = cmd[1]; 41825219Smckusick if (slavepid[i] == 0) { /* Slave starts up here */ 41918012Smckusick for (j = 0; j <= i; j++) 42018012Smckusick close(slavefd[j]); 42125219Smckusick signal(SIGINT, SIG_IGN); /* Master handles this */ 42225219Smckusick doslave(cmd[0], prev, next); 42318012Smckusick Exit(X_FINOK); 42418012Smckusick } 42518012Smckusick close(cmd[0]); 42624181Smckusick if (i > 0) { 42724181Smckusick close(prev[0]); 42824181Smckusick close(prev[1]); 42924181Smckusick } 43018012Smckusick } 43124181Smckusick close(first[0]); 43224181Smckusick close(first[1]); 43324181Smckusick master = 0; rotor = 0; 43418012Smckusick } 43518012Smckusick 43646585Storek void 43724181Smckusick killall() 43818012Smckusick { 43924181Smckusick register int i; 44019982Smckusick 44124181Smckusick for (i = 0; i < SLAVES; i++) 44224181Smckusick if (slavepid[i] > 0) 44324181Smckusick kill(slavepid[i], SIGKILL); 44418012Smckusick } 44518012Smckusick 44624181Smckusick /* 44724181Smckusick * Synchronization - each process has a lockfile, and shares file 44824181Smckusick * descriptors to the following process's lockfile. When our write 44924181Smckusick * completes, we release our lock on the following process's lock- 45024181Smckusick * file, allowing the following process to lock it and proceed. We 45124181Smckusick * get the lock back for the next cycle by swapping descriptors. 45224181Smckusick */ 45346585Storek void 45425219Smckusick doslave(cmd, prev, next) 45525219Smckusick register int cmd, prev[2], next[2]; 45619982Smckusick { 45725219Smckusick register int nread, toggle = 0; 458*46795Sbostic #ifndef __STDC__ 459*46795Sbostic int read(); 460*46795Sbostic #endif 46119982Smckusick 46246789Smckusick /* 46346789Smckusick * Need our own seek pointer. 46446789Smckusick */ 46546789Smckusick close(diskfd); 46646789Smckusick if ((diskfd = open(disk, O_RDONLY)) < 0) 46746585Storek quit("slave couldn't reopen disk: %s\n", strerror(errno)); 46824181Smckusick /* 46925219Smckusick * Get list of blocks to dump, read the blocks into tape buffer 47024181Smckusick */ 47125219Smckusick while ((nread = atomic(read, cmd, req, reqsiz)) == reqsiz) { 47218012Smckusick register struct req *p = req; 47318012Smckusick for (trecno = 0; trecno < ntrec; trecno += p->count, p += p->count) { 47418012Smckusick if (p->dblk) { 47518012Smckusick bread(p->dblk, tblock[trecno], 47625219Smckusick p->count * TP_BSIZE); 47718012Smckusick } else { 47825219Smckusick if (p->count != 1 || atomic(read, cmd, 47946585Storek tblock[trecno], TP_BSIZE) != TP_BSIZE) 48046585Storek quit("master/slave protocol botched.\n"); 48118012Smckusick } 48218012Smckusick } 48324181Smckusick flock(prev[toggle], LOCK_EX); /* Wait our turn */ 48425219Smckusick 48518012Smckusick #ifdef RDUMP 48625219Smckusick if ((host ? rmtwrite(tblock[0], writesize) 48746789Smckusick : write(tapefd, tblock[0], writesize)) != writesize) { 48825219Smckusick #else RDUMP 48946789Smckusick if (write(tapefd, tblock[0], writesize) != writesize) { 49024181Smckusick #endif RDUMP 49125219Smckusick kill(master, SIGUSR1); 49225219Smckusick for (;;) 49325219Smckusick sigpause(0); 49418012Smckusick } 49524181Smckusick toggle ^= 1; 49624181Smckusick flock(next[toggle], LOCK_UN); /* Next slave's turn */ 49724181Smckusick } /* Also jolts him awake */ 49846585Storek if (nread != 0) 49946585Storek quit("error reading command pipe: %s\n", strerror(errno)); 50018012Smckusick } 50119947Smckusick 50219947Smckusick /* 50325219Smckusick * Since a read from a pipe may not return all we asked for, 50425219Smckusick * or a write may not write all we ask if we get a signal, 50525219Smckusick * loop until the count is satisfied (or error). 50619947Smckusick */ 50746585Storek int 50825219Smckusick atomic(func, fd, buf, count) 50925219Smckusick int (*func)(), fd, count; 51019947Smckusick char *buf; 51119947Smckusick { 51225219Smckusick int got, need = count; 51319947Smckusick 51425219Smckusick while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0) 51519947Smckusick buf += got; 51625219Smckusick return (got < 0 ? got : count - need); 51719947Smckusick } 518