#
# 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
# 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
#
#
#
#
# Only change PATH if you give full consideration to GNU or other variants
# of common commands having different arguments and output.
#
unset LD_LIBRARY_PATH
PROP_PARENT="org.opensolaris.libbe:parentbe"
PROP_ACTIVE="org.opensolaris.libbe:active"
PROP_BE_HANDLE="com.oracle.libbe:nbe_handle"
PROP_CANDIDATE="com.oracle.zoneadm:candidate_zbe"
sanity_fail_vers=$(gettext " Sanity Check: the Solaris image (release %s) is not an OpenSolaris image and cannot be installed in this type of branded zone.")
f_zfs_in_root=$(gettext "Installing a zone inside of the root pool's 'ROOT' dataset is unsupported.")
f_zfs_unmount=$(gettext "Unable to unmount the zone's root ZFS dataset (%s).\nIs there a global zone process inside the zone root?\nThe current zone boot environment will remain mounted.\n")
f_sysrepo_fail=$(gettext "Unable to enable svc:/application/pkg/system-repository, please enable the service manually.")
f_zones_proxyd_fail=$(gettext "Unable to enable svc:/application/pkg/zones-proxyd, please enable the service manually.")
# Used by install and attach during argument processing
f_image_arch_mismatch=$(gettext "Current architecture (%s) doesn't match zone image architecture (%s).")
m_zbe_discover_failed=$(gettext "Unable to determine which boot environment to activate. Candidates are:\n")
m_zbe_discover_header=$(gettext "Zone Boot Environment Active Global Zone Boot Environment\n--------------------- ------ ------------------------------------")
m_again_with_dash_z=$(gettext "Use the following command to attach a specific zone boot environment:\n%s")
if [[ -z $ALTROOT ]]; then
AR_OPTIONS=""
else
AR_OPTIONS="-R $ALTROOT"
fi
return 1
}
typeset dir="$1"
shift
res=0
#
# Check for some required directories.
#
if [[ ! -e $dir/$x ]]; then
log "$f_sanity_detail" "$x" "$dir"
res=1
fi
done
fi
# Check for existence of pkg command.
fi
#
# XXX There should be a better way to do this.
# Check image release. We only work on the same minor release as the
# system is running. The INST_RELEASE file doesn't exist with IPS on
# OpenSolaris, so its presence means we have an earlier Solaris
# (i.e. non-OpenSolaris) image.
#
fi
}
#
# If there is no alternate root (normal case) then set the
# global zone boot environment by finding the boot environment
# that is active now.
# If a zone exists in a boot environment mounted on an alternate root,
# then find the boot environment where the alternate root is mounted.
#
if (length(alt) == 0) {
# Field 3 is the BE status. "N" is the active BE.
if ($3 !~ "N")
next
} else {
# Field 4 is the BE mountpoint.
if ($4 != alt)
next
}
# Field 2 is the BE UUID
print $2
}')
return 0
}
#
# get_active_be zone
#
# Finds the active boot environment for the given zone.
#
# Arguments:
#
# zone zone structure initialized with init_zone
#
# Globals:
#
# CURRENT_GZBE Current global zone boot environment. If not already set,
# it will be set.
#
# Returns:
#
# 0 on success, else 1.
#
typeset -n zone=$1
typeset active_ds=
fi
zfs list -H -r -d 1 -t filesystem -o name,$PROP_PARENT,$PROP_ACTIVE \
return 1
fi
done
if [[ -z $active_ds ]]; then
return 1
fi
}
#
# claim_zbe zone zbe
#
# If the zbe belongs to the existing gzbe or the zbe was extracted from an
# archive and has not yet been attached, set it as the active zbe. Otherwise,
# clone it and set the clone to the active zbe.
#
# Globals:
#
# EXIT_CODE On success, set as described in clone_zbe.
#
# Returns 0 on success, !0 on failure.
#
typeset -n zone=$1
typeset zbe=$2
typeset dss
fi
return $?
fi
# Sets EXIT_CODE.
return $?
}
#
# clone_zbe [-u] zone zbe
#
# Clones a zbe within a zone and sets the new ZBE as the active BE for this
# zone.
#
# Options and arguments:
#
# zone zone structure initialized with init_zone
# zbe Name of the zone boot environment to clone.
#
# Globals:
#
# EXIT_CODE On success, set to ZONE_SUBPROC_UNAVAILABLE.
# On failure, set as described in clone_zone_rpool.
#
# Returns:
#
# 0 Success, and members of the zone structure have been updated.
# zone.active_ds Updated with the dataset that is
# the root of the zbe.
# zone.zbe_cloned_from Set to the name of the zbe passed in
# 1 Failure. Error message has been logged.
#
typeset -n zone=$1
typeset zbe=$2
typeset -i i
typeset snapname
[[ -z $zbe ]] && fail_internal "zbe not specified"
fail_internal "Dataset '%s' does not exist" "$dsn"
for (( i=0; i < 100; i++ )); do
break
done
if (( i == 100 )); then
return 1
fi
# Clone, activate, and mount ZBE
# Sets EXIT_CODE. "zone" appears twice as it is the source and target.
return 0
}
#
# discover_active_be zone
#
# Looks for the ZBE that is best suited to be the active ZBE.
#
# The caller may optionally constrain the list of ZBEs that is considered for
# activation by populating the zone.allowed_bes associative array. In such a
# case, If zone.allowed_bes is a non-empty associative array, only BEs in that
# array are considered.
#
# After selecting which ZBE is the best candidate for activation, care will
# be taken not to "steal" the ZBE from another global zone. If the chosen
# zbe has $PROP_PARENT matching the UUID of an extant global zone BE, it is
# the chosen ZBE is cloned and this new clone is the ZBE that is selected for
# activation.
#
# Note, however, that a ZBE that appears in zone.allowed_bes is
# never cloned. It is assumed that zone.allowed_bes contains a set of ZBEs
# that was received from an archive and any existing values in $PROP_PARENT
# on these ZBEs are stale.
#
# Arguments
#
# zone A zone structure initialized with init_zone.
#
# Globals
#
# EXIT_CODE ZONE_SUBPROC_UNAVAILABLE ZBE cloning was attempted
# and succeeded.
# ZONE_SUBPROC_FATAL ZBE cloning was attempted,
# failed, and cleanup failed.
# ZONE_SUBPROC_TRYAGAIN ZBE selection required. A
# list of available ZBEs was
# displayed.
#
# Returns:
#
# 0 Success. The discovered active_be has been activated (see
# set_active_be() for details) and has been mounted on the zone root.
# 1 Active dataset could not be found and an error message has been
# printed.
#
typeset -n zone=$1
shift
typeset -A uuid2gzbe
typeset -i needs_selection=0
#
# Load an associative array of global zone BEs. Store current uuid
# of GZBE in $active_gzbe.
#
# uuid2gzbe[<gzbe uuid>]=<gzbe name>
#
typeset active_gzbe
[[ $active == *N* ]] && active_gzbe=$uuid
done
if [[ -z $active_gzbe ]]; then
return 1
fi
#
# Load an associative array of non-global zone BEs and arrays of
# likely candidates.
#
# ngzbe[<ngzbe name>].parent=<gzbe uuid>
# ngzbe[<ngzbe name>].active=<on|off|->
# ngzbe[<ngzbe name>].mountpoint=</|zoneroot>
#
typeset name mountpoint parent active candidate
typeset -A ngzbe
typeset -a activezbe # NGZ BEs that are active
typeset -a this_gz # NGZ BEs that match this GZ BE
typeset -a this_gz_active # match GZ BE and is active
zfs list -H -r -d 1 -t filesystem -o \
do
# skip the non-BE top-level dataset
# skip BEs that are not in the allowed_bes list
[[ -z ${zone.allowed_bes[$curbe]} ]]; then
continue
fi
fi
# Update some arrays used in decision process.
[[ $active == on ]] && a_push this_gz_active "$curbe"
fi
done
#
# If there are no BEs, return an error
#
if (( ${#ngzbe[@]} == 0 )); then
else
error "$e_no_be_1" "${!zone.allowed_bes[*]}"
fi
return 1
fi
# If there was only one ZBE active for this GZ, activate it.
if (( ${#this_gz_active[@]} == 1 )); then
return $?
fi
# If there was only one ZBE associated with this GZ, activate it.
if (( ${#this_gz[@]} == 1 )); then
return $?
fi
if (( ${#activezbe[@]} == 1 )); then
typeset zbe="${activezbe[0]}"
if [[ ${#zone.allowed_bes[@]} != 0 ]]; then
return $?
fi
# If the zbe is not associated with any gzbe, do not clone it.
then
return $?
fi
return $?
fi
if (( ${#ngzbe[@]} == 1 )); then
#
# We really want the name of index 0, but a subscript of 0
# is not supported. Since we know that there is only one
# item in the associative array, the name of all the items
# is equivalent to the name of the first item.
#
typeset zbe="${!ngzbe[@]}"
if [[ ${#zone.allowed_bes[@]} ]]; then
return $?
fi
# Sets EXIT_CODE.
return $?
fi
typeset zbe
if [[ $bename == $missing_gzbe ]]; then
if [[ $cuuid != $missing_gzbe && \
else
fi
fi
done
log "%s" ""
#
# Install and attach need different messages. m_usage_dash_z should
# be defined in the brand's install and attach scripts.
#
if (( ${#ATTACH_Z_COMMAND[@]} != 0 )); then
log "$m_again_with_dash_z" "${ATTACH_Z_COMMAND[*]}"
fi
return 1
}
#
# set_active_be zone bootenv
#
# Sets the active boot environment for the zone. This includes updating the
# zone structure and setting the required properties ($PROP_PARENT,
# $PROP_ACTIVE) on the top-level BE datasets.
#
function set_active_be {
typeset -n zone="$1"
typeset be=$2
[[ -z $be ]] && fail_internal "zbe not specified"
fi
#
# Turn off the active property on BE's with the same GZBE and ensure
# that there aren't any BE datasets that will mount automatically.
#
zfs list -H -r -d 1 -t filesystem -o \
# skip the ROOT dataset
# The root of each BE should only be mounted explicitly.
fi
#
# If this was extracted from an archive within this GZ,
# finish the association process. In the unlikely event
# that these property updates fail, manual cleanup may
# be required, but it should not prevent the attach.
#
#
# Setting the parent to this gzbe makes it possible
# for beadm to clean up the zbes within the zone
# once one of the candidate zbe's is attached.
#
log "$e_zfs_set" "$PROP_PARENT" "$name"
fi
fi
# Deactivate BEs for this GZ that are not being set to active.
done
zone.active_ds="${zone.ROOT_ds}/$be"
|| return 1
typeset origin
do
vlog "Promoting active dataset '%s'" "${zone.active_ds}"
zfs promote "${zone.active_ds}"
fi
done
return 0
}
#
# Run system configuration inside a zone.
#
function reconfigure_zone {
typeset sc_config=$1
zoneadm -z $ZONENAME mount -f || fatal "$e_badmount"
if [[ -n $sc_config ]]; then
# Remove in case $sc_config_base is a directory
safe_dir "/system"
-c /system/volatile/$sc_config_base --destructive" \
else
fi
if (( $? != 0 )); then
failed=1
fi
zoneadm -z $ZONENAME unmount || fatal "$e_badunmount"
[[ -n $failed ]] && fatal "$e_exitfail"
}
#
# Emits to stdout the fmri for the supplied package,
# stripped of publisher name and other junk.
#
function get_pkg_fmri {
typeset pname=$1
typeset pkg_fmri=
typeset info_out=
if (( $? != 0 )); then
return 1
fi
return 0
}
#
# Emits to stdout the entire incorporation for this image,
# stripped of publisher name and other junk.
#
function get_entire_incorp {
return $?
}
function get_osnet_incorp {
return $?
}
#
# Handle pkg exit code. Exit 0 means Command succeeded, exit 4 means
# No changes were made - nothing to do. Any other exit code is an error.
#
# Arguments:
#
# msg Error message passed as argument to error function
# errfn Function to call if an error is detected. If not specified,
# fail_fatal is used.
#
function pkg_err_check {
typeset res=$?
typeset msg=$1
typeset errfn=${2:-fail_fatal}
}
#
# Enable the services needed to perform packaging operations inside a zone.
#
function enable_zones_services {
services_required="$1"
return 1
fi
return 1
fi
return 0
}
#
# tag_candidate_zbes ROOTdsn [be_array_name [curgz_assoc_array_name]]
#
# Tags each dataset that is a child of ROOTdsn with
# $PROP_CANDIDATE=$CURRENT_GZBE.
#
# Arguments:
# ROOTdsn The name of a dataset that contains zbes.
# be_array_name If specified, this variable will contain an array
# of candidate zbes on return.
# curgz_assoc_array_name If specified and any zbes have $PROP_PARENT that
# matches $CURRENT_GZBE, curgz_assoc_array_name will
# contain that list. Otherwise, curgz_assoc_array_name
# will be updated to reflect all of the zbes found. Note
# that curgz_assoc_array_name is an associative (not
# indexed) array with keys that match the zbe name. The
# value assigned to each key is not significant.
#
# Returns 0 if there is at least one zbe found
# Returns 1 if there are no zbes or there is a failure updating properties.
#
function tag_candidate_zbes {
typeset ROOTdsn=$1
if [[ -n $2 ]]; then
typeset -n bes=$2
fi
typeset -a bes
if [[ -n $3 ]]; then
typeset -n curgzbes=$3
fi
typeset -A curgzbes
fi
# See if the zbe is already associated with the GZBE
fi
done
if (( ${#bes[@]} == 0 )); then
return 1
fi
#
# If there were no zbes that already had a parent of $CURRENT_GZBE,
# mark all of the found zbes as being allowed.
#
if (( ${#curgzbes[@]} == 0 )); then
typeset be
done
fi
return 0
}
#
# attach_image zone allow_update
#
# Arguments:
#
# zone A zone reference, initialized by init_zone. A BE
# must be fully mounted at ${zone.root}. The BE may
# contain a GZ or NGZ pkg(5) image.
# allow_update One of "none", "min", or "all".
# none: No updates to the zone image are allowed.
# min: The mininal updates required to attach the
# zone are allowed. This includes updating
# the pkg(5) image format and satisfying
# parent dependencies. Corresponds to the
# default action of p2v (install <-a|-d>)
# and the historical -u option to attach.
# all: Like min, but updates all packages to the
# latest version that are compatible with the
# global zone image.
#
# Side effects on global variables:
#
# PKG If not set on entry, set to pkg
# GZ_IMAGE If not set on entry, set to /
# PKG_CACHEROOT If not set on entry and a pkg(5) cache exists at
# to the environment.
# OPT_V If set, pkg(1) commands will be verbose.
# EXIT_CODE Not changed, but must be set.
#
# Returns:
# If it returns, all went well - the zone is attached. Otherwise, it fails.
#
function attach_image {
typeset -n zone=$1
typeset allow_update=$2
typeset variantname=variant.opensolaris.zone
typeset -a variant
typeset savestate
typeset variant_changed=false
typeset verbose
# Sanity check arguments
(( $# == 2 )) ||
[[ ! -d ${zone.root} ]] &&
fail_internal "zone root '%s' does not exist" "${zone.root}"
fail_internal "invalid value '%s' for allow_update" "$allow_update"
# EXIT_CODE must be set for proper error handling
[[ -z $EXIT_CODE ]] && fail_internal "EXIT_CODE is not set"
# get the current architecture.
typeset arch
# If there is a cache, use it.
# respect PKG_CACHEROOT if the caller has it set.
export PKG_CACHEROOT=/var/pkg/publisher
fi
#
# pkg update-format doesn't allow a dry run or provide any other way to
# see if an update is needed.
#
if [[ $allow_update != none ]]; then
fi
# Set the use-system-repo property.
#
# Update that catalogs once, subsequent packaging operations will use
# --no-refresh to avoid unnecessary catalog checks and updates. If
# image updates are not allowed, there's no need to refresh the
# catalogs.
#
if [[ $allow_update != none ]]; then
fi
# prevent attach of a image with a different architecture
typeset ngz_arch
#
# Attach the zone to the global zone as a linked image. This
# writes linked image metadata into the non-global zone which
# will constrain subsequent packaging operations (but only after
# the zone variant is set to nonglobal.) The attach operation is
# performed on the global zone image (and the pkg command will
# subsequently recurse into and modify the zone image).
#
typeset -a pkg_attach_args cmd
set -A pkg_attach_args -- \
-f $verbose
vlog "Running '%s'" "${cmd[*]}"
# get a list of fmris installed in the ngz image.
vlog "Running '%s'" "${cmd[*]}"
#
# Lookup the 'entire' incorporation in the global zone. We
# check for this because if the user has removed it then we'll
# want to remove it from the zone during attach. The reason is
# that we're unlikely to be able to attach a highly constrained
# zone (one that has entire installed) to a loosely constrained
# global zone (one that doesn't have entire installed).
#
typeset gz_entire_pkgver=$(PKG_IMAGE=$GZ_IMAGE; get_entire_incorp)
#
# Lookup the 'osnet-incorporation' package in the global zone.
# We need this to do a manual sync check (and possible manual
# sync) of the zone. (and by manual we mean a sync that does
# not utilize the pkg linked image framework.)
#
typeset gz_osnet_pkgver=$(PKG_IMAGE=$GZ_IMAGE; get_osnet_incorp)
# Lookup for the 'entire' incorporation in the non-global zone.
# Get the full package name and version.
typeset ngz_entire_pkgver=${ngz_entire_fmri#pkg://*/}
# Lookup for the 'osnet-incorporation' package in the non-global zone.
# Get the full package name and version.
typeset ngz_osnet_fmri=$(egrep \
typeset ngz_osnet_pkgver=${ngz_osnet_fmri#pkg://*/}
# get a list of incorporation fmris installed in the ngz image.
typeset ngz_incorporations=$(
)
# we're done with the ngz list of packages
# check if the 'osnet-incorporation' package is in sync between
# the gz and ngz.
typeset manual_sync_required=1
if [[ $manual_sync_required != 0 && $allow_update == none ]]; then
elif [[ $manual_sync_required != 0 ]]; then
#
# before we do a manual sync, we need to make sure that
# images may not have this directory, which in turn will
# cause rem_drv and update_drv to fail (since they want
# to create lock files in that directory).
#
fi
# prepare to try and manually sync the zone image
typeset -a pkg_install_args
set -A pkg_install_args -- \
#
# if the gz doesn't have entire installed we need to
# remove it from the ngz during out sync.
#
#
# to manually sync the image we must relax all the image
# install holds. we do this by specifying the names of
# all the installed incorporations (excluding publisher
# and version numbers).
#
for fmri in $ngz_incorporations; do
# strip version and publisher from the fmri
#
# if entire is not installed then we're going to
# try to sync the image by specifying a version
# of the osnet-incorporation, but older versions
# of the pkg client don't allow you to specify
# overlapping pkg patterns on the cli, so don't
# try to relax the osnet-incorporation if we're
# going to explicitly sync it.
#
continue
#
# due to bugs in s11 fcs, it's possible that we
# may have the ldoms-incorporation installed on
# non-sparc machines (or nvidia-incorporation
# on non-i386 machines). if so we don't want to
# specify these packages on the command line.
# (since that would require them to be installed
# and as part of this image update the solver
# may want to remove them.)
#
continue
continue
# relax this incorporation
done
# entire is installed in the gz and ngz, so sync that.
else
# we're in one of the following three cases:
# 1) entire is installed in the gz but not in
# the ngz.
# 2) entire is not installed in the gz but is in
# the ngz (it will be rejected from the ngz).
# 3) entire is not installed in the gz or ngz
# in all these cases we must sync osnet-incorporation
fi
# try to manually sync the image.
else
fail_internal "allow_update is '$allow_update'"
fi
vlog "Running '%s'" "${cmd[*]}"
fi
#
# If the image is a global zone and updates are allowed, do p2v.
#
set -A variant \
# We can't change the variant if updates aren't allowed.
typeset -a p2vopts
set -A p2vopts $verbose_mode "${zone.name}" "${zone.path}"
vlog "running: p2v ${p2vopts[@]}"
p2v_exit=$?
# Pass the p2v exit code up to zoneadm
fatal "\n$install_fail"
fi
;;
:
;;
*) fail_internal "$variantname is '${variant[1]}'"
;;
esac
# Assemble the arguments to sync the zone image
typeset -a pkg_sync_args
typeset log_msg
case $allow_update in
none)
#
# $f_update_required advises the use of -u or -U. It should
# only be used with attach, as -u means something different
# with install. install will only have "min" or "all" as
# values for allow_update.
#
;;
min)
if [[ $manual_sync_required != 0 ]]; then
else
fi
set -A pkg_sync_args -- sync-linked
;;
all)
set -A pkg_sync_args -- update -f
;;
*)
fail_internal "Invalid allow_update value: $allow_update"
esac
[[ $allow_update != none && -z $gz_entire_pkgver ]] &&
a_push pkg_sync_args --reject pkg:///entire
# Sync the zone image.
vlog "Running '%s'" "${cmd[*]}"
log "\n$m_sync_done"
}