11558Srgrimes/*- 21558Srgrimes * Copyright (c) 1980, 1991, 1993 31558Srgrimes * The Regents of the University of California. All rights reserved. 41558Srgrimes * 51558Srgrimes * Redistribution and use in source and binary forms, with or without 61558Srgrimes * modification, are permitted provided that the following conditions 71558Srgrimes * are met: 81558Srgrimes * 1. Redistributions of source code must retain the above copyright 91558Srgrimes * notice, this list of conditions and the following disclaimer. 101558Srgrimes * 2. Redistributions in binary form must reproduce the above copyright 111558Srgrimes * notice, this list of conditions and the following disclaimer in the 121558Srgrimes * documentation and/or other materials provided with the distribution. 131558Srgrimes * 4. Neither the name of the University nor the names of its contributors 141558Srgrimes * may be used to endorse or promote products derived from this software 151558Srgrimes * without specific prior written permission. 161558Srgrimes * 171558Srgrimes * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 181558Srgrimes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 191558Srgrimes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 201558Srgrimes * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 211558Srgrimes * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 221558Srgrimes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 231558Srgrimes * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 241558Srgrimes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 251558Srgrimes * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 261558Srgrimes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 271558Srgrimes * SUCH DAMAGE. 281558Srgrimes */ 291558Srgrimes 301558Srgrimes#ifndef lint 3136997Scharnier#if 0 3223672Speterstatic char sccsid[] = "@(#)tape.c 8.4 (Berkeley) 5/1/95"; 3336997Scharnier#endif 3436997Scharnierstatic const char rcsid[] = 3550476Speter "$FreeBSD$"; 361558Srgrimes#endif /* not lint */ 371558Srgrimes 381558Srgrimes#include <sys/param.h> 391558Srgrimes#include <sys/socket.h> 401558Srgrimes#include <sys/wait.h> 4190496Siedowse#include <sys/stat.h> 421558Srgrimes 4323672Speter#include <ufs/ufs/dinode.h> 441558Srgrimes#include <ufs/ffs/fs.h> 451558Srgrimes 461558Srgrimes#include <protocols/dumprestore.h> 471558Srgrimes 481558Srgrimes#include <errno.h> 491558Srgrimes#include <fcntl.h> 50103949Smike#include <limits.h> 511558Srgrimes#include <setjmp.h> 521558Srgrimes#include <signal.h> 531558Srgrimes#include <stdio.h> 541558Srgrimes#include <stdlib.h> 551558Srgrimes#include <string.h> 56217769Smckusick#include <time.h> 571558Srgrimes#include <unistd.h> 581558Srgrimes 591558Srgrimes#include "dump.h" 601558Srgrimes 611558Srgrimesint writesize; /* size of malloc()ed buffer for tape */ 6298542Smckusickint64_t lastspclrec = -1; /* tape block number of last written header */ 631558Srgrimesint trecno = 0; /* next record to write in current block */ 641558Srgrimesextern long blocksperfile; /* number of blocks per output file */ 651558Srgrimeslong blocksthisvol; /* number of blocks on current output file */ 661558Srgrimesextern int ntrec; /* blocking factor on tape */ 671558Srgrimesextern int cartridge; 681558Srgrimesextern char *host; 691558Srgrimeschar *nexttape; 70128175SgreenFILE *popenfp = NULL; 711558Srgrimes 7292837Simpstatic int atomic(ssize_t (*)(), int, char *, int); 7392837Simpstatic void doslave(int, int); 7492837Simpstatic void enslave(void); 7592837Simpstatic void flushtape(void); 7692837Simpstatic void killall(void); 7792837Simpstatic void rollforward(void); 781558Srgrimes 791558Srgrimes/* 801558Srgrimes * Concurrent dump mods (Caltech) - disk block reading and tape writing 811558Srgrimes * are exported to several slave processes. While one slave writes the 821558Srgrimes * tape, the others read disk blocks; they pass control of the tape in 83102231Strhodes * a ring via signals. The parent process traverses the file system and 841558Srgrimes * sends writeheader()'s and lists of daddr's to the slaves via pipes. 851558Srgrimes * The following structure defines the instruction packets sent to slaves. 861558Srgrimes */ 871558Srgrimesstruct req { 8898542Smckusick ufs2_daddr_t dblk; 891558Srgrimes int count; 901558Srgrimes}; 911558Srgrimesint reqsiz; 921558Srgrimes 931558Srgrimes#define SLAVES 3 /* 1 slave writing, 1 reading, 1 for slack */ 941558Srgrimesstruct slave { 9598542Smckusick int64_t tapea; /* header number at start of this chunk */ 9698542Smckusick int64_t firstrec; /* record number of this block */ 971558Srgrimes int count; /* count to next header (used for TS_TAPE */ 981558Srgrimes /* after EOT) */ 991558Srgrimes int inode; /* inode that we are currently dealing with */ 1001558Srgrimes int fd; /* FD for this slave */ 1011558Srgrimes int pid; /* PID for this slave */ 1021558Srgrimes int sent; /* 1 == we've sent this slave requests */ 1031558Srgrimes char (*tblock)[TP_BSIZE]; /* buffer for data blocks */ 1041558Srgrimes struct req *req; /* buffer for requests */ 1051558Srgrimes} slaves[SLAVES+1]; 1061558Srgrimesstruct slave *slp; 1071558Srgrimes 1081558Srgrimeschar (*nextblock)[TP_BSIZE]; 1091558Srgrimes 1101558Srgrimesint master; /* pid of master, for sending error signals */ 1111558Srgrimesint tenths; /* length of tape used per block written */ 112142968Siedowsestatic volatile sig_atomic_t caught; /* have we caught the signal to proceed? */ 113142968Siedowsestatic volatile sig_atomic_t ready; /* reached the lock point without having */ 1141558Srgrimes /* received the SIGUSR2 signal from the prev slave? */ 1151558Srgrimesstatic jmp_buf jmpbuf; /* where to jump to if we are ready when the */ 1161558Srgrimes /* SIGUSR2 arrives from the previous slave */ 1171558Srgrimes 1181558Srgrimesint 11992837Simpalloctape(void) 1201558Srgrimes{ 1211558Srgrimes int pgoff = getpagesize() - 1; 1221558Srgrimes char *buf; 1231558Srgrimes int i; 1241558Srgrimes 1251558Srgrimes writesize = ntrec * TP_BSIZE; 1261558Srgrimes reqsiz = (ntrec + 1) * sizeof(struct req); 1271558Srgrimes /* 1281558Srgrimes * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode 1291558Srgrimes * (see DEC TU80 User's Guide). The shorter gaps of 6250-bpi require 1301558Srgrimes * repositioning after stopping, i.e, streaming mode, where the gap is 1311558Srgrimes * variable, 0.30" to 0.45". The gap is maximal when the tape stops. 1321558Srgrimes */ 13322192Sjoerg if (blocksperfile == 0 && !unlimited) 1341558Srgrimes tenths = writesize / density + 1351558Srgrimes (cartridge ? 16 : density == 625 ? 5 : 8); 1361558Srgrimes /* 1371558Srgrimes * Allocate tape buffer contiguous with the array of instruction 1381558Srgrimes * packets, so flushtape() can write them together with one write(). 1391558Srgrimes * Align tape buffer on page boundary to speed up tape write(). 1401558Srgrimes */ 1411558Srgrimes for (i = 0; i <= SLAVES; i++) { 1421558Srgrimes buf = (char *) 1431558Srgrimes malloc((unsigned)(reqsiz + writesize + pgoff + TP_BSIZE)); 1441558Srgrimes if (buf == NULL) 1451558Srgrimes return(0); 1461558Srgrimes slaves[i].tblock = (char (*)[TP_BSIZE]) 1471558Srgrimes (((long)&buf[ntrec + 1] + pgoff) &~ pgoff); 1481558Srgrimes slaves[i].req = (struct req *)slaves[i].tblock - ntrec - 1; 1491558Srgrimes } 1501558Srgrimes slp = &slaves[0]; 1511558Srgrimes slp->count = 1; 1521558Srgrimes slp->tapea = 0; 1531558Srgrimes slp->firstrec = 0; 1541558Srgrimes nextblock = slp->tblock; 1551558Srgrimes return(1); 1561558Srgrimes} 1571558Srgrimes 1581558Srgrimesvoid 15992837Simpwriterec(char *dp, int isspcl) 1601558Srgrimes{ 1611558Srgrimes 16298542Smckusick slp->req[trecno].dblk = (ufs2_daddr_t)0; 1631558Srgrimes slp->req[trecno].count = 1; 16492837Simp /* Can't do a structure assignment due to alignment problems */ 16546360Smjacob bcopy(dp, *(nextblock)++, sizeof (union u_spcl)); 1661558Srgrimes if (isspcl) 1671558Srgrimes lastspclrec = spcl.c_tapea; 1681558Srgrimes trecno++; 1691558Srgrimes spcl.c_tapea++; 1701558Srgrimes if (trecno >= ntrec) 1711558Srgrimes flushtape(); 1721558Srgrimes} 1731558Srgrimes 1741558Srgrimesvoid 17598542Smckusickdumpblock(ufs2_daddr_t blkno, int size) 1761558Srgrimes{ 17798542Smckusick int avail, tpblks; 17898542Smckusick ufs2_daddr_t dblkno; 1791558Srgrimes 1801558Srgrimes dblkno = fsbtodb(sblock, blkno); 1811558Srgrimes tpblks = size >> tp_bshift; 1821558Srgrimes while ((avail = MIN(tpblks, ntrec - trecno)) > 0) { 1831558Srgrimes slp->req[trecno].dblk = dblkno; 1841558Srgrimes slp->req[trecno].count = avail; 1851558Srgrimes trecno += avail; 1861558Srgrimes spcl.c_tapea += avail; 1871558Srgrimes if (trecno >= ntrec) 1881558Srgrimes flushtape(); 1891558Srgrimes dblkno += avail << (tp_bshift - dev_bshift); 1901558Srgrimes tpblks -= avail; 1911558Srgrimes } 1921558Srgrimes} 1931558Srgrimes 1941558Srgrimesint nogripe = 0; 1951558Srgrimes 1961558Srgrimesvoid 19792837Simptperror(int signo __unused) 1981558Srgrimes{ 1991558Srgrimes 2001558Srgrimes if (pipeout) { 2011558Srgrimes msg("write error on %s\n", tape); 2021558Srgrimes quit("Cannot recover\n"); 2031558Srgrimes /* NOTREACHED */ 2041558Srgrimes } 20599530Siedowse msg("write error %ld blocks into volume %d\n", blocksthisvol, tapeno); 2061558Srgrimes broadcast("DUMP WRITE ERROR!\n"); 2071558Srgrimes if (!query("Do you want to restart?")) 2081558Srgrimes dumpabort(0); 2091558Srgrimes msg("Closing this volume. Prepare to restart with new media;\n"); 2101558Srgrimes msg("this dump volume will be rewritten.\n"); 2111558Srgrimes killall(); 2121558Srgrimes nogripe = 1; 2131558Srgrimes close_rewind(); 2141558Srgrimes Exit(X_REWRITE); 2151558Srgrimes} 2161558Srgrimes 2171558Srgrimesvoid 21892837Simpsigpipe(int signo __unused) 2191558Srgrimes{ 2201558Srgrimes 2211558Srgrimes quit("Broken pipe\n"); 2221558Srgrimes} 2231558Srgrimes 2241558Srgrimesstatic void 22592837Simpflushtape(void) 2261558Srgrimes{ 2271558Srgrimes int i, blks, got; 22898542Smckusick int64_t lastfirstrec; 2291558Srgrimes 2301558Srgrimes int siz = (char *)nextblock - (char *)slp->req; 2311558Srgrimes 2321558Srgrimes slp->req[trecno].count = 0; /* Sentinel */ 2331558Srgrimes 2341558Srgrimes if (atomic(write, slp->fd, (char *)slp->req, siz) != siz) 2351558Srgrimes quit("error writing command pipe: %s\n", strerror(errno)); 2361558Srgrimes slp->sent = 1; /* we sent a request, read the response later */ 2371558Srgrimes 2381558Srgrimes lastfirstrec = slp->firstrec; 2391558Srgrimes 2401558Srgrimes if (++slp >= &slaves[SLAVES]) 2411558Srgrimes slp = &slaves[0]; 2421558Srgrimes 2431558Srgrimes /* Read results back from next slave */ 2441558Srgrimes if (slp->sent) { 2451558Srgrimes if (atomic(read, slp->fd, (char *)&got, sizeof got) 2461558Srgrimes != sizeof got) { 2471558Srgrimes perror(" DUMP: error reading command pipe in master"); 2481558Srgrimes dumpabort(0); 2491558Srgrimes } 2501558Srgrimes slp->sent = 0; 2511558Srgrimes 2521558Srgrimes /* Check for end of tape */ 2531558Srgrimes if (got < writesize) { 2541558Srgrimes msg("End of tape detected\n"); 2551558Srgrimes 2561558Srgrimes /* 2571558Srgrimes * Drain the results, don't care what the values were. 2581558Srgrimes * If we read them here then trewind won't... 2591558Srgrimes */ 2601558Srgrimes for (i = 0; i < SLAVES; i++) { 2611558Srgrimes if (slaves[i].sent) { 2621558Srgrimes if (atomic(read, slaves[i].fd, 2631558Srgrimes (char *)&got, sizeof got) 2641558Srgrimes != sizeof got) { 2651558Srgrimes perror(" DUMP: error reading command pipe in master"); 2661558Srgrimes dumpabort(0); 2671558Srgrimes } 2681558Srgrimes slaves[i].sent = 0; 2691558Srgrimes } 2701558Srgrimes } 2711558Srgrimes 2721558Srgrimes close_rewind(); 2731558Srgrimes rollforward(); 2741558Srgrimes return; 2751558Srgrimes } 2761558Srgrimes } 2771558Srgrimes 2781558Srgrimes blks = 0; 2791558Srgrimes if (spcl.c_type != TS_END) { 2801558Srgrimes for (i = 0; i < spcl.c_count; i++) 2811558Srgrimes if (spcl.c_addr[i] != 0) 2821558Srgrimes blks++; 2831558Srgrimes } 2841558Srgrimes slp->count = lastspclrec + blks + 1 - spcl.c_tapea; 2851558Srgrimes slp->tapea = spcl.c_tapea; 2861558Srgrimes slp->firstrec = lastfirstrec + ntrec; 2871558Srgrimes slp->inode = curino; 2881558Srgrimes nextblock = slp->tblock; 2891558Srgrimes trecno = 0; 2901558Srgrimes asize += tenths; 2911558Srgrimes blockswritten += ntrec; 2921558Srgrimes blocksthisvol += ntrec; 29322192Sjoerg if (!pipeout && !unlimited && (blocksperfile ? 2941558Srgrimes (blocksthisvol >= blocksperfile) : (asize > tsize))) { 2951558Srgrimes close_rewind(); 2961558Srgrimes startnewtape(0); 2971558Srgrimes } 2981558Srgrimes timeest(); 2991558Srgrimes} 3001558Srgrimes 3011558Srgrimesvoid 30292837Simptrewind(void) 3031558Srgrimes{ 30490496Siedowse struct stat sb; 3051558Srgrimes int f; 3061558Srgrimes int got; 3071558Srgrimes 3081558Srgrimes for (f = 0; f < SLAVES; f++) { 3091558Srgrimes /* 3108871Srgrimes * Drain the results, but unlike EOT we DO (or should) care 3118871Srgrimes * what the return values were, since if we detect EOT after 3128871Srgrimes * we think we've written the last blocks to the tape anyway, 3131558Srgrimes * we have to replay those blocks with rollforward. 3141558Srgrimes * 3158871Srgrimes * fixme: punt for now. 3161558Srgrimes */ 3171558Srgrimes if (slaves[f].sent) { 3181558Srgrimes if (atomic(read, slaves[f].fd, (char *)&got, sizeof got) 3191558Srgrimes != sizeof got) { 3201558Srgrimes perror(" DUMP: error reading command pipe in master"); 3211558Srgrimes dumpabort(0); 3221558Srgrimes } 3231558Srgrimes slaves[f].sent = 0; 3241558Srgrimes if (got != writesize) { 3251558Srgrimes msg("EOT detected in last 2 tape records!\n"); 3261558Srgrimes msg("Use a longer tape, decrease the size estimate\n"); 3271558Srgrimes quit("or use no size estimate at all.\n"); 3281558Srgrimes } 3291558Srgrimes } 3301558Srgrimes (void) close(slaves[f].fd); 3311558Srgrimes } 3321558Srgrimes while (wait((int *)NULL) >= 0) /* wait for any signals from slaves */ 3331558Srgrimes /* void */; 3341558Srgrimes 3351558Srgrimes if (pipeout) 3361558Srgrimes return; 3371558Srgrimes 3381558Srgrimes msg("Closing %s\n", tape); 3391558Srgrimes 340128175Sgreen if (popenout) { 341128175Sgreen tapefd = -1; 342128175Sgreen (void)pclose(popenfp); 343128175Sgreen popenfp = NULL; 344128175Sgreen return; 345128175Sgreen } 3461558Srgrimes#ifdef RDUMP 3471558Srgrimes if (host) { 3481558Srgrimes rmtclose(); 3491558Srgrimes while (rmtopen(tape, 0) < 0) 3501558Srgrimes sleep(10); 3511558Srgrimes rmtclose(); 3521558Srgrimes return; 3531558Srgrimes } 3541558Srgrimes#endif 35590496Siedowse if (fstat(tapefd, &sb) == 0 && S_ISFIFO(sb.st_mode)) { 35690496Siedowse (void)close(tapefd); 35790496Siedowse return; 35890496Siedowse } 3591558Srgrimes (void) close(tapefd); 3601558Srgrimes while ((f = open(tape, 0)) < 0) 3611558Srgrimes sleep (10); 3621558Srgrimes (void) close(f); 3631558Srgrimes} 3641558Srgrimes 3651558Srgrimesvoid 3661558Srgrimesclose_rewind() 3671558Srgrimes{ 36830341Sjoerg time_t tstart_changevol, tend_changevol; 36930341Sjoerg 3701558Srgrimes trewind(); 3711558Srgrimes if (nexttape) 3721558Srgrimes return; 37330341Sjoerg (void)time((time_t *)&(tstart_changevol)); 3741558Srgrimes if (!nogripe) { 3751558Srgrimes msg("Change Volumes: Mount volume #%d\n", tapeno+1); 37671750Sphk broadcast("CHANGE DUMP VOLUMES!\a\a\n"); 3771558Srgrimes } 3781558Srgrimes while (!query("Is the new volume mounted and ready to go?")) 3791558Srgrimes if (query("Do you want to abort?")) { 3801558Srgrimes dumpabort(0); 3811558Srgrimes /*NOTREACHED*/ 3821558Srgrimes } 38330341Sjoerg (void)time((time_t *)&(tend_changevol)); 38430341Sjoerg if ((tstart_changevol != (time_t)-1) && (tend_changevol != (time_t)-1)) 38530341Sjoerg tstart_writing += (tend_changevol - tstart_changevol); 3861558Srgrimes} 3871558Srgrimes 3881558Srgrimesvoid 38992837Simprollforward(void) 3901558Srgrimes{ 39186473Siedowse struct req *p, *q, *prev; 39286473Siedowse struct slave *tslp; 39398542Smckusick int i, size, got; 39498542Smckusick int64_t savedtapea; 3951558Srgrimes union u_spcl *ntb, *otb; 3961558Srgrimes tslp = &slaves[SLAVES]; 3971558Srgrimes ntb = (union u_spcl *)tslp->tblock[1]; 3981558Srgrimes 3991558Srgrimes /* 4008871Srgrimes * Each of the N slaves should have requests that need to 4018871Srgrimes * be replayed on the next tape. Use the extra slave buffers 4028871Srgrimes * (slaves[SLAVES]) to construct request lists to be sent to 4031558Srgrimes * each slave in turn. 4041558Srgrimes */ 4051558Srgrimes for (i = 0; i < SLAVES; i++) { 4061558Srgrimes q = &tslp->req[1]; 4071558Srgrimes otb = (union u_spcl *)slp->tblock; 4081558Srgrimes 4091558Srgrimes /* 4108871Srgrimes * For each request in the current slave, copy it to tslp. 4111558Srgrimes */ 4121558Srgrimes 4131558Srgrimes prev = NULL; 4141558Srgrimes for (p = slp->req; p->count > 0; p += p->count) { 4151558Srgrimes *q = *p; 4161558Srgrimes if (p->dblk == 0) 4171558Srgrimes *ntb++ = *otb++; /* copy the datablock also */ 4181558Srgrimes prev = q; 4191558Srgrimes q += q->count; 4201558Srgrimes } 4211558Srgrimes if (prev == NULL) 4221558Srgrimes quit("rollforward: protocol botch"); 4231558Srgrimes if (prev->dblk != 0) 4241558Srgrimes prev->count -= 1; 4251558Srgrimes else 4261558Srgrimes ntb--; 4271558Srgrimes q -= 1; 4281558Srgrimes q->count = 0; 4291558Srgrimes q = &tslp->req[0]; 4301558Srgrimes if (i == 0) { 4311558Srgrimes q->dblk = 0; 4321558Srgrimes q->count = 1; 4331558Srgrimes trecno = 0; 4341558Srgrimes nextblock = tslp->tblock; 4351558Srgrimes savedtapea = spcl.c_tapea; 4361558Srgrimes spcl.c_tapea = slp->tapea; 4371558Srgrimes startnewtape(0); 4381558Srgrimes spcl.c_tapea = savedtapea; 4391558Srgrimes lastspclrec = savedtapea - 1; 4401558Srgrimes } 4411558Srgrimes size = (char *)ntb - (char *)q; 4421558Srgrimes if (atomic(write, slp->fd, (char *)q, size) != size) { 4431558Srgrimes perror(" DUMP: error writing command pipe"); 4441558Srgrimes dumpabort(0); 4451558Srgrimes } 4461558Srgrimes slp->sent = 1; 4471558Srgrimes if (++slp >= &slaves[SLAVES]) 4481558Srgrimes slp = &slaves[0]; 4491558Srgrimes 4501558Srgrimes q->count = 1; 4511558Srgrimes 4521558Srgrimes if (prev->dblk != 0) { 4531558Srgrimes /* 4548871Srgrimes * If the last one was a disk block, make the 4558871Srgrimes * first of this one be the last bit of that disk 4561558Srgrimes * block... 4571558Srgrimes */ 4581558Srgrimes q->dblk = prev->dblk + 4591558Srgrimes prev->count * (TP_BSIZE / DEV_BSIZE); 4601558Srgrimes ntb = (union u_spcl *)tslp->tblock; 4611558Srgrimes } else { 4621558Srgrimes /* 4638871Srgrimes * It wasn't a disk block. Copy the data to its 4641558Srgrimes * new location in the buffer. 4651558Srgrimes */ 4661558Srgrimes q->dblk = 0; 4671558Srgrimes *((union u_spcl *)tslp->tblock) = *ntb; 4681558Srgrimes ntb = (union u_spcl *)tslp->tblock[1]; 4691558Srgrimes } 4701558Srgrimes } 4711558Srgrimes slp->req[0] = *q; 4721558Srgrimes nextblock = slp->tblock; 4731558Srgrimes if (q->dblk == 0) 4741558Srgrimes nextblock++; 4751558Srgrimes trecno = 1; 4761558Srgrimes 4771558Srgrimes /* 4781558Srgrimes * Clear the first slaves' response. One hopes that it 4791558Srgrimes * worked ok, otherwise the tape is much too short! 4801558Srgrimes */ 4811558Srgrimes if (slp->sent) { 4821558Srgrimes if (atomic(read, slp->fd, (char *)&got, sizeof got) 4831558Srgrimes != sizeof got) { 4841558Srgrimes perror(" DUMP: error reading command pipe in master"); 4851558Srgrimes dumpabort(0); 4861558Srgrimes } 4871558Srgrimes slp->sent = 0; 4881558Srgrimes 4891558Srgrimes if (got != writesize) { 4901558Srgrimes quit("EOT detected at start of the tape!\n"); 4911558Srgrimes } 4921558Srgrimes } 4931558Srgrimes} 4941558Srgrimes 4951558Srgrimes/* 4961558Srgrimes * We implement taking and restoring checkpoints on the tape level. 4971558Srgrimes * When each tape is opened, a new process is created by forking; this 4981558Srgrimes * saves all of the necessary context in the parent. The child 4991558Srgrimes * continues the dump; the parent waits around, saving the context. 5001558Srgrimes * If the child returns X_REWRITE, then it had problems writing that tape; 5011558Srgrimes * this causes the parent to fork again, duplicating the context, and 5021558Srgrimes * everything continues as if nothing had happened. 5031558Srgrimes */ 5041558Srgrimesvoid 50592837Simpstartnewtape(int top) 5061558Srgrimes{ 5071558Srgrimes int parentpid; 5081558Srgrimes int childpid; 5091558Srgrimes int status; 5101558Srgrimes char *p; 5111558Srgrimes sig_t interrupt_save; 5121558Srgrimes 5131558Srgrimes interrupt_save = signal(SIGINT, SIG_IGN); 5141558Srgrimes parentpid = getpid(); 5151558Srgrimes 5161558Srgrimesrestore_check_point: 5171558Srgrimes (void)signal(SIGINT, interrupt_save); 5181558Srgrimes /* 5191558Srgrimes * All signals are inherited... 5201558Srgrimes */ 52190743Siedowse setproctitle(NULL); /* Restore the proctitle. */ 5221558Srgrimes childpid = fork(); 5231558Srgrimes if (childpid < 0) { 5241558Srgrimes msg("Context save fork fails in parent %d\n", parentpid); 5251558Srgrimes Exit(X_ABORT); 5261558Srgrimes } 5271558Srgrimes if (childpid != 0) { 5281558Srgrimes /* 5291558Srgrimes * PARENT: 5301558Srgrimes * save the context by waiting 5311558Srgrimes * until the child doing all of the work returns. 5321558Srgrimes * don't catch the interrupt 5331558Srgrimes */ 5341558Srgrimes signal(SIGINT, SIG_IGN); 5351558Srgrimes#ifdef TDEBUG 5361558Srgrimes msg("Tape: %d; parent process: %d child process %d\n", 5371558Srgrimes tapeno+1, parentpid, childpid); 5381558Srgrimes#endif /* TDEBUG */ 539128175Sgreen if (waitpid(childpid, &status, 0) == -1) 540128175Sgreen msg("Waiting for child %d: %s\n", childpid, 541128175Sgreen strerror(errno)); 5421558Srgrimes if (status & 0xFF) { 5431558Srgrimes msg("Child %d returns LOB status %o\n", 5441558Srgrimes childpid, status&0xFF); 5451558Srgrimes } 5461558Srgrimes status = (status >> 8) & 0xFF; 5471558Srgrimes#ifdef TDEBUG 5481558Srgrimes switch(status) { 5491558Srgrimes case X_FINOK: 5501558Srgrimes msg("Child %d finishes X_FINOK\n", childpid); 5511558Srgrimes break; 5528871Srgrimes case X_ABORT: 5531558Srgrimes msg("Child %d finishes X_ABORT\n", childpid); 5541558Srgrimes break; 5551558Srgrimes case X_REWRITE: 5561558Srgrimes msg("Child %d finishes X_REWRITE\n", childpid); 5571558Srgrimes break; 5581558Srgrimes default: 5591558Srgrimes msg("Child %d finishes unknown %d\n", 5601558Srgrimes childpid, status); 5611558Srgrimes break; 5621558Srgrimes } 5631558Srgrimes#endif /* TDEBUG */ 5641558Srgrimes switch(status) { 5651558Srgrimes case X_FINOK: 5661558Srgrimes Exit(X_FINOK); 5671558Srgrimes case X_ABORT: 5681558Srgrimes Exit(X_ABORT); 5691558Srgrimes case X_REWRITE: 5701558Srgrimes goto restore_check_point; 5711558Srgrimes default: 5721558Srgrimes msg("Bad return code from dump: %d\n", status); 5731558Srgrimes Exit(X_ABORT); 5741558Srgrimes } 5751558Srgrimes /*NOTREACHED*/ 5761558Srgrimes } else { /* we are the child; just continue */ 5771558Srgrimes#ifdef TDEBUG 5781558Srgrimes sleep(4); /* allow time for parent's message to get out */ 5791558Srgrimes msg("Child on Tape %d has parent %d, my pid = %d\n", 5801558Srgrimes tapeno+1, parentpid, getpid()); 5811558Srgrimes#endif /* TDEBUG */ 5821558Srgrimes /* 5831558Srgrimes * If we have a name like "/dev/rmt0,/dev/rmt1", 5841558Srgrimes * use the name before the comma first, and save 5851558Srgrimes * the remaining names for subsequent volumes. 5861558Srgrimes */ 5871558Srgrimes tapeno++; /* current tape sequence */ 58823672Speter if (nexttape || strchr(tape, ',')) { 5891558Srgrimes if (nexttape && *nexttape) 5901558Srgrimes tape = nexttape; 59123672Speter if ((p = strchr(tape, ',')) != NULL) { 5921558Srgrimes *p = '\0'; 5931558Srgrimes nexttape = p + 1; 5941558Srgrimes } else 5951558Srgrimes nexttape = NULL; 5961558Srgrimes msg("Dumping volume %d on %s\n", tapeno, tape); 5971558Srgrimes } 598128175Sgreen if (pipeout) { 599128175Sgreen tapefd = STDOUT_FILENO; 600128175Sgreen } else if (popenout) { 601128175Sgreen char volno[sizeof("2147483647")]; 602128175Sgreen 603128175Sgreen (void)sprintf(volno, "%d", spcl.c_volume + 1); 604128175Sgreen if (setenv("DUMP_VOLUME", volno, 1) == -1) { 605128175Sgreen msg("Cannot set $DUMP_VOLUME.\n"); 606128175Sgreen dumpabort(0); 607128175Sgreen } 608128175Sgreen popenfp = popen(popenout, "w"); 609128175Sgreen if (popenfp == NULL) { 610128175Sgreen msg("Cannot open output pipeline \"%s\".\n", 611128175Sgreen popenout); 612128175Sgreen dumpabort(0); 613128175Sgreen } 614128175Sgreen tapefd = fileno(popenfp); 615128175Sgreen } else { 6161558Srgrimes#ifdef RDUMP 617128175Sgreen while ((tapefd = (host ? rmtopen(tape, 2) : 618128175Sgreen open(tape, O_WRONLY|O_CREAT, 0666))) < 0) 6191558Srgrimes#else 620128175Sgreen while ((tapefd = 621128175Sgreen open(tape, O_WRONLY|O_CREAT, 0666)) < 0) 6221558Srgrimes#endif 623128175Sgreen { 624128175Sgreen msg("Cannot open output \"%s\".\n", tape); 625128175Sgreen if (!query("Do you want to retry the open?")) 626128175Sgreen dumpabort(0); 627128175Sgreen } 6281558Srgrimes } 6291558Srgrimes 6301558Srgrimes enslave(); /* Share open tape file descriptor with slaves */ 631128175Sgreen if (popenout) 632128175Sgreen close(tapefd); /* Give up our copy of it. */ 63390742Siedowse signal(SIGINFO, infosch); 6341558Srgrimes 6351558Srgrimes asize = 0; 6361558Srgrimes blocksthisvol = 0; 6371558Srgrimes if (top) 6381558Srgrimes newtape++; /* new tape signal */ 6398871Srgrimes spcl.c_count = slp->count; 6401558Srgrimes /* 6411558Srgrimes * measure firstrec in TP_BSIZE units since restore doesn't 6421558Srgrimes * know the correct ntrec value... 6431558Srgrimes */ 6441558Srgrimes spcl.c_firstrec = slp->firstrec; 6451558Srgrimes spcl.c_volume++; 6461558Srgrimes spcl.c_type = TS_TAPE; 6471558Srgrimes writeheader((ino_t)slp->inode); 6481558Srgrimes if (tapeno > 1) 6491558Srgrimes msg("Volume %d begins with blocks from inode %d\n", 6501558Srgrimes tapeno, slp->inode); 6511558Srgrimes } 6521558Srgrimes} 6531558Srgrimes 6541558Srgrimesvoid 65592837Simpdumpabort(int signo __unused) 6561558Srgrimes{ 6571558Srgrimes 6581558Srgrimes if (master != 0 && master != getpid()) 6591558Srgrimes /* Signals master to call dumpabort */ 6601558Srgrimes (void) kill(master, SIGTERM); 6611558Srgrimes else { 6621558Srgrimes killall(); 6631558Srgrimes msg("The ENTIRE dump is aborted.\n"); 6641558Srgrimes } 6651558Srgrimes#ifdef RDUMP 6661558Srgrimes rmtclose(); 6671558Srgrimes#endif 6681558Srgrimes Exit(X_ABORT); 6691558Srgrimes} 6701558Srgrimes 67118286Sbdevoid 6721558SrgrimesExit(status) 6731558Srgrimes int status; 6741558Srgrimes{ 6751558Srgrimes 6761558Srgrimes#ifdef TDEBUG 6771558Srgrimes msg("pid = %d exits with status %d\n", getpid(), status); 6781558Srgrimes#endif /* TDEBUG */ 6791558Srgrimes exit(status); 6801558Srgrimes} 6811558Srgrimes 6821558Srgrimes/* 6831558Srgrimes * proceed - handler for SIGUSR2, used to synchronize IO between the slaves. 6841558Srgrimes */ 6851558Srgrimesvoid 68692837Simpproceed(int signo __unused) 6871558Srgrimes{ 6881558Srgrimes 6891558Srgrimes if (ready) 6901558Srgrimes longjmp(jmpbuf, 1); 6911558Srgrimes caught++; 6921558Srgrimes} 6931558Srgrimes 6941558Srgrimesvoid 69592837Simpenslave(void) 6961558Srgrimes{ 6971558Srgrimes int cmd[2]; 69886473Siedowse int i, j; 6991558Srgrimes 7001558Srgrimes master = getpid(); 7011558Srgrimes 7021558Srgrimes signal(SIGTERM, dumpabort); /* Slave sends SIGTERM on dumpabort() */ 7031558Srgrimes signal(SIGPIPE, sigpipe); 7041558Srgrimes signal(SIGUSR1, tperror); /* Slave sends SIGUSR1 on tape errors */ 7051558Srgrimes signal(SIGUSR2, proceed); /* Slave sends SIGUSR2 to next slave */ 7061558Srgrimes 7071558Srgrimes for (i = 0; i < SLAVES; i++) { 7081558Srgrimes if (i == slp - &slaves[0]) { 7091558Srgrimes caught = 1; 7101558Srgrimes } else { 7111558Srgrimes caught = 0; 7121558Srgrimes } 7131558Srgrimes 7141558Srgrimes if (socketpair(AF_UNIX, SOCK_STREAM, 0, cmd) < 0 || 7151558Srgrimes (slaves[i].pid = fork()) < 0) 7161558Srgrimes quit("too many slaves, %d (recompile smaller): %s\n", 7171558Srgrimes i, strerror(errno)); 7181558Srgrimes 7191558Srgrimes slaves[i].fd = cmd[1]; 7201558Srgrimes slaves[i].sent = 0; 7211558Srgrimes if (slaves[i].pid == 0) { /* Slave starts up here */ 7221558Srgrimes for (j = 0; j <= i; j++) 7231558Srgrimes (void) close(slaves[j].fd); 7241558Srgrimes signal(SIGINT, SIG_IGN); /* Master handles this */ 7251558Srgrimes doslave(cmd[0], i); 7261558Srgrimes Exit(X_FINOK); 7271558Srgrimes } 7281558Srgrimes } 7298871Srgrimes 7301558Srgrimes for (i = 0; i < SLAVES; i++) 7318871Srgrimes (void) atomic(write, slaves[i].fd, 7328871Srgrimes (char *) &slaves[(i + 1) % SLAVES].pid, 7331558Srgrimes sizeof slaves[0].pid); 7348871Srgrimes 7358871Srgrimes master = 0; 7361558Srgrimes} 7371558Srgrimes 7381558Srgrimesvoid 73992837Simpkillall(void) 7401558Srgrimes{ 74186473Siedowse int i; 7421558Srgrimes 7431558Srgrimes for (i = 0; i < SLAVES; i++) 74425514Sjoerg if (slaves[i].pid > 0) { 7451558Srgrimes (void) kill(slaves[i].pid, SIGKILL); 74625514Sjoerg slaves[i].sent = 0; 74725514Sjoerg } 7481558Srgrimes} 7491558Srgrimes 7501558Srgrimes/* 7511558Srgrimes * Synchronization - each process has a lockfile, and shares file 7521558Srgrimes * descriptors to the following process's lockfile. When our write 7531558Srgrimes * completes, we release our lock on the following process's lock- 7541558Srgrimes * file, allowing the following process to lock it and proceed. We 7551558Srgrimes * get the lock back for the next cycle by swapping descriptors. 7561558Srgrimes */ 7571558Srgrimesstatic void 75892837Simpdoslave(int cmd, int slave_number) 7591558Srgrimes{ 76086473Siedowse int nread; 7611558Srgrimes int nextslave, size, wrote, eot_count; 7621558Srgrimes 7631558Srgrimes /* 7641558Srgrimes * Need our own seek pointer. 7651558Srgrimes */ 7661558Srgrimes (void) close(diskfd); 7671558Srgrimes if ((diskfd = open(disk, O_RDONLY)) < 0) 7681558Srgrimes quit("slave couldn't reopen disk: %s\n", strerror(errno)); 7691558Srgrimes 7701558Srgrimes /* 7711558Srgrimes * Need the pid of the next slave in the loop... 7721558Srgrimes */ 7731558Srgrimes if ((nread = atomic(read, cmd, (char *)&nextslave, sizeof nextslave)) 7741558Srgrimes != sizeof nextslave) { 7751558Srgrimes quit("master/slave protocol botched - didn't get pid of next slave.\n"); 7761558Srgrimes } 7771558Srgrimes 7781558Srgrimes /* 7791558Srgrimes * Get list of blocks to dump, read the blocks into tape buffer 7801558Srgrimes */ 7811558Srgrimes while ((nread = atomic(read, cmd, (char *)slp->req, reqsiz)) == reqsiz) { 78286473Siedowse struct req *p = slp->req; 7831558Srgrimes 7841558Srgrimes for (trecno = 0; trecno < ntrec; 7851558Srgrimes trecno += p->count, p += p->count) { 7861558Srgrimes if (p->dblk) { 7871558Srgrimes bread(p->dblk, slp->tblock[trecno], 7881558Srgrimes p->count * TP_BSIZE); 7891558Srgrimes } else { 7901558Srgrimes if (p->count != 1 || atomic(read, cmd, 7918871Srgrimes (char *)slp->tblock[trecno], 7921558Srgrimes TP_BSIZE) != TP_BSIZE) 7931558Srgrimes quit("master/slave protocol botched.\n"); 7941558Srgrimes } 7951558Srgrimes } 7961558Srgrimes if (setjmp(jmpbuf) == 0) { 7971558Srgrimes ready = 1; 7981558Srgrimes if (!caught) 7991558Srgrimes (void) pause(); 8001558Srgrimes } 8011558Srgrimes ready = 0; 8021558Srgrimes caught = 0; 8031558Srgrimes 8041558Srgrimes /* Try to write the data... */ 8051558Srgrimes eot_count = 0; 8061558Srgrimes size = 0; 8071558Srgrimes 80899562Siedowse wrote = 0; 8091558Srgrimes while (eot_count < 10 && size < writesize) { 8101558Srgrimes#ifdef RDUMP 8111558Srgrimes if (host) 8121558Srgrimes wrote = rmtwrite(slp->tblock[0]+size, 8131558Srgrimes writesize-size); 8141558Srgrimes else 8151558Srgrimes#endif 8161558Srgrimes wrote = write(tapefd, slp->tblock[0]+size, 8171558Srgrimes writesize-size); 8181558Srgrimes#ifdef WRITEDEBUG 8191558Srgrimes printf("slave %d wrote %d\n", slave_number, wrote); 8201558Srgrimes#endif 8218871Srgrimes if (wrote < 0) 8221558Srgrimes break; 8231558Srgrimes if (wrote == 0) 8241558Srgrimes eot_count++; 8251558Srgrimes size += wrote; 8261558Srgrimes } 8271558Srgrimes 8281558Srgrimes#ifdef WRITEDEBUG 8298871Srgrimes if (size != writesize) 8301558Srgrimes printf("slave %d only wrote %d out of %d bytes and gave up.\n", 8311558Srgrimes slave_number, size, writesize); 8321558Srgrimes#endif 8331558Srgrimes 83439256Sgibbs /* 83539256Sgibbs * Handle ENOSPC as an EOT condition. 83639256Sgibbs */ 83739256Sgibbs if (wrote < 0 && errno == ENOSPC) { 83839256Sgibbs wrote = 0; 83939256Sgibbs eot_count++; 84039256Sgibbs } 84139256Sgibbs 8421558Srgrimes if (eot_count > 0) 8431558Srgrimes size = 0; 8441558Srgrimes 84525514Sjoerg if (wrote < 0) { 8461558Srgrimes (void) kill(master, SIGUSR1); 8471558Srgrimes for (;;) 8481558Srgrimes (void) sigpause(0); 8491558Srgrimes } else { 8501558Srgrimes /* 8511558Srgrimes * pass size of write back to master 8521558Srgrimes * (for EOT handling) 8531558Srgrimes */ 8541558Srgrimes (void) atomic(write, cmd, (char *)&size, sizeof size); 8558871Srgrimes } 8561558Srgrimes 8571558Srgrimes /* 8581558Srgrimes * If partial write, don't want next slave to go. 8591558Srgrimes * Also jolts him awake. 8601558Srgrimes */ 8611558Srgrimes (void) kill(nextslave, SIGUSR2); 8621558Srgrimes } 8631558Srgrimes if (nread != 0) 8641558Srgrimes quit("error reading command pipe: %s\n", strerror(errno)); 8651558Srgrimes} 8661558Srgrimes 8671558Srgrimes/* 8681558Srgrimes * Since a read from a pipe may not return all we asked for, 8691558Srgrimes * or a write may not write all we ask if we get a signal, 8701558Srgrimes * loop until the count is satisfied (or error). 8711558Srgrimes */ 8721558Srgrimesstatic int 87392837Simpatomic(ssize_t (*func)(), int fd, char *buf, int count) 8741558Srgrimes{ 8751558Srgrimes int got, need = count; 8761558Srgrimes 8771558Srgrimes while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0) 8781558Srgrimes buf += got; 8791558Srgrimes return (got < 0 ? got : count - need); 8801558Srgrimes} 881