home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Education Sampler 1992 [NeXTSTEP]
/
Education_1992_Sampler.iso
/
Utilities
/
TarChive
/
TapeLibrary.perl
< prev
next >
Wrap
Text File
|
1992-08-24
|
31KB
|
899 lines
####################################################################
# TapeLibrary.perl #
# #
# This set of routines handles tar files to tape in a flexible #
# and useful manner, doing a lot of error checking along the way. #
# It includes: #
# Interfacing to the mt tape movement commands. #
# Rudimentary tape ownership. #
# Status checking and error recovery. #
# Creation of labeled backup logs in the users directory. #
# #
# To use this library you will need: #
# Perl 4.10 or higher. #
# gnutar 1.10 or higher. #
# #
# Next binaries & source for mtsense are included. #
# #
# The library has a few things hardwired in that you will need to #
# customize to your setup. e.g. which hosts have DAT drives, which #
# have exebyte drives. #
# #
# It was originally designed for use on Nexts. If porting to other #
# machines, you will need to modify TapeStatus, which parses the #
# output of mt status. You will also have to recompile mtsense #
# for your platform. #
# #
# This file can be located anywhere, but the scripts assume #
# a default location of /usr/local/lib/perl. #
# #
# #
####################################################################
####################################################################
# Set up Global variables #
# and initialize drive. #
####################################################################
$true = 1;
$false = 0;
# reset is used to keep track of error recovery status when
# there is a SCSI bus reset.
$reset = $false;
# tabcount is for spacing in the debug display routine Say
$tabcount = 0;
# TapeMode is either read or write. Determines certain actions
# in error recover.
$Read = $false;
$Write = $true;
$TapeMode = $Read;
# tarnum is the number of the current tar we're working on.
$tarnum = 0;
# gtar is the command used for taring. If not on your standard
# path, fill in path here.
$gtar= "gnutar";
# mtsense is the command used to set drive controller and drive
# to the chosen mode for that drive.
$mtsense = "/u/arafel/adm/backup/mtsense";
# Turn flush on (Output from script and from external commands
# seems to appear out of sequence sometimes)
select(STDERR); $|=1;
select(STDOUT); $|=1;
# Create a datestring of the form dd-MON-yy.
# This datestring is used as part of the name of all log files.
($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime;
$MonOfYear = (Jan,Feb,Mar,Apr,May,Jun,Jul,Aug,Sep,Oct,Nov,Dec)[$mon];
$DateString = $mday."-".$MonOfYear."-".$year;
# A seperate routine Say checks the value of DEBUG. If it's YES
# then Say prints it's message, indented according to the depth of
# the subroutine call. Otherwise it says nothing.
$DEBUG= $ENV{DEBUG};
# Customize:
# nrxt0 is the non-rewind exabyte driver. nrst0 for other scsi
# tape drives. We have both, so we first check the environment
# variable TAPE. If it's blank, then we make the decision on the
# basis of what host we're running on. The use of the environment
# variable makes it easy to test additional drivers.
#
$TAPE = $ENV{TAPE};
if ("$TAPE" eq "") {
if (`/bin/hostname` eq "arafel\n" ) { $TAPE = "/dev/nrst0";}
else { $TAPE ="/dev/nrxt0"; }
}
#print "TapeLibrary thinks it should use tape device $TAPE \n";
# TapeMessage is a global variable that is set to the decimal
# SCSI SenseKey byte by any routine that moves the tape.
# ErrorCode is set by the various routines that verify the label
# and permissions for writing on the tape.
$TapeMessage = 0;
$ErrorCode = 0;
# Since the previous tape operation may have ended with an error
# we call TapeStatus twice so that we know what state the drive is in NOW
&TapeStatus; &TapeStatus; #clear previous error conditions
# Not being very trusting, we will set the tape controller
# AND the drive to specified format.
&SetupDrive;
####################################################################
# #
# sub SetupDrive #
# Sets the driver to fixed blocks of 8192, #
# and calls a mode select to set the drive too. #
# This routine needs to be called every time the drive resets #
# or the host reboots. Thus it is called on startup and on #
# certain error recoveries #
# #
####################################################################
# Customize:
# I decided on a common format of 8192 bytes, fixed block as being the
# easiest to maintain for portability. You may decide to format your
# tapes differently.
sub SetupDrive {
$tabcount++;
&Say("Setting Tape Block Size");
local(@message);
@message = `$mtsense -b 8192 `;
$mtreturn = $?;
print @message, $mtreturn, "\n";
$mtreturn = $?;
&TapeStatusTrans(&TapeStatus);
&Say("Setting tape driver to fixed blocks of size 8192");
$tabcount--;
}
####################################################################
# sub TapeReady #
# #
# It seems to be easy to get Exabyte tape #
# drives confused. TapeReady has evoloved through many #
# incarnations before settling into this form. #
# #
# TapeReady depends on a global variable set by #
# TapeStatus: This is TapeMessage, which is the SCSI #
# sense key byte. #
# #
# TapeReady calls TapeStatus, looks at the output, and #
# decides whether the drive is ready to go to work, if it's #
# still busy with a previous command, or if the device #
# glitched, and has reset, in which case it's state is #
# unknown #
# #
# Tapeready respectively returns, waits, and #
# resets the tape position in each of these three #
# circumstances. #
# #
####################################################################
sub TapeReady {
$tabcount++;
&Say("Tapeready...");
$TRcount=0;
do {
&TapeStatusTrans(&TapeStatus);
if ($TapeMessageStat == 8) {
&Say("Drive is still busy with previous command.");
# I've yet to get one of these. Next ioctl
# intercepts and returns on standard error.
}
if (($TapeMessage & 6) == 6) {
# message: device reset.
# This typically will occur about 5% of the time.
# It has caused me an unreal amount of grief
# as the current command is aborted, and
# the tape rewound.
&Say("TapeReady: Device Reset while executing");
if ($reset >= 0 ) {&ResetTapePosition;}
$tabcount--;
return;
}
sleep 5 * $TRcount++;
# sleeps start at 0 and increase in steps of 5. Some
# drives get unhappy if they receive a command while
# resetting. With this, eventually the sleep gets long
# enough to work. You may wish to change the 5 to a higher
# number if you are getting repeated waits.
if ($TapeMessage == 512 ) { #i/o error (device is busy)
print "...waiting ";
}
if ($TRcount == 20) {# Probably no tape in drive
# You want this to time out eventually. I put a tape in
# one day to do a test. Two copies of the program running
# from the weekend tried to grab the tape out from under me.
print "Tape drive is not ready after $TRcount tries\n";
exit(37); # 37?? Seemed that odd behaviour deserves odd numbers
}
} until (($TapeMessage & 15) == 0) && ($TapeMessage < 256);
# Lower 4 bits of $TapeMessage report errors. Top 4 bits are flags
# indicating tape position and such. If mt status returned on stderr
# TapeMessage is 256 * error returned.
&Say("Tapeready...succeeded");
$tabcount--;
return;
}
####################################################################
# sub TapeStatus #
# TapeStatus parses the output of mt status. If the tape #
# drive is available, it sets $TapeMessage to be the SCSI #
# sense code. It can also return stuff on standard error #
# if the device is allocated by another process. Or there #
# can be an io error. These return 256 and 512 respectively #
# as their error code. #
####################################################################
sub TapeStatus {
$tabcount++;
&Say("TapeStatus...");
# Uncomment following three lines for thorough diagnostics
# print "TapeStatus Diagnostic: @message\n";
# Note: mt status clears any error conditions.
local(@mtsmessage) =` $mtsense -rm `;
local(@message) = ` mt -f $TAPE status 2>&1`;
#print(@message, "\n");
local ($mtreturn) = $?;
($dev,$device,$busy) = split(/\s+/,$message[0]);
if (($busy eq "busy")||
($message[0] eq "$TAPE: No such device or address\n")){
# Drive is in use or tied up by some other program.
#print "\tTapeStatus: busy $busy dev $dev \n";
print $message[0];
$TapeMessage=256;
}
elsif ("$dev" eq "${TAPE}:" ){ # error of the form /dev/tape: i/o error
$TapeMessage=512;
}
else {
($t,$s,$r,$SenseKey) = split(/\s+/,$message[1]);
($t,$s,$r,$TapeMessage) = split(/\s+/,$message[2]);
$TapeMessage = hex(substr($SenseKey,2,2));
$TapeMessageStat = hex(substr($SenseCode,2,2));
if (($TapeMessage & 15) != 0) {
&Say("\nTapeStatus Found Error:", `/bin/date`);
&Say(`/bin/date`);
print @message, "\n\n";
#print @mtsmessage,"\n\n";
}
}
&Say("Sense Byte: $TapeMessage \tSense Status: $TapeMessageStat.");
&Say("TapeStatus... exited");
$tabcount--;
return ($TapeMessage);
}
####################################################################
# sub TapeStatusTrans #
# #
# This takes the output of TapeStatus and translates it #
# back into English. #
# #
# Error 512 has usually occured when Next's scsi #
# controller issued a device reset. This is a major hassle, #
# as it automatically rewinds the tape, and restores the #
# drive to poweron defaults. This should cause the program #
# to exit, but it happens so often, that I've gone to great #
# lengths to keep track of the state of the drive, and now, it #
# will reset the tape to the start of that file and try again. #
# #
# Error 256 has been associated with the device being taken #
# by another process. This is reason for immediate exit. #
# #
# The sense key byte is broken into two sections: The first 3 #
# bits are flags, giving details about the tape location. #
# (At a filemark, at beginning of tape, and so on) These #
# first 3 can be set without an error occuring. The last 5 #
# bits are flags that result in a check condition status #
# being issued by the drive. #
# #
# Because any combination of flags can exist, #
# TapeStatusTrans checks each flag in turn, prints the #
# appropriate message, and decrements the byte. It could #
# be done with masks, but this seemed more transparent. #
# #
# Ones that say, #
# "Your Software is out of date" #
# indicate that this error has no translatation in the #
# Exabyte programming guide. #
# #
####################################################################
sub TapeStatusTrans {
local($code) = $_[0];
$tabcount++;
&Say("TapeStatusTranslate...");
if ($code == 0) { &Say("\tTapeStatus OK");}
if ($code >= 512) {
print " Next\'s tape driver screws up again! \n ";
$code -= 512;
}
if ($code >= 256) {
print "\tCome again later. Someone else has glommed onto the drive. \n";
exit;
}
if ($code >= 128) {
print "\tTape is at a file mark \n";
$code-=128;
}
if ($code >= 64) {
print "\tTape is at Logical Begining or End of Tape \n";
$code-=64;
}
if ($code >= 32) {
print "\tData request length doesn't equal logical block length \n";
$code-=32;
}
if ($code >= 16) {
print "\tReserved Bit. This software is out of date. \n";
$code-=16;
}
if (($code == 0) && $diag ){
print "\tNo Additional Data to report. All is well. \n";
}
if ($code == 1) {
print "\tThis Software is out of date. \n";
}
if ($code == 2) {
print "\tDrive not ready. Check drive for tape, and close door. \n";
}
if ($code == 3) {
print "\tUnrecoverable medium error. Buy a new gypsy. \n";
# Ok, I'm being flippent. This is either a bad tape, or the drive
# and the driver aren't agreeing on blocking.
}
if ($code == 4) {
print "\tHardware error. Check your warranty. \n";
}
if ($code == 5) {
print "\tIllegal Request. (Wrong mode, ill-formed, or bad parameter) \n";
}
if ($code == 6) {
print "\tUnit Attention. (Cartridge change or device reset) \n";
# Next and I are trying to find the source of this bug. It happens
# quite regularly on opening a new file on the non-rewind device.
}
if ($code == 7) {
print "\tCartridge is write protected. \n";
}
if ($code == 8) {
print "\tTape is blank, or is at end of data. \n";
}
if ($code == 9) {
print "\tExabyte specific: Tape mark detect error or Transfer Abort.\n";
}
if ($code == 10) {
print "\tYour software is out of date.\n";
}
if ($code == 11) {
print "\tAborted Command.\n";
}
if ($code == 12 ) {
print "\tYour software is out of date.\n";
}
if ($code == 13) {
print "\tVolume Overflow. You ran off the end of the tape. \n";
}
if ($code == 14 ) {
print "\tYour software is out of date.\n";
}
if ($code == 15 ) {
print "\tYour software is out of date.\n";
}
&Say("TapeStatusTranslate...exited");
$tabcount--;
}
# sub RewindTape
#
# RewindTape is called for two reasons. It's called at the
# beginning of the script in the event that the script
# glitched and is restarting itself. It's also called when
# the program gets confused as to where the tape is. If this
# is the result of a device reset, then the tapedrive will
# not recognize commands until it finishes its reset. Thus
# RewindTape, will try repeatedly until mt rewind returns
# 0, or until it times out.
#
# Note that sending the tape drive commands while it's
# resetting can be upsetting, and cause a rereset.
# (re-squared-set?) Thus, the progressively longer waits
# between retries.
#
# Note also, that mt rewind can return before the tape is
# actually rewound. Hence the call to TapeReady *after*
# the execution of mt. Most of the other tape commands only
# call it before.
sub RewindTape {
$tabcount++;
&Say("RewindTape...");
local (@message, $mtreturn );
&TapeReady($reset);
# a bunch of odd conditions could make it other than 64 and the End of
# tape, so we make a strict compare. May result in rewinding when it's
# already rewound. No harm. Just wasted time.
if ($TapeMessage == 64 ) {
print "\t\tTape is already rewound. \n";
&Say("RewindTape...exited");
$tabcount--;
return(0);
}
else {
print "\t\tExecuting rewind sequence \n";
$count = 0;
do {
@message = `/bin/mt -f $TAPE rewind`;
$mtreturn = $?;
if ($mtreturn != 0) {
system("/bin/date");
print "\tRewind attempt #$count failed:\n";
&ErrorTranslate(110);
print "\t\t mtreturn $mtreturn \n";
sleep( 5 * $count);
}
if ($count++ >= 10) {
print "\tRewind failed after many attempts. Exiting \n";
exit;
}
&TapeReady($reset);
} until $mtreturn == 0 ;
}
&Say("RewindTape...exited");
$tabcount--;
return(0);
}
# sub ForwardSpaceTape
#
# Moving a tape forward positions it after the next
# tapemark. Unless the end of the tape gets in the way. On the
# Exabyte, you cannot fsf across an EOD marker. You can fsf
# from an EOD marker. This rather strange behaviour can get
# one in trouble, so we checkfor that.
sub ForwardSpaceTape {
$tabcount++;
local(@message,$mtreturn,$Marks);
$Marks = $_[0]; $mtreturn = 0;
&Say("ForwardSpaceTape...$Marks");
if ($Marks >= 0){
&TapeStatus;
if (($TapeMessage & 8) != 8) { #tape is not at EOD/blank
print "\t\tMoving tape forward past $Marks Tapemarks.\n";
&TapeReady($reset);
foreach (1..$Marks) {
@message = `/bin/mt -f $TAPE fsf 2>&1`;
}
$mtreturn = $?;
if ($mtreturn != 0 ) {
system("/bin/date");
print "\t***ForwardSpaceTape: @message";
$CurrentStatus = &TapeStatus;
&TapeStatusTrans($CurrentStatus);
print "\t******Failed to move tape forward. \n";
print "\t******mtreturn $mtreturn \n";
#&ResetTapePosition;
print @message;
exit;
}
}
}
$ErrorCode = 200 + $mtreturn;
&Say("ForwardSpaceTape...exited");
$tabcount--;
return($ErrorCode);
}
# sub BackSpaceTape
#
# Backspacing a tape can be done anywhere-- except at the
# beginning of the tape. The verification is done in the
# event that a Reset occurs during a rewind.
#
sub BackSpaceTape {
$tabcount++;
local(@message,$mtreturn,$marks);
$Marks = $_[0];
&Say("BackSpaceTape...$Marks");
&TapeReady($reset);
if (($TapeMessage & 64) != 64) { #tape is not at BOT
print "\t\tMoving tape back past $Marks Tapemarks.\n";
@message = `/bin/mt -f $TAPE bsf $Marks 2>&1`;
$mtreturn = $?;
if ($mtreturn != 0) {
system("/bin/date");
print "\t***Failed to move tape back: \n";
print "\t***BackSpaceTape: @message";
print " mtreturn $mtreturn \n";
&TapeStatusTrans(&TapeStatus);
if (($TapeMessage & 6) == 6) {
&ResetTapePosition;
&TapeStatusTrans(&TapeStatus);
}
}
}
else {
&Say("**Tape is at beginning of tape. Can't backspace");
}
$ErrorCode = 200 + $mtreturn;
&Say("BackSpaceTape...exited");
$tabcount--;
return($ErrorCode);
}
# WriteMark expects tape to be positioned before an EOF. This is
# done by gnutar reading. It is not done by a mt fsf. If tape
# is positioned with a fsf, issue a bsf before running this command.
#
# Exabyte tapes will not write except on an eof, or on blank tape. Thus
# to overwrite, but still leave a mark on the tape, one must write TWO file
# marks, then backup to the spot between them.
#
# A failure of WriteMark should cause the script to abort.
#
sub WriteMark {
$tabcount++;
&Say("WriteMark $_[0] ...");
local (@message, $mtreturn,$Marks);
$Marks = $_[0];
&TapeReady($reset);
print "\t\tWriting $Marks Tapemarks \n";
@message = `/bin/mt -f $TAPE weof $Marks`;
$mtreturn = $?;
if ($mtreturn == 0) {$ErrorCode =200;} else {$ErrorCode =111;}
&Say("WriteMark...exited");
$tabcount--;
return($ErrorCode);
}
# sub EraseRestOfTape
#
# Again, this takes account of the way both Exabyte's and
# DATs deal with filemarks. A program can only start
# writing at the BOT side of a filemark, or on blank tape. If
# restarting a command, you want to overwrite a part of the
# file, but you also want to leave a filemark at the end of the
# previous file. The way to solve this is to write two
# filemarks, then backspace over 1 of them. This is what
# EraseRestOfTape does.
#
# Note: An fsf, then another fsf will move past an
# EraseRestOfTape, leaving the tape at the filemark
# following the Erase. This gets very messy, and is beyond
# the scope of this program.
#
sub EraseRestOfTape {
$tabcount++;
&Say("EraseRestOfTape ...");
print "\t\tErasing from Here to end of tape. \n";
&WriteMark(2);
if ($ErrorCode = 200) { # proceed only if ok
&BackSpaceTape(1);
}
&ErrorTranslate($ErrorCode);
&Say("EraseRestOfTape ... exited");
$tabcount--;
return($ErrorCode);
}
sub Offline {
$tabcount++;
&Say("Offline ...");
local (@message);
&TapeReady($reset);
print "\tEjecting tape...\n";
@message = `/bin/mt -f $TAPE offline`;
print @message;
#&TapeStatusTrans(&TapeStatus);
&Say("Offline ...exited");
$tabcount--;
}
# sub CheckVolumeLabel
#
# The heart of the tapelabeling system is here: When a tape
# is labeled, a 0 length file is created in
# /usr/local/tapelabels. This file is owned by the person who ran
# the tape labeler. /etc/tapelables is public writable,
# but the sticky bit is set, so only a file's owner can delete
# it. After this file is created, it is tarred onto the tape.
#
# CheckVolumeLabel reads the tarfile, getting just the
# name. It then checks that this name is registered, that
# the program user id and the owner of the file are indeed the
# same. It returns with an error if the tape is either
# unlabeled, is labeled, but not registered, or if the ID's
# don't match.
#
# Note that an error is returned even for root. This
# prevents the nightly backup script from clobbering a
# user who left his tape in the drive.
#
# This system (at present) does not stop users from using
# the tape directly.
#
sub CheckVolumeLabel {
local( $label);
$tabcount++;
&Say("CheckVolumeLabel ...");
&RewindTape;
&TapeReady($reset);
chop ($label=`$gtar -tb 16 -f $TAPE`);
&TapeStatus; # Clear residual errors from tape.
#
# Check the label
#
$LabelErrorCode = 100; # No Errors found
# tape is unlabeled
if ( -d "/usr/local/tapelabels/$label" ) {
$LabelErrorCode = 102;
} # label is null, hence checking /usr/local/tapelabels, a directory.
elsif ( ! -e "/usr/local/tapelabels/$label" ) {
$LabelErrorCode = 103;
} # tape isn't registered
elsif ( ! -o "/usr/local/tapelabels/$label" ) {
$LabelErrorCode = 104;
} # tape is not owned by user
&TapeStatus; # Clear residual errors from tape.
&Say("CheckVolumeLabel ...exited");
$tabcount--;
return($label);
}
# sub TarDir
#
# This is one of two routines that actually do anything.
# Tardir expects to be passed the full directory name of the
# directory to archive. It will then cd to the parent
# directory of this directory, and make the archive
# storeing the relative path from that point.
#
# Since gnutar has provision for labeling an archive, we
# make a label of the form Tapename-Dirname-Date. This can
# be used to rebuild the files in the backups directory if
# they are lost.
#
# Various things can go wrong, so TarDir only tries a finite
# number of times. It also times the run, and figures the
# transfer rate. If this transfer rate is below the minimum
# streaming rate for the drive, the drive will fill in with
# blank tracks. This reduces the capacity of the drive.
sub TarDir {
$tabcount++;
$tarnum++;
&Say("TarDir...");
local($FullDirName,$DirName,$Lbl,$gtreturn,$TarCount);
$TapeMode=$Write;
($FullDirName) = @_;
$TarCount = 0;
$DirName = `basename $FullDirName`;
chop $DirName;
$Lbl = $tapelabel."-".$DirName."-".$DateString;
print "Archive Label is $Lbl \n";
chdir($FullDirName);
chdir "..";
TarLoop:
do {
&TapeReady($reset);
$timestart=time;
&Say("Tardir: tarring:");
@gtmessage =`$gtar -c -b 16 -f $TAPE -V $Lbl -L 1250000 +totals $DirName 2>&1`;
$gtreturn=$?;
$timestop=time;
$tartime=$timestop-$timestart;
#!&TapeStatusTrans($TarTapeStatus);
print $gtmessage, "\n";
if ($TapeMessage == 6) {
system("/bin/date");
print "***TarDir: Another damn device reset!! \n";
print "***TarDir: Tape Driver Failed. \n";
&ResetTapePosition;
redo TarLoop;
# The above oddity due to gnutar not always
# taking heed of the tape status, and continues
# merily on it's way, reporting no error,
# giving the totals properly, but the file not
# showing up on the tape. Strange.
}
if ($gtreturn == 256){
print "*TarDir: Gnutar reports error $gtreturn \n";
print "*TarDir: This error does NOT compromise \n";
print "*TarDir: the archive or the tape. \n";
print "*TarDir: Gnutar's message: \n @gtmessage \n";
}
if ($gtreturn > 256) {
system("/bin/date");
sleep (5 * $TarCount++);
print "@gtmessage \nRunning Time: $tartime seconds \n";
&Say("*Gnutar reports error $gtreturn");
&Say("*Repositioning tape and will try again.");
if ($tartime >= 10) { # was an execution, not an open error.
&BackSpaceTape(1);
&EraseRestOfTape;
&Say("Gnutar ran for $tartime but glitched.", `/bin/date`);
}
else {&ResetTapePosition;} #We don't know where we are.
}
if ($count >= 3) {
print "\t**************** Backup Failed. ******************\n";
print "\t************** Restarting Script ****************\n" ;
exec($0);
}
}until ($gtreturn == 0 );
for ( @gtmessage ) {
chop;
($T,$b,$w,$Size)=split(/\s+/);
if ( $Size =~ m/[0-9]+/) {last;}
}
chop $gtmessage[0];
print "Tar of $DirName completed. File $tarnum\n";
print "Gnutar wrote $Size bytes in $tartime seconds = ";
print int($Size/$tartime), " B/s \n";
&Say("Tardir...exited");
$tabcount--;
}
sub VerifyDir {
$TapeMode=$Read;
&ForwardSpaceTape(1);
$tarnum++; print "Tarnum is $tarnum\n";
# VerifyDir is going to be called most of the time right after
# a VerifyDir or a RewindTape. In either case, the beginning
# of the next file to read is an fsf away; as gnutar doesn't
# read the filemark in the first case, and we have to get
# past the label in the second case.
local($BackDir,$FullDirName)=@_;
local($DirBaseName,$logfile,$count);
$count = 0;
$DirName = `basename $FullDirName`;
chop $DirName;
$LogLabel = $tapelabel."-".$DirName."-".$DateString;
$logfile=$BackDir."/".$LogLabel;
chdir($FullDirName);
chdir "..";
do {
open(LOGFILE,">$logfile");
print "Starting Verify of Directory $FullDirName \n" ;
&TapeReady($reset);
@message = `$gtar -b 16 +verbose +compare -f $TAPE `;
$gtreturn = $?;
if ($gtreturn == 0) {
open(LOGFILE,">$logfile");
print LOGFILE "$BackupType Backup: File $tarnum ";
print LOGFILE "on Tape $tapelabel\n";
print LOGFILE "Directory: $FullDirName\n";
print LOGFILE "Directory stored as ./$DirName\n";
print LOGFILE "Date of backup: ",`date`;
print LOGFILE "Archive size: ",`du -s $FullDirName`;
print LOGFILE "This file will be deleted when tape is erased.\n";
print(LOGFILE shift @message);
@message = sort @message;
print(LOGFILE @message);
print "Verification for $DirName complete.\n";
close(LOGFILE);
&TarErrors(@message);
}
else {
sleep ($count++);
print "VerifyDir: gnutar reports error $gtreturn \n";
&BackSpaceTape(1);
&ForwardSpaceTape(1);
close(LOGFILE);
}
} until (($gtreturn == 0)||(count > 2));
if (count > 2) {
print "\t*************Something is badly screwed up. \n";
print "\tCut and paste the last few screenfulls into a mail message\n";
print "\t********to your friendly neighboorhood sysadmin.\n";
}
}
sub TarErrors {
$count = 0;
for ( @_ ) {
if ( /\: .*differs$/ || /\: .*does not exist/ ){$count++;}
shift;
}
if ($count) {
print "$count ERRORS WERE FOUND DURING THE VERIFY PASS.\n";
}
print "Full list is in $logfile.\n";
}
sub ResetTapePosition {
$DEBUG = "YES";
$tabcount++;
&Say("ResetTapePosition...");
$reset = -1;
print "\n********** A Critical Tape Error has occured. **********\n";
print " Tape will be repositioned to the start of this archive\n";
print " and the operation will be attempted again.\n";
print " Please stand by...\n\n";
#sleep(45);
#print " Repositioning tape: Tape is rewinding \n";
Loop:{
&RewindTape;
&SetupDrive;
if (&ForwardSpaceTape($tarnum) != 200) {redo Loop;}
if ($TapeMode==$Write) {
if (&BackSpaceTape(1) != 200) {redo Loop;}
if (&EraseRestOfTape != 200) {redo Loop; }
}
print "*** Tape repositioning was successful. ***\n";
print " We now return you to your interupted program. \n";
$reset = 0;
}
&Say("ResetTapePosition...exited");
$tabcount--;
}
####################################################################
# sub ErrorTranslate #
# Sometimes errors are easier to deal with as numbers. #
# People prefer words. ErrorTransLate prints the associated #
# error that corresponds to ErrorCode #
####################################################################
sub ErrorTranslate {
local ($code) = $_[0];
# 100 - 109 Tape Label & permissions errors
if ($code == 101) {print "Blank Tape. \n";}
if ($code == 102) {print "Tape label is null string. \n";;}
if ($code == 103) {print "Label is not registered. \n";}
if ($code == 104) {print "Not owned by user. \n";}
# 110 - 120 mt transport errors
if ($code == 110) {print "***mt failed rewind. Trying again. \n";}
if ($code == 111) {print "***mt failed writing tapemark ";}
if ($code == 112) {print "***mt failed advancing tape ";}
if ($code == 113) {print "***mt failed backspacing tape ";}
# if ($code == ) {print " ";}
}
sub Say {
if ($DEBUG eq "YES") {
for($i=0;$i<$tabcount;$i++) {print "> ";}
print (@_,"\n");
}
}
sub Quit {
$exitcode = @_[0];
#&Offline;
exit($exitcode);
}
1;