#! /usr/bin/perl

#
#   Copyright (C) Dr. Heinz-Josef Claes (2003-2004)
#                 hjclaes@web.de
#   
#   This program is free software; you can redistribute it and/or modify
#   it under the terms of the GNU General Public License as published by
#   the Free Software Foundation; either version 2 of the License, or
#   (at your option) any later version.
#   
#   This program is distributed in the hope that it will be useful,
#   but WITHOUT ANY WARRANTY; without even the implied warranty of
#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#   GNU General Public License for more details.
#   
#   You should have received a copy of the GNU General Public License
#   along with this program; if not, write to the Free Software
#   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#


my $VERSION = '$Id: storeBackupDel.pl 327 2004-03-07 17:44:44Z hjc $ ';
push @VERSION, $VERSION;


use strict;

sub libPath
{
    my $file = shift;

    my $dir;

    # Falls Datei selbst ein symlink ist, solange folgen, bis aufgelst
    if (-f $file)
    {
	while (-l $file)
	{
	    my $link = readlink($file);

	    if (substr($link, 0, 1) ne "/")
	    {
		$file =~ s/[^\/]+$/$link/;
	    }
	    else
	    {
		$file = $link;
	    }
	}

	($dir, $file) = &splitFileDir($file);
	$file = "/$file";
    }
    else
    {
	print STDERR "<$file> does not exist!\n";
	exit 1;
    }

    $dir .= "/../lib";           # Pfad zu den Bibliotheken
    my $oldDir = `/bin/pwd`;
    chomp $oldDir;
    if (chdir $dir)
    {
	my $absDir = `/bin/pwd`;
	chop $absDir;
	chdir $oldDir;

	return (&splitFileDir("$absDir$file"));
    }
    else
    {
	print STDERR "<$dir> does not exist, exiting\n";
    }
}
sub splitFileDir
{
    my $name = shift;

    return ('.', $name) unless ($name =~/\//);    # nur einfacher Dateiname

    my ($dir, $file) = $name =~ /^(.*)\/(.*)$/s;
    $dir = '/' if ($dir eq '');                   # gilt, falls z.B. /filename
    return ($dir, $file);
}
my ($req, $prog) = &libPath($0);
(@INC) = ($req, @INC);

require 'readKeyFromFile.pl';
require 'storeBackupLib.pl';
require 'checkParam.pl';
require 'checkObjPar.pl';
require 'prLog.pl';
require 'version.pl';
require 'dateTools.pl';
require 'fileDir.pl';
require 'humanRead.pl';

my $keepAll = '30d';
my $keepDuplicate = '7d';
my $checkSumFile = '.md5CheckSums';
my $chmodMD5File = '0600';

my $Help = <<EOH;
this program deletes backups created by storeBackup

usage:
	$prog -f configFile
or
	$prog -t targetDir [--doNotDelete] [-L lockFile] 
	[--keepAll timePeriod] [--keepWeekday entry]
	[--keepFirstOfYear] [--keepLastOfYear]
	[--keepFirstOfMonth] [--keepLastOfMonth]
	[--keepFirstOfWeek] [--keepLastOfWeek]
	[--keepDuplicate] [--keepMinNumber] [--keepMaxNumber]
	[-l logFile
	 [--plusLogStdout] [--withTime yes|no] [-m maxFilelen]
	 [[-n noOfOldFiles] | [--saveLogs yes|no]
	 [--compressWith compressprog]]

--file		-f configuration file (instead of parameters)

--targetDir	-t directory in which the backup date directory exists
		   same parameter as in storeBackup
--doNotDelete	   test only, do not delete any backup
--lockFile      -L lock file, if exist, new instances will finish if
		   an old is allready running
		   If set to the same file as in storeBackup it will
		   prevent $prog from running in parallel
		   to storeBackup
--keepAll	   keep backups which are not older than the specified amount
		   of time. This is like a default value for all days in
		   --keepWeekday. Begins deleting at the end of the script
		   the time range has to be specified in format 'dhms', e.g.
		      10d4h means 10 days and 4 hours
		   if you also use the 'archive flag' it means to not
		   delete the affected directories via --keepMaxNumber:
		      a10d4h means 10 days and 4 hours and 'archive flag'
		   default = $keepAll;
--keepWeekday	   keep backups for the specified days for the specified
		   amount of time. Overwrites the default values choosen in
		   --keepAll. 'Mon,Wed:40d Sat:60d10m' means:
			keep backups of Mon and Wed 40days + 5mins
			keep backups of Sat 60days + 10mins
			keep backups of the rest of the days like spcified in
				--keepAll (default $keepAll)
		   you can also set the 'archive flag'.
		   'Mon,Wed:a40d Sat:60d10m' means:
			keep backups of Mon and Wed 40days + 5mins + 'archive'
			keep backups of Sat 60days + 10mins
			keep backups of the rest of the days like specified in
				--keepAll (default $keepAll)
--keepFirstOfYear  do not delete the first backup of a year
		   format is timePeriod with possible 'archive flag'
--keepLastOfYear   do not delete the last backup of a year
		   format is timePeriod with possible 'archive flag'
--keepFirstOfMonth do not delete the first backup of a month
		   format is timePeriod with possible 'archive flag'
--keepLastOfMonth  do not delete the last backup of a month
		   format is timePeriod with possible 'archive flag'
--firstDayOfWeek   default: 'Sun'. This value is used for calculating
		   --keepFirstOfWeek and --keepLastOfWeek
--keepFirstOfWeek  do not delete the first backup of a week
		   format is timePeriod with possible 'archive flag'
--keepLastOfWeek   do not delete the last backup of a week
		   format is timePeriod with possible 'archive flag'
--keepDuplicate    keep multiple backups of one day up to timePeriod
		   format is timePeriod, 'archive flag' is not possible
		    default = $keepDuplicate;
--keepMinNumber	   Keep that miminum of backups. Multiple backups of one
		   day are counted as one backup.
--keepMaxNumber	   Try to keep only that maximum of backups. If you have more
		   backups, the following sequence of deleting will happen:
		   - delete all duplicates of a day, beginning with the old
		     once, except the oldest of every day
		   - if this is not enough, delete the rest of the backups
		     beginning with the oldest, but *never* a backup with
		     the 'archive flag' or the last backup
--logFile	-l  log file (default is STDOUT)
--plusLogStdout	    if you specify a log file with --logFile you can
		    additionally print the output to STDOUT with this flag
--withTime	-w  output in logfile with time: 'yes' or 'no'
		    default = 'yes'
--maxFilelen	-m  maximal length of file, default = 1e6
--noOfOldFiles	-n  number of old log files, default = 5
--saveLogs	    save log files with date and time instead of deleting the
		    old (with [-noOldFiles]): 'yes' or 'no', default = 'no'
--compressWith	    compress saved log files (e.g. with 'gzip -9')
		    default is 'bzip2'

  !!! USAGE IN PARALLEL WITH storeBackup.pl CAN DESTROY YOUR BACKUPS !!!

Copyright (c) 2003-2004 by Heinz-Josef Claes
Published under the GNU General Public License
EOH
    ;

&printVersions(\@ARGV, '-V');

my $startDate = dateTools->new();

my $CheckPar =
    CheckParam->new('-allowLists' => 'yes',
		    '-list' => [Option->new('-option' => '-f',
                                            '-alias' => '--file',
                                            '-param' => 'yes'),
                                Option->new('-option' => '-t',
				            '-alias' => '--targetDir',
                                            '-param' => 'yes'),
				Option->new('-option' => '--doNotDelete'),
				Option->new('-option' => '-L',
                                            '-alias' => '--lockFile',
                                            '-param' => 'yes'),
				Option->new('-option' => '--keepAll',
                                            '-default' => $keepAll),
				Option->new('-option' => '--keepWeekday',
					    '-param' => 'yes'),
				Option->new('-option' => '--keepFirstOfYear',
					    '-param' => 'yes'),
				Option->new('-option' => '--keepLastOfYear',
					    '-param' => 'yes'),
				Option->new('-option' => '--keepFirstOfMonth',
					    '-param' => 'yes'),
				Option->new('-option' => '--keepLastOfMonth',
					    '-param' => 'yes'),
                                Option->new('-option' => '--firstDayOfWeek',
                                            '-default' => 'Sun'),
				Option->new('-option' => '--keepFirstOfWeek',
					    '-param' => 'yes'),
				Option->new('-option' => '--keepLastOfWeek',
					    '-param' => 'yes'),
                                Option->new('-option' => '--keepDuplicate',
					    '-default' => $keepDuplicate),
                                Option->new('-option' => '--keepMinNumber',
					    '-default' => 0,
                                            '-pattern' => '\A\d+\Z'),
                                Option->new('-option' => '--keepMaxNumber',
					    '-default' => 0,
                                            '-pattern' => '\A\d+\Z'),
				Option->new('-option' => '-l',
					    '-alias' => '--logFile',
					    '-param' => 'yes'),
				Option->new('-option' => '--plusLogStdout'),
				Option->new('-option' => '-w',
					    '-alias' => '--withTime',
					    '-default' => 'yes',
					    '-only_if' => '[-l]',
                                            '-pattern' => '\Ayes\Z|\Ano\Z'),
				Option->new('-option' => '-m',
					    '-alias' => '--maxFilelen',
					    '-default' => 1e6,
					    '-pattern' => '\A[e\d]+\Z',
                                            '-only_if' => '[-l]'),
				Option->new('-option' => '-n',
					    '-alias' => '--noOfOldFiles',
					    '-default' => 5,
					    '-pattern' => '\A\d+\Z',
                                            '-only_if' => '[-l]'),
                                Option->new('-option' => '--saveLogs',
                                            '-default' => 'no',
                                            '-only_if' => '[-l]',
                                            '-pattern' => '\Ayes\Z|\Ano\Z'),
                                Option->new('-option' => '--compressWith',
                                            '-default' => 'bzip2',
                                            '-only_if' =>'[-l]')
				]
		    );

$CheckPar->check('-argv' => \@ARGV,
                 '-help' => $Help
                 );

# Auswertung der Parameter
my $configFile = $CheckPar->getOptWithPar('-f');

my $targetDir = $CheckPar->getOptWithPar('-t');
my $doNotDelete = $CheckPar->getOptWithoutPar('--doNotDelete');
my $lockFile = $CheckPar->getOptWithPar('-L');
my $keepAll = $CheckPar->getOptWithPar('--keepAll');
my $keepWeekday = $CheckPar->getOptWithPar('--keepWeekday');
my $keepFirstOfYear = $CheckPar->getOptWithPar('--keepFirstOfYear');
my $keepLastOfYear = $CheckPar->getOptWithPar('--keepLastOfYear');
my $keepFirstOfMonth = $CheckPar->getOptWithPar('--keepFirstOfMonth');
my $keepLastOfMonth = $CheckPar->getOptWithPar('--keepLastOfMonth');
my $firstDayOfWeek = $CheckPar->getOptWithPar('--firstDayOfWeek');
my $keepFirstOfWeek = $CheckPar->getOptWithPar('--keepFirstOfWeek');
my $keepLastOfWeek = $CheckPar->getOptWithPar('--keepLastOfWeek');
my $keepDuplicate = $CheckPar->getOptWithPar('--keepDuplicate');
my $keepMinNumber = $CheckPar->getOptWithPar('--keepMinNumber');
my $keepMaxNumber = $CheckPar->getOptWithPar('--keepMaxNumber');
my $logFile = $CheckPar->getOptWithPar('-l');
my $plusLogStdout = $CheckPar->getOptWithoutPar('--plusLogStdout');
my $withTime = $CheckPar->getOptWithPar('-w');
my $maxFilelen = $CheckPar->getOptWithPar('-m');
my $noOfOldFiles = $CheckPar->getOptWithPar('-n');
my $saveLogs = $CheckPar->getOptWithPar('--saveLogs');
my $compressWith = $CheckPar->getOptWithPar('--compressWith');

if ($configFile)       # Konfigurationsdatei gewhlt
{
    my $prLog = printLog->new();
    my $rcf = readConfigFile->new('-configFile' => $configFile,
				  '-print' => undef,
				  '-prLog' => $prLog,
				  '-tmpdir' => undef,
				  '-compress' => undef,
				  '-uncompress' => undef,
				  '-postfix' => undef,
				  '-noCompress' => undef,
				  '-queueCompress' => undef,
				  '-noCopy' => undef,
				  '-queueCopy' => undef,
				  '-exceptSuffix' => [],
				  '-chmodMD5File' => $chmodMD5File,
				  '-keepAll' => $keepAll,
				  '-keepDuplicate' => $keepDuplicate,
				  '-logInBackupDirFileName' => undef);

    $targetDir = $rcf->get('targetDir');
    $keepAll = $rcf->get('keepAll');
    $keepWeekday = join(' ', @{$rcf->get('keepWeekday')});
    $keepFirstOfYear = $rcf->get('keepFirstOfYear');
    $keepLastOfYear = $rcf->get('keepLastOfYear');
    $keepFirstOfMonth = $rcf->get('keepFirstOfMonth');
    $keepLastOfMonth = $rcf->get('keepLastOfMonth');
    $firstDayOfWeek = $rcf->get('firstDayOfWeek');
    $keepFirstOfWeek = $rcf->get('keepFirstOfWeek');
    $keepLastOfWeek = $rcf->get('keepLastOfWeek');
    $keepDuplicate = $rcf->get('keepDuplicate');
    $keepMinNumber = $rcf->get('keepMinNumber');
    $keepMaxNumber = $rcf->get('keepMaxNumber');
}
elsif (!$targetDir)    # nichts gewhlt
{
    print "You have to chose option (-f) or (-s and -t)!\n$Help";
    exit 1;
}

$targetDir = &::absolutePath($targetDir);

my $prLog;
if ($logFile)
{
    $prLog = printLog->new('-file' => $logFile,
			  '-withTime' => $withTime,
			  '-maxFilelen' => $maxFilelen,
			  '-noOfOldFiles' => $noOfOldFiles);
}
else
{
    $prLog = printLog->new();
}

#
# lock file berprfen
#
if ($lockFile)
{
    if (-f $lockFile)
    {
	open(FILE, "< $lockFile") or
	    $prLog->print('-kind' => 'E',
			  '-str' => ["cannot read lock file <$lockFile>"],
			  '-exit' => 1);
	my $pid = <FILE>;
	chop $pid;
	close(FILE);
	$prLog->print('-kind' => 'E',
		      '-str' => ["strange format in lock file <$lockFile>, " .
				 "line is <$pid>\n"],
		      '-exit' => 1)
	    unless ($pid =~ /\A\d+\Z/o);
	if (kill(0, $pid) == 1)   # alte Instanz luft noch
	{
	    $prLog->print('-kind' => 'E',
			  '-str' => ["cannot start, old instance with pid " .
				     "<$pid> is allready running"],
			  '-exit' => 1);
	}
	else
	{
	    $prLog->print('-kind' => 'I',
			  '-str' => ["removing old lock file of process <$pid>"]
			  );
	}
    }

    open(FILE, "> $lockFile") or
	$prLog->print('-kind' => 'E',
		      '-str' => ["cannot create lock file <$lockFile>"],
		      '-exit' => 1);
    print FILE "$$\n";
    close(FILE);
}

my $statDelOldBackupDirs =
    statisticDeleteOldBackupDirs->new('-prLog' => $prLog);
my $today = dateTools->new();
my $delOld =
    deleteOldBackupDirs->new('-targetDir' => $targetDir,
			     '-doNotDelete' => $doNotDelete,
			     '-checkSumFile' => $checkSumFile,
			     '-prLog' => $prLog,
			     '-today' => $today,
			     '-keepFirstOfYear' => $keepFirstOfYear,
			     '-keepLastOfYear' => $keepLastOfYear,
			     '-keepFirstOfMonth' => $keepFirstOfMonth,
			     '-keepLastOfMonth' => $keepLastOfMonth,
			     '-firstDayOfWeek' => $firstDayOfWeek,
			     '-keepFirstOfWeek' => $keepFirstOfWeek,
			     '-keepLastOfWeek' => $keepLastOfWeek,
			     '-keepAll' => $keepAll,
			     '-keepWeekday' => $keepWeekday,
			     '-keepDuplicate' => $keepDuplicate,
			     '-keepMinNumber' => $keepMinNumber,
			     '-keepMaxNumber' => $keepMaxNumber,
			     '-statDelOldBackupDirs' => $statDelOldBackupDirs
			     );

$delOld->checkBackups();

$delOld->deleteBackups();
$statDelOldBackupDirs->print();

# Statistik ber Dauer und CPU-Verbrauch

my (@l);
my ($user,$system,$cuser,$csystem) = times;
my ($trenn) = "-------+----------+----------";
push @l, sprintf("%-7s|%10s|%10s", " [sec]", "user", "system");
push @l, "$trenn";
push @l, sprintf("%-7s|%10.2f|%10.2f", "process", $user, $system);
push @l, sprintf("%-7s|%10.2f|%10.2f", "childs", $cuser, $csystem);
push @l, "$trenn";
my ($u, $s) = ($cuser + $user, $csystem + $system);
push @l, sprintf("%-7s|%10.2f|%10.2f => %.2f", "sum", $u, $s, $u + $s);

my (@startDate) = ();
if ($startDate)
{
    push @startDate, '           precommand duration = ' .
	$startDate->deltaInStr('-secondDate' => $startDate);
}

my $dEnd = dateTools->new();
my $duration = $startDate->deltaInSecs('-secondDate' => $dEnd);
$duration = 1 if ($duration == 0);   # Minimaler Wert

$prLog->print('-kind' => 'S',
	      '-str' =>
	      ['                      duration = ' .
	       dateTools::valToStr('-sec' => $duration),
	       @l
	       ]);

exit 0;
