#!/bin/ksh -p # # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (the "License"). # You may not use this file except in compliance with the License. # # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE # or http://www.opensolaris.org/os/licensing. # See the License for the specific language governing permissions # and limitations under the License. # # When distributing Covered Code, include this CDDL HEADER in each # file and include the License file at usr/src/OPENSOLARIS.LICENSE. # If applicable, add the following below this CDDL HEADER, with the # fields enclosed by brackets "[]" replaced with your own identifying # information: Portions Copyright [yyyy] [name of copyright owner] # # CDDL HEADER END # # Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. # # NOTE: this script runs in the global zone and touches the non-global # zone, so care should be taken to validate any modifications so that they # are safe. . /usr/lib/brand/solaris10/common.ksh LOGFILE= MSG_PREFIX="p2v: " EXIT_CODE=1 usage() { echo "$0 [-s] [-m msgprefix] [-u] [-v] [-b patchid]* zonename" >&2 exit $EXIT_CODE } # Clean up on interrupt trap_cleanup() { msg=$(gettext "Postprocessing cancelled due to interrupt.") error "$msg" if (( $zone_is_running != 0 )); then error "$e_shutdown" "$ZONENAME" /usr/sbin/zoneadm -z $ZONENAME halt fi # # Delete temporary files created during the hollow package removal # process. # rm -f $hollow_pkgs $hollow_file_list $hollow_dir_list exit $EXIT_CODE } # # Disable any existing live-upgrade configuration. # We have already called safe_dir to validate the etc/lu directory. # fix_lu() { ludir=$ZONEROOT/etc/lu [[ ! -d $ludir ]] && return safe_rm etc/lutab safe_rm etc/lu/.BE_CONFIG safe_rm etc/lu/.CURR_VARS safe_rm etc/lu/ludb.local.xml for i in $ludir/ICF* $ludir/vtoc* $ludir/GRUB* do nm=`basename $i` safe_rm etc/lu/$nm done } # # For an exclusive stack zone, fix up the network configuration files. # We need to do this even if unconfiguring the zone so sys-unconfig works # correctly. # fix_net() { [[ "$STACK_TYPE" == "shared" ]] && return NETIF_CNT=$(/usr/bin/ls $ZONEROOT/etc/hostname.* 2>/dev/null | \ /usr/bin/wc -l) if (( $NETIF_CNT != 1 )); then vlog "$v_nonetfix" return fi NET=$(LC_ALL=C /usr/sbin/zonecfg -z $ZONENAME info net) if (( $? != 0 )); then error "$e_badinfo" "net" return fi NETIF=$(echo $NET | /usr/bin/nawk '{ for (i = 1; i < NF; i++) { if ($i == "physical:") { if (length(net) == 0) { i++ net = $i } else { multiple=1 } } } } END { if (!multiple) print net }') if [[ -z "$NETIF" ]]; then vlog "$v_nonetfix" return fi OLD_HOSTNET=$(/usr/bin/ls $ZONEROOT/etc/hostname.*) if [[ "$OLD_HOSTNET" != "$ZONEROOT/etc/hostname.$NETIF" ]]; then safe_move $OLD_HOSTNET $ZONEROOT/etc/hostname.$NETIF fi } # # Disable all of the shares since the zone cannot be an NFS server. # Note that we disable the various instances of the svc:/network/shares/group # SMF service in the fix_smf function. # fix_nfs() { zonedfs=$ZONEROOT/etc/dfs [[ ! -d $zonedfs ]] && return if [[ -h $zonedfs/dfstab || ! -f $zonedfs/dfstab ]]; then error "$e_badfile" "/etc/dfs/dfstab" return fi tmpfile=$(mktemp -t) if [[ $? == 1 || -z "$tmpfile" ]]; then error "$e_tmpfile" return fi /usr/bin/nawk '{ if (substr($1, 0, 1) == "#") { print $0 } else { print "#", $0 modified=1 } } END { if (modified == 1) { printf("# Modified by p2v ") system("/usr/bin/date") exit 0 } exit 1 }' $zonedfs/dfstab >>$tmpfile if (( $? == 0 )); then if [[ ! -f $zonedfs/dfstab.pre_p2v ]]; then safe_copy $zonedfs/dfstab $zonedfs/dfstab.pre_p2v fi safe_copy $tmpfile $zonedfs/dfstab chown root:sys $zonedfs/dfstab || \ fail_fatal "$f_chown" "$zonedfs/dfstab" chmod 644 $zonedfs/dfstab || \ fail_fatal "$f_chmod" "$zonedfs/dfstab" fi /usr/bin/rm -f $tmpfile } # # Comment out most of the old mounts since they are either unneeded or # likely incorrect within a zone. Specific mounts can be manually # reenabled if the corresponding device is added to the zone. # fix_vfstab() { if [[ -h $ZONEROOT/etc/vfstab || ! -f $ZONEROOT/etc/vfstab ]]; then error "$e_badfile" "/etc/vfstab" return fi tmpfile=$(mktemp -t) if [[ $? == 1 || -z "$tmpfile" ]]; then error "$e_tmpfile" return fi /usr/bin/nawk '{ if (substr($1, 0, 1) == "#") { print $0 } else if ($1 == "fd" || $1 == "/proc" || $1 == "swap" || $1 == "ctfs" || $1 == "objfs" || $1 == "sharefs" || $4 == "nfs" || $4 == "lofs") { print $0 } else { print "#", $0 modified=1 } } END { if (modified == 1) { printf("# Modified by p2v ") system("/usr/bin/date") exit 0 } exit 1 }' $ZONEROOT/etc/vfstab >>$tmpfile if (( $? == 0 )); then if [[ ! -f $ZONEROOT/etc/vfstab.pre_p2v ]]; then safe_copy $ZONEROOT/etc/vfstab \ $ZONEROOT/etc/vfstab.pre_p2v fi safe_copy $tmpfile $ZONEROOT/etc/vfstab chown root:sys $ZONEROOT/etc/vfstab || \ fail_fatal "$f_chown" "$ZONEROOT/etc/vfstab" chmod 644 $ZONEROOT/etc/vfstab || \ fail_fatal "$f_chmod" "$ZONEROOT/etc/vfstab" fi /usr/bin/rm -f $tmpfile } # # Collect the data needed to delete SMF services. Since we're p2v-ing a # physical image there are SMF services which must be deleted. # fix_smf_pre_uoa() { # # Start by getting the svc manifests that are delivered by hollow # pkgs then use 'svccfg inventory' to get the names of the svcs # delivered by those manifests. The svc names are saved into a # temporary file. # SMFTMPFILE=$(mktemp -t smf.XXXXXX) if [[ $? == 1 || -z "$SMFTMPFILE" ]]; then error "$e_tmpfile" return fi for i in $ZONEROOT/var/sadm/pkg/* do pkg=$(/usr/bin/basename $i) [[ ! -f $ZONEROOT/var/sadm/pkg/$pkg/save/pspool/$pkg/pkgmap ]] \ && continue /usr/bin/egrep -s "SUNW_PKG_HOLLOW=true" \ $ZONEROOT/var/sadm/pkg/$pkg/pkginfo || continue for j in $(/usr/bin/nawk '{if ($2 == "f" && substr($4, 1, 17) == "var/svc/manifest/") print $4}' \ $ZONEROOT/var/sadm/pkg/$pkg/save/pspool/$pkg/pkgmap) do svcs=$(SVCCFG_NOVALIDATE=1 \ SVCCFG_REPOSITORY=$ZONEROOT/etc/svc/repository.db \ /usr/sbin/svccfg inventory $ZONEROOT/$j) for k in $svcs do echo $k /$j >> $SMFTMPFILE done done done } # # Delete or disable SMF services. # Zone is booted to milestone=none when this function is called. # Use the SMF data collected by fix_smf_pre_uoa() to delete the services. # fix_smf() { # # Zone was already booted to milestone=none, wait until SMF door exists. # for i in 0 1 2 3 4 5 6 7 8 9 do [[ -r $ZONEROOT/etc/svc/volatile/repository_door ]] && break sleep 5 done if [[ $i -eq 9 && ! -r $ZONEROOT/etc/svc/volatile/repository_door ]]; then # # The zone never booted, something is wrong. # error "$e_nosmf" error "$e_bootfail" /usr/bin/rm -f $SMFTMPFILE return 1 fi insttmpfile=$(mktemp -t instsmf.XXXXXX) if [[ $? == 1 || -z "$insttmpfile" ]]; then error "$e_tmpfile" /usr/bin/rm -f $SMFTMPFILE return 1 fi vlog "$v_rmhollowsvcs" while read fmri mfst do # Delete the svc. vlog "$v_delsvc" "$fmri" echo "/usr/sbin/svccfg delete -f $fmri" echo "/usr/sbin/svccfg delhash -d $mfst" echo "rm -f $mfst" done < $SMFTMPFILE > $ZONEROOT/tmp/smf_rm /usr/sbin/zlogin -S $ZONENAME /bin/sh /tmp/smf_rm >/dev/null 2>&1 /usr/bin/rm -f $SMFTMPFILE # Get a list of the svcs that now exist in the zone. LANG=C /usr/sbin/zlogin -S $ZONENAME /usr/bin/svcs -aH | \ /usr/bin/nawk '{print $3}' >>$insttmpfile [[ -n $LOGFILE ]] && \ printf "[$(date)] ${MSG_PREFIX}${v_svcsinzone}\n" >&2 [[ -n $LOGFILE ]] && cat $insttmpfile >&2 # # Import ip-interface-management service in S10C, network # loopback service requires ipmgmtd in exclusive stack zones. # /usr/sbin/zlogin -S $ZONENAME /usr/sbin/svccfg import \ $ZONEROOT/var/svc/manifest/network/network-ipmgmt.xml # # Fix network services if shared stack. # if [[ "$STACK_TYPE" == "shared" ]]; then vlog "$v_fixnetsvcs" NETPHYSDEF="svc:/network/physical:default" NETPHYSNWAM="svc:/network/physical:nwam" /usr/bin/egrep -s "$NETPHYSDEF" $insttmpfile if (( $? == 0 )); then vlog "$v_enblsvc" "$NETPHYSDEF" /usr/sbin/zlogin -S $ZONENAME \ /usr/sbin/svcadm enable $NETPHYSDEF || \ error "$e_dissvc" "$NETPHYSDEF" fi /usr/bin/egrep -s "$NETPHYSNWAM" $insttmpfile if (( $? == 0 )); then vlog "$v_dissvc" "$NETPHYSNWAM" /usr/sbin/zlogin -S $ZONENAME \ /usr/sbin/svcadm disable $NETPHYSNWAM || \ error "$e_enblsvc" "$NETPHYSNWAM" fi for i in $(/usr/bin/egrep network/routing $insttmpfile) do # Disable the svc. vlog "$v_dissvc" "$i" /usr/sbin/zlogin -S $ZONENAME \ /usr/sbin/svcadm disable $i || \ error "$e_dissvc" $i done fi # # Disable well-known services that don't run in a zone. # vlog "$v_rminvalidsvcs" for i in $(/usr/bin/egrep -hv "^#" \ /usr/lib/brand/solaris10/smf_disable.lst \ /etc/brand/solaris10/smf_disable.conf) do # Skip svcs not installed in the zone. /usr/bin/egrep -s "$i:" $insttmpfile || continue # Disable the svc. vlog "$v_dissvc" "$i" /usr/sbin/zlogin -S $ZONENAME /usr/sbin/svcadm disable $i || \ error "$e_dissvc" $i done # # Since zones can't be NFS servers, disable all of the instances of # the shares svc. # for i in $(/usr/bin/egrep network/shares/group $insttmpfile) do vlog "$v_dissvc" "$i" /usr/sbin/zlogin -S $ZONENAME /usr/sbin/svcadm disable $i || \ error "$e_dissvc" $i done /usr/bin/rm -f $insttmpfile return 0 } # # Remove well-known pkgs that do not work inside a zone. # rm_pkgs() { /usr/bin/cat <<-EOF > $ZONEROOT/tmp/admin || fatal "$e_adminf" mail= instance=overwrite partial=nocheck runlevel=nocheck idepend=nocheck rdepend=nocheck space=nocheck setuid=nocheck conflict=nocheck action=nocheck basedir=default EOF for i in $(/usr/bin/egrep -hv "^#" /usr/lib/brand/solaris10/pkgrm.lst \ /etc/brand/solaris10/pkgrm.conf) do [[ ! -d $ZONEROOT/var/sadm/pkg/$i ]] && continue vlog "$v_rmpkg" "$i" /usr/sbin/zlogin -S $ZONENAME \ /usr/sbin/pkgrm -na /tmp/admin $i >&2 || error "$e_rmpkg" $i done } # # Zoneadmd writes a one-line index file into the zone when the zone boots, # so any information about installed zones from the original system will # be lost at that time. Here we'll warn the sysadmin about any pre-existing # zones that they might want to clean up by hand, but we'll leave the zonepaths # in place in case they're on shared storage and will be migrated to # a new host. # warn_zones() { zoneconfig=$ZONEROOT/etc/zones [[ ! -d $zoneconfig ]] && return if [[ -h $zoneconfig/index || ! -f $zoneconfig/index ]]; then error "$e_badfile" "/etc/zones/index" return fi NGZ=$(/usr/bin/nawk -F: '{ if (substr($1, 0, 1) == "#" || $1 == "global") next if ($2 == "installed") printf("%s ", $1) }' $zoneconfig/index) # Return if there are no installed zones to warn about. [[ -z "$NGZ" ]] && return log "$v_rmzones" "$NGZ" NGZP=$(/usr/bin/nawk -F: '{ if (substr($1, 0, 1) == "#" || $1 == "global") continue if ($2 == "installed") printf("%s ", $3) }' $zoneconfig/index) log "$v_rmzonepaths" for i in $NGZP do log " %s" "$i" done } # # ^C Should cleanup; if the zone is running, it should try to halt it. # zone_is_running=0 trap trap_cleanup INT # # Parse the command line options. # OPT_U= OPT_V= OPT_M= OPT_L= while getopts "uvm:l:" opt do case "$opt" in u) OPT_U="-u";; v) OPT_V="-v";; m) MSG_PREFIX="$OPTARG"; OPT_M="-m \"$OPTARG\"";; l) LOGFILE="$OPTARG"; OPT_L="-l \"$OPTARG\"";; *) usage;; esac done shift OPTIND-1 (( $# < 1 )) && usage (( $# > 2 )) && usage [[ -n $LOGFILE ]] && exec 2>>$LOGFILE ZONENAME=$1 ZONEPATH=$2 # XXX shared/common script currently uses lower case zonename & zonepath zonename="$ZONENAME" zonepath="$ZONEPATH" ZONEROOT=$ZONEPATH/root e_badinfo=$(gettext "Failed to get '%s' zone resource") e_badfile=$(gettext "Invalid '%s' file within the zone") v_mkdirs=$(gettext "Creating mount points") v_nonetfix=$(gettext "Cannot update /etc/hostname.{net} file") v_adjust=$(gettext "Updating the image to run within a zone") v_stacktype=$(gettext "Stack type '%s'") v_booting=$(gettext "Booting zone to single user mode") e_bootfail=$(gettext "Failed to boot zone to single user mode.") e_nosmf=$(gettext "SMF repository unavailable.") v_svcsinzone=$(gettext "The following SMF services are installed:") v_rmhollowsvcs=$(gettext "Deleting SMF services from hollow packages") v_fixnetsvcs=$(gettext "Adjusting network SMF services") v_rminvalidsvcs=$(gettext "Disabling invalid SMF services") v_delsvc=$(gettext "Delete SMF svc '%s'") e_delsvc=$(gettext "deleting SMF svc '%s'") v_enblsvc=$(gettext "Enable SMF svc '%s'") e_enblsvc=$(gettext "enabling SMF svc '%s'") v_dissvc=$(gettext "Disable SMF svc '%s'") e_dissvc=$(gettext "disabling SMF svc '%s'") e_adminf=$(gettext "Unable to create admin file") v_rmpkg=$(gettext "Remove package '%s'") e_rmpkg=$(gettext "removing package '%s'") v_rmzones=$(gettext "The following zones in this image will be unusable: %s") v_rmzonepaths=$(gettext "These zonepaths could be removed from this image:") v_halting=$(gettext "Halting zone") e_shutdown=$(gettext "Shutting down zone %s...") e_badhalt=$(gettext "Zone halt failed") v_exitgood=$(gettext "Postprocessing successful.") e_exitfail=$(gettext "Postprocessing failed.") # # Do some validation on the paths we'll be accessing # safe_dir /etc safe_dir /var safe_dir /var/sadm safe_dir /var/sadm/install safe_dir /var/sadm/pkg safe_opt_dir /etc/dfs safe_opt_dir /etc/lu safe_opt_dir /etc/zones mk_zone_dirs # Now do the work to update the zone. # Check for zones inside of image. warn_zones fix_smf_pre_uoa log "$v_adjust" # # Any errors in these functions are not considered fatal. The zone can be # be fixed up manually afterwards and it may need some additional manual # cleanup in any case. # STACK_TYPE=$(/usr/sbin/zoneadm -z $ZONENAME list -p | \ /usr/bin/nawk -F: '{print $7}') if (( $? != 0 )); then error "$e_badinfo" "stacktype" fi vlog "$v_stacktype" "$STACK_TYPE" fix_lu fix_net fix_nfs fix_vfstab vlog "$v_booting" # # Boot the zone so that we can do all of the SMF updates needed on the zone's # repository. # zone_is_running=1 /usr/sbin/zoneadm -z $ZONENAME boot -f -- -m milestone=none if (( $? != 0 )); then error "$e_badboot" /usr/bin/rm -f $SMFTMPFILE fatal "$e_exitfail" fi # # Remove all files and directories installed by hollow packages. Such files # and directories shouldn't exist inside zones. # hollow_pkgs=$(mktemp -t .hollow.pkgs.XXXXXX) hollow_file_list=$(mktemp $ZONEROOT/.hollow.pkgs.files.XXXXXX) hollow_dir_list=$(mktemp $ZONEROOT/.hollow.pkgs.dirs.XXXXXX) [ -f "$hollow_pkgs" -a -f "$hollow_file_list" -a -f "$hollow_dir_list" ] || { error "$e_tmpfile" rm -f $hollow_pkgs $hollow_file_list $hollow_dir_list fatal "$e_exitfail" } for pkg_name in $ZONEROOT/var/sadm/pkg/*; do grep 'SUNW_PKG_HOLLOW=true' $pkg_name/pkginfo >/dev/null 2>&1 && \ basename $pkg_name >>$hollow_pkgs done /usr/bin/nawk -v hollowpkgs=$hollow_pkgs -v filelist=$hollow_file_list \ -v dirlist=$hollow_dir_list ' BEGIN { while (getline p 0) pkgs[p] = 1; close(hollowpkgs); } { # fld is the field where the pkg names begin. # nm is the file/dir entry name. if ($2 == "f") { fld=10; nm=$1; } else if ($2 == "d") { fld=7; nm=$1; } else if ($2 == "s" || $2 == "l") { fld=4; split($1, a, "="); nm=a[1]; } else { next; } # Determine whether the file or directory is delivered by any # non-hollow packages. Files and directories can be # delivered by multiple pkgs. The file or directory should only # be removed if it is only delivered by hollow packages. for (i = fld; i <= NF; i++) { if (pkgs[get_pkg_name($i)] != 1) { # We encountered a non-hollow package. Skip # this entry. next; } } # The file or directory is only delivered by hollow packages. # Mark it for removal. if (fld != 7) print nm >>filelist else print nm >>dirlist } # Get the clean pkg name from the fld entry. function get_pkg_name(fld) { # Remove any pkg control prefix (e.g. *, !) first = substr(fld, 1, 1) if (match(first, /[A-Za-z]/)) { pname = fld } else { pname = substr(fld, 2) } # Then remove any class action script name pos = index(pname, ":") if (pos != 0) pname = substr(pname, 1, pos - 1) return (pname) } ' $ZONEROOT/var/sadm/install/contents /usr/sbin/zlogin -S $ZONENAME "cat /$(basename $hollow_file_list) | xargs rm -f" /usr/sbin/zlogin -S $ZONENAME "sort -r /$(basename $hollow_dir_list) | \ xargs rmdir >/dev/null 2>&1" rm -f $hollow_pkgs $hollow_file_list $hollow_dir_list # cleanup SMF services fix_smf || failed=1 # remove invalid pkgs [[ -z $failed ]] && rm_pkgs if [[ -z $failed && -n $OPT_U ]]; then vlog "$v_unconfig" sysunconfig_zone if (( $? != 0 )); then failed=1 fi fi vlog "$v_halting" /usr/sbin/zoneadm -z $ZONENAME halt if (( $? != 0 )); then error "$e_badhalt" failed=1 fi zone_is_running=0 if [[ -n $failed ]]; then fatal "$e_exitfail" fi vlog "$v_exitgood" exit 0