📄 tape.c
字号:
startnewtape(0); spcl.c_tapea = savedtapea; lastspclrec = savedtapea - 1; } size = (char *)ntb - (char *)q; if (atomic(write, slp->fd, (char *)q, size) != size) { perror(" DUMP: error writing command pipe"); dumpabort(0); } slp->sent = 1; if (++slp >= &slaves[SLAVES]) slp = &slaves[0]; q->count = 1; if (prev->dblk != 0) { /* * If the last one was a disk block, make the * first of this one be the last bit of that disk * block... */ q->dblk = prev->dblk + prev->count * (TP_BSIZE / DEV_BSIZE); ntb = (union u_spcl *)tslp->tblock; } else { /* * It wasn't a disk block. Copy the data to its * new location in the buffer. */ q->dblk = 0; *((union u_spcl *)tslp->tblock) = *ntb; ntb = (union u_spcl *)tslp->tblock[1]; } } slp->req[0] = *q; nextblock = slp->tblock; if (q->dblk == 0) nextblock++; trecno = 1; /* * Clear the first slaves' response. One hopes that it * worked ok, otherwise the tape is much too short! */ if (slp->sent) { if (atomic(read, slp->fd, (char *)&got, sizeof got) != sizeof got) { perror(" DUMP: error reading command pipe in master"); dumpabort(0); } slp->sent = 0; if (got != writesize) { quit("EOT detected at start of the tape!\n"); } }}/* * We implement taking and restoring checkpoints on the tape level. * When each tape is opened, a new process is created by forking; this * saves all of the necessary context in the parent. The child * continues the dump; the parent waits around, saving the context. * If the child returns X_REWRITE, then it had problems writing that tape; * this causes the parent to fork again, duplicating the context, and * everything continues as if nothing had happened. */voidstartnewtape(top) int top;{ int parentpid; int childpid; int status; int waitpid; char *p;#ifdef sunos void (*interrupt_save)();#else sig_t interrupt_save;#endif interrupt_save = signal(SIGINT, SIG_IGN); parentpid = getpid();restore_check_point: (void)signal(SIGINT, interrupt_save); /* * All signals are inherited... */ childpid = fork(); if (childpid < 0) { msg("Context save fork fails in parent %d\n", parentpid); Exit(X_ABORT); } if (childpid != 0) { /* * PARENT: * save the context by waiting * until the child doing all of the work returns. * don't catch the interrupt */ signal(SIGINT, SIG_IGN);#ifdef TDEBUG msg("Tape: %d; parent process: %d child process %d\n", tapeno+1, parentpid, childpid);#endif /* TDEBUG */ while ((waitpid = wait(&status)) != childpid) msg("Parent %d waiting for child %d has another child %d return\n", parentpid, childpid, waitpid); if (status & 0xFF) { msg("Child %d returns LOB status %o\n", childpid, status&0xFF); } status = (status >> 8) & 0xFF;#ifdef TDEBUG switch(status) { case X_FINOK: msg("Child %d finishes X_FINOK\n", childpid); break; case X_ABORT: msg("Child %d finishes X_ABORT\n", childpid); break; case X_REWRITE: msg("Child %d finishes X_REWRITE\n", childpid); break; default: msg("Child %d finishes unknown %d\n", childpid, status); break; }#endif /* TDEBUG */ switch(status) { case X_FINOK: Exit(X_FINOK); case X_ABORT: Exit(X_ABORT); case X_REWRITE: goto restore_check_point; default: msg("Bad return code from dump: %d\n", status); Exit(X_ABORT); } /*NOTREACHED*/ } else { /* we are the child; just continue */#ifdef TDEBUG sleep(4); /* allow time for parent's message to get out */ msg("Child on Tape %d has parent %d, my pid = %d\n", tapeno+1, parentpid, getpid());#endif /* TDEBUG */ /* * If we have a name like "/dev/rmt0,/dev/rmt1", * use the name before the comma first, and save * the remaining names for subsequent volumes. */ tapeno++; /* current tape sequence */ if (nexttape || index(tape, ',')) { if (nexttape && *nexttape) tape = nexttape; if ((p = index(tape, ',')) != NULL) { *p = '\0'; nexttape = p + 1; } else nexttape = NULL; msg("Dumping volume %d on %s\n", tapeno, tape); }#ifdef RDUMP while ((tapefd = (host ? rmtopen(tape, 2) : pipeout ? 1 : open(tape, O_WRONLY|O_CREAT, 0666))) < 0)#else while ((tapefd = (pipeout ? 1 : open(tape, O_WRONLY|O_CREAT, 0666))) < 0)#endif { msg("Cannot open output \"%s\".\n", tape); if (!query("Do you want to retry the open?")) dumpabort(0); } enslave(); /* Share open tape file descriptor with slaves */ asize = 0; blocksthisvol = 0; if (top) newtape++; /* new tape signal */ spcl.c_count = slp->count; /* * measure firstrec in TP_BSIZE units since restore doesn't * know the correct ntrec value... */ spcl.c_firstrec = slp->firstrec; spcl.c_volume++; spcl.c_type = TS_TAPE; spcl.c_flags |= DR_NEWHEADER; writeheader((ino_t)slp->inode); spcl.c_flags &=~ DR_NEWHEADER; if (tapeno > 1) msg("Volume %d begins with blocks from inode %d\n", tapeno, slp->inode); }}voiddumpabort(signo) int signo;{ if (master != 0 && master != getpid()) /* Signals master to call dumpabort */ (void) kill(master, SIGTERM); else { killall(); msg("The ENTIRE dump is aborted.\n"); }#ifdef RDUMP rmtclose();#endif Exit(X_ABORT);}__dead voidExit(status) int status;{#ifdef TDEBUG msg("pid = %d exits with status %d\n", getpid(), status);#endif /* TDEBUG */ exit(status);}/* * proceed - handler for SIGUSR2, used to synchronize IO between the slaves. */voidproceed(signo) int signo;{ if (ready) longjmp(jmpbuf, 1); caught++;}voidenslave(){ int cmd[2]; register int i, j; master = getpid(); signal(SIGTERM, dumpabort); /* Slave sends SIGTERM on dumpabort() */ signal(SIGPIPE, sigpipe); signal(SIGUSR1, tperror); /* Slave sends SIGUSR1 on tape errors */ signal(SIGUSR2, proceed); /* Slave sends SIGUSR2 to next slave */ for (i = 0; i < SLAVES; i++) { if (i == slp - &slaves[0]) { caught = 1; } else { caught = 0; } if (socketpair(AF_UNIX, SOCK_STREAM, 0, cmd) < 0 || (slaves[i].pid = fork()) < 0) quit("too many slaves, %d (recompile smaller): %s\n", i, strerror(errno)); slaves[i].fd = cmd[1]; slaves[i].sent = 0; if (slaves[i].pid == 0) { /* Slave starts up here */ for (j = 0; j <= i; j++) (void) close(slaves[j].fd); signal(SIGINT, SIG_IGN); /* Master handles this */ doslave(cmd[0], i); Exit(X_FINOK); } } for (i = 0; i < SLAVES; i++) (void) atomic(write, slaves[i].fd, (char *) &slaves[(i + 1) % SLAVES].pid, sizeof slaves[0].pid); master = 0; }voidkillall(){ register int i; for (i = 0; i < SLAVES; i++) if (slaves[i].pid > 0) (void) kill(slaves[i].pid, SIGKILL);}/* * Synchronization - each process has a lockfile, and shares file * descriptors to the following process's lockfile. When our write * completes, we release our lock on the following process's lock- * file, allowing the following process to lock it and proceed. We * get the lock back for the next cycle by swapping descriptors. */static voiddoslave(cmd, slave_number) register int cmd; int slave_number;{ register int nread; int nextslave, size, wrote, eot_count; /* * Need our own seek pointer. */ (void) close(diskfd); if ((diskfd = open(disk, O_RDONLY)) < 0) quit("slave couldn't reopen disk: %s\n", strerror(errno)); /* * Need the pid of the next slave in the loop... */ if ((nread = atomic(read, cmd, (char *)&nextslave, sizeof nextslave)) != sizeof nextslave) { quit("master/slave protocol botched - didn't get pid of next slave.\n"); } /* * Get list of blocks to dump, read the blocks into tape buffer */ while ((nread = atomic(read, cmd, (char *)slp->req, reqsiz)) == reqsiz) { register struct req *p = slp->req; for (trecno = 0; trecno < ntrec; trecno += p->count, p += p->count) { if (p->dblk) { bread(p->dblk, slp->tblock[trecno], p->count * TP_BSIZE); } else { if (p->count != 1 || atomic(read, cmd, (char *)slp->tblock[trecno], TP_BSIZE) != TP_BSIZE) quit("master/slave protocol botched.\n"); } } if (setjmp(jmpbuf) == 0) { ready = 1; if (!caught) (void) pause(); } ready = 0; caught = 0; /* Try to write the data... */ eot_count = 0; size = 0; while (eot_count < 10 && size < writesize) {#ifdef RDUMP if (host) wrote = rmtwrite(slp->tblock[0]+size, writesize-size); else#endif wrote = write(tapefd, slp->tblock[0]+size, writesize-size);#ifdef WRITEDEBUG printf("slave %d wrote %d\n", slave_number, wrote);#endif if (wrote < 0) break; if (wrote == 0) eot_count++; size += wrote; }#ifdef WRITEDEBUG if (size != writesize) printf("slave %d only wrote %d out of %d bytes and gave up.\n", slave_number, size, writesize);#endif if (eot_count > 0) size = 0; /* * fixme: Pyramids running OSx return ENOSPC * at EOT on 1/2 inch drives. */ if (size < 0) { (void) kill(master, SIGUSR1); for (;;) (void) sigpause(0); } else { /* * pass size of write back to master * (for EOT handling) */ (void) atomic(write, cmd, (char *)&size, sizeof size); } /* * If partial write, don't want next slave to go. * Also jolts him awake. */ (void) kill(nextslave, SIGUSR2); } if (nread != 0) quit("error reading command pipe: %s\n", strerror(errno));}/* * Since a read from a pipe may not return all we asked for, * or a write may not write all we ask if we get a signal, * loop until the count is satisfied (or error). */static intatomic(func, fd, buf, count) int (*func)(), fd; char *buf; int count;{ int got, need = count; while ((got = (*func)(fd, buf, need)) > 0 && (need -= got) > 0) buf += got; return (got < 0 ? got : count - need);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -