1214571Sdim/*- 2214571Sdim * Copyright (c) 1980, 1991, 1993 3214571Sdim * The Regents of the University of California. All rights reserved. 4214571Sdim * 5214571Sdim * Redistribution and use in source and binary forms, with or without 6214571Sdim * modification, are permitted provided that the following conditions 7214571Sdim * are met: 8214571Sdim * 1. Redistributions of source code must retain the above copyright 9214571Sdim * notice, this list of conditions and the following disclaimer. 10214571Sdim * 2. Redistributions in binary form must reproduce the above copyright 11214571Sdim * notice, this list of conditions and the following disclaimer in the 12214571Sdim * documentation and/or other materials provided with the distribution. 13214571Sdim * 4. Neither the name of the University nor the names of its contributors 14214571Sdim * may be used to endorse or promote products derived from this software 15214571Sdim * without specific prior written permission. 16214571Sdim * 17214571Sdim * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 18214571Sdim * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19214571Sdim * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20214571Sdim * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 21214571Sdim * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22214571Sdim * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 23214571Sdim * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 24214571Sdim * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 25214571Sdim * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 26214571Sdim * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 27214571Sdim * SUCH DAMAGE. 28214571Sdim */ 29214571Sdim 30214571Sdim#ifndef lint 31214571Sdim#if 0 32214571Sdimstatic char sccsid[] = "@(#)tape.c 8.4 (Berkeley) 5/1/95"; 33214571Sdim#endif 34214571Sdimstatic const char rcsid[] = 35214571Sdim "$FreeBSD$"; 36214571Sdim#endif /* not lint */ 37214571Sdim 38214571Sdim#include <sys/param.h> 39214571Sdim#include <sys/socket.h> 40214571Sdim#include <sys/wait.h> 41214571Sdim#include <sys/stat.h> 42214571Sdim 43214571Sdim#include <ufs/ufs/dinode.h> 44214571Sdim#include <ufs/ffs/fs.h> 45214571Sdim 46214571Sdim#include <protocols/dumprestore.h> 47214571Sdim 48214571Sdim#include <errno.h> 49214571Sdim#include <fcntl.h> 50214571Sdim#include <limits.h> 51214571Sdim#include <setjmp.h> 52214571Sdim#include <signal.h> 53214571Sdim#include <stdio.h> 54214571Sdim#include <stdlib.h> 55214571Sdim#include <string.h> 56214571Sdim#include <time.h> 57214571Sdim#include <unistd.h> 58214571Sdim 59214571Sdim#include "dump.h" 60214571Sdim 61214571Sdimint writesize; /* size of malloc()ed buffer for tape */ 62214571Sdimint64_t lastspclrec = -1; /* tape block number of last written header */ 63214571Sdimint trecno = 0; /* next record to write in current block */ 64214571Sdimextern long blocksperfile; /* number of blocks per output file */ 65214571Sdimlong blocksthisvol; /* number of blocks on current output file */ 66214571Sdimextern int ntrec; /* blocking factor on tape */ 67214571Sdimextern int cartridge; 68214571Sdimextern char *host; 69214571Sdimchar *nexttape; 70214571SdimFILE *popenfp = NULL; 71214571Sdim 72214571Sdimstatic int atomic(ssize_t (*)(), int, char *, int); 73214571Sdimstatic void doslave(int, int); 74214571Sdimstatic void enslave(void); 75214571Sdimstatic void flushtape(void); 76214571Sdimstatic void killall(void); 77214571Sdimstatic void rollforward(void); 78214571Sdim 79214571Sdim/* 80214571Sdim * Concurrent dump mods (Caltech) - disk block reading and tape writing 81214571Sdim * are exported to several slave processes. While one slave writes the 82214571Sdim * tape, the others read disk blocks; they pass control of the tape in 83214571Sdim * a ring via signals. The parent process traverses the file system and 84214571Sdim * sends writeheader()'s and lists of daddr's to the slaves via pipes. 85214571Sdim * The following structure defines the instruction packets sent to slaves. 86214571Sdim */ 87214571Sdimstruct req { 88214571Sdim ufs2_daddr_t dblk; 89214571Sdim int count; 90214571Sdim}; 91214571Sdimint reqsiz; 92214571Sdim 93214571Sdim#define SLAVES 3 /* 1 slave writing, 1 reading, 1 for slack */ 94214571Sdimstruct slave { 95214571Sdim int64_t tapea; /* header number at start of this chunk */ 96214571Sdim int64_t firstrec; /* record number of this block */ 97214571Sdim int count; /* count to next header (used for TS_TAPE */ 98214571Sdim /* after EOT) */ 99214571Sdim int inode; /* inode that we are currently dealing with */ 100214571Sdim int fd; /* FD for this slave */ 101214571Sdim int pid; /* PID for this slave */ 102214571Sdim int sent; /* 1 == we've sent this slave requests */ 103214571Sdim char (*tblock)[TP_BSIZE]; /* buffer for data blocks */ 104214571Sdim struct req *req; /* buffer for requests */ 105214571Sdim} slaves[SLAVES+1]; 106214571Sdimstruct slave *slp; 107214571Sdim 108214571Sdimchar (*nextblock)[TP_BSIZE]; 109214571Sdim 110214571Sdimint master; /* pid of master, for sending error signals */ 111214571Sdimint tenths; /* length of tape used per block written */ 112214571Sdimstatic volatile sig_atomic_t caught; /* have we caught the signal to proceed? */ 113214571Sdimstatic volatile sig_atomic_t ready; /* reached the lock point without having */ 114214571Sdim /* received the SIGUSR2 signal from the prev slave? */ 115214571Sdimstatic jmp_buf jmpbuf; /* where to jump to if we are ready when the */ 116214571Sdim /* SIGUSR2 arrives from the previous slave */ 117214571Sdim 118214571Sdimint 119214571Sdimalloctape(void) 120214571Sdim{ 121214571Sdim int pgoff = getpagesize() - 1; 122214571Sdim char *buf; 123214571Sdim int i; 124214571Sdim 125214571Sdim writesize = ntrec * TP_BSIZE; 126214571Sdim reqsiz = (ntrec + 1) * sizeof(struct req); 127214571Sdim /* 128214571Sdim * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode 129214571Sdim * (see DEC TU80 User's Guide). The shorter gaps of 6250-bpi require 130214571Sdim * repositioning after stopping, i.e, streaming mode, where the gap is 131214571Sdim * variable, 0.30" to 0.45". The gap is maximal when the tape stops. 132214571Sdim */ 133214571Sdim if (blocksperfile == 0 && !unlimited) 134214571Sdim tenths = writesize / density + 135214571Sdim (cartridge ? 16 : density == 625 ? 5 : 8); 136214571Sdim /* 137214571Sdim * Allocate tape buffer contiguous with the array of instruction 138214571Sdim * packets, so flushtape() can write them together with one write(). 139214571Sdim * Align tape buffer on page boundary to speed up tape write(). 140214571Sdim */ 141214571Sdim for (i = 0; i <= SLAVES; i++) { 142214571Sdim buf = (char *) 143214571Sdim malloc((unsigned)(reqsiz + writesize + pgoff + TP_BSIZE)); 144214571Sdim if (buf == NULL) 145214571Sdim return(0); 146214571Sdim slaves[i].tblock = (char (*)[TP_BSIZE]) 147214571Sdim (((long)&buf[ntrec + 1] + pgoff) &~ pgoff); 148214571Sdim slaves[i].req = (struct req *)slaves[i].tblock - ntrec - 1; 149214571Sdim } 150214571Sdim slp = &slaves[0]; 151214571Sdim slp->count = 1; 152214571Sdim slp->tapea = 0; 153214571Sdim slp->firstrec = 0; 154214571Sdim nextblock = slp->tblock; 155214571Sdim return(1); 156214571Sdim} 157214571Sdim 158214571Sdimvoid 159214571Sdimwriterec(char *dp, int isspcl) 160214571Sdim{ 161214571Sdim 162214571Sdim slp->req[trecno].dblk = (ufs2_daddr_t)0; 163214571Sdim slp->req[trecno].count = 1; 164214571Sdim /* Can't do a structure assignment due to alignment problems */ 165214571Sdim bcopy(dp, *(nextblock)++, sizeof (union u_spcl)); 166214571Sdim if (isspcl) 167214571Sdim lastspclrec = spcl.c_tapea; 168214571Sdim trecno++; 169214571Sdim spcl.c_tapea++; 170214571Sdim if (trecno >= ntrec) 171214571Sdim flushtape(); 172214571Sdim} 173214571Sdim 174214571Sdimvoid 175214571Sdimdumpblock(ufs2_daddr_t blkno, int size) 176214571Sdim{ 177214571Sdim int avail, tpblks; 178214571Sdim ufs2_daddr_t dblkno; 179214571Sdim 180214571Sdim dblkno = fsbtodb(sblock, blkno); 181214571Sdim tpblks = size >> tp_bshift; 182214571Sdim while ((avail = MIN(tpblks, ntrec - trecno)) > 0) { 183214571Sdim slp->req[trecno].dblk = dblkno; 184214571Sdim slp->req[trecno].count = avail; 185214571Sdim trecno += avail; 186214571Sdim spcl.c_tapea += avail; 187214571Sdim if (trecno >= ntrec) 188214571Sdim flushtape(); 189214571Sdim dblkno += avail << (tp_bshift - dev_bshift); 190214571Sdim tpblks -= avail; 191214571Sdim } 192214571Sdim} 193214571Sdim 194214571Sdimint nogripe = 0; 195214571Sdim 196214571Sdimvoid 197214571Sdimtperror(int signo __unused) 198214571Sdim{ 199214571Sdim 200214571Sdim if (pipeout) { 201214571Sdim msg("write error on %s\n", tape); 202214571Sdim quit("Cannot recover\n"); 203214571Sdim /* NOTREACHED */ 204214571Sdim } 205214571Sdim msg("write error %ld blocks into volume %d\n", blocksthisvol, tapeno); 206214571Sdim broadcast("DUMP WRITE ERROR!\n"); 207214571Sdim if (!query("Do you want to restart?")) 208214571Sdim dumpabort(0); 209214571Sdim msg("Closing this volume. Prepare to restart with new media;\n"); 210214571Sdim msg("this dump volume will be rewritten.\n"); 211214571Sdim killall(); 212214571Sdim nogripe = 1; 213214571Sdim close_rewind(); 214214571Sdim Exit(X_REWRITE); 215214571Sdim} 216214571Sdim 217214571Sdimvoid 218214571Sdimsigpipe(int signo __unused) 219214571Sdim{ 220214571Sdim 221214571Sdim quit("Broken pipe\n"); 222214571Sdim} 223214571Sdim 224214571Sdimstatic void 225214571Sdimflushtape(void) 226214571Sdim{ 227214571Sdim int i, blks, got; 228214571Sdim int64_t lastfirstrec; 229214571Sdim 230214571Sdim int siz = (char *)nextblock - (char *)slp->req; 231214571Sdim 232214571Sdim slp->req[trecno].count = 0; /* Sentinel */ 233214571Sdim 234214571Sdim if (atomic(write, slp->fd, (char *)slp->req, siz) != siz) 235214571Sdim quit("error writing command pipe: %s\n", strerror(errno)); 236214571Sdim slp->sent = 1; /* we sent a request, read the response later */ 237214571Sdim 238214571Sdim lastfirstrec = slp->firstrec; 239214571Sdim 240214571Sdim if (++slp >= &slaves[SLAVES]) 241214571Sdim slp = &slaves[0]; 242214571Sdim 243214571Sdim /* Read results back from next slave */ 244214571Sdim if (slp->sent) { 245214571Sdim if (atomic(read, slp->fd, (char *)&got, sizeof got) 246214571Sdim != sizeof got) { 247214571Sdim perror(" DUMP: error reading command pipe in master"); 248214571Sdim dumpabort(0); 249214571Sdim } 250214571Sdim slp->sent = 0; 251214571Sdim 252214571Sdim /* Check for end of tape */ 253214571Sdim if (got < writesize) { 254214571Sdim msg("End of tape detected\n"); 255214571Sdim 256214571Sdim /* 257214571Sdim * Drain the results, don't care what the values were. 258214571Sdim * If we read them here then trewind won't... 259214571Sdim */ 260214571Sdim for (i = 0; i < SLAVES; i++) { 261214571Sdim if (slaves[i].sent) { 262214571Sdim if (atomic(read, slaves[i].fd, 263214571Sdim (char *)&got, sizeof got) 264214571Sdim != sizeof got) { 265214571Sdim perror(" DUMP: error reading command pipe in master"); 266214571Sdim dumpabort(0); 267214571Sdim } 268214571Sdim slaves[i].sent = 0; 269214571Sdim } 270214571Sdim } 271214571Sdim 272214571Sdim close_rewind(); 273214571Sdim rollforward(); 274214571Sdim return; 275214571Sdim } 276214571Sdim } 277214571Sdim 278214571Sdim blks = 0; 279214571Sdim if (spcl.c_type != TS_END) { 280214571Sdim for (i = 0; i < spcl.c_count; i++) 281214571Sdim if (spcl.c_addr[i] != 0) 282214571Sdim blks++; 283214571Sdim } 284214571Sdim slp->count = lastspclrec + blks + 1 - spcl.c_tapea; 285214571Sdim slp->tapea = spcl.c_tapea; 286214571Sdim slp->firstrec = lastfirstrec + ntrec; 287214571Sdim slp->inode = curino; 288214571Sdim nextblock = slp->tblock; 289214571Sdim trecno = 0; 290214571Sdim asize += tenths; 291214571Sdim blockswritten += ntrec; 292214571Sdim blocksthisvol += ntrec; 293214571Sdim if (!pipeout && !unlimited && (blocksperfile ? 294214571Sdim (blocksthisvol >= blocksperfile) : (asize > tsize))) { 295214571Sdim close_rewind(); 296214571Sdim startnewtape(0); 297214571Sdim } 298214571Sdim timeest(); 299214571Sdim} 300214571Sdim 301214571Sdimvoid 302214571Sdimtrewind(void) 303214571Sdim{ 304214571Sdim struct stat sb; 305214571Sdim int f; 306214571Sdim int got; 307214571Sdim 308214571Sdim for (f = 0; f < SLAVES; f++) { 309214571Sdim /* 310214571Sdim * Drain the results, but unlike EOT we DO (or should) care 311214571Sdim * what the return values were, since if we detect EOT after 312214571Sdim * we think we've written the last blocks to the tape anyway, 313214571Sdim * we have to replay those blocks with rollforward. 314214571Sdim * 315214571Sdim * fixme: punt for now. 316214571Sdim */ 317214571Sdim if (slaves[f].sent) { 318214571Sdim if (atomic(read, slaves[f].fd, (char *)&got, sizeof got) 319214571Sdim != sizeof got) { 320214571Sdim perror(" DUMP: error reading command pipe in master"); 321214571Sdim dumpabort(0); 322214571Sdim } 323214571Sdim slaves[f].sent = 0; 324214571Sdim if (got != writesize) { 325214571Sdim msg("EOT detected in last 2 tape records!\n"); 326214571Sdim msg("Use a longer tape, decrease the size estimate\n"); 327214571Sdim quit("or use no size estimate at all.\n"); 328214571Sdim } 329214571Sdim } 330214571Sdim (void) close(slaves[f].fd); 331214571Sdim } 332214571Sdim while (wait((int *)NULL) >= 0) /* wait for any signals from slaves */ 333214571Sdim /* void */; 334214571Sdim 335214571Sdim if (pipeout) 336214571Sdim return; 337214571Sdim 338214571Sdim msg("Closing %s\n", tape); 339214571Sdim 340214571Sdim if (popenout) { 341214571Sdim tapefd = -1; 342214571Sdim (void)pclose(popenfp); 343214571Sdim popenfp = NULL; 344214571Sdim return; 345214571Sdim } 346214571Sdim#ifdef RDUMP 347214571Sdim if (host) { 348214571Sdim rmtclose(); 349214571Sdim while (rmtopen(tape, 0) < 0) 350214571Sdim sleep(10); 351214571Sdim rmtclose(); 352214571Sdim return; 353214571Sdim } 354214571Sdim#endif 355214571Sdim if (fstat(tapefd, &sb) == 0 && S_ISFIFO(sb.st_mode)) { 356214571Sdim (void)close(tapefd); 357214571Sdim return; 358214571Sdim } 359214571Sdim (void) close(tapefd); 360214571Sdim while ((f = open(tape, 0)) < 0) 361214571Sdim sleep (10); 362214571Sdim (void) close(f); 363214571Sdim} 364214571Sdim 365214571Sdimvoid 366214571Sdimclose_rewind() 367214571Sdim{ 368214571Sdim time_t tstart_changevol, tend_changevol; 369214571Sdim 370214571Sdim trewind(); 371214571Sdim if (nexttape) 372214571Sdim return; 373214571Sdim (void)time((time_t *)&(tstart_changevol)); 374214571Sdim if (!nogripe) { 375214571Sdim msg("Change Volumes: Mount volume #%d\n", tapeno+1); 376214571Sdim broadcast("CHANGE DUMP VOLUMES!\a\a\n"); 377214571Sdim } 378214571Sdim while (!query("Is the new volume mounted and ready to go?")) 379214571Sdim if (query("Do you want to abort?")) { 380214571Sdim dumpabort(0); 381214571Sdim /*NOTREACHED*/ 382214571Sdim } 383214571Sdim (void)time((time_t *)&(tend_changevol)); 384214571Sdim if ((tstart_changevol != (time_t)-1) && (tend_changevol != (time_t)-1)) 385214571Sdim tstart_writing += (tend_changevol - tstart_changevol); 386214571Sdim} 387214571Sdim 388214571Sdimvoid 389214571Sdimrollforward(void) 390214571Sdim{ 391214571Sdim struct req *p, *q, *prev; 392214571Sdim struct slave *tslp; 393214571Sdim int i, size, got; 394214571Sdim int64_t savedtapea; 395214571Sdim union u_spcl *ntb, *otb; 396214571Sdim tslp = &slaves[SLAVES]; 397214571Sdim ntb = (union u_spcl *)tslp->tblock[1]; 398214571Sdim 399214571Sdim /* 400214571Sdim * Each of the N slaves should have requests that need to 401214571Sdim * be replayed on the next tape. Use the extra slave buffers 402214571Sdim * (slaves[SLAVES]) to construct request lists to be sent to 403214571Sdim * each slave in turn. 404214571Sdim */ 405214571Sdim for (i = 0; i < SLAVES; i++) { 406214571Sdim q = &tslp->req[1]; 407214571Sdim otb = (union u_spcl *)slp->tblock; 408214571Sdim 409214571Sdim /* 410214571Sdim * For each request in the current slave, copy it to tslp. 411214571Sdim */ 412214571Sdim 413214571Sdim prev = NULL; 414214571Sdim for (p = slp->req; p->count > 0; p += p->count) { 415214571Sdim *q = *p; 416214571Sdim if (p->dblk == 0) 417214571Sdim *ntb++ = *otb++; /* copy the datablock also */ 418214571Sdim prev = q; 419214571Sdim q += q->count; 420214571Sdim } 421214571Sdim if (prev == NULL) 422214571Sdim quit("rollforward: protocol botch"); 423214571Sdim if (prev->dblk != 0) 424214571Sdim prev->count -= 1; 425214571Sdim else 426214571Sdim ntb--; 427214571Sdim q -= 1; 428214571Sdim q->count = 0; 429214571Sdim q = &tslp->req[0]; 430214571Sdim if (i == 0) { 431214571Sdim q->dblk = 0; 432214571Sdim q->count = 1; 433214571Sdim trecno = 0; 434214571Sdim nextblock = tslp->tblock; 435214571Sdim savedtapea = spcl.c_tapea; 436214571Sdim spcl.c_tapea = slp->tapea; 437214571Sdim startnewtape(0); 438214571Sdim spcl.c_tapea = savedtapea; 439214571Sdim lastspclrec = savedtapea - 1; 440214571Sdim } 441214571Sdim size = (char *)ntb - (char *)q; 442214571Sdim if (atomic(write, slp->fd, (char *)q, size) != size) { 443214571Sdim perror(" DUMP: error writing command pipe"); 444214571Sdim dumpabort(0); 445214571Sdim } 446214571Sdim slp->sent = 1; 447214571Sdim if (++slp >= &slaves[SLAVES]) 448214571Sdim slp = &slaves[0]; 449214571Sdim 450214571Sdim q->count = 1; 451214571Sdim 452214571Sdim if (prev->dblk != 0) { 453214571Sdim /* 454214571Sdim * If the last one was a disk block, make the 455214571Sdim * first of this one be the last bit of that disk 456214571Sdim * block... 457214571Sdim */ 458214571Sdim q->dblk = prev->dblk + 459214571Sdim prev->count * (TP_BSIZE / DEV_BSIZE); 460214571Sdim ntb = (union u_spcl *)tslp->tblock; 461214571Sdim } else { 462214571Sdim /* 463214571Sdim * It wasn't a disk block. Copy the data to its 464214571Sdim * new location in the buffer. 465214571Sdim */ 466214571Sdim q->dblk = 0; 467214571Sdim *((union u_spcl *)tslp->tblock) = *ntb; 468214571Sdim ntb = (union u_spcl *)tslp->tblock[1]; 469214571Sdim } 470214571Sdim } 471214571Sdim slp->req[0] = *q; 472214571Sdim nextblock = slp->tblock; 473214571Sdim if (q->dblk == 0) 474214571Sdim nextblock++; 475214571Sdim trecno = 1; 476214571Sdim 477214571Sdim /* 478214571Sdim * Clear the first slaves' response. One hopes that it 479214571Sdim * worked ok, otherwise the tape is much too short! 480214571Sdim */ 481214571Sdim if (slp->sent) { 482214571Sdim if (atomic(read, slp->fd, (char *)&got, sizeof got) 483214571Sdim != sizeof got) { 484214571Sdim perror(" DUMP: error reading command pipe in master"); 485214571Sdim dumpabort(0); 486214571Sdim } 487214571Sdim slp->sent = 0; 488214571Sdim 489214571Sdim if (got != writesize) { 490214571Sdim quit("EOT detected at start of the tape!\n"); 491214571Sdim } 492214571Sdim } 493214571Sdim} 494214571Sdim 495214571Sdim/* 496214571Sdim * We implement taking and restoring checkpoints on the tape level. 497214571Sdim * When each tape is opened, a new process is created by forking; this 498214571Sdim * saves all of the necessary context in the parent. The child 499214571Sdim * continues the dump; the parent waits around, saving the context. 500214571Sdim * If the child returns X_REWRITE, then it had problems writing that tape; 501214571Sdim * this causes the parent to fork again, duplicating the context, and 502214571Sdim * everything continues as if nothing had happened. 503214571Sdim */ 504214571Sdimvoid 505214571Sdimstartnewtape(int top) 506214571Sdim{ 507214571Sdim int parentpid; 508214571Sdim int childpid; 509214571Sdim int status; 510214571Sdim char *p; 511214571Sdim sig_t interrupt_save; 512214571Sdim 513214571Sdim interrupt_save = signal(SIGINT, SIG_IGN); 514214571Sdim parentpid = getpid(); 515214571Sdim 516214571Sdimrestore_check_point: 517214571Sdim (void)signal(SIGINT, interrupt_save); 518214571Sdim /* 519214571Sdim * All signals are inherited... 520214571Sdim */ 521214571Sdim setproctitle(NULL); /* Restore the proctitle. */ 522214571Sdim childpid = fork(); 523214571Sdim if (childpid < 0) { 524214571Sdim msg("Context save fork fails in parent %d\n", parentpid); 525214571Sdim Exit(X_ABORT); 526214571Sdim } 527214571Sdim if (childpid != 0) { 528214571Sdim /* 529214571Sdim * PARENT: 530214571Sdim * save the context by waiting 531214571Sdim * until the child doing all of the work returns. 532214571Sdim * don't catch the interrupt 533214571Sdim */ 534214571Sdim signal(SIGINT, SIG_IGN); 535214571Sdim#ifdef TDEBUG 536214571Sdim msg("Tape: %d; parent process: %d child process %d\n", 537214571Sdim tapeno+1, parentpid, childpid); 538214571Sdim#endif /* TDEBUG */ 539214571Sdim if (waitpid(childpid, &status, 0) == -1) 540214571Sdim msg("Waiting for child %d: %s\n", childpid, 541214571Sdim strerror(errno)); 542214571Sdim if (status & 0xFF) { 543214571Sdim msg("Child %d returns LOB status %o\n", 544214571Sdim childpid, status&0xFF); 545214571Sdim } 546214571Sdim status = (status >> 8) & 0xFF; 547214571Sdim#ifdef TDEBUG 548214571Sdim switch(status) { 549214571Sdim case X_FINOK: 550214571Sdim msg("Child %d finishes X_FINOK\n", childpid); 551214571Sdim break; 552214571Sdim case X_ABORT: 553214571Sdim msg("Child %d finishes X_ABORT\n", childpid); 554214571Sdim break; 555214571Sdim case X_REWRITE: 556214571Sdim msg("Child %d finishes X_REWRITE\n", childpid); 557214571Sdim break; 558214571Sdim default: 559214571Sdim msg("Child %d finishes unknown %d\n", 560214571Sdim childpid, status); 561214571Sdim break; 562214571Sdim } 563214571Sdim#endif /* TDEBUG */ 564214571Sdim switch(status) { 565214571Sdim case X_FINOK: 566214571Sdim Exit(X_FINOK); 567214571Sdim case X_ABORT: 568214571Sdim Exit(X_ABORT); 569214571Sdim case X_REWRITE: 570214571Sdim goto restore_check_point; 571214571Sdim default: 572214571Sdim msg("Bad return code from dump: %d\n", status); 573214571Sdim Exit(X_ABORT); 574214571Sdim } 575214571Sdim /*NOTREACHED*/ 576214571Sdim } else { /* we are the child; just continue */ 577214571Sdim#ifdef TDEBUG 578214571Sdim sleep(4); /* allow time for parent's message to get out */ 579214571Sdim msg("Child on Tape %d has parent %d, my pid = %d\n", 580214571Sdim tapeno+1, parentpid, getpid()); 581214571Sdim#endif /* TDEBUG */ 582214571Sdim /* 583214571Sdim * If we have a name like "/dev/rmt0,/dev/rmt1", 584214571Sdim * use the name before the comma first, and save 585214571Sdim * the remaining names for subsequent volumes. 586214571Sdim */ 587214571Sdim tapeno++; /* current tape sequence */ 588214571Sdim if (nexttape || strchr(tape, ',')) { 589214571Sdim if (nexttape && *nexttape) 590214571Sdim tape = nexttape; 591214571Sdim if ((p = strchr(tape, ',')) != NULL) { 592214571Sdim *p = '\0'; 593214571Sdim nexttape = p + 1; 594214571Sdim } else 595214571Sdim nexttape = NULL; 596214571Sdim msg("Dumping volume %d on %s\n", tapeno, tape); 597214571Sdim } 598214571Sdim if (pipeout) { 599214571Sdim tapefd = STDOUT_FILENO; 600214571Sdim } else if (popenout) { 601214571Sdim char volno[sizeof("2147483647")]; 602214571Sdim 603214571Sdim (void)sprintf(volno, "%d", spcl.c_volume + 1); 604214571Sdim if (setenv("DUMP_VOLUME", volno, 1) == -1) { 605214571Sdim msg("Cannot set $DUMP_VOLUME.\n"); 606214571Sdim dumpabort(0); 607214571Sdim } 608214571Sdim popenfp = popen(popenout, "w"); 609214571Sdim if (popenfp == NULL) { 610214571Sdim msg("Cannot open output pipeline \"%s\".\n", 611214571Sdim popenout); 612214571Sdim dumpabort(0); 613214571Sdim } 614214571Sdim tapefd = fileno(popenfp); 615214571Sdim } else { 616214571Sdim#ifdef RDUMP 617214571Sdim while ((tapefd = (host ? rmtopen(tape, 2) : 618214571Sdim open(tape, O_WRONLY|O_CREAT, 0666))) < 0) 619214571Sdim#else 620214571Sdim while ((tapefd = 621214571Sdim open(tape, O_WRONLY|O_CREAT, 0666)) < 0) 622214571Sdim#endif 623214571Sdim { 624214571Sdim msg("Cannot open output \"%s\".\n", tape); 625214571Sdim if (!query("Do you want to retry the open?")) 626214571Sdim dumpabort(0); 627214571Sdim } 628214571Sdim } 629214571Sdim 630214571Sdim enslave(); /* Share open tape file descriptor with slaves */ 631214571Sdim if (popenout) 632214571Sdim close(tapefd); /* Give up our copy of it. */ 633214571Sdim signal(SIGINFO, infosch); 634214571Sdim 635214571Sdim asize = 0; 636214571Sdim blocksthisvol = 0; 637214571Sdim if (top) 638214571Sdim newtape++; /* new tape signal */ 639214571Sdim spcl.c_count = slp->count; 640214571Sdim /* 641214571Sdim * measure firstrec in TP_BSIZE units since restore doesn't 642214571Sdim * know the correct ntrec value... 643214571Sdim */ 644214571Sdim spcl.c_firstrec = slp->firstrec; 645214571Sdim spcl.c_volume++; 646214571Sdim spcl.c_type = TS_TAPE; 647214571Sdim writeheader((ino_t)slp->inode); 648214571Sdim if (tapeno > 1) 649214571Sdim msg("Volume %d begins with blocks from inode %d\n", 650214571Sdim tapeno, slp->inode); 651214571Sdim } 652214571Sdim} 653214571Sdim 654214571Sdimvoid 655214571Sdimdumpabort(int signo __unused) 656214571Sdim{ 657214571Sdim 658214571Sdim if (master != 0 && master != getpid()) 659214571Sdim /* Signals master to call dumpabort */ 660214571Sdim (void) kill(master, SIGTERM); 661214571Sdim else { 662214571Sdim killall(); 663214571Sdim msg("The ENTIRE dump is aborted.\n"); 664214571Sdim } 665214571Sdim#ifdef RDUMP 666214571Sdim rmtclose(); 667214571Sdim#endif 668214571Sdim Exit(X_ABORT); 669214571Sdim} 670214571Sdim 671214571Sdimvoid 672214571SdimExit(status) 673214571Sdim int status; 674214571Sdim{ 675214571Sdim 676214571Sdim#ifdef TDEBUG 677214571Sdim msg("pid = %d exits with status %d\n", getpid(), status); 678214571Sdim#endif /* TDEBUG */ 679214571Sdim exit(status); 680214571Sdim} 681214571Sdim 682214571Sdim/* 683214571Sdim * proceed - handler for SIGUSR2, used to synchronize IO between the slaves. 684214571Sdim */ 685214571Sdimvoid 686214571Sdimproceed(int signo __unused) 687214571Sdim{ 688214571Sdim 689214571Sdim if (ready) 690214571Sdim longjmp(jmpbuf, 1); 691214571Sdim caught++; 692214571Sdim} 693214571Sdim 694214571Sdimvoid 695214571Sdimenslave(void) 696214571Sdim{ 697214571Sdim int cmd[2]; 698214571Sdim int i, j; 699214571Sdim 700214571Sdim master = getpid(); 701214571Sdim 702214571Sdim signal(SIGTERM, dumpabort); /* Slave sends SIGTERM on dumpabort() */ 703214571Sdim signal(SIGPIPE, sigpipe); 704214571Sdim signal(SIGUSR1, tperror); /* Slave sends SIGUSR1 on tape errors */ 705214571Sdim signal(SIGUSR2, proceed); /* Slave sends SIGUSR2 to next slave */ 706214571Sdim 707214571Sdim for (i = 0; i < SLAVES; i++) { 708214571Sdim if (i == slp - &slaves[0]) { 709214571Sdim caught = 1; 710214571Sdim } else { 711214571Sdim caught = 0; 712214571Sdim } 713214571Sdim 714214571Sdim if (socketpair(AF_UNIX, SOCK_STREAM, 0, cmd) < 0 || 715214571Sdim (slaves[i].pid = fork()) < 0) 716214571Sdim quit("too many slaves, %d (recompile smaller): %s\n", 717214571Sdim i, strerror(errno)); 718214571Sdim 719214571Sdim slaves[i].fd = cmd[1]; 720214571Sdim slaves[i].sent = 0; 721214571Sdim if (slaves[i].pid == 0) { /* Slave starts up here */ 722214571Sdim for (j = 0; j <= i; j++) 723214571Sdim (void) close(slaves[j].fd); 724214571Sdim signal(SIGINT, SIG_IGN); /* Master handles this */ 725214571Sdim doslave(cmd[0], i); 726214571Sdim Exit(X_FINOK); 727214571Sdim } 728214571Sdim } 729214571Sdim 730214571Sdim for (i = 0; i < SLAVES; i++) 731214571Sdim (void) atomic(write, slaves[i].fd, 732214571Sdim (char *) &slaves[(i + 1) % SLAVES].pid, 733214571Sdim sizeof slaves[0].pid); 734214571Sdim 735214571Sdim master = 0; 736214571Sdim} 737214571Sdim 738214571Sdimvoid 739214571Sdimkillall(void) 740214571Sdim{ 741214571Sdim int i; 742214571Sdim 743214571Sdim for (i = 0; i < SLAVES; i++) 744214571Sdim if (slaves[i].pid > 0) { 745214571Sdim (void) kill(slaves[i].pid, SIGKILL); 746214571Sdim slaves[i].sent = 0; 747214571Sdim } 748214571Sdim} 749214571Sdim 750214571Sdim/* 751214571Sdim * Synchronization - each process has a lockfile, and shares file 752214571Sdim * descriptors to the following process's lockfile. When our write 753214571Sdim * completes, we release our lock on the following process's lock- 754214571Sdim * file, allowing the following process to lock it and proceed. We 755214571Sdim * get the lock back for the next cycle by swapping descriptors. 756214571Sdim */ 757214571Sdimstatic void 758214571Sdimdoslave(int cmd, int slave_number) 759214571Sdim{ 760214571Sdim int nread; 761214571Sdim int nextslave, size, wrote, eot_count; 762214571Sdim 763214571Sdim /* 764214571Sdim * Need our own seek pointer. 765214571Sdim */ 766214571Sdim (void) close(diskfd); 767214571Sdim if ((diskfd = open(disk, O_RDONLY)) < 0) 768214571Sdim quit("slave couldn't reopen disk: %s\n", strerror(errno)); 769214571Sdim 770214571Sdim /* 771214571Sdim * Need the pid of the next slave in the loop... 772214571Sdim */ 773214571Sdim if ((nread = atomic(read, cmd, (char *)&nextslave, sizeof nextslave)) 774214571Sdim != sizeof nextslave) { 775214571Sdim quit("master/slave protocol botched - didn't get pid of next slave.\n"); 776214571Sdim } 777214571Sdim 778214571Sdim /* 779214571Sdim * Get list of blocks to dump, read the blocks into tape buffer 780214571Sdim */ 781214571Sdim while ((nread = atomic(read, cmd, (char *)slp->req, reqsiz)) == reqsiz) { 782214571Sdim struct req *p = slp->req; 783214571Sdim 784214571Sdim for (trecno = 0; trecno < ntrec; 785214571Sdim trecno += p->count, p += p->count) { 786214571Sdim if (p->dblk) { 787214571Sdim bread(p->dblk, slp->tblock[trecno], 788214571Sdim p->count * TP_BSIZE); 789214571Sdim } else { 790214571Sdim if (p->count != 1 || atomic(read, cmd, 791214571Sdim (char *)slp->tblock[trecno], 792214571Sdim TP_BSIZE) != TP_BSIZE) 793214571Sdim quit("master/slave protocol botched.\n"); 794214571Sdim } 795214571Sdim } 796214571Sdim if (setjmp(jmpbuf) == 0) { 797214571Sdim ready = 1; 798214571Sdim if (!caught) 799214571Sdim (void) pause(); 800214571Sdim } 801214571Sdim ready = 0; 802214571Sdim caught = 0; 803214571Sdim 804214571Sdim /* Try to write the data... */ 805214571Sdim eot_count = 0; 806214571Sdim size = 0; 807214571Sdim 808214571Sdim wrote = 0; 809214571Sdim while (eot_count < 10 && size < writesize) { 810214571Sdim#ifdef RDUMP 811214571Sdim if (host) 812214571Sdim wrote = rmtwrite(slp->tblock[0]+size, 813214571Sdim writesize-size); 814214571Sdim else 815214571Sdim#endif 816214571Sdim wrote = write(tapefd, slp->tblock[0]+size, 817214571Sdim writesize-size); 818214571Sdim#ifdef WRITEDEBUG 819214571Sdim printf("slave %d wrote %d\n", slave_number, wrote); 820214571Sdim#endif 821214571Sdim if (wrote < 0) 822214571Sdim break; 823214571Sdim if (wrote == 0) 824214571Sdim eot_count++; 825214571Sdim size += wrote; 826214571Sdim } 827214571Sdim 828214571Sdim#ifdef WRITEDEBUG 829214571Sdim if (size != writesize) 830214571Sdim printf("slave %d only wrote %d out of %d bytes and gave up.\n", 831214571Sdim slave_number, size, writesize); 832214571Sdim#endif 833214571Sdim 834214571Sdim /* 835214571Sdim * Handle ENOSPC as an EOT condition. 836214571Sdim */ 837214571Sdim if (wrote < 0 && errno == ENOSPC) { 838214571Sdim wrote = 0; 839214571Sdim eot_count++; 840214571Sdim } 841214571Sdim 842214571Sdim if (eot_count > 0) 843214571Sdim size = 0; 844214571Sdim 845214571Sdim if (wrote < 0) { 846214571Sdim (void) kill(master, SIGUSR1); 847214571Sdim for (;;) 848214571Sdim (void) sigpause(0); 849214571Sdim } else { 850214571Sdim /* 851214571Sdim * pass size of write back to master 852214571Sdim * (for EOT handling) 853214571Sdim */ 854214571Sdim (void) atomic(write, cmd, (char *)&size, sizeof size); 855214571Sdim } 856214571Sdim 857214571Sdim /* 858214571Sdim * If partial write, don't want next slave to go. 859214571Sdim * Also jolts him awake. 860214571Sdim */ 861214571Sdim (void) kill(nextslave, SIGUSR2); 862214571Sdim } 863214571Sdim if (nread != 0) 864214571Sdim quit("error reading command pipe: %s\n", strerror(errno)); 865214571Sdim} 866214571Sdim 867214571Sdim/* 868214571Sdim * Since a read from a pipe may not return all we asked for, 869214571Sdim * or a write may not write all we ask if we get a signal, 870214571Sdim * loop until the count is satisfied (or error). 871214571Sdim */ 872214571Sdimstatic int 873214571Sdimatomic(ssize_t (*func)(), int fd, char *buf, int count) 874214571Sdim{ 875214571Sdim int got, need = count; 876214571Sdim 877214571Sdim while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0) 878214571Sdim buf += got; 879214571Sdim return (got < 0 ? got : count - need); 880214571Sdim} 881214571Sdim