home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 October / usenetsourcesnewsgroupsinfomagicoctober1994disk2.iso / unix / volume5 / backup < prev    next >
Text File  |  1986-11-30  |  32KB  |  1,138 lines

  1. Subject: backup - front end for BSD dump
  2. Newsgroups: mod.sources
  3. Approved: jpn@panda.UUCP
  4.  
  5. Mod.sources:  Volume 5, Issue 5
  6. Submitted by: Wombat <pur-ee!pucc-j.Purdue.EDU!rsk>
  7.  
  8. #    This is a shell archive.
  9. #    Remove everything above and including the cut line.
  10. #    Then run the rest of the file through sh.
  11. #----cut here-----cut here-----cut here-----cut here----#
  12. #!/bin/sh
  13. # shar:    Shell Archiver
  14. #    Run the following text with /bin/sh to create:
  15. #    README
  16. #    backup.8l
  17. #    backup.c
  18. #    backup.list
  19. #    tape.label
  20. # This archive created: Thu May 15 13:49:26 1986
  21. # By:    Wombat (Purdue University)
  22. cat << \SHAR_EOF > README
  23. Backup is a front-end for 4.[23]bsd dump, allowing non-programmers to
  24. successfully run disk-to-tape backups.  It incorporates what seems to
  25. be a sufficient amount of paranoid thinking to prevent data-destroying
  26. blunders.  I'm sending this out partially because I feel it could be of
  27. some use, and partially because I'm interested in seeing what
  28. [constructive] comments we'll receive; we're considering some changes
  29. in the way we do backups.
  30.  
  31. Note that this program is NOT a standalone backup program; it has never
  32. been tested with anything other than 4.2BSD and 4.3BSD dump.
  33.  
  34. -- 
  35. Rich Kulawiec, pucc-j!rsk, rsk@asc.purdue.edu
  36.  
  37. Contents:
  38.  
  39. README
  40. backup.8l            Man page for the "backup" command
  41.     (nroff -man backup.8l)
  42. backup.c            The backup program itself
  43.     (cc -O -DPUCC -DBSD4_2  -DALLINONE -DVERIFYTAPE -o backup backup.c)
  44. backup.list            A sample "config" file for backup
  45. tape.label            A lore document explaining how tape labels work
  46.     (nroff -ms tape.label)
  47. SHAR_EOF
  48. cat << \SHAR_EOF > backup.8l
  49. .TH BACKUP 8L PUCC
  50. .SH NAME
  51. backup \- run periodic tape dumps
  52. .SH SYNOPSIS
  53. .B /etc/backup
  54. .br
  55. .SH DESCRIPTION
  56. .I Backup
  57. reads
  58. .I /etc/backup.list
  59. and attempts the necessary invocations of 
  60. .IR dump (8)
  61. to back up (to tape) all filesystems listed therein.
  62. It asks the invoker to mount the appropriate
  63. tape, and checks the tape label to make sure that
  64. it is correct with respect to machine, filesystem,
  65. and level of backup being done.
  66. It then calls
  67. .IR dump (8)
  68. with appropriate arguments and waits
  69. for it to finish.  When dump is done,
  70. .I backup
  71. will go on to the next filesystem.
  72. .PP
  73. The backup schedule in the file
  74. .I /etc/backup.list
  75. consists of lines of the form:
  76. .nf
  77.  
  78. \ \ \ \ \ pucc-h:x0x9x9x:/usr
  79.  
  80. .fi
  81. which are interpreted as follows:
  82. .nf
  83.  
  84. \ \ \ \ \ machine-name:schedule:filesystem
  85.  
  86. .fi
  87. where ``machine-name'' must be exactly the same as the string returned by
  88. .IR hostname (2),
  89. and ``filesystem'' the same as given in /etc/fstab.
  90. The ``schedule'' is a seven character string, with one character for
  91. each day of the week, with the first character representing Sunday.
  92. The character corresponding to a given day of the week represents the level
  93. of dump for that day.  For example, the sample line indicates that for pucc-h,
  94. filesystem /usr, an epoch (level 0) dump should be done on Monday,
  95. with incremental (level 9) dumps on Wednesday and Friday.
  96. No dumps will be done on Sunday, Tuesday, Thursday, or Saturday.
  97. Note that any numeric character will cause a dump of that level
  98. to be done on that day; any non-numeric character
  99. will prevent dumps from being done on that day.
  100. Colons (``:'') are not allowed, since they are used as
  101. field separators.
  102. .PP
  103. Additionally,
  104. .I backup
  105. runs ``/etc/dump #W'' (with # being the level of the dump)
  106. and decides from its output whether a filesystem has already been
  107. backed up on the same day.
  108. If it finds that any particular filesystem
  109. has already been dumped, it will ask the
  110. operator to verify that it should be dumped again.
  111. .PP
  112. Any question that
  113. .I backup
  114. asks must be answered with 
  115. .B go
  116. or 
  117. .BR stop ,
  118. exactly.
  119. Any other answer will cause an error message to be printed,
  120. and the question will be asked again.
  121. .SH FILES
  122. .nf
  123. /etc/backup.list
  124. /etc/fstab
  125. /etc/dumpdates
  126. .fi
  127. .SH SEE ALSO
  128. dump(8)
  129. .SH BUGS
  130. .PP
  131. There's no way to cleanly restart backup (if interrupted) without
  132. repeating some work.
  133. .PP
  134. Since
  135. .I backup
  136. tries to fit all partial dumps for a machine on one tape, selecting
  137. too low a level or waiting too long between backups
  138. may cause it to run out of tape.
  139. .PP
  140. Mixing dump levels on one partial tape is not recommended.
  141. .PP
  142. Dump always claims to be rewinding the tape, even if it's not.
  143. .PP
  144. The tape labelling scheme is not fail safe.
  145. SHAR_EOF
  146. cat << \SHAR_EOF > backup.c
  147. /*
  148. *    backup.c        Rich Kulawiec, PUCC, 1985
  149. *
  150. *    Run dumps according to a fixed schedule encoded in file CONTROL.
  151. *
  152. *    File contains:
  153. *
  154. *    main()        -- main program, of course
  155. *    check_sched()    -- examine a schedule line for information
  156. *    already_done()    -- check to see if dumps already done today
  157. *    ask_for_tape()    -- ask the operator to mount the appropriate tape
  158. *    verify()    -- prompt operator to confirm tape load
  159. *    verifytape()    -- check tape label file for correctness
  160. *    write_label()    -- write a tape label file
  161. *    rundump()    -- build a dump command and run it
  162. *    query()        -- ask a question and get an answer
  163. *    tapeskip()    -- skip one file on the tapedrive
  164. *    offline()    -- take tapedrive offline
  165. *    message()    -- print a message on the terminal
  166. *
  167. *    Defining DEBUG changes backup's idea of the tape drive, and
  168. *    causes it to echo "dump" and "mt" commands instead of running them.
  169. *    It will still run dump W as needed, though.
  170. *    Depending on what's left in here, it may also print some
  171. *    additional information as  it runs.
  172. *
  173. *    Defining ALLINONE causes all partial dumps to be placed on
  174. *    the same tape.  It would probably be a bad idea to mix partial
  175. *    dump levels on that tape.
  176. *
  177. *    Defining VERIFYTAPE causes backup to check the tape for a header
  178. *    indicating whether or not it's the right tape.  Note that leaving
  179. *    this undefined will still let backup query the operator before
  180. *    firing up dump to scribble on the tape.
  181. */
  182.  
  183. #ifndef lint
  184. static char *rcsid = "$Header: /usr/src/etc/RCS/backup.c,v 1.13 85/08/19 19:49:42 rsk Exp $";
  185. #endif lint
  186. /*
  187.  * $Log:    backup.c,v $
  188.  * Revision 1.13  85/08/19  19:49:42  rsk
  189.  * Changed the handling of offline() to fix a buglet; if backuup
  190.  * ran epoch dumps using a schedule file whose last line did not
  191.  * have a '0' in the same position as the '0' for the epoch dumps
  192.  * that were run (confusing isn't it?) then what happened was that
  193.  * Level was set to, say, 'x', and the test at the end of main
  194.  * succeeded...and backup tried to take the tapedrive offline after
  195.  * already doing so.
  196.  * Therefore, the following was done:
  197.  * rundump() now calls offline() (1) all the time if ALLINONE is
  198.  * not defined (2) for all epoch dumps if ALLINONE is defined.
  199.  * To take care of multiple partial dumps, a variable "Rewind"
  200.  * was created; if it is set, then main() will call offline()
  201.  * just prior to exiting.  The only place that sets Rewind is
  202.  * inside verifytape() -- and it sets it iff it's looking at
  203.  * a partial tape and is running with ALLINONE defined.
  204.  * 
  205.  * Revision 1.12  85/08/18  20:34:51  rsk
  206.  * Added several (void)'s in front of calls to offline()
  207.  * to satisfy lint.
  208.  * 
  209.  * Revision 1.11  85/08/18  16:17:35  rsk
  210.  * Several changes.
  211.  * If DEBUG is defined, then MT_OFFLINE is fixed so that
  212.  * it just rewinds the tape rather than taking it offline.
  213.  * The series of if statements in verifytape() are now
  214.  * chained with else clauses.
  215.  * When write_label() is called in verifytape(), verifytape()
  216.  * calls itself to check the results.
  217.  * Added several calls to offline() in verifytape() to
  218.  * make sure bad tape gets noticed.
  219.  * Took out the level sanity check in write_label() since it
  220.  * really didn't do much.
  221.  * Fixed a small bug in write_label(); if ALLINONE is defined
  222.  * and this is a partial tape, then should write PARTIAL_LABEL
  223.  * rather than the name of the filesystem.  This only shows up
  224.  * if a SPARE tape is used for a partial and then the order
  225.  * of partial dumps gets re-arranged later, but it's fixed
  226.  * anyhow.
  227.  * 
  228.  * Revision 1.10  85/08/17  13:14:58  rsk
  229.  * Updated list of functions at top to reflect reality.
  230.  * 
  231.  * Revision 1.9  85/08/16  13:01:13  rsk
  232.  * Rearranged functions to reflect logical order.
  233.  * 
  234.  * Revision 1.8  85/08/16  11:29:22  rsk
  235.  * Added a little debugging code to already_done(); fixed up the
  236.  * function headers to more accurately reflect what goes on in
  237.  * each function.
  238.  * 
  239.  * Revision 1.7  85/08/16  10:58:42  rsk
  240.  * Ripped out pseudo-exit routine from last night.  Simplified
  241.  * exit codes.  Switched calls to rundump() and verify(), since
  242.  * an accidental change last night had backup trying to verify
  243.  * the tape after running the dump.  Made verify return a value
  244.  * even though it's ignored, 'cause (a) might use it later,
  245.  * and (b) couldn't figure out how to recursively return a void.
  246.  * 
  247.  * Revision 1.6  85/08/16  02:01:24  rsk
  248.  * Added a new routine that's called instead of exit() with
  249.  * a wealth (10, actually) of exit codes; might be handy if
  250.  * this is run by a script someday.  Will probably trim the
  251.  * list tomorrow, as well as fixing a known bug.
  252.  * The program is definitely easier manage now that it's
  253.  * broken up into chunks, although the growth in the number
  254.  * of (void) functions and global variables isn't very aesthetic.
  255.  * 
  256.  * Revision 1.5  85/08/16  01:31:44  rsk
  257.  * Many changes.  Several new functions, which have taken chunks
  258.  * of the large loop that used to be in main() and broken them
  259.  * up into little pieces.  Tapeok is now a global, among other
  260.  * things, and is used when putting multiple partial dumps on
  261.  * one tape to avoid attempting to reverify the tape after it's
  262.  * been done once.   Most new functions are (void) since if they
  263.  * fail we have to abort anyway; no sense testing for a return
  264.  * value and then aborting.
  265.  * 
  266.  * Revision 1.4  85/08/16  00:00:42  rsk
  267.  * Made Filesys, Tapedrive, and Schedline global
  268.  * prepatory to breaking the large loop in main()
  269.  * up into several function calls.
  270.  * 
  271.  * Revision 1.3  85/08/14  18:45:52  rsk
  272.  * Parameterized the sizes of a number of characters arrays.
  273.  * Used STRSIZE (255) for devices, filesystems, command lines,
  274.  * hostnames, lines from the schedule file, and replies from
  275.  * the operator.
  276.  * Used DATESTRSIZE (30) for dates...since all dates are in
  277.  * ctime() format, which takes 26 characters, this should
  278.  * be sufficient.
  279.  * Only non-parameterized array is "weekmask", which is
  280.  * set to 7.  This will need to be changed if another
  281.  * day is added to the week.
  282.  * 
  283.  * Revision 1.2  85/08/12  20:16:00  rsk
  284.  * Added changes for "spare" tapes, including auto-labelling.
  285.  * Slightly changed what happens when dump fails if DEBUG is defined.
  286.  * Took #ifdef DEBUG off write_label() routine.
  287.  * 
  288.  * Revision 1.1  85/08/09  16:13:05  rsk
  289.  * Initial revision
  290.  * 
  291. */
  292.  
  293. #include <stdio.h>
  294. #include <ctype.h>
  295. #include <sys/time.h>
  296.  
  297. #define    OKAY        0
  298. #define STRSIZE        255    /* Should be large enough for most everything */
  299. #define DATESTRSIZE    30    /* Should only need to be 26 (for ctime) */
  300. #define    FAIL        -1
  301. #define    DUMPFLAGS    "ufd"
  302. #define    DENSITY        "6250"
  303. #define    DUMPINFOFLAG    "W"
  304. #define    MT_SKIP        "fsf 1"
  305. #define    MYNAME        "BACKUP"
  306. #define    SPARE_LABEL    "SPARE"
  307.  
  308. #ifdef ALLINONE
  309. #define PARTIAL_LABEL    "PARTIAL"
  310. #endif ALLINONE
  311.  
  312. #ifdef DEBUG
  313. #define    DUMP        "echo /etc/dump"
  314. #define    TAPE        "dev.rmt8"
  315. #define    NTAPE        "dev.nrmt8"
  316. #define    CONTROL        "backup.list"
  317. #define    MT        "/bin/echo mt -f"
  318. #define    MT_OFFLINE    "rewind"
  319. #else DEBUG
  320. #define    DUMP        "/etc/dump"
  321. #define    TAPE        "/dev/rmt8"
  322. #define    NTAPE        "/dev/nrmt8"
  323. #define    CONTROL        "/etc/backup.list"
  324. #define    MT        "/bin/mt -f"
  325. #define    MT_OFFLINE    "offline"
  326. #endif DEBUG
  327.  
  328. #define    GO        "go"
  329. #define STOP        "stop"
  330.  
  331. /*
  332. * Different classes of errors; provides additional error message on exit.
  333. */
  334.  
  335. #define ST_OKAY        0
  336. #define ST_NOWORK    1
  337. #define ST_DUMP        2
  338. #define ST_ABORT    3
  339. #define ST_CTL        4
  340. #define ST_TAPE        5
  341. #define ST_SYSCALL    6
  342. #define ST_INTERNAL    7
  343.  
  344. char    Command[STRSIZE];    /* constructed shell commands */
  345. char    Date[DATESTRSIZE];    /* current date/time in ctime() format */
  346. char    Hostname[STRSIZE];    /* the name of this host */
  347. char    Filesys[STRSIZE];    /* name of filesystem(s) to dump */
  348. char    Tapedrive[STRSIZE];    /* name of tapedrive to use */
  349. char    Schedline[STRSIZE];    /* a line read out of schedule file */
  350. char    Weekmask[7];        /* one slot per day of week */
  351. char    Level;            /* level of this dump */
  352. #ifdef ALLINONE
  353. int    Tapeok = FAIL;        /* Has this tape been verified? */
  354. int    Rewind = FAIL;        /* Does tape need to be rewound at end? */
  355. #endif ALLINONE
  356.  
  357. struct tm    *localtime();
  358. FILE    *popen();
  359. void    message();
  360. void    check_sched();
  361. void    ask_for_tape();
  362. void    rundump();
  363. char    *strcpy();
  364. char    *strncpy();
  365. char    *index();
  366. char    *rindex();
  367. char    *gets();
  368. long    time();
  369.  
  370. main()
  371. {
  372.     FILE    *fp;
  373.     long    timebuf;        /* seconds since epoch */
  374.     struct    tm *ldate;        /* date broken down into fields */
  375.  
  376.     if(gethostname(Hostname,sizeof(Hostname)) == FAIL) {
  377.         message("gethostname() failed");
  378.         exit(ST_SYSCALL);
  379.     }
  380.  
  381.     (void) time(&timebuf);
  382.     (void) strcpy(Date,ctime(&timebuf));
  383.     ldate = localtime(&timebuf);
  384.  
  385.     if( (fp = fopen(CONTROL,"r")) == NULL) {
  386.         message("can't open control file");
  387.         exit(ST_SYSCALL);
  388.     }
  389.  
  390.     /*
  391.     * Read the schedule file; make rudimentary checks for integrity.
  392.     */
  393.  
  394.     while( fscanf(fp,"%s",Schedline) != FAIL) {
  395.  
  396.     /*
  397.     * Break out the fields of this line of the schedule.
  398.     */
  399.  
  400.         check_sched(ldate->tm_wday);
  401.  
  402.     /*
  403.     * If this line of the schedule file pertains to this machine,
  404.     * and if there's a digit in the dump schedule for today,
  405.     * then we probably have some work to do.
  406.     */
  407.  
  408.         if( (strncmp(Schedline,Hostname,strlen(Hostname)) == OKAY)
  409.             && isascii(Level) && isdigit(Level) ) {
  410.  
  411.     /*
  412.     * Check to see if we've already dumped this filesystem today.
  413.     */
  414.  
  415.             if( already_done() == OKAY ){
  416.                 (void) printf("%s: filesystem %s has already been dumped today\n",MYNAME,Filesys);
  417.                 if(query ("Do you want to dump it again?") != OKAY)
  418.                     continue;
  419.             }
  420.     /*
  421.     * Request the proper tape from the operator.
  422.     */
  423.             ask_for_tape();
  424.  
  425.     /*
  426.     * If ALLINONE is defined, then first pass through here will
  427.     * set Tapeok to OKAY, assuming it's the right tape,
  428.     * when we're putting multiple partial dumps on one tape.
  429.     * Successive passes won't bother to verify the tape.
  430.     */
  431.  
  432. #ifdef ALLINONE
  433.             if(Level == '0' )
  434.                 (void) verify();
  435.             else 
  436.                 if(Tapeok == FAIL)
  437.                     (void) verify();
  438. #else ALLINONE
  439.             (void) verify();
  440. #endif ALLINONE
  441.  
  442.     /*
  443.     * Build the call the dump, and run it.  This does the real work.
  444.     * Note that rundump() also has the responsibility for rewinding
  445.     * the tape and taking the drive offline if needed.
  446.     */
  447.             rundump();
  448.         }
  449.     }
  450.     message("finished");
  451.     (void) fclose(fp);
  452.  
  453.     /*
  454.     * If in ALLINONE mode, then take tape drive offline at the
  455.     * end of series of partials.  If these were epoch dumps,
  456.     * the drive will already be offline...see above.
  457.     * Note: this is one of the places where assumptions about
  458.     * NOT mixing epoch and partial dumps are important.
  459.     */
  460.  
  461. #ifdef ALLINONE
  462.     if (Rewind == OKAY)
  463.         (void) offline();
  464. #endif ALLINONE
  465.  
  466.     exit(ST_OKAY);
  467. }
  468. /*
  469. * check_sched() -- examine line of schedule file for work to do
  470. *
  471. * Parameters: day_of_week -- part of ldate structure
  472. *
  473. * Global Variables:    Schedline, Filesys, Weekmask, Level
  474. *
  475. * Returns: nothing
  476. *
  477. * Side Effects: sets Filesys, Weekmask, and Level according to schedule line
  478. *
  479. */
  480. void check_sched(day_of_week)
  481. int day_of_week;
  482. {
  483.     char    *charptr;        /* scratch character pointer */
  484.  
  485.  
  486.     if( (charptr = rindex(Schedline,':')) == NULL) {
  487.         message("Garbled control file");
  488.         exit(ST_CTL);
  489.     }
  490.     (void) strcpy(Filesys,++charptr);    /* got filesystem */
  491.  
  492.     if( (charptr = index(Schedline,':')) == NULL) {
  493.         message("Garbled control file");
  494.         exit(ST_CTL);
  495.     }
  496.     (void) strncpy(Weekmask,++charptr,7);    /* got dump levels */
  497.  
  498.     /*
  499.     * Get today's dump level.
  500.     */
  501.  
  502.     Level = Weekmask[day_of_week];
  503. }
  504. /*
  505. * already_done() -- see if this filesystem was dumped today
  506. *
  507. * Parameters:    none
  508. *
  509. * Global Variables: Command, Date, Filesys, Level
  510. *
  511. * Returns:    OKAY -- if already dumped Filesystem at level Level today.
  512. *        FAIL -- otherwise
  513. *
  514. * Side Effects: runs "dump W" to gather information
  515. *
  516. */
  517. already_done()
  518. {
  519.     char    last_device[STRSIZE];    /* device filesystem is mounted on */
  520.     char    last_filesys[STRSIZE];    /* name of filesystem */
  521.     char    last_level;        /* level of last dump */
  522.     char    last_date[DATESTRSIZE];    /* ctime format date of last dump */
  523.     char    replybuf[STRSIZE];    /* holds a line of reply from dump W */
  524.     FILE    *sp;            /* stream pointer */
  525.     int    returnval;        /* temporary place for return value */
  526.  
  527.     returnval = FAIL;
  528.  
  529.     /*
  530.     * Build a command line for dump that will cause it to print
  531.     * its idea of which filesystems need to be dumped.
  532.     * Do this rather than reading /etc/dumpdates and /etc/fstab
  533.     * and deciphering everything ourself.
  534.     */
  535.  
  536.     (void) sprintf(Command,"%s %c%s",DUMP,Level,DUMPINFOFLAG);
  537.     (void) fflush(stdout);
  538.  
  539.     /*
  540.     * Run the command, and read its output.
  541.     */
  542.  
  543.     if( (sp = popen(Command,"r")) == NULL) {
  544.         message("popen() failed");
  545.         exit(ST_SYSCALL);
  546.     }
  547.  
  548.     /*
  549.     * Eat first line of dump's verbose output
  550.     */
  551.     
  552.     if(fscanf(sp,"%[^\n]\n",replybuf) == FAIL) {
  553.         message("can't fscanf() reply from dump");
  554.         exit(ST_SYSCALL);
  555.     }
  556.  
  557.     /*
  558.     * Grab all necessary fields out of dump's output.
  559.     * Slight trickiness; must eat everything up to the first '('
  560.     * in order to get filesystem name; thus, last_device
  561.     * might contain some garbage characters.  We don't care, since
  562.     * we don't really use it.  Then we eat everything to ')'
  563.     * in order to get filesystem name.
  564.     */
  565.  
  566.     while (fscanf(sp,"%[^(]( %[^)]) Last dump: Level %c, Date %[^\n]\n",last_device,last_filesys,&last_level,last_date) != FAIL) {
  567.  
  568.     /*
  569.     * If this line of "dump W" output pertains to the filesystem in
  570.     * question, and if the dump level matches the current one,
  571.     * AND if the date of the last dump appears to be today,
  572.     * then it would seem that this filesystem has been dumped today.
  573.     * Note that fact, and just keep going; must read all of dump's
  574.     * output to avoid causing SIGPIPE on early pclose().
  575.     */
  576.         if(strncmp(last_filesys,Filesys,strlen(Filesys)) == OKAY 
  577.             && last_level == Level
  578.             && strncmp(last_date,Date,10) == OKAY)
  579.             returnval = OKAY;
  580.     }
  581.  
  582.     if( pclose(sp) == FAIL) {
  583.         message("pclose() failed");
  584.         exit(ST_SYSCALL);
  585.     }
  586.  
  587. #ifdef DEBUG
  588.     (void) printf("already_done() returning %s\n",returnval == OKAY ? "OKAY" : "FAIL");
  589.     return(query("Type go for OKAY(yes), stop for FAIL(no)."));
  590. #else DEBUG
  591.     return(returnval);
  592. #endif DEBUG
  593. }
  594. /*
  595. * ask_for_tape() -- request the operator to mount the proper tape
  596. *
  597. * Parameters: none
  598. *
  599. * Global Variables: Tapedrive, Hostname, Filesys, Date
  600. *
  601. * Returns: nothing
  602. *
  603. * Side Effects: none
  604. *
  605. */
  606. void ask_for_tape()
  607. {
  608.  
  609.     /*
  610.     * Ask for the proper tape; use rewind device for epochs,
  611.     * no-rewind device for incrementals.
  612.     * Note that if this tape has already been verified, i.e.
  613.     * we compiled with ALLINONE defined and Tapeok has been
  614.     * set to OKAY by a previous call to verify(), then we do nothing.
  615.     */
  616.  
  617.     if(Level == '0') {
  618.         (void) strcpy(Tapedrive,TAPE);
  619.         (void) printf("%s: get the FULL backup tape for machine %s, filesystem %s for %.3s\n",MYNAME,Hostname,Filesys,Date);
  620.     }
  621.     else {
  622.         (void) strcpy(Tapedrive,NTAPE);
  623. #ifdef ALLINONE
  624.         if (Tapeok == FAIL)
  625.             (void) printf("%s: get the PARTIAL backup tape for machine %s for %.3s\n",MYNAME,Hostname,Date);
  626.         
  627. #else ALLINONE
  628.         (void) printf("%s: get the PARTIAL backup tape for machine %s, filesystem %s for %.3s\n",MYNAME,Hostname,Filesys,Date);
  629. #endif ALLINONE
  630.  
  631.     }
  632. }
  633. /*
  634. * verify() -- prompt operator to mount tape
  635. *
  636. * Parameters:    none
  637. *
  638. * Global Variables: none
  639. *
  640. * Returns:    OKAY if tape verified.
  641. *
  642. * Side Effects: exits if operator wants to quit
  643. *
  644. */
  645. verify()
  646. {
  647.     if( query("Is the correct tape mounted?") == OKAY) {
  648.         if( verifytape() == OKAY) {
  649.             message("tape verified");
  650.             return(OKAY);
  651.         }
  652.         else {
  653.             message("\007\007\007INCORRECT TAPE MOUNTED!!");
  654.             message("Get the correct tape!!");
  655.             return(verify());
  656.         }
  657.     }
  658.     else {
  659.         if( query ("Do you want to continue the backup?") == OKAY) {
  660.             return(verify());
  661.         }
  662.         else {
  663.             message("aborting on operator command");
  664.             exit(ST_ABORT);
  665.         }
  666.     }
  667.     /*
  668.     * We should never get here; abort if it happens.
  669.     */
  670.  
  671.     message("Something very bad has happened in verify()");
  672.     exit(ST_INTERNAL);
  673.     
  674. }
  675. /*
  676. * verifytape() -- check tape label vs. current filesystem
  677. *          Is smart enough to recognize a spare tape; also
  678. *          recognizes partial tape labels if in ALLINONE mode.
  679. *
  680. * Parameters:    none
  681. *
  682. * Global Variables: Hostname, Date, Filesys, Level
  683. *
  684. * Returns:    OKAY -- if this is the correct w.r.t. filesys & level
  685. *        FAIL -- otherwise
  686. *
  687. * Side Effects: leaves tape positioned at end of label file if correct tape.
  688. *        writes a new label if this is a spare tape
  689. *        set the Rewind flag if ALLINONE is defined and this is
  690. *            a multiple partial dump tape
  691. *
  692. */
  693. verifytape()
  694. {
  695. #ifdef VERIFYTAPE
  696.     char    last_hostname[STRSIZE];    /* hostname as written on tape */
  697.     char    last_date[DATESTRSIZE];    /* date as written on tape */
  698.     char    last_filesys[STRSIZE];    /* filesystem as written on tape */
  699.     char    last_level;        /* dump level as written on tape */
  700.     FILE    *fp;
  701.  
  702.     if( (fp = fopen(TAPE,"r")) == NULL) {
  703.         message("can't open tapedrive");
  704.         exit(ST_SYSCALL);
  705.     }
  706.  
  707. #ifdef DEBUG
  708.     (void) printf("%s: searching for label: %s %.3s %s %c\n",MYNAME,Hostname,Date,Filesys,Level);
  709. #endif DEBUG
  710.  
  711.     if(fscanf(fp,"%s %s %s %c",last_hostname,last_date,last_filesys,&last_level) == FAIL) {
  712.         message("can't read tape label");
  713.         exit(ST_SYSCALL);
  714.     }
  715.  
  716. #ifdef DEBUG
  717.     (void) printf("%s:    found this label: %s %.3s %s %c\n",MYNAME,last_hostname,last_date,last_filesys,last_level);
  718. #endif DEBUG
  719.  
  720.     (void) fclose(fp);
  721.  
  722.     /*
  723.     * If the label appears to be okay, i.e. all four fields match
  724.     * our idea of what should be there, then skip over the label
  725.     * file and get ready to run dump.
  726.     */
  727.  
  728.     if( strcmp(Hostname,last_hostname) == OKAY
  729.         && strncmp(Date,last_date,3) == OKAY
  730.         && strcmp(Filesys,last_filesys) == OKAY
  731.         && Level == last_level) {
  732.         (void) tapeskip();
  733.         return(OKAY);
  734.     }
  735.  
  736. #ifdef ALLINONE
  737.  
  738.     /*
  739.     * If we're dealing with multiple partial dumps on one tape,
  740.     * then we need to check for the special label marking this
  741.     * tape; therefore, a slightly modified test is made.
  742.     *
  743.     * Must also set Rewind so that tape will be taken care of
  744.     * when all dumps are done, just before we exit...see the
  745.     * end of main().
  746.     */
  747.  
  748.     else if( strcmp(Hostname,last_hostname) == OKAY
  749.         && strncmp(Date,last_date,3) == OKAY
  750.         && strcmp(PARTIAL_LABEL,last_filesys) == OKAY
  751.         && Level != '0'
  752.         && Level == last_level) {
  753.         Tapeok = OKAY;
  754.         Rewind = OKAY;
  755.         (void) tapeskip();
  756.         return(OKAY);
  757.     }
  758.  
  759. #endif ALLINONE
  760.  
  761.     /*
  762.     * This might be a spare tape; if so, ignore the date and dumplevel
  763.     * fields; just check the host, and look for SPARE_LABEL in place
  764.     * of the filesystem name.
  765.     */
  766.  
  767.     else if (strcmp(Hostname,last_hostname) == OKAY
  768.         && strncmp(last_filesys,SPARE_LABEL,strlen(SPARE_LABEL)) == OKAY) {
  769.         message("This looks like a spare tape.");
  770.         if( query("Go ahead and use it?") == OKAY) {
  771.             if( write_label() == OKAY) {
  772.                 return(verifytape());
  773.             }
  774.             else {
  775.                 (void) offline();
  776.                 return(FAIL);
  777.             }
  778.         }
  779.         else {
  780.             (void) offline();
  781.             return(FAIL);
  782.         }
  783.     }
  784.     else {
  785.         (void) offline();
  786.         return(FAIL);
  787.     }
  788.  
  789. #else VERIFYTAPE
  790.     return(OKAY);
  791. #endif VERIFYTAPE
  792. }
  793. /*
  794. * write_label() -- write a label on a spare tape
  795. *
  796. * Parameters:    none
  797. *
  798. * Global Variables:    Hostname, Date, Filesys, Level
  799. *
  800. * Returns:    OKAY if label successfully written
  801. *        FAIL otherwise
  802. *
  803. * Side Effects: writes a tiny label file on the beginning of the tape
  804. *
  805. */
  806. write_label()
  807. {
  808.     FILE    *fp;
  809.  
  810.     if( (fp = fopen(TAPE,"w")) == NULL) {
  811.         message("can't open tapedrive");
  812.         exit(ST_SYSCALL);
  813.     }
  814.  
  815. #ifdef ALLINONE
  816.     if( Level == '0')
  817.         fprintf(fp,"%s %.3s %s %c\n",Hostname,Date,Filesys,Level);
  818.     else
  819.         fprintf(fp,"%s %.3s %s %c\n",Hostname,Date,PARTIAL_LABEL,Level);
  820. #else ALLINONE
  821.     fprintf(fp,"%s %.3s %s %c\n",Hostname,Date,Filesys,Level);
  822. #endif ALLINONE
  823.  
  824.     (void) fclose(fp);
  825.  
  826.     return(OKAY);
  827. }
  828. /*
  829. * rundump() -- call the dump program to do the real work
  830. *
  831. * Parameters: none
  832. *
  833. * Global Variables: Level, Tapedrive, Filesys, Command
  834. *
  835. * Returns: nothing
  836. *
  837. * Side Effects: runs the actual dump
  838. *         queries the operator for advice if dump fails
  839. *
  840. */
  841. void rundump()
  842. {
  843.     int    status;            /* return status of dump command */
  844.  
  845.     /*
  846.     * Build the proper command line to call dump.
  847.     */
  848.  
  849.     (void) sprintf(Command,"%s %c%s %s %s %s",DUMP,Level,DUMPFLAGS,Tapedrive,DENSITY,Filesys);
  850.  
  851.     (void) printf("%s: running %s\n",MYNAME,Command);
  852.     (void) fflush(stdout);
  853.  
  854.     /*
  855.     * Call dump; check the return status to make sure it's alright.
  856.     */
  857.  
  858.     if( (status = system(Command)) != OKAY) {
  859.         (void) printf("%s: dump failed, status=%d\n",MYNAME,(status>>8));
  860. #ifdef DEBUG
  861.         if( query("Want to go on?") == FAIL) {
  862.             (void) offline();
  863.             exit(ST_ABORT);
  864.         }
  865. #else DEBUG
  866.         (void) offline();
  867.         exit(ST_DUMP);
  868. #endif DEBUG
  869.     }
  870.  
  871. #ifdef ALLINONE
  872.     if(Level == '0')
  873.         (void) offline();
  874. #else ALLINONE
  875.     (void) offline();
  876. #endif ALLINONE
  877. }
  878. /*
  879. * query(question) -- prints "question" on the tty, then waits for answer
  880. *
  881. * Parameters:    question -- a null-terminated string
  882. *
  883. * Global Variables: none
  884. *
  885. * Returns:    OKAY -- if question eventually answered with GO
  886. *        FAIL -- if question eventually answered with STOP
  887. *
  888. * Side Effects: recursive routine
  889. *
  890. */
  891. query(question)
  892. char    *question;
  893. {
  894.     char    reply[STRSIZE];        /* Whatever the user types in reply */
  895.  
  896.     (void) printf("%s: %s <Answer %s or %s only>: ",MYNAME,question,GO,STOP);
  897.     /*
  898.     * Print the question and wait for an answer.
  899.     * If we don't understand the answer, retry (recursively).
  900.     */
  901.  
  902.     if( gets(reply) == NULL) {
  903.         message("can't talk to console");
  904.         exit(ST_SYSCALL);
  905.     }
  906.     else if( strcmp(reply,GO) == 0 ) {
  907.         return(OKAY);
  908.     }
  909.     else if( strcmp(reply,STOP) == 0) {
  910.         return(FAIL);
  911.     }
  912.     else {
  913.         message("incorrect response, try again");
  914.         return(query(question));
  915.     }
  916.  
  917.     /*
  918.     * We should never get here; abort if it happens.
  919.     */
  920.  
  921.     message("Something very bad has happened in query()");
  922.     exit(ST_INTERNAL);
  923. }
  924. /*
  925. * takeskip() -- skip forward one file on tape
  926. *
  927. * Parameters: none
  928. *
  929. * Global Variables:    Command
  930. *
  931. * Returns:    OKAY
  932. *
  933. * Side Effects: skips forward one file on the tape via "mt"
  934. *
  935. */
  936. tapeskip()
  937. {
  938.     int    status;
  939.  
  940.     (void) sprintf(Command,"%s %s %s",MT,NTAPE,MT_SKIP);
  941.  
  942.     if( (status = system(Command)) != OKAY) {
  943.         (void) printf("%s: mt failed, status=%d\n",MYNAME,(status>>8));
  944.         exit(ST_TAPE);
  945.     }
  946.  
  947.     return(OKAY);
  948. }
  949. /*
  950. * offline() -- takes tapedrive offline
  951. *
  952. * Parameters:    none
  953. *
  954. * Global Variables:    Command, Tapedrive
  955. *
  956. * Returns:    OKAY
  957. *
  958. * Side Effects:    calls "mt" to do the work; leaves tape drive off line.
  959. *
  960. */
  961. offline()
  962. {
  963.     int    status;
  964.  
  965.     (void) sprintf(Command,"%s %s %s",MT,Tapedrive,MT_OFFLINE);
  966.  
  967.     if( (status = system(Command)) != OKAY) {
  968.         (void) printf("%s: mt failed, status=%d\n",MYNAME,(status>>8));
  969.         exit(ST_TAPE);
  970.     }
  971.  
  972.     return(OKAY);
  973. }
  974. /*
  975. * message(msg) -- print a message labelled with name of this program on tty
  976. *
  977. * Parameters:    msg -- the string to print
  978. *
  979. * Global Variables: none
  980. *
  981. * Returns: nothing
  982. *
  983. * Side Effects: none
  984. *
  985. */
  986. void message(msg)
  987. char    *msg;
  988. {
  989.     (void) printf("%s: %s\n",MYNAME,msg);
  990. }
  991. SHAR_EOF
  992. cat << \SHAR_EOF > backup.list
  993. pucc-h:x0x9x9x:/
  994. pucc-h:x0x9x9x:/sys
  995. pucc-h:x0x9x9x:/usr
  996. pucc-h:x0x9x9x:/usr/spool
  997. pucc-i:xx0x9x9:/
  998. pucc-i:xx0x9x9:/sys
  999. pucc-i:xx0x9x9:/usr
  1000. pucc-i:xx0x9x9:/usr/pub
  1001. pucc-i:xx0x9x9:/usr/spool
  1002. SHAR_EOF
  1003. cat << \SHAR_EOF > tape.label
  1004. .LP
  1005. .nr LT 6.5i
  1006. .lt 6.5i
  1007. .nr LL 6.5i
  1008. .ll 6.5i
  1009. .TL
  1010. How backup tape labels work.
  1011. .AU
  1012. Rich Kulawiec
  1013. .NH
  1014. Overview 
  1015. .PP
  1016. Our backup tapes are \*Qlabelled\*U on the autoload ring (in writing)
  1017. and on the tape itself (in a small file).  This document explains how
  1018. that system works and how to label tapes.
  1019. .NH
  1020. What's in a label
  1021. .PP
  1022. A sample tape label file looks like this:
  1023. .DS
  1024. pucc-j Mon /usera 0
  1025. .DE
  1026. This means that the tape in question is supposed to be used to do
  1027. a level 0 dump of /usera on pucc-j on Monday.  If someone tries
  1028. to use it for any other dump, \fIbackup(1l)\fR will stop them.
  1029. \fBThere is no way to force backup to override this.\fR
  1030. The only way out is manual intervention.
  1031. .PP
  1032. The whitespace-separated fields in the label file have meaning as follows:
  1033. .DS
  1034. .nf
  1035. hostname:    eg. \*Qpucc-j\*U, as returned by gethostname()
  1036. day-of-week: eg. \*QMon\*U, as returned in the first 3 characters of ctime()
  1037. filesystem:  eg. \*Q/usera\*U, as shown in /etc/fstab
  1038. dump level:  eg. \*Q0\*U, a single digit between 0 and 9
  1039. .fi
  1040. .DE
  1041. .NH
  1042. How backup uses labels
  1043. .PP
  1044. Before \fIbackup\fR will scribble on a tape, it reads the label file and
  1045. compares it against its idea of what it's about to try to do.
  1046. If everything looks okay, it will skip past that file and write
  1047. the dump on tape.
  1048. However,
  1049. if any of the four fields fail to match
  1050. (with certain exceptions, see below),
  1051. it complains vigorously and demands a different tape.
  1052. .NH
  1053. The exceptions
  1054. .PP
  1055. Since we put all of the partial backups for an entire machine on
  1056. one tape (at least for the moment) that tape is labelled with
  1057. the string \*QPARTIAL\*U where the filesystem should be.
  1058. Incidentally, partial backup tapes only have one label at beginning
  1059. of the tape\(emthere's nothing between the actual dumps.
  1060. .PP
  1061. The other case we have to discuss concerns the pool of spare tapes.
  1062. This pool is actually several small pools of several tapes per machine,
  1063. each of which has been labelled as follows:
  1064. .DS
  1065. hostname sss SPARE c
  1066. .DE
  1067. where \*Qhostname\*U is as above; \*Qsss\*U is just a scratch character string;
  1068. \*QSPARE\*U is the string that tells backup that this is a spare tape; and
  1069. \*Qc\*U is just a scratch character.
  1070. .PP
  1071. When a spare tape is presented to \fIbackup\fR, it verifies that it is indeed
  1072. a spare, and that it's for the correct machine, and then writes a \fIreal\fR
  1073. label file on it.  In other words, the tape is no longer a spare.
  1074. .NH
  1075. Creating a tape label file
  1076. .PP
  1077. Simple; you can just \*Qecho\*U the information onto that tape; or type it
  1078. into a file and \*Qcat\*U it on, or whatever.  Make sure that you write it
  1079. on the tape at 6250 bpi using the RAW device, since \fIbackup\fR
  1080. runs at that density.
  1081. It's also important to point out that writing a label file on a tape
  1082. which already contains an old label file and a dump image will make the dump
  1083. inacessible, since a double EOF will be written after the label file.
  1084. Make \fIcertain\fR that the dump isn't needed before rewriting the label file.
  1085. .PP
  1086. It's also easy to check a label file, just by using cat or dd to read the
  1087. first file on the tape.   Again, make sure you use the 6250 bpi RAW device.
  1088. Here's an example:
  1089. .DS
  1090. % echo \*Qpucc-j Mon /usera 0\*U > /dev/rmt6250
  1091.  
  1092. % cat /dev/rmt6250
  1093. pucc-j Mon /usera 0
  1094. .DE
  1095. .NH
  1096. Faking it
  1097. .PP
  1098. Occasionally, due to a failed backup or a massive change in a filesystem
  1099. between epoch dumps, there is a need to do a manual dump.  This is a
  1100. slightly dangerous and tricky business, and should not be attempted unless
  1101. you understand \fBexactly\fR what you are doing.  Caveat emptor.
  1102. .PP
  1103. Generally speaking, these "special-case" dumps are epoch dumps and are done
  1104. on the same tapes normally used by \fIbackup\fR.  Mount the tape and verify
  1105. that it is indeed the correct one by checking the label on it; one way
  1106. to do this is
  1107. .DS
  1108. % cat /dev/nrmt8
  1109. .DE
  1110. which will show you the label--it also leaves the tape positioned after the
  1111. end of the label and ready for dumping, since the no-rewind tape device
  1112. is used.
  1113. .PP
  1114. The correct invocation for dump is of the form
  1115. .DS
  1116. % /etc/dump 0ufd /dev/rmt8 6250 \fI/filesystem\fR
  1117. .DE
  1118. where \fIfilesystem\fR is the name (e.g. /usr) of the filesystem
  1119. you wish to dump.
  1120. The effects of the "u" flag are important to note; if present, it causes
  1121. the file /etc/dumpdates to keep a record of the date of this dump, and
  1122. that record is used to decide which files will be included on subsequent
  1123. incremental dumps.  If absent, no such record will be kept, and incremental
  1124. dumps will be based on whatever date is already in /etc/dumpdates.
  1125. For our purpose, which is usually to "catch up" after making many
  1126. changes to a filesystem, the "u" flag should be used.
  1127. .NH
  1128. Summary
  1129. .PP
  1130. Most of the tape labelling functions are automatic; human intervention
  1131. should only be required if (a) we run out of spare tapes or (b) we want
  1132. to reuse some of the (formerly spare) tapes, or (c) we have to set up
  1133. the backup scheme on a new machine, or on new filesystems.
  1134. SHAR_EOF
  1135. #    End of shell archive
  1136. exit 0
  1137.  
  1138.