147082Smckusick /*- 247082Smckusick * Copyright (c) 1980, 1991 The Regents of the University of California. 347082Smckusick * All rights reserved. 447082Smckusick * 547082Smckusick * %sccs.include.redist.c% 622040Sdist */ 722040Sdist 817527Ssam #ifndef lint 9*55288Sbostic static char sccsid[] = "@(#)tape.c 5.25 (Berkeley) 07/16/92"; 1046585Storek #endif /* not lint */ 1117527Ssam 1250505Smckusick #ifdef sunos 1350574Smckusick #include <sys/param.h> 1450505Smckusick #include <stdio.h> 1550505Smckusick #include <ctype.h> 1650505Smckusick #include <sys/stat.h> 1751605Sbostic #include <ufs/fs.h> 1850505Smckusick #else 1946795Sbostic #include <sys/param.h> 2046585Storek #include <sys/wait.h> 2151605Sbostic #include <ufs/ffs/fs.h> 2250505Smckusick #endif 2354047Smckusick #include <sys/time.h> 2454047Smckusick #include <ufs/ufs/dinode.h> 2546795Sbostic #include <signal.h> 2646795Sbostic #include <fcntl.h> 2746795Sbostic #include <protocols/dumprestore.h> 2846585Storek #include <errno.h> 2950504Smckusick #include <setjmp.h> 3046795Sbostic #ifdef __STDC__ 3146795Sbostic #include <unistd.h> 3246795Sbostic #include <stdlib.h> 3346795Sbostic #include <string.h> 3446795Sbostic #endif 3550504Smckusick #include <sys/socket.h> 3646795Sbostic #include "dump.h" 3739128Smckusick #include "pathnames.h" 381425Sroot 3929899Smckusick int writesize; /* size of malloc()ed buffer for tape */ 4029899Smckusick long lastspclrec = -1; /* tape block number of last written header */ 4129899Smckusick int trecno = 0; /* next record to write in current block */ 4246614Smckusick extern long blocksperfile; /* number of blocks per output file */ 4348621Skarels long blocksthisvol; /* number of blocks on current output file */ 4448621Skarels extern int ntrec; /* blocking factor on tape */ 4548621Skarels extern int cartridge; 4650504Smckusick extern char *host; 4747056Skarels char *nexttape; 4825219Smckusick #ifdef RDUMP 4946585Storek int rmtopen(), rmtwrite(); 5046585Storek void rmtclose(); 5125219Smckusick #endif RDUMP 5250504Smckusick void rollforward(); 5346585Storek int atomic(); 5446789Smckusick void doslave(), enslave(), flushtape(), killall(); 5546585Storek 5610911Ssam /* 5724181Smckusick * Concurrent dump mods (Caltech) - disk block reading and tape writing 5818012Smckusick * are exported to several slave processes. While one slave writes the 5918012Smckusick * tape, the others read disk blocks; they pass control of the tape in 6050504Smckusick * a ring via signals. The parent process traverses the filesystem and 6146789Smckusick * sends writeheader()'s and lists of daddr's to the slaves via pipes. 6250504Smckusick * The following structure defines the instruction packets sent to slaves. 6310911Ssam */ 6450504Smckusick struct req { 6518012Smckusick daddr_t dblk; 6618012Smckusick int count; 6750504Smckusick }; 6818012Smckusick int reqsiz; 6918012Smckusick 7024181Smckusick #define SLAVES 3 /* 1 slave writing, 1 reading, 1 for slack */ 7150504Smckusick struct slave { 7250504Smckusick int tapea; /* header number at start of this chunk */ 7350504Smckusick int count; /* count to next header (used for TS_TAPE */ 7450504Smckusick /* after EOT) */ 7550504Smckusick int inode; /* inode that we are currently dealing with */ 7650504Smckusick int fd; /* FD for this slave */ 7750504Smckusick int pid; /* PID for this slave */ 7850504Smckusick int sent; /* 1 == we've sent this slave requests */ 7950504Smckusick int firstrec; /* record number of this block */ 8050504Smckusick char (*tblock)[TP_BSIZE]; /* buffer for data blocks */ 8150504Smckusick struct req *req; /* buffer for requests */ 8250504Smckusick } slaves[SLAVES+1]; 8350504Smckusick struct slave *slp; 8418012Smckusick 8550504Smckusick char (*nextblock)[TP_BSIZE]; 8650504Smckusick 8750504Smckusick int master; /* pid of master, for sending error signals */ 8850504Smckusick int tenths; /* length of tape used per block written */ 8950504Smckusick static int caught; /* have we caught the signal to proceed? */ 9050504Smckusick static int ready; /* have we reached the lock point without having */ 9150504Smckusick /* received the SIGUSR2 signal from the prev slave? */ 9250504Smckusick static jmp_buf jmpbuf; /* where to jump to if we are ready when the */ 9350504Smckusick /* SIGUSR2 arrives from the previous slave */ 9450504Smckusick 9546585Storek int 9610911Ssam alloctape() 9710911Ssam { 9825219Smckusick int pgoff = getpagesize() - 1; 9950504Smckusick char *buf; 10050504Smckusick int i; 10110911Ssam 10210911Ssam writesize = ntrec * TP_BSIZE; 10350504Smckusick reqsiz = (ntrec + 1) * sizeof(struct req); 10424181Smckusick /* 10525219Smckusick * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode 10625219Smckusick * (see DEC TU80 User's Guide). The shorter gaps of 6250-bpi require 10725219Smckusick * repositioning after stopping, i.e, streaming mode, where the gap is 10825219Smckusick * variable, 0.30" to 0.45". The gap is maximal when the tape stops. 10924181Smckusick */ 11047056Skarels if (blocksperfile == 0) 11147056Skarels tenths = writesize / density + 11247056Skarels (cartridge ? 16 : density == 625 ? 5 : 8); 11325219Smckusick /* 11425219Smckusick * Allocate tape buffer contiguous with the array of instruction 11546789Smckusick * packets, so flushtape() can write them together with one write(). 11625219Smckusick * Align tape buffer on page boundary to speed up tape write(). 11725219Smckusick */ 11850504Smckusick for (i = 0; i <= SLAVES; i++) { 11954047Smckusick buf = (char *) 12054047Smckusick malloc((unsigned)(reqsiz + writesize + pgoff + TP_BSIZE)); 12150504Smckusick if (buf == NULL) 12254047Smckusick return(0); 12350504Smckusick slaves[i].tblock = (char (*)[TP_BSIZE]) 12450504Smckusick (((long)&buf[ntrec + 1] + pgoff) &~ pgoff); 12550504Smckusick slaves[i].req = (struct req *)slaves[i].tblock - ntrec - 1; 12650504Smckusick } 12750504Smckusick slp = &slaves[0]; 12850504Smckusick slp->count = 1; 12950504Smckusick slp->tapea = 0; 13050504Smckusick slp->firstrec = 0; 13150504Smckusick nextblock = slp->tblock; 13224181Smckusick return(1); 13310911Ssam } 13410911Ssam 13546585Storek void 13654595Smckusick writerec(dp, isspcl) 1375329Smckusic char *dp; 13854595Smckusick int isspcl; 1391425Sroot { 14050504Smckusick 14150504Smckusick slp->req[trecno].dblk = (daddr_t)0; 14250504Smckusick slp->req[trecno].count = 1; 14350504Smckusick *(union u_spcl *)(*(nextblock)++) = *(union u_spcl *)dp; 14454595Smckusick if (isspcl) 14554595Smckusick lastspclrec = spcl.c_tapea; 14624181Smckusick trecno++; 1471425Sroot spcl.c_tapea++; 14846585Storek if (trecno >= ntrec) 14946789Smckusick flushtape(); 1501425Sroot } 1511425Sroot 15246585Storek void 15346789Smckusick dumpblock(blkno, size) 1544774Smckusic daddr_t blkno; 1554774Smckusic int size; 1561425Sroot { 15725219Smckusick int avail, tpblks, dblkno; 1581425Sroot 1595329Smckusic dblkno = fsbtodb(sblock, blkno); 16046585Storek tpblks = size >> tp_bshift; 16118012Smckusick while ((avail = MIN(tpblks, ntrec - trecno)) > 0) { 16250504Smckusick slp->req[trecno].dblk = dblkno; 16350504Smckusick slp->req[trecno].count = avail; 16425219Smckusick trecno += avail; 1654774Smckusic spcl.c_tapea += avail; 16625219Smckusick if (trecno >= ntrec) 16746789Smckusick flushtape(); 16846585Storek dblkno += avail << (tp_bshift - dev_bshift); 1695329Smckusic tpblks -= avail; 1704774Smckusic } 1711425Sroot } 1721425Sroot 1731425Sroot int nogripe = 0; 1741425Sroot 17546585Storek void 176*55288Sbostic tperror(signo) 177*55288Sbostic int signo; 17846585Storek { 17950504Smckusick 18018012Smckusick if (pipeout) { 18146614Smckusick msg("write error on %s\n", tape); 18246585Storek quit("Cannot recover\n"); 18318012Smckusick /* NOTREACHED */ 18418012Smckusick } 18548621Skarels msg("write error %d blocks into volume %d\n", blocksthisvol, tapeno); 18646614Smckusick broadcast("DUMP WRITE ERROR!\n"); 18718012Smckusick if (!query("Do you want to restart?")) 188*55288Sbostic dumpabort(0); 18946614Smckusick msg("Closing this volume. Prepare to restart with new media;\n"); 19018012Smckusick msg("this dump volume will be rewritten.\n"); 19124181Smckusick killall(); 19218012Smckusick nogripe = 1; 19318012Smckusick close_rewind(); 19418012Smckusick Exit(X_REWRITE); 19518012Smckusick } 19618012Smckusick 19746585Storek void 198*55288Sbostic sigpipe(signo) 199*55288Sbostic int signo; 20025219Smckusick { 20125219Smckusick 20246585Storek quit("Broken pipe\n"); 20325219Smckusick } 20425219Smckusick 20546585Storek void 20646789Smckusick flushtape() 20718012Smckusick { 20850504Smckusick int i, blks, got; 20950504Smckusick long lastfirstrec; 21046795Sbostic #ifndef __STDC__ 21150504Smckusick int write(), read(); 21246795Sbostic #endif 21346795Sbostic 21450504Smckusick int siz = (char *)nextblock - (char *)slp->req; 2151425Sroot 21650504Smckusick slp->req[trecno].count = 0; /* Sentinel */ 21750504Smckusick 21854047Smckusick if (atomic(write, slp->fd, (char *)slp->req, siz) != siz) 21946585Storek quit("error writing command pipe: %s\n", strerror(errno)); 22050504Smckusick slp->sent = 1; /* we sent a request, read the response later */ 22150504Smckusick 22250504Smckusick lastfirstrec = slp->firstrec; 22350504Smckusick 22450504Smckusick if (++slp >= &slaves[SLAVES]) 22550504Smckusick slp = &slaves[0]; 22650504Smckusick 22750504Smckusick /* Read results back from next slave */ 22850504Smckusick if (slp->sent) { 22954047Smckusick if (atomic(read, slp->fd, (char *)&got, sizeof got) 23054047Smckusick != sizeof got) { 23150504Smckusick perror(" DUMP: error reading command pipe in master"); 232*55288Sbostic dumpabort(0); 23350504Smckusick } 23450504Smckusick slp->sent = 0; 23550504Smckusick 23650504Smckusick /* Check for end of tape */ 23750504Smckusick if (got < writesize) { 23850504Smckusick msg("End of tape detected\n"); 23950504Smckusick 24050504Smckusick /* 24150504Smckusick * Drain the results, don't care what the values were. 24250504Smckusick * If we read them here then trewind won't... 24350504Smckusick */ 24450504Smckusick for (i = 0; i < SLAVES; i++) { 24550504Smckusick if (slaves[i].sent) { 24654047Smckusick if (atomic(read, slaves[i].fd, 24754047Smckusick (char *)&got, sizeof got) 24854047Smckusick != sizeof got) { 24950504Smckusick perror(" DUMP: error reading command pipe in master"); 250*55288Sbostic dumpabort(0); 25150504Smckusick } 25250504Smckusick slaves[i].sent = 0; 25350504Smckusick } 25450504Smckusick } 25550504Smckusick 25650504Smckusick close_rewind(); 25750504Smckusick rollforward(); 25850504Smckusick return; 25950504Smckusick } 26050504Smckusick } 26150504Smckusick 26250504Smckusick blks = 0; 26350504Smckusick if (spcl.c_type != TS_END) { 26450504Smckusick for (i = 0; i < spcl.c_count; i++) 26550504Smckusick if (spcl.c_addr[i] != 0) 26650504Smckusick blks++; 26750504Smckusick } 26850504Smckusick slp->count = lastspclrec + blks + 1 - spcl.c_tapea; 26950504Smckusick slp->tapea = spcl.c_tapea; 27050504Smckusick slp->firstrec = lastfirstrec + ntrec; 27150504Smckusick slp->inode = curino; 27250504Smckusick nextblock = slp->tblock; 2731425Sroot trecno = 0; 27424181Smckusick asize += tenths; 27510911Ssam blockswritten += ntrec; 27648621Skarels blocksthisvol += ntrec; 27746614Smckusick if (!pipeout && (blocksperfile ? 27848621Skarels (blocksthisvol >= blocksperfile) : (asize > tsize))) { 2791425Sroot close_rewind(); 28050504Smckusick startnewtape(0); 2811425Sroot } 2821425Sroot timeest(); 2831425Sroot } 2841425Sroot 28546585Storek void 28646239Storek trewind() 2871425Sroot { 28824181Smckusick int f; 28950504Smckusick int got; 29012331Smckusick 29150504Smckusick for (f = 0; f < SLAVES; f++) { 29250504Smckusick /* 29350504Smckusick * Drain the results, but unlike EOT we DO (or should) care 29450504Smckusick * what the return values were, since if we detect EOT after 29550504Smckusick * we think we've written the last blocks to the tape anyway, 29650504Smckusick * we have to replay those blocks with rollforward. 29750504Smckusick * 29850504Smckusick * fixme: punt for now. 29950504Smckusick */ 30050504Smckusick if (slaves[f].sent) { 30154047Smckusick if (atomic(read, slaves[f].fd, (char *)&got, sizeof got) 30250504Smckusick != sizeof got) { 30350504Smckusick perror(" DUMP: error reading command pipe in master"); 304*55288Sbostic dumpabort(0); 30550504Smckusick } 30650504Smckusick slaves[f].sent = 0; 30750504Smckusick if (got != writesize) { 30850504Smckusick msg("EOT detected in last 2 tape records!\n"); 30950504Smckusick msg("Use a longer tape, decrease the size estimate\n"); 31050504Smckusick quit("or use no size estimate at all.\n"); 31150504Smckusick } 31250504Smckusick } 31354047Smckusick (void) close(slaves[f].fd); 31450504Smckusick } 31546585Storek while (wait((int *)NULL) >= 0) /* wait for any signals from slaves */ 31646585Storek /* void */; 31754047Smckusick 31854047Smckusick if (pipeout) 31954047Smckusick return; 32054047Smckusick 32148621Skarels msg("Closing %s\n", tape); 32250504Smckusick 32318012Smckusick #ifdef RDUMP 32425219Smckusick if (host) { 32525219Smckusick rmtclose(); 32625219Smckusick while (rmtopen(tape, 0) < 0) 32725219Smckusick sleep(10); 32825219Smckusick rmtclose(); 32925219Smckusick return; 33025219Smckusick } 33150504Smckusick #endif 33254047Smckusick (void) close(tapefd); 3333214Swnj while ((f = open(tape, 0)) < 0) 3343214Swnj sleep (10); 33554047Smckusick (void) close(f); 3361425Sroot } 3371425Sroot 33846585Storek void 3391425Sroot close_rewind() 3401425Sroot { 34146239Storek trewind(); 34248621Skarels if (nexttape) 34348621Skarels return; 34418012Smckusick if (!nogripe) { 34546614Smckusick msg("Change Volumes: Mount volume #%d\n", tapeno+1); 34646614Smckusick broadcast("CHANGE DUMP VOLUMES!\7\7\n"); 3471425Sroot } 34848621Skarels while (!query("Is the new volume mounted and ready to go?")) 34925219Smckusick if (query("Do you want to abort?")) { 350*55288Sbostic dumpabort(0); 35125219Smckusick /*NOTREACHED*/ 35225219Smckusick } 3531425Sroot } 3541425Sroot 35550504Smckusick void 35650504Smckusick rollforward() 35750504Smckusick { 35850504Smckusick register struct req *p, *q, *prev; 35950504Smckusick register struct slave *tslp; 36054047Smckusick int i, size, savedtapea, got; 36150504Smckusick union u_spcl *ntb, *otb; 36250504Smckusick tslp = &slaves[SLAVES]; 36350504Smckusick ntb = (union u_spcl *)tslp->tblock[1]; 36450504Smckusick 36550504Smckusick /* 36650504Smckusick * Each of the N slaves should have requests that need to 36750504Smckusick * be replayed on the next tape. Use the extra slave buffers 36850504Smckusick * (slaves[SLAVES]) to construct request lists to be sent to 36950504Smckusick * each slave in turn. 37050504Smckusick */ 37150504Smckusick for (i = 0; i < SLAVES; i++) { 37250504Smckusick q = &tslp->req[1]; 37350504Smckusick otb = (union u_spcl *)slp->tblock; 37450504Smckusick 37550504Smckusick /* 37650504Smckusick * For each request in the current slave, copy it to tslp. 37750504Smckusick */ 37850504Smckusick 37950504Smckusick for (p = slp->req; p->count > 0; p += p->count) { 38050504Smckusick *q = *p; 38150504Smckusick if (p->dblk == 0) 38250504Smckusick *ntb++ = *otb++; /* copy the datablock also */ 38350504Smckusick prev = q; 38450504Smckusick q += q->count; 38550504Smckusick } 38650504Smckusick if (prev->dblk != 0) 38750504Smckusick prev->count -= 1; 38850504Smckusick else 38950504Smckusick ntb--; 39050504Smckusick q -= 1; 39150504Smckusick q->count = 0; 39250504Smckusick q = &tslp->req[0]; 39350504Smckusick if (i == 0) { 39450504Smckusick q->dblk = 0; 39550504Smckusick q->count = 1; 39650504Smckusick trecno = 0; 39750504Smckusick nextblock = tslp->tblock; 39850504Smckusick savedtapea = spcl.c_tapea; 39950504Smckusick spcl.c_tapea = slp->tapea; 40050504Smckusick startnewtape(0); 40150504Smckusick spcl.c_tapea = savedtapea; 40250504Smckusick lastspclrec = savedtapea - 1; 40350504Smckusick } 40450504Smckusick size = (char *)ntb - (char *)q; 40554047Smckusick if (atomic(write, slp->fd, (char *)q, size) != size) { 40650504Smckusick perror(" DUMP: error writing command pipe"); 407*55288Sbostic dumpabort(0); 40850504Smckusick } 40950504Smckusick slp->sent = 1; 41050504Smckusick if (++slp >= &slaves[SLAVES]) 41150504Smckusick slp = &slaves[0]; 41250504Smckusick 41350504Smckusick q->count = 1; 41450504Smckusick 41550504Smckusick if (prev->dblk != 0) { 41650504Smckusick /* 41750504Smckusick * If the last one was a disk block, make the 41850504Smckusick * first of this one be the last bit of that disk 41950504Smckusick * block... 42050504Smckusick */ 42150504Smckusick q->dblk = prev->dblk + 42250504Smckusick prev->count * (TP_BSIZE / DEV_BSIZE); 42350504Smckusick ntb = (union u_spcl *)tslp->tblock; 42450504Smckusick } else { 42550504Smckusick /* 42650504Smckusick * It wasn't a disk block. Copy the data to its 42750504Smckusick * new location in the buffer. 42850504Smckusick */ 42950504Smckusick q->dblk = 0; 43050504Smckusick *((union u_spcl *)tslp->tblock) = *ntb; 43150504Smckusick ntb = (union u_spcl *)tslp->tblock[1]; 43250504Smckusick } 43350504Smckusick } 43450504Smckusick slp->req[0] = *q; 43550504Smckusick nextblock = slp->tblock; 43650504Smckusick if (q->dblk == 0) 43750504Smckusick nextblock++; 43850504Smckusick trecno = 1; 43950504Smckusick 44050504Smckusick /* 44150504Smckusick * Clear the first slaves' response. One hopes that it 44250504Smckusick * worked ok, otherwise the tape is much too short! 44350504Smckusick */ 44450504Smckusick if (slp->sent) { 44554047Smckusick if (atomic(read, slp->fd, (char *)&got, sizeof got) 44654047Smckusick != sizeof got) { 44750504Smckusick perror(" DUMP: error reading command pipe in master"); 448*55288Sbostic dumpabort(0); 44950504Smckusick } 45050504Smckusick slp->sent = 0; 45150504Smckusick 45250504Smckusick if (got != writesize) { 45350504Smckusick quit("EOT detected at start of the tape!\n"); 45450504Smckusick } 45550504Smckusick } 45650504Smckusick } 45750504Smckusick 4581425Sroot /* 45950504Smckusick * We implement taking and restoring checkpoints on the tape level. 46050504Smckusick * When each tape is opened, a new process is created by forking; this 46150504Smckusick * saves all of the necessary context in the parent. The child 46250504Smckusick * continues the dump; the parent waits around, saving the context. 46350504Smckusick * If the child returns X_REWRITE, then it had problems writing that tape; 46450504Smckusick * this causes the parent to fork again, duplicating the context, and 46550504Smckusick * everything continues as if nothing had happened. 4661425Sroot */ 46746585Storek void 46850504Smckusick startnewtape(top) 46950504Smckusick int top; 4701425Sroot { 4711425Sroot int parentpid; 4721425Sroot int childpid; 4731425Sroot int status; 4741425Sroot int waitpid; 47547056Skarels char *p; 47650574Smckusick #ifdef sunos 47754047Smckusick void (*interrupt_save)(); 47850574Smckusick char *index(); 47950574Smckusick #else 48054047Smckusick sig_t interrupt_save; 48150574Smckusick #endif 4821425Sroot 48354047Smckusick interrupt_save = signal(SIGINT, SIG_IGN); 4841425Sroot parentpid = getpid(); 4851425Sroot 4861425Sroot restore_check_point: 48754047Smckusick (void)signal(SIGINT, interrupt_save); 48825219Smckusick /* 48925219Smckusick * All signals are inherited... 49025219Smckusick */ 4911425Sroot childpid = fork(); 49218012Smckusick if (childpid < 0) { 4931425Sroot msg("Context save fork fails in parent %d\n", parentpid); 4941425Sroot Exit(X_ABORT); 4951425Sroot } 49618012Smckusick if (childpid != 0) { 4971425Sroot /* 4981425Sroot * PARENT: 4991425Sroot * save the context by waiting 5001425Sroot * until the child doing all of the work returns. 50118012Smckusick * don't catch the interrupt 5021425Sroot */ 50325219Smckusick signal(SIGINT, SIG_IGN); 5041425Sroot #ifdef TDEBUG 5051425Sroot msg("Tape: %d; parent process: %d child process %d\n", 5061425Sroot tapeno+1, parentpid, childpid); 5071425Sroot #endif TDEBUG 50818012Smckusick while ((waitpid = wait(&status)) != childpid) 50918012Smckusick msg("Parent %d waiting for child %d has another child %d return\n", 51018012Smckusick parentpid, childpid, waitpid); 51118012Smckusick if (status & 0xFF) { 5121425Sroot msg("Child %d returns LOB status %o\n", 5131425Sroot childpid, status&0xFF); 5141425Sroot } 5151425Sroot status = (status >> 8) & 0xFF; 5161425Sroot #ifdef TDEBUG 51718012Smckusick switch(status) { 5181425Sroot case X_FINOK: 5191425Sroot msg("Child %d finishes X_FINOK\n", childpid); 5201425Sroot break; 52150504Smckusick case X_ABORT: 5221425Sroot msg("Child %d finishes X_ABORT\n", childpid); 5231425Sroot break; 5241425Sroot case X_REWRITE: 5251425Sroot msg("Child %d finishes X_REWRITE\n", childpid); 5261425Sroot break; 5271425Sroot default: 52818012Smckusick msg("Child %d finishes unknown %d\n", 52925219Smckusick childpid, status); 5301425Sroot break; 5311425Sroot } 5321425Sroot #endif TDEBUG 53318012Smckusick switch(status) { 5341425Sroot case X_FINOK: 5351425Sroot Exit(X_FINOK); 5361425Sroot case X_ABORT: 5371425Sroot Exit(X_ABORT); 5381425Sroot case X_REWRITE: 5391425Sroot goto restore_check_point; 5401425Sroot default: 5411425Sroot msg("Bad return code from dump: %d\n", status); 5421425Sroot Exit(X_ABORT); 5431425Sroot } 5441425Sroot /*NOTREACHED*/ 5451425Sroot } else { /* we are the child; just continue */ 5461425Sroot #ifdef TDEBUG 5471425Sroot sleep(4); /* allow time for parent's message to get out */ 5481425Sroot msg("Child on Tape %d has parent %d, my pid = %d\n", 5491425Sroot tapeno+1, parentpid, getpid()); 55025219Smckusick #endif TDEBUG 55147056Skarels /* 55247056Skarels * If we have a name like "/dev/rmt0,/dev/rmt1", 55347056Skarels * use the name before the comma first, and save 55448621Skarels * the remaining names for subsequent volumes. 55547056Skarels */ 55650504Smckusick tapeno++; /* current tape sequence */ 55748621Skarels if (nexttape || index(tape, ',')) { 55848621Skarels if (nexttape && *nexttape) 55948621Skarels tape = nexttape; 56048621Skarels if (p = index(tape, ',')) { 56148621Skarels *p = '\0'; 56248621Skarels nexttape = p + 1; 56348621Skarels } else 56448621Skarels nexttape = NULL; 56548621Skarels msg("Dumping volume %d on %s\n", tapeno, tape); 56648621Skarels } 56718012Smckusick #ifdef RDUMP 56846789Smckusick while ((tapefd = (host ? rmtopen(tape, 2) : 56946789Smckusick pipeout ? 1 : open(tape, O_WRONLY|O_CREAT, 0666))) < 0) 57050504Smckusick #else 57150504Smckusick while ((tapefd = (pipeout ? 1 : 57250504Smckusick open(tape, O_WRONLY|O_CREAT, 0666))) < 0) 57350504Smckusick #endif 57439128Smckusick { 57546614Smckusick msg("Cannot open output \"%s\".\n", tape); 57639128Smckusick if (!query("Do you want to retry the open?")) 577*55288Sbostic dumpabort(0); 57839128Smckusick } 5791425Sroot 58018012Smckusick enslave(); /* Share open tape file descriptor with slaves */ 58118012Smckusick 5821425Sroot asize = 0; 58348621Skarels blocksthisvol = 0; 58450504Smckusick if (top) 58550504Smckusick newtape++; /* new tape signal */ 58650504Smckusick spcl.c_count = slp->count; 58750504Smckusick /* 58850504Smckusick * measure firstrec in TP_BSIZE units since restore doesn't 58950504Smckusick * know the correct ntrec value... 59050504Smckusick */ 59150504Smckusick spcl.c_firstrec = slp->firstrec; 5921425Sroot spcl.c_volume++; 5931425Sroot spcl.c_type = TS_TAPE; 59430432Smckusick spcl.c_flags |= DR_NEWHEADER; 59554047Smckusick writeheader((ino_t)slp->inode); 59630432Smckusick spcl.c_flags &=~ DR_NEWHEADER; 5971425Sroot if (tapeno > 1) 59848621Skarels msg("Volume %d begins with blocks from inode %d\n", 59950504Smckusick tapeno, slp->inode); 6001425Sroot } 6011425Sroot } 6021425Sroot 60346585Storek void 604*55288Sbostic dumpabort(signo) 605*55288Sbostic int signo; 6061425Sroot { 60750504Smckusick 60818012Smckusick if (master != 0 && master != getpid()) 60954047Smckusick /* Signals master to call dumpabort */ 61054047Smckusick (void) kill(master, SIGTERM); 61124181Smckusick else { 61224181Smckusick killall(); 61324181Smckusick msg("The ENTIRE dump is aborted.\n"); 61424181Smckusick } 6151425Sroot Exit(X_ABORT); 6161425Sroot } 6171425Sroot 61846585Storek void 6191425Sroot Exit(status) 62046239Storek int status; 6211425Sroot { 62250504Smckusick 6231425Sroot #ifdef TDEBUG 6241425Sroot msg("pid = %d exits with status %d\n", getpid(), status); 6251425Sroot #endif TDEBUG 62654047Smckusick (void) exit(status); 6271425Sroot } 62818012Smckusick 62924181Smckusick /* 63050504Smckusick * proceed - handler for SIGUSR2, used to synchronize IO between the slaves. 63124181Smckusick */ 63246585Storek void 633*55288Sbostic proceed(signo) 634*55288Sbostic int signo; 63524181Smckusick { 63618012Smckusick 63750504Smckusick if (ready) 63850504Smckusick longjmp(jmpbuf, 1); 63950504Smckusick caught++; 64024181Smckusick } 64124181Smckusick 64246585Storek void 64318012Smckusick enslave() 64418012Smckusick { 64550504Smckusick int cmd[2]; 64624181Smckusick register int i, j; 64718012Smckusick 64818012Smckusick master = getpid(); 64950504Smckusick 65050504Smckusick signal(SIGTERM, dumpabort); /* Slave sends SIGTERM on dumpabort() */ 65125219Smckusick signal(SIGPIPE, sigpipe); 65225219Smckusick signal(SIGUSR1, tperror); /* Slave sends SIGUSR1 on tape errors */ 65350504Smckusick signal(SIGUSR2, proceed); /* Slave sends SIGUSR2 to next slave */ 65450504Smckusick 65524181Smckusick for (i = 0; i < SLAVES; i++) { 65650504Smckusick if (i == slp - &slaves[0]) { 65750504Smckusick caught = 1; 65824181Smckusick } else { 65950504Smckusick caught = 0; 66024181Smckusick } 66150504Smckusick 66250504Smckusick if (socketpair(AF_UNIX, SOCK_STREAM, 0, cmd) < 0 || 66350504Smckusick (slaves[i].pid = fork()) < 0) 66446585Storek quit("too many slaves, %d (recompile smaller): %s\n", 66546585Storek i, strerror(errno)); 66650504Smckusick 66750504Smckusick slaves[i].fd = cmd[1]; 66850504Smckusick slaves[i].sent = 0; 66950504Smckusick if (slaves[i].pid == 0) { /* Slave starts up here */ 67018012Smckusick for (j = 0; j <= i; j++) 67154047Smckusick (void) close(slaves[j].fd); 67225219Smckusick signal(SIGINT, SIG_IGN); /* Master handles this */ 67350504Smckusick doslave(cmd[0], i); 67418012Smckusick Exit(X_FINOK); 67518012Smckusick } 67618012Smckusick } 67750504Smckusick 67850504Smckusick for (i = 0; i < SLAVES; i++) 67954047Smckusick (void) atomic(write, slaves[i].fd, 68054047Smckusick (char *) &slaves[(i + 1) % SLAVES].pid, 68154047Smckusick sizeof slaves[0].pid); 68250504Smckusick 68350504Smckusick master = 0; 68418012Smckusick } 68518012Smckusick 68646585Storek void 68724181Smckusick killall() 68818012Smckusick { 68924181Smckusick register int i; 69019982Smckusick 69124181Smckusick for (i = 0; i < SLAVES; i++) 69250504Smckusick if (slaves[i].pid > 0) 69354047Smckusick (void) kill(slaves[i].pid, SIGKILL); 69418012Smckusick } 69518012Smckusick 69624181Smckusick /* 69724181Smckusick * Synchronization - each process has a lockfile, and shares file 69824181Smckusick * descriptors to the following process's lockfile. When our write 69924181Smckusick * completes, we release our lock on the following process's lock- 70024181Smckusick * file, allowing the following process to lock it and proceed. We 70124181Smckusick * get the lock back for the next cycle by swapping descriptors. 70224181Smckusick */ 70346585Storek void 70450504Smckusick doslave(cmd, slave_number) 70550504Smckusick register int cmd; 70650504Smckusick int slave_number; 70719982Smckusick { 70850504Smckusick register int nread; 70950504Smckusick int nextslave, size, wrote, eot_count; 71046795Sbostic #ifndef __STDC__ 71146795Sbostic int read(); 71246795Sbostic #endif 71319982Smckusick 71446789Smckusick /* 71546789Smckusick * Need our own seek pointer. 71646789Smckusick */ 71754047Smckusick (void) close(diskfd); 71846789Smckusick if ((diskfd = open(disk, O_RDONLY)) < 0) 71946585Storek quit("slave couldn't reopen disk: %s\n", strerror(errno)); 72050504Smckusick 72124181Smckusick /* 72250504Smckusick * Need the pid of the next slave in the loop... 72350504Smckusick */ 72454047Smckusick if ((nread = atomic(read, cmd, (char *)&nextslave, sizeof nextslave)) 72550504Smckusick != sizeof nextslave) { 72650504Smckusick quit("master/slave protocol botched - didn't get pid of next slave.\n"); 72750504Smckusick } 72850504Smckusick 72950504Smckusick /* 73025219Smckusick * Get list of blocks to dump, read the blocks into tape buffer 73124181Smckusick */ 73254047Smckusick while ((nread = atomic(read, cmd, (char *)slp->req, reqsiz)) == reqsiz) { 73350504Smckusick register struct req *p = slp->req; 73450504Smckusick 73550504Smckusick for (trecno = 0; trecno < ntrec; 73650504Smckusick trecno += p->count, p += p->count) { 73718012Smckusick if (p->dblk) { 73850504Smckusick bread(p->dblk, slp->tblock[trecno], 73925219Smckusick p->count * TP_BSIZE); 74018012Smckusick } else { 74125219Smckusick if (p->count != 1 || atomic(read, cmd, 74254047Smckusick (char *)slp->tblock[trecno], 74354047Smckusick TP_BSIZE) != TP_BSIZE) 74454047Smckusick quit("master/slave protocol botched.\n"); 74518012Smckusick } 74618012Smckusick } 74750504Smckusick if (setjmp(jmpbuf) == 0) { 74850504Smckusick ready = 1; 74950504Smckusick if (!caught) 75054047Smckusick (void) pause(); 75150504Smckusick } 75250504Smckusick ready = 0; 75350504Smckusick caught = 0; 75425219Smckusick 75550504Smckusick /* Try to write the data... */ 75650504Smckusick eot_count = 0; 75750504Smckusick size = 0; 75850504Smckusick 75950504Smckusick while (eot_count < 10 && size < writesize) { 76018012Smckusick #ifdef RDUMP 76150504Smckusick if (host) 76250504Smckusick wrote = rmtwrite(slp->tblock[0]+size, 76350504Smckusick writesize-size); 76448621Skarels else 76550504Smckusick #endif 76650504Smckusick wrote = write(tapefd, slp->tblock[0]+size, 76750504Smckusick writesize-size); 76850504Smckusick #ifdef WRITEDEBUG 76950504Smckusick printf("slave %d wrote %d\n", slave_number, wrote); 77050504Smckusick #endif 77150504Smckusick if (wrote < 0) 77250504Smckusick break; 77350504Smckusick if (wrote == 0) 77450504Smckusick eot_count++; 77550504Smckusick size += wrote; 77650504Smckusick } 77750504Smckusick 77850504Smckusick #ifdef WRITEDEBUG 77950504Smckusick if (size != writesize) 78050504Smckusick printf("slave %d only wrote %d out of %d bytes and gave up.\n", 78150504Smckusick slave_number, size, writesize); 78250504Smckusick #endif 78350504Smckusick 78450504Smckusick if (eot_count > 0) 78550504Smckusick size = 0; 78650504Smckusick 78750504Smckusick /* 78850504Smckusick * fixme: Pyramids running OSx return ENOSPC 78950504Smckusick * at EOT on 1/2 inch drives. 79050504Smckusick */ 79150504Smckusick if (size < 0) { 79254047Smckusick (void) kill(master, SIGUSR1); 79325219Smckusick for (;;) 79454047Smckusick (void) sigpause(0); 79550504Smckusick } else { 79650504Smckusick /* 79750504Smckusick * pass size of write back to master 79850504Smckusick * (for EOT handling) 79950504Smckusick */ 80054047Smckusick (void) atomic(write, cmd, (char *)&size, sizeof size); 80150504Smckusick } 80250504Smckusick 80350504Smckusick /* 80450504Smckusick * If partial write, don't want next slave to go. 80550504Smckusick * Also jolts him awake. 80650504Smckusick */ 80754047Smckusick (void) kill(nextslave, SIGUSR2); 80850504Smckusick } 80946585Storek if (nread != 0) 81046585Storek quit("error reading command pipe: %s\n", strerror(errno)); 81118012Smckusick } 81219947Smckusick 81319947Smckusick /* 81425219Smckusick * Since a read from a pipe may not return all we asked for, 81525219Smckusick * or a write may not write all we ask if we get a signal, 81625219Smckusick * loop until the count is satisfied (or error). 81719947Smckusick */ 81846585Storek int 81925219Smckusick atomic(func, fd, buf, count) 82025219Smckusick int (*func)(), fd, count; 82119947Smckusick char *buf; 82219947Smckusick { 82325219Smckusick int got, need = count; 82419947Smckusick 82525219Smckusick while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0) 82619947Smckusick buf += got; 82725219Smckusick return (got < 0 ? got : count - need); 82819947Smckusick } 829