#!/usr/bin/perl -w

# base_install_mgr.pl - help automate the install brocess for brt manager
#
# Copyright (C) 2005 Leann Ogasawara Open Source Development labs
#
# written by Leann Ogasawara <ogasawara@osdl.org>
#
# 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

use warnings;
use strict;
use Term::ANSIColor;

my $level = 0;
my $ret;
my $BOOT = 0;
my $ROOT = 1;
my $message;
my $brt_dir;
my $q1;
my $q2;
my $tar;
my $gnupg_dir;
my $system_root;

my @partition = (
	{
		name => "boot",
		partition => "",
		mounted => "/boot",
		fs => "",
	},
	{
		name => "root",
		partition => "",
		mounted => "/mnt/brt",
		fs => "",
	},
);

sub proceed($) {
	my $input = shift;

	#strip leading and trailing whitespace from input
	$input =~ s/^\s+//g;
	$input =~ s/\s+$//g;

	return 1 if lc($input) eq 'y';
	return 0 if lc($input) eq 'n';
	return -1; #invalid y/n answer
}

sub check_step {
	my ($stmt1, $stmt2) = @_;
	my $repeat;
	my $input;
	my $val;

	do {

	        $repeat = 0;
		print $stmt1;

		$input = <STDIN>;

		$val = proceed($input);

		if ($val == 0) {
			print $stmt2;
			$input = <STDIN>;
		}
		elsif ($val < 0) {
			print "Invalid command, please try again\n";
			$repeat = 1;
		} else {
			$input = 0;
		}
	} while ($repeat);
	return $input;
}

sub mount {
	my ($part, $base_dir, $dir, $step) = @_;
	my $msg;
	my $val;

	$msg = `mount $part $base_dir/$dir 2>&1 1>/dev/null`;
	$val = $?;
	if ($val) {
		print_status("red", "ERROR", $msg);
		clean_env($$step, $base_dir);
		exit 1;
	} else {
		print_status("green", "OK", "");
		$$step++;
	}
}

sub get_partition_info {
	my ($node) = @_;

	if (! -d $partition[$node]->{mounted}) {
	    $message = "$partition[$node]->{mounted} does not exist.\n";
	    print_status ("red", "ERROR", $message);
	    exit 1;
	}

	$message = `mount | grep "$partition[$node]->{mounted}"`;
	$message =~ m/^(\S+) on (\S+) type (\S+) /;
	if (!$1 || !$2 || !$3) {
	    return 1;
	} else {
	    print "\nGrabbing partition and file system information for $partition[$node]->{mounted}. . .";
	    $partition[$node]->{partition} = $1;
	    $partition[$node]->{fs} = $3;
	    print_status("green", "OK", "");
	}
	return 0;
}

sub umount {
	my ($dir, $step) = @_;
	my $msg;
	my $val;

	$msg = `umount $dir 2>&1 1>/dev/null`;
	$val = $?;
	if ($val) {
		print_status("red", "ERROR", $msg);
		clean_env($$step, $dir);
		exit 1;
	} else {
		print_status("green", "OK", "");
		$$step++;
	}
}

sub download {
	my ($file, $step, $dir) = @_;
	my $msg;
	my $val;
	$msg = `wget -N $file 2>&1 1>/dev/null`;
	$val = $?;
	if ($val) {
		$msg .= "Unable to download file.\n";
		$msg .= "Exiting base_install.pl\n";
		print_status("red", "ERROR", $msg);
		clean_env($$step, $dir);
		exit 1;
	} else {
		print_status("green", "OK", "");
		$$step++;
	}
}

sub unpack_tar {
	my ($file, $step, $dir) = @_;
	my $msg;
	my $val;
	$msg = `tar $file 2>&1 1>/dev/null`;
	$val = $?;
	if ($val) {
		$msg .= "Unable to unpack file.\n";
		$msg .= "Exiting base_insall.pl\n";
		print_status ("red", "ERROR", $msg);
		clean_env ($step, $dir);
		exit 1;
	} else {
		print_status ("green", "OK", "");
	}
}

sub print_status {
	my ($color, $status, $msg) = @_;
	print color ("bold $color");
	print "[$status]\n";
	print "$msg";
	print color ("reset");
}

sub file_error {
	my ($type, $step, $dir) = @_;
	my $msg;
	$msg = "Error $type file.  Exiting base_install.pl\n";
	print_status ("red", "ERROR", $msg);
	clean_env ($step, $dir);
	exit 1;
}

sub clean_env {
	use Switch;

        my($val, $dir) = @_;
	my $ret;
	my $all_output = "";
	my $msg;

	print "cleaning up system . . .";

	if ($val >= 8) {
	    if ( -f "$partition[$BOOT]->{mounted}/brt.gpgkey" ) {
		$msg = `rm -f $partition[$BOOT]->{mounted}/brt.gpgkey 2>&1 1>/dev/null`;
		if ($?) {
		    $all_output .= "Error deleting $partition[$BOOT]->{mounted}/brt.gpgkey\n$msg";
		}
	    }
	}
	if ($val >= 7) {
		$msg = `rm -f $dir/base_install_client.pl 2>&1 1>/dev/null`;
		if ($?) {
			$all_output .= "Error deleting $dir/base_install_client.pl\n$msg";
		}
	}
	if ($val >= 6) {
		$msg = `rm -f $dir/partition.dat 2>&1 1>/dev/null`;
		if ($?) {
			$all_output .= "Error deleting $dir/partition.dat\n$msg";
		}
	}
	if ($val >= 5) {
		$msg = `rm -f $dir/etc/fstab.temp`;
		if ($?) {
			$all_output = "Error deleting $dir/etc/fstab.temp\n$msg";
		}
	}
	if ($val >= 4) {
		$msg = `rm -f $dir/portage-current.tar.bz2 2>&1 1>/dev/null`;
		if ($?) {
			$all_output .= "Error deleting portage snapshot.\n$msg";
		}
	}
	if ($val >= 0) {
		$ret = chdir;
		if (!$ret) {
			$all_output .= "Error changing to /root dir\n";
		}
	}
	if ($val >= 3) {
		$msg .= `umount $dir/proc 2>&1 1>/dev/null`;
		if ($?) {
			$all_output .= "Error unmounting $dir/proc\n$msg";
		}
	}
	if ($val >= 2) {
		$msg .= `umount $dir/boot 2>&1 1>/dev/null`;
		if ($?) {
			$all_output .= "Error unmounting $dir/boot\n$msg";
		}
	}
	if ($val >= 1) {
		$msg = `rm -f $dir/base_install_current.tar.bz2 2>&1 1>/dev/null`;
		if ($?) {
			$all_output .= "Error deleting $dir/base_install_current.tar.bz2\n$msg";
		}
	}

	if ($all_output) {
		print_status ("red", "ERROR", $all_output);
	} else {
		print_status ("green", "OK", "");
	}
}

if (!$ARGV[0] && !$ARGV[1]) {
	print "\nStage:  Installation\n\n";
}
elsif (($ARGV[0] eq "clean") && $ARGV[1]) {
	#read in partition information

	$ARGV[1] =~ s/^\s+|\s+$//g;
	$ARGV[1] =~ s/\/+$//g;

	print "Opening file $ARGV[1]/partition.dat . . .";

	if (-f "$ARGV[1]/partition.dat") {
		open(FILE_IN, "< $ARGV[1]/partition.dat") || file_error("opening", 0, $brt_dir);
		my $node = "partition";
		my $j = 0;
		for(<FILE_IN>){
		    if ($j > 0) {
			my $i = 1;
			for $message (split) {
				$partition[$i]->{$node} = $message;
				$i--;
			}
			if ($j == 1) {
				$node = "mounted";
			} else {
				$node = "fs";
			}
		    }
			$j++;
		}
		close(FILE_IN) || file_error ("closing", 0, $brt_dir);
		print_status ("green", "OK", "");
	} else {
		$message = "Can't find partition.dat file.\n";
		print_status("red", "ERROR", $message);
		exit 1;
	}

	clean_env("8", $ARGV[1]);
	exit 0;
} else {
	print "Invalid parameters.  Please use the form:\n\n";
	print "./base_install_mgr.pl clean [dir]\n\n";
	print "where [dir] is your base install root directory";
	exit 1;
}

print "Checking network connection. . .";
$message = `ping -c 3 www.osdl.org 2>&1 1>/dev/null`;
$ret = $?;
if ($ret) {
	print_status ("red", "ERROR", "");
	$q1 = "Is your networking up? y/n:  ";
	$q2 = "Please set up networking first.\n";
	$q2 .= "When finished, press Return to continue . . ."; 
	check_step ($q1, $q2);
} else {
	print_status ("green", "OK", "");
}

#get .gnupg directory from user
for (my $i = 1; $i; ) {
    print "\nWhere is your";
    print color 'bold red';
    print ".gnupg ";
    print color 'reset';
    print "directory located?\n(please type full directory path):  ";
    $gnupg_dir = <STDIN>;
    $gnupg_dir =~ s/^\s+|\s+$//g;
    $gnupg_dir =~ s/\/+$//g;

    #verify if this is correct
    if (! -d $gnupg_dir) {
	print "\ndirectory $gnupg_dir does not exist.  Try again.\n";
	$gnupg_dir = "";
	$i = 1;
	next;
    } elsif (! -f "$gnupg_dir/gpg.conf"){
	print "\n$gnupg_dir does not appear to be a proper\n";
	print "gnupg directory because $gnupg_dir/gpg.conf\ndoes not exist.\n";
	$gnupg_dir = "";
	$i = 1;
    }

    $i = 0;
}

get_partition_info($ROOT);

$brt_dir = $partition[$ROOT]->{mounted};

$q1 = "\nNote:  Any existing data on $partition[$ROOT]->{mounted}\n";
$q1.= "       will be deleted/overwritten.  Proceed? y/n:  ";
$q2 = "\nPlease transfer data before proceeding.\n";
$q2.= "When finished, press Return to continue . . .";
check_step($q1, $q2);

get_partition_info($BOOT);

print "Wiping $brt_dir. . .";
$ret = chdir("$brt_dir");
if ($ret) {
	$message = `rm -rf * 2>&1 1>/dev/null`;
	if ($?) {
		print_status("red", "ERROR", $message);
		exit 1;
	}
	print_status("green", "OK", "");
} else {
	$message = "Unable to cd into $brt_dir.  Exiting base_install.pl\n";
	print_status ("red", "ERROR", $message);
	exit 1;
}

print "Downloading brt stage 3 tarball into $brt_dir. . .";
$tar = "http://developer.osdl.org/dev/brt/downloads/base_install_current.tar.bz2";
download($tar, \$level, $brt_dir);

print "Unpacking brt stage 3 tarball . . .";
$tar = "-xjpf base_install_current.tar.bz2 .";
unpack_tar($tar, $level, $brt_dir);

print "Binding $partition[$BOOT]->{mounted} to $brt_dir/boot . . .";
if ($partition[$BOOT]->{fs}) {
    $message = "-t $partition[$BOOT]->{fs} --bind $partition[$BOOT]->{mounted}";} else {
    $message = "--bind $partition[$BOOT]->{mounted}"
    }
mount($message, "$brt_dir", "boot", \$level);

print "Binding /proc to $brt_dir/proc . . .";
$message = "-t proc --bind /proc";
mount($message, "$brt_dir", "proc", \$level);

print "Downloading portage snapshot . . .";
$tar = "http://developer.osdl.org/dev/brt/downloads/portage-current.tar.bz2";
download($tar, \$level, $brt_dir);

print "Unpacking portage snapshot . . .";
$tar = "-xjf portage-current.tar.bz2 -C $brt_dir/usr";
unpack_tar($tar, $level, $brt_dir);

print "Remove $brt_dir/root/.gnupg if it exists . . .";
$message = "rm -f $brt_dir/root/.gnupg";
$ret = $?;
if ($ret) {
	$message = "Unable to remove directory.  Not Critical.\n";
	print_status ("yellow", "ERROR", $message);
} else {
	print_status ("green", "OK", "");
}

print "Copying $gnupg_dir to $brt_dir/root/.gnupg . . .";
$message = `cp -r $gnupg_dir $brt_dir/root/.gnupg`;
$ret = $?;
if ($ret) {
	$message = "Unable to copy over gnupg directory. Exiting base_install.pl\n";
	print_status ("red", "ERROR", $message);
	clean_env($level, $brt_dir);
	exit 1;
} else {
	print_status ("green", "OK", "");
}

print "Copying /etc/resolv.conf to $brt_dir/etc/resolv.conf . . .";
$message = `cp -L /etc/resolv.conf $brt_dir/etc/resolv.conf`;
$ret = $?;
if ($ret) {
	$message = "Unable to copy over DNS info. Exiting base_install.pl\n";
	print_status ("red", "ERROR", $message);
	clean_env($level, $brt_dir);
	exit 1;
} else {
	print_status ("green", "OK", "");
}

print "Copying /etc/hosts to $brt_dir/etc/hosts . . .";
$message = `cp -L /etc/hosts $brt_dir/etc/hosts`;
$ret = $?;
if ($ret) {
	$message = "Unable to copy over /etc/hosts info. Exiting base_install.pl\n";
	print_status ("red", "ERROR", $message);
	clean_env($level, $brt_dir);
	exit 1;
} else {
	print_status ("green", "OK", "");
}

print "Copying /etc/fstab to $brt_dir/etc/fstab.temp . . .";
$message = `cp -L /etc/fstab $brt_dir/etc/fstab.temp`;
$ret = $?;
if ($ret) {
	$message = "Unable to copy over /etc/fstab info.  Not critical.\n";
	print_status ("yellow", "ERROR", $message);
} else {
	print_status ("green", "OK", "");
	$level++;
}

print "Copying /etc/localtime to $brt_dir/etc/localtime . . .";
$message = `cp -L /etc/localtime $brt_dir/etc/localtime`;
$ret = $?;
if ($ret) {
	$message = "Unable to copy over /etc/localtime info.  Not critical.\n";
	print_status ("yellow", "ERROR", $message);
} else {
	print_status ("green", "OK", "");
}

my $hostname;
$hostname = `hostname`;
chomp $hostname;
print "Copying hostname information to $brt_dir/etc/conf.d/hostname . . .";
$message = `echo -e "#/etc/conf.d/hostname\n\n#Set to the hostname of this machine\nHOSTNAME=\\\"$hostname\\\"\n"  > $brt_dir/etc/conf.d/hostname`;
$ret = $?;
if ($ret) {
    $message = "Unable to copy over hostname information.\n";
    print_status ("red", "ERROR", $message);
    clean_env($level, $brt_dir);
    exit 1;
} else {
	print_status ("green", "OK", "");
}

my $domainname;
$domainname = `hostname -d`;
chomp $domainname;
print "Copying domainname information to $brt_dir/etc/conf.d/domainname . . .";
$message = `echo -e "# /etc/conf.d/domainname\n\n# When setting up resolv.conf, what should take precedence?\n# If you wish to always override DHCP/whatever, set this to 1.\nOVERRIDE=1\n\n# To have a proper FQDN, you need to setup /etc/hosts and /etc/resolv.conf\n# properly (domain entry in /etc/resolv.conf, and FQDN in /etc/hosts).\n#\nDNSDOMAIN=\\\"$domainname\\\"\n\n# This only set what /bin/hostname returns.  If you need to setup NIS, meaning\n# what /bin/domainname returns, please see:\n#\n#   http://www.linux-nis.org/nis-howto/HOWTO/\n#\n#NISDOMAIN=\\\"\\\"\n" > $brt_dir/etc/conf.d/domainname`;
$ret = ($?);
if ($ret) {
    $message = "Unable to copy over domainname information.\n";
    print_status ("red", "ERROR", $message);
    clean_env($level, $brt_dir);
    exit 1;
} else {
	print_status ("green", "OK", "");
}

print "Parsing root file system and partition information . . .";
$message = `mount | grep "on / type"`;
$message =~ m/^(\S+) on (\S+) type (\S+) /;
if (!$1 || !$2 || !$3) {
    $message = "Error parsing root file system and partition information\n";
    print_status ("red", "ERROR", $message);
    clean_env($level, $brt_dir);
    exit 1;
} else {
    $system_root = $1;
    print_status("green", "OK", "");
}

print "Writing out partition information to partition.dat . . .";
open(FILE_OUT, "> partition.dat") || file_error("opening", $level, $brt_dir);
print FILE_OUT "$system_root\n";
print FILE_OUT "$partition[$ROOT]->{partition} ";
print FILE_OUT "$partition[$BOOT]->{partition}\n";

print FILE_OUT "$partition[$ROOT]->{mounted} ";
print FILE_OUT "$partition[$BOOT]->{mounted}\n";

print FILE_OUT "$partition[$ROOT]->{fs} ";
print FILE_OUT "$partition[$BOOT]->{fs}\n";

close(FILE_OUT) || file_error("closing", $level, $brt_dir);
print_status ("green", "OK", "");
$level++;

print "Downloading base_install_client.pl . . .";
$tar = "http://developer.osdl.org/dev/brt/downloads/base_install_client.pl";
download($tar, \$level, $brt_dir);

$message = `chmod 744 base_install_client.pl`;
$ret = $?;

print "\nbase_install_mgr.pl has successfully finished installing\n";
print "the base install on the manager side.  Now we need to finish\n";
print "installation on the client side.  Please execute the following\n";
print "command:\n\n";
if ($ret) {
	print "\tchmod 744 $brt_dir/base_install_client.pl\n";
}

print "\tchroot $brt_dir ./base_install_client.pl\n\n";

exit 0;

