#
# "SystemImager" 
#
#  Copyright (C) 1999-2004 Brian Elliott Finley
#
#  $Id: functions,v 1.63 2004/09/12 04:25:29 brianfinley Exp $
#
#  Others who have contributed to this code:
#   Charles C. Bennett, Jr. <ccb@acm.org>
#   Sean Dague <japh@us.ibm.com>
#   Dann Frazier <dannf@dannf.org>
#   Curtis Zinzilieta <czinzilieta@valinux.com>
#
#   2004.03.24  Brian Elliott Finley
#   - get entire scripts directory at once, whether using rsync or multicast
#   - redirect output from kills in shellout() to /dev/null
#   - create choose_autoinstall_script() function to simplify script selection
#   - create run_post_install_scripts() function
#   2004.03.24  Brian Elliott Finley
#   - start spinner with 1 second delay, so we don't get a / at the start of
#     command being run.
#   2004.04.14  Brian Elliott Finley
#   - add pre-install script support at Rick Bradshaw's request
#   2004.05.12  Brian Elliott Finley
#   - added OVERRIDES to post-install script support
#   - changed up the way pre and post-install scripts are found
#   2004.05.26  Brian Elliott Finley
#   - look for "<number>all.<description>" pre and post-install scripts
#   - source file at end of pre-install scripts (/tmp/pre-install_variables.txt)
#   - make seperate function for variablizing kernel append parameters
#   2004.06.03  Brian Elliott Finley
#   - fix regex used by get_base_hostname()
#   2004.06.04  Brian Elliott Finley
#   - add support for SCRIPTNAME as a pre-boot variable
#   - improve some of the "these settings override those settings logic"
#   2004.06.05  Brian Elliott Finley
#   - add double quotes around non-null tests in pre and post-install script functions
#   2004.07.26  Brian Elliott Finley
#   - check kernel version against intrd version
#   2004.09.10  Brian Elliott Finley
#   - remove get_ssh_tarball() -- now handled as part of get_boel_binaries_tarball()
#


################################################################################
#
#   Variables
#
PATH=/sbin:/bin:/usr/bin:/usr/sbin:/tmp
SCRIPTS=scripts
SCRIPTS_DIR=/scripts
FLAMETHROWER_DIRECTORY_DIR=/var/lib/systemimager/flamethrower
BOEL_BINARIES_DIR=/tmp/boel_binaries
VERSION="SYSTEMIMAGER_VERSION_STRING"
FLAVOR="SYSTEMIMAGER_FLAVOR_STRING"
#
################################################################################


################################################################################
#
#   Subroutines
#
################################################################################
#
#  check_version
#
# Usage: check_version
check_version() {
    echo
    echo check_version
    INITRD_VERSION=$VERSION
    KERNEL_VERSION=`uname -r | sed -e s/.*boel_v//`
    if [ "$INITRD_VERSION" != "$KERNEL_VERSION" ] ; then
        echo "FATAL: Kernel version ($KERNEL_VERSION) doesn't match initrd version ($INITRD_VERSION)!"
        shellout
    fi
}
#
################################################################################
#
#  get_arch
#
# Usage: get_arch; echo $ARCH
get_arch() {
    echo
    echo get_arch
    ARCH=`uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/`
}
#
################################################################################
#
#  adjust_arch
#
#  based on info in /proc adjust the ARCH variable.  This needs to run
#  after proc is mounted.
#
adjust_arch() {
    echo
    echo adjust_arch
    if [ "ppc64" = "$ARCH" ] ; then
        # This takes a little bit of futzing with due to all the PPC platforms that exist.
        if [ -d /proc/iSeries ] ; then
            ARCH=ppc64-iSeries
            echo "Detected ppc64 is really an iSeries partition..."
        fi
    fi
}
#
################################################################################
#
#  write_variables
#
# Usage: write_variables
write_variables() {

    echo
    echo write_variables

    # pass all variables set here on to the hostname.sh script
    rm -f /tmp/variables.txt
  
    echo "HOSTNAME=$HOSTNAME"                   >> /tmp/variables.txt || shellout
    echo "DOMAINNAME=$DOMAINNAME"               >> /tmp/variables.txt
  
    echo "DEVICE=$DEVICE"                       >> /tmp/variables.txt
    echo "IPADDR=$IPADDR"                       >> /tmp/variables.txt
    echo "NETMASK=$NETMASK"                     >> /tmp/variables.txt
    echo "NETWORK=$NETWORK"                     >> /tmp/variables.txt
    echo "BROADCAST=$BROADCAST"                 >> /tmp/variables.txt
  
    echo "GATEWAY=$GATEWAY"                     >> /tmp/variables.txt
    echo "GATEWAYDEV=$GATEWAYDEV"               >> /tmp/variables.txt
  
    echo "IMAGESERVER=$IMAGESERVER"             >> /tmp/variables.txt
    echo "IMAGENAME=$IMAGENAME"                 >> /tmp/variables.txt
  
    echo "LOG_SERVER=$LOG_SERVER"               >> /tmp/variables.txt
    echo "LOG_SERVER_PORT=$LOG_SERVER_PORT" 	>> /tmp/variables.txt

    echo "TMPFS_STAGING=$TMPFS_STAGING"         >> /tmp/variables.txt
 
    echo "SSH_USER=$SSH_USER"	            	>> /tmp/variables.txt
    echo "SSH_DOWNLOAD_URL=$SSH_DOWNLOAD_URL"	>> /tmp/variables.txt

    echo "FLAMETHROWER_DIRECTORY_PORTBASE=$FLAMETHROWER_DIRECTORY_PORTBASE"	>> /tmp/variables.txt
}
#
################################################################################
#
#   Description:
#   Dann's spinner
#
#   Usage: start_spinner
#   Also see: stop_spinner
#
start_spinner() {
    sleep 1
    { while :; do
            echo -ne "\b/";  sleep 1;
            echo -ne "\b-";  sleep 1;
            echo -ne "\b\\"; sleep 1;
            echo -ne "\b|";  sleep 1;
      done
    }&
    SPINNER_PID=$!
}
#
################################################################################
#
#   Usage: stop_spinner
#   Also see: start_spinner
#
stop_spinner() {
    echo
    echo "stop_spinner"
    kill -9 $SPINNER_PID
}
#
################################################################################
#
#   Description:
#   watches the tmpfs filesystem (/) and gives warnings and/or does a shellout
#
#   Usage: tmpfs_watcher
#
tmpfs_watcher() {

    echo
    echo tmpfs_watcher

    # Note: Transfer to staging area can fail if tmpfs runs out of inodes.
    { 
        while :; do
            DF=`df -k / | grep -v 'Filesystem' | sed -e 's/  */ /g' -e 's/.*[0-9] //' -e 's/%.*//'`
            [ $DF -ge 95 ] && echo "WARNING: Your tmpfs filesystem is ${DF}% full!"
            [ $DF -ge 99 ] && echo "         Search the FAQ for tmpfs to learn about sizing options."
            [ $DF -ge 99 ] && shellout
            sleep 1
        done
        unset DF
    }&
    TMPFS_WATCHER_PID=$!
}
#
################################################################################
#
#   Description:
#   Exit with the message stored in /etc/issue.
#
#   Usage: $COMMAND || shellout
#
shellout() {
    COUNT="$RETRY"
    echo "Killing off running processes."
    kill -9 $TMPFS_WATCHER_PID  >/dev/null 2>/dev/null
    killall -9 udp-receiver rsync  >/dev/null 2>/dev/null
    write_variables
    cat /etc/issue
    exec sh
}
#
################################################################################
#
#   Description:  
#   Count the specified number, printing each number, and exit only the count
#   loop when <ctrl>+<c> is hit (SIGINT, or Signal 2).  Thanks to 
#   CCB <ccb@acm.org> for this chunk of code.  -BEF-
#
#   Usage: 
#   count_loop 35
#   count_loop $ETHER_SLEEP
#
count_loop() {

  COUNT=$1

  trap 'echo ; echo "Skipping ETHER_SLEEP -> Caught <ctrl>+<c>" ; I=$COUNT' INT

  I=0
  while [ $I -lt $COUNT ]; do
    I=$(( $I + 1 ))
    echo -n "$I "
    sleep 1
  done

  trap INT

  echo
}
#
################################################################################
#
# Usage: get_scripts_directory
#
get_scripts_directory() {
    echo
    echo get_scripts_directory

    if [ ! -z $FLAMETHROWER_DIRECTORY_PORTBASE ]; then
        #
        # We're using Multicast, so we should already have a directory 
        # full of scripts.  Break out here, so that we don't try to pull
        # the scripts dir again (that would be redundant).
        #
        MODULE_NAME="autoinstall_scripts"
        DIR="${SCRIPTS_DIR}"
        RETRY=7
        flamethrower_client

    else
        mkdir -p ${SCRIPTS_DIR}
        CMD="rsync -a ${IMAGESERVER}::${SCRIPTS}/ ${SCRIPTS_DIR}/"
        echo "$CMD"
        $CMD
    fi
}
#
################################################################################
#
# Usage: get_flamethrower_directory
#
get_flamethrower_directory() {
    echo
    echo Using multicast...
    echo get_flamethrower_directory

    MODULE_NAME=flamethrower_directory
    DIR=${FLAMETHROWER_DIRECTORY_DIR}
    RETRY=7
    PORTBASE=9000
    flamethrower_client
}
#
################################################################################
#
# Usage: 
#
#   MODULE_NAME=my_module   # Required
#   DIR=/my/destination/dir # Required
#   [RETRY=7]               # Optional
#   [PORTBASE=9000]         # Required if a sourceable file called $MODULE_NAME
#                           #   doesn't exist
#
#   flamethrower_client
#
flamethrower_client() {

    echo
    echo "flamethrower_client(${MODULE_NAME})"
    echo ---------------------------------------------------------------------

    # validate
    if [ -z $PORTBASE ]; then
        . ${FLAMETHROWER_DIRECTORY_DIR}/${MODULE_NAME}
        if [ $? != 0 ]; then
            echo You must either set PORTBASE, or have a sourceable file called
            echo ${FLAMETHROWER_DIRECTORY_DIR}/MODULE_NAME
            shellout
        fi
    fi
    if [ -z $DIR ]; then
        echo Must set DIR
    else
        mkdir -p $DIR
    fi
    

    # build command
    UDP_RECEIVER_OPTIONS="--interface ${DEVICE} --portbase $PORTBASE --nokbd"
    if [ ! -z $TTL ]; then
        UDP_RECEIVER_OPTIONS="$UDP_RECEIVER_OPTIONS --ttl $TTL"
    fi
    if [ "$NOSYNC" = "on" ]; then
        UDP_RECEIVER_OPTIONS="$UDP_RECEIVER_OPTIONS --nosync"
    fi
    if [ "$ASYNC" = "on" ]; then
        UDP_RECEIVER_OPTIONS="$UDP_RECEIVER_OPTIONS --async"
    fi
    if [ ! -z $MCAST_ALL_ADDR ]; then
        UDP_RECEIVER_OPTIONS="$UDP_RECEIVER_OPTIONS --mcast-all-addr $MCAST_ALL_ADDR"
    fi

    # Which tar opts should we use?  If our tar has --overwrite capability, use it.
    #   Summary: busybox tar doesn't (boel_binaries and prior).
    #            Debian patched gnu tar does (image and on).
    #            We want this option enabled for the image to ensure proper directory
    #            permissions. -BEF-
    tar --help 2>&1 | grep -q overwrite && TAR_OPTS='--overwrite -xp' || TAR_OPTS='-x'

    # set vars
    [ -z $RETRY ] && RETRY=0
    COUNT=0
    FLAMETHROWER_CLIENT_SLEEP=3

    # it's the new new style (loop)
    SUCCESS="Not Yet"
    until [ "$SUCCESS" = "yes" ]
    do
        # receive cast
        #   example udp-receiver command:
        #   udp-receiver --interface lo --portbase 9002 --nokbd --nosync --file /tmp/multicast.tar
        echo udp-receiver $UDP_RECEIVER_OPTIONS --file /tmp/multicast.tar
        udp-receiver $UDP_RECEIVER_OPTIONS --file /tmp/multicast.tar
        UDP_RECEIVER_EXIT_STATUS=$?

        # untar it
        if [ "$UDP_RECEIVER_EXIT_STATUS" = "0" ]; then
            echo tar ${TAR_OPTS} -f /tmp/multicast.tar -C ${DIR}
            tar ${TAR_OPTS} -f /tmp/multicast.tar -C ${DIR}
            TAR_EXIT_STATUS=$?
        fi

        # discard used tarball like an old sock (recommended by: Ramon Bastiaans <bastiaans@sara.nl>)
        rm -f /tmp/multicast.tar

        # did everything work properly
        if [ $UDP_RECEIVER_EXIT_STATUS -eq 0 ] && [ $TAR_EXIT_STATUS -eq 0 ]; then
            SUCCESS=yes
        else
            if [ $COUNT -lt $RETRY ]; then
                COUNT=$(( $COUNT + 1 ))
                echo "flamethrower_client: Proceeding with retry $COUNT of $RETRY"
            else
                echo
                echo "flamethrower_client: FATAL: Initial attempt and $RETRY retries failed!"
                shellout
            fi
        fi

        # sleep apnea
        sleep_loop $FLAMETHROWER_CLIENT_SLEEP
    done

    # done
    echo 'finished!'
    echo 


    # Unset vars, so next module (which may not have them set) won't use then unintentially
    unset TTL
    unset NOSYNC
    unset ASYNC
    unset MCAST_ALL_ADDR
    unset RETRY
    unset COUNT
    unset DIR
    unset PORTBASE
    unset UDP_RECEIVER_EXIT_STATUS
    unset UDP_RECEIVER_OPTIONS
    unset TAR_EXIT_STATUS
    unset TAR_OPTS
    unset SUCCESS
}

#
################################################################################
#
#   Get other binaries, kernel module tree, and miscellaneous other stuff that
#   got put in the binaries tarball. -BEF-
#
get_boel_binaries_tarball() {

    echo
    echo get_boel_binaries_tarball
    mkdir -p ${BOEL_BINARIES_DIR}

    if [ ! -z $SSH_DOWNLOAD_URL ]; then
        # If we're using SSH, get the boel_binaries from a web server.
        echo "SSH_DOWNLOAD_URL variable is set, so we will install over SSH!"

        if [ ! -z $FLAMETHROWER_DIRECTORY_PORTBASE ]; then
            echo "FLAMETHROWER_DIRECTORY_PORTBASE is also set, but I will be conservative and proceed with SSH."
        fi

        # Remove possible trailing / from URL
        SSH_DOWNLOAD_URL=`echo $SSH_DOWNLOAD_URL | sed 's/\/$//'`

        cd ${BOEL_BINARIES_DIR}
        CMD="wget ${SSH_DOWNLOAD_URL}/${ARCH}/${FLAVOR}/boel_binaries.tar.gz"
        echo "$CMD"
        $CMD || shellout

    elif [ ! -z $FLAMETHROWER_DIRECTORY_PORTBASE ]; then

        MODULE_NAME="boot-${ARCH}-${FLAVOR}"
        DIR="${BOEL_BINARIES_DIR}"
        RETRY=7
        flamethrower_client

    else
        # Use rsync
        CMD="rsync -av ${IMAGESERVER}::boot/${ARCH}/${FLAVOR}/boel_binaries.tar.gz ${BOEL_BINARIES_DIR}"
        echo "$CMD"
        $CMD || shellout
    fi

    # Untar the tarball
    tar -C / -xzf ${BOEL_BINARIES_DIR}/boel_binaries.tar.gz || shellout
    chown -R 0.0 /lib/modules || shellout
}
#
################################################################################
#
#   Switch root to tmpfs
#
switch_root_to_tmpfs() {
    mount_proc
    tmpfs_size=$(tr ' ' '\n' < /proc/cmdline | grep tmpfs_size\= | sed 's/.*=//')
    tmpfs_nr_blocks=$(tr ' ' '\n' < /proc/cmdline | grep tmpfs_nr_blocks\= | sed 's/.*=//')
    tmpfs_nr_inodes=$(tr ' ' '\n' < /proc/cmdline | grep tmpfs_nr_inodes\= | sed 's/.*=//')
    tmpfs_mode=$(tr ' ' '\n' < /proc/cmdline | grep tmpfs_mode\= | sed 's/.*=//')
    umount /proc

    if [ "$tmpfs_size" != "" ]; then
        tmpfs_opts="size=$tmpfs_size"
    fi

    if [ "$tmpfs_nr_blocks" != "" ]; then
        if [ "$tmpfs_opts" != "" ]; then
	    tmpfs_opts="${tmpfs_opts},nr_blocks=$tmpfs_nr_blocks"
        else
            tmpfs_opts="nr_blocks=$tmpfs_nr_blocks"
	fi
    fi

    if [ "$tmpfs_nr_inodes" != "" ]; then
        if [ "$tmpfs_opts" != "" ]; then
	    tmpfs_opts="${tmpfs_opts},nr_inodes=$tmpfs_nr_inodes"
        else
            tmpfs_opts="nr_inodes=$tmpfs_nr_inodes"
	fi
    fi

    if [ "$tmpfs_mode" != "" ]; then
        if [ "$tmpfs_opts" != "" ]; then
	    tmpfs_opts="${tmpfs_opts},mode=$tmpfs_mode"
        else
            tmpfs_opts="mode=$tmpfs_mode"
	fi
    fi

    if [ "$tmpfs_opts" != "" ]; then
        tmpfs_opts="-o $tmpfs_opts"
    fi

    echo
    echo "switch_root_to_tmpfs"
    # Switch root over to tmpfs so we don't have to worry about the size of
    # the tarball and binaries that users may decide to copy over. -BEF-
    mkdir -p /new_root || shellout
    mount tmpfs /new_root -t tmpfs $tmpfs_opts || shellout
    cd / || shellout
    rsync -a --exclude='/proc' --exclude='/dev' * /new_root/ || shellout
    cd /new_root || shellout
    mkdir -p old_root || shellout
    pivot_root . old_root || shellout

    unset tmpfs_size
    unset tmpfs_nr_blocks
    unset tmpfs_nr_inodes
    unset tmpfs_mode
    unset tmpfs_opts
}
#
################################################################################
#
mount_dev_on_devfs() {
    echo
    echo mount_dev_on_devfs
    # Re-mount /dev on devfs. -BEF-
    mkdir -p /dev || shellout
    mount devfs /dev -t devfs || shellout

    # Crank up devfsd
    echo -n '>>> '
    /sbin/devfsd /dev || shellout

}
#
################################################################################
#
# Now mount proc. -BEF-
mount_proc() {
    echo
    echo mount_proc
    mkdir -p proc || shellout
    mount proc /proc -t proc || shellout
}
#
################################################################################
#
# Configure loopback interface (may as well)
ifconfig_loopback() {
    echo
    echo ifconfig_loopback
    ifconfig lo 127.0.0.1
}
#
################################################################################
#
# Load any modules that were placed in the my_modules directory prior to
# running "make initrd.gz".  -BEF-
load_my_modules() {
    echo
    echo load_my_modules
    cd /my_modules || shellout
    sh ./INSMOD_COMMANDS
}
#
################################################################################
#
# read in varibles obtained from kernel appends
#
read_kernel_append_parameters() {
    echo
    echo read_kernel_append_parameters

    . /tmp/kernel_append_parameter_variables.txt
}
#
################################################################################
#
# Variable-ize /proc/cmdline arguments
#
variableize_kernel_append_parameters() {
    echo
    echo variableize_kernel_append_parameters
    cat /proc/cmdline | tr ' ' '\n' | grep '=' > /tmp/kernel_append_parameter_variables.txt
}
#
################################################################################
#
# Look for local.cfg file
#   This code inspired by Ian McLeod <ian@valinux.com>
#
read_local_cfg() {
    echo
    echo read_local_cfg

    #
    # BEGIN try hard drive
    #
    if [ ! -z "$LAST_ROOT" ]; then

        echo
        echo "Checking for /local.cfg file on hard drive..."
        mkdir /last_root
        echo "Mounting hard drive..."
        mount $LAST_ROOT /last_root -o ro > /dev/null 2>&1
        if [ $? != 0 ]; then
            echo "FATAL: Couldn't mount hard drive!"
            echo "Your kernel must have all necessary block and filesystem drivers compiled in"
            echo "statically (not modules) in order to use a local.cfg on your hard drive.  The"
            echo "standard SystemImager kernel is modular, so you will need to compile your own"
            echo "kernel.  See the SystemImager documentation for details.  To proceed at this"
            echo "point, you will need to unset the LAST_ROOT append parameter by typing"
            echo ""systemimager LAST_ROOT=", or similar, at your boot prompt.  This will not use"
            echo "the local.cfg file on your hard drive, but will still use one on a floppy."
            shellout
        fi

        if [ -f /last_root/local.cfg ]; then
            echo "Found /local.cfg on hard drive."
            echo "Copying /local.cfg settings to /tmp/local.cfg."
            cat /last_root/local.cfg >> /tmp/local.cfg || shellout
        else
            echo "No /local.cfg on hard drive."
        fi
        echo "Unmounting hard drive..."
        umount /last_root || shellout
        echo
    fi
    # END try hard drive

    ### BEGIN try floppy ###
    echo "Checking for floppy diskette."
    echo 'YOU MAY SEE SOME "wrong magic" ERRORS HERE, AND THAT IS NORMAL.'
    mkdir -p /floppy
    mount /dev/fd0 /floppy -o ro > /dev/null 2>&1
    if [ $? = 0 ]; then
        echo "Found floppy diskette."
        if [ -f /floppy/local.cfg ]; then
            echo "Found /local.cfg on floppy."
            echo "Copying /local.cfg settings to /tmp/local.cfg."
            echo "NOTE: local.cfg settings from a floppy will override settings from"
            echo "      a local.cfg file on your hard drive and DHCP."
            # We use cat instead of copy, so that floppy settings can
            # override hard disk settings. -BEF-
            cat /floppy/local.cfg >> /tmp/local.cfg || shellout
        else
            echo "No /local.cfg on floppy diskette."
        fi
    else
        echo "No floppy diskette in drive."
    fi
    ### END try floppy ###

    # /tmp/local.cfg may be created from a local.cfg file on the hard drive, or a
    # floppy.  If both are used, settings on the floppy take precedence. -BEF-
    if [ -f /tmp/local.cfg ]; then
        echo "Reading configuration from /tmp/local.cfg"
        . /tmp/local.cfg || shellout
    fi
}
#
################################################################################
#
#   Configure network interface using local.cfg settings if possible, else
#   use DHCP. -BEF-
#
start_network() {
    echo
    echo start_network
    if [ ! -z $IPADDR ]; then

        # configure interface and add default gateway
        ifconfig $DEVICE $IPADDR  netmask $NETMASK  broadcast $BROADCAST
        if [ $? != 0 ]; then
            echo
            echo "I couldn't configure the network interface using your pre-boot settings:"
            echo "  DEVICE:     $DEVICE"
            echo "  IPADDR:     $IPADDR"
            echo "  NETMASK:    $NETMASK"
            echo "  BROADCAST:  $BROADCAST"
            echo
            shellout
        fi

        if [ ! -z $GATEWAY ]; then
            route add default gw $GATEWAY
            if [ $? != 0 ]; then
                echo
                echo "The command \"route add default gw $GATEWAY\" failed."
                echo "Check your pre-boot network settings."
                echo
                shellout
            fi
        fi

    else

        ### try dhcp ###
        echo "IP Address not set with pre-boot settings."
        
        ### BEGIN ether sleep ###
        # Give the switch time to start passing packets.  Some switches won't
        # forward packets until 30 seconds or so after an interface comes up.
        # This means the dhcp server won't even get the request for 30 seconds.
        # Many ethernet cards aren't considered "up" by the switch until the
        # driver is loaded.  Because the driver is compiled directly into the
        # kernel here, the driver is definitely loaded at this point. 
        # 
        # Default is 0.  The recommended setting of ETHER_SLEEP=35 can be set 
        # with a local.cfg file. -BEF-
        #
        [ -z $ETHER_SLEEP ] && ETHER_SLEEP=0
        echo
        echo "sleep $ETHER_SLEEP:  This is to give your switch (if you're using one) time to"
        echo "           recognize your ethernet card before we try the network."
        echo "           Tip: You can use <ctrl>+<c> to pass the time (pun intended)."
        echo
        count_loop $ETHER_SLEEP
        echo
        ### END ether sleep ###
        
        # create directory to catch dhcp information
        DHCLIENT_DIR="/var/state/dhcp"
        mkdir -p $DHCLIENT_DIR
        
        # combine systemimager code to the stock debian dhclient-script
        # and make executable
        cat /etc/dhclient-script.systemimager-prefix \
            /etc/dhclient-script.debian-dist \
            > /etc/dhclient-script
        chmod +x /etc/dhclient-script
        
        # get info via dhcp
        echo
        echo "dhclient"
        dhclient
        if [ ! -s ${DHCLIENT_DIR}/dhclient.leases ]; then
            echo
            echo "I couldn't configure the network interface using DHCP."
            echo
            shellout
        fi
        
        if [ -z ${DEVICE} ]; then
            # Figure out which interface actually got configured.
            # Suggested by James Oakley.
            #
            DEVICE=`grep interface ${DHCLIENT_DIR}/dhclient.leases | \
                sed -e 's/^.*interface "//' -e 's/";//'`
        fi
        
        # read dhcp info in as variables -- this file will be created by 
        # the /etc/dhclient-start script that is run automatically by
        # dhclient.
        . /tmp/dhcp_info.${DEVICE} || shellout
        ### END dhcp ###
        
        # Re-read configuration information from local.cfg to over-ride
        # DHCP settings, if necessary. -BEF-
        if [ -f /tmp/local.cfg ]; then
            echo
            echo "Overriding any DHCP settings with pre-boot local.cfg settings."
            . /tmp/local.cfg || shellout
        fi

        echo
        echo "Overriding any DHCP settings with pre-boot settings from kernel append"
        echo "parameters."
        read_kernel_append_parameters
    fi
}
#
################################################################################
#
#   Ping test
ping_test() {
    echo
    echo ping_test

    # The reason we don't ping the IMAGESERVER if FLAMETHROWER_DIRECTORY_PORTBASE
    # is set, is that the client may never be given, know, or need to know, the 
    # IP address of the imageserver because the client is receiving _all_ of it's
    # data via multicast, which is more like listening to a channel, as compared 
    # with connecting directly to a server.  -BEF-
    #
    if [ ! -z "$FLAMETHROWER_DIRECTORY_PORTBASE" ]; then
        PING_DESTINATION=$GATEWAY
        HOST_TYPE="default gateway"
    else
        PING_DESTINATION=$IMAGESERVER
    fi 
    echo
    echo "Pinging your $HOST_TYPE to ensure we have network connectivity."
    echo


    # Ping test code submitted by Grant Noruschat <grant@eigen.ee.ualberta.ca>
    # modified slightly by Brian Finley.
    PING_COUNT=1
    PING_EXIT_STATUS=1
    while [ "$PING_EXIT_STATUS" != "0" ]
    do
        echo "PING ATTEMPT $PING_COUNT: "
        ping -c 1 $PING_DESTINATION
        PING_EXIT_STATUS=$?

        if [ "$PING_EXIT_STATUS" = "0" ]; then
            echo
            echo "  We have connectivity to your $HOST_TYPE!"
        fi

        PING_COUNT=$(( $PING_COUNT + 1 ))
        if [ "$PING_COUNT" = "4" ]; then
            echo
            echo "  WARNING:  Failed ping test."
            echo "            Despite this seemingly depressing result, I will attempt"
            echo "            to proceed with the install.  Your $HOST_TYPE may be"
            echo "            configured to not respond to pings, but it wouldn't hurt"
            echo "            to double check that your networking equipment is"
            echo "            working properly!"
            echo
            sleep 5
            PING_EXIT_STATUS=0
        fi
    done

    unset PING_DESTINATION
    unset HOST_TYPE

}
#
################################################################################
#
start_syslogd() {
    echo
    echo start_syslogd
    if [ ! -z $LOG_SERVER ]; then
        echo -n "Starting syslogd..."
        [ -z $LOG_SERVER_PORT ] && LOG_SERVER_PORT="514"
        syslogd -R ${LOG_SERVER}:${LOG_SERVER_PORT}
        echo "done!"
    fi
}
#
################################################################################
#
# Detect what we can.
# Note: Modules that are needed during the initial boot stages of BOEL should
#       be copied to the "skel/my_modules" directory prior to creating your
#       custom initrd.gz. -BEF-
#
autodetect_hardware_and_load_modules() {
    echo
    echo autodetect_hardware_and_load_modules
    echo -n "Detecting hardware: "
    MODULES=`discover --module bridge ethernet ide scsi usb`
    echo $MODULES

    # Prepend with other modules that we will probably need.
    MODULES="sd_mod ide-disk $MODULES"

    for MODULE in $MODULES
    do
        echo
        echo "Loading $MODULE..."
        modprobe $MODULE 2>/dev/null || echo "Assuming $MODULE is compiled into the kernel, not needed, or already loaded."
    done
}
#
################################################################################
#
get_hostname_by_hosts_file() {

    echo
    echo get_hostname_by_hosts_file

    #
    # Look in $FILE for that magic joy.
    #
    FILE=${SCRIPTS_DIR}/hosts
    if [ -e $FILE ]; then

        echo "Hosts file exists..."

        # add escape characters to IPADDR so that it can be used to find HOSTNAME below
        IPADDR_ESCAPED=`echo "$IPADDR" | sed -e 's/\./\\\./g'`
        
        # get HOSTNAME by parsing hosts file
        echo "Searching for this machine's hostname in $FILE by IP: $IPADDR"
        
        # Command summary by line:
        # 1: convert tabs to spaces -- contains a literal tab: <ctrl>+<v> then <tab>
        # 2: remove comments
        # 3: add a space at the beginning of every line
        # 4: get line with IP address (no more no less)
        # 5: strip out ip address
        # 6: strip out space(s) before first hostname on line
        # 7: remove any aliases on line
        # 8: remove domain name, leaving naught but the hostname, naked as the day it were born
        
        HOSTNAME=`
            sed 's/[[:space:]]/ /g' $FILE | \
            grep -v '^ *#' | \
            sed 's/^/ /' | \
            grep " $IPADDR_ESCAPED " | \
            sed 's/ [0-9]*\.[0-9]*\.[0-9]*\.[0-9]*//' | \
            sed 's/ *//' | \
            sed 's/ .*//' | \
            sed 's/\..*$//g'
        `
    else
        echo "No hosts file."
    fi
}
#
################################################################################
#
get_hostname_by_dns() {

    echo
    echo get_hostname_by_dns

    # Get base hostname.  For example, www7.domain.com will become www7. -BEF-
    HOSTNAME=`nslookup $IPADDR | grep "^Name:" | sed -e 's/Name:[[:space:]]*//' -e 's/\..*$//g'`
}
#
################################################################################
#
get_base_hostname() {
    BASE_HOSTNAME=`echo $HOSTNAME | sed "s/[.0-9].*$//"` 
}
#
################################################################################
#
choose_autoinstall_script() {

    echo
    echo choose_autoinstall_script

    #
    # Get the base hostname for the last attempt at choosing an autoinstall
    # script.  For example, if the hostname is compute99, then try to get 
    # compute.master. -BEF-
    #
    get_base_hostname

    # 
    # If SCRIPTNAME is specified as a kernel append, or via local.cfg, then use that script.
    #
    if [ -z $SCRIPTNAME ]; then
        # 
        # If SCRIPTNAME was not specified, choose one, in order of preference.  First hit wins.
        #
        if [ ! -z $IMAGENAME ]; then
            SCRIPTNAMES="${SCRIPTS_DIR}/${IMAGENAME}.master ${SCRIPTS_DIR}/${HOSTNAME}.sh ${SCRIPTS_DIR}/${BASE_HOSTNAME}.master"
        else
            SCRIPTNAMES="${SCRIPTS_DIR}/${HOSTNAME}.sh ${SCRIPTS_DIR}/${BASE_HOSTNAME}.master"
        fi
        
        #
        # Choose a winner!
        #
        for SCRIPTNAME in $SCRIPTNAMES
        do
            [ -e $SCRIPTNAME ] && break
        done
    fi

    # make sure it exists
    if [ ! -e $SCRIPTNAME ]; then
        echo "FATAL: I can't find ${SCRIPTNAME}!"
    fi
    echo "Using autoinstall script: ${SCRIPTNAME}"
}
#
################################################################################
#
run_autoinstall_script() {

    echo
    echo run_autoinstall_script

    # Run the autoinstall script.
    if [ -f $SCRIPTNAME ]; then
        chmod 755 $SCRIPTNAME || shellout
        echo ">>> $SCRIPTNAME"
        $SCRIPTNAME || shellout
    else
        echo
        echo "FATAL: Your autoinstall script doesn't appear to exist: $SCRIPTNAME"
        echo "       Be sure this script exists in the autoinstall scripts directory"
        echo "       on your image server."
        shellout
    fi
}
#
################################################################################
#
run_pre_install_scripts() {

    echo
    echo run_pre_install_scripts

    get_base_hostname

    if [ -e "${SCRIPTS_DIR}/pre-install/" ]; then

        cd ${SCRIPTS_DIR}/pre-install/

        PRE_INSTALL_SCRIPTS="$PRE_INSTALL_SCRIPTS `ls | grep "^[0-9][0-9]${IMAGENAME}\..*"`"
        PRE_INSTALL_SCRIPTS="$PRE_INSTALL_SCRIPTS `ls | grep "^[0-9][0-9]${BASE_HOSTNAME}\..*"`"
        PRE_INSTALL_SCRIPTS="$PRE_INSTALL_SCRIPTS `ls | grep "^[0-9][0-9]${HOSTNAME}\..*"`"
        PRE_INSTALL_SCRIPTS="$PRE_INSTALL_SCRIPTS `ls | grep "^[0-9][0-9]all\..*"`"

        # Now, to get rid of those pesky newlines. -BEF-
        PRE_INSTALL_SCRIPTS=`echo $PRE_INSTALL_SCRIPTS | tr '\n' ' '`
        
        if [ ! -z "`echo ${PRE_INSTALL_SCRIPTS}|sed 's/ //'`" ]; then

            for PRE_INSTALL_SCRIPT in $PRE_INSTALL_SCRIPTS
            do
                echo ">>> $PRE_INSTALL_SCRIPT"
                chmod +x $PRE_INSTALL_SCRIPT || shellout
                ./$PRE_INSTALL_SCRIPT || shellout
            done
        else
            echo "No pre-install scripts found."
        fi

        if [ -e "/tmp/pre-install_variables.txt" ]; then
            . /tmp/pre-install_variables.txt
        fi

    fi
}
#
################################################################################
#
run_post_install_scripts() {

    echo
    echo run_post_install_scripts

    get_base_hostname

    if [ -e "${SCRIPTS_DIR}/post-install/" ]; then

        cd ${SCRIPTS_DIR}/post-install/

        POST_INSTALL_SCRIPTS="$POST_INSTALL_SCRIPTS `ls | grep "^[0-9][0-9]${IMAGENAME}\..*"`"
        POST_INSTALL_SCRIPTS="$POST_INSTALL_SCRIPTS `ls | grep "^[0-9][0-9]${BASE_HOSTNAME}\..*"`"
        POST_INSTALL_SCRIPTS="$POST_INSTALL_SCRIPTS `ls | grep "^[0-9][0-9]${HOSTNAME}\..*"`"
        POST_INSTALL_SCRIPTS="$POST_INSTALL_SCRIPTS `ls | grep "^[0-9][0-9]all\..*"`"

        for OVERRIDE in $OVERRIDES
        do
            POST_INSTALL_SCRIPTS="$POST_INSTALL_SCRIPTS `ls | grep "^[0-9][0-9]${OVERRIDE}\..*"`"
        done

        # Now, to get rid of those pesky newlines. -BEF-
        POST_INSTALL_SCRIPTS=`echo $POST_INSTALL_SCRIPTS | tr '\n' ' '`
        
        if [ ! -z "`echo ${POST_INSTALL_SCRIPTS}|sed 's/ //'`" ]; then

            mkdir -p /a/tmp/post-install/ || shellout

            rsync -a ${SCRIPTS_DIR}/post-install/ /a/tmp/post-install/ || shellout

            for POST_INSTALL_SCRIPT in $POST_INSTALL_SCRIPTS
            do
                if [ -e "$POST_INSTALL_SCRIPT" ]; then
                    echo ">>> $POST_INSTALL_SCRIPT"
                    chmod +x /a/tmp/post-install/$POST_INSTALL_SCRIPT || shellout
                    chroot /a/ /tmp/post-install/$POST_INSTALL_SCRIPT || shellout
                fi
            done
        else
            echo "No post-install scripts found."
        fi
    fi
}
#
################################################################################
#
#   Stuff for SSH installs
#

start_ssh() {

    # create root's ssh dir
    mkdir /root/.ssh

    ############################################################################
    #
    # If a private key exists, put it in the right place so this autoinstall
    # client can use it to authenticate itself to the imageserver.
    #
    # (ssh2 dsa style user private key)
    if [ -e /floppy/id_dsa ]; then
        PRIVATE_KEY=/root/.ssh/id_dsa
        cp /floppy/id_dsa $PRIVATE_KEY || shellout
        chmod 600 $PRIVATE_KEY         || shellout
    fi
    #
    # (ssh2 rsa style user private key)
    if [ -e /floppy/id_rsa ]; then
        PRIVATE_KEY=/root/.ssh/id_rsa
        cp /floppy/id_rsa $PRIVATE_KEY || shellout
        chmod 600 $PRIVATE_KEY         || shellout
    fi
    #
    ############################################################################

    # If we have a private key from the media above, go ahead and open secure tunnel
    # to the imageserver and continue with the autoinstall like normal.
    if [ ! -z $PRIVATE_KEY ]; then

        # With the prep ready, start the ssh tunnel connection.
        # the sleep command executes remotely.  Just need something long here
        # as the connection will be severed when the newly imaged client reboots
        # 14400 = 4 hours...if we're not imaged by then....oh boy!
        #
        # Determine if we should run interactive and set redirection options appropriately.
        # So if the key is blank, go interactive. (Suggested by Don Stocks <don_stocks@leaseloan.com>)
        if [ -s $PRIVATE_KEY ]; then
            # key is *not* blank
            REDIRECTION_OPTIONS="> /dev/null 2>&1"
        else
            # key is blank - go interactive
            REDIRECTION_OPTIONS=""
        fi

        CMD="ssh -l $SSH_USER -n -f -L873:127.0.0.1:873 $IMAGESERVER sleep 14400 $REDIRECTION_OPTIONS"
        echo $CMD
        $CMD || shellout
        
        # Since we're using SSH, change the $IMAGESERVER variable to reflect
        # the forwarded connection.
        IMAGESERVER=127.0.0.1

    else

        ########################################################################
        #
        # Looks like we didn't get a private key from the floppy, so let's just
        # fire up sshd and wait for someone to connect to us to initiate the
        # next step of the autoinstall. -BEF-
        #
        # download authorized_keys
        # (public keys of users allowed to ssh *in* to this machine)
        cd /root/.ssh/ || shellout
        CMD="wget ${SSH_DOWNLOAD_URL}/${ARCH}/ssh/authorized_keys"
        echo
        echo $CMD
        $CMD || shellout
        
        # set permissions to 600 -- otherwise, sshd will refuse to use it
        chmod 600 /root/.ssh/authorized_keys || shellout

        # must be owned by root
        chown -R 0.0 /root/
        
        # Since we're using SSH, change the $IMAGESERVER variable to reflect
        # the forwarded connection.
        IMAGESERVER=127.0.0.1
        
        # save variables for autoinstall script
        write_variables || shellout
        
        # create a private host key for this autoinstall client
        echo
        echo "Using ssh-keygen to create this hosts private key"
        echo
        mkdir -p /var/empty || shellout
        ssh-keygen -t dsa -N "" -f /etc/ssh/ssh_host_dsa_key || shellout
        ssh-keygen -t rsa -N "" -f /etc/ssh/ssh_host_rsa_key || shellout
        
        # if hostname not set, try DNS
        if [ -z $HOSTNAME ]; then
            echo
            echo "Trying to get hostname via DNS..."
            echo
            get_hostname_by_dns
        fi
        
        if [ -z $HOSTNAME ]; then
            HOST_OR_IP=$IPADDR
        else
            HOST_OR_IP=$HOSTNAME
        fi
        
        echo
        echo
        echo "Starting sshd.  You must now go to your imageserver and issue"
        echo "the following command:"
        echo
        echo " \"pushupdate -continue-install -image <IMAGENAME> -client ${HOST_OR_IP}\"."
        echo
        echo
        
        # fire up sshd and wait
        mkdir -p /var/run/sshd || shellout
        chmod 0755 /var/run/sshd || shellout
        sshd || shellout
        
        # Give sshd time to initialize before we yank the parent process
        # rug out from underneath it.
        sleep 15
        
        # remove rug
        exit 1
    fi
}
#
################################################################################
#
#   Beep incessantly
#
beep_incessantly() {
    local SECONDS=1
    local MINUTES
    local MINUTES_X_SIXTY
    { while :;
        do
            echo -n -e "\\a"
            if [ $SECONDS -lt 60 ]; then 
                echo "I have been done for $SECONDS seconds.  Reboot me already!"
            else
                MINUTES=`echo "$SECONDS / 60"|bc`
                MINUTES_X_SIXTY=`echo "$MINUTES * 60"|bc`
                if [ "$MINUTES_X_SIXTY" = "$SECONDS" ]; then 
                    echo "I have been done for $MINUTES minutes now.  Reboot me already!"
                fi  
            fi
            sleep 1
            SECONDS=`echo "$SECONDS + 1"|bc`
        done
    }
}
#
################################################################################
#
#   Beep incessantly
#
# Usage: beep [$COUNT [$INTERVAL]]
# Usage: beep
beep() {
    local COUNT=$1
    local INTERVAL=$2

    [ -z $COUNT ] && COUNT=1
    [ -z $INTERVAL ] && INTERVAL=1

    local COUNTED=0
    until [ "$COUNTED" = "$COUNT" ]
    do
        echo -n -e "\\a"
        sleep $INTERVAL
        COUNTED=$(( $COUNTED + 1 ))
    done
}
#
################################################################################
#
#   Print out dots while sleeping
#
# Usage: sleep_loop [[$COUNT [$INTERVAL]] $CHARACTER]
# Usage: sleep_loop
sleep_loop() {
    local COUNT=$1
    local INTERVAL=$2
    local CHARACTER=$3
    local COUNTED

    [ -z $COUNT ] && COUNT=1
    [ -z $INTERVAL ] && INTERVAL=1
    [ -z $CHARACTER ] && CHARACTER=.

    COUNTED=0
    until [ "$COUNTED" = "$COUNT" ]
    do
        echo -n "$CHARACTER"
        sleep $INTERVAL
        COUNTED=$(( $COUNTED + 1 ))
    done
}
#
################################################################################
