home *** CD-ROM | disk | FTP | other *** search
Text File | 1988-09-11 | 31.3 KB | 1,138 lines |
- Subject: backup - front end for BSD dump
- Newsgroups: mod.sources
- Approved: jpn@panda.UUCP
-
- Mod.sources: Volume 5, Issue 5
- Submitted by: Wombat <pur-ee!pucc-j.Purdue.EDU!rsk>
-
- # This is a shell archive.
- # Remove everything above and including the cut line.
- # Then run the rest of the file through sh.
- #----cut here-----cut here-----cut here-----cut here----#
- #!/bin/sh
- # shar: Shell Archiver
- # Run the following text with /bin/sh to create:
- # README
- # backup.8l
- # backup.c
- # backup.list
- # tape.label
- # This archive created: Thu May 15 13:49:26 1986
- # By: Wombat (Purdue University)
- cat << \SHAR_EOF > README
- Backup is a front-end for 4.[23]bsd dump, allowing non-programmers to
- successfully run disk-to-tape backups. It incorporates what seems to
- be a sufficient amount of paranoid thinking to prevent data-destroying
- blunders. I'm sending this out partially because I feel it could be of
- some use, and partially because I'm interested in seeing what
- [constructive] comments we'll receive; we're considering some changes
- in the way we do backups.
-
- Note that this program is NOT a standalone backup program; it has never
- been tested with anything other than 4.2BSD and 4.3BSD dump.
-
- --
- Rich Kulawiec, pucc-j!rsk, rsk@asc.purdue.edu
-
- Contents:
-
- README
- backup.8l Man page for the "backup" command
- (nroff -man backup.8l)
- backup.c The backup program itself
- (cc -O -DPUCC -DBSD4_2 -DALLINONE -DVERIFYTAPE -o backup backup.c)
- backup.list A sample "config" file for backup
- tape.label A lore document explaining how tape labels work
- (nroff -ms tape.label)
- SHAR_EOF
- cat << \SHAR_EOF > backup.8l
- .TH BACKUP 8L PUCC
- .SH NAME
- backup \- run periodic tape dumps
- .SH SYNOPSIS
- .B /etc/backup
- .br
- .SH DESCRIPTION
- .I Backup
- reads
- .I /etc/backup.list
- and attempts the necessary invocations of
- .IR dump (8)
- to back up (to tape) all filesystems listed therein.
- It asks the invoker to mount the appropriate
- tape, and checks the tape label to make sure that
- it is correct with respect to machine, filesystem,
- and level of backup being done.
- It then calls
- .IR dump (8)
- with appropriate arguments and waits
- for it to finish. When dump is done,
- .I backup
- will go on to the next filesystem.
- .PP
- The backup schedule in the file
- .I /etc/backup.list
- consists of lines of the form:
- .nf
-
- \ \ \ \ \ pucc-h:x0x9x9x:/usr
-
- .fi
- which are interpreted as follows:
- .nf
-
- \ \ \ \ \ machine-name:schedule:filesystem
-
- .fi
- where ``machine-name'' must be exactly the same as the string returned by
- .IR hostname (2),
- and ``filesystem'' the same as given in /etc/fstab.
- The ``schedule'' is a seven character string, with one character for
- each day of the week, with the first character representing Sunday.
- The character corresponding to a given day of the week represents the level
- of dump for that day. For example, the sample line indicates that for pucc-h,
- filesystem /usr, an epoch (level 0) dump should be done on Monday,
- with incremental (level 9) dumps on Wednesday and Friday.
- No dumps will be done on Sunday, Tuesday, Thursday, or Saturday.
- Note that any numeric character will cause a dump of that level
- to be done on that day; any non-numeric character
- will prevent dumps from being done on that day.
- Colons (``:'') are not allowed, since they are used as
- field separators.
- .PP
- Additionally,
- .I backup
- runs ``/etc/dump #W'' (with # being the level of the dump)
- and decides from its output whether a filesystem has already been
- backed up on the same day.
- If it finds that any particular filesystem
- has already been dumped, it will ask the
- operator to verify that it should be dumped again.
- .PP
- Any question that
- .I backup
- asks must be answered with
- .B go
- or
- .BR stop ,
- exactly.
- Any other answer will cause an error message to be printed,
- and the question will be asked again.
- .SH FILES
- .nf
- /etc/backup.list
- /etc/fstab
- /etc/dumpdates
- .fi
- .SH SEE ALSO
- dump(8)
- .SH BUGS
- .PP
- There's no way to cleanly restart backup (if interrupted) without
- repeating some work.
- .PP
- Since
- .I backup
- tries to fit all partial dumps for a machine on one tape, selecting
- too low a level or waiting too long between backups
- may cause it to run out of tape.
- .PP
- Mixing dump levels on one partial tape is not recommended.
- .PP
- Dump always claims to be rewinding the tape, even if it's not.
- .PP
- The tape labelling scheme is not fail safe.
- SHAR_EOF
- cat << \SHAR_EOF > backup.c
- /*
- * backup.c Rich Kulawiec, PUCC, 1985
- *
- * Run dumps according to a fixed schedule encoded in file CONTROL.
- *
- * File contains:
- *
- * main() -- main program, of course
- * check_sched() -- examine a schedule line for information
- * already_done() -- check to see if dumps already done today
- * ask_for_tape() -- ask the operator to mount the appropriate tape
- * verify() -- prompt operator to confirm tape load
- * verifytape() -- check tape label file for correctness
- * write_label() -- write a tape label file
- * rundump() -- build a dump command and run it
- * query() -- ask a question and get an answer
- * tapeskip() -- skip one file on the tapedrive
- * offline() -- take tapedrive offline
- * message() -- print a message on the terminal
- *
- * Defining DEBUG changes backup's idea of the tape drive, and
- * causes it to echo "dump" and "mt" commands instead of running them.
- * It will still run dump W as needed, though.
- * Depending on what's left in here, it may also print some
- * additional information as it runs.
- *
- * Defining ALLINONE causes all partial dumps to be placed on
- * the same tape. It would probably be a bad idea to mix partial
- * dump levels on that tape.
- *
- * Defining VERIFYTAPE causes backup to check the tape for a header
- * indicating whether or not it's the right tape. Note that leaving
- * this undefined will still let backup query the operator before
- * firing up dump to scribble on the tape.
- */
-
- #ifndef lint
- static char *rcsid = "$Header: /usr/src/etc/RCS/backup.c,v 1.13 85/08/19 19:49:42 rsk Exp $";
- #endif lint
- /*
- * $Log: backup.c,v $
- * Revision 1.13 85/08/19 19:49:42 rsk
- * Changed the handling of offline() to fix a buglet; if backuup
- * ran epoch dumps using a schedule file whose last line did not
- * have a '0' in the same position as the '0' for the epoch dumps
- * that were run (confusing isn't it?) then what happened was that
- * Level was set to, say, 'x', and the test at the end of main
- * succeeded...and backup tried to take the tapedrive offline after
- * already doing so.
- * Therefore, the following was done:
- * rundump() now calls offline() (1) all the time if ALLINONE is
- * not defined (2) for all epoch dumps if ALLINONE is defined.
- * To take care of multiple partial dumps, a variable "Rewind"
- * was created; if it is set, then main() will call offline()
- * just prior to exiting. The only place that sets Rewind is
- * inside verifytape() -- and it sets it iff it's looking at
- * a partial tape and is running with ALLINONE defined.
- *
- * Revision 1.12 85/08/18 20:34:51 rsk
- * Added several (void)'s in front of calls to offline()
- * to satisfy lint.
- *
- * Revision 1.11 85/08/18 16:17:35 rsk
- * Several changes.
- * If DEBUG is defined, then MT_OFFLINE is fixed so that
- * it just rewinds the tape rather than taking it offline.
- * The series of if statements in verifytape() are now
- * chained with else clauses.
- * When write_label() is called in verifytape(), verifytape()
- * calls itself to check the results.
- * Added several calls to offline() in verifytape() to
- * make sure bad tape gets noticed.
- * Took out the level sanity check in write_label() since it
- * really didn't do much.
- * Fixed a small bug in write_label(); if ALLINONE is defined
- * and this is a partial tape, then should write PARTIAL_LABEL
- * rather than the name of the filesystem. This only shows up
- * if a SPARE tape is used for a partial and then the order
- * of partial dumps gets re-arranged later, but it's fixed
- * anyhow.
- *
- * Revision 1.10 85/08/17 13:14:58 rsk
- * Updated list of functions at top to reflect reality.
- *
- * Revision 1.9 85/08/16 13:01:13 rsk
- * Rearranged functions to reflect logical order.
- *
- * Revision 1.8 85/08/16 11:29:22 rsk
- * Added a little debugging code to already_done(); fixed up the
- * function headers to more accurately reflect what goes on in
- * each function.
- *
- * Revision 1.7 85/08/16 10:58:42 rsk
- * Ripped out pseudo-exit routine from last night. Simplified
- * exit codes. Switched calls to rundump() and verify(), since
- * an accidental change last night had backup trying to verify
- * the tape after running the dump. Made verify return a value
- * even though it's ignored, 'cause (a) might use it later,
- * and (b) couldn't figure out how to recursively return a void.
- *
- * Revision 1.6 85/08/16 02:01:24 rsk
- * Added a new routine that's called instead of exit() with
- * a wealth (10, actually) of exit codes; might be handy if
- * this is run by a script someday. Will probably trim the
- * list tomorrow, as well as fixing a known bug.
- * The program is definitely easier manage now that it's
- * broken up into chunks, although the growth in the number
- * of (void) functions and global variables isn't very aesthetic.
- *
- * Revision 1.5 85/08/16 01:31:44 rsk
- * Many changes. Several new functions, which have taken chunks
- * of the large loop that used to be in main() and broken them
- * up into little pieces. Tapeok is now a global, among other
- * things, and is used when putting multiple partial dumps on
- * one tape to avoid attempting to reverify the tape after it's
- * been done once. Most new functions are (void) since if they
- * fail we have to abort anyway; no sense testing for a return
- * value and then aborting.
- *
- * Revision 1.4 85/08/16 00:00:42 rsk
- * Made Filesys, Tapedrive, and Schedline global
- * prepatory to breaking the large loop in main()
- * up into several function calls.
- *
- * Revision 1.3 85/08/14 18:45:52 rsk
- * Parameterized the sizes of a number of characters arrays.
- * Used STRSIZE (255) for devices, filesystems, command lines,
- * hostnames, lines from the schedule file, and replies from
- * the operator.
- * Used DATESTRSIZE (30) for dates...since all dates are in
- * ctime() format, which takes 26 characters, this should
- * be sufficient.
- * Only non-parameterized array is "weekmask", which is
- * set to 7. This will need to be changed if another
- * day is added to the week.
- *
- * Revision 1.2 85/08/12 20:16:00 rsk
- * Added changes for "spare" tapes, including auto-labelling.
- * Slightly changed what happens when dump fails if DEBUG is defined.
- * Took #ifdef DEBUG off write_label() routine.
- *
- * Revision 1.1 85/08/09 16:13:05 rsk
- * Initial revision
- *
- */
-
- #include <stdio.h>
- #include <ctype.h>
- #include <sys/time.h>
-
- #define OKAY 0
- #define STRSIZE 255 /* Should be large enough for most everything */
- #define DATESTRSIZE 30 /* Should only need to be 26 (for ctime) */
- #define FAIL -1
- #define DUMPFLAGS "ufd"
- #define DENSITY "6250"
- #define DUMPINFOFLAG "W"
- #define MT_SKIP "fsf 1"
- #define MYNAME "BACKUP"
- #define SPARE_LABEL "SPARE"
-
- #ifdef ALLINONE
- #define PARTIAL_LABEL "PARTIAL"
- #endif ALLINONE
-
- #ifdef DEBUG
- #define DUMP "echo /etc/dump"
- #define TAPE "dev.rmt8"
- #define NTAPE "dev.nrmt8"
- #define CONTROL "backup.list"
- #define MT "/bin/echo mt -f"
- #define MT_OFFLINE "rewind"
- #else DEBUG
- #define DUMP "/etc/dump"
- #define TAPE "/dev/rmt8"
- #define NTAPE "/dev/nrmt8"
- #define CONTROL "/etc/backup.list"
- #define MT "/bin/mt -f"
- #define MT_OFFLINE "offline"
- #endif DEBUG
-
- #define GO "go"
- #define STOP "stop"
-
- /*
- * Different classes of errors; provides additional error message on exit.
- */
-
- #define ST_OKAY 0
- #define ST_NOWORK 1
- #define ST_DUMP 2
- #define ST_ABORT 3
- #define ST_CTL 4
- #define ST_TAPE 5
- #define ST_SYSCALL 6
- #define ST_INTERNAL 7
-
- char Command[STRSIZE]; /* constructed shell commands */
- char Date[DATESTRSIZE]; /* current date/time in ctime() format */
- char Hostname[STRSIZE]; /* the name of this host */
- char Filesys[STRSIZE]; /* name of filesystem(s) to dump */
- char Tapedrive[STRSIZE]; /* name of tapedrive to use */
- char Schedline[STRSIZE]; /* a line read out of schedule file */
- char Weekmask[7]; /* one slot per day of week */
- char Level; /* level of this dump */
- #ifdef ALLINONE
- int Tapeok = FAIL; /* Has this tape been verified? */
- int Rewind = FAIL; /* Does tape need to be rewound at end? */
- #endif ALLINONE
-
- struct tm *localtime();
- FILE *popen();
- void message();
- void check_sched();
- void ask_for_tape();
- void rundump();
- char *strcpy();
- char *strncpy();
- char *index();
- char *rindex();
- char *gets();
- long time();
-
- main()
- {
- FILE *fp;
- long timebuf; /* seconds since epoch */
- struct tm *ldate; /* date broken down into fields */
-
- if(gethostname(Hostname,sizeof(Hostname)) == FAIL) {
- message("gethostname() failed");
- exit(ST_SYSCALL);
- }
-
- (void) time(&timebuf);
- (void) strcpy(Date,ctime(&timebuf));
- ldate = localtime(&timebuf);
-
- if( (fp = fopen(CONTROL,"r")) == NULL) {
- message("can't open control file");
- exit(ST_SYSCALL);
- }
-
- /*
- * Read the schedule file; make rudimentary checks for integrity.
- */
-
- while( fscanf(fp,"%s",Schedline) != FAIL) {
-
- /*
- * Break out the fields of this line of the schedule.
- */
-
- check_sched(ldate->tm_wday);
-
- /*
- * If this line of the schedule file pertains to this machine,
- * and if there's a digit in the dump schedule for today,
- * then we probably have some work to do.
- */
-
- if( (strncmp(Schedline,Hostname,strlen(Hostname)) == OKAY)
- && isascii(Level) && isdigit(Level) ) {
-
- /*
- * Check to see if we've already dumped this filesystem today.
- */
-
- if( already_done() == OKAY ){
- (void) printf("%s: filesystem %s has already been dumped today\n",MYNAME,Filesys);
- if(query ("Do you want to dump it again?") != OKAY)
- continue;
- }
- /*
- * Request the proper tape from the operator.
- */
- ask_for_tape();
-
- /*
- * If ALLINONE is defined, then first pass through here will
- * set Tapeok to OKAY, assuming it's the right tape,
- * when we're putting multiple partial dumps on one tape.
- * Successive passes won't bother to verify the tape.
- */
-
- #ifdef ALLINONE
- if(Level == '0' )
- (void) verify();
- else
- if(Tapeok == FAIL)
- (void) verify();
- #else ALLINONE
- (void) verify();
- #endif ALLINONE
-
- /*
- * Build the call the dump, and run it. This does the real work.
- * Note that rundump() also has the responsibility for rewinding
- * the tape and taking the drive offline if needed.
- */
- rundump();
- }
- }
- message("finished");
- (void) fclose(fp);
-
- /*
- * If in ALLINONE mode, then take tape drive offline at the
- * end of series of partials. If these were epoch dumps,
- * the drive will already be offline...see above.
- * Note: this is one of the places where assumptions about
- * NOT mixing epoch and partial dumps are important.
- */
-
- #ifdef ALLINONE
- if (Rewind == OKAY)
- (void) offline();
- #endif ALLINONE
-
- exit(ST_OKAY);
- }
- /*
- * check_sched() -- examine line of schedule file for work to do
- *
- * Parameters: day_of_week -- part of ldate structure
- *
- * Global Variables: Schedline, Filesys, Weekmask, Level
- *
- * Returns: nothing
- *
- * Side Effects: sets Filesys, Weekmask, and Level according to schedule line
- *
- */
- void check_sched(day_of_week)
- int day_of_week;
- {
- char *charptr; /* scratch character pointer */
-
-
- if( (charptr = rindex(Schedline,':')) == NULL) {
- message("Garbled control file");
- exit(ST_CTL);
- }
- (void) strcpy(Filesys,++charptr); /* got filesystem */
-
- if( (charptr = index(Schedline,':')) == NULL) {
- message("Garbled control file");
- exit(ST_CTL);
- }
- (void) strncpy(Weekmask,++charptr,7); /* got dump levels */
-
- /*
- * Get today's dump level.
- */
-
- Level = Weekmask[day_of_week];
- }
- /*
- * already_done() -- see if this filesystem was dumped today
- *
- * Parameters: none
- *
- * Global Variables: Command, Date, Filesys, Level
- *
- * Returns: OKAY -- if already dumped Filesystem at level Level today.
- * FAIL -- otherwise
- *
- * Side Effects: runs "dump W" to gather information
- *
- */
- already_done()
- {
- char last_device[STRSIZE]; /* device filesystem is mounted on */
- char last_filesys[STRSIZE]; /* name of filesystem */
- char last_level; /* level of last dump */
- char last_date[DATESTRSIZE]; /* ctime format date of last dump */
- char replybuf[STRSIZE]; /* holds a line of reply from dump W */
- FILE *sp; /* stream pointer */
- int returnval; /* temporary place for return value */
-
- returnval = FAIL;
-
- /*
- * Build a command line for dump that will cause it to print
- * its idea of which filesystems need to be dumped.
- * Do this rather than reading /etc/dumpdates and /etc/fstab
- * and deciphering everything ourself.
- */
-
- (void) sprintf(Command,"%s %c%s",DUMP,Level,DUMPINFOFLAG);
- (void) fflush(stdout);
-
- /*
- * Run the command, and read its output.
- */
-
- if( (sp = popen(Command,"r")) == NULL) {
- message("popen() failed");
- exit(ST_SYSCALL);
- }
-
- /*
- * Eat first line of dump's verbose output
- */
-
- if(fscanf(sp,"%[^\n]\n",replybuf) == FAIL) {
- message("can't fscanf() reply from dump");
- exit(ST_SYSCALL);
- }
-
- /*
- * Grab all necessary fields out of dump's output.
- * Slight trickiness; must eat everything up to the first '('
- * in order to get filesystem name; thus, last_device
- * might contain some garbage characters. We don't care, since
- * we don't really use it. Then we eat everything to ')'
- * in order to get filesystem name.
- */
-
- while (fscanf(sp,"%[^(]( %[^)]) Last dump: Level %c, Date %[^\n]\n",last_device,last_filesys,&last_level,last_date) != FAIL) {
-
- /*
- * If this line of "dump W" output pertains to the filesystem in
- * question, and if the dump level matches the current one,
- * AND if the date of the last dump appears to be today,
- * then it would seem that this filesystem has been dumped today.
- * Note that fact, and just keep going; must read all of dump's
- * output to avoid causing SIGPIPE on early pclose().
- */
- if(strncmp(last_filesys,Filesys,strlen(Filesys)) == OKAY
- && last_level == Level
- && strncmp(last_date,Date,10) == OKAY)
- returnval = OKAY;
- }
-
- if( pclose(sp) == FAIL) {
- message("pclose() failed");
- exit(ST_SYSCALL);
- }
-
- #ifdef DEBUG
- (void) printf("already_done() returning %s\n",returnval == OKAY ? "OKAY" : "FAIL");
- return(query("Type go for OKAY(yes), stop for FAIL(no)."));
- #else DEBUG
- return(returnval);
- #endif DEBUG
- }
- /*
- * ask_for_tape() -- request the operator to mount the proper tape
- *
- * Parameters: none
- *
- * Global Variables: Tapedrive, Hostname, Filesys, Date
- *
- * Returns: nothing
- *
- * Side Effects: none
- *
- */
- void ask_for_tape()
- {
-
- /*
- * Ask for the proper tape; use rewind device for epochs,
- * no-rewind device for incrementals.
- * Note that if this tape has already been verified, i.e.
- * we compiled with ALLINONE defined and Tapeok has been
- * set to OKAY by a previous call to verify(), then we do nothing.
- */
-
- if(Level == '0') {
- (void) strcpy(Tapedrive,TAPE);
- (void) printf("%s: get the FULL backup tape for machine %s, filesystem %s for %.3s\n",MYNAME,Hostname,Filesys,Date);
- }
- else {
- (void) strcpy(Tapedrive,NTAPE);
- #ifdef ALLINONE
- if (Tapeok == FAIL)
- (void) printf("%s: get the PARTIAL backup tape for machine %s for %.3s\n",MYNAME,Hostname,Date);
-
- #else ALLINONE
- (void) printf("%s: get the PARTIAL backup tape for machine %s, filesystem %s for %.3s\n",MYNAME,Hostname,Filesys,Date);
- #endif ALLINONE
-
- }
- }
- /*
- * verify() -- prompt operator to mount tape
- *
- * Parameters: none
- *
- * Global Variables: none
- *
- * Returns: OKAY if tape verified.
- *
- * Side Effects: exits if operator wants to quit
- *
- */
- verify()
- {
- if( query("Is the correct tape mounted?") == OKAY) {
- if( verifytape() == OKAY) {
- message("tape verified");
- return(OKAY);
- }
- else {
- message("\007\007\007INCORRECT TAPE MOUNTED!!");
- message("Get the correct tape!!");
- return(verify());
- }
- }
- else {
- if( query ("Do you want to continue the backup?") == OKAY) {
- return(verify());
- }
- else {
- message("aborting on operator command");
- exit(ST_ABORT);
- }
- }
- /*
- * We should never get here; abort if it happens.
- */
-
- message("Something very bad has happened in verify()");
- exit(ST_INTERNAL);
-
- }
- /*
- * verifytape() -- check tape label vs. current filesystem
- * Is smart enough to recognize a spare tape; also
- * recognizes partial tape labels if in ALLINONE mode.
- *
- * Parameters: none
- *
- * Global Variables: Hostname, Date, Filesys, Level
- *
- * Returns: OKAY -- if this is the correct w.r.t. filesys & level
- * FAIL -- otherwise
- *
- * Side Effects: leaves tape positioned at end of label file if correct tape.
- * writes a new label if this is a spare tape
- * set the Rewind flag if ALLINONE is defined and this is
- * a multiple partial dump tape
- *
- */
- verifytape()
- {
- #ifdef VERIFYTAPE
- char last_hostname[STRSIZE]; /* hostname as written on tape */
- char last_date[DATESTRSIZE]; /* date as written on tape */
- char last_filesys[STRSIZE]; /* filesystem as written on tape */
- char last_level; /* dump level as written on tape */
- FILE *fp;
-
- if( (fp = fopen(TAPE,"r")) == NULL) {
- message("can't open tapedrive");
- exit(ST_SYSCALL);
- }
-
- #ifdef DEBUG
- (void) printf("%s: searching for label: %s %.3s %s %c\n",MYNAME,Hostname,Date,Filesys,Level);
- #endif DEBUG
-
- if(fscanf(fp,"%s %s %s %c",last_hostname,last_date,last_filesys,&last_level) == FAIL) {
- message("can't read tape label");
- exit(ST_SYSCALL);
- }
-
- #ifdef DEBUG
- (void) printf("%s: found this label: %s %.3s %s %c\n",MYNAME,last_hostname,last_date,last_filesys,last_level);
- #endif DEBUG
-
- (void) fclose(fp);
-
- /*
- * If the label appears to be okay, i.e. all four fields match
- * our idea of what should be there, then skip over the label
- * file and get ready to run dump.
- */
-
- if( strcmp(Hostname,last_hostname) == OKAY
- && strncmp(Date,last_date,3) == OKAY
- && strcmp(Filesys,last_filesys) == OKAY
- && Level == last_level) {
- (void) tapeskip();
- return(OKAY);
- }
-
- #ifdef ALLINONE
-
- /*
- * If we're dealing with multiple partial dumps on one tape,
- * then we need to check for the special label marking this
- * tape; therefore, a slightly modified test is made.
- *
- * Must also set Rewind so that tape will be taken care of
- * when all dumps are done, just before we exit...see the
- * end of main().
- */
-
- else if( strcmp(Hostname,last_hostname) == OKAY
- && strncmp(Date,last_date,3) == OKAY
- && strcmp(PARTIAL_LABEL,last_filesys) == OKAY
- && Level != '0'
- && Level == last_level) {
- Tapeok = OKAY;
- Rewind = OKAY;
- (void) tapeskip();
- return(OKAY);
- }
-
- #endif ALLINONE
-
- /*
- * This might be a spare tape; if so, ignore the date and dumplevel
- * fields; just check the host, and look for SPARE_LABEL in place
- * of the filesystem name.
- */
-
- else if (strcmp(Hostname,last_hostname) == OKAY
- && strncmp(last_filesys,SPARE_LABEL,strlen(SPARE_LABEL)) == OKAY) {
- message("This looks like a spare tape.");
- if( query("Go ahead and use it?") == OKAY) {
- if( write_label() == OKAY) {
- return(verifytape());
- }
- else {
- (void) offline();
- return(FAIL);
- }
- }
- else {
- (void) offline();
- return(FAIL);
- }
- }
- else {
- (void) offline();
- return(FAIL);
- }
-
- #else VERIFYTAPE
- return(OKAY);
- #endif VERIFYTAPE
- }
- /*
- * write_label() -- write a label on a spare tape
- *
- * Parameters: none
- *
- * Global Variables: Hostname, Date, Filesys, Level
- *
- * Returns: OKAY if label successfully written
- * FAIL otherwise
- *
- * Side Effects: writes a tiny label file on the beginning of the tape
- *
- */
- write_label()
- {
- FILE *fp;
-
- if( (fp = fopen(TAPE,"w")) == NULL) {
- message("can't open tapedrive");
- exit(ST_SYSCALL);
- }
-
- #ifdef ALLINONE
- if( Level == '0')
- fprintf(fp,"%s %.3s %s %c\n",Hostname,Date,Filesys,Level);
- else
- fprintf(fp,"%s %.3s %s %c\n",Hostname,Date,PARTIAL_LABEL,Level);
- #else ALLINONE
- fprintf(fp,"%s %.3s %s %c\n",Hostname,Date,Filesys,Level);
- #endif ALLINONE
-
- (void) fclose(fp);
-
- return(OKAY);
- }
- /*
- * rundump() -- call the dump program to do the real work
- *
- * Parameters: none
- *
- * Global Variables: Level, Tapedrive, Filesys, Command
- *
- * Returns: nothing
- *
- * Side Effects: runs the actual dump
- * queries the operator for advice if dump fails
- *
- */
- void rundump()
- {
- int status; /* return status of dump command */
-
- /*
- * Build the proper command line to call dump.
- */
-
- (void) sprintf(Command,"%s %c%s %s %s %s",DUMP,Level,DUMPFLAGS,Tapedrive,DENSITY,Filesys);
-
- (void) printf("%s: running %s\n",MYNAME,Command);
- (void) fflush(stdout);
-
- /*
- * Call dump; check the return status to make sure it's alright.
- */
-
- if( (status = system(Command)) != OKAY) {
- (void) printf("%s: dump failed, status=%d\n",MYNAME,(status>>8));
- #ifdef DEBUG
- if( query("Want to go on?") == FAIL) {
- (void) offline();
- exit(ST_ABORT);
- }
- #else DEBUG
- (void) offline();
- exit(ST_DUMP);
- #endif DEBUG
- }
-
- #ifdef ALLINONE
- if(Level == '0')
- (void) offline();
- #else ALLINONE
- (void) offline();
- #endif ALLINONE
- }
- /*
- * query(question) -- prints "question" on the tty, then waits for answer
- *
- * Parameters: question -- a null-terminated string
- *
- * Global Variables: none
- *
- * Returns: OKAY -- if question eventually answered with GO
- * FAIL -- if question eventually answered with STOP
- *
- * Side Effects: recursive routine
- *
- */
- query(question)
- char *question;
- {
- char reply[STRSIZE]; /* Whatever the user types in reply */
-
- (void) printf("%s: %s <Answer %s or %s only>: ",MYNAME,question,GO,STOP);
- /*
- * Print the question and wait for an answer.
- * If we don't understand the answer, retry (recursively).
- */
-
- if( gets(reply) == NULL) {
- message("can't talk to console");
- exit(ST_SYSCALL);
- }
- else if( strcmp(reply,GO) == 0 ) {
- return(OKAY);
- }
- else if( strcmp(reply,STOP) == 0) {
- return(FAIL);
- }
- else {
- message("incorrect response, try again");
- return(query(question));
- }
-
- /*
- * We should never get here; abort if it happens.
- */
-
- message("Something very bad has happened in query()");
- exit(ST_INTERNAL);
- }
- /*
- * takeskip() -- skip forward one file on tape
- *
- * Parameters: none
- *
- * Global Variables: Command
- *
- * Returns: OKAY
- *
- * Side Effects: skips forward one file on the tape via "mt"
- *
- */
- tapeskip()
- {
- int status;
-
- (void) sprintf(Command,"%s %s %s",MT,NTAPE,MT_SKIP);
-
- if( (status = system(Command)) != OKAY) {
- (void) printf("%s: mt failed, status=%d\n",MYNAME,(status>>8));
- exit(ST_TAPE);
- }
-
- return(OKAY);
- }
- /*
- * offline() -- takes tapedrive offline
- *
- * Parameters: none
- *
- * Global Variables: Command, Tapedrive
- *
- * Returns: OKAY
- *
- * Side Effects: calls "mt" to do the work; leaves tape drive off line.
- *
- */
- offline()
- {
- int status;
-
- (void) sprintf(Command,"%s %s %s",MT,Tapedrive,MT_OFFLINE);
-
- if( (status = system(Command)) != OKAY) {
- (void) printf("%s: mt failed, status=%d\n",MYNAME,(status>>8));
- exit(ST_TAPE);
- }
-
- return(OKAY);
- }
- /*
- * message(msg) -- print a message labelled with name of this program on tty
- *
- * Parameters: msg -- the string to print
- *
- * Global Variables: none
- *
- * Returns: nothing
- *
- * Side Effects: none
- *
- */
- void message(msg)
- char *msg;
- {
- (void) printf("%s: %s\n",MYNAME,msg);
- }
- SHAR_EOF
- cat << \SHAR_EOF > backup.list
- pucc-h:x0x9x9x:/
- pucc-h:x0x9x9x:/sys
- pucc-h:x0x9x9x:/usr
- pucc-h:x0x9x9x:/usr/spool
- pucc-i:xx0x9x9:/
- pucc-i:xx0x9x9:/sys
- pucc-i:xx0x9x9:/usr
- pucc-i:xx0x9x9:/usr/pub
- pucc-i:xx0x9x9:/usr/spool
- SHAR_EOF
- cat << \SHAR_EOF > tape.label
- .LP
- .nr LT 6.5i
- .lt 6.5i
- .nr LL 6.5i
- .ll 6.5i
- .TL
- How backup tape labels work.
- .AU
- Rich Kulawiec
- .NH
- Overview
- .PP
- Our backup tapes are \*Qlabelled\*U on the autoload ring (in writing)
- and on the tape itself (in a small file). This document explains how
- that system works and how to label tapes.
- .NH
- What's in a label
- .PP
- A sample tape label file looks like this:
- .DS
- pucc-j Mon /usera 0
- .DE
- This means that the tape in question is supposed to be used to do
- a level 0 dump of /usera on pucc-j on Monday. If someone tries
- to use it for any other dump, \fIbackup(1l)\fR will stop them.
- \fBThere is no way to force backup to override this.\fR
- The only way out is manual intervention.
- .PP
- The whitespace-separated fields in the label file have meaning as follows:
- .DS
- .nf
- hostname: eg. \*Qpucc-j\*U, as returned by gethostname()
- day-of-week: eg. \*QMon\*U, as returned in the first 3 characters of ctime()
- filesystem: eg. \*Q/usera\*U, as shown in /etc/fstab
- dump level: eg. \*Q0\*U, a single digit between 0 and 9
- .fi
- .DE
- .NH
- How backup uses labels
- .PP
- Before \fIbackup\fR will scribble on a tape, it reads the label file and
- compares it against its idea of what it's about to try to do.
- If everything looks okay, it will skip past that file and write
- the dump on tape.
- However,
- if any of the four fields fail to match
- (with certain exceptions, see below),
- it complains vigorously and demands a different tape.
- .NH
- The exceptions
- .PP
- Since we put all of the partial backups for an entire machine on
- one tape (at least for the moment) that tape is labelled with
- the string \*QPARTIAL\*U where the filesystem should be.
- Incidentally, partial backup tapes only have one label at beginning
- of the tape\(emthere's nothing between the actual dumps.
- .PP
- The other case we have to discuss concerns the pool of spare tapes.
- This pool is actually several small pools of several tapes per machine,
- each of which has been labelled as follows:
- .DS
- hostname sss SPARE c
- .DE
- where \*Qhostname\*U is as above; \*Qsss\*U is just a scratch character string;
- \*QSPARE\*U is the string that tells backup that this is a spare tape; and
- \*Qc\*U is just a scratch character.
- .PP
- When a spare tape is presented to \fIbackup\fR, it verifies that it is indeed
- a spare, and that it's for the correct machine, and then writes a \fIreal\fR
- label file on it. In other words, the tape is no longer a spare.
- .NH
- Creating a tape label file
- .PP
- Simple; you can just \*Qecho\*U the information onto that tape; or type it
- into a file and \*Qcat\*U it on, or whatever. Make sure that you write it
- on the tape at 6250 bpi using the RAW device, since \fIbackup\fR
- runs at that density.
- It's also important to point out that writing a label file on a tape
- which already contains an old label file and a dump image will make the dump
- inacessible, since a double EOF will be written after the label file.
- Make \fIcertain\fR that the dump isn't needed before rewriting the label file.
- .PP
- It's also easy to check a label file, just by using cat or dd to read the
- first file on the tape. Again, make sure you use the 6250 bpi RAW device.
- Here's an example:
- .DS
- % echo \*Qpucc-j Mon /usera 0\*U > /dev/rmt6250
-
- % cat /dev/rmt6250
- pucc-j Mon /usera 0
- .DE
- .NH
- Faking it
- .PP
- Occasionally, due to a failed backup or a massive change in a filesystem
- between epoch dumps, there is a need to do a manual dump. This is a
- slightly dangerous and tricky business, and should not be attempted unless
- you understand \fBexactly\fR what you are doing. Caveat emptor.
- .PP
- Generally speaking, these "special-case" dumps are epoch dumps and are done
- on the same tapes normally used by \fIbackup\fR. Mount the tape and verify
- that it is indeed the correct one by checking the label on it; one way
- to do this is
- .DS
- % cat /dev/nrmt8
- .DE
- which will show you the label--it also leaves the tape positioned after the
- end of the label and ready for dumping, since the no-rewind tape device
- is used.
- .PP
- The correct invocation for dump is of the form
- .DS
- % /etc/dump 0ufd /dev/rmt8 6250 \fI/filesystem\fR
- .DE
- where \fIfilesystem\fR is the name (e.g. /usr) of the filesystem
- you wish to dump.
- The effects of the "u" flag are important to note; if present, it causes
- the file /etc/dumpdates to keep a record of the date of this dump, and
- that record is used to decide which files will be included on subsequent
- incremental dumps. If absent, no such record will be kept, and incremental
- dumps will be based on whatever date is already in /etc/dumpdates.
- For our purpose, which is usually to "catch up" after making many
- changes to a filesystem, the "u" flag should be used.
- .NH
- Summary
- .PP
- Most of the tape labelling functions are automatic; human intervention
- should only be required if (a) we run out of spare tapes or (b) we want
- to reuse some of the (formerly spare) tapes, or (c) we have to set up
- the backup scheme on a new machine, or on new filesystems.
- SHAR_EOF
- # End of shell archive
- exit 0
-
-