#!/usr/bin/python2.4
"""
# Filename:      deb2flash.py
# Maintainer:    Dave Vehrs <dave@feraga.com>
# Created:       07 Aug 2006 08:20:10 PM
# Last Modified: 12 Apr 2007 11:24:23 AM by Dave V
# Copyright:     (C) 2006 Dave Vehrs
#
#                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 _OR_ download at copy at
#                http://www.gnu.org/licenses/licenses.html#TOCGPL
#
# Description:   A tool to automate the installation and customization of Debian GNU/Linux onto a USB Flash Thumbdrive or other
#                removable media.  Designed to be run from an existing Debian session.  (May work for other Debian derivatives 
#                but that is untested at this point.)
#
# Installation:  cp deb2flash.py /usr/local/bin
#                chmod 755 /usr/local/bin/deb2flash.py
"""
# ----------------------------------------------------------------------------------------------------------------------------------
# Import Modules                                                                                                                 {{{
# fcntl
try:
    import apt_pkg, atexit, commands, getpass, os, pexpect, posix, re, signal, sys, time
    from optparse import make_option, OptionGroup, OptionParser
except ImportError, e:
    raise ImportError, str(e)
#                                                                                                                                }}}
# ----------------------------------------------------------------------------------------------------------------------------------
# defaults                                                                                                                       {{{
VERSION = "0.1"

__revision__ = "alpha"

APP_DEPS = [ "coreutils", "debootstrap", "e2fsprogs", "gawk", "grep", "grub",
             "module-init-tools", "mount", "parted"]

MOD_DEPS = [ "ehci_hcd", "ohci_hcd", "usbhid", "usb_storage" ]

USE_PDB = "yes"

DEFAULT_HOSTNAME = "Feraga"
#                                                                                                                                }}}
# ----------------------------------------------------------------------------------------------------------------------------------
# Pexpect prompts to watch for                                                                                                   {{{

PROMPT_APT_YN = re.compile('^Do\syou\swant\sto\scontinue\s\[Y/n\]\?\s*$\Z', re.M)
PROMPT_APT_WO_VERF = re.compile('^Install\sthese\spackages\swithout\sverification\s\[y/N\]\?\s*$\Z', re.M)
PROMPT_APT_KERN_LINK = re.compile('^\s*Create\sa\ssymbolic\slink\sto\sthe\scurrent\skernel\simage\?\s*$\Z', re.M)

PROMPT_CRYPTSETUP_SURE = re.compile('^Are\syou\ssure\?\s[(]Type uppercase yes[)]:\s*$\Z', re.M)
PROMPT_CRYPTSETUP_PASS1 = re.compile('^Enter\sLUKS\spassphrase:\s*$\Z', re.M)
PROMPT_CRYPTSETUP_PASS2 = re.compile('^Verify\spassphrase:\s*$\Z', re.M)

PROMPT_GRUB = re.compile('^grub>\s*$\Z', re.M)

PROMPT_PASSWORD = re.compile('(^\s*Retype|Enter)\snew\sUNIX\spassword:\s*$\Z', re.M)

CMD_PROMPT_ERRORTEST = re.compile('^[0-9]+\r\n^feraga[#]+\s*$\Z', re.M)

CMD_PROMPT = re.compile('^feraga[#]+\s*$\Z', re.M)

#CMD_PROMPT = re.compile('^[\S]+[#]+\s*$\Z', re.M)
#CMD_PROMPT      = re.compile('^[\S@]*?[$]?$\Z', re.M)
#CMD_PROMPT      = re.compile('^[\S@]+\s{\s[\S]+\s}[$]?$\Z', re.M)

#                                                                                                                                }}}
# ----------------------------------------------------------------------------------------------------------------------------------
# functions                                                                                                                      {{{

def chroot_cmd (sess, cmd, timeout, okfail=0):
    """
    Function: chroot_cmd

    Run specifcied command (cmd) in specified chroot session (sess).
    """
    print "    Running Command: %s" % cmd
    sess.sendline("%s" % cmd)
    pl_cmdrun = sess.compile_pattern_list([pexpect.TIMEOUT, pexpect.EOF, PROMPT_APT_YN, PROMPT_APT_WO_VERF, CMD_PROMPT])
    time.sleep(1)
    while True:
        prompt = sess.expect_list(pl_cmdrun, timeout=float("%s" % timeout))
        if prompt == 0: # Timeout
            display_error_pexpect("TIMEOUT", "%s" % cmd, sess.before, sess.after, 1)
        elif prompt == 1: # EOF
            display_error_pexpect("EOF", "%s" % cmd, sess.before, sess.after, 1)
        elif prompt == 2: # Additional packages to install for apt.
            sess.sendline("y")
        elif prompt == 3: # Additional packages to install for apt.
            sess.sendline("y")
        elif prompt == 4: # Command Prompt - Success!
            if okfail == 0:
                sess.sendline("echo $?")
                time.sleep(1)
                pl_cmdtest = sess.compile_pattern_list([pexpect.TIMEOUT, pexpect.EOF, CMD_PROMPT_ERRORTEST])
                prompt = sess.expect_list(pl_cmdtest)
                if prompt == 0: # Timeout
                    display_error_pexpect("TIMEOUT", "Success Test on: %s" % cmd, sess.before, sess.after, 1)
                elif prompt == 1: # EOF
                    display_error_pexpect("EOF", "Success Test on: %s" % cmd, sess.before, sess.after, 1)
                elif prompt == 2: # Additional packages to install for apt.
                    test = []
                    test = sess.after.splitlines()
                    if int(test[0]) == 0:
                        break
                    else:
                        retry_test = raw_input("    Command Failed -- Retry? (yes/NO)  ")
                        if retry_test.lower() != "yes" and retry_test.lower() != 'y' :
                            sess.sendline("%s" % cmd)
                            continue
                        else:
                            print "    Bailing out."
                            sys.exit(1)
            else:
                break

def cleanup_base ():
    """
    Function: cleanup_base

    Display cleanup header as a signal that the program reached the end.
    """
    print "\nCLeaning up temporary build stuff."

def cleanup_bind_dev (builddir):
    """
    Function: cleanup_bind_dev

    Unmount (unbind) /dev.
    """
    print "    Unbinding %s" % (builddir + "/dev.")
    test_unbind = commands.getstatusoutput("umount %s" % (builddir + "/dev"))
    if test_unbind[0] >= 1 :
        print "        Error unmounting %s" % (builddir + "/dev.")

def cleanup_bind_proc (builddir):
    """
    Function: cleanup_bind_proc

    Unmount (unbind) /proc.
    """
    print "    Unbinding %s" % (builddir + "/proc.")
    test_unbind = commands.getstatusoutput("umount %s" % (builddir + "/proc"))
#    if test_unbind[0] >= 1 :
#        print "        Error unmounting %s" % (builddir + "/proc.")

def cleanup_bind_sys (builddir):
    """
    Function: cleanup_bind_sys

    Unmount (unbind) /sys.
    """
    print "    Unbinding %s" % (builddir + "/sys.")
    test_unbind = commands.getstatusoutput("umount %s" % (builddir + "/sys"))
#    if test_unbind[0] >= 1 :
#        print "        Error unmounting %s" % (builddir + "/sys.")

def cleanup_mount (builddir, boot, single):
    """
    Function: cleanup_mount

    Unmount root and boot partitions as necessary.
    """
    if boot >= 1 and single == 0 :
        print "    Unmounting %s." % (builddir + "/boot")
        test_umount = commands.getstatusoutput("umount %s" % (builddir + "/boot"))
        if test_umount[0] >= 1 :
            print "        Error unmounting %s." % (builddir + "/boot")
    print "    Unmounting %s." % (builddir)
    test_umount = commands.getstatusoutput("umount %s" % (builddir))
    if test_umount[0] >= 1 :
        print "        Error unmounting %s." % (builddir)

def cleanup_dmcrypt ():
    """
    Function: cleanup_dmcrypt

    Cryptsetup unmount encrypted partitions we mounted.
    """
    print "    Cryptsetup unmounting /dev/mapper/rootfs."
    test_crypt_umount = commands.getstatusoutput("cryptsetup luksClose /dev/mapper/rootfs")
    if test_crypt_umount[0] >= 1 :
        print "        Error cryptsetup unmounting /dev/mapper/rootfs."

def cleanup_rmbuilddir (builddir):
    """
    Function: cleanup_rmbuilddir

    Remove temporary mount point if we created it.
    """
    print "    Removing %s." % (builddir)
    test_crypt_umount = commands.getstatusoutput("rmdir %s" % (builddir))
    if test_crypt_umount[0] >= 1 :
        print "        Error removing %s directory." % (builddir)

def command_options (usage):
    """
    Function: command_options

    Get and set configuration from command line options
    """
    # Options
    cmd_options = [make_option("-a", "--arch", action="store", type="str",  metavar="<ARCH>", dest="arch", default="i386",
                               help="Set target architecture (i386, x86_64, etc. - Default: i386)"),
                   make_option("--builddir", action="store", type="str", metavar="<DIR>", dest="builddir", default="/mnt/usbbuild",
                               help="Where to mount during build (Default: /mnt/usbbuild)."),
                   make_option("-d", "--device", action="store", type="str", metavar="<DEV>", dest="device",
                               help="Device to install on (ex. /dev/sda)"),
                   make_option("-m", "--mirror", action="store", type="str", metavar="<MIRROR>", dest="mirror",
                               default="http://ftp.us.debian.org/debian",
                               help="Repository Mirror to retrieve packages from (default: http://ftp.us.debian.org/debian)"),
                   make_option("--shred", action="store", type="int",  metavar="<ROUNDS>", default=0, dest="shred",
                               help="Enable shredding of old data on device (Warning: May destroy some USB devices)"),
                   make_option("-s", "--suite", action="store", type="str", metavar="<SUITE>", default="sid", dest="suite",
                               help="Suite to install (Sid, Etch, etc).")]
    cmd_parser = OptionParser(option_list=cmd_options, usage=usage, version="Version: %s-%s" % (VERSION, __revision__))

    # Base Config Options.
    base_options = OptionGroup(cmd_parser, "Core System Options")
    base_options.add_option("--hardware-autoconfig", action="count", dest="hwauto",
                            help="Enable hardware autodetection and configuration with Discover.")
    base_options.add_option("--hostname", action="store", type="str", metavar="<NAME>", dest="hostname",
                            default="%s" % DEFAULT_HOSTNAME, help="Set system hostname.")
    base_options.add_option("--kernel", action="store", type="str", metavar="<KERNEL>", dest="kernel",
                            help="Specify kernel .deb package to install.")
    cmd_parser.add_option_group(base_options)

    # Crypto Options
    crypto_options = OptionGroup(cmd_parser, "Encrypted Root Directory Options")
    crypto_options.add_option("--dmcrypt", action="count", dest="dmcrypt", help="Enable DM-Crypt & LUKS encrypted root partition.")
    crypto_options.add_option("--pass", action="store", default="", dest="passphrase", help="Password for root filesystem.")
    cmd_parser.add_option_group(crypto_options)

    # Network Options
    network_options = OptionGroup(cmd_parser, "Networking Options")
    network_options.add_option("--dhcp", action="count", dest="dhcp", help="Enable DHCP for ETH0 device.")
    network_options.add_option("--ip-address", action="store", type="str", dest="ip_addr", metavar="<IP>", 
                               help="Set ETH0 IP Address to <IP>.")
    network_options.add_option("--ip-gateway", action="store", type="str", dest="ip_gate", metavar="<GATEWAY>", 
                               help="Set ETH0 IP gateway to <GATEWAY>.")
    network_options.add_option("--ip-subnet", action="store", type="str", dest="ip_sub", metavar="<SUBNET>", 
                               help="Set ETH0 IP subnet to <SUBNET>.")
    network_options.add_option("--spoof-mac", action="count", dest="spoofmac", help="Install MacChanger and configure to" + \
                              " set a random MAC address for ETH0 each time it is initialized.")
    cmd_parser.add_option_group(network_options)

    # Partitioning Options.
    warning_options = OptionGroup(cmd_parser, "Partitioning Options")
    warning_options.add_option("--boot", action="store", type="int", metavar="<SIZE>", dest="boot", default=0,
                               help="Enable 2 partitions with boot partition of <SIZE> in MB")
    warning_options.add_option("--single", action="count", dest="single", default=0, help="Force single partition format.")
    cmd_parser.add_option_group(warning_options)

    # Disable various warnings and tests.
    warning_options = OptionGroup(cmd_parser, "Tests and Warnings")
    warning_options.add_option("--no-modtest", action="count", dest="nomodtest", default=0,
                               help="Disable kernel module problem reporting.")
    warning_options.add_option("--no-warning", action="count", dest="nowarning", default=0, help="Disable saftey warning.")
    cmd_parser.add_option_group(warning_options)

    return cmd_parser

def display_error_pexpect (error, cmd, before, after, test_exit):
    """
    Function: display_error_pexpect

    Format and display any error from the execution of a command via pexpect.
    """
    print "Error:  %s." % error
    print "Running Command: %s" % cmd
    if len(before) > 0:
        print '-----------------------------------------'
        print 'BEFORE %s' % (before)
        print '-----------------------------------------'
        print 'AFTER    %s' % (after)
        print '-----------------------------------------'
    if test_exit >= 1 :
        print "Bailing out."
        sys.exit(1)

def extract_substring (text, sub1, sub2):
    """
    Function: extract_substring

    Return substring from text between sub1 and sub2
    """
    return text.split(sub1)[-1].split(sub2)[0]

def sigint_handler (sig, frame):
    """
    Function: sigint_handler

    Captures CTRL-C and sends it through normal shutdown to catch the cleanup routines.
    """
    print "Caught SIGINT (CTRL-C), cleaning up and exiting."
    sys.exit(130)

def test_dependencies (deps):
    """
    Function: test_dependencies

    Test to be sure required applications are already installed.
    """
    apt_pkg.InitConfig()
    apt_pkg.InitSystem()
    cache = apt_pkg.GetCache()

    uninstalled = []

    for application in deps:
        try:
            cache["%s" % application]
        except StandardError:
            uninstalled.append("%s" % application)
    return uninstalled


#                                                                                                                                }}}
# ----------------------------------------------------------------------------------------------------------------------------------
# Catch exceptions and interrupts.                                                                                               {{{

# Send exceptions to PDB for debugging.
if USE_PDB == "yes" :
    import IPython.ultraTB
    sys.excepthook = IPython.ultraTB.FormattedTB(mode='Verbose', color_scheme='Linux', call_pdb=1)

# Capture CTRL-C and send it through a normal shutdown to catch cleanup routines.
signal.signal(signal.SIGINT, sigint_handler)

#                                                                                                                                }}}
# ----------------------------------------------------------------------------------------------------------------------------------
# main                                                                                                                           {{{

def main (argv):
    """
    Function Main for deb2flash
    """

    # Variables
    usage = "%prog [options]"

    # Print Title
    print "deb2flash ver. %s-%s\n" % ( VERSION, __revision__)

    # Command line options
    cmdln_opts = command_options(usage)
    options, args = cmdln_opts.parse_args(argv)

    if len(args) >= 2:
        print "Undefined options on the command line:"
        print "        %s" % args
        sys.exit(1)

    # print warning.
    if options.nowarning < 1:
        print "WARNING:"
        print "This is a development version of deb2flash.  As this tool must be"
        print "run as root, and because it works with disk wiping, partitioning"
        print "and formating tools, it is possible for even a minor bug to do"
        print "large amounts of damage to your system which may or may not"
        print "require reinstalling it.  Use this version at your own risk."
        continue_test = raw_input("continue? (yes/NO)  ")
        if continue_test.lower() != "yes" and continue_test.lower() != 'y' :
            print "Continuation confirmation not recieved, Bailing out."
            sys.exit(0)
        else:
            print

    # Test to be sure its root running it.
    if posix.getuid() != 0 :
        print "This application requires root priviledges to get raw access"
        print "to the devices so we can format them, etc.  Please open a"
        print "root session and rerun the command."
        sys.exit(1)

    # Test for needed applications.
    print "Testing install system for application dependencies..."
    missing_apps = test_dependencies(APP_DEPS)
    if len(missing_apps) > 0:
        print >> sys.stderr, "Missing Dependencies."
        print >> sys.stderr, "Please install the following applications"
        for app in missing_apps :
            print >> sys.stderr, "        %s" % app
        sys.exit(1)

    # Load Kernel Modules as necessary.
    print "\nLoading kernel modules as necessary."
    mod_probs = []
    for module in MOD_DEPS:
        test_output = commands.getstatusoutput('modprobe %s' % module)
        if test_output[0] != 0:
            mod_probs.append("%s" % module)
    if options.nomodtest < 1:
        if len(mod_probs) > 0 :
            print "\nWe experienced problems attempting to load the"
            print "following modules:"
            for module in mod_probs :
                print "        %s" % module
            print "If you know that these modules are not required for your"
            print "environment or if they are built into your kernel, you"
            print "may continue."
            cont_test = raw_input("continue? (yes/NO)  ")
            if cont_test.lower() != "yes" and cont_test.lower() != 'y' :
                print "Bailing out."
                sys.exit(1)
    print

    # Select Media and Verify
    if options.device == None :
        print "What device would you like to install to? ",
        device_selection = raw_input()
        options.device = device_selection

    print "Installation Target: %s" % options.device
    # Test for mounted, if found, unmount or bail out.
    test_mounted = commands.getoutput("mount | grep %s | awk '{print $3}' | sort -r" % (options.device))
    test_mounted_list = []
    test_mounted_list = test_mounted.split()
    if len(test_mounted_list) >= 1:
        for line in test_mounted_list :
            if line == "/" :
                print "Selected device is mounted at /.  You cannot use this"
                print "tool to installed onto your running system."
                print "Bailing out."
                sys.exit(1)
        print "Warning -  I found the device to be installed is already mounted at:"
        for line in test_mounted_list :
            print "        %s" % line
        print "Should I unmount them and continue or exit now?"
        unmount_test = raw_input("continue (c) or EXIT (e): ")
        if unmount_test.lower() != "continue" and unmount_test != "c" :
            print "Bailing out."
            sys.exit(1)
        for line in test_mounted_list :
            unmount_command = commands.getstatusoutput("umount %s" % line)
            if unmount_command[0] != 0 :
                print "Problem unmounting %s" % line
                print "Error was: %s" % unmount_command[1]
                print "Bailing out."
                sys.exit(1)

    # Test that the system can see the device
    device_substring = extract_substring(options.device, '/','\n')
    test_device = commands.getstatusoutput("cat /proc/partitions | grep %s" % device_substring)
    if test_device[0] != 0 :
        print "System cannot see device.  Try unplugging it, then wait a few"
        print "seconds before replugging it in.  Then retry running."
        sys.exit(1)

    # --------------
    # Get User Input.

    print "\nUser Configuration:"

    # DM-Crypt Encrypted Partition Password
    if options.dmcrypt >= 1 and options.passphrase == "" :
        while True :
            encc_pass1 = getpass.getpass('    Please enter password for Root Partition: ')
            encc_pass2 = getpass.getpass('    Verify: ')
            if encc_pass1 == encc_pass2 :
                if encc_pass1 != "" :
                    options.passphrase = encc_pass1
                    break
                else :
                    print "    Passwords cannot be blank."
            else :
                print "    Passwords don't match."

    # Userlist
    userlist = []

    # Root Password.
    while True :
        print 
        root_pass1 = getpass.getpass('\n    Please enter password for the Root user account: ')
        root_pass2 = getpass.getpass('    Verify: ')
        if root_pass1 == root_pass2 :
            if root_pass1 != "" :
                userlist.append(["root", "%s" % root_pass1])
                break
            else :
                print "    Passwords cannot be blank."
        else :
            print "    Passwords don't match."

    print "\n    Enter user accounts and passwords.  To end, press <ENTER> for the username:"
    usercount = 1
    while True :
        username = raw_input('\n    Username: ')
        if username == "" :
            break
        else :
            dup_name = 0
            temp_list = userlist[:]
            while True :
                curr_user = []
                try :
                    curr_user = temp_list.pop()
                except IndexError :
                    break
                if curr_user[0] == username :
                    print "Username already used."
                    dup_name = 1
            if dup_name == 0 :   
                while True :
                    user_pass1 = getpass.getpass('    Please enter password for %s: ' % username)
                    user_pass2 = getpass.getpass('    Verify: ')
                    if user_pass1 == user_pass2 :
                        if user_pass1 != "" :
                            userlist.append(["%s" % username, "%s" % user_pass1])
                            usercount = usercount + 1
                            break
                        else :
                            print "    Passwords cannot be blank."
                    else :
                        print "    Passwords don't match."

# Set default hostname so this condition is no longer possible.   Should we ask if the user wants to change the default?
#    if options.hostname == None :
#        options.hostname = raw_input("\nWhat would you like the hostname to be? ")

    if options.dhcp == None and options.ip_addr == None :
        print "\nNetwork Configuration:"
        net_test = raw_input('    Configure ETH0 [no, manual or DHCP (default)]: ')
        if net_test.lower == "dhcp" or net_test == "" :
            options.dhcp = 1
        elif net_test.lower == "manual" :
            options.ip_addr = raw_input("    Enter the IP Address for ETH0: ")
            options.ip_sub = raw_input("    Enter the Subnet Mask for ETH0: ")
            options.ip_gate = raw_input("    Enter the default gateway for ETH0: ")
        else :
            print "    No network selected (or unrecognized input)."


    # Partition the disk
    if options.boot == 0 and options.single == 0 and options.dmcrypt != 0:
        print "\nPartition Configuration:"
        if options.boot == 0 and options.dmcrypt != 0 :
            options.single = 0
            print "For an encrypted device, we need two paritions.  One for /boot"
            print "and one for everything else on /."
            options.boot = raw_input("How large in MB should the /boot partition be?\n(Generally 15-20MB is enough): ")
        if options.boot == 0 and options.single == 0:
            print "Partitioning the Device"
            print "For small devices (<512mb), it is best to use one large"
            print "partition.  For larger devices, we need two."
            test_partnum = raw_input("Use one large partition or two (1 or 2)?")
            if test_partnum == "1" :
                options.boot = 0
                options.single = 1
            elif test_partnum == "2" :
                options.single = 0
                options.boot = raw_input("How large in MB should the /boot partition be?\n(Generally 15-20MB is enough): ")
            else :
                print "Invalid choice."
                print "Bailing out."
                sys.exit(1)
    

    print "\nEnd configuration stage, starting installation."

    # End User Input.
    # --------------

    # Wipe the disk
    if options.shred != 0 :
        print
        print "Shred any old data on the device (not implemented yet)"
    
    # Partition Media
    print "\nPartitioning Media."
    if options.boot == 0 and options.single == 1 :
        test_part = commands.getstatusoutput('parted %s "mklabel msdos mkpart primary 0.0 -0 set 1 boot on"' % options.device)

    if options.boot >= 1 and options.single == 0 :
        test_part = commands.getstatusoutput('parted %s "mklabel msdos mkpart primary 0.0 %s.0 mkpart primary %s.0 -0 set 1 boot on"' % (options.device, options.boot, options.boot))
    if test_part[0] != 0 :
        print "Error Partitioning."
        print "Bailing out."
        sys.exit(1)

    # Must sleep one second between partitioning and formating the first
    # partition or it will not be found.
    time.sleep(1)

    # dm-crypt start
    if options.dmcrypt >= 1 :
        print
        print "Encrypting the root (/) partition."
        session = pexpect.spawn ("env -i PATH=/bin:/usr/bin:/sbin:/usr/sbin  HOME=$HOME PS1='feraga# ' TERM=$TERM /bin/bash --norc")
        logout = file('/tmp/chroot-encrypt.log', 'w')
        session.logfile = logout
        pl_bash_open = session.compile_pattern_list([pexpect.TIMEOUT, pexpect.EOF, CMD_PROMPT])
        prompt = session.expect_list(pl_bash_open)
        if prompt == 0: # Timeout
            display_error_pexpect("TIMEOUT", "bash", session.before, session.after, 1)
        elif prompt == 1: # EOF
            display_error_pexpect("EOF", "bash", session.before, session.after, 1)
        elif prompt == 2: # Command Prompt - Success!

            pl_cryptsetup = session.compile_pattern_list([pexpect.TIMEOUT, pexpect.EOF, PROMPT_CRYPTSETUP_SURE,
                                                 PROMPT_CRYPTSETUP_PASS1, PROMPT_CRYPTSETUP_PASS2, CMD_PROMPT])
            session.sendline("cryptsetup luksFormat %s" % options.device + '2')
            while True :
                time.sleep(0.1)
                prompt = session.expect_list(pl_cryptsetup)
                if prompt == 0: # Timeout
                    display_error_pexpect("TIMEOUT", "cryptsetup luksFormat %s" % options.device + '2',
                                          session.before, session.after, 1)
                elif prompt == 1: # EOF
                    display_error_pexpect("EOF", "cryptsetup luksFormat %s" % options.device + '2',
                                          session.before, session.after, 1)
                elif prompt == 2:  # Verify that your are sure.
                    session.sendline("YES")
                elif prompt == 3 or prompt == 4: # Command Prompt - Success!
                    session.sendline("%s" % options.passphrase)
                elif prompt == 5: # Command Prompt - Success!
                    session.sendline("echo $?")
                    time.sleep(1)
                    pl_cmdtest = session.compile_pattern_list([pexpect.TIMEOUT, pexpect.EOF, CMD_PROMPT_ERRORTEST])
                    prompt = session.expect_list(pl_cmdtest)
                    if prompt == 0: # Timeout
                        display_error_pexpect("TIMEOUT", "Success Test on: cryptsetup luksFormat %s" % options.device + '2',
                                              session.before, session.after, 1)
                    elif prompt == 1: # EOF
                        display_error_pexpect("EOF", "Success Test on: cryptsetup luksFormat %s" % options.device + '2',
                                              session.before, session.after, 1)
                    elif prompt == 2:
                        test = session.after.splitlines()
                        if int(test[0]) == 0:
                            atexit.register(cleanup_dmcrypt)
                            print "    Command completed successfully."
                            break
                        else:
                            print "    Command failed."
                            print "    Bailing out."
                            sys.exit(1)

        print
        print "Cryptsetup Mounting the encrypted the root (/) partition."
        session.sendline("cryptsetup luksOpen %s rootfs" % (options.device + '2'))
        while True :
            time.sleep(0.1)
            prompt = session.expect_list(pl_cryptsetup)
            if prompt == 0: # Timeout
                display_error_pexpect("TIMEOUT", "cryptsetup luksOpen %s"%(options.device + '2'), session.before, session.after, 1)
            elif prompt == 1: # EOF
                display_error_pexpect("EOF", "cryptsetup luksOpen %s" % (options.device + '2'), session.before, session.after, 1)
            elif prompt == 2:  # Verify that your are sure.
                session.sendline("YES")
            elif prompt == 3 or prompt == 4: # Prompt for password, send it.
                    session.sendline("%s" % options.passphrase)
            elif prompt == 5: # Command Prompt - Success!
                session.sendline("echo $?")
                time.sleep(1)
                pl_cmdtest = session.compile_pattern_list([pexpect.TIMEOUT, pexpect.EOF, CMD_PROMPT_ERRORTEST])
                prompt = session.expect_list(pl_cmdtest)
                if prompt == 0: # Timeout
                    display_error_pexpect("TIMEOUT", "Success Test on: cryptsetup luksOpen %s" % (options.device + '2'),
                                          session.before, session.after, 1)
                elif prompt == 1: # EOF
                    display_error_pexpect("EOF", "Success Test on: cryptsetup luksOpen %s" % (options.device + '2'),
                                          session.before, session.after, 1)
                elif prompt == 2: # Additional packages to install for apt.
                    test = session.after.splitlines()
                    if int(test[0]) == 0:
                        print "    Command completed successfully."
                        break
                    else:
                        print "    Command failed."
                        print "    Bailing out."
                        sys.exit(1)
        # Exit Chroot Jail
        session.close()
        print

    # Format the disk
    print
    print "Formating the partition(s)."
    print "    Partition to format %s" % options.device + "1"
    commands.getstatusoutput("umount %s" % (options.device + "1"))
    test_format = commands.getstatusoutput("mkfs.ext2 %s" % (options.device + "1"))
    if test_format[0] != 0 :
        print "Error Formating:\n %s" % test_format[1]
        print "Bailing out."
        sys.exit(1)
    if options.dmcrypt <= 0 :
        if options.boot >= 1 and options.single == 0 :
            print "    Partition to format %s" % options.device + "2"
            commands.getstatusoutput('umount %s' % (options.device + "2"))
            test_format = commands.getstatusoutput('mkfs.ext2 %s' % (options.device + "2"))
            if test_format[0] != 0 :
                print "Error Formating:\n %s" % test_format[1]
                print "Bailing out."
                sys.exit(1)
    else :
        print "    Partition to format /dev/mapper/rootfs"
        test_format = commands.getstatusoutput('mkfs.ext2 /dev/mapper/rootfs')
        if test_format[0] != 0 :
            print "Error Formating:\n %s" % test_format[1]
            print "Bailing out."
            sys.exit(1)
    
    # Get Block IDs for partitions.
    print
    blkid_getboot = commands.getstatusoutput("blkid -s UUID %s | cut -d = -f 2" %  (options.device + "1"))
    if blkid_getboot[0] != 0 :
        print "Error Getting Block ID for:\n %s" % (options.device + "1")
        print "Error Msg: %s" % blkid_getboot[1]
        print "Bailing out."
        sys.exit(1)
    else: 
        blkid_boot = blkid_getboot[1].strip('" ')
    if options.dmcrypt <= 0 :
        if options.single == 0 and options.boot >= 0:
            blkid_getroot = commands.getstatusoutput("blkid -s UUID %s | cut -d = -f 2" %  (options.device + "2"))
    else :
        blkid_getroot = commands.getstatusoutput("blkid -s UUID /dev/mapper/rootfs | cut -d = -f 2")
        blkid_getencc = commands.getstatusoutput("cryptsetup luksUUID %s" % (options.device + "2"))
        if blkid_getencc[0] != 0 :
            print "Error Getting Block ID for:\n %s" % (options.device + "2")
            print "Error Msg: %s" % blkid_getencc[1]
            print "Bailing out."
            sys.exit(1)
        else: 
            blkid_encc = blkid_getencc[1].strip('" ')
    if blkid_getroot[0] != 0 :
        if options.dmcrypt == 0 and options.single == 0 :
            print "Error Getting Block ID for:\n %s" % (options.device + "2")
        else:
            print "Error Getting Block ID for:\n /dev/mapper/rootfs"
        print "Error Msg: %s" % blkid_getroot[1]
        print "Bailing out."
        sys.exit(1)
    else: 
        blkid_root = blkid_getroot[1].strip('" ')

    print "Block UUIDs:"
    if  options.boot == 0 and options.single >= 1 :
        print "    Root Partition:       %s" % blkid_boot
    else:
        print "    Boot Partition:       %s" % blkid_boot
        if options.dmcrypt >= 1 :
            print "    Enciphered Container: %s" % blkid_encc 
        print "    Root Partition:       %s" % blkid_root

    print
    # Create temp mount points and mount the partitions.
    print "Create temporary build mount point and mount partitions."
    test_direxist = os.access("%s" % options.builddir, 0)
    if test_direxist == False :
        print "    Making temporary build directory mount point (%s)." % options.builddir
        test_mkdir = commands.getstatusoutput("mkdir -p %s " % options.builddir)
        if test_mkdir[0] >= 1 :
            print "Error creating directory"
            print "Bailing out."
            sys.exit(1)
        else:
            atexit.register(cleanup_rmbuilddir, builddir = options.builddir)

    if options.boot == 0 and options.single == 1 :
        print "    Mounting %s on %s" % (options.device + "1", options.builddir)
        test_mount = commands.getstatusoutput("mount %s %s" % (options.device + "1", options.builddir))
        if test_mount[0] >= 1 :
            print "Error mounting root directory"
            print "Bailing out."
            sys.exit(1)
    elif options.boot >= 1 and options.single == 0 :
        if options.dmcrypt <= 0 :
            print "    Mounting %s on %s" % (options.device + "2", options.builddir)
            test_mount = commands.getstatusoutput("mount %s %s" % (options.device + "2", options.builddir))
            if test_mount[0] >= 1 :
                print "Error mounting root directory"
                print "Bailing out."
                sys.exit(1)
        else :
            print "    Mounting /dev/mapper/rootfs on %s" % (options.builddir)
            test_mount = commands.getstatusoutput("mount /dev/mapper/rootfs %s" % (options.builddir))
            if test_mount[0] >= 1 :
                print "Error mounting root directory"
                print "Bailing out."
                sys.exit(1)

        print "    Mounting %s on %s" % (options.device + "1", options.builddir + "/boot")
        test_mkdir = commands.getstatusoutput("mkdir %s" % (options.builddir + "/boot"))
        if test_mkdir[0] >= 1 :
            print "Error creating boot directory"
            print "Bailing out."
            sys.exit(1)
        test_mount = commands.getstatusoutput("mount %s %s" % (options.device + "1", options.builddir + "/boot"))
        if test_mount[0] >= 1 :
            print "Error mounting boot directory"
            print "Bailing out."
            sys.exit(1)
    atexit.register(cleanup_mount, builddir = options.builddir, boot = options.boot, single = options.single)


    print
    # Install base system
    print "Running Debootstrap to install base system"
    print "    This may take a while.  You may monitor its progress by"
    print "    opening another terminal session and running:"
    print "        tail -f %s" % options.builddir + '/install-debootstrap.log'
    print "    If debootstrap encounters errors, you can learn more about what"
    print "    happened by viewing its log files:"
    print "        %s"% options.builddir + '/debootstrap/debpaths'
    print "        %s"% options.builddir + '/debootstrap/debootstrap.log'
    print
    session = pexpect.spawn ("env -i HOME=$HOME PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin PS1='feraga# ' TERM=$TERM /bin/bash --norc")
    logout = file('%s' % options.builddir + '/install-debootstrap.log', 'w')
    session.logfile = logout
    pl_bash_open = session.compile_pattern_list([pexpect.TIMEOUT, pexpect.EOF,
                                                 CMD_PROMPT])
    time.sleep(1)
    prompt = session.expect_list(pl_bash_open)
    if prompt == 0: # Timeout
        display_error_pexpect("TIMEOUT", "Could not start bash session to run debootstrap", session.before, session.after, 1)
    if prompt == 1: # EOF
        display_error_pexpect("EOF", "Could not start bash session to run debootstrap", session.before, session.after, 1)
    if prompt == 2: # Command Prompt - Success!
        print "    Started Bash session."
    debootstrap_count = 0
    while True:
        print "    Starting Debootstrap."
        session.sendline("debootstrap --arch %s %s %s %s" % (options.arch, options.suite, options.builddir, options.mirror))
        pl_cmdrun = session.compile_pattern_list([pexpect.TIMEOUT, pexpect.EOF, CMD_PROMPT])
        time.sleep(1)
        prompt = session.expect_list(pl_cmdrun, timeout=None)
        if prompt == 0: # Timeout
            display_error_pexpect("TIMEOUT", "Debootstrap command.", session.before, session.after, 1)
        elif prompt == 1: # EOF
            display_error_pexpect("EOF", "Debootstrap command.", session.before, session.after, 1)

        elif prompt == 2: # Command Prompt - Success!
            print "    Debootstrap run complete, testing for success."
        session.sendline("echo $?")
        pl_cmdrun = session.compile_pattern_list([pexpect.TIMEOUT, pexpect.EOF, CMD_PROMPT_ERRORTEST])
        time.sleep(1)
        prompt = session.expect_list(pl_cmdrun)
        if prompt == 0: # Timeout
            display_error_pexpect("TIMEOUT", "Debootstrap command.", session.before, session.after, 1)
        elif prompt == 1: # EOF
            display_error_pexpect("EOF", "Debootstrap command.", session.before, session.after, 1)

        elif prompt == 2: # Command Prompt - Success!
            test = session.after.splitlines()
            if int(test[0]) == 0:
                print "    Success, continuing."
                break
            else:
                print "    Test: %s" % test[0]
                if debootstrap_count == 0:
                    print "    Debootstrap exited with an error.  However many"
                    print "    of its errors can be fixed by simply rerunning"
                    print "    it.  So we will sleep 30 seconds and attempt to"
                    print "    run it again."
                    time.sleep(30)
                    print
                elif debootstrap_count == 1:
                    print "    Debootstrap failed a second time.  Waiting 90"
                    print "    seconds and retrying."
                    time.sleep(90)
                    print
                elif debootstrap_count > 1:
                    print "    Debootstrap has failed three times.  Please "
                    print "    check its logs for what is causing it to fail."
                    print "    Bailing out."
                    sys.exit(1)
                debootstrap_count = debootstrap_count + 1

    session.close()
    print "    Close Bash session."

    print
    # Temp bind /proc, and /sys to existing mounts.
    print "Binding /dev, /proc, and /sys to our internal mount points."
    test_bind = commands.getstatusoutput('mount -o bind /proc %s' % options.builddir + '/proc')
    if test_bind[0] != 0 :
        print "Error binding /proc to %s." % options.builddir + '/proc'
        print "Error: %s" % test_bind[1]
        print "Bailing out."
        sys.exit(1)
    else:
        atexit.register(cleanup_bind_proc, builddir = options.builddir)


    test_bind = commands.getstatusoutput('mount -o bind /sys %s' % options.builddir + '/sys')
    if test_bind[0] != 0 :
        print "Error binding /sys to %s." % options.builddir + '/sys'
        print "Error: %s" % test_bind[1]
        print "Bailing out."
        sys.exit(1)
    else:
        atexit.register(cleanup_bind_sys, builddir = options.builddir)

    test_bind = commands.getstatusoutput('mount -o bind /dev %s' % options.builddir + '/dev')
    if test_bind[0] != 0 :
        print "Error binding /dev to %s." % options.builddir + '/dev'
        print "Error: %s" % test_bind[1]
        print "Bailing out."
        sys.exit(1)
    else:
        atexit.register(cleanup_bind_dev, builddir = options.builddir)

    print
    # Input base system configs
    print "Entering various configuation files."

    # fstab
    print "    Configuring /etc/fstab."
    outputfile = file('%s' % options.builddir + '/etc/fstab', 'w')
    if options.boot == 0 and options.single == 1 :
        outputfile.write("UUID=%s       /              ext2 defaults,errors=remount-ro,noatime 0 1\n" % blkid_boot)
    elif options.boot >= 1 and options.single == 0 :
        if options.dmcrypt >= 1 :
            outputfile.write("/dev/mapper/rootfs /              ext2    defaults,errors=remount-ro,noatime   0 1\n")
        else :
            outputfile.write("UUID=%s       /              ext2    defaults,errors=remount-ro,noatime   0 1\n" % blkid_root)
        outputfile.write("UUID=%s       /boot          ext2    defaults,noatime,nodev,noexec,nosuid,ro 0 1\n" % blkid_boot)
        
        # Because /boot is mounted read-only we need to remount it before apt can run and possible update it.
        outputfile2 = file('%s' % options.builddir + '/etc/apt/apt.conf.d/30remount_readwrite', 'w')
        outputfile2.write("DPkg\n")
        outputfile2.write("{\n")
        outputfile2.write('    Pre-Invoke {\n "mount -o remount,rw /boot;" }')
        outputfile2.write('    Post-Invoke {\n "mount -o remount,ro /boot;" }')
        outputfile2.write("}\n")
        outputfile2.close()

    outputfile.write("proc            /proc          proc    defaults                             0 0\n")
    outputfile.write("tmpfs           /tmp           tmpfs   defaults,noatime                     0 0\n")
    outputfile.write("tmpfs           /var/lock      tmpfs   defaults,noatime                     0 0\n")
    outputfile.write("tmpfs           /var/log       tmpfs   defaults,noatime                     0 0\n")
    outputfile.write("tmpfs           /var/run       tmpfs   defaults,noatime                     0 0\n")
    outputfile.write("tmpfs           /var/cache/apt/archives tmpfs defaults,noatime              0 0\n")
    outputfile.write("\n")
    outputfile.write("# /var/tmp is not included as tmpfs because its contents are intended to\n")
    outputfile.write("# survive a reboot.\n")
    outputfile.close()

    # set hostname
    print "    Configuring hostname and hosts file."
    hostname = options.hostname

    outputfile = file('%s' % options.builddir + '/etc/hostname', 'w')
    outputfile.write("%s\n" % (hostname))
    outputfile.close()

    outputfile = file('%s' % options.builddir + '/etc/hosts', 'w')
    outputfile.write("127.0.0.1 localhost.localdoman localhost %s\n" % (hostname))
    outputfile.close()

    # Add script to add final tweaks to run config
    # (/etc/rcS.d/S98feraga-init.sh)
    outputfile = file('%s' % options.builddir + '/etc/init.d/feraga-init.sh', 'w')
    outputfile.write("#!/bin/sh\n")
    outputfile.write("\n")
    outputfile.write("# Feraga chooses to have /var mounted as tmpfs for two reasons:\n")
    outputfile.write("#   1.  Saves space on system.\n")
    outputfile.write("#   2.  Removes all logs at shutdown so fewer traces and potential\n")
    outputfile.write("#       privacy issues remain.\n")
    outputfile.write("# Because of this, we need to recreate any subdirectories that the\n")
    outputfile.write("# installed applications need at start.\n")
    outputfile.write("\n")
    outputfile.write("case \"$1\" in\n")
    outputfile.write("    start)\n")
    outputfile.write("        echo\n")
    outputfile.write("        echo \"Feraga: Privacy Enhanced Portable Workstation.\"\n")
    outputfile.write("        echo \"-Recreating required subdirectories on /var/log (tmpfs)\"\n")
    outputfile.write("        \n")
    outputfile.write("        # /var/log subdirectories.\n")
    outputfile.write("        mkdir -p /var/log/fsck\n")
    outputfile.write("        mkdir -p /var/log/ksymoops\n")
    outputfile.write("        mkdir -p /var/log/news\n")
    outputfile.write("        \n")
    outputfile.write("        # Example for the mixmaster anonymous remailer\n")
    outputfile.write("        if [ -f /usr/bin/mixmaster ] ; then\n")
    outputfile.write("            mkdir -p /var/log/mixmaster\n")
    outputfile.write("        fi\n")
    outputfile.write("        \n")
    outputfile.write("        # Example for the Tor proxy\n")
    outputfile.write("        if [ -f /usr/sbin/tor ] ; then\n")
    outputfile.write("            mkdir -p /var/log/tor\n")
    outputfile.write("        fi\n")
    outputfile.write("        \n")
    outputfile.write("        # Example for Privoxy\n")
    outputfile.write("        if [ -f /usr/sbin/privoxy ] ; then\n")
    outputfile.write("            mkdir -p /var/log/privoxy\n")
    outputfile.write("        fi\n")
    outputfile.write("        \n")
    outputfile.write("        # Apt archive subdirectories.\n")
    outputfile.write("        mkdir -p /var/cache/apt/archives/partial\n")
    outputfile.write("        \n")
    outputfile.write("        echo \"-done.\"\n")
    outputfile.write("        echo\n")
    outputfile.write("        ;;\n")
    outputfile.write("    stop)\n")
    outputfile.write("        echo \"Feraga shutting down.\"\n")
    outputfile.write("        # Add anything that needs to happen at shutdown here.\n")
    outputfile.write("        echo\n")
    outputfile.write("        ;;\n")
    outputfile.write("    *)\n")
    outputfile.write("        echo \"Usage: cryptdisks {start|stop}\"\n")
    outputfile.write("        exit 1\n")
    outputfile.write("        ;;\n")
    outputfile.write("esac\n")
    outputfile.close()
    test_chmod = commands.getstatusoutput('chmod u+rwx,go+rx %s' % options.builddir + '/etc/init.d/feraga-init.sh')
    if test_chmod[0] != 0 :
        print "Error changing permissions on " % options.builddir + '/etc/init.d/feraga-init.sh'
        print "Error: %s" % test_chmod[1]
        print "Bailing out."
        sys.exit(1)
    test_ln = commands.getstatusoutput('cd %s' % options.builddir + '/etc/rcS.d ; ln -s ../init.d/feraga-init.sh ./S37-feraga0-init.sh')
    if test_ln[0] != 0 :
        print "Error linking to " % options.builddir + '/etc/init.d/feraga-init.sh'
        print "Error: %s" % test_ln[1]
        print "Bailing out."
        sys.exit(1)

        # set apt sources.list

    print
    # Enter Chroot Jail
    print "Performing Core Configuration from within a chroot jail."
    print "    In case of errors, session log at be viewed at:"
    print "        %s" % (options.builddir + '/install-chroot.log')
    session = pexpect.spawn ("env -i PATH=/bin:/usr/bin:/sbin:/usr/sbin  HOME=$HOME PS1='feraga# ' TERM=$TERM /usr/sbin/chroot %s" % options.builddir)
    logout = file('%s' % options.builddir + '/install-chroot.log', 'w')
    session.logfile = logout
    pl_bash_open = session.compile_pattern_list([pexpect.TIMEOUT, pexpect.EOF, CMD_PROMPT])
    time.sleep(1)
    prompt = session.expect_list(pl_bash_open)
    if prompt == 0: # Timeout
        display_error_pexpect("TIMEOUT", "Could not start chroot jail", session.before, session.after, 1)
    if prompt == 1: # EOF
        display_error_pexpect("EOF", "Could not start chroot jail", session.before, session.after, 1)
    if prompt == 2: # Command Prompt - Success!
        print "    Started chroot session."

    # Install additional applications.
    chroot_cmd(session, "apt-get clean", 300)
    chroot_cmd(session, "apt-get update", 600)
    chroot_cmd(session, "DEBIAN_FRONTEND=readline apt-get install libterm-readline-gnu-perl", 300)

    # initramfs
    chroot_cmd(session, "DEBIAN_FRONTEND=readline apt-get install initramfs-tools", 300)

    # Cryptsetup, dm-crypt, etc.
    if options.dmcrypt >= 1 :
        chroot_cmd(session, "DEBIAN_FRONTEND=readline apt-get install cryptsetup dmsetup hashalot", 300)
        
        print "        Configuring /etc/crypttab."
        outputfile = file('%s' % options.builddir + '/etc/crypttab', 'w')
        outputfile.write("# <target name> <source device> <key file> <options>\n")
        outputfile.write("rootfs /dev/disk/by-uuid/%s none luks\n" % blkid_encc)
        outputfile.close()

        print "        Configuring /etc/initramfs-tools/scripts/local-top/cryptroot."
        test_copy = commands.getstatusoutput('cp %s %s' % (options.builddir + '/usr/share/initramfs-tools/hooks/cryptroot', options.builddir + '/etc/initramfs-tools/hooks/'))
        if test_copy[0] != 0 :
            print "Error copying %s." % options.builddir + '/etc/initramfs-tools/scripts/local-top/cryptroot'
            print "Error: %s" % test_copy[1]
            print "Bailing out."
            sys.exit(1)
        test_perms = commands.getstatusoutput('chmod ugo+x %s' % options.builddir + '/etc/initramfs-tools/hooks/cryptroot')
        if test_perms[0] != 0 :
            print "Error setting permissions on %s." % options.builddir + '/etc/initramfs-tools/hooks/cryptroot'
            print "Error: %s" % test_perms[1]
            print "Bailing out."
            sys.exit(1)
        test_copy = commands.getstatusoutput("sed '/modprobe -q dm_crypt/a\\\n\\\n    \\# Sleep for USB Devices (Feraga)\\n    echo \\\"Sleeping" \
                                             + " for 5 seconds to allow USB Device detection.\\\"\\n    sleep 5\\n    echo \\\"Awake," \
                                             + " attempting to mount USB Cryptographic Volumes\\\"\\n' %s > %s" % (
                                             options.builddir + '/usr/share/initramfs-tools/scripts/local-top/cryptroot', 
                                             options.builddir + '/etc/initramfs-tools/scripts/local-top/cryptroot'))
        if test_copy[0] != 0 :
            print "Error copying %s." % options.builddir + '/etc/initramfs-tools/scripts/local-top/cryptroot'
            print "Error: %s" % test_copy[1]
            print "Bailing out."
            sys.exit(1)
        test_perms = commands.getstatusoutput('chmod ugo+x %s' % options.builddir + '/etc/initramfs-tools/scripts/local-top/cryptroot')
        if test_perms[0] != 0 :
            print "Error setting permissions on %s." % options.builddir + '/etc/initramfs-tools/scripts/local-top/cryptroot'
            print "Error: %s" % test_perms[1]
            print "Bailing out."
            sys.exit(1)

        print "        Configuring /etc/initramfs-tools/modules."
        outputfile = file('%s' % options.builddir + '/etc/initramfs-tools/modules', 'w')
        outputfile.write('# List of modules that you want to include in your initramfs.\n')
        outputfile.write('# Syntax:  module_name [args ...]\n')
        outputfile.write('# You must run update-initramfs(8) to effect this change.\n')
        outputfile.write('usbcore\n')
        outputfile.write('ehci-hcd\n')
        outputfile.write('ohci-hcd\n')
        outputfile.write('uhci-hcd\n')
        outputfile.write('usbhid\n')
        outputfile.write('ide-core\n')
        outputfile.write('scsi_mod\n')
        outputfile.write('usb-storage\n')
        outputfile.write('mbcache\n')
        outputfile.write('ext2\n')
        outputfile.write('ide-cd\n')
        outputfile.write('ide-disk\n')
        outputfile.write('ide-generic\n')
        outputfile.write('sd_mod\n')
        outputfile.close()

        print "        Configuring /etc/initramfs-tools/initramfs.conf."
        outputfile = file('%s' % options.builddir + '/etc/initramfs-tools/initramfs.conf', 'w')
        outputfile.write('# initramfs.conf\n')
        outputfile.write('# Configuration file for mkinitramfs(8). See initramfs.conf(5).\n')
        outputfile.write('MODULES=most\n')
        outputfile.write('BUSYBOX=y\n')
        outputfile.write('BOOT=local\n')
        outputfile.write('DEVICE=eth0\n')
#        outputfile.write('ROOT=probe\n')
        outputfile.close()
    
    # network configuration
    if options.spoofmac != None or options.dhcp != None :
        if options.spoofmac != None :
            chroot_cmd(session, "DEBIAN_FRONTEND=readline apt-get install macchanger", 300)
        if options.dhcp != None :
            chroot_cmd(session, "DEBIAN_FRONTEND=readline apt-get install dhcp-client", 300)
        print "        Configuring network."
        outputfile = file('%s' % options.builddir + '/etc/network/interfaces', 'w')
        outputfile.write('# This file describes the network interfaces available on your system\n')
        outputfile.write('# and how to activate them. For more information, see interfaces(5).\n')
        outputfile.write('\n')
        outputfile.write('# The loopback network interface\n')
        outputfile.write('auto lo\n')
        outputfile.write('iface lo inet loopback\n')
        outputfile.write('\n')
        outputfile.write('# Primary network interface (eth0)\n')
        outputfile.write('auto eth0\n')
        if options.dhcp != None :
            outputfile.write('iface eth0 inet dhcp\n')
        if options.ip_addr != None :
            outputfile.write('iface eth0 static\n')
            outputfile.write('    address %s\n' % options.ip_addr)
            outputfile.write('    netmask %s\n' % options.ip_sub)
            outputfile.write('    gateway %s\n' % options.ip_gate)
        if options.spoofmac != None :
            outputfile.write('    pre-up macchanger -a eth0\n')
        outputfile.write('\n')
        outputfile.close()

    
    # hardware autoconfiguration:  (i.e. install discover and libdiscover)
    if options.hwauto != None :
        chroot_cmd(session, "DEBIAN_FRONTEND=readline apt-get install discover libdiscover2 discover-data", 300)

    # Grub
    chroot_cmd(session, "DEBIAN_FRONTEND=readline apt-get install grub", 300)
    chroot_cmd(session, "mount -o remount,rw /boot", 300)
    # Configure Grub
    print "        Configuring Grub."

    test_grub_install = commands.getstatusoutput("grub-install --recheck --root-directory=%s %s 2>&1" %
                                                 (options.builddir, options.device))
    if test_grub_install[0] != 0 :
        print "Error installing grub to %s" % options.builddir
        print "Error: %s" % test_grub_install[1]
        print "Bailing out."
        sys.exit(1)
    test_mounted_list = test_grub_install[1].splitlines()
    for line in test_mounted_list:
        if line.find("%s" % options.device) >= 1:
            grub_device = extract_substring(line, '(',')')
            print "        Grub Device: %s" % grub_device

    outputfile = file('%s' % options.builddir + '/boot/grub/menu.lst', 'w')
    outputfile.write("## Feraga Grub Configuration\n")
    outputfile.write("\n")
    outputfile.write(" # default num\n")
    outputfile.write("default         0\n")
    outputfile.write("\n")
    outputfile.write("# timeout sec\n")
    outputfile.write("timeout         5\n")
    outputfile.write("\n")
    outputfile.write("# pretty colours\n")
    outputfile.write("color green/black black/green\n")
    outputfile.write("\n")
    outputfile.write("### BEGIN AUTOMAGIC KERNELS LIST\n")
    outputfile.write("\n")
    outputfile.write("## ## Start Default Options ##\n")
    outputfile.write("\n")
    if options.boot == 0 and options.single == 1 :
        outputfile.write("# kopt=root=UUID=%s ro\n" % blkid_boot)
    elif options.boot >= 1 and options.single == 0 :
        if options.dmcrypt >= 1 :
            outputfile.write("# kopt=root=/dev/mapper/rootfs ro\n")
        else :
            outputfile.write("# kopt=root=UUID=%s ro\n" % blkid_root)
    outputfile.write("# groot=(hd0,0)\n")
    outputfile.write("# alternative=true\n")
    outputfile.write("# lockalternative=false\n")
    outputfile.write("# defoptions=\n")
    outputfile.write("# lockold=false\n")
    outputfile.write("# howmany=all\n")
    outputfile.write("# memtest86=true\n")
    outputfile.write("# updatedefaultentry=false\n")
    outputfile.write("\n")
    outputfile.write("## ## End Default Options ##\n")
    outputfile.write("\n")
    outputfile.write("### END DEBIAN AUTOMAGIC KERNELS LIST\n")
    outputfile.close()

    test_grub_rmdevmap = commands.getstatusoutput('rm -f %s' % options.builddir + '/boot/grub/device.map')
    if test_grub_rmdevmap[0] != 0 :
        print "Error: Failed to remove %s." % options.builddir + '/boot/grub/device.map'
        print "Error: %s" % test_grub_rmdevmap[1]
        print "Bailing out."
        sys.exit(1)
    
    test_grub_install2 = commands.getstatusoutput('grub --batch <<"root (%s,0)\nsetup (%s)\nquit\n"' % (grub_device, grub_device))
    if test_grub_install2[0] != 0 :
        print "Error stage 2 installing grub to %s." % options.builddir
        print "Error: %s" % test_grub_install2[1]
        print "Bailing out."
        sys.exit(1)
    else :
        print "        Grub install complete"
    
    chroot_cmd(session, "mount -o remount,ro /boot", 300)

    # kernel image config
    print "        Configuring /etc/kernel-img.conf."
    outputfile = file('%s' % options.builddir + '/etc/kernel-img.conf', 'w')
    outputfile.write("# Set by deb2flash installer.\n")
    outputfile.write("do_bootfloppy = no\n")
    outputfile.write("do_bootloader = no\n")
    outputfile.write("do_initrd = yes\n")
    outputfile.write("do_symlinks = yes\n")
    outputfile.write("image_in_boot = yes\n")
    outputfile.write("postinst_hook = /usr/sbin/update-grub\n")
    outputfile.write("postrm_hook   = /usr/sbin/update-grub\n")
    outputfile.write("ramdisk = /usr/sbin/mkinitramfs\n")
    outputfile.write("relative_links = yes\n")
    outputfile.write("warn_initrd = no\n")
    outputfile.close()

    # kernel install
    chroot_cmd(session, "DEBIAN_FRONTEND=readline apt-get clean", 300)
    if options.kernel == None :
        if options.arch == "i386" :
            chroot_cmd(session, "DEBIAN_FRONTEND=readline apt-get install linux-image-686", 300)
        else:
            chroot_cmd(session, "DEBIAN_FRONTEND=readline apt-get install %s" % ("linux-image-2.6-" + options.arch), 300)
        chroot_cmd(session, "DEBIAN_FRONTEND=readline apt-get clean", 300)
    else:
        # copy in user build kernel deb package and install it.
        test_kernelcopy = commands.getstatusoutput('cp %s %s' % (options.kernel, options.builddir))
        if test_kernelcopy[0] != 0 :
            print "Error copying kernel into chroot:\n" % test_format[1]
            print "Bailing out."
            sys.exit(1)
        kernel_name = options.kernel.rpartition("/")
        chroot_cmd(session, "DEBIAN_FRONTEND=readline dpkg -i %s" % ("/" + kernel_name[2]), 180)
        # remove kernel package
        chroot_cmd(session, "rm -f %s" % ("/" + kernel_name[2]), 30)

    # Users
    print "    User Configuration:"
    session.logfile = None
    while True :
        curr_user = []
        try :
            curr_user = userlist.pop()
        except IndexError :
            break
        if curr_user[0] != "root" :
            session.sendline("useradd --create-home %s" % curr_user[0])
            pl_useradd = session.compile_pattern_list([pexpect.TIMEOUT, pexpect.EOF, CMD_PROMPT])
            time.sleep(1)
            prompt = session.expect_list(pl_useradd)
            if prompt == 0: # Timeout
                display_error_pexpect("TIMEOUT", "Could not add user: %s" % curr_user[0], session.before, session.after, 1)
            if prompt == 1: # EOF
                display_error_pexpect("EOF", "Could not add user: %s" % curr_user[0], session.before, session.after, 1)
            if prompt == 2: # Command Prompt - Success!
                session.sendline("echo $?")
                time.sleep(1)
                pl_cmdtest = session.compile_pattern_list([pexpect.TIMEOUT, pexpect.EOF, CMD_PROMPT_ERRORTEST])
                prompt = session.expect_list(pl_cmdtest)
                if prompt == 0: # Timeout
                    display_error_pexpect("TIMEOUT", "Success Test on adding user: %s" % (curr_user[0]),
                                          session.before, session.after, 1)
                elif prompt == 1: # EOF
                    display_error_pexpect("EOF", "Success Test on adding user: %s" % (curr_user[0]),
                                          session.before, session.after, 1)
                elif prompt == 2:
                    test = session.after.splitlines()
                    if int(test[0]) == 0:
                        print "        Added user: %s" % curr_user[0]
                        session.sendline("passwd %s" % curr_user[0])

                    else:
                        print "    Adding user %s failed." % curr_user[0]
                        print "    Bailing out."
                        sys.exit(1)
        else :
            session.sendline("passwd")
        pl_passwd = session.compile_pattern_list([pexpect.TIMEOUT, pexpect.EOF, PROMPT_PASSWORD, CMD_PROMPT])
        while True :
            time.sleep(1)
            prompt = session.expect_list(pl_passwd)
            if prompt == 0: # Timeout
                display_error_pexpect("TIMEOUT", "Could not set password for user: %s" % curr_user[0], 
                                      session.before, session.after, 1)
            if prompt == 1: # EOF
                display_error_pexpect("EOF", "Could not set password for user: %s" % curr_user[0], 
                                      session.before, session.after, 1)
            if prompt == 2:
                session.sendline("%s" % curr_user[1])
            if prompt == 3: # Command Prompt - Success!
                session.sendline("echo $?")
                time.sleep(1)
                pl_cmdtest = session.compile_pattern_list([pexpect.TIMEOUT, pexpect.EOF, CMD_PROMPT_ERRORTEST])
                prompt = session.expect_list(pl_cmdtest)
                if prompt == 0: # Timeout
                    display_error_pexpect("TIMEOUT", "Success Test on setting password for user: %s" % (curr_user[0]),
                                          session.before, session.after, 1)
                elif prompt == 1: # EOF
                    display_error_pexpect("EOF", "Success Test on setting password for user: %s" % (curr_user[0]),
                                          session.before, session.after, 1)
                elif prompt == 2: # Success - password set for user.
                    test = session.after.splitlines()
                    if int(test[0]) == 0:
                        print "        Set password for user: %s" % curr_user[0]
                        # Tighten directory permissions
                        if curr_user[0] != "root" :
                            chroot_cmd(session, "chmod 700 %s"% ("/home/" + curr_user[0]), 300)
                        else:
                            chroot_cmd(session, "chmod 700 /root", 300)
                        break
                    else:
                        print "    Setting password for %s failed." % curr_user[0]
                        print "    Bailing out."
                        sys.exit(1)
    session.logfile = logout

    chroot_cmd(session, "DEBIAN_FRONTEND=readline apt-get install console-setup", 300)
    chroot_cmd(session, "DEBIAN_FRONTEND=readline apt-get install xfonts-base xserver-xorg xterm synaptic gdm", 300)
    chroot_cmd(session, "DEBIAN_FRONTEND=readline apt-get install lxde", 300)
    chroot_cmd(session, "DEBIAN_FRONTEND=readline apt-get clean", 300)

    print "    Base system installation complete.  Any errors after this stage may be"
    print "        easiest to fix by booting into the new system on %s." % options.device

    # cleanup starts here.
    chroot_cmd(session, "apt-get clean", 300)

    # remove subdirectories from those dirs to be mounted tmpfs.
    chroot_cmd(session, "rm -rf /var/log/*", 300)
    chroot_cmd(session, "rm -rf /var/cache/apt/archives/*", 300)
    
    # start cleanup by umounting everything we can.
    chroot_cmd(session, "umount -a", 120, 1)
    chroot_cmd(session, "umount /proc", 120, 1)

    # Exit Chroot Jail
    session.close()
    print "    Close Chroot session."

    print "\nInstallation Complete.\n"

    cleanup_logs = raw_input("Should I remove build log files? (Y/n)  (keeping them can expose root and user passwords)")
    if cleanup_logs != "n" and cleanup_logs != "N" :
        test_cleanup_logs = commands.getstatusoutput("rm -f %s" % (options.builddir + '/install-chroot.log'))
        if test_cleanup_logs[0] != 0 :
            print "Error: Failed to remove %s." % options.builddir + '/install-chroot.log'
            print "Error: %s" % test_cleanup_log[1]
            print "Bailing out."
            sys.exit(1)
        test_cleanup_logs = commands.getstatusoutput("rm -f %s" % (options.builddir + '/install-debootstrap.log'))
        if test_cleanup_logs[0] != 0 :
            print "Error: Failed to remove %s." % options.builddir + '/install-debootstrap.log'
            print "Error: %s" % test_cleanup_log[1]
            print "Bailing out."
            sys.exit(1)

    atexit.register(cleanup_base)
    sys.exit(0)
# Call to main with args.
if __name__ == "__main__" :
    main(sys.argv[0:])

#                                                                                                                                }}}
# ----------------------------------------------------------------------------------------------------------------------------------
# Notes:                                                                                                                         {{{
#
# 1. Developed and tested with Python version 2.4 on Debian Sid.
#                                                                                                                                }}}
# ----------------------------------------------------------------------------------------------------------------------------------
# vim:tw=132:sw=4:ts=4:et

