--- a/src/brand/attach Sat Apr 24 23:40:12 2010 -0400
+++ b/src/brand/attach Mon Apr 26 02:19:08 2010 -0700
@@ -19,9 +19,9 @@
#
# CDDL HEADER END
#
+
#
-# Copyright 2010 Sun Microsystems, Inc. All rights reserved.
-# Use is subject to license terms.
+# Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
#
. /usr/lib/brand/ipkg/common.ksh
@@ -30,38 +30,24 @@
m_zfs=$(gettext "A ZFS file system was created for the zone.")
m_attaching=$(gettext "Attaching...")
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_gzinc=$( gettext " Global zone version: %s")
-m_zinc=$( gettext " Non-Global zone version: %s")
-m_insync=$(gettext " Evaluation: Packages in %s are in sync with global zone.")
-m_uprev=$(gettext " Evaluation: %s is newer than the global zone.")
-m_dnrev=$(gettext " Evaluation: %s is downrev of the global zone.")
-m_resetpub=$(gettext " Publisher Check: Zone preferred publisher does not contain")
-m_resetpub2=$(gettext " %s.")
-m_resetpub3=$(gettext " Publisher Reset: Copying preferred publisher from global zone.")
-m_pubfine=$(gettext " Publisher Check: Looks good.")
-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 to %s")
-m_complete=$(gettext "Attach complete.")
-
+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.")
sanity_fail_global=$(gettext " Sanity Check: FAILED, the image is from the global zone.")
-f_downrev=$(gettext "Zone is downrev of global zone. Specify -u to update it.")
-f_uprev=$(gettext "Zone is uprev of global zone. Global zone will need to be updated before attach can proceed.")
f_update=$(gettext "Could not update attaching zone")
-f_gz_entire=$(gettext "Could not find 'entire' incorporation for global zone.")
-f_zone_entire=$(gettext "Could not find 'entire' incorporation for non-global zone.")
-f_fmri_compare=$(gettext "Failed to compare 'entire' FMRIs")
-f_reset_pub=$(gettext "Failed to reset publisher to %s %s")
-f_del_pub=$(gettext "Failed to unset publisher '%s' for zone.")
f_no_pref_publisher=$(gettext "Unable to get preferred publisher information for zone '%s'.")
-f_get_secinfo=$(gettext "Failed to get key/cert information for publisher %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")
# Clean up on interrupt
-trap_cleanup()
-{
- msg=$(gettext "Installation cancelled due to interrupt.")
+trap_cleanup() {
+ typeset msg=$(gettext "Installation cancelled due to interrupt.")
+
log "$msg"
# umount any mounted file systems
@@ -71,13 +57,12 @@
}
# If the attach failed then clean up the ZFS datasets we created.
-trap_exit()
-{
+trap_exit() {
if [[ $EXIT_CODE == $ZONE_SUBPROC_OK ]]; then
# unmount the zoneroot if labeled brand
is_brand_labeled
(( $? == 1 )) && ( umount $ZONEROOT || \
- printf "$f_zfs_unmount" "$ZONEPATH/root" )
+ log "$f_zfs_unmount" "$ZONEPATH/root" )
else
if [[ "$install_media" != "-" ]]; then
/usr/lib/brand/ipkg/uninstall $ZONENAME $ZONEPATH -F
@@ -91,6 +76,7 @@
>/dev/null 2>&1
fi
fi
+ log "$m_failed"
fi
exit $EXIT_CODE
@@ -130,10 +116,181 @@
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}
+ 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
+ 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}
+ 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
@@ -189,6 +346,7 @@
fatal "$e_tmpfile"
fi
exec 2>>"$LOGFILE"
+
log "$m_attach_log" "$LOGFILE"
#
@@ -217,8 +375,8 @@
#
ACTIVE_DS=`mount -p | nawk -v zroot=$ZONEROOT '{
if ($3 == zroot && $4 == "zfs")
- print $1
- }'`
+ print $1
+ }'`
[[ -z "$ACTIVE_DS" ]] && fatal "$f_no_active_ds"
@@ -247,8 +405,8 @@
mounted_ds=`mount -p | nawk -v zroot=$ZONEROOT '{
if ($3 == zroot && $4 == "zfs")
- print $1
- }'`
+ print $1
+ }'`
if [[ -z $mounted_ds ]]; then
mount -F zfs $ACTIVE_DS $ZONEROOT || fatal "$f_zfs_mount"
@@ -296,36 +454,41 @@
[[ -f $ZONEROOT/var/svc/manifest/system/dumpadm.xml ]] && \
fatal "$sanity_fail_global"
-#
-# Look for the 'entire' incorporation's FMRI in the current image; due to users
-# doing weird machinations with their publishers, we strip off the publisher
-# from the FMRI if it is present.
-#
-gz_entire_fmri=$(get_entire_incorp) || fatal "$f_gz_entire"
+# 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.
#
-# Get publisher information for global zone.
+# 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.
#
-
-get_publisher_urls preferred origin |
- IFS="=" read gz_publisher gz_publisher_origins
+gz_publisher_pref=$(get_preferred_publisher gz_publishers)
-[[ -z $gz_publisher ]] && fail_usage "$f_no_pref_publisher" "global"
-[[ -z $gz_publisher_origins ]] && fail_usage "$f_no_pref_publisher" "global"
+if [[ $? -ne 0 ]]; then
+ fail_usage "$f_no_pref_publisher" "global"
+fi
-get_publisher_urls preferred mirror |
- IFS="=" read ignored gz_publisher_mirrors
+log "preferred global publisher: $gz_publisher_pref"
-get_pub_secinfo $gz_publisher | read gzkeyfile gzcertfile
-if [[ $? -ne 0 ]]; then
- fail_usage "$f_get_secinfo" $gz_publisher
+#
+# 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
-[[ $gzkeyfile != "None" && ! -f $gzkeyfile ]] && \
- fail_usage "$f_nosuch_key" $gzkeyfile
-[[ $gzcertfile != "None" && ! -f $gzcertfile ]] && \
- fail_usage "$f_nosuch_cert" $gzcertfile
-
#
# We're done with the global zone: switch images to the non-global
@@ -334,73 +497,57 @@
PKG_IMAGE="$ZONEROOT"
#
-# Get publisher information for non global zone.
-# If we were not able to get the zone's preferred publisher, complain.
+# Try to find the "entire" incorporation's FMRI in the ngz.
#
-get_publisher_urls preferred origin |
- IFS="=" read zone_publisher zone_publisher_origins
+ngz_entire_fmri=$(get_entire_incorp)
-[[ -z $zone_publisher ]] && fail_usage "$f_no_pref_publisher" $ZONENAME
-[[ -z $zone_publisher_origins ]] && fail_usage "$f_no_pref_publisher" $ZONENAME
-
-get_publisher_urls preferred mirror |
- IFS="=" read ignored zone_publisher_mirrors
+[[ -n $gz_entire_fmri ]] && log "$m_gzinc" "$gz_entire_fmri"
+[[ -n $ngz_entire_fmri ]] && log "$m_zinc\n" "$ngz_entire_fmri"
#
-# Get entire incorp for non-global zone
-#
-zone_entire_fmri=$(get_entire_incorp) || fatal "$f_zone_entire"
-
-printf "$m_gzinc\n" $gz_entire_fmri
-printf "$m_zinc\n" $zone_entire_fmri
-#
-# if the zone entire and the gz entire match, we're good.
+# Create the list of incorporations we wish to install/update in the
+# ngz.
#
-comp=$(/usr/lib/brand/ipkg/fmri_compare $zone_entire_fmri $gz_entire_fmri)
-if [[ $? -ne 0 ]]; then
- fatal "$f_fmri_compare"
-fi
-if [[ $comp = "=" ]]; then
- printf "$m_insync\n" $ZONENAME
- echo $m_complete
- EXIT_CODE=$ZONE_SUBPROC_OK
- exit $ZONE_SUBPROC_OK
-fi
-if [[ $comp = ">" ]]; then
- printf "$m_uprev\n" $ZONENAME
- fatal "$f_uprev"
+typeset -n incorp_list
+if [[ -n $gz_entire_fmri ]]; then
+ incorp_list=gz_entire_fmri
+else
+ incorp_list=gz_incorporations
fi
#
-# If we're here, the zone is downrev of the global zone
+# If there is a cache, use it.
#
-if [ -z $allow_update ]; then
- # zone is downrev
- printf "$m_dnrev\n" $ZONENAME
- fatal "$f_downrev"
+if [ -d /var/pkg/download ]; then
+ PKG_CACHEDIR=/var/pkg/download
+ export PKG_CACHEDIR
+ log "$m_cache" "$PKG_CACHEDIR"
fi
+log "$m_updating"
+
#
-# See if the zone knows about the gz entire fmri in question. If yes,
-# we'll try using that.
-#
-LC_ALL=C $PKG list --no-refresh -a $gz_entire_fmri > /dev/null 2>&1
+# 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
-#
-# If this doesn't exist, then we reset the preferred pub for
-# the zone to that of the global zone on attach, and try again.
-#
-if [[ $? -ne 0 ]]; then
- printf "$m_resetpub\n"
- printf "$m_resetpub2\n" $gz_entire_fmri
- printf "$m_resetpub3\n"
+LC_ALL=C $PKG set-publisher --no-refresh -P -g http://localhost:10000 $ZNAME
+for key in ${!gz_publishers[*]}; do
+ typeset newlocation=""
+ args=$(print_publisher_pkg_defn gz_publishers $key)
+
+ # Copy credentials from global zone.
safe_dir var
safe_dir var/pkg
- # Copy credentials from global zone.
- secinfo=""
- if [[ $gzkeyfile != "None" || $gzcertfile != "None" ]]; then
+ eval publisher="${gz_publishers[$key]}"
+ if [[ ${publisher.keyfile} != "None" || \
+ ${publisher.certfile} != "None" ]]; then
if [[ -e $ZONEROOT/$KEYDIR ]]; then
safe_dir $KEYDIR
else
@@ -408,106 +555,77 @@
fi
fi
- if [[ $gzkeyfile != None ]]; then
- newlocation="$KEYDIR/attach_$(basename $gzkeyfile)"
- safe_copy $gzkeyfile $ZONEROOT/$newlocation
- chmod 644 $ZONEROOT/$newkeylocation
- chown -h root:root $ZONEROOT/$newkeylocation
- secinfo="$secinfo -k $newlocation"
- fi
- if [[ $gzcertfile != None ]]; then
- newlocation="$KEYDIR/attach_$(basename $gzcertfile)"
- safe_copy $gzcertfile $ZONEROOT/$newlocation
- chmod 644 $ZONEROOT/$newkeylocation
- chown -h root:root $ZONEROOT/$newkeylocation
- secinfo="$secinfo -c $newlocation"
+ if [[ ${publisher.keyfile} != "None" ]]; then
+ newlocation="$KEYDIR/$(basename ${publisher.keyfile})"
+ safe_copy ${publisher.keyfile} $newlocation
+ chmod 644 $newlocation
+ chown -h root:root $newlocation
+ args="$args -k $newlocation"
fi
-
- # Note that we do cause a refresh here--at some point we need the
- # catalog to be updated.
-
- # Be certain that the global zone preferred publisher exists in
- # the non-global zone. If it doesn't, all of these comparisons
- # are not valid.
- del_ngz_pub=0
- if [[ -n "$zone_publisher" && "$zone_publisher" != "$gz_publisher" ]];
- then
- zone_publisher_origins=""
- zone_publisher_mirrors=""
- del_ngz_pub=1
+ if [[ ${publisher.certfile} != "None" ]]; then
+ newlocation="$KEYDIR/$(basename ${publisher.certfile})"
+ safe_copy ${publisher.certfile} $newlocation
+ chmod 644 $newlocation
+ chown -h root:root $newlocation
+ args="$args -c $newlocation"
fi
-
- # Add any origins not found in zone configuration.
- origins=""
- src="$(echo $zone_publisher_origins | sed 's/ /|/g')"
- for u in $gz_publisher_origins; do
- [[ -z "$u" ]] && continue
- case $u in
- $src) ;; # Already in zone config.
- *) origins="${origins}-g $u ";; # Add.
- esac
- done
+ LC_ALL=C $PKG unset-publisher $key >/dev/null 2>&1
+ LC_ALL=C $PKG set-publisher $args $key
+
+done
- # Remove any origins not found in global configuration.
- src="$(echo $gz_publisher_origins | sed 's/ /|/g')"
- for u in $zone_publisher_origins; do
- [[ -z "$u" ]] && continue
- case $u in
- $src) ;; # In global config.
- *) origins="${origins}-G $u ";; # Remove.
- esac
- done
-
- # Add any mirrors not found in zone configuration.
- mirrors=""
- src="$(echo $zone_publisher_mirrors | sed 's/ /|/g')"
- for u in $gz_publisher_mirrors; do
- case $u in
- $src) ;; # Already in zone config.
- *) mirrors="${mirrors}-m $u ";; # Add.
- esac
- done
+#
+# Now remove our temporary publisher
+#
+LC_ALL=C $PKG unset-publisher $ZNAME
- # Remove any mirrors not found in global configuration.
- src="$(echo $gz_publisher_mirrors | sed 's/ /|/g')"
- for u in $zone_publisher_mirrors; do
- case $u in
- $src) ;; # In global config.
- *) mirrors="${mirrors}-M $u ";; # Remove.
- esac
- done
-
- $PKG set-publisher -P $origins $mirrors $secinfo $gz_publisher
- if [[ $? -ne 0 ]]; then
- fatal "$f_reset_pub" $gz_publisher \
- "$gz_publisher_origins $gz_publisher_mirrors"
- fi
-
- # If the original preferred non-global zone publisher has the
- # same URI as the global zone publisher we just stuck into the zone,
- # then we now need to delete the old ngz publisher.
- (( $del_ngz_pub == 1 )) && ( $PKG unset-publisher $zone_publisher || \
- fatal "$f_del_pub" $zone_publisher )
-
- zone_publisher=$gz_publisher
- zone_publisher_origins=$gz_publisher_origins
- zone_publisher_mirrors=$gz_publisher_mirrors
-else
- printf "$m_pubfine\n"
+#
+# 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 "$m_need_update" "$ZONENAME"
+ EXIT_CODE=$ZONE_SUBPROC_NOTCOMPLETE
+ exit $EXIT_CODE
+ fi
fi
-if [ -d /var/pkg/download ]; then
- PKG_CACHEDIR=/var/pkg/download
- export PKG_CACHEDIR
- printf "$m_cache\n" $PKG_CACHEDIR
+if [[ $allow_update == 0 ]]; then
+ LC_ALL=C $PKG install --no-refresh -n $incorp_list
+ if [[ $? == 4 ]]; then
+ log "$m_complete"
+ EXIT_CODE=$ZONE_SUBPROC_OK
+ exit $EXIT_CODE
+ else
+ log "$m_need_update" "$ZONENAME"
+ EXIT_CODE=$ZONE_SUBPROC_NOTCOMPLETE
+ exit $EXIT_CODE
+ fi
fi
-printf "$m_updating\n"
+#
+# 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 --no-refresh entire || pkg_err_check "$f_update"
+fi
-$PKG install $gz_entire_fmri || pkg_err_check "$f_update"
+LC_ALL=C $PKG install --no-refresh $incorp_list || pkg_err_check "$f_update"
-printf "$m_sync_done\n" $gz_entire_fmri
-printf "$m_complete\n"
+log "$m_sync_done"
+log "$m_complete"
EXIT_CODE=$ZONE_SUBPROC_OK
exit $ZONE_SUBPROC_OK