+static char *sccsid = "@(#)dumptape.c 1.1 (Berkeley) %G%";
+#include "dump.h"
+
+char tblock[NTREC][BSIZE];
+daddr_t tdaddr[NTREC];
+int trecno;
+
+taprec(dp)
+char *dp;
+{
+ register i;
+
+ for(i=0; i<BSIZE; i++)
+ tblock[trecno][i] = *dp++;
+ tdaddr[trecno] = 0;
+ trecno++;
+ spcl.c_tapea++;
+ if(trecno >= NTREC)
+ flusht();
+}
+
+tapsrec(d)
+daddr_t d;
+{
+
+ if(d == 0)
+ return;
+ tdaddr[trecno] = d;
+ trecno++;
+ spcl.c_tapea++;
+ if(trecno >= NTREC)
+ flusht();
+}
+
+int nogripe = 0;
+
+flusht()
+{
+ register i, si;
+ daddr_t d;
+
+ while(trecno < NTREC)
+ tdaddr[trecno++] = 1;
+
+loop:
+ d = 0;
+ for(i=0; i<NTREC; i++)
+ if(tdaddr[i] != 0)
+ if(d == 0 || tdaddr[i] < d) {
+ si = i;
+ d = tdaddr[i];
+ }
+ if(d != 0) {
+ bread(d, tblock[si], BSIZE);
+ tdaddr[si] = 0;
+ goto loop;
+ }
+ trecno = 0;
+ if (write(to, tblock[0], sizeof(tblock)) != sizeof(tblock) ){
+ msg("Tape write error on tape %d\n", tapeno);
+ broadcast("TAPE ERROR!\n");
+ if (query("Do you want to restart?")){
+ msg("This tape will rewind. After it is rewound,\n");
+ msg("replace the faulty tape with a new one;\n");
+ msg("this dump volumne will be rewritten.\n");
+ /*
+ * Temporarily change the tapeno identification
+ */
+ tapeno--;
+ nogripe = 1;
+ close_rewind();
+ nogripe = 0;
+ tapeno++;
+ Exit(X_REWRITE);
+ } else {
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ }
+
+ asize += sizeof(tblock)/density;
+ asize += 7;
+ blockswritten += NTREC;
+ if (asize > tsize) {
+ close_rewind();
+ otape();
+ }
+ timeest();
+}
+
+rewind()
+{
+ int secs;
+#ifdef DEBUG
+ msg("Waiting 10 seconds to rewind.\n");
+ sleep(10);
+#else
+ /*
+ * It takes about 3 minutes, 25secs to rewind 2300' of tape
+ */
+ secs = (( (60*3) + 25)*asize)/(2300L*12L*10L);
+ msg("Waiting %d seconds to rewind.\n", secs);
+ sleep(secs);
+#endif
+}
+
+close_rewind()
+{
+ close(to);
+ if (!nogripe){
+ rewind();
+ msg("Change Tapes: Mount tape #%d\n", tapeno+1);
+ broadcast("CHANGE TAPES!\7\7\n");
+ }
+ do{
+ if (query ("Is the new tape mounted and ready to go?"))
+ break;
+ if (query ("Do you want to abort?")){
+ dumpabort();
+ /*NOTREACHED*/
+ }
+ } while (1);
+}
+
+/*
+ * 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.
+ */
+
+otape()
+{
+ int parentpid;
+ int childpid;
+ int status;
+ int waitpid;
+ int sig_ign_parent();
+ int interrupt();
+
+ /*
+ * Force the tape to be closed
+ */
+ close(to);
+ parentpid = getpid();
+
+ restore_check_point:
+ signal(SIGINT, interrupt);
+ /*
+ * 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
+ for (;;){
+ waitpid = wait(&status);
+ if (waitpid != childpid){
+ msg("Parent %d waiting for child %d has another child %d return\n",
+ parentpid, childpid, waitpid);
+ } else
+ break;
+ }
+ 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
+ do{
+ to = creat(tape, 0666);
+ if (to < 0) {
+ if (!query("Cannot open tape. Do you want to retry the open?"))
+ dumpabort();
+ } else break;
+ } while (1);
+
+ asize = 0;
+ tapeno++; /* current tape sequence */
+ newtape++; /* new tape signal */
+ spcl.c_volume++;
+ spcl.c_type = TS_TAPE;
+ spclrec();
+ if (tapeno > 1)
+ msg("Tape %d begins with blocks from ino %d\n",
+ tapeno, ino);
+ }
+}
+
+/*
+ * The parent still catches interrupts, but does nothing with them
+ */
+sig_ign_parent()
+{
+ msg("Waiting parent receives interrupt\n");
+ signal(SIGINT, sig_ign_parent);
+}
+
+dumpabort()
+{
+ msg("The ENTIRE dump is aborted. NO second chances (tough luck sucker).\n");
+ Exit(X_ABORT);
+}
+
+Exit(status)
+{
+#ifdef TDEBUG
+ msg("pid = %d exits with status %d\n", getpid(), status);
+#endif TDEBUG
+ henryexit(status);
+}
+
+#ifdef TDEBUG
+exit(status)
+ /*ARGSUSED*/
+{
+ fflush(stdout);
+ fprintf(stderr, "Somebody called exit: halt executed\n");
+ fflush(stderr);
+ abort();
+}
+
+_exit(status)
+ /*ARGSUSED*/
+{
+ fflush(stdout);
+ fprintf(stderr, "Somebody called _exit: halt executed\n");
+ fflush(stderr);
+ abort();
+}
+#endif TDEBUG
+
+henryexit(status)
+ /* ARGSUSED */
+{
+ _cleanup();
+ asm(" chmk $1");
+ asm("halt");
+}