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