#!/usr/bin/perl -w

=head1 NAME

passwd - xen-tools role-script to setup root password for new guests.

=head1 AUTHOR

 Steve
 --
 http://www.steve.org.uk/

 $Id: passwd,v 1.1 2007-04-03 00:01:18 steve Exp $

=cut



use strict;
use warnings;
use Digest::MD5;
use Expect;



#
#  Get the arguments our role script was passed.
#
my( $prefix, $passwd ) = ( @ARGV );

#
#  Make sure they are valid.
#
if ( ! -d $prefix )
{
    print "Prefix not found: $prefix\n";
    exit;
}

if ( ! length( $passwd ) )
{
    print "Password missing.\n";
    exit;
}


#
#  Now make sure we have the module we require.
#
eval "use Expect;";
if ( $@ )
{
    print "Expect.pm not found.  Aborting.\n";
    print "(For Debian systems run:\n\tapt-get install libexpect-perl\n";
    exit;
}


#
#  OK now we're good to go:  Continue until it succeeds.
#
my $count = 0;
while( $count < 10 )
{
    changePassword( $prefix, $passwd );
    $count += 1;
}

print "Failed to setup root password\n";
print "-----------------------------\n";



#
#  Don't look at this code.
#
#  OK.  Expect sometimes fails.  It sucks.
#
#  So how do we know if a password change operation fails?  We take
# an MD5(/etc/passwd + /etc/shadow) and if they don't change we've
# failed.
#
#  Upon success we will simply exit();
#
#
sub changePassword
{
    my ( $prefix, $passwd ) = ( @_ );

    #
    # Find current checksum.
    #
    my $orig = checksum( $prefix );

    #
    #  Create the expect object.
    #
    my $exp = Expect->spawn( "/usr/sbin/chroot",
                             $prefix,
                             "/usr/bin/passwd",
                             "root" )
      or die "Cannot spawn the password under chroot: $!\n";

    # prompt
    unless ($exp->expect(5,"Enter new"))
    {
        print "Failed 1st prompt\n";
        $exp->hard_close();
        return;
    }

    # send + wait.
    $exp->send( $passwd . "\n" );
    sleep( 1 );

    # confirm
    unless ($exp->expect(5,"Retype new"))
    {
        print "Failed 2nd prompt\n";
        $exp->hard_close();
        return;
    }

    # send.
    $exp->send( $passwd . "\n" );


    #  Closeup.
    $exp->soft_close();

    #
    #  Did it work?
    #
    my $updated = checksum( $prefix );

    if ( $updated ne $orig )
    {
        print "Password setup correctly.\n";
        exit;
    }
    else
    {
        print "Setting password failed.\n";
    }
}



#
#  Checksum /etc/passwd + /etc/shadow if they exist in the
# given chroot()
#
sub checksum
{
    my( $prefix ) = ( @_ );

    my $sum = '';

    foreach my $file ( qw! /etc/passwd /etc/shadow ! )
    {
        if ( -e $prefix . "/" . $file )
        {
            #
            #  Open the file.
            #
            open(FILE, $prefix . "/" . $file) 
              or die "Cant open $prefix/$file: $!";
            binmode(FILE);

            #
            #  Add the data
            #
            my $md5 = Digest::MD5->new;
            while (<FILE>)
            {
                $md5->add($_);
            }
            close(FILE);

            #
            #  Update the sum
            #
            $sum .= $md5->b64digest;
        }
    }

    return $sum;
}
