17791 ipkg brand needs to be in sync with revised dataset layout
17654 check for zonepath in GZ ROOT assumes root pool named rpool
#!/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) 2008, 2011, Oracle and/or its affiliates. All rights reserved.
#
. /usr/lib/brand/ipkg/common.ksh
m_attach_log=$(gettext "Log File: %s")
m_zfs=$(gettext "A ZFS file system was created for the zone.")
m_usage=$(gettext "attach [-a archive] [-d dataset] [-n] [-r zfs-recv] [-u]\n\tThe -a archive option specifies a tar file or cpio archive.\n\tThe -d dataset option specifies an existing dataset.\n\tThe -r zfs-recv option receives the output of a 'zfs send' command\n\tof an existing zone root dataset.\n\tThe -u option indicates that the software should be updated to match\n\tthe current host.")
m_attach_root=$(gettext " Attach Path: %s")
m_attach_ds=$(gettext " Attach ZFS Dataset: %s")
m_gzinc=$(gettext " Global zone version: %s")
m_zinc=$(gettext " Non-Global zone version: %s")
m_need_update=$(gettext " Evaluation: Packages in zone %s are out of sync with the global zone. To proceed, retry with the -u flag.")
m_cache=$(gettext " Cache: Using %s.")
m_updating=$(gettext " Updating non-global zone: Output follows")
m_sync_done=$(gettext " Updating non-global zone: Zone updated.")
m_complete=$(gettext " Result: Attach Succeeded.")
m_failed=$(gettext " Result: Attach Failed.")
#
# These two messages are used by the install_image function in
# /usr/lib/brand/shared/common.ksh. Yes, this is terrible.
#
installing=$(gettext " Installing: This may take several minutes...")
no_installing=$(gettext " Installing: Using pre-existing data in zonepath")
f_sanity_variant=$(gettext " Sanity Check: FAILED, couldn't determine %s from image.")
f_sanity_global=$(gettext " Sanity Check: FAILED, appears to be a global zone (%s=%s).")
f_update=$(gettext "Could not update attaching zone")
f_no_pref_publisher=$(gettext "Unable to get preferred publisher information for zone '%s'.")
f_nosuch_key=$(gettext "Failed to find key %s for global zone publisher")
f_nosuch_cert=$(gettext "Failed to find cert %s for global zone publisher")
f_ds_config=$(gettext "Failed to configure dataset %s: could not set %s.")
f_no_active_ds_mounted=$(gettext "Failed to locate any dataset mounted at %s. Attach requires a mounted dataset.")
e_dataset_disappeared=$(gettext "Dataset %s was unexpectedly unmounted")
e_dataset_not_mounted=$(gettext "Dataset %s exists in boot environment but not mounted")
e_dataset_not_in_be=$(gettext "Dataset %s mountpoint %s is not under zone root %s")
# Clean up on interrupt
trap_cleanup() {
typeset msg=$(gettext "Installation cancelled due to interrupt.")
log "$msg"
# umount any mounted file systems
umnt_fs
trap_exit
}
# If the attach failed then clean up the ZFS datasets we created.
trap_exit() {
if [[ $EXIT_CODE == $ZONE_SUBPROC_OK ]]; then
# unmount the zoneroot if labeled brand
is_brand_labeled
(( $? == 1 )) && ( umount $ZONEROOT || \
log "$f_zfs_unmount" "$ZONEPATH/root" )
else
if [[ "$install_media" != "-" ]]; then
/usr/lib/brand/ipkg/uninstall $ZONENAME $ZONEPATH -F
else
# Restore the zone properties for the pre-existing
# dataset.
if [[ -n "$ACTIVE_DS" ]]; then
zfs set canmount=off $ACTIVE_DS
(( $? != 0 )) && error "$f_ds_config" \
"$ACTIVE_DS" "canmount=on"
zfs set zoned=off $ACTIVE_DS
(( $? != 0 )) && error "$f_ds_config" \
"$ACTIVE_DS" "zoned=off"
zfs set mountpoint=$ZONEROOT $ACTIVE_DS
(( $? != 0 )) && error "$f_ds_config" \
"$ACTIVE_DS" "mountpoint=$ZONEROOT"
zfs set canmount=on $ACTIVE_DS
(( $? != 0 )) && error "$f_ds_config" \
"$ACTIVE_DS" "canmount=on"
zfs mount "$ACTIVE_DS" || \
error "$e_mount1_failed" "$ACTIVE_DS"
fi
fi
log "$m_failed"
fi
exit $EXIT_CODE
}
EXIT_CODE=$ZONE_SUBPROC_USAGE
install_media="-"
trap trap_cleanup INT
trap trap_exit EXIT
PKG="/usr/bin/pkg"
KEYDIR=/var/pkg/ssl
# If we weren't passed at least two arguments, exit now.
(( $# < 2 )) && exit $ZONE_SUBPROC_USAGE
zone=
init_zone zone "$1" "$2"
# Set ZONEPATH, etc.
eval $(bind_legacy_zone_globals zone)
shift; shift # remove ZONENAME and ZONEPATH from arguments array
logdir="$ZONEROOT/var/log"
#
# Resetting GZ_IMAGE to something besides slash allows for simplified
# debugging of various global zone image configurations-- simply make
# an image somewhere with the appropriate interesting parameters.
#
GZ_IMAGE=${GZ_IMAGE:-/}
PKG_IMAGE=$GZ_IMAGE
export PKG_IMAGE
allow_update=0
noexecute=0
unset inst_type
# Get publisher information for global zone. These structures are used
# to store information about the global zone publishers and
# incorporations.
typeset -A gz_publishers
typeset gz_incorporations=""
#
# Gather the zone publisher details. $1 is the location of the image we
# are processing and $2 is an associative array used to store publisher
# details.
#
gather_zone_publisher_details() {
STORED_IMAGE=$PKG_IMAGE
PKG_IMAGE=$1;export PKG_IMAGE
typeset -n publishers=$2
typeset -li publisher_count=0
typeset -li url_count=0
typeset line=
typeset name=
typeset mirror=
typeset origin=
typeset opublisher=
#
# Store publisher, origin and security details. It is assumed
# that mirrors all use the same key as the origins.
#
for line in $(get_publisher_urls all origin); do
print $line | IFS="=" read name origin
# When a publisher has multiple origins, the
# additional origins don't contain the publisher
# name. Correct for this by checking if origin is not
# set by get_publisher_urls() and, if so, use the
# "name" as the origin and set the name to the value
# we have already saved.
if [[ -z $origin ]]; then
origin=$name
name=${publisher.name}
elif [[ "$origin" == "None" ]]; then
# Publisher with no origins.
origin=""
fi
# Use a compound variable to store all the data
# relating to a publisher.
if [[ -z ${publishers[$name]} ]]; then
typeset -C publisher_$publisher_count
typeset -n publisher=publisher_$publisher_count
typeset publisher.sticky=""
typeset publisher.preferred=""
typeset publisher.enabled=""
typeset -a publisher.origins
typeset -a publisher.mirrors
typeset publisher.name=$name
typeset publisher.keyfile=""
typeset publisher.certfile=""
get_publisher_attrs ${publisher.name} origin | \
IFS=" " read publisher.sticky publisher.preferred \
publisher.enabled
if [[ -n "$origin" ]]; then
get_pub_secinfo ${publisher.name} | \
read publisher.keyfile publisher.certfile
[[ ${publisher.keyfile} != "None" && \
! -f ${PKG_IMAGE}/${publisher.keyfile} ]] && \
fail_usage "$f_nosuch_key" \
${publisher.keyfile}
[[ ${publisher.certfile} != "None" && \
! -f ${PKG_IMAGE}/${publisher.certfile} ]] && \
fail_usage "$f_nosuch_cert" \
${publisher.certfile}
else
# Publisher has no origins.
publisher.keyfile="None"
publisher.certfile="None"
fi
publisher_count=publisher_count+1
url_count=0
fi
publisher.origins[$url_count]=$origin
publishers[$name]=${publisher}
url_count=url_count+1
done
#
# Store mirror details
#
url_count=0
for line in $(get_publisher_urls all mirror); do
print $line | IFS="=" read name mirror
if [[ -z $mirror ]]; then
mirror=$name
name=${publisher.name}
fi
if [[ -z $opublisher || $opublisher != $name ]]; then
opublisher=$name
eval publisher="${publishers[$name]}"
url_count=0
fi
publisher.mirrors[$url_count]=$mirror
publishers[$name]=${publisher}
url_count=url_count+1
done
PKG_IMAGE=$STORED_IMAGE;export PKG_IMAGE
}
#
# $1 is an associative array of publishers. Search this array and
# return the preferred publisher.
#
get_preferred_publisher() {
typeset -n publishers=$1
typeset publisher=
for key in ${!publishers[*]}; do
eval publisher="${publishers[$key]}"
if [[ ${publisher.preferred} == "true" ]]; then
print ${key}
return 0
fi
done
return 1
}
#
# $1 is an empty string to be populated with a list of incorporation
# fmris.
#
gather_incorporations() {
typeset -n incorporations=$1
typeset p=
for p in \
$(LC_ALL=C $PKG search -Hl -o pkg.name \
':pkg.depend.install-hold:core-os*');do
incorporations="$incorporations $(get_pkg_fmri $p)"
done
}
#
# Print the pkg(1) command which defines a publisher. $1 is an associative
# array of publisher details and $2 is the publisher to be printed.
#
print_publisher_pkg_defn() {
typeset -n publishers=$1
typeset pname=$2
typeset publisher=
typeset args=""
typeset origin=
typeset mirror=
eval publisher="${publishers[$pname]}"
if [[ ${publisher.preferred} == "true" ]]; then
args="$args -P"
fi
for origin in "${publisher.origins[@]}"; do
args="$args -g $origin"
done
for mirror in "${publisher.mirrors[@]}"; do
args="$args -m $mirror"
done
if [[ ${publisher.sticky} == "true" ]]; then
args="$args --sticky"
else
args="$args --non-sticky"
fi
if [[ ${publisher.enabled} == "true" ]]; then
args="$args --enable"
else
args="$args --disable"
fi
echo "$args"
}
# Other brand attach options are invalid for this brand.
while getopts "a:d:nr:u" opt; do
case $opt in
a)
if [[ -n "$inst_type" ]]; then
fatal "$incompat_options" "$m_usage"
fi
inst_type="archive"
install_media="$OPTARG"
;;
d)
if [[ -n "$inst_type" ]]; then
fatal "$incompat_options" "$m_usage"
fi
inst_type="directory"
install_media="$OPTARG"
;;
n) noexecute=1 ;;
r)
if [[ -n "$inst_type" ]]; then
fatal "$incompat_options" "$m_usage"
fi
inst_type="stdin"
install_media="$OPTARG"
;;
u) allow_update=1 ;;
?) fail_usage "" ;;
*) fail_usage "";;
esac
done
shift $((OPTIND-1))
if [[ $noexecute == 1 && -n "$inst_type" ]]; then
fatal "$m_usage"
fi
[[ -z "$inst_type" ]] && inst_type="directory"
if [ $noexecute -eq 1 ]; then
#
# The zone doesn't have to exist when the -n option is used, so do
# this work early.
#
# XXX There is no sw validation for IPS right now, so just pretend
# everything will be ok.
EXIT_CODE=$ZONE_SUBPROC_OK
exit $ZONE_SUBPROC_OK
fi
LOGFILE=$(/usr/bin/mktemp -t -p /var/tmp ${zone.name}.attach_log.XXXXXX)
if [[ -z "$LOGFILE" ]]; then
fatal "$e_tmpfile"
fi
exec 2>>"$LOGFILE"
log "$m_attach_log" "$LOGFILE"
# Remember what was mounted on the zone root in case the attach fails.
get_ds_from_path "${zone.root}" ACTIVE_DS
attach_datasets -t "$inst_type" -m "$install_media" zone
migrate_export zone
#
# Perform a sanity check to confirm that the image is not a global zone.
#
VARIANT=variant.opensolaris.zone
variant=$(LC_ALL=C $PKG -R $ZONEROOT variant -H $VARIANT)
[[ $? -ne 0 ]] && fatal "$f_sanity_variant" $VARIANT
echo $variant | IFS=" " read variantname variantval
[[ $? -ne 0 ]] && fatal "$f_sanity_variant"
# Check that we got the output we expect...
[[ $variantname = "$VARIANT" ]] || fatal "$f_sanity_variant" $VARIANT
# Check that the variant is non-global, else fail
[[ $variantval = "nonglobal" ]] || fatal "$f_sanity_global" $VARIANT $variantval
# We would like to ensure that our NGZ publishers are a superset of
# those in the GZ. We do this by building a list of all publishers in
# the GZ. We then process this list in the NGZ, first removing (if
# present) and then installing all publishers in this list. Other
# publisher, i.e. those not in the GZ list, are left as is.
#
# Gather all the publisher details for the global zone
#
gather_zone_publisher_details $PKG_IMAGE gz_publishers
#
# Get the preferred publisher for the global zone
# If we were not able to get the zone's preferred publisher, complain.
#
gz_publisher_pref=$(get_preferred_publisher gz_publishers)
if [[ $? -ne 0 ]]; then
fail_usage "$f_no_pref_publisher" "global"
fi
vlog "Preferred global publisher: $gz_publisher_pref"
#
# Try to find the "entire" incorporation's FMRI in the gz.
#
gz_entire_fmri=$(get_entire_incorp)
#
# If entire isn't installed, create an array of global zone core-os
# incorporations.
#
if [[ -z $gz_entire_fmri ]]; then
gather_incorporations gz_incorporations
fi
#
# We're done with the global zone: switch images to the non-global
# zone.
#
PKG_IMAGE="$ZONEROOT"
#
# Try to find the "entire" incorporation's FMRI in the ngz.
#
ngz_entire_fmri=$(get_entire_incorp)
[[ -n $gz_entire_fmri ]] && log "$m_gzinc" "$gz_entire_fmri"
[[ -n $ngz_entire_fmri ]] && log "$m_zinc" "$ngz_entire_fmri"
#
# Create the list of incorporations we wish to install/update in the
# ngz.
#
typeset -n incorp_list
if [[ -n $gz_entire_fmri ]]; then
incorp_list=gz_entire_fmri
else
incorp_list=gz_incorporations
fi
#
# If there is a cache, use it.
#
if [[ -f /var/pkg/pkg5.image && -d /var/pkg/publisher ]]; then
PKG_CACHEROOT=/var/pkg/publisher
export PKG_CACHEROOT
log "$m_cache" "$PKG_CACHEROOT"
fi
log "$m_updating"
#
# The NGZ publishers must be a superset of the GZ publisher. Process
# the GZ publishers and make the NGZ publishers match them.
# You can't remove a preferred publisher, so temporarily create
# a preferred publisher
RANDOM=$$
ZNAME=za$RANDOM
LC_ALL=C $PKG set-publisher --no-refresh -P -g http://localhost:10000 $ZNAME
for key in ${!gz_publishers[*]}; do
typeset newloc=""
args=$(print_publisher_pkg_defn gz_publishers $key)
# Copy credentials from global zone.
safe_dir var
safe_dir var/pkg
eval publisher="${gz_publishers[$key]}"
if [[ ${publisher.keyfile} != "None" || \
${publisher.certfile} != "None" ]]; then
if [[ -e $ZONEROOT/$KEYDIR ]]; then
safe_dir $KEYDIR
else
mkdir -m 755 $ZONEROOT/$KEYDIR
fi
fi
if [[ ${publisher.keyfile} != "None" ]]; then
relnewloc="$KEYDIR/$(basename ${publisher.keyfile})"
newloc="$ZONEROOT/$relnewloc"
safe_copy ${publisher.keyfile} $newloc
chmod 644 $newloc
chown -h root:root $newloc
args="$args -k $relnewloc"
fi
if [[ ${publisher.certfile} != "None" ]]; then
relnewloc="$KEYDIR/$(basename ${publisher.certfile})"
newloc="$ZONEROOT/$relnewloc"
safe_copy ${publisher.certfile} $newloc
chmod 644 $newloc
chown -h root:root $newloc
args="$args -c $relnewloc"
fi
LC_ALL=C $PKG unset-publisher $key >/dev/null 2>&1
LC_ALL=C $PKG set-publisher $args $key
done
#
# Now remove our temporary publisher
#
LC_ALL=C $PKG unset-publisher $ZNAME
#
# Bring the ngz entire incorporation into sync with the gz as follows:
# - First compare the existence of entire in both global and non-global
# zone and update the non-global zone accordingly.
# - Then, if updates aren't allowed check if we can attach because no
# updates are required. If we can, then we are finished.
# - Finally, we know we can do updates and they are required, so update
# all the non-global zone incorporations using the list we gathered
# from the global zone earlier.
#
if [[ -z $gz_entire_fmri && -n $ngz_entire_fmri ]]; then
if [[ $allow_update == 1 ]]; then
LC_ALL=C $PKG uninstall entire || pkg_err_check "$f_update"
else
log "\n$m_need_update" "$ZONENAME"
EXIT_CODE=$ZONE_SUBPROC_NOTCOMPLETE
exit $EXIT_CODE
fi
fi
if [[ $allow_update == 0 ]]; then
LC_ALL=C $PKG install --accept --no-refresh -n $incorp_list
if [[ $? == 4 ]]; then
log "\n$m_complete"
EXIT_CODE=$ZONE_SUBPROC_OK
exit $EXIT_CODE
else
log "\n$m_need_update" "$ZONENAME"
EXIT_CODE=$ZONE_SUBPROC_NOTCOMPLETE
exit $EXIT_CODE
fi
fi
#
# If the NGZ doesn't have entire, but the GZ does, then we have to install
# entire twice. First time we don't specify a version and let constraining
# incorporations determine the version. Second time, we try to install the
# same version as we have in the GZ.
#
if [[ -n $gz_entire_fmri && -z $ngz_entire_fmri ]]; then
LC_ALL=C $PKG install --accept --no-refresh entire || \
pkg_err_check "$f_update"
fi
LC_ALL=C $PKG install --accept --no-refresh $incorp_list || \
pkg_err_check "$f_update"
log "\n$m_sync_done"
log "$m_complete"
EXIT_CODE=$ZONE_SUBPROC_OK
exit $ZONE_SUBPROC_OK