#!/usr/bin/perl -w

#
# "SystemImager"
#
#  Copyright (C) 1999-2001 Brian Elliott Finley <brian.finley@baldguysoftware.com>
#  Copyright (C) 2002 Bald Guy Software <brian.finley@baldguysoftware.com>
#
#  $Id: si_mkautoinstalldiskette,v 1.2 2004/09/29 18:37:19 brianfinley Exp $
#
#  Others who have contributed to this code (alphabetically):
#    Frazier, Dann <daniel_frazier@hp.com>
#    Smith, Wesley <wessmith@engr.sgi.com>
#    Zinzilieta, Curtis <czinzilieta@valinux.com>
#
#   2004.07.27  Brian Elliott Finley
#   - allow users to specify _any_ floppy device
#

use lib "USR_PREFIX/lib/systemimager/perl";
use File::Copy;
use Getopt::Long;
use File::Basename;
use POSIX qw(uname);
use SystemImager::Config;
use SystemImager::Common;
use SystemImager::Server;
use vars qw($config $VERSION);

### BEGIN parse the config file ###

my $autoinstall_boot_dir = $config->autoinstall_boot_dir();

if (!$autoinstall_boot_dir) {
    die "AUTOINSTALL_BOOT_DIR not defined in the config file.";
}
### END parse the config file ###

# set shell PATH for system() calls 
$ENV{PATH} = "/usr/local/sbin:/usr/sbin:/sbin:/usr/local/bin:/usr/bin:/bin";

$VERSION="SYSTEMIMAGER_VERSION_STRING";
$program_name = "si_mkautoinstalldiskette";
$version_info = <<"EOF";
$program_name (part of SystemImager) v$VERSION
    
Copyright (C) 1999-2001 Brian Elliott Finley <brian\@systemimager.org>
Copyright (C) 2002 Bald Guy Software <brian.finley\@baldguysoftware.com>
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
EOF

$help_info = $version_info . <<"EOF";

Usage: $program_name [OPTION]...

Options: (options can be presented in any order and may be abbreviated)
 --help
    Display this output.

 --version
    Display version and copyright information.

 --quiet
    Don\'t print any output, just provide an appropriate exit code.
    (requires --floppy or --out-file)

 --floppy DEVICE
    The floppy drive device containing the disk to format.  If not 
    specified, /dev/fd0 is assumed.  If your kernel, initrd, and 
    local.cfg file won't fit on a standard 1.44 floppy, you may 
    specify an alternate floppy device (such as /dev/fd0u1680).

    Use alternate devices at your own risk -- they can do physical
    damage to _really_ old floppy drives in some cases.  But you 
    probably wanted to get rid of that floppy drive anyway...

    (if you specify -floppy, $program_name will run non-interactively)

 --out-file FILE    
    Create a 1.44MB floppy image in FILE.

 --flavor FLAVOR    
    Specify a flavor of boot media.  
    (defaults to "standard" if -quiet is used)

 --kernel FILE      
    Specify an alternate autoinstall kernel.

 --initrd FILE      
    Specify an alternate autoinstall ramdisk.

 --append STRING    
    A string of options that will be passed to the autoinstall
    kernel.

 --ssh-key FILE	   
    Where FILE is the SSH2 id_dsa or id_rsa private key of the
    user account that the autoinstall client will use to
    connect to the imageserver.  

 --config FILE      
    Where FILE contains all the settings necessary for
    the client to set it's hostname and configure it's
    networking information without DHCP.  This file is
    copied to /local.cfg on the autoinstalldiskette.  See 
    /usr/share/doc/systemimager/local.cfg for a well 
    commented example.

Download, report bugs, and make suggestions at:
http://systemimager.org/
EOF

GetOptions( 
    "help"          => \$help,
    "version"       => \$version,
    "floppy=s"      => \$floppy_device_prefix,
    "config=s"      => \$local_cfg,
    "ssh-key=s"     => \$ssh_key,
    "flavor=s"      => \$flavor,
    "kernel=s"      => \$kernel,
    "initrd=s"      => \$initrd,
    "append=s"      => \$append_string,
    "quiet"         => \$quiet,
    "out-file=s"    => \$file
) || die "$help_info";

# if requested, print help information
if($help) {
  print "$help_info";
  exit 0;
}

# if requested, print version and copyright information
if($version) {
  print "$version_info";
  exit 0;
}

# i386 is the only supported architecture for autoinstall floppies. -BEF-
my $arch = (uname())[4];
$arch =~ s/i.86/i386/;
if ($arch ne "i386") {
    die "Autoinstall diskettes are only supported for the i386 architecture.";
}

# if not run as root, this script will surely fail
unless($< == 0) { die "Must be run as root!\n"; }

# if floppy device not specified, set value to run in interactive mode
if ($floppy_device_prefix and $file) {
    die "--floppy and --file are mutually exclusive";
}

if($floppy_device_prefix) { 
    $user_specified_floppy=1;
} elsif($file) { 
    $user_specified_file=1;
}

if($ssh_key) {
    unless(-e $ssh_key) { 
        print qq(SSH private key "$ssh_key" doesn't seem to exist.\n);
        print qq(Please try again.\n);
        exit 1;
    }
}

unless ($kernel and $initrd) {
    my %available_flavors = 
	SystemImager::Common->get_boot_flavors("i386", $autoinstall_boot_dir);
    unless (%available_flavors) {
	print qq(\nI couldn't find any boot flavors for SystemImager on $arch.\n);
        print qq(Please install the appropriate boot files.\n\n);
        exit 1;
    }

    if (($quiet) and (!$flavor)) { $flavor = "standard"; }

    unless ($flavor) {

	print "Here is a list of available flavors:\n\n";
	foreach (sort (keys %available_flavors)) {
	    print "  $_\n";
	    $flavor = $_;
	}

        # If "standard" is one of the available flavours, default to it. -BEF-
        if ($available_flavors{"standard"}) { $flavor = "standard"; }
	
	print "\nWhich flavor would you like to use? [$flavor]: ";
	$flavor = get_response($flavor);
    }
    
    
    # make sure the specified flavor is available
    unless ($available_flavors{$flavor}) {
      print qq(\nI can't find boot files of flavor "$flavor" for the "$arch" architecture.\n);
      print "\nTry running with no options.\n";
      exit 1;
    }

    # only floppies for i386 are supported in this release
    $bin_dir = "$autoinstall_boot_dir/i386/$flavor";

    if (! $kernel) { $kernel = "$bin_dir/kernel"; }
    if (! $initrd) { $initrd = "$bin_dir/initrd.img"; }
}
    
if (!$append_string) { $append_string = ""; }

# Be sure the user includes --floppy or --out-file when doing --quiet
if($quiet and !$user_specified_floppy and !$user_specified_file) { 
    print "FATAL: Must use --floppy or --out-file with --quiet! (use -help for options)\n";
    exit 1;
}

# if user did not specify a floppy device, choose the default
if(!$user_specified_floppy and !$user_specified_file) {
    $floppy_device_prefix = "/dev/fd0";
}

# verify that a valid floppy device was specified
if (! $file) {
    if (! ($floppy_device_prefix =~ /^\/dev\/fd/)) {
	    die "ERROR: Invalid --floppy argument.\n $help_info";
    }
}

# if appropriate, run interactively
if (!$user_specified_floppy and !$user_specified_file) {
    system('clear');
    print << 'EOF';

This program assumes that you have a 1.44MB floppy drive and that
it is /dev/fd0.  You can use the --floppy command line option to
change this value.

If you do use --floppy, this command will run non-interactively!!!
Use the --help option to see all options.

Insert your floppy diskette now.  This will overwrite all
information on your diskette.

EOF

    print "Continue? (y/[n]): ";
    $continue=<STDIN>;
    chomp $continue;
    $continue = lc $continue;
    ($continue eq "y") or die "\nYour diskette has not been modified...\n";
}

# create floppy device if necessary
if ($floppy_device_prefix and !$file) {
    $floppy_device=$floppy_device_prefix;
    unless (-b $floppy_device) {
	    die "Couldn't find device file $floppy_device.\n";
    }

    # format floppy
    if (!$quiet) { print "Formatting floppy using $floppy_device ...\n"; }
    if (-x "/usr/bin/superformat") {

        if ($quiet) {
            system ("superformat $floppy_device hd > /dev/null 2>&1");
        } else {
            system ("superformat $floppy_device hd");
        }
        unless ($? == 0) { die "Couldn't format $floppy_device.\n"; }

    } elsif (-x "/usr/bin/fdformat") {
        
        if ($quiet) {
            system ("fdformat $floppy_device > /dev/null 2>&1");
        } else {
            system ("fdformat $floppy_device");
        }
        if($? != 0) { die "Couldn't format $floppy_device.\n"; }

    }

    if (!$quiet) {
	    print "Creating DOS filesystem on floppy...\n";
    }

    if ($quiet) {
	    system("mkdosfs $floppy_device > /dev/null");
    } else {
	    system("mkdosfs $floppy_device");
    }

    unless ($? == 0) { die "Couldn't create dos filesystem on $floppy_device.\n"; }
    $loop = "";
}

elsif ($file) {
    if (-e "$file") {
        if ($quiet) { 
            print "FATAL: $file already exists, taking conservative action and exiting.\n";
            print "Your diskette has not been modified...\n";
            exit 1;
        } else {
            print "$file already exists, overwrite? (y/[n]): ";
            $continue=<STDIN>;
            chomp $continue;
            $continue = lc $continue;
            ($continue eq "y") or die "\nYour diskette has not been modified...\n";
        }
    }
    system ("dd if=/dev/zero of=$file bs=512 count=2880");
    if ($? != 0) { die "Couldn't create $file"; }

    $floppy_device=$file;
    $loop = "-o loop";
    
    # create dos filesystem on floppy
    if (!$quiet) {
        print "Creating DOS filesystem in $file...\n";
    }
    if ($quiet) {
        system("mkdosfs $floppy_device > /dev/null");
    } else {
        system("mkdosfs $floppy_device");
    }
    unless ($? == 0) { die "Couldn't create dos filesystem on $floppy_device.\n"; }
}

# Run syslinux *before* copying files to prevent syslinux from partially 
# overwriting the first large on the diskette.  Bad syslinux, bad!
if (!$quiet) {print "Using \"syslinux\" to make floppy bootable...\n";}
$command="syslinux -s $floppy_device";
system($command);
if($? != 0) { die "\"syslinux -s $floppy_device\" failed.\n"; }

# create temporary mount point
if (!$quiet) { print "Creating temporary mount point...\n"; }
$mnt_dir="/tmp/.autoinstalldiskette.$$";
mkdir $mnt_dir, 0770 or die "Couldn't create temporary mount point $mnt_dir.\n";

# mount the freshly created filesystem
if (!$quiet) { print "Mounting floppy...\n"; }
$command="mount -t msdos $floppy_device $mnt_dir $loop";
system($command);
if($? != 0) { die "Couldn't execute: $command!\n"; }

# copy stuff to floppy
SystemImager::Server->copy_boot_files_to_boot_media($kernel, $initrd, $local_cfg, $arch, $mnt_dir, $append_string, $ssh_key)
    or die("Couldn't copy required files into the image!");

# unmount floppy
if (!$quiet) {print "Un-mounting floppy...\n";}
system('umount', $floppy_device);
if($? != 0) { 
  die "Couldn't un-mount $floppy_device from $mnt_dir.\n";
}

# get rid of temporary directory
if (!$quiet) { print "Removing temporary mount point...\n";}
rmdir $mnt_dir or die "Couldn't remove temporary mount point $mnt_dir\n";

# print done!
if (!$quiet) { print "Done!\n";}
exit 0;


sub get_response {
    my $garbage_out=$_[0];
    my $garbage_in=<STDIN>;
    chomp $garbage_in;
    unless($garbage_in eq "") { $garbage_out = $garbage_in; }
    return $garbage_out;
}

