Alwanza Home Extraordin-Air Team Final Project Linux Course Home
#!/usr/bin/perl
#
#        Projects 20 and 21
#        UW Extension UNIX/Linux Summer Intensive Program
#
#        Jesse Snyder
#        July 29th, 2002
#
#        The purpose of the program is to create a compressed .tar file
#        composed of all files listed in the output of another Perl program.
#        Additional conditions include:
#                -The name of the file should include the date and the machine name
#
#                -The program should estimate the disk space required to hold the
#                .tar file, compare it to available disk space, and proceed with the
#                backup only if there is ample room.
#

use strict;


# We never want to run backup if there are 100 kb or less on disk
use constant MIN_FREE_SPACE => 100 * 1000;    
use constant MIN_TAR_FILES => 1;
use constant TRUE => 1;
use constant FALSE => 0;

my $archiveDir = "/root/bin/archive/";
my $archiveList = "/root/bin/changes.ls";
my $fileSystem = "/dev/hda2";

# if we've deleted all but one of our old backup files
# and we still don't have room, this will be set to 1
my $jammed = 0;
my $tars_remaining;        
my $enough_space = 0;    #   assume there's no room to run backup until proven otherwise

open (WORKTODO, "<$archiveList") or die "Can\'t open $archiveList: $!";

my @files_to_tar;
my @lines;

chomp(@lines = <WORKTODO>);
my $item;
my $count = 0;    #   apparently there is a limit to how many files tar
# can accept but I don't know what that limit is.

foreach $item(@lines) {
    next if -d $item;    #   ignore if item is directory
    next unless -s $item;    #   ignore if file doesn't exist or has zero size
    next if $item =~ /linux-2.5.9/;    #   don't archive kernals

    $item =~ s|^\/||;    #   remove leading "/" for tar
    # print "item = $item\n";
    push(@files_to_tar, $item);
    $count++;
    # last if $count == 1000;
}
opendir (ARCHIVES, $archiveDir);
my @archive_list = grep !/^\./, (readdir(ARCHIVES));
# print "archive_list = @archive_list" . "\n";
#### Make initial check of space required for backup and available disk space
($tars_remaining, $jammed) = &Check_Jam(@archive_list);
# print "jammed = $jammed\n";
$enough_space = &Check_Space($fileSystem, @files_to_tar);

#### If there isn't enough space, try deleting old backup files until there is enough space
until(($enough_space) || ($jammed)||($tars_remaining == 1)) {
    &Delete_Oldest_Tar(@archive_list);
    $enough_space = &Check_Space($archiveList);
    ($tars_remaining, $jammed) = &Check_Jam(@archive_list);
}

#### If we're down to one file and there still isn't room, report the situation and we're done
if ($jammed) {
    &Report_Jam;
} elsif ($enough_space) {
    #### If there is enough room, we can proceed with backup
    &Do_Backup($archiveDir, @files_to_tar);
} else {
    &Report_Error($enough_space);
    #### Error condition
}

closedir ARCHIVES;
close WORKTODO;

#### Function Definitions #####
#*********************
sub Check_Space {
    my ($fileSystem, @files_to_tar) = @_;
    my $section;
    my $total;
    my $used;
    my $unused;
    my @usage = `df -kl`;
    # print "usage =\n@usage" . "\n";
    my $use;
    foreach $use(@usage) {
        next if $use !~ /^\W*$fileSystem/;
        # print "use line 97 = $use\n";
        ($section, $total, $used, $unused) = split (" ", $use);
    }

    my $free_space = $unused;    # total kilobytes free on disk
    print "space available on disk = $free_space\n";

    my $tar;
    my $size;
    my $totalsize;
    foreach $tar(@files_to_tar){
        $tar = "/" . $tar;    #   restore beginning / to files to check size
        $size = ((stat($tar))[7]) * 2;
        $totalsize += $size;
        # print "file = $tar\n    total size = $totalsize\n";
    }
    # 65% of total file size in kilobytes
    my $backup_requires = ($totalsize) * (65/100);    
    print "back-up size estimate = $backup_requires kilobytes\n";
    
    if ($free_space - $backup_requires < MIN_FREE_SPACE){
        return FALSE;
    } else {
        return TRUE;
    }
}

#*********************
sub Check_Jam {
    my @archive_list = @_;    # listing of the whole archive directory

    # number of .tar files we've got in archive
    my $tars_remaining = $#archive_list + 1;
    # print "tars remaining = $tars_remaining\n";
    
    my $jammed = ($tars_remaining < MIN_TAR_FILES)? 1 : 0;
    return $tars_remaining, $jammed;
}

#*********************
sub Delete_Oldest_Tar {
    my @archive_list = @_;    # listing of the whole archive directory
    my $oldest;                #   name of oldest tar in archive

    #open ARCHV, "/root/bin/changes.ls"
    #or die "Cant open changes.ls: $!";
    
    #@archive_list = ARCHV;
    
    $oldest = @archive_list;
    if ($oldest > 1) {
        sort(@archive_list);
        $oldest = pop(@archive_list);    # get the oldest entry
        my $success = (0 == system "rm -f $oldest");

        return $success;
    } else { return 1; }
}

#*********************
sub Do_Backup {
    my ($archiveDir, @files_to_tar) = @_;
    # we must start in / directory, because tar uses relative paths

    chdir "/" or die "cannot chdir to /: $!";    
    # chdir($archiveDir) or die "cannot chdir to $archiveDir: $!";    

    chomp (my $date = `date +%Y%m%d`);
    chomp (my $machine = `uname -n`);
    my $tar_name = $archiveDir . $date . $machine . ".tar.gz";
    my $tar_log = $archiveDir . $date . $machine . ".log";
    
    print "This process takes about 2 to 5 minutes\n";
    
    my $files_to_tar = join (" ", @files_to_tar);
    # print "files to tar = $files_to_tar\n";

    # the ORDER of the switches is important
    # must begin with "c" and end in "f"
    my $success = `tar -cvzf $tar_name $files_to_tar`;

    open (TARLOG, "> $tar_log");
    if ($success) {
        foreach $_ (@files_to_tar) {
            print TARLOG "$_";
            print TARLOG "\n";
        }    
        print "tar file is in $tar_name\n";
    } else {
        print "tar failed\n";
        print TARLOG "tar failed\n";
    }
    close TARLOG;
    return;

}

#*********************
sub Report_Jam {
    print "There's a problem - 'Report_Jam'.\n";
}

#*********************
sub Report_Error {
    $enough_space = shift;
    print "Not enough space to do backup\n";
}