#!/bin/sh -e

##########################################################################
#   Script description:
#       Set up a PXE install server including dhcpd, tftpf, and nfsd.
#       Primary references:
#       http://www.ixsystems.com/whats-new/freebsd-unattended-installation-of-servers/
#       man bsdinstall
#       http://www.wonkity.com/~wblock/docs/html/pxe.html
#
#   Arguments:
#       Name of ISO image to install.
#
#   Returns:
#       NA
#
#   History:
#   Date        Name        Modification
#   2014-11-15  Jason Bacon Begin
##########################################################################

# FIXME: Add option to change all config files and restart services instead
# of just regenerating them or doing nothing.

# Test file to paste into command:
# /home/bacon/Save/FreeBSD-10.1-RELEASE-amd64-disc1.iso

usage()
{
    cat << EOM

Usage: $0 file.iso

FreeBSD users:

    A disc1 or dvd1 ISO is recommended.  The bootonly ISOs are not supported
    at this time.

EOM
    exit 1
}


##########################################################################
#   Function description:
#       Pause until user presses return
##########################################################################

pause()
{
    local junk
    
    printf "Press return to continue..."
    read junk
}


##########################################################################
#   Function description:
#       Print a line
##########################################################################

line()
{
    printf '================================================================\n'
}
    
case $(auto-ostype) in
FreeBSD)
    ##########################################################################
    #   Function description:
    #       
    #   Arguments:
    #       
    #   Returns:
    #       
    #   History:
    #   Date        Name        Modification
    #   2014-11-18  Jason Bacon Begin
    ##########################################################################
    
    tftp_inetd()
    {
	conf='/etc/inetd.conf'
	if [ ! -e $conf.orig ]; then
	    cp $conf $conf.orig
	fi
	if ! grep -q '^tftp' $conf; then
	    awk -v installer_path=$installer_path ' {
		if ($1 == "#tftp" && $3 == "udp")
		{
		    printf("tftp dgram udp wait root /usr/libexec/tftpd tftpd -l -s %s\n",
		    installer_path);
		}
		print $0;
	    }' $conf > $conf.temp
	    mv $conf.temp $conf
	fi
	cat << EOM
    
    Editing $conf.
    
    Double-check the tftp entry.
    
EOM
	pause
	$EDITOR $conf
	auto-append-line 'inetd_enable' 'inetd_enable="YES"' /etc/rc.conf $0
	service inetd restart
    }
    
    
    ##########################################################################
    #   Function description:
    #       
    #   Arguments:
    #       
    #   Returns:
    #       
    #   History:
    #   Date        Name        Modification
    #   2014-11-18  Jason Bacon Begin
    ##########################################################################
    
    tftp_hpa()
    {
	if [ ! -e /usr/local/libexec/in.tftpd ]; then
	    pkg install -y tftp-hpa
	fi
	mkdir -p $prefix/pxelinux.cfg $prefix/Dist
	auto-append-line 'tftpd_enable' 'tftpd_enable="YES"' /etc/rc.conf $0
	# FIXME: Change to --permissive --secure --blocksize
	auto-append-line "tftpd_flags=\"-p -s $prefix -B 1024 --ipv4\"" \
	    /etc/rc.conf $0
	printf "Make sure no other tftp servers are running.\n"
	pause
	service tftpd restart
	save_cwd=`pwd`
	
	# FIXME: Use syslinux port
	cd $prefix/Dist
	dist=syslinux-4.04
	if [ ! -e $dist.tar.xz ]; then
	    fetch --no-verify-peer \
		https://www.kernel.org/pub/linux/utils/boot/syslinux/$dist.tar.xz
	    tar zxvf $dist.tar.xz
	fi
	cp $dist/gpxe/gpxelinux.0 $prefix
	cp $dist/com32/menu/menu.c32 $prefix
	cp $dist/com32/menu/vesamenu.c32 $prefix
	cp $dist/com32/modules/reboot.c32 $prefix
	cp $dist/com32/modules/chain.c32 $prefix
	cp $dist/memdisk/memdisk $prefix
	cd $save_cwd
	
	# Create a simple PXELINUX boot menu
	config=$prefix/pxelinux.cfg/default
	cat << EOM > $config
timeout 600
ui menu.c32
menu title PXELINUX Boot Menu

label localboot
  menu label Boot from local disk
  localboot

label freebsd
  menu label FreeBSD Install
  pxe ${installer_path##$prefix/}/boot/pxeboot

label reboot
  menu label Reboot
  kernel reboot.c32
EOM
	printf "Editing $config\n"
	pause
	$EDITOR $config
    }
    
    
    ##########################################################################
    #   Main
    ##########################################################################
    
    if [ $# != 1 ]; then
	usage
    fi
    
    iso="$1"
    if [ ! -e $iso ]; then
	printf "$iso: No such file.\n"
	exit 1
    fi
    
    if [ -z $EDITOR ]; then
	EDITOR=vi
    fi
    
    export AUTO_ASK_TAG_PREFIX=auto-pxe-installer-setup
    
    line
    df -h
    cat << EOM

Your PXE installation directory must be on a partition with enough space
to contain the contents of your install CD or DVD.

EOM
    prefix=`auto-ask pxe-prefix 'Prefix for PXE installation files?' /pxeserver`
    : ${LOCALBASE:=/usr/local}
    cat << EOM > $LOCALBASE/etc/auto-admin/pxe
# Generated by $0
# DO NOT EDIT

prefix $prefix
EOM
    installer_path=${iso%.iso}
    installer_path=$prefix/images/${installer_path##*/}
    
    
    ##########################################################################
    #   Set up tftp
    ##########################################################################
    
    line
    cat << EOM

The PXELINUX boot image supports a PXE boot menu with potentially many
different boot options.  If you select this option, auto-pxe-installer-setup
will create a simple boot menu that will allow you to choose between
booting from the local disk or starting a FreeBSD installation.  You may
add additional boot options such as memtest86 afterward.

Without this menu, booting from PXE will go straight to the installer every
time.  If your BIOS is configured to PXE boot by default, you should either
choose the PXELINUX menu option or find another way to ensure that you
don't accidentally reimage your computer every time it boots!

EOM
    pxe_boot_menu=`auto-ask pxe-menu "Install a PXE boot menu using syslinux boot images?" y`
    if [ $pxe_boot_menu = y ]; then
	tftp_hpa
    else
	tftp_inetd
    fi
    
    
    ##########################################################################
    #   Set up dhcpd
    ##########################################################################
    
    line
    cat << EOM

PXE booting requires a DHCP server on the local network.  You can run it
on this server or use the DHCP server provided by the network router or
other device.

***************************************************************************
*                                WARNING                                  *
* DO NOT ENABLE A DHCP SERVER ON THIS HOST UNLESS YOU MANAGE THE NETWORK  *
* AND KNOW WHAT YOU'RE DOING.  ENABLING A "ROGUE" DHCP SERVER CAN CAUSE   *
* SERIOUS PROBLEMS FOR OTHER USERS ON THE NETWORK.                        *
***************************************************************************

EOM
    
    printf 'Available network interfaces:\n\n'
    ifconfig|egrep 'flags|inet'
    while [ 0$iface = 0 ]; do
	iface=`auto-ask pxe-iface '\nNetwork interface?' ''`
    done
    default_ip=$(auto-get-ip octet)
    local_ip=`auto-ask pxe-local-ip 'IP address of this server?' $default_ip`
    default_subnet=$(auto-get-network octet)
    subnet=`auto-ask pxe-subnet 'Subnet?' $default_subnet`
    # Cannot use hex notation
    # default_mask=`ifconfig 2> /dev/null $iface | awk '$1 == "inet" {print $4}' | cut -d : -f 2`
    default_mask=$(auto-get-netmask octet)
    netmask=`auto-ask pxe-netmask 'Net mask?' $default_mask`
    conf='/usr/local/etc/dhcpd.conf'
    
    cat << EOM

The DHCP server can run on this host or elsewherem, such as a router.  
If not running on this host, be sure that IP address assignments are
permanent.  A long lease time is *not* permanent.  Addresses may still
change for various reasons regardless of lease time.

EOM
    dhcpd=`auto-ask run-dhcpd "[Re]configure dhcpd on this host?" y`
    if [ 0$dhcpd != 0n ]; then
	if [ ! -e /usr/local/sbin/dhcpd ]; then
	    pkg_dir=$(echo /usr/ports/net/isc-dhcp*-server)
	    pkg_name=$(basename $pkg_dir)
	    pkg install -y $pkg_name
	fi
	
	cat << EOM

Note: Your original dhcpd.conf (from before the first pxe-server-setup)
run will be saved to dhcpd.conf.orig and your current dhcpd.conf will
be overwritten.

If you don't like this idea, press Ctrl+C now.

EOM
	pause
    
	default_domain=$(hostname | cut -d . -f 2-)
	domain=`auto-ask pxe-domain 'Domain name?' $default_domain`
	
	first_three=$(echo $subnet | cut -d . -f 1-3)
	default_router=$(printf "%s.1" $first_three)
	router=`auto-ask pxe-router 'Router?' $default_router`
	
	printf "Enter 1 or more name servers separated by commas.\n"
	nameserver=`auto-ask pxe-nameserver 'Name server?' $default_router`
	
	# Reserve some IPs for static addresses
	default_first=$(printf "%s.21" $first_three)
	first_address=`auto-ask pxe-first-dhcp 'First DHCP address?' $default_first`
	
	# FIXME: Base this on bits in the netmask
	default_last=$(printf "%s.254" $first_three)
	last_address=`auto-ask pxe-last-dhcp 'Last DHCP address?' $default_last`
	# Default lease time 20 years (effectively permanent)
	lease_time=`auto-ask pxe-lease 'Lease time (seconds)?' '630720000'`
    
	if [ ! -e $conf.orig ]; then
	    cp $conf $conf.orig
	fi
	
	if [ 0$pxe_boot_menu = 0y ]; then
	    boot_filename=gpxelinux.0
	else
	    boot_filename="boot/pxeboot"
	fi
	cat << EOM > $conf
allow booting;
allow bootp;
authoritative;
option domain-name "$domain";
option subnet-mask $netmask;
option routers $router;
default-lease-time $lease_time;
max-lease-time $lease_time;
ddns-update-style none;
log-facility local7;
local-address $local_ip;
subnet $subnet netmask $netmask
{
    range $first_address $last_address;
    # IP of PXE server required by some clients.
    next-server $local_ip;
    # Location of PXE boot image within bootable system
    filename "$boot_filename";
    # Location of bootable system for NFS
    option root-path "$installer_path";
    option domain-name-servers $nameserver;
}
EOM
	cat << EOM

Editing $conf.

Double-check the addresses, etc.

EOM
	pause
	$EDITOR $conf
	
	auto-append-line 'dhcpd_enable' 'dhcpd_enable="YES"' /etc/rc.conf $0
	auto-append-line 'dhcpd_ifaces' 'dhcpd_ifaces="'$iface'"' /etc/rc.conf $0
	# Use onerestart here in case user has it disabled in rc.conf to
	# prevent conflicts.
	service isc-dhcpd onerestart
    else
	cat << EOM

Be sure to set up your DHCP server with the following parameters:

o Enable PXE/Network booting
o Default BIOS filename gpxelinux.0
o tftp server:          $local_ip
o next-server:          $local_ip
o root-path:            $installer_path

If you're running DHCP on this host and chose not to reconfigure it, be
sure to edit $conf and restart dhcpd if necessary.

EOM
	pause
    fi
    
    
    ##########################################################################
    #   Set up NFS
    ##########################################################################
    
    printf "[Re]configure NFS? [y]/n "
    read do_nfs
    
    if [ 0$do_nfs != 0n ]; then
	df
	
	# Need -alldirs to allow PXE to mount individual ISO image directories
	# under $prefix/images.
	if mount | fgrep zfs; then
	    default_zfs=y
	else
	    default_zfs=n
	fi
	use_zfs=`auto-ask use-zfs "Use ZFS filesystem?" $default_zfs`
	if [ $use_zfs = 'y' ]; then
	    zfs_pool=`auto-ask zfs-pool "ZFS Pool?" zroot`
	    zfs create -o mountpoint=$prefix/images ${zfs_pool}/pxe-images || true
	    zfs set sharenfs="-alldirs,ro -network $subnet -mask $netmask" \
		${zfs_pool}/pxe-images
	    zfs get sharenfs
	    pause
	else
	    auto-append-line \
		"$prefix/images -alldirs,ro -network $subnet -mask $netmask" \
		/etc/exports $0
	    
	    cat << EOM

Editing /etc/exports.

The $prefix/images entry is only a guess.

There should not be multiple entries within the same partition.

Instead, there should be one entry for the highest directory within that
partition and that entry should have the -alldirs flag.

EOM
	    pause
	    $EDITOR /etc/exports
	fi
    fi
    
    printf "Unpacking ISO...\n"
    if [ ! -e $installer_path ]; then
	mkdir -p $installer_path
	tar -C $installer_path -pxf $iso || true
    fi
    printf "Set a root password for the PXE installed systems:\n"
    chroot $installer_path passwd root
    root_pw="`awk -F : '$1 == \"root\" { print $2 }' /etc/master.passwd`"
    
    if [ ! -e $installer_path/usr/sbin/bsdinstall ]; then
	cat << EOM

Warning: $0 only supports bsdinstall.

Did not find $installer_path/usr/sbin/bsdinstall.

We can continue, but you're on your own as far as setting up unattended
installation.

EOM
	pause
    fi
    
    # Prevent mount_nfs: no <host>:<dirpath> nfs-name error during boot
    # https://wiki.koumbit.net/FreeBsd/PxeBootConfiguration
    if [ -e $installer_path/etc/fstab ]; then
	mv $installer_path/etc/fstab $installer_path/etc/fstab.hide
    fi
    
    # Hack to get around resolution issue with 10.1-RELEASE
    rm -f $installer_path/etc/resolv.conf
    echo "nameserver $nameserver" > $installer_path/etc/resolv.conf
    
    # Add root ssh key from this server to new node
    if [ ! -e /root/.ssh/id_rsa.pub ]; then
	ssh-keygen -f /root/.ssh/id_rsa -N ''
    fi
    root_key=`cat /root/.ssh/id_rsa.pub`
    
    # Create automated install script
    # man bsdinstall lacks an explanation of PARTITIONS
    # http://www.sysadminwiki.net/site/doku.php/os/bsd/freebsd/automating_freebsd_installations
    installerconfig=$installer_path/etc/installerconfig
    
    cat << EOM > $installerconfig
##########################################################################
# PXE installer script.  All syntax is Bourne shell.
# "man bsdinstall" for more information.
#
# You will generally find this file in
#
#   /pxeserver/images/FreeBSD-XX.X-RELEASE-<arch>-disc1/etc/installerconfig
#
# in case you want to make changes later, though auto-pxe-installer-setup
# allows the user to specify other locations.
##########################################################################

##########################################################################
# First section: PREAMBLE ( Executed before install )
# Use this section only to set up the environment for bsdinstall.
##########################################################################

##########################################################################
# Select boot disk(s).
#
# ATA/IDE take priority, followed by SAS/SCSI/external (anything handled
# by the da driver), followed by RAIDs.  You can change the priority or
# check additional disk drivers editing the loops below.
#
# Make sure there are no external disks plugged in at boot time unless
# you want them as part of the boot volume.

# Pick either UFS2 or ZFS.
# UFS2 is generally preferable for HPC compute nodes since it uses
# less RAM, leaving more for computational tasks.
# ZFS is preferable for NFS servers, so if you configure them using PXE,
# uncomment ZFS to install the file servers, then switch back to UFS2 for
# installing the compute nodes.

fs=UFS2
# fs=ZFS

case \$fs in
UFS2)
    # ada = ATA/IDE, da = SAS/USB/etc, mfi = LSI MegaRAID
    # Add other drivers here if your system has them
    # This will use the first ATA, SCSI, or RAID disk found
    for DISK in ada0 da0 mfid0; do
	if [ -e /dev/\$DISK ]; then
	    break
	fi
    done

    # Note: / must come first in PARTITIONS, or the new system will not boot
    # Reasonable default partitions for most users.  Edit to taste.
    PARTITIONS="\$DISK { 4G freebsd-ufs /, 4G freebsd-swap, 32G freebsd-ufs /tmp, 32G freebsd-ufs /var, auto freebsd-ufs /usr }"
    
    # Alternative: Very basic partition scheme with auto-sized swap and / only.
    # PARTITIONS="\$DISK"
    ;;

ZFS)
    # Build a list of all ATA, SCSI, or RAID disks and create a stripe, mirror
    # or raidz1, depending on how many disks of the same type are found.
    # Add other drivers here if your system has them
    # RAID or JBOD volumes must already exist during PXE booting, or the
    # devices will not be found in /dev.
    
    export nonInteractive="YES"
    
    # Override default swap partition size
    # export ZFSBOOT_SWAP_SIZE=4g
    
    # Not sure if bsdinstall cares about CWD, so playing it safe
    save_cwd=\$(pwd)
    cd /dev
    for driver in ada da mfid; do
	if [ -e \${driver}0 ]; then
	    disk=0
	    while [ -e \$driver\$disk ]; do
		ZFSBOOT_DISKS="\$ZFSBOOT_DISKS \$driver\$disk"
		disk=\$((disk + 1))
	    done
	    break   # Only use first detected disk type for pool
	fi
    done
    cd \$save_cwd
    export ZFSBOOT_DISKS
    
    # awk removes leading space from wc -l output
    disk_count=$(echo $ZFSBOOT_DISKS | wc -w | awk '{ print $1 }')
    case \$disk_count in
    1)
	export ZFSBOOT_VDEV_TYPE=stripe
	;;
    
    2)
	export ZFSBOOT_VDEV_TYPE=mirror
	;;
    
    *)
	export ZFSBOOT_VDEV_TYPE=raidz1
	;;
    
    esac
    ;;

esac    # UFS2 or ZFS

# Remove DISTRIBUTIONS you don't need to speed up install.
# ports can be installed later with git.
# src is only needed if you plan to rebuild the kernel or userland, or
# for building certain drivers from ports, including some WiFi drivers.

# Complete set
# DISTRIBUTIONS="base.txz doc.txz kernel.txz lib32.txz ports.txz src.txz"

# These should be sufficient for most users.
DISTRIBUTIONS="base.txz kernel.txz lib32.txz"

##########################################################################
#   Post-install
#
#   Commands below are executed on the newly installed system after
#   installation is complete and before the first reboot.
#
#   If you want to unplug the keyboard and display before installation
#   completes and never come back, do not enable interactive commands
#   below, like tzsetup.
#
#   With the default non-interactive config below, the new node will
#   accept passwordless root login from this host via ssh after the first
#   reboot, so you can immediately run more post-install options over the
#   network.  If you are running DHCP on a server, the IP address
#   will be in /var/db/dhcpd/dhcpd.leases.  If your network supports it,
#   the hostname should also self-register in DNS.
#
#   auto-pxe-node-discover can be run on the DHCP server during or after
#   PXE install to add permanent leases to dhcp.conf and hostnames to
#   /etc/hosts.
##########################################################################

#!/bin/sh

# Installing any packages here used to require another interaction with the
# new node after installation.  The first run of 'pkg' required keyboard
# input to confirm initialization.  Does not seem to be the case anymore
# so this should work.

pkg install -y rsync

##########################################################################
#   Set a hostname
##########################################################################

# Use IP as a unique hostname temporarily
# Not sure where the 0.0.0.0 is coming from during PXE install.  It's not
# in ifconfig output after install.
hostname=\`ifconfig | awk '\$1 == "inet" { print \$2 }' | fgrep -v 127.0.0 | fgrep -v 0.0.0.0 | sed -e 's|\.|-|g'\`'.local'
echo "hostname=\$hostname" >> /etc/rc.conf

##########################################################################
#   Set time zone
##########################################################################

# Change this to your own timezone
cp /usr/share/zoneinfo/America/Chicago /etc/localtime

# Set timezone interactively during PXE install
# tzsetup

# Set all available interfaces to DHCP for now.
for iface in \`dmesg | awk '\$2 == "Ethernet" { print \$1 }'\`; do
    iface=\${iface%:}
    echo "ifconfig_\$iface=DHCP" >> /etc/rc.conf
done

# Configure basic services.  Most configuration can be done after reboot, but
# a few basics here will save a little hassle.
echo 'sshd_enable=YES' >> /etc/rc.conf
echo 'ntpd_enable=YES' >> /etc/rc.conf
echo 'ntpdate_enable=YES' >> /etc/rc.conf
echo 'ntpdate_flags="-u pool.ntp.org"' >> /etc/rc.conf
echo 'dumpdev=NO' >> /etc/rc.conf

sed -i '' 's|#PermitRootLogin no|PermitRootLogin prohibit-password|g' /etc/ssh/sshd_config
mkdir -p /root/.ssh
echo '$root_key' >> /root/.ssh/authorized_keys

touch /etc/fstab

# Install root password
awk -F : -v pw='$root_pw' ' {
    if ( \$1 == "root" )
    {
	printf("%s:%s:%s:%s:%s:%s:%s:%s:%s:%s\n",
	    \$1,pw,\$3,\$4,\$5,\$6,\$7,\$8,\$9,\$10);
    }
    else
    {
	print \$0;
    }
}' /etc/master.passwd > /etc/master.passwd.tmp

# Install updated master password file if it passes sanity test
if pwd_mkdb -C /etc/master.passwd.tmp; then
    pwd_mkdb /etc/master.passwd.tmp
fi

EOM
    
    cat << EOM

Opening installerconfig automated install script.

Edit partitions, distributions, etc. to taste.

EOM
    
    pause
    $EDITOR $installerconfig
    
    # Make sure everything is readable!
    chmod -R a+rX $installer_path
    
    auto-append-line 'rpcbind_enable' 'rpcbind_enable="YES"' /etc/rc.conf $0
    auto-append-line 'mountd_enable' 'mountd_enable="YES"' /etc/rc.conf $0
    auto-append-line 'nfs_server_enable' 'nfs_server_enable="YES"' /etc/rc.conf $0
    auto-append-line nfs_server_flags 'nfs_server_flags="-t -u -n 16"' /etc/rc.conf $0
    service nfsd restart
    service mountd reload
    ;;

RHEL)
    ##########################################################################
    #   Script description:
    #       Set up a PXE install server including dhcpd, tftpf, and nfsd.
    #       Primary references:
    #       http://www.ixsystems.com/whats-new/freebsd-unattended-installation-of-servers/
    #       http://www.wonkity.com/~wblock/docs/html/pxe.html
    #       http://www.smtps.net/pxe-kickstart.html
    #       https://people.redhat.com/sgrubb/files/usgcb/rhel5/workstation-ks.cfg
    #
    #   Arguments:
    #       Name of ISO image to install.
    #
    #   Returns:
    #       NA
    #
    #   History:
    #   Date        Name        Modification
    #   2014-11-15  Jason Bacon Begin
    #                           Based on Jim Wagner's kickstart-server-setup
    #                           and my own FreeBSD auto-pxe-installer-setup
    ##########################################################################
    
    ##########################################################################
    #   Function description:
    #       Set up tftp via Linux xinetd
    #
    #   Arguments:
    #       NA
    #
    #   Returns:
    #       NA
    #
    #   History:
    #   Date        Name        Modification
    #   2014-11-18  Jason Bacon Begin
    ##########################################################################
    
    tftp_pxelinux_setup()
    {
	local conf
	
	# FIXME: Skip if already installed
	yum install -y syslinux xinetd tftp-server
    
	tftpboot=/var/lib/tftpboot
	mkdir -p $tftpboot/pxelinux.cfg
	cp /usr/share/syslinux/pxelinux.0 $tftpboot
	cp /usr/share/syslinux/menu.c32 $tftpboot
	cp /usr/share/syslinux/reboot.c32 $tftpboot
    
	# Create a simple PXELINUX boot menu
	conf=$tftpboot/pxelinux.cfg/default
	cat << EOM > $conf
# 600 centiseconds = 60 seconds
timeout 600
ui menu.c32
menu title CentOS PXE Installer Boot Menu

label localboot
  menu label Boot from local disk
  localboot

label install
  menu label Install CentOS
  kernel images/$installer_leaf_dir/images/pxeboot/vmlinuz
  # FIXME: Can ksdevice be ommitted to support hetergeneous hardware?
  append initrd=images/$installer_leaf_dir/images/pxeboot/initrd.img ksdevice=$iface load_ramdisk=1 network ks=nfs:${local_ip}:/$installer_path/ks.cfg

label reboot
  menu label Reboot
  kernel reboot.c32
EOM
	
	printf "Editing $conf\n"
	pause
	$EDITOR $conf
    
	# Enable tftpd
	conf='/etc/xinetd.d/tftp'
	if [ ! -e $conf.orig ]; then
	    cp $conf $conf.orig
	fi
	sed -i '/disable/ c\\tdisable\t\t\t= no' /etc/xinetd.d/tftp
    
	chmod -R a+rX /var/lib/tftpboot
	
	printf "Make sure no other tftp servers are running.\n"
	pause
    
	# CentOS 6
	# chkconfig xinetd on
	# service xinetd restart

	# CentOS 7
	# http://www.cyberphoton.com/tftp-server-in-rhel7/
	# FIXME: Need selinux set to permissive?
	systemctl enable xinetd
	systemctl enable tftp
	systemctl restart xinetd
	systemctl restart tftp
    }
    
    
    ##########################################################################
    #   Function description:
    #       
    #   Arguments:
    #       
    #   Returns:
    #       
    #   History:
    #   Date        Name        Modification
    #   2015-12-16  root        Begin
    ##########################################################################
    
    nfs_setup()
    {
	printf "Configure NFS? [y]/n "
	read resp
	if [ 0$resp != 0n ]; then
	    yum install -y nfs-utils
	    
	    # FIXME: Use proper mask
	    auto-append-line "$prefix/images $subnet/$netmask(ro)" /etc/exports $0
	    
	    cat << EOM

Editing /etc/exports.  Make sure your syntax is correct and that you are
properly handling mount points within the same partition.

EOM
	    pause
	    $EDITOR /etc/exports
	    
	    # Make sure everything is readable!
	    chmod -R a+rX $installer_path
	    
	    case $(auto-ostype) in
	    RHEL6)
		chkconfig rpcbind on
		chkconfig nfslock on
		chkconfig nfs on
		
		service rpcbind restart
		service nfslock restart
		service nfs restart
		;;
	    
	    RHEL7)
		systemctl enable rpcbind
		systemctl enable nfs-server
		systemctl enable nfs-lock
		systemctl enable nfs-idmap
		
		systemctl restart rpcbind
		systemctl restart nfs-server
		systemctl restart nfs-lock
		systemctl restart nfs-idmap
		;;
	    
	    *)
		;;
	    
	    esac
	    exportfs -rv
	fi
	return 0
    }
    
    
    ##########################################################################
    #   Function description:
    #       
    #   Arguments:
    #       
    #   Returns:
    #       
    #   History:
    #   Date        Name        Modification
    #   2015-12-16  root        Begin
    ##########################################################################
    
    dhcp_setup()
    {
	cat << EOM

PXE booting requires a DHCP server on the local network.  You can run it
on this server or use the DHCP server provided by the network router or
other device.

***************************************************************************
*                                WARNING                                  *
* DO NOT ENABLE A DHCP SERVER ON THIS HOST UNLESS YOU MANAGE THE NETWORK  *
* AND KNOW WHAT YOU'RE DOING.  ENABLING A "ROGUE" DHCP SERVER CAN CAUSE   *
* SERIOUS PROBLEMS FOR OTHER USERS ON THE NETWORK.                        *
***************************************************************************

EOM
	printf "Configure dhcpd on this host? (y/[n]) "
	read dhcpd
	
	if [ 0$dhcpd = 0y ]; then
	    if [ ! -e /usr/sbin/dhcpd ]; then
		yum install -y dhcp
	    fi
	
	    cat << EOM

Note: Your original dhcpd.conf (from before the first pxe-server-setup)
run will be saved to dhcpd.conf.orig and your current dhcpd.conf will
be overwritten.

If you don't like this idea, press Ctrl+C now.

EOM
	    pause
	
	    default_domain=$(hostname | cut -d . -f 2-)
	    domain=`auto-ask pxe-domain 'Domain name?' $default_domain`
	    
	    first_three=$(echo $subnet | cut -d . -f 1-3)
	    default_router=$(printf "%s.1" $first_three)
	    router=`auto-ask pxe-router 'Router?' $default_router`
	    nameserver=`auto-ask pxe-nameserver 'Name server?' 192.168.0.1`
	    # Reserve some IPs for static addresses
	    
	    first_address=`auto-ask pxe-first-dhcp 'First DHCP address?' 192.168.1.1`
	    last_address=`auto-ask pxe-last-dhcp 'Last DHCP address?' 192.168.63.255`
	    # Default lease time 20 years (effectively permanent)
	    lease_time=`auto-ask pxe-lease 'Lease time (seconds)?' '630720000'`
	
	    conf='/etc/dhcp/dhcpd.conf'
	    if [ ! -e $conf.orig ]; then
		cp $conf $conf.orig
	    fi
	    boot_filename=gpxelinux.0
	    
    #
    # DHCP Server Configuration file.
    #   see /usr/share/doc/dhcp*/dhcpd.conf.sample
    #   see 'man 5 dhcpd.conf'
    #
    
    # Example from Avi produced by kickstart-server-setup
    # option domain-name    "hpc.uwm.edu";
    # option domain-name-servers   newavi1.hpc.uwm.edu;
    # default-lease-time    -1;
    # max-lease-time    -1;
    # authoritative;
    # subnet 192.168.1.0 netmask 255.255.255.0 {
    #     range dynamic-bootp 192.168.1.2 192.168.1.200;
    #     use-host-decl-names on;
    #     option broadcast-address 192.168.1.255;
    #     option domain-name-servers 129.89.192.168, 129.89.10.2;
    #     option routers 192.168.1.253;
    # 
    #     host compute-5-36 {
    #         hardware ethernet 00:26:b9:2e:21:3c;
    #         option host-name "compute-5-36";
    #         fixed-address 192.168.1.143;
    #     }
    # filename    "pxelinux.0";
    # next-server    192.168.1.253;
	
	    cat << EOM > $conf
allow booting;
allow bootp;
authoritative;
option domain-name "$domain";
option subnet-mask $netmask;
option routers $router;
ddns-update-style none;
log-facility local7;
local-address $local_ip;
subnet $subnet netmask $netmask
{
    range $first_address $last_address;
    default-lease-time $lease_time;
    max-lease-time $lease_time;
    
    # IP of PXE server required by some clients.
    next-server $local_ip;
    
    # Location of PXE boot image within bootable system
    filename "pxelinux.0";
    
    # Location of bootable system for NFS
    option root-path "$installer_path";
    option domain-name-servers $nameserver;
}
EOM
	    cat << EOM

Editing $conf.

Double-check the addresses, etc.

EOM
	    pause
	    $EDITOR $conf
	
	    # Make the DHCP server only serve to private iface's subnet
	    sed -i '/DHCPDARGS/ c\DHCPDARGS=$iface' /etc/sysconfig/dhcpd
	    
	    case $(auto-os-release) in
	    RHEL6)
		chkconfig dhcpd on
		service dhcpd restart
		;;
	    RHEL7)
		systemctl enable dhcpd
		systemctl restart dhcpd
		;;
	    esac
	else
	    cat << EOM

You will need to manually configure your DHCP server to point to this PXE
server.

EOM
	    # FIXME: Unfinished
	    # Set up for situations where the DHCP server is not running on
	    # this server.  ( e.g. DHCP is handled by a hardware router. )
	    # pkg install -y pxe-pdhcp
	    # pxe-pdhcp -i bge0
	    # What else?
	fi
    }
    
    
    ##########################################################################
    #   Main
    ##########################################################################
    
    case $(auto-os-release) in
    RHEL6)
	;;
    
    RHEL7)
	;;
    
    *)
	auto-unsupported-release $0
	exit 1
	;;
    
    esac
    
    if [ $# != 1 ]; then
	usage
    fi
    
    if [ -z $EDITOR ]; then
	EDITOR=vi
    fi
    
    iso="$1"
    if [ ! -e $iso ]; then
	printf "$iso: No such file.\n"
	exit 1
    fi
    
    line
    df -h
    cat << EOM

Your PXE installation directory must be on a partition with enough space
to contain the contents of your install CD or DVD.

EOM
    prefix=`auto-ask pxe-prefix 'Prefix for PXE installation files?' /var/lib/tftpboot`
    installer_leaf_dir=${iso%.iso}
    installer_leaf_dir=${installer_leaf_dir##*/}
    installer_path=$prefix/images/${installer_leaf_dir}
    
    printf 'Available network interfaces:\n\n'
    ip addr 2> /dev/null | egrep 'mtu|inet'
    while [ 0$iface = 0 ]; do
	iface=`auto-ask pxe-iface '\nNetwork interface?' ''`
    done
    default_ip=`ip addr 2> /dev/null show dev $iface | \
	awk '$1 == "inet" { print $2 }' | cut -d / -f 1`
    local_ip=`auto-ask pxe-local-ip 'IP address of this server?' $default_ip`
    
    # Needed by both dhcp_setup and nfs_setup
    # FIXME: CentOS 7 needs octets, not bit count
    default_mask=`ip address show dev $iface 2> /dev/null | \
	awk '$1 == "inet" { print $2 }' | cut -d / -f 2`
    default_mask='255.255.128.0'
    subnet=`auto-ask pxe-subnet 'Subnet?' 192.168.0.0`
    netmask=`auto-ask pxe-netmask 'Net mask?' $default_mask`
    
    ##########################################################################
    #   Set up dhcpd
    ##########################################################################
    
    line
    dhcp_setup
    
    ##########################################################################
    #   Set up tftp
    ##########################################################################
    
    # Must come after dhcp setup, which sets variables used here
    line
    tftp_pxelinux_setup
    
    ##########################################################################
    #   Copy install media to server
    ##########################################################################
    
    ksconfig=$installer_path/ks.cfg
    if [ ! -d $installer_path ]; then
	if ! which rsync; then
	    yum install -y rsync
	fi
	mkdir -p $prefix/mnt
	if ! mount -t iso9660 -o loop $iso $prefix/mnt; then
	    printf "CD already mounted.\n"
	fi
	mkdir -p $installer_path
	rsync -av $prefix/mnt/ $installer_path
	chmod -R a-w,a+rX $installer_path
	umount $prefix/mnt
	# We'll generate a new one later
	mv $ksconfig $ksconfig.orig || true
    fi
    
    # Set root password in install media?
    # No can do unless it's a live filesystem.
    # chroot $installer_path root
    
    # FIXME: Should not be necessary if $installer_path is under $tftpboot
    #mkdir -p $tftpboot/$installer_leaf_dir
    #cp $installer_path/images/pxeboot/vmlinuz $tftpboot/$installer_leaf_dir
    #cp $installer_path/images/pxeboot/initrd.img $tftpboot/$installer_leaf_dir
    
    ##########################################################################
    #   Set up NFS
    ##########################################################################
    
    nfs_setup
    
    ##########################################################################
    #   Set up ssh keys to new server here and in kickstart script
    ##########################################################################
    
    # Add root ssh key from this server to be placed on new node
    if [ ! -e /root/.ssh/id_rsa.pub ]; then
	ssh-keygen -f /root/.ssh/id_rsa -N ''
    fi
    root_key="`cat /root/.ssh/id_rsa.pub`"
    restorecon -R -v /root/.ssh     # Set security context for selinux
    
    ##########################################################################
    #   Kickstart
    ##########################################################################
    
    yum install -y pykickstart
    root_pw="`awk -F : '$1 == \"root\" { print $2 }' /etc/shadow`"
    
    printf "Time zone? [America/Chicago] "
    read time_zone
    if [ 0$time_zone = 0 ]; then
	time_zone='America/Chicago'
    fi
    
    # RHEL/CentOS 6 cannot boot from xfs, while 7 defaults to it
    if fgrep 'release 6' /etc/redhat-release; then
	boot_fs=ext4
    else
	boot_fs=xfs
    fi
    
    if [ ! -e $ksconfig ]; then
	cat << EOM > $ksconfig
# auto-pxe-linux-setup kickstart file

install
# ncurses mode
text
# terminal mode
# (CentOS 6.6 minimal fails with "In interactive step tasksel can't continue")
# cmdline
autostep
reboot
nfs --server $local_ip --dir $installer_path

lang en_US.UTF-8
keyboard us

# --device=link should use the first interface found in an "up" state
# Useful for heterogeneous hardware (i.e. not all servers use em0, etc.)
# network --device=link --onboot=yes --bootproto=dhcp
# --device=bootif uses the value from PXELINUX BOOTIF variable
# network --device=bootif --onboot=yes --bootproto=dhcp
network --device=$iface --onboot=yes --bootproto=dhcp
firewall --enabled --service=ssh
authconfig --enableshadow --passalgo=sha512
rootpw --iscrypted "$root_pw"
timezone --utc $time_zone

# Default: --location=mbr
bootloader --driveorder=sda

# selinux --permissive

# Remove known partitions
clearpart --drives=sda

# Remove invalid partition tables
zerombr

# Protect existing data on all but primary disk.  If you want kickstart to
# spread partitions across all disks, comment this out.  Note that in some
# cases, RHEL/CentOS 7 assigns different hardware to sda during install
# than it does afterward.  (e.g. our PowerEdge systems sda=USB during install
# even when installing via PXE with no USB drives present, sda=1st hard
# disk after install.  --only-use=sdb works in this case.)
ignoredisk --only-use=sda

############################################################################
# Minimal partition scheme suitable for a single-user workstation
# Adjust swap size to taste
#
# Warning: If you do not specify which device(s) to use, kickstart will
# reformat and use all available disks.

# part swap --size=4096
# part / --fstype $boot_fs --size=1024 --grow --ondisk=sda

############################################################################
# Secure server config, allows partitions writable by users
# (/var, /tmp, /home) to be mounted with nosuid.
# Use hda instead of sda for IDE drives

part /boot --fstype $boot_fs --size=1024 --ondisk=sda
part swap --size=4096 --ondisk=sda
part / --fstype $boot_fs --size=32768 --ondisk=sda
part /var --fstype $boot_fs --size=32768 --ondisk=sda --fsoptions=rw,nosuid
part /tmp --fstype $boot_fs --size=32768 --ondisk=sda --fsoptions=rw,nosuid

# For typical machines and head nodes, use all remaining space for /home
# part /home --fstype $boot_fs --size=1024 --grow --ondisk=sda --fsoptions=rw,nosuid

# For nodes with /home NFS-mounted, such as compute nodes, don't allocate
# local disk space for /home.  Use the space for local scratch.
part /bigtmp --fstype xfs --size=1024 --grow --ondisk=sda --fsoptions=rw,nosuid

%post

# Anything done here can more easily be done after the first reboot, so
# I like to keep it minimal.

# Allow passwordless root login from the image server after first boot
sed -i'' 's|#PermitRootLogin yes|PermitRootLogin without-password|g' /etc/ssh/sshd_config
if [ ! -e /root/.ssh/id_rsa.pub ]; then
    ssh-keygen -f /root/.ssh/id_rsa -N ''
fi
printf "$root_key\n" >> /root/.ssh/authorized_keys
chmod 600 /root/.ssh/authorized_keys
restorecon -R -v /root/.ssh     # Set security context for selinux

# Boot into a secure system right off the bat!
# Need to enable em0 first?
yum update -y
yum clean all

%end
EOM
    
	chmod 755 $ksconfig
	sed -i '/%packages/,$d' $ksconfig
fi
    printf "Editing kickstart config file.\n"
    pause

    $EDITOR $ksconfig
    ;;

*)
    auto-unsupported-os $0
    exit 1
    ;;

esac

cat << EOM

Be sure to run auto-pxe-node-discover (from the Node management menu of SPCM)
BEFORE PXE installing new nodes, and complete the DHCP configuration of each
new node before it reboots following install (usually by simply accepting the
defaults offered by auto-pxe-node-discover).  This will permanently reserve
the first IP address granted to node MAC in dhcp.conf.

Otherwise, the DHCP server may create duplicate leases for the same MAC
address on subsequent boots.  This will require stopping the DHCP server and
manually cleaning up the MAC/IP links in dhcp.leases, dhcp.conf, and
/etc/hosts.

EOM
pause
