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*50504Smckusick static char sccsid[] = "@(#)tape.c 5.19 (Berkeley) 07/23/91"; 1046585Storek #endif /* not lint */ 1117527Ssam 1246795Sbostic #include <sys/param.h> 1346585Storek #include <sys/wait.h> 1446795Sbostic #include <ufs/dinode.h> 1546795Sbostic #include <ufs/fs.h> 1646795Sbostic #include <signal.h> 1746795Sbostic #include <fcntl.h> 1846795Sbostic #include <protocols/dumprestore.h> 1946585Storek #include <errno.h> 20*50504Smckusick #include <setjmp.h> 2146795Sbostic #ifdef __STDC__ 2246795Sbostic #include <unistd.h> 2346795Sbostic #include <stdlib.h> 2446795Sbostic #include <string.h> 2546795Sbostic #endif 26*50504Smckusick #include <sys/socket.h> 2746795Sbostic #include "dump.h" 2839128Smckusick #include "pathnames.h" 291425Sroot 3029899Smckusick int writesize; /* size of malloc()ed buffer for tape */ 3129899Smckusick long lastspclrec = -1; /* tape block number of last written header */ 3229899Smckusick int trecno = 0; /* next record to write in current block */ 3346614Smckusick extern long blocksperfile; /* number of blocks per output file */ 3448621Skarels long blocksthisvol; /* number of blocks on current output file */ 3548621Skarels extern int ntrec; /* blocking factor on tape */ 3648621Skarels extern int cartridge; 37*50504Smckusick extern char *host; 3847056Skarels char *nexttape; 3925219Smckusick #ifdef RDUMP 4046585Storek int rmtopen(), rmtwrite(); 4146585Storek void rmtclose(); 4225219Smckusick #endif RDUMP 43*50504Smckusick void rollforward(); 4446585Storek int atomic(); 4546789Smckusick void doslave(), enslave(), flushtape(), killall(); 4646585Storek 4710911Ssam /* 4824181Smckusick * Concurrent dump mods (Caltech) - disk block reading and tape writing 4918012Smckusick * are exported to several slave processes. While one slave writes the 5018012Smckusick * tape, the others read disk blocks; they pass control of the tape in 51*50504Smckusick * a ring via signals. The parent process traverses the filesystem and 5246789Smckusick * sends writeheader()'s and lists of daddr's to the slaves via pipes. 53*50504Smckusick * The following structure defines the instruction packets sent to slaves. 5410911Ssam */ 55*50504Smckusick struct req { 5618012Smckusick daddr_t dblk; 5718012Smckusick int count; 58*50504Smckusick }; 5918012Smckusick int reqsiz; 6018012Smckusick 6124181Smckusick #define SLAVES 3 /* 1 slave writing, 1 reading, 1 for slack */ 62*50504Smckusick struct slave { 63*50504Smckusick int tapea; /* header number at start of this chunk */ 64*50504Smckusick int count; /* count to next header (used for TS_TAPE */ 65*50504Smckusick /* after EOT) */ 66*50504Smckusick int inode; /* inode that we are currently dealing with */ 67*50504Smckusick int fd; /* FD for this slave */ 68*50504Smckusick int pid; /* PID for this slave */ 69*50504Smckusick int sent; /* 1 == we've sent this slave requests */ 70*50504Smckusick int firstrec; /* record number of this block */ 71*50504Smckusick char (*tblock)[TP_BSIZE]; /* buffer for data blocks */ 72*50504Smckusick struct req *req; /* buffer for requests */ 73*50504Smckusick } slaves[SLAVES+1]; 74*50504Smckusick struct slave *slp; 7518012Smckusick 76*50504Smckusick char (*nextblock)[TP_BSIZE]; 77*50504Smckusick 78*50504Smckusick int master; /* pid of master, for sending error signals */ 79*50504Smckusick int tenths; /* length of tape used per block written */ 80*50504Smckusick static int caught; /* have we caught the signal to proceed? */ 81*50504Smckusick static int ready; /* have we reached the lock point without having */ 82*50504Smckusick /* received the SIGUSR2 signal from the prev slave? */ 83*50504Smckusick static jmp_buf jmpbuf; /* where to jump to if we are ready when the */ 84*50504Smckusick /* SIGUSR2 arrives from the previous slave */ 85*50504Smckusick 8646585Storek int 8710911Ssam alloctape() 8810911Ssam { 8925219Smckusick int pgoff = getpagesize() - 1; 90*50504Smckusick char *buf; 91*50504Smckusick int i; 9210911Ssam 9310911Ssam writesize = ntrec * TP_BSIZE; 94*50504Smckusick reqsiz = (ntrec + 1) * sizeof(struct req); 9524181Smckusick /* 9625219Smckusick * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode 9725219Smckusick * (see DEC TU80 User's Guide). The shorter gaps of 6250-bpi require 9825219Smckusick * repositioning after stopping, i.e, streaming mode, where the gap is 9925219Smckusick * variable, 0.30" to 0.45". The gap is maximal when the tape stops. 10024181Smckusick */ 10147056Skarels if (blocksperfile == 0) 10247056Skarels tenths = writesize / density + 10347056Skarels (cartridge ? 16 : density == 625 ? 5 : 8); 10425219Smckusick /* 10525219Smckusick * Allocate tape buffer contiguous with the array of instruction 10646789Smckusick * packets, so flushtape() can write them together with one write(). 10725219Smckusick * Align tape buffer on page boundary to speed up tape write(). 10825219Smckusick */ 109*50504Smckusick for (i = 0; i <= SLAVES; i++) { 110*50504Smckusick buf = (char *) malloc(reqsiz + writesize + pgoff + TP_BSIZE); 111*50504Smckusick if (buf == NULL) 112*50504Smckusick return(0); 113*50504Smckusick slaves[i].tblock = (char (*)[TP_BSIZE]) 114*50504Smckusick (((long)&buf[ntrec + 1] + pgoff) &~ pgoff); 115*50504Smckusick slaves[i].req = (struct req *)slaves[i].tblock - ntrec - 1; 116*50504Smckusick } 117*50504Smckusick slp = &slaves[0]; 118*50504Smckusick slp->count = 1; 119*50504Smckusick slp->tapea = 0; 120*50504Smckusick slp->firstrec = 0; 121*50504Smckusick nextblock = slp->tblock; 12224181Smckusick return(1); 12310911Ssam } 12410911Ssam 12546585Storek void 12646789Smckusick writerec(dp) 1275329Smckusic char *dp; 1281425Sroot { 129*50504Smckusick 130*50504Smckusick slp->req[trecno].dblk = (daddr_t)0; 131*50504Smckusick slp->req[trecno].count = 1; 132*50504Smckusick *(union u_spcl *)(*(nextblock)++) = *(union u_spcl *)dp; 13329899Smckusick lastspclrec = spcl.c_tapea; 13424181Smckusick trecno++; 1351425Sroot spcl.c_tapea++; 13646585Storek if (trecno >= ntrec) 13746789Smckusick flushtape(); 1381425Sroot } 1391425Sroot 14046585Storek void 14146789Smckusick dumpblock(blkno, size) 1424774Smckusic daddr_t blkno; 1434774Smckusic int size; 1441425Sroot { 14525219Smckusick int avail, tpblks, dblkno; 1461425Sroot 1475329Smckusic dblkno = fsbtodb(sblock, blkno); 14846585Storek tpblks = size >> tp_bshift; 14918012Smckusick while ((avail = MIN(tpblks, ntrec - trecno)) > 0) { 150*50504Smckusick slp->req[trecno].dblk = dblkno; 151*50504Smckusick slp->req[trecno].count = avail; 15225219Smckusick trecno += avail; 1534774Smckusic spcl.c_tapea += avail; 15425219Smckusick if (trecno >= ntrec) 15546789Smckusick flushtape(); 15646585Storek dblkno += avail << (tp_bshift - dev_bshift); 1575329Smckusic tpblks -= avail; 1584774Smckusic } 1591425Sroot } 1601425Sroot 1611425Sroot int nogripe = 0; 1621425Sroot 16346585Storek void 16446585Storek tperror() 16546585Storek { 166*50504Smckusick 16718012Smckusick if (pipeout) { 16846614Smckusick msg("write error on %s\n", tape); 16946585Storek quit("Cannot recover\n"); 17018012Smckusick /* NOTREACHED */ 17118012Smckusick } 17248621Skarels msg("write error %d blocks into volume %d\n", blocksthisvol, tapeno); 17346614Smckusick broadcast("DUMP WRITE ERROR!\n"); 17418012Smckusick if (!query("Do you want to restart?")) 17518012Smckusick dumpabort(); 17646614Smckusick msg("Closing this volume. Prepare to restart with new media;\n"); 17718012Smckusick msg("this dump volume will be rewritten.\n"); 17824181Smckusick killall(); 17918012Smckusick nogripe = 1; 18018012Smckusick close_rewind(); 18118012Smckusick Exit(X_REWRITE); 18218012Smckusick } 18318012Smckusick 18446585Storek void 18525219Smckusick sigpipe() 18625219Smckusick { 18725219Smckusick 18846585Storek quit("Broken pipe\n"); 18925219Smckusick } 19025219Smckusick 19146585Storek void 19246789Smckusick flushtape() 19318012Smckusick { 194*50504Smckusick int i, blks, got; 195*50504Smckusick long lastfirstrec; 19646795Sbostic #ifndef __STDC__ 197*50504Smckusick int write(), read(); 19846795Sbostic #endif 19946795Sbostic 200*50504Smckusick int siz = (char *)nextblock - (char *)slp->req; 2011425Sroot 202*50504Smckusick slp->req[trecno].count = 0; /* Sentinel */ 203*50504Smckusick 204*50504Smckusick if (atomic(write, slp->fd, slp->req, siz) != siz) 20546585Storek quit("error writing command pipe: %s\n", strerror(errno)); 206*50504Smckusick slp->sent = 1; /* we sent a request, read the response later */ 207*50504Smckusick 208*50504Smckusick lastfirstrec = slp->firstrec; 209*50504Smckusick 210*50504Smckusick if (++slp >= &slaves[SLAVES]) 211*50504Smckusick slp = &slaves[0]; 212*50504Smckusick 213*50504Smckusick /* Read results back from next slave */ 214*50504Smckusick if (slp->sent) { 215*50504Smckusick if (atomic(read, slp->fd, &got, sizeof got) != sizeof got) { 216*50504Smckusick perror(" DUMP: error reading command pipe in master"); 217*50504Smckusick dumpabort(); 218*50504Smckusick } 219*50504Smckusick slp->sent = 0; 220*50504Smckusick 221*50504Smckusick /* Check for end of tape */ 222*50504Smckusick if (got < writesize) { 223*50504Smckusick msg("End of tape detected\n"); 224*50504Smckusick 225*50504Smckusick /* 226*50504Smckusick * Drain the results, don't care what the values were. 227*50504Smckusick * If we read them here then trewind won't... 228*50504Smckusick */ 229*50504Smckusick for (i = 0; i < SLAVES; i++) { 230*50504Smckusick if (slaves[i].sent) { 231*50504Smckusick if (atomic(read, slaves[i].fd, &got, 232*50504Smckusick sizeof got) != sizeof got) { 233*50504Smckusick perror(" DUMP: error reading command pipe in master"); 234*50504Smckusick dumpabort(); 235*50504Smckusick } 236*50504Smckusick slaves[i].sent = 0; 237*50504Smckusick } 238*50504Smckusick } 239*50504Smckusick 240*50504Smckusick close_rewind(); 241*50504Smckusick rollforward(); 242*50504Smckusick return; 243*50504Smckusick } 244*50504Smckusick } 245*50504Smckusick 246*50504Smckusick blks = 0; 247*50504Smckusick if (spcl.c_type != TS_END) { 248*50504Smckusick for (i = 0; i < spcl.c_count; i++) 249*50504Smckusick if (spcl.c_addr[i] != 0) 250*50504Smckusick blks++; 251*50504Smckusick } 252*50504Smckusick slp->count = lastspclrec + blks + 1 - spcl.c_tapea; 253*50504Smckusick slp->tapea = spcl.c_tapea; 254*50504Smckusick slp->firstrec = lastfirstrec + ntrec; 255*50504Smckusick slp->inode = curino; 256*50504Smckusick nextblock = slp->tblock; 2571425Sroot trecno = 0; 25824181Smckusick asize += tenths; 25910911Ssam blockswritten += ntrec; 26048621Skarels blocksthisvol += ntrec; 26146614Smckusick if (!pipeout && (blocksperfile ? 26248621Skarels (blocksthisvol >= blocksperfile) : (asize > tsize))) { 2631425Sroot close_rewind(); 264*50504Smckusick startnewtape(0); 2651425Sroot } 2661425Sroot timeest(); 2671425Sroot } 2681425Sroot 26946585Storek void 27046239Storek trewind() 2711425Sroot { 27224181Smckusick int f; 273*50504Smckusick int got; 27412331Smckusick 27512331Smckusick if (pipeout) 27612331Smckusick return; 277*50504Smckusick for (f = 0; f < SLAVES; f++) { 278*50504Smckusick /* 279*50504Smckusick * Drain the results, but unlike EOT we DO (or should) care 280*50504Smckusick * what the return values were, since if we detect EOT after 281*50504Smckusick * we think we've written the last blocks to the tape anyway, 282*50504Smckusick * we have to replay those blocks with rollforward. 283*50504Smckusick * 284*50504Smckusick * fixme: punt for now. 285*50504Smckusick */ 286*50504Smckusick if (slaves[f].sent) { 287*50504Smckusick if (atomic(read, slaves[f].fd, &got, sizeof got) 288*50504Smckusick != sizeof got) { 289*50504Smckusick perror(" DUMP: error reading command pipe in master"); 290*50504Smckusick dumpabort(); 291*50504Smckusick } 292*50504Smckusick slaves[f].sent = 0; 293*50504Smckusick if (got != writesize) { 294*50504Smckusick msg("EOT detected in last 2 tape records!\n"); 295*50504Smckusick msg("Use a longer tape, decrease the size estimate\n"); 296*50504Smckusick quit("or use no size estimate at all.\n"); 297*50504Smckusick } 298*50504Smckusick } 299*50504Smckusick close(slaves[f].fd); 300*50504Smckusick } 30146585Storek while (wait((int *)NULL) >= 0) /* wait for any signals from slaves */ 30246585Storek /* void */; 30348621Skarels msg("Closing %s\n", tape); 304*50504Smckusick 30518012Smckusick #ifdef RDUMP 30625219Smckusick if (host) { 30725219Smckusick rmtclose(); 30825219Smckusick while (rmtopen(tape, 0) < 0) 30925219Smckusick sleep(10); 31025219Smckusick rmtclose(); 31125219Smckusick return; 31225219Smckusick } 313*50504Smckusick #endif 31446789Smckusick close(tapefd); 3153214Swnj while ((f = open(tape, 0)) < 0) 3163214Swnj sleep (10); 3173214Swnj close(f); 3181425Sroot } 3191425Sroot 32046585Storek void 3211425Sroot close_rewind() 3221425Sroot { 32346239Storek trewind(); 32448621Skarels if (nexttape) 32548621Skarels return; 32618012Smckusick if (!nogripe) { 32746614Smckusick msg("Change Volumes: Mount volume #%d\n", tapeno+1); 32846614Smckusick broadcast("CHANGE DUMP VOLUMES!\7\7\n"); 3291425Sroot } 33048621Skarels while (!query("Is the new volume mounted and ready to go?")) 33125219Smckusick if (query("Do you want to abort?")) { 3321425Sroot dumpabort(); 33325219Smckusick /*NOTREACHED*/ 33425219Smckusick } 3351425Sroot } 3361425Sroot 337*50504Smckusick #ifdef ROLLDEBUG 338*50504Smckusick int do_sum(block) 339*50504Smckusick union u_spcl *block; 340*50504Smckusick 341*50504Smckusick { 342*50504Smckusick char sum = 0; 343*50504Smckusick int i; 344*50504Smckusick 345*50504Smckusick for (i = 0; i < TP_BSIZE; i++) { 346*50504Smckusick sum = sum ^ block->dummy[i]; 347*50504Smckusick } 348*50504Smckusick return(sum); 349*50504Smckusick } 350*50504Smckusick #endif 351*50504Smckusick 352*50504Smckusick void 353*50504Smckusick rollforward() 354*50504Smckusick { 355*50504Smckusick register struct req *p, *q, *prev; 356*50504Smckusick register struct slave *tslp; 357*50504Smckusick int i, next, size, savedtapea, got; 358*50504Smckusick union u_spcl *ntb, *otb; 359*50504Smckusick #ifdef ROLLDEBUG 360*50504Smckusick int j; 361*50504Smckusick #endif 362*50504Smckusick tslp = &slaves[SLAVES]; 363*50504Smckusick ntb = (union u_spcl *)tslp->tblock[1]; 364*50504Smckusick 365*50504Smckusick /* 366*50504Smckusick * Each of the N slaves should have requests that need to 367*50504Smckusick * be replayed on the next tape. Use the extra slave buffers 368*50504Smckusick * (slaves[SLAVES]) to construct request lists to be sent to 369*50504Smckusick * each slave in turn. 370*50504Smckusick */ 371*50504Smckusick for (i = 0; i < SLAVES; i++) { 372*50504Smckusick q = &tslp->req[1]; 373*50504Smckusick otb = (union u_spcl *)slp->tblock; 374*50504Smckusick 375*50504Smckusick /* 376*50504Smckusick * For each request in the current slave, copy it to tslp. 377*50504Smckusick */ 378*50504Smckusick #ifdef ROLLDEBUG 379*50504Smckusick printf("replaying reqs to slave %d (%d)\n", slp - &slaves[0], 380*50504Smckusick slp->pid); 381*50504Smckusick j = 0; 382*50504Smckusick #endif 383*50504Smckusick 384*50504Smckusick for (p = slp->req; p->count > 0; p += p->count) { 385*50504Smckusick #ifdef ROLLDEBUG 386*50504Smckusick printf(" req %d count %d dblk %d\n", 387*50504Smckusick j++, p->count, p->dblk); 388*50504Smckusick if (p->dblk == 0) 389*50504Smckusick printf("\tsum %x\n", do_sum(otb)); 390*50504Smckusick #endif 391*50504Smckusick *q = *p; 392*50504Smckusick if (p->dblk == 0) 393*50504Smckusick *ntb++ = *otb++; /* copy the datablock also */ 394*50504Smckusick prev = q; 395*50504Smckusick q += q->count; 396*50504Smckusick } 397*50504Smckusick if (prev->dblk != 0) 398*50504Smckusick prev->count -= 1; 399*50504Smckusick else 400*50504Smckusick ntb--; 401*50504Smckusick q -= 1; 402*50504Smckusick q->count = 0; 403*50504Smckusick q = &tslp->req[0]; 404*50504Smckusick if (i == 0) { 405*50504Smckusick q->dblk = 0; 406*50504Smckusick q->count = 1; 407*50504Smckusick trecno = 0; 408*50504Smckusick nextblock = tslp->tblock; 409*50504Smckusick savedtapea = spcl.c_tapea; 410*50504Smckusick spcl.c_tapea = slp->tapea; 411*50504Smckusick startnewtape(0); 412*50504Smckusick spcl.c_tapea = savedtapea; 413*50504Smckusick lastspclrec = savedtapea - 1; 414*50504Smckusick } 415*50504Smckusick size = (char *)ntb - (char *)q; 416*50504Smckusick if (atomic(write, slp->fd, q, size) != size) { 417*50504Smckusick perror(" DUMP: error writing command pipe"); 418*50504Smckusick dumpabort(); 419*50504Smckusick } 420*50504Smckusick slp->sent = 1; 421*50504Smckusick #ifdef ROLLDEBUG 422*50504Smckusick printf("after the shift:\n"); 423*50504Smckusick j = 0; 424*50504Smckusick for (p = tslp->req; p->count > 0; p += p->count) { 425*50504Smckusick printf(" req %d count %d dblk %d\n", 426*50504Smckusick j++, p->count, p->dblk); 427*50504Smckusick if (p->dblk == 0) { 428*50504Smckusick /* dump block also */ 429*50504Smckusick } 430*50504Smckusick } 431*50504Smckusick #endif 432*50504Smckusick if (++slp >= &slaves[SLAVES]) 433*50504Smckusick slp = &slaves[0]; 434*50504Smckusick 435*50504Smckusick q->count = 1; 436*50504Smckusick 437*50504Smckusick if (prev->dblk != 0) { 438*50504Smckusick /* 439*50504Smckusick * If the last one was a disk block, make the 440*50504Smckusick * first of this one be the last bit of that disk 441*50504Smckusick * block... 442*50504Smckusick */ 443*50504Smckusick q->dblk = prev->dblk + 444*50504Smckusick prev->count * (TP_BSIZE / DEV_BSIZE); 445*50504Smckusick ntb = (union u_spcl *)tslp->tblock; 446*50504Smckusick } else { 447*50504Smckusick /* 448*50504Smckusick * It wasn't a disk block. Copy the data to its 449*50504Smckusick * new location in the buffer. 450*50504Smckusick */ 451*50504Smckusick q->dblk = 0; 452*50504Smckusick *((union u_spcl *)tslp->tblock) = *ntb; 453*50504Smckusick ntb = (union u_spcl *)tslp->tblock[1]; 454*50504Smckusick } 455*50504Smckusick } 456*50504Smckusick slp->req[0] = *q; 457*50504Smckusick nextblock = slp->tblock; 458*50504Smckusick if (q->dblk == 0) 459*50504Smckusick nextblock++; 460*50504Smckusick trecno = 1; 461*50504Smckusick 462*50504Smckusick /* 463*50504Smckusick * Clear the first slaves' response. One hopes that it 464*50504Smckusick * worked ok, otherwise the tape is much too short! 465*50504Smckusick */ 466*50504Smckusick if (slp->sent) { 467*50504Smckusick if (atomic(read, slp->fd, &got, sizeof got) != sizeof got) { 468*50504Smckusick perror(" DUMP: error reading command pipe in master"); 469*50504Smckusick dumpabort(); 470*50504Smckusick } 471*50504Smckusick slp->sent = 0; 472*50504Smckusick 473*50504Smckusick if (got != writesize) { 474*50504Smckusick quit("EOT detected at start of the tape!\n"); 475*50504Smckusick } 476*50504Smckusick } 477*50504Smckusick } 478*50504Smckusick 4791425Sroot /* 480*50504Smckusick * We implement taking and restoring checkpoints on the tape level. 481*50504Smckusick * When each tape is opened, a new process is created by forking; this 482*50504Smckusick * saves all of the necessary context in the parent. The child 483*50504Smckusick * continues the dump; the parent waits around, saving the context. 484*50504Smckusick * If the child returns X_REWRITE, then it had problems writing that tape; 485*50504Smckusick * this causes the parent to fork again, duplicating the context, and 486*50504Smckusick * everything continues as if nothing had happened. 4871425Sroot */ 48846585Storek void 489*50504Smckusick startnewtape(top) 490*50504Smckusick int top; 4911425Sroot { 4921425Sroot int parentpid; 4931425Sroot int childpid; 4941425Sroot int status; 4951425Sroot int waitpid; 49639164Sbostic sig_t interrupt; 497*50504Smckusick int i; 49847056Skarels char *p; 4991425Sroot 50039164Sbostic interrupt = signal(SIGINT, SIG_IGN); 5011425Sroot parentpid = getpid(); 5021425Sroot 5031425Sroot restore_check_point: 50439164Sbostic (void)signal(SIGINT, interrupt); 50525219Smckusick /* 50625219Smckusick * All signals are inherited... 50725219Smckusick */ 5081425Sroot childpid = fork(); 50918012Smckusick if (childpid < 0) { 5101425Sroot msg("Context save fork fails in parent %d\n", parentpid); 5111425Sroot Exit(X_ABORT); 5121425Sroot } 51318012Smckusick if (childpid != 0) { 5141425Sroot /* 5151425Sroot * PARENT: 5161425Sroot * save the context by waiting 5171425Sroot * until the child doing all of the work returns. 51818012Smckusick * don't catch the interrupt 5191425Sroot */ 52025219Smckusick signal(SIGINT, SIG_IGN); 5211425Sroot #ifdef TDEBUG 5221425Sroot msg("Tape: %d; parent process: %d child process %d\n", 5231425Sroot tapeno+1, parentpid, childpid); 5241425Sroot #endif TDEBUG 52518012Smckusick while ((waitpid = wait(&status)) != childpid) 52618012Smckusick msg("Parent %d waiting for child %d has another child %d return\n", 52718012Smckusick parentpid, childpid, waitpid); 52818012Smckusick if (status & 0xFF) { 5291425Sroot msg("Child %d returns LOB status %o\n", 5301425Sroot childpid, status&0xFF); 5311425Sroot } 5321425Sroot status = (status >> 8) & 0xFF; 5331425Sroot #ifdef TDEBUG 53418012Smckusick switch(status) { 5351425Sroot case X_FINOK: 5361425Sroot msg("Child %d finishes X_FINOK\n", childpid); 5371425Sroot break; 538*50504Smckusick case X_ABORT: 5391425Sroot msg("Child %d finishes X_ABORT\n", childpid); 5401425Sroot break; 5411425Sroot case X_REWRITE: 5421425Sroot msg("Child %d finishes X_REWRITE\n", childpid); 5431425Sroot break; 5441425Sroot default: 54518012Smckusick msg("Child %d finishes unknown %d\n", 54625219Smckusick childpid, status); 5471425Sroot break; 5481425Sroot } 5491425Sroot #endif TDEBUG 55018012Smckusick switch(status) { 5511425Sroot case X_FINOK: 5521425Sroot Exit(X_FINOK); 5531425Sroot case X_ABORT: 5541425Sroot Exit(X_ABORT); 5551425Sroot case X_REWRITE: 5561425Sroot goto restore_check_point; 5571425Sroot default: 5581425Sroot msg("Bad return code from dump: %d\n", status); 5591425Sroot Exit(X_ABORT); 5601425Sroot } 5611425Sroot /*NOTREACHED*/ 5621425Sroot } else { /* we are the child; just continue */ 5631425Sroot #ifdef TDEBUG 5641425Sroot sleep(4); /* allow time for parent's message to get out */ 5651425Sroot msg("Child on Tape %d has parent %d, my pid = %d\n", 5661425Sroot tapeno+1, parentpid, getpid()); 56725219Smckusick #endif TDEBUG 56847056Skarels /* 56947056Skarels * If we have a name like "/dev/rmt0,/dev/rmt1", 57047056Skarels * use the name before the comma first, and save 57148621Skarels * the remaining names for subsequent volumes. 57247056Skarels */ 573*50504Smckusick tapeno++; /* current tape sequence */ 57448621Skarels if (nexttape || index(tape, ',')) { 57548621Skarels if (nexttape && *nexttape) 57648621Skarels tape = nexttape; 57748621Skarels if (p = index(tape, ',')) { 57848621Skarels *p = '\0'; 57948621Skarels nexttape = p + 1; 58048621Skarels } else 58148621Skarels nexttape = NULL; 58248621Skarels msg("Dumping volume %d on %s\n", tapeno, tape); 58348621Skarels } 58418012Smckusick #ifdef RDUMP 58546789Smckusick while ((tapefd = (host ? rmtopen(tape, 2) : 58646789Smckusick pipeout ? 1 : open(tape, O_WRONLY|O_CREAT, 0666))) < 0) 587*50504Smckusick #else 588*50504Smckusick while ((tapefd = (pipeout ? 1 : 589*50504Smckusick open(tape, O_WRONLY|O_CREAT, 0666))) < 0) 590*50504Smckusick #endif 59139128Smckusick { 59246614Smckusick msg("Cannot open output \"%s\".\n", tape); 59339128Smckusick if (!query("Do you want to retry the open?")) 59418012Smckusick dumpabort(); 59539128Smckusick } 5961425Sroot 59718012Smckusick enslave(); /* Share open tape file descriptor with slaves */ 59818012Smckusick 5991425Sroot asize = 0; 60048621Skarels blocksthisvol = 0; 601*50504Smckusick if (top) 602*50504Smckusick newtape++; /* new tape signal */ 603*50504Smckusick spcl.c_count = slp->count; 604*50504Smckusick /* 605*50504Smckusick * measure firstrec in TP_BSIZE units since restore doesn't 606*50504Smckusick * know the correct ntrec value... 607*50504Smckusick */ 608*50504Smckusick spcl.c_firstrec = slp->firstrec; 6091425Sroot spcl.c_volume++; 6101425Sroot spcl.c_type = TS_TAPE; 61130432Smckusick spcl.c_flags |= DR_NEWHEADER; 612*50504Smckusick writeheader(slp->inode); 61330432Smckusick spcl.c_flags &=~ DR_NEWHEADER; 6141425Sroot if (tapeno > 1) 61548621Skarels msg("Volume %d begins with blocks from inode %d\n", 616*50504Smckusick tapeno, slp->inode); 6171425Sroot } 6181425Sroot } 6191425Sroot 62046585Storek void 6211425Sroot dumpabort() 6221425Sroot { 623*50504Smckusick 62418012Smckusick if (master != 0 && master != getpid()) 62525219Smckusick kill(master, SIGTERM); /* Signals master to call dumpabort */ 62624181Smckusick else { 62724181Smckusick killall(); 62824181Smckusick msg("The ENTIRE dump is aborted.\n"); 62924181Smckusick } 6301425Sroot Exit(X_ABORT); 6311425Sroot } 6321425Sroot 63346585Storek void 6341425Sroot Exit(status) 63546239Storek int status; 6361425Sroot { 637*50504Smckusick 6381425Sroot #ifdef TDEBUG 6391425Sroot msg("pid = %d exits with status %d\n", getpid(), status); 6401425Sroot #endif TDEBUG 6411925Swnj exit(status); 6421425Sroot } 64318012Smckusick 64424181Smckusick /* 645*50504Smckusick * proceed - handler for SIGUSR2, used to synchronize IO between the slaves. 64624181Smckusick */ 64746585Storek void 648*50504Smckusick proceed() 64924181Smckusick { 65018012Smckusick 651*50504Smckusick if (ready) 652*50504Smckusick longjmp(jmpbuf, 1); 653*50504Smckusick caught++; 65424181Smckusick } 65524181Smckusick 65646585Storek void 65718012Smckusick enslave() 65818012Smckusick { 659*50504Smckusick int cmd[2]; 66024181Smckusick register int i, j; 66118012Smckusick 66218012Smckusick master = getpid(); 663*50504Smckusick 664*50504Smckusick signal(SIGTERM, dumpabort); /* Slave sends SIGTERM on dumpabort() */ 66525219Smckusick signal(SIGPIPE, sigpipe); 66625219Smckusick signal(SIGUSR1, tperror); /* Slave sends SIGUSR1 on tape errors */ 667*50504Smckusick signal(SIGUSR2, proceed); /* Slave sends SIGUSR2 to next slave */ 668*50504Smckusick 66924181Smckusick for (i = 0; i < SLAVES; i++) { 670*50504Smckusick if (i == slp - &slaves[0]) { 671*50504Smckusick caught = 1; 67224181Smckusick } else { 673*50504Smckusick caught = 0; 67424181Smckusick } 675*50504Smckusick 676*50504Smckusick if (socketpair(AF_UNIX, SOCK_STREAM, 0, cmd) < 0 || 677*50504Smckusick (slaves[i].pid = fork()) < 0) 67846585Storek quit("too many slaves, %d (recompile smaller): %s\n", 67946585Storek i, strerror(errno)); 680*50504Smckusick 681*50504Smckusick slaves[i].fd = cmd[1]; 682*50504Smckusick slaves[i].sent = 0; 683*50504Smckusick if (slaves[i].pid == 0) { /* Slave starts up here */ 68418012Smckusick for (j = 0; j <= i; j++) 685*50504Smckusick close(slaves[j].fd); 68625219Smckusick signal(SIGINT, SIG_IGN); /* Master handles this */ 687*50504Smckusick doslave(cmd[0], i); 68818012Smckusick Exit(X_FINOK); 68918012Smckusick } 69018012Smckusick } 691*50504Smckusick 692*50504Smckusick for (i = 0; i < SLAVES; i++) 693*50504Smckusick atomic(write, slaves[i].fd, &slaves[(i + 1) % SLAVES].pid, 694*50504Smckusick sizeof slaves[0].pid); 695*50504Smckusick 696*50504Smckusick master = 0; 69718012Smckusick } 69818012Smckusick 69946585Storek void 70024181Smckusick killall() 70118012Smckusick { 70224181Smckusick register int i; 70319982Smckusick 70424181Smckusick for (i = 0; i < SLAVES; i++) 705*50504Smckusick if (slaves[i].pid > 0) 706*50504Smckusick kill(slaves[i].pid, SIGKILL); 70718012Smckusick } 70818012Smckusick 70924181Smckusick /* 71024181Smckusick * Synchronization - each process has a lockfile, and shares file 71124181Smckusick * descriptors to the following process's lockfile. When our write 71224181Smckusick * completes, we release our lock on the following process's lock- 71324181Smckusick * file, allowing the following process to lock it and proceed. We 71424181Smckusick * get the lock back for the next cycle by swapping descriptors. 71524181Smckusick */ 71646585Storek void 717*50504Smckusick doslave(cmd, slave_number) 718*50504Smckusick register int cmd; 719*50504Smckusick int slave_number; 72019982Smckusick { 721*50504Smckusick register int nread; 722*50504Smckusick int nextslave, size, wrote, eot_count; 72346795Sbostic #ifndef __STDC__ 72446795Sbostic int read(); 72546795Sbostic #endif 726*50504Smckusick #ifdef ROLLDEBUG 727*50504Smckusick int dodump = 2; 728*50504Smckusick FILE *out; 729*50504Smckusick char name[64]; 730*50504Smckusick #endif 73119982Smckusick 73246789Smckusick /* 73346789Smckusick * Need our own seek pointer. 73446789Smckusick */ 73546789Smckusick close(diskfd); 73646789Smckusick if ((diskfd = open(disk, O_RDONLY)) < 0) 73746585Storek quit("slave couldn't reopen disk: %s\n", strerror(errno)); 738*50504Smckusick 73924181Smckusick /* 740*50504Smckusick * Need the pid of the next slave in the loop... 741*50504Smckusick */ 742*50504Smckusick if ((nread = atomic(read, cmd, &nextslave, sizeof nextslave)) 743*50504Smckusick != sizeof nextslave) { 744*50504Smckusick quit("master/slave protocol botched - didn't get pid of next slave.\n"); 745*50504Smckusick } 746*50504Smckusick 747*50504Smckusick #ifdef ROLLDEBUG 748*50504Smckusick sprintf(name, "slave.%d", slave_number); 749*50504Smckusick out = fopen(name, "w"); 750*50504Smckusick #endif 751*50504Smckusick /* 75225219Smckusick * Get list of blocks to dump, read the blocks into tape buffer 75324181Smckusick */ 754*50504Smckusick while ((nread = atomic(read, cmd, slp->req, reqsiz)) == reqsiz) { 755*50504Smckusick register struct req *p = slp->req; 756*50504Smckusick int j; 757*50504Smckusick struct req *rover; 758*50504Smckusick char (*orover)[TP_BSIZE]; 759*50504Smckusick 760*50504Smckusick j = 0; 761*50504Smckusick for (trecno = 0; trecno < ntrec; 762*50504Smckusick trecno += p->count, p += p->count) { 76318012Smckusick if (p->dblk) { 764*50504Smckusick bread(p->dblk, slp->tblock[trecno], 76525219Smckusick p->count * TP_BSIZE); 76618012Smckusick } else { 76725219Smckusick if (p->count != 1 || atomic(read, cmd, 768*50504Smckusick slp->tblock[trecno], TP_BSIZE) != TP_BSIZE) 76946585Storek quit("master/slave protocol botched.\n"); 77018012Smckusick } 771*50504Smckusick #ifdef ROLLDEBUG 772*50504Smckusick if (dodump) { 773*50504Smckusick fprintf(out, " req %d count %d dblk %d\n", 774*50504Smckusick j++, p->count, p->dblk); 775*50504Smckusick if (p->dblk == 0) { 776*50504Smckusick fprintf(out, "\tsum %x\n", 777*50504Smckusick do_sum(slp->tblock[trecno])); 778*50504Smckusick } 779*50504Smckusick } 780*50504Smckusick #endif 78118012Smckusick } 782*50504Smckusick #ifdef ROLLDEBUG 783*50504Smckusick if (dodump) { 784*50504Smckusick fprintf(out, "\n"); 785*50504Smckusick } 786*50504Smckusick if (--dodump == 0) { 787*50504Smckusick fclose(out); 788*50504Smckusick } 789*50504Smckusick #endif 790*50504Smckusick if (setjmp(jmpbuf) == 0) { 791*50504Smckusick ready = 1; 792*50504Smckusick if (!caught) 793*50504Smckusick pause(); 794*50504Smckusick } 795*50504Smckusick ready = 0; 796*50504Smckusick caught = 0; 79725219Smckusick 798*50504Smckusick /* Try to write the data... */ 799*50504Smckusick eot_count = 0; 800*50504Smckusick size = 0; 801*50504Smckusick 802*50504Smckusick while (eot_count < 10 && size < writesize) { 80318012Smckusick #ifdef RDUMP 804*50504Smckusick if (host) 805*50504Smckusick wrote = rmtwrite(slp->tblock[0]+size, 806*50504Smckusick writesize-size); 80748621Skarels else 808*50504Smckusick #endif 809*50504Smckusick wrote = write(tapefd, slp->tblock[0]+size, 810*50504Smckusick writesize-size); 811*50504Smckusick #ifdef WRITEDEBUG 812*50504Smckusick printf("slave %d wrote %d\n", slave_number, wrote); 813*50504Smckusick #endif 814*50504Smckusick if (wrote < 0) 815*50504Smckusick break; 816*50504Smckusick if (wrote == 0) 817*50504Smckusick eot_count++; 818*50504Smckusick size += wrote; 819*50504Smckusick } 820*50504Smckusick 821*50504Smckusick #ifdef WRITEDEBUG 822*50504Smckusick if (size != writesize) 823*50504Smckusick printf("slave %d only wrote %d out of %d bytes and gave up.\n", 824*50504Smckusick slave_number, size, writesize); 825*50504Smckusick #endif 826*50504Smckusick 827*50504Smckusick if (eot_count > 0) 828*50504Smckusick size = 0; 829*50504Smckusick 830*50504Smckusick /* 831*50504Smckusick * fixme: Pyramids running OSx return ENOSPC 832*50504Smckusick * at EOT on 1/2 inch drives. 833*50504Smckusick */ 834*50504Smckusick if (size < 0) { 83525219Smckusick kill(master, SIGUSR1); 83625219Smckusick for (;;) 83725219Smckusick sigpause(0); 838*50504Smckusick } else { 839*50504Smckusick /* 840*50504Smckusick * pass size of write back to master 841*50504Smckusick * (for EOT handling) 842*50504Smckusick */ 843*50504Smckusick atomic(write, cmd, &size, sizeof size); 844*50504Smckusick } 845*50504Smckusick 846*50504Smckusick /* 847*50504Smckusick * If partial write, don't want next slave to go. 848*50504Smckusick * Also jolts him awake. 849*50504Smckusick */ 850*50504Smckusick kill(nextslave, SIGUSR2); 851*50504Smckusick } 85246585Storek if (nread != 0) 85346585Storek quit("error reading command pipe: %s\n", strerror(errno)); 85418012Smckusick } 85519947Smckusick 85619947Smckusick /* 85725219Smckusick * Since a read from a pipe may not return all we asked for, 85825219Smckusick * or a write may not write all we ask if we get a signal, 85925219Smckusick * loop until the count is satisfied (or error). 86019947Smckusick */ 86146585Storek int 86225219Smckusick atomic(func, fd, buf, count) 86325219Smckusick int (*func)(), fd, count; 86419947Smckusick char *buf; 86519947Smckusick { 86625219Smckusick int got, need = count; 86719947Smckusick 86825219Smckusick while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0) 86919947Smckusick buf += got; 87025219Smckusick return (got < 0 ? got : count - need); 87119947Smckusick } 872