#!/bin/bash
#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#

#
# Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#
# ident	"%Z%%M%	%I%	%E% SMI"
#

#
# Known issue: if you clone an ipkg zone but choose a different locale
# from the cloned zone, the locale package may not be installed in the
# new zone.
#

ipkg_packages=""
#Old definition...
#ipkg_dash_o="SUNWftp SUNWrcmds SUNWsshd"
#New definition, note no FTP server, as it was obsoleted.
ipkg_dash_o="network-servers service/network/ssh"
def_ufs_zone_path=/export/home/zones
def_zfs_zone_path=/zones
# Source zone for ZFS cloning, empty means do not clone
clone_src=""
zone_base=""
zone_add_cfg=""
zone_ns=NONE
zone_blank=""
# Last index assigned
vnic_last=""
# Index for vnic auto name generation
vnic_auto=""
vnic_cnt=0
ip_type="none"
intf_cnt=0
wait_confirm=0
# Additional arguments for the
zone_inst_args=""
bar=\
"------------------------------------------------------------------------------"

usage () {
cmd=`basename $0`
cat <<-EOF
Usage: $cmd -z <zone_name> [options]

 Options supported:
   -B          Create the zone with the specified brand
   -C <zone>	Create the new zone by cloning the specified zone.
   -b          Start with a blank zone config
   -c <config> Add a line to the zone configuration
   -e <pkgs>	Comma separated list of packages to install (ipkg zone only)
   -h          Prints this usage message
   -l <locale>	Sets the locale for the zone (defaults to \$LANG)
   -i <ifspec> Configure an ip-type shared interface  (OBSOLETE)
   -n          Name service, one of NIS, DNS, NONE (default)
   -o          Open the zone up for testing *NOT SECURE*
   -p <path>   Base path for zones
   -w          Prompt for confirmation before installing zone
   -v <vnspec> Create a VNIC on the specified device
   -x <ifspec> Configure an ip-type exclusive interface

 Specifying interfaces:
   <ifspec> is a comma separated lists of assignments or keywords:
     dev=<interface name, ex. e1000g1>
     dhcp  (-x only)
     ip=<IP address>[/<mask>]
     ipv6
     ipv6=<IPv6 address>/<mask>  (-i only)
     name=<hostname>  (-x only)
     route=<default route ip>  (-x only)
   No whitespace is permitted between arguments.
   Since both flags set ip-type, -i and -x are mutually exclusive.
   The special value "VNIC" for dev uses the last vnic created via -v.
   Keyword ipv6 automatically generates a link-local address.

 Specifying VNICs:
   <vnspec> is a comma separated lists of assignments:
     name=<name, defaults to vnic#>
     dev=<interface name, ex. e1000g1>
     mac=<hardware address>
   No whitespace is permitted between arguments.
   MAC address is optional; by default, a random MAC is generated.
EOF
}

lindex () {
	typeset -a arr=($1)
	echo ${arr[$2]}
}

log_err () {
	echo "ERROR: $*" >&2
}

log_warn () {
	echo "WARNING: $*" >&2
}

new_interface () {
	for setting in ${1//,/ }; do
		case $setting in
			dev=*)
				intf_devs[$intf_cnt]=${setting/dev=/}
				;;
			dhcp)
				intf_ips[$intf_cnt]=DHCP
				;;
			ip=*)
				setting=${setting/ip=/}
				intf_ips[$intf_cnt]=${setting%%/*}
				if [[ "${setting%%/*}" != "${setting##*/}" ]]; \
				    then
					intf_masks[$intf_cnt]=${setting##*/}
				fi
				;;
			ipv6=*)
				intf_ipv6s[$intf_cnt]=${setting/ipv6=/}
				;;
			ipv6)
				#
				# Set a special case for just the link-local
				# address.
				#
				if [[ ${intf_ipv6s[$intf_cnt]} == "" ]]; then
					intf_ipv6s[$intf_cnt]="-"
				fi
				;;
			name=*)
				intf_names[$intf_cnt]=${setting/name=/}
				;;
			route=*)
				intf_routes[$intf_cnt]=${setting/route=/}
				;;
		esac
	done

	#
	# VNIC is a special case to use the last vnic configured via -v.
	#
	if [[ "${intf_devs[$intf_cnt]}" == "VNIC" ]]; then
		if [[ -z "$vnic_last" ]]; then
			log_err "for intf <$1>, dev=VNIC but no prior -v option"
			exit 1
		fi
		intf_devs[$intf_cnt]=${vnic_names[$vnic_last]}
	fi

	#
	# Name defaults to the zone name for the first interface.
	#
	if [[ $intf_cnt -eq 0 ]] && [[ "${intf_names[$intf_cnt]}" == "" ]]; then
		intf_names[$intf_cnt]=$zone_name
	fi

	#
	# If a name was specified, but no IP was given, try looking up the name.
	#
	if [[ "${intf_ips[$intf_cnt]}" == "" ]]  && \
	    [[ "${intf_names[$intf_cnt]}" != "" ]] ; then
		temp=`getent hosts ${intf_names[$intf_cnt]}`
		if [[ $? -eq 0 ]] && [[ "$temp" != "" ]]; then
			intf_ips[$intf_cnt]=`lindex "$temp" 0`
		fi
	fi

	#
	# Special case: if no device is supplied for the first interface,
	# use the outgoing interface for the system's default route.
	#
	if [[ "${intf_devs[$intf_cnt]}" == "" ]]; then
		if [[ $intf_cnt -eq 0 ]] && [[ "$ip_type" == "shared" ]]; then
			temp=`route -n get default | grep interface`
			temp=`lindex "$temp" 1`
			intf_devs[0]=$temp
		else
			log_err "Interface <$1>: missing device"
			exit 1
		fi
	fi
	((intf_cnt++))
}

new_vnic () {
	for setting in ${1//,/ }; do
		case $setting in
			name=*)
				vnic_names[$vnic_cnt]=${setting/name=/}
				;;
			dev=*)
				vnic_devs[$vnic_cnt]=${setting/dev=/}
				;;
			mac=*)
				vnic_macs[$vnic_cnt]=${setting/mac=/}
				;;
		esac
	done

	# If no name was given for the VNIC, generate one automatically.
	if [[ "${vnic_names[$vnic_cnt]}" == "" ]]; then
		# Find the first available vnic# name
		if [[ -z "$vnic_auto" ]]; then
			vnic_auto=`dladm show-vnic | tail -1 | sed \
			    's/vnic\([0-9][0-9]*\).*$/\1/g'`
			if [[ "$vnic_auto" == "" ]]; then
				vnic_auto=0
			else
				((vnic_auto++))
			fi
		fi
		vnic_names[$vnic_cnt]=vnic$vnic_auto
		((vnic_auto++))
	fi

	if [[ "${vnic_devs[$vnic_cnt]}" == "" ]]; then
		log_err "VNIC <$1>: missing device"
		exit 1
	fi

	if [[ "${vnic_macs[$vnic_cnt]}" == "" ]]; then
		vnic_macs[$vnic_cnt]=auto
	fi

	vnic_last=$vnic_cnt
	((vnic_cnt++))
}

#
# Enable or disable a service within the zone.  This function sets the
# state and then checks to see if it worked, trying for up to 10 minutes
# to make the change stick.
#
# The first argument is the service, and the second is action, which can
# be either enable or disable.
#
change_zone_service () {
	local service=$1
	local action=$2
	local i=0

	case $action in
		enable)
			local good_state=online
			;;
		disable)
			local good_state=disabled
			;;
		*)
			log_err "invalid action for change_zone_service"
			exit 1
			;;
	esac

	while [[ $i -lt 120 ]]; do
		zlogin $zone_name svcadm $action $service > /dev/null 2>&1
		zlogin $zone_name svcs $service 2>/dev/null | \
		    grep -w $good_state > /dev/null
		if [[ $? -eq 0 ]]; then
			break
		fi
		((i++))
		sleep 5
	done
}

open_for_testing () {
	echo " ** WARNING: Opening zone for rsh, ssh, and ftp as root."
	echo "             This may take a few minutes."
	local temp_file=/tmp/open_for_testing.$$
	local zone_root=$zone_base/$zone_name/root
	cat $zone_root/etc/default/login | sed "s/^CONSOLE/#CONSOLE/" > \
	    $temp_file
	cp $temp_file $zone_root/etc/default/login
       cat $zone_root/etc/ftpd/ftpusers | sed "s/^root/#root/" > \
           $temp_file
       cp $temp_file $zone_root/etc/ftpd/ftpusers
	cat $zone_root/etc/ssh/sshd_config | \
	    sed "s/^PermitRootLogin no/PermitRootLogin yes/" > \
	    $temp_file
	cp $temp_file $zone_root/etc/ssh/sshd_config
	# Assumes ~root is the same frament in global and created zone.
	local rhosts_file=~root/.rhosts
	echo "+ +" > ${zone_root}${rhosts_file}
	rm -f $temp_file

	change_zone_service svc:/network/login:rlogin enable
}

#
# Two arguments, index of the interface and the file to write to.
# Uses global interface arrays, ip_type and zone_name.
#
ip_into_rc3 () {
    local idx=$1
    local filename=$2

    if [[ "${intf_ips[$idx]}" == "DHCP" ]]; then
	echo "ipadm create-addr -T dhcp ${intf_devs[$idx]}/v4"
    else
	if [[ "${intf_names[$idx]}" != "" && ${intf_ips[$idx]} != "" ]]; then
	    echo "echo '${intf_ips[$idx]} ${intf_names[$idx]}' >> /etc/hosts" \
		>> $filename
	fi
	mask=24
	if [[ "${intf_masks[$idx]}" != "" ]]; then
	    mask=${intf_masks[$idx]}
	fi
	echo "ipadm create-addr -T static -a local=${intf_ips[$idx]}/$mask" \
	    " ${intf_devs[$idx]}/v4" >> $filename
	if [[ "${intf_routes[$idx]}" != "" ]]; then
		#echo "  default_route=${intf_routes[$idx]}" >> $filename
	    echo "route add -p default $intf_routes[$idx]" >> $filename
	fi
    fi
}

expand_mask () {
       local mask=$((0xffffffff << (32 - $1)))
       printf "%u.%u.%u.%u\n" $((($mask >> 24) & 0xff)) \
           $((($mask >> 16) & 0xff)) \
           $((($mask >> 8) & 0xff)) \
           $(($mask & 0xff))
}

#
# Two arguments, index of the interface and the file to write to.
# Uses global interface arrays and ip_type.
#
#ipv6_into_syscfg () {
ipv6_into_rc3 () {
    local idx=$1
    local filename=$2
    
    if [[ "${intf_ipv6s[$idx]}" != "" ]]; then
	echo "ipadm create-addr -T addrconf -p stateless=yes "\
		"${intf_devs[$idx]}/v6" >> $filename
    fi
}

get_nfsmapid_domain () {
	local temp=""

	if [[ -f /etc/default/nfs ]]; then
		temp=`grep NFSMAPID_DOMAIN /etc/default/nfs | grep -v "#"`
		temp=${temp##*NFSMAPID_DOMAIN=}
		if [[ ! -z "$temp" ]]; then
			echo $temp
			return
		fi
	fi
	echo "dynamic"
}

map_locale_to_pkgs() {
	local pkg

	pkg search -l /usr/lib/locale/$1 | tail +2 | while read line
	do
		pkg=`lindex "$line" 3`
		pkg=${pkg#pkg:/}
		pkg=${pkg%@*}
		echo -n "$pkg "
	done
	echo ""
}

#
# Add the brand specific arguments for "zone install" for ipkg
#
add_ipkg_args() {
	local locale_pkg=""

	if [[ "$zone_locale" != C ]]; then
		ipkg_packages="$ipkg_packages `map_locale_to_pkgs $zone_locale`"
	fi
	#
	# Work arround the issue that causes excessive messages like:
	# en_US.UTF-8: unknown locale
	# from zlogin due to the system local being missing from the zone.
	#
	if [[ "$LANG" != C ]]; then
		ipkg_packages="$ipkg_packages `map_locale_to_pkgs $LANG`"
	fi

	# Clean up an duplicates and sort the package list for easy review
	ipkg_packages=`for pkg in $ipkg_packages; do echo $pkg; done | sort -u`

	echo "These addition packages will be installed into the ipkg zone:"
	echo "$ipkg_packages"
	echo $bar

	for pkg in $ipkg_packages; do
		zone_inst_args="$zone_inst_args -e $pkg"
	done
}

get_random_v6token () {
	for i in 0 1 2 3 4; do
		b[$i]=`get_random`
	done
	echo "00${b[0]}:${b[1]}ff:fe${b[2]}:${b[3]}${b[4]}"
}

get_random () {
	local res=`dd if=/dev/urandom bs=1 count=1 2>/dev/null | \
	    od -tx1 | head -1 | cut -d' ' -f2-`
	res=${res/ /}
	echo $res
}

get_zonestate () {
	echo `(zoneadm -z $1 list -p 2>/dev/null || echo ::noexist) | \
	    cut -d: -f3 `
}

while getopts "B:C:bc:e:hi:l:n:op:rv:wx:z:" opt; do
	case $opt in
		B)
			zone_brand="set brand=$OPTARG"
			;;
		C)
			clone_src=$OPTARG
			zoneadm -z $clone_src list > /dev/null 2>&1
			if [[ $? -ne 0 ]]; then
				log_err "clone source $clone_src does not exist"
				exit 1
			fi
			;;
		b)
			zone_blank=-b
			;;
		c)
			zone_add_cfg="$zone_add_cfg
$OPTARG"
			;;
		e)
			ipkg_packages="$ipkg_packages ${OPTARG//,/ }"
			;;
		h)
			usage
			exit 0
			;;
		i)
			log_err "-i no longer supported."
			exit 1
			;;
		l)
			zone_locale=$OPTARG
			;;
		n)
			zone_ns=$OPTARG
			if  [ "$zone_ns" != "NIS" ] && \
			    [ "$zone_ns" != "DNS" ] && \
			    [ "$zone_ns" != "NONE" ] && \
			    [ "$zone_ns" != "PARENT" ]; then
				log_err "bad argument for -n: $zone_ns"
				exit 1
			fi
			;;
		N)
			zone_nfs4=$OPTARG
			;;
		o)
			zone_open=1;
			# -o implies these network services are desired
			ipkg_packages="$ipkg_packages $ipkg_dash_o"
			;;
		p)
			zone_base=$OPTARG
			;;
		r)	
			blank_root=1
			;;
		v)
			new_vnic $OPTARG
			;;
		w)
			wait_confirm=1
			;;
		x)
			if [[ "$ip_type" == "shared" ]]; then
				log_err "-i and -x are mutually exclusive"
				exit 1
			fi
			ip_type=exclusive
			new_interface $OPTARG
			;;
		z)
			zone_name=$OPTARG
			;;
		?)
			log_err "invalid argument: $opt"
			log_err "try `basename $0` -h for help"
			exit 1
			;;
	esac
done

if [ -z $zone_name ]; then
	log_err "missing required -z option"
	log_err "try `basename $0` -h for help"
	exit 1
fi

#
# Determine the zone path to use.  If -p was supplied, there's nothing to do.
# Otherwise, zfs and ufs root systems require different handling.  For
# UFS, we use the default path, creating it if needed.  For ZFS, we may need
# to create a new files system for the zones default path if it doesn't
# already exist.  This will only be done if the pool rpool exists.
#
if [[ -z "$zone_base" ]]; then
	zfs list / > /dev/null 2>&1
	if [[ $? -ne 0 ]]; then
		# System root is not ZFS
		zone_base=$def_ufs_zone_path
		mkdir -p $zone_base
		if [[ ! -d "$zone_base" ]]; then
			log_err "failed to create default zone path: $zone_base"
			exit 1
		fi
	else
		# System root is ZFS
		zone_base=$def_zfs_zone_path

		if [[ ! -d "$zone_base" ]]; then
			zpool list rpool > /dev/null 2>&1
			if [[ "$?" -ne 0 ]]; then
				log_err "non-default zfs configuration detected"
				log_err "the -p option is required"
				exit 1
			fi
			echo "Creating ZFS pool rpool$zone_base"
			echo $bar
			zfs create -o mountpoint=$zone_base rpool$zone_base
			if [[ "$?" -ne 0 ]]; then
				log_err "failed to create ZFS filesystem"
				log_err "cannot install zone at $zone_base"
				exit 1
			fi
		else
			echo "Using existing default ZFS zone path: $zone_base"
			echo $bar
		fi
	fi
fi

#
# Network Interface Configuration
#
if [[ "$ip_type" == "shared" ]]; then
	#
	# Shared IP Instance
	#
	for ((i=0 ; $i < $intf_cnt; i++)); do
		if [[ "${intf_ips[$i]}" == "" ]] && \
		    [[ "${intf_ipv6s[$i]}" == "" ]]; then
			if [[ $i -eq 0 ]]; then
				temp=`getent hosts $zone_name`
				zone_ips[$i]=`lindex "$temp" 0`
			fi
			if [[ "${intf_ips[$i]}" == "" ]]; then
				log_err "missing IP address for interface $i"
				exit 1
			fi
		fi
		if [[ "${intf_ips[$i]}" != "" ]]; then
			mask=24
			if [[ "${intf_masks[$i]}" != "" ]]; then
				mask=${intf_masks[$i]}
			fi
			IP="${IP}
add net
set physical=${intf_devs[$i]}
set address=${intf_ips[$i]}/$mask
end"
		fi
		if [[ "${intf_ipv6s[$i]}" == "-" ]]; then
			if [[ "${intf_ips[$i]}" != "" ]]; then
				ipv6_ll=fe80::${intf_ips[$i]//./:}
			else
				token=`get_random_v6token`
				ipv6_ll=fe80::$token
			fi
			IP="${IP}
add net
set physical=${intf_devs[$i]}
set address=$ipv6_ll/16
end"
		fi
		if [[ "${intf_ipv6s[$i]}" != "" ]] && \
		    [[ "${intf_ipv6s[$i]}" != "-" ]]; then
			IP="${IP}
add net
set physical=${intf_devs[$i]}
set address=${intf_ipv6s[$i]}
end"
		fi
	done
fi
if [[ "$ip_type" == "exclusive" ]]; then
	#
	# Exclusive IP Instance
	#
	IP="set ip-type=exclusive"
	for ((i=0 ; $i < $intf_cnt; i++)); do
		IP="${IP}
add net
set physical=${intf_devs[$i]} 
end"
	done
fi

#
# Determine the root password to use in the zone.
#
# zone_rootpasswd=`cat /etc/shadow | grep -w root | cut -f2 -d:`
# 
# emtpy root password to work around the security hash changes breaking
# the sysidcfg ability to set the root password
zone_rootpasswd=""

#
# Make sure this zone isn't already configured.
#
zoneadm -z $zone_name list -c > /dev/null 2>&1
if [ $? -ne 1 ]; then
	log_err "configuration for zone $zone_name already exists!"
	exit 1
fi

zonecfg_tmpfile=/tmp/zonecfg_temp.$$

#
# Create the zone configuration in a temporary file.
#
cat <<-EOF > $zonecfg_tmpfile
create $zone_blank
set zonepath=$zone_base/$zone_name
set autoboot=true
$zone_brand
$IP
$zone_add_cfg
commit
exit
EOF

zonecfg -z $zone_name -f $zonecfg_tmpfile > /tmp/${zone_name}-config.log 2>&1

if [[ $? -ne 0 ]]; then
	log_err "failed to configure zone: $zone_name"
	log_err "  zonecfg output logged in: /tmp/${zone_name}-config.log"
	log_err "  attempted configuration left in: $zonecfg_tmpfile"
	exit 1
fi

rm -f $zonecfg_tmpfile

zone_brand=`zonecfg -z $zone_name info brand | cut  -d' ' -f2`

if [[ "$zone_brand" == "ipkg" ]]; then
	add_ipkg_args
fi

##
## OmniOS removed sysidcfg.  To configure a zone with networking up front
## one either needs to set the config files in the zone's root, OR set up
## an rc3 script that runs once, runs the commands, and deletes itself.
##
## I will do a bit of both, because ipadm(1M) is the way forward for
## configuration.
##

rc3_tmpfile=/tmp/rc3_temp.$$

#
# We now need to do cleanup should the script end early.
#
cleanup_zonecfg () {
	zonecfg -z $zone_name delete -F
	rm -f $sysid_tmpfile
	exit 1
}

trap cleanup_zonecfg SIGINT

echo " ** Configuration created for zone $zone_name:"
zonecfg -z $zone_name info
echo " ** Configuration output logged in /tmp/${zone_name}-config.log"
echo $bar
#
# Common configuration
#
if [[ -L /etc/TIMEZONE ]]; then
	zone_tz=`grep -v "#" /etc/TIMEZONE | grep TZ=`
	zone_tz=${zone_tz/TZ=/}
else
	zone_tz=US/Pacific
fi

#
# NFSv4 Domain
#
#zone_nfsmapid_domain=`get_nfsmapid_domain`
#if [[ -z $zone_nfsmapid_domain ]]; then
#	log_warn "unknown NFSMAPID_DOMAIN"
#       log_warn "you will be prompted at first zone boot"
#fi

#
# Set the locale based on what $LANG is set to in the caller's environment.
#
#if [[ -z "$zone_locale" ]]; then
#	zone_locale=$LANG
#	if [[ -z "$zone_locale" ]]; then
#		zone_locale=C
#	fi
#fi

#if [[ ! -d /usr/lib/locale/$zone_locale ]]; then
#	log_err "zone locale $zone_locale not found, aborting"
#	exit 1
#fi

#cat > $sysid_tmpfile <<-EOF
#system_locale=$zone_locale
#terminal=xterms
#security_policy=NONE
#timezone=$zone_tz
#nfs4_domain=$zone_nfsmapid_domain
#root_password="$zone_rootpasswd"
#EOF

#
# Interface configuration
#
case $ip_type in
	shared)
		cat >> $sysid_tmpfile <<-EOF
network_interface=primary {
 hostname=$zone_name
}
EOF
	;;
	exclusive)
		for ((i=0 ; $i < $intf_cnt; i++)); do
		    echo "ipadm create-if ${intf_devs[$i]}" >> $rc3_tmpfile
		    ip_into_rc3 $i $rc3_tmpfile
		    ipv6_into_rc3 $i $rc3_tmpfile
		done
	;;
	none)
		cat >> $sysid_tmpfile <<-EOF
network_interface=NONE {
 hostname=$zone_name
}
EOF
	;;
esac

#
# Name service configuration
#
case $zone_ns in
	NIS)
		zone_domainname=`domainname`
		nis_server=`ypwhich`
		nis_server_ip=`getent ipnodes $nis_server | cut -f 1`

		if [[ -z "$zone_domainname" ]] || \
		    [[ -z "$nis_server" ]] || \
		    [[ -z "$nis_server_ip" ]]; then
			log_err "could not gather enough info to configure NIS:"
			log_err "  domain_name=$zone_domainname"
			log_err "  name_server=$nis_server"
			log_err "  name_server_ip=$nis_server_ip"
			log_err "NIS must be configured in the global zone"
			cleanup_zonecfg
			# Above command exits
		fi

		cat >> $sysid_tmpfile <<-EOF
name_service=NIS {
 domain_name=$zone_domainname
 name_server=$nis_server($nis_server_ip)
}
EOF
		;;
	DNS)
		zone_dnsdomain=`grep domain /etc/resolv.conf | grep -v "#"`
		zone_dnsdomain=`lindex "$zone_dnsdomain" 1`
		zone_dnssvrs=`grep nameserver /etc/resolv.conf | grep -v "#"`
		zone_dnssvrs=${zone_dnssvrs/nameserver}
		zone_dnssvrs=${zone_dnssvrs//nameserver/,}
		zone_dnssvrs=`echo $zone_dnssvrs | tr -d '[:space:]'`
		zone_search=`grep search /etc/resolv.conf | grep -v "#"`
		zone_search=${zone_search/search}
		zone_search=${zone_search//search/,}
		zone_search=`echo $zone_search | tr -d '[:space:]'`

		if [[ -z "$zone_dnsdomain" ]] || \
		    [[ -z "$zone_dnssvrs" ]]; then
			log_err "could not gather enough info to configure DNS:"
			log_err "  domain_name=$zone_dnsdomain"
			log_err "  name_servers=$zone_dnssvers"
			log_err "please ensure a usable /etc/resolv.conf"
			cleanup_zonecfg
			# Above command exits
		fi

		cat >> $sysid_tmpfile <<-EOF
name_service=DNS {
 domain_name=$zone_dnsdomain
 name_server=$zone_dnssvrs
EOF
		if [[ ! -z "$zone_search" ]]; then
			echo "  search=$zone_search" >> $sysid_tmpfile
		fi
		echo "}" >> $sysid_tmpfile
		;;
	NONE|PARENT)
		echo "name_service=NONE" >> $sysid_tmpfile
		;;
esac

#echo " ** /etc/sysidcfg created for zone $zone_name:"
#cat $sysid_tmpfile
echo $bar

if [[ ! -z "$clone_src" ]]; then
	echo "Zone will be cloned from $clone_src"
	echo $bar
fi

if [[ $vnic_cnt -gt 0 ]]; then
	echo "The following VNICs will be created:"
	for ((i=0 ; $i < $vnic_cnt; i++)); do
		dev=${vnic_devs[$i]}
		mac=${vnic_macs[$i]}
		name=${vnic_names[$i]}
		echo "    ${name} on $dev with hardware address $mac"
	done
	echo $bar
fi

#
# Install the zone.
#
if [[ "$wait_confirm" -eq 1 ]]; then
	echo ""
	echo " ** Press enter to install zone, or CTRL-C to abort"
	read
fi

if [[ "$do_update_sysidtools" -eq 1 ]]; then
	update_sysidtools
fi

if [[ "$vnic_cnt" -gt 0 ]]; then
	echo "Creating VNICs ..."
	for ((i=0 ; $i < $vnic_cnt; i++)); do
		dladm create-vnic -l ${vnic_devs[$i]} -m ${vnic_macs[$i]} \
		    ${vnic_names[$i]}
		if [[ "$?" -ne 0 ]]; then
			log_err "failed to create vnic ${vnic_names[$i]}"
			exit 1
		fi
	done
fi

if [[ -z "$clone_src" ]]; then
	zoneadm -z $zone_name install $zone_inst_args
	if [[ "$?" -ne 0 ]]; then
		log_err "non-zero return from zoneadm install"
		cleanup_zonecfg
		# Above command exits
	fi
else
	case `get_zonestate $clone_src` in
		installed)
			zoneadm -z $zone_name clone $clone_src
			;;
		running)
			zoneadm -z $clone_src halt
			zoneadm -z $zone_name clone $clone_src
			zoneadm -z $clone_src boot
			;;
		*)
			log_error "clone source zone in unexpected state"
			cleanup_zonecfg
			;;
	esac
fi

#
# We're now past the point where cleaning up makes sense.
#
trap - SIGINT

zoneadm -z $zone_name mount
if [[ "$?" -eq 0 ]]; then
	#
	# Move over the sysidcfg file created earlier
	#
	# mv $sysid_tmpfile $zone_base/$zone_name/root/etc/sysidcfg
    # Clean up and install the one-time rc3 file.
    echo "/bin/rm -f /etc/rc3.d/S99hacky-addrconf" >>$rc3_tmpfile
    mv $rc3_tmpfile $zone_base/$zone_name/root/etc/rc3.d/S99hacky-addrconf
    echo "Here's the file: (press RETURN) to continue"
    echo "-------"
    cat $zone_base/$zone_name/root/etc/rc3.d/S99hacky-addrconf
    echo "-------"
    read

	#
	# We'll want a hosts file entry for the zone name.
	#
	if [ ! -z "${intf_ips[0]}" ]; then
		if [[ -z "${intf_names[0]}" ]]; then
			echo "${intf_ips[0]} $zone_name" >> \
			    $zone_base/$zone_name/root/etc/hosts
		else
			echo "${intf_ips[0]} ${intf_names[0]}" >> \
			    $zone_base/$zone_name/root/etc/hosts
		fi
	fi

	#
	# Setup NFSv4 mapid domain in the zone
	#
	if [[ ! -z "$zone_nfsmapid_domain" ]]; then
		echo "NFSMAPID_DOMAIN=$zone_nfsmapid_domain" >> \
		    $zone_base/$zone_name/root/etc/default/nfs
		touch $zone_base/$zone_name/root/etc/.NFS4inst_state.domain
	fi
	zoneadm -z $zone_name unmount
else
	log_warn "zoneadm -z $zone_name mount: failed"
	log_warn "some zone pre-configuration will be incomplete"
fi

echo $bar

#
# Boot the zone.
#
#echo "Booting zone $zone_name"
zoneadm -z $zone_name boot

if [[ "$zone_open" -eq 1 ]]; then
	open_for_testing
fi

zoneadm -z $zone_name reboot
# In OmniOS, these aren't needed.  They might be in other IPS-running distros.
#rm $zone_base/$zone_name/root/etc/sysidcfg
#rm $zone_base/$zone_name/root/etc/.UNCONFIGURED

#
# Allow empty passwords for user login.
#
login_file=$zone_base/$zone_name/root/etc/default/login
sed 's/PASSREQ=.*/PASSREQ=NO/' ${login_file} > ${login_file}.tmp
mv ${login_file}.tmp ${login_file}

echo "`basename $0` completed successfully."
