#!/usr/bin/perl -w

#----------------------------------------------------------------------
# copyright (C) 1999-2005 Mitel Networks Corporation
# 
# 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
# 
#----------------------------------------------------------------------
# Restore e-smith config etc from tape using flexbackup

package esmith;
use esmith::ConfigDB;
use esmith::Backup;
use strict;

my $conf = esmith::ConfigDB->open_ro or die "Could not open config db";

my $backup_program = $conf->get('backup')->prop('Program');

unless (defined $backup_program && $backup_program eq 'flexbackup')
{
    exit(0);
}

package main;

###########################################################################

use POSIX;
use English;
use strict;

my $configfile = "/etc/flexbackup.conf";
my $filesystem = "/";
my $tempfile = "/tmp/restore.$$";

my $backup = new esmith::Backup or die "Couldn't create Backup object\n";

my @restore = $backup->restore_list;

package cfg;
require $configfile;
@cfg::filesystems = split(/ /, $cfg::set{full});
package main;

sub FindTapeFile ($);
sub ProbeTape ($);
sub RewindTape ($);
sub FSFTape ($$);
sub DetermineBlocksize($);
sub CreateFileList($);
sub ExtractFiles($$$);
sub NotifyAdmin ($$);
sub DetermineBackupType;

unless (defined @cfg::filesystems && @cfg::filesystems)
{
    my $message = << "EOF";
An error occurred while restoring from tape.

As there were no filesystems listed for backup in /etc/flexbackup.conf
the correct dump file on the tape could not be located.
EOF

    NotifyAdmin("Tape restore failed", $message);

    die "Tape restore failed: no filesystems listed for backup"
	. " in $cfg::filesystems.\n";
}

my $tapefile = &FindTapeFile($filesystem);
++$tapefile;					# Skip the index file

&ProbeTape($cfg::device);
&RewindTape($cfg::device);
&FSFTape($cfg::device, $tapefile);
my $tape_blocksize = &DetermineBlocksize($cfg::device);
&RewindTape($cfg::device);
&FSFTape($cfg::device, $tapefile);
&CreateFileList($tempfile);
&ExtractFiles($filesystem, $tempfile, $tape_blocksize);

###########################################################################

sub FindTapeFile ($)
{
    # Need to locate which file on the tape contains the dump of the
    # root filesystem. Assume it is a one tape archive for the moment.

    my $filesystem = shift;

    foreach (@cfg::filesystems)
    {
	if (m:\W*$filesystem\W*:)
	{
	    my @fs = split(/\s+/, $_);

	    for (my $i = 0; $i < @fs; $i++)
	    {
		return $i if ($fs[$i] eq "$filesystem")
	    }
	}

	my $message = << "EOF";
An error occurred while restoring from tape.

The $filesystem filesystem is not listed on the first tape.
EOF

	NotifyAdmin("Tape restore failed", $message);

	die "Tape restore failed: $filesystem filesystem is not listed"
	    . " on the first tape.\n";
    }
}

sub ProbeTape ($)
{
    my $device = shift;

    system("/bin/mt -f $device status > /dev/null 2>&1");

    if ($CHILD_ERROR)
    {

	my $message = << "EOF";
An error occurred while restoring from tape.

No tape loaded at $device.
EOF

	NotifyAdmin("Tape restore failed", $message);

	die "Tape restore failed: no tape loaded at $device.\n";
    }
}

sub RewindTape ($)
{
    my $device = shift;

    system("/bin/mt -f $device rewind > /dev/null 2>&1");

    if ($CHILD_ERROR)
    {

	my $message = << "EOF";
An error occurred while restoring from tape.

Could not rewind tape at $device.
EOF

	NotifyAdmin("Tape restore failed", $message);

	die "Tape restore failed: could not rewind tape at $device.\n";
    }
}

sub FSFTape ($$)
{
    my $device = shift;
    my $files = shift;

    system("/bin/mt -f $device fsf $files > /dev/null 2>&1");

    if ($CHILD_ERROR)
    {

	my $message = << "EOF";
An error occurred while restoring from tape.

Could not position tape at file $files at $device.
EOF

	NotifyAdmin("Tape restore failed", $message);

	die "Tape restore failed: could not position tape at $device.\n";
    }
}

sub DetermineBlocksize($)
{
    my $device = shift;
    use File::Temp 'tempdir';
    use File::stat;

    my $dir = File::Temp::tempdir( CLEANUP => 1 );
    my $file = "$dir/xx";

    system("dd", "if=$device", "bs=1M", "count=1", "of=$file");

    my $st = stat($file) or die "No $file: $!";
    return (($st->size)/1024);
}

sub DetermineBackupType
{
    my $device = shift;
    my $magic = `/usr/bin/file -sz $device 2>/dev/null`;

    return "tar" if ($magic =~ /tar archive/);
    return "dump" if ($magic =~ /new-fs dump file/);
    return undef;
}

sub CreateFileList ($)
{
    my $file = shift;

    unless (open(FILE, "> $file"))
    {
	my $error = $!;

	my $message = << "EOF";
An error occurred while restoring from tape.

Could not open temporary file $file for writing: $!.
EOF

	NotifyAdmin("Tape restore failed", $message);

	die "Tape restore failed: Could not open temporary file"
	    . " $file for writing: $error\n";
    }

    foreach (@restore)
    {
	print FILE "$_\n";
    }

    close FILE;
}

sub ExtractFiles ($$$)
{
    my $filesystem = shift;
    my $filelist = shift;
    my $blocksize = shift;

    unless (chdir $filesystem)
    {
	my $error = $!;

	my $message = << "EOF";
An error occurred while restoring from tape.

Could not change directories to $filesystem: $!.
EOF

	NotifyAdmin("Tape restore failed", $message);

	die "Tape restore failed: could not change directories"
	    . " to $filesystem. $error\n";
    }

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

    system(
	    "/usr/bin/flexbackup",
	    "-extract",
	    "-d", "blksize=$blocksize",
	    "-flist",
	    $filelist
	);

    if ($CHILD_ERROR)
    {

	my $message = << "EOF";
An error occurred while restoring from tape.

Flexbackup returned with an error. For more details concerning this
error see:

    /var/log/messages
    /flexbackup.extract.log
EOF

	NotifyAdmin("Tape restore failed", $message);

	die "Tape restore failed: flexbackup error.\n";
    }

    unlink $filelist;

    my $message = << "EOF";
Restoring from tape was successful.

For more details concerning this successful restore see:

    /var/log/messages
    /flexbackup.extract.log
EOF

    NotifyAdmin("Tape restore succeeded", $message);
}

sub NotifyAdmin ($$)
{
    my $subject = shift;
    my $message = shift;

    open(DATEMAIL, "|-")
	or exec qw(/var/qmail/bin/datemail -t);

    print DATEMAIL << "EOF";
To: admin
Subject: $subject

$message

EOF

    close DATEMAIL;
}
