#!/bin/bash

#
#   $Id: si_mkbootserver 4471 2009-08-27 08:21:03Z arighi $
#
#   Written by Dann Frazier <daniel_frazier@hp.com>
#   Copyright (C) 2006 Brian Elliott Finley
#

#########################################################################
# The intent of this script is to automate the configuration of a
# linux PXE server, and is written with SystemImager in mind.
# it uses pxelinux and configures it for a kernel/single ramdisk scenario.
#
# it is written as a set of independent tasks so that it can hopefully
# be expanded for other architectures, and other non-SystemImager purposes
#
#########################################################################

CONFIG_FILE=/etc/systemimager/systemimager.conf
INTERACTIVE="yes"
INTERFACE=""
LOCAL_DHCP=""
TFTPDIR=`sed -ne "s/^[[:space:]]*[^#]*[[:space:]]*TFTP_DIR[[:space:]]*=[[:space:]]*\\([^#]*\\).*$/\1/p" $CONFIG_FILE`
BOOTDIR=`sed -ne "s/^[[:space:]]*[^#]*[[:space:]]*AUTOINSTALL_BOOT_DIR[[:space:]]*=[[:space:]]*\\([^#]*\\).*$/\1/p" $CONFIG_FILE`
PXELINUX=""

program_name=$(basename $0)

usage="\
Usage:  $program_name [-f] [--interface=interface] [--localdhcp=y|n]
	[--pxelinux=path]"
       
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin

if [ -z "$TFTPDIR" ]; then
    echo "ERROR: TFTP_DIR not defined in $CONFIG_FILE"
    exit 1
fi
if [ -z "$BOOTDIR" ]; then
    echo "ERROR: AUTOINSTALL_BOOT_DIR not defined in $CONFIG_FILE"
    exit 1
fi

TFTPD_PATH=""
TFTP_TEST_FILE_TEXT="all your tftp test file are belong to us"
TFTP_DAEMON=""

CONFIG_DIR="/etc/systemimager"

#SYSLINUX_DEFAULT="\
#DEFAULT kernel
#APPEND vga=extended load_ramdisk=1 prompt_ramdisk=0 initrd=initrd.img root=/dev/ram rw
#DISPLAY message.txt
#PROMPT 1
#TIMEOUT 50"

PXE_CONF="\
#  *******  PXE CONFIGURATION FILE *******
# Any Line starting with a '#\" is treated as a comment line
# and ignored. However, '#' must be the first character on
# a line and no spaces before that are allowed.


# The following entry is the name of the interface on which pxe is going
# to operate. We use this interface to get the ip address automatically.

[Network_Interface_Name]
eth0


# The following entry will be queried if PXE fails to get 
# the IP address automatically through 'ifconfig' like code built-in. This
# is only needed if the above interface name is not present in the system


[OurIpAddress]
#192.215.100.202


# This entry shows the base directory of the mtftpd. All file names/paths
# will be relative to this directory. This is the same name that should 
# be used as the start up argument to the mtftpd daemon.

[Mtftpd_Root_Directory]
/var/lib/tftpboot


# Set the following entry to 0 if you have a DHCP server running on this 
# system.

[UseDHCPPort]
1


# Set the entry below to zero if you don't want this system to act 
# as a PXE proxyDHCP

[StartProxy]
1


# Set the entry below to zero if you don't want this system to act as a 
# PXE boot server

[startBootService]
1


[MasterProxy]
1


# 0 - broadcast discovery is enabled; 1 - broadcast discovery is disabled

[Discovery_BCast_Disabled]
0


# 0 - multicast discovery is enabled; 1 - multicast discovery is disabled

[Discovery_MCast_Disabled]
0


# Multicast Discovery address. The boot server would listen on this address 
# for multicast boot server packets.

[Discovery_MCast_Addr]
224.0.1.2


# Prompt to display on the user screen
# format of this entry: timeout,<string>

[Prompt]
10,Press F8 to view menu ...


# This entry is set to 1 if the client should accept only responses
# from servers in this list

[Discovery_Server_List_Only]
0


# the format of the discovery_list entry is as follows
# type of the server, # of entries, Ip address of each entry 
# separated by commas

[Discovery_List]
#3,2,192.215.100.49,192.215.100.99
#65535,1,192.215.100.45


# In some systems, the all '1' broadcast address 255.255.255.255 
# won't work as the system will fail with a network unreachable message.
# In those cases, you can use the 'route add -host 255.255.255.255 eth0' 
# command to add a route. Or else, you can define the host portion of the 
# address alone as '1' in the following entry. This entry is not 
# required if your system is set up to transmit
# broadcast packets on 255.255.255.255

[Broadcast_ip_address]
#192.215.100.255


# Architectures supported
# format : <arch-type>,<arch-string>

[PROC_ARCH]
0,X86PC


# Boot server types supported

[Service_Types]
0,BStrap
13,linux-install
# 14,linux-boot


# Menu string that will be displayed on the client screen
# after F8 is pressed.
[X86PC/UNDI/MENU]
0,Local Boot
13,Remote Install Linux
# 14,Remote Boot Linux


# Image file name for Linux install boot server type
# format : <min layer #> <max layer #> <base file name>
[X86PC/UNDI/linux-install/ImageFile_Name]
0
2
linux


# Image file name for Linux boot server type
# format : <min layer #> <max layer #> <base file name>
# [X86PC/UNDI/linux-boot/ImageFile_Name]
# 0
# 2
# linux


# Image file name for BStrap boot server
# format : <min layer #> <max layer #> <base file name>

[X86PC/UNDI/BStrap/ImageFile_Name]
0
0
bstrap


# List of plug-ins. The PXEClientTester is the plug-in that
# validates received packets for PXE compliance. By default, this
# plug-in is DISBALED. Remove the '#\" in front of it, if you want
# packet validation to happen

[Parsers]
# PXEClientTester
PxeParser


[Parsers/PxeParser/MainFunction]
PxeParser


[Parsers/PxeParser/DllPath]
/usr/lib/libpxeParser.so


[Parsers/PXEClientTester/MainFunction]
PXEClientTester


[Parsers/PXEClientTester/DllPath]
/usr/lib/libpxeTester.so


# Even if the PXEClientTester is loaded, you still need to
# turn this flag to '1' for packet validation.

[TestOn]
1


# Directory where the results of the packet validation by PXEClientTester
# will be written to. Under this directory, there will be 
# sub-directories with the last 8 digits of the MAc address of
# each client.

[TestPath]
/tmp


# Option DLL for BStrap

[X86PC/UNDI/BStrap/Vendor_Dll]
/usr/lib/libBstrapOpts.so


# Fill in a NT server name that will be contacted by the client
# to log back in for APitest

[ServerName]
#vviswana-desk2


# Specify whether the above server is in a domain or a workgroup

[IsDomain]
0


# specify the name of the workgroup or domain of the server

[Domain]
starwars


# All debug related stuff here


# If this entry is set to 1, pxe services will write
# more debug info to the system log using syslog()

[DEBUG/PxeServices_DebugOutToWindow_On]
0


# If this entry is set to 1, pxe parser will write
# more debug info to the system log using syslog()

[DEBUG/PxeParsers_DebugOutToWindow_On]
0

# If this entry is set to 1, pxe services will write
# more debug info to the file mentioned
# under the entry [DEBUG/PxeServices_Debug_FileName]

[DEBUG/PxeServices_DebugOutToFile_On]
0


# If this entry is set to 1, pxe parser will write
# more debug info to the file mentioned
# under the entry [DEBUG/PxeParsers_Debug_FileName]

[DEBUG/PxeParsers_DebugOutToFile_On]
0


[DEBUG/PxeServices_Debug_FileName]
/PxeServiceLog.txt


[DEBUG/PxeParsers_Debug_FileName]
/PxeParserLog.txt


[End]"

#
# link_tftpboot()
#
link_tftpboot() {
    
    if [ "$TFTPDIR" == "" ]; then
        echo -n "Where should tftpd serve files from ([/var/lib/tftpboot]? "
        if [ "$INTERACTIVE" == "no" ]; then
            REPLY=""
            echo "Non-interactive mode, taking the default."
        else
            read REPLY
        fi
        if [ "$REPLY" == "" ]; then
            TFTPDIR="/var/lib/tftpboot"
        else
            TFTPDIR=$REPLY
        fi
    fi

    if [ -L "$TFTPDIR" ]; then
        echo "$TFTPDIR exists and is a symlink to `readlink $TFTPDIR`."
        return 0
    fi

    if [ -e "$TFTPDIR" ]; then
        if [ ! -e "${TFTPDIR}.orig" ]; then
            echo "WARNING: ${TFTPDIR} exists, moving to ${TFTPDIR}.orig"
            mv $TFTPDIR ${TFTPDIR}.orig
            if [ $? -ne 0 ]; then
                echo "ERROR: couldn't move ${TFTPDIR} to ${TFTPDIR}.orig"
                return 1
            fi
        else
            done=0
            for i in $(seq 1 100); do
                if [ ! -e "${TFTPDIR}.orig-${i}" ]; then
                    echo "WARNING: ${TFTPDIR} exists, moving to ${TFTPDIR}.orig-${i}"
                    mv $TFTPDIR ${TFTPDIR}.orig-${i}
                    if [ $? -ne 0 ]; then
                        echo "ERROR: couldn't move ${TFTPDIR} to ${TFTPDIR}.orig-${i}"
                        return 1
                    fi
                    done=1
                    break
                fi
            done
            if [ $done -eq 0 ]; then
                echo "ERROR: please clean some of your ${TFTPDIR}.* directories."
                return 1
            fi
        fi
    fi

    # Create the symlink.
    cd / && ln -s $BOOTDIR $TFTPDIR
    if [ $? -ne 0 ]; then
        echo "ERROR: couldn't create the symlink $TFTPDIR -> $BOOTDIR"
        return 1
    fi

}


### BEGIN BACKUP_FILE()
############################################################
# takes the variable $FILE_TO_BACKUP, and renames the file #
# to <$FILE_TO_BACKUP>.<name of this script>.bak.<N>       #
# where <N> is the next number, starting at 0, that does   #
# not conflict with a file that already exists             #
############################################################
backup_file() {
    if [ "$FILE_TO_BACKUP" == "" ]; then
	echo "BUG:  call to backup_file() with FILE_TO_BACKUP not set"
	exit 1
    fi
    
    if [ ! -e "$FILE_TO_BACKUP" ]; then
	echo "$FILE_TO_BACKUP does not already exist, so not backing it up."
	return 0
    fi
    echo "Backing up $FILE_TO_BACKUP..."
    backup_file_i=0
    while /bin/true; do
	if [ ! -e $FILE_TO_BACKUP.$program_name.bak$backup_file_i ]; then
	    echo -n "Moving $FILE_TO_BACKUP to $FILE_TO_BACKUP.$program_name.bak$backup_file_i..."
	    mv $FILE_TO_BACKUP $FILE_TO_BACKUP.$program_name.bak$backup_file_i
	    if [ $? -ne 0 ]; then
		echo "failed."
		return 1
	    fi
	    break
	fi
	backup_file_i=`expr $backup_file_i + 1`
    done
    echo "done."
    return 0
}
### END BACKUP_FILE()

### BEGIN CHECK_TFTPD
##############################
# checks for any tftp server #
##############################
check_tftpd() {
    # check to see if a tftp server is installed
    echo -n "Checking for a tftp server... "
    TFTP_PATH=`which in.tftpd`
    if [ "$TFTP_PATH" == "" ]; then
	echo "failed."
	echo "****************************************************************"
	echo "A tftp server is required, but I couldn't find one in:"
	echo "$PATH."
	echo "You will need to install one."
	echo ""
	echo "I suggest using a tftp server with H. Peter Anvin's patch, or"
	echo "atftpd.  If you're using RedHat 7.0 or higher, the included"
	echo "tftp-server package will do the trick.  For earlier versions"
	echo "of RedHat, you can try using the tftp-hpa package which is"
	echo "available at http://systemimager.org.  For Debian systems"
	echo "more recent than Potato (2.2) you should just be able to"
	echo "apt-get install either tftpd-hpa or atftpd.  For earlier"
	echo "systems you can add a deb-src entry to point to a"
	echo "Debian/unstable mirror, apt-get source one of these packages,"
	echo "and run dpkg-buildpackage to build debs for your system."
	echo "Once you've installed an appropriate tftp server, you can"
	echo "then re-run this script to complete the configuration."
	echo "****************************************************************"
	return 1
    else
	echo "found."
    fi

    # at this point, there should be a tftp server installed
    TFTPD_PATH=`which in.tftpd`
    if [ "$TFTPD_PATH" == "" ]; then
	echo "Still can't find in.tftpd in your PATH.  Try updating your PATH."
	return 1
    fi
}    
### END CHECK_TFTPD

### BEGIN CHECK_TFTPD_HPA
########################################################
# Check to see if the tftp server is hpa's tftp server #
# by looking for the string "hpa" in the binary        #
########################################################
check_tftpd_hpa() {
    echo -n "Checking if tftp server is H. Peter Anvin's tftp server... "
    TFTPD_PATH=`which in.tftpd`
    if [ "$TFTPD_PATH" == "" ]; then
	echo "Couldn't find in.tftpd in your path."
	return 1
    fi
    
    # suggestions for a better test welcome!
    strings $TFTPD_PATH | grep "hpa" 1> /dev/null 2> /dev/null
    if [ $? -eq 0 ]; then
	echo "yup - right on!"
	return 0
    else
	echo "doesn't look like it."
	return 1
    fi
}
### END CHECK_TFTPD_HPA

### BEGIN CHECK_ATFTPD
######################################################
# check to see if in.tftpd is atftp by examining the #
# output of in.tftpd -V                              #
######################################################
check_atftpd() {
    echo -n "Checking if tftp server is atftpd... "
    TFTPD_PATH=`which in.tftpd`
    if [ "$TFTPD_PATH" == "" ]; then
	echo "Couldn't find in.tftpd in your path."
	return 1
    fi

    $TFTPD_PATH -V | grep "atftp" 1> /dev/null 2> /dev/null
    if [ $? -eq 0 ]; then
	echo "yup - right on!"
	return 0
    else
	echo "doesn't look like it."
	return 1
    fi
}
### END CHECK_ATFTPD

##############################################################################
# configures inetd and xinetd for use with hpa's tftp server
##############################################################################

### BEGIN CONFIGURE INETD FOR TFTPD_HPA
configure_inetd_for_tftpd_hpa() {

    if [ -e /etc/init.d/openbsd-inetd ]; then
        INETD_SERVICE=openbsd-inetd
    elif [ -e /etc/init.d/xinetd ]; then
        INETD_SERVICE=xinetd
    elif [ -e /etc/init.d/inetd ]; then
        INETD_SERVICE=inetd
    else
        echo "No inetd service found. Try to install: inetd, openbsd-inetd, xinted."
        exit 1
    fi

    if [ "$TFTP_DAEMON" == "tftpd-hpa" ]; then
	inetd_entry="tftp\tdgram\tudp\twait.100\troot\t/usr/sbin/tcpd\tin.tftpd -s -r blksize $TFTPDIR"
	tftp_xinetd="$TFTP_HPA_XINETD"
    elif [ "$TFTP_DAEMON" == "atftpd" ]; then
	inetd_entry="tftp\tdgram\tudp\twait\tnobody\t/usr/sbin/tcpd\t/usr/sbin/in.tftpd --tftpd-timeout 300  --retry-timeout 5 --maxthread 100 --verbose=5 --logfile /var/log/atftpd.log $TFTPDIR"
	tftp_xinetd="$ATFTP_XINETD"
    else
	echo "Unrecognized tftp server, cannot create an inetd entry."
    fi

    ### BEGIN LOOK FOR SUPERSERVER
    echo -n "Checking for a running inetd... "
    inetd_pid=`pidof $(type -p inetd)`
    if [ "$inetd_pid" == "" ]; then
        echo "Not found."
    else
        echo "$inetd_pid."
    fi
    echo -n "Checking for a running xinetd... "
    xinetd_pid=`pidof $(type -p xinetd)`
    if [ "$xinetd_pid" == "" ]; then
        echo "Not found."
    else
        echo "$xinetd_pid."
    fi

    ### END LOOK FOR SUPERSERVER

    echo -n "Looking for update-inetd... "
    which update-inetd 1> /dev/null 2> /dev/null
    if [ $? -eq 0 ]; then
	echo "found."
	# Debian has a tool for this.  Does this work for xinetd?
	echo -n "Updating inetd.conf... "
	update-inetd --remove "^#?tftp.*"
	update-inetd --group BOOT --add "$inetd_entry"
	echo "done."
    else
	echo "not found."
	
	### BEGIN CONFIGURE INETD
	if [ "$inetd_pid" != "" ]; then
	    sed s/"^[[:space:]]*tftp"/"#tftp"/ < /etc/inetd.conf > /tmp/$program_name.inetd.conf
	    mv /etc/inetd.conf /etc/inetd.conf.$program_name.bak
	    mv /tmp/$program_name.inetd.conf /etc/inetd.conf
	    echo -e "$inetd_entry" >> /etc/inetd.conf
	### END CONFIGURE INETD
	    
	### BEGIN CONFIGURE XINETD
	elif [ "$xinetd_pid" != "" ]; then
	    grep "includedir[[:space:]*]/etc/xinetd.d" /etc/xinetd.conf 1> /dev/null 2> /dev/null
	    if [ $? -ne 0 ]; then
		echo "xinetd does not include the /etc/xinetd.d directory"
		echo "you are running an unsupported configuration."
		return 1
	    fi
	    echo "${tftp_xinetd}" > /etc/xinetd.d/tftp
	### END CONFIGURE XINETD
	else
	    echo "I couldn't find a running inetd or xinetd."
	    echo "Nothing to configure."
	    return 1
	fi
    fi

    ### BEGIN RELOAD SUPERSERVER
    if [ "$inetd_pid" != "" -o "$xinetd_pid" != "" ]; then
	echo "Restaring ${INETD_SERVICE} ..."
        /etc/init.d/${INETD_SERVICE} restart
        if [ $? -eq 0 ]; then
            echo "done."
        else
            echo "failed."
            exit 1
        fi
    else
        echo "No superserver found."
        echo -n "Checking for a running standalone tftp server... "
        tftpd_pid=`pidof $(type -p in.tftpd)`
        if [ "$tftpd_pid" != "" ]; then
            echo "$tftp_pid."
            ### BEGIN CONFIGURE TFTP SERVER ###
            # TODO: implement tftpd auto-configuration.
            echo
            echo "WARNING: please configure your tftpd service to distribute files from $TFTPDIR"
            echo "         or disable tftpd to work as a standalone server, in order to use"
            echo "         inetd/xinetd as a superserver."
            echo
            return 1
            ### END CONFIGURE TFTP SERVER ###
        else
            echo
            echo "No standalone server found."
            echo "Try to restart /etc/init.d/$INETD_SERVICE manually and re-run si_mkbootserver."
            return 1
        fi
     fi
     ### END RELOAD SUPERSERVER

}
### END CONFIGURE INETD FOR TFTPD_HPA

### BEGIN TEST_TFTPD
test_tftpd() {
    echo -n "Looking for a tftp client... "
    TFTP_CLIENT=`which tftp`

    ### BEGIN TFTP CLIENT NOT FOUND
    if [ "$TFTP_CLIENT" == "" ]; then
	echo "tftp client not found in your PATH."
	echo ""
	echo "A tftp client is required to test the functionality of your"
	echo "tftp server.  Please install an appropriate tftp client package"
	echo "for your distribution."
	return 1
    fi

    TFTP_CLIENT=`which tftp`
    if [ "$TFTP_CLIENT" == "" ]; then
	echo "I still couldn't find a tftp client in your path."
	return 1
    else
	echo "found."
    fi
    ### END TFTP CLIENT NOT FOUND

    # i'm trying to test tftp by requesting a file from localhost
    # but there's not much point if the loopback interface is down
    echo -n "Checking for loopback interface... "
    ifconfig | grep -e "^lo" 1> /dev/null 2> /dev/null
    if [ $? -ne 0 ]; then
	echo "down."
	echo "I need the loopback interface to be up so I can test tftp, but"
	echo "it appears to be down."
	return 1
    fi
    echo "up."
    
    echo -n "Does tftp server work... "
    # the TFTPDIR variable should have been set to a valid path by now
    if [ "$TFTPDIR" == "" ]; then
	echo "no."
	echo "\"$TFTPDIR\" is not a valid tftp directory."
	return 1
    fi

    TMPFILE=""
    for i in `seq 0 9`; do
	if [ -f "$TFTPDIR/test$i" ]; then
	    continue
	fi
	TMPFILE="$TFTPDIR/test$i"
    done
    
    if [ "$TMPFILE" == "" ]; then
	echo "couldn't tell."
	echo "Unable to come up with an unused filename"
	return 1
    fi

    echo "$TFTP_TEST_FILE_TEXT" > $TMPFILE
    if [ $? -ne 0 ]; then
	echo "no."
	echo "Unable to write to $TMPFILE"
	return 1
    fi
    
    chmod 644 $TMPFILE
    if [ $? -ne 0 ]; then
	echo "Error: chmod failed."
	rm -f $TMPFILE
	exit 1
    fi
    
    tftp localhost 1> /dev/null 2> /dev/null <<EOF
get `basename $TMPFILE`
EOF
    if [ $? -ne 0 ]; then
	echo "Error: tftp returned a non-zero status"
	rm -f $TMPFILE
	exit 1
    fi
    
    diff "./`basename $TMPFILE`" "$TMPFILE" 1> /dev/null 2> /dev/null
    if [ $? -ne 0 ]; then
	echo "no."
	echo "I couldn't tftp a test file from localhost"
	rm -f $TMPFILE
	return 1
    fi

    echo "yes."
    rm -f $TMPFILE
    rm -f `basename $TMPFILE`
    return 0
}    
### END TEST_TFTPD

install_pxe_daemon() {
    echo -n "Looking for a pxe daemon... "
    pxe_daemon=`which pxe`
    if [ "$pxe_daemon" != "" ]; then
	echo "found."
	return 0
    else
	echo "not found."
        echo "WARNING: your bootserver will be configured without a pxe daemon!" 
        echo "         (ignore this warning if you're using a recent distro)"
	return 1
    fi
}

# the config file for pxe is pretty awkward.
# you can't go all crazy & start changing the order of things in the file.
# and why can't the key & value be on the same line?
configure_pxe_daemon() {
    FILE_TO_BACKUP=/etc/pxe.conf
    backup_file
    if [ $? -ne 0 ]; then
	return 1
    fi

    echo "${PXE_CONF}" > /etc/pxe.conf

    ### BEGIN edit /etc/pxe.conf
    if [ "$INTERFACE" != "" ]; then
	REPLY=$INTERFACE
    else
	echo -n "What network interface should pxe run on? ([eth0])? "
	read REPLY
    fi
    if [ "$REPLY" = "" ]; then
	interface="eth0"
    else
	interface=$REPLY
    fi

    echo -n "Checking to make sure $interface exists... "
    cut -d ":" -f 1 /proc/net/dev | grep "$interface" 1> /dev/null 2> /dev/null
    if [ $? -eq 0 ]; then
	echo "yeah."
    else
	echo "no."
	return 1
    fi
    
    echo -n "Checking to make sure $interface is up... "
    ifconfig $interface | grep "inet" 1> /dev/null 2> /dev/null
    if [ $? -eq 0 ]; then
	echo "yup."
    else
        echo "nope."
	echo "The pxe daemon won't start if the interface it's using is down."
	return 1
    fi
    
    line=`grep -n "\[Network_Interface_Name\]" /etc/pxe.conf | cut -d ":" -f 1`
    sed -e `expr $line + 1`'c\
'"$interface" < /etc/pxe.conf > /tmp/pxe.conf.$program_name.tmp
    mv /tmp/pxe.conf.$program_name.tmp /etc/pxe.conf

    if [ "$LOCAL_DHCP" != "" ]; then
	REPLY="$LOCAL_DHCP"
    else
	echo -n "Will the DHCP server be running on this machine? ([y]/n)? "
	read REPLY
    fi
    case $REPLY in
	n|N|No|NO ) usedhcpport="1" ;;
	* ) usedhcpport="0" ;;
    esac

    line=`grep -n "\[UseDHCPPort\]" /etc/pxe.conf | cut -d ":" -f 1`
    sed -e `expr $line + 1`'c\
'"$usedhcpport" < /etc/pxe.conf > /tmp/pxe.conf.$program_name.tmp
    mv /tmp/pxe.conf.$program_name.tmp /etc/pxe.conf
    
    ### END edit /etc/pxe.conf

    ### BEGIN restart pxe daemon
    echo "Restarting pxe daemon... "
    if [ -x /etc/init.d/pxe ]; then
	/etc/init.d/pxe stop
	/etc/init.d/pxe start
	echo "done."
    elif [ -x /etc/rc.d/init.d/pxe ]; then
	/etc/rc.d/init.d/pxe stop
	/etc/rc.d/init.d/pxe start
	echo "done."
    else
	echo "failed."
	return 1
    fi

    echo -n "Checking to see if pxe daemon exited silently... "
    if [ "`ps -C pxe | grep pxe`" == "" ]; then
	echo "it did."
	echo "it looks like pxe is misconfigured."
	return 1
    else
	echo "nope - still running :)"
    fi
    ### END restart pxe daemon
}

########################################################
# this function aims to put all the files required by  #
# PXE clients in the right place(s) in TFTPDIR         #
#                                                      #
# i only have access to 2 different PXE clients, and   #
# they do things differently.  The older one uses      #
# Intel Boot Agent (TM) 3.x, the other uses 4.x.       #
# I can't get the older one to work without running    #
# the pxe daemon, and it looks for files in            #
# the $LINUX_INSTALL directory, the other just looks   #
# for files directly under $TFTPDIR.                   #
# The way you can tell which type of client you have   #
# (assuming it's one of these 2 types) is by watching  #
# the console when the client boots.  if it give you   #
# a small menu, offering a choice between a linux      #
# install and a local boot, you have the former.       #
# if you get no such menu, you are probably using      #
# the newer type of client and do not need the pxe     #
# daemon (one less thing.) - it can accept the boot    #
# file option from your DHCP server.                   #
#                                                      #
# this is obviously a lot of hand-waving, because i    #
# really don't know which is abiding to the spec, or   #
# if both abide and a single general config exists     #
# to satisfy both, i just link stuff in both places    #
# (i still want my machine to boot, even if it         #
# violates the spec :)                                 #
#              -dann                                   #
########################################################
pxelinux_cfg() {
    LINUX_INSTALL=$TFTPDIR/X86PC/UNDI/linux-install
    if [ ! -d $LINUX_INSTALL/pxelinux.cfg ]; then
	mkdir -p $LINUX_INSTALL/pxelinux.cfg
    	
	if [ $? -ne 0 ]; then
	    echo "Couldn't create $LINUX_INSTALL"
	    return 1
	fi
    fi
    
    cp -f ${CONFIG_DIR}/pxelinux.cfg/syslinux.cfg.localboot $LINUX_INSTALL/pxelinux.cfg/default
    if [ $? -ne 0 ]; then
	echo "failed."
	return 1
    fi

    cp -f ${CONFIG_DIR}/pxelinux.cfg/message.txt $LINUX_INSTALL/message.txt
    if [ $? -ne 0 ]; then
	echo "failed."
	return 1
    fi
    echo "done."

    if [ -z "$PXELINUX" ]; then
        # Try to autodetect pxelinux.0.
        if [ -f /usr/share/syslinux/pxelinux.0 ]; then
            PXELINUX="/usr/share/syslinux/pxelinux.0"
        elif [ -f /usr/lib/syslinux/pxelinux.0 ]; then
            PXELINUX=/usr/lib/syslinux/pxelinux.0
        fi
	echo -n "What is the path to the pxelinux bootloader [$PXELINUX]? "
	if [ "$INTERACTIVE" == "no" ]; then
	    REPLY=""
	    echo "Non-interactive mode, assuming default."
	else
	    read REPLY
	fi
	if [ ! "$REPLY" == "" ]; then
	    PXELINUX=$REPLY
	fi
    fi
    if [ ! -f "$PXELINUX" ]; then
	echo "$PXELINUX does not exist or is not a regular file."
	return 1
    fi	
    if [ ! -f $PXELINUX ]; then
	echo "Couldn't find $PXELINUX"
	return 1
    fi

    ### BEGIN BACKUP EXISTING PXELINUX.BIN
    if [ -e $LINUX_INSTALL/linux.0 ]; then
	if [ "`md5sum $LINUX_INSTALL/linux.0 | sed 's/\( .*\)//'`" != \
			"`md5sum $PXELINUX | sed 's/\( .*\)//'`" ]; then
            echo "$LINUX_INSTALL/linux.0 already exists, but doesn't match the pxelinux.0 I want to install."

	    FILE_TO_BACKUP=$LINUX_INSTALL/linux.0
	    backup_file
	    if [ $? -ne 0 ]; then
		return 1
	    fi
	fi
    fi
    ### END BACKUP EXISTING PXELINUX.BIN

    ### BEGIN COPY OVER PXELINUX.BIN
    if [ ! -e $LINUX_INSTALL/linux.0 ]; then
	echo -n "Copying $PXELINUX to $LINUX_INSTALL/linux.0 ... "
	cp -a $PXELINUX $LINUX_INSTALL/linux.0
	if [ $? -ne 0 ]; then
	    echo "failed."
	    return 1
	fi
	echo "done."
    fi
    ### END COPY OVER PXELINUX.BIN
	    
    ### BEGIN BACKUP EXISTING PXELINUX.BIN IN TFTPDIR
    if [ -e $TFTPDIR/pxelinux.bin ]; then
	if [ "`md5sum $LINUX_INSTALL/linux.0 | sed 's/\( .*\)//'`" != \
		"`md5sum $TFTPDIR/pxelinux.bin | sed 's/\( .*\)//'`" ]; then
	    echo "$TFTPDIR/pxelinux.bin already exists, but doesn't match the pxelinux.0 I want to install."
	    FILE_TO_BACKUP=$TFTPDIR/pxelinux.bin
	    backup_file
	    if [ $? -ne 0 ]; then
		return 1
	    fi
	fi
    fi
    ### END BACKUP EXISTING PXELINUX.BIN IN TFTPDIR

    ### BEGIN CREATE PXELINUX.BIN LINK IN TFTPDIR
    if [ ! -e $TFTPDIR/pxelinux.bin ]; then
	echo -n "Linking $TFTPDIR/pxelinux.bin to $LINUX_INSTALL/linux.0 ..."
	ln $LINUX_INSTALL/linux.0 $TFTPDIR/pxelinux.bin
	if [ $? -ne 0 ]; then
	    echo "failed."
	    return 1
	fi
	echo "done."
    fi
    ### END CREATE PXELINUX.BIN LINK IN TFTPDIR

    ### BEGIN COPY PXELINUX.CFG
    if [ -e $TFTPDIR/pxelinux.cfg ]; then
	FILE_TO_BACKUP=$TFTPDIR/pxelinux.cfg
	backup_file
	if [ $? -ne 0 ]; then
	    return 1
	fi
    fi
    echo -n "Copying $LINUX_INSTALL/pxelinux.cfg to $TFTPDIR/pxelinux.cfg..."
    cp -a $LINUX_INSTALL/pxelinux.cfg $TFTPDIR
    ### END COPY PXELINUX.CFG

}    

### BEGIN MAIN        
if [ "`whoami`" != "root" ]; then
    echo "You must be root to run this script, exiting."
    exit 1
fi

# so we can use relative pathnames - hopefully dirname is pretty standard.
cd `dirname $0`
    
while [ $# -gt 0 ]; do
    case "$1" in
	-f )               INTERACTIVE="no" ; shift ;;
        --interface=* )    INTERFACE="`echo \"${1}\" | sed -e 's/^[^=]*=//'`" ; shift ;;
	--localdhcp=* )    LOCAL_DHCP="`echo \"${1}\" | sed -e 's/^[^=]*=//'`" ; shift ;;
	--pxelinux=* )     PXELINUX="`echo \"${1}\" | sed -e 's/^[^=]*=//'`" ; shift ;;
	
	* ) echo "${usage}" 1>&2; exit 1 ;;
    esac
done

echo "WARNING: this script may modify the following files:"
echo "  --> /etc/services"
echo "  --> /etc/inetd.conf"
echo "  --> /etc/xinetd.d/tftp"
echo "And can restart inetd, xinetd or tftp servers."
echo -n "Do you wish to continue (y/[n])? "
if [ "$INTERACTIVE" == "no" ]; then
    REPLY="y"
    echo "Non-interactive mode, answering yes."
else
    read REPLY
fi
case $REPLY in
    y|Y|Yes|yes|YES ) echo "Ok, continuing..." ;;
    * ) echo "Cancelled." && exit 1 ;;
esac

if [ "$TFTPDIR" == "" ]; then
    echo -n "Where should tftpd serve files from ([/var/lib/tftpboot]? "
    if [ "$INTERACTIVE" == "no" ]; then
	REPLY=""
	echo "Non-interactive mode, taking the default."
    else
	read REPLY
    fi
    if [ "$REPLY" == "" ]; then
	TFTPDIR="/var/lib/tftpboot"
    else
	TFTPDIR=$REPLY
    fi
fi

link_tftpboot
if [ $? -ne 0 ]; then
    exit 1
fi

check_tftpd
if [ $? -ne 0 ]; then
    exit 1
fi

TFTP_HPA_XINETD="\
# default: off
# description: The tftp server serves files using the trivial file transfer
# 	protocol.  The tftp protocol is often used to boot diskless
#	workstations, download configuration files to network-aware printers,
#	and to start the installation process for some operating systems.
service tftp
{
        socket_type             = dgram
        protocol                = udp
        wait                    = yes
        user                    = root
        server                  = `which in.tftpd`
        server_args             = -s $TFTPDIR -r blksize
        disable                 = no
}"

ATFTP_XINETD="\
# default: off
# description: The tftp server serves files using the trivial file transfer
# 	protocol.  The tftp protocol is often used to boot diskless
#	workstations, download configuration files to network-aware printers,
#	and to start the installation process for some operating systems.
service tftp
{
        socket_type             = dgram
        protocol                = udp
        wait                    = yes
        user                    = root
        server                  = `which in.tftpd`
        server_args             = --tftpd-timeout 300  --retry-timeout 5 --maxthread 100 --verbose=5 --logfile /var/log/atftpd.log $TFTPDIR
        disable                 = no
}"

# check to see if the installed tftpd is one
# of the ones that is known to support options
# necessary for PXE booting.
check_tftpd_hpa
if [ $? -ne 0 ]; then
    check_atftpd
    if [ $? -ne 0 ]; then
	exit 1
    else
	TFTP_DAEMON="atftpd"
    fi
else
    TFTP_DAEMON="tftpd-hpa"
fi

configure_inetd_for_tftpd_hpa
if [ $? -ne 0 ]; then
    echo "ERROR: tftp server configuration failed."
    exit 1
fi

test_tftpd
if [ $? -ne 0 ]; then
    echo "ERROR: tftp server test failed."
    exit 1
fi

install_pxe_daemon
if [ $? -eq 0 ]; then
    configure_pxe_daemon
    if [ $? -ne 0 ]; then
        echo "ERROR: pxe daemon configuration failed"
        exit 1
    fi
fi    

pxelinux_cfg
if [ $? -ne 0 ]; then
    echo "ERROR: pxelinux configuration failed"
    exit 1
fi

echo "Ok, configuration complete."
echo "Once you're DHCP server is configured, you should be all set."
if [ "$(which si_mkdhcpserver)" != "" ]; then
    echo -n "Do you want to run si_mkdhcpserver to configure your DHCP server ([y]/n)? "
    if [ "$INTERACTIVE" == "no" ]; then
	REPLY="n"
	echo "Non-interactive mode, answering no."
    else
	read REPLY
    fi
    case $REPLY in
	n|N|No|NO ) exit 0 ;;
	    * ) si_mkdhcpserver ;;
    esac
fi
