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