1 #!/bin/ksh -p
   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 # Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
  23 # Use is subject to license terms.
  24 #
  25 
  26 # NOTE: this script runs in the global zone and touches the non-global
  27 # zone, so care should be taken to validate any modifications so that they
  28 # are safe.
  29 
  30 . /usr/lib/brand/shared/common.ksh
  31 
  32 LOGFILE=
  33 MSG_PREFIX="p2v: "
  34 EXIT_CODE=1
  35 
  36 usage()
  37 {
  38         echo "$0 [-s] [-m msgprefix] [-u] [-v] [-b patchid]* zonename" >&2
  39         exit $EXIT_CODE
  40 }
  41 
  42 # Clean up on interrupt
  43 trap_cleanup()
  44 {
  45         msg=$(gettext "Postprocessing cancelled due to interrupt.")
  46         error "$msg"
  47 
  48         if (( $zone_is_running != 0 )); then
  49                 error "$e_shutdown" "$ZONENAME"
  50                 /usr/sbin/zoneadm -z $ZONENAME halt
  51         fi
  52 
  53         exit $EXIT_CODE
  54 }
  55 
  56 #
  57 # For an exclusive stack zone, fix up the network configuration files.
  58 # We need to do this even if unconfiguring the zone so sys-unconfig works
  59 # correctly.
  60 #
  61 fix_net()
  62 {
  63         [[ "$STACK_TYPE" == "shared" ]] && return
  64 
  65         NETIF_CNT=$(/usr/bin/ls $ZONEROOT/etc/hostname.* 2>/dev/null | \
  66             /usr/bin/wc -l)
  67         if (( $NETIF_CNT != 1 )); then
  68                 vlog "$v_nonetfix"
  69                 return
  70         fi
  71 
  72         NET=$(LC_ALL=C /usr/sbin/zonecfg -z $ZONENAME info net)
  73         if (( $? != 0 )); then
  74                 error "$e_badinfo" "net"
  75                 return
  76         fi
  77 
  78         NETIF=$(echo $NET | /usr/bin/nawk '{
  79                 for (i = 1; i < NF; i++) {
  80                         if ($i == "physical:") {
  81                                 if (length(net) == 0) {
  82                                         i++
  83                                         net = $i
  84                                 } else {
  85                                         multiple=1
  86                                 }
  87                         }
  88                 }
  89         }
  90         END {   if (!multiple)
  91                         print net
  92         }')
  93 
  94         if [[ -z "$NETIF" ]]; then
  95                 vlog "$v_nonetfix"
  96                 return
  97         fi
  98 
  99         OLD_HOSTNET=$(/usr/bin/ls $ZONEROOT/etc/hostname.*)
 100         if [[ "$OLD_HOSTNET" != "$ZONEROOT/etc/hostname.$NETIF" ]]; then
 101                 safe_move $OLD_HOSTNET $ZONEROOT/etc/hostname.$NETIF
 102         fi
 103 }
 104 
 105 #
 106 # Disable all of the shares since the zone cannot be an NFS server.
 107 # Note that we disable the various instances of the svc:/network/shares/group
 108 # SMF service in the fix_smf function. 
 109 #
 110 fix_nfs()
 111 {
 112         zonedfs=$ZONEROOT/etc/dfs
 113 
 114         if [[ -h $zonedfs/dfstab || ! -f $zonedfs/dfstab ]]; then
 115                 error "$e_badfile" "/etc/dfs/dfstab"
 116                 return
 117         fi
 118 
 119         tmpfile=$(/usr/bin/mktemp -t -p /var/tmp)
 120         if [[ -z "$tmpfile" ]]; then
 121                 error "$e_tmpfile"
 122                 return
 123         fi
 124 
 125         /usr/bin/nawk '{
 126                 if (substr($1, 0, 1) == "#") {
 127                         print $0
 128                 } else {
 129                         print "#", $0
 130                         modified=1
 131                 }
 132         }
 133         END {
 134                 if (modified == 1) {
 135                         printf("# Modified by p2v ")
 136                         system("/usr/bin/date")
 137                         exit 0
 138                 }
 139                 exit 1
 140         }' $zonedfs/dfstab >>$tmpfile
 141 
 142         if (( $? == 0 )); then
 143                 if [[ ! -f $zonedfs/dfstab.pre_p2v ]]; then
 144                         safe_copy $zonedfs/dfstab $zonedfs/dfstab.pre_p2v
 145                 fi
 146                 safe_copy $tmpfile $zonedfs/dfstab
 147         fi
 148         /usr/bin/rm -f $tmpfile
 149 }
 150 
 151 #
 152 # Comment out most of the old mounts since they are either unneeded or
 153 # likely incorrect within a zone.  Specific mounts can be manually 
 154 # reenabled if the corresponding device is added to the zone.
 155 #
 156 fix_vfstab()
 157 {
 158         if [[ -h $ZONEROOT/etc/vfstab || ! -f $ZONEROOT/etc/vfstab ]]; then
 159                 error "$e_badfile" "/etc/vfstab"
 160                 return
 161         fi
 162 
 163         tmpfile=$(/usr/bin/mktemp -t -p /var/tmp)
 164         if [[ -z "$tmpfile" ]]; then
 165                 error "$e_tmpfile"
 166                 return
 167         fi
 168 
 169         /usr/bin/nawk '{
 170                 if (substr($1, 0, 1) == "#") {
 171                         print $0
 172                 } else if ($1 == "fd" || $1 == "/proc" || $1 == "swap" ||
 173                     $1 == "ctfs" || $1 == "objfs" || $1 == "sharefs" ||
 174                     $4 == "nfs" || $4 == "lofs") {
 175                         print $0
 176                 } else {
 177                         print "#", $0
 178                         modified=1
 179                 }
 180         }
 181         END {
 182                 if (modified == 1) {
 183                         printf("# Modified by p2v ")
 184                         system("/usr/bin/date")
 185                         exit 0
 186                 }
 187                 exit 1
 188         }' $ZONEROOT/etc/vfstab >>$tmpfile
 189 
 190         if (( $? == 0 )); then
 191                 if [[ ! -f $ZONEROOT/etc/vfstab.pre_p2v ]]; then
 192                         safe_copy $ZONEROOT/etc/vfstab \
 193                             $ZONEROOT/etc/vfstab.pre_p2v
 194                 fi
 195                 safe_copy $tmpfile $ZONEROOT/etc/vfstab
 196         fi
 197         /usr/bin/rm -f $tmpfile
 198 }
 199 
 200 #
 201 # Delete or disable SMF services.
 202 # Zone is booted to milestone=none when this function is called.
 203 #
 204 fix_smf()
 205 {
 206         #
 207         # Delete services that are delivered in hollow pkgs.
 208         #
 209         # Start by getting the svc manifests that are delivered by hollow
 210         # pkgs then use 'svccfg inventory' to get the names of the svcs
 211         # delivered by those manifests.  The svc names are saved into a
 212         # temporary file.  We then login to the zone and delete them from SMF
 213         # so that the various dependencies also get cleaned up properly.
 214         #
 215 
 216         smftmpfile=$(/usr/bin/mktemp -t -p /var/tmp smf.XXXXXX)
 217         if [[ -z "$smftmpfile" ]]; then
 218                 error "$e_tmpfile"
 219                 return
 220         fi
 221 
 222         for i in /var/sadm/pkg/*
 223         do
 224                 pkg=$(/usr/bin/basename $i)
 225                 [[ ! -f /var/sadm/pkg/$pkg/save/pspool/$pkg/pkgmap ]] && \
 226                     continue
 227 
 228                 manifests=$(/usr/bin/nawk '{if ($2 == "f" &&
 229                     substr($4, 1, 17) == "var/svc/manifest/") print $4}' \
 230                     /var/sadm/pkg/$pkg/save/pspool/$pkg/pkgmap)
 231 
 232                 if [[ -n "$manifests" ]]; then
 233                         /usr/bin/egrep -s "SUNW_PKG_HOLLOW=true" \
 234                             /var/sadm/pkg/$pkg/pkginfo || continue
 235 
 236                         for j in $manifests
 237                         do
 238                                 svcs=$(SVCCFG_NOVALIDATE=1 /usr/sbin/svccfg \
 239                                     inventory /$j)
 240                                 for k in $svcs
 241                                 do
 242                                         case $k in
 243                                         *:default)
 244                                                 # ignore default instance
 245                                                 ;;
 246                                         *)
 247                                                 echo $k >> $smftmpfile
 248                                                 ;;
 249                                         esac
 250                                 done
 251                         done
 252                 fi
 253         done
 254 
 255         # 
 256         # Zone was already booted to milestone=none, wait until SMF door exists.
 257         #
 258         for i in 0 1 2 3 4 5 6 7 8 9
 259         do
 260                 [[ -r $ZONEROOT/etc/svc/volatile/repository_door ]] && break
 261                 sleep 5
 262         done
 263 
 264         if [[ $i -eq 9 && ! -r $ZONEROOT/etc/svc/volatile/repository_door ]];
 265         then
 266                 error "$e_nosmf"
 267                 /usr/bin/rm -f $smftmpfile
 268                 return
 269         fi
 270 
 271         insttmpfile=$(/usr/bin/mktemp -t -p /var/tmp instsmf.XXXXXX)
 272         if [[ -z "$insttmpfile" ]]; then
 273                 error "$e_tmpfile"
 274                 /usr/bin/rm -f $smftmpfile
 275                 return
 276         fi
 277 
 278         # Get a list of the svcs that exist in the zone.
 279         /usr/sbin/zlogin -S $ZONENAME /usr/bin/svcs -aH | \
 280             /usr/bin/nawk '{print $3}' >>$insttmpfile
 281 
 282         [[ -n $LOGFILE ]] && \
 283             printf "[$(date)] ${MSG_PREFIX}${v_svcsinzone}\n" >&2
 284         [[ -n $LOGFILE ]] && cat $insttmpfile >&2
 285 
 286         vlog "$v_rmhollowsvcs"
 287         for i in $(cat $smftmpfile)
 288         do
 289                 # Skip svcs not installed in the zone.
 290                 /usr/bin/egrep -s "$i:" $insttmpfile || continue
 291 
 292                 # Delete the svc.
 293                 vlog "$v_delsvc" "$i"
 294                 /usr/sbin/zlogin -S $ZONENAME /usr/sbin/svccfg delete $i >&2 \
 295                     || error "$e_delsvc" $i
 296         done
 297 
 298         /usr/bin/rm -f $smftmpfile
 299 
 300         #
 301         # Fix network services if shared stack.
 302         #
 303         if [[ "$STACK_TYPE" == "shared" ]]; then
 304                 vlog "$v_fixnetsvcs"
 305 
 306                 NETPHYSDEF="svc:/network/physical:default"
 307                 NETPHYSNWAM="svc:/network/physical:nwam"
 308 
 309                 /usr/bin/egrep -s "$NETPHYSDEF" $insttmpfile
 310                 if (( $? == 0 )); then
 311                         vlog "$v_enblsvc" "$NETPHYSDEF"
 312                         /usr/sbin/zlogin -S $ZONENAME \
 313                             /usr/sbin/svcadm enable $NETPHYSDEF || \
 314                             error "$e_dissvc" "$NETPHYSDEF"
 315                 fi
 316 
 317                 /usr/bin/egrep -s "$NETPHYSNWAM" $insttmpfile
 318                 if (( $? == 0 )); then
 319                         vlog "$v_dissvc" "$NETPHYSNWAM"
 320                         /usr/sbin/zlogin -S $ZONENAME \
 321                             /usr/sbin/svcadm disable $NETPHYSNWAM || \
 322                             error "$e_enblsvc" "$NETPHYSNWAM"
 323                 fi
 324 
 325                 for i in $(/usr/bin/egrep network/routing $insttmpfile)
 326                 do
 327                         # Disable the svc.
 328                         vlog "$v_dissvc" "$i"
 329                         /usr/sbin/zlogin -S $ZONENAME \
 330                             /usr/sbin/svcadm disable $i || \
 331                             error "$e_dissvc" $i
 332                 done
 333         fi
 334 
 335         #
 336         # Disable well-known services that don't run in a zone.
 337         #
 338         vlog "$v_rminvalidsvcs"
 339         for i in $(/usr/bin/egrep -hv "^#" \
 340             /usr/lib/brand/native/smf_disable.lst \
 341             /etc/brand/native/smf_disable.conf)
 342         do
 343                 # Skip svcs not installed in the zone.
 344                 /usr/bin/egrep -s "$i:" $insttmpfile || continue
 345 
 346                 # Disable the svc.
 347                 vlog "$v_dissvc" "$i"
 348                 /usr/sbin/zlogin -S $ZONENAME /usr/sbin/svcadm disable $i || \
 349                     error "$e_dissvc" $i
 350         done
 351 
 352         #
 353         # Since zones can't be NFS servers, disable all of the instances of
 354         # the shares svc.
 355         #
 356         for i in $(/usr/bin/egrep network/shares/group $insttmpfile)
 357         do
 358                 vlog "$v_dissvc" "$i"
 359                 /usr/sbin/zlogin -S $ZONENAME /usr/sbin/svcadm disable $i || \
 360                     error "$e_dissvc" $i
 361         done
 362 
 363         /usr/bin/rm -f $insttmpfile
 364 }
 365 
 366 #
 367 # Remove well-known pkgs that do not work inside a zone.
 368 #
 369 rm_pkgs()
 370 {
 371         /usr/bin/cat <<-EOF > $ZONEROOT/tmp/admin || fatal "$e_adminf"
 372         mail=
 373         instance=overwrite
 374         partial=nocheck
 375         runlevel=nocheck
 376         idepend=nocheck
 377         rdepend=nocheck
 378         space=nocheck
 379         setuid=nocheck
 380         conflict=nocheck
 381         action=nocheck
 382         basedir=default
 383         EOF
 384 
 385         for i in $(/usr/bin/egrep -hv "^#" /usr/lib/brand/native/pkgrm.lst \
 386             /etc/brand/native/pkgrm.conf)
 387         do
 388                 [[ ! -d $ZONEROOT/var/sadm/pkg/$i ]] && continue
 389 
 390                 vlog "$v_rmpkg" "$i"
 391                 /usr/sbin/zlogin -S $ZONENAME \
 392                     /usr/sbin/pkgrm -na /tmp/admin $i >&2 || error "$e_rmpkg" $i
 393         done
 394 }
 395 
 396 #
 397 # Zoneadmd writes a one-line index file into the zone when the zone boots,
 398 # so any information about installed zones from the original system will
 399 # be lost at that time.  Here we'll warn the sysadmin about any pre-existing
 400 # zones that they might want to clean up by hand, but we'll leave the zonepaths
 401 # in place in case they're on shared storage and will be migrated to
 402 # a new host.
 403 #
 404 warn_zones()
 405 {
 406         zoneconfig=$ZONEROOT/etc/zones
 407 
 408         if [[ -h $zoneconfig/index || ! -f $zoneconfig/index ]]; then
 409                 error "$e_badfile" "/etc/zones/index"
 410                 return
 411         fi
 412 
 413         NGZ=$(/usr/bin/nawk -F: '{
 414                 if (substr($1, 0, 1) == "#" || $1 == "global")
 415                         continue
 416 
 417                 if ($2 == "installed")
 418                         printf("%s ", $1)
 419         }' $zoneconfig/index)
 420 
 421         # Return if there are no installed zones to warn about.
 422         [[ -z "$NGZ" ]] && return
 423 
 424         log "$v_rmzones" "$NGZ"
 425 
 426         NGZP=$(/usr/bin/nawk -F: '{
 427                 if (substr($1, 0, 1) == "#" || $1 == "global")
 428                         continue
 429 
 430                 if ($2 == "installed")
 431                         printf("%s ", $3)
 432         }' $zoneconfig/index)
 433 
 434         log "$v_rmzonepaths"
 435 
 436         for i in $NGZP
 437         do
 438                 log "    %s" "$i"
 439         done
 440 }
 441 
 442 unset LD_LIBRARY_PATH
 443 PATH=/usr/sbin:/usr/bin
 444 export PATH
 445 
 446 #
 447 # ^C Should cleanup; if the zone is running, it should try to halt it.
 448 #
 449 zone_is_running=0
 450 trap trap_cleanup INT
 451 
 452 #
 453 # Parse the command line options.
 454 #
 455 unset backout
 456 OPT_U=
 457 OPT_V=
 458 OPT_M=
 459 OPT_L=
 460 while getopts "b:uvm:l:" opt
 461 do
 462         case "$opt" in
 463                 b)      if [[ -n "$backout" ]]; then
 464                                 backout="$backout -b $OPTARG"
 465                         else
 466                                 backout="-b $OPTARG"
 467                         fi
 468                         ;;
 469                 u)      OPT_U="-u";;
 470                 v)      OPT_V="-v";;
 471                 m)      MSG_PREFIX="$OPTARG"; OPT_M="-m \"$OPTARG\"";;
 472                 l)      LOGFILE="$OPTARG"; OPT_L="-l \"$OPTARG\"";;
 473                 *)      usage;;
 474         esac
 475 done
 476 shift OPTIND-1
 477 
 478 (( $# < 1 )) && usage
 479 
 480 (( $# > 2 )) && usage
 481 
 482 [[ -n $LOGFILE ]] && exec 2>>$LOGFILE
 483 
 484 ZONENAME=$1
 485 ZONEPATH=$2
 486 ZONEROOT=$ZONEPATH/root
 487 
 488 e_badinfo=$(gettext "Failed to get '%s' zone resource")
 489 e_badfile=$(gettext "Invalid '%s' file within the zone")
 490 e_tmpfile=$(gettext "Unable to create temporary file")
 491 v_mkdirs=$(gettext "Creating mount points")
 492 v_nonetfix=$(gettext "Cannot update /etc/hostname.{net} file")
 493 v_update=$(gettext "Updating the zone software to match the global zone...")
 494 v_updatedone=$(gettext "Zone software update complete")
 495 e_badupdate=$(gettext "Updating the Zone software failed")
 496 v_adjust=$(gettext "Updating the image to run within a zone")
 497 v_stacktype=$(gettext "Stack type '%s'")
 498 v_booting=$(gettext "Booting zone to single user mode")
 499 e_badboot=$(gettext "Zone boot failed")
 500 e_nosmf=$(gettext "ERROR: SMF repository unavailable.")
 501 e_nosingleuser=$(gettext "ERROR: zone did not finish booting to single-user.")
 502 v_svcsinzone=$(gettext "The following SMF services are installed:")
 503 v_rmhollowsvcs=$(gettext "Deleting SMF services from hollow packages")
 504 v_fixnetsvcs=$(gettext "Adjusting network SMF services")
 505 v_rminvalidsvcs=$(gettext "Disabling invalid SMF services")
 506 v_delsvc=$(gettext "Delete SMF svc '%s'")
 507 e_delsvc=$(gettext "deleting SMF svc '%s'")
 508 v_enblsvc=$(gettext "Enable SMF svc '%s'")
 509 e_enblsvc=$(gettext "enabling SMF svc '%s'")
 510 v_dissvc=$(gettext "Disable SMF svc '%s'")
 511 e_dissvc=$(gettext "disabling SMF svc '%s'")
 512 e_adminf=$(gettext "Unable to create admin file")
 513 v_rmpkg=$(gettext "Remove package '%s'")
 514 e_rmpkg=$(gettext "removing package '%s'")
 515 v_rmzones=$(gettext "The following zones in this image will be unusable: %s")
 516 v_rmzonepaths=$(gettext "These zonepaths could be removed from this image:")
 517 v_unconfig=$(gettext "Performing zone sys-unconfig")
 518 e_unconfig=$(gettext "sys-unconfig failed")
 519 v_halting=$(gettext "Halting zone")
 520 e_shutdown=$(gettext "Shutting down zone %s...")
 521 e_badhalt=$(gettext "Zone halt failed")
 522 v_exitgood=$(gettext "Postprocessing successful.")
 523 e_exitfail=$(gettext "Postprocessing failed.")
 524 
 525 #
 526 # Do some validation on the paths we'll be accessing
 527 #
 528 safe_dir etc
 529 safe_dir etc/dfs
 530 safe_dir etc/zones
 531 safe_dir var
 532 
 533 # Now do the work to update the zone.
 534 
 535 # Before booting the zone we may need to create a few mnt points, just in
 536 # case they don't exist for some reason.
 537 #
 538 # Whenever we reach into the zone while running in the global zone we
 539 # need to validate that none of the interim directories are symlinks
 540 # that could cause us to inadvertently modify the global zone.
 541 vlog "$v_mkdirs"
 542 if [[ ! -f $ZONEROOT/tmp && ! -d $ZONEROOT/tmp ]]; then
 543         mkdir -m 1777 -p $ZONEROOT/tmp || exit $EXIT_CODE
 544 fi
 545 if [[ ! -f $ZONEROOT/var/run && ! -d $ZONEROOT/var/run ]]; then
 546         mkdir -m 1755 -p $ZONEROOT/var/run || exit $EXIT_CODE
 547 fi
 548 if [[ ! -h $ZONEROOT/etc && ! -f $ZONEROOT/etc/mnttab ]]; then
 549         /usr/bin/touch $ZONEROOT/etc/mnttab || exit $EXIT_CODE
 550         /usr/bin/chmod 444 $ZONEROOT/etc/mnttab || exit $EXIT_CODE
 551 fi
 552 if [[ ! -f $ZONEROOT/proc && ! -d $ZONEROOT/proc ]]; then
 553         mkdir -m 755 -p $ZONEROOT/proc || exit $EXIT_CODE
 554 fi
 555 if [[ ! -f $ZONEROOT/dev && ! -d $ZONEROOT/dev ]]; then
 556         mkdir -m 755 -p $ZONEROOT/dev || exit $EXIT_CODE
 557 fi
 558 if [[ ! -h $ZONEROOT/etc && ! -h $ZONEROOT/etc/svc && ! -d $ZONEROOT/etc/svc ]]
 559 then
 560         mkdir -m 755 -p $ZONEROOT/etc/svc/volatile || exit $EXIT_CODE
 561 fi
 562 
 563 # Check for zones inside of image.
 564 warn_zones
 565 
 566 #
 567 # Run update on attach.  State is currently 'incomplete' so use the private
 568 # force-update option.
 569 #
 570 log "$v_update"
 571 /usr/sbin/zoneadm -z $ZONENAME attach -U $backout >&2
 572 res=$?
 573 if (( $? != 0 )); then
 574         fatal "$e_badupdate"
 575 else
 576         log "$v_updatedone"
 577 fi
 578 
 579 log "$v_adjust"
 580 
 581 #
 582 # Any errors in these functions are not considered fatal.  The zone can be
 583 # be fixed up manually afterwards and it may need some additional manual
 584 # cleanup in any case.
 585 #
 586 
 587 STACK_TYPE=$(/usr/sbin/zoneadm -z $ZONENAME list -p | \
 588     /usr/bin/nawk -F: '{print $7}')
 589 if (( $? != 0 )); then
 590         error "$e_badinfo" "stacktype"
 591 fi
 592 vlog "$v_stacktype" "$STACK_TYPE"
 593 
 594 fix_net
 595 fix_nfs
 596 fix_vfstab
 597 
 598 vlog "$v_booting"
 599 
 600 #
 601 # Boot the zone so that we can do all of the SMF updates needed on the zone's
 602 # repository.
 603 #
 604 
 605 zone_is_running=1
 606 
 607 # The 'update on attach' left the zone installed.
 608 /usr/sbin/zoneadm -z $ZONENAME boot -f -- -m milestone=none
 609 if (( $? != 0 )); then
 610         error "$e_badboot"
 611         fatal "$e_exitfail"
 612 fi
 613 
 614 # cleanup SMF services
 615 fix_smf
 616 
 617 # remove invalid pkgs
 618 rm_pkgs
 619 
 620 vlog "$v_halting"
 621 /usr/sbin/zoneadm -z $ZONENAME halt
 622 if (( $? != 0 )); then
 623         error "$e_badhalt"
 624         failed=1
 625 fi
 626 zone_is_running=0
 627 
 628 if [[ -z $failed && -n $OPT_U ]]; then
 629         #
 630         # We're sys-unconfiging the zone.  This will halt the zone, however
 631         # there are problems with sys-unconfig and it usually hangs when the
 632         # zone is booted to milestone=none.  This is why we previously halted
 633         # the zone.  We now boot to milestone=single-user.  Again, the
 634         # sys-unconfig can hang if the zone is still in the process of
 635         # booting when we try to run sys-unconfig.  Wait until the boot is
 636         # done, which we do by checking for sulogin, or waiting 30 seconds,
 637         # whichever comes first.
 638         #
 639 
 640         vlog "$v_unconfig"
 641 
 642         zone_is_running=1
 643         /usr/sbin/zoneadm -z $ZONENAME boot -- -m milestone=single-user
 644         if (( $? != 0 )); then
 645                 error "$e_badboot"
 646                 fatal "$e_exitfail"
 647         fi
 648 
 649         for i in 0 1 2 3 4 5 6 7 8 9
 650         do
 651                 sleep 10
 652                 /usr/sbin/zlogin $ZONENAME \
 653                     /usr/bin/svcs -H svc:/milestone/single-user:default 2>&1 |
 654                     /usr/bin/nawk '{
 655                         if ($1 == "online")
 656                                 exit 0
 657                         else
 658                                 exit 1
 659                     }' && break
 660         done
 661 
 662         if (( $i == 9 )); then
 663                 vlog "$e_nosingleuser"
 664         fi
 665 
 666         echo "yes" | /usr/sbin/zlogin -S $ZONENAME \
 667             /usr/sbin/sys-unconfig >/dev/null 2>&1
 668         if (( $? != 0 )); then
 669                 error "$e_unconfig"
 670                 failed=1
 671         fi
 672 fi
 673 
 674 
 675 if [[ -n $failed ]]; then
 676         fatal "$e_exitfail"
 677 fi
 678 
 679 vlog "$v_exitgood"
 680 exit 0