#!/usr/bin/perl -w

use strict;

# Copyright (c) 2001-2004, Kungliga Tekniska Högskolan
# (Royal Institute of Technology, Stockholm Sweden)
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
#    notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
#    notice, this list of conditions and the following disclaimer in the
#    documentation and/or other materials provided with the distribution.
#
# 3. Neither the name of the university nor the names of its contributors
#    may be used to endorse or promote products derived from this software
#    without specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.

my $dsmpath = "/opt/tivoli/tsm/client/ba/bin";
my $dsmconfig = "$dsmpath/dsm-afsbackup.opt";

my $scratchpath = "/var/tsm/afsbackup";

$ENV{DSM_CONFIG} = $dsmconfig;

use Date::Manip;
use File::Temp;
my @saveintervals = (0, 60, 150, 240, 360, 540, 720, 32767);
my $now = ParseDate("now");
my %bupset;
my @periodlimits;
foreach my $j (0..7) {
    my $delta = " - $saveintervals[$j] days";
#    print $delta, "\n";
    $periodlimits[$j] = DateCalc("now", $delta);
}
my %bupsetsorted;
my %tosave;
my %fullbackups;




(my $outfh, my $outfname) = mkstemp("/tmp/cleanarchXXXXXX");


sub read_archive {
#    open(ARCHIVE, "</tmp/buplist2") or die " can't open file";
    open(ARCHIVE, "${dsmpath}/dsmc query archive \"$scratchpath/*\"| "); #
    while (<ARCHIVE>) {
	if (m;$scratchpath/(.+)\.backup\.(\d+)\.(\d+);) {
	    my $vol = $1;
	    my @date = ($2, $3);
	    push @{$bupset{$vol}}, \@date;
	}
    }
    close ARCHIVE;
    return \%bupset;
}

sub sortbup {
#    print $a->[1], "\n";
    my $date1 = ParseDate($a->[1]);
    my $date2 = ParseDate($b->[1]);
    return Date_Cmp($date2, $date1);
}

sub delete_this_backup {
    my $volname = shift;
    my $archdates = shift;
    print LOGFD "Deleting incr $volname.backup.$archdates->[0].$archdates->[1] too old.\n";
    print $outfh "$scratchpath/$volname.backup.$archdates->[0].$archdates->[1]\n";
}
    
sub print_buplist {
    my $str = shift;
    my $listp = shift;
    my $vol = shift;
    my $i = 0;
    while (exists $listp->[$i]) {
	print LOGFD "$str $vol.$listp->[$i]->[0].$listp->[$i]->[1] \n";
	$i++
    }
}

sub divide_in_timeperiods {
    my $listp = shift;
    my $vol = shift;
    my @period;
    my $i = 0;
    my $j = 0;
    while (exists $listp->[$i]) {
	for $j (1..8) {
	    if (Date_Cmp($periodlimits[$j - 1], $listp->[$i]->[1]) >= 0 and
		Date_Cmp($listp->[$i]->[1], $periodlimits[$j])>0) {
		push @{$period[$j]}, $listp->[$i];
		$i++;
		last;
	    }
	}
    }
    return \@period;
}

sub printout_timeperiods {
    my $listp = shift;
    my $key = shift;
    my $i;
    my $j;
    foreach $j (1..7) {
	print "$key, $j";
	$i = 0;
	while (exists $listp->[$j]->[$i]) {
	    print ", $listp->[$j]->[$i]->[1]";
	    $i++
	}
	print "\n";
    }
#    print "\n";
}

sub delete_full_bups {
    my $periodsp = shift;
    my $vol = shift;
    my $i = 1;
    for (1..7) {
	if (exists($periodsp->[$_])) {
	    while (length(@{$periodsp->[$_]} > 1)) {
		print LOGFD "Deleting backup $vol.backup.$periodsp->[$_]->[0]->[0].$periodsp->[$_]->[0]->[1] not last in period $_.\n";
		print $outfh "$scratchpath/$vol.backup.$periodsp->[$_]->[0]->[0].$periodsp->[$_]->[0]->[1]\n";
		shift @{$periodsp->[$_]};
	    }
	    print LOGFD "Keeping $vol.backup.$periodsp->[$_]->[0]->[0].$periodsp->[$_]->[0]->[1] last in its period $_.\n";
	}
    }
}

Date_Init();

my $nowdate = UnixDate("now", "%Q");

open LOGFD, ">>", "/var/log/afs-tsm/cleanlog.$nowdate" or die "Can't open logfile";


open STDOUT, ">&LOGFD" or die "Can't dup LOGFILE: $!";
open STDERR, ">&LOGFD" or die "Can't dup LOGFILE: $!";

LOGFD->autoflush(1);


print LOGFD "AFS Clean script\n";
print LOGFD "\n";



%bupset = %{read_archive()};
foreach my $key  (sort keys %bupset ) {
    my $foundincrbup = 0;
    my $j = 0;
    my $i = 0;
#    print "\n";
    
    @{$bupsetsorted{$key}} =  sort sortbup @{$bupset{$key}};

    print LOGFD "Processing volume $key\n";

# save backups for 30 days ie shift off the backups that are less than 30 days
# old. Both full and incremental backups are shifted off.

    while (exists( $bupsetsorted{$key}->[$i])) {
	my $datebup = ParseDate($bupsetsorted{$key}->[$i]->[1]);
	my $delta = DateCalc($datebup, $now); # computes $now - $datebup
	my $deltadays = Delta_Format($delta, 1, "%dt");
	$j = $i; # save the loopvariables value
	$foundincrbup = 1 if ($bupsetsorted{$key}->[$i]->[0] ne "00000000");
	last if ($deltadays > 30);
	$i++
	}

    @{$tosave{$key}} = splice @{$bupsetsorted{$key}}, 0, $j;

    my $lastinc = -1;
    for $i (0..$#{$bupsetsorted{$key}}) {
	if ($bupsetsorted{$key}->[$i]->[0] ne "00000000") {
	    $lastinc = $i;
	}
    }

#    splice @{$bupsetsorted{$key}}, 0, $j;
    $i=0;
    while (exists($bupsetsorted{$key}->[$i])) {
	if ($bupsetsorted{$key}->[$i]->[0] ne "00000000") {
	    unless ($i == $lastinc) {
		delete_this_backup($key, $bupsetsorted{$key}->[$i]);
	    }
	}
	else {
	    push @{$fullbackups{$key}}, $bupsetsorted{$key}->[$i];
	}
	$i++;
    }

#    if (not $foundincrbup) {
#	print "No incremental backups taken for volume $key\n";
#    }
#    print_buplist(\@{$bupsetsorted{$key}}, $key);
#    print_buplist(\@{$fullbackups{$key}}, $key);
    print_buplist("Keeping incr volume", \@{$tosave{$key}}, $key);
#    print "---------------------------------\n";

# Save one extra fullbackup so as to make all incremental backups useful.
    my $extravol = shift @{$fullbackups{$key}};
    print LOGFD "Keeping $key.$extravol->[0].$extravol->[1] extra vol.\n";
    my $periodsp = divide_in_timeperiods(\@{$fullbackups{$key}}, $key);

#    printout_timeperiods($periodsp, $key);

    delete_full_bups($periodsp, $key);

    print $outfh "\n";
    print LOGFD "\n";
}

close($outfh) or die;

my $ret = system("${dsmpath}/dsmc delete archive -NOPROMPT -VERBOSE -FILELIST=$outfname");
if($ret != 0) {
    unlink($outfname) or die;
}

close(LOGFD) or die;
