#!/bin/ksh -p
#
#ident "@(#)utfwadm.sh	1.30 05/06/13 SMI"
#
# Copyright 1998-2004,2005 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#

#
# TBD:
# * make sure incremental add and remove works for different interfaces as
#   well as different units.
# * make all exits clean up properly
#

export PATH=/usr/bin:/usr/ccs/bin:/usr/sbin:/bin:/sbin
# exporting LC_ALL is necessary for non English locales
# Bugid 6275880
# utfwadm fails on non-english locales with "Error: Improper firmware file"
export LC_ALL=C

#Set Platform Dependencies
function SetPlatformDependencies {
  OS=`/bin/uname -s`
  case "$OS" in
                SunOS)	DHCPD=/etc/init.d/dhcp
			INETD_CONF_FILE=/etc/inet/inetd.conf
			;;
		Linux)	DHCPD=/etc/init.d/dhcpd
			# Path for /etc/inetd.conf file on JDS and SuSe
			INETD_CONF_FILE=/etc/inetd.conf
			;;
		*)      error "unknown OS name $OS"
                        exit 2
  esac
}


#
# Function Definitions
#

function Usage {
print "\nUsage:"
print "\t${PROG##*/} -P [ -V ]"
print "\t${PROG##*/} -R"
print "\t${PROG##*/} -A [ -f firmware ] { -n intf | -N subnet | -V }"
print "\t\t{ -a | -e enetAddr } [ -d ] [ -F ] [ -u ] [ -i filename ]"
print "\t${PROG##*/} -D { -n intf | -N subnet | -V } { -a | -e enetAddr }"
print "\nOptions:"
print "\t-A            # add the specified unit(s) to the upgrade list"
print "\t-D            # delete the specified unit(s) from the upgrade list"
print "\t-P            # print version information" 
print "\t-R            # remove firmware modules from boot directory"
print "\t-a            # apply to all units connected to the specific interface"
print "\t              #  or subnet"
print "\t-e enetAddr   # apply to the unit given by the six hex bytes"
print "\t              #  of its ethernet address"
print "\t-n intf       # name of a dedicated network interface to enable upgrades on"
print "\t              #  (e.g., hme0, vge1, etc. \"all\" = all interfaces)"
print "\t-i filename   # append contents of filename to config files"
print "\t-N subnetwork # shared subnetwork address to enable upgrades on"
print "\t-d            # actively disable firmware download (useful with \"-e\")"
print "\t-V            # only generate version files, do not configure DHCP"
print "\t-F            # force firmware load even if downgrading"
print "\t-u            # use frame buffer to do download and decompression"
print "\t-f firmware   # use the firmware described by the path \"firmware\""
print "\t              #  for upgrades on the given network interface(s)"
exit 1;
}

# Verify a dotted IP address
#  usage:  CheckIPA <ipaddr>
# returns a 0 if a good ipa is given, and a 1 otherwise
#
function CheckIPA {
  IP_ADDR=`expr ${1} : '\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\)$'`
  if [ ! "${IP_ADDR}" ]; then
    return 1
  fi
  NNO1=${1%%.*}
  tmp=${1#*.}
  NNO2=${tmp%%.*}
  tmp=${tmp#*.}
  NNO3=${tmp%%.*}
  tmp=${tmp#*.}
  NNO4=${tmp%%.*}
  if [ ${NNO1} -gt 255 -o ${NNO2} -gt 255 -o ${NNO3} -gt 255 -o ${NNO4} -gt 255 ]; then
    return 1
  fi
  return 0
}


# get a list of existing interfaces with DHCP entries
function GetCurrentCfg {
    typeset DHCP_INFO="${TMPDIR}/dhcp_info.$$"
    rm -f $DHCP_INFO 2> /dev/null
    $UTDHCPNET 2>/dev/null  > $DHCP_INFO

    # get interfaces that currently configure to support Sun Ray
    awk -F= '/begin interface/,/end/ {if ($1 == "interface") print $2}' $DHCP_INFO >${TMPDIR}/newtifs.$$     

    # get subnetworks that are currently configured for Sun Ray
    # this does not include the networks for interfaces.
    awk -F= '/begin subnet/,/end/ {if ($1 == "network") print $2}' $DHCP_INFO >${TMPDIR}/allnets.$$
    rm -f $DHCP_INFO
}


#
# HupDHCP - send a HUP signal to the dhcp daemon
#
HupDHCP () {

    $UTDHCPSERVICE restart
    case $? in
    0)	# signal sent succesfully
	print "### reinitialized DHCP daemon"
	;;
    1)	# dhcp is not currently running
	print "#### DHCP daemon not reinitialized because it is not currently running.\
	    \nYou may want to start one manually using \"$DHCPD start\"."
	;;
    *)	# failed with some other error
	print -u2 "Warning: failed to initialize the DHCP daemon.\
	\nYou may want to restart it manually."
	;;
    esac
}

function GetVersion  {
    FW_FILE_INPUT=${1}
    # extract the version string and make file names
    FW_TYPE=$(od -t x1 $FW_FILE_INPUT | head -1 | awk '{ print sprintf("%s%s%s%s", $2,$3,$4,$5)}') 2> /dev/null
    case $FW_TYPE in
	4badbeef | 8badbeef)	
            $UTWHAT $FW_FILE_INPUT > ${TMPDIR}/fw_strings.$$
	    if [ ${?} -ne 0 ]
	    then
		VERSION=""
            	return 1
	    fi
	    VERSION="$(sed -n '/^	version=/s///p' ${TMPDIR}/fw_strings.$$)" 
			;;
	abadbeef)	# compressed, execute in RAM
	    dd if=$FW_FILE_INPUT bs=8 skip=1 2> /dev/null | $LZD > $UC_FW_FILE  2> /dev/null
	    $UTWHAT $UC_FW_FILE  > ${TMPDIR}/fw_strings.$$
	    if [ ${?} -ne 0 ]
	    then
		VERSION=""
	        rm -f $UC_FW_FILE
		return 1
	    fi
	    VERSION="$(sed -n '/^	version=/s///p' ${TMPDIR}/fw_strings.$$)" 2>/dev/null
	    rm -f $UC_FW_FILE
			;;
	*)	print -u2 "Error: Unknown firmware module type (magic number=\"$FW_TYPE\")."
	    exit 1;
		;;
    esac

}


#
# Normalize Ethernet address for use with DHCP
#
function normalizeEther {

	# Some people will insist on entering the colons
	case "$ETHER" in
	*[0-9A-F]:[0-9A-F]*)
		# Get rid of colons
		SAVEIFS=$IFS
		IFS=:			# colon is separator
		for BYTE in $ETHER
		do
			case $BYTE in
			?)
				# single digit gets leading zero
				NETHER=${NETHER}0$BYTE
				;;
			*)
				NETHER=${NETHER}$BYTE
				;;
			esac
		done
		IFS=$SAVEIFS
		ETHER=$NETHER
		;;
	esac
	case $ETHER in
	[0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F][0-9A-F]) # ok, exactly 12 hex characters
		;;
	*)
		print -u2 "Error: Invalid ethernet address: \"$ETHER\""
		Usage;
		;;
	esac
}

#
# check if we need to uncomment the tftp line in inetd.conf
#
function updateInetd {
# It is either /etc/inetd.conf for JDS/SuSe OR
# It is /etc/inet/inetd.conf for Solaris OR
# /etc/xinetd.d/tftp file for RedHat

    INETCONV=/usr/sbin/inetconv

    if [ -f $INETD_CONF_FILE ]; then
    	typeset RESULT=$(grep "^#[ 	]*tftp[ 	]*dgram[ 	]*udp\
.*\/tftpboot[ 	]*" $CONF)
    	if [ -n "$RESULT" ]
    	then
		TEMP=$CONF.$$
		rm -f $TEMP
		trap "rm -f $TEMP 2>/dev/null" 0 1 2
		cp -p $CONF $TEMP
		chmod u+w $TEMP
		ed $TEMP <<-2HERE2 >/dev/null
		g/^#[ 	]*tftp[ 	]*dgram[ 	]*udp.*\/tftpboot[   ]*/s/^#[ 	]*tftp/tftp/p
		w
		q
		2HERE2
		chmod u-w $TEMP
		mv $TEMP $CONF
	fi
    else
    	typeset RESULT=$(awk -F= '/disable/ {print $2}' $CONF)
        if [ "$RESULT" == " yes" ]; then
    		TEMP=$CONF.$$
		sed '/disable/ s/yes/no/' $CONF > $TEMP
                mv $TEMP $CONF
	else RESULT=""
	fi
   fi

    if [ -n "$RESULT" ]
    then
	if [ -x $INETCONV ]
    	then
		$INETCONV -i $CONF 1>/dev/null 2>&1
	else
    		# restart the inetd
    		pkill -HUP $INETD
	fi
	if [ $? = 0 ]
	then
		print "### Successfully enabled tftp for firmware downloads"
	else
		print "### Please ensure tftp is enabled for firmware downloads"
	fi
    fi
}

#Generates the Subnet/Interface block for newtver
function GenerateFWVersion {
                print "begin $1"
		if [ $1 == "subnet" ]; then
                	print "network=$2"
		elif [ $1 == "interface" ]; then
			print "interface=$2"
		fi
                print "newtver=$3"
                print "end"
}

#Generates the ether block for newtver
function GenerateFWVersion_ether {
                print "begin ether"
		print "macaddress=$1"
		if [ $# == 2 ]; then
			print "NewTVer=$2"
		fi
		print "end"
}

#Process utdhcpopt output to check if ether block is to be removed
function ProcessOptions {
                inputfile=$1
               	found=0
                while read input
                do
                        case $input in
                        (begin*)
                		options=0
                		fwset=0
                                ;;
                        (end)
                                if [ $found -eq 1 ]
                                then
                                        break;
                                fi
                                ;;
                        (macaddress=*)
                                ETHER=${input#macaddress=}
                                if [ $ETHER == $2 ]
                                then
                                        found=1
                                fi
                                ;;
                        (newtver=*)
                                fwset=1
                                ;;
                        (*)
                                options=`expr $options + 1`
                                ;;
			esac
		done    < $inputfile
		if [ $found -eq 1 -a $fwset -eq 1  -a $options -eq 0 ]
                then
		# Only newtver option was found
			return 0
		elif [ $found -eq 1 -a $fwset -eq 1 -a $options -gt 0 ]
		then 
		# Other options besides newtver were found
			return 1
		else
		# Mac address was not found or newtver not set on this mac
			return 2
		fi
}


#
# Global Variables
#

OS=""		#OS Name
WHICH=""	# One or all selection; "all" or "one"
MODE=""		# Major program option; "add" or "delete"
typeset -x -u ETHER=""	# Ethernet address of a particular terminal
INTFS_OPT=""	# the interface option before validity is checked
INTFS=""	# List of network interfaces
SUBNETS=""	# List of subnetworks
PROD_NAME="Corona"	# The name of the product
FW_PATH=""	# path to the firmware file(s)
FW_DIR=""	# directory of where the firmware file(s) live
FW_NAMES=""	# list of the name(s) of the firmware file(s) to use
TMPDIR="/var/opt/SUNWut/tmp"
UC_FW_FILE=${TMPDIR}/tmpfile.$$
UT_ETC="/etc/opt/SUNWut";
UT_DHCP_BASEDIR=`(cd ${UT_ETC}/dhcp ; /bin/pwd)`
UTDHCPNET="$UT_DHCP_BASEDIR/utdhcpnet"
UTDHCPOPT="$UT_DHCP_BASEDIR/utdhcpopt"
UTDHCPSERVICE="$UT_DHCP_BASEDIR/utdhcpservice"
USEFB="false"
DISABLE="false"
FORCE="false"
VONLY=false
PARMOPTS=false
TYPECNT=0
INCLUDE=

#
# MAIN
#

trap "rm -f ${TMPDIR}/*.$$ 2>/dev/null; exit" 0 1 2 3 9 15

SetPlatformDependencies

if [ -f $INETD_CONF_FILE ]
then
	CONF=$INETD_CONF_FILE
	BOOTDIR=$(grep 'in\.tftpd.*/' $CONF | awk '{print $NF}' -)
	INETD=inetd
# RedHat
elif [ -f /etc/xinetd.d/tftp ]
then
	CONF=/etc/xinetd.d/tftp
	BOOTDIR=$(awk '{if ($1 == "server_args") print $NF}' $CONF )
    	INETD=xinetd
else
	print "Unsupported platform "
	exit 1
fi
if [ -z "$BOOTDIR" -o ! -d "$BOOTDIR" ]
then
	BOOTDIR="/tftpboot"
fi
PROG=$0

# check if we're root
if [ $(IFS='=(';set $(id) ; print $2) -ne 0 ]
then
	print -u2 "Error: must have super user root privileges\n"
	exit 1;
fi
BASEDIR=/etc/opt/SUNWut/basedir
SUNWUT=`${BASEDIR}/lib/utprodinfo -r SUNWuto 2>/dev/null`/SUNWut
if [ ${?} -ne 0 ]
then
  print -u2 "Error: SUNWuto package must be installed"
  exit 1;
fi
FWLIBDIR=$SUNWUT/lib/firmware
BINDIR=$SUNWUT/bin
LZD=$SUNWUT/lib/lzd
UTWHAT=$SUNWUT/lib/utwhat
DODHCP="Y"

GetCurrentCfg;

# Test to make sure there are arguments for the script
while getopts ADFN:PRVade:f:hi:n:uz NAME 2>/dev/null
do
	case $NAME in
	A) # add the specified unit(s) to the upgrade list
		if [ -n "$MODE" ]
		then
			print -u2 "Error: can only specify -A, -D, -R or -P"
			Usage;
		fi
		MODE=add
		;;
	D) # delete the specified unit(s) from the upgrade list
		if [ -n "$MODE" ]
		then
			print -u2 "Error: can only specify -A, -D, -R or -P"
			Usage;
		fi
		MODE=delete
		;;
	F) # force download even if this is a downgrade
		FORCE=true
		PARMOPTS=true
		;;
	P) # print information about NewTVer values
		if [ -n "$MODE" ]
		then
			print -u2 "Error: can only specify -A, -D, -R or -P"
			Usage;
		fi
		MODE=prt
		;;
	R) # remove firmware files from $BOOTDIR
		if [ -n "$MODE" ]
		then
			print -u2 "Error: can only specifiy -A, -D, -R or -P"
			Usage;
		fi
		MODE=clean
		;;
	a) # apply to all units
		if [ -n "$WHICH" ]
		then
			print -u2 "Error: conflicting -a and -e options"
			Usage;
		fi
		WHICH=all
		;;
	d) # disable download
		DISABLE=true;
		PARMOPTS=true
		;;
	e) # apply to the unit given by the six hex bytes of its EA
		if [ -n "$WHICH" ]
		then
			print -u2 "Error: conflicting -a and -e options"
			Usage;
		fi
		WHICH=one
		ETHER="$OPTARG"
		;;
	f) # use \"firmware\" as pathname for the module to use
	   	FW_PATH="$OPTARG"
		;;
	i) # append given file name to config file
		INCLUDE="$OPTARG"
		PARMOPTS=true
		;;
	n) # a network interface name to enable upgrades on (allow multiple)
		INTFS_OPT="$OPTARG"
		let "TYPECNT = TYPECNT + 1"
		;;
	N) # a subnetwork address to enable upgrades on (allow multiple)
		if [ $OPTARG = "all" ]; then
			if [ ! -s ${TMPDIR}/allnets.$$ ]; then
				# WARNING - do not change this error message,
				# without also modifying 2.0 patch postpatch
				# script which parses it
				print -u2 "Warning: no subnetworks configured - no action taken"
				exit 0
			fi
			SUBNETS=`cat ${TMPDIR}/allnets.$$`
		else
			SUBNETS="$SUBNETS $OPTARG"
		fi	
		rm -f ${TMPDIR}/allnets.$$
		let "TYPECNT = TYPECNT + 1"
		;;
	h)
		Usage
		;;
	u) # set usefb flag	
		USEFB=true
		PARMOPTS=true
		;;
	z)
		DODHCP="N"
		;;
	V) # only do version files
		VONLY=true
		let "TYPECNT = TYPECNT + 1"
		;;
	?)
		Usage
		;;
	esac
done

if [[ $OPTIND -le $# ]]; then
	print -u2 "Error: unexpected argument encountered"
	Usage
fi

if [ "$MODE" != "add" -a "$PARMOPTS" = "true" ]
then
	print -u2 "Error: Options that relate to .parms files must only be"
	print -u2 "	  specified with -A"
	Usage
fi

if [ "$MODE" != "add" -a "$MODE" != "prt" -a "$MODE" != "delete" -a \
	"$VONLY" = "true" ]
then
	print -u2 "Error: -V must be specified only with -A, -P, or -D"
	Usage
fi

# validate the input
case $MODE in
add | delete)
	# check add/delete option
	case "$WHICH" in
	all)
		if [ $TYPECNT -ne 1 ]
		then
			print -u2 "Error: Only one of -n, -N, or -V may be specified."
			Usage
		fi
		;;
	one)
		if [ -z "$ETHER" ]
		then
			print -u2 "Error: Must specify the ethernet address for the unit"
			Usage
		else
			if [ -n "$SUBNETS" ]
			then
				print -u2 -- "-N subnet option ignored.  It is no longer"\
					"required with -e option."
			elif [ -n "$INTFS_OPT" ]
			then
				print -u2 -- "-n interface option ignored.  It is no longer"\
					"required with -e option."
			fi
		fi
		normalizeEther
		;;
	"")
		print -u2 "Error: Must specify the unit to be affected"
		Usage
		;;
	esac

	# if delete operation, no -f option
	if [ $WHICH = "delete" -a -n "$FW_PATH" ]
	then
		print -u2 "Error: -f option not allowed with delete operation."
		Usage
	fi
	;;
prt | clean)
	# check print/remove options
	if [ -n "$WHICH" -o -n "$FW_PATH" -o -n "$SUBNETS" -o -n "$INTFS_OPT" ]
	then
		print -n -u2 "Error: invalid option specified for "
		if [ $MODE = "prt" ]
		then
			print -u2 -- "-P"
		else
			print -u2 -- "-R"
		fi
		Usage
	fi
	;;
"")
	print -u2 "Error: Must specify either -A, -D, -R or -P.\n"
	Usage
	;;
esac


if [ "$MODE" = "prt" ]
then
	NETWORK_INFO=${TMPDIR}/utdhcpnet.$$
	$UTDHCPNET > ${NETWORK_INFO} 2>/dev/null
	GetVersion $BOOTDIR/CoronaP1
	rm -f ${TMPDIR}/fw_strings.$$
	if [ -z "$VERSION" ]
	then
            	print -u2 "Error: Unable to get firmware version."
          	exit 1
	fi
	FW_VERSION="${VERSION#*-}"
	print "System Version(P1)     " $FW_VERSION
	print
	if [ ! -s ${NETWORK_INFO} ]; then
		print "Sun Ray interconnect framework is not configured";
		rm -f ${NETWORK_INFO}
          	exit 1;

	fi
	print 	 "Domain          Intf    Upgrade to"
	print -- "------------    ------  --------------------------"
	awk -F= '
			BEGIN   { domain = ""; intf = ""; ver = "" }
			/^begin subnet/,/^end/ {
				if ($1 == "network")
					domain = $2
				if ($1 == "newtver")
					ver = substr($0,index($0,"=")+1)
				if ( $1 == "end" ) {
					printf "%-16s%-8s%s\n", domain, "subnet", ver;
					domain = "";
					ver = "";
				}
			}
			/^begin interface/,/^end/ {
				if ($1 == "network")
					domain = $2
				if ($1 == "interface")
					intf = $2
                                if ($1 == "newtver")
                                        ver = substr($0,index($0,"=")+1)
				if ( $1 == "end" ) {
                                        printf "%-16s%-8s%s\n", domain, intf, ver;
                                        domain = "";
                                        intf = "";
                                        ver = "";
				}
			}'	${NETWORK_INFO}
	rm -f ${NETWORK_INFO}
	$UTDHCPOPT | awk -F= '
			BEGIN { mac = ""; ver = "" }
			/begin ether/,/end/ {
				if ($1 == "macaddress")
					mac = $2
				if ($1 == "newtver") {
					ver = substr($0,index($0,"=")+1);
				}
				if ( (mac != "") && (ver != "") ) {
					printf "%-16s%-8s%s\n", mac, "ether", ver;
					mac = "";
					ver = "";
				}
			}'

	if [ "$VONLY" = "true" ]
	then
		first=""
		print
		for i in `ls $BOOTDIR/*.parms | sed -n '/^[^.]*\.[^.]*$/p'`
		do
			if [ -z $first ]
			then
				print "Version files:"
				printf "%-16.16s %s" "Model" "Version"
				print
				first=done
			fi
			FILE=`basename $i .parms`
			VERS=`sed -n '/version=/s///p' $i`
			printf "%-16.16s %s\n" "$FILE" "$VERS"
		done 2>/dev/null
		for i in `ls $BOOTDIR/*P1.*.parms`
		do
			if [ -z $first ]
			then
				print "Version files:"
				printf "%-16.16s %s" "Model" "Version"
				print
				first=done
			fi
			FILE=`basename $i .parms`
			FILE=`print $FILE | sed 's/^[^.]*\.//'`
			VERS=`sed -n '/version=/s///p' $i`
			printf "%-16.16s %s\n" "$FILE" "$VERS"
		done 2>/dev/null

		if [ "$first" != "done" ]
		then
			print -u2 "No .parms files present"
		fi
	fi

	exit 0
fi

if [ "$MODE" = "clean" ]
then
	rm -f $BOOTDIR/$PROD_NAME*
	rm -f $BOOTDIR/SunRay*
	rm -f $BOOTDIR/rewrite.*
	rm -f $BOOTDIR/view
	rm -f $BOOTDIR/view.*
	rm -f $BOOTDIR/nukeview
	rm -f $BOOTDIR/*.parms
	exit 0
fi

if [ "$WHICH" = "all" ]
then
    if [ -n "$INTFS_OPT" ]; then
	if [ $INTFS_OPT = "all" ]; then
		if [ ! -s ${TMPDIR}/newtifs.$$ ]; then
			# WARNING - do not change this error message,
			# without also modifying 2.0 patch postpatch
			# script which parses it
			print -u2 "Warning: no private interconnect interfaces configured - no action taken"
			exit 0
		fi
		INTFS=`cat ${TMPDIR}/newtifs.$$`
	else
		for Intf in `cat ${TMPDIR}/newtifs.$$`
		do
			if [[ "$INTFS_OPT" == "$Intf" ]]; then
				INTFS="$INTFS_OPT"
				break
			fi
		done
		if [ -z "$INTFS" ]; then
			print -u2 \
			"\nError: Interface \"${INTFS_OPT}\" is not currently configured as a dedicated\n" \
			"interconnect.  You must configure the interface as a dedicated interconnect\n" \
			"before using this command to configure the firmware.  If you are trying to\n" \
			"configure the firmware for a shared network, please use the -N option.\n"

			Usage
			exit 1
		fi
	fi	
	rm -f ${TMPDIR}/newtifs.$$
    fi

    # only -a option requires the interface name or subnet address
    if [ -n "$INTFS" ]
    then
	for INTF in $INTFS
	do 
		INTF_INFO=$(ifconfig $INTF);
		if [ ${?} -ne 0 ] || [ -z "${INTF_INFO}" ]
		then
			print -u2 "Error: Interface \"$INTF\" not found"
			exit 1
		fi
	done
    elif [ -n "$SUBNETS" ]
    then
	for NET in $SUBNETS
	do
		CheckIPA ${NET}
		if [ ${?} -ne 0 ]; then
			print -u2 "Error:invalid subnetwork address: \"${NET}\""
			exit 1
		fi
	done
    fi
fi

#
# This section sets up the firmware information.  This consists of retrieving the
# firmware filename and creating the links in /tftpboot directory.  This is only
# done for the adding operation. 
#
if [ $MODE == "add" ]
then
    # get the firmware file(s) to use
    if [ -n "$FW_PATH" -a ! -d "$FW_PATH" ]
    then
	# user specified firmware path, so check it out
	FW_DIR="$(dirname "$FW_PATH")"
	if [ "$FW_DIR" == "." ]
	then
		print -u2 "Error: Must provide full pathname for alternative firmware"
		Usage
	fi
	if [ ! -d "$FW_DIR" ]
	then
		print -u2 "Error: Firmware directory \"$FW_DIR\" not found"
		Usage
	fi

	FW_NAMES="$(basename "$FW_PATH")"
	if [ ! -f "$FW_DIR/$FW_NAMES" ]
	then
		print -u2 "Error: Firmware file \"$FW_DIR/$FW_NAMES\" not found"
		Usage
	fi
    else
	# use the system firmware location and get the firmware files
	if [ -d "$FW_PATH" ]
	then
		FW_DIR="$FW_PATH"
	else
		FW_DIR="$FWLIBDIR"
	fi
	if [ ! -d "$FW_DIR" ]
	then
		print -u2 "Error: Firmware directory \"$FW_DIR\" not found"
		Usage
	fi
	FW_NAMES=""
	for FW_NAME in $(ls $FW_DIR/*)
	do
		FW_NAMES="$FW_NAMES $(basename "$FW_NAME")"
	done
    fi

    COMPARE_FW_VERSION=""
    fw_version_mismatch=0
    for FW_NAME in $FW_NAMES
    do
	# check the firmware file's validity
	FW_FILE="$FW_DIR/$FW_NAME"
	if [ ! -r "$FW_FILE" ]
	then
		print -u2 "Error: Firmware file \"$FW_FILE\" not accessible"
		Usage
	fi
	TYPE=$(file $FW_FILE | awk '{print $2}')
	if [ "$TYPE" != "data" ]
	then
		print -u2 "Error: Improper firmware file \"$FW_FILE\"."
		Usage
	fi

	# extract the version string and make file names
	GetVersion $FW_FILE
	if [ -z "$VERSION" ]
	then
		print -u2 "Error: Unable to get firmware version from file \"$FW_FILE\"."
		continue
	fi
	FW_BASENAME="${VERSION%%-*}"
	FW_VERSION="${VERSION#*-}"
	if [ -z "$COMPARE_FW_VERSION" ]
	then
		COMPARE_FW_VERSION=$FW_VERSION
	else
		if [ "$FW_VERSION" != "$COMPARE_FW_VERSION" ] && [ $fw_version_mismatch -eq 0 ]
		then
			fw_version_mismatch=1
		fi
	fi

	# Now set up the version file

	if [ -n "$ETHER" ]
	then
		VFILE=$FW_BASENAME.$ETHER.parms
	else
		VFILE=$FW_BASENAME.parms
	fi

	(umask 0022 ; mkdir -p $BOOTDIR)

	# Actively disable firmware download 

	if [ "$DISABLE" = "true" ]
	then
		print "version=_NONE_" >$BOOTDIR/$VFILE
		FW_VERSION=_NONE_
		continue	# No point copying files
	else
		if [ $VONLY = "true" ]
		then
			printf "%-12.12s %s\n" $FW_BASENAME $FW_VERSION
		fi
		print "version=${FW_VERSION}" >$BOOTDIR/$VFILE
		egrep 'revision=|barrier=' ${TMPDIR}/fw_strings.$$ | \
		    sed -e 's/^[	 ]*//' -e 's/ *$//' >> $BOOTDIR/$VFILE
		if [ $FORCE = "true" ]
		then
			sed -n '/^[	 ]*barrier=/s//BarrierLevel=/p' \
			    ${TMPDIR}/fw_strings.$$ >> $BOOTDIR/$VFILE
		fi
		rm -f ${TMPDIR}/fw_strings.$$

		# set usefb flag if defined
		if [ "$USEFB" = "true" ]
		then
			print "usefb=$USEFB" >> $BOOTDIR/$VFILE
		fi

		# append additional options if supplied
		if [ -n "$INCLUDE" ]
		then
			# only accept lines with valid looking options
			egrep '^servers=|^select=random$|^select=inorder$' \
				"$INCLUDE" >> $BOOTDIR/$VFILE
		fi
	fi

	# copy the fw to bootdir with ugly name
	cp -p "$FW_FILE" "$BOOTDIR/$VERSION"
	if [ ${?} -ne 0 ]
	then
		print -u2 "Error: unable to copy \"$FW_FILE\" to \"$BOOTDIR/$VERSION\""
		exit 1
	fi

	# determine if this is a unit-specific operation
	if [ $WHICH == "all" ]
	then
		# link the general name to the ugly name
		# N.B.:  target of symlink in tftpboot directory must
		# not have a leading '/'
		rm -f "$BOOTDIR/$FW_BASENAME"
		ln -s "$VERSION" "$BOOTDIR/$FW_BASENAME"
		if [ ${?} -ne 0 ]
		then
			print -u2 "Error: unable to link \"$VERSION\" to \"$FW_BASENAME\""
			exit 1
		fi
	else
		# link the specific name to the real ugly name
		# N.B.:  target of symlink in tftpboot directory must
		# not have a leading '/'
		rm -f "$BOOTDIR/$FW_BASENAME.$ETHER"
		ln -s "$VERSION" "$BOOTDIR/$FW_BASENAME.$ETHER"
		if [ ${?} -ne 0 ]
		then
			print -u2 "Error: unable to link \"$VERSION"\" to \"$FW_BASENAME.$ETHER"\""
			exit 1
		fi
	fi
    done
    if [ $fw_version_mismatch -eq 1 -a "$VONLY" = "false" ]
    then
	print -u2 "\tWarning: More than one version of firmware has been detected." 
	print -u2 "\tIt is recommended that all models have the same firmware version"
	print -u2 "\tand only one file for each model be present in "
	print -u2 "\t$FW_DIR.\n"
    fi
    updateInetd		# update the inetd.conf file
else # delete
    # Get rid of all specific files
    # Leave the actual firmware files there, as is currently done
    if [ -n "$ETHER" ]
    then
	rm -f $BOOTDIR/*.$ETHER.parms
    else
	rm -f `ls $BOOTDIR/*.parms | sed -n '/^[^.]*\.[^.]*$/p'`
    fi
fi

# Done if no DHCP to do
if [ "$VONLY" = "true" ]
then
    exit 0
fi

#
# This section updates the info in the DHCP table.  This consists of updating either
# the interface or subnet Macro in the DHCP table with the proper firmware.
#
if [ "$WHICH" = "all" ]
then
    # for each given network interface do either add or remove of the defined units
    for INTF in $INTFS $SUBNETS
    do
	IsSubnet=false
	IsIntf=false
	CheckIPA $INTF
	# if failed this means the INTF is an interface, otherwise a subnetwork
	if [ $? -eq 0 ]; then
		if [ ${?} -ne 0 ]
		then
			print -u2 "Error: unable to get information on the primary interface"
			exit 1;
		fi
		INTF_NET=${INTF}
		IsSubnet=true
	else 
		# get info on the interface
		IsIntf=true
		INTF_NET=$($UTDHCPNET | awk -F= '/begin interface/,/end/ {if ($1 == "network") print $2}')
		if [ ${?} -ne 0 ]
		then
			print -u2 "Error: unable to get information on interface \"$INTF\"";
			exit 1;
		fi
	fi

	# set the version option for the selected unit(s)
	if [ $MODE == "add" ]
	then
		# upgrade all units by putting the version option
		# in the network macro
		if $IsSubnet ; then
			GenerateFWVersion "subnet" $INTF_NET $FW_VERSION | $UTDHCPNET change 1>/dev/null 2> ${TMPDIR}/Err.$$
		elif $IsIntf ; then 
			GenerateFWVersion "interface" $INTF $FW_VERSION | $UTDHCPNET change 1>/dev/null 2> ${TMPDIR}/Err.$$
		fi
		if [ ${?} -ne 0 ]
		then
			print -u2 "Error: cannot set firmware version to network macro \"$INTF_NET\":"
			print -u2 $(cat ${TMPDIR}/Err.$$)
			rm -f ${TMPDIR}/Err.$$;
			exit 1;
		fi
		rm -f ${TMPDIR}/Err.$$;

		# print successfully done message
		print "\tAll the units served by \"$(uname -n)\" on the $INTF_NET"
		print "\tnetwork interface, running firmware other than version"
		print "\t\"$FW_VERSION\" will be upgraded at their next power-on.\n"
	else
		# disable upgrade(s) for selected unit(s)
		# remove fw version
		if $IsSubnet ; then
			GenerateFWVersion "subnet" $INTF_NET "" | $UTDHCPNET change 1>/dev/null 2> ${TMPDIR}/Err.$$;
		elif $IsIntf ; then
			GenerateFWVersion "interface" $INTF "" | $UTDHCPNET change 1>/dev/null 2> ${TMPDIR}/Err.$$;
		fi
			
		if [ ${?} -ne 0 ]
		then
			print -u2 "Error: cannot remove version option from \"$INTF_NET\" macro:"
			print -u2 $(cat ${TMPDIR}/Err.$$)
			rm -f ${TMPDIR}/Err.$$;
			exit 1;
		fi
		rm -f ${TMPDIR}/Err.$$;
		print "\tAutomatic firmware upgrade is disabled for all the units served by "
		print "\t\"$(uname -n)\" on the $INTF_NET network interface.\n"
	fi
    done
else
# upgrade a specific unit, check if unit macro exists
    if [ $MODE == "add" ]
    then
         # add/upgrade a specific unit, check if unit macro exists
	if [ -z "$($UTDHCPOPT | grep "$ETHER")" ]
	then
		# doesn't exist, so create it
		GenerateFWVersion_ether $ETHER $FW_VERSION | $UTDHCPOPT add 1>/dev/null 2> ${TMPDIR}/Err.$$
		if [ ${?} -ne 0 ]
		then
			print -u2 "Error: cannot create macro \"$ETHER\":"
			print -u2 $(cat ${TMPDIR}/Err.$$)
			rm -f ${TMPDIR}/Err.$$;
			exit 1;
		fi
	else # modify individual unit's macro
		GenerateFWVersion_ether $ETHER $FW_VERSION | $UTDHCPOPT change 1>/dev/null 2> ${TMPDIR}/Err.$$
		if [ ${?} -ne 0 ]
		then
			print -u2 "Error: cannot set firmware version option to \"$ETHER\" macro:"
			print -u2 $(cat ${TMPDIR}/Err.$$)
			rm -f ${TMPDIR}/Err.$$;
			exit 1;
		fi
	fi
	rm -f ${TMPDIR}/Err.$$;
	# print successfully done message
	print "\tUnit \"$ETHER\" will be upgraded at its next power-on"
	print "\tif it is served by host \"$(uname -n)\" and is connected to"
	print "\tthe $INTF_NET network and is not already running firmware"
	print "\tversion \"$FW_VERSION\".\n"
   else
	# remove the fw version from the given individual unit's macro
	ETHER_OPTIONS=${TMPDIR}/ether_options.$$
	$UTDHCPOPT list ether >${ETHER_OPTIONS}
	if [ ! -s ${ETHER_OPTIONS} ]
	then
		print "\tNo firmware is currently configured for \"${ETHER}\""
		rm -f ${ETHER_OPTIONS}
		exit 0
	fi

	ProcessOptions ${ETHER_OPTIONS} $ETHER
	options=$?
	rm -f ${ETHER_OPTIONS}
	if [ $options -eq 0 ]
	then
		# delete the current macro
		GenerateFWVersion_ether $ETHER | $UTDHCPOPT delete 1>/dev/null 2> ${TMPDIR}/Err.$$
		if [ ${?} -ne 0 ]
		then
			print -u2 "Error: cannot remove \"$ETHER\" macro:"
			print -u2 $(cat ${TMPDIR}/Err.$$)
			rm -f ${TMPDIR}/Err.$$;
			exit 1;
		fi
		# print successfully done message
		print "\tAutomatic firmware upgrade is disabled for \"${ETHER}\"".
	elif [ $options -eq 1 ]
	then
		# Change the current macro
		GenerateFWVersion_ether $ETHER "" | $UTDHCPOPT change 1>/dev/null 2> ${TMPDIR}/Err.$$
		if [ ${?} -ne 0 ]
		then
			print -u2 "Error: cannot create the \"$ETHER\" macro as $MACRO:"
			print -u2 $(cat ${TMPDIR}/Err.$$)
			rm -f ${TMPDIR}/Err.$$;
			exit 1;
		fi
		# print successfully done message
		print "\tAutomatic firmware upgrade is disabled for \"${ETHER}\"".
	else
		print "\tNo firmware is currently configured for \"${ETHER}\""
		exit 0
	fi
	rm -f ${TMPDIR}/Err.$$;
	rm -f ${BOOTDIR}/*.${ETHER}
    fi
fi


#### temp hack for backward compatibility
if [ "$MODE" == "add" ]
then
	REWR1="$FWLIBDIR/rewrite.sim"
	REWR2="$FWLIBDIR/rewrite.csi"
	if [ -f "$REWR1" -o -f "$REWR2" ]
	then
		rm -f "$BOOTDIR/view"
		rm -f "$BOOTDIR/view.*"
		rm -f "$BOOTDIR/nukeview"
		cp "$REWR1" "$BOOTDIR"
		cp "$REWR2" "$BOOTDIR"
		ln -s "rewrite.sim" "$BOOTDIR/nukeview"
	fi
fi
####

if [ $DODHCP = "Y" ]; then
	HupDHCP
fi

exit 0;
