1 #!/bin/bash
   2 #
   3 # CDDL HEADER START
   4 #
   5 # The contents of this file are subject to the terms of the
   6 # Common Development and Distribution License (the "License").
   7 # You may not use this file except in compliance with the License.
   8 #
   9 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
  10 # or http://www.opensolaris.org/os/licensing.
  11 # See the License for the specific language governing permissions
  12 # and limitations under the License.
  13 #
  14 # When distributing Covered Code, include this CDDL HEADER in each
  15 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
  16 # If applicable, add the following below this CDDL HEADER, with the
  17 # fields enclosed by brackets "[]" replaced with your own identifying
  18 # information: Portions Copyright [yyyy] [name of copyright owner]
  19 #
  20 # CDDL HEADER END
  21 #
  22 
  23 #
  24 # Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  25 # Use is subject to license terms.
  26 #
  27 # ident "%Z%%M% %I%     %E% SMI"
  28 #
  29 
  30 #
  31 # Known issue: if you clone an ipkg zone but choose a different locale
  32 # from the cloned zone, the locale package may not be installed in the
  33 # new zone.
  34 #
  35 
  36 ipkg_packages=""
  37 #Old definition...
  38 #ipkg_dash_o="SUNWftp SUNWrcmds SUNWsshd"
  39 #New definition, note no FTP server, as it was obsoleted.
  40 ipkg_dash_o="network-servers service/network/ssh"
  41 def_ufs_zone_path=/export/home/zones
  42 def_zfs_zone_path=/zones
  43 # Source zone for ZFS cloning, empty means do not clone
  44 clone_src=""
  45 zone_base=""
  46 zone_add_cfg=""
  47 zone_ns=NONE
  48 zone_blank=""
  49 # Last index assigned
  50 vnic_last=""
  51 # Index for vnic auto name generation
  52 vnic_auto=""
  53 vnic_cnt=0
  54 ip_type="none"
  55 intf_cnt=0
  56 wait_confirm=0
  57 # Additional arguments for the
  58 zone_inst_args=""
  59 bar=\
  60 "------------------------------------------------------------------------------"
  61 
  62 usage () {
  63 cmd=`basename $0`
  64 cat <<-EOF
  65 Usage: $cmd -z <zone_name> [options]
  66 
  67  Options supported:
  68    -B          Create the zone with the specified brand
  69    -C <zone>      Create the new zone by cloning the specified zone.
  70    -b          Start with a blank zone config
  71    -c <config> Add a line to the zone configuration
  72    -e <pkgs>      Comma separated list of packages to install (ipkg zone only)
  73    -h          Prints this usage message
  74    -l <locale>    Sets the locale for the zone (defaults to \$LANG)
  75    -i <ifspec> Configure an ip-type shared interface  (OBSOLETE)
  76    -n          Name service, one of NIS, DNS, NONE (default)
  77    -o          Open the zone up for testing *NOT SECURE*
  78    -p <path>   Base path for zones
  79    -w          Prompt for confirmation before installing zone
  80    -v <vnspec> Create a VNIC on the specified device
  81    -x <ifspec> Configure an ip-type exclusive interface
  82 
  83  Specifying interfaces:
  84    <ifspec> is a comma separated lists of assignments or keywords:
  85      dev=<interface name, ex. e1000g1>
  86      dhcp  (-x only)
  87      ip=<IP address>[/<mask>]
  88      ipv6
  89      ipv6=<IPv6 address>/<mask>  (-i only)
  90      name=<hostname>  (-x only)
  91      route=<default route ip>  (-x only)
  92    No whitespace is permitted between arguments.
  93    Since both flags set ip-type, -i and -x are mutually exclusive.
  94    The special value "VNIC" for dev uses the last vnic created via -v.
  95    Keyword ipv6 automatically generates a link-local address.
  96 
  97  Specifying VNICs:
  98    <vnspec> is a comma separated lists of assignments:
  99      name=<name, defaults to vnic#>
 100      dev=<interface name, ex. e1000g1>
 101      mac=<hardware address>
 102    No whitespace is permitted between arguments.
 103    MAC address is optional; by default, a random MAC is generated.
 104 EOF
 105 }
 106 
 107 lindex () {
 108         typeset -a arr=($1)
 109         echo ${arr[$2]}
 110 }
 111 
 112 log_err () {
 113         echo "ERROR: $*" >&2
 114 }
 115 
 116 log_warn () {
 117         echo "WARNING: $*" >&2
 118 }
 119 
 120 new_interface () {
 121         for setting in ${1//,/ }; do
 122                 case $setting in
 123                         dev=*)
 124                                 intf_devs[$intf_cnt]=${setting/dev=/}
 125                                 ;;
 126                         dhcp)
 127                                 intf_ips[$intf_cnt]=DHCP
 128                                 ;;
 129                         ip=*)
 130                                 setting=${setting/ip=/}
 131                                 intf_ips[$intf_cnt]=${setting%%/*}
 132                                 if [[ "${setting%%/*}" != "${setting##*/}" ]]; \
 133                                     then
 134                                         intf_masks[$intf_cnt]=${setting##*/}
 135                                 fi
 136                                 ;;
 137                         ipv6=*)
 138                                 intf_ipv6s[$intf_cnt]=${setting/ipv6=/}
 139                                 ;;
 140                         ipv6)
 141                                 #
 142                                 # Set a special case for just the link-local
 143                                 # address.
 144                                 #
 145                                 if [[ ${intf_ipv6s[$intf_cnt]} == "" ]]; then
 146                                         intf_ipv6s[$intf_cnt]="-"
 147                                 fi
 148                                 ;;
 149                         name=*)
 150                                 intf_names[$intf_cnt]=${setting/name=/}
 151                                 ;;
 152                         route=*)
 153                                 intf_routes[$intf_cnt]=${setting/route=/}
 154                                 ;;
 155                 esac
 156         done
 157 
 158         #
 159         # VNIC is a special case to use the last vnic configured via -v.
 160         #
 161         if [[ "${intf_devs[$intf_cnt]}" == "VNIC" ]]; then
 162                 if [[ -z "$vnic_last" ]]; then
 163                         log_err "for intf <$1>, dev=VNIC but no prior -v option"
 164                         exit 1
 165                 fi
 166                 intf_devs[$intf_cnt]=${vnic_names[$vnic_last]}
 167         fi
 168 
 169         #
 170         # Name defaults to the zone name for the first interface.
 171         #
 172         if [[ $intf_cnt -eq 0 ]] && [[ "${intf_names[$intf_cnt]}" == "" ]]; then
 173                 intf_names[$intf_cnt]=$zone_name
 174         fi
 175 
 176         #
 177         # If a name was specified, but no IP was given, try looking up the name.
 178         #
 179         if [[ "${intf_ips[$intf_cnt]}" == "" ]]  && \
 180             [[ "${intf_names[$intf_cnt]}" != "" ]] ; then
 181                 temp=`getent hosts ${intf_names[$intf_cnt]}`
 182                 if [[ $? -eq 0 ]] && [[ "$temp" != "" ]]; then
 183                         intf_ips[$intf_cnt]=`lindex "$temp" 0`
 184                 fi
 185         fi
 186 
 187         #
 188         # Special case: if no device is supplied for the first interface,
 189         # use the outgoing interface for the system's default route.
 190         #
 191         if [[ "${intf_devs[$intf_cnt]}" == "" ]]; then
 192                 if [[ $intf_cnt -eq 0 ]] && [[ "$ip_type" == "shared" ]]; then
 193                         temp=`route -n get default | grep interface`
 194                         temp=`lindex "$temp" 1`
 195                         intf_devs[0]=$temp
 196                 else
 197                         log_err "Interface <$1>: missing device"
 198                         exit 1
 199                 fi
 200         fi
 201         ((intf_cnt++))
 202 }
 203 
 204 new_vnic () {
 205         for setting in ${1//,/ }; do
 206                 case $setting in
 207                         name=*)
 208                                 vnic_names[$vnic_cnt]=${setting/name=/}
 209                                 ;;
 210                         dev=*)
 211                                 vnic_devs[$vnic_cnt]=${setting/dev=/}
 212                                 ;;
 213                         mac=*)
 214                                 vnic_macs[$vnic_cnt]=${setting/mac=/}
 215                                 ;;
 216                 esac
 217         done
 218 
 219         # If no name was given for the VNIC, generate one automatically.
 220         if [[ "${vnic_names[$vnic_cnt]}" == "" ]]; then
 221                 # Find the first available vnic# name
 222                 if [[ -z "$vnic_auto" ]]; then
 223                         vnic_auto=`dladm show-vnic | tail -1 | sed \
 224                             's/vnic\([0-9][0-9]*\).*$/\1/g'`
 225                         if [[ "$vnic_auto" == "" ]]; then
 226                                 vnic_auto=0
 227                         else
 228                                 ((vnic_auto++))
 229                         fi
 230                 fi
 231                 vnic_names[$vnic_cnt]=vnic$vnic_auto
 232                 ((vnic_auto++))
 233         fi
 234 
 235         if [[ "${vnic_devs[$vnic_cnt]}" == "" ]]; then
 236                 log_err "VNIC <$1>: missing device"
 237                 exit 1
 238         fi
 239 
 240         if [[ "${vnic_macs[$vnic_cnt]}" == "" ]]; then
 241                 vnic_macs[$vnic_cnt]=auto
 242         fi
 243 
 244         vnic_last=$vnic_cnt
 245         ((vnic_cnt++))
 246 }
 247 
 248 #
 249 # Enable or disable a service within the zone.  This function sets the
 250 # state and then checks to see if it worked, trying for up to 10 minutes
 251 # to make the change stick.
 252 #
 253 # The first argument is the service, and the second is action, which can
 254 # be either enable or disable.
 255 #
 256 change_zone_service () {
 257         local service=$1
 258         local action=$2
 259         local i=0
 260 
 261         case $action in
 262                 enable)
 263                         local good_state=online
 264                         ;;
 265                 disable)
 266                         local good_state=disabled
 267                         ;;
 268                 *)
 269                         log_err "invalid action for change_zone_service"
 270                         exit 1
 271                         ;;
 272         esac
 273 
 274         while [[ $i -lt 120 ]]; do
 275                 zlogin $zone_name svcadm $action $service > /dev/null 2>&1
 276                 zlogin $zone_name svcs $service 2>/dev/null | \
 277                     grep -w $good_state > /dev/null
 278                 if [[ $? -eq 0 ]]; then
 279                         break
 280                 fi
 281                 ((i++))
 282                 sleep 5
 283         done
 284 }
 285 
 286 open_for_testing () {
 287         echo " ** WARNING: Opening zone for rsh, ssh, and ftp as root."
 288         echo "             This may take a few minutes."
 289         local temp_file=/tmp/open_for_testing.$$
 290         local zone_root=$zone_base/$zone_name/root
 291         cat $zone_root/etc/default/login | sed "s/^CONSOLE/#CONSOLE/" > \
 292             $temp_file
 293         cp $temp_file $zone_root/etc/default/login
 294        cat $zone_root/etc/ftpd/ftpusers | sed "s/^root/#root/" > \
 295            $temp_file
 296        cp $temp_file $zone_root/etc/ftpd/ftpusers
 297         cat $zone_root/etc/ssh/sshd_config | \
 298             sed "s/^PermitRootLogin no/PermitRootLogin yes/" > \
 299             $temp_file
 300         cp $temp_file $zone_root/etc/ssh/sshd_config
 301         # Assumes ~root is the same frament in global and created zone.
 302         local rhosts_file=~root/.rhosts
 303         echo "+ +" > ${zone_root}${rhosts_file}
 304         rm -f $temp_file
 305 
 306         change_zone_service svc:/network/login:rlogin enable
 307 }
 308 
 309 #
 310 # Two arguments, index of the interface and the file to write to.
 311 # Uses global interface arrays, ip_type and zone_name.
 312 #
 313 ip_into_rc3 () {
 314     local idx=$1
 315     local filename=$2
 316 
 317     if [[ "${intf_ips[$idx]}" == "DHCP" ]]; then
 318         echo "ipadm create-addr -T dhcp ${intf_devs[$idx]}/v4"
 319     else
 320         if [[ "${intf_names[$idx]}" != "" && ${intf_ips[$idx]} != "" ]]; then
 321             echo "echo '${intf_ips[$idx]} ${intf_names[$idx]}' >> /etc/hosts" \
 322                 >> $filename
 323         fi
 324         mask=24
 325         if [[ "${intf_masks[$idx]}" != "" ]]; then
 326             mask=${intf_masks[$idx]}
 327         fi
 328         echo "ipadm create-addr -T static -a local=${intf_ips[$idx]}/$mask" \
 329             " ${intf_devs[$idx]}/v4" >> $filename
 330         if [[ "${intf_routes[$idx]}" != "" ]]; then
 331                 #echo "  default_route=${intf_routes[$idx]}" >> $filename
 332             echo "route add -p default $intf_routes[$idx]" >> $filename
 333         fi
 334     fi
 335 }
 336 
 337 expand_mask () {
 338        local mask=$((0xffffffff << (32 - $1)))
 339        printf "%u.%u.%u.%u\n" $((($mask >> 24) & 0xff)) \
 340            $((($mask >> 16) & 0xff)) \
 341            $((($mask >> 8) & 0xff)) \
 342            $(($mask & 0xff))
 343 }
 344 
 345 #
 346 # Two arguments, index of the interface and the file to write to.
 347 # Uses global interface arrays and ip_type.
 348 #
 349 #ipv6_into_syscfg () {
 350 ipv6_into_rc3 () {
 351     local idx=$1
 352     local filename=$2
 353     
 354     if [[ "${intf_ipv6s[$idx]}" != "" ]]; then
 355         echo "ipadm create-addr -T addrconf -p stateless=yes "\
 356                 "${intf_devs[$idx]}/v6" >> $filename
 357     fi
 358 }
 359 
 360 get_nfsmapid_domain () {
 361         local temp=""
 362 
 363         if [[ -f /etc/default/nfs ]]; then
 364                 temp=`grep NFSMAPID_DOMAIN /etc/default/nfs | grep -v "#"`
 365                 temp=${temp##*NFSMAPID_DOMAIN=}
 366                 if [[ ! -z "$temp" ]]; then
 367                         echo $temp
 368                         return
 369                 fi
 370         fi
 371         echo "dynamic"
 372 }
 373 
 374 map_locale_to_pkgs() {
 375         local pkg
 376 
 377         pkg search -l /usr/lib/locale/$1 | tail +2 | while read line
 378         do
 379                 pkg=`lindex "$line" 3`
 380                 pkg=${pkg#pkg:/}
 381                 pkg=${pkg%@*}
 382                 echo -n "$pkg "
 383         done
 384         echo ""
 385 }
 386 
 387 #
 388 # Add the brand specific arguments for "zone install" for ipkg
 389 #
 390 add_ipkg_args() {
 391         local locale_pkg=""
 392 
 393         if [[ "$zone_locale" != C ]]; then
 394                 ipkg_packages="$ipkg_packages `map_locale_to_pkgs $zone_locale`"
 395         fi
 396         #
 397         # Work arround the issue that causes excessive messages like:
 398         # en_US.UTF-8: unknown locale
 399         # from zlogin due to the system local being missing from the zone.
 400         #
 401         if [[ "$LANG" != C ]]; then
 402                 ipkg_packages="$ipkg_packages `map_locale_to_pkgs $LANG`"
 403         fi
 404 
 405         # Clean up an duplicates and sort the package list for easy review
 406         ipkg_packages=`for pkg in $ipkg_packages; do echo $pkg; done | sort -u`
 407 
 408         echo "These addition packages will be installed into the ipkg zone:"
 409         echo "$ipkg_packages"
 410         echo $bar
 411 
 412         for pkg in $ipkg_packages; do
 413                 zone_inst_args="$zone_inst_args -e $pkg"
 414         done
 415 }
 416 
 417 get_random_v6token () {
 418         for i in 0 1 2 3 4; do
 419                 b[$i]=`get_random`
 420         done
 421         echo "00${b[0]}:${b[1]}ff:fe${b[2]}:${b[3]}${b[4]}"
 422 }
 423 
 424 get_random () {
 425         local res=`dd if=/dev/urandom bs=1 count=1 2>/dev/null | \
 426             od -tx1 | head -1 | cut -d' ' -f2-`
 427         res=${res/ /}
 428         echo $res
 429 }
 430 
 431 get_zonestate () {
 432         echo `(zoneadm -z $1 list -p 2>/dev/null || echo ::noexist) | \
 433             cut -d: -f3 `
 434 }
 435 
 436 while getopts "B:C:bc:e:hi:l:n:op:rv:wx:z:" opt; do
 437         case $opt in
 438                 B)
 439                         zone_brand="set brand=$OPTARG"
 440                         ;;
 441                 C)
 442                         clone_src=$OPTARG
 443                         zoneadm -z $clone_src list > /dev/null 2>&1
 444                         if [[ $? -ne 0 ]]; then
 445                                 log_err "clone source $clone_src does not exist"
 446                                 exit 1
 447                         fi
 448                         ;;
 449                 b)
 450                         zone_blank=-b
 451                         ;;
 452                 c)
 453                         zone_add_cfg="$zone_add_cfg
 454 $OPTARG"
 455                         ;;
 456                 e)
 457                         ipkg_packages="$ipkg_packages ${OPTARG//,/ }"
 458                         ;;
 459                 h)
 460                         usage
 461                         exit 0
 462                         ;;
 463                 i)
 464                         log_err "-i no longer supported."
 465                         exit 1
 466                         ;;
 467                 l)
 468                         zone_locale=$OPTARG
 469                         ;;
 470                 n)
 471                         zone_ns=$OPTARG
 472                         if  [ "$zone_ns" != "NIS" ] && \
 473                             [ "$zone_ns" != "DNS" ] && \
 474                             [ "$zone_ns" != "NONE" ] && \
 475                             [ "$zone_ns" != "PARENT" ]; then
 476                                 log_err "bad argument for -n: $zone_ns"
 477                                 exit 1
 478                         fi
 479                         ;;
 480                 N)
 481                         zone_nfs4=$OPTARG
 482                         ;;
 483                 o)
 484                         zone_open=1;
 485                         # -o implies these network services are desired
 486                         ipkg_packages="$ipkg_packages $ipkg_dash_o"
 487                         ;;
 488                 p)
 489                         zone_base=$OPTARG
 490                         ;;
 491                 r)      
 492                         blank_root=1
 493                         ;;
 494                 v)
 495                         new_vnic $OPTARG
 496                         ;;
 497                 w)
 498                         wait_confirm=1
 499                         ;;
 500                 x)
 501                         if [[ "$ip_type" == "shared" ]]; then
 502                                 log_err "-i and -x are mutually exclusive"
 503                                 exit 1
 504                         fi
 505                         ip_type=exclusive
 506                         new_interface $OPTARG
 507                         ;;
 508                 z)
 509                         zone_name=$OPTARG
 510                         ;;
 511                 ?)
 512                         log_err "invalid argument: $opt"
 513                         log_err "try `basename $0` -h for help"
 514                         exit 1
 515                         ;;
 516         esac
 517 done
 518 
 519 if [ -z $zone_name ]; then
 520         log_err "missing required -z option"
 521         log_err "try `basename $0` -h for help"
 522         exit 1
 523 fi
 524 
 525 #
 526 # Determine the zone path to use.  If -p was supplied, there's nothing to do.
 527 # Otherwise, zfs and ufs root systems require different handling.  For
 528 # UFS, we use the default path, creating it if needed.  For ZFS, we may need
 529 # to create a new files system for the zones default path if it doesn't
 530 # already exist.  This will only be done if the pool rpool exists.
 531 #
 532 if [[ -z "$zone_base" ]]; then
 533         zfs list / > /dev/null 2>&1
 534         if [[ $? -ne 0 ]]; then
 535                 # System root is not ZFS
 536                 zone_base=$def_ufs_zone_path
 537                 mkdir -p $zone_base
 538                 if [[ ! -d "$zone_base" ]]; then
 539                         log_err "failed to create default zone path: $zone_base"
 540                         exit 1
 541                 fi
 542         else
 543                 # System root is ZFS
 544                 zone_base=$def_zfs_zone_path
 545 
 546                 if [[ ! -d "$zone_base" ]]; then
 547                         zpool list rpool > /dev/null 2>&1
 548                         if [[ "$?" -ne 0 ]]; then
 549                                 log_err "non-default zfs configuration detected"
 550                                 log_err "the -p option is required"
 551                                 exit 1
 552                         fi
 553                         echo "Creating ZFS pool rpool$zone_base"
 554                         echo $bar
 555                         zfs create -o mountpoint=$zone_base rpool$zone_base
 556                         if [[ "$?" -ne 0 ]]; then
 557                                 log_err "failed to create ZFS filesystem"
 558                                 log_err "cannot install zone at $zone_base"
 559                                 exit 1
 560                         fi
 561                 else
 562                         echo "Using existing default ZFS zone path: $zone_base"
 563                         echo $bar
 564                 fi
 565         fi
 566 fi
 567 
 568 #
 569 # Network Interface Configuration
 570 #
 571 if [[ "$ip_type" == "shared" ]]; then
 572         #
 573         # Shared IP Instance
 574         #
 575         for ((i=0 ; $i < $intf_cnt; i++)); do
 576                 if [[ "${intf_ips[$i]}" == "" ]] && \
 577                     [[ "${intf_ipv6s[$i]}" == "" ]]; then
 578                         if [[ $i -eq 0 ]]; then
 579                                 temp=`getent hosts $zone_name`
 580                                 zone_ips[$i]=`lindex "$temp" 0`
 581                         fi
 582                         if [[ "${intf_ips[$i]}" == "" ]]; then
 583                                 log_err "missing IP address for interface $i"
 584                                 exit 1
 585                         fi
 586                 fi
 587                 if [[ "${intf_ips[$i]}" != "" ]]; then
 588                         mask=24
 589                         if [[ "${intf_masks[$i]}" != "" ]]; then
 590                                 mask=${intf_masks[$i]}
 591                         fi
 592                         IP="${IP}
 593 add net
 594 set physical=${intf_devs[$i]}
 595 set address=${intf_ips[$i]}/$mask
 596 end"
 597                 fi
 598                 if [[ "${intf_ipv6s[$i]}" == "-" ]]; then
 599                         if [[ "${intf_ips[$i]}" != "" ]]; then
 600                                 ipv6_ll=fe80::${intf_ips[$i]//./:}
 601                         else
 602                                 token=`get_random_v6token`
 603                                 ipv6_ll=fe80::$token
 604                         fi
 605                         IP="${IP}
 606 add net
 607 set physical=${intf_devs[$i]}
 608 set address=$ipv6_ll/16
 609 end"
 610                 fi
 611                 if [[ "${intf_ipv6s[$i]}" != "" ]] && \
 612                     [[ "${intf_ipv6s[$i]}" != "-" ]]; then
 613                         IP="${IP}
 614 add net
 615 set physical=${intf_devs[$i]}
 616 set address=${intf_ipv6s[$i]}
 617 end"
 618                 fi
 619         done
 620 fi
 621 if [[ "$ip_type" == "exclusive" ]]; then
 622         #
 623         # Exclusive IP Instance
 624         #
 625         IP="set ip-type=exclusive"
 626         for ((i=0 ; $i < $intf_cnt; i++)); do
 627                 IP="${IP}
 628 add net
 629 set physical=${intf_devs[$i]} 
 630 end"
 631         done
 632 fi
 633 
 634 #
 635 # Determine the root password to use in the zone.
 636 #
 637 # zone_rootpasswd=`cat /etc/shadow | grep -w root | cut -f2 -d:`
 638 # 
 639 # emtpy root password to work around the security hash changes breaking
 640 # the sysidcfg ability to set the root password
 641 zone_rootpasswd=""
 642 
 643 #
 644 # Make sure this zone isn't already configured.
 645 #
 646 zoneadm -z $zone_name list -c > /dev/null 2>&1
 647 if [ $? -ne 1 ]; then
 648         log_err "configuration for zone $zone_name already exists!"
 649         exit 1
 650 fi
 651 
 652 zonecfg_tmpfile=/tmp/zonecfg_temp.$$
 653 
 654 #
 655 # Create the zone configuration in a temporary file.
 656 #
 657 cat <<-EOF > $zonecfg_tmpfile
 658 create $zone_blank
 659 set zonepath=$zone_base/$zone_name
 660 set autoboot=true
 661 $zone_brand
 662 $IP
 663 $zone_add_cfg
 664 commit
 665 exit
 666 EOF
 667 
 668 zonecfg -z $zone_name -f $zonecfg_tmpfile > /tmp/${zone_name}-config.log 2>&1
 669 
 670 if [[ $? -ne 0 ]]; then
 671         log_err "failed to configure zone: $zone_name"
 672         log_err "  zonecfg output logged in: /tmp/${zone_name}-config.log"
 673         log_err "  attempted configuration left in: $zonecfg_tmpfile"
 674         exit 1
 675 fi
 676 
 677 rm -f $zonecfg_tmpfile
 678 
 679 zone_brand=`zonecfg -z $zone_name info brand | cut  -d' ' -f2`
 680 
 681 if [[ "$zone_brand" == "ipkg" ]]; then
 682         add_ipkg_args
 683 fi
 684 
 685 ##
 686 ## OmniOS removed sysidcfg.  To configure a zone with networking up front
 687 ## one either needs to set the config files in the zone's root, OR set up
 688 ## an rc3 script that runs once, runs the commands, and deletes itself.
 689 ##
 690 ## I will do a bit of both, because ipadm(1M) is the way forward for
 691 ## configuration.
 692 ##
 693 
 694 rc3_tmpfile=/tmp/rc3_temp.$$
 695 
 696 #
 697 # We now need to do cleanup should the script end early.
 698 #
 699 cleanup_zonecfg () {
 700         zonecfg -z $zone_name delete -F
 701         rm -f $sysid_tmpfile
 702         exit 1
 703 }
 704 
 705 trap cleanup_zonecfg SIGINT
 706 
 707 echo " ** Configuration created for zone $zone_name:"
 708 zonecfg -z $zone_name info
 709 echo " ** Configuration output logged in /tmp/${zone_name}-config.log"
 710 echo $bar
 711 #
 712 # Common configuration
 713 #
 714 if [[ -L /etc/TIMEZONE ]]; then
 715         zone_tz=`grep -v "#" /etc/TIMEZONE | grep TZ=`
 716         zone_tz=${zone_tz/TZ=/}
 717 else
 718         zone_tz=US/Pacific
 719 fi
 720 
 721 #
 722 # NFSv4 Domain
 723 #
 724 #zone_nfsmapid_domain=`get_nfsmapid_domain`
 725 #if [[ -z $zone_nfsmapid_domain ]]; then
 726 #       log_warn "unknown NFSMAPID_DOMAIN"
 727 #       log_warn "you will be prompted at first zone boot"
 728 #fi
 729 
 730 #
 731 # Set the locale based on what $LANG is set to in the caller's environment.
 732 #
 733 #if [[ -z "$zone_locale" ]]; then
 734 #       zone_locale=$LANG
 735 #       if [[ -z "$zone_locale" ]]; then
 736 #               zone_locale=C
 737 #       fi
 738 #fi
 739 
 740 #if [[ ! -d /usr/lib/locale/$zone_locale ]]; then
 741 #       log_err "zone locale $zone_locale not found, aborting"
 742 #       exit 1
 743 #fi
 744 
 745 #cat > $sysid_tmpfile <<-EOF
 746 #system_locale=$zone_locale
 747 #terminal=xterms
 748 #security_policy=NONE
 749 #timezone=$zone_tz
 750 #nfs4_domain=$zone_nfsmapid_domain
 751 #root_password="$zone_rootpasswd"
 752 #EOF
 753 
 754 #
 755 # Interface configuration
 756 #
 757 case $ip_type in
 758         shared)
 759                 cat >> $sysid_tmpfile <<-EOF
 760 network_interface=primary {
 761  hostname=$zone_name
 762 }
 763 EOF
 764         ;;
 765         exclusive)
 766                 for ((i=0 ; $i < $intf_cnt; i++)); do
 767                     echo "ipadm create-if ${intf_devs[$i]}" >> $rc3_tmpfile
 768                     ip_into_rc3 $i $rc3_tmpfile
 769                     ipv6_into_rc3 $i $rc3_tmpfile
 770                 done
 771         ;;
 772         none)
 773                 cat >> $sysid_tmpfile <<-EOF
 774 network_interface=NONE {
 775  hostname=$zone_name
 776 }
 777 EOF
 778         ;;
 779 esac
 780 
 781 #
 782 # Name service configuration
 783 #
 784 case $zone_ns in
 785         NIS)
 786                 zone_domainname=`domainname`
 787                 nis_server=`ypwhich`
 788                 nis_server_ip=`getent ipnodes $nis_server | cut -f 1`
 789 
 790                 if [[ -z "$zone_domainname" ]] || \
 791                     [[ -z "$nis_server" ]] || \
 792                     [[ -z "$nis_server_ip" ]]; then
 793                         log_err "could not gather enough info to configure NIS:"
 794                         log_err "  domain_name=$zone_domainname"
 795                         log_err "  name_server=$nis_server"
 796                         log_err "  name_server_ip=$nis_server_ip"
 797                         log_err "NIS must be configured in the global zone"
 798                         cleanup_zonecfg
 799                         # Above command exits
 800                 fi
 801 
 802                 cat >> $sysid_tmpfile <<-EOF
 803 name_service=NIS {
 804  domain_name=$zone_domainname
 805  name_server=$nis_server($nis_server_ip)
 806 }
 807 EOF
 808                 ;;
 809         DNS)
 810                 zone_dnsdomain=`grep domain /etc/resolv.conf | grep -v "#"`
 811                 zone_dnsdomain=`lindex "$zone_dnsdomain" 1`
 812                 zone_dnssvrs=`grep nameserver /etc/resolv.conf | grep -v "#"`
 813                 zone_dnssvrs=${zone_dnssvrs/nameserver}
 814                 zone_dnssvrs=${zone_dnssvrs//nameserver/,}
 815                 zone_dnssvrs=`echo $zone_dnssvrs | tr -d '[:space:]'`
 816                 zone_search=`grep search /etc/resolv.conf | grep -v "#"`
 817                 zone_search=${zone_search/search}
 818                 zone_search=${zone_search//search/,}
 819                 zone_search=`echo $zone_search | tr -d '[:space:]'`
 820 
 821                 if [[ -z "$zone_dnsdomain" ]] || \
 822                     [[ -z "$zone_dnssvrs" ]]; then
 823                         log_err "could not gather enough info to configure DNS:"
 824                         log_err "  domain_name=$zone_dnsdomain"
 825                         log_err "  name_servers=$zone_dnssvers"
 826                         log_err "please ensure a usable /etc/resolv.conf"
 827                         cleanup_zonecfg
 828                         # Above command exits
 829                 fi
 830 
 831                 cat >> $sysid_tmpfile <<-EOF
 832 name_service=DNS {
 833  domain_name=$zone_dnsdomain
 834  name_server=$zone_dnssvrs
 835 EOF
 836                 if [[ ! -z "$zone_search" ]]; then
 837                         echo "  search=$zone_search" >> $sysid_tmpfile
 838                 fi
 839                 echo "}" >> $sysid_tmpfile
 840                 ;;
 841         NONE|PARENT)
 842                 echo "name_service=NONE" >> $sysid_tmpfile
 843                 ;;
 844 esac
 845 
 846 #echo " ** /etc/sysidcfg created for zone $zone_name:"
 847 #cat $sysid_tmpfile
 848 echo $bar
 849 
 850 if [[ ! -z "$clone_src" ]]; then
 851         echo "Zone will be cloned from $clone_src"
 852         echo $bar
 853 fi
 854 
 855 if [[ $vnic_cnt -gt 0 ]]; then
 856         echo "The following VNICs will be created:"
 857         for ((i=0 ; $i < $vnic_cnt; i++)); do
 858                 dev=${vnic_devs[$i]}
 859                 mac=${vnic_macs[$i]}
 860                 name=${vnic_names[$i]}
 861                 echo "    ${name} on $dev with hardware address $mac"
 862         done
 863         echo $bar
 864 fi
 865 
 866 #
 867 # Install the zone.
 868 #
 869 if [[ "$wait_confirm" -eq 1 ]]; then
 870         echo ""
 871         echo " ** Press enter to install zone, or CTRL-C to abort"
 872         read
 873 fi
 874 
 875 if [[ "$do_update_sysidtools" -eq 1 ]]; then
 876         update_sysidtools
 877 fi
 878 
 879 if [[ "$vnic_cnt" -gt 0 ]]; then
 880         echo "Creating VNICs ..."
 881         for ((i=0 ; $i < $vnic_cnt; i++)); do
 882                 dladm create-vnic -l ${vnic_devs[$i]} -m ${vnic_macs[$i]} \
 883                     ${vnic_names[$i]}
 884                 if [[ "$?" -ne 0 ]]; then
 885                         log_err "failed to create vnic ${vnic_names[$i]}"
 886                         exit 1
 887                 fi
 888         done
 889 fi
 890 
 891 if [[ -z "$clone_src" ]]; then
 892         zoneadm -z $zone_name install $zone_inst_args
 893         if [[ "$?" -ne 0 ]]; then
 894                 log_err "non-zero return from zoneadm install"
 895                 cleanup_zonecfg
 896                 # Above command exits
 897         fi
 898 else
 899         case `get_zonestate $clone_src` in
 900                 installed)
 901                         zoneadm -z $zone_name clone $clone_src
 902                         ;;
 903                 running)
 904                         zoneadm -z $clone_src halt
 905                         zoneadm -z $zone_name clone $clone_src
 906                         zoneadm -z $clone_src boot
 907                         ;;
 908                 *)
 909                         log_error "clone source zone in unexpected state"
 910                         cleanup_zonecfg
 911                         ;;
 912         esac
 913 fi
 914 
 915 #
 916 # We're now past the point where cleaning up makes sense.
 917 #
 918 trap - SIGINT
 919 
 920 zoneadm -z $zone_name mount
 921 if [[ "$?" -eq 0 ]]; then
 922         #
 923         # Move over the sysidcfg file created earlier
 924         #
 925         # mv $sysid_tmpfile $zone_base/$zone_name/root/etc/sysidcfg
 926     # Clean up and install the one-time rc3 file.
 927     echo "/bin/rm -f /etc/rc3.d/S99hacky-addrconf" >>$rc3_tmpfile
 928     mv $rc3_tmpfile $zone_base/$zone_name/root/etc/rc3.d/S99hacky-addrconf
 929     echo "Here's the file: (press RETURN) to continue"
 930     echo "-------"
 931     cat $zone_base/$zone_name/root/etc/rc3.d/S99hacky-addrconf
 932     echo "-------"
 933     read
 934 
 935         #
 936         # We'll want a hosts file entry for the zone name.
 937         #
 938         if [ ! -z "${intf_ips[0]}" ]; then
 939                 if [[ -z "${intf_names[0]}" ]]; then
 940                         echo "${intf_ips[0]} $zone_name" >> \
 941                             $zone_base/$zone_name/root/etc/hosts
 942                 else
 943                         echo "${intf_ips[0]} ${intf_names[0]}" >> \
 944                             $zone_base/$zone_name/root/etc/hosts
 945                 fi
 946         fi
 947 
 948         #
 949         # Setup NFSv4 mapid domain in the zone
 950         #
 951         if [[ ! -z "$zone_nfsmapid_domain" ]]; then
 952                 echo "NFSMAPID_DOMAIN=$zone_nfsmapid_domain" >> \
 953                     $zone_base/$zone_name/root/etc/default/nfs
 954                 touch $zone_base/$zone_name/root/etc/.NFS4inst_state.domain
 955         fi
 956         zoneadm -z $zone_name unmount
 957 else
 958         log_warn "zoneadm -z $zone_name mount: failed"
 959         log_warn "some zone pre-configuration will be incomplete"
 960 fi
 961 
 962 echo $bar
 963 
 964 #
 965 # Boot the zone.
 966 #
 967 #echo "Booting zone $zone_name"
 968 zoneadm -z $zone_name boot
 969 
 970 if [[ "$zone_open" -eq 1 ]]; then
 971         open_for_testing
 972 fi
 973 
 974 zoneadm -z $zone_name reboot
 975 # In OmniOS, these aren't needed.  They might be in other IPS-running distros.
 976 #rm $zone_base/$zone_name/root/etc/sysidcfg
 977 #rm $zone_base/$zone_name/root/etc/.UNCONFIGURED
 978 
 979 #
 980 # Allow empty passwords for user login.
 981 #
 982 login_file=$zone_base/$zone_name/root/etc/default/login
 983 sed 's/PASSREQ=.*/PASSREQ=NO/' ${login_file} > ${login_file}.tmp
 984 mv ${login_file}.tmp ${login_file}
 985 
 986 echo "`basename $0` completed successfully."