home *** CD-ROM | disk | FTP | other *** search
/ Education Sampler 1992 [NeXTSTEP] / Education_1992_Sampler.iso / Utilities / TarChive / TapeLibrary.perl < prev    next >
Text File  |  1992-08-24  |  31KB  |  899 lines

  1. ####################################################################
  2. #                        TapeLibrary.perl                          #
  3. #                                                                  #
  4. #  This set of routines handles tar files to tape in a flexible    #
  5. # and useful manner, doing a lot of error checking along the way.  #
  6. #                          It includes:                            #
  7. #          Interfacing to the mt tape movement commands.           #
  8. #                   Rudimentary tape ownership.                    #
  9. #               Status checking and error recovery.                #
  10. #     Creation of labeled backup logs in the users directory.      #
  11. #                                                                  #
  12. #               To use this library you will need:                 #
  13. #                      Perl 4.10 or higher.                        #
  14. #                     gnutar 1.10 or higher.                       #
  15. #                                                                  #
  16. #        Next binaries & source for mtsense are included.          #
  17. #                                                                  #
  18. # The library has a few things hardwired in that you will need to  #
  19. # customize to your setup. e.g. which hosts have DAT drives, which #
  20. #                      have exebyte drives.                        #
  21. #                                                                  #
  22. # It was originally designed for use on Nexts. If porting to other #
  23. # machines, you will need to modify TapeStatus, which parses the   #
  24. #  output of mt status. You will also have to recompile mtsense    #
  25. #                       for your platform.                         #
  26. #                                                                  #
  27. #    This file can be located anywhere, but the scripts assume     #
  28. #           a default location of /usr/local/lib/perl.             #
  29. #                                                                  #
  30. #                                                                  #
  31. ####################################################################
  32.  
  33.  
  34. ####################################################################
  35. #                     Set up Global variables                      #
  36. #                      and initialize drive.                       #
  37. ####################################################################
  38.  
  39. $true =  1;
  40. $false =  0;
  41.  
  42. #    reset is used to keep track of error recovery status when 
  43. #    there is a SCSI bus reset.
  44. $reset = $false;
  45.  
  46. #     tabcount is for spacing in the debug display routine Say
  47. $tabcount = 0;
  48.  
  49. #     TapeMode is either read or write.  Determines certain actions
  50. #    in error recover.
  51. $Read = $false;
  52. $Write = $true;
  53. $TapeMode = $Read;
  54.  
  55. # tarnum is the number of the current tar we're working on.
  56. $tarnum = 0;    
  57.  
  58. #    gtar is the command used for taring.  If not on your standard
  59. #    path, fill in path here.
  60. $gtar= "gnutar";
  61.  
  62. #    mtsense is the command used to set drive controller and drive
  63. #    to the chosen mode for that drive.
  64. $mtsense = "/u/arafel/adm/backup/mtsense";
  65.  
  66.  
  67. #    Turn flush on (Output from script and from external commands
  68. #    seems to appear out of sequence sometimes)
  69.  
  70. select(STDERR); $|=1;        
  71. select(STDOUT); $|=1;
  72.  
  73. #    Create a datestring of the form dd-MON-yy.
  74. #    This datestring is used as part of the name of all log files.
  75.  
  76. ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime;
  77. $MonOfYear    = (Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec)[$mon];
  78. $DateString    = $mday."-".$MonOfYear."-".$year;    
  79.  
  80. #    A seperate routine Say checks the value of DEBUG.  If it's YES
  81. #    then Say prints it's message, indented according to the depth of 
  82. #    the subroutine call.  Otherwise it says nothing. 
  83.  
  84. $DEBUG= $ENV{DEBUG};
  85.  
  86. #    Customize:
  87. #    nrxt0 is the non-rewind exabyte driver. nrst0 for other scsi
  88. #    tape drives.  We have both, so we first check the environment 
  89. #    variable TAPE.  If it's blank, then we make the decision on the
  90. #    basis of what host we're running on.  The use of the environment 
  91. #    variable makes it easy to test additional drivers.
  92. #
  93.  
  94. $TAPE = $ENV{TAPE};
  95. if  ("$TAPE" eq "") { 
  96.      if (`/bin/hostname` eq "arafel\n" ) { $TAPE = "/dev/nrst0";}
  97.     else { $TAPE ="/dev/nrxt0"; }
  98.     }
  99. #print "TapeLibrary thinks it should use tape device $TAPE \n";
  100.  
  101. #    TapeMessage is a global variable that is set to the decimal
  102. #    SCSI SenseKey byte by any routine that moves the tape.    
  103.  
  104. #    ErrorCode is set by the various routines that verify the label
  105. #    and permissions for writing on the tape.
  106.  
  107. $TapeMessage = 0;
  108. $ErrorCode = 0;
  109.  
  110. #    Since the previous tape operation may have ended with an error
  111. #   we call TapeStatus twice so that we know what state the drive is in NOW
  112.  
  113. &TapeStatus; &TapeStatus;  #clear previous error conditions
  114.  
  115. #    Not being very trusting, we will set the tape controller 
  116. #    AND the drive to specified format.  
  117.  
  118. &SetupDrive;
  119.  
  120.  
  121. ####################################################################
  122. #                                                                  #
  123. #                      sub SetupDrive                              #
  124. #            Sets the driver to fixed blocks of 8192,              #
  125. #          and calls a mode select to set the drive too.           #
  126. #   This routine needs to be called every time the drive resets    #
  127. #    or the host reboots. Thus it is called on startup and on      #
  128. #                    certain error recoveries                      #
  129. #                                                                  #
  130. ####################################################################
  131. #    Customize:
  132. #    I decided on a common format of 8192 bytes, fixed block as being the 
  133. #    easiest to maintain for portability.  You may decide to format your 
  134. #    tapes differently. 
  135.  
  136. sub SetupDrive {
  137.     $tabcount++;
  138.     &Say("Setting Tape Block Size");
  139.     local(@message);
  140.     @message = `$mtsense -b 8192 `;
  141.     $mtreturn = $?;
  142.     print @message, $mtreturn, "\n";
  143.     $mtreturn = $?;
  144.     &TapeStatusTrans(&TapeStatus);
  145.     &Say("Setting tape driver to fixed blocks of size 8192");
  146.     $tabcount--;
  147.     }
  148.  
  149.  
  150. ####################################################################
  151. #                          sub TapeReady                           #
  152. #                                                                  #
  153. #             It seems to be easy to get Exabyte tape              #
  154. #      drives confused. TapeReady has evoloved through many        #
  155. #          incarnations before settling into this form.            #
  156. #                                                                  #
  157. #         TapeReady depends on a global variable set by            #
  158. #       TapeStatus: This is TapeMessage, which is the SCSI         #
  159. #                         sense key byte.                          #
  160. #                                                                  #
  161. #      TapeReady calls TapeStatus, looks at the output, and        #
  162. #    decides whether the drive is ready to go to work, if it's     #
  163. #      still busy with a previous command, or if the device        #
  164. #      glitched, and has reset, in which case it's state is        #
  165. #                             unknown                              #
  166. #                                                                  #
  167. #           Tapeready respectively returns, waits, and             #
  168. #         resets the tape position in each of these three          #
  169. #                         circumstances.                           #
  170. #                                                                  #
  171. ####################################################################
  172. sub TapeReady {
  173.     $tabcount++;
  174.     &Say("Tapeready...");
  175.     $TRcount=0;
  176.     do {
  177.         &TapeStatusTrans(&TapeStatus);
  178.         if ($TapeMessageStat == 8) { 
  179.             &Say("Drive is still busy with previous command.");
  180.             #  I've yet to get one of these.  Next ioctl
  181.             #  intercepts and returns on standard error.
  182.             }
  183.  
  184.         if (($TapeMessage & 6) == 6) { 
  185.             # message: device reset.
  186.             # This typically will occur about 5% of the time.
  187.             # It has caused me an unreal amount of grief
  188.             # as the current command is aborted, and 
  189.             # the tape rewound.
  190.             &Say("TapeReady: Device Reset while executing");
  191.             if ($reset >= 0 ) {&ResetTapePosition;}
  192.             $tabcount--;
  193.             return;
  194.             }
  195.  
  196.         sleep  5 * $TRcount++;
  197.         # sleeps start at 0 and increase in steps of 5.  Some 
  198.         # drives get unhappy if they receive a command while 
  199.         # resetting.  With this, eventually the sleep gets long
  200.         # enough to work.  You may wish to change the 5 to a higher
  201.         # number if you are getting repeated waits.
  202.         if ($TapeMessage == 512 ) { #i/o error (device is busy)
  203.             print "...waiting ";
  204.             }
  205.         if ($TRcount == 20) {# Probably no tape in drive
  206.             # You want this to time out eventually.  I put a tape in 
  207.             # one day to do a test.  Two copies of the program running
  208.             # from the weekend tried to grab the tape out from under me.
  209.             print "Tape drive is not ready after $TRcount tries\n";
  210.             exit(37); # 37??  Seemed that odd behaviour deserves odd numbers
  211.             } 
  212.         } until (($TapeMessage & 15) == 0) && ($TapeMessage < 256);
  213.         # Lower 4 bits of $TapeMessage report errors.  Top 4 bits are flags
  214.         # indicating tape position and such.  If mt status returned on stderr
  215.         # TapeMessage is 256 * error returned.
  216.     &Say("Tapeready...succeeded");
  217.     $tabcount--;
  218.     return;
  219.     }    
  220.     
  221. ####################################################################
  222. #                         sub TapeStatus                           #
  223. #     TapeStatus parses the output of mt status. If the tape       #
  224. #     drive is available, it sets $TapeMessage to be the SCSI      #
  225. #     sense code. It can also return stuff on standard error       #
  226. #     if the device is allocated by another process. Or there      #
  227. #    can be an io error. These return 256 and 512 respectively     #
  228. #                      as their error code.                        #
  229. ####################################################################
  230.  
  231.  
  232. sub TapeStatus {
  233.     $tabcount++;
  234.     &Say("TapeStatus...");
  235.     #    Uncomment following three lines for thorough diagnostics
  236.     #    print "TapeStatus Diagnostic: @message\n";
  237.     #    Note: mt status clears any error conditions.
  238.     local(@mtsmessage) =` $mtsense -rm `;
  239.     local(@message) =   ` mt -f $TAPE status 2>&1`;
  240.     #print(@message, "\n");
  241.     local ($mtreturn) = $?;
  242.     ($dev,$device,$busy) = split(/\s+/,$message[0]);
  243.     if (($busy eq "busy")||
  244.         ($message[0] eq "$TAPE: No such device or address\n")){
  245.         # Drive is in use or tied up by some other program.
  246.         #print "\tTapeStatus: busy $busy dev $dev \n";
  247.         print $message[0];
  248.         $TapeMessage=256;
  249.         }
  250.     elsif ("$dev" eq "${TAPE}:" ){ # error of the form /dev/tape: i/o error
  251.         $TapeMessage=512;
  252.         }
  253.         else {
  254.         ($t,$s,$r,$SenseKey) = split(/\s+/,$message[1]);
  255.         ($t,$s,$r,$TapeMessage) = split(/\s+/,$message[2]);
  256.         $TapeMessage = hex(substr($SenseKey,2,2));
  257.         $TapeMessageStat = hex(substr($SenseCode,2,2));
  258.         if (($TapeMessage & 15) != 0) {
  259.             &Say("\nTapeStatus Found Error:", `/bin/date`);
  260.              &Say(`/bin/date`);
  261.             print @message, "\n\n";
  262.             #print @mtsmessage,"\n\n";
  263.             }
  264.         }
  265.     &Say("Sense Byte: $TapeMessage \tSense Status: $TapeMessageStat.");
  266.     &Say("TapeStatus... exited");
  267.     
  268.     $tabcount--;
  269.     return ($TapeMessage);
  270.         
  271.     }
  272.  
  273. ####################################################################
  274. #                       sub TapeStatusTrans                        #
  275. #                                                                  #
  276. #      This takes the output of TapeStatus and translates it       #
  277. #                       back into English.                         #
  278. #                                                                  #
  279. #         Error 512 has usually occured when Next's scsi           #
  280. #    controller issued a device reset. This is a major hassle,     #
  281. #     as it automatically rewinds the tape, and restores the       #
  282. #    drive to poweron defaults. This should cause the program      #
  283. #    to exit, but it happens so often, that I've gone to great     #
  284. #  lengths to keep track of the state of the drive, and now, it    #
  285. #  will reset the tape to the start of that file and try again.    #
  286. #                                                                  #
  287. #    Error 256 has been associated with the device being taken     #
  288. #     by another process. This is reason for immediate exit.       #
  289. #                                                                  #
  290. #   The sense key byte is broken into two sections: The first 3    #
  291. #     bits are flags, giving details about the tape location.      #
  292. #     (At a filemark, at beginning of tape, and so on) These       #
  293. #    first 3 can be set without an error occuring. The last 5      #
  294. #     bits are flags that result in a check condition status       #
  295. #                   being issued by the drive.                     #
  296. #                                                                  #
  297. #           Because any combination of flags can exist,            #
  298. #      TapeStatusTrans checks each flag in turn, prints the        #
  299. #     appropriate message, and decrements the byte. It could       #
  300. #      be done with masks, but this seemed more transparent.       #
  301. #                                                                  #
  302. #                         Ones that say,                           #
  303. #                 "Your Software is out of date"                   #
  304. #      indicate that this error has no translatation in the        #
  305. #                   Exabyte programming guide.                     #
  306. #                                                                  #
  307. ####################################################################
  308. sub TapeStatusTrans {
  309. local($code) = $_[0];
  310. $tabcount++;
  311. &Say("TapeStatusTranslate...");
  312. if ($code == 0) { &Say("\tTapeStatus OK");}
  313. if ($code >= 512) {
  314. print " Next\'s tape driver screws up again! \n ";
  315. $code -= 512;
  316. }
  317. if ($code >= 256) {
  318. print "\tCome again later.  Someone else has glommed onto the drive. \n";
  319. exit;
  320. }
  321. if ($code >= 128) {
  322. print "\tTape is at a file mark \n";
  323. $code-=128;
  324. }
  325. if ($code >= 64) {
  326. print "\tTape is at Logical Begining or End of Tape \n";
  327. $code-=64;
  328. }
  329. if ($code >= 32) {
  330. print "\tData request length doesn't equal logical block length \n";
  331. $code-=32;
  332. }
  333. if ($code >= 16) {
  334. print "\tReserved Bit.  This software is out of date. \n";
  335. $code-=16;
  336. }
  337. if (($code == 0) && $diag ){
  338. print "\tNo Additional Data to report.  All is well. \n";
  339. }
  340. if ($code == 1) {
  341. print "\tThis Software is out of date. \n";
  342. }
  343. if ($code == 2) {
  344. print "\tDrive not ready. Check drive for tape, and close door. \n";
  345. }
  346. if ($code == 3) {
  347. print "\tUnrecoverable medium error.  Buy a new gypsy. \n";
  348. # Ok, I'm being flippent.  This is either a bad tape, or the drive
  349. # and the driver aren't agreeing on blocking.
  350. }
  351. if ($code == 4) {
  352. print "\tHardware error.  Check your warranty. \n";
  353. }
  354. if ($code == 5) {
  355. print "\tIllegal Request. (Wrong mode, ill-formed, or bad parameter) \n";
  356. }
  357. if ($code == 6) {
  358. print "\tUnit Attention.  (Cartridge change or device reset) \n";
  359. #  Next and I are trying to find the source of this bug.  It happens
  360. # quite regularly on opening a new file on the non-rewind device.
  361. }
  362. if ($code == 7) {
  363. print "\tCartridge is write protected. \n";
  364. }
  365.  
  366. if ($code == 8) {
  367. print "\tTape is blank, or is at end of data. \n";
  368. }
  369. if ($code == 9) {
  370. print "\tExabyte specific: Tape mark detect error or Transfer Abort.\n";
  371. }
  372. if ($code == 10) {
  373. print "\tYour software is out of date.\n";
  374. }
  375. if ($code == 11) {
  376. print "\tAborted Command.\n";
  377. }
  378. if ($code == 12 ) {
  379. print "\tYour software is out of date.\n";
  380. }
  381. if ($code == 13) {
  382. print "\tVolume Overflow.  You ran off the end of the tape. \n";
  383. }
  384. if ($code == 14 ) {
  385. print "\tYour software is out of date.\n";
  386. }
  387. if ($code == 15 ) {
  388. print "\tYour software is out of date.\n";
  389. }
  390. &Say("TapeStatusTranslate...exited");
  391. $tabcount--;
  392. }
  393.  
  394.  
  395.  
  396. #    sub RewindTape 
  397. #     
  398. #    RewindTape is called for two reasons.  It's called at the 
  399. #    beginning of the script in the event that the script 
  400. #    glitched and is restarting itself. It's also called when 
  401. #    the program gets confused as to where the tape is.  If this 
  402. #    is the result of a device reset, then the tapedrive will 
  403. #    not recognize commands until it finishes its reset.  Thus 
  404. #    RewindTape, will try repeatedly until mt rewind returns 
  405. #    0, or until it times out. 
  406. #     
  407. #    Note that sending the tape drive commands while it's 
  408. #    resetting can be upsetting, and cause a rereset. 
  409. #    (re-squared-set?)  Thus, the progressively longer waits 
  410. #    between retries. 
  411. #     
  412. #    Note also, that mt rewind can return before the tape is 
  413. #    actually rewound.  Hence the call to TapeReady *after* 
  414. #    the execution of mt.  Most of the other tape commands only 
  415. #    call it before. 
  416.  
  417.  
  418.  
  419. sub RewindTape {
  420.     $tabcount++;
  421.     &Say("RewindTape...");
  422.     local (@message, $mtreturn );
  423.     &TapeReady($reset);
  424.     # a bunch of odd conditions could make it other than 64 and the End of 
  425.     # tape, so we make a strict compare.  May result in rewinding when it's
  426.     # already rewound.  No harm.  Just wasted time.
  427.     if ($TapeMessage  == 64 ) {
  428.         print "\t\tTape is already rewound. \n";
  429.         &Say("RewindTape...exited");
  430.         $tabcount--;
  431.         return(0);
  432.         }
  433.     else {
  434.         print "\t\tExecuting rewind sequence \n";
  435.         $count = 0;
  436.         do {
  437.             @message = `/bin/mt -f $TAPE rewind`;
  438.             $mtreturn = $?;
  439.             if ($mtreturn != 0) { 
  440.                 system("/bin/date");
  441.                 print "\tRewind attempt #$count failed:\n";
  442.                 &ErrorTranslate(110);
  443.                 print "\t\t mtreturn $mtreturn  \n";
  444.                 sleep( 5 * $count);
  445.                 }
  446.             if ($count++ >= 10) { 
  447.                 print "\tRewind failed after many attempts. Exiting \n";
  448.                 exit;
  449.                 }
  450.             &TapeReady($reset);
  451.             } until $mtreturn == 0 ;
  452.         }
  453.     
  454. &Say("RewindTape...exited");
  455. $tabcount--;
  456. return(0);
  457. }
  458.  
  459. #    sub ForwardSpaceTape 
  460. #     
  461. #    Moving a tape forward positions it after the next 
  462. #    tapemark.  Unless the end of the tape gets in the way.  On the 
  463. #    Exabyte, you cannot fsf across an EOD marker.  You can fsf 
  464. #    from an EOD marker.  This rather strange behaviour can get 
  465. #    one in trouble, so we checkfor that. 
  466.  
  467.  
  468. sub ForwardSpaceTape { 
  469.     $tabcount++;
  470.     local(@message,$mtreturn,$Marks); 
  471.     $Marks = $_[0]; $mtreturn = 0;
  472.     &Say("ForwardSpaceTape...$Marks");
  473.     if ($Marks >= 0){
  474.         &TapeStatus;
  475.         if (($TapeMessage & 8) != 8) { #tape is not at EOD/blank
  476.             print "\t\tMoving tape forward past $Marks Tapemarks.\n";
  477.             &TapeReady($reset);
  478.             foreach (1..$Marks) {
  479.                 @message = `/bin/mt -f $TAPE fsf  2>&1`;
  480.                 }
  481.             $mtreturn = $?; 
  482.             if ($mtreturn != 0 ) {
  483.                 system("/bin/date");
  484.                 print "\t***ForwardSpaceTape: @message";
  485.                 $CurrentStatus = &TapeStatus;
  486.                 &TapeStatusTrans($CurrentStatus);
  487.                 print "\t******Failed to move tape forward. \n";
  488.                 print "\t******mtreturn $mtreturn \n";    
  489.                 #&ResetTapePosition;
  490.                 print @message;    
  491.                 exit;
  492.                 }
  493.             }
  494.         }
  495.     $ErrorCode = 200 + $mtreturn; 
  496.     &Say("ForwardSpaceTape...exited");
  497.     $tabcount--;
  498.     return($ErrorCode);
  499.     
  500.     }
  501.  
  502.  
  503. #    sub BackSpaceTape 
  504. #     
  505. #    Backspacing a tape can be done anywhere-- except at the 
  506. #    beginning of the tape. The verification is done in the 
  507. #    event that a Reset occurs during a rewind. 
  508. #     
  509.  
  510. sub BackSpaceTape {  
  511.     $tabcount++;
  512.     local(@message,$mtreturn,$marks);
  513.     $Marks = $_[0];
  514.     &Say("BackSpaceTape...$Marks");
  515.     &TapeReady($reset);
  516.     if (($TapeMessage & 64) != 64) { #tape is not at BOT
  517.         print "\t\tMoving tape back past $Marks Tapemarks.\n";
  518.         @message = `/bin/mt -f $TAPE bsf $Marks 2>&1`;
  519.         $mtreturn = $?;
  520.         if ($mtreturn != 0) {
  521.             system("/bin/date");
  522.             print "\t***Failed to move tape back: \n";
  523.             print "\t***BackSpaceTape: @message";
  524.             print " mtreturn $mtreturn \n";    
  525.             &TapeStatusTrans(&TapeStatus);
  526.             if (($TapeMessage & 6) == 6) { 
  527.                 &ResetTapePosition;
  528.                 &TapeStatusTrans(&TapeStatus);
  529.                 }
  530.             }
  531.         }
  532.     else {
  533.         &Say("**Tape is at beginning of tape. Can't backspace");
  534.         }
  535.     $ErrorCode = 200 + $mtreturn;     
  536.     &Say("BackSpaceTape...exited");
  537.     $tabcount--;
  538.     return($ErrorCode);
  539.     }
  540.  
  541. # WriteMark expects tape to be positioned before an EOF.  This is
  542. # done by gnutar reading.  It is not done by a mt fsf.  If tape
  543. # is positioned with a fsf, issue a bsf before running this command.
  544. #
  545. # Exabyte tapes will not write except on an eof, or on blank tape.  Thus
  546. # to overwrite, but still leave a mark on the tape, one must write TWO file 
  547. # marks, then backup to the spot between them.  
  548. #
  549. # A failure of WriteMark should cause the script to abort.  
  550. #
  551. sub WriteMark {  
  552.     $tabcount++;
  553.     &Say("WriteMark $_[0] ...");
  554.     local (@message, $mtreturn,$Marks);
  555.     $Marks = $_[0];
  556.     &TapeReady($reset);
  557.     print "\t\tWriting $Marks Tapemarks \n";
  558.     @message = `/bin/mt -f $TAPE weof $Marks`;
  559.     $mtreturn = $?;
  560.     if ($mtreturn == 0) {$ErrorCode =200;} else {$ErrorCode =111;}
  561.     &Say("WriteMark...exited");
  562.     $tabcount--;
  563.     return($ErrorCode);
  564.     }
  565.  
  566.  
  567. #    sub EraseRestOfTape 
  568. #     
  569. #    Again, this takes account of the way both Exabyte's and 
  570. #    DATs deal with filemarks.  A program can only start 
  571. #    writing at the BOT side of a filemark, or on blank tape. If 
  572. #    restarting a command, you want to overwrite a part of the 
  573. #    file, but you also want to leave a filemark at the end of the 
  574. #    previous file. The way to solve this is to write two 
  575. #    filemarks, then backspace over 1 of them.  This is what 
  576. #    EraseRestOfTape does. 
  577. #     
  578. #    Note:  An fsf, then another fsf will move past an 
  579. #    EraseRestOfTape, leaving the tape at the filemark 
  580. #    following the Erase.  This gets very messy, and is beyond 
  581. #    the scope of this program. 
  582. #     
  583.  
  584.  
  585.  
  586. sub EraseRestOfTape {
  587.     $tabcount++;
  588.     &Say("EraseRestOfTape ...");
  589.     print "\t\tErasing from Here to end of tape. \n";
  590.     &WriteMark(2);
  591.     if ($ErrorCode = 200) { # proceed only if ok
  592.         &BackSpaceTape(1);
  593.         }
  594.     &ErrorTranslate($ErrorCode);
  595.     &Say("EraseRestOfTape ... exited");
  596.     $tabcount--;
  597.     return($ErrorCode);
  598.     }
  599.  
  600. sub Offline {
  601.     $tabcount++;
  602.     &Say("Offline ...");
  603.     local (@message);
  604.     &TapeReady($reset);
  605.     print "\tEjecting tape...\n";
  606.     @message = `/bin/mt -f $TAPE offline`;
  607.     print @message;
  608.     #&TapeStatusTrans(&TapeStatus);
  609.     &Say("Offline ...exited");
  610.     $tabcount--;
  611.     }
  612.  
  613.  
  614.  
  615.  
  616. #    sub CheckVolumeLabel 
  617. #     
  618. #    The heart of the tapelabeling system is here: When a tape 
  619. #    is labeled, a 0 length file is created in 
  620. #    /usr/local/tapelabels. This file is owned by the person who ran 
  621. #    the tape labeler. /etc/tapelables is public writable, 
  622. #    but the sticky bit is set, so only a file's owner can delete 
  623. #    it.  After this file is created, it is tarred onto the tape. 
  624. #     
  625. #    CheckVolumeLabel reads the tarfile, getting just the 
  626. #    name.  It then checks that this name is registered, that 
  627. #    the program user id and the owner of the file are indeed the 
  628. #    same.  It returns with an error if the tape is either 
  629. #    unlabeled, is labeled, but not registered, or if the ID's 
  630. #    don't match. 
  631. #     
  632. #    Note that an error is returned even for root. This 
  633. #    prevents the nightly backup script from clobbering a 
  634. #    user who left his tape in the drive. 
  635. #     
  636. #    This system (at present) does not stop users from using 
  637. #    the tape directly. 
  638. #     
  639.  
  640.  
  641. sub CheckVolumeLabel    {
  642. local( $label);
  643.     $tabcount++;
  644.     &Say("CheckVolumeLabel ...");
  645.     &RewindTape;
  646.     &TapeReady($reset);
  647.     chop ($label=`$gtar  -tb 16 -f $TAPE`);
  648.     &TapeStatus; # Clear residual errors from tape.
  649.     #
  650.     #    Check the label
  651.     #
  652.     $LabelErrorCode = 100;    #    No Errors found
  653.     #     tape is unlabeled
  654.     if ( -d  "/usr/local/tapelabels/$label" ) {
  655.         $LabelErrorCode = 102;
  656.         }    # label is null, hence checking /usr/local/tapelabels, a directory.
  657.     
  658.     elsif ( ! -e "/usr/local/tapelabels/$label" ) {
  659.         $LabelErrorCode = 103; 
  660.         }    #    tape isn't registered    
  661.     
  662.     elsif ( ! -o "/usr/local/tapelabels/$label" ) {
  663.         $LabelErrorCode = 104;
  664.         }    #    tape is not owned by user
  665.     &TapeStatus; # Clear residual errors from tape.
  666.     &Say("CheckVolumeLabel ...exited");
  667.     $tabcount--;
  668.     return($label);
  669. }
  670.  
  671.  
  672. #    sub TarDir 
  673. #     
  674. #    This is one of two routines that actually do anything. 
  675. #    Tardir expects to be passed the full directory name of the 
  676. #    directory to archive.  It will then cd to the parent 
  677. #    directory of this directory, and make the archive 
  678. #    storeing the relative path from that point. 
  679. #     
  680. #    Since gnutar has provision for labeling an archive, we 
  681. #    make a label of  the form Tapename-Dirname-Date.  This can 
  682. #    be used to rebuild the files in the backups directory if 
  683. #    they are lost. 
  684. #     
  685. #    Various things can go wrong, so TarDir only tries a finite 
  686. #    number of times. It also times the run, and figures the 
  687. #    transfer rate.  If this transfer rate is below the minimum 
  688. #    streaming rate for the drive, the drive will fill in with 
  689. #    blank tracks.  This reduces the capacity of the drive. 
  690.  
  691.  
  692. sub TarDir {
  693.     $tabcount++;
  694.     $tarnum++;
  695.     &Say("TarDir...");
  696.     local($FullDirName,$DirName,$Lbl,$gtreturn,$TarCount);
  697.     $TapeMode=$Write;
  698.     ($FullDirName) = @_;
  699.     $TarCount = 0;    
  700.     $DirName = `basename $FullDirName`;
  701.     chop $DirName;
  702.     $Lbl = $tapelabel."-".$DirName."-".$DateString;
  703.     print "Archive Label is $Lbl \n";
  704.     chdir($FullDirName);
  705.     chdir "..";
  706.     TarLoop:
  707.     do    {
  708.         &TapeReady($reset);
  709.         $timestart=time;
  710.         &Say("Tardir: tarring:");
  711.         @gtmessage =`$gtar  -c -b 16 -f $TAPE -V $Lbl -L 1250000  +totals $DirName 2>&1`;
  712.         $gtreturn=$?;
  713.         $timestop=time;
  714.         $tartime=$timestop-$timestart;
  715.         #!&TapeStatusTrans($TarTapeStatus);
  716.                 print $gtmessage, "\n";
  717.         if ($TapeMessage == 6) {
  718.             system("/bin/date");
  719.             print "***TarDir: Another damn device reset!!  \n";
  720.             print "***TarDir: Tape Driver Failed. \n";
  721.             &ResetTapePosition;
  722.             redo TarLoop;
  723.             #    The above oddity due to gnutar not always 
  724.             #    taking heed of the tape status, and continues
  725.             #    merily on it's way, reporting no error, 
  726.             #    giving the totals properly, but the file not
  727.             #    showing up on the tape.  Strange.
  728.             }
  729.         if ($gtreturn == 256){
  730.             print "*TarDir: Gnutar reports error $gtreturn \n";
  731.             print "*TarDir: This error does NOT compromise \n";
  732.             print "*TarDir: the archive or the tape. \n";
  733.             print "*TarDir: Gnutar's message: \n @gtmessage \n";
  734.             }
  735.         if ($gtreturn > 256) {
  736.                 system("/bin/date");
  737.                 sleep (5 * $TarCount++);
  738.                 print "@gtmessage \nRunning Time: $tartime seconds \n";
  739.                 &Say("*Gnutar reports error $gtreturn");
  740.                 &Say("*Repositioning tape and will try again."); 
  741.                 if ($tartime >= 10) { # was an execution, not an open error.
  742.                     &BackSpaceTape(1);
  743.                     &EraseRestOfTape;
  744.                     &Say("Gnutar ran for $tartime but glitched.", `/bin/date`);
  745.                     }
  746.                 else {&ResetTapePosition;} #We don't know where we are.
  747.                 }
  748.         if ($count >= 3)  {
  749.             print "\t**************** Backup Failed. ******************\n";
  750.             print "\t************** Restarting Script  ****************\n" ;
  751.             exec($0);
  752.             }
  753.         }until ($gtreturn == 0 );
  754.         for ( @gtmessage ) {
  755.             chop;
  756.             ($T,$b,$w,$Size)=split(/\s+/);
  757.             if ( $Size =~ m/[0-9]+/) {last;}
  758.             }
  759.         chop $gtmessage[0];
  760.         print "Tar of $DirName completed. File $tarnum\n";
  761.         print "Gnutar wrote $Size bytes in $tartime seconds = ";
  762.         print int($Size/$tartime), " B/s \n";
  763.         &Say("Tardir...exited");
  764.         $tabcount--;
  765. }
  766. sub VerifyDir {
  767.     $TapeMode=$Read;
  768.     &ForwardSpaceTape(1);
  769.     $tarnum++; print "Tarnum is $tarnum\n";
  770.     #    VerifyDir is going to be called most of the time right after
  771.     #    a VerifyDir or a RewindTape.  In either case, the beginning
  772.     #    of the next file to read is an fsf away; as gnutar doesn't
  773.     #    read the filemark in the first case, and we have to get
  774.     #    past the label in the second case.
  775.     local($BackDir,$FullDirName)=@_;
  776.     local($DirBaseName,$logfile,$count);
  777.     $count = 0;
  778.     $DirName = `basename $FullDirName`;
  779.     chop $DirName;
  780.     $LogLabel = $tapelabel."-".$DirName."-".$DateString;
  781.     $logfile=$BackDir."/".$LogLabel;
  782.     chdir($FullDirName);
  783.     chdir "..";
  784.     do {
  785.         open(LOGFILE,">$logfile");
  786.             
  787.                                                         
  788.         print "Starting Verify of Directory $FullDirName \n" ;
  789.         &TapeReady($reset);
  790.         @message = `$gtar  -b 16 +verbose  +compare -f $TAPE  `;
  791.         $gtreturn = $?;
  792.         if ($gtreturn == 0) {
  793.             open(LOGFILE,">$logfile");
  794.                 
  795.             print LOGFILE "$BackupType Backup: File $tarnum ";
  796.             print LOGFILE "on Tape $tapelabel\n";  
  797.             print LOGFILE "Directory:          $FullDirName\n";                        
  798.             print LOGFILE "Directory stored as ./$DirName\n";        
  799.             print LOGFILE "Date of backup:     ",`date`;                          
  800.             print LOGFILE "Archive size:       ",`du -s $FullDirName`;                
  801.             print LOGFILE "This file will be deleted when tape is erased.\n";
  802.             print(LOGFILE shift @message);
  803.             @message = sort @message;
  804.             print(LOGFILE   @message);
  805.             print "Verification for $DirName complete.\n";
  806.             close(LOGFILE);
  807.             &TarErrors(@message);
  808.                         }
  809.         else {
  810.             sleep ($count++);
  811.             print "VerifyDir: gnutar reports error $gtreturn \n";
  812.             &BackSpaceTape(1);
  813.             &ForwardSpaceTape(1);
  814.             close(LOGFILE);
  815.             }
  816.     }    until (($gtreturn == 0)||(count > 2));
  817.     if (count > 2)  {
  818.         print "\t*************Something is badly screwed up. \n";
  819.         print "\tCut and paste the last few screenfulls into a mail message\n";
  820.         print "\t********to your friendly neighboorhood sysadmin.\n";
  821.         }
  822.     }
  823. sub TarErrors {
  824.     $count = 0;
  825.     for ( @_ ) {
  826.         if (  /\: .*differs$/ || /\: .*does not exist/ ){$count++;}
  827.         shift;
  828.         }
  829.         if ($count) {
  830.             print "$count ERRORS WERE FOUND DURING THE VERIFY PASS.\n";
  831.             }
  832.         print "Full list is in $logfile.\n";
  833.     } 
  834.  
  835. sub ResetTapePosition {
  836.     $DEBUG = "YES";
  837.     $tabcount++;
  838.     &Say("ResetTapePosition...");
  839.         $reset = -1;
  840.         print "\n********** A Critical Tape Error has occured. **********\n";
  841.         print "   Tape will be repositioned to the start of this archive\n";
  842.         print "         and the operation will be attempted again.\n";
  843.         print "                   Please stand by...\n\n";
  844.         #sleep(45);
  845.         #print "  Repositioning tape: Tape is rewinding \n";
  846.         Loop:{
  847.             &RewindTape;
  848.             &SetupDrive;
  849.             if (&ForwardSpaceTape($tarnum) != 200) {redo Loop;}
  850.         if ($TapeMode==$Write) {
  851.             if (&BackSpaceTape(1) != 200) {redo Loop;}
  852.             if (&EraseRestOfTape != 200) {redo Loop; }
  853.             }
  854.     
  855.         print "***      Tape repositioning was successful.             ***\n";
  856.         print "    We now return you to your interupted program. \n";
  857.         $reset = 0;
  858.         }
  859.     &Say("ResetTapePosition...exited");
  860.     $tabcount--;
  861.     }
  862. ####################################################################
  863. #                       sub ErrorTranslate                         #
  864. #      Sometimes errors are easier to deal with as numbers.        #
  865. #    People prefer words. ErrorTransLate prints the associated     #
  866. #               error that corresponds to ErrorCode                #
  867. ####################################################################
  868.  
  869. sub ErrorTranslate {
  870. local ($code) = $_[0];
  871. # 100 - 109 Tape Label & permissions errors
  872. if ($code == 101)    {print "Blank Tape.  \n";}
  873. if ($code == 102)    {print "Tape label is null string.   \n";;}
  874. if ($code == 103)    {print "Label is not registered.    \n";}
  875. if ($code == 104)    {print "Not owned by user.   \n";}
  876. # 110 - 120 mt transport errors
  877. if ($code == 110)    {print "***mt failed rewind.  Trying again. \n";}
  878. if ($code == 111)    {print "***mt failed writing tapemark ";}
  879. if ($code == 112)    {print "***mt failed advancing tape ";}
  880. if ($code == 113)    {print "***mt failed backspacing tape ";}
  881. #    if ($code == )    {print " ";}
  882. }
  883.  
  884. sub Say {
  885.     if ($DEBUG eq "YES") {
  886.         for($i=0;$i<$tabcount;$i++) {print ">  ";}
  887.         print (@_,"\n");
  888.         }
  889.     }
  890. sub Quit {
  891.     $exitcode = @_[0];
  892.     #&Offline;
  893.     exit($exitcode);
  894.     }
  895. 1;    
  896.  
  897.  
  898.  
  899.