#!/usr/bin/perl -w
#----------------------------------------------------------------------
# add_drive_to_raid: Add spare disk to existing raid arrays
#----------------------------------------------------------------------
# Copyright (C) 2005 Gordon Rowell <gordonr@gormand.com.au>
# Copyright (C) 2006 Shad L. Lords <slords@mail.com>
#
# 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 strict;
use warnings;

use Getopt::Long;
use Data::Dumper;

my %options = ();

GetOptions(\%options, 'f', 'force');

my $force = $options{f} || $options{force};

my $newdev = $ARGV[0] || die "usage:\n\n\tadd_drive_to_raid [-f] dev\n\n";

my $raid = require "/sbin/e-smith/console-menu-items/manageRAID.pl";

my %devices = $raid->get_raid_details();
my @devices = sort { $devices{$a}{DeviceSize} <=> $devices{$b}{DeviceSize} } keys %devices;

die "There are no RAID devices configured\n" unless $#devices >= 0;

my %partitions = $raid->get_partitions();
my @partitions;

my $minsize = 0;

die "/dev/$newdev is not a block special device\n" unless -b "/dev/$newdev";

for my $dev (@devices)
{
    die "/dev/$newdev is already in use\n" if grep m#^$newdev$#, @{$devices{$dev}{UsedDisks}};
    $minsize += $devices{$dev}{DeviceSize} + 65;
}

die "/dev/$newdev is not large enough\n" unless $partitions{$newdev}{blocks} >= $minsize;

die "/dev/$newdev already contains partitions\n" unless $force or ! grep m#^$newdev.+$#, keys %partitions;

my $pid = open(STDERR, "|-");
die gettext("Can't fork"), ": $!\n" unless defined $pid;

unless ($pid)
{
    exec qw(/usr/bin/logger -p local1.info -t add_drive_to_raid);
}

unless (open(OUTPUT, "-|"))
{
    my $boot = "*";
    my $pid = open(SFDISK, "|-");
    if ($pid)
    {
	# parent
	for my $dev (@devices)
	{
	    unless ($dev eq $devices[$#devices])
	    {
		print SFDISK ",", $devices{$dev}{DeviceSize} + 65, ",fd,$boot\n";
	    }
	    else
	    {
		print SFDISK ",,fd,$boot\n";
	    }
	    $boot = "-";
	}
	print SFDISK ",0\n" for (1..4);
	close(SFDISK) || die "SFDISK kid exited $?\n";
    }
    else
    {
	# child
	exec("/sbin/sfdisk", "-uB", "--no-reread", "--force", "/dev/$newdev")
	    or die "can't exec program: $!\n";
	# NOTREACHED
    }

    print "\nChecking partitions on /dev/$newdev...\n";
    sleep(3);
    my $good;
    my $cnt = 0;
    do {
	$cnt++;
	$good = 1;
	sleep(1);

	%partitions = $raid->get_partitions();
	@partitions = sort grep m#^$newdev.+$#, keys %partitions;
	if ( $#devices == $#partitions ) {
	    foreach my $part ( 0..($#devices-1) ) {
		$good &= $partitions{$partitions[$part]}{blocks} >= $devices{$devices[$part]}{DeviceSize} + 64;
		$good &= $partitions{$partitions[$part]}{blocks} <= $devices{$devices[$part]}{DeviceSize} + 68;
		$good &= -b "/dev/$partitions[$part]" || 0;
	    }
	    $good &= $partitions{$partitions[$#devices]}{blocks} >= $devices{$devices[$#devices]}{DeviceSize} + 64;
	    $good &= -b "/dev/$partitions[$#devices]" || 0;
	} else {
	    $good = 0;
	}
    } until ( $good || $cnt > 60 );
    print "\n";

    die "\nPartitions on /dev/$newdev aren't correct.  Reboot may be necessary.\n" unless $good;

    sleep(3);
    foreach my $part (0..$#devices)
    {
	print "Going to add /dev/$partitions[$part] to  $devices[$part]\n";
	system("/sbin/mdadm", $devices[$part], "--add", "/dev/$partitions[$part]");
	sleep(2);
    }

    print "\nWaiting for boot partition to sync before installing grub...\n";
    sleep(15);

    $pid = open(GRUB, "|-");
    if ($pid)
    {
	# parent
	print GRUB "device (hd0) /dev/$newdev\n";
	print GRUB "root (hd0,0)\n";
	print GRUB "setup (hd0)\n";
	print GRUB "quit\n";
	close(GRUB) || die "GRUB kid exited $?\n";
	exit(0);
    }
    else
    {
	# child
	exec("/sbin/grub", "--no-floppy", "--batch")
	    or die "can't exec program: $!\n";
	# NOTREACHED
    }
}
while(<OUTPUT>)
{
	print "$_";
	next unless $_;
	warn "$_";
}
close(OUTPUT) or die "Closing stdin pipe reported: $!\n";

