#
# 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) 2008, 2012, Oracle and/or its affiliates. All rights reserved.
#
#
# Only change PATH if you give full consideration to GNU or other variants
# of common commands having different arguments and output.
#
export PATH=/usr/bin:/usr/sbin
unset LD_LIBRARY_PATH
. /usr/lib/brand/shared/common.ksh
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"
f_incompat_options=$(gettext "cannot specify both %s and %s options")
f_sanity_detail=$(gettext "Missing %s at %s")
sanity_ok=$(gettext " Sanity Check: Passed. Looks like a Solaris system.")
sanity_fail=$(gettext " Sanity Check: FAILED (see log for details).")
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.")
p2ving=$(gettext " Updating: Converting image to non-global.")
p2v_done=$(gettext " Result: Conversion complete.")
p2v_fail=$(gettext " Result: Conversion failed")
install_fail=$(gettext " Result: *** Installation FAILED ***")
f_zfs_in_root=$(gettext "Installing a zone inside of the root pool's 'ROOT' dataset is unsupported.")
f_no_gzbe=$(gettext "unable to determine global zone boot environment.")
f_multiple_ds=$(gettext "multiple active datasets.")
f_no_active_ds=$(gettext "no active dataset.")
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_zfs_mount=$(gettext "Unable to mount the zone's ZFS dataset.")
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.")
f_set_sysrepo_prop_fail=$(gettext "Unable to set the use-system-repo property.")
# Used by install and attach during argument processing
f_arg_not_dir=$(gettext "Argument %s is not a directory")
f_arg_not_file=$(gettext "Argument %s is not a regular file")
f_arg_not_file_or_dir=$(gettext "Argument %s is not a file or directory")
f_scxml=$(gettext "Configuration profile %s must have an .xml suffix")
f_gz_image=$(gettext "Cannot attach global zone image")
f_image_arch_mismatch=$(gettext "Current architecture (%s) doesn't match zone image architecture (%s).")
m_brnd_usage=$(gettext "brand-specific usage: ")
v_reconfig=$(gettext "Performing zone system configuration")
e_reconfig=$(gettext "System configuration failed")
v_mounting=$(gettext "Mounting the zone")
e_badmount=$(gettext "Zone mount failed")
v_unmount=$(gettext "Unmounting zone")
e_badunmount=$(gettext "Zone unmount failed")
e_exitfail=$(gettext "Postprocessing failed.")
v_update_format=$(gettext "Updating image format")
e_update_format=$(gettext "Updating image format failed")
m_complete_seconds=$(gettext " Done: Installation completed in %s seconds.")
m_postnote=$(gettext " Next Steps: Boot the zone, then log into the zone console (zlogin -C)")
m_postnote2=$(gettext " to complete the configuration process.")
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--------------------- ------ ------------------------------------")
missing_gzbe=$(gettext "Missing Global Zone Boot Environment")
m_again_with_dash_z=$(gettext "Use the following command to attach a specific zone boot environment:\n%s")
e_no_be_0=$(gettext "Zone has no boot environments")
e_no_be_1=$(gettext "Zone has no boot environment with any of these names: %s")
f_sanity_variant=$(gettext " Sanity Check: FAILED, couldn't determine %s from image.")
m_cache=$(gettext " Cache: Using %s.")
f_update_required=$(gettext "Attach failed. This zone must be attached with the -u or -U option.")
f_uname_p=$(gettext "Unable to determine current architecture.")
f_list=$(gettext "Could not list packages in image: %s")
f_osnet=$(gettext "Could not find the osnet-incorporation package installed in image: %s")
f_update=$(gettext "Could not update attaching zone")
f_system_volatile=$(gettext "Could not create /system/volatile in the zone.")
m_active_zbe=$(gettext " Zone BE root dataset: %s")
m_prep_image=$(gettext " Updating non-global zone: Preparing packages.")
m_image_link=$(gettext " Updating non-global zone: Linking to image %s.")
m_image_audit=$(gettext " Updating non-global zone: Auditing packages.")
m_image_sync=$(gettext " Updating non-global zone: Syncing packages.")
m_image_sync1=$(gettext " Updating non-global zone: Syncing packages (pass 1 of 2).")
m_image_sync2=$(gettext " Updating non-global zone: Syncing packages (pass 2 of 2).")
m_image_update=$(gettext " Updating non-global zone: Updating packages.")
m_sync_done=$(gettext " Updating non-global zone: Zone updated.")
m_complete=$(gettext " Result: Attach Succeeded.")
is_brand_labeled() {
if [[ -z $ALTROOT ]]; then
AR_OPTIONS=""
else
AR_OPTIONS="-R $ALTROOT"
fi
brand=$(/usr/sbin/zoneadm $AR_OPTIONS -z $ZONENAME \
list -p | awk -F: '{print $6}')
[[ $brand == "labeled" ]] && return 0
return 1
}
function sanity_check {
typeset dir="$1"
shift
res=0
#
# Check for some required directories.
#
checks="etc etc/svc var var/svc"
for x in $checks; do
if [[ ! -e $dir/$x ]]; then
log "$f_sanity_detail" "$x" "$dir"
res=1
fi
done
if (( $res != 0 )); then
log "$sanity_fail"
fatal "$install_fail" "$ZONENAME"
fi
# Check for existence of pkg command.
if [[ ! -x $dir/usr/bin/pkg ]]; then
log "$f_sanity_detail" "usr/bin/pkg" "$dir"
log "$sanity_fail"
fatal "$install_fail" "$ZONENAME"
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.
#
if [[ -f "$dir/var/sadm/system/admin/INST_RELEASE" ]]; then
image_vers=$(nawk -F= '{if ($1 == "VERSION") print $2}' \
$dir/var/sadm/system/admin/INST_RELEASE)
vlog "$sanity_fail_vers" "$image_vers"
fatal "$install_fail" "$ZONENAME"
fi
vlog "$sanity_ok"
}
function get_current_gzbe {
#
# 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.
#
CURRENT_GZBE=$(beadm list -H | nawk -v alt=$ALTROOT -F\; '{
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
}')
[[ -z "$CURRENT_GZBE" ]] && fatal "$f_no_gzbe"
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.
#
function get_active_be {
typeset -n zone=$1
typeset active_ds=
typeset tab=$(printf "\t")
if [[ -z "$CURRENT_GZBE" ]]; then
get_current_gzbe
fi
typeset name parent active
zfs list -H -r -d 1 -t filesystem -o name,$PROP_PARENT,$PROP_ACTIVE \
"${zone.ROOT_ds}" | while IFS=$'\t' read name parent active ; do
[[ $parent == "$CURRENT_GZBE" ]] || continue
[[ $active == on ]] || continue
vlog "Found active dataset %s" "$name"
if [[ -n "$active_ds" ]]; then
error "$f_multiple_ds"
return 1
fi
active_ds=$name
done
if [[ -z $active_ds ]]; then
error "$f_no_active_ds"
return 1
fi
zone.active_ds=$active_ds
}
#
# 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.
#
function claim_zbe {
typeset -n zone=$1
typeset zbe=$2
typeset dss
init_dataset dss "${zone.ROOT_ds}/$zbe" || return 1
if [[ -z "$CURRENT_GZBE" ]]; then
get_current_gzbe
fi
if [[ ${dss.props[$PROP_CANDIDATE].value} == "$CURRENT_GZBE" ||
${dss.props[$PROP_PARENT].value} == "$CURRENT_GZBE" ]]; then
mount_active_be -b "$zbe" zone
return $?
fi
# Sets EXIT_CODE.
clone_zbe zone "$zbe"
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.
#
function clone_zbe {
typeset -n zone=$1
typeset zbe=$2
typeset -i i
typeset snapname
typeset dsn=${zone.ROOT_ds}/$zbe
[[ -z $zbe ]] && fail_internal "zbe not specified"
/usr/sbin/zfs list -o name "$dsn" >/dev/null 2>&1 || \
fail_internal "Dataset '%s' does not exist" "$dsn"
typeset now=$(date +%Y-%m-%d-%H:%M:%S)
snapname=$now
for (( i=0; i < 100; i++ )); do
/usr/sbin/zfs snapshot -r "$dsn@$snapname" >/dev/null 2>&1 && \
break
snapname=$(printf "%s-%02d" "$now" $i)
done
if (( i == 100 )); then
error "$f_zfs_snapshot_of" "$dsn"
return 1
fi
# Clone, activate, and mount ZBE
zone.active_ds=${zone.ROOT_ds}/$zbe
# Sets EXIT_CODE. "zone" appears twice as it is the source and target.
clone_zone_rpool zone zone "$snapname" || return 1
zone.zbe_cloned_from=$zbe
EXIT_CODE=$ZONE_SUBPROC_UNAVAILABLE
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.
#
function discover_active_be {
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 be uuid active junk
typeset active_gzbe
beadm list -H | while IFS=\; read be uuid active junk ; do
uuid2gzbe[$uuid]=$be
[[ $active == *N* ]] && active_gzbe=$uuid
done
if [[ -z $active_gzbe ]]; then
error "%s: unable to get global zone BE UUID" "${zone.name}"
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 \
name,mountpoint,$PROP_PARENT,$PROP_ACTIVE,$PROP_CANDIDATE \
"${zone.ROOT_ds}" | \
while IFS=$'\t' read name mountpoint parent active candidate
do
# skip the non-BE top-level dataset
[[ $name == "${zone.ROOT_ds}" ]] && continue
typeset curbe=$(basename "$name")
# skip BEs that are not in the allowed_bes list
if (( ${#zone.allowed_bes[@]} != 0 )) &&
[[ -z ${zone.allowed_bes[$curbe]} ]]; then
vlog "Ignoring dataset %s: %s not in allowed_bes" \
"$name" "$(basename "$name")"
continue
fi
if [[ -z ${uuid2gzbe[$parent]} ]]; then
uuid2gzbe[$parent]=$missing_gzbe
fi
ngzbe[$curbe].parent=$parent
ngzbe[$curbe].active=$active
ngzbe[$curbe].mountpoint=$mountpoint
ngzbe[$curbe].candidate=$candidate
# Update some arrays used in decision process.
if [[ $parent == "$active_gzbe" ]]; then
a_push this_gz "$curbe"
[[ $active == on ]] && a_push this_gz_active "$curbe"
fi
[[ $active == on ]] && a_push activezbe "$curbe"
done
#
# If there are no BEs, return an error
#
if (( ${#ngzbe[@]} == 0 )); then
if (( ${#zone.allowed_bes[@]} == 0 )); then
error "$e_no_be_0"
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
mount_active_be -c -b "${this_gz_active[0]}" zone
return $?
fi
# If there was only one ZBE associated with this GZ, activate it.
if (( ${#this_gz[@]} == 1 )); then
mount_active_be -c -b "${this_gz[0]}" zone
return $?
fi
# If there was only one ZBE that was active, clone and/or activate it.
if (( ${#activezbe[@]} == 1 )); then
typeset zbe="${activezbe[0]}"
if [[ ${#zone.allowed_bes[@]} != 0 ]]; then
mount_active_be -c -b "$zbe" zone
return $?
fi
# If the zbe is not associated with any gzbe, do not clone it.
if [[ ${uuid2gzbe[${ngzbe[$zbe].parent}]} == "$missing_gzbe" ]]
then
mount_active_be -c -b "$zbe" zone
return $?
fi
clone_zbe zone "$zbe"
return $?
fi
# If there was only one ZBE, clone and/or activate it
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
mount_active_be -c -b "$zbe" zone
return $?
fi
# Sets EXIT_CODE.
clone_zbe zone "$zbe"
return $?
fi
log "$m_zbe_discover_failed"
log "$m_zbe_discover_header"
typeset zbe
for zbe in "${!ngzbe[@]}" ; do
typeset uuid=${ngzbe[$zbe].parent}
typeset bename=${uuid2gzbe[$uuid]}
if [[ $bename == $missing_gzbe ]]; then
typeset cuuid=${ngzbe[$zbe].candidate}
if [[ $cuuid != $missing_gzbe && \
-n ${uuid2gzbe[$cuuid]} ]]; then
bename="Extracted for ${uuid2gzbe[$cuuid]}"
else
bename=$uuid
fi
fi
log "%-21s %-6s %s" "$zbe" "${ngzbe[$zbe].active}" "$bename"
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
EXIT_CODE=$ZONE_SUBPROC_TRYAGAIN
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
typeset name canmount parent active candidate
[[ -z $be ]] && fail_internal "zbe not specified"
if [[ -z "$CURRENT_GZBE" ]]; then
get_current_gzbe
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 \
name,canmount,$PROP_PARENT,$PROP_ACTIVE,$PROP_CANDIDATE \
${zone.ROOT_ds} | \
while IFS=$'\t' read name canmount parent active candidate ; do
# skip the ROOT dataset
[[ $name == "${zone.ROOT_ds}" ]] && continue
# The root of each BE should only be mounted explicitly.
if [[ $canmount != noauto ]]; then
zfs set canmount=noauto "$name" || \
fail_internal "$e_zfs_set" canmount "$name"
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.
#
if [[ $candidate == "$CURRENT_GZBE" ]]; then
zfs inherit "$PROP_CANDIDATE" "$name" ||
log "$e_zfs_inherit" "$PROP_CANDIDATE" "$name"
#
# 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.
#
if [[ $parent != "$CURRENT_GZBE" ]]; then
zfs set "$PROP_PARENT=$CURRENT_GZBE" "$name" ||
log "$e_zfs_set" "$PROP_PARENT" "$name"
parent=$CURRENT_GZBE
fi
fi
# Deactivate BEs for this GZ that are not being set to active.
[[ $parent == "$CURRENT_GZBE" ]] || continue
[[ $active == on ]] || continue
[[ $name == "${zone.ROOT_ds}/$be" ]] && continue
vlog "Deactivating active dataset %s" "$name"
zfs set $PROP_ACTIVE=off "$name" || return 1
done
zone.active_ds="${zone.ROOT_ds}/$be"
zfs set "$PROP_PARENT=$CURRENT_GZBE" ${zone.active_ds} \
|| return 1
zfs set "$PROP_ACTIVE=on" ${zone.active_ds} || return 1
zfs set "$PROP_BE_HANDLE=on" "${zone.rpool_ds}" || return 1
typeset origin
zfs list -H -o name,origin "${zone.active_ds}" | while read name origin
do
if [[ $origin == "${zone.ROOT_ds}"/* ]]; then
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
vlog "$v_reconfig"
vlog "$v_mounting"
ZONE_IS_MOUNTED=1
zoneadm -z $ZONENAME mount -f || fatal "$e_badmount"
if [[ -n $sc_config ]]; then
sc_config_base=$(basename "$sc_config")
# Remove in case $sc_config_base is a directory
safe_dir "/system"
safe_dir "/system/volatile"
rm -rf "$ZONEPATH/lu/system/volatile/$sc_config_base"
safe_copy_rec $sc_config \
"$ZONEPATH/lu/system/volatile/$sc_config_base"
zlogin -S $ZONENAME "_UNCONFIG_ALT_ROOT=/a \
/usr/sbin/sysconfig configure -g system \
-c /system/volatile/$sc_config_base --destructive" \
</dev/null >/dev/null 2>&1
else
zlogin -S $ZONENAME "_UNCONFIG_ALT_ROOT=/a \
/usr/sbin/sysconfig configure -g system --destructive" \
</dev/null >/dev/null 2>&1
fi
if (( $? != 0 )); then
error "$e_reconfig"
failed=1
fi
vlog "$v_unmount"
zoneadm -z $ZONENAME unmount || fatal "$e_badunmount"
ZONE_IS_MOUNTED=0
[[ -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=
info_out=$(LC_ALL=C ${PKG:-pkg} info pkg:/$pname 2>/dev/null)
if (( $? != 0 )); then
return 1
fi
pkg_fmri=$(echo $info_out | grep FMRI | cut -d'@' -f 2)
echo "$pname@$pkg_fmri"
return 0
}
#
# Emits to stdout the entire incorporation for this image,
# stripped of publisher name and other junk.
#
function get_entire_incorp {
get_pkg_fmri entire
return $?
}
function get_osnet_incorp {
get_pkg_fmri consolidation/osnet/osnet-incorporation
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}
(( $res != 0 && $res != 4 )) && "$errfn" "$msg"
}
#
# Enable the services needed to perform packaging operations inside a zone.
#
function enable_zones_services {
services_required="$1"
/usr/sbin/svcadm enable -st svc:/application/pkg/system-repository
if (( $? != 0 )) && [[ -n "$services_required" ]]; then
error "$f_sysrepo_fail"
return 1
fi
/usr/sbin/svcadm enable -st svc:/application/pkg/zones-proxyd
if (( $? != 0 )) && [[ -n "$services_required" ]]; then
error "$f_zones_proxyd_fail"
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
typeset dsn parent
if [[ -z "$CURRENT_GZBE" ]]; then
get_current_gzbe
fi
/usr/sbin/zfs list -H -o name -r -d 1 -t filesystem "$ROOTdsn" \
2>/dev/null | while read dsn ; do
[[ $dsn == "$ROOTdsn" ]] && continue
a_push bes "$(basename "$dsn")"
zfs set "$PROP_CANDIDATE=$CURRENT_GZBE" "$dsn" || return 1
# See if the zbe is already associated with the GZBE
parent=$(zfs get "$PROP_PARENT" "$dsn")
if [[ $parent == "$CURRENT_GZBE" ]]; then
curgzbes[$(basename "$dsn")]=1
fi
done
if (( ${#bes[@]} == 0 )); then
error "$e_no_active_be"
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
for be in "${bes[@]}"; do
curgzbes[$be]=1
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
# /var/pkg/publisher, it is set to PKG_CACHEROOT and exported
# 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
PKG=${PKG:-pkg}
GZ_IMAGE=${GZ_IMAGE:-/}
[[ -n $OPT_V ]] && verbose=-v
# Sanity check arguments
(( $# == 2 )) ||
fail_internal "expected 2 args, got %d (%s)" $# "$*"
[[ ! -d ${zone.root} ]] &&
fail_internal "zone root '%s' does not exist" "${zone.root}"
[[ $allow_update != @(none|min|all) ]] &&
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
arch=$(uname -p) || fail "$f_uname_p"
# If there is a cache, use it.
if [[ -f /var/pkg/pkg5.image && -d /var/pkg/publisher ]]; then
# respect PKG_CACHEROOT if the caller has it set.
[[ -z "$PKG_CACHEROOT" ]] &&
export PKG_CACHEROOT=/var/pkg/publisher
log "$m_cache" "$PKG_CACHEROOT"
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
log "$v_update_format"
$PKG -R "${zone.root}" update-format ||
pkg_err_check "$e_update_format" fatal
fi
# Set the use-system-repo property.
$PKG -R "${zone.root}" set-property use-system-repo true ||
fatal "\n$f_set_sysrepo_prop_fail"
#
# 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
$PKG -R "${zone.root}" refresh --full ||
pkg_err_check "$e_update_format"
fi
# prevent attach of a image with a different architecture
typeset ngz_arch
ngz_arch=$($PKG -R "${zone.root}" variant -H variant.arch) ||
(( $? == 0 )) || fatal "$f_sanity_variant" variant.arch
ngz_arch=$(set -- $ngz_arch; echo $2)
[[ "$ngz_arch" != "$arch" ]] &&
fatal "$f_image_arch_mismatch" "$arch" "$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 -- \
attach-linked --no-refresh --linked-md-only --allow-relink \
-f $verbose
log "$m_image_link" "$GZ_IMAGE"
set -A cmd $PKG -R "${GZ_IMAGE}" "${pkg_attach_args[@]}" \
-c "zone:${zone.name}" "${zone.root}"
vlog "Running '%s'" "${cmd[*]}"
"${cmd[@]}" || pkg_err_check "$f_update" fatal
# get a list of fmris installed in the ngz image.
typeset tmpfile1 tmpfile2
tmpfile1=$(mktemp -t zoneadm_image_attach.$$.XXXXXX) || \
fatal "$e_tmpfile"
tmpfile2=$(mktemp -t zoneadm_image_attach.$$.XXXXXX) || \
fatal "$e_tmpfile"
set -A cmd $PKG -R "${zone.root}" list --no-refresh -Hv
vlog "Running '%s'" "${cmd[*]}"
"${cmd[@]}" > $tmpfile2 || fatal "$f_list" "${zone.root}"
nawk '{print $1}' $tmpfile2 > $tmpfile1 || fatal "$f_list"
rm -f "$tmpfile2"
#
# 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)
[[ -z "$gz_osnet_pkgver" ]] && fatal "$f_osnet" "$GZ_IMAGE"
# Lookup for the 'entire' incorporation in the non-global zone.
# Get the full package name and version.
typeset ngz_entire_fmri=$(egrep '^pkg://[^]]*/entire@' $tmpfile1)
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 \
'^pkg://[^]]*/consolidation/osnet/osnet-incorporation@' $tmpfile1)
typeset ngz_osnet_pkgver=${ngz_osnet_fmri#pkg://*/}
[[ -z "$gz_osnet_pkgver" ]] && fatal "$f_osnet" "${zone.root}"
# get a list of incorporation fmris installed in the ngz image.
typeset ngz_incorporations=$(
egrep '^pkg://.*/consolidation/.*/.*-incorporation@' "$tmpfile1"
)
# we're done with the ngz list of packages
rm -f "$tmpfile1"
# check if the 'osnet-incorporation' package is in sync between
# the gz and ngz.
typeset manual_sync_required=1
[[ "$gz_osnet_pkgver" == "$ngz_osnet_pkgver" ]] &&
manual_sync_required=0
if [[ $manual_sync_required != 0 && $allow_update == none ]]; then
fail_tryagain "\n$f_update_required"
elif [[ $manual_sync_required != 0 ]]; then
#
# before we do a manual sync, we need to make sure that
# system/volatile exists. old (s11 express / snv_151a)
# 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).
#
ZONEROOT=${zone.root} safe_opt_dir system
ZONEROOT=${zone.root} safe_opt_dir system/volatile
if [[ ! -d "${zone.root}/system/volatile" ]]; then
mkdir -m 755 -p "${zone.root}/system/volatile" || \
fatal "$f_system_volatile"
fi
# prepare to try and manually sync the zone image
typeset -a pkg_install_args
set -A pkg_install_args -- \
install -I $verbose --no-refresh --accept
#
# if the gz doesn't have entire installed we need to
# remove it from the ngz during out sync.
#
[[ -z "$gz_entire_pkgver" ]] &&
a_push pkg_install_args --reject pkg:///entire
#
# 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
pub_pkg=${fmri%@*}
pkg=${pub_pkg#pkg://*/}
#
# 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.
#
[[ -z "$gz_entire_pkgver" && $pkg == \
consolidation/osnet/osnet-incorporation ]] &&
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.)
#
[[ "$arch" != sparc && $pkg == \
consolidation/ldoms/ldoms-incorporation ]] &&
continue
[[ "$arch" != i386 && $pkg == \
consolidation/nvidia/nvidia-incorporation ]] &&
continue
# relax this incorporation
a_push pkg_install_args pkg:///$pkg
done
if [[ -n "$gz_entire_pkgver" && -n "$ngz_entire_pkgver" ]]; then
# entire is installed in the gz and ngz, so sync that.
a_push pkg_install_args $gz_entire_pkgver
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
a_push pkg_install_args $gz_osnet_pkgver
fi
# try to manually sync the image.
set -A cmd $PKG -R "${zone.root}" "${pkg_install_args[@]}"
if [[ "$allow_update" == all ]]; then
log "$m_image_sync"
elif [[ "$allow_update" == min ]]; then
log "$m_image_sync1"
else
fail_internal "allow_update is '$allow_update'"
fi
vlog "Running '%s'" "${cmd[*]}"
"${cmd[@]}" || pkg_err_check "$f_update" fatal
fi
#
# If the image is a global zone and updates are allowed, do p2v.
#
set -A variant \
$(LC_ALL=C $PKG -R "${zone.root}" variant -H $variantname)
(( $? == 0 )) || fatal "$f_sanity_variant" "$variantname"
[[ ${variant[0]} != "${variantname}" ]] &&
fatal "$f_sanity_variant" "$variantname"
case ${variant[1]} in
global)
# We can't change the variant if updates aren't allowed.
[[ $allow_update == @(min|all) ]] || fatal "$f_gz_image"
log "$p2ving"
typeset -a p2vopts
set -A p2vopts $verbose_mode "${zone.name}" "${zone.path}"
vlog "running: p2v ${p2vopts[@]}"
/usr/lib/brand/solaris/p2v "${p2vopts[@]}"
p2v_exit=$?
if (( $p2v_exit != 0 )); then
# Pass the p2v exit code up to zoneadm
EXIT_CODE=$p2v_exit
log "$p2v_fail"
fatal "\n$install_fail"
fi
vlog "$p2v_done"
;;
nonglobal)
:
;;
*) fail_internal "$variantname is '${variant[1]}'"
;;
esac
# Assemble the arguments to sync the zone image
typeset -a pkg_sync_args
typeset log_msg
typeset fail_msg=$f_update
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.
#
fail_msg=$f_update_required
log_msg=$m_image_audit
set -A pkg_sync_args -- sync-linked --no-pkg-updates
;;
min)
if [[ $manual_sync_required != 0 ]]; then
log_msg=$m_image_sync2
else
log_msg=$m_image_sync
fi
set -A pkg_sync_args -- sync-linked
;;
all)
log_msg=$m_image_update
set -A pkg_sync_args -- update -f
;;
*)
fail_internal "Invalid allow_update value: $allow_update"
esac
a_push pkg_sync_args -I $verbose --no-refresh --accept
[[ $allow_update != none && -z $gz_entire_pkgver ]] &&
a_push pkg_sync_args --reject pkg:///entire
# Sync the zone image.
log "$log_msg"
set -A cmd $PKG -R "${zone.root}" "${pkg_sync_args[@]}"
vlog "Running '%s'" "${cmd[*]}"
"${cmd[@]}" || pkg_err_check "$fail_msg" fatal
log "\n$m_sync_done"
log "$m_complete"
}