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