#!/usr/bin/perl
# dump multiple file systems to one tape
# usage: mdump [options] conf
#
# options:
#	-l #	dump level, 		default 0
#	-u	update dumptable,	default no
#	-f dev	tape device		default back:/dev/nrst24
#		no-rewind device name required
#	-e	eject the tape
#
# The configure file conf contains file systems to be dumped. The content looks
# like this
#	host:/
#	host:/usr
#
# Each line contains exactly one file system to dump. The file system should be give
# in the form of the path to the mount point of the filesystem, not the raw device
# name. The host part can be omitted if the file system is on local disk where the mdump
# is running
# 
# mdump uses rsh, hostname, dd, mt, dump and rdump. Make sure the path include all directories on
# all systems you want to do backup
# On SunOS 4.1.3, dump and rdump is in /usr/etc
# On Linux RedHat 5.1, rdump is in /sbin.
# Add other directory to suite your needs

$ENV{'PATH'} = '/bin:/usr/etc:/sbin:/usr/bin:/usr/ucb';

# default options
$opt_l		=	"5";
$opt_u    	=	"";
$opt_f		=	"back:/dev/nrst24";
$opt_e		=	0;
$opt_d		=	0;

$magic		= 	'###mdump###';

chop( $localhost = qx/hostname/ );
$localhost || die "Unable to determine the local host name: $!\n";

# I am using a Exabyte 8700LT which is able to write 8500c format onto 8mm tapes
# your parameters may vary

$bdsf = "bdsf 126 54000 26000";

# collect all the options

use Getopt::Std;
&getopts('l:f:ued');
$opt_u && ($opt_u = 'u');
print "Mdump options:
	Level=$opt_l
	Update=$opt_u
	device=$opt_f
	Eject=$opt_e
	\n" if $opt_d;

@ARGV || die "No file system to dump. Exiting\n";

# decide the dump device name and host
($dump_host, $dump_dev) = split(':', $opt_f);
$dump_dev || do { $dump_dev = $dump_host; $dump_host = "localhost"; };
($dump_host eq $localhost) && ($dump_host = 'localhost');
($dump_dev && $dump_host) || die "Please supply dump device using -f option\n";
print "Dump device: $dump_dev on $dump_host\n" if $opt_d;

print "Local host name is: $localhost\n" if $opt_d;


# collect file systems to dump

@filesys = ();
$error = 0;

while( @ARGV )
{
	incl( shift @ARGV );
}

@filesys || die "No file system to dump\n";


if( $opt_d )
{
	my($fs) = 0;
	print "Dumpping following file systems using level $opt_l:\n";
	foreach $name ( @filesys )
	{
		$fs++;
		print "\t$fs	$name\n";
	}
}

$error && die "Error enountered, exiting\n";
$| = 1;

# decide the mt command
$mt = "mt -f $dump_dev";
$mt = "rsh $dump_host $mt" unless $dump_host eq 'localhost';

# rewind the tape
do_system("$mt rewind");

# decide the dd command to use
$dd = "dd";
$dd = "rsh $dump_host $dd" unless $dump_host eq 'localhost';

# save the configuration at the beginning of the tape
print "$dd of=$dump_dev\n" if $opt_d;

open( DD, "| $dd of=$dump_dev" ) || die "Cannot run $dd: $!\n";
# print the magic word
print DD "$magic Version 1.0 (July 9 1998)\n";
print DD join(';', @filesys),"\n", ' 'x1024, "\n";
close DD;


# do the real dump
while( $arg = shift @filesys )
{
	my($dev) = $opt_f;
	# remove the host part if it is the same as the device host
	$host = "";
	$fs = $arg;
	($fs =~ /:/) && (($host,$fs)=split(':', $fs) );
	$dev =~ s/^$host://;
	$cmd = "rdump $opt_l$opt_u" . "$bdsf $dev";
	if( $host && ($host ne $localhost) )
	{
		$cmd = "rsh -n $host $cmd";
	}
	print "$cmd $fs\n";
	system "$cmd $fs" || warn "$cmd $fs: $!\n";

}

# eject the tape if required
$opt_e && do_system("$mt offline");

# include file system into the dump list in the order given
# if a name given is a file, the file is taken to be a configuration file
# with each line corresponding to a file system

sub incl {
	my( $file ) = @_;

	# if $file is a directory, it is treated as a mount point for a file system
	#
	if( ! -f $file ) { 
		my($host) = ();
		( $file =~ /:/ ) && ( ($host,$file) = split(':', $file) );
		( !$host || $host eq 'localhost') && ($host = $localhost);

		push @filesys, "$host:$file"; return; 
	}

	open(IN, $file ) || warn "Cannot open configuration $file: $!\n"
		&& ($error = 1) && return;
	while(<IN>)
	{
		/^#/ && next;
		foreach $fs ( split )
		{
			$fs =~ s/\s//g;
			# the contents of the configuration file is pushed back to
			# ARGV so we can have nested configuration files
			$fs && push(@ARGV, $fs);
		}
	}
	close IN;
}

sub do_system {
	my($cmd) = @_;

	print "$cmd ...." if $opt_d;
	my($system) = system($cmd);
	print "successed\n" if $system == 0 && $opt_d;
	print "failed\n" if $system && $opt_d;
}

