|
|
1.1 ! root 1: /* ! 2: * Copyright (c) 1980 Regents of the University of California. ! 3: * All rights reserved. The Berkeley software License Agreement ! 4: * specifies the terms and conditions for redistribution. ! 5: */ ! 6: ! 7: #ifndef lint ! 8: static char sccsid[] = "@(#)dumptape.c 5.10 (Berkeley) 9/15/89"; ! 9: #endif not lint ! 10: ! 11: #include <sys/file.h> ! 12: #include "dump.h" ! 13: #include "pathnames.h" ! 14: ! 15: char (*tblock)[TP_BSIZE]; /* pointer to malloc()ed buffer for tape */ ! 16: int writesize; /* size of malloc()ed buffer for tape */ ! 17: long lastspclrec = -1; /* tape block number of last written header */ ! 18: int trecno = 0; /* next record to write in current block */ ! 19: extern int ntrec; /* blocking factor on tape */ ! 20: extern int cartridge; ! 21: extern int read(), write(); ! 22: #ifdef RDUMP ! 23: extern char *host; ! 24: #endif RDUMP ! 25: ! 26: /* ! 27: * Concurrent dump mods (Caltech) - disk block reading and tape writing ! 28: * are exported to several slave processes. While one slave writes the ! 29: * tape, the others read disk blocks; they pass control of the tape in ! 30: * a ring via flock(). The parent process traverses the filesystem and ! 31: * sends spclrec()'s and lists of daddr's to the slaves via pipes. ! 32: */ ! 33: struct req { /* instruction packets sent to slaves */ ! 34: daddr_t dblk; ! 35: int count; ! 36: } *req; ! 37: int reqsiz; ! 38: ! 39: #define SLAVES 3 /* 1 slave writing, 1 reading, 1 for slack */ ! 40: int slavefd[SLAVES]; /* pipes from master to each slave */ ! 41: int slavepid[SLAVES]; /* used by killall() */ ! 42: int rotor; /* next slave to be instructed */ ! 43: int master; /* pid of master, for sending error signals */ ! 44: int tenths; /* length of tape used per block written */ ! 45: ! 46: alloctape() ! 47: { ! 48: int pgoff = getpagesize() - 1; ! 49: ! 50: writesize = ntrec * TP_BSIZE; ! 51: reqsiz = ntrec * sizeof(struct req); ! 52: /* ! 53: * CDC 92181's and 92185's make 0.8" gaps in 1600-bpi start/stop mode ! 54: * (see DEC TU80 User's Guide). The shorter gaps of 6250-bpi require ! 55: * repositioning after stopping, i.e, streaming mode, where the gap is ! 56: * variable, 0.30" to 0.45". The gap is maximal when the tape stops. ! 57: */ ! 58: tenths = writesize/density + (cartridge ? 16 : density == 625 ? 5 : 8); ! 59: /* ! 60: * Allocate tape buffer contiguous with the array of instruction ! 61: * packets, so flusht() can write them together with one write(). ! 62: * Align tape buffer on page boundary to speed up tape write(). ! 63: */ ! 64: req = (struct req *)malloc(reqsiz + writesize + pgoff); ! 65: if (req == NULL) ! 66: return(0); ! 67: tblock = (char (*)[TP_BSIZE]) (((long)&req[ntrec] + pgoff) &~ pgoff); ! 68: req = (struct req *)tblock - ntrec; ! 69: return(1); ! 70: } ! 71: ! 72: ! 73: taprec(dp) ! 74: char *dp; ! 75: { ! 76: req[trecno].dblk = (daddr_t)0; ! 77: req[trecno].count = 1; ! 78: *(union u_spcl *)(*tblock++) = *(union u_spcl *)dp; /* movc3 */ ! 79: lastspclrec = spcl.c_tapea; ! 80: trecno++; ! 81: spcl.c_tapea++; ! 82: if(trecno >= ntrec) ! 83: flusht(); ! 84: } ! 85: ! 86: dmpblk(blkno, size) ! 87: daddr_t blkno; ! 88: int size; ! 89: { ! 90: int avail, tpblks, dblkno; ! 91: ! 92: dblkno = fsbtodb(sblock, blkno); ! 93: tpblks = size / TP_BSIZE; ! 94: while ((avail = MIN(tpblks, ntrec - trecno)) > 0) { ! 95: req[trecno].dblk = dblkno; ! 96: req[trecno].count = avail; ! 97: trecno += avail; ! 98: spcl.c_tapea += avail; ! 99: if (trecno >= ntrec) ! 100: flusht(); ! 101: dblkno += avail * (TP_BSIZE / dev_bsize); ! 102: tpblks -= avail; ! 103: } ! 104: } ! 105: ! 106: int nogripe = 0; ! 107: ! 108: tperror() { ! 109: if (pipeout) { ! 110: msg("Tape write error on %s\n", tape); ! 111: msg("Cannot recover\n"); ! 112: dumpabort(); ! 113: /* NOTREACHED */ ! 114: } ! 115: msg("Tape write error %d feet into tape %d\n", asize/120L, tapeno); ! 116: broadcast("TAPE ERROR!\n"); ! 117: if (!query("Do you want to restart?")) ! 118: dumpabort(); ! 119: msg("This tape will rewind. After it is rewound,\n"); ! 120: msg("replace the faulty tape with a new one;\n"); ! 121: msg("this dump volume will be rewritten.\n"); ! 122: killall(); ! 123: nogripe = 1; ! 124: close_rewind(); ! 125: Exit(X_REWRITE); ! 126: } ! 127: ! 128: sigpipe() ! 129: { ! 130: ! 131: msg("Broken pipe\n"); ! 132: dumpabort(); ! 133: } ! 134: ! 135: #ifdef RDUMP ! 136: /* ! 137: * compatibility routine ! 138: */ ! 139: tflush(i) ! 140: int i; ! 141: { ! 142: ! 143: for (i = 0; i < ntrec; i++) ! 144: spclrec(); ! 145: } ! 146: #endif RDUMP ! 147: ! 148: flusht() ! 149: { ! 150: int siz = (char *)tblock - (char *)req; ! 151: ! 152: if (atomic(write, slavefd[rotor], req, siz) != siz) { ! 153: perror(" DUMP: error writing command pipe"); ! 154: dumpabort(); ! 155: } ! 156: if (++rotor >= SLAVES) rotor = 0; ! 157: tblock = (char (*)[TP_BSIZE]) &req[ntrec]; ! 158: trecno = 0; ! 159: asize += tenths; ! 160: blockswritten += ntrec; ! 161: if (!pipeout && asize > tsize) { ! 162: close_rewind(); ! 163: otape(); ! 164: } ! 165: timeest(); ! 166: } ! 167: ! 168: rewind() ! 169: { ! 170: int f; ! 171: ! 172: if (pipeout) ! 173: return; ! 174: for (f = 0; f < SLAVES; f++) ! 175: close(slavefd[f]); ! 176: while (wait(NULL) >= 0) ; /* wait for any signals from slaves */ ! 177: msg("Tape rewinding\n"); ! 178: #ifdef RDUMP ! 179: if (host) { ! 180: rmtclose(); ! 181: while (rmtopen(tape, 0) < 0) ! 182: sleep(10); ! 183: rmtclose(); ! 184: return; ! 185: } ! 186: #endif RDUMP ! 187: close(to); ! 188: while ((f = open(tape, 0)) < 0) ! 189: sleep (10); ! 190: close(f); ! 191: } ! 192: ! 193: close_rewind() ! 194: { ! 195: rewind(); ! 196: if (!nogripe) { ! 197: msg("Change Tapes: Mount tape #%d\n", tapeno+1); ! 198: broadcast("CHANGE TAPES!\7\7\n"); ! 199: } ! 200: while (!query("Is the new tape mounted and ready to go?")) ! 201: if (query("Do you want to abort?")) { ! 202: dumpabort(); ! 203: /*NOTREACHED*/ ! 204: } ! 205: } ! 206: ! 207: /* ! 208: * We implement taking and restoring checkpoints on the tape level. ! 209: * When each tape is opened, a new process is created by forking; this ! 210: * saves all of the necessary context in the parent. The child ! 211: * continues the dump; the parent waits around, saving the context. ! 212: * If the child returns X_REWRITE, then it had problems writing that tape; ! 213: * this causes the parent to fork again, duplicating the context, and ! 214: * everything continues as if nothing had happened. ! 215: */ ! 216: ! 217: otape() ! 218: { ! 219: int parentpid; ! 220: int childpid; ! 221: int status; ! 222: int waitpid; ! 223: sig_t interrupt; ! 224: int blks, i; ! 225: ! 226: interrupt = signal(SIGINT, SIG_IGN); ! 227: parentpid = getpid(); ! 228: ! 229: restore_check_point: ! 230: (void)signal(SIGINT, interrupt); ! 231: /* ! 232: * All signals are inherited... ! 233: */ ! 234: childpid = fork(); ! 235: if (childpid < 0) { ! 236: msg("Context save fork fails in parent %d\n", parentpid); ! 237: Exit(X_ABORT); ! 238: } ! 239: if (childpid != 0) { ! 240: /* ! 241: * PARENT: ! 242: * save the context by waiting ! 243: * until the child doing all of the work returns. ! 244: * don't catch the interrupt ! 245: */ ! 246: signal(SIGINT, SIG_IGN); ! 247: #ifdef TDEBUG ! 248: msg("Tape: %d; parent process: %d child process %d\n", ! 249: tapeno+1, parentpid, childpid); ! 250: #endif TDEBUG ! 251: while ((waitpid = wait(&status)) != childpid) ! 252: msg("Parent %d waiting for child %d has another child %d return\n", ! 253: parentpid, childpid, waitpid); ! 254: if (status & 0xFF) { ! 255: msg("Child %d returns LOB status %o\n", ! 256: childpid, status&0xFF); ! 257: } ! 258: status = (status >> 8) & 0xFF; ! 259: #ifdef TDEBUG ! 260: switch(status) { ! 261: case X_FINOK: ! 262: msg("Child %d finishes X_FINOK\n", childpid); ! 263: break; ! 264: case X_ABORT: ! 265: msg("Child %d finishes X_ABORT\n", childpid); ! 266: break; ! 267: case X_REWRITE: ! 268: msg("Child %d finishes X_REWRITE\n", childpid); ! 269: break; ! 270: default: ! 271: msg("Child %d finishes unknown %d\n", ! 272: childpid, status); ! 273: break; ! 274: } ! 275: #endif TDEBUG ! 276: switch(status) { ! 277: case X_FINOK: ! 278: Exit(X_FINOK); ! 279: case X_ABORT: ! 280: Exit(X_ABORT); ! 281: case X_REWRITE: ! 282: goto restore_check_point; ! 283: default: ! 284: msg("Bad return code from dump: %d\n", status); ! 285: Exit(X_ABORT); ! 286: } ! 287: /*NOTREACHED*/ ! 288: } else { /* we are the child; just continue */ ! 289: #ifdef TDEBUG ! 290: sleep(4); /* allow time for parent's message to get out */ ! 291: msg("Child on Tape %d has parent %d, my pid = %d\n", ! 292: tapeno+1, parentpid, getpid()); ! 293: #endif TDEBUG ! 294: #ifdef RDUMP ! 295: while ((to = (host ? rmtopen(tape, 2) : ! 296: pipeout ? 1 : creat(tape, 0666))) < 0) ! 297: #else RDUMP ! 298: while ((to = pipeout ? 1 : creat(tape, 0666)) < 0) ! 299: #endif RDUMP ! 300: { ! 301: msg("Cannot open tape \"%s\".\n", tape); ! 302: if (!query("Do you want to retry the open?")) ! 303: dumpabort(); ! 304: } ! 305: ! 306: enslave(); /* Share open tape file descriptor with slaves */ ! 307: ! 308: asize = 0; ! 309: tapeno++; /* current tape sequence */ ! 310: newtape++; /* new tape signal */ ! 311: blks = 0; ! 312: if (spcl.c_type != TS_END) ! 313: for (i = 0; i < spcl.c_count; i++) ! 314: if (spcl.c_addr[i] != 0) ! 315: blks++; ! 316: spcl.c_count = blks + 1 - spcl.c_tapea + lastspclrec; ! 317: spcl.c_volume++; ! 318: spcl.c_type = TS_TAPE; ! 319: spcl.c_flags |= DR_NEWHEADER; ! 320: spclrec(); ! 321: spcl.c_flags &=~ DR_NEWHEADER; ! 322: if (tapeno > 1) ! 323: msg("Tape %d begins with blocks from ino %d\n", ! 324: tapeno, ino); ! 325: } ! 326: } ! 327: ! 328: dumpabort() ! 329: { ! 330: if (master != 0 && master != getpid()) ! 331: kill(master, SIGTERM); /* Signals master to call dumpabort */ ! 332: else { ! 333: killall(); ! 334: msg("The ENTIRE dump is aborted.\n"); ! 335: } ! 336: Exit(X_ABORT); ! 337: } ! 338: ! 339: Exit(status) ! 340: { ! 341: #ifdef TDEBUG ! 342: msg("pid = %d exits with status %d\n", getpid(), status); ! 343: #endif TDEBUG ! 344: exit(status); ! 345: } ! 346: ! 347: /* ! 348: * could use pipe() for this if flock() worked on pipes ! 349: */ ! 350: lockfile(fd) ! 351: int fd[2]; ! 352: { ! 353: char tmpname[20]; ! 354: ! 355: strcpy(tmpname, _PATH_LOCK); ! 356: mktemp(tmpname); ! 357: if ((fd[1] = creat(tmpname, 0400)) < 0) { ! 358: msg("Could not create lockfile "); ! 359: perror(tmpname); ! 360: dumpabort(); ! 361: } ! 362: if ((fd[0] = open(tmpname, 0)) < 0) { ! 363: msg("Could not reopen lockfile "); ! 364: perror(tmpname); ! 365: dumpabort(); ! 366: } ! 367: unlink(tmpname); ! 368: } ! 369: ! 370: enslave() ! 371: { ! 372: int first[2], prev[2], next[2], cmd[2]; /* file descriptors */ ! 373: register int i, j; ! 374: ! 375: master = getpid(); ! 376: signal(SIGTERM, dumpabort); /* Slave sends SIGTERM on dumpabort() */ ! 377: signal(SIGPIPE, sigpipe); ! 378: signal(SIGUSR1, tperror); /* Slave sends SIGUSR1 on tape errors */ ! 379: lockfile(first); ! 380: for (i = 0; i < SLAVES; i++) { ! 381: if (i == 0) { ! 382: prev[0] = first[1]; ! 383: prev[1] = first[0]; ! 384: } else { ! 385: prev[0] = next[0]; ! 386: prev[1] = next[1]; ! 387: flock(prev[1], LOCK_EX); ! 388: } ! 389: if (i < SLAVES - 1) { ! 390: lockfile(next); ! 391: } else { ! 392: next[0] = first[0]; ! 393: next[1] = first[1]; /* Last slave loops back */ ! 394: } ! 395: if (pipe(cmd) < 0 || (slavepid[i] = fork()) < 0) { ! 396: msg("too many slaves, %d (recompile smaller) ", i); ! 397: perror(""); ! 398: dumpabort(); ! 399: } ! 400: slavefd[i] = cmd[1]; ! 401: if (slavepid[i] == 0) { /* Slave starts up here */ ! 402: for (j = 0; j <= i; j++) ! 403: close(slavefd[j]); ! 404: signal(SIGINT, SIG_IGN); /* Master handles this */ ! 405: doslave(cmd[0], prev, next); ! 406: Exit(X_FINOK); ! 407: } ! 408: close(cmd[0]); ! 409: if (i > 0) { ! 410: close(prev[0]); ! 411: close(prev[1]); ! 412: } ! 413: } ! 414: close(first[0]); ! 415: close(first[1]); ! 416: master = 0; rotor = 0; ! 417: } ! 418: ! 419: killall() ! 420: { ! 421: register int i; ! 422: ! 423: for (i = 0; i < SLAVES; i++) ! 424: if (slavepid[i] > 0) ! 425: kill(slavepid[i], SIGKILL); ! 426: } ! 427: ! 428: /* ! 429: * Synchronization - each process has a lockfile, and shares file ! 430: * descriptors to the following process's lockfile. When our write ! 431: * completes, we release our lock on the following process's lock- ! 432: * file, allowing the following process to lock it and proceed. We ! 433: * get the lock back for the next cycle by swapping descriptors. ! 434: */ ! 435: doslave(cmd, prev, next) ! 436: register int cmd, prev[2], next[2]; ! 437: { ! 438: register int nread, toggle = 0; ! 439: ! 440: close(fi); ! 441: if ((fi = open(disk, 0)) < 0) { /* Need our own seek pointer */ ! 442: perror(" DUMP: slave couldn't reopen disk"); ! 443: dumpabort(); ! 444: } ! 445: /* ! 446: * Get list of blocks to dump, read the blocks into tape buffer ! 447: */ ! 448: while ((nread = atomic(read, cmd, req, reqsiz)) == reqsiz) { ! 449: register struct req *p = req; ! 450: for (trecno = 0; trecno < ntrec; trecno += p->count, p += p->count) { ! 451: if (p->dblk) { ! 452: bread(p->dblk, tblock[trecno], ! 453: p->count * TP_BSIZE); ! 454: } else { ! 455: if (p->count != 1 || atomic(read, cmd, ! 456: tblock[trecno], TP_BSIZE) != TP_BSIZE) { ! 457: msg("Master/slave protocol botched.\n"); ! 458: dumpabort(); ! 459: } ! 460: } ! 461: } ! 462: flock(prev[toggle], LOCK_EX); /* Wait our turn */ ! 463: ! 464: #ifdef RDUMP ! 465: if ((host ? rmtwrite(tblock[0], writesize) ! 466: : write(to, tblock[0], writesize)) != writesize) { ! 467: #else RDUMP ! 468: if (write(to, tblock[0], writesize) != writesize) { ! 469: #endif RDUMP ! 470: kill(master, SIGUSR1); ! 471: for (;;) ! 472: sigpause(0); ! 473: } ! 474: toggle ^= 1; ! 475: flock(next[toggle], LOCK_UN); /* Next slave's turn */ ! 476: } /* Also jolts him awake */ ! 477: if (nread != 0) { ! 478: perror(" DUMP: error reading command pipe"); ! 479: dumpabort(); ! 480: } ! 481: } ! 482: ! 483: /* ! 484: * Since a read from a pipe may not return all we asked for, ! 485: * or a write may not write all we ask if we get a signal, ! 486: * loop until the count is satisfied (or error). ! 487: */ ! 488: atomic(func, fd, buf, count) ! 489: int (*func)(), fd, count; ! 490: char *buf; ! 491: { ! 492: int got, need = count; ! 493: ! 494: while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0) ! 495: buf += got; ! 496: return (got < 0 ? got : count - need); ! 497: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.