#!/usr/local/bin/perl -w
#
# (C) Copyright XS4ALL, 1997
# written by Jan-Pieter Cornet <johnpc@xs4all.net>

use FileHandle;
use Time::Local;
use strict;

my $mon = 0;
my %mon = map { $_, $mon++ } qw(
    Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec
);

sub LOCK_SH () { 1 }
sub LOCK_EX () { 2 }
sub LOCK_NB () { 4 }
sub LOCK_UN () { 8 }

my $hadsig = 0;
my($pid, $host, $path, $dumpargs);

sub forward_sig () {
    my($signame) = @_;

    $hadsig++;
    kill $signame, $pid if $pid;
}

my $dump_snapshot = "snapshot_for_amanda";

my $debugfile = "/tmp/dumpout";
my $debug = -f $debugfile;

($host, $path) = $ARGV[-1] =~ m-^/dev/([^/]*)(.*)$-;
$path ||= "/";

if ( $debug ) {
    unless ( open(TMP, ">>$debugfile") ) {
	warn("cannot append $debugfile: $!\n");
	$debug = 0;
    }
    else {
	TMP->autoflush(1);
    }
}

if ( $ARGV[0] =~ /^(\d)sf$/ && $ARGV[2] eq "-" ) {
    $dumpargs = "$1f -";
}
elsif ( $ARGV[0] =~ /^(\d)usf$/ && $ARGV[2] eq "-" ) {
    $dumpargs = "$1uf -";
}
elsif ( $ARGV[0] =~ /W/ ) {
    # add extra dump W lines
    my $file;
    foreach $file ( </netapp*/etc/dumpdates> ) {
	next unless open(DATES, $file);
	my $sysname;
	($sysname) = $file =~ m-^/([^/]*)/-; 
	my %filesys;
	while ( <DATES> ) {
	    my ($dev, $level, $wday, $mon, $mday, $time, $year) = split;
	    my ($h, $m, $s) = split(/:/, $time);
	    next unless $wday =~ /^[A-Z][a-z]{2}$/ && defined $s &&
		    exists $mon{$mon} && $year > 1900 && $mday <= 31;
	    my $date = timelocal($s, $m, $h, $mday, $mon{$mon}, $year - 1900);
	    $dev =~ s-^/\.snapshot/[^/]+/-/-;
	    if ( !exists $filesys{$dev} || $filesys{$dev}[0] < $date ) {
		$filesys{$dev} = [ $date, $level ];
	    }
	}
	close(DATES);
	my($filesys, $nfilesys);
	foreach $filesys ( sort keys %filesys ) {
	    ($nfilesys = $filesys) =~ s-/$--;
	    printf
    "  %-13s (%6s) Last dump: Level %d, Date %s %s %2d %02d:%02d\n",
		"/dev/$sysname$nfilesys", "/$sysname$nfilesys",
		    $filesys{$filesys}[1],
		    (split /[ :]+/, localtime($filesys{$filesys}[0]))[0..4];
	    print TMP "[dump W]: added /$sysname$nfilesys level ",
		$filesys{$filesys}[1], " Date ",
		scalar localtime($filesys{$filesys}[0]), "\n" if $debug;
	}
    }
    exit 0;
}
else {
    die("cannot handle dump @ARGV\n");
}

die("NETAPP dump: no host!\n") unless $host;
my $lockfile = "/tmp/$host-dumplock";

# setup signal forwarding
$SIG{INT} = $SIG{HUP} = $SIG{PIPE} = $SIG{TERM} = $SIG{QUIT} =
    \&forward_sig;

my $have_snapshot;
# lock this host
if ( -f $lockfile ) {
    # allow previous dump enough time to delete snapshot before restarting
    print TMP "[$$]: $lockfile found, delaying for snapshot deletion...\n"
	if $debug;
    sleep 60;
}
open(LOCK, "+>$lockfile") or die("cannot open $lockfile: $!\n");
if ( ! flock(LOCK, LOCK_EX | LOCK_NB) ) {
    print TMP "[$$]: Waiting for lock on $lockfile\n" if $debug;
    flock(LOCK, LOCK_EX) or
	die("cannot acquire lock on $lockfile: $!\n");
}

# try backing up from the snapshot if it exists
$path = "/.snapshot/$dump_snapshot" if -d "/$host/.snapshot/$dump_snapshot";

# loop when netapp complains about another dump running (now unlikely)
DUMP:
while ( 1 ) {
    # fork a dump and filter its STDERR
    pipe RDERR, WRERR;
    $pid = fork;
    die("cannot fork: $!\n") unless defined $pid;
    if ( $pid == 0 ) {
	# child
	close(LOCK);
	open(STDERR, ">&WRERR") or die("cannot dump WRERR: $!\n");
	close(WRERR);
	close(RDERR);
	close(TMP) if $debug;
	exec("/usr/bin/rsh", $host, "dump $dumpargs $path");
	die("cannot exec /usr/bin/rsh: $!\n");
    }
    # parent
    close(WRERR);
    close(STDIN);
    print TMP "----\n[$pid]: (perl pid [$$]) ", scalar localtime,
	"\n[$pid]: dump @ARGV\n",
	"[$pid]: NETAPP $host dump $dumpargs $path\n" if $debug;
    my $first = 1;
    while ( <RDERR> ) {
	# mangle output for dump so amanda recognises it
	if ( /^DUMP: running already; can't start another/ ||
	     /^select: protocol failure in circuit setup/ ||
	     /^Permission denied/ ||
	     ($first && /DUMP: The ENTIRE dump is aborted/ && !$hadsig) )
	{
	    print TMP "[$pid]: (restarting) $_" if $debug;
	    close(RDERR);
	    close(WRERR);
	    wait;
	    sleep 15;
	    redo DUMP;
	}
	$first = 0;
	if ( /^DUMP:.*snapshot_for_dump/ || /^creating\./ || /^deleting\./ ) {
	    print TMP "[$pid]: (ignored) $_" if $debug;
	    $have_snapshot++ if /^creating\./;
	    $have_snapshot-- if /^deleting\./;
	}
	elsif ( /^DUMP: (\d+) tape blocks/ ) {
	    print TMP "[$pid]: (size is $1) $_" if $debug;
	    print STDERR "  DUMP: DUMP: $1 tape blocks\n";
	}
	elsif ( /^DUMP IS DONE/ ) {
	    print TMP "[$pid]: $_" if $debug;
	    print STDERR "  DUMP: DUMP IS DONE\n";
	}
	else {
	    print TMP "[$pid]: $_" if $debug;
	    print STDERR "  $_";
	}
    }
    close(RDERR);
    wait;
    unlink($lockfile) unless $have_snapshot;
    exit ($? >> 8);
}
