From: rsnow@rice.edu (Robert Snow)
Subject: Perl network backup script for SUN/Exabyte
Summary: Perl network backup script for SUN/Exabyte
Keywords: Exabyte backup sun network
Organization: Rice University
Date: Sat, 7 Nov 1992 00:46:41 GMT

The following is a script for backing up all the drives on a network onto
an Exabyte 8mm (8500) SCSI drive.

I have seen it run on SGI also.

It prints out labels also.

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  bcs.pl
# Wrapped by rsnow@proton on Fri Nov  6 18:44:43 1992
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'bcs.pl' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'bcs.pl'\"
else
echo shar: Extracting \"'bcs.pl'\" \(11264 characters\)
sed "s/^X//" >'bcs.pl' <<'END_OF_FILE'
X#!/usr/local/bin/perl 
X#
X# backup all listed disks to the exabyte.
X#
X# original was SGI version ported to Sun by Rob Snow.
X# mail suggestions or problems to:
X# 
X#	Rob Snow
X#	rsnow@proton.rice.edu
X#
X#
X# comand line options used to overide default options
X#   -o sysop        - uid of system operator
X#   -m kbytes       - size of tape in kbytes of tape
X#   -d host         - remote host.
X#   -L logfile      - name of logfile.
X#   -p              - print a label using data in $logfile starting w/ partition $recover
X#   -r partition    - restart backup w/ partition partition.  this option assumes that
X#                     the preceding partitions in the list have already been backed up
X#                     on the current tape.  the script will automatically adjust the
X#                     the $sum variable.  the user need not calculate the num of kbytes
X#                     on the tape.
X#  -s kbytes        - set starting number of kbytes used on tape. if sum is specified, amount
X#                     of data on the tape is _NOT_ calculated.
X#  -l level         - dump level
X#  -P printer       - over ride the default printer for lpr.
X#  -D               - turn on debugging
X#  -t tapenum       - starting tape number
X#  -a               - append to current tape.
X
X# 
X# user selectable parameters. can also be set via comand line options
X$sysop     = 'rob';                # uid of person to receive mail
X$maxkbytes = 4.8*1024*1024;        # max size of tape
X$dhost     = 'oasis';              # remote host containing the tape drive
X$rawdev    = '/dev/nrst1';
X$dump      = '/usr/etc/rdump';          # dump program to use
X$logfile   = '/home/rob/tmp/backup.log';     # name of logfile
X$recover   = "";                    # if not null continue backup starting w/ partition name = $recover
X$sum       = -1;                    # number of kbytes used so far.
X$level     = 0;                     # dump level
X$printer   = "ps";                  # printer for lpr.
X$debug     = 0;                     # 1 == debug mode.
X$label     = 0;                     # 1 == print a label using $logfile starting w/ partition $recover
X									# no other action is taken.
X$tape      = 1;                     # current tape number
X$append    = 0;                     # true if you want to seek to the end of the tape before starting
X
X#
X# some constants
X$USAGE = "Usage: $argv[0] [-o uid][-m tapelen][-d host:dev][-L log][-r part][-s len][-l level][-P printer][-t num][-paD]";
X
X# to add a new partition to the list
X#   a) add partition name and machine to the list
X#   b) edit /.rhosts on remote machine to add the name of the machine
X#       running this scrip.
X#   c) add remote machine to /.rhosts table on machine containing the
X#       tape drive.
X#
X@partitions = (
X#   'partition',    'machine',   bytes/block
X# backup  partitions
X	'/',        'oasis',           512, 
X	'/usr',     'oasis',           512,
X	'/home',    'oasis',	       512,
X	'/trep3',   'oasis',           512,
X	'/',        'sweetie',         512,
X	'/usr',     'sweetie',         512,
X	'/trep1',   'sweetie',         512,
X	'/trep2',   'sweetie',         512,
X);
X
Xdo ParseArgs();
X
Xif ( $label == 1 ) {
X	do PrintLabel(do GetPartName($recover), do GetHostName($recover), $level);
X	exit(0);
X}
X
Xdo CheckMachines();
X
Xif ( $recover eq "" ) {
X	@oldlogfile[0] = $logfile;
X	@oldlogfile[1] = `date +'%m%d%y'`;
X	chop @oldlogfile[1];
X	rename($logfile, join('',@oldlogfile));
X	$sum = 0;
X} elsif ( $sum != -1 ) {
X	local ($host) = do GetHostName($recover);
X	local ($part) = do GetPartName($recover);
X	for ( $i = 0; $i < $#partitions; $i += 3 ) {
X		if ( $host eq $partitions[$i+1] && $part eq $partitions[$i] ) {
X			last;
X		}
X	}
X	$startindex = $i;
X} elsif ( $sum == -1 && -s $logfile )  {
X	# determine num kbytes on tape if recovering and sum not specified by the user
X	local ($host) = do GetHostName($recover);
X	local ($part) = do GetPartName($recover);
X	local ($numFilePerTape) = 0;
X	for ( $i = 0; $i < $#partitions; $i += 3 ) {
X
X		# get current partition size from the log file
X		$size = do GetPartSize($partitions[$i], $partitions[$i+1],
X							   $partitions[$i+2], $level);
X
X		$sum += $size;
X		$numFilesPerTape += 1;
Xprint STDERR "sum = $sum \n";
X		if ( $sum > ($maxkbytes) ) {
X			$numFilesPerTape = 0;
X			$sum = $size;
X			$tape += 1;
X		} 
X		if ( $partitions[$i] eq $part && $partitions[$i+1] eq $host) {
X			$sum -= $size;
X			last;
X		}
X	}
X	$startindex = $i - 3;
X	# advance tape to new position
X	do PrintLog ("rsh $dhost mt -f $rawdev rew\n");
X	do PrintLog ("rsh $dhost mt -f $rawdev fsf $numFilesPerTape\n");
X	if ( $debug == 0 ) {
X		system ("rsh $dhost mt -f $rawdev rew");
X		system ("rsh $dhost mt -f $rawdev fsf $numFilesPerTape");
X	}
X} elsif ( $sum == -1 ) {
X	do PrintMailLog ("Backup aborted, Could not determine amount of data on tape. Use -s options");
X	do PrintLog("\n");
X	die ("Backup aborted, Could not determine amount of data on tape. Use -s options");
X}
X
Xif ( $append == 1 ) {
X	do PrintLog("seeking to end of tape.  note that sum may be in correct.\n");
X	do PrintLog("rsh $dhost mt -f $rawdev feom\n");
X	system("rsh $dhost mt -f $rawdev feom");
X}
X
Xdo OpenLpr("");
X	
X$firstlevel  = sprintf("%dubsdf 1024 6000 108000", $level);
X#$firstlevel  = sprintf("%dubsf 6000 108000", $level);
Xfor ( $i = $startindex; $i < $#partitions; $i += 3 ) {
X
X	# make a guess at the partition size so that we dont overflow the tape.
X	# df gives us the MAX possible size for the partitions.
X	do PrintLog ("rsh $partitions[$i+1] df | grep $partitions[$i]\n");
X	$tmp = `rsh $partitions[$i+1] df | grep $partitions[$i]`;
X	if ( $? ) {
X		do PrintLog ("$partitions[$i+1] is down, skiping partition $partitions[$i]\n");
X		next;
X	}
X	@tmp1 = split(/ \ */,$tmp);
X	if ( ($sum + (@tmp1[2])) > ($maxkbytes) ) {
X		do CloseLpr ("");
X		do OpenLpr("");
X		do PrintLog ("rsh $dhost mt -f $rawdev rewoffl\n");
X		if ( $debug == 0 ) {
X			system("rsh $dhost mt -f $rawdev rew");
X			system("rsh $dhost mt -f $rawdev offline");
X		}
X		do PrintMailLog ("insert new tape, CR when done");
X		do PrintLog ("\n");
X		getc;
X		$sum = 0;
X	} 
X	do PrintLog ("rsh $partitions[$i+1] $dump $firstlevel $dhost:$rawdev $partitions[$i]\n");
X	if ( $debug == 0 ) {
X		system("(rsh $partitions[$i+1] $dump $firstlevel $dhost:$rawdev $partitions[$i]) 2>&1 | tee -a $logfile");
X	} else {
X		system("(rsh $partitions[$i+1] ls -C $partitions[$i]) 2>&1 | tee -a $logfile");
X	}
X
X	# get the actual paritions size from the log file and use this value
X	# to update the running sum of bytes on tape.
X	$sum += do PrintPartSize($i, $level);
X
X	do PrintLog("running sum = $sum \n");
X}
Xdo CloseLpr ("");
Xdo PrintLog ("rsh $dhost mt -f $rawdev rewoffl \n");
Xif ( $debug == 0 ) {
X	system("rsh $dhost mt -f $rawdev rew");
X	system("rsh $dhost mt -f $rawdev offline");
X}
Xdo PrintMailLog ("======= TAPE BACKUP DONE ======= ");
Xdo PrintLog ("\n");
X
Xformat LABEL = 
X@<<<@<<<<<<<<<<<<<<<<<<<@<<<<<<<<<<@||||
X$_offset, $_partition, $_size, $_level
X.
X
Xsub PrintPartSize {
X	local ($partindex, $dumplevel)  = @_;
X	local ($size);
X	$size = do GetPartSize($partitions[$partindex], 
X						   $partitions[$partindex +1], 
X						   $partitions[$partindex +2], 
X						   $dumplevel);
X	if ( $size != 0) {
X		$_partition = "$partitions[$partindex+1]:$partitions[$partindex]";
X		$_level = $dumplevel;
X		$_size  = $size;
X		write LABEL;
X		$_offset += 1;
X	}
X	$size;
X}
X
Xsub CloseLpr {
X	print LABEL "---------------------------------------\n";
X	print LABEL "                        $sum\n";
X	close (LABEL);
X	$tape += 1;
X}
X
Xsub OpenLpr {
X	open(LABEL,"| lp -d$printer");
X	$date = `date`;
X	$_offset = 0;
X	print LABEL "Backup: $date";
X	print LABEL "Tape: $tape\n";
X	print LABEL "Machine:Partition       Size(KB)  Level\n";
X	print LABEL "---------------------------------------\n";
X}
X
X# search thru the log file for the size of the partition named
X# in the argument.
Xsub GetPartSize {
X	local ($partition, $host, $BytesPerBlock, $dumplevel) = @_;
X	local ($curhost);
X	local ($size) = 0;
X	local ($curdumplevel);
X
X	open(LOGFIL,"<$logfile");
X
X	while ( <LOGFIL> ) {
X		if ( $_ ne "\n" ) {
X			@tmp = split(/ \ */,$_);
X		}
X
X		if ( $#tmp > 2 && @tmp[0] eq "rsh" ) {
X			$curhost = $tmp[1];
X		}
X		# look for DUMP: Date of this level ? dump ...
X		if ( $#tmp > 3 && @tmp[4] eq "this" ) {
X			$curdumplevel = @tmp[6];
X		}
X		if ( $curdumplevel == $dumplevel && $#tmp > 3 && @tmp[4] eq "($partition)" && $curhost eq $host) {
X			while ( <LOGFIL> ) {
X				if ( $_ ne "\n" ) {
X					@tmp = split(/ \ */,$_);
X				}
X				if ( @tmp[3] eq "blocks" ) {
X					$size = @tmp[2];
X					last;
X				}
X			}
X		}
X	}
X	close(LOGFIL);
X	$size = ($size*$BytesPerBlock)/1024;
X
Xprint STDERR "GetPartSize: size of level $dumplevel dump for $host:$partition = $size\n";
X
X	$size;
X}
X
Xsub PrintMailLog {
X	do PrintLog (@_);
X	system ("echo @_ | mail $sysop");
X}
X
Xsub PrintLog {
X	open(LOGFIL,">>$logfile");
X	print STDERR @_;
X	print LOGFIL @_;
X	close(LOGFIL);
X}
X
Xsub PrintLabel {
X	local ($partition, $host, $dumplevel) = @_;
X	local ($i, $size);
X	local ($sum) = 0;
X
X	if ( $partition eq "" ) {
X		$i = 0;
X	} else {
X		for ( $i = 0; $i < $#partitions && 
X					  ($partitions[$i] ne $partition || 
X					  $partitions[$i+1] ne $host); $i += 3 ) {
X			;
X		}
X	}
X	do OpenLpr();
X	do {
X		$size = do PrintPartSize($i, $level);
X		$sum += $size;
X		$i += 3;
X	} while ( $size && $i < $#partitions);
X	do CloseLpr();
X}
X
Xsub GetName {
X	local ($rpart) = @_;
X	local (@retv);
X	if ( index($rpart, ":") == $[-1 ) {
X		print STDERR "partitions must be specified as \"host:/partition\" \n";
X		exit(1);
X	}
X	@retv = split(':', $rpart);
X	if ( @retv[0] eq "" || @retv[1] eq "" ) {
X		print STDERR "partitions must be specified as \"host:/partition\" \n";
X		exit(1);
X	}
X	@retv;
X}
X
Xsub GetHostName {
X	local ($rpart) = @_;
X	local (@retval) = do GetName($rpart);
X	@retval[0];
X}
X
Xsub GetPartName {
X	local ($rpart) = @_;
X	local (@retval) = do GetName($rpart);
X	@retval[1];
X}
X
Xsub ParseArgs {
X	do 'getopt.pl';
X	do Getopt('omdlrsPLt');  # add only options that have arguments
X	foreach $i ( ('a','p','o','m','d','l','r','s','L','P','D','t') ) { # include _ALL_ arguments here
X		$tmp = eval"\$opt_$i";
X		if ( $tmp ne "" ) {
X			# options w/ aruguments go here.
X			if ( $tmp =~ /-/ ) {
X				die $USAGE;
X			}
X			if ( $i eq 'D' ) {$debug = 1;}
X			if ( $i eq 'a' ) {$append = 1;}
X			if ( $i eq 'o' ) {$sysop = $tmp ;}
X			if ( $i eq 'm' ) {$maxkbytes = $tmp;}
X			if ( $i eq 'd' ) {$dhost = $tmp;}
X			if ( $i eq 'L' ) {$logfile = $tmp;}
X			if ( $i eq 'r' ) {$recover = $tmp;}
X			if ( $i eq 's' ) {$sum = $tmp;}
X			if ( $i eq 'P' ) {$printer = "-P $tmp";}
X			if ( $i eq 'l' ) {$level = $tmp;}
X			if ( $i eq 'p' ) {$label = 1;}
X			if ( $i eq 't' ) {$tape = $tmp;}    
X		}
X	}
X}
X
X
X# make sure that all machines are alive 
Xsub CheckMachines {
X	local ($i, $j, @dead);
X	for ( $i = 1, $j = 0; $i < $#partitions+1; $i += 3 ) {
X		system("/usr/etc/ping -c1 $partitions[$i]");
X		if ( $? ) {
X			$dead[$j++] = $partitions[$i];
X		}
X	}
X	if ( $dead[0] ne "" ) {
X		do PrintMailLog ("Backup aborted, the following systems are dead: @dead");
X		do PrintLog("\n");
X		die ("\n");
X	}
X}
X

Newsgroups: comp.lang.perl