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 # Restrict executables to /usr/bin and /usr/sbin
  31 PATH=/usr/bin:/usr/sbin
  32 export PATH
  33 unset LD_LIBRARY_PATH
  34 
  35 . /usr/lib/brand/ipkg/common.ksh
  36 
  37 LOGFILE=
  38 EXIT_CODE=1
  39 
  40 # Clean up on failure
  41 trap_exit()
  42 {
  43         if (( $ZONE_IS_MOUNTED != 0 )); then
  44                 error "$v_unmount"
  45                 zoneadm -z $ZONENAME unmount
  46         fi
  47 
  48         exit $EXIT_CODE
  49 }
  50 
  51 #
  52 # For an exclusive stack zone, fix up the network configuration files.
  53 # We need to do this even if unconfiguring the zone so sys-unconfig works
  54 # correctly.
  55 #
  56 fix_net()
  57 {
  58         [[ "$STACK_TYPE" == "shared" ]] && return
  59 
  60         NETIF_CNT=0
  61         for i in $ZONEROOT/etc/hostname.* $ZONEROOT/etc/dhcp.*
  62         do
  63                 if [[ -f "$i" ]]; then
  64                         NETIF_CNT=$(expr $NETIF_CNT + 1)
  65                         OLD_HOSTNET="$i"
  66                 fi
  67         done
  68         if (( $NETIF_CNT != 1 )); then
  69                 vlog "$v_nonetfix"
  70                 return
  71         fi
  72 
  73         NET=$(LC_ALL=C zonecfg -z $ZONENAME info net)
  74         if (( $? != 0 )); then
  75                 error "$e_badinfo" "net"
  76                 return
  77         fi
  78 
  79         NETIF=$(echo $NET | nawk '{
  80                 for (i = 1; i < NF; i++) {
  81                         if ($i == "physical:") {
  82                                 if (length(net) == 0) {
  83                                         i++
  84                                         net = $i
  85                                 } else {
  86                                         multiple=1
  87                                 }
  88                         }
  89                 }
  90         }
  91         END {   if (!multiple)
  92                         print net
  93         }')
  94 
  95         if [[ -z "$NETIF" ]]; then
  96                 vlog "$v_nonetfix"
  97                 return
  98         fi
  99 
 100         NEWHOSTNET=${OLD_HOSTNET%*.*}
 101         if [[ "$OLD_HOSTNET" != "$NEWHOSTNET.$NETIF" ]]; then
 102                 safe_move $OLD_HOSTNET $NEWHOSTNET.$NETIF
 103         fi
 104 }
 105 
 106 #
 107 # Disable all of the shares since the zone cannot be an NFS server.
 108 # Note that we disable the various instances of the svc:/network/shares/group
 109 # SMF service in the fix_smf function. 
 110 #
 111 fix_nfs()
 112 {
 113         zonedfs=$ZONEROOT/etc/dfs
 114 
 115         if [[ -h $zonedfs/dfstab || ! -f $zonedfs/dfstab ]]; then
 116                 error "$e_badfile" "/etc/dfs/dfstab"
 117                 return
 118         fi
 119 
 120         tmpfile=$(mktemp -t)
 121         if [[ -z "$tmpfile" ]]; then
 122                 error "$e_tmpfile"
 123                 return
 124         fi
 125 
 126         nawk '{
 127                 if (substr($1, 0, 1) == "#") {
 128                         print $0
 129                 } else {
 130                         print "#", $0
 131                         modified=1
 132                 }
 133         }
 134         END {
 135                 if (modified == 1) {
 136                         printf("# Modified by p2v ")
 137                         system("/usr/bin/date")
 138                         exit 0
 139                 }
 140                 exit 1
 141         }' $zonedfs/dfstab >>$tmpfile
 142 
 143         if (( $? == 0 )); then
 144                 if [[ ! -f $zonedfs/dfstab.pre_p2v ]]; then
 145                         safe_copy $zonedfs/dfstab $zonedfs/dfstab.pre_p2v
 146                 fi
 147                 safe_copy $tmpfile $zonedfs/dfstab
 148         fi
 149         rm -f $tmpfile
 150 }
 151 
 152 #
 153 # Comment out most of the old mounts since they are either unneeded or
 154 # likely incorrect within a zone.  Specific mounts can be manually 
 155 # reenabled if the corresponding device is added to the zone.
 156 #
 157 fix_vfstab()
 158 {
 159         if [[ -h $ZONEROOT/etc/vfstab || ! -f $ZONEROOT/etc/vfstab ]]; then
 160                 error "$e_badfile" "/etc/vfstab"
 161                 return
 162         fi
 163 
 164         tmpfile=$(mktemp -t)
 165         if [[ -z "$tmpfile" ]]; then
 166                 error "$e_tmpfile"
 167                 return
 168         fi
 169 
 170         nawk '{
 171                 if (substr($1, 0, 1) == "#") {
 172                         print $0
 173                 } else if ($1 == "fd" || $1 == "/proc" || $1 == "swap" ||
 174                     $1 == "ctfs" || $1 == "objfs" || $1 == "sharefs" ||
 175                     $4 == "nfs" || $4 == "lofs") {
 176                         print $0
 177                 } else {
 178                         print "#", $0
 179                         modified=1
 180                 }
 181         }
 182         END {
 183                 if (modified == 1) {
 184                         printf("# Modified by p2v ")
 185                         system("/usr/bin/date")
 186                         exit 0
 187                 }
 188                 exit 1
 189         }' $ZONEROOT/etc/vfstab >>$tmpfile
 190 
 191         if (( $? == 0 )); then
 192                 if [[ ! -f $ZONEROOT/etc/vfstab.pre_p2v ]]; then
 193                         safe_copy $ZONEROOT/etc/vfstab \
 194                             $ZONEROOT/etc/vfstab.pre_p2v
 195                 fi
 196                 safe_copy $tmpfile $ZONEROOT/etc/vfstab
 197         fi
 198         rm -f $tmpfile
 199 }
 200 
 201 #
 202 # Delete or disable SMF services.
 203 #
 204 fix_smf()
 205 {
 206         SMF_UPGRADE=/a/var/svc/profile/upgrade
 207 
 208         #
 209         # Fix network services if shared stack.
 210         #
 211         if [[ "$STACK_TYPE" == "shared" ]]; then
 212                 vlog "$v_fixnetsvcs"
 213 
 214                 NETPHYSDEF="svc:/network/physical:default"
 215                 NETPHYSNWAM="svc:/network/physical:nwam"
 216 
 217                 vlog "$v_enblsvc" "$NETPHYSDEF"
 218                 zlogin -S $ZONENAME "echo /usr/sbin/svcadm enable $NETPHYSDEF \
 219                     >>$SMF_UPGRADE" </dev/null
 220 
 221                 vlog "$v_dissvc" "$NETPHYSNWAM"
 222                 zlogin -S $ZONENAME \
 223                     "echo /usr/sbin/svcadm disable $NETPHYSNWAM \
 224                     >>$SMF_UPGRADE" </dev/null
 225 
 226                 # Disable routing svcs.
 227                 vlog "$v_dissvc" 'svc:/network/routing/*'
 228                 zlogin -S $ZONENAME \
 229                     "echo /usr/sbin/svcadm disable 'svc:/network/routing/*' \
 230                     >>$SMF_UPGRADE" </dev/null
 231         fi
 232 
 233         #
 234         # Disable well-known services that don't run in a zone.
 235         #
 236         vlog "$v_rminvalidsvcs"
 237         for i in $(egrep -hv "^#" \
 238             /usr/lib/brand/ipkg/smf_disable.lst \
 239             /etc/brand/ipkg/smf_disable.conf)
 240         do
 241                 # Disable the svc.
 242                 vlog "$v_dissvc" "$i"
 243                 zlogin -S $ZONENAME \
 244                     "echo /usr/sbin/svcadm disable $i >>$SMF_UPGRADE" </dev/null
 245         done
 246 
 247         #
 248         # Since zones can't be NFS servers, disable all of the instances of
 249         # the shares svc.
 250         #
 251         vlog "$v_dissvc" 'svc:/network/shares/*'
 252         zlogin -S $ZONENAME \
 253             "echo /usr/sbin/svcadm disable 'svc:/network/shares/*' \
 254             >>$SMF_UPGRADE" </dev/null
 255 }
 256 
 257 #
 258 # Remove well-known pkgs that do not work inside a zone.
 259 #
 260 rm_pkgs()
 261 {
 262         for i in $(egrep -hv "^#" /usr/lib/brand/ipkg/pkgrm.lst \
 263             /etc/brand/ipkg/pkgrm.conf)
 264         do
 265                 pkg info $i >/dev/null 2>&1
 266                 if (( $? != 0 )); then
 267                         continue
 268                 fi
 269 
 270                 vlog "$v_rmpkg" "$i"
 271                 zlogin -S $ZONENAME LC_ALL=C \
 272                     /usr/bin/pkg -R /a uninstall -r $i </dev/null >&2 || \
 273                     error "$e_rmpkg" $i
 274         done
 275 }
 276 
 277 #
 278 # Zoneadmd writes a one-line index file into the zone when the zone boots,
 279 # so any information about installed zones from the original system will
 280 # be lost at that time.  Here we'll warn the sysadmin about any pre-existing
 281 # zones that they might want to clean up by hand, but we'll leave the zonepaths
 282 # in place in case they're on shared storage and will be migrated to
 283 # a new host.
 284 #
 285 warn_zones()
 286 {
 287         zoneconfig=$ZONEROOT/etc/zones
 288 
 289         if [[ -h $zoneconfig/index || ! -f $zoneconfig/index ]]; then
 290                 error "$e_badfile" "/etc/zones/index"
 291                 return
 292         fi
 293 
 294         NGZ=$(nawk -F: '{
 295                 if (substr($1, 0, 1) == "#" || $1 == "global")
 296                         continue
 297 
 298                 if ($2 == "installed")
 299                         printf("%s ", $1)
 300         }' $zoneconfig/index)
 301 
 302         # Return if there are no installed zones to warn about.
 303         [[ -z "$NGZ" ]] && return
 304 
 305         log "$v_rmzones" "$NGZ"
 306 
 307         NGZP=$(nawk -F: '{
 308                 if (substr($1, 0, 1) == "#" || $1 == "global")
 309                         continue
 310 
 311                 if ($2 == "installed")
 312                         printf("%s ", $3)
 313         }' $zoneconfig/index)
 314 
 315         log "$v_rmzonepaths"
 316 
 317         for i in $NGZP
 318         do
 319                 log "    %s" "$i"
 320         done
 321 }
 322 
 323 #
 324 # failure should unmount the zone if necessary;
 325 #
 326 ZONE_IS_MOUNTED=0
 327 trap trap_exit EXIT
 328 
 329 #
 330 # Parse the command line options.
 331 #
 332 OPT_U=
 333 OPT_V=
 334 OPT_L=
 335 while getopts "b:uvl:" opt
 336 do
 337         case "$opt" in
 338                 u)      OPT_U="-u";;
 339                 v)      OPT_V="-v";;
 340                 l)      LOGFILE="$OPTARG"; OPT_L="-l \"$OPTARG\"";;
 341                 *)      exit 1;;
 342         esac
 343 done
 344 shift OPTIND-1
 345 
 346 (( $# != 2 )) && exit 1
 347 
 348 [[ -n $LOGFILE ]] && exec 2>>$LOGFILE
 349 
 350 ZONENAME=$1
 351 ZONEPATH=$2
 352 ZONEROOT=$ZONEPATH/root
 353 
 354 e_badinfo=$(gettext "Failed to get '%s' zone resource")
 355 e_badfile=$(gettext "Invalid '%s' file within the zone")
 356 e_tmpfile=$(gettext "Unable to create temporary file")
 357 v_mkdirs=$(gettext "Creating mount points")
 358 v_nonetfix=$(gettext "Cannot update /etc/hostname.{net} file")
 359 v_change_var=$(gettext "Changing the pkg variant to nonglobal...")
 360 e_change_var=$(gettext "Changing the pkg variant to nonglobal failed")
 361 v_update=$(gettext "Updating the zone software to match the global zone...")
 362 v_updatedone=$(gettext "Zone software update complete")
 363 e_badupdate=$(gettext "Updating the Zone software failed")
 364 v_adjust=$(gettext "Updating the image to run within a zone")
 365 v_stacktype=$(gettext "Stack type '%s'")
 366 v_rmhollowsvcs=$(gettext "Deleting global zone-only SMF services")
 367 v_fixnetsvcs=$(gettext "Adjusting network SMF services")
 368 v_rminvalidsvcs=$(gettext "Disabling invalid SMF services")
 369 v_collectingsmf=$(gettext "Collecting SMF svc data")
 370 v_delsvc=$(gettext "Delete SMF svc '%s'")
 371 e_delsvc=$(gettext "deleting SMF svc '%s'")
 372 v_enblsvc=$(gettext "Enable SMF svc '%s'")
 373 e_enblsvc=$(gettext "enabling SMF svc '%s'")
 374 v_dissvc=$(gettext "Disable SMF svc '%s'")
 375 e_adminf=$(gettext "Unable to create admin file")
 376 v_rmpkg=$(gettext "Remove package '%s'")
 377 e_rmpkg=$(gettext "removing package '%s'")
 378 v_rmzones=$(gettext "The following zones in this image will be unusable: %s")
 379 v_rmzonepaths=$(gettext "These zonepaths could be removed from this image:")
 380 v_exitgood=$(gettext "Postprocessing successful.")
 381 
 382 #
 383 # Do some validation on the paths we'll be accessing
 384 #
 385 safe_dir etc
 386 safe_dir etc/dfs
 387 safe_dir etc/zones
 388 safe_dir var
 389 safe_dir var/log
 390 safe_dir var/pkg
 391 
 392 # Now do the work to update the zone.
 393 
 394 # Before booting the zone we may need to create a few mnt points, just in
 395 # case they don't exist for some reason.
 396 #
 397 # Whenever we reach into the zone while running in the global zone we
 398 # need to validate that none of the interim directories are symlinks
 399 # that could cause us to inadvertently modify the global zone.
 400 vlog "$v_mkdirs"
 401 if [[ ! -f $ZONEROOT/tmp && ! -d $ZONEROOT/tmp ]]; then
 402         mkdir -m 1777 -p $ZONEROOT/tmp || exit $EXIT_CODE
 403 fi
 404 if [[ ! -f $ZONEROOT/var/run && ! -d $ZONEROOT/var/run ]]; then
 405         mkdir -m 1755 -p $ZONEROOT/var/run || exit $EXIT_CODE
 406 fi
 407 if [[ ! -h $ZONEROOT/etc && ! -f $ZONEROOT/etc/mnttab ]]; then
 408         touch $ZONEROOT/etc/mnttab || exit $EXIT_CODE
 409         chmod 444 $ZONEROOT/etc/mnttab || exit $EXIT_CODE
 410 fi
 411 if [[ ! -f $ZONEROOT/proc && ! -d $ZONEROOT/proc ]]; then
 412         mkdir -m 755 -p $ZONEROOT/proc || exit $EXIT_CODE
 413 fi
 414 if [[ ! -f $ZONEROOT/dev && ! -d $ZONEROOT/dev ]]; then
 415         mkdir -m 755 -p $ZONEROOT/dev || exit $EXIT_CODE
 416 fi
 417 if [[ ! -h $ZONEROOT/etc && ! -h $ZONEROOT/etc/svc && ! -d $ZONEROOT/etc/svc ]]
 418 then
 419         mkdir -m 755 -p $ZONEROOT/etc/svc/volatile || exit $EXIT_CODE
 420 fi
 421 
 422 # Check for zones inside of image.
 423 warn_zones
 424 
 425 STACK_TYPE=$(zoneadm -z $ZONENAME list -p | nawk -F: '{print $7}')
 426 if (( $? != 0 )); then
 427         error "$e_badinfo" "stacktype"
 428 fi
 429 vlog "$v_stacktype" "$STACK_TYPE"
 430 
 431 # Note that we're doing this before update-on-attach has run.
 432 fix_net
 433 fix_nfs
 434 fix_vfstab
 435 
 436 #
 437 # Mount the zone so that we can do all of the updates needed on the zone.
 438 #
 439 vlog "$v_mounting"
 440 ZONE_IS_MOUNTED=1
 441 zoneadm -z $ZONENAME mount -f || fatal "$e_badmount"
 442 
 443 #
 444 # Any errors in these functions are not considered fatal.  The zone can be
 445 # be fixed up manually afterwards and it may need some additional manual
 446 # cleanup in any case.
 447 #
 448 
 449 log "$v_adjust"
 450 # cleanup SMF services
 451 fix_smf
 452 # remove invalid pkgs
 453 rm_pkgs
 454 
 455 vlog "$v_unmount"
 456 zoneadm -z $ZONENAME unmount || fatal "$e_badunmount"
 457 ZONE_IS_MOUNTED=0
 458 
 459 is_brand_labeled
 460 brand_labeled=$?
 461 if (( $brand_labeled == 1 )); then
 462         # The labeled brand needs to mount the zone's root dataset back onto
 463         # ZONEROOT so we can finish processing.
 464         mount_active_ds
 465 fi
 466 
 467 # Change the pkging variant from global zone to non-global zone.
 468 log "$v_change_var"
 469 pkg -R $ZONEROOT change-variant variant.opensolaris.zone=nonglobal || \
 470     fatal "$e_change_var"
 471 
 472 #
 473 # Run update on attach.  State is currently 'incomplete' so use the private
 474 # force-update option.
 475 # This also leaves the zone in the 'installed' state.  This is a known bug
 476 # in 'zoneadm attach'.  We change the zone state back to 'incomplete' for
 477 # now but this can be removed once 'zoneadm attach' is fixed.
 478 #
 479 log "$v_update"
 480 zoneadm -z $ZONENAME attach -U >&2 || fatal "$e_badupdate"
 481 zoneadm -z $ZONENAME mark incomplete || fatal "$e_badupdate"
 482 log "$v_updatedone"
 483 
 484 [[ -n $OPT_U ]] && unconfigure_zone
 485 
 486 (( $brand_labeled == 1 )) && mount_active_ds
 487 
 488 trap - EXIT
 489 vlog "$v_exitgood"
 490 exit 0